Skip to content

Commit baf9f01

Browse files
committed
fix trait objects with a Self-having projection va
This follows ALT2 in the issue. Fixes #56288.
1 parent 0a1b226 commit baf9f01

5 files changed

+103
-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,22 @@
1+
trait Base {
2+
type Output;
3+
}
4+
5+
trait Helper: Base<Output=<Self as Helper>::Target> {
6+
type Target;
7+
}
8+
9+
impl Base for u32
10+
{
11+
type Output = i32;
12+
}
13+
14+
impl Helper for u32
15+
{
16+
type Target = i32;
17+
}
18+
19+
fn main() {
20+
let _x: Box<dyn Helper<Target=i32>> = Box::new(2u32);
21+
//~^ ERROR the value of the associated type `Output` (from the trait `Base`) must be specified
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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:20: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: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0191`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// compile-pass
2+
3+
trait Base {
4+
type Output;
5+
}
6+
7+
trait Helper: Base<Output=<Self as Helper>::Target> {
8+
type Target;
9+
}
10+
11+
impl Base for u32
12+
{
13+
type Output = i32;
14+
}
15+
16+
impl Helper for u32
17+
{
18+
type Target = i32;
19+
}
20+
21+
fn main() {
22+
let _x: Box<dyn Helper<Target=i32, Output=i32>> = Box::new(2u32);
23+
}

0 commit comments

Comments
 (0)