Skip to content

Commit addc404

Browse files
committed
Check WF of predicate with defaults only if all in LHS have default
Given a trait predicate, if all params appearing in the LHS have defaults then it should be a backwards compatible predicate. We verify that by checking the WF of predicate with all defaults substituted simultaneously.
1 parent 35499aa commit addc404

File tree

5 files changed

+99
-110
lines changed

5 files changed

+99
-110
lines changed

src/librustc/ty/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,13 @@ 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+
}
10431050
}
10441051

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

src/librustc_typeck/check/wfcheck.rs

Lines changed: 61 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -378,67 +378,69 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
378378
let mut substituted_predicates = Vec::new();
379379

380380
let generics = self.tcx.generics_of(def_id);
381-
let defaulted_params = generics.types.iter()
382-
.filter(|def| def.has_default &&
383-
def.index >= generics.parent_count() as u32);
384-
// WF checks for type parameter defaults. See test `type-check-defaults.rs` for examples.
385-
for param_def in defaulted_params {
386-
// This parameter has a default value. Check that this default value is well-formed.
387-
// For example this forbids the declaration:
388-
// struct Foo<T = Vec<[u32]>> { .. }
389-
// Here `Vec<[u32]>` is not WF because `[u32]: Sized` does not hold.
390-
let d = param_def.def_id;
381+
let is_our_default = |def: &ty::TypeParameterDef|
382+
def.has_default && def.index >= generics.parent_count() as u32;
383+
let defaulted_params = generics.types.iter().cloned().filter(&is_our_default);
384+
// Check that defaults are well-formed. See test `type-check-defaults.rs`.
385+
// For example this forbids the declaration:
386+
// struct Foo<T = Vec<[u32]>> { .. }
387+
// Here the default `Vec<[u32]>` is not WF because `[u32]: Sized` does not hold.
388+
for d in defaulted_params.map(|p| p.def_id) {
391389
fcx.register_wf_obligation(fcx.tcx.type_of(d), fcx.tcx.def_span(d), self.code.clone());
390+
}
392391

393-
// Check the clauses are well-formed when the param is substituted by it's default.
394-
// For example this forbids the following declaration because `String` is not `Copy`:
395-
// struct Foo<T: Copy = String> { .. }
396-
//
397-
// In `trait Trait: Super`, checking `Self: Trait` or `Self: Super` is problematic.
398-
// Therefore we skip such predicates. This means we check less than we could.
399-
for pred in predicates.predicates.iter().filter(|p| !(is_trait && p.has_self_ty())) {
400-
let mut skip = true;
401-
let substs = ty::subst::Substs::for_item(fcx.tcx, def_id, |def, _| {
402-
// All regions are identity.
403-
fcx.tcx.mk_region(ty::ReEarlyBound(def.to_early_bound_region_data()))
404-
}, |def, _| {
405-
let identity_ty = fcx.tcx.mk_param_from_def(def);
406-
if def.index != param_def.index {
407-
identity_ty
408-
} else {
409-
let sized = fcx.tcx.lang_items().sized_trait();
410-
let pred_is_sized = match pred {
411-
ty::Predicate::Trait(p) => Some(p.def_id()) == sized,
412-
_ => false,
413-
};
414-
let default_ty = fcx.tcx.type_of(def.def_id);
415-
let default_is_self = match default_ty.sty {
416-
ty::TyParam(ref p) => p.is_self(),
417-
_ => false
418-
};
419-
// In trait defs, skip `Self: Sized` when `Self` is the default.
420-
if is_trait && pred_is_sized && default_is_self {
421-
identity_ty
422-
} else {
423-
skip = false;
424-
default_ty
425-
}
426-
}
427-
});
428-
if skip { continue; }
429-
substituted_predicates.push(match pred {
430-
// In trait predicates, substitute defaults only for the LHS.
431-
// See test `defaults-well-formedness.rs` for why substituting the RHS is bad.
432-
ty::Predicate::Trait(t_pred) => {
433-
let trait_ref = t_pred.map_bound(|t_pred| {
434-
let mut trait_subs = t_pred.trait_ref.substs.to_vec();
435-
trait_subs[0] = t_pred.self_ty().subst(fcx.tcx, substs).into();
436-
ty::TraitRef::new(t_pred.def_id(), fcx.tcx.intern_substs(&trait_subs))
437-
});
438-
ty::Predicate::Trait(trait_ref.to_poly_trait_predicate())
439-
}
440-
_ => pred.subst(fcx.tcx, substs)
441-
});
392+
// Check that trait predicates are WF when params are substituted by their defaults.
393+
// We don't want to overly constrain the predicates that may be written but we
394+
// 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.
400+
// For more examples see tests `defaults-well-formedness.rs` and `type-check-defaults.rs`.
401+
//
402+
// First, we build the defaulted substitution.
403+
let mut defaulted_params = Vec::new();
404+
let substs = ty::subst::Substs::for_item(fcx.tcx, def_id, |def, _| {
405+
// All regions are identity.
406+
fcx.tcx.mk_region(ty::ReEarlyBound(def.to_early_bound_region_data()))
407+
}, |def, _| {
408+
if !is_our_default(def) {
409+
// Identity substitution.
410+
fcx.tcx.mk_param_from_def(def)
411+
} else {
412+
// Substitute with default.
413+
defaulted_params.push(def.index);
414+
fcx.tcx.type_of(def.def_id)
415+
}
416+
});
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) {
430+
continue;
431+
}
432+
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();
435+
// 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;
439+
}
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);
442444
}
443445
}
444446

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

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

