Skip to content

Commit

Permalink
Detect trait upcasting through struct tail unsizing
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Jul 29, 2023
1 parent b6dd153 commit 752e6e1
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 5 deletions.
25 changes: 20 additions & 5 deletions compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,18 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
rematch_impl(self, goal, def_id, nested_obligations)
}

// If an unsize goal is ambiguous, then we can manually rematch it to make
// selection progress for coercion during HIR typeck. If it is *not* ambiguous,
// but is `BuiltinImplSource::Misc`, it may have nested `Unsize` goals,
// and we need to rematch those to detect tuple unsizing and trait upcasting.
// FIXME: This will be wrong if we have param-env or where-clause bounds
// with the unsize goal -- we may need to mark those with different impl
// sources.
(Certainty::Maybe(_), CandidateSource::BuiltinImpl(src))
| (Certainty::Yes, CandidateSource::BuiltinImpl(src @ BuiltinImplSource::Misc))
if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
{
rematch_unsize(self, goal, nested_obligations, src)
rematch_unsize(self, goal, nested_obligations, src, certainty)
}

// Technically some builtin impls have nested obligations, but if
Expand Down Expand Up @@ -217,6 +225,7 @@ fn rematch_unsize<'tcx>(
goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
mut nested: Vec<PredicateObligation<'tcx>>,
source: BuiltinImplSource,
certainty: Certainty,
) -> SelectionResult<'tcx, Selection<'tcx>> {
let tcx = infcx.tcx;
let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
Expand All @@ -227,6 +236,12 @@ fn rematch_unsize<'tcx>(
&mut nested,
);
match (a_ty.kind(), b_ty.kind()) {
// Stall any ambiguous upcasting goals, since we can't rematch those
(ty::Dynamic(_, _, ty::Dyn), ty::Dynamic(_, _, ty::Dyn)) => match certainty {
Certainty::Yes => Ok(Some(ImplSource::Builtin(source, nested))),
_ => Ok(None),
},
// `T` -> `dyn Trait` upcasting
(_, &ty::Dynamic(data, region, ty::Dyn)) => {
// Check that the type implements all of the predicates of the def-id.
// (i.e. the principal, all of the associated types match, and any auto traits)
Expand Down Expand Up @@ -354,10 +369,10 @@ fn rematch_unsize<'tcx>(
);
Ok(Some(ImplSource::Builtin(source, nested)))
}
// FIXME: We *could* ICE here if either:
// 1. the certainty is `Certainty::Yes`,
// 2. we're in codegen (which should mean `Certainty::Yes`).
_ => Ok(None),
_ => {
assert_ne!(certainty, Certainty::Yes);
Ok(None)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error[E0658]: cannot cast `dyn A` to `dyn B`, trait upcasting coercion is experimental
--> $DIR/upcast-through-struct-tail.rs:10:5
|
LL | x
| ^
|
= note: see issue #65991 <https://github.com/rust-lang/rust/issues/65991> for more information
= help: add `#![feature(trait_upcasting)]` to the crate attributes to enable
= note: required when coercing `Box<Wrapper<(dyn A + 'a)>>` into `Box<Wrapper<(dyn B + 'a)>>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0658`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error[E0658]: cannot cast `dyn A` to `dyn B`, trait upcasting coercion is experimental
--> $DIR/upcast-through-struct-tail.rs:10:5
|
LL | x
| ^
|
= note: see issue #65991 <https://github.com/rust-lang/rust/issues/65991> for more information
= help: add `#![feature(trait_upcasting)]` to the crate attributes to enable
= note: required when coercing `Box<Wrapper<(dyn A + 'a)>>` into `Box<Wrapper<(dyn B + 'a)>>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0658`.
14 changes: 14 additions & 0 deletions tests/ui/traits/trait-upcasting/upcast-through-struct-tail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// revisions: current next
//[next] compile-flags: -Ztrait-solver=next

struct Wrapper<T: ?Sized>(T);

trait A: B {}
trait B {}

fn test<'a>(x: Box<Wrapper<dyn A + 'a>>) -> Box<Wrapper<dyn B + 'a>> {
x
//~^ ERROR cannot cast `dyn A` to `dyn B`, trait upcasting coercion is experimental
}

fn main() {}

0 comments on commit 752e6e1

Please sign in to comment.