Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #15135 - HKalbasi:mir, r=HKalbasi
Fix some unsizing problems in mir
bors 2023-06-26
parent b092b45 · parent 6d2d138 · commit 4a2ceef
-rw-r--r--crates/hir-ty/src/consteval/tests.rs49
-rw-r--r--crates/hir-ty/src/layout/tests.rs8
-rw-r--r--crates/hir-ty/src/mir/eval.rs101
-rw-r--r--crates/hir-ty/src/mir/lower.rs12
-rw-r--r--crates/test-utils/src/minicore.rs14
5 files changed, 134 insertions, 50 deletions
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index b487d7d049..de3a947f35 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -166,14 +166,21 @@ fn casts() {
check_number(
r#"
//- minicore: coerce_unsized, index, slice
+ struct X {
+ unsize_field: [u8],
+ }
+
const GOAL: usize = {
let a = [10, 20, 3, 15];
let x: &[i32] = &a;
- let y: *const [i32] = x;
- let z = y as *const [u8]; // slice fat pointer cast don't touch metadata
- let q = z as *const str;
- let p = q as *const [u8];
- let w = unsafe { &*z };
+ let x: *const [i32] = x;
+ let x = x as *const [u8]; // slice fat pointer cast don't touch metadata
+ let x = x as *const str;
+ let x = x as *const X;
+ let x = x as *const [i16];
+ let x = x as *const X;
+ let x = x as *const [u8];
+ let w = unsafe { &*x };
w.len()
};
"#,
@@ -1874,6 +1881,38 @@ fn dyn_trait() {
}
#[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]
fn boxes() {
check_number(
r#"
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
index 0ff8c532d4..b75f213df2 100644
--- a/crates/hir-ty/src/layout/tests.rs
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -369,11 +369,11 @@ fn tuple() {
}
#[test]
-fn non_zero() {
+fn non_zero_and_non_null() {
size_and_align! {
- minicore: non_zero, option;
- use core::num::NonZeroU8;
- struct Goal(Option<NonZeroU8>);
+ minicore: non_zero, non_null, option;
+ use core::{num::NonZeroU8, ptr::NonNull};
+ struct Goal(Option<NonZeroU8>, Option<NonNull<i32>>);
}
}
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 78961e1982..01295a9fc3 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -1263,50 +1263,81 @@ impl Evaluator<'_> {
current_ty: &Ty,
target_ty: &Ty,
) -> Result<IntervalOrOwned> {
- use IntervalOrOwned::*;
fn for_ptr(x: &TyKind) -> Option<Ty> {
match x {
TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()),
_ => None,
}
}
- Ok(match self.coerce_unsized_look_through_fields(target_ty, for_ptr)? {
- ty => match &ty.data(Interner).kind {
- TyKind::Slice(_) => {
- match self.coerce_unsized_look_through_fields(current_ty, for_ptr)? {
- ty => match &ty.data(Interner).kind {
- TyKind::Array(_, size) => {
- let len = match try_const_usize(self.db, size) {
- None => 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:?}")
- }
- },
- }
+ let target_ty = self.coerce_unsized_look_through_fields(target_ty, for_ptr)?;
+ let current_ty = self.coerce_unsized_look_through_fields(current_ty, for_ptr)?;
+
+ self.unsizing_ptr_from_addr(target_ty, current_ty, addr)
+ }
+
+ /// Adds metadata to the address and create the fat pointer result of the unsizing operation.
+ fn unsizing_ptr_from_addr(
+ &mut self,
+ target_ty: Ty,
+ current_ty: Ty,
+ addr: Interval,
+ ) -> Result<IntervalOrOwned> {
+ use IntervalOrOwned::*;
+ Ok(match &target_ty.data(Interner).kind {
+ TyKind::Slice(_) => match &current_ty.data(Interner).kind {
+ TyKind::Array(_, size) => {
+ let len = match try_const_usize(self.db, size) {
+ None => {
+ 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)
}
- 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)
+ t => {
+ not_supported!("slice unsizing from non array type {t:?}")
+ }
+ },
+ 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");
}
- _ => not_supported!("dyn unsizing from non pointers"),
- },
- _ => not_supported!("unknown unsized cast"),
+ 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"),
})
}
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 2cb29b4ab9..800e490e9a 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -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)) => {
CastKind::Pointer(if a == b {
PointerCast::MutToConstPointer
- } else if matches!(a.kind(Interner), TyKind::Slice(_) | TyKind::Str)
- && matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Str)
+ } else if matches!(b.kind(Interner), TyKind::Slice(_))
+ && 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
} else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) {
PointerCast::ArrayToPointer
} 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`
PointerCast::MutToConstPointer
})
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 0d6e4b98d8..d7201f996d 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -37,6 +37,7 @@
//! iterator: option
//! iterators: iterator, fn
//! manually_drop: drop
+//! non_null:
//! non_zero:
//! option: panic
//! ord: eq, option
@@ -386,6 +387,19 @@ pub mod ptr {
type Metadata;
}
// 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 {