about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJack Huey <31162821+jackh726@users.noreply.github.com>2022-01-08 23:30:19 -0500
committerJack Huey <31162821+jackh726@users.noreply.github.com>2022-02-17 14:09:21 -0500
commit3d19c8defd776eb7e9e113cf49c3e1d2f51e408e (patch)
tree1a033554d1cbe17bd8dda1f54886017acd4b3548
parent30b3f35c420694a4f24e5a4df00f06073f4f3a37 (diff)
downloadrust-3d19c8defd776eb7e9e113cf49c3e1d2f51e408e.tar.gz
rust-3d19c8defd776eb7e9e113cf49c3e1d2f51e408e.zip
Suggest copying trait associated type bounds on lifetime error
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs52
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs17
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs3
-rw-r--r--compiler/rustc_typeck/src/check/compare_method.rs9
-rw-r--r--src/test/ui/generic-associated-types/impl_bounds.stderr7
-rw-r--r--src/test/ui/generic-associated-types/issue-88595.rs5
-rw-r--r--src/test/ui/generic-associated-types/issue-88595.stderr22
-rw-r--r--src/test/ui/generic-associated-types/issue-90014.stderr7
-rw-r--r--src/test/ui/generic-associated-types/issue-92033.rs39
-rw-r--r--src/test/ui/generic-associated-types/issue-92033.stderr22
12 files changed, 163 insertions, 28 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
index 8512597cb91..f44e6e04346 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
@@ -64,7 +64,7 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
             .or_else(|| self.try_report_mismatched_static_lifetime())
     }
 
-    pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {
+    pub(super) fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {
         match (&self.error, self.regions) {
             (Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), *sub, *sup)),
             (Some(SubSupConflict(_, _, origin, sub, _, sup, _)), None) => {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 8e5efa12ac6..8671ecba6e9 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -102,6 +102,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     "...so that the definition in impl matches the definition from the trait",
                 );
             }
+            infer::CheckAssociatedTypeBounds { ref parent, .. } => {
+                self.note_region_origin(err, &parent);
+            }
         }
     }
 
@@ -345,6 +348,55 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     trait_item_def_id,
                     &format!("`{}: {}`", sup, sub),
                 ),
+            infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
+                let mut err = self.report_concrete_failure(*parent, sub, sup);
+
+                let trait_item_span = self.tcx.def_span(trait_item_def_id);
+                let item_name = self.tcx.item_name(impl_item_def_id);
+                err.span_label(
+                    trait_item_span,
+                    format!("definition of `{}` from trait", item_name),
+                );
+
+                let trait_predicates = self.tcx.explicit_predicates_of(trait_item_def_id);
+                let impl_predicates = self.tcx.explicit_predicates_of(impl_item_def_id);
+
+                let impl_predicates: rustc_data_structures::stable_set::FxHashSet<_> =
+                    impl_predicates.predicates.into_iter().map(|(pred, _)| pred).collect();
+                let clauses: Vec<_> = trait_predicates
+                    .predicates
+                    .into_iter()
+                    .filter(|&(pred, _)| !impl_predicates.contains(pred))
+                    .map(|(pred, _)| format!("{}", pred))
+                    .collect();
+
+                if !clauses.is_empty() {
+                    let where_clause_span = self
+                        .tcx
+                        .hir()
+                        .get_generics(impl_item_def_id.expect_local())
+                        .unwrap()
+                        .where_clause
+                        .tail_span_for_suggestion();
+
+                    let suggestion = format!(
+                        "{} {}",
+                        if !impl_predicates.is_empty() { "," } else { " where" },
+                        clauses.join(", "),
+                    );
+                    err.span_suggestion(
+                        where_clause_span,
+                        &format!(
+                            "try copying {} from the trait",
+                            if clauses.len() > 1 { "these clauses" } else { "this clause" }
+                        ),
+                        suggestion,
+                        rustc_errors::Applicability::MaybeIncorrect,
+                    );
+                }
+
+                err
+            }
         }
     }
 
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index f0a4ec81313..57ac98ca897 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -438,6 +438,13 @@ pub enum SubregionOrigin<'tcx> {
     /// Comparing the signature and requirements of an impl associated type
     /// against the containing trait
     CompareImplTypeObligation { span: Span, impl_item_def_id: DefId, trait_item_def_id: DefId },
+
+    /// Checking that the bounds of a trait's associated type hold for a given impl
+    CheckAssociatedTypeBounds {
+        parent: Box<SubregionOrigin<'tcx>>,
+        impl_item_def_id: DefId,
+        trait_item_def_id: DefId,
+    },
 }
 
 // `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger.
@@ -1832,6 +1839,7 @@ impl<'tcx> SubregionOrigin<'tcx> {
             ReferenceOutlivesReferent(_, a) => a,
             CompareImplMethodObligation { span, .. } => span,
             CompareImplTypeObligation { span, .. } => span,
+            CheckAssociatedTypeBounds { ref parent, .. } => parent.span(),
         }
     }
 
