about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthew Jasper <mjjasper1@gmail.com>2020-06-28 12:41:46 +0100
committerMatthew Jasper <mjjasper1@gmail.com>2020-10-06 11:19:29 +0100
commitb3057f4d5fe7156aa85e1844d3638fa14ba48b79 (patch)
tree3dcb6de03084fe3bdd14ba25d09009c6b427b5cf
parent87f2f42dc2c81dd842451e6c68d12abdc5fe3ecf (diff)
Check projections are well-formed when using projection candidates
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs34
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs29
-rw-r--r--compiler/rustc_typeck/src/check/compare_method.rs6
-rw-r--r--compiler/rustc_typeck/src/check/wfcheck.rs2
-rw-r--r--src/test/ui/generic-associated-types/projection-bound-cycle-generic.rs62
-rw-r--r--src/test/ui/generic-associated-types/projection-bound-cycle-generic.stderr21
-rw-r--r--src/test/ui/generic-associated-types/projection-bound-cycle.rs64
-rw-r--r--src/test/ui/generic-associated-types/projection-bound-cycle.stderr21
-rw-r--r--src/test/ui/specialization/issue-38091-2.rs23
-rw-r--r--src/test/ui/specialization/issue-38091-2.stderr23
-rw-r--r--src/test/ui/specialization/issue-38091.rs (renamed from src/test/ui/issues/issue-38091.rs)1
-rw-r--r--src/test/ui/specialization/issue-38091.stderr (renamed from src/test/ui/issues/issue-38091.stderr)16
13 files changed, 278 insertions, 30 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 9cb5c232646..6c7732f28e4 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -323,9 +323,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             _ => return,
         }
 
-        let result = self
-            .infcx
-            .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
+        let result = self.infcx.probe(|_| {
+            self.match_projection_obligation_against_definition_bounds(obligation).is_some()
+        });
 
         if result {
             candidates.vec.push(ProjectionCandidate);
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 88b656ce680..625bb5d7a44 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -69,8 +69,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
 
             ProjectionCandidate => {
-                self.confirm_projection_candidate(obligation);
-                Ok(ImplSource::Param(Vec::new()))
+                let obligations = self.confirm_projection_candidate(obligation);
+                Ok(ImplSource::Param(obligations))
             }
 
             ClosureCandidate => {
@@ -116,10 +116,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
     }
 
-    fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) {
+    fn confirm_projection_candidate(
+        &mut self,
+        obligation: &TraitObligation<'tcx>,
+    ) -> Vec<PredicateObligation<'tcx>> {
         self.infcx.commit_unconditionally(|_| {
-            let result = self.match_projection_obligation_against_definition_bounds(obligation);
-            assert!(result);
+            let candidate = self
+                .match_projection_obligation_against_definition_bounds(obligation)
+                .unwrap_or_else(|| bug!("Can't find selected projection candidate"));
+            let mut obligations = self
+                .infcx
+                .at(&obligation.cause, obligation.param_env)
+                .sup(obligation.predicate.to_poly_trait_ref(), candidate)
+                .map(|InferOk { obligations, .. }| obligations)
+                .unwrap_or_else(|_| {
+                    bug!(
+                        "Projection bound `{:?}` was applicable to `{:?}` but now is not",
+                        candidate,
+                        obligation
+                    );
+                });
+            // Require that the projection is well-formed.
+            let self_ty = obligation.predicate.skip_binder().self_ty();
+            obligations.push(Obligation::new(
+                obligation.cause.clone(),
+                obligation.param_env,
+                ty::PredicateKind::WellFormed(self_ty.into()).to_predicate(self.tcx()),
+            ));
+            obligations
         })
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 9bebf8d90c2..2d6ccfe031e 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1159,7 +1159,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn match_projection_obligation_against_definition_bounds(
         &mut self,
         obligation: &TraitObligation<'tcx>,
-    ) -> bool {
+    ) -> Option<ty::PolyTraitRef<'tcx>> {
         let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate);
         let (placeholder_trait_predicate, _) =
             self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate);
@@ -1170,9 +1170,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         );
 
         let tcx = self.infcx.tcx;
