diff options
| author | Dylan DPC <dylan.dpc@gmail.com> | 2020-02-13 02:52:51 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-02-13 02:52:51 +0100 |
| commit | 8d00adf28943db3361d1d425bafc21584b238eaa (patch) | |
| tree | 672a165eded9d95b99454bb088eba505b3bc8db6 | |
| parent | e9f391e09a43680cfe24077ba750997eb3893ade (diff) | |
| parent | bde96776a199064dec3c825ca5ada8f90e1e12d4 (diff) | |
| download | rust-8d00adf28943db3361d1d425bafc21584b238eaa.tar.gz rust-8d00adf28943db3361d1d425bafc21584b238eaa.zip | |
Rollup merge of #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 #69000.
9 files changed, 226 insertions, 20 deletions
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index a7d38725d32..f3a1f412d0d 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,80 @@ 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::Ctor(_) | hir::Node::Variant(_) => { + let item = + self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(self.hir_id())); + match &item.kind { + hir::ItemKind::Enum(_, generics) + | hir::ItemKind::Struct(_, generics) + | hir::ItemKind::Union(_, generics) => { + // FIXME: look for an appropriate lt name if `'a` is already used + let (lt_sp, sugg) = match &generics.params[..] { + [] => (generics.span, "<'a>".to_string()), + [bound, ..] => (bound.span.shrink_to_lo(), "'a, ".to_string()), + }; + let suggestions = vec![ + (lt_sp, sugg), + ( + span, + format!( + "{}::{}", + // Replace the existing lifetimes with a new named lifetime. + self.tcx + .replace_late_bound_regions(&poly_trait_ref, |_| { + self.tcx.mk_region(ty::ReEarlyBound( + ty::EarlyBoundRegion { + def_id: item_def_id, + index: 0, + name: Symbol::intern("'a"), + }, + )) + }) + .0, + item_segment.ident + ), + ), + ]; + err.multipart_suggestion( + "use a fully qualified path with explicit lifetimes", + suggestions, + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + } + 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(..), .. }) => {} + 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 } } 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 00000000000..760d2b433c8 --- /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<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() {} 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 bf13c410cc6..6eb584ea645 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 09f4f99703f..f2137f68665 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: `<I as Foo<&isize>>::A` error: aborting due to previous error diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs index 20f11ecf638..8a5777d4d7c 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs @@ -7,18 +7,25 @@ pub trait Foo<T> { fn get(&self, t: T) -> Self::A; } -struct SomeStruct<I : for<'x> Foo<&'x isize>> { +struct SomeStruct<I: for<'x> Foo<&'x isize>> { field: I::A //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context } +enum SomeEnum<I: for<'x> Foo<&'x isize>> { + TupleVariant(I::A), + //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context + StructVariant { field: I::A }, + //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context +} + // FIXME(eddyb) This one doesn't even compile because of the unsupported syntax. // struct AnotherStruct<I : for<'x> Foo<&'x isize>> { // field: <I as for<'y> Foo<&'y isize>>::A // } -struct YetAnotherStruct<'a, I : for<'x> Foo<&'x isize>> { +struct YetAnotherStruct<'a, I: for<'x> Foo<&'x isize>> { field: <I as Foo<&'a isize>>::A } diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr index 189b19461f4..c71bc70ea6c 100644 --- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr +++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr @@ -3,6 +3,38 @@ error[E0212]: cannot extract an associated type from a higher-ranked trait bound | LL | field: I::A | ^^^^ + | +help: use a fully qualified path with explicit lifetimes + | +LL | struct SomeStruct<'a, I: for<'x> Foo<&'x isize>> { +LL | field: <I as Foo<&'a isize>>::A + | + +error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context + --> $DIR/associated-types-project-from-hrtb-in-struct.rs:16:18 + | +LL | TupleVariant(I::A), + | ^^^^ + | +help: use a fully qualified path with explicit lifetimes + | +LL | enum SomeEnum<'a, I: for<'x> Foo<&'x isize>> { +LL | TupleVariant(<I as Foo<&'a isize>>::A), + | + +error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context + --> $DIR/associated-types-project-from-hrtb-in-struct.rs:18:28 + | +LL | StructVariant { field: I::A }, + | ^^^^ + | +help: use a fully qualified path with explicit lifetimes + | +LL | enum SomeEnum<'a, I: for<'x> Foo<&'x isize>> { +LL | TupleVariant(I::A), +LL | +LL | StructVariant { field: <I as Foo<&'a isize>>::A }, + | -error: aborting due to previous error +error: aborting due to 3 previous errors 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 00000000000..acf32bccbec --- /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<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() {} 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 cb52c2b4f15..a249f89685e 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<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() {} 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 e1c169028c5..a37fec24493 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: `<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 |
