From e561f97b488d5c3b192c822df98640e2df920784 Mon Sep 17 00:00:00 2001 From: ltdk Date: Fri, 18 Oct 2024 21:54:11 -0400 Subject: [PATCH 1/4] Add relaxed DST field ordering RFC --- text/0000-relaxed-dst-field-ordering.md | 65 +++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 text/0000-relaxed-dst-field-ordering.md diff --git a/text/0000-relaxed-dst-field-ordering.md b/text/0000-relaxed-dst-field-ordering.md new file mode 100644 index 00000000000..21e59fa283a --- /dev/null +++ b/text/0000-relaxed-dst-field-ordering.md @@ -0,0 +1,65 @@ +- Feature Name: `relaxed_dst_field_ordering` +- Start Date: 2024-10-18 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +# Summary +[summary]: #summary + +Relax the requirements on struct field ordering for dynamically sized fields: + +* for `repr(Rust)`, `?Sized` fields can be anywhere in the field list, as long as there is only one +* for `repr(C)`, `?Sized` fields only have to be the last non-ZST field, and can be followed by ZST fields +* for `repr(transparent)`, apply both rules, since only one non-ZST field is allowed anyway + +# Motivation +[motivation]: #motivation + +Rust allows creating structs with dynamically sized fields, but in a very limited way: since the size is dynamic, dynamically sized fields must be the last field in the struct, to avoid making the offsets of fields also dynamic. + +However, `repr(Rust)` allows reordering fields, and this is not not reflected in this rule for dynamically sized fields: no matter what you do, the dynamically sized field must be at the end. This is inconsistent with the rest of the language and limits the ability of authors to reorder the fields in a more natural way, like they can for statically sized structs. + +Additionally, Rust has fully committed to zero-sized fields being truly invisible to struct layout, encoding this in the definition of `repr(transparent)`. So, why are dynamically sized fields unable to be followed by zero-sized fields, or reordered among statically sized fields in a `repr(Rust)` struct. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +Before, structs were allowed to have dynamically sized types (DSTs) in their last field only. Now, this restriction has been relaxed to allow exactly one DST field, although it can occur anywhere inside the struct. + +For `repr(C)` structs specifically, an additional requirement is added that the DST must be the last field that is not a zero-sized type (ZST), which is still more permissive than the previous definition. + +The dynamically sized field will always be physically located at the end of the struct, although because `repr(Rust)` can reorder fields and because ZST fields do not affect layout, this doesn't have to be reflected in the actual struct definition. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +This feature should be relatively easy to implement: we already reorder fields in `repr(Rust)` structs explicitly, so, this is just ensuring that DST fields are always placed last without relying on the definition order. + +The code for `repr(transparent)` effectively doesn't change, and the code for ensuring that the DST is the last field can be mostly reused for `repr(C)`, assuming that ZST fields are still ignored. + +# Drawbacks +[drawbacks]: #drawbacks + +It's work to change the status quo. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +This arguably simplifies the language and makes the DST field more in line with the existing field ordering rules. + +But we could always not do it, I guess. + +# Prior art +[prior-art]: #prior-art + +None currently. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +None currently. + +# Future possibilities +[future-possibilities]: #future-possibilities + +In the future, we'll hopefully have the ability to define custom DSTs, but such extensions are very compatible with this RFC. From 2bf4fd6bbdc50c53dc2a7251fcff8870c3d9c873 Mon Sep 17 00:00:00 2001 From: ltdk Date: Fri, 18 Oct 2024 21:55:54 -0400 Subject: [PATCH 2/4] Typos --- text/0000-relaxed-dst-field-ordering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-relaxed-dst-field-ordering.md b/text/0000-relaxed-dst-field-ordering.md index 21e59fa283a..998a1e01b8b 100644 --- a/text/0000-relaxed-dst-field-ordering.md +++ b/text/0000-relaxed-dst-field-ordering.md @@ -19,7 +19,7 @@ Rust allows creating structs with dynamically sized fields, but in a very limite However, `repr(Rust)` allows reordering fields, and this is not not reflected in this rule for dynamically sized fields: no matter what you do, the dynamically sized field must be at the end. This is inconsistent with the rest of the language and limits the ability of authors to reorder the fields in a more natural way, like they can for statically sized structs. -Additionally, Rust has fully committed to zero-sized fields being truly invisible to struct layout, encoding this in the definition of `repr(transparent)`. So, why are dynamically sized fields unable to be followed by zero-sized fields, or reordered among statically sized fields in a `repr(Rust)` struct. +Additionally, Rust has fully committed to zero-sized fields being truly invisible to struct layout, encoding this in the definition of `repr(transparent)`. So, why are dynamically sized fields unable to be followed by zero-sized fields, or reordered among statically sized fields in a `repr(Rust)` struct? # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From f6bf38fe5415c6ecae99d4b7000844762a957a04 Mon Sep 17 00:00:00 2001 From: ltdk Date: Fri, 18 Oct 2024 21:56:23 -0400 Subject: [PATCH 3/4] Copy-editing --- text/0000-relaxed-dst-field-ordering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-relaxed-dst-field-ordering.md b/text/0000-relaxed-dst-field-ordering.md index 998a1e01b8b..0e1d60f4db5 100644 --- a/text/0000-relaxed-dst-field-ordering.md +++ b/text/0000-relaxed-dst-field-ordering.md @@ -24,7 +24,7 @@ Additionally, Rust has fully committed to zero-sized fields being truly invisibl # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -Before, structs were allowed to have dynamically sized types (DSTs) in their last field only. Now, this restriction has been relaxed to allow exactly one DST field, although it can occur anywhere inside the struct. +Before, structs were allowed to have dynamically sized types (DSTs) in their last field only. Now, this restriction has been relaxed to allow at most one DST field, although it can occur anywhere inside the struct. For `repr(C)` structs specifically, an additional requirement is added that the DST must be the last field that is not a zero-sized type (ZST), which is still more permissive than the previous definition. From 4d01f5bb5a76b123cb083d0deb62045422f7332b Mon Sep 17 00:00:00 2001 From: ltdk Date: Sat, 19 Oct 2024 14:09:45 -0400 Subject: [PATCH 4/4] Remove repr(C) from RFC --- text/0000-relaxed-dst-field-ordering.md | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/text/0000-relaxed-dst-field-ordering.md b/text/0000-relaxed-dst-field-ordering.md index 0e1d60f4db5..378e7af77e1 100644 --- a/text/0000-relaxed-dst-field-ordering.md +++ b/text/0000-relaxed-dst-field-ordering.md @@ -6,11 +6,7 @@ # Summary [summary]: #summary -Relax the requirements on struct field ordering for dynamically sized fields: - -* for `repr(Rust)`, `?Sized` fields can be anywhere in the field list, as long as there is only one -* for `repr(C)`, `?Sized` fields only have to be the last non-ZST field, and can be followed by ZST fields -* for `repr(transparent)`, apply both rules, since only one non-ZST field is allowed anyway +Relax the requirements on struct field ordering for dynamically sized fields for `repr(Rust)` and `repr(transparent)`, such that `?Sized` fields can be anywhere in the field list, as long as there is only one. # Motivation [motivation]: #motivation @@ -26,16 +22,16 @@ Additionally, Rust has fully committed to zero-sized fields being truly invisibl Before, structs were allowed to have dynamically sized types (DSTs) in their last field only. Now, this restriction has been relaxed to allow at most one DST field, although it can occur anywhere inside the struct. -For `repr(C)` structs specifically, an additional requirement is added that the DST must be the last field that is not a zero-sized type (ZST), which is still more permissive than the previous definition. +For `repr(C)` structs specifically, the old requirement that DSTs be at the end remains. -The dynamically sized field will always be physically located at the end of the struct, although because `repr(Rust)` can reorder fields and because ZST fields do not affect layout, this doesn't have to be reflected in the actual struct definition. +The dynamically sized field will always be physically located at the end of the struct, although because `repr(Rust)` can reorder fields, this doesn't have to be reflected in the actual struct definition. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -This feature should be relatively easy to implement: we already reorder fields in `repr(Rust)` structs explicitly, so, this is just ensuring that DST fields are always placed last without relying on the definition order. +For `repr(transparent)` structs, the layout is altered so that ZST fields are always reordered to the beginning of the struct, so that their offsets are always zero. This is technically different than the status quo, since you could have the offset be the size of the struct, but this has never been specified or guaranteed. This ensures that the offsets are still static. -The code for `repr(transparent)` effectively doesn't change, and the code for ensuring that the DST is the last field can be mostly reused for `repr(C)`, assuming that ZST fields are still ignored. +Per the current requirements, `repr(transparent)` structs still can only have ZSTs with trivial alignment, and types like `[T; 0]` will still be rejected. # Drawbacks [drawbacks]: #drawbacks @@ -45,9 +41,7 @@ It's work to change the status quo. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -This arguably simplifies the language and makes the DST field more in line with the existing field ordering rules. - -But we could always not do it, I guess. +We could extend the rules on ZST field ordering to `repr(C)` too, to allow ZSTs past the DST field. However, since `repr(C)` does imply strict ordering, it's more likely that people have been relying on the offsets for ZSTs in structs, and it's better to avoid breaking this. # Prior art [prior-art]: #prior-art