Skip to content

Commit

Permalink
implement Waffle's cast semantics
Browse files Browse the repository at this point in the history
  • Loading branch information
Lukas Markeffsky committed Jan 24, 2024
1 parent 54e49e0 commit 5927b91
Show file tree
Hide file tree
Showing 19 changed files with 185 additions and 69 deletions.
4 changes: 3 additions & 1 deletion compiler/rustc_middle/src/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,13 @@ pub enum SelectionCandidate<'tcx> {
}

#[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)]
pub enum MetadataCastKind {
pub enum MetadataCastKind<'tcx> {
/// No further obligations.
Unconditional,
/// `T: MetadataCast<U>` if `T <: U`.
Subtype,
/// Require principals of dyn traits to be equal.
Dyn(ty::PolyExistentialTraitRef<'tcx>, ty::PolyExistentialTraitRef<'tcx>),
}

/// The result of trait evaluation. The order is important
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1760,7 +1760,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// does not compute the full elaborated super-predicates but just the set of def-ids. It is used
/// to identify which traits may define a given associated type to help avoid cycle errors.
/// Returns a `DefId` iterator.
fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx {
pub fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx {
let mut set = FxHashSet::default();
let mut stack = vec![trait_def_id];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1183,21 +1183,47 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return;
}

let tcx = self.tcx();
if let ty::Adt(src_def, src_args) = source.kind()
&& let ty::Adt(tgt_def, tgt_args) = target.kind()
&& src_def == tgt_def
&& Some(src_def.did()) == self.tcx().lang_items().dyn_metadata()
&& Some(src_def.did()) == tcx.lang_items().dyn_metadata()
{
let src_dyn = src_args.type_at(0);
let tgt_dyn = tgt_args.type_at(0);

// We could theoretically allow casting the principal away, but `as` casts
// don't allow that, so neither does `MetadataCast` for now.
if let ty::Dynamic(src_pred, _, ty::Dyn) = src_dyn.kind()
&& let ty::Dynamic(tgt_pred, _, ty::Dyn) = tgt_dyn.kind()
&& src_pred.principal_def_id() == tgt_pred.principal_def_id()
if let ty::Dynamic(src_preds, _, ty::Dyn) = src_dyn.kind()
&& let ty::Dynamic(tgt_preds, _, ty::Dyn) = tgt_dyn.kind()
{
candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Unconditional));
let mut src_traits = src_preds.auto_traits().collect::<Vec<_>>();

if let Some(src_principal_did) = src_preds.principal_def_id() {
src_traits.extend(tcx.super_traits_of(src_principal_did));
}

for tgt_auto_trait in tgt_preds.auto_traits() {
if !src_traits.contains(&tgt_auto_trait) {
// Adding auto traits that aren't on the source or implied by
// the source principal is not allowed.
return;
}
}

if let Some(src_principal) = src_preds.principal() {
if let Some(tgt_principal) = tgt_preds.principal() {
candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Dyn(
src_principal,
tgt_principal,
)));
} else {
// We could theoretically allow casting the principal away, but `as` casts
// don't allow that, so neither does `MetadataCast` for now.
}
} else if tgt_preds.principal().is_none() {
// Casting between auto-trait-only trait objects is allowed if the target
// traits are a subset of the source traits, which we checked above.
candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Unconditional));
}
return;
}
}
Expand Down
11 changes: 10 additions & 1 deletion compiler/rustc_trait_selection/src/traits/select/confirmation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_metadata_cast_candidate(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
kind: MetadataCastKind,
kind: MetadataCastKind<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
match kind {
MetadataCastKind::Unconditional => Ok(Vec::new()),
Expand All @@ -297,6 +297,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

Ok(obligations)
}
MetadataCastKind::Dyn(source, target) => {
let InferOk { obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
.eq(DefineOpaqueTypes::No, source, target)
.map_err(|_| Unimplemented)?;

Ok(obligations)
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1910,7 +1910,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| BuiltinUnsizeCandidate
| TraitUpcastingUnsizeCandidate(_)
| BuiltinCandidate { has_nested: true }
| MetadataCastCandidate(MetadataCastKind::Subtype)
| MetadataCastCandidate(MetadataCastKind::Subtype | MetadataCastKind::Dyn(..))
| TraitAliasCandidate,
ParamCandidate(ref victim_cand),
) => {
Expand Down Expand Up @@ -2073,7 +2073,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| BuiltinUnsizeCandidate
| TraitUpcastingUnsizeCandidate(_)
| BuiltinCandidate { has_nested: true }
| MetadataCastCandidate(MetadataCastKind::Subtype)
| MetadataCastCandidate(MetadataCastKind::Subtype | MetadataCastKind::Dyn(..))
| TraitAliasCandidate,
ImplCandidate(_)
| ClosureCandidate { .. }
Expand All @@ -2086,7 +2086,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| BuiltinUnsizeCandidate
| TraitUpcastingUnsizeCandidate(_)
| BuiltinCandidate { has_nested: true }
| MetadataCastCandidate(MetadataCastKind::Subtype)
| MetadataCastCandidate(MetadataCastKind::Subtype | MetadataCastKind::Dyn(..))
| TraitAliasCandidate,
) => DropVictim::No,
}
Expand Down
6 changes: 3 additions & 3 deletions library/alloc/src/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2192,7 +2192,7 @@ impl dyn Error + Send {
let err: Box<dyn Error> = self;
<dyn Error>::downcast(err).map_err(|s| unsafe {
// Reapply the `Send` marker.
Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send))
mem::transmute::<Box<dyn Error>, Box<dyn Error + Send>>(s)
})
}
}
Expand All @@ -2205,8 +2205,8 @@ impl dyn Error + Send + Sync {
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
let err: Box<dyn Error> = self;
<dyn Error>::downcast(err).map_err(|s| unsafe {
// Reapply the `Send + Sync` marker.
Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send + Sync))
// Reapply the `Send + Sync` markers.
mem::transmute::<Box<dyn Error>, Box<dyn Error + Send + Sync>>(s)
})
}
}
Expand Down
24 changes: 0 additions & 24 deletions tests/ui/cast/cast-rfc0401-vtable-kinds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,6 @@

