Skip to content

Commit 0f83ac1

Browse files
committed
Auto merge of rust-lang#87515 - crlf0710:trait_upcasting_part2, r=bjorn3
Trait upcasting coercion (part2) This is the second part of trait upcasting coercion implementation. Currently this is blocked on rust-lang#86264 . The third part might be implemented using unsafety checking r? `@bjorn3`
2 parents 966e54e + 6f68223 commit 0f83ac1

File tree

2 files changed

+49
-16
lines changed

2 files changed

+49
-16
lines changed

src/unsize.rs

+48-15
Original file line numberDiff line numberDiff line change
@@ -25,39 +25,73 @@ pub(crate) fn unsized_info<'tcx>(
2525
.bcx
2626
.ins()
2727
.iconst(fx.pointer_type, len.eval_usize(fx.tcx, ParamEnv::reveal_all()) as i64),
28-
(&ty::Dynamic(..), &ty::Dynamic(..)) => {
29-
// For now, upcasts are limited to changes in marker
30-
// traits, and hence never actually require an actual
31-
// change to the vtable.
32-
old_info.expect("unsized_info: missing old info for trait upcast")
28+
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
29+
let old_info =
30+
old_info.expect("unsized_info: missing old info for trait upcasting coercion");
31+
if data_a.principal_def_id() == data_b.principal_def_id() {
32+
return old_info;
33+
}
34+
// trait upcasting coercion
35+
36+
// if both of the two `principal`s are `None`, this function would have returned early above.
37+
// and if one of the two `principal`s is `None`, typechecking would have rejected this case.
38+
let principal_a = data_a
39+
.principal()
40+
.expect("unsized_info: missing principal trait for trait upcasting coercion");
41+
let principal_b = data_b
42+
.principal()
43+
.expect("unsized_info: missing principal trait for trait upcasting coercion");
44+
45+
let vptr_entry_idx = fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
46+
principal_a.with_self_ty(fx.tcx, source),
47+
principal_b.with_self_ty(fx.tcx, source),
48+
));
49+
50+
if let Some(entry_idx) = vptr_entry_idx {
51+
let entry_idx = u32::try_from(entry_idx).unwrap();
52+
let entry_offset = entry_idx * fx.pointer_type.bytes();
53+
let vptr_ptr = Pointer::new(old_info).offset_i64(fx, entry_offset.into()).load(
54+
fx,
55+
fx.pointer_type,
56+
crate::vtable::vtable_memflags(),
57+
);
58+
vptr_ptr
59+
} else {
60+
old_info
61+
}
3362
}
3463
(_, &ty::Dynamic(ref data, ..)) => crate::vtable::get_vtable(fx, source, data.principal()),
3564
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
3665
}
3766
}
3867

39-
/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer.
40-
fn unsize_thin_ptr<'tcx>(
68+
/// Coerce `src` to `dst_ty`.
69+
fn unsize_ptr<'tcx>(
4170
fx: &mut FunctionCx<'_, '_, 'tcx>,
4271
src: Value,
4372
src_layout: TyAndLayout<'tcx>,
4473
dst_layout: TyAndLayout<'tcx>,
74+
old_info: Option<Value>,
4575
) -> (Value, Value) {
4676
match (&src_layout.ty.kind(), &dst_layout.ty.kind()) {
4777
(&ty::Ref(_, a, _), &ty::Ref(_, b, _))
4878
| (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. }))
4979
| (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
5080
assert!(!fx.layout_of(a).is_unsized());
51-
(src, unsized_info(fx, a, b, None))
81+
(src, unsized_info(fx, a, b, old_info))
5282
}
5383
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => {
5484
let (a, b) = (src_layout.ty.boxed_ty(), dst_layout.ty.boxed_ty());
5585
assert!(!fx.layout_of(a).is_unsized());
56-
(src, unsized_info(fx, a, b, None))
86+
(src, unsized_info(fx, a, b, old_info))
5787
}
5888
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
5989
assert_eq!(def_a, def_b);
6090

91+
if src_layout == dst_layout {
92+
return (src, old_info.unwrap());
93+
}
94+
6195
let mut result = None;
6296
for i in 0..src_layout.fields.count() {
6397
let src_f = src_layout.field(fx, i);
@@ -71,11 +105,11 @@ fn unsize_thin_ptr<'tcx>(
71105
let dst_f = dst_layout.field(fx, i);
72106
assert_ne!(src_f.ty, dst_f.ty);
73107
assert_eq!(result, None);
74-
result = Some(unsize_thin_ptr(fx, src, src_f, dst_f));
108+
result = Some(unsize_ptr(fx, src, src_f, dst_f, old_info));
75109
}
76110
result.unwrap()
77111
}
78-
_ => bug!("unsize_thin_ptr: called on bad types"),
112+
_ => bug!("unsize_ptr: called on bad types"),
79113
}
80114
}
81115

@@ -91,12 +125,11 @@ pub(crate) fn coerce_unsized_into<'tcx>(
91125
let mut coerce_ptr = || {
92126
let (base, info) =
93127
if fx.layout_of(src.layout().ty.builtin_deref(true).unwrap().ty).is_unsized() {
94-
// fat-ptr to fat-ptr unsize preserves the vtable
95-
// i.e., &'a fmt::Debug+Send => &'a fmt::Debug
96-
src.load_scalar_pair(fx)
128+
let (old_base, old_info) = src.load_scalar_pair(fx);
129+
unsize_ptr(fx, old_base, src.layout(), dst.layout(), Some(old_info))
97130
} else {
98131
let base = src.load_scalar(fx);
99-
unsize_thin_ptr(fx, base, src.layout(), dst.layout())
132+
unsize_ptr(fx, base, src.layout(), dst.layout(), None)
100133
};
101134
dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout()));
102135
};

src/vtable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use crate::constant::data_id_for_alloc_id;
66
use crate::prelude::*;
77

8-
fn vtable_memflags() -> MemFlags {
8+
pub(crate) fn vtable_memflags() -> MemFlags {
99
let mut flags = MemFlags::trusted(); // A vtable access is always aligned and will never trap.
1010
flags.set_readonly(); // A vtable is always read-only.
1111
flags

0 commit comments

Comments
 (0)