about summary refs log tree commit diff
diff options
context:
space:
mode:
authorScott McMurray <scottmcm@users.noreply.github.com>2024-05-23 23:17:58 -0700
committerScott McMurray <scottmcm@users.noreply.github.com>2024-05-23 23:38:44 -0700
commitd83f3ca8ca2d20eadf92a135a1a4b65ca91a24f6 (patch)
tree9e8e3c2d1fda3846a2717118941c93d0a97945f4
parent5baee04b6349d176440cb1fcd5424a89f67b9f7b (diff)
downloadrust-d83f3ca8ca2d20eadf92a135a1a4b65ca91a24f6.tar.gz
rust-d83f3ca8ca2d20eadf92a135a1a4b65ca91a24f6.zip
Validate the special layout restriction on DynMetadata
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs9
-rw-r--r--library/core/src/ptr/metadata.rs27
2 files changed, 28 insertions, 8 deletions
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 3a2b2c5f300..66cc65de647 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -685,6 +685,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         check_equal(self, location, *f_ty);
                     }
                     ty::Adt(adt_def, args) => {
+                        // see <https://github.com/rust-lang/rust/blob/7601adcc764d42c9f2984082b49948af652df986/compiler/rustc_middle/src/ty/layout.rs#L861-L864>
+                        if Some(adt_def.did()) == self.tcx.lang_items().dyn_metadata() {
+                            self.fail(
+                                location,
+                                format!("You can't project to field {f:?} of `DynMetadata` because \
+                                         layout is weird and thinks it doesn't have fields."),
+                            );
+                        }
+
                         let var = parent_ty.variant_index.unwrap_or(FIRST_VARIANT);
                         let Some(field) = adt_def.variant(var).fields.get(f) else {
                             fail_out_of_bounds(self, location);
diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs
index eb815b6d822..e501970b580 100644
--- a/library/core/src/ptr/metadata.rs
+++ b/library/core/src/ptr/metadata.rs
@@ -178,8 +178,8 @@ impl<T: ?Sized> Clone for PtrComponents<T> {
 /// compare equal (since identical vtables can be deduplicated within a codegen unit).
 #[lang = "dyn_metadata"]
 pub struct DynMetadata<Dyn: ?Sized> {
-    vtable_ptr: &'static VTable,
-    phantom: crate::marker::PhantomData<Dyn>,
+    _vtable_ptr: &'static VTable,
+    _phantom: crate::marker::PhantomData<Dyn>,
 }
 
 extern "C" {
@@ -191,6 +191,17 @@ extern "C" {
 }
 
 impl<Dyn: ?Sized> DynMetadata<Dyn> {
+    /// One of the things that rustc_middle does with this being a lang item is
+    /// give it `FieldsShape::Primitive`, which means that as far as codegen can
+    /// tell, it *is* a reference, and thus doesn't have any fields.
+    /// That means we can't use field access, and have to transmute it instead.
+    #[inline]
+    fn vtable_ptr(self) -> *const VTable {
+        // SAFETY: this layout assumption is hard-coded into the compiler.
+        // If it's somehow not a size match, the transmute will error.
+        unsafe { crate::mem::transmute::<Self, &'static VTable>(self) }
+    }
+
     /// Returns the size of the type associated with this vtable.
     #[inline]
     pub fn size_of(self) -> usize {
@@ -199,7 +210,7 @@ impl<Dyn: ?Sized> DynMetadata<Dyn> {
         // `Send` part!
         // SAFETY: DynMetadata always contains a valid vtable pointer
         return unsafe {
-            crate::intrinsics::vtable_size(self.vtable_ptr as *const VTable as *const ())
+            crate::intrinsics::vtable_size(self.vtable_ptr() as *const ())
         };
     }
 
@@ -208,7 +219,7 @@ impl<Dyn: ?Sized> DynMetadata<Dyn> {
     pub fn align_of(self) -> usize {
         // SAFETY: DynMetadata always contains a valid vtable pointer
         return unsafe {
-            crate::intrinsics::vtable_align(self.vtable_ptr as *const VTable as *const ())
+            crate::intrinsics::vtable_align(self.vtable_ptr() as *const ())
         };
     }
 
@@ -226,7 +237,7 @@ unsafe impl<Dyn: ?Sized> Sync for DynMetadata<Dyn> {}
 
 impl<Dyn: ?Sized> fmt::Debug for DynMetadata<Dyn> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_tuple("DynMetadata").field(&(self.vtable_ptr as *const VTable)).finish()
+        f.debug_tuple("DynMetadata").field(&self.vtable_ptr()).finish()
     }
 }
 
@@ -248,7 +259,7 @@ impl<Dyn: ?Sized> Eq for DynMetadata<Dyn> {}
 impl<Dyn: ?Sized> PartialEq for DynMetadata<Dyn> {
     #[inline]
     fn eq(&self, other: &Self) -> bool {
-        crate::ptr::eq::<VTable>(self.vtable_ptr, other.vtable_ptr)
+        crate::ptr::eq::<VTable>(self.vtable_ptr(), other.vtable_ptr())
     }
 }
 
@@ -256,7 +267,7 @@ impl<Dyn: ?Sized> Ord for DynMetadata<Dyn> {
     #[inline]
     #[allow(ambiguous_wide_pointer_comparisons)]
     fn cmp(&self, other: &Self) -> crate::cmp::Ordering {
-        (self.vtable_ptr as *const VTable).cmp(&(other.vtable_ptr as *const VTable))
+        <*const VTable>::cmp(&self.vtable_ptr(), &other.vtable_ptr())
     }
 }
 
@@ -270,6 +281,6 @@ impl<Dyn: ?Sized> PartialOrd for DynMetadata<Dyn> {
 impl<Dyn: ?Sized> Hash for DynMetadata<Dyn> {
     #[inline]
     fn hash<H: Hasher>(&self, hasher: &mut H) {
-        crate::ptr::hash::<VTable, _>(self.vtable_ptr, hasher)
+        crate::ptr::hash::<VTable, _>(self.vtable_ptr(), hasher)
     }
 }