Skip to content

Commit 8d00adf

Browse files
authored
Rollup merge of rust-lang#69048 - estebank:hrlt-assoc, r=nagisa
Suggestion when encountering assoc types from hrtb When encountering E0212, detect whether this is a representable case or not, i.e. if it's happening on an `fn` or on an ADT. If the former, provide a structured suggestion, otherwise note that this can't be represented in Rust. Fix rust-lang#69000.
2 parents e9f391e + bde9677 commit 8d00adf

9 files changed

+226
-20
lines changed

src/librustc_typeck/collect.rs

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,17 @@ impl ItemCtxt<'tcx> {
278278
pub fn to_ty(&self, ast_ty: &'tcx hir::Ty<'tcx>) -> Ty<'tcx> {
279279
AstConv::ast_ty_to_ty(self, ast_ty)
280280
}
281+
282+
pub fn hir_id(&self) -> hir::HirId {
283+
self.tcx
284+
.hir()
285+
.as_local_hir_id(self.item_def_id)
286+
.expect("Non-local call to local provider is_const_fn")
287+
}
288+
289+
pub fn node(&self) -> hir::Node<'tcx> {
290+
self.tcx.hir().get(self.hir_id())
291+
}
281292
}
282293

283294
impl AstConv<'tcx> for ItemCtxt<'tcx> {
@@ -290,15 +301,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
290301
}
291302

292303
fn default_constness_for_trait_bounds(&self) -> ast::Constness {
293-
// FIXME: refactor this into a method
294-
let hir_id = self
295-
.tcx
296-
.hir()
297-
.as_local_hir_id(self.item_def_id)
298-
.expect("Non-local call to local provider is_const_fn");
299-
300-
let node = self.tcx.hir().get(hir_id);
301-
if let Some(fn_like) = FnLikeNode::from_node(node) {
304+
if let Some(fn_like) = FnLikeNode::from_node(self.node()) {
302305
fn_like.constness()
303306
} else {
304307
ast::Constness::NotConst
@@ -352,14 +355,80 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
352355
self.tcx().mk_projection(item_def_id, item_substs)
353356
} else {
354357
// There are no late-bound regions; we can just ignore the binder.
355-
struct_span_err!(
358+
let mut err = struct_span_err!(
356359
self.tcx().sess,
357360
span,
358361
E0212,
359362
"cannot extract an associated type from a higher-ranked trait bound \
360363
in this context"
361-
)
362-
.emit();
364+
);
365+
366+
match self.node() {
367+
hir::Node::Field(_) | hir::Node::Ctor(_) | hir::Node::Variant(_) => {
368+
let item =
369+
self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(self.hir_id()));
370+
match &item.kind {
371+
hir::ItemKind::Enum(_, generics)
372+
| hir::ItemKind::Struct(_, generics)
373+
| hir::ItemKind::Union(_, generics) => {
374+
// FIXME: look for an appropriate lt name if `'a` is already used
375+
let (lt_sp, sugg) = match &generics.params[..] {
376+
[] => (generics.span, "<'a>".to_string()),
377+
[bound, ..] => (bound.span.shrink_to_lo(), "'a, ".to_string()),
378+
};
379+
let suggestions = vec![
380+
(lt_sp, sugg),
381+
(
382+
span,
383+
format!(
384+
"{}::{}",
385+
// Replace the existing lifetimes with a new named lifetime.
386+
self.tcx
387+
.replace_late_bound_regions(&poly_trait_ref, |_| {
388+
self.tcx.mk_region(ty::ReEarlyBound(
389+
ty::EarlyBoundRegion {
390+
def_id: item_def_id,
391+
index: 0,
392+
name: Symbol::intern("'a"),
393+
},
394+
))
395+
})
396+
.0,
397+
item_segment.ident
398+
),
399+
),
400+
];
401+
err.multipart_suggestion(
402+
"use a fully qualified path with explicit lifetimes",
403+
suggestions,
404+
Applicability::MaybeIncorrect,
405+
);
406+
}
407+
_ => {}
408+
}
409+
}
410+
hir::Node::Item(hir::Item { kind: hir::ItemKind::Struct(..), .. })
411+
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(..), .. })
412+
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Union(..), .. }) => {}
413+
hir::Node::Item(_)
414+
| hir::Node::ForeignItem(_)
415+
| hir::Node::TraitItem(_)
416+
| hir::Node::ImplItem(_) => {
417+
err.span_suggestion(
418+
span,
419+
"use a fully qualified path with inferred lifetimes",
420+
format!(
421+
"{}::{}",
422+
// Erase named lt, we want `<A as B<'_>::C`, not `<A as B<'a>::C`.
423+
self.tcx.anonymize_late_bound_regions(&poly_trait_ref).skip_binder(),
424+
item_segment.ident
425+
),
426+
Applicability::MaybeIncorrect,
427+
);
428+
}
429+
_ => {}
430+
}
431+
err.emit();
363432
self.tcx().types.err
364433
}
365434
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#![allow(dead_code, unused_variables)]
2+
// run-rustfix
3+
// Check projection of an associated type out of a higher-ranked trait-bound
4+
// in the context of a function signature.
5+
6+
pub trait Foo<T> {
7+
type A;
8+
9+
fn get(&self, t: T) -> Self::A;
10+
}
11+
12+
fn foo2<I : for<'x> Foo<&'x isize>>(
13+
x: <I as Foo<&isize>>::A)
14+
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
15+
{
16+
// This case is illegal because we have to instantiate `'x`, and
17+
// we don't know what region to instantiate it with.
18+
//
19+
// This could perhaps be made equivalent to the examples below,
20+
// specifically for fn signatures.
21+
}
22+
23+
fn foo3<I : for<'x> Foo<&'x isize>>(
24+
x: <I as Foo<&isize>>::A)
25+
{
26+
// OK, in this case we spelled out the precise regions involved, though we left one of
27+
// them anonymous.
28+
}
29+
30+
fn foo4<'a, I : for<'x> Foo<&'x isize>>(
31+
x: <I as Foo<&'a isize>>::A)
32+
{
33+
// OK, in this case we spelled out the precise regions involved.
34+
}
35+
36+
37+
pub fn main() {}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(dead_code, unused_variables)]
2+
// run-rustfix
13
// Check projection of an associated type out of a higher-ranked trait-bound
24
// in the context of a function signature.
35

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
2-
--> $DIR/associated-types-project-from-hrtb-in-fn.rs:11:8
2+
--> $DIR/associated-types-project-from-hrtb-in-fn.rs:13:8
33
|
44
LL | x: I::A)
5-
| ^^^^
5+
| ^^^^ help: use a fully qualified path with inferred lifetimes: `<I as Foo<&isize>>::A`
66

