Skip to content

Commit

Permalink
Suggestion when encountering assoc types from hrtb
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
estebank committed Feb 11, 2020
1 parent a19edd6 commit 24be307
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 17 deletions.
55 changes: 43 additions & 12 deletions src/librustc_typeck/collect.rs
Expand Up @@ -278,6 +278,17 @@ impl ItemCtxt<'tcx> {
pub fn to_ty(&self, ast_ty: &'tcx hir::Ty<'tcx>) -> Ty<'tcx> {
AstConv::ast_ty_to_ty(self, ast_ty)
}

pub fn hir_id(&self) -> hir::HirId {
self.tcx
.hir()
.as_local_hir_id(self.item_def_id)
.expect("Non-local call to local provider is_const_fn")
}

pub fn node(&self) -> hir::Node<'tcx> {
self.tcx.hir().get(self.hir_id())
}
}

impl AstConv<'tcx> for ItemCtxt<'tcx> {
Expand All @@ -290,15 +301,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
}

fn default_constness_for_trait_bounds(&self) -> ast::Constness {
// FIXME: refactor this into a method
let hir_id = self
.tcx
.hir()
.as_local_hir_id(self.item_def_id)
.expect("Non-local call to local provider is_const_fn");

let node = self.tcx.hir().get(hir_id);
if let Some(fn_like) = FnLikeNode::from_node(node) {
if let Some(fn_like) = FnLikeNode::from_node(self.node()) {
fn_like.constness()
} else {
ast::Constness::NotConst
Expand Down Expand Up @@ -352,14 +355,42 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
self.tcx().mk_projection(item_def_id, item_substs)
} else {
// There are no late-bound regions; we can just ignore the binder.
struct_span_err!(
let mut err = struct_span_err!(
self.tcx().sess,
span,
E0212,
"cannot extract an associated type from a higher-ranked trait bound \
in this context"
)
.emit();
);

match self.node() {
hir::Node::Field(_)
| hir::Node::Variant(_)
| hir::Node::Ctor(_)
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Struct(..), .. })
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(..), .. })
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Union(..), .. }) => {
// The suggestion is only valid if this is not an ADT.
}
hir::Node::Item(_)
| hir::Node::ForeignItem(_)
| hir::Node::TraitItem(_)
| hir::Node::ImplItem(_) => {
err.span_suggestion(
span,
"use a fully qualified path with inferred lifetimes",
format!(
"{}::{}",
// Erase named lt, we want `<A as B<'_>::C`, not `<A as B<'a>::C`.
self.tcx.anonymize_late_bound_regions(&poly_trait_ref).skip_binder(),
item_segment.ident
),
Applicability::MaybeIncorrect,
);
}
_ => {}
}
err.emit();
self.tcx().types.err
}
}
Expand Down
@@ -0,0 +1,37 @@
#![allow(dead_code, unused_variables)]
// run-rustfix
// Check projection of an associated type out of a higher-ranked trait-bound
// in the context of a function signature.

pub trait Foo<T> {
type A;

fn get(&self, t: T) -> Self::A;
}

fn foo2<I : for<'x> Foo<&'x isize>>(
x: <I as Foo<&isize>>::A)
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
{
// This case is illegal because we have to instantiate `'x`, and
// we don't know what region to instantiate it with.
//
// This could perhaps be made equivalent to the examples below,
// specifically for fn signatures.
}

fn foo3<I : for<'x> Foo<&'x isize>>(
x: <I as Foo<&isize>>::A)
{
// OK, in this case we spelled out the precise regions involved, though we left one of
// them anonymous.
}

fn foo4<'a, I : for<'x> Foo<&'x isize>>(
x: <I as Foo<&'a isize>>::A)
{
// OK, in this case we spelled out the precise regions involved.
}


pub fn main() {}
@@ -1,3 +1,5 @@
#![allow(dead_code, unused_variables)]
// run-rustfix
// Check projection of an associated type out of a higher-ranked trait-bound
// in the context of a function signature.

Expand Down
@@ -1,8 +1,8 @@
error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
--> $DIR/associated-types-project-from-hrtb-in-fn.rs:11:8
--> $DIR/associated-types-project-from-hrtb-in-fn.rs:13:8
|
LL | x: I::A)
| ^^^^
| ^^^^ help: use a fully qualified path with inferred lifetimes: `<I as Foo<&isize>>::A`

error: aborting due to previous error

@@ -0,0 +1,38 @@
#![allow(dead_code)]
// run-rustfix
// Check projection of an associated type out of a higher-ranked trait-bound
// in the context of a method definition in a trait.

pub trait Foo<T> {
type A;

fn get(&self, t: T) -> Self::A;
}

trait SomeTrait<I : for<'x> Foo<&'x isize>> {
fn some_method(&self, arg: <I as Foo<&isize>>::A);
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
}

trait AnotherTrait<I : for<'x> Foo<&'x isize>> {
fn some_method(&self, arg: <I as Foo<&isize>>::A);
}

trait YetAnotherTrait<I : for<'x> Foo<&'x isize>> {
fn some_method<'a>(&self, arg: <I as Foo<&'a isize>>::A);
}

trait Banana<'a> {
type Assoc: Default;
}

struct Peach<X>(std::marker::PhantomData<X>);

impl<X: for<'a> Banana<'a>> Peach<X> {
fn mango(&self) -> <X as Banana<'_>>::Assoc {
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
Default::default()
}
}

pub fn main() {}
@@ -1,3 +1,5 @@
#![allow(dead_code)]
// run-rustfix
// Check projection of an associated type out of a higher-ranked trait-bound
// in the context of a method definition in a trait.

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

trait Banana<'a> {
type Assoc: Default;
}

struct Peach<X>(std::marker::PhantomData<X>);

impl<X: for<'a> Banana<'a>> Peach<X> {
fn mango(&self) -> X::Assoc {
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
Default::default()
}
}

pub fn main() {}
@@ -1,8 +1,14 @@
error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
--> $DIR/associated-types-project-from-hrtb-in-trait-method.rs:11:32
--> $DIR/associated-types-project-from-hrtb-in-trait-method.rs:13:32
|
LL | fn some_method(&self, arg: I::A);
| ^^^^
| ^^^^ help: use a fully qualified path with inferred lifetimes: `<I as Foo<&isize>>::A`

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

error: aborting due to 2 previous errors

0 comments on commit 24be307

Please sign in to comment.