Rollup merge of #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 #127619, only the last commit is relevant. r? oli-obk Tracking: - https://github.com/rust-lang/rust/issues/123432
This commit is contained in:
commit
b5771e7e0d
@ -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)
|
||||
@ -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
|
||||
|
@ -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 {
|
||||
@ -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 {
|
||||
@ -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 { .. } => {
|
||||
@ -1340,6 +1343,7 @@ fn suggest_precise_capturing<'tcx>(
|
||||
return;
|
||||
}
|
||||
|
||||
if synthetics.is_empty() {
|
||||
let concatenated_bounds = captured_lifetimes
|
||||
.into_iter()
|
||||
.chain(captured_non_lifetimes)
|
||||
@ -1352,5 +1356,73 @@ fn suggest_precise_capturing<'tcx>(
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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() {}
|
||||
|
@ -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…
Reference in New Issue
Block a user