Skip to content

Commit 8bafe88

Browse files
committed
Select an appropriate unused lifetime name in suggestion
1 parent a643ee8 commit 8bafe88

File tree

3 files changed

+71
-13
lines changed

3 files changed

+71
-13
lines changed

src/librustc_typeck/collect.rs

+44-6
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use rustc::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt};
3333
use rustc::ty::{ReprOptions, ToPredicate, WithConstness};
3434
use rustc_attr::{list_contains_name, mark_used, InlineAttr, OptimizeAttr};
3535
use rustc_data_structures::captures::Captures;
36-
use rustc_data_structures::fx::FxHashMap;
36+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
3737
use rustc_errors::{struct_span_err, Applicability};
3838
use rustc_hir as hir;
3939
use rustc_hir::def::{CtorKind, DefKind, Res};
@@ -369,10 +369,12 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
369369
hir::ItemKind::Enum(_, generics)
370370
| hir::ItemKind::Struct(_, generics)
371371
| hir::ItemKind::Union(_, generics) => {
372-
// FIXME: look for an appropriate lt name if `'a` is already used
372+
let lt_name = get_new_lifetime_name(self.tcx, poly_trait_ref, generics);
373373
let (lt_sp, sugg) = match &generics.params[..] {
374-
[] => (generics.span, "<'a>".to_string()),
375-
[bound, ..] => (bound.span.shrink_to_lo(), "'a, ".to_string()),
374+
[] => (generics.span, format!("<{}>", lt_name)),
375+
[bound, ..] => {
376+
(bound.span.shrink_to_lo(), format!("{}, ", lt_name))
377+
}
376378
};
377379
let suggestions = vec![
378380
(lt_sp, sugg),
@@ -387,7 +389,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
387389
ty::EarlyBoundRegion {
388390
def_id: item_def_id,
389391
index: 0,
390-
name: Symbol::intern("'a"),
392+
name: Symbol::intern(&lt_name),
391393
},
392394
))
393395
})
@@ -445,6 +447,43 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
445447
}
446448
}
447449

450+
/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present.
451+
fn get_new_lifetime_name<'tcx>(
452+
tcx: TyCtxt<'tcx>,
453+
poly_trait_ref: ty::PolyTraitRef<'tcx>,
454+
generics: &hir::Generics<'tcx>,
455+
) -> String {
456+
let existing_lifetimes = tcx
457+
.collect_referenced_late_bound_regions(&poly_trait_ref)
458+
.into_iter()
459+
.filter_map(|lt| {
460+
if let ty::BoundRegion::BrNamed(_, name) = lt {
461+
Some(name.as_str().to_string())
462+
} else {
463+
None
464+
}
465+
})
466+
.chain(generics.params.iter().filter_map(|param| {
467+
if let hir::GenericParamKind::Lifetime { .. } = &param.kind {
468+
Some(param.name.ident().as_str().to_string())
469+
} else {
470+
None
471+
}
472+
}))
473+
.collect::<FxHashSet<String>>();
474+
475+
let a_to_z_repeat_n = |n| {
476+
(b'a'..=b'z').map(move |c| {
477+
let mut s = format!("'");
478+
s.extend(std::iter::repeat(char::from(c)).take(n));
479+
s
480+
})
481+
};
482+
483+
// If all single char lifetime names are present, we wrap around and double the chars.
484+
(1..).flat_map(a_to_z_repeat_n).find(|lt| !existing_lifetimes.contains(lt.as_str())).unwrap()
485+
}
486+
448487
/// Returns the predicates defined on `item_def_id` of the form
449488
/// `X: Foo` where `X` is the type parameter `def_id`.
450489
fn type_param_predicates(
@@ -1588,7 +1627,6 @@ fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
15881627
/// Returns a list of user-specified type predicates for the definition with ID `def_id`.
15891628
/// N.B., this does not include any implied/inferred constraints.
15901629
fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
1591-
use rustc_data_structures::fx::FxHashSet;
15921630
use rustc_hir::*;
15931631

15941632
debug!("explicit_predicates_of(def_id={:?})", def_id);

src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ struct SomeStruct<I: for<'x> Foo<&'x isize>> {
1212
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
1313
}
1414

15-
enum SomeEnum<I: for<'x> Foo<&'x isize>> {
15+
enum SomeEnum<'b, I: for<'a> Foo<&'a isize>> {
1616
TupleVariant(I::A),
1717
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
1818
StructVariant { field: I::A },
1919
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
20+
OkVariant(&'b usize),
2021
}
2122

2223
// FIXME(eddyb) This one doesn't even compile because of the unsupported syntax.
@@ -26,7 +27,13 @@ enum SomeEnum<I: for<'x> Foo<&'x isize>> {
2627
// }
2728

2829
struct YetAnotherStruct<'a, I: for<'x> Foo<&'x isize>> {
29-
field: <I as Foo<&'a isize>>::A
30+
field: <I as Foo<&'a isize>>::A,
31+
}
32+
33+
struct Why<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'n, 'o, 'p, 'q, 'r, 's, 't, 'u, 'v, 'w, 'x,
34+
'y, 'z, 'aa, I: for<'l, 'm> Foo<&'l &'m isize>> {
35+
field: I::A,
36+
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
3037
}
3138

3239
pub fn main() {}

src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr

+18-5
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ LL | TupleVariant(I::A),
1818
|
1919
help: use a fully qualified path with explicit lifetimes
2020
|
21-
LL | enum SomeEnum<'a, I: for<'x> Foo<&'x isize>> {
22-
LL | TupleVariant(<I as Foo<&'a isize>>::A),
21+
LL | enum SomeEnum<'c, 'b, I: for<'a> Foo<&'a isize>> {
22+
LL | TupleVariant(<I as Foo<&'c isize>>::A),
2323
|
2424

2525
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 },
3030
|
3131
help: use a fully qualified path with explicit lifetimes
3232
|
33-
LL | enum SomeEnum<'a, I: for<'x> Foo<&'x isize>> {
33+
LL | enum SomeEnum<'c, 'b, I: for<'a> Foo<&'a isize>> {
3434
LL | TupleVariant(I::A),
3535
LL |
36-
LL | StructVariant { field: <I as Foo<&'a isize>>::A },
36+
LL | StructVariant { field: <I as Foo<&'c isize>>::A },
3737
|
3838

39-
error: aborting due to 3 previous errors
39+
error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
40+
--> $DIR/associated-types-project-from-hrtb-in-struct.rs:35:12
41+
|
42+
LL | field: I::A,
43+
| ^^^^
44+
|
45+
help: use a fully qualified path with explicit lifetimes
46+
|
47+
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,
48+
LL | 'y, 'z, 'aa, I: for<'l, 'm> Foo<&'l &'m isize>> {
49+
LL | field: <I as Foo<&'bb &'bb isize>>::A,
50+
|
51+
52+
error: aborting due to 4 previous errors
4053

0 commit comments

Comments
 (0)