Skip to content

Commit 24be307

Browse files
committed
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.
1 parent a19edd6 commit 24be307

7 files changed

+146
-17
lines changed

src/librustc_typeck/collect.rs

+43-12
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,42 @@ 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(_)
368+
| hir::Node::Variant(_)
369+
| hir::Node::Ctor(_)
370+
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Struct(..), .. })
371+
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(..), .. })
372+
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Union(..), .. }) => {
373+
// The suggestion is only valid if this is not an ADT.
374+
}
375+
hir::Node::Item(_)
376+
| hir::Node::ForeignItem(_)
377+
| hir::Node::TraitItem(_)
378+
| hir::Node::ImplItem(_) => {
379+
err.span_suggestion(
380+
span,
381+
"use a fully qualified path with inferred lifetimes",
382+
format!(
383+
"{}::{}",
384+
// Erase named lt, we want `<A as B<'_>::C`, not `<A as B<'a>::C`.
385+
self.tcx.anonymize_late_bound_regions(&poly_trait_ref).skip_binder(),
386+
item_segment.ident
387+
),
388+
Applicability::MaybeIncorrect,
389+
);
390+
}
391+
_ => {}
392+
}
393+
err.emit();
363394
self.tcx().types.err
364395
}
365396
}
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

+2
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

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

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

+15
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() {}
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)