From 24be307b53931a9824829f63aa65fa5c6042ed21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 10 Feb 2020 19:14:31 -0800 Subject: [PATCH] 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. --- src/librustc_typeck/collect.rs | 55 +++++++++++++++---- ...ciated-types-project-from-hrtb-in-fn.fixed | 37 +++++++++++++ ...ssociated-types-project-from-hrtb-in-fn.rs | 2 + ...iated-types-project-from-hrtb-in-fn.stderr | 4 +- ...es-project-from-hrtb-in-trait-method.fixed | 38 +++++++++++++ ...types-project-from-hrtb-in-trait-method.rs | 15 +++++ ...s-project-from-hrtb-in-trait-method.stderr | 12 +++- 7 files changed, 146 insertions(+), 17 deletions(-) create mode 100644 src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.fixed create mode 100644 src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.fixed diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 2a450f4b4e8b1..8d2b6512cfe46 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -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> { @@ -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 @@ -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 `::C`, not `::C`. + self.tcx.anonymize_late_bound_regions(&poly_trait_ref).skip_binder(), + item_segment.ident + ), + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + err.emit(); self.tcx().types.err } } diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.fixed b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.fixed new file mode 100644 index 0000000000000..760d2b433c87a --- /dev/null +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.fixed @@ -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 { + type A; + + fn get(&self, t: T) -> Self::A; +} + +fn foo2 Foo<&'x isize>>( + x: >::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 Foo<&'x isize>>( + x: >::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: >::A) +{ + // OK, in this case we spelled out the precise regions involved. +} + + +pub fn main() {} diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.rs b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.rs index bf13c410cc60b..6eb584ea645ac 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.rs +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.rs @@ -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. diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr index 09f4f99703f9c..f2137f68665db 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr @@ -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: `>::A` error: aborting due to previous error diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.fixed b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.fixed new file mode 100644 index 0000000000000..acf32bccbecfd --- /dev/null +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.fixed @@ -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 { + type A; + + fn get(&self, t: T) -> Self::A; +} + +trait SomeTrait Foo<&'x isize>> { + fn some_method(&self, arg: >::A); + //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context +} + +trait AnotherTrait Foo<&'x isize>> { + fn some_method(&self, arg: >::A); +} + +trait YetAnotherTrait Foo<&'x isize>> { + fn some_method<'a>(&self, arg: >::A); +} + +trait Banana<'a> { + type Assoc: Default; +} + +struct Peach(std::marker::PhantomData); + +impl Banana<'a>> Peach { + fn mango(&self) -> >::Assoc { + //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + Default::default() + } +} + +pub fn main() {} diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.rs b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.rs index cb52c2b4f15d6..a249f89685e39 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.rs +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.rs @@ -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. @@ -20,4 +22,17 @@ trait YetAnotherTrait Foo<&'x isize>> { fn some_method<'a>(&self, arg: >::A); } +trait Banana<'a> { + type Assoc: Default; +} + +struct Peach(std::marker::PhantomData); + +impl Banana<'a>> Peach { + 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() {} diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr index e1c169028c5c4..a37fec244933c 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr @@ -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: `>::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: `>::Assoc` + +error: aborting due to 2 previous errors