rust-lang/rust · #163684
suggest named lifetimes instead of `'_` in `use<...>` for E0700
compiler/rustc_trait_selection/src/diagnostics.rs14 + / 0 −
@@ -1921,6 +1921,20 @@ impl Subdiagnostic for AddPreciseCapturingAndParams { } } +pub(crate) struct AddPreciseCapturingAndNameLifetimes {+ pub suggs: Vec<(Span, String)>,+}++impl Subdiagnostic for AddPreciseCapturingAndNameLifetimes {+ fn add_to_diag(self, diag: &mut Diag<'_>) {+ diag.multipart_suggestion(+ msg!("add `use<...>` bound and name the elided lifetimes to explicitly capture them"),+ self.suggs,+ Applicability::MachineApplicable,+ );+ }+}+ /// Given a set of captured `DefId` for an RPIT (opaque_def_id) and a given /// function (fn_def_id), try to suggest adding `+ use<...>` to capture just /// the specified parameters. If one of those parameters is an APIT, then trycompiler/rustc_trait_selection/src/error_reporting/infer/region.rs233 + / 69 −
@@ -1,13 +1,15 @@ use std::iter; -use rustc_data_structures::fx::FxIndexSet;+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ Applicability, Diag, E0309, E0310, E0311, E0803, Subdiagnostic, msg, struct_span_code_err, }; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, ParamName};+use rustc_middle::hir::nested_filter;+use rustc_middle::middle::resolve_bound_vars as rbv; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::RegionHighlightMode;@@ -934,72 +936,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { lifetime: Region<'tcx>, add_lt_suggs: &mut Vec<(Span, String)>, ) -> Option<String> {- struct LifetimeReplaceVisitor<'a> {- needle: hir::LifetimeKind,- new_lt: &'a str,- add_lt_suggs: &'a mut Vec<(Span, String)>,- }-- impl<'hir> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'_> {- fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {- if lt.kind == self.needle {- self.add_lt_suggs.push(lt.suggestion(self.new_lt));- }- }- }-- let (lifetime_def_id, lifetime_scope) =- match self.tcx.is_suitable_region(generic_param_scope, lifetime) {- Some(info) if !lifetime.is_named(self.tcx) => {- (info.region_def_id.expect_local(), info.scope)- }- _ => return Some(lifetime.get_name(self.tcx)?.to_string()),- };-- let new_lt = {- let generics = self.tcx.generics_of(lifetime_scope);- let mut used_names =- iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))- .flat_map(|g| &g.own_params)- .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))- .map(|p| p.name)- .collect::<Vec<_>>();- let hir_id = self.tcx.local_def_id_to_hir_id(lifetime_scope);- // consider late-bound lifetimes ...- used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map(- |p| match p {- ty::BoundVariableKind::Region(lt) => lt.get_name(self.tcx),- _ => None,- },- ));- (b'a'..=b'z')- .map(|c| format!("'{}", c as char))- .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate))- .unwrap_or_else(|| "'lt".to_string())- };-- let mut visitor = LifetimeReplaceVisitor {- needle: hir::LifetimeKind::Param(lifetime_def_id),- add_lt_suggs,- new_lt: &new_lt,- };- match self.tcx.expect_hir_owner_node(lifetime_scope) {- hir::OwnerNode::Item(i) => visitor.visit_item(i),- hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i),- hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i),- hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i),- hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"),- hir::OwnerNode::Synthetic => unreachable!(),- }-- let ast_generics = self.tcx.hir_get_generics(lifetime_scope).unwrap();- let sugg = ast_generics- .span_for_lifetime_suggestion()- .map(|span| (span, format!("{new_lt}, ")))- .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>")));- add_lt_suggs.push(sugg);-- Some(new_lt)+ let [name] =+ suggest_name_regions(self.tcx, generic_param_scope, &[lifetime], add_lt_suggs)?+ .try_into()+ .unwrap();+ Some(name) } fn report_sub_sup_conflict(@@ -1169,6 +1110,101 @@ pub(super) fn note_and_explain_region<'tcx>( emit_msg_span(err, prefix, description, span, suffix); } +fn suggest_name_regions<'tcx>(+ tcx: TyCtxt<'tcx>,+ generic_param_scope: LocalDefId,+ lifetimes: &[Region<'tcx>],+ add_lt_suggs: &mut Vec<(Span, String)>,+) -> Option<Vec<String>> {+ struct LifetimeReplaceVisitor<'a> {+ new_lts: &'a [(LocalDefId, String)],+ add_lt_suggs: &'a mut Vec<(Span, String)>,+ }++ impl<'hir> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'_> {+ fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {+ if let hir::LifetimeKind::Param(def_id) = lt.kind+ && let Some((_, new_lt)) = self.new_lts.iter().find(|(id, _)| *id == def_id)+ {+ self.add_lt_suggs.push(lt.suggestion(new_lt));+ }+ }+ }++ let mut lifetime_scope = None;+ let mut used_names = vec![];+ let mut new_lts: Vec<(LocalDefId, String)> = vec![];+ let names: Vec<_> = lifetimes+ .iter()+ .map(|&lifetime| {+ let (lifetime_def_id, scope) =+ match tcx.is_suitable_region(generic_param_scope, lifetime) {+ Some(info) if !lifetime.is_named(tcx) => {+ (info.region_def_id.expect_local(), info.scope)+ }+ _ => return Some(lifetime.get_name(tcx)?.to_string()),+ };+ if *lifetime_scope.get_or_insert_with(|| {+ used_names.extend(+ iter::successors(Some(tcx.generics_of(scope)), |g| {+ g.parent.map(|p| tcx.generics_of(p))+ })+ .flat_map(|g| &g.own_params)+ .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))+ .map(|p| p.name),+ );+ // consider late-bound lifetimes ...+ used_names.extend(+ tcx.late_bound_vars(tcx.local_def_id_to_hir_id(scope)).into_iter().filter_map(+ |p| match p {+ ty::BoundVariableKind::Region(lt) => lt.get_name(tcx),+ _ => None,+ },+ ),+ );+ scope+ }) != scope+ {+ return Some(lifetime.get_name(tcx)?.to_string());+ }+ if let Some((_, name)) = new_lts.iter().find(|(id, _)| *id == lifetime_def_id) {+ return Some(name.clone());+ }+ let new_lt = (b'a'..=b'z')+ .map(|c| format!("'{}", c as char))+ .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate))+ .unwrap_or_else(|| "'lt".to_string());+ used_names.push(Symbol::intern(&new_lt));+ new_lts.push((lifetime_def_id, new_lt.clone()));+ Some(new_lt)+ })+ .collect::<Option<_>>()?;++ let Some(lifetime_scope) = lifetime_scope else {+ return Some(names);+ };++ let mut visitor = LifetimeReplaceVisitor { new_lts: &new_lts, add_lt_suggs };+ match tcx.expect_hir_owner_node(lifetime_scope) {+ hir::OwnerNode::Item(i) => visitor.visit_item(i),+ hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i),+ hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i),+ hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i),+ hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"),+ hir::OwnerNode::Synthetic => unreachable!(),+ }++ let new_lt = new_lts.into_iter().map(|(_, name)| name).collect::<Vec<_>>().join(", ");+ let ast_generics = tcx.hir_get_generics(lifetime_scope).unwrap();+ let sugg = ast_generics+ .span_for_lifetime_suggestion()+ .map(|span| (span, format!("{new_lt}, ")))+ .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>")));+ add_lt_suggs.push(sugg);++ Some(names)+}+ fn explain_free_region<'tcx>( tcx: TyCtxt<'tcx>, err: &mut Diag<'_>,@@ -1319,7 +1355,14 @@ pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>( "", ); if let Some(_) = tcx.is_suitable_region(generic_param_scope, hidden_region) {- suggest_precise_capturing(tcx, opaque_ty_key.def_id, hidden_region, &mut err);+ suggest_precise_capturing(+ tcx,+ generic_param_scope,+ opaque_ty_key.def_id,+ hidden_ty.value,+ hidden_region,+ &mut err,+ ); } } ty::RePlaceholder(_) => {@@ -1368,7 +1411,9 @@ pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>( fn suggest_precise_capturing<'tcx>( tcx: TyCtxt<'tcx>,+ generic_param_scope: LocalDefId, opaque_def_id: LocalDefId,+ hidden_ty: Ty<'tcx>, captured_lifetime: ty::Region<'tcx>, diag: &mut Diag<'_>, ) {@@ -1455,11 +1500,30 @@ fn suggest_precise_capturing<'tcx>( } } - if !captured_lifetimes.insert(new_lifetime) {+ if captured_lifetimes.contains(&new_lifetime) { // Uh, strange. This lifetime appears to already be captured... return; } + // `'_` 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;+ }++ captured_lifetimes.insert(new_lifetime);+ if synthetics.is_empty() { let concatenated_bounds = captured_lifetimes .into_iter()@@ -1543,3 +1607,103 @@ fn suggest_precise_capturing<'tcx>( } } }++/// Build a suggestion that names every anonymous lifetime the hidden type captures and then+/// lists them in a `use<..>` bound.+///+/// This exists because a `use<..>` list cannot refer to more than one anonymous lifetime: `'_`+/// resolves like any other elided lifetime, so with several in the signature it either picks the+/// wrong one or fails to resolve at all. Naming them is the only way to write the bound the hidden+/// type actually needs.+///+/// Returns `None` whenever the plain `'_` suggestion is still correct, so that the common+/// single-lifetime case keeps its shorter form.+fn name_anon_lifetimes_suggestion<'tcx>(+ tcx: TyCtxt<'tcx>,+ generic_param_scope: LocalDefId,+ opaque_def_id: LocalDefId,+ fn_def_id: LocalDefId,+ hidden_ty: Ty<'tcx>,+ captured_lifetimes: &FxIndexSet<Symbol>,+ captured_non_lifetimes: &FxIndexSet<Symbol>,+) -> Option<Vec<(Span, String)>> {+ let fn_sig = tcx.hir_node_by_def_id(fn_def_id).fn_sig()?;++ let mut collector = AnonLifetimeCollector { tcx, anon_lifetimes: Default::default() };+ for input in fn_sig.decl.inputs {+ collector.visit_ty_unambig(input);+ }+ // With a single anonymous lifetime in the signature `'_` resolves to it, so leave that case+ // to the caller.+ if collector.anon_lifetimes.len() < 2 {+ return None;+ }++ let mut hidden_regions = FxIndexMap::default();+ tcx.for_each_free_region(&hidden_ty, |region| {+ if let Some(info) = tcx.is_suitable_region(generic_param_scope, region) {+ hidden_regions.entry(info.region_def_id).or_insert(region);+ }+ });++ // A lifetime the user already named stays as it is; only the anonymous ones need inventing+ // a name, and they are named in the order they appear in the signature.+ let named = hidden_regions+ .iter()+ .filter(|(def_id, _)| !collector.anon_lifetimes.contains(*def_id))+ .map(|(_, ®ion)| region);+ let anon: Vec<_> = collector+ .anon_lifetimes+ .iter()+ .filter_map(|def_id| hidden_regions.get(def_id).copied())+ .collect();+ if anon.is_empty() {+ return None;+ }+ let regions: Vec<_> = named.chain(anon).collect();++ let mut suggs = vec![];+ let mut bounds = captured_lifetimes.clone();+ bounds.extend(+ suggest_name_regions(tcx, generic_param_scope, ®ions, &mut suggs)?+ .iter()+ .map(|name| Symbol::intern(name)),+ );++ let concatenated_bounds = bounds+ .into_iter()+ .chain(captured_non_lifetimes.iter().copied())+ .map(|sym| sym.to_string())+ .collect::<Vec<_>>()+ .join(", ");+ suggs.push((+ tcx.def_span(opaque_def_id).shrink_to_hi(),+ format!(" + use<{concatenated_bounds}>"),+ ));++ Some(suggs)+}++/// Collects every anonymous lifetime written in a function signature.+struct AnonLifetimeCollector<'tcx> {+ tcx: TyCtxt<'tcx>,+ anon_lifetimes: FxIndexSet<DefId>,+}++impl<'tcx> Visitor<'tcx> for AnonLifetimeCollector<'tcx> {+ type NestedFilter = nested_filter::OnlyBodies;++ fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {+ self.tcx+ }++ fn visit_lifetime(&mut self, lifetime: &'tcx hir::Lifetime) {+ if lifetime.is_anonymous()+ && let Some(+ rbv::ResolvedArg::EarlyBound(def_id) | rbv::ResolvedArg::LateBound(_, _, def_id),+ ) = self.tcx.named_bound_var(lifetime.hir_id)+ {+ self.anon_lifetimes.insert(def_id.to_def_id());+ }+ }+}tests/ui/impl-trait/precise-capturing/name-anon-lifetime-suggestion-issue-132199.fixedadded48 + / 0 −
@@ -0,0 +1,48 @@+//@ edition: 2021+//@ run-rustfix++// regression test for https://github.com/rust-lang/rust/issues/132199.+// in edition 2024 impl Trait captures every in-scope lifetime by default,+// so none of these shapes error there. edition 2021 is used throughout.++#![allow(dead_code)]++struct T;+struct Foo<'a>(&'a u8);++impl T {+ // two_anon: two elided lifetimes, use<'_> captures only the first so a+ // second E0700 follows with no suggestion.+ fn two_anon<'a, 'b>(&'a self, t: &'b T) -> impl Sized + use<'a, 'b> {+ (self, t)+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+ }++ // one_anon: one elided lifetime, use<'_> is correct and must stay byte-identical.+ fn one_anon(&self) -> impl Sized + use<'_> {+ self+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+ }++ // named: an already-named lifetime, unaffected by this change.+ fn named<'a>(&'a self, t: &'a T) -> impl Sized + use<'a> {+ (self, t)+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+ }+}++// second_anon: two elided lifetimes where use<'_> does not resolve and+// produces E0106 on top of the original error.+fn second_anon<'a>(_a: &u8, b: &'a T) -> impl Sized + use<'a> {+ b+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++// path_anon: elision inside a path argument, so the name goes in as Foo<'a>+// and exercises the other arm of hir::LifetimeName matching.+fn path_anon<'a, 'b>(x: Foo<'a>, y: Foo<'b>) -> impl Sized + use<'a, 'b> {+ (x, y)+ //~^ 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-issue-132199.rsadded48 + / 0 −
@@ -0,0 +1,48 @@+//@ edition: 2021+//@ run-rustfix++// regression test for https://github.com/rust-lang/rust/issues/132199.+// in edition 2024 impl Trait captures every in-scope lifetime by default,+// so none of these shapes error there. edition 2021 is used throughout.++#![allow(dead_code)]++struct T;+struct Foo<'a>(&'a u8);++impl T {+ // two_anon: two elided lifetimes, use<'_> captures only the first so a+ // second E0700 follows with no suggestion.+ fn two_anon(&self, t: &T) -> impl Sized {+ (self, t)+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+ }++ // one_anon: one elided lifetime, use<'_> is correct and must stay byte-identical.+ fn one_anon(&self) -> impl Sized {+ self+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+ }++ // named: an already-named lifetime, unaffected by this change.+ fn named<'a>(&'a self, t: &'a T) -> impl Sized {+ (self, t)+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+ }+}++// second_anon: two elided lifetimes where use<'_> does not resolve and+// produces E0106 on top of the original error.+fn second_anon(_a: &u8, b: &T) -> impl Sized {+ b+ //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++// path_anon: elision inside a path argument, so the name goes in as Foo<'a>+// and exercises the other arm of hir::LifetimeName matching.+fn path_anon(x: Foo, y: Foo) -> impl Sized {+ (x, y)+ //~^ 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-issue-132199.stderradded78 + / 0 −
@@ -0,0 +1,78 @@+error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+ --> $DIR/name-anon-lifetime-suggestion-issue-132199.rs:17:9+ |+LL | fn two_anon(&self, t: &T) -> impl Sized {+ | ----- ---------- opaque type defined here+ | |+ | hidden type `(&T, &T)` captures the anonymous lifetime defined here+LL | (self, t)+ | ^^^^^^^^^+ |+help: add `use<...>` bound and name the elided lifetimes to explicitly capture them+ |+LL | fn two_anon<'a, 'b>(&'a self, t: &'b T) -> impl Sized + use<'a, 'b> {+ | ++++++++ ++ ++ +++++++++++++++error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+ --> $DIR/name-anon-lifetime-suggestion-issue-132199.rs:23:9+ |+LL | fn one_anon(&self) -> impl Sized {+ | ----- ---------- opaque type defined here+ | |+ | hidden type `&T` captures the anonymous lifetime defined here+LL | self+ | ^^^^+ |+help: add a `use<...>` bound to explicitly capture `'_`+ |+LL | fn one_anon(&self) -> impl Sized + use<'_> {+ | +++++++++++error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+ --> $DIR/name-anon-lifetime-suggestion-issue-132199.rs:29:9+ |+LL | fn named<'a>(&'a self, t: &'a T) -> impl Sized {+ | -- ---------- opaque type defined here+ | |+ | hidden type `(&'a T, &'a T)` captures the lifetime `'a` as defined here+LL | (self, t)+ | ^^^^^^^^^+ |+help: add a `use<...>` bound to explicitly capture `'a`+ |+LL | fn named<'a>(&'a self, t: &'a T) -> impl Sized + use<'a> {+ | +++++++++++error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+ --> $DIR/name-anon-lifetime-suggestion-issue-132199.rs:37:5+ |+LL | fn second_anon(_a: &u8, b: &T) -> impl Sized {+ | -- ---------- opaque type defined here+ | |+ | hidden type `&T` captures the anonymous lifetime defined here+LL | b+ | ^+ |+help: add `use<...>` bound and name the elided lifetimes to explicitly capture them+ |+LL | fn second_anon<'a>(_a: &u8, b: &'a T) -> impl Sized + use<'a> {+ | ++++ ++ +++++++++++error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+ --> $DIR/name-anon-lifetime-suggestion-issue-132199.rs:44:5+ |+LL | fn path_anon(x: Foo, y: Foo) -> impl Sized {+ | --- ---------- opaque type defined here+ | |+ | hidden type `(Foo<'_>, Foo<'_>)` captures the anonymous lifetime defined here+LL | (x, y)+ | ^^^^^^+ |+help: add `use<...>` bound and name the elided lifetimes to explicitly capture them+ |+LL | fn path_anon<'a, 'b>(x: Foo<'a>, y: Foo<'b>) -> impl Sized + use<'a, 'b> {+ | ++++++++ ++++ ++++ +++++++++++++++error: aborting due to 5 previous errors++For more information about this error, try `rustc --explain E0700`.tests/ui/lifetimes/issue-105227.fixedadded26 + / 0 −
@@ -0,0 +1,26 @@+// Regression test for issue #105227.++//@ run-rustfix++#![allow(unused)]++fn chars0<'a, 'b>(v :(&'a str, &'b str)) -> impl Iterator<Item = char> + use<'a, 'b> {+ //~^ HELP add `use<...>` bound and name the elided lifetimes to explicitly capture them+ v.0.chars().chain(v.1.chars())+ //~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bounds+}++fn chars1<'a, 'b>(v0 : &'a str, v1 : &'b str) -> impl Iterator<Item = char> + use<'a, 'b> {+ //~^ HELP add `use<...>` bound and name the elided lifetimes to explicitly capture them+ v0.chars().chain(v1.chars())+ //~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bound+}++fn chars2<'a, 'c, 'b>(v0 : &'a str, v1 : &'c str, v2 : &'b str) -> (impl Iterator<Item = char> + use<'a, 'c>, &'b str) {+ //~^ HELP add `use<...>` bound and name the elided lifetimes to explicitly capture them+ (v0.chars().chain(v1.chars()), v2)+ //~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bound+}++fn main() {+}tests/ui/lifetimes/issue-105227.rs4 + / 4 −
@@ -1,23 +1,23 @@ // Regression test for issue #105227. -// FIXME(precise_capturing): Add rustfix here after dealing w/ elided lifetimes+//@ run-rustfix #![allow(unused)] fn chars0(v :(& str, &str)) -> impl Iterator<Item = char> {- //~^ HELP add a `use<...>` bound+ //~^ HELP add `use<...>` bound and name the elided lifetimes to explicitly capture them v.0.chars().chain(v.1.chars()) //~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bounds } fn chars1(v0 : & str, v1 : &str) -> impl Iterator<Item = char> {- //~^ HELP add a `use<...>` bound+ //~^ HELP add `use<...>` bound and name the elided lifetimes to explicitly capture them v0.chars().chain(v1.chars()) //~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bound } fn chars2<'b>(v0 : &str, v1 : &'_ str, v2 : &'b str) -> (impl Iterator<Item = char>, &'b str) {- //~^ HELP add a `use<...>` bound+ //~^ HELP add `use<...>` bound and name the elided lifetimes to explicitly capture them (v0.chars().chain(v1.chars()), v2) //~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bound }tests/ui/lifetimes/issue-105227.stderr10 + / 9 −
@@ -9,10 +9,10 @@ LL | LL | v.0.chars().chain(v.1.chars()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |-help: add a `use<...>` bound to explicitly capture `'_`+help: add `use<...>` bound and name the elided lifetimes to explicitly capture them |-LL | fn chars0(v :(& str, &str)) -> impl Iterator<Item = char> + use<'_> {- | ++++++++++LL | fn chars0<'a, 'b>(v :(&'a str, &'b str)) -> impl Iterator<Item = char> + use<'a, 'b> {+ | ++++++++ ++ ++ +++++++++++++ error[E0700]: hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bounds --> $DIR/issue-105227.rs:15:5@@ -25,10 +25,10 @@ LL | LL | v0.chars().chain(v1.chars()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |-help: add a `use<...>` bound to explicitly capture `'_`+help: add `use<...>` bound and name the elided lifetimes to explicitly capture them |-LL | fn chars1(v0 : & str, v1 : &str) -> impl Iterator<Item = char> + use<'_> {- | ++++++++++LL | fn chars1<'a, 'b>(v0 : &'a str, v1 : &'b str) -> impl Iterator<Item = char> + use<'a, 'b> {+ | ++++++++ ++ ++ +++++++++++++ error[E0700]: hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bounds --> $DIR/issue-105227.rs:21:5@@ -41,10 +41,11 @@ LL | LL | (v0.chars().chain(v1.chars()), v2) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |-help: add a `use<...>` bound to explicitly capture `'_`+help: add `use<...>` bound and name the elided lifetimes to explicitly capture them+ |+LL - fn chars2<'b>(v0 : &str, v1 : &'_ str, v2 : &'b str) -> (impl Iterator<Item = char>, &'b str) {+LL + fn chars2<'a, 'c, 'b>(v0 : &'a str, v1 : &'c str, v2 : &'b str) -> (impl Iterator<Item = char> + use<'a, 'c>, &'b str) { |-LL | fn chars2<'b>(v0 : &str, v1 : &'_ str, v2 : &'b str) -> (impl Iterator<Item = char> + use<'_>, &'b str) {- | +++++++++ error: aborting due to 3 previous errors