You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/type-layout.md
+105-61
Original file line number
Diff line number
Diff line change
@@ -140,6 +140,29 @@ r[layout.closure]
140
140
141
141
Closures have no layout guarantees.
142
142
143
+
## Aggregate Types
144
+
145
+
r[layout.aggregate]
146
+
147
+
r[layout.aggregate.intro] Aggregate types, `struct`s and `union`s, determine the layout based on its fields, and the [repr][layout.repr] attribute. Each field is categorized by its alignment and size, and an offset within that struct which, by default, is a multiple of its alignment.
148
+
149
+
r[layout.aggregate.struct-offsets] The fields of a `struct` have offsets such that none of the fields overlap within the `struct`.
150
+
151
+
r[layout.aggregate.struct-size-align]
152
+
The size of a `struct` is at least such that each field can be placed within the struct's storage, and the size is a multiple of its alignment. The alignment of a `struct`, unless modified by the [`repr(packed)`][layout.repr.alignment.packed] attribute, is at least the largest alignment of all fields.
153
+
154
+
r[layout.aggregate.union-offsets] The fields of a `union` may have any offset within the `union`. In particular, `union` fields are permitted to overlap within the `union`.
155
+
156
+
> [!NOTE]
157
+
> Typically, union fields are all given offset 0 and are all allocated in the same storage. This is not necessarily guaranteed, however, for [The Rust Representation][layout.repr.rust]
158
+
159
+
r[layout.aggregate.union-size-align] The size of a `union` is at least such that the largest field, and the size is a multiple of the alignment. The alignment of a `union`, unless modified by the [`repr(packed)`][layout.repr.alignment.packed] attribute, is at least the largest alignment of all fields.
160
+
161
+
r[layout.aggregate.repr] The [`repr`][layout.repr] attribute may restrict the valid offsets for each field, as well as the size and alignment of the aggregate type. By [default][layout.repr.rust], the offsets of fields are unspecified.
162
+
163
+
> [!NOTE]
164
+
> The choices of the offsets of fields are made per-struct. Two `struct`s or two `union`s with the same fields using [`repr(Rust)`][layout.repr.rust] are not necessarily layed out the same
165
+
143
166
## Representations
144
167
145
168
r[layout.repr]
@@ -211,33 +234,59 @@ r[layout.repr.rust.intro]
211
234
The `Rust` representation is the default representation for nominal types
212
235
without a `repr` attribute. Using this representation explicitly through a
213
236
`repr` attribute is guaranteed to be the same as omitting the attribute
214
-
entirely.
237
+
entirely.
238
+
239
+
r[layout.repr.rust.aggregate] The layout of aggregate types with this repr is unspecified, such that the guarantees of all `struct` and `union` types are upheld.
240
+
241
+
> [!NOTE]
242
+
> `repr(Rust)` does not provide any guarantees other than the default constraints.
243
+
244
+
#### `repr(Rust)` Enums
245
+
246
+
r[layout.repr.rust.enum]
247
+
248
+
r[layout.repr.rust.enum.intro] Like `struct`s and `union`s with the `Rust` representation, `enum`s typically have no guarantees. The fields of individual variants are layed out with the same rules as a `repr(Rust)` struct, and the `enum` contains sufficient information to determine which variant is being represented.
249
+
250
+
r[layout.repr.rust.enum.variant-fields] Each field of a variant has an offset within the `enum`, such that no two fields of the same variant overlap, and each field of each variant is aligned, unless modified by the [`repr(packed)`][layout.repr.alignment.packed] attribute. Fields of different variants are allowed to overlap, as though each variant appears within a `union` definition.
251
+
252
+
r[layout.repr.rust.enum.size-align] The size of an enum is such that each field of each variant that is not uninhabited can be allocated within the enum at its given offset. The alignment of an enum, unless modified by the [`repr(packed)`][layout.repr.alignment.packed] attribute, is at least the largest alignment of all fields in variants that are inhabited.
253
+
254
+
> [!NOTE]
255
+
> A Rough approximation of a `repr(Rust)` enum is a `repr(Rust)` union containing `repr(Rust)` structs for each variant, with a discriminant field present in each struct.
256
+
> However, there is no guarantee that the discriminant field is any particular size, or exists within the union, nor stores any particular value.
257
+
> Several optimizations can also reduce the size of the `enum` beyond what is valid for a union-of-structs layout, even using the `Rust` representation.
258
+
259
+
> [!NOTE]
260
+
> It is not yet decided whether or not uninhabited variants still need to have storage allocated for their non-zero-sized fields. The documentation here conservatively assumes it is not required.
215
261
216
-
r[layout.repr.rust.layout]
217
-
The only data layout guarantees made by this representation are those required
218
-
for soundness. They are:
262
+
#### Discriminant Ellision
219
263
220
-
1. The fields are properly aligned.
221
-
2. The fields do not overlap.
222
-
3. The alignment of the type is at least the maximum alignment of its fields.
264
+
r[layout.repr.rust.option]
223
265
224
-
r[layout.repr.rust.alignment]
225
-
Formally, the first guarantee means that the offset of any field is divisible by
226
-
that field's alignment.
266
+
r[layout.repr.rust.option.intro] Certain `repr(Rust)` enums are specially layed out when used with certain types. This layout process is known as discriminant elision.
227
267
228
-
r[layout.repr.rust.field-storage]
229
-
The second guarantee means that the fields can be
230
-
ordered such that the offset plus the size of any field is less than or equal to
231
-
the offset of the next field in the ordering. The ordering does not have to be
232
-
the same as the order in which the fields are specified in the declaration of
233
-
the type.
268
+
r[layout.repr.rust.option.elligible] An `enum` type is a *discriminant elision eligible* enum if:
269
+
* It has exactly two variants,
270
+
* One variant has exactly one field, known as the *elision candidate field*, and
271
+
* The other variant has no fields that have size greater than 0 or alignment greater than 1, known as the *elided variant*.
234
272
235
-
Be aware that the second guarantee does not imply that the fields have distinct
236
-
addresses: zero-sized types may have the same address as other fields in the
237
-
same struct.
273
+
> [!NOTE]
274
+
> The determination of whether an `enum` type is *discriminant elision eligible* applies after mono-morphization.
275
+
> In particular, a type like [`core::result::Result<T,E>`] is *discriminant elision eligible* if either `T` or `E` is instantiated with a 1-ZST type, and [`core::option::Option<T>`] is *discriminant elision eligible* always (due to its `None` variant).
238
276
239
-
r[layout.repr.rust.unspecified]
240
-
There are no other guarantees of data layout made by this representation.
277
+
r[layout.repr.rust.option.candidates] The following types are *elision candidate types*:
278
+
*[`&U`][type.pointer.reference.shared], if `U` is a `Sized` type
279
+
*[`&mut U`][type.pointer.reference.mut], if `U` is a `Sized` type
280
+
*[`core::ptr::NonNull<U>`], if `U` is a `Sized` type
281
+
*[`alloc::boxed::Box<U>`], if `U` is a `Sized` type
282
+
*[`core::num::NonZero<T>`] if `T` is an integer type
283
+
* A [function pointer][type.fn-pointer] type
284
+
* A `struct`, defined using [`repr(transparent)`][layout.repr.transparent], which has a field that is an *elision candidate*, and all other fields have size 0 and alignment 1.
285
+
286
+
r[layout.repr.rust.option.elision] If the *elision candidate field* of a *discriminant elision eligible*`enum` has an *elision candidate type*, then the `enum` has the same layout as that field, except that the value consisting of all `0` bytes represents the *elided variant* of the `enum`.
287
+
288
+
> [!NOTE]
289
+
> It is valid for this optimization to be performed for types an `enum`s other than those mentioned above, however, it is only guaranteed for these types and `enum`s.
241
290
242
291
### The `C` Representation
243
292
@@ -256,15 +305,44 @@ r[layout.repr.c.constraint]
256
305
This representation can be applied to structs, unions, and enums. The exception
257
306
is [zero-variant enums] for which the `C` representation is an error.
258
307
308
+
r[layout.repr.c.aggregate] An aggregate type (`struct` or `union`) with the `C` representation is laid out by each field being given by the smallest valid offset for that field that is ascending in declaration order. That is, for a `union`, each field is at offset `0`, and for a struct, the first field is at offset `0`, then the second is at the next offset aligned to the field type. The size of such an aggregate type is the minimum value valid for the type.
309
+
310
+
311
+
r[layout.repr.c.align] An aggregate type with the `C` representation has an alignment equal to the maximum alignment of each of its fields, unless an [alignment modifier][layout.repr.alignment] is present.
312
+
313
+
> [!NOTE]
314
+
> The layout of unions in particular is maximally compact - the size of a `C` representation `union` is the size of its largest field, rounded up to the alignment of the `union`
315
+
316
+
```rust
317
+
// Example of union layouts
318
+
#[repr(C)]
319
+
unionUnion {
320
+
f1:u16,
321
+
f2: [u8; 4],
322
+
}
323
+
324
+
assert_eq!(std::mem::size_of::<Union>(), 4); // From f2
325
+
assert_eq!(std::mem::align_of::<Union>(), 2); // From f1
326
+
327
+
#[repr(C)]
328
+
unionSizeRoundedUp {
329
+
a:u32,
330
+
b: [u16; 3],
331
+
}
332
+
333
+
assert_eq!(std::mem::size_of::<SizeRoundedUp>(), 8); // Size of 6 from b,
334
+
// rounded up to 8 from
335
+
// alignment of a.
336
+
assert_eq!(std::mem::align_of::<SizeRoundedUp>(), 4); // From a
337
+
```
338
+
339
+
259
340
#### `#[repr(C)]` Structs
260
341
261
342
r[layout.repr.c.struct]
262
343
263
-
r[layout.repr.c.struct.align]
264
-
The alignment of the struct is the alignment of the most-aligned field in it.
265
-
266
344
r[layout.repr.c.struct.size-field-offset]
267
-
The size and offset of fields is determined by the following algorithm.
345
+
The specific algorithm for the layout of `repr(C) struct` is given as follows:
> This pseudocode uses a naive algorithm that ignores overflow issues for the sake of clarity. To perform memory layout computations in actual code, use [`Layout`].
317
395
318
-
> Note: This algorithm can produce zero-sized structs. In C, an empty struct
396
+
> [!NOTE]
397
+
> This algorithm can produce zero-sized structs. In C, an empty struct
319
398
> declaration like `struct Foo { }` is illegal. However, both gcc and clang
320
399
> support options to enable such structs, and assign them size zero. C++, in
321
400
> contrast, gives empty structs a size of 1, unless they are inherited from or
322
401
> they are fields that have the `[[no_unique_address]]` attribute, in which
323
402
> case they do not increase the overall size of the struct.
324
403
325
-
#### `#[repr(C)]` Unions
326
-
327
-
r[layout.repr.c.union]
328
-
329
-
r[layout.repr.c.union.intro]
330
-
A union declared with `#[repr(C)]` will have the same size and alignment as an
331
-
equivalent C union declaration in the C language for the target platform.
332
-
333
-
r[layout.repr.c.union.size-align]
334
-
The union will have a size of the maximum size of all of its fields rounded to
335
-
its alignment, and an alignment of the maximum alignment of all of its fields.
336
-
These maximums may come from different fields.
337
-
338
-
```rust
339
-
#[repr(C)]
340
-
unionUnion {
341
-
f1:u16,
342
-
f2: [u8; 4],
343
-
}
344
-
345
-
assert_eq!(std::mem::size_of::<Union>(), 4); // From f2
346
-
assert_eq!(std::mem::align_of::<Union>(), 2); // From f1
347
-
348
-
#[repr(C)]
349
-
unionSizeRoundedUp {
350
-
a:u32,
351
-
b: [u16; 3],
352
-
}
353
-
354
-
assert_eq!(std::mem::size_of::<SizeRoundedUp>(), 8); // Size of 6 from b,
355
-
// rounded up to 8 from
356
-
// alignment of a.
357
-
assert_eq!(std::mem::align_of::<SizeRoundedUp>(), 4); // From a
0 commit comments