From bbfe88276b2a21050f7baed05a253d9c072ff70a Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 28 Apr 2026 13:37:33 -0700 Subject: [PATCH 1/2] type-layout: rewrite `#[repr(C)]` struct layout algorithm Make the code actual compile by defining types, not using a variable named `struct`, adding `mut` where needed, etc. Properly handle the `max()` alignment call returning `Option` rather than `usize` by using 1 for the alignment if there are no fields. Add comments with the parts of the algorithm that are represented by the statements. --- src/type-layout.md | 101 +++++++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 30 deletions(-) diff --git a/src/type-layout.md b/src/type-layout.md index 6d25fbb4f1..7106d68778 100644 --- a/src/type-layout.md +++ b/src/type-layout.md @@ -209,43 +209,84 @@ For each field in declaration order in the struct, first determine the size and Finally, the size of the struct is the current offset rounded up to the nearest multiple of the struct's alignment. -Here is this algorithm described in pseudocode. - - -```rust,ignore -/// Returns the amount of padding needed after `offset` to ensure that the -/// following address will be aligned to `alignment`. -fn padding_needed_for(offset: usize, alignment: usize) -> usize { - let misalignment = offset % alignment; - if misalignment > 0 { - // round up to next multiple of `alignment` - alignment - misalignment - } else { - // already a multiple of `alignment` - 0 - } -} - -struct.alignment = struct.fields().map(|field| field.alignment).max(); +Here is the algorithm: -let current_offset = 0; - -for field in struct.fields_in_declaration_order() { - // Increase the current offset so that it's a multiple of the alignment - // of this field. For the first field, this will always be zero. - // The skipped bytes are called padding bytes. - current_offset += padding_needed_for(current_offset, field.alignment); +```rust +# /// A field of a struct +struct Field { + alignment: usize, + size: usize, +} +# /// User-defined structs +struct UserStruct { +# /// Fields stored in declaration order + fields: Vec, +# /// Offset of each field from the start of the struct + field_offsets: Vec, +# /// Overall alignment + alignment: usize, +# /// Overall size + size: usize, +} - struct[field].offset = current_offset; +impl UserStruct { + /// Returns the amount of padding needed after `offset` to ensure that the + /// following address will be aligned to `alignment`. + fn padding_needed_for(offset: usize, alignment: usize) -> usize { + let misalignment = offset % alignment; + if misalignment > 0 { + // round up to next multiple of `alignment` + alignment - misalignment + } else { + // already a multiple of `alignment` + 0 + } + } - current_offset += field.size; + /// Fields must be in declaration order! + /// By this point, they have already had their alignments and sizes calculated. + pub fn from_fields(fields: Vec) -> Self { + // "The alignment of the struct is the alignment of the most-aligned + // field in it." + let max_alignment = fields.iter().map(|field| field.alignment).max(); + // max_alignment is None iff there are no fields, in which case the + // overall alignment is 1 (the minimum). + let alignment = max_alignment.unwrap_or(1); + + // "Start with a current offset of 0 bytes." + let mut current_offset = 0; + + let mut field_offsets = vec![]; + for field in &fields { + // "If the current offset is not a multiple of the field's alignment, + // then add padding bytes to the current offset until it is a multiple + // of the field's alignment." + current_offset += Self::padding_needed_for( + current_offset, + field.alignment + ); + + // "The offset for the field is what the current offset is now." + field_offsets.push(current_offset); + + // "Then increase the current offset by the size of the field." + current_offset += field.size; + } + + // "Finally, the size of the struct is the current offset rounded up to + // the nearest multiple of the struct's alignment." + let size = current_offset + Self::padding_needed_for( + current_offset, + alignment + ); + + UserStruct { fields, field_offsets, alignment, size } + } } - -struct.size = current_offset + padding_needed_for(current_offset, struct.alignment); ``` > [!WARNING] -> 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`]. +> This mock implementation uses a naive algorithm that ignores overflow issues for the sake of clarity. To perform memory layout computations in actual code, use [`Layout`]. > [!NOTE] > This algorithm can produce zero-sized structs. In C, an empty struct declaration like `struct Foo { }` is illegal. However, both gcc and clang support options to enable such structs, and assign them size zero. C++, in contrast, gives empty structs a size of 1, unless they are inherited from or they are fields that have the `[[no_unique_address]]` attribute, in which case they do not increase the overall size of the struct. From 98e21c526db7ffe8a07ecd5635e66eab98ec0d3b Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 5 May 2026 12:31:03 -0700 Subject: [PATCH 2/2] Add `layout.repr.c.struct.align-empty` --- src/type-layout.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/type-layout.md b/src/type-layout.md index 7106d68778..b837a7d026 100644 --- a/src/type-layout.md +++ b/src/type-layout.md @@ -200,6 +200,9 @@ r[layout.repr.c.struct] r[layout.repr.c.struct.align] The alignment of the struct is the alignment of the most-aligned field in it. +r[layout.repr.c.struct.align-empty] +Structs with no fields are guaranteed to have alignment of 1. + r[layout.repr.c.struct.size-field-offset] The size and offset of fields is determined by the following algorithm. @@ -249,8 +252,7 @@ impl UserStruct { // "The alignment of the struct is the alignment of the most-aligned // field in it." let max_alignment = fields.iter().map(|field| field.alignment).max(); - // max_alignment is None iff there are no fields, in which case the - // overall alignment is 1 (the minimum). + // "Structs with no fields are guaranteed to have alignment of 1." let alignment = max_alignment.unwrap_or(1); // "Start with a current offset of 0 bytes."