diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 80d914d8d0ae5..ca3bcac1206be 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -33,7 +33,7 @@ use rustc::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt}; use rustc::ty::{ReprOptions, ToPredicate, WithConstness}; use rustc_attr::{list_contains_name, mark_used, InlineAttr, OptimizeAttr}; use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -369,10 +369,12 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { hir::ItemKind::Enum(_, generics) | hir::ItemKind::Struct(_, generics) | hir::ItemKind::Union(_, generics) => { - // FIXME: look for an appropriate lt name if `'a` is already used + let lt_name = get_new_lifetime_name(self.tcx, poly_trait_ref, generics); let (lt_sp, sugg) = match &generics.params[..] { - [] => (generics.span, "<'a>".to_string()), - [bound, ..] => (bound.span.shrink_to_lo(), "'a, ".to_string()), + [] => (generics.span, format!("<{}>", lt_name)), + [bound, ..] => { + (bound.span.shrink_to_lo(), format!("{}, ", lt_name)) + } }; let suggestions = vec![ (lt_sp, sugg), @@ -387,7 +389,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { ty::EarlyBoundRegion { def_id: item_def_id, index: 0, - name: Symbol::intern("'a"), + name: Symbol::intern(<_name), }, )) }) @@ -445,6 +447,43 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { } } +/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present. +fn get_new_lifetime_name<'tcx>( + tcx: TyCtxt<'tcx>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + generics: &hir::Generics<'tcx>, +) -> String { + let existing_lifetimes = tcx + .collect_referenced_late_bound_regions(&poly_trait_ref) + .into_iter() + .filter_map(|lt| { + if let ty::BoundRegion::BrNamed(_, name) = lt { + Some(name.as_str().to_string()) + } else { + None + } + }) + .chain(generics.params.iter().filter_map(|param| { + if let hir::GenericParamKind::Lifetime { .. } = ¶m.kind { + Some(param.name.ident().as_str().to_string()) + } else { + None + } + })) + .collect::>(); + + let a_to_z_repeat_n = |n| { + (b'a'..=b'z').map(move |c| { + let mut s = format!("'"); + s.extend(std::iter::repeat(char::from(c)).take(n)); + s + }) + }; + + // If all single char lifetime names are present, we wrap around and double the chars. + (1..).flat_map(a_to_z_repeat_n).find(|lt| !existing_lifetimes.contains(lt.as_str())).unwrap() +} + /// Returns the predicates defined on `item_def_id` of the form /// `X: Foo` where `X` is the type parameter `def_id`. fn type_param_predicates( @@ -1588,7 +1627,6 @@ fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { /// Returns a list of user-specified type predicates for the definition with ID `def_id`. /// N.B., this does not include any implied/inferred constraints. fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { - use rustc_data_structures::fx::FxHashSet; use rustc_hir::*; debug!("explicit_predicates_of(def_id={:?})", def_id); diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs index 8a5777d4d7cb5..58f186d7775ea 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs @@ -12,11 +12,12 @@ struct SomeStruct Foo<&'x isize>> { //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context } -enum SomeEnum Foo<&'x isize>> { +enum SomeEnum<'b, I: for<'a> Foo<&'a isize>> { TupleVariant(I::A), //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context StructVariant { field: I::A }, //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + OkVariant(&'b usize), } // FIXME(eddyb) This one doesn't even compile because of the unsupported syntax. @@ -26,7 +27,13 @@ enum SomeEnum Foo<&'x isize>> { // } struct YetAnotherStruct<'a, I: for<'x> Foo<&'x isize>> { - field: >::A + field: >::A, +} + +struct Why<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'n, 'o, 'p, 'q, 'r, 's, 't, 'u, 'v, 'w, 'x, + 'y, 'z, 'aa, I: for<'l, 'm> Foo<&'l &'m isize>> { + field: I::A, + //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context } pub fn main() {} diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr index c71bc70ea6c4e..e3fd2860ebcf3 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr @@ -18,8 +18,8 @@ LL | TupleVariant(I::A), | help: use a fully qualified path with explicit lifetimes | -LL | enum SomeEnum<'a, I: for<'x> Foo<&'x isize>> { -LL | TupleVariant(>::A), +LL | enum SomeEnum<'c, 'b, I: for<'a> Foo<&'a isize>> { +LL | TupleVariant(>::A), | error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context @@ -30,11 +30,24 @@ LL | StructVariant { field: I::A }, | help: use a fully qualified path with explicit lifetimes | -LL | enum SomeEnum<'a, I: for<'x> Foo<&'x isize>> { +LL | enum SomeEnum<'c, 'b, I: for<'a> Foo<&'a isize>> { LL | TupleVariant(I::A), LL | -LL | StructVariant { field: >::A }, +LL | StructVariant { field: >::A }, | -error: aborting due to 3 previous errors +error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context + --> $DIR/associated-types-project-from-hrtb-in-struct.rs:35:12 + | +LL | field: I::A, + | ^^^^ + | +help: use a fully qualified path with explicit lifetimes + | +LL | struct Why<'bb, 'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'n, 'o, 'p, 'q, 'r, 's, 't, 'u, 'v, 'w, 'x, +LL | 'y, 'z, 'aa, I: for<'l, 'm> Foo<&'l &'m isize>> { +LL | field: >::A, + | + +error: aborting due to 4 previous errors