77
error: aborting due to previous error
88

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,25 @@ pub trait Foo<T> {
77
fn get(&self, t: T) -> Self::A;
88
}
99

10-
struct SomeStruct<I : for<'x> Foo<&'x isize>> {
10+
struct SomeStruct<I: for<'x> Foo<&'x isize>> {
1111
field: I::A
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>> {
16+
TupleVariant(I::A),
17+
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
18+
StructVariant { field: I::A },
19+
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
20+
}
21+
1522
// FIXME(eddyb) This one doesn't even compile because of the unsupported syntax.
1623

1724
// struct AnotherStruct<I : for<'x> Foo<&'x isize>> {
1825
// field: <I as for<'y> Foo<&'y isize>>::A
1926
// }
2027

21-
struct YetAnotherStruct<'a, I : for<'x> Foo<&'x isize>> {
28+
struct YetAnotherStruct<'a, I: for<'x> Foo<&'x isize>> {
2229
field: <I as Foo<&'a isize>>::A
2330
}
2431

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,38 @@ error[E0212]: cannot extract an associated type from a higher-ranked trait bound
33
|
44
LL | field: I::A
55
| ^^^^
6+
|
7+
help: use a fully qualified path with explicit lifetimes
8+
|
9+
LL | struct SomeStruct<'a, I: for<'x> Foo<&'x isize>> {
10+
LL | field: <I as Foo<&'a isize>>::A
11+
|
12+
13+
error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
14+
--> $DIR/associated-types-project-from-hrtb-in-struct.rs:16:18
15+
|
16+
LL | TupleVariant(I::A),
17+
| ^^^^
18+
|
19+
help: use a fully qualified path with explicit lifetimes
20+
|
21+
LL | enum SomeEnum<'a, I: for<'x> Foo<&'x isize>> {
22+
LL | TupleVariant(<I as Foo<&'a isize>>::A),
23+
|
24+
25+
error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
26+
--> $DIR/associated-types-project-from-hrtb-in-struct.rs:18:28
27+
|
28+
LL | StructVariant { field: I::A },
29+
| ^^^^
30+
|
31+
help: use a fully qualified path with explicit lifetimes
32+
|
33+
LL | enum SomeEnum<'a, I: for<'x> Foo<&'x isize>> {
34+
LL | TupleVariant(I::A),
35+
LL |
36+
LL | StructVariant { field: <I as Foo<&'a isize>>::A },
37+
|
638

7-
error: aborting due to previous error
39+
error: aborting due to 3 previous errors
840

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#![allow(dead_code)]
2+
// run-rustfix
3+
// Check projection of an associated type out of a higher-ranked trait-bound
4+
// in the context of a method definition in a trait.
5+
6+
pub trait Foo<T> {
7+
type A;
8+
9+
fn get(&self, t: T) -> Self::A;
10+
}
11+
12+
trait SomeTrait<I : for<'x> Foo<&'x isize>> {
13+
fn some_method(&self, arg: <I as Foo<&isize>>::A);
14+
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
15+
}
16+
17+
trait AnotherTrait<I : for<'x> Foo<&'x isize>> {
18+
fn some_method(&self, arg: <I as Foo<&isize>>::A);
19+
}
20+
21+
trait YetAnotherTrait<I : for<'x> Foo<&'x isize>> {
22+
fn some_method<'a>(&self, arg: <I as Foo<&'a isize>>::A);
23+
}
24+
25+
trait Banana<'a> {
26+
type Assoc: Default;
27+
}
28+
29+
struct Peach<X>(std::marker::PhantomData<X>);
30+
31+
impl<X: for<'a> Banana<'a>> Peach<X> {
32+
fn mango(&self) -> <X as Banana<'_>>::Assoc {
33+
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
34+
Default::default()
35+
}
36+
}
37+
38+
pub fn main() {}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(dead_code)]
2+
// run-rustfix
13
// Check projection of an associated type out of a higher-ranked trait-bound
24
// in the context of a method definition in a trait.
35

