Skip to content

Commit 1a5a469

Browse files
author
Lukas Markeffsky
committed
implement Waffle's cast semantics
1 parent 54e49e0 commit 1a5a469

19 files changed

+185
-69
lines changed

compiler/rustc_middle/src/traits/select.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,13 @@ pub enum SelectionCandidate<'tcx> {
180180
}
181181

182182
#[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)]
183-
pub enum MetadataCastKind {
183+
pub enum MetadataCastKind<'tcx> {
184184
/// No further obligations.
185185
Unconditional,
186186
/// `T: MetadataCast<U>` if `T <: U`.
187187
Subtype,
188+
/// Require principals of dyn traits to be equal.
189+
Dyn(ty::PolyExistentialTraitRef<'tcx>, ty::PolyExistentialTraitRef<'tcx>),
188190
}
189191

190192
/// The result of trait evaluation. The order is important

compiler/rustc_middle/src/ty/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1760,7 +1760,7 @@ impl<'tcx> TyCtxt<'tcx> {
17601760
/// does not compute the full elaborated super-predicates but just the set of def-ids. It is used
17611761
/// to identify which traits may define a given associated type to help avoid cycle errors.
17621762
/// Returns a `DefId` iterator.
1763-
fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx {
1763+
pub fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx {
17641764
let mut set = FxHashSet::default();
17651765
let mut stack = vec![trait_def_id];
17661766

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+33-7
Original file line numberDiff line numberDiff line change
@@ -1183,21 +1183,47 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11831183
return;
11841184
}
11851185

1186+
let tcx = self.tcx();
11861187
if let ty::Adt(src_def, src_args) = source.kind()
11871188
&& let ty::Adt(tgt_def, tgt_args) = target.kind()
11881189
&& src_def == tgt_def
1189-
&& Some(src_def.did()) == self.tcx().lang_items().dyn_metadata()
1190+
&& Some(src_def.did()) == tcx.lang_items().dyn_metadata()
11901191
{
11911192
let src_dyn = src_args.type_at(0);
11921193
let tgt_dyn = tgt_args.type_at(0);
11931194

1194-
// We could theoretically allow casting the principal away, but `as` casts
1195-
// don't allow that, so neither does `MetadataCast` for now.
1196-
if let ty::Dynamic(src_pred, _, ty::Dyn) = src_dyn.kind()
1197-
&& let ty::Dynamic(tgt_pred, _, ty::Dyn) = tgt_dyn.kind()
1198-
&& src_pred.principal_def_id() == tgt_pred.principal_def_id()
1195+
if let ty::Dynamic(src_preds, _, ty::Dyn) = src_dyn.kind()
1196+
&& let ty::Dynamic(tgt_preds, _, ty::Dyn) = tgt_dyn.kind()
11991197
{
1200-
candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Unconditional));
1198+
let mut src_traits = src_preds.auto_traits().collect::<Vec<_>>();
1199+
1200+
if let Some(src_principal_did) = src_preds.principal_def_id() {
1201+
src_traits.extend(tcx.super_traits_of(src_principal_did));
1202+
}
1203+
1204+
for tgt_auto_trait in tgt_preds.auto_traits() {
1205+
if !src_traits.contains(&tgt_auto_trait) {
1206+
// Adding auto traits that aren't on the source or implied by
1207+
// the source principal is not allowed.
1208+
return;
1209+
}
1210+
}
1211+
1212+
if let Some(src_principal) = src_preds.principal() {
1213+
if let Some(tgt_principal) = tgt_preds.principal() {
1214+
candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Dyn(
1215+
src_principal,
1216+
tgt_principal,
1217+
)));
1218+
} else {
1219+
// We could theoretically allow casting the principal away, but `as` casts
1220+
// don't allow that, so neither does `MetadataCast` for now.
1221+
}
1222+
} else if tgt_preds.principal().is_none() {
1223+
// Casting between auto-trait-only trait objects is allowed if the target
1224+
// traits are a subset of the source traits, which we checked above.
1225+
candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Unconditional));
1226+
}
12011227
return;
12021228
}
12031229
}

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
281281
fn confirm_metadata_cast_candidate(
282282
&mut self,
283283
obligation: &PolyTraitObligation<'tcx>,
284-
kind: MetadataCastKind,
284+
kind: MetadataCastKind<'tcx>,
285285
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
286286
match kind {
287287
MetadataCastKind::Unconditional => Ok(Vec::new()),
@@ -297,6 +297,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
297297

298298
Ok(obligations)
299299
}
300+
MetadataCastKind::Dyn(source, target) => {
301+
let InferOk { obligations, .. } = self
302+
.infcx
303+
.at(&obligation.cause, obligation.param_env)
304+
.eq(DefineOpaqueTypes::No, source, target)
305+
.map_err(|_| Unimplemented)?;
306+
307+
Ok(obligations)
308+
}
300309
}
301310
}
302311