14+
trait Trait2 {}
15+
struct TwoParams<T, U>(T, U);
16+
impl Trait2 for TwoParams<i32, i32> {}
17+
// Check that defaults are substituted simultaneously.
18+
struct IndividuallyBogus<T = i32, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Trait2;
19+
// Clauses with non-defaulted params are not checked.
20+
struct NonDefaultedInClause<T, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Trait2;
1421
fn main() {}

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

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,15 @@ trait TraitBound<T:Copy=String> {}
3333
trait SelfBound<T:Copy=Self> {}
3434
//~^ error: the trait bound `Self: std::marker::Copy` is not satisfied [E0277]
3535

36-
trait FooTrait<T:Iterator = IntoIter<i32>> where T::Item : Add<u8> {}
37-
//~^ error: the trait bound `i32: std::ops::Add<u8>` is not satisfied [E0277]
38-
39-
trait Trait {}
40-
struct TwoParams<T, U>(T, U);
41-
impl Trait for TwoParams<i32, i32> {}
42-
// Check that each default is substituted individually in the clauses.
43-
struct Bogus<T = i32, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Trait;
44-
//~^ error: the trait bound `TwoParams<i32, U>: Trait` is not satisfied [E0277]
45-
//~^^ error: the trait bound `TwoParams<T, i32>: Trait` is not satisfied [E0277]
46-
4736
trait Super<T: Copy> { }
4837
trait Base<T = String>: Super<T> { }
4938
//~^ error: the trait bound `T: std::marker::Copy` is not satisfied [E0277]
5039

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]
43+
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]
5147
fn main() { }

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

Lines changed: 17 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -66,53 +66,30 @@ error[E0277]: the trait bound `Self: std::marker::Copy` is not satisfied
6666
= help: consider adding a `where Self: std::marker::Copy` bound
6767
= note: required by `std::marker::Copy`
6868

69-
error[E0277]: the trait bound `i32: std::ops::Add<u8>` is not satisfied
70-
--> $DIR/type-check-defaults.rs:36:1
71-
|
72-
36 | trait FooTrait<T:Iterator = IntoIter<i32>> where T::Item : Add<u8> {}
73-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `i32 + u8`
74-
|
75-
= help: the trait `std::ops::Add<u8>` is not implemented for `i32`
76-
= note: required by `std::ops::Add`
77-
78-
error[E0277]: the trait bound `TwoParams<i32, U>: Trait` is not satisfied
79-
--> $DIR/type-check-defaults.rs:43:1
80-
|
81-
43 | struct Bogus<T = i32, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Trait;
82-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `TwoParams<i32, U>`
83-
|
84-
= help: consider adding a `where TwoParams<i32, U>: Trait` bound
85-
note: required by `Trait`
86-
--> $DIR/type-check-defaults.rs:39:1
87-
|
88-
39 | trait Trait {}
89-
| ^^^^^^^^^^^
90-
91-
error[E0277]: the trait bound `TwoParams<T, i32>: Trait` is not satisfied
92-
--> $DIR/type-check-defaults.rs:43:1
93-
|
94-
43 | struct Bogus<T = i32, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Trait;
95-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `TwoParams<T, i32>`
96-
|
97-
= help: consider adding a `where TwoParams<T, i32>: Trait` bound
98-
note: required by `Trait`
99-
--> $DIR/type-check-defaults.rs:39:1
100-
|
101-
39 | trait Trait {}
102-
| ^^^^^^^^^^^
103-
10469
error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
105-
--> $DIR/type-check-defaults.rs:48:1
70+
--> $DIR/type-check-defaults.rs:37:1
10671
|
107-
48 | trait Base<T = String>: Super<T> { }
72+
37 | trait Base<T = String>: Super<T> { }
10873
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
10974
|
11075
= help: consider adding a `where T: std::marker::Copy` bound
11176
note: required by `Super`
112-
--> $DIR/type-check-defaults.rs:47:1
77+
--> $DIR/type-check-defaults.rs:36:1
11378
|
114-
47 | trait Super<T: Copy> { }
79+
36 | trait Super<T: Copy> { }
11580
| ^^^^^^^^^^^^^^^^^^^^
11681

117-
error: aborting due to 11 previous errors
82+
error[E0277]: the trait bound `i32: Trait<U>` is not satisfied
83+
--> $DIR/type-check-defaults.rs:41:1
84+
|
85+
41 | struct DefaultedLhs<U, V=i32>(U, V) where V: Trait<U>;
86+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait<U>` is not implemented for `i32`
87+
|
88+
note: required by `Trait`
89+
--> $DIR/type-check-defaults.rs:40:1
90+
|
91+
40 | trait Trait<T> {}
92+
| ^^^^^^^^^^^^^^
93+
94+
error: aborting due to 9 previous errors
11895

0 commit comments

Comments
 (0)