@@ -20,4 +22,17 @@ trait YetAnotherTrait<I : for<'x> Foo<&'x isize>> {
2022
fn some_method<'a>(&self, arg: <I as Foo<&'a isize>>::A);
2123
}
2224

25+
trait Banana<'a> {
26+
type Assoc: Default;
27+
}
28+
29+
struct Peach<X>(std::marker::PhantomData<X>);
30+
31+
impl<X: for<'a> Banana<'a>> Peach<X> {
32+
fn mango(&self) -> X::Assoc {
33+
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
34+
Default::default()
35+
}
36+
}
37+
2338
pub fn main() {}
Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
2-
--> $DIR/associated-types-project-from-hrtb-in-trait-method.rs:11:32
2+
--> $DIR/associated-types-project-from-hrtb-in-trait-method.rs:13:32
33
|
44
LL | fn some_method(&self, arg: I::A);
5-
| ^^^^
5+
| ^^^^ help: use a fully qualified path with inferred lifetimes: `<I as Foo<&isize>>::A`
66

7-
error: aborting due to previous error
7+
error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
8+
--> $DIR/associated-types-project-from-hrtb-in-trait-method.rs:32:24
9+
|
10+
LL | fn mango(&self) -> X::Assoc {
11+
| ^^^^^^^^ help: use a fully qualified path with inferred lifetimes: `<X as Banana<'_>>::Assoc`
12+
13+
error: aborting due to 2 previous errors
814

0 commit comments

Comments
 (0)