compiler/rustc_trait_selection/src/traits/select/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1910,7 +1910,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
19101910
| BuiltinUnsizeCandidate
19111911
| TraitUpcastingUnsizeCandidate(_)
19121912
| BuiltinCandidate { has_nested: true }
1913-
| MetadataCastCandidate(MetadataCastKind::Subtype)
1913+
| MetadataCastCandidate(MetadataCastKind::Subtype | MetadataCastKind::Dyn(..))
19141914
| TraitAliasCandidate,
19151915
ParamCandidate(ref victim_cand),
19161916
) => {
@@ -2073,7 +2073,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
20732073
| BuiltinUnsizeCandidate
20742074
| TraitUpcastingUnsizeCandidate(_)
20752075
| BuiltinCandidate { has_nested: true }
2076-
| MetadataCastCandidate(MetadataCastKind::Subtype)
2076+
| MetadataCastCandidate(MetadataCastKind::Subtype | MetadataCastKind::Dyn(..))
20772077
| TraitAliasCandidate,
20782078
ImplCandidate(_)
20792079
| ClosureCandidate { .. }
@@ -2086,7 +2086,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
20862086
| BuiltinUnsizeCandidate
20872087
| TraitUpcastingUnsizeCandidate(_)
20882088
| BuiltinCandidate { has_nested: true }
2089-
| MetadataCastCandidate(MetadataCastKind::Subtype)
2089+
| MetadataCastCandidate(MetadataCastKind::Subtype | MetadataCastKind::Dyn(..))
20902090
| TraitAliasCandidate,
20912091
) => DropVictim::No,
20922092
}

library/alloc/src/boxed.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -2192,7 +2192,7 @@ impl dyn Error + Send {
21922192
let err: Box<dyn Error> = self;
21932193
<dyn Error>::downcast(err).map_err(|s| unsafe {
21942194
// Reapply the `Send` marker.
2195-
Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send))
2195+
mem::transmute::<Box<dyn Error>, Box<dyn Error + Send>>(s)
21962196
})
21972197
}
21982198
}
@@ -2205,8 +2205,8 @@ impl dyn Error + Send + Sync {
22052205
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
22062206
let err: Box<dyn Error> = self;
22072207
<dyn Error>::downcast(err).map_err(|s| unsafe {
2208-
// Reapply the `Send + Sync` marker.
2209-
Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send + Sync))
2208+
// Reapply the `Send + Sync` markers.
2209+
mem::transmute::<Box<dyn Error>, Box<dyn Error + Send + Sync>>(s)
22102210
})
22112211
}
22122212
}

tests/ui/cast/cast-rfc0401-vtable-kinds.rs

-24
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,6 @@
44

55
#![feature(unsized_tuple_coercion)]
66

