about summary refs log tree commit diff
diff options
context:
space:
mode:
authorhkalbasi <hamidrezakalbasi@protonmail.com>2023-06-26 01:57:12 +0330
committerhkalbasi <hamidrezakalbasi@protonmail.com>2023-06-26 01:57:12 +0330
commit6d2d1387afdef3ecbbe54ba26f109d63e3c24638 (patch)
tree8f6318daad4df647d33d61bdf8395efafb877041
parent5eb4796d3dd50389e342f485a385519e1300e561 (diff)
downloadrust-6d2d1387afdef3ecbbe54ba26f109d63e3c24638.tar.gz
rust-6d2d1387afdef3ecbbe54ba26f109d63e3c24638.zip
Fix some unsizing problems in mir
-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 b487d7d049b..de3a947f358 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 0ff8c532d47..b75f213df2d 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 78961e19828..01295a9fc3c 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 2cb29b4ab91..800e490e9a6 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 0d6e4b98d8c..d7201f996d9 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 {