Skip to content

Commit 4649877

Browse files
committed
Auto merge of rust-lang#125479 - scottmcm:validate-vtable-projections, r=Nilstrieb
Validate the special layout restriction on `DynMetadata` If you look at <https://stdrs.dev/nightly/x86_64-unknown-linux-gnu/std/ptr/struct.DynMetadata.html>, you'd think that `DynMetadata` is a struct with fields. But it's actually not, because the lang item is special-cased in rustc_middle layout: https://github.com/rust-lang/rust/blob/7601adcc764d42c9f2984082b49948af652df986/compiler/rustc_middle/src/ty/layout.rs#L861-L864 That explains the very confusing codegen ICEs I was getting in rust-lang#124251 (comment) > Tried to extract_field 0 from primitive OperandRef(Immediate((ptr: %5 = load ptr, ptr %4, align 8, !nonnull !3, !align !5, !noundef !3)) @ TyAndLayout { ty: DynMetadata<dyn Callsite>, layout: Layout { size: Size(8 bytes), align: AbiAndPrefAlign { abi: Align(8 bytes), pref: Align(8 bytes) }, abi: Scalar(Initialized { value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), fields: Primitive, largest_niche: Some(Niche { offset: Size(0 bytes), value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes) } }) because there was a `Field` projection despite the layout clearly saying it's [`Primitive`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/abi/enum.FieldsShape.html#variant.Primitive). Thus this PR updates the MIR validator to check for such a projection, and changes `libcore` to not ever emit any projections into `DynMetadata`, just to transmute the whole thing when it wants a pointer.
2 parents 7c54789 + d83f3ca commit 4649877

File tree

2 files changed

+28
-8
lines changed

2 files changed

+28
-8
lines changed

compiler/rustc_const_eval/src/transform/validate.rs

+9
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
685685
check_equal(self, location, *f_ty);
686686
}
687687
ty::Adt(adt_def, args) => {
688+
// see <https://github.com/rust-lang/rust/blob/7601adcc764d42c9f2984082b49948af652df986/compiler/rustc_middle/src/ty/layout.rs#L861-L864>
689+
if Some(adt_def.did()) == self.tcx.lang_items().dyn_metadata() {
690+
self.fail(
691+
location,
692+
format!("You can't project to field {f:?} of `DynMetadata` because \
693+
layout is weird and thinks it doesn't have fields."),
694+
);
695+
}
696+
688697
let var = parent_ty.variant_index.unwrap_or(FIRST_VARIANT);
689698
let Some(field) = adt_def.variant(var).fields.get(f) else {
690699
fail_out_of_bounds(self, location);

library/core/src/ptr/metadata.rs

+19-8
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ impl<T: ?Sized> Clone for PtrComponents<T> {
178178
/// compare equal (since identical vtables can be deduplicated within a codegen unit).
179179
#[lang = "dyn_metadata"]
180180
pub struct DynMetadata<Dyn: ?Sized> {
181-
vtable_ptr: &'static VTable,
182-
phantom: crate::marker::PhantomData<Dyn>,
181+
_vtable_ptr: &'static VTable,
182+
_phantom: crate::marker::PhantomData<Dyn>,
183183
}
184184

185185
extern "C" {
@@ -191,6 +191,17 @@ extern "C" {
191191
}
192192

193193
impl<Dyn: ?Sized> DynMetadata<Dyn> {
194+
/// One of the things that rustc_middle does with this being a lang item is
195+
/// give it `FieldsShape::Primitive`, which means that as far as codegen can
196+
/// tell, it *is* a reference, and thus doesn't have any fields.
197+
/// That means we can't use field access, and have to transmute it instead.
198+
#[inline]
199+
fn vtable_ptr(self) -> *const VTable {
200+
// SAFETY: this layout assumption is hard-coded into the compiler.
201+
// If it's somehow not a size match, the transmute will error.
202+
unsafe { crate::mem::transmute::<Self, &'static VTable>(self) }
203+
}
204+
194205
/// Returns the size of the type associated with this vtable.
195206
#[inline]
196207
pub fn size_of(self) -> usize {
@@ -199,7 +210,7 @@ impl<Dyn: ?Sized> DynMetadata<Dyn> {
199210
// `Send` part!
200211
// SAFETY: DynMetadata always contains a valid vtable pointer
201212
return unsafe {
202-
crate::intrinsics::vtable_size(self.vtable_ptr as *const VTable as *const ())
213+
crate::intrinsics::vtable_size(self.vtable_ptr() as *const ())
203214
};
204215
}
205216

@@ -208,7 +219,7 @@ impl<Dyn: ?Sized> DynMetadata<Dyn> {
208219
pub fn align_of(self) -> usize {
209220
// SAFETY: DynMetadata always contains a valid vtable pointer
210221
return unsafe {
211-
crate::intrinsics::vtable_align(self.vtable_ptr as *const VTable as *const ())
222+
crate::intrinsics::vtable_align(self.vtable_ptr() as *const ())
212223
};
213224
}
214225

@@ -226,7 +237,7 @@ unsafe impl<Dyn: ?Sized> Sync for DynMetadata<Dyn> {}
226237

227238
impl<Dyn: ?Sized> fmt::Debug for DynMetadata<Dyn> {
228239
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229-
f.debug_tuple("DynMetadata").field(&(self.vtable_ptr as *const VTable)).finish()
240+
f.debug_tuple("DynMetadata").field(&self.vtable_ptr()).finish()
230241
}
231242
}
232243

@@ -248,15 +259,15 @@ impl<Dyn: ?Sized> Eq for DynMetadata<Dyn> {}
248259
impl<Dyn: ?Sized> PartialEq for DynMetadata<Dyn> {
249260
#[inline]
250261
fn eq(&self, other: &Self) -> bool {
251-
crate::ptr::eq::<VTable>(self.vtable_ptr, other.vtable_ptr)
262+
crate::ptr::eq::<VTable>(self.vtable_ptr(), other.vtable_ptr())
252263
}
253264
}
254265

255266
impl<Dyn: ?Sized> Ord for DynMetadata<Dyn> {
256267
#[inline]
257268
#[allow(ambiguous_wide_pointer_comparisons)]
258269
fn cmp(&self, other: &Self) -> crate::cmp::Ordering {
259-
(self.vtable_ptr as *const VTable).cmp(&(other.vtable_ptr as *const VTable))
270+
<*const VTable>::cmp(&self.vtable_ptr(), &other.vtable_ptr())
260271
}
261272
}
262273

@@ -270,6 +281,6 @@ impl<Dyn: ?Sized> PartialOrd for DynMetadata<Dyn> {
270281
impl<Dyn: ?Sized> Hash for DynMetadata<Dyn> {
271282
#[inline]
272283
fn hash<H: Hasher>(&self, hasher: &mut H) {
273-
crate::ptr::hash::<VTable, _>(self.vtable_ptr, hasher)
284+
crate::ptr::hash::<VTable, _>(self.vtable_ptr(), hasher)
274285
}
275286
}

0 commit comments

Comments
 (0)