Skip to content

Commit 75997d8

Browse files
committed
Check WF of predicates with defaults only if all params have defaults
1 parent addc404 commit 75997d8

File tree

5 files changed

+64
-54
lines changed

5 files changed

+64
-54
lines changed

src/librustc/ty/mod.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,13 +1040,6 @@ impl<'a, 'gcx, 'tcx> Predicate<'tcx> {
10401040
Predicate::ConstEvaluatable(def_id, const_substs.subst(tcx, substs)),
10411041
}
10421042
}
1043-
1044-
pub fn as_poly_trait_predicate(&self) -> Option<&PolyTraitPredicate<'tcx>> {
1045-
match self {
1046-
Predicate::Trait(trait_pred) => Some(trait_pred),
1047-
_ => None
1048-
}
1049-
}
10501043
}
10511044

10521045
#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]

src/librustc_typeck/check/wfcheck.rs

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -392,14 +392,13 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
392392
// Check that trait predicates are WF when params are substituted by their defaults.
393393
// We don't want to overly constrain the predicates that may be written but we
394394
// want to catch obviously wrong cases such as `struct Foo<T: Copy = String>`
395-
// or cases that may cause backwards incompatibility such as a library going from
396-
// `pub struct Foo<T>` to `pub struct Foo<T, U = i32>` where U: Trait<T>`
397-
// which may break existing uses of Foo<T>.
398-
// Therefore the check we do is: If if all params appearing in the LHS of the predicate
399-
// have defaults then we verify that it is WF with all defaults substituted simultaneously.
395+
// or cases where defaults don't work together such as:
396+
// `struct Foo<T = i32, U = u8> where T: into<U>`
397+
// Therefore we check if a predicate in which all type params are defaulted
398+
// is WF with those defaults simultaneously substituted.
400399
// For more examples see tests `defaults-well-formedness.rs` and `type-check-defaults.rs`.
401400
//
402-
// First, we build the defaulted substitution.
401+
// First we build the defaulted substitution.
403402
let mut defaulted_params = Vec::new();
404403
let substs = ty::subst::Substs::for_item(fcx.tcx, def_id, |def, _| {
405404
// All regions are identity.
@@ -414,33 +413,35 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
414413
fcx.tcx.type_of(def.def_id)
415414
}
416415
});
417-
// In `trait Trait: Super`, checking `Self: Trait` or `Self: Super` is problematic.
418-
// We avoid those by skipping any predicates in trait declarations that contain `Self`,
419-
// which is excessive so we end up checking less than we could.
420-
for pred in predicates.predicates.iter()
421-
.filter_map(ty::Predicate::as_poly_trait_predicate)
422-
.filter(|p| !(is_trait && p.has_self_ty())) {
423-
let is_defaulted_param = |ty: ty::Ty| match ty.sty {
424-
ty::TyParam(p) => defaulted_params.contains(&p.idx),
425-
_ => false
426-
};
427-
// If there is a non-defaulted param in the LHS, don't check the substituted predicate.
428-
// `skip_binder()` is ok, we're only inspecting the type params.
429-
if !pred.skip_binder().self_ty().walk().all(is_defaulted_param) {
416+
let defaulted_params = &defaulted_params;
417+
// Now we build the substituted predicates.
418+
for &pred in predicates.predicates.iter() {
419+
struct HasNonDefaulted<'a> { defaulted_params: &'a Vec<u32> }
420+
impl<'tcx, 'a> ty::fold::TypeVisitor<'tcx> for HasNonDefaulted<'a> {
421+
fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
422+
match t.sty {
423+
ty::TyParam(p) => !self.defaulted_params.contains(&p.idx),
424+
_ => t.super_visit_with(self)
425+
}
426+
}
427+
}
428+
// If there is a non-defaulted param in the predicate, don't check it.
429+
if pred.visit_with(&mut HasNonDefaulted { defaulted_params }) {
430430
continue;
431431
}
432432
let substituted_pred = pred.subst(fcx.tcx, substs);
433-
// `skip_binder()` is ok, we're only inspecting for `has_self_ty()`.
434-
let substituted_lhs = substituted_pred.skip_binder().self_ty();
435433
// In trait defs, don't check `Self: Sized` when `Self` is the default.
436-
let pred_is_sized = Some(pred.def_id()) == fcx.tcx.lang_items().sized_trait();
437-
if is_trait && substituted_lhs.has_self_ty() && pred_is_sized {
438-
continue;
434+
if let ty::Predicate::Trait(trait_pred) = substituted_pred {
435+
// `skip_binder()` is ok, we're only inspecting for `has_self_ty()`.
436+
let lhs_is_self = trait_pred.skip_binder().self_ty().has_self_ty();
437+
let pred_sized = Some(trait_pred.def_id()) == fcx.tcx.lang_items().sized_trait();
438+
if is_trait && lhs_is_self && pred_sized {
439+
continue;
440+
}
439441
}
440-
let pred = ty::Predicate::Trait(pred.subst(fcx.tcx, substs));
441-
// Avoid duplicates.
442-
if !predicates.predicates.contains(&pred) {
443-
substituted_predicates.push(pred);
442+
// Avoid duplication of predicates that contain no parameters, for example.
443+
if !predicates.predicates.contains(&substituted_pred) {
444+
substituted_predicates.push(substituted_pred);
444445
}
445446
}
446447

src/test/run-pass/defaults-well-formedness.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
trait Trait<T> {}
1212
struct Foo<U, V=i32>(U, V) where U: Trait<V>;
1313

14-
trait Trait2 {}
14+
trait Marker {}
1515
struct TwoParams<T, U>(T, U);
16-
impl Trait2 for TwoParams<i32, i32> {}
16+
impl Marker for TwoParams<i32, i32> {}
1717
// Check that defaults are substituted simultaneously.
18-
struct IndividuallyBogus<T = i32, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Trait2;
18+
struct IndividuallyBogus<T = i32, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Marker;
1919
// Clauses with non-defaulted params are not checked.
20-
struct NonDefaultedInClause<T, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Trait2;
20+
struct NonDefaultedInClause<T, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Marker;
21+
struct DefaultedLhs<U, V=i32>(U, V) where V: Trait<U>;
22+
2123
fn main() {}

src/test/ui/type-check-defaults.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,14 @@ trait Super<T: Copy> { }
3737
trait Base<T = String>: Super<T> { }
3838
//~^ error: the trait bound `T: std::marker::Copy` is not satisfied [E0277]
3939

40-
trait Trait<T> {}
41-
struct DefaultedLhs<U, V=i32>(U, V) where V: Trait<U>;
42-
//~^ error: the trait bound `i32: Trait<U>` is not satisfied [E0277]
40+
trait ProjectionPred<T:Iterator = IntoIter<i32>> where T::Item : Add<u8> {}
41+
//~^ error: the trait bound `i32: std::ops::Add<u8>` is not satisfied [E0277]
42+
43+
// Defaults must work together.
44+
struct TwoParams<T = u32, U = i32>(T, U) where T: Bar<U>;
45+
//~^ the trait bound `u32: Bar<i32>` is not satisfied [E0277]
46+
trait Bar<V> {}
47+
impl Bar<String> for u32 { }
48+
impl Bar<i32> for String { }
4349

44-
// FIXME: Deal with projection predicates
45-
// trait ProjectionPred<T:Iterator = IntoIter<i32>> where T::Item : Add<u8> {}
46-
// ~^ error: the trait bound `i32: std::ops::Add<u8>` is not satisfied [E0277]
4750
fn main() { }

src/test/ui/type-check-defaults.stderr

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,28 @@ note: required by `Super`
7979
36 | trait Super<T: Copy> { }
8080
| ^^^^^^^^^^^^^^^^^^^^
8181

82-
error[E0277]: the trait bound `i32: Trait<U>` is not satisfied
83-
--> $DIR/type-check-defaults.rs:41:1
82+
error[E0277]: the trait bound `i32: std::ops::Add<u8>` is not satisfied
83+
--> $DIR/type-check-defaults.rs:40:1
8484
|
85-
41 | struct DefaultedLhs<U, V=i32>(U, V) where V: Trait<U>;
86-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait<U>` is not implemented for `i32`
85+
40 | trait ProjectionPred<T:Iterator = IntoIter<i32>> where T::Item : Add<u8> {}
86+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `i32 + u8`
8787
|
88-
note: required by `Trait`
89-
--> $DIR/type-check-defaults.rs:40:1
88+
= help: the trait `std::ops::Add<u8>` is not implemented for `i32`
89+
= note: required by `std::ops::Add`
90+
91+
error[E0277]: the trait bound `u32: Bar<i32>` is not satisfied
92+
--> $DIR/type-check-defaults.rs:44:1
93+
|
94+
44 | struct TwoParams<T = u32, U = i32>(T, U) where T: Bar<U>;
95+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar<i32>` is not implemented for `u32`
96+
|
97+
= help: the following implementations were found:
98+
<u32 as Bar<std::string::String>>
99+
note: required by `Bar`
100+
--> $DIR/type-check-defaults.rs:46:1
90101
|
91-
40 | trait Trait<T> {}
92-
| ^^^^^^^^^^^^^^
102+
46 | trait Bar<V> {}
103+
| ^^^^^^^^^^^^
93104

94-
error: aborting due to 9 previous errors
105+
error: aborting due to 10 previous errors
95106

0 commit comments

Comments
 (0)