@@ -1862,6 +1870,15 @@ impl<'tcx> SubregionOrigin<'tcx> {
                 trait_item_def_id,
             },
 
+            traits::ObligationCauseCode::CheckAssociatedTypeBounds {
+                impl_item_def_id,
+                trait_item_def_id,
+            } => SubregionOrigin::CheckAssociatedTypeBounds {
+                impl_item_def_id,
+                trait_item_def_id,
+                parent: Box::new(default()),
+            },
+
             _ => default(),
         }
     }
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 1123cab8076..b54418e5201 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -285,6 +285,12 @@ pub enum ObligationCauseCode<'tcx> {
         trait_item_def_id: DefId,
     },
 
+    /// Checking that the bounds of a trait's associated type hold for a given impl
+    CheckAssociatedTypeBounds {
+        impl_item_def_id: DefId,
+        trait_item_def_id: DefId,
+    },
+
     /// Checking that this expression can be assigned where it needs to be
     // FIXME(eddyb) #11161 is the original Expr required?
     ExprAssignable,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 40cb9647a35..6068b90aef8 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -1932,7 +1932,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             | ObligationCauseCode::AwaitableExpr(_)
             | ObligationCauseCode::ForLoopIterator
             | ObligationCauseCode::QuestionMark
-            | ObligationCauseCode::LetElse => {}
+            | ObligationCauseCode::LetElse
+            | ObligationCauseCode::CheckAssociatedTypeBounds { .. } => {}
             ObligationCauseCode::SliceOrArrayElem => {
                 err.note("slice and array elements must have `Sized` type");
             }
diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs
index 457e9cf1ea5..38449c2a76a 100644
--- a/compiler/rustc_typeck/src/check/compare_method.rs
+++ b/compiler/rustc_typeck/src/check/compare_method.rs
@@ -1378,7 +1378,14 @@ pub fn check_type_bounds<'tcx>(
         let mut selcx = traits::SelectionContext::new(&infcx);
 
         let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
