From c3a54f9bb69eaee532c4a55c0de8bd6444e7c236 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 25 Sep 2018 17:45:03 -0400 Subject: [PATCH 01/19] add a stucts-and-tuples chapter --- reference/src/SUMMARY.md | 1 + reference/src/representation.md | 5 - .../src/representation/structs-and-tuples.md | 258 ++++++++++++++++++ 3 files changed, 259 insertions(+), 5 deletions(-) create mode 100644 reference/src/representation/structs-and-tuples.md diff --git a/reference/src/SUMMARY.md b/reference/src/SUMMARY.md index 001e2572..45a8fea8 100644 --- a/reference/src/SUMMARY.md +++ b/reference/src/SUMMARY.md @@ -14,5 +14,6 @@ - [Unions](./active_discussion/unions.md) - [Uninitialized memory](./active_discussion/uninitialized_memory.md) - [Data representation](./representation.md) + - [Structs and tuples](./representation/structs-and-tuples.md) - [Optimizations](./optimizations.md) - [Optimizing immutable memory](./optimizations/immutable_memory.md) diff --git a/reference/src/representation.md b/reference/src/representation.md index bdfb13d1..8e7fe35e 100644 --- a/reference/src/representation.md +++ b/reference/src/representation.md @@ -6,11 +6,6 @@ https://github.com/rust-rfcs/unsafe-code-guidelines/issues/9 -## Representation of structs and tuples - -https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11 -https://github.com/rust-rfcs/unsafe-code-guidelines/issues/12 - ## Representation of enums https://github.com/rust-rfcs/unsafe-code-guidelines/issues/10 diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md new file mode 100644 index 00000000..98240682 --- /dev/null +++ b/reference/src/representation/structs-and-tuples.md @@ -0,0 +1,258 @@ +# Representation of structs and tuples + +**Disclaimer:** This chapter represents the consensus from issues +[#11] and [#12]. The statements in here are not (yet) "guaranteed" +not to change. + +[#11]: https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11 +[#12]: https://github.com/rust-rfcs/unsafe-code-guidelines/issues/12 + +## Tuple types + +In general, an anonymous tuple type `(T1..Tn)` of arity N is laid out +"as if" there were a corresponding tuple struct declared in libcore: + +```rust +#[repr(Rust)] +struct TupleN(P1..Pn); +``` + +In this case, `(T1..Tn)` would be compatible with `TupleN`. +As discussed below, this generally means that the compiler is **free +to re-order field layout** as it wishes. Thus, if you would like a +guaranteed layout from a tuple, you are generally advised to create a +named struct with a `#[repr(C)]` annotation (see [the section on +structs for more details](#structs)). + +There is one exception: if all N fields of the tuple are of the same +type `T` (with lifetime erased), then the tuple is guaranteed to be +laid out as the fixed-length array type `[T; N]` (with the numbered +tuple fields placed in the corresponding indices as expected). This +permits such tuples to be transmuted and then indexed using an integer +index.[^exception] + +[^exception]: [Proposed in this comment](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/12#issuecomment-417680324). + +### Other notes on tuples + +Some related discussion: + +- [RFC #1582](https://github.com/rust-lang/rfcs/pull/1582) proposed + that tuple structs should have a "nested representation", where + e.g. `(T1, T2, T3)` would in fact be laid out as `(T1, (T2, + T3))`. The purpose of this was to permit variadic matching and so + forth against some suffix of the struct. This RFC was not accepted, + however. This lay out requires extra padding and seems somewhat + surprising: it means that the layout of tuples and tuple structs + would diverge significantly from structs with named fields. This + proposal is also incompatible with the guaranteed array layout + described above. + + + +## Struct types + +Structs come in two principle varieties: + +```rust +// Structs with named fields +struct Foo { f1: T1, .., fn: Tn } + +// Tuple structs +struct Foo(T1, .., Tn) +``` + +In general, tuple structs can be understood as equivalent to named +structs declared with the same order; therefore, the two declarations +of `Foo` in the previous example are treated equivalently. + +Structs can have various `#[repr]` flags that influence their layout: + +- `#[repr(Rust)]` -- the default. +- `#[repr(C)]` -- request C compatibility +- `#[repr(align(N))]` -- specify the alignment +- `#[repr(packed)]` -- request packed layout where fields are not internally aligned +- `#[repr(transparent)]` -- request that a single-field struct be + treated "as if" it were an instance of its field type when passed as + an argument + +### Default layout ("repr rust") + +The default layout of structs is undefined and subject to change +between compiler revisions. We further do not guarantee that two +structs with different names (but the same field types) will be laid +out in the same way (for example, the hypothetical struct representing +tuples ). Finally, the presence or absence of generics can make a +difference (e.g., `struct Foo { x: u16, y: u32 }` and `struct Foo { +x: u16, y: T }` where `T = u32` are not guaranteed to be identical), +owing to the possibility of unsizing coercions. + +**Compiler's current behavior.** As of the time of this writing, the +compiler will reorder struct fields to minimize the overall size of +the struct (and in particular to eliminate padding due to alignment +restrictions). The final field, however, is not reordered if an +unsizing coercion may be applied. + +### C-compatible layout ("repr C") + +For structs tagged `#[repr(C)]`, the compiler will apply a C-like +layout scheme (see section 6.7.2.1 of the [C17 specification][C17] for +a detailed write-up): + +[C17]: http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf + +- Field order is preserved. +- The first field begins at offset 0. +- Assuming the struct is not packed, each field's offset is aligned to + the natural alignment for that field's type, possibly creating + unused padding bits. +- The total size of the struct is rounded up to its overall alignemnt. + +The intention is that if one has a set of C struct declarations and a +corresponding set of Rust struct declarations, all of which are tagged +with `#[repr(C)]`, then the layout of those structs will all be +identical. Note that this setup implies that none of the structs in +question can contain any `#[repr(Rust)]` structs (or Rust tuples), as +those would have no corresponding C struct declaration -- as +`#[repr(Rust)]` types have undefined layout, you cannot safely declare +their layout in a C program. + +See also the notes on ABI compatibility under the section on `#[repr(transparent)]`. + +### Fixed alignment + +The `#[repr(align(N))]` attribute may be used to raise the alignment +of a struct, as described in [The Rust Reference][TRR-align]. + +[TRR-align]: (https://doc.rust-lang.org/stable/reference/type-layout.html#the-align-representation). + +### Packed layout + +The `#[repr(packed)]` attribute may be used to remove all padding from +the struct. The resulting fields may not fall at properly aligned +boundaries in memory. This makes it unsafe to create a Rust reference +(`&T` or `&mut T`) to those fields, as the compiler requires that all +reference values must always be aligned (so that it can use more +efficient load/store instructions at runtime). See the [Rust reference +for more details][TRR-packed]. + +[TRR-packed]: https://doc.rust-lang.org/stable/reference/type-layout.html#the-packed-representation + +### ABI Compatibility + +In general, when invoking functions that use the C ABI, `#[repr(C)]` +structs are guaranteed to be passed in the same way as their +corresponding C counterpart (presuming one exists). `#[repr(Rust)]` +structs have no such guarantee. This means that if you have an `extern +"C"` function, you cannot pass a `#[repr(Rust)]` struct as one of its +arguments. Instead, one would typically pass `#[repr(C)]` structs (or +possibly pointers to Rust-structs, if those structs are opaque on the +other side, or the callee is defined in Rust). + +However, there is a subtle point about C ABIs: in some C ABIs, passing +a struct with one field of type `T` as an argument is **not** +equivalent to just passing a value of type `T`. So e.g. if you have a +C function that is defined to take a `uint32_t`: + +```C +void some_function(uint32_t value) { .. } +``` + +It is **incorrect** to pass in a struct as that value, even if that +struct is `#[repr(C)`] and has only one field: + +```rust +#[repr(C)] +struct Foo { x: u32 } + +extern "C" some_function(Foo); + +some_function(Foo { x: 22 }); // Bad! +``` + +Instead, you should declare the struct with `#[repr(transparent)]`, +which specifies that `Foo` should use the ABI rules for its field +type, `u32`. This is useful when using "wrapper structs" in Rust to +give stronger typing guarantees. + +(Note further that the Rust ABI is undefined and theoretically may +vary from compiler revision to compiler revision.) + +## Unresolved question: Guaranteeing compatible layouts? + +One key unresolved question was whether we would want to guarantee +that two `#[repr(Rust)]` structs whose fields have the same types are +laid out in a "compatible" way, such that one could be transmuted to +the other. @rkruppe laid out a [number of +examples](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11#issuecomment-419956939) +where this might be a reasonable thing to expect. As currently +written, and in an effort to be conservative, we make no such +guarantee, though we do not firmly rule out doing such a thing in the future. + +It seems like it may well be desirable to -- at minimum -- guarantee +that `#[repr(Rust)]` layout is "some deterministic function of the +struct declaration and the monomorphized types of its fields". Note +that it is not sufficient to consider the monomorphized type of a +struct's fields: due to unsizing coercions, it matters whether the +struct is declared in a generic way or not, since the "unsized" field +must presently be [laid out last in the +structure](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/12#issuecomment-417843595). (Note +that tuples are always coercible (see [#42877] for more information), +and are always declared as generics.) This implies that our +"deterministic function" also takes as input the form in which the +fields are declared in the struct. + +However, that rule is not true today. For example, the compiler +includes an option (called "optimization fuel") that will enable us to +alter the layout of only the "first N" structs declared in the +source. When one is accidentally relying on the layout of a structure, +this can be used to track down the struct that is causing the problem. + +[#42877]: https://github.com/rust-lang/rust/issues/42877 +[pg-unsized-tuple]: https://play.rust-lang.org/?gist=46399bb68ac685f23beffefc014203ce&version=nightly&mode=debug&edition=2015 + +There are also benefits also to having fewer guarantees. For example: + +- Code hardening tools can be used to randomize the layout of individual structs. +- Profile-guided optimization might analyze how instances of a +particular struct are used and tweak the layout (e.g., to insert +padding and reduce false sharing). + - However, there aren't many tools that do this sort of thing +([1](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11#issuecomment-420650851), +[2](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11#issuecomment-420681763)). Moreover, +it would probably be better for the tools to merely recommend +annotations that could be added +([1](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11#issuecomment-420077105), +[2](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11#issuecomment-420077105)), +such that the knowledge of the improved layouts can be recorded in the +source. + +As a more declarative alternative, @alercah [proposed a possible +extension](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/12#issuecomment-420165155) +that would permit one to declare that the layout of two structs or +types are compatible (e.g., `#[repr(as(Foo))] struct Bar { .. }`), +thus permitting safe transmutes (and also ABI compatibility). One +might also use some weaker form of `#[repr(C)]` to specify a "more +deterministic" layout. These areas need future exploration. + +## Counteropinions and other notes + +@joshtrippler [argued against reordering struct +fields](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11#issuecomment-417953576), +suggesting instead it would be better if users reordering fields +themselves. However, there are a number of downsides to such a +proposal (and -- further -- it does not match our existing behavior): + +- In a generic struct, the [best ordering of fields may not be known + ahead of + time](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11#issuecomment-420659840), + so the user cannot do it manually. +- If layout is defined, then it becomes part of your API, such taht + reordering fields is a breaking change for your clients (if we + consider unsafe code that may rely on the layout, then this applies + [even to structs with named + fields](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11#issuecomment-420117856). +- Many people would prefer the name ordering to be chosen for + "readability" and not optimal layout. + +## Footnotes From 08062bd0633db99abf5c0964d2b3d0119ef1537e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 26 Sep 2018 10:17:16 -0400 Subject: [PATCH 02/19] add nits from eddyb --- .../src/representation/structs-and-tuples.md | 71 ++++++++++++++----- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index 98240682..eab3ae94 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -14,7 +14,7 @@ In general, an anonymous tuple type `(T1..Tn)` of arity N is laid out ```rust #[repr(Rust)] -struct TupleN(P1..Pn); +struct TupleN(P1..Pn); ``` In this case, `(T1..Tn)` would be compatible with `TupleN`. @@ -33,6 +33,13 @@ index.[^exception] [^exception]: [Proposed in this comment](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/12#issuecomment-417680324). +Note that the final element of a tuple (`Pn`) is marked as `?Sized` to +permit unsized tuple coercion -- this is implemented on nightly but is +currently unstable ([tracking issue][#42877]). In the future, we may +extend unsizing to other elements of tuples as well. + +[#42877]: https://github.com/rust-lang/rust/issues/42877 + ### Other notes on tuples Some related discussion: @@ -59,12 +66,25 @@ Structs come in two principle varieties: struct Foo { f1: T1, .., fn: Tn } // Tuple structs -struct Foo(T1, .., Tn) +struct Foo(T1, .., Tn); ``` -In general, tuple structs can be understood as equivalent to named -structs declared with the same order; therefore, the two declarations -of `Foo` in the previous example are treated equivalently. +In terms of their layout, tuple structs can be understood as +equivalent to a named struct with fields named `0..n-1`: + +```rust +struct Foo { + 0: T1, + ... + n-1: Tn +} +``` + +(In fact, one may use such field names in patterns or in accessor +expressions like `foo.0`.) + +Field names are not relevant to layout: changing the name of a field +in a struct will never affect its layout. Structs can have various `#[repr]` flags that influence their layout: @@ -72,9 +92,9 @@ Structs can have various `#[repr]` flags that influence their layout: - `#[repr(C)]` -- request C compatibility - `#[repr(align(N))]` -- specify the alignment - `#[repr(packed)]` -- request packed layout where fields are not internally aligned -- `#[repr(transparent)]` -- request that a single-field struct be - treated "as if" it were an instance of its field type when passed as - an argument +- `#[repr(transparent)]` -- request that a "wrapper struct" be treated + "as if" it were an instance of its field type when passed as an + argument ### Default layout ("repr rust") @@ -103,10 +123,12 @@ a detailed write-up): - Field order is preserved. - The first field begins at offset 0. -- Assuming the struct is not packed, each field's offset is aligned to - the natural alignment for that field's type, possibly creating +- Assuming the struct is not packed, each field's offset is aligned[^aligned] to + the ABI-mandated alignment for that field's type, possibly creating unused padding bits. -- The total size of the struct is rounded up to its overall alignemnt. +- The total size of the struct is rounded up to its overall alignment. + +[^aligned]: Aligning an offset O to an alignment A means to round up the offset O until it is a multiple of the alignment A. The intention is that if one has a set of C struct declarations and a corresponding set of Rust struct declarations, all of which are tagged @@ -128,13 +150,19 @@ of a struct, as described in [The Rust Reference][TRR-align]. ### Packed layout -The `#[repr(packed)]` attribute may be used to remove all padding from -the struct. The resulting fields may not fall at properly aligned -boundaries in memory. This makes it unsafe to create a Rust reference -(`&T` or `&mut T`) to those fields, as the compiler requires that all -reference values must always be aligned (so that it can use more -efficient load/store instructions at runtime). See the [Rust reference -for more details][TRR-packed]. +The `#[repr(packed(N))]` attribute may be used to impose a maximum +limit on the alignments for individual fields. It is most commonly +used with an alignment of 1, which makes the struct as small as +possible. For example, in a `#[repr(packed(2))]` struct, a `u8` or +`u16` would be aligned at 1- or 2-bytes respectively (as normal), but +a `u32` would be aligned at only 2 bytes instead of 4. + +The resulting fields may not fall at properly aligned boundaries in +memory. This makes it unsafe to create a Rust reference (`&T` or `&mut +T`) to those fields, as the compiler requires that all reference +values must always be aligned (so that it can use more efficient +load/store instructions at runtime). See the [Rust reference for more +details][TRR-packed]. [TRR-packed]: https://doc.rust-lang.org/stable/reference/type-layout.html#the-packed-representation @@ -175,6 +203,13 @@ which specifies that `Foo` should use the ABI rules for its field type, `u32`. This is useful when using "wrapper structs" in Rust to give stronger typing guarantees. +`#[repr(transparent)]` cannot be applied to *any* struct. It is +limited to structs with a single field whose type `T` has non-zero +size, along with some number of other fields whose types are all +zero-sized (typically `std::marker::PhantomData` fields). The struct +then takes on the "ABI behavior" of the type `T` that has non-zero +size. + (Note further that the Rust ABI is undefined and theoretically may vary from compiler revision to compiler revision.) From eb951c231dac7b9e91409a41fad5d065d7e1dc94 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 11 Oct 2018 08:44:43 -0400 Subject: [PATCH 03/19] correct typo --- reference/src/representation/structs-and-tuples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index eab3ae94..d6f33132 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -246,7 +246,7 @@ this can be used to track down the struct that is causing the problem. [#42877]: https://github.com/rust-lang/rust/issues/42877 [pg-unsized-tuple]: https://play.rust-lang.org/?gist=46399bb68ac685f23beffefc014203ce&version=nightly&mode=debug&edition=2015 -There are also benefits also to having fewer guarantees. For example: +There are also benefits to having fewer guarantees. For example: - Code hardening tools can be used to randomize the layout of individual structs. - Profile-guided optimization might analyze how instances of a From d5a144b3bf00b2437b8802681176736279f3866e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 11 Oct 2018 08:45:08 -0400 Subject: [PATCH 04/19] strengthen to "subject to change between compilations" --- reference/src/representation/structs-and-tuples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index d6f33132..f092cf0b 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -99,7 +99,7 @@ Structs can have various `#[repr]` flags that influence their layout: ### Default layout ("repr rust") The default layout of structs is undefined and subject to change -between compiler revisions. We further do not guarantee that two +between individual compilations. We further do not guarantee that two structs with different names (but the same field types) will be laid out in the same way (for example, the hypothetical struct representing tuples ). Finally, the presence or absence of generics can make a From 569338b21f8fa93dab73a9f7995fbb9de7524881 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 11 Oct 2018 08:47:17 -0400 Subject: [PATCH 05/19] add note that packed adjusts alignment of the struct as a whole --- reference/src/representation/structs-and-tuples.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index f092cf0b..66444523 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -155,7 +155,9 @@ limit on the alignments for individual fields. It is most commonly used with an alignment of 1, which makes the struct as small as possible. For example, in a `#[repr(packed(2))]` struct, a `u8` or `u16` would be aligned at 1- or 2-bytes respectively (as normal), but -a `u32` would be aligned at only 2 bytes instead of 4. +a `u32` would be aligned at only 2 bytes instead of 4. In the absence +of an explicit `#[repr(align)]` directive, `#[repr(packed(N))]` also +sets the alignment for the struct as a whole to N bytes. The resulting fields may not fall at properly aligned boundaries in memory. This makes it unsafe to create a Rust reference (`&T` or `&mut From d96723f8639522c809e3a1b7e77dacb4c5edd25e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 11 Oct 2018 08:48:51 -0400 Subject: [PATCH 06/19] `[T; 3]` and `(T, (T, T))` would have the same memory layout. --- reference/src/representation/structs-and-tuples.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index 66444523..f386d456 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -51,9 +51,7 @@ Some related discussion: forth against some suffix of the struct. This RFC was not accepted, however. This lay out requires extra padding and seems somewhat surprising: it means that the layout of tuples and tuple structs - would diverge significantly from structs with named fields. This - proposal is also incompatible with the guaranteed array layout - described above. + would diverge significantly from structs with named fields. From 497419267d224e4e3e3eb70b3b4dca1be744ac70 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 11 Oct 2018 08:52:10 -0400 Subject: [PATCH 07/19] rename ABI compatibility to "function call ABI compatibility" --- reference/src/representation/structs-and-tuples.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index f386d456..0248f4b1 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -137,7 +137,7 @@ those would have no corresponding C struct declaration -- as `#[repr(Rust)]` types have undefined layout, you cannot safely declare their layout in a C program. -See also the notes on ABI compatibility under the section on `#[repr(transparent)]`. +See also the notes on [ABI compatibility](#fnabi) under the section on `#[repr(transparent)]`. ### Fixed alignment @@ -166,7 +166,9 @@ details][TRR-packed]. [TRR-packed]: https://doc.rust-lang.org/stable/reference/type-layout.html#the-packed-representation -### ABI Compatibility + + +### Function call ABI compatibility In general, when invoking functions that use the C ABI, `#[repr(C)]` structs are guaranteed to be passed in the same way as their @@ -272,7 +274,7 @@ deterministic" layout. These areas need future exploration. ## Counteropinions and other notes -@joshtrippler [argued against reordering struct +@joshtripplet [argued against reordering struct fields](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11#issuecomment-417953576), suggesting instead it would be better if users reordering fields themselves. However, there are a number of downsides to such a From 89a8a703a6af709a234bf94b0ab75f692d272c91 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 11 Oct 2018 08:57:07 -0400 Subject: [PATCH 08/19] rework to call out details *not* relevant to layout --- .../src/representation/structs-and-tuples.md | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index 0248f4b1..d46095c3 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -81,9 +81,6 @@ struct Foo { (In fact, one may use such field names in patterns or in accessor expressions like `foo.0`.) -Field names are not relevant to layout: changing the name of a field -in a struct will never affect its layout. - Structs can have various `#[repr]` flags that influence their layout: - `#[repr(Rust)]` -- the default. @@ -94,6 +91,25 @@ Structs can have various `#[repr]` flags that influence their layout: "as if" it were an instance of its field type when passed as an argument +### Details *not* relevant to layout + +Although the compiler is free to choose the layout of structs as it +likes, we do put some restrictions on it: + +- Layout must be **deterministic** -- if you compile the same program + twice (with the same compilation settings), the layout of the + structs in the program should not change. + - Open question: What sorts of changes can cause layout to change? + Do you have to make changes to the types (transitively) included + in the struct? Or can any sort of change suffice. +- **Field names** are not relevant to layout: changing the name of a field + in a struct will never affect its layout. +- **Privacy** is not relevant to layout: changing whether a field is + public or private does not affect layout, although it may affect who + can observe the layout (e.g., in a `#[repr(C)]` struct, if fields + are public, then one may find that client libraries are relying on + the order of fields in your struct in some fashion). + ### Default layout ("repr rust") The default layout of structs is undefined and subject to change From c7d728e48c3d40e0a0b0d764c5aeae948ed0e760 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 11 Oct 2018 13:26:37 -0400 Subject: [PATCH 09/19] make it clear that C17 etc are the "normative" spec for `#[repr(C)]` --- reference/src/representation/structs-and-tuples.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index d46095c3..79960964 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -130,8 +130,9 @@ unsizing coercion may be applied. ### C-compatible layout ("repr C") For structs tagged `#[repr(C)]`, the compiler will apply a C-like -layout scheme (see section 6.7.2.1 of the [C17 specification][C17] for -a detailed write-up): +layout scheme. See section 6.7.2.1 of the [C17 specification][C17] for +a detailed write-up of what such rules entail. For most platforms, +however, this means the following: [C17]: http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf From 65f65f9edde62745932413d39e04ee88b5af2594 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 11 Oct 2018 13:27:19 -0400 Subject: [PATCH 10/19] extract controversial unresolved questions --- .../src/representation/structs-and-tuples.md | 98 ++++++++++++------- 1 file changed, 60 insertions(+), 38 deletions(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index 79960964..f7d10b6c 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -24,15 +24,6 @@ guaranteed layout from a tuple, you are generally advised to create a named struct with a `#[repr(C)]` annotation (see [the section on structs for more details](#structs)). -There is one exception: if all N fields of the tuple are of the same -type `T` (with lifetime erased), then the tuple is guaranteed to be -laid out as the fixed-length array type `[T; N]` (with the numbered -tuple fields placed in the corresponding indices as expected). This -permits such tuples to be transmuted and then indexed using an integer -index.[^exception] - -[^exception]: [Proposed in this comment](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/12#issuecomment-417680324). - Note that the final element of a tuple (`Pn`) is marked as `?Sized` to permit unsized tuple coercion -- this is implemented on nightly but is currently unstable ([tracking issue][#42877]). In the future, we may @@ -91,35 +82,33 @@ Structs can have various `#[repr]` flags that influence their layout: "as if" it were an instance of its field type when passed as an argument -### Details *not* relevant to layout - -Although the compiler is free to choose the layout of structs as it -likes, we do put some restrictions on it: - -- Layout must be **deterministic** -- if you compile the same program - twice (with the same compilation settings), the layout of the - structs in the program should not change. - - Open question: What sorts of changes can cause layout to change? - Do you have to make changes to the types (transitively) included - in the struct? Or can any sort of change suffice. -- **Field names** are not relevant to layout: changing the name of a field - in a struct will never affect its layout. -- **Privacy** is not relevant to layout: changing whether a field is - public or private does not affect layout, although it may affect who - can observe the layout (e.g., in a `#[repr(C)]` struct, if fields - are public, then one may find that client libraries are relying on - the order of fields in your struct in some fashion). - ### Default layout ("repr rust") -The default layout of structs is undefined and subject to change -between individual compilations. We further do not guarantee that two -structs with different names (but the same field types) will be laid -out in the same way (for example, the hypothetical struct representing -tuples ). Finally, the presence or absence of generics can make a -difference (e.g., `struct Foo { x: u16, y: u32 }` and `struct Foo { -x: u16, y: T }` where `T = u32` are not guaranteed to be identical), -owing to the possibility of unsizing coercions. +The default layout of structs is not specified. Effectively, the +compiler provdes a deterministic function per struct definition that +defines its layout. This function may as principle take as input the +entire input program. Therefore: + +- the types of the struct's fields +- the layout of other structs (including structs not included within this struct) +- compiler settings + +As of this writing, we have not reached a full consensus on what +limitations should exist on possible field struct layouts. Therefore, +the default layout of structs is considered undefined and subject to +change between individual compilations. This implies that (among other +things) two structs with the same field types may not be laid out in +the same way (for example, the hypothetical struct representing tuples +may be laid out differently from user-declared structs). + +Further, the layout of structs is always defined relative to the +**struct definition** plus a substitution supplying values for each of +the struct's generic types (in contrast to just considering the fully +monomorphized field types). This is necessary because the presence or +absence of generics can make a difference (e.g., `struct Foo { x: u16, +y: u32 }` and `struct Foo { x: u16, y: T }` where `T = u32` are not +guaranteed to be identical), owing to the possibility of unsizing +coercions. **Compiler's current behavior.** As of the time of this writing, the compiler will reorder struct fields to minimize the overall size of @@ -127,12 +116,45 @@ the struct (and in particular to eliminate padding due to alignment restrictions). The final field, however, is not reordered if an unsizing coercion may be applied. +#### Unresolved questions + +During the course of the discussion in [#11] and [#12], various +suggestions arose to limit the compiler's flexibility. These questions +are currently considering **unresolved** and -- for each of them -- an +issue has been opened for further discussion on the repository. This +section documents the questions and gives a few light details, but the +reader is referred to the issues for further discussion. + +**Single-field structs.** If you have a struct with single field +(`struct Foo { x: T }`), should we guarantee that the memory layout of +`Foo` is identical to the memory layout of `T` (note that ABI details +around function calls may still draw a distinction, which is why +`#[repr(transparent)]` is needed). What about zero-sized types like +`PhantomData`? + +**Homogeneous structs.** If you have homogeneous structs, where all +the `N` fields are of a single type `T`, can we guarantee a mapping to +the memory layout of `[T; N]`? How do we map between the field names +and the indices? What about zero-sized types? + +**Deterministic layout.** Can we say that layout is some deterministic +function of a certain, fixed set of inputs? This would allow you to be +sure that if you do not alter those inputs, your struct layout would +not change, even if it meant that you can't predict precisely what it +will be. For example, we might say that struct layout is a function of +the struct's generic types and its substitutions, full stop -- this +would imply that any two structs with the same definition are laid out +the same. This might interfere with our ability to do profile-guided +layout or to analyze how a struct is used and optimize based on +that. Some would call that a feature. + ### C-compatible layout ("repr C") For structs tagged `#[repr(C)]`, the compiler will apply a C-like layout scheme. See section 6.7.2.1 of the [C17 specification][C17] for -a detailed write-up of what such rules entail. For most platforms, -however, this means the following: +a detailed write-up of what such rules entail (as well as the relevant +specs for your platform). For most platforms, however, this means the +following: [C17]: http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf From fbafc2dcf026e21bc15b00579c181b455bfc32c7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 11 Oct 2018 13:28:17 -0400 Subject: [PATCH 11/19] clarify wording --- reference/src/representation/structs-and-tuples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index f7d10b6c..892604e5 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -2,7 +2,7 @@ **Disclaimer:** This chapter represents the consensus from issues [#11] and [#12]. The statements in here are not (yet) "guaranteed" -not to change. +not to change until an RFC ratifies them. [#11]: https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11 [#12]: https://github.com/rust-rfcs/unsafe-code-guidelines/issues/12 From fbf35bf40dcca64c559fb5d0b1d2a394bc2606d8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 11 Oct 2018 13:43:44 -0400 Subject: [PATCH 12/19] assign issues --- .../src/representation/structs-and-tuples.md | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index 892604e5..2e4d6233 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -125,19 +125,32 @@ issue has been opened for further discussion on the repository. This section documents the questions and gives a few light details, but the reader is referred to the issues for further discussion. -**Single-field structs.** If you have a struct with single field +**Zero-sized structs ([#37]).** If you have a struct which -- +transitively -- contains no data of non-zero size, then the size of +that struct will be zero as well. These zero-sized structs appear +frequently as exceptions in other layout considerations (e.g., +single-field structs). An example of such a struct is +`std::marker::PhantomData`. + +[#37]: https://github.com/rust-rfcs/unsafe-code-guidelines/issues/37 + +**Single-field structs ([#34]).** If you have a struct with single field (`struct Foo { x: T }`), should we guarantee that the memory layout of `Foo` is identical to the memory layout of `T` (note that ABI details around function calls may still draw a distinction, which is why `#[repr(transparent)]` is needed). What about zero-sized types like `PhantomData`? -**Homogeneous structs.** If you have homogeneous structs, where all +[#34]: https://github.com/rust-rfcs/unsafe-code-guidelines/issues/34 + +**Homogeneous structs ([#36]).** If you have homogeneous structs, where all the `N` fields are of a single type `T`, can we guarantee a mapping to the memory layout of `[T; N]`? How do we map between the field names and the indices? What about zero-sized types? -**Deterministic layout.** Can we say that layout is some deterministic +[#36]: https://github.com/rust-rfcs/unsafe-code-guidelines/issues/36 + +**Deterministic layout ([#35]).** Can we say that layout is some deterministic function of a certain, fixed set of inputs? This would allow you to be sure that if you do not alter those inputs, your struct layout would not change, even if it meant that you can't predict precisely what it @@ -148,6 +161,8 @@ the same. This might interfere with our ability to do profile-guided layout or to analyze how a struct is used and optimize based on that. Some would call that a feature. +[#35]: https://github.com/rust-rfcs/unsafe-code-guidelines/issues/35 + ### C-compatible layout ("repr C") For structs tagged `#[repr(C)]`, the compiler will apply a C-like From 97622bc42fc7f610972e2c1ff2da89e9d933b439 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 23 Oct 2018 12:29:39 -0400 Subject: [PATCH 13/19] generalize "input program" to "input to compiler" Also, fix the dangling "therefore". --- reference/src/representation/structs-and-tuples.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index 2e4d6233..e41e1bab 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -87,11 +87,14 @@ Structs can have various `#[repr]` flags that influence their layout: The default layout of structs is not specified. Effectively, the compiler provdes a deterministic function per struct definition that defines its layout. This function may as principle take as input the -entire input program. Therefore: +entire input to the compiler. Therefore, any of the the following might +influence layout: - the types of the struct's fields - the layout of other structs (including structs not included within this struct) - compiler settings +- the results of profile information which are given to the compiler + for the purpose of PGO etc As of this writing, we have not reached a full consensus on what limitations should exist on possible field struct layouts. Therefore, From b0c86ea1b00150991ae97e6a79487d76a44a5a02 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 23 Oct 2018 12:37:46 -0400 Subject: [PATCH 14/19] be clearer about when reordering fields would be a breaking change --- .../src/representation/structs-and-tuples.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index e41e1bab..0f6754d6 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -341,11 +341,19 @@ proposal (and -- further -- it does not match our existing behavior): ahead of time](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11#issuecomment-420659840), so the user cannot do it manually. -- If layout is defined, then it becomes part of your API, such taht - reordering fields is a breaking change for your clients (if we - consider unsafe code that may rely on the layout, then this applies - [even to structs with named - fields](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11#issuecomment-420117856). +- If layout is defined, and a library exposes a struct with all public + fields, then clients may be more likely to assume that the layout of + that struct is stable. If they were to write unsafe code that relied + on this assumption, that would break if fields were reordered. But + libraries may well expect the freedom to reorder fields. This case + is weakened because of the requirement to write unsafe code (after + all, one can always write unsafe code that relies on virtually any + implementation detail); if we were to permit **safe** casts that + rely on the layout, then reordering fields would clearly be a + breaking change (see also [this + comment](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/11#issuecomment-420117856) + and [this + thread](https://github.com/rust-rfcs/unsafe-code-guidelines/pull/31#discussion_r224955817)). - Many people would prefer the name ordering to be chosen for "readability" and not optimal layout. From f146fc4e72857f413bde6f4c49b4a84c5681208c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 23 Oct 2018 12:39:52 -0400 Subject: [PATCH 15/19] rephrase "any" struct for clarity --- reference/src/representation/structs-and-tuples.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index 0f6754d6..c3d94468 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -262,12 +262,11 @@ which specifies that `Foo` should use the ABI rules for its field type, `u32`. This is useful when using "wrapper structs" in Rust to give stronger typing guarantees. -`#[repr(transparent)]` cannot be applied to *any* struct. It is -limited to structs with a single field whose type `T` has non-zero -size, along with some number of other fields whose types are all -zero-sized (typically `std::marker::PhantomData` fields). The struct -then takes on the "ABI behavior" of the type `T` that has non-zero -size. +`#[repr(transparent)]` can only be applied to structs with a single +field whose type `T` has non-zero size, along with some number of +other fields whose types are all zero-sized (typically +`std::marker::PhantomData` fields). The struct then takes on the "ABI +behavior" of the type `T` that has non-zero size. (Note further that the Rust ABI is undefined and theoretically may vary from compiler revision to compiler revision.) From 72f5db3846b875cae140b7ec4a3ea33e9b9b583d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 23 Oct 2018 13:02:36 -0400 Subject: [PATCH 16/19] rework "default layout" to guarantee nothing for now per rkruppe's points --- .../src/representation/structs-and-tuples.md | 63 ++++++++++--------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index c3d94468..0bd2d094 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -84,40 +84,43 @@ Structs can have various `#[repr]` flags that influence their layout: ### Default layout ("repr rust") -The default layout of structs is not specified. Effectively, the -compiler provdes a deterministic function per struct definition that -defines its layout. This function may as principle take as input the -entire input to the compiler. Therefore, any of the the following might -influence layout: - -- the types of the struct's fields -- the layout of other structs (including structs not included within this struct) -- compiler settings -- the results of profile information which are given to the compiler - for the purpose of PGO etc - -As of this writing, we have not reached a full consensus on what -limitations should exist on possible field struct layouts. Therefore, -the default layout of structs is considered undefined and subject to -change between individual compilations. This implies that (among other -things) two structs with the same field types may not be laid out in -the same way (for example, the hypothetical struct representing tuples -may be laid out differently from user-declared structs). - -Further, the layout of structs is always defined relative to the -**struct definition** plus a substitution supplying values for each of -the struct's generic types (in contrast to just considering the fully -monomorphized field types). This is necessary because the presence or -absence of generics can make a difference (e.g., `struct Foo { x: u16, -y: u32 }` and `struct Foo { x: u16, y: T }` where `T = u32` are not -guaranteed to be identical), owing to the possibility of unsizing -coercions. +**The default layout of structs is not specified.** As of this +writing, we have not reached a full consensus on what limitations +should exist on possible field struct layouts, so effectively one must +assume that the compiler can select any layout it likes for each +struct on each compilation, and it is not required to select the same +layout across two compilations. This implies that (among other things) +two structs with the same field types may not be laid out in the same +way (for example, the hypothetical struct representing tuples may be +laid out differently from user-declared structs). + +Known things that can influence layout (non-exhaustive): + +- the type of the struct fields and the layout of those types +- compiler settings, including esoteric choices like optimization fuel + +**A note on determinism.** The definition above does not guarantee +determinism between executions of the compiler -- two executions may +select different layouts, even if all inputs are identical. Naturally, +in practice, the compiler aims to produce deterministic output for a +given set of inputs. However, it is difficult to produce a +comprehensive summary of the various factors that may affect the +layout of structs, and so for the time being we have opted for a +conservative definition. **Compiler's current behavior.** As of the time of this writing, the compiler will reorder struct fields to minimize the overall size of the struct (and in particular to eliminate padding due to alignment -restrictions). The final field, however, is not reordered if an -unsizing coercion may be applied. +restrictions). + +Layout is presently defined not in terms of a "fully monomorphized" +struct definition but rather in terms of its generic definition along +with a set of substitutions (values for each type parameter; lifetime +parameters do not affect layout). This distinction is important +because of *unsizing* -- if the final field has generic type, the +compiler will not reorder it, to allow for the possibility of +unsizing. E.g., `struct Foo { x: u16, y: u32 }` and `struct Foo { +x: u16, y: T }` where `T = u32` are not guaranteed to be identical. #### Unresolved questions From 17dab338ddd685a97df9fae0021ce600007c9858 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 23 Oct 2018 13:03:07 -0400 Subject: [PATCH 17/19] document zero-sized struct behavior --- reference/src/representation/structs-and-tuples.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index 0bd2d094..ebafaf83 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -188,6 +188,14 @@ following: [^aligned]: Aligning an offset O to an alignment A means to round up the offset O until it is a multiple of the alignment A. +One deviation from C comes about with "empty structs". In Rust, a +struct that contains (transitively) no data members is considered to +have size zero, which is not something that exists in C. This includes +a struct like `#[repr(C)] struct Foo { }`. Further, when a +`#[repr(C)]` struct has a field whose type has zero-size, that field +may induce padding due to its alignment, but will not otherwise affect +the offsets of subsequent fields (as it takes up zero space). + The intention is that if one has a set of C struct declarations and a corresponding set of Rust struct declarations, all of which are tagged with `#[repr(C)]`, then the layout of those structs will all be From a9223e2cf85cd2814ab0f205fd1cc265072c0cfc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 25 Oct 2018 11:00:15 -0400 Subject: [PATCH 18/19] say more about zero-sized things --- .../src/representation/structs-and-tuples.md | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index ebafaf83..84da29a3 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -188,14 +188,6 @@ following: [^aligned]: Aligning an offset O to an alignment A means to round up the offset O until it is a multiple of the alignment A. -One deviation from C comes about with "empty structs". In Rust, a -struct that contains (transitively) no data members is considered to -have size zero, which is not something that exists in C. This includes -a struct like `#[repr(C)] struct Foo { }`. Further, when a -`#[repr(C)]` struct has a field whose type has zero-size, that field -may induce padding due to its alignment, but will not otherwise affect -the offsets of subsequent fields (as it takes up zero space). - The intention is that if one has a set of C struct declarations and a corresponding set of Rust struct declarations, all of which are tagged with `#[repr(C)]`, then the layout of those structs will all be @@ -207,6 +199,38 @@ their layout in a C program. See also the notes on [ABI compatibility](#fnabi) under the section on `#[repr(transparent)]`. +**Structs with no fields.** One area where Rust layout can deviate +from C/C++ -- even with `#[repr(C)]` -- comes about with "empty +structs" that have no fields. 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](https://godbolt.org/z/AS2gdC). Rust behaves the same way -- +empty structs have size 0 and alignment 1 (unless an explicit +`#[repr(align)]` is present). 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. + +**Structs of zero-size.** It is also possible to have structs that +have fields but have non-zero size. In this case, the size of the +struct would be zero, but its alignment may be greater. For example, +`#[repr(C)] struct Foo { x: [u16; 0] }` would have an alignment of 2 +bytes by default. ([This matches the behavior in gcc and +clang](https://godbolt.org/z/5w0gkq).) + +**Structs with fields of zero-size.** If a `#[repr(C)]` struct +containing a field of zero-size, that field does not occupy space in +the struct; it can affect the offsets of subsequent fields if it +induces padding due to the alignment on its type. ([This matches the +behavior in gcc and clang](https://godbolt.org/z/5w0gkq).) + +**C++ compatibility hazard.** As noted above when discussing structs +with no fields, C++ treats empty structs like `struct Foo { }` +differently from C and Rust. This can introduce subtle compatibility +hazards. If you have an empty struct in your C++ code and you make the +"naive" translation into Rust, even tagging with `#[repr(C)]` will not +produce layout- or ABI-compatible results. + ### Fixed alignment The `#[repr(align(N))]` attribute may be used to raise the alignment From ece91f5f3933c395389389f71197be697e3c0f90 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 25 Oct 2018 11:37:32 -0400 Subject: [PATCH 19/19] fix typo --- reference/src/representation/structs-and-tuples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/src/representation/structs-and-tuples.md b/reference/src/representation/structs-and-tuples.md index 84da29a3..a64083f8 100644 --- a/reference/src/representation/structs-and-tuples.md +++ b/reference/src/representation/structs-and-tuples.md @@ -212,7 +212,7 @@ the `[[no_unique_address]]` attribute, in which case they do not increase the overall size of the struct. **Structs of zero-size.** It is also possible to have structs that -have fields but have non-zero size. In this case, the size of the +have fields but still have zero size. In this case, the size of the struct would be zero, but its alignment may be greater. For example, `#[repr(C)] struct Foo { x: [u16; 0] }` would have an alignment of 2 bytes by default. ([This matches the behavior in gcc and