about summary refs log tree commit diff
diff options
context:
space:
mode:
authorleonardo.yvens <leoyvens@gmail.com>2018-01-22 13:36:51 -0200
committerleonardo.yvens <leoyvens@gmail.com>2018-02-28 12:33:15 -0300
commit75997d85c50b3c489f6a0bf0e388c86ad7a839e4 (patch)
treeceff99d4d82fa6f9711f776c8ccc5c12ab3589ef
parentaddc404d32b8b153f65b180c4bcdc5ccaaba2a77 (diff)
downloadrust-75997d85c50b3c489f6a0bf0e388c86ad7a839e4.tar.gz
rust-75997d85c50b3c489f6a0bf0e388c86ad7a839e4.zip
Check WF of predicates with defaults only if all params have defaults
-rw-r--r--src/librustc/ty/mod.rs7
-rw-r--r--src/librustc_typeck/check/wfcheck.rs57
-rw-r--r--src/test/run-pass/defaults-well-formedness.rs10
-rw-r--r--src/test/ui/type-check-defaults.rs15
-rw-r--r--src/test/ui/type-check-defaults.stderr29
5 files changed, 64 insertions, 54 deletions
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 2c538d2bba5..3ab2cd274b9 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1040,13 +1040,6 @@ impl<'a, 'gcx, 'tcx> Predicate<'tcx> {
                 Predicate::ConstEvaluatable(def_id, const_substs.subst(tcx, substs)),
         }
     }
-
-    pub fn as_poly_trait_predicate(&self) -> Option<&PolyTraitPredicate<'tcx>> {
-        match self {
-            Predicate::Trait(trait_pred) => Some(trait_pred),
-            _ => None
-        }
-    }
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index 463cc2aa6b3..16115fa9be4 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -392,14 +392,13 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
         // Check that trait predicates are WF when params are substituted by their defaults.
         // We don't want to overly constrain the predicates that may be written but we
         // want to catch obviously wrong cases such as `struct Foo<T: Copy = String>`
-        // or cases that may cause backwards incompatibility such as a library going from
-        // `pub struct Foo<T>` to `pub struct Foo<T, U = i32>` where U: Trait<T>`
-        // which may break existing uses of Foo<T>.
-        // Therefore the check we do is: If if all params appearing in the LHS of the predicate
-        // have defaults then we verify that it is WF with all defaults substituted simultaneously.
+        // or cases where defaults don't work together such as:
+        // `struct Foo<T = i32, U = u8> where T: into<U>`
+        // Therefore we check if a predicate in which all type params are defaulted
+        // is WF with those defaults simultaneously substituted.
         // For more examples see tests `defaults-well-formedness.rs` and `type-check-defaults.rs`.
         //
-        // First, we build the defaulted substitution.
+        // First we build the defaulted substitution.
         let mut defaulted_params = Vec::new();
         let substs = ty::subst::Substs::for_item(fcx.tcx, def_id, |def, _| {
                 // All regions are identity.
@@ -414,33 +413,35 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
                     fcx.tcx.type_of(def.def_id)
                 }
             });