-        let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id);
+        let normalize_cause = ObligationCause::new(
+            impl_ty_span,
+            impl_ty_hir_id,
+            ObligationCauseCode::CheckAssociatedTypeBounds {
+                impl_item_def_id: impl_ty.def_id,
+                trait_item_def_id: trait_ty.def_id,
+            },
+        );
         let mk_cause = |span: Span| {
             let code = if span.is_dummy() {
                 traits::MiscObligation
diff --git a/src/test/ui/generic-associated-types/impl_bounds.stderr b/src/test/ui/generic-associated-types/impl_bounds.stderr
index 5be431f2933..bd0dea37219 100644
--- a/src/test/ui/generic-associated-types/impl_bounds.stderr
+++ b/src/test/ui/generic-associated-types/impl_bounds.stderr
@@ -19,8 +19,13 @@ LL |     type B<'a, 'b> where 'b: 'a = (&'a(), &'b ());
 error[E0478]: lifetime bound not satisfied
   --> $DIR/impl_bounds.rs:17:35
    |
+LL |     type B<'a, 'b> where 'a: 'b;
+   |     ---------------------------- definition of `B` from trait
+...
 LL |     type B<'a, 'b> where 'b: 'a = (&'a(), &'b ());
-   |                                   ^^^^^^^^^^^^^^^
+   |                                -  ^^^^^^^^^^^^^^^
+   |                                |
+   |                                help: try copying this clause from the trait: `, 'a: 'b`
    |
 note: lifetime parameter instantiated with the lifetime `'a` as defined here
   --> $DIR/impl_bounds.rs:17:12
diff --git a/src/test/ui/generic-associated-types/issue-88595.rs b/src/test/ui/generic-associated-types/issue-88595.rs
index e397390783f..c97d17811ba 100644
--- a/src/test/ui/generic-associated-types/issue-88595.rs
+++ b/src/test/ui/generic-associated-types/issue-88595.rs
@@ -8,7 +8,7 @@ trait A<'a> {
     // FIXME(generic_associated_types): Remove one of the below bounds
     // https://github.com/rust-lang/rust/pull/90678#discussion_r744976085
     where
-        'a: 'b, Self: 'a, Self: 'b;
+        Self: 'a, Self: 'b;
 
     fn a(&'a self) -> Self::B<'a>;
 }
@@ -17,8 +17,7 @@ struct C;
 
 impl<'a> A<'a> for C {
     type B<'b> = impl Clone;
-    //~^ ERROR: lifetime bound not satisfied
-    //~| ERROR: could not find defining uses
+    //~^ ERROR: could not find defining uses
 
     fn a(&'a self) -> Self::B<'a> {} //~ ERROR: non-defining opaque type use in defining scope
 }
diff --git a/src/test/ui/generic-associated-types/issue-88595.stderr b/src/test/ui/generic-associated-types/issue-88595.stderr
index cb462871ccd..4e4f86bbac8 100644
--- a/src/test/ui/generic-associated-types/issue-88595.stderr
+++ b/src/test/ui/generic-associated-types/issue-88595.stderr
@@ -1,22 +1,5 @@
-error[E0478]: lifetime bound not satisfied
-  --> $DIR/issue-88595.rs:19:18
-   |
-LL |     type B<'b> = impl Clone;
-   |                  ^^^^^^^^^^
-   |
-note: lifetime parameter instantiated with the lifetime `'a` as defined here
-  --> $DIR/issue-88595.rs:18:6
-   |
-LL | impl<'a> A<'a> for C {
-   |      ^^
-note: but lifetime parameter must outlive the lifetime `'b` as defined here
-  --> $DIR/issue-88595.rs:19:12
-   |
-LL |     type B<'b> = impl Clone;
-   |            ^^
-
 error: non-defining opaque type use in defining scope
-  --> $DIR/issue-88595.rs:23:23
+  --> $DIR/issue-88595.rs:22:23
    |
 LL |     fn a(&'a self) -> Self::B<'a> {}
    |                       ^^^^^^^^^^^
@@ -35,6 +18,5 @@ error: could not find defining uses
 LL |     type B<'b> = impl Clone;
    |                  ^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0478`.
diff --git a/src/test/ui/generic-associated-types/issue-90014.stderr b/src/test/ui/generic-associated-types/issue-90014.stderr
index 23e8d08af34..f8fb71bbddb 100644
--- a/src/test/ui/generic-associated-types/issue-90014.stderr
+++ b/src/test/ui/generic-associated-types/issue-90014.stderr
@@ -1,8 +1,13 @@
 error[E0477]: the type `&mut ()` does not fulfill the required lifetime
   --> $DIR/issue-90014.rs:14:20
    |
+LL |     type Fut<'a> where Self: 'a;
+   |     ---------------------------- definition of `Fut` from trait
+...
 LL |     type Fut<'a> = impl Future<Output = ()>;
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^
+   |                 -  ^^^^^^^^^^^^^^^^^^^^^^^^
+   |                 |
+   |                 help: try copying this clause from the trait: `where Self: 'a`
    |
 note: type must outlive the lifetime `'a` as defined here
   --> $DIR/issue-90014.rs:14:14
diff --git a/src/test/ui/generic-associated-types/issue-92033.rs b/src/test/ui/generic-associated-types/issue-92033.rs
new file mode 100644
index 00000000000..1d5f7d5c009
--- /dev/null
+++ b/src/test/ui/generic-associated-types/issue-92033.rs
@@ -0,0 +1,39 @@
+#![feature(generic_associated_types)]
+
+struct Texture;
+
+trait Surface {
+    type TextureIter<'a>: Iterator<Item = &'a Texture>
+    where
+        Self: 'a;
+
+    fn get_texture(&self) -> Self::TextureIter<'_>;
+}
+
+trait Swapchain {
+    type Surface<'a>: Surface
+    where
+        Self: 'a;
+
+    fn get_surface(&self) -> Self::Surface<'_>;
+}
+
+impl<'s> Surface for &'s Texture {
+    type TextureIter<'a> = std::option::IntoIter<&'a Texture>;
+    //~^ ERROR the type
+
+    fn get_texture(&self) -> Self::TextureIter<'_> {
+        let option: Option<&Texture> = Some(self);
+        option.into_iter()
+    }
+}
+
+impl Swapchain for Texture {
+    type Surface<'a> = &'a Texture;
+
+    fn get_surface(&self) -> Self::Surface<'_> {
+        self
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/generic-associated-types/issue-92033.stderr b/src/test/ui/generic-associated-types/issue-92033.stderr
new file mode 100644
index 00000000000..caa6618f398
--- /dev/null
+++ b/src/test/ui/generic-associated-types/issue-92033.stderr
@@ -0,0 +1,22 @@
+error[E0477]: the type `&'s Texture` does not fulfill the required lifetime
+  --> $DIR/issue-92033.rs:22:28
+   |
+LL | /     type TextureIter<'a>: Iterator<Item = &'a Texture>
+LL | |     where
+LL | |         Self: 'a;
+   | |_________________- definition of `TextureIter` from trait
+...
+LL |       type TextureIter<'a> = std::option::IntoIter<&'a Texture>;
+   |                           -  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                           |
+   |                           help: try copying this clause from the trait: `where Self: 'a`
+   |
+note: type must outlive the lifetime `'a` as defined here
+  --> $DIR/issue-92033.rs:22:22
+   |
+LL |     type TextureIter<'a> = std::option::IntoIter<&'a Texture>;
+   |                      ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0477`.