@@ -6,29 +6,107 @@ not to change until an RFC ratifies them.
66
77[ #13 ] : https://github.com/rust-rfcs/unsafe-code-guidelines/issues/13
88
9- The only degree of freedom the compiler has when computing the layout of a union
10- like
9+ ### Layout of individual union fields
1110
12- ``` rust,ignore
13- union U { f1: T1, f2: T2 }
11+ A union consists of several variants, one for each field. All variants have the
12+ same size and start at the same memory address, such that in memory the variants
13+ overlap. This can be visualized as follows:
14+
15+ ``` text
16+ [ <--> [field0_ty] <----> ]
17+ [ <----> [field1_ty] <--> ]
18+ [ <---> [field2_ty] <---> ]
1419```
20+ ** Figure 1** (union-field layout): Each row in the picture shows the layout of
21+ the union for each of its variants. The ` <-...-> ` and ` [ ... ] ` denote the
22+ differently-sized gaps and fields, respectively.
23+
24+ The individual fields (` [field{i}_ty_] ` ) are blocks of fixed size determined by
25+ the field's [ layout] . Since we allow creating references to union fields
26+ (` &u.i ` ), the only degrees of freedom the compiler has when computing the layout
27+ of a union are the size of the union, which can be larger than the size of its
28+ largest field, and the offset of each union field within its variant. How these
29+ are picked depends on certain constraints like, for example, the alignment
30+ requirements of the fields, the ` #[repr] ` attribute of the ` union ` , etc.
31+
32+ [ padding ] : ../glossary.md#padding
33+ [ layout ] : ../glossary.md#layout
34+
35+ ### Unions with default layout ("` repr(Rust) ` ")
1536
16- is to determine the offset of the fields. The layout of these fields themselves
17- is already entirely determined by their types, and since we intend to allow
18- creating references to fields (` &u.f1 ` ), unions do not have any wiggle-room
19- there.
37+ Except for the guarantees provided below for some specific cases, the default
38+ layout of Rust unions is, _ in general_ , ** unspecified** .
2039
21- ### Default layout ("repr rust")
40+ That is, there are no _ general_ guarantees about the offset of the fields,
41+ whether all fields have the same offset, what the call ABI of the union is, etc.
2242
23- ** The default layout of unions is not specified.** As of this writing, we want
24- to keep the option of using non-zero offsets open for the future; whether this
25- is useful depends on what exactly the compiler-assumed invariants about union
26- contents are.
43+ <details ><summary ><b >Rationale</b ></summary >
44+
45+ As of this writing, we want to keep the option of using non-zero offsets open
46+ for the future; whether this is useful depends on what exactly the
47+ compiler-assumed invariants about union contents are. This might become clearer
48+ after the [ validity of unions] [ #73 ] is settled.
2749
2850Even if the offsets happen to be all 0, there might still be differences in the
2951function call ABI. If you need to pass unions by-value across an FFI boundary,
3052you have to use ` #[repr(C)] ` .
3153
54+ [ #73 ] : https://github.com/rust-lang/unsafe-code-guidelines/issues/73
55+
56+ </details >
57+
58+ #### Layout of unions with a single non-zero-sized field
59+
60+ The layout of unions with a single non-[ 1-ZST] -field" is the same as the
61+ layout of that field if it has no [ padding] bytes.
62+
63+ For example, here:
64+
65+ ``` rust
66+ # use std :: mem :: {size_of, align_of};
67+ # #[derive(Copy , Clone )]
68+ #[repr(transparent)]
69+ struct SomeStruct (i32 );
70+ # #[derive(Copy , Clone )]
71+ struct Zst ;
72+ union U0 {
73+ f0 : SomeStruct ,
74+ f1 : Zst ,
75+ }
76+ # fn main () {
77+ # assert_eq! (size_of :: <U0 >(), size_of :: <SomeStruct >());
78+ # assert_eq! (align_of :: <U0 >(), align_of :: <SomeStruct >());
79+ # }
80+ ```
81+
82+ the union ` U0 ` has the same layout as ` SomeStruct ` , because ` SomeStruct ` has no
83+ padding bits - it is equivalent to an ` i32 ` due to ` repr(transparent) ` - and
84+ because ` Zst ` is a [ 1-ZST] .
85+
86+ On the other hand, here:
87+
88+ ``` rust
89+ # use std :: mem :: {size_of, align_of};
90+ # #[derive(Copy , Clone )]
91+ struct SomeOtherStruct (i32 );
92+ # #[derive(Copy , Clone )]
93+ #[repr(align(16))] struct Zst2 ;
94+ union U1 {
95+ f0 : SomeOtherStruct ,
96+ f1 : Zst2 ,
97+ }
98+ # fn main () {
99+ # assert_eq! (size_of :: <U1 >(), align_of :: <Zst2 >());
100+ # assert_eq! (align_of :: <U1 >(), align_of :: <Zst2 >());
101+ assert_eq! (align_of :: <Zst2 >(), 16 );
102+ # }
103+ ```
104+
105+ the layout of ` U1 ` is ** unspecified** because:
106+
107+ * ` Zst2 ` is not a [ 1-ZST] , and
108+ * ` SomeOtherStruct ` has an unspecified layout and could contain padding bytes.
109+
32110### C-compatible layout ("repr C")
33111
34112The layout of ` repr(C) ` unions follows the C layout scheme. Per sections
@@ -93,3 +171,5 @@ with no fields. When such types are used as an union field in C++, a "naive"
93171translation of that code into Rust will not produce a compatible result. Refer
94172to the [ struct chapter] ( structs-and-tuples.md#c-compatible-layout-repr-c ) for
95173further details.
174+
175+ [ 1-ZST ] : ../glossary.md#zero-sized-type--zst
0 commit comments