Fix some unsizing problems in mir
This commit is contained in:
parent
5eb4796d3d
commit
6d2d1387af
@ -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(
|
||||||
|
@ -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>>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 ¤t_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 ¤t_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 ¤t_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"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
})
|
})
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user