]> git.lizzy.rs Git - rust.git/commitdiff
Check only predicates with a single param with a concrete default.
authorleonardo.yvens <leoyvens@gmail.com>
Fri, 9 Feb 2018 10:42:11 +0000 (08:42 -0200)
committerleonardo.yvens <leoyvens@gmail.com>
Wed, 28 Feb 2018 15:33:16 +0000 (12:33 -0300)
This is the most conservative possible and should be always correct.

src/librustc_typeck/check/wfcheck.rs
src/test/run-pass/defaults-well-formedness.rs
src/test/ui/type-check-defaults.rs
src/test/ui/type-check-defaults.stderr

index 21660d817b213f901cd4b2ad54a56f73add8ce86..258860d3d5e404deb844b8d673fc62c1c258fbee 100644 (file)
@@ -278,7 +278,7 @@ fn check_type_defn<F>(&mut self, item: &hir::Item, all_sized: bool, mut lookup_f
     fn check_trait(&mut self, item: &hir::Item) {
         let trait_def_id = self.tcx.hir.local_def_id(item.id);
         self.for_item(item).with_fcx(|fcx, _| {
-            self.check_trait_where_clauses(fcx, item.span, trait_def_id);
+            self.check_where_clauses(fcx, item.span, trait_def_id);
             vec![]
         });
     }
@@ -354,23 +354,6 @@ fn check_where_clauses<'fcx, 'tcx>(&mut self,
                                        fcx: &FnCtxt<'fcx, 'gcx, 'tcx>,
                                        span: Span,
                                        def_id: DefId) {
-        self.inner_check_where_clauses(fcx, span, def_id, false)
-    }
-
-    fn check_trait_where_clauses<'fcx, 'tcx>(&mut self,
-                                       fcx: &FnCtxt<'fcx, 'gcx, 'tcx>,
-                                       span: Span,
-                                       def_id: DefId) {
-        self.inner_check_where_clauses(fcx, span, def_id, true)
-    }
-
-    /// Checks where clauses and inline bounds that are declared on def_id.
-    fn inner_check_where_clauses<'fcx, 'tcx>(&mut self,
-                                       fcx: &FnCtxt<'fcx, 'gcx, 'tcx>,
-                                       span: Span,
-                                       def_id: DefId,
-                                       is_trait: bool)
-    {
         use ty::subst::Subst;
         use rustc::ty::TypeFoldable;
 
@@ -390,12 +373,10 @@ fn inner_check_where_clauses<'fcx, 'tcx>(&mut self,
         }
 
         // 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 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.
+        // We don't want to overly constrain the predicates that may be written but we want to
+        // catch cases where a default my never be applied such as `struct Foo<T: Copy = String>`.
+        // Therefore we check if a predicate which contains a single type param
+        // with a concrete default is WF with that default substituted.
         // For more examples see tests `defaults-well-formedness.rs` and `type-check-defaults.rs`.
         //
         // First we build the defaulted substitution.
@@ -403,30 +384,39 @@ fn inner_check_where_clauses<'fcx, 'tcx>(&mut self,
                 // All regions are identity.
                 fcx.tcx.mk_region(ty::ReEarlyBound(def.to_early_bound_region_data()))
             }, |def, _| {
-                if !is_our_default(def) {
-                    // We don't want to use non-defaulted params in a substitution, mark as err.
-                    fcx.tcx.types.err
-                } else  {
-                    // Substitute with default.
-                    fcx.tcx.type_of(def.def_id)
+                // If the param has a default,
+                if is_our_default(def) {
+                    let default_ty = fcx.tcx.type_of(def.def_id);
+                    // and it's not a dependent default
+                    if !default_ty.needs_subst() {
+                        // then substitute with the default.
+                        return default_ty;
+                    }
                 }
+                // Mark unwanted params as err.
+                fcx.tcx.types.err
             });
         // Now we build the substituted predicates.
         for &pred in predicates.predicates.iter() {
+            struct CountParams { params: FxHashSet<u32> }
+            impl<'tcx> ty::fold::TypeVisitor<'tcx> for CountParams {
+                fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
+                    match t.sty {
+                        ty::TyParam(p) => {
+                            self.params.insert(p.idx);
+                            t.super_visit_with(self)
+                        }
+                        _ => t.super_visit_with(self)
+                    }
+                }
+            }
+            let mut param_count = CountParams { params: FxHashSet() };
+            pred.visit_with(&mut param_count);
             let substituted_pred = pred.subst(fcx.tcx, substs);
-            // If there is a non-defaulted param in the predicate, don't check it.
-            if substituted_pred.references_error() {
+            // Don't check non-defaulted params, dependent defaults or preds with multiple params.
+            if substituted_pred.references_error() || param_count.params.len() > 1 {
                 continue;
             }
-            // In trait defs, don't check `Self: Sized` when `Self` is the default.
-            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;
-                }
-            }
             // Avoid duplication of predicates that contain no parameters, for example.
             if !predicates.predicates.contains(&substituted_pred) {
                 substituted_predicates.push(substituted_pred);
index 60e78e29afd8bda3cac7b1c1e7dc2404d3218ba9..552665bac4e6c12afee08636b137109679ca0013 100644 (file)
@@ -14,12 +14,15 @@ trait Trait<T> {}
 trait Marker {}
 struct TwoParams<T, U>(T, U);
 impl Marker for TwoParams<i32, i32> {}
-// Check that defaults are substituted simultaneously.
+
+// Clauses with more than 1 param are not checked.
 struct IndividuallyBogus<T = i32, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Marker;
+struct BogusTogether<T = u32, U = i32>(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>: Marker;
 struct DefaultedLhs<U, V=i32>(U, V) where V: Trait<U>;
-// Dependent defaults.
-struct Dependent<T: Copy, U = T>(T, U) where U: Copy;
+// Dependent defaults are not checked.
+struct Dependent<T, U = T>(T, U) where U: Copy;
+trait SelfBound<T: Copy=Self> {}
 
 fn main() {}
index d57b39464806ac98ad97e8d02c8ff125c5ce4ac8..65560df9f21f24d97da995ef834d5a0f6d1ca18a 100644 (file)
 trait TraitBound<T:Copy=String> {}
 //~^ error: the trait bound `std::string::String: std::marker::Copy` is not satisfied [E0277]
 
-trait SelfBound<T:Copy=Self> {}
-//~^ error: the trait bound `Self: std::marker::Copy` is not satisfied [E0277]
-
 trait Super<T: Copy> { }
 trait Base<T = String>: Super<T> { }
 //~^ error: the trait bound `T: std::marker::Copy` 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 { }
-
-// Dependent defaults.
-struct Dependent<T, U = T>(T, U) where U: Copy;
-//~^ the trait bound `T: std::marker::Copy` is not satisfied [E0277]
+//~^ error:  cannot add `u8` to `i32` [E0277]
 
 fn main() { }
index 032e80cdb14855e200c2c7a4ef79bd6195a01d20..c73c0792f8e7c06a4aa52a97bdd0941d6703edb5 100644 (file)
@@ -25,13 +25,14 @@ note: required by `Foo`
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0277]: the trait bound `A: std::iter::Iterator` is not satisfied
-  --> $DIR/type-check-defaults.rs:21:1
+  --> $DIR/type-check-defaults.rs:21:32
    |
 21 | struct WellFormedProjection<A, T=<A as Iterator>::Item>(A, T);
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `A` is not an iterator; maybe try calling `.iter()` or a similar method
+   |                                ^ `A` is not an iterator; maybe try calling `.iter()` or a similar method
    |
    = help: the trait `std::iter::Iterator` is not implemented for `A`
    = help: consider adding a `where A: std::iter::Iterator` bound
+   = note: required by `std::iter::Iterator`
 
 error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not satisfied
   --> $DIR/type-check-defaults.rs:24:1
@@ -57,59 +58,27 @@ error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not sa
    |
    = note: required by `std::marker::Copy`
 
-error[E0277]: the trait bound `Self: std::marker::Copy` is not satisfied
-  --> $DIR/type-check-defaults.rs:33:1
-   |
-33 | trait SelfBound<T:Copy=Self> {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `Self`
-   |
-   = help: consider adding a `where Self: std::marker::Copy` bound
-   = note: required by `std::marker::Copy`
-
 error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
-  --> $DIR/type-check-defaults.rs:37:1
+  --> $DIR/type-check-defaults.rs:34:1
    |
-37 | trait Base<T = String>: Super<T> { }
+34 | trait Base<T = String>: Super<T> { }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
    |
    = help: consider adding a `where T: std::marker::Copy` bound
 note: required by `Super`
-  --> $DIR/type-check-defaults.rs:36:1
+  --> $DIR/type-check-defaults.rs:33:1
    |
-36 | trait Super<T: Copy> { }
+33 | trait Super<T: Copy> { }
    | ^^^^^^^^^^^^^^^^^^^^
 
-error[E0277]: the trait bound `i32: std::ops::Add<u8>` is not satisfied
-  --> $DIR/type-check-defaults.rs:40:1
+error[E0277]: cannot add `u8` to `i32`
+  --> $DIR/type-check-defaults.rs:37:1
    |
-40 | trait ProjectionPred<T:Iterator = IntoIter<i32>> where T::Item : Add<u8> {}
+37 | trait ProjectionPred<T:Iterator = IntoIter<i32>> where T::Item : Add<u8> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `i32 + u8`
    |
    = 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
-   |
-46 | trait Bar<V> {}
-   | ^^^^^^^^^^^^
-
-error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
-  --> $DIR/type-check-defaults.rs:51:1
-   |
-51 | struct Dependent<T, U = T>(T, U) where U: Copy;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
-   |
-   = help: consider adding a `where T: std::marker::Copy` bound
-   = note: required by `std::marker::Copy`
-
-error: aborting due to 11 previous errors
+error: aborting due to 8 previous errors