Skip to content

Commit 72a9d17

Browse files
repr(transparent): don't consider most length-0 arrays trivial
With this PR, an array type is considered trivial for the purpose of `repr(transparent)` only if its element type is—we emit the `repr_transparent_non_zst_fields` FCW otherwise. This has two benefits: ## Forbid non-portable definitions Some types have alignment 1 only on certain platforms. Prior to this PR, the following snippet would compile on AVR, and *only* on AVR: ```rust #[repr(transparent)] struct Foo(i32, [u16; 0]); ``` After this PR, the above now fails to compile on any target. ## FFI and CFI compatibility We want to add support for Control Flow Integrity to Rust at some point. There are some good reasons to want CFI to consider `*const [u8; 0]` and `*const [u8; 1]` compatible with one another. But that means we must consider `*const [u8; 0]` and `*const ()` to be CFI-incompatible. Declaring `[u8; 0]` non-trivial for `repr(transparent)` makes that easier to achieve. See discussion on Zulip: <https://rust-lang.zulipchat.com/#narrow/channel/136281-t-opsem/topic/ABI-compatibility.20rules.20of.20ZST.20types/near/591412488>
1 parent a021a77 commit 72a9d17

5 files changed

Lines changed: 202 additions & 8 deletions

File tree

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 14 additions & 1 deletion
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,6 +1789,7 @@ 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>(
@@ -1797,7 +1802,15 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
17971802
tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty);
17981803
match ty.kind() {
17991804
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::Array(elem_ty, _) => {
1806+
let elem_layout = tcx.layout_of(typing_env.as_query_input(*elem_ty));
1807+
let elem_trivial = elem_layout.is_ok_and(|layout| layout.is_1zst());
1808+
if elem_trivial {
1809+
check_unsuited(tcx, typing_env, *elem_ty)
1810+
} else {
1811+
return ControlFlow::Break(UnsuitedInfo { ty, reason: UnsuitedReason::Array });
1812+
}
1813+
}
18011814
ty::Adt(def, args) => {
18021815
if !def.did().is_local() && !find_attr!(tcx, def.did(), RustcPubTransparent(_)) {
18031816
let non_exhaustive = def.is_variant_list_non_exhaustive()

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: (),
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#![deny(repr_transparent_non_zst_fields)]
2+
3+
type SusArray = [u8; 0];
4+
5+
type GoodArray = [[(); 0]; 42];
6+
7+
pub type Sized = i32;
8+
9+
#[repr(transparent)]
10+
pub struct T1(SusArray);
11+
#[repr(transparent)]
12+
pub struct T2(GoodArray, SusArray);
13+
#[repr(transparent)]
14+
pub struct T3(SusArray, GoodArray);
15+
16+
#[repr(transparent)]
17+
pub struct T5(Sized, SusArray);
18+
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
19+
//~| WARN this was previously accepted by the compiler
20+
21+
#[repr(transparent)]
22+
pub struct T6(SusArray, Sized);
23+
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
24+
//~| WARN this was previously accepted by the compiler
25+
26+
#[repr(transparent)]
27+
pub struct T7(T1, SusArray); // still wrong, even when the array is hidden inside another type
28+
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
29+
//~| WARN this was previously accepted by the compiler
30+
31+
#[repr(transparent)]
32+
pub struct T8(SusArray, SusArray); // still wrong
33+
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
34+
//~| WARN this was previously accepted by the compiler
35+
36+
#[repr(transparent)]
37+
pub struct T9([[u8; 0]; 0], SusArray); // still wrong
38+
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
39+
//~| WARN this was previously accepted by the compiler
40+
41+
fn main() {}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
error: zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
2+
--> $DIR/repr-transparent-array.rs:17:22
3+
|
4+
LL | pub struct T5(Sized, SusArray);
5+
| ^^^^^^^^
6+
|
7+
= note: this field contains `[u8; 0]`, which is an array with a non-trivial element type, so it is not guaranteed to have a trivial ABI in all situations.
8+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
9+
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
10+
note: the lint level is defined here
11+
--> $DIR/repr-transparent-array.rs:1:9
12+
|
13+
LL | #![deny(repr_transparent_non_zst_fields)]
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
16+
error: zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
17+
--> $DIR/repr-transparent-array.rs:22:15
18+
|
19+
LL | pub struct T6(SusArray, Sized);
20+
| ^^^^^^^^
21+
|
22+
= note: this field contains `[u8; 0]`, which is an array with a non-trivial element type, so it is not guaranteed to have a trivial ABI in all situations.
23+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
24+
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
25+
26+
error: zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
27+
--> $DIR/repr-transparent-array.rs:27:19
28+
|
29+
LL | pub struct T7(T1, SusArray); // still wrong, even when the array is hidden inside another type
30+
| ^^^^^^^^
31+
|
32+
= note: this field contains `[u8; 0]`, which is an array with a non-trivial element type, so it is not guaranteed to have a trivial ABI in all situations.
33+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
34+
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
35+
36+
error: zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
37+
--> $DIR/repr-transparent-array.rs:32:25
38+
|
39+
LL | pub struct T8(SusArray, SusArray); // still wrong
40+
| ^^^^^^^^
41+
|
42+
= note: this field contains `[u8; 0]`, which is an array with a non-trivial element type, so it is not guaranteed to have a trivial ABI in all situations.
43+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
44+
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
45+
46+
error: zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
47+
--> $DIR/repr-transparent-array.rs:37:29
48+
|
49+
LL | pub struct T9([[u8; 0]; 0], SusArray); // still wrong
50+
| ^^^^^^^^
51+
|
52+
= note: this field contains `[u8; 0]`, which is an array with a non-trivial element type, so it is not guaranteed to have a trivial ABI in all situations.
53+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
54+
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
55+
56+
error: aborting due to 5 previous errors
57+
58+
Future incompatibility report: Future breakage diagnostic:
59+
error: zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
60+
--> $DIR/repr-transparent-array.rs:17:22
61+
|
62+
LL | pub struct T5(Sized, SusArray);
63+
| ^^^^^^^^
64+
|
65+
= note: this field contains `[u8; 0]`, which is an array with a non-trivial element type, so it is not guaranteed to have a trivial ABI in all situations.
66+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
67+
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
68+
note: the lint level is defined here
69+
--> $DIR/repr-transparent-array.rs:1:9
70+
|
71+
LL | #![deny(repr_transparent_non_zst_fields)]
72+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
73+
74+
Future breakage diagnostic:
75+
error: zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
76+
--> $DIR/repr-transparent-array.rs:22:15
77+
|
78+
LL | pub struct T6(SusArray, Sized);
79+
| ^^^^^^^^
80+
|
81+
= note: this field contains `[u8; 0]`, which is an array with a non-trivial element type, so it is not guaranteed to have a trivial ABI in all situations.
82+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
83+
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
84+
note: the lint level is defined here
85+
--> $DIR/repr-transparent-array.rs:1:9
86+
|
87+
LL | #![deny(repr_transparent_non_zst_fields)]
88+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
89+
90+
Future breakage diagnostic:
91+
error: zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
92+
--> $DIR/repr-transparent-array.rs:27:19
93+
|
94+
LL | pub struct T7(T1, SusArray); // still wrong, even when the array is hidden inside another type
95+
| ^^^^^^^^
96+
|
97+
= note: this field contains `[u8; 0]`, which is an array with a non-trivial element type, so it is not guaranteed to have a trivial ABI in all situations.
98+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
99+
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
100+
note: the lint level is defined here
101+
--> $DIR/repr-transparent-array.rs:1:9
102+
|
103+
LL | #![deny(repr_transparent_non_zst_fields)]
104+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
105+
106+
Future breakage diagnostic:
107+
error: zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
108+
--> $DIR/repr-transparent-array.rs:32:25
109+
|
110+
LL | pub struct T8(SusArray, SusArray); // still wrong
111+
| ^^^^^^^^
112+
|
113+
= note: this field contains `[u8; 0]`, which is an array with a non-trivial element type, so it is not guaranteed to have a trivial ABI in all situations.
114+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
115+
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
116+
note: the lint level is defined here
117+
--> $DIR/repr-transparent-array.rs:1:9
118+
|
119+
LL | #![deny(repr_transparent_non_zst_fields)]
120+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
121+
122+
Future breakage diagnostic:
123+
error: zero-sized fields in `repr(transparent)` cannot contain array types with non-trivial element types
124+
--> $DIR/repr-transparent-array.rs:37:29
125+
|
126+
LL | pub struct T9([[u8; 0]; 0], SusArray); // still wrong
127+
| ^^^^^^^^
128+
|
129+
= note: this field contains `[u8; 0]`, which is an array with a non-trivial element type, so it is not guaranteed to have a trivial ABI in all situations.
130+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
131+
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
132+
note: the lint level is defined here
133+
--> $DIR/repr-transparent-array.rs:1:9
134+
|
135+
LL | #![deny(repr_transparent_non_zst_fields)]
136+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
137+

0 commit comments

Comments
 (0)