Skip to content

Commit ff5d11e

Browse files
Add comments and tests explaining the shallow substitution rule
1 parent 5e9317a commit ff5d11e

File tree

3 files changed

+73
-49
lines changed

3 files changed

+73
-49
lines changed

src/librustc_typeck/check/wfcheck.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,27 @@ fn check_associated_type_defaults(
461461
})
462462
.collect::<FxHashMap<_, _>>();
463463

464+
/// Replaces projections of associated types with their default types.
465+
///
466+
/// This does a "shallow substitution", meaning that defaults that refer to
467+
/// other defaulted assoc. types will still refer to the projection
468+
/// afterwards, not to the other default. For example:
469+
///
470+
/// ```compile_fail
471+
/// trait Tr {
472+
/// type A: Clone = Vec<Self::B>;
473+
/// type B = u8;
474+
/// }
475+
/// ```
476+
///
477+
/// This will end up replacing the bound `Self::A: Clone` with
478+
/// `Vec<Self::B>: Clone`, not with `Vec<u8>: Clone`. If we did a deep
479+
/// substitution and ended up with the latter, the trait would be accepted.
480+
/// If an `impl` then replaced `B` with something that isn't `Clone`,
481+
/// suddenly the default for `A` is no longer valid. The shallow
482+
/// substitution forces the trait to add a `B: Clone` bound to be accepted,
483+
/// which means that an `impl` can replace any default without breaking
484+
/// others.
464485
struct DefaultNormalizer<'tcx> {
465486
tcx: TyCtxt<'tcx>,
466487
map: FxHashMap<ty::ProjectionTy<'tcx>, Ty<'tcx>>,

src/test/ui/associated-types/defaults-suitability.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,28 +66,43 @@ trait D where
6666
type Assoc = NotClone;
6767
}
6868

69-
trait Foo2<T> where
70-
<Self as Foo2<T>>::Bar: Clone,
69+
// Test behavior of the check when defaults refer to other defaults:
70+
71+
// Shallow substitution rejects this trait since `Baz` isn't guaranteed to be
72+
// `Clone`.
73+
trait Foo2<T> {
74+
type Bar: Clone = Vec<Self::Baz>;
7175
//~^ ERROR the trait bound `<Self as Foo2<T>>::Baz: std::clone::Clone` is not satisfied
72-
{
73-
type Bar = Vec<Self::Baz>;
7476
type Baz = T;
7577
}
7678

77-
trait Foo3<T: Clone> where
78-
<Self as Foo3<T>>::Bar: Clone,
79-
//~^ ERROR the trait bound `<Self as Foo3<T>>::Baz: std::clone::Clone` is not satisfied
79+
// Adding a `T: Clone` bound doesn't help since the requirement doesn't see `T`
80+
// because of the shallow substitution. If we did a deep substitution instead,
81+
// this would be accepted.
82+
trait Foo25<T: Clone> {
83+
type Bar: Clone = Vec<Self::Baz>;
84+
//~^ ERROR the trait bound `<Self as Foo25<T>>::Baz: std::clone::Clone` is not satisfied
85+
type Baz = T;
86+
}
87+
88+
// Adding the `Baz: Clone` bound isn't enough since the default is type
89+
// parameter `T`, which also might not be `Clone`.
90+
trait Foo3<T> where
91+
Self::Bar: Clone,
92+
Self::Baz: Clone,
93+
//~^ ERROR the trait bound `T: std::clone::Clone` is not satisfied
8094
{
8195
type Bar = Vec<Self::Baz>;
8296
type Baz = T;
8397
}
8498

99+
// This one finally works, with `Clone` bounds on all assoc. types and the type
100+
// parameter.
85101
trait Foo4<T> where
86-
<Self as Foo4<T>>::Bar: Clone,
102+
T: Clone,
87103
{
88-
type Bar = Vec<Self::Baz>;
104+
type Bar: Clone = Vec<Self::Baz>;
89105
type Baz: Clone = T;
90-
//~^ ERROR the trait bound `T: std::clone::Clone` is not satisfied
91106
}
92107

93108
fn main() {}

src/test/ui/associated-types/defaults-suitability.stderr

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -104,61 +104,49 @@ LL | | }
104104
| |_^
105105

