Fix some unsizing problems in mir

This commit is contained in:
hkalbasi 2023-06-26 01:57:12 +03:30
parent 5eb4796d3d
commit 6d2d1387af
5 changed files with 135 additions and 51 deletions

View File

@ -166,14 +166,21 @@ fn casts() {
check_number( check_number(
r#" r#"
//- minicore: coerce_unsized, index, slice //- minicore: coerce_unsized, index, slice
struct X {
unsize_field: [u8],
}
const GOAL: usize = { const GOAL: usize = {
let a = [10, 20, 3, 15]; let a = [10, 20, 3, 15];
let x: &[i32] = &a; let x: &[i32] = &a;
let y: *const [i32] = x; let x: *const [i32] = x;
let z = y as *const [u8]; // slice fat pointer cast don't touch metadata let x = x as *const [u8]; // slice fat pointer cast don't touch metadata
let q = z as *const str; let x = x as *const str;
let p = q as *const [u8]; let x = x as *const X;
let w = unsafe { &*z }; let x = x as *const [i16];
let x = x as *const X;
let x = x as *const [u8];
let w = unsafe { &*x };
w.len() w.len()
}; };
"#, "#,
@ -1873,6 +1880,38 @@ fn bar(&self) -> i32 { 700 }
); );
} }
#[test]
fn coerce_unsized() {
check_number(
r#"
//- minicore: coerce_unsized, deref_mut, slice, index, transmute, non_null
use core::ops::{Deref, DerefMut, CoerceUnsized};
use core::{marker::Unsize, mem::transmute, ptr::NonNull};
struct ArcInner<T: ?Sized> {
strong: usize,
weak: usize,
data: T,
}
pub struct Arc<T: ?Sized> {
inner: NonNull<ArcInner<T>>,
}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Arc<U>> for Arc<T> {}
const GOAL: usize = {
let x = transmute::<usize, Arc<[i32; 3]>>(12);
let y: Arc<[i32]> = x;
let z = transmute::<Arc<[i32]>, (usize, usize)>(y);
z.1
};
"#,
3,
);
}
#[test] #[test]
fn boxes() { fn boxes() {
check_number( check_number(

View File

@ -369,11 +369,11 @@ fn tuple() {
} }
#[test] #[test]
fn non_zero() { fn non_zero_and_non_null() {
size_and_align! { size_and_align! {
minicore: non_zero, option; minicore: non_zero, non_null, option;
use core::num::NonZeroU8; use core::{num::NonZeroU8, ptr::NonNull};
struct Goal(Option<NonZeroU8>); struct Goal(Option<NonZeroU8>, Option<NonNull<i32>>);
} }
} }

View File

@ -1263,50 +1263,81 @@ fn coerce_unsized(
current_ty: &Ty, current_ty: &Ty,
target_ty: &Ty, target_ty: &Ty,
) -> Result<IntervalOrOwned> { ) -> Result<IntervalOrOwned> {
use IntervalOrOwned::*;
fn for_ptr(x: &TyKind) -> Option<Ty> { fn for_ptr(x: &TyKind) -> Option<Ty> {
match x { match x {
TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()), TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()),
_ => None, _ => None,
} }
} }
Ok(match self.coerce_unsized_look_through_fields(target_ty, for_ptr)? { let target_ty = self.coerce_unsized_look_through_fields(target_ty, for_ptr)?;
ty => match &ty.data(Interner).kind { let current_ty = self.coerce_unsized_look_through_fields(current_ty, for_ptr)?;
TyKind::Slice(_) => {
match self.coerce_unsized_look_through_fields(current_ty, for_ptr)? { self.unsizing_ptr_from_addr(target_ty, current_ty, addr)
ty => match &ty.data(Interner).kind { }
TyKind::Array(_, size) => {
let len = match try_const_usize(self.db, size) { /// Adds metadata to the address and create the fat pointer result of the unsizing operation.
None => not_supported!( fn unsizing_ptr_from_addr(
"unevaluatble len of array in coerce unsized" &mut self,
), target_ty: Ty,
Some(x) => x as usize, current_ty: Ty,
}; addr: Interval,
let mut r = Vec::with_capacity(16); ) -> Result<IntervalOrOwned> {
let addr = addr.get(self)?; use IntervalOrOwned::*;
r.extend(addr.iter().copied()); Ok(match &target_ty.data(Interner).kind {
r.extend(len.to_le_bytes().into_iter()); TyKind::Slice(_) => match &current_ty.data(Interner).kind {
Owned(r) TyKind::Array(_, size) => {
} let len = match try_const_usize(self.db, size) {
t => { None => {
not_supported!("slice unsizing from non array type {t:?}") not_supported!("unevaluatble len of array in coerce unsized")
} }
}, Some(x) => x as usize,
} };
let mut r = Vec::with_capacity(16);
let addr = addr.get(self)?;
r.extend(addr.iter().copied());
r.extend(len.to_le_bytes().into_iter());
Owned(r)
}
t => {
not_supported!("slice unsizing from non array type {t:?}")
} }
TyKind::Dyn(_) => match &current_ty.data(Interner).kind {
TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
let vtable = self.vtable_map.id(ty.clone());
let mut r = Vec::with_capacity(16);
let addr = addr.get(self)?;
r.extend(addr.iter().copied());
r.extend(vtable.to_le_bytes().into_iter());
Owned(r)
}
_ => not_supported!("dyn unsizing from non pointers"),
},
_ => not_supported!("unknown unsized cast"),
}, },
TyKind::Dyn(_) => {
let vtable = self.vtable_map.id(current_ty.clone());
let mut r = Vec::with_capacity(16);
let addr = addr.get(self)?;
r.extend(addr.iter().copied());
r.extend(vtable.to_le_bytes().into_iter());
Owned(r)
}
TyKind::Adt(id, target_subst) => match &current_ty.data(Interner).kind {
TyKind::Adt(current_id, current_subst) => {
if id != current_id {
not_supported!("unsizing struct with different type");
}
let id = match id.0 {
AdtId::StructId(s) => s,
AdtId::UnionId(_) => not_supported!("unsizing unions"),
AdtId::EnumId(_) => not_supported!("unsizing enums"),
};
let Some((last_field, _)) = self.db.struct_data(id).variant_data.fields().iter().rev().next() else {
not_supported!("unsizing struct without field");
};
let target_last_field = self.db.field_types(id.into())[last_field]
.clone()
.substitute(Interner, target_subst);
let current_last_field = self.db.field_types(id.into())[last_field]
.clone()
.substitute(Interner, current_subst);
return self.unsizing_ptr_from_addr(
target_last_field,
current_last_field,
addr,
);
}
_ => not_supported!("unsizing struct with non adt type"),
},
_ => not_supported!("unknown unsized cast"),
}) })
} }

