Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix trait objects with a Self-containing projection values #56863

Merged
merged 2 commits into from
Dec 18, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/librustc/traits/object_safety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,26 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
// In the case of a trait predicate, we can skip the "self" type.
data.skip_binder().input_types().skip(1).any(|t| t.has_self_ty())
}
ty::Predicate::Projection(..) |
ty::Predicate::Projection(ref data) => {
// And similarly for projections. This should be redundant with
// the previous check because any projection should have a
// matching `Trait` predicate with the same inputs, but we do
// the check to be safe.
//
// Note that we *do* allow projection *outputs* to contain
// `self` (i.e., `trait Foo: Bar<Output=Self::Result> { type Result; }`),
// we just require the user to specify *both* outputs
// in the object type (i.e., `dyn Foo<Output=(), Result=()>`).
//
// This is ALT2 in issue #56288, see that for discussion of the
// possible alternatives.
data.skip_binder()
.projection_ty
.trait_ref(self)
.input_types()
.skip(1)
.any(|t| t.has_self_ty())
}
ty::Predicate::WellFormed(..) |
ty::Predicate::ObjectSafe(..) |
ty::Predicate::TypeOutlives(..) |
Expand Down
28 changes: 26 additions & 2 deletions src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1013,15 +1013,39 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
let mut associated_types = BTreeSet::default();

for tr in traits::elaborate_trait_ref(tcx, principal) {
debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", tr);
match tr {
ty::Predicate::Trait(pred) => {
associated_types.extend(tcx.associated_items(pred.def_id())
.filter(|item| item.kind == ty::AssociatedKind::Type)
.map(|item| item.def_id));
}
ty::Predicate::Projection(pred) => {
// Include projections defined on supertraits.
projection_bounds.push((pred, DUMMY_SP))
// A `Self` within the original bound will be substituted with a
// `TRAIT_OBJECT_DUMMY_SELF`, so check for that.
let references_self =
pred.skip_binder().ty.walk().any(|t| t == dummy_self);

// If the projection output contains `Self`, force the user to
// elaborate it explicitly to avoid a bunch of complexity.
//
// The "classicaly useful" case is the following:
// ```
// trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
// type MyOutput;
// }
// ```
//
// Here, the user could theoretically write `dyn MyTrait<Output=X>`,
// but actually supporting that would "expand" to an infinitely-long type
// `fix $ τ → dyn MyTrait<MyOutput=X, Output=<τ as MyTrait>::MyOutput`.
//
// Instead, we force the user to write `dyn MyTrait<MyOutput=X, Output=X>`,
// which is uglier but works. See the discussion in #56288 for alternatives.
if !references_self {
// Include projections defined on supertraits,
projection_bounds.push((pred, DUMMY_SP))
}
}
_ => ()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
trait Base {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a comment to this test:


Regression test for #56288. Checks that if a supertrait defines an associated type projection that references Self, then that associated type must still be explicitly specified in the dyn Trait variant, since we don't know what Self is anymore.

type Output;
}

trait Helper: Base<Output=<Self as Helper>::Target> {
type Target;
}

impl Base for u32
{
type Output = i32;
}

impl Helper for u32
{
type Target = i32;
}

fn main() {
let _x: Box<dyn Helper<Target=i32>> = Box::new(2u32);
//~^ ERROR the value of the associated type `Output` (from the trait `Base`) must be specified
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0191]: the value of the associated type `Output` (from the trait `Base`) must be specified
--> $DIR/trait-object-with-self-in-projection-output-bad.rs:20:17
|
LL | type Output;
| ------------ `Output` defined here
...
LL | let _x: Box<dyn Helper<Target=i32>> = Box::new(2u32);
| ^^^^^^^^^^^^^^^^^^^^^^ associated type `Output` must be specified

error: aborting due to previous error

For more information about this error, try `rustc --explain E0191`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// compile-pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly a comment here would be good:


Regression test related to #56288. Check that a supertrait projection (of Output) that references Self can be ok if it is referencing a projection (of Self::Target, in this case). Note that we still require the user to manually specify both Target and Output for now.


trait Base {
type Output;
}

trait Helper: Base<Output=<Self as Helper>::Target> {
type Target;
}

impl Base for u32
{
type Output = i32;
}

impl Helper for u32
{
type Target = i32;
}

fn main() {
let _x: Box<dyn Helper<Target=i32, Output=i32>> = Box::new(2u32);
}