-        let predicates = match *placeholder_trait_predicate.trait_ref.self_ty().kind() {
-            ty::Projection(ref data) => tcx.item_bounds(data.item_def_id).subst(tcx, data.substs),
-            ty::Opaque(def_id, substs) => tcx.item_bounds(def_id).subst(tcx, substs),
+        let (def_id, substs) = match *placeholder_trait_predicate.trait_ref.self_ty().kind() {
+            ty::Projection(ref data) => (data.item_def_id, data.substs),
+            ty::Opaque(def_id, substs) => (def_id, substs),
             _ => {
                 span_bug!(
                     obligation.cause.span,
@@ -1182,12 +1182,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 );
             }
         };
+        let bounds = tcx.item_bounds(def_id).subst(tcx, substs);
 
-        let matching_bound = predicates.iter().find_map(|bound| {
+        let matching_bound = bounds.iter().find_map(|bound| {
             if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() {
                 let bound = ty::Binder::bind(pred.trait_ref);
                 if self.infcx.probe(|_| {
                     self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref)
+                        .is_ok()
                 }) {
                     return Some(bound);
                 }
@@ -1200,17 +1202,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
              matching_bound={:?}",
             matching_bound
         );
-        match matching_bound {
-            None => false,
-            Some(bound) => {
-                // Repeat the successful match, if any, this time outside of a probe.
-                let result =
-                    self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref);
-
-                assert!(result);
-                true
-            }
-        }
+        matching_bound
     }
 
     fn match_projection(
@@ -1218,12 +1210,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         obligation: &TraitObligation<'tcx>,
         trait_bound: ty::PolyTraitRef<'tcx>,
         placeholder_trait_ref: ty::TraitRef<'tcx>,
-    ) -> bool {
+    ) -> Result<Vec<PredicateObligation<'tcx>>, ()> {
         debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars());
         self.infcx
             .at(&obligation.cause, obligation.param_env)
             .sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound)
-            .is_ok()
+            .map(|InferOk { obligations, .. }| obligations)
+            .map_err(|_| ())
     }
 
     fn evaluate_where_clause<'o>(
diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs
index 3515fb12e4c..b059b03bc05 100644
--- a/compiler/rustc_typeck/src/check/compare_method.rs
+++ b/compiler/rustc_typeck/src/check/compare_method.rs
@@ -1269,7 +1269,11 @@ pub fn check_type_bounds<'tcx>(
         // Finally, resolve all regions. This catches wily misuses of
         // lifetime parameters.
         let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id);
-        fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &[]);
+        let implied_bounds = match impl_ty.container {
+            ty::TraitContainer(_) => vec![],
+            ty::ImplContainer(def_id) => fcx.impl_implied_bounds(def_id, impl_ty_span),
+        };
+        fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &implied_bounds);
 
         Ok(())
     })
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs
index f31ba886d52..b4e950ab6e9 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -1425,7 +1425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             .collect()
     }
 
