Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix precise capturing suggestion for hidden regions when we have APITs #127664

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions compiler/rustc_infer/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ infer_outlives_content = lifetime of reference outlives lifetime of borrowed con
infer_precise_capturing_existing = add `{$new_lifetime}` to the `use<...>` bound to explicitly capture it
infer_precise_capturing_new = add a `use<...>` bound to explicitly capture `{$new_lifetime}`

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

infer_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here...
infer_prlf_defined_without_sub = the lifetime defined here...
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)
Expand Down Expand Up @@ -387,6 +389,9 @@ infer_type_annotations_needed = {$source_kind ->
.label = type must be known at this point

infer_types_declared_different = these two types are declared with different lifetimes...

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

infer_where_copy_predicates = copy the `where` clause predicates from the trait

infer_where_remove = remove the `where` clause
104 changes: 88 additions & 16 deletions compiler/rustc_infer/src/error_reporting/infer/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1269,9 +1269,13 @@ fn suggest_precise_capturing<'tcx>(
captured_lifetime: ty::Region<'tcx>,
diag: &mut Diag<'_>,
) {
let hir::OpaqueTy { bounds, .. } =
let hir::OpaqueTy { bounds, origin, .. } =
tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();

let hir::OpaqueTyOrigin::FnReturn(fn_def_id) = *origin else {
return;
};

let new_lifetime = Symbol::intern(&captured_lifetime.to_string());

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

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

let concatenated_bounds = captured_lifetimes
.into_iter()
.chain(captured_non_lifetimes)
.map(|sym| sym.to_string())
.collect::<Vec<_>>()
.join(", ");

diag.subdiagnostic(errors::AddPreciseCapturing::New {
span: tcx.def_span(opaque_def_id).shrink_to_hi(),
new_lifetime,
concatenated_bounds,
});
if synthetics.is_empty() {
let concatenated_bounds = captured_lifetimes
.into_iter()
.chain(captured_non_lifetimes)
.map(|sym| sym.to_string())
.collect::<Vec<_>>()
.join(", ");

diag.subdiagnostic(errors::AddPreciseCapturing::New {
span: tcx.def_span(opaque_def_id).shrink_to_hi(),
new_lifetime,
concatenated_bounds,
});
} else {
let mut next_fresh_param = || {
["T", "U", "V", "W", "X", "Y", "A", "B", "C"]
.into_iter()
.map(Symbol::intern)
.chain((0..).map(|i| Symbol::intern(&format!("T{i}"))))
.find(|s| captured_non_lifetimes.insert(*s))
.unwrap()
};

let mut new_params = String::new();
let mut suggs = vec![];
let mut apit_spans = vec![];

for (i, (span, name)) in synthetics.into_iter().enumerate() {
apit_spans.push(span);

let fresh_param = next_fresh_param();

// Suggest renaming.
suggs.push((span, fresh_param.to_string()));

// Super jank. Turn `impl Trait` into `T: Trait`.
//
// This currently involves stripping the `impl` from the name of
// the parameter, since APITs are always named after how they are
// rendered in the AST. This sucks! But to recreate the bound list
// from the APIT itself would be miserable, so we're stuck with
// this for now!
if i > 0 {
new_params += ", ";
}
let name_as_bounds = name.as_str().trim_start_matches("impl").trim_start();
new_params += fresh_param.as_str();
new_params += ": ";
new_params += name_as_bounds;
}

let Some(generics) = tcx.hir().get_generics(fn_def_id) else {
// This shouldn't happen, but don't ICE.
return;
};

// Add generics or concatenate to the end of the list.
suggs.push(if let Some(params_span) = generics.span_for_param_suggestion() {
(params_span, format!(", {new_params}"))
} else {
(generics.span, format!("<{new_params}>"))
});

let concatenated_bounds = captured_lifetimes
.into_iter()
.chain(captured_non_lifetimes)
.map(|sym| sym.to_string())
.collect::<Vec<_>>()
.join(", ");

suggs.push((
tcx.def_span(opaque_def_id).shrink_to_hi(),
format!(" + use<{concatenated_bounds}>"),
));

diag.subdiagnostic(errors::AddPreciseCapturingAndParams {
suggs,
new_lifetime,
apit_spans,
});
}
}
}
22 changes: 22 additions & 0 deletions compiler/rustc_infer/src/errors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1609,3 +1609,25 @@ pub enum AddPreciseCapturing {
post: &'static str,
},
}

pub struct AddPreciseCapturingAndParams {
pub suggs: Vec<(Span, String)>,
pub new_lifetime: Symbol,
pub apit_spans: Vec<Span>,
}

impl Subdiagnostic for AddPreciseCapturingAndParams {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self,
diag: &mut Diag<'_, G>,
_f: &F,
) {
diag.arg("new_lifetime", self.new_lifetime);
diag.multipart_suggestion_verbose(
fluent::infer_precise_capturing_new_but_apit,
self.suggs,
Applicability::MaybeIncorrect,
);
diag.span_note(self.apit_spans, fluent::infer_warn_removing_apit_params);
}
}
12 changes: 12 additions & 0 deletions tests/ui/impl-trait/precise-capturing/hidden-type-suggestion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,16 @@ fn missing<'a, 'captured, 'not_captured, Captured>(x: &'a ()) -> impl Captures<'
//~^ ERROR hidden type for
}

fn no_params_yet(_: impl Sized, y: &()) -> impl Sized {
//~^ HELP add a `use<...>` bound
y
//~^ ERROR hidden type for
}

fn yes_params_yet<'a, T>(_: impl Sized, y: &'a ()) -> impl Sized {
//~^ HELP add a `use<...>` bound
y
//~^ ERROR hidden type for
}

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,48 @@ help: add a `use<...>` bound to explicitly capture `'a`
LL | fn missing<'a, 'captured, 'not_captured, Captured>(x: &'a ()) -> impl Captures<'captured> + use<'captured, 'a, Captured> {
| ++++++++++++++++++++++++++++++

error: aborting due to 4 previous errors
error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
--> $DIR/hidden-type-suggestion.rs:32:5
|
LL | fn no_params_yet(_: impl Sized, y: &()) -> impl Sized {
| --- ---------- opaque type defined here
| |
| hidden type `&()` captures the anonymous lifetime defined here
LL |
LL | y
| ^
|
note: you could use a `use<...>` bound to explicitly capture `'_`, but argument-position `impl Trait`s are not nameable
--> $DIR/hidden-type-suggestion.rs:30:21
|
LL | fn no_params_yet(_: impl Sized, y: &()) -> impl Sized {
| ^^^^^^^^^^
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
|
LL | fn no_params_yet<T: Sized>(_: T, y: &()) -> impl Sized + use<'_, T> {
| ++++++++++ ~ ++++++++++++

error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
--> $DIR/hidden-type-suggestion.rs:38:5
|
LL | fn yes_params_yet<'a, T>(_: impl Sized, y: &'a ()) -> impl Sized {
| -- ---------- opaque type defined here
| |
| hidden type `&'a ()` captures the lifetime `'a` as defined here
LL |
LL | y
| ^
|
note: you could use a `use<...>` bound to explicitly capture `'a`, but argument-position `impl Trait`s are not nameable
--> $DIR/hidden-type-suggestion.rs:36:29
|
LL | fn yes_params_yet<'a, T>(_: impl Sized, y: &'a ()) -> impl Sized {
| ^^^^^^^^^^
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
|
LL | fn yes_params_yet<'a, T, U: Sized>(_: U, y: &'a ()) -> impl Sized + use<'a, T, U> {
| ++++++++++ ~ +++++++++++++++

error: aborting due to 6 previous errors

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