Skip to content

Commit a38a3bf

Browse files
committed
implement and test ABI compatibility for transparent wrappers around NPO types
1 parent b5bab2b commit a38a3bf

File tree

2 files changed

+35
-19
lines changed

2 files changed

+35
-19
lines changed

compiler/rustc_const_eval/src/interpret/terminator.rs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc_middle::{
77
ty::{
88
self,
99
layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout},
10-
Instance, Ty,
10+
AdtDef, Instance, Ty,
1111
},
1212
};
1313
use rustc_span::sym;
@@ -261,9 +261,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
261261
/// Must not be called on 1-ZST (as they don't have a uniquely defined "wrapped field").
262262
///
263263
/// We work with `TyAndLayout` here since that makes it much easier to iterate over all fields.
264-
fn unfold_transparent(&self, layout: TyAndLayout<'tcx>) -> TyAndLayout<'tcx> {
264+
fn unfold_transparent(
265+
&self,
266+
layout: TyAndLayout<'tcx>,
267+
may_unfold: impl Fn(AdtDef<'tcx>) -> bool,
268+
) -> TyAndLayout<'tcx> {
265269
match layout.ty.kind() {
266-
ty::Adt(adt_def, _) if adt_def.repr().transparent() => {
270+
ty::Adt(adt_def, _) if adt_def.repr().transparent() && may_unfold(*adt_def) => {
267271
assert!(!adt_def.is_enum());
268272
// Find the non-1-ZST field.
269273
let mut non_1zst_fields = (0..layout.fields.count()).filter_map(|idx| {
@@ -277,7 +281,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
277281
);
278282

279283
// Found it!
280-
self.unfold_transparent(first)
284+
self.unfold_transparent(first, may_unfold)
281285
}
282286
// Not a transparent type, no further unfolding.
283287
_ => layout,
@@ -287,7 +291,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
287291
/// Unwrap types that are guaranteed a null-pointer-optimization
288292
fn unfold_npo(&self, ty: Ty<'tcx>) -> InterpResult<'tcx, Ty<'tcx>> {
289293
// Check if this is `Option` wrapping some type.
290-
let inner_ty = match ty.kind() {
294+
let inner = match ty.kind() {
291295
ty::Adt(def, args) if self.tcx.is_diagnostic_item(sym::Option, def.did()) => {
292296
args[0].as_type().unwrap()
293297
}
@@ -297,16 +301,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
297301
}
298302
};
299303
// Check if the inner type is one of the NPO-guaranteed ones.
300-
Ok(match inner_ty.kind() {
304+
// For that we first unpeel transparent *structs* (but not unions).
305+
let is_npo = |def: AdtDef<'tcx>| {
306+
self.tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed)
307+
};
308+
let inner = self.unfold_transparent(self.layout_of(inner)?, /* may_unfold */ |def| {
309+
// Stop at NPO tpyes so that we don't miss that attribute in the check below!
310+
def.is_struct() && !is_npo(def)
311+
});
312+
Ok(match inner.ty.kind() {
301313
ty::Ref(..) | ty::FnPtr(..) => {
302314
// Option<&T> behaves like &T, and same for fn()
303-
inner_ty
315+
inner.ty
304316
}
305-
ty::Adt(def, _)
306-
if self.tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed) =>
307-
{
308-
// For non-null-guaranteed structs, unwrap newtypes.
309-
self.unfold_transparent(self.layout_of(inner_ty)?).ty
317+
ty::Adt(def, _) if is_npo(*def) => {
318+
// Once we found a `nonnull_optimization_guaranteed` type, further strip off
319+
// newtype structs from it to find the underlying ABI type.
320+
self.unfold_transparent(inner, /* may_unfold */ |def| def.is_struct()).ty
310321
}
311322
_ => {
312323
// Everything else we do not unfold.
@@ -332,8 +343,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
332343
return Ok(caller_layout.is_1zst() && callee_layout.is_1zst());
333344
}
334345
// Unfold newtypes and NPO optimizations.
335-
let caller_ty = self.unfold_npo(self.unfold_transparent(caller_layout).ty)?;
336-
let callee_ty = self.unfold_npo(self.unfold_transparent(callee_layout).ty)?;
346+
let caller_ty =
347+
self.unfold_npo(self.unfold_transparent(caller_layout, /* may_unfold */ |_| true).ty)?;
348+
let callee_ty =
349+
self.unfold_npo(self.unfold_transparent(callee_layout, /* may_unfold */ |_| true).ty)?;
337350
// Now see if these inner types are compatible.
338351

339352
// Compatible pointer types.

src/tools/miri/tests/pass/function_calls/abi_compat.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ use std::ptr;
55
#[derive(Copy, Clone, Default)]
66
struct Zst;
77

8+
#[repr(transparent)]
9+
#[derive(Copy, Clone)]
10+
struct Wrapper<T>(T);
11+
812
fn id<T>(x: T) -> T { x }
913

1014
fn test_abi_compat<T: Clone, U: Clone>(t: T, u: U) {
@@ -35,9 +39,6 @@ fn test_abi_compat<T: Clone, U: Clone>(t: T, u: U) {
3539

3640
/// Ensure that `T` is compatible with various repr(transparent) wrappers around `T`.
3741
fn test_abi_newtype<T: Copy + Default>() {
38-
#[repr(transparent)]
39-
#[derive(Copy, Clone)]
40-
struct Wrapper1<T>(T);
4142
#[repr(transparent)]
4243
#[derive(Copy, Clone)]
4344
struct Wrapper2<T>(T, ());
@@ -49,7 +50,7 @@ fn test_abi_newtype<T: Copy + Default>() {
4950
struct Wrapper3<T>(Zst, T, [u8; 0]);
5051

5152
let t = T::default();
52-
test_abi_compat(t, Wrapper1(t));
53+
test_abi_compat(t, Wrapper(t));
5354
test_abi_compat(t, Wrapper2(t, ()));
5455
test_abi_compat(t, Wrapper2a((), t));
5556
test_abi_compat(t, Wrapper3(Zst, t, []));
@@ -66,6 +67,7 @@ fn main() {
6667
test_abi_compat(0usize, 0u64);
6768
test_abi_compat(0isize, 0i64);
6869
}
70+
test_abi_compat(42u32, num::NonZeroU32::new(1).unwrap());
6971
// Reference/pointer types with the same pointee.
7072
test_abi_compat(&0u32, &0u32 as *const u32);
7173
test_abi_compat(&mut 0u32 as *mut u32, Box::new(0u32));
@@ -77,8 +79,9 @@ fn main() {
7779
// Guaranteed null-pointer-optimizations.
7880
test_abi_compat(&0u32 as *const u32, Some(&0u32));
7981
test_abi_compat(main as fn(), Some(main as fn()));
80-
test_abi_compat(42u32, num::NonZeroU32::new(1).unwrap());
8182
test_abi_compat(0u32, Some(num::NonZeroU32::new(1).unwrap()));
83+
test_abi_compat(&0u32 as *const u32, Some(Wrapper(&0u32)));
84+
test_abi_compat(0u32, Some(Wrapper(num::NonZeroU32::new(1).unwrap())));
8285

8386
// These must work for *any* type, since we guarantee that `repr(transparent)` is ABI-compatible
8487
// with the wrapped field.

0 commit comments

Comments
 (0)