-    fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec<Ty<'tcx>> {
+    pub(super) fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec<Ty<'tcx>> {
         match self.tcx.impl_trait_ref(impl_def_id) {
             Some(ref trait_ref) => {
                 // Trait impl: take implied bounds from all types that
diff --git a/src/test/ui/generic-associated-types/projection-bound-cycle-generic.rs b/src/test/ui/generic-associated-types/projection-bound-cycle-generic.rs
new file mode 100644
index 00000000000..c88fb8ad96e
--- /dev/null
+++ b/src/test/ui/generic-associated-types/projection-bound-cycle-generic.rs
@@ -0,0 +1,62 @@
+// Like `projection-bound-cycle.rs` but this avoids using
+// `feature(trivial_bounds)`.
+
+#![feature(generic_associated_types)]
+//~^ WARNING the feature `generic_associated_types` is incomplete
+
+trait Print {
+    fn print();
+}
+
+trait Foo {
+    type Item: Sized where <Self as Foo>::Item: Sized;
+}
+
+struct Number<T> { t: T }
+
+impl<T> Foo for Number<T> {
+    // Well-formedness checks require that the following
+    // goal is true:
+    // ```
+    // if ([T]: Sized) { # if the where clauses hold
+    //     [T]: Sized # then the bound on the associated type hold
+    // }
+    // ```
+    // which it is :)
+    type Item where [T]: Sized = [T];
+}
+
+struct OnlySized<T> where T: Sized { f: T }
+impl<T> Print for OnlySized<T> {
+    fn print() {
+        println!("{}", std::mem::size_of::<T>());
+    }
+}
+
+trait Bar {
+    type Assoc: Print;
+}
+
+impl<T> Bar for T where T: Foo {
+    // This is not ok, we need to prove `wf(<T as Foo>::Item)`, which requires
+    // knowing that `<T as Foo>::Item: Sized` to satisfy the where clause. We
+    // can use the bound on `Foo::Item` for this, but that requires
+    // `wf(<T as Foo>::Item)`, which is an invalid cycle.
+    type Assoc = OnlySized<<T as Foo>::Item>;
+    //~^ ERROR overflow evaluating the requirement `<T as Foo>::Item: std::marker::Sized`
+}
+
+fn foo<T: Print>() {
+    T::print() // oops, in fact `T = OnlySized<str>` which is ill-formed
+}
+
+fn bar<T: Bar>() {
+    // we have `FromEnv(T: Bar)` hence
+    // `<T as Bar>::Assoc` is well-formed and
+    // `Implemented(<T as Bar>::Assoc: Print)` hold
+    foo::<<T as Bar>::Assoc>()
+}
+
+fn main() {
+    bar::<Number<u8>>()
+}
diff --git a/src/test/ui/generic-associated-types/projection-bound-cycle-generic.stderr b/src/test/ui/generic-associated-types/projection-bound-cycle-generic.stderr
new file mode 100644
index 00000000000..5967752ef79
--- /dev/null
+++ b/src/test/ui/generic-associated-types/projection-bound-cycle-generic.stderr
@@ -0,0 +1,21 @@
+warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/projection-bound-cycle-generic.rs:4:12
+   |
+LL | #![feature(generic_associated_types)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
+
+error[E0275]: overflow evaluating the requirement `<T as Foo>::Item: std::marker::Sized`
+  --> $DIR/projection-bound-cycle-generic.rs:45:5
+   |
+LL | struct OnlySized<T> where T: Sized { f: T }
+   |                  - required by this bound in `OnlySized`
+...
+LL |     type Assoc = OnlySized<<T as Foo>::Item>;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/src/test/ui/generic-associated-types/projection-bound-cycle.rs b/src/test/ui/generic-associated-types/projection-bound-cycle.rs
new file mode 100644
index 00000000000..0728dc1bcbc
--- /dev/null
+++ b/src/test/ui/generic-associated-types/projection-bound-cycle.rs
@@ -0,0 +1,64 @@
+// Test case from Chalk.
+// Make sure that we make sure that we don't allow arbitrary bounds to be
+// proven when a bound and a where clause of an associated type are the same.
+
+#![feature(generic_associated_types)]
+//~^ WARNING the feature `generic_associated_types` is incomplete
+#![feature(trivial_bounds)]
+
+trait Print {
+    fn print();
+}
+
+trait Foo {
+    type Item: Sized where <Self as Foo>::Item: Sized;
+}
+
+struct Number { }
+
+impl Foo for Number {
+    // Well-formedness checks require that the following
+    // goal is true:
+    // ```
+    // if (str: Sized) { # if the where clauses hold
+    //     str: Sized # then the bound on the associated type hold
+    // }
+    // ```
+    // which it is :)
+    type Item where str: Sized = str;
+}
+
+struct OnlySized<T> where T: Sized { f: T }
+impl<T> Print for OnlySized<T> {
+    fn print() {
+        println!("{}", std::mem::size_of::<T>());
+    }
+}
+
+trait Bar {
+    type Assoc: Print;
+}
+
+impl<T> Bar for T where T: Foo {
+    // This is not ok, we need to prove `wf(<T as Foo>::Item)`, which requires
+    // knowing that `<T as Foo>::Item: Sized` to satisfy the where clause. We
+    // can use the bound on `Foo::Item` for this, but that requires
+    // `wf(<T as Foo>::Item)`, which is an invalid cycle.
+    type Assoc = OnlySized<<T as Foo>::Item>;
+    //~^ ERROR overflow evaluating the requirement `<T as Foo>::Item: std::marker::Sized`
+}
+
+fn foo<T: Print>() {
+    T::print() // oops, in fact `T = OnlySized<str>` which is ill-formed
+}
+
+fn bar<T: Bar>() {
+    // we have `FromEnv(T: Bar)` hence
+    // `<T as Bar>::Assoc` is well-formed and
+    // `Implemented(<T as Bar>::Assoc: Print)` hold
+    foo::<<T as Bar>::Assoc>()
+}
+
+fn main() {
+    bar::<Number>()
+}
diff --git a/src/test/ui/generic-associated-types/projection-bound-cycle.stderr b/src/test/ui/generic-associated-types/projection-bound-cycle.stderr
new file mode 100644
index 00000000000..80d102013a7
--- /dev/null
+++ b/src/test/ui/generic-associated-types/projection-bound-cycle.stderr
@@ -0,0 +1,21 @@
+warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/projection-bound-cycle.rs:5:12
+   |
+LL | #![feature(generic_associated_types)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
+
+error[E0275]: overflow evaluating the requirement `<T as Foo>::Item: std::marker::Sized`
+  --> $DIR/projection-bound-cycle.rs:47:5
+   |
+LL | struct OnlySized<T> where T: Sized { f: T }
+   |                  - required by this bound in `OnlySized`
+...
+LL |     type Assoc = OnlySized<<T as Foo>::Item>;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/src/test/ui/specialization/issue-38091-2.rs b/src/test/ui/specialization/issue-38091-2.rs
new file mode 100644
index 00000000000..da7f6737733
--- /dev/null
+++ b/src/test/ui/specialization/issue-38091-2.rs
@@ -0,0 +1,23 @@
+#![feature(specialization)]
+//~^ WARN the feature `specialization` is incomplete
+
+trait Iterate<'a> {
+    type Ty: Valid;
+    fn iterate(self);
+}
+impl<'a, T> Iterate<'a> for T where T: Check {
+    default type Ty = ();
+    default fn iterate(self) {}
+}
+
+trait Check {}
+impl<'a, T> Check for T where <T as Iterate<'a>>::Ty: Valid {}
+
+trait Valid {}
+
+impl Valid for () {}
+
+fn main() {
+    Iterate::iterate(0);
+    //~^ ERROR overflow evaluating the requirement `{integer}: Check`
+}
diff --git a/src/test/ui/specialization/issue-38091-2.stderr b/src/test/ui/specialization/issue-38091-2.stderr
new file mode 100644
index 00000000000..e776ff0fafb
--- /dev/null
+++ b/src/test/ui/specialization/issue-38091-2.stderr
@@ -0,0 +1,23 @@
+warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/issue-38091-2.rs:1:12
+   |
+LL | #![feature(specialization)]
+   |            ^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
+
+error[E0275]: overflow evaluating the requirement `{integer}: Check`
+  --> $DIR/issue-38091-2.rs:21:5
+   |
+LL |     fn iterate(self);
+   |     ----------------- required by `Iterate::iterate`
+...
+LL |     Iterate::iterate(0);
+   |     ^^^^^^^^^^^^^^^^
+   |
+   = note: required because of the requirements on the impl of `Iterate<'_>` for `{integer}`
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/src/test/ui/issues/issue-38091.rs b/src/test/ui/specialization/issue-38091.rs
index a84391b94d1..cc0eeb53687 100644
--- a/src/test/ui/issues/issue-38091.rs
+++ b/src/test/ui/specialization/issue-38091.rs
@@ -18,4 +18,5 @@ trait Valid {}
 
 fn main() {
     Iterate::iterate(0);
+    //~^ ERROR overflow evaluating the requirement `{integer}: Check`
 }
diff --git a/src/test/ui/issues/issue-38091.stderr b/src/test/ui/specialization/issue-38091.stderr
index f47fd8d1b0d..beec702b89e 100644
--- a/src/test/ui/issues/issue-38091.stderr
+++ b/src/test/ui/specialization/issue-38091.stderr
@@ -16,6 +16,18 @@ LL |     type Ty: Valid;
 LL |     default type Ty = ();
    |     ^^^^^^^^^^^^^^^^^^^^^ the trait `Valid` is not implemented for `()`
 
-error: aborting due to previous error; 1 warning emitted
+error[E0275]: overflow evaluating the requirement `{integer}: Check`
+  --> $DIR/issue-38091.rs:20:5
+   |
+LL |     fn iterate(self);
+   |     ----------------- required by `Iterate::iterate`
+...
+LL |     Iterate::iterate(0);
+   |     ^^^^^^^^^^^^^^^^
+   |
+   = note: required because of the requirements on the impl of `Iterate<'_>` for `{integer}`
+
+error: aborting due to 2 previous errors; 1 warning emitted
 
-For more information about this error, try `rustc --explain E0277`.
+Some errors have detailed explanations: E0275, E0277.
+For more information about an error, try `rustc --explain E0275`.