#![feature(unsized_tuple_coercion)]

trait Foo<T> {
fn foo(&self, _: T) -> u32 { 42 }
}

trait Bar {
fn bar(&self) { println!("Bar!"); }
}

impl<T> Foo<T> for () {}
impl Foo<u32> for u32 { fn foo(&self, _: u32) -> u32 { self+43 } }
impl Bar for () {}

unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo<u32>+'a)) -> u32 {
let foo_e : *const dyn Foo<u16> = t as *const _;
let r_1 = foo_e as *mut dyn Foo<u32>;

(&*r_1).foo(0)
}

#[repr(C)]
struct FooS<T:?Sized>(T);
#[repr(C)]
Expand All @@ -38,11 +19,6 @@ fn tuple_i32_to_u32<T:?Sized>(u: *const (i32, T)) -> *const (u32, T) {


fn main() {
let x = 4u32;
let y : &dyn Foo<u32> = &x;
let fl = unsafe { round_trip_and_call(y as *const dyn Foo<u32>) };
assert_eq!(fl, (43+4));

let s = FooS([0,1,2]);
let u: &FooS<[u32]> = &s;
let u: *const FooS<[u32]> = u;
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/cast/fat-ptr-cast-borrowck.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
trait LifetimeParam<'a> {}
fn lifetime_param<'a, 'b>(ptr: *const dyn LifetimeParam<'a>) -> *const dyn LifetimeParam<'b> {
ptr as _
//~^ ERROR lifetime may not live long enough
//~| ERROR lifetime may not live long enough
}

fn main() {}
28 changes: 28 additions & 0 deletions tests/ui/cast/fat-ptr-cast-borrowck.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
error: lifetime may not live long enough
--> $DIR/fat-ptr-cast-borrowck.rs:3:5
|
LL | fn lifetime_param<'a, 'b>(ptr: *const dyn LifetimeParam<'a>) -> *const dyn LifetimeParam<'b> {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | ptr as _
| ^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`

error: lifetime may not live long enough
--> $DIR/fat-ptr-cast-borrowck.rs:3:5
|
LL | fn lifetime_param<'a, 'b>(ptr: *const dyn LifetimeParam<'a>) -> *const dyn LifetimeParam<'b> {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | ptr as _
| ^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`

help: `'b` and `'a` must be the same: replace one with the other

error: aborting due to 2 previous errors

8 changes: 4 additions & 4 deletions tests/ui/cast/fat-ptr-cast-rpass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#![feature(ptr_metadata)]

trait Foo {
trait Foo: Send {
fn foo(&self) {}
}

Expand Down Expand Up @@ -54,10 +54,10 @@ fn main() {
assert_eq!(b, d);
assert_eq!(c, d);

// Adding auto traits is OK.
// Adding auto traits is OK if they're implied by the principal.
let _ = a as *mut (dyn Foo + Send);

// Casting between auto-trait-only trait objects is OK.
let unprincipled: *mut dyn Send = &mut Bar;
// Removing traits from auto-trait-only trait objects is OK.
let unprincipled: *mut (dyn Send + Sync) = &mut Bar;
let _ = unprincipled as *mut dyn Sync;
}
13 changes: 13 additions & 0 deletions tests/ui/cast/fat-ptr-cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,17 @@ fn main() {
let _ = cf as *const [u16]; //~ ERROR is invalid
let _ = cf as *const dyn Bar; //~ ERROR is invalid

// Adding auto traits is not allowed.
let _ = cf as *const (dyn Foo + Send); //~ ERROR is invalid

// casting principal away is not allowed for now
let _ = cf as *const dyn Send; //~ ERROR is invalid

// Casting between auto-trait-only trait objects requires the target traits
// to be a subset of the source traits.
let unprincipled: *mut dyn Send = &mut ();
let _ = unprincipled as *mut dyn Sync; //~ ERROR is invalid

vec![0.0].iter().map(|s| s as f32).collect::<Vec<f32>>(); //~ ERROR is invalid
}

Expand All @@ -101,3 +109,8 @@ fn illegal_cast_2<U:?Sized>(u: *const U) -> *const str
{
u as *const str //~ ERROR is invalid
}

trait TypeParam<T> {}
fn type_param<T, U>(ptr: *const dyn TypeParam<T>) -> *const dyn TypeParam<U> {
ptr as _ //~ ERROR is invalid
}
36 changes: 30 additions & 6 deletions tests/ui/cast/fat-ptr-cast.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -278,14 +278,30 @@ LL | let _ = cf as *const dyn Bar;
|
= note: vtable kinds may not match

error[E0606]: casting `*const dyn Foo` as `*const dyn Send` is invalid
error[E0606]: casting `*const dyn Foo` as `*const dyn Foo + Send` is invalid
--> $DIR/fat-ptr-cast.rs:85:13
|
LL | let _ = cf as *const (dyn Foo + Send);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: vtable kinds may not match

error[E0606]: casting `*const dyn Foo` as `*const dyn Send` is invalid
--> $DIR/fat-ptr-cast.rs:88:13
|
LL | let _ = cf as *const dyn Send;
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: vtable kinds may not match

error[E0606]: casting `*mut dyn Send` as `*mut dyn Sync` is invalid
--> $DIR/fat-ptr-cast.rs:93:13
|
LL | let _ = unprincipled as *mut dyn Sync;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: vtable kinds may not match

error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/fat-ptr-cast.rs:66:13
|
Expand All @@ -305,7 +321,7 @@ LL | let _ = a as *const dyn Foo;
= note: required for the cast from `*const str` to `*const dyn Foo`

error[E0606]: casting `&{float}` as `f32` is invalid
--> $DIR/fat-ptr-cast.rs:87:30
--> $DIR/fat-ptr-cast.rs:95:30
|
LL | vec![0.0].iter().map(|s| s as f32).collect::<Vec<f32>>();
| ^^^^^^^^
Expand All @@ -316,30 +332,38 @@ LL | vec![0.0].iter().map(|s| *s as f32).collect::<Vec<f32>>();
| +

error[E0606]: cannot cast `usize` to a pointer that may be wide
--> $DIR/fat-ptr-cast.rs:91:18
--> $DIR/fat-ptr-cast.rs:99:18
|
LL | let s = 0 as *const T;
| - ^^^^^^^^ creating a `*const T` requires both an address and type-specific metadata
| |
| consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts`

error[E0606]: casting `*const U` as `*const V` is invalid
--> $DIR/fat-ptr-cast.rs:97:5
--> $DIR/fat-ptr-cast.rs:105:5
|
LL | u as *const V
| ^^^^^^^^^^^^^
|
= note: vtable kinds may not match

error[E0606]: casting `*const U` as `*const str` is invalid
--> $DIR/fat-ptr-cast.rs:102:5
--> $DIR/fat-ptr-cast.rs:110:5
|
LL | u as *const str
| ^^^^^^^^^^^^^^^
|
= note: vtable kinds may not match

error: aborting due to 46 previous errors
error[E0606]: casting `*const (dyn TypeParam<T> + 'static)` as `*const dyn TypeParam<U>` is invalid
--> $DIR/fat-ptr-cast.rs:115:5
|
LL | ptr as _
| ^^^^^^^^
|
= note: vtable kinds may not match

error: aborting due to 49 previous errors

Some errors have detailed explanations: E0054, E0277, E0604, E0605, E0606, E0607, E0609.
For more information about an error, try `rustc --explain E0054`.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ trait Trait {}

fn assert_send(ptr: *mut dyn Trait) -> *mut (dyn Trait + Send) {
//~^ ERROR incorrect parentheses around trait bounds
ptr as _
unsafe { std::mem::transmute(ptr) }
}

fn foo2(_: &(dyn Trait + Send)) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ trait Trait {}

fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) {
//~^ ERROR incorrect parentheses around trait bounds
ptr as _
unsafe { std::mem::transmute(ptr) }
}

fn foo2(_: &dyn (Trait + Send)) {}
Expand Down
1 change: 1 addition & 0 deletions tests/ui/traits/metadata-cast-fail-traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ fn main() {
check::<(), DynMetadata<dyn Trait>>(); //~ ERROR not satisfied
check::<usize, DynMetadata<dyn Trait>>(); //~ ERROR not satisfied
check::<DynMetadata<dyn Trait>, usize>(); //~ ERROR not satisfied
check::<DynMetadata<dyn Trait>, dyn Trait + Send>(); //~ ERROR not satisfied
check::<DynMetadata<dyn Trait>, DynMetadata<dyn Trait2>>(); //~ ERROR not satisfied
check::<dyn Trait + Send, dyn Trait + Sync>(); //~ ERROR not satisfied

Expand Down
Loading

0 comments on commit 5927b91

Please sign in to comment.