diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 603d877cf53f..9f1ae37da1ad 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -461,6 +461,27 @@ fn check_associated_type_defaults( }) .collect::>(); + /// 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; + /// type B = u8; + /// } + /// ``` + /// + /// This will end up replacing the bound `Self::A: Clone` with + /// `Vec: Clone`, not with `Vec: 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<'tcx>>, diff --git a/src/test/ui/associated-types/defaults-suitability.rs b/src/test/ui/associated-types/defaults-suitability.rs index 6a8ea3b6d42f..5a128e56bcb1 100644 --- a/src/test/ui/associated-types/defaults-suitability.rs +++ b/src/test/ui/associated-types/defaults-suitability.rs @@ -66,28 +66,43 @@ trait D where type Assoc = NotClone; } -trait Foo2 where - >::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 { + type Bar: Clone = Vec; //~^ ERROR the trait bound `>::Baz: std::clone::Clone` is not satisfied -{ - type Bar = Vec; type Baz = T; } -trait Foo3 where - >::Bar: Clone, - //~^ ERROR the trait bound `>::Baz: std::clone::Clone` is not satisfied -{ - type Bar = Vec; +// 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 { + type Bar: Clone = Vec; + //~^ ERROR the trait bound `>::Baz: std::clone::Clone` is not satisfied type Baz = T; } -trait Foo4 where - >::Bar: Clone, -{ - type Bar = Vec; - 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 where + Self::Bar: Clone, + Self::Baz: Clone, //~^ ERROR the trait bound `T: std::clone::Clone` is not satisfied +{ + type Bar = Vec; + type Baz = T; +} + +// This one finally works, with `Clone` bounds on all assoc. types and the type +// parameter. +trait Foo4 where + T: Clone, +{ + type Bar: Clone = Vec; + type Baz: Clone = T; } fn main() {} diff --git a/src/test/ui/associated-types/defaults-suitability.stderr b/src/test/ui/associated-types/defaults-suitability.stderr index 67b31b414fa9..9a7febeb62de 100644 --- a/src/test/ui/associated-types/defaults-suitability.stderr +++ b/src/test/ui/associated-types/defaults-suitability.stderr @@ -104,61 +104,49 @@ LL | | } | |_^ error[E0277]: the trait bound `>::Baz: std::clone::Clone` is not satisfied - --> $DIR/defaults-suitability.rs:70:29 + --> $DIR/defaults-suitability.rs:74:15 | -LL | >::Bar: Clone, - | ^^^^^ the trait `std::clone::Clone` is not implemented for `>::Baz` +LL | type Bar: Clone = Vec; + | ^^^^^ the trait `std::clone::Clone` is not implemented for `>::Baz` | = help: consider adding a `where >::Baz: std::clone::Clone` bound = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<>::Baz>` note: required by `Foo2` - --> $DIR/defaults-suitability.rs:69:1 + --> $DIR/defaults-suitability.rs:73:1 | -LL | / trait Foo2 where -LL | | >::Bar: Clone, -LL | | -LL | | { -LL | | type Bar = Vec; -LL | | type Baz = T; -LL | | } - | |_^ +LL | trait Foo2 { + | ^^^^^^^^^^^^^ -error[E0277]: the trait bound `>::Baz: std::clone::Clone` is not satisfied - --> $DIR/defaults-suitability.rs:78:29 +error[E0277]: the trait bound `>::Baz: std::clone::Clone` is not satisfied + --> $DIR/defaults-suitability.rs:83:15 | -LL | >::Bar: Clone, - | ^^^^^ the trait `std::clone::Clone` is not implemented for `>::Baz` +LL | type Bar: Clone = Vec; + | ^^^^^ the trait `std::clone::Clone` is not implemented for `>::Baz` | - = help: consider adding a `where >::Baz: std::clone::Clone` bound - = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<>::Baz>` -note: required by `Foo3` - --> $DIR/defaults-suitability.rs:77:1 + = help: consider adding a `where >::Baz: std::clone::Clone` bound + = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<>::Baz>` +note: required by `Foo25` + --> $DIR/defaults-suitability.rs:82:1 | -LL | / trait Foo3 where -LL | | >::Bar: Clone, -LL | | -LL | | { -LL | | type Bar = Vec; -LL | | type Baz = T; -LL | | } - | |_^ +LL | trait Foo25 { + | ^^^^^^^^^^^^^^^^^^^^^ 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 where -LL | | >::Bar: Clone, -LL | | { -LL | | type Bar = Vec; -LL | | type Baz: Clone = T; +LL | / trait Foo3 where +LL | | Self::Bar: Clone, +LL | | Self::Baz: Clone, LL | | +... | +LL | | type Baz = T; LL | | } | |_^