106106
error[E0277]: the trait bound `<Self as Foo2<T>>::Baz: std::clone::Clone` is not satisfied
107-
--> $DIR/defaults-suitability.rs:70:29
107+
--> $DIR/defaults-suitability.rs:74:15
108108
|
109-
LL | <Self as Foo2<T>>::Bar: Clone,
110-
| ^^^^^ the trait `std::clone::Clone` is not implemented for `<Self as Foo2<T>>::Baz`
109+
LL | type Bar: Clone = Vec<Self::Baz>;
110+
| ^^^^^ the trait `std::clone::Clone` is not implemented for `<Self as Foo2<T>>::Baz`
111111
|
112112
= help: consider adding a `where <Self as Foo2<T>>::Baz: std::clone::Clone` bound
113113
= note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<<Self as Foo2<T>>::Baz>`
114114
note: required by `Foo2`
115-
--> $DIR/defaults-suitability.rs:69:1
115+
--> $DIR/defaults-suitability.rs:73:1
116116
|
117-
LL | / trait Foo2<T> where
118-
LL | | <Self as Foo2<T>>::Bar: Clone,
119-
LL | |
120-
LL | | {
121-
LL | | type Bar = Vec<Self::Baz>;
122-
LL | | type Baz = T;
123-
LL | | }
124-
| |_^
117+
LL | trait Foo2<T> {
118+
| ^^^^^^^^^^^^^
125119

126-
error[E0277]: the trait bound `<Self as Foo3<T>>::Baz: std::clone::Clone` is not satisfied
127-
--> $DIR/defaults-suitability.rs:78:29
120+
error[E0277]: the trait bound `<Self as Foo25<T>>::Baz: std::clone::Clone` is not satisfied
121+
--> $DIR/defaults-suitability.rs:83:15
128122
|
129-
LL | <Self as Foo3<T>>::Bar: Clone,
130-
| ^^^^^ the trait `std::clone::Clone` is not implemented for `<Self as Foo3<T>>::Baz`
123+
LL | type Bar: Clone = Vec<Self::Baz>;
124+
| ^^^^^ the trait `std::clone::Clone` is not implemented for `<Self as Foo25<T>>::Baz`
131125
|
132-
= help: consider adding a `where <Self as Foo3<T>>::Baz: std::clone::Clone` bound
133-
= note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<<Self as Foo3<T>>::Baz>`
134-
note: required by `Foo3`
135-
--> $DIR/defaults-suitability.rs:77:1
126+
= help: consider adding a `where <Self as Foo25<T>>::Baz: std::clone::Clone` bound
127+
= note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<<Self as Foo25<T>>::Baz>`
128+
note: required by `Foo25`
129+
--> $DIR/defaults-suitability.rs:82:1
136130
|
137-
LL | / trait Foo3<T: Clone> where
138-
LL | | <Self as Foo3<T>>::Bar: Clone,
139-
LL | |
140-
LL | | {
141-
LL | | type Bar = Vec<Self::Baz>;
142-
LL | | type Baz = T;
143-
LL | | }
144-
| |_^
131+
LL | trait Foo25<T: Clone> {
132+
| ^^^^^^^^^^^^^^^^^^^^^
145133

146134
error[E0277]: the trait bound `T: std::clone::Clone` is not satisfied
147-
--> $DIR/defaults-suitability.rs:89:15
135+
--> $DIR/defaults-suitability.rs:92:16
148136
|
149-
LL | type Baz: Clone = T;
150-
| ^^^^^ the trait `std::clone::Clone` is not implemented for `T`
137+
LL | Self::Baz: Clone,
138+
| ^^^^^ the trait `std::clone::Clone` is not implemented for `T`
151139
|
152140
= help: consider adding a `where T: std::clone::Clone` bound
153-
note: required by `Foo4`
154-
--> $DIR/defaults-suitability.rs:85:1
155-
|
156-
LL | / trait Foo4<T> where
157-
LL | | <Self as Foo4<T>>::Bar: Clone,
158-
LL | | {
159-
LL | | type Bar = Vec<Self::Baz>;
160-
LL | | type Baz: Clone = T;
141+
note: required by `Foo3`
142+
--> $DIR/defaults-suitability.rs:90:1
143+
|
144+
LL | / trait Foo3<T> where
145+
LL | | Self::Bar: Clone,
146+
LL | | Self::Baz: Clone,
161147
LL | |
148+
... |
149+
LL | | type Baz = T;
162150
LL | | }
163151
| |_^
164152

0 commit comments

Comments
 (0)