Skip to content

Commit d99a320

Browse files
committed
Auto merge of #56863 - arielb1:supertrait-self-4, r=nikomatsakis
fix trait objects with a Self-containing projection values Fixes #56288. This follows ALT2 in the issue. beta-nominating since this is a regression. r? @nikomatsakis
2 parents cb84844 + 1fd23f5 commit d99a320

6 files changed

+193
-3
lines changed

src/librustc/traits/object_safety.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,26 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
190190
// In the case of a trait predicate, we can skip the "self" type.
191191
data.skip_binder().input_types().skip(1).any(|t| t.has_self_ty())
192192
}
193-
ty::Predicate::Projection(..) |
193+
ty::Predicate::Projection(ref data) => {
194+
// And similarly for projections. This should be redundant with
195+
// the previous check because any projection should have a
196+
// matching `Trait` predicate with the same inputs, but we do
197+
// the check to be safe.
198+
//
199+
// Note that we *do* allow projection *outputs* to contain
200+
// `self` (i.e., `trait Foo: Bar<Output=Self::Result> { type Result; }`),
201+
// we just require the user to specify *both* outputs
202+
// in the object type (i.e., `dyn Foo<Output=(), Result=()>`).
203+
//
204+
// This is ALT2 in issue #56288, see that for discussion of the
205+
// possible alternatives.
206+
data.skip_binder()
207+
.projection_ty
208+
.trait_ref(self)
209+
.input_types()
210+
.skip(1)
211+
.any(|t| t.has_self_ty())
212+
}
194213
ty::Predicate::WellFormed(..) |
195214
ty::Predicate::ObjectSafe(..) |
196215
ty::Predicate::TypeOutlives(..) |

src/librustc_typeck/astconv.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -1013,15 +1013,39 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
10131013
let mut associated_types = BTreeSet::default();
10141014

