Skip to content

Commit 5a1a949

Browse files
Fix precise capturing suggestion for hidden type when APITs are involved
1 parent 8aa7081 commit 5a1a949

File tree

5 files changed

+170
-17
lines changed

5 files changed

+170
-17
lines changed

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

compiler/rustc_infer/src/errors/mod.rs

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

compiler/rustc_infer/src/infer/error_reporting/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+
// Suggst 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
}

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() {}

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)