Fix unsize coercions and the size_of_val and min_align_of_val intrinsics for custom dst's
This commit is contained in:
parent
8f2ca706c2
commit
16e43c9d21
7 changed files with 244 additions and 86 deletions
|
@ -12,6 +12,7 @@ pub trait Unsize<T: ?Sized> {}
|
|||
pub trait CoerceUnsized<T> {}
|
||||
|
||||
impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
|
||||
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
|
||||
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
|
||||
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
|
||||
|
||||
|
|
|
@ -151,6 +151,10 @@ fn main() {
|
|||
pointer: 0 as *const &str,
|
||||
_marker: PhantomData,
|
||||
} as Unique<dyn SomeTrait>;
|
||||
|
||||
struct MyDst<T: ?Sized>(T);
|
||||
|
||||
intrinsics::size_of_val(&MyDst([0u8; 4]) as &MyDst<[u8]>);
|
||||
}
|
||||
|
||||
let _ = NoisyDrop {
|
||||
|
|
|
@ -221,69 +221,7 @@ impl<'tcx> CValue<'tcx> {
|
|||
}
|
||||
|
||||
pub fn unsize_value<'a>(self, fx: &mut FunctionCx<'a, 'tcx, impl Backend>, dest: CPlace<'tcx>) {
|
||||
if self.layout().ty == dest.layout().ty {
|
||||
dest.write_cvalue(fx, self); // FIXME this shouldn't happen (rust-lang/rust#53602)
|
||||
return;
|
||||
}
|
||||
match &self.layout().ty.sty {
|
||||
ty::Ref(_, ty, _) | ty::RawPtr(TypeAndMut { ty, mutbl: _ }) => {
|
||||
let (ptr, extra) = match dest.layout().ty.builtin_deref(true).unwrap().ty.sty {
|
||||
ty::Slice(slice_elem_ty) => match ty.sty {
|
||||
ty::Array(array_elem_ty, size) => {
|
||||
assert_eq!(slice_elem_ty, array_elem_ty);
|
||||
let ptr = self.load_value(fx);
|
||||
let extra = fx
|
||||
.bcx
|
||||
.ins()
|
||||
.iconst(fx.pointer_type, size.unwrap_usize(fx.tcx) as i64);
|
||||
(ptr, extra)
|
||||
}
|
||||
_ => bug!("unsize non array {:?} to slice", ty),
|
||||
},
|
||||
ty::Dynamic(data, _) => match ty.sty {
|
||||
ty::Dynamic(_, _) => self.load_value_pair(fx),
|
||||
_ => {
|
||||
let ptr = self.load_value(fx);
|
||||
let vtable = crate::vtable::get_vtable(fx, ty, data.principal());
|
||||
(ptr, vtable)
|
||||
}
|
||||
},
|
||||
_ => bug!(
|
||||
"unsize of type {:?} to {:?}",
|
||||
self.layout().ty,
|
||||
dest.layout().ty
|
||||
),
|
||||
};
|
||||
dest.write_cvalue(fx, CValue::ByValPair(ptr, extra, dest.layout()));
|
||||
}
|
||||
_ => {
|
||||
assert!(!self.layout().ty.is_enum(), "Tried to unsize enum");
|
||||
let field_count = self.layout().fields.count();
|
||||
let mut found_unsize_field = false;
|
||||
for idx in 0..field_count {
|
||||
let field_dest = dest.place_field(fx, mir::Field::new(idx));
|
||||
let field_src = self.value_field(fx, mir::Field::new(idx));
|
||||
if field_src.layout().ty.is_phantom_data() {
|
||||
// Ignore PhantomData so for example `Unique<()>` can coerce to `Unique<Debug>`
|
||||
//
|
||||
// ```rust
|
||||
// struct Unique<T: ?Sized> {
|
||||
// pointer: NonZero<*const T>,
|
||||
// _marker: PhantomData<T>,
|
||||
// }
|
||||
// ```
|
||||
continue;
|
||||
}
|
||||
if field_src.layout().ty != field_dest.layout().ty {
|
||||
assert!(!found_unsize_field);
|
||||
found_unsize_field = true;
|
||||
field_src.unsize_value(fx, field_dest);
|
||||
} else {
|
||||
field_dest.write_cvalue(fx, field_src);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
crate::unsize::coerce_unsized_into(fx, self, dest);
|
||||
}
|
||||
|
||||
pub fn const_val<'a>(
|
||||
|
|
|
@ -153,18 +153,15 @@ pub fn codegen_intrinsic_call<'a, 'tcx: 'a>(
|
|||
};
|
||||
size_of_val, <T> (c ptr) {
|
||||
let layout = fx.layout_of(T);
|
||||
let size = match &layout.ty.sty {
|
||||
_ if !layout.is_unsized() => fx
|
||||
let size = if layout.is_unsized() {
|
||||
let (_ptr, info) = ptr.load_value_pair(fx);
|
||||
let (size, _align) = crate::unsize::size_and_align_of_dst(fx, layout.ty, info);
|
||||
size
|
||||
} else {
|
||||
fx
|
||||
.bcx
|
||||
.ins()
|
||||
.iconst(fx.pointer_type, layout.size.bytes() as i64),
|
||||
ty::Slice(elem) => {
|
||||
let len = ptr.load_value_pair(fx).1;
|
||||
let elem_size = fx.layout_of(elem).size.bytes();
|
||||
fx.bcx.ins().imul_imm(len, elem_size as i64)
|
||||
}
|
||||
ty::Dynamic(..) => crate::vtable::size_of_obj(fx, ptr),
|
||||
ty => bug!("size_of_val for unknown unsized type {:?}", ty),
|
||||
.iconst(fx.pointer_type, layout.size.bytes() as i64)
|
||||
};
|
||||
ret.write_cvalue(fx, CValue::ByVal(size, usize_layout));
|
||||
};
|
||||
|
@ -175,17 +172,15 @@ pub fn codegen_intrinsic_call<'a, 'tcx: 'a>(
|
|||
};
|
||||
min_align_of_val, <T> (c ptr) {
|
||||
let layout = fx.layout_of(T);
|
||||
let align = match &layout.ty.sty {
|
||||
_ if !layout.is_unsized() => fx
|
||||
let align = if layout.is_unsized() {
|
||||
let (_ptr, info) = ptr.load_value_pair(fx);
|
||||
let (_size, align) = crate::unsize::size_and_align_of_dst(fx, layout.ty, info);
|
||||
align
|
||||
} else {
|
||||
fx
|
||||
.bcx
|
||||
.ins()
|
||||
.iconst(fx.pointer_type, layout.align.abi.bytes() as i64),
|
||||
ty::Slice(elem) => {
|
||||
let align = fx.layout_of(elem).align.abi.bytes() as i64;
|
||||
fx.bcx.ins().iconst(fx.pointer_type, align)
|
||||
}
|
||||
ty::Dynamic(..) => crate::vtable::min_align_of_obj(fx, ptr),
|
||||
ty => unimplemented!("min_align_of_val for {:?}", ty),
|
||||
.iconst(fx.pointer_type, layout.align.abi.bytes() as i64)
|
||||
};
|
||||
ret.write_cvalue(fx, CValue::ByVal(align, usize_layout));
|
||||
};
|
||||
|
|
|
@ -51,6 +51,7 @@ mod metadata;
|
|||
mod pretty_clif;
|
||||
mod trap;
|
||||
mod unimpl;
|
||||
mod unsize;
|
||||
mod vtable;
|
||||
|
||||
mod prelude {
|
||||
|
|
221
src/unsize.rs
Normal file
221
src/unsize.rs
Normal file
|
@ -0,0 +1,221 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
// Adapted from https://github.com/rust-lang/rust/blob/2a663555ddf36f6b041445894a8c175cd1bc718c/src/librustc_codegen_ssa/base.rs#L159-L307
|
||||
|
||||
/// Retrieve the information we are losing (making dynamic) in an unsizing
|
||||
/// adjustment.
|
||||
///
|
||||
/// The `old_info` argument is a bit funny. It is intended for use
|
||||
/// in an upcast, where the new vtable for an object will be derived
|
||||
/// from the old one.
|
||||
pub fn unsized_info<'a, 'tcx: 'a>(
|
||||
fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
|
||||
source: Ty<'tcx>,
|
||||
target: Ty<'tcx>,
|
||||
old_info: Option<Value>,
|
||||
) -> Value {
|
||||
let (source, target) = fx.tcx.struct_lockstep_tails(source, target);
|
||||
match (&source.sty, &target.sty) {
|
||||
(&ty::Array(_, len), &ty::Slice(_)) => {
|
||||
fx.bcx.ins().iconst(fx.pointer_type, len.unwrap_usize(fx.tcx) as i64)
|
||||
}
|
||||
(&ty::Dynamic(..), &ty::Dynamic(..)) => {
|
||||
// For now, upcasts are limited to changes in marker
|
||||
// traits, and hence never actually require an actual
|
||||
// change to the vtable.
|
||||
old_info.expect("unsized_info: missing old info for trait upcast")
|
||||
}
|
||||
(_, &ty::Dynamic(ref data, ..)) => {
|
||||
crate::vtable::get_vtable(fx, source, data.principal())
|
||||
}
|
||||
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}",
|
||||
source,
|
||||
target),
|
||||
}
|
||||
}
|
||||
|
||||
/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer.
|
||||
pub fn unsize_thin_ptr<'a, 'tcx: 'a>(
|
||||
fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
|
||||
src: Value,
|
||||
src_ty: Ty<'tcx>,
|
||||
dst_ty: Ty<'tcx>
|
||||
) -> (Value, Value) {
|
||||
match (&src_ty.sty, &dst_ty.sty) {
|
||||
(&ty::Ref(_, a, _),
|
||||
&ty::Ref(_, b, _)) |
|
||||
(&ty::Ref(_, a, _),
|
||||
&ty::RawPtr(ty::TypeAndMut { ty: b, .. })) |
|
||||
(&ty::RawPtr(ty::TypeAndMut { ty: a, .. }),
|
||||
&ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
|
||||
assert!(!fx.layout_of(a).is_unsized());
|
||||
(src, unsized_info(fx, a, b, None))
|
||||
}
|
||||
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => {
|
||||
let (a, b) = (src_ty.boxed_ty(), dst_ty.boxed_ty());
|
||||
assert!(!fx.layout_of(a).is_unsized());
|
||||
(src, unsized_info(fx, a, b, None))
|
||||
}
|
||||
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
|
||||
assert_eq!(def_a, def_b);
|
||||
|
||||
let src_layout = fx.layout_of(src_ty);
|
||||
let dst_layout = fx.layout_of(dst_ty);
|
||||
let mut result = None;
|
||||
for i in 0..src_layout.fields.count() {
|
||||
let src_f = src_layout.field(fx, i);
|
||||
assert_eq!(src_layout.fields.offset(i).bytes(), 0);
|
||||
assert_eq!(dst_layout.fields.offset(i).bytes(), 0);
|
||||
if src_f.is_zst() {
|
||||
continue;
|
||||
}
|
||||
assert_eq!(src_layout.size, src_f.size);
|
||||
|
||||
let dst_f = dst_layout.field(fx, i);
|
||||
assert_ne!(src_f.ty, dst_f.ty);
|
||||
assert_eq!(result, None);
|
||||
result = Some(unsize_thin_ptr(fx, src, src_f.ty, dst_f.ty));
|
||||
}
|
||||
result.unwrap()
|
||||
}
|
||||
_ => bug!("unsize_thin_ptr: called on bad types"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Coerce `src`, which is a reference to a value of type `src_ty`,
|
||||
/// to a value of type `dst_ty` and store the result in `dst`
|
||||
pub fn coerce_unsized_into<'a, 'tcx: 'a>(
|
||||
fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
|
||||
src: CValue<'tcx>,
|
||||
dst: CPlace<'tcx>,
|
||||
) {
|
||||
let src_ty = src.layout().ty;
|
||||
let dst_ty = dst.layout().ty;
|
||||
let mut coerce_ptr = || {
|
||||
let (base, info) = if fx.layout_of(src.layout().ty.builtin_deref(true).unwrap().ty).is_unsized() {
|
||||
// fat-ptr to fat-ptr unsize preserves the vtable
|
||||
// i.e., &'a fmt::Debug+Send => &'a fmt::Debug
|
||||
src.load_value_pair(fx)
|
||||
} else {
|
||||
let base = src.load_value(fx);
|
||||
unsize_thin_ptr(fx, base, src_ty, dst_ty)
|
||||
};
|
||||
dst.write_cvalue(fx, CValue::ByValPair(base, info, dst.layout()));
|
||||
};
|
||||
match (&src_ty.sty, &dst_ty.sty) {
|
||||
(&ty::Ref(..), &ty::Ref(..)) |
|
||||
(&ty::Ref(..), &ty::RawPtr(..)) |
|
||||
(&ty::RawPtr(..), &ty::RawPtr(..)) => {
|
||||
coerce_ptr()
|
||||
}
|
||||
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
|
||||
assert_eq!(def_a, def_b);
|
||||
|
||||
for i in 0..def_a.variants[VariantIdx::new(0)].fields.len() {
|
||||
let src_f = src.value_field(fx, mir::Field::new(i));
|
||||
let dst_f = dst.place_field(fx, mir::Field::new(i));
|
||||
|
||||
if dst_f.layout().is_zst() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if src_f.layout().ty == dst_f.layout().ty {
|
||||
dst_f.write_cvalue(fx, src_f);
|
||||
} else {
|
||||
coerce_unsized_into(fx, src_f, dst_f);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => bug!("coerce_unsized_into: invalid coercion {:?} -> {:?}",
|
||||
src_ty,
|
||||
dst_ty),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Adapted from https://github.com/rust-lang/rust/blob/2a663555ddf36f6b041445894a8c175cd1bc718c/src/librustc_codegen_ssa/glue.rs
|
||||
|
||||
pub fn size_and_align_of_dst<'a, 'tcx: 'a>(
|
||||
fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
|
||||
ty: Ty<'tcx>,
|
||||
info: Value,
|
||||
) -> (Value, Value) {
|
||||
let layout = fx.layout_of(ty);
|
||||
if !layout.is_unsized() {
|
||||
let size = fx.bcx.ins().iconst(fx.pointer_type, layout.size.bytes() as i64);
|
||||
let align = fx.bcx.ins().iconst(fx.pointer_type, layout.align.abi.bytes() as i64);
|
||||
return (size, align);
|
||||
}
|
||||
match ty.sty {
|
||||
ty::Dynamic(..) => {
|
||||
// load size/align from vtable
|
||||
(
|
||||
crate::vtable::size_of_obj(fx, info),
|
||||
crate::vtable::min_align_of_obj(fx, info),
|
||||
)
|
||||
}
|
||||
ty::Slice(_) | ty::Str => {
|
||||
let unit = layout.field(fx, 0);
|
||||
// The info in this case is the length of the str, so the size is that
|
||||
// times the unit size.
|
||||
(fx.bcx.ins().imul_imm(info, unit.size.bytes() as i64),
|
||||
fx.bcx.ins().iconst(fx.pointer_type, unit.align.abi.bytes() as i64))
|
||||
}
|
||||
_ => {
|
||||
// First get the size of all statically known fields.
|
||||
// Don't use size_of because it also rounds up to alignment, which we
|
||||
// want to avoid, as the unsized field's alignment could be smaller.
|
||||
assert!(!ty.is_simd());
|
||||
|
||||
let i = layout.fields.count() - 1;
|
||||
let sized_size = layout.fields.offset(i).bytes();
|
||||
let sized_align = layout.align.abi.bytes();
|
||||
let sized_align = fx.bcx.ins().iconst(fx.pointer_type, sized_align as i64);
|
||||
|
||||
// Recurse to get the size of the dynamically sized field (must be
|
||||
// the last field).
|
||||
let field_ty = layout.field(fx, i).ty;
|
||||
let (unsized_size, mut unsized_align) = size_and_align_of_dst(fx, field_ty, info);
|
||||
|
||||
// FIXME (#26403, #27023): We should be adding padding
|
||||
// to `sized_size` (to accommodate the `unsized_align`
|
||||
// required of the unsized field that follows) before
|
||||
// summing it with `sized_size`. (Note that since #26403
|
||||
// is unfixed, we do not yet add the necessary padding
|
||||
// here. But this is where the add would go.)
|
||||
|
||||
// Return the sum of sizes and max of aligns.
|
||||
let size = fx.bcx.ins().iadd_imm(unsized_size, sized_size as i64);
|
||||
|
||||
// Packed types ignore the alignment of their fields.
|
||||
if let ty::Adt(def, _) = ty.sty {
|
||||
if def.repr.packed() {
|
||||
unsized_align = sized_align;
|
||||
}
|
||||
}
|
||||
|
||||
// Choose max of two known alignments (combined value must
|
||||
// be aligned according to more restrictive of the two).
|
||||
let cmp = fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, sized_align, unsized_align);
|
||||
let align = fx.bcx.ins().select(cmp, sized_align, unsized_align);
|
||||
|
||||
// Issue #27023: must add any necessary padding to `size`
|
||||
// (to make it a multiple of `align`) before returning it.
|
||||
//
|
||||
// Namely, the returned size should be, in C notation:
|
||||
//
|
||||
// `size + ((size & (align-1)) ? align : 0)`
|
||||
//
|
||||
// emulated via the semi-standard fast bit trick:
|
||||
//
|
||||
// `(size + (align-1)) & -align`
|
||||
let addend = fx.bcx.ins().iadd_imm(align, -1);
|
||||
let add = fx.bcx.ins().iadd(size, addend);
|
||||
let zero = fx.bcx.ins().iconst(fx.pointer_type, 0);
|
||||
let neg = fx.bcx.ins().isub(zero, align);
|
||||
let size = fx.bcx.ins().band(add, neg);
|
||||
|
||||
(size, align)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,9 +8,8 @@ const ALIGN_INDEX: usize = 2;
|
|||
|
||||
pub fn size_of_obj<'a, 'tcx: 'a>(
|
||||
fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
|
||||
val: CValue<'tcx>,
|
||||
vtable: Value,
|
||||
) -> Value {
|
||||
let (_ptr, vtable) = val.load_value_pair(fx);
|
||||
let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize;
|
||||
fx.bcx.ins().load(
|
||||
pointer_ty(fx.tcx),
|
||||
|
@ -22,9 +21,8 @@ pub fn size_of_obj<'a, 'tcx: 'a>(
|
|||
|
||||
pub fn min_align_of_obj<'a, 'tcx: 'a>(
|
||||
fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
|
||||
val: CValue<'tcx>,
|
||||
vtable: Value,
|
||||
) -> Value {
|
||||
let (_ptr, vtable) = val.load_value_pair(fx);
|
||||
let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize;
|
||||
fx.bcx.ins().load(
|
||||
pointer_ty(fx.tcx),
|
||||
|
|
Loading…
Add table
Reference in a new issue