Skip to content

Commit 5fe1ec2

Browse files
authored
Unrolled build for rust-lang#127664
Rollup merge of rust-lang#127664 - compiler-errors:precise-capturing-better-sugg-apit, r=oli-obk Fix precise capturing suggestion for hidden regions when we have APITs Suggests to turn APITs into type parameters so they can be named in precise capturing syntax for hidden type lifetime errors. We also note that it may change the API. This is currently done via a note *and* a suggestion, which feels a bit redundant, but I wasn't totally sure of a better alternative for the presentation. Code is kind of a mess but there's a lot of cases to consider. Happy to iterate on this if you think the approach is too messy. Based on rust-lang#127619, only the last commit is relevant. r? oli-obk Tracking: - rust-lang#123432
2 parents e35364a + 1d40d4c commit 5fe1ec2

File tree

5 files changed

+170
-17
lines changed

5 files changed

+170
-17
lines changed

Diff for: compiler/rustc_infer/messages.ftl

+5
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ infer_outlives_content = lifetime of reference outlives lifetime of borrowed con
225225
infer_precise_capturing_existing = add `{$new_lifetime}` to the `use<...>` bound to explicitly capture it
226226
infer_precise_capturing_new = add a `use<...>` bound to explicitly capture `{$new_lifetime}`
227227
228+
infer_precise_capturing_new_but_apit = add a `use<...>` bound to explicitly capture `{$new_lifetime}` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate
229+
228230
infer_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here...
229231
infer_prlf_defined_without_sub = the lifetime defined here...
230232
infer_prlf_known_limitation = this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
@@ -387,6 +389,9 @@ infer_type_annotations_needed = {$source_kind ->
387389
.label = type must be known at this point
388390
389391
infer_types_declared_different = these two types are declared with different lifetimes...
392+
393+
infer_warn_removing_apit_params = you could use a `use<...>` bound to explicitly capture `{$new_lifetime}`, but argument-position `impl Trait`s are not nameable
394+
390395
infer_where_copy_predicates = copy the `where` clause predicates from the trait
391396
392397
infer_where_remove = remove the `where` clause

Diff for: compiler/rustc_infer/src/error_reporting/infer/region.rs

+88-16
Original file line numberDiff line numberDiff line change
@@ -1269,9 +1269,13 @@ fn suggest_precise_capturing<'tcx>(
12691269
captured_lifetime: ty::Region<'tcx>,
12701270
diag: &mut Diag<'_>,
12711271
) {
1272-
let hir::OpaqueTy { bounds, .. } =
1272+
let hir::OpaqueTy { bounds, origin, .. } =
12731273
tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
12741274

1275+
let hir::OpaqueTyOrigin::FnReturn(fn_def_id) = *origin else {
1276+
return;
1277+
};
1278+
12751279
let new_lifetime = Symbol::intern(&captured_lifetime.to_string());
12761280

12771281
if let Some((args, span)) = bounds.iter().find_map(|bound| match bound {
@@ -1306,6 +1310,7 @@ fn suggest_precise_capturing<'tcx>(
13061310

13071311
let variances = tcx.variances_of(opaque_def_id);
13081312
let mut generics = tcx.generics_of(opaque_def_id);
1313+
let mut synthetics = vec![];
13091314
loop {
13101315
for param in &generics.own_params {
13111316
if variances[param.index as usize] == ty::Bivariant {
@@ -1317,9 +1322,7 @@ fn suggest_precise_capturing<'tcx>(
13171322
captured_lifetimes.insert(param.name);
13181323
}
13191324
ty::GenericParamDefKind::Type { synthetic: true, .. } => {
1320-
// FIXME: We can't provide a good suggestion for
1321-
// `use<...>` if we have an APIT. Bail for now.
1322-
return;
1325+
synthetics.push((tcx.def_span(param.def_id), param.name));
13231326
}
13241327
ty::GenericParamDefKind::Type { .. }
13251328
| ty::GenericParamDefKind::Const { .. } => {
@@ -1340,17 +1343,86 @@ fn suggest_precise_capturing<'tcx>(
13401343
return;
13411344
}
13421345

1343-
let concatenated_bounds = captured_lifetimes
1344-
.into_iter()
1345-
.chain(captured_non_lifetimes)
1346-
.map(|sym| sym.to_string())
1347-
.collect::<Vec<_>>()
1348-
.join(", ");
1349-
1350-
diag.subdiagnostic(errors::AddPreciseCapturing::New {
1351-
span: tcx.def_span(opaque_def_id).shrink_to_hi(),
1352-
new_lifetime,
1353-
concatenated_bounds,
1354-
});
1346+
if synthetics.is_empty() {
1347+
let concatenated_bounds = captured_lifetimes
1348+
.into_iter()
1349+
.chain(captured_non_lifetimes)
1350+
.map(|sym| sym.to_string())
1351+
.collect::<Vec<_>>()
1352+
.join(", ");
1353+
1354+
diag.subdiagnostic(errors::AddPreciseCapturing::New {
1355+
span: tcx.def_span(opaque_def_id).shrink_to_hi(),
1356+
new_lifetime,
1357+
concatenated_bounds,
1358+
});
1359+
} else {
1360+
let mut next_fresh_param = || {
1361+
["T", "U", "V", "W", "X", "Y", "A", "B", "C"]
1362+
.into_iter()
1363+
.map(Symbol::intern)
1364+
.chain((0..).map(|i| Symbol::intern(&format!("T{i}"))))
1365+
.find(|s| captured_non_lifetimes.insert(*s))
1366+
.unwrap()
1367+
};
1368+
1369+
let mut new_params = String::new();
1370+
let mut suggs = vec![];
1371+
let mut apit_spans = vec![];
1372+
1373+
for (i, (span, name)) in synthetics.into_iter().enumerate() {
1374+
apit_spans.push(span);
1375+
1376+
let fresh_param = next_fresh_param();
1377+
1378+
// Suggest renaming.
1379+
suggs.push((span, fresh_param.to_string()));
1380+
1381+
// Super jank. Turn `impl Trait` into `T: Trait`.
1382+
//
1383+
// This currently involves stripping the `impl` from the name of
1384+
// the parameter, since APITs are always named after how they are
1385+
// rendered in the AST. This sucks! But to recreate the bound list
1386+
// from the APIT itself would be miserable, so we're stuck with
1387+
// this for now!
1388+
if i > 0 {
1389+
new_params += ", ";
1390+
}
1391+
let name_as_bounds = name.as_str().trim_start_matches("impl").trim_start();
1392+
new_params += fresh_param.as_str();
1393+
new_params += ": ";
1394+
new_params += name_as_bounds;
1395+
}
1396+
1397+
let Some(generics) = tcx.hir().get_generics(fn_def_id) else {
1398+
// This shouldn't happen, but don't ICE.
1399+
return;
1400+
};
1401+
1402+
// Add generics or concatenate to the end of the list.
1403+
suggs.push(if let Some(params_span) = generics.span_for_param_suggestion() {
1404+
(params_span, format!(", {new_params}"))
1405+
} else {
1406+
(generics.span, format!("<{new_params}>"))
1407+
});
1408+
1409+
let concatenated_bounds = captured_lifetimes
1410+
.into_iter()
1411+
.chain(captured_non_lifetimes)
1412+
.map(|sym| sym.to_string())
1413+
.collect::<Vec<_>>()
1414+
.join(", ");
1415+
1416+
suggs.push((
1417+
tcx.def_span(opaque_def_id).shrink_to_hi(),
1418+
format!(" + use<{concatenated_bounds}>"),
1419+
));
1420+
1421+
diag.subdiagnostic(errors::AddPreciseCapturingAndParams {
1422+
suggs,
1423+
new_lifetime,
1424+
apit_spans,
1425+
});
1426+
}
13551427
}
13561428
}

Diff for: compiler/rustc_infer/src/errors/mod.rs

+22
Original file line numberDiff line numberDiff line change
@@ -1609,3 +1609,25 @@ pub enum AddPreciseCapturing {
16091609
post: &'static str,
16101610
},
16111611
}
1612+
1613+
pub struct AddPreciseCapturingAndParams {
1614+
pub suggs: Vec<(Span, String)>,
1615+
pub new_lifetime: Symbol,
1616+
pub apit_spans: Vec<Span>,
1617+
}
1618+
1619+
impl Subdiagnostic for AddPreciseCapturingAndParams {
1620+
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
1621+
self,
1622+
diag: &mut Diag<'_, G>,
1623+
_f: &F,
1624+
) {
1625+
diag.arg("new_lifetime", self.new_lifetime);
1626+
diag.multipart_suggestion_verbose(
1627+
fluent::infer_precise_capturing_new_but_apit,
1628+
self.suggs,
1629+
Applicability::MaybeIncorrect,
1630+
);
1631+
diag.span_note(self.apit_spans, fluent::infer_warn_removing_apit_params);
1632+
}
1633+
}

Diff for: tests/ui/impl-trait/precise-capturing/hidden-type-suggestion.rs

+12
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,16 @@ fn missing<'a, 'captured, 'not_captured, Captured>(x: &'a ()) -> impl Captures<'
2727
//~^ ERROR hidden type for
2828
}
2929

