Skip to content

Commit 47f5a12

Browse files
committed
Auto merge of #155984 - Jules-Bertholet:nontrivial-array, r=<try>
`repr(transparent)`: don't consider most length-0 arrays trivial
2 parents 67bcaa9 + fe0c86e commit 47f5a12

5 files changed

Lines changed: 69 additions & 17 deletions

File tree

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,6 +1709,10 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
17091709
"`repr(C)` types",
17101710
"is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets.",
17111711
),
1712+
UnsuitedReason::Array => (
1713+
"array types with non-trivial element types",
1714+
"is an array with a non-trivial element type, so it is not guaranteed to have a trivial ABI in all situations.",
1715+
),
17121716
};
17131717
Diag::new(
17141718
dcx,
@@ -1785,19 +1789,43 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
17851789
NonExhaustive,
17861790
PrivateField,
17871791
ReprC,
1792+
Array,
17881793
}
17891794

17901795
fn check_unsuited<'tcx>(
17911796
tcx: TyCtxt<'tcx>,
17921797
typing_env: ty::TypingEnv<'tcx>,
17931798
ty: Ty<'tcx>,
1799+
inside_repr_rust_packed_1: bool,
17941800
) -> ControlFlow<UnsuitedInfo<'tcx>> {
17951801
// We can encounter projections during traversal, so ensure the type is normalized.
17961802
let ty =
17971803
tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty);
17981804
match ty.kind() {
1799-
ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited(tcx, typing_env, t)),
1800-
ty::Array(ty, _) => check_unsuited(tcx, typing_env, *ty),
1805+
ty::Tuple(list) => list
1806+
.iter()
1807+
.try_for_each(|t| check_unsuited(tcx, typing_env, t, inside_repr_rust_packed_1)),
1808+
ty::Array(elem_ty, len) => {
1809+
// If we are inside a `#[repr(Rust, packed(1))]` ADT,
1810+
// the alignment is guaranteed to be 1 and Rust has full control over the ABI.
1811+
// Therefore, we can allow any length-0 array.
1812+
// This special case is needed to support the `ghost` crate.
1813+
if inside_repr_rust_packed_1
1814+
&& let ty::ConstKind::Value(v) = len.kind()
1815+
&& v.try_to_target_usize(tcx) == Some(0)
1816+
{
1817+
return ControlFlow::Continue(());
1818+
}
1819+
1820+
let elem_layout = tcx.layout_of(typing_env.as_query_input(*elem_ty));
1821+
let elem_trivial = elem_layout.is_ok_and(|layout| layout.is_1zst());
1822+
1823+
if elem_trivial {
1824+
check_unsuited(tcx, typing_env, *elem_ty, inside_repr_rust_packed_1)
1825+
} else {
1826+
ControlFlow::Break(UnsuitedInfo { ty, reason: UnsuitedReason::Array })
1827+
}
1828+
}
18011829
ty::Adt(def, args) => {
18021830
if !def.did().is_local() && !find_attr!(tcx, def.did(), RustcPubTransparent(_)) {
18031831
let non_exhaustive = def.is_variant_list_non_exhaustive()
@@ -1814,12 +1842,22 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
18141842
});
18151843
}
18161844
}
1817-
if def.repr().c() {
1845+
1846+
let repr = def.repr();
1847+
1848+
if repr.c() {
18181849
return ControlFlow::Break(UnsuitedInfo { ty, reason: UnsuitedReason::ReprC });
18191850
}
1820-
def.all_fields()
1821-
.map(|field| field.ty(tcx, args))
1822-
.try_for_each(|t| check_unsuited(tcx, typing_env, t))
1851+
1852+
def.all_fields().map(|field| field.ty(tcx, args)).try_for_each(|t| {
1853+
check_unsuited(
1854+
tcx,
1855+
typing_env,
1856+
t,
1857+
inside_repr_rust_packed_1
1858+
|| def.repr().pack.is_some_and(|a| a.bytes() == 1),
1859+
)
1860+
})
18231861
}
18241862
_ => ControlFlow::Continue(()),
18251863
}
@@ -1828,11 +1866,22 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
18281866
let mut prev_unsuited_1zst = false;
18291867
for field in field_infos {
18301868
if field.trivial
1831-
&& let Some(unsuited) = check_unsuited(tcx, typing_env, field.ty).break_value()
1869+
&& let Some(unsuited) = check_unsuited(tcx, typing_env, field.ty, false).break_value()
18321870
{
18331871
// If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
18341872
// Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
18351873
if non_trivial_count > 0 || prev_unsuited_1zst {
1874+
if matches!(unsuited.reason, UnsuitedReason::Array) {
1875+
#[derive(Diagnostic)]
1876+
#[diag("😱 Crater REGRESSION 😱")]
1877+
struct CraterFail {
1878+
#[primary_span]
1879+
span: Span,
1880+
}
1881+
1882+
tcx.dcx().emit_err(CraterFail { span: field.span });
1883+
}
1884+
18361885
tcx.emit_node_span_lint(
18371886
REPR_TRANSPARENT_NON_ZST_FIELDS,
18381887
tcx.local_def_id_to_hir_id(adt.did().expect_local()),

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,13 @@ fn test_abi_newtype<T: Copy + Default>() {
6262
struct Wrapper2a<T>((), T);
6363
#[repr(transparent)]
6464
#[derive(Copy, Clone)]
65-
struct Wrapper3<T>(Zst, T, [u8; 0]);
65+
struct Wrapper3<T>(Zst, T, [(); 42]);
6666

6767
let t = T::default();
6868
test_abi_compat(t, Wrapper(t));
6969
test_abi_compat(t, Wrapper2(t, ()));
7070
test_abi_compat(t, Wrapper2a((), t));
71-
test_abi_compat(t, Wrapper3(Zst, t, []));
71+
test_abi_compat(t, Wrapper3(Zst, t, [(); 42]));
7272
test_abi_compat(t, mem::MaybeUninit::new(t)); // MaybeUninit is `repr(transparent)`
7373
}
7474

tests/codegen-llvm/repr/transparent.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub extern "C" fn test_WithZst(_: WithZst) -> WithZst {
4747
}
4848

4949
#[repr(transparent)]
50-
pub struct WithZeroSizedArray(*const f32, [i8; 0]);
50+
pub struct WithZeroSizedArray(*const f32, [(); 42]);
5151

5252
// CHECK: define{{.*}}ptr @test_WithZeroSizedArray(ptr noundef %_1)
5353
#[no_mangle]

tests/ui/abi/compatibility.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,13 @@ macro_rules! test_abi_compatible {
145145
use super::*;
146146
// Declaring a `type` doesn't even check well-formedness, so we also declare a function.
147147
fn check_wf(_x: $t1, _y: $t2) {}
148-
// Test argument and return value, `Rust` and `C` ABIs.
148+
// Test argument and return value in various ABIs.
149149
#[rustc_abi(assert_eq)]
150150
type TestRust = (fn($t1) -> $t1, fn($t2) -> $t2);
151151
#[rustc_abi(assert_eq)]
152152
type TestC = (extern "C" fn($t1) -> $t1, extern "C" fn($t2) -> $t2);
153+
#[rustc_abi(assert_eq)]
154+
type TestSystem = (extern "system" fn($t1) -> $t1, extern "system" fn($t2) -> $t2);
153155
}
154156
};
155157
}
@@ -200,7 +202,8 @@ test_abi_compatible!(isize_int, isize, i64);
200202

201203
// Compatibility of 1-ZST.
202204
test_abi_compatible!(zst_unit, Zst, ());
203-
test_abi_compatible!(zst_array, Zst, [u8; 0]);
205+
test_abi_compatible!(zst_array, Zst, [(); 0]);
206+
test_abi_compatible!(zst_array_2, Zst, [(); 42]);
204207
test_abi_compatible!(nonzero_int, NonZero<i32>, i32);
205208

206209
// `#[repr(C)]` enums should not change ABI based on individual variant inhabitedness.
@@ -220,7 +223,7 @@ struct TransparentWrapper1<T: ?Sized>(T);
220223
#[repr(transparent)]
221224
struct TransparentWrapper2<T: ?Sized>((), Zst, T);
222225
#[repr(transparent)]
223-
struct TransparentWrapper3<T>(T, [u8; 0], PhantomData<u64>);
226+
struct TransparentWrapper3<T>(T, [Zst; 42], PhantomData<u64>);
224227
#[repr(transparent)]
225228
union TransparentWrapperUnion<T> {
226229
nothing: (),
@@ -299,14 +302,14 @@ macro_rules! test_nonnull {
299302
test_abi_compatible!(result_ok_unit, Result<(), $t>, $t);
300303
test_abi_compatible!(result_err_zst, Result<$t, Zst>, $t);
301304
test_abi_compatible!(result_ok_zst, Result<Zst, $t>, $t);
302-
test_abi_compatible!(result_err_arr, Result<$t, [i8; 0]>, $t);
303-
test_abi_compatible!(result_ok_arr, Result<[i8; 0], $t>, $t);
305+
test_abi_compatible!(result_err_arr, Result<$t, [(); 42]>, $t);
306+
test_abi_compatible!(result_ok_arr, Result<[(); 42], $t>, $t);
304307
test_abi_compatible!(result_err_void, Result<$t, Void>, $t);
305308
test_abi_compatible!(result_ok_void, Result<Void, $t>, $t);
306309
test_abi_compatible!(either_err_zst, Either<$t, Zst>, $t);
307310
test_abi_compatible!(either_ok_zst, Either<Zst, $t>, $t);
308311
test_abi_compatible!(either2_err_zst, Either2<$t, Zst>, $t);
309-
test_abi_compatible!(either2_err_arr, Either2<$t, [i8; 0]>, $t);
312+
test_abi_compatible!(either2_err_arr, Either2<$t, [(); 42]>, $t);
310313
}
311314
}
312315
}

tests/ui/layout/homogeneous-aggr-transparent.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct Wrapper1<T>(T);
1414
#[repr(transparent)]
1515
struct Wrapper2<T>((), Zst, T);
1616
#[repr(transparent)]
17-
struct Wrapper3<T>(T, [u8; 0], PhantomData<u64>);
17+
struct Wrapper3<T>(T, [Zst; 42], PhantomData<u64>);
1818
#[repr(transparent)]
1919
union WrapperUnion<T: Copy> {
2020
nothing: (),

0 commit comments

Comments
 (0)