about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-02-13 02:52:51 +0100
committerGitHub <noreply@github.com>2020-02-13 02:52:51 +0100
commit8d00adf28943db3361d1d425bafc21584b238eaa (patch)
tree672a165eded9d95b99454bb088eba505b3bc8db6
parente9f391e09a43680cfe24077ba750997eb3893ade (diff)
parentbde96776a199064dec3c825ca5ada8f90e1e12d4 (diff)
downloadrust-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.
-rw-r--r--src/librustc_typeck/collect.rs93
-rw-r--r--src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.fixed37
-rw-r--r--src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.rs2
-rw-r--r--src/test/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr4
-rw-r--r--src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs11
-rw-r--r--src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr34
-rw-r--r--src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.fixed38
-rw-r--r--src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.rs15
-rw-r--r--src/test/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr12
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