Add comments and tests explaining the shallow substitution rule

This commit is contained in:
Jonas Schievink 2019-06-16 20:53:35 +02:00
parent 5e9317a023
commit ff5d11e043
3 changed files with 76 additions and 52 deletions

View File

@ -461,6 +461,27 @@ fn check_associated_type_defaults(
})
.collect::<FxHashMap<_, _>>();
/// Replaces projections of associated types with their default types.
///
/// This does a "shallow substitution", meaning that defaults that refer to
/// other defaulted assoc. types will still refer to the projection
/// afterwards, not to the other default. For example:
///
/// ```compile_fail
/// trait Tr {
/// type A: Clone = Vec<Self::B>;
/// type B = u8;
/// }
/// ```
///
/// This will end up replacing the bound `Self::A: Clone` with
/// `Vec<Self::B>: Clone`, not with `Vec<u8>: Clone`. If we did a deep
/// substitution and ended up with the latter, the trait would be accepted.
/// If an `impl` then replaced `B` with something that isn't `Clone`,
/// suddenly the default for `A` is no longer valid. The shallow
/// substitution forces the trait to add a `B: Clone` bound to be accepted,
/// which means that an `impl` can replace any default without breaking
/// others.
struct DefaultNormalizer<'tcx> {
tcx: TyCtxt<'tcx>,
map: FxHashMap<ty::ProjectionTy<'tcx>, Ty<'tcx>>,

View File

@ -66,28 +66,43 @@ trait D where
type Assoc = NotClone;
}
trait Foo2<T> where
<Self as Foo2<T>>::Bar: Clone,
// Test behavior of the check when defaults refer to other defaults:
// Shallow substitution rejects this trait since `Baz` isn't guaranteed to be
// `Clone`.
trait Foo2<T> {
type Bar: Clone = Vec<Self::Baz>;
//~^ ERROR the trait bound `<Self as Foo2<T>>::Baz: std::clone::Clone` is not satisfied
{
type Bar = Vec<Self::Baz>;
type Baz = T;
}
trait Foo3<T: Clone> where
<Self as Foo3<T>>::Bar: Clone,
//~^ ERROR the trait bound `<Self as Foo3<T>>::Baz: std::clone::Clone` is not satisfied
{
type Bar = Vec<Self::Baz>;
// Adding a `T: Clone` bound doesn't help since the requirement doesn't see `T`
// because of the shallow substitution. If we did a deep substitution instead,
// this would be accepted.
trait Foo25<T: Clone> {
type Bar: Clone = Vec<Self::Baz>;
//~^ ERROR the trait bound `<Self as Foo25<T>>::Baz: std::clone::Clone` is not satisfied
type Baz = T;
}
trait Foo4<T> where
<Self as Foo4<T>>::Bar: Clone,
{
type Bar = Vec<Self::Baz>;
type Baz: Clone = T;
// Adding the `Baz: Clone` bound isn't enough since the default is type
// parameter `T`, which also might not be `Clone`.
trait Foo3<T> where
Self::Bar: Clone,
Self::Baz: Clone,
//~^ ERROR the trait bound `T: std::clone::Clone` is not satisfied
{
type Bar = Vec<Self::Baz>;
type Baz = T;
}
// This one finally works, with `Clone` bounds on all assoc. types and the type
// parameter.
trait Foo4<T> where
T: Clone,
{
type Bar: Clone = Vec<Self::Baz>;
type Baz: Clone = T;
}
fn main() {}

View File

@ -104,61 +104,49 @@ LL | | }
| |_^
error[E0277]: the trait bound `<Self as Foo2<T>>::Baz: std::clone::Clone` is not satisfied
--> $DIR/defaults-suitability.rs:70:29
--> $DIR/defaults-suitability.rs:74:15
|
LL | <Self as Foo2<T>>::Bar: Clone,
| ^^^^^ the trait `std::clone::Clone` is not implemented for `<Self as Foo2<T>>::Baz`
LL | type Bar: Clone = Vec<Self::Baz>;
| ^^^^^ the trait `std::clone::Clone` is not implemented for `<Self as Foo2<T>>::Baz`
|
= help: consider adding a `where <Self as Foo2<T>>::Baz: std::clone::Clone` bound
= note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<<Self as Foo2<T>>::Baz>`
note: required by `Foo2`
--> $DIR/defaults-suitability.rs:69:1
--> $DIR/defaults-suitability.rs:73:1
|
LL | / trait Foo2<T> where
LL | | <Self as Foo2<T>>::Bar: Clone,
LL | |
LL | | {
LL | | type Bar = Vec<Self::Baz>;
LL | | type Baz = T;
LL | | }
| |_^
LL | trait Foo2<T> {
| ^^^^^^^^^^^^^
error[E0277]: the trait bound `<Self as Foo3<T>>::Baz: std::clone::Clone` is not satisfied
--> $DIR/defaults-suitability.rs:78:29
error[E0277]: the trait bound `<Self as Foo25<T>>::Baz: std::clone::Clone` is not satisfied
--> $DIR/defaults-suitability.rs:83:15
|
LL | <Self as Foo3<T>>::Bar: Clone,
| ^^^^^ the trait `std::clone::Clone` is not implemented for `<Self as Foo3<T>>::Baz`
LL | type Bar: Clone = Vec<Self::Baz>;
| ^^^^^ the trait `std::clone::Clone` is not implemented for `<Self as Foo25<T>>::Baz`
|
= help: consider adding a `where <Self as Foo3<T>>::Baz: std::clone::Clone` bound
= note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<<Self as Foo3<T>>::Baz>`
note: required by `Foo3`
--> $DIR/defaults-suitability.rs:77:1
= help: consider adding a `where <Self as Foo25<T>>::Baz: std::clone::Clone` bound
= note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<<Self as Foo25<T>>::Baz>`
note: required by `Foo25`
--> $DIR/defaults-suitability.rs:82:1
|
LL | / trait Foo3<T: Clone> where
LL | | <Self as Foo3<T>>::Bar: Clone,
LL | |
LL | | {
LL | | type Bar = Vec<Self::Baz>;
LL | | type Baz = T;
LL | | }
| |_^
LL | trait Foo25<T: Clone> {
| ^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `T: std::clone::Clone` is not satisfied
--> $DIR/defaults-suitability.rs:89:15
--> $DIR/defaults-suitability.rs:92:16
|
LL | type Baz: Clone = T;
| ^^^^^ the trait `std::clone::Clone` is not implemented for `T`
LL | Self::Baz: Clone,
| ^^^^^ the trait `std::clone::Clone` is not implemented for `T`
|
= help: consider adding a `where T: std::clone::Clone` bound
note: required by `Foo4`
--> $DIR/defaults-suitability.rs:85:1
note: required by `Foo3`
--> $DIR/defaults-suitability.rs:90:1
|
LL | / trait Foo4<T> where
LL | | <Self as Foo4<T>>::Bar: Clone,
LL | | {
LL | | type Bar = Vec<Self::Baz>;
LL | | type Baz: Clone = T;
LL | / trait Foo3<T> where
LL | | Self::Bar: Clone,
LL | | Self::Baz: Clone,
LL | |
... |
LL | | type Baz = T;
LL | | }
| |_^