rust-lang/rust · #165194
name elided lifetimes in `use<...>` suggestion when `impl Trait` params are present
compiler/rustc_trait_selection/src/diagnostics.rs19 + / 0 −
@@ -1921,6 +1921,25 @@ impl Subdiagnostic for AddPreciseCapturingAndParams { } } +pub(crate) struct AddPreciseCapturingNameLifetimesAndParams {+ pub suggs: Vec<(Span, String)>,+ pub apit_spans: Vec<Span>,+}++impl Subdiagnostic for AddPreciseCapturingNameLifetimesAndParams {+ fn add_to_diag(self, diag: &mut Diag<'_>) {+ diag.multipart_suggestion(+ msg!("add a `use<...>` bound and name the elided lifetimes after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate"),+ self.suggs,+ Applicability::MaybeIncorrect,+ );+ diag.span_note(+ self.apit_spans,+ msg!("argument-position `impl Trait` cannot be named in a `use<...>` bound and must be turned into type parameters"),+ );+ }+}+ pub(crate) struct AddPreciseCapturingAndNameLifetimes { pub suggs: Vec<(Span, String)>, }compiler/rustc_trait_selection/src/error_reporting/infer/region.rs101 + / 73 −
@@ -1528,26 +1528,70 @@ fn suggest_precise_capturing<'tcx>( return; } + let mut next_fresh_param = || {+ ['T', 'U', 'V', 'W', 'X', 'Y', 'A', 'B', 'C']+ .into_iter()+ .map(sym::character)+ .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;+ }+ // `'_` in a capture list only resolves when the signature has a single anonymous // lifetime, so when there are several the suggestion has to name them first.- if synthetics.is_empty()- && let Some(suggs) = name_anon_lifetimes_suggestion(- tcx,- generic_param_scope,- opaque_def_id,- fn_def_id,- hidden_ty,- &captured_lifetimes,- &captured_non_lifetimes,- )- {- diag.subdiagnostic(diagnostics::AddPreciseCapturingAndNameLifetimes { suggs });- return;- }+ let named_suggs = name_anon_lifetimes_suggestion(+ tcx,+ generic_param_scope,+ opaque_def_id,+ fn_def_id,+ hidden_ty,+ &captured_lifetimes,+ &captured_non_lifetimes,+ )+ // a lifetime inside an APIT bound gets its own replacement part from+ // `LifetimeReplaceVisitor`, whose span falls inside the APIT span. overlapping parts in one+ // multipart suggestion panic the emitter, so the filter drops the whole named suggestion+ // and falls back to `'_`.+ .filter(|named| {+ !named.iter().any(|(span, _)| apit_spans.iter().any(|apit| apit.overlaps(*span)))+ }); - captured_lifetimes.insert(new_lifetime);+ if apit_spans.is_empty() {+ if let Some(suggs) = named_suggs {+ diag.subdiagnostic(diagnostics::AddPreciseCapturingAndNameLifetimes { suggs });+ return;+ }++ captured_lifetimes.insert(new_lifetime); - if synthetics.is_empty() { let concatenated_bounds = captured_lifetimes .into_iter() .chain(captured_non_lifetimes)@@ -1561,72 +1605,56 @@ fn suggest_precise_capturing<'tcx>( concatenated_bounds, }); } else {- let mut next_fresh_param = || {- ['T', 'U', 'V', 'W', 'X', 'Y', 'A', 'B', 'C']- .into_iter()- .map(sym::character)- .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}"))+ let names_lifetimes = named_suggs.is_some();+ if let Some(named) = named_suggs {+ suggs.extend(named); } else {- (generics.span, format!("<{new_params}>"))- });+ captured_lifetimes.insert(new_lifetime); - let concatenated_bounds = captured_lifetimes- .into_iter()- .chain(captured_non_lifetimes)- .map(|sym| sym.to_string())- .collect::<Vec<_>>()- .join(", ");+ 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}>"),+ ));+ } - suggs.push((- tcx.def_span(opaque_def_id).shrink_to_hi(),- format!(" + use<{concatenated_bounds}>"),- ));+ // Add generics or concatenate to the end of the list.+ if let Some(params_span) = generics.span_for_param_suggestion() {+ suggs.push((params_span, format!(", {new_params}")));+ } else if let Some((_, new_lifetimes)) =+ suggs.iter_mut().find(|(span, _)| *span == generics.span)+ {+ // `suggest_name_regions` already pushed `<'a, 'b>` at `generics.span`, so a second+ // part at the same empty span cannot apply. the type params are spliced into the+ // existing string, with lifetimes first because the grammar requires it.+ *new_lifetimes = format!("{}, {new_params}>", new_lifetimes.trim_end_matches('>'));+ } else {+ suggs.push((generics.span, format!("<{new_params}>")));+ } - diag.subdiagnostic(diagnostics::AddPreciseCapturingAndParams {- suggs,- new_lifetime,- apit_spans,- });+ if names_lifetimes {+ diag.subdiagnostic(diagnostics::AddPreciseCapturingNameLifetimesAndParams {+ suggs,+ apit_spans,+ });+ } else {+ diag.subdiagnostic(diagnostics::AddPreciseCapturingAndParams {+ suggs,+ new_lifetime,+ apit_spans,+ });+ } } } }tests/ui/impl-trait/precise-capturing/name-anon-lifetime-suggestion-apit-bound.rsadded15 + / 0 −
@@ -0,0 +1,15 @@+//@ edition: 2021++// regression test: a lifetime inside an APIT bound would panic the suggestion emitter+// once naming was applied to APITs, because the replacement part overlapped the APIT span.+// the expected output is the '_ fallback, which is not a correct suggestion but avoids+// the panic. the sibling test covers the cases where naming succeeds.++#![feature(anonymous_lifetime_in_impl_trait)]++fn anon_in_apit_bound(x: &(), y: &(), mut z: impl Iterator<Item = &u8>) -> impl Sized {+ (x, y, z.next())+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++fn main() {}tests/ui/impl-trait/precise-capturing/name-anon-lifetime-suggestion-apit-bound.stderradded24 + / 0 −
@@ -0,0 +1,24 @@+error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+ --> $DIR/name-anon-lifetime-suggestion-apit-bound.rs:11:5+ |+LL | fn anon_in_apit_bound(x: &(), y: &(), mut z: impl Iterator<Item = &u8>) -> impl Sized {+ | --- ---------- opaque type defined here+ | |+ | hidden type `(&(), &(), Option<&u8>)` captures the anonymous lifetime defined here+LL | (x, y, z.next())+ | ^^^^^^^^^^^^^^^^+ |+note: you could use a `use<...>` bound to explicitly capture `'_`, but argument-position `impl Trait`s are not nameable+ --> $DIR/name-anon-lifetime-suggestion-apit-bound.rs:10:46+ |+LL | fn anon_in_apit_bound(x: &(), y: &(), mut z: impl Iterator<Item = &u8>) -> 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 anon_in_apit_bound(x: &(), y: &(), mut z: impl Iterator<Item = &u8>) -> impl Sized {+LL + fn anon_in_apit_bound<T: Iterator<Item = &u8>>(x: &(), y: &(), mut z: T) -> impl Sized + use<'_, T> {+ |++error: aborting due to 1 previous error++For more information about this error, try `rustc --explain E0700`.tests/ui/impl-trait/precise-capturing/name-anon-lifetime-suggestion-apit.fixedadded31 + / 0 −
@@ -0,0 +1,31 @@+//@ edition: 2021+//@ run-rustfix++#![allow(dead_code)]++fn no_generics<'a, 'b, T: Sized>(x: &'a (), y: &'b (), _z: T) -> impl Sized + use<'a, 'b, T> {+ (x, y)+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++fn with_type_param<'a, 'b, U, T: Sized>(x: &'a (), y: &'b (), _z: T, _u: U) -> impl Sized + use<'a, 'b, U, T> {+ (x, y)+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++fn with_lifetime_param<'b, 'c, 'a, T: Sized>(x: &'a (), y: &'b (), z: &'c (), _w: T) -> impl Sized + use<'a, 'b, 'c, T> {+ (x, y, z)+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++fn two_apits<'a, 'b, T: Sized, U: Copy>(x: &'a (), y: &'b (), _a: T, _b: U) -> impl Sized + use<'a, 'b, T, U> {+ (x, y)+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++fn one_anon<T: Sized>(x: &(), _z: T) -> impl Sized + use<'_, T> {+ x+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++fn main() {}tests/ui/impl-trait/precise-capturing/name-anon-lifetime-suggestion-apit.rsadded31 + / 0 −
@@ -0,0 +1,31 @@+//@ edition: 2021+//@ run-rustfix++#![allow(dead_code)]++fn no_generics(x: &(), y: &(), _z: impl Sized) -> impl Sized {+ (x, y)+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++fn with_type_param<U>(x: &(), y: &(), _z: impl Sized, _u: U) -> impl Sized {+ (x, y)+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++fn with_lifetime_param<'a>(x: &'a (), y: &(), z: &(), _w: impl Sized) -> impl Sized {+ (x, y, z)+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++fn two_apits(x: &(), y: &(), _a: impl Sized, _b: impl Copy) -> impl Sized {+ (x, y)+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++fn one_anon(x: &(), _z: impl Sized) -> impl Sized {+ x+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++fn main() {}tests/ui/impl-trait/precise-capturing/name-anon-lifetime-suggestion-apit.stderradded108 + / 0 −
@@ -0,0 +1,108 @@+error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+ --> $DIR/name-anon-lifetime-suggestion-apit.rs:7:5+ |+LL | fn no_generics(x: &(), y: &(), _z: impl Sized) -> impl Sized {+ | --- ---------- opaque type defined here+ | |+ | hidden type `(&(), &())` captures the anonymous lifetime defined here+LL | (x, y)+ | ^^^^^^+ |+note: argument-position `impl Trait` cannot be named in a `use<...>` bound and must be turned into type parameters+ --> $DIR/name-anon-lifetime-suggestion-apit.rs:6:36+ |+LL | fn no_generics(x: &(), y: &(), _z: impl Sized) -> impl Sized {+ | ^^^^^^^^^^+help: add a `use<...>` bound and name the elided lifetimes after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate+ |+LL - fn no_generics(x: &(), y: &(), _z: impl Sized) -> impl Sized {+LL + fn no_generics<'a, 'b, T: Sized>(x: &'a (), y: &'b (), _z: T) -> impl Sized + use<'a, 'b, T> {+ |++error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+ --> $DIR/name-anon-lifetime-suggestion-apit.rs:12:5+ |+LL | fn with_type_param<U>(x: &(), y: &(), _z: impl Sized, _u: U) -> impl Sized {+ | --- ---------- opaque type defined here+ | |+ | hidden type `(&(), &())` captures the anonymous lifetime defined here+LL | (x, y)+ | ^^^^^^+ |+note: argument-position `impl Trait` cannot be named in a `use<...>` bound and must be turned into type parameters+ --> $DIR/name-anon-lifetime-suggestion-apit.rs:11:43+ |+LL | fn with_type_param<U>(x: &(), y: &(), _z: impl Sized, _u: U) -> impl Sized {+ | ^^^^^^^^^^+help: add a `use<...>` bound and name the elided lifetimes after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate+ |+LL - fn with_type_param<U>(x: &(), y: &(), _z: impl Sized, _u: U) -> impl Sized {+LL + fn with_type_param<'a, 'b, U, T: Sized>(x: &'a (), y: &'b (), _z: T, _u: U) -> impl Sized + use<'a, 'b, U, T> {+ |++error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+ --> $DIR/name-anon-lifetime-suggestion-apit.rs:17:5+ |+LL | fn with_lifetime_param<'a>(x: &'a (), y: &(), z: &(), _w: impl Sized) -> impl Sized {+ | -- ---------- opaque type defined here+ | |+ | hidden type `(&'a (), &(), &())` captures the lifetime `'a` as defined here+LL | (x, y, z)+ | ^^^^^^^^^+ |+note: argument-position `impl Trait` cannot be named in a `use<...>` bound and must be turned into type parameters+ --> $DIR/name-anon-lifetime-suggestion-apit.rs:16:59+ |+LL | fn with_lifetime_param<'a>(x: &'a (), y: &(), z: &(), _w: impl Sized) -> impl Sized {+ | ^^^^^^^^^^+help: add a `use<...>` bound and name the elided lifetimes after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate+ |+LL - fn with_lifetime_param<'a>(x: &'a (), y: &(), z: &(), _w: impl Sized) -> impl Sized {+LL + fn with_lifetime_param<'b, 'c, 'a, T: Sized>(x: &'a (), y: &'b (), z: &'c (), _w: T) -> impl Sized + use<'a, 'b, 'c, T> {+ |++error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+ --> $DIR/name-anon-lifetime-suggestion-apit.rs:22:5+ |+LL | fn two_apits(x: &(), y: &(), _a: impl Sized, _b: impl Copy) -> impl Sized {+ | --- ---------- opaque type defined here+ | |+ | hidden type `(&(), &())` captures the anonymous lifetime defined here+LL | (x, y)+ | ^^^^^^+ |+note: argument-position `impl Trait` cannot be named in a `use<...>` bound and must be turned into type parameters+ --> $DIR/name-anon-lifetime-suggestion-apit.rs:21:34+ |+LL | fn two_apits(x: &(), y: &(), _a: impl Sized, _b: impl Copy) -> impl Sized {+ | ^^^^^^^^^^ ^^^^^^^^^+help: add a `use<...>` bound and name the elided lifetimes after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate+ |+LL - fn two_apits(x: &(), y: &(), _a: impl Sized, _b: impl Copy) -> impl Sized {+LL + fn two_apits<'a, 'b, T: Sized, U: Copy>(x: &'a (), y: &'b (), _a: T, _b: U) -> impl Sized + use<'a, 'b, T, U> {+ |++error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+ --> $DIR/name-anon-lifetime-suggestion-apit.rs:27:5+ |+LL | fn one_anon(x: &(), _z: impl Sized) -> impl Sized {+ | --- ---------- opaque type defined here+ | |+ | hidden type `&()` captures the anonymous lifetime defined here+LL | x+ | ^+ |+note: you could use a `use<...>` bound to explicitly capture `'_`, but argument-position `impl Trait`s are not nameable+ --> $DIR/name-anon-lifetime-suggestion-apit.rs:26:25+ |+LL | fn one_anon(x: &(), _z: impl Sized) -> 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 one_anon(x: &(), _z: impl Sized) -> impl Sized {+LL + fn one_anon<T: Sized>(x: &(), _z: T) -> impl Sized + use<'_, T> {+ |++error: aborting due to 5 previous errors++For more information about this error, try `rustc --explain E0700`.