7-
trait Foo<T> {
8-
fn foo(&self, _: T) -> u32 { 42 }
9-
}
10-
11-
trait Bar {
12-
fn bar(&self) { println!("Bar!"); }
13-
}
14-
15-
impl<T> Foo<T> for () {}
16-
impl Foo<u32> for u32 { fn foo(&self, _: u32) -> u32 { self+43 } }
17-
impl Bar for () {}
18-
19-
unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo<u32>+'a)) -> u32 {
20-
let foo_e : *const dyn Foo<u16> = t as *const _;
21-
let r_1 = foo_e as *mut dyn Foo<u32>;
22-
23-
(&*r_1).foo(0)
24-
}
25-
267
#[repr(C)]
278
struct FooS<T:?Sized>(T);
289
#[repr(C)]
@@ -38,11 +19,6 @@ fn tuple_i32_to_u32<T:?Sized>(u: *const (i32, T)) -> *const (u32, T) {
3819

3920

4021
fn main() {
41-
let x = 4u32;
42-
let y : &dyn Foo<u32> = &x;
43-
let fl = unsafe { round_trip_and_call(y as *const dyn Foo<u32>) };
44-
assert_eq!(fl, (43+4));
45-
4622
let s = FooS([0,1,2]);
4723
let u: &FooS<[u32]> = &s;
4824
let u: *const FooS<[u32]> = u;
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
trait LifetimeParam<'a> {}
2+
fn lifetime_param<'a, 'b>(ptr: *const dyn LifetimeParam<'a>) -> *const dyn LifetimeParam<'b> {
3+
ptr as _
4+
//~^ ERROR lifetime may not live long enough
5+
//~| ERROR lifetime may not live long enough
6+
}
7+
8+
fn main() {}
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/fat-ptr-cast-borrowck.rs:3:5
3+
|
4+
LL | fn lifetime_param<'a, 'b>(ptr: *const dyn LifetimeParam<'a>) -> *const dyn LifetimeParam<'b> {
5+
| -- -- lifetime `'b` defined here
6+
| |
7+
| lifetime `'a` defined here
8+
LL | ptr as _
9+
| ^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
10+
|
11+
= help: consider adding the following bound: `'b: 'a`
12+
13+
error: lifetime may not live long enough
14+
--> $DIR/fat-ptr-cast-borrowck.rs:3:5
15+
|
16+
LL | fn lifetime_param<'a, 'b>(ptr: *const dyn LifetimeParam<'a>) -> *const dyn LifetimeParam<'b> {
17+
| -- -- lifetime `'b` defined here
18+
| |
19+
| lifetime `'a` defined here
20+
LL | ptr as _
21+
| ^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
22+
|
23+
= help: consider adding the following bound: `'a: 'b`
24+
25+
help: `'b` and `'a` must be the same: replace one with the other
26+
27+
error: aborting due to 2 previous errors
28+

tests/ui/cast/fat-ptr-cast-rpass.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
#![feature(ptr_metadata)]
44

5-
trait Foo {
5+
trait Foo: Send {
66
fn foo(&self) {}
77
}
88

@@ -54,10 +54,10 @@ fn main() {
5454
assert_eq!(b, d);
5555
assert_eq!(c, d);
5656

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

60-
// Casting between auto-trait-only trait objects is OK.
61-
let unprincipled: *mut dyn Send = &mut Bar;
60+
// Removing traits from auto-trait-only trait objects is OK.
61+
let unprincipled: *mut (dyn Send + Sync) = &mut Bar;
6262
let _ = unprincipled as *mut dyn Sync;
6363
}

tests/ui/cast/fat-ptr-cast.rs

+13
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,17 @@ fn main() {
8181
let _ = cf as *const [u16]; //~ ERROR is invalid
8282
let _ = cf as *const dyn Bar; //~ ERROR is invalid
8383

84+
// Adding auto traits is not allowed.
85+
let _ = cf as *mut (dyn Foo + Send); //~ ERROR is invalid
86+
8487
// casting principal away is not allowed for now
8588
let _ = cf as *const dyn Send; //~ ERROR is invalid
8689

90+
// Casting between auto-trait-only trait objects requires the target traits
91+
// to be a subset of the source traits.
92+
let unprincipled: *mut dyn Send = &mut ();
93+
let _ = unprincipled as *mut dyn Sync; //~ ERROR is invalid
94+
8795
vec![0.0].iter().map(|s| s as f32).collect::<Vec<f32>>(); //~ ERROR is invalid
8896
}
8997

@@ -101,3 +109,8 @@ fn illegal_cast_2<U:?Sized>(u: *const U) -> *const str
101109
{
102110
u as *const str //~ ERROR is invalid
103111
}
112+
113+
trait TypeParam<T> {}
114+
fn type_param<T, U>(ptr: *const dyn TypeParam<T>) -> *const dyn TypeParam<U> {
115+
ptr as _ //~ ERROR is invalid
116+
}

tests/ui/cast/fat-ptr-cast.stderr

+30-6
Original file line numberDiff line numberDiff line change
@@ -278,14 +278,30 @@ LL | let _ = cf as *const dyn Bar;
278278
|
279279
= note: vtable kinds may not match
280280

281-
error[E0606]: casting `*const dyn Foo` as `*const dyn Send` is invalid
281+
error[E0606]: casting `*const dyn Foo` as `*mut dyn Foo + Send` is invalid
282282
--> $DIR/fat-ptr-cast.rs:85:13
283283
|
284+
LL | let _ = cf as *mut (dyn Foo + Send);
285+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
286+
|
287+
= note: vtable kinds may not match
288+
289+
error[E0606]: casting `*const dyn Foo` as `*const dyn Send` is invalid
290+
--> $DIR/fat-ptr-cast.rs:88:13
291+
|
284292
LL | let _ = cf as *const dyn Send;
285293
| ^^^^^^^^^^^^^^^^^^^^^
286294
|
287295
= note: vtable kinds may not match
288296

297+
error[E0606]: casting `*mut dyn Send` as `*mut dyn Sync` is invalid
298+
--> $DIR/fat-ptr-cast.rs:93:13
299+
|
300+
LL | let _ = unprincipled as *mut dyn Sync;
301+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
302+
|
303+
= note: vtable kinds may not match
304+
289305
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
290306
--> $DIR/fat-ptr-cast.rs:66:13
291307
|
@@ -305,7 +321,7 @@ LL | let _ = a as *const dyn Foo;
305321
= note: required for the cast from `*const str` to `*const dyn Foo`
306322

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

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

326342
error[E0606]: casting `*const U` as `*const V` is invalid
327-
--> $DIR/fat-ptr-cast.rs:97:5
343+
--> $DIR/fat-ptr-cast.rs:105:5
328344
|
329345
LL | u as *const V
330346
| ^^^^^^^^^^^^^
331347
|
332348
= note: vtable kinds may not match
333349

334350
error[E0606]: casting `*const U` as `*const str` is invalid
335-
--> $DIR/fat-ptr-cast.rs:102:5
351+
--> $DIR/fat-ptr-cast.rs:110:5
336352
|
337353
LL | u as *const str
338354
| ^^^^^^^^^^^^^^^
339355
|
340356
= note: vtable kinds may not match
341357

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

344368
Some errors have detailed explanations: E0054, E0277, E0604, E0605, E0606, E0607, E0609.
345369
For more information about an error, try `rustc --explain E0054`.

tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ trait Trait {}
55

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

1111
fn foo2(_: &(dyn Trait + Send)) {}

tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ trait Trait {}
55

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

1111
fn foo2(_: &dyn (Trait + Send)) {}

tests/ui/traits/metadata-cast-fail-traits.rs

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ fn main() {
2323
check::<(), DynMetadata<dyn Trait>>(); //~ ERROR not satisfied
2424
check::<usize, DynMetadata<dyn Trait>>(); //~ ERROR not satisfied
2525
check::<DynMetadata<dyn Trait>, usize>(); //~ ERROR not satisfied
26+
check::<DynMetadata<dyn Trait>, dyn Trait + Send>(); //~ ERROR not satisfied
2627
check::<DynMetadata<dyn Trait>, DynMetadata<dyn Trait2>>(); //~ ERROR not satisfied
2728
check::<dyn Trait + Send, dyn Trait + Sync>(); //~ ERROR not satisfied
2829

0 commit comments

Comments
 (0)