10151015
for tr in traits::elaborate_trait_ref(tcx, principal) {
1016+
debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", tr);
10161017
match tr {
10171018
ty::Predicate::Trait(pred) => {
10181019
associated_types.extend(tcx.associated_items(pred.def_id())
10191020
.filter(|item| item.kind == ty::AssociatedKind::Type)
10201021
.map(|item| item.def_id));
10211022
}
10221023
ty::Predicate::Projection(pred) => {
1023-
// Include projections defined on supertraits.
1024-
projection_bounds.push((pred, DUMMY_SP))
1024+
// A `Self` within the original bound will be substituted with a
1025+
// `TRAIT_OBJECT_DUMMY_SELF`, so check for that.
1026+
let references_self =
1027+
pred.skip_binder().ty.walk().any(|t| t == dummy_self);
1028+
1029+
// If the projection output contains `Self`, force the user to
1030+
// elaborate it explicitly to avoid a bunch of complexity.
1031+
//
1032+
// The "classicaly useful" case is the following:
1033+
// ```
1034+
// trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
1035+
// type MyOutput;
1036+
// }
1037+
// ```
1038+
//
1039+
// Here, the user could theoretically write `dyn MyTrait<Output=X>`,
1040+
// but actually supporting that would "expand" to an infinitely-long type
1041+
// `fix $ τ → dyn MyTrait<MyOutput=X, Output=<τ as MyTrait>::MyOutput`.
1042+
//
1043+
// Instead, we force the user to write `dyn MyTrait<MyOutput=X, Output=X>`,
1044+
// which is uglier but works. See the discussion in #56288 for alternatives.
1045+
if !references_self {
1046+
// Include projections defined on supertraits,
1047+
projection_bounds.push((pred, DUMMY_SP))
1048+
}
10251049
}
10261050
_ => ()
10271051
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Regression test for #56288. Checks that if a supertrait defines an associated type
2+
// projection that references `Self`, then that associated type must still be explicitly
3+
// specified in the `dyn Trait` variant, since we don't know what `Self` is anymore.
4+
5+
trait Base {
6+
type Output;
7+
}
8+
9+
trait Helper: Base<Output=<Self as Helper>::Target> {
10+
type Target;
11+
}
12+
13+
impl Base for u32
14+
{
15+
type Output = i32;
16+
}
17+
18+
impl Helper for u32
19+
{
20+
type Target = i32;
21+
}
22+
23+
trait ConstI32 {
24+
type Out;
25+
}
26+
27+
impl<T: ?Sized> ConstI32 for T {
28+
type Out = i32;
29+
}
30+
31+
// Test that you still need to manually give a projection type if the Output type
32+
// is normalizable.
33+
trait NormalizableHelper:
34+
Base<Output=<Self as ConstI32>::Out>
35+
{
36+
type Target;
37+
}
38+
39+
impl NormalizableHelper for u32
40+
{
41+
type Target = i32;
42+
}
43+
44+
fn main() {
45+
let _x: Box<dyn Helper<Target=i32>> = Box::new(2u32);
46+
//~^ ERROR the value of the associated type `Output` (from the trait `Base`) must be specified
47+
48+
let _y: Box<dyn NormalizableHelper<Target=i32>> = Box::new(2u32);
49+
//~^ ERROR the value of the associated type `Output` (from the trait `Base`) must be specified
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0191]: the value of the associated type `Output` (from the trait `Base`) must be specified
2+
--> $DIR/trait-object-with-self-in-projection-output-bad.rs:45:17
3+
|
4+
LL | type Output;
5+
| ------------ `Output` defined here
6+
...
7+
LL | let _x: Box<dyn Helper<Target=i32>> = Box::new(2u32);
8+
| ^^^^^^^^^^^^^^^^^^^^^^ associated type `Output` must be specified
9+
10+
error[E0191]: the value of the associated type `Output` (from the trait `Base`) must be specified
11+
--> $DIR/trait-object-with-self-in-projection-output-bad.rs:48:17
12+
|
13+
LL | type Output;
14+
| ------------ `Output` defined here
15+
...
16+
LL | let _y: Box<dyn NormalizableHelper<Target=i32>> = Box::new(2u32);
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated type `Output` must be specified
18+
19+
error: aborting due to 2 previous errors
20+
21+
For more information about this error, try `rustc --explain E0191`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// compile-pass
2+
3+
// Regression test related to #56288. Check that a supertrait projection (of
4+
// `Output`) that references `Self` can be ok if it is referencing a projection (of
5+
// `Self::Target`, in this case). Note that we still require the user to manually
6+
// specify both `Target` and `Output` for now.
7+
8+
trait Base {
9+
type Output;
10+
}
11+
12+
trait Helper: Base<Output=<Self as Helper>::Target> {
13+
type Target;
14+
}
15+
16+
impl Base for u32
17+
{
18+
type Output = i32;
19+
}
20+
21+
impl Helper for u32
22+
{
23+
type Target = i32;
24+
}
25+
26+
fn main() {
27+
let _x: Box<dyn Helper<Target=i32, Output=i32>> = Box::new(2u32);
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// compile-pass
2+
3+
// Regression test related to #56288. Check that a supertrait projection (of
4+
// `Output`) that references `Self` is ok if there is another occurence of
5+
// the same supertrait that specifies the projection explicitly, even if
6+
// the projection's associated type is not explicitly specified in the object type.
7+
//
8+
// Note that in order for this to compile, we need the `Self`-referencing projection
9+
// to normalize fairly directly to a concrete type, otherwise the trait resolver
10+
// will hate us.
11+
//
12+
// There is a test in `trait-object-with-self-in-projection-output-bad.rs` that
13+
// having a normalizing, but `Self`-containing projection does not *by itself*
14+
// allow you to avoid writing the projected type (`Output`, in this example)
15+
// explicitly.
16+
17+
trait ConstI32 {
18+
type Out;
19+
}
20+
21+
impl<T: ?Sized> ConstI32 for T {
22+
type Out = i32;
23+
}
24+
25+
trait Base {
26+
type Output;
27+
}
28+
29+
trait NormalizingHelper: Base<Output=<Self as ConstI32>::Out> + Base<Output=i32> {
30+
type Target;
31+
}
32+
33+
impl Base for u32
34+
{
35+
type Output = i32;
36+
}
37+
38+
impl NormalizingHelper for u32
39+
{
40+
type Target = i32;
41+
}
42+
43+
fn main() {
44+
// Make sure this works both with and without the associated type
45+
// being specified.
46+
let _x: Box<dyn NormalizingHelper<Target=i32>> = Box::new(2u32);
47+
let _y: Box<dyn NormalizingHelper<Target=i32, Output=i32>> = Box::new(2u32);
48+
}

0 commit comments

Comments
 (0)