-        // In `trait Trait: Super`, checking `Self: Trait` or `Self: Super` is problematic.
-        // We avoid those by skipping any predicates in trait declarations that contain `Self`,
-        // which is excessive so we end up checking less than we could.
-        for pred in predicates.predicates.iter()
-                                         .filter_map(ty::Predicate::as_poly_trait_predicate)
-                                         .filter(|p| !(is_trait && p.has_self_ty())) {
-            let is_defaulted_param = |ty: ty::Ty| match ty.sty {
-                                            ty::TyParam(p) => defaulted_params.contains(&p.idx),
-                                            _ => false
-                                          };
-            // If there is a non-defaulted param in the LHS, don't check the substituted predicate.
-            // `skip_binder()` is ok, we're only inspecting the type params.
-            if !pred.skip_binder().self_ty().walk().all(is_defaulted_param) {
+        let defaulted_params = &defaulted_params;
+        // Now we build the substituted predicates.
+        for &pred in predicates.predicates.iter() {
+            struct HasNonDefaulted<'a> { defaulted_params: &'a Vec<u32> }
+            impl<'tcx, 'a> ty::fold::TypeVisitor<'tcx> for HasNonDefaulted<'a> {
+                fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
+                    match t.sty {
+                        ty::TyParam(p) => !self.defaulted_params.contains(&p.idx),
+                        _ => t.super_visit_with(self)
+                    }
+                }
+            }
+            // If there is a non-defaulted param in the predicate, don't check it.
+            if pred.visit_with(&mut HasNonDefaulted { defaulted_params }) {
                 continue;
             }
             let substituted_pred = pred.subst(fcx.tcx, substs);
-            // `skip_binder()` is ok, we're only inspecting for `has_self_ty()`.
-            let substituted_lhs = substituted_pred.skip_binder().self_ty();
             // In trait defs, don't check `Self: Sized` when `Self` is the default.
-            let pred_is_sized = Some(pred.def_id()) == fcx.tcx.lang_items().sized_trait();
-            if is_trait && substituted_lhs.has_self_ty() && pred_is_sized {
-                continue;
+            if let ty::Predicate::Trait(trait_pred) = substituted_pred {
+                // `skip_binder()` is ok, we're only inspecting for `has_self_ty()`.
+                let lhs_is_self = trait_pred.skip_binder().self_ty().has_self_ty();
+                let pred_sized = Some(trait_pred.def_id()) == fcx.tcx.lang_items().sized_trait();
+                if is_trait && lhs_is_self && pred_sized {
+                    continue;
+                }
             }
-            let pred = ty::Predicate::Trait(pred.subst(fcx.tcx, substs));
-            // Avoid duplicates.
-            if !predicates.predicates.contains(&pred) {
-                substituted_predicates.push(pred);
+            // Avoid duplication of predicates that contain no parameters, for example.
+            if !predicates.predicates.contains(&substituted_pred) {
+                substituted_predicates.push(substituted_pred);
             }
         }
 
diff --git a/src/test/run-pass/defaults-well-formedness.rs b/src/test/run-pass/defaults-well-formedness.rs
index abbd35b3909..6a7b1c51ccd 100644
--- a/src/test/run-pass/defaults-well-formedness.rs
+++ b/src/test/run-pass/defaults-well-formedness.rs
@@ -11,11 +11,13 @@
 trait Trait<T> {}
 struct Foo<U, V=i32>(U, V) where U: Trait<V>;
 
-trait Trait2 {}
+trait Marker {}
 struct TwoParams<T, U>(T, U);
-impl Trait2 for TwoParams<i32, i32> {}
+impl Marker for TwoParams<i32, i32> {}
 // Check that defaults are substituted simultaneously.
-struct IndividuallyBogus<T = i32, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Trait2;
+struct IndividuallyBogus<T = i32, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Marker;
 // Clauses with non-defaulted params are not checked.
-struct NonDefaultedInClause<T, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Trait2;
+struct NonDefaultedInClause<T, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Marker;
+struct DefaultedLhs<U, V=i32>(U, V) where V: Trait<U>;
+
 fn main() {}
diff --git a/src/test/ui/type-check-defaults.rs b/src/test/ui/type-check-defaults.rs
index cc20f81ae16..ff3fb44bb26 100644
--- a/src/test/ui/type-check-defaults.rs
+++ b/src/test/ui/type-check-defaults.rs
@@ -37,11 +37,14 @@ trait Super<T: Copy> { }
 trait Base<T = String>: Super<T> { }
 //~^ error: the trait bound `T: std::marker::Copy` is not satisfied [E0277]
 
-trait Trait<T> {}
-struct DefaultedLhs<U, V=i32>(U, V) where V: Trait<U>;
-//~^ error: the trait bound `i32: Trait<U>` is not satisfied [E0277]
+trait ProjectionPred<T:Iterator = IntoIter<i32>> where T::Item : Add<u8> {}
+//~^ error: the trait bound `i32: std::ops::Add<u8>` is not satisfied [E0277]
+
+// Defaults must work together.
+struct TwoParams<T = u32, U = i32>(T, U) where T: Bar<U>;
+//~^ the trait bound `u32: Bar<i32>` is not satisfied [E0277]
+trait Bar<V> {}
+impl Bar<String> for u32 { }
+impl Bar<i32> for String { }
 
-// FIXME: Deal with projection predicates
-// trait ProjectionPred<T:Iterator = IntoIter<i32>> where T::Item : Add<u8> {}
-// ~^ error: the trait bound `i32: std::ops::Add<u8>` is not satisfied [E0277]
 fn main() { }
diff --git a/src/test/ui/type-check-defaults.stderr b/src/test/ui/type-check-defaults.stderr
index ede1b417c7c..412a7966f0a 100644
--- a/src/test/ui/type-check-defaults.stderr
+++ b/src/test/ui/type-check-defaults.stderr
@@ -79,17 +79,28 @@ note: required by `Super`
 36 | trait Super<T: Copy> { }
    | ^^^^^^^^^^^^^^^^^^^^
 
-error[E0277]: the trait bound `i32: Trait<U>` is not satisfied
-  --> $DIR/type-check-defaults.rs:41:1
+error[E0277]: the trait bound `i32: std::ops::Add<u8>` is not satisfied
+  --> $DIR/type-check-defaults.rs:40:1
    |
-41 | struct DefaultedLhs<U, V=i32>(U, V) where V: Trait<U>;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait<U>` is not implemented for `i32`
+40 | trait ProjectionPred<T:Iterator = IntoIter<i32>> where T::Item : Add<u8> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `i32 + u8`
    |
-note: required by `Trait`
-  --> $DIR/type-check-defaults.rs:40:1
+   = help: the trait `std::ops::Add<u8>` is not implemented for `i32`
+   = note: required by `std::ops::Add`
+
+error[E0277]: the trait bound `u32: Bar<i32>` is not satisfied
+  --> $DIR/type-check-defaults.rs:44:1
+   |
+44 | struct TwoParams<T = u32, U = i32>(T, U) where T: Bar<U>;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar<i32>` is not implemented for `u32`
+   |
+   = help: the following implementations were found:
+             <u32 as Bar<std::string::String>>
+note: required by `Bar`
+  --> $DIR/type-check-defaults.rs:46:1
    |
-40 | trait Trait<T> {}
-   | ^^^^^^^^^^^^^^
+46 | trait Bar<V> {}
+   | ^^^^^^^^^^^^
 
-error: aborting due to 9 previous errors
+error: aborting due to 10 previous errors