View File

@ -1742,17 +1742,17 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
(TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => { (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => {
CastKind::Pointer(if a == b { CastKind::Pointer(if a == b {
PointerCast::MutToConstPointer PointerCast::MutToConstPointer
} else if matches!(a.kind(Interner), TyKind::Slice(_) | TyKind::Str) } else if matches!(b.kind(Interner), TyKind::Slice(_))
&& matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Str) && matches!(a.kind(Interner), TyKind::Array(_, _))
|| matches!(b.kind(Interner), TyKind::Dyn(_))
{ {
// slice to slice cast is no-op (metadata is not touched), so we use this
PointerCast::MutToConstPointer
} else if matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
PointerCast::Unsize PointerCast::Unsize
} else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) { } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) {
PointerCast::ArrayToPointer PointerCast::ArrayToPointer
} else { } else {
// cast between two sized pointer, like *const i32 to *const i8. There is no specific variant // cast between two sized pointer, like *const i32 to *const i8, or two unsized pointer, like
// slice to slice, slice to str, ... . These are no-ops (even in the unsized case, no metadata
// will be touched) but there is no specific variant
// for it in `PointerCast` so we use `MutToConstPointer` // for it in `PointerCast` so we use `MutToConstPointer`
PointerCast::MutToConstPointer PointerCast::MutToConstPointer
}) })

View File

@ -37,6 +37,7 @@
//! iterator: option //! iterator: option
//! iterators: iterator, fn //! iterators: iterator, fn
//! manually_drop: drop //! manually_drop: drop
//! non_null:
//! non_zero: //! non_zero:
//! option: panic //! option: panic
//! ord: eq, option //! ord: eq, option
@ -386,6 +387,19 @@ pub trait Pointee {
type Metadata; type Metadata;
} }
// endregion:pointee // endregion:pointee
// region:non_null
#[rustc_layout_scalar_valid_range_start(1)]
#[rustc_nonnull_optimization_guaranteed]
pub struct NonNull<T: ?Sized> {
pointer: *const T,
}
// region:coerce_unsized
impl<T: ?Sized, U: ?Sized> crate::ops::CoerceUnsized<NonNull<U>> for NonNull<T> where
T: crate::marker::Unsize<U>
{
}
// endregion:coerce_unsized
// endregion:non_null
} }
pub mod ops { pub mod ops {