30+
fn no_params_yet(_: impl Sized, y: &()) -> impl Sized {
31+
//~^ HELP add a `use<...>` bound
32+
y
33+
//~^ ERROR hidden type for
34+
}
35+
36+
fn yes_params_yet<'a, T>(_: impl Sized, y: &'a ()) -> impl Sized {
37+
//~^ HELP add a `use<...>` bound
38+
y
39+
//~^ ERROR hidden type for
40+
}
41+
3042
fn main() {}

Diff for: tests/ui/impl-trait/precise-capturing/hidden-type-suggestion.stderr

+43-1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,48 @@ help: add a `use<...>` bound to explicitly capture `'a`
6262
LL | fn missing<'a, 'captured, 'not_captured, Captured>(x: &'a ()) -> impl Captures<'captured> + use<'captured, 'a, Captured> {
6363
| ++++++++++++++++++++++++++++++
6464

65-
error: aborting due to 4 previous errors
65+
error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
66+
--> $DIR/hidden-type-suggestion.rs:32:5
67+
|
68+
LL | fn no_params_yet(_: impl Sized, y: &()) -> impl Sized {
69+
| --- ---------- opaque type defined here
70+
| |
71+
| hidden type `&()` captures the anonymous lifetime defined here
72+
LL |
73+
LL | y
74+
| ^
75+
|
76+
note: you could use a `use<...>` bound to explicitly capture `'_`, but argument-position `impl Trait`s are not nameable
77+
--> $DIR/hidden-type-suggestion.rs:30:21
78+
|
79+
LL | fn no_params_yet(_: impl Sized, y: &()) -> impl Sized {
80+
| ^^^^^^^^^^
81+
help: add a `use<...>` bound to explicitly capture `'_` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate
82+
|
83+
LL | fn no_params_yet<T: Sized>(_: T, y: &()) -> impl Sized + use<'_, T> {
84+
| ++++++++++ ~ ++++++++++++
85+
86+
error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
87+
--> $DIR/hidden-type-suggestion.rs:38:5
88+
|
89+
LL | fn yes_params_yet<'a, T>(_: impl Sized, y: &'a ()) -> impl Sized {
90+
| -- ---------- opaque type defined here
91+
| |
92+
| hidden type `&'a ()` captures the lifetime `'a` as defined here
93+
LL |
94+
LL | y
95+
| ^
96+
|
97+
note: you could use a `use<...>` bound to explicitly capture `'a`, but argument-position `impl Trait`s are not nameable
98+
--> $DIR/hidden-type-suggestion.rs:36:29
99+
|
100+
LL | fn yes_params_yet<'a, T>(_: impl Sized, y: &'a ()) -> impl Sized {
101+
| ^^^^^^^^^^
102+
help: add a `use<...>` bound to explicitly capture `'a` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate
103+
|
104+
LL | fn yes_params_yet<'a, T, U: Sized>(_: U, y: &'a ()) -> impl Sized + use<'a, T, U> {
105+
| ++++++++++ ~ +++++++++++++++
106+
107+
error: aborting due to 6 previous errors
66108

67109
For more information about this error, try `rustc --explain E0700`.

0 commit comments

Comments
 (0)