Skip to content

Commit 1fd8ac3

Browse files
committed
Guarantee the default layout of unions with a single field
1 parent 97cbda4 commit 1fd8ac3

File tree

1 file changed

+89
-19
lines changed

1 file changed

+89
-19
lines changed

reference/src/layout/unions.md

+89-19
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,98 @@ 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+
The layout of each union field is determined by its type.
12+
13+
<details><summary><b>Rationale</b></summary>
14+
15+
This is required to allow creating references to union fields:
16+
17+
```rust
18+
# fn main() { unsafe {
19+
# #[derive(Copy, Clone)]
20+
struct T1;
21+
union U { f1: T1 }
22+
let u = U { f1: T1 };
23+
let t1: &T1 = &u.f1;
24+
// &T1 works for all references
25+
# }}
26+
```
27+
</details>
28+
29+
### Unions with default layout ("`repr(Rust)`")
30+
31+
**The default layout of unions is**, in general, **not specified.**
32+
33+
<details><summary><b>Rationale</b></summary>
34+
35+
As of this writing, we want to keep the option of using non-zero offsets open
36+
for the future; whether this is useful depends on what exactly the
37+
compiler-assumed invariants about union contents are. This might become clearer
38+
after the validity of unions
39+
([unsafe-code-guidelines/73](https://github.com/rust-lang/unsafe-code-guidelines/issues/73))
40+
is settled.
41+
42+
Even if the offsets happen to be all 0, there might still be differences in the
43+
function call ABI. If you need to pass unions by-value across an FFI boundary,
44+
you have to use `#[repr(C)]`.
45+
46+
</details>
47+
48+
#### Layout of unions with a single non-zero-sized field
49+
50+
The layout of unions with a single non-zero-sized field is the same as the
51+
layout of that field if:
52+
53+
* that field has no padding bits, and
54+
* the alignment requirement of all zero-sized fields is 1.
55+
56+
For example, here:
57+
58+
```rust
59+
# use std::mem::{size_of, align_of};
60+
# #[derive(Copy, Clone)]
61+
#[repr(transparent)]
62+
struct SomeStruct(i32);
63+
# #[derive(Copy, Clone)]
64+
struct Zst;
65+
union U0 {
66+
f0: SomeStruct,
67+
f1: Zst,
68+
}
69+
# fn main() {
70+
# assert_eq!(size_of::<U0>(), size_of::<SomeStruct>());
71+
# assert_eq!(align_of::<U0>(), align_of::<SomeStruct>());
72+
# }
73+
```
74+
75+
the union `U0` has the same layout as `SomeStruct`, because `SomeStruct` has no
76+
padding bits - it is equivalent to an `i32` due to `repr(transparent)` - and
77+
because the alignment of `Zst` is 1.
78+
79+
On the other hand, here:
80+
81+
```rust
82+
# use std::mem::{size_of, align_of};
83+
# #[derive(Copy, Clone)]
84+
struct SomeOtherStruct(i32);
85+
# #[derive(Copy, Clone)]
86+
#[repr(align(16))] struct Zst2;
87+
union U1 {
88+
f0: SomeOtherStruct,
89+
f1: Zst2,
90+
}
91+
# fn main() {
92+
# assert_eq!(size_of::<U1>(), align_of::<Zst2>());
93+
# assert_eq!(align_of::<U1>(), align_of::<Zst2>());
94+
assert_eq!(align_of::<Zst2>(), 16);
95+
# }
1496
```
1597

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.
98+
the alignment requirement of `Zst2` is not 1, and `SomeOtherStruct` has an
99+
unspecified layout and could contain padding bits. Therefore, the layout of `U1`
100+
is **unspecified**.
20101

21102
### C-compatible layout ("repr C")
22103

@@ -27,14 +108,3 @@ a field type to obtain a pointer to any field, and vice versa.
27108

28109
[6.5.8.5]: http://port70.net/~nsz/c/c11/n1570.html#6.5.8p5
29110
[6.7.2.1.16]: http://port70.net/~nsz/c/c11/n1570.html#6.7.2.1p16
30-
31-
### Default layout ("repr rust")
32-
33-
**The default layout of unions is not specified.** As of this writing, we want
34-
to keep the option of using non-zero offsets open for the future; whether this
35-
is useful depends on what exactly the compiler-assumed invariants about union
36-
contents are.
37-
38-
Even if the offsets happen to be all 0, there might still be differences in the
39-
function call ABI. If you need to pass unions by-value across an FFI boundary,
40-
you have to use `#[repr(C)]`.

0 commit comments

Comments
 (0)