|
| 1 | +- Feature Name: `repr_pack` |
| 2 | +- Start Date: 2015-12-06 |
| 3 | +- RFC PR: (leave this empty) |
| 4 | +- Rust Issue: (leave this empty) |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +Extend the existing `#[repr]` attribute on structs with a `pack = "N"` option to |
| 10 | +specify a custom packing for `struct` types. |
| 11 | + |
| 12 | +# Motivation |
| 13 | +[motivation]: #motivation |
| 14 | + |
| 15 | +Many C/C++ compilers allow a packing to be specified for structs which |
| 16 | +effectively lowers the alignment for a struct and its fields (for example with |
| 17 | +MSVC there is `#pragma pack(N)`). Such packing is used extensively in certain |
| 18 | +C/C++ libraries (such as Windows API which uses it pervasively making writing |
| 19 | +Rust libraries such as `winapi` challenging). |
| 20 | + |
| 21 | +At the moment the only way to work around the lack of a proper |
| 22 | +`#[repr(pack = "N")]` attribute is to use `#[repr(packed)]` and then manually |
| 23 | +fill in padding which is a burdensome task. Even then that isn't quite right |
| 24 | +because the overall alignment of the struct would end up as 1 even though it |
| 25 | +needs to be N (or the default if that is smaller than N), so this fills in a gap |
| 26 | +which is impossible to do in Rust at the moment. |
| 27 | + |
| 28 | +# Detailed design |
| 29 | +[design]: #detailed-design |
| 30 | + |
| 31 | +The `#[repr]` attribute on `struct`s will be extended to include a form such as: |
| 32 | + |
| 33 | +```rust |
| 34 | +#[repr(pack = "2")] |
| 35 | +struct LessAligned(i16, i32); |
| 36 | +``` |
| 37 | + |
| 38 | +This structure will have an alignment of 2 and a size of 6, as well as the |
| 39 | +second field having an offset of 2 instead of 4 from the base of the struct. |
| 40 | +This is in contrast to without the attribute where the structure would have an |
| 41 | +alignment of 4 and a size of 8, and the second field would have an offset of 4 |
| 42 | +from the base of the struct. |
| 43 | + |
| 44 | +Syntactically, the `repr` meta list will be extended to accept a meta item |
| 45 | +name/value pair with the name "pack" and the value as a string which can be |
| 46 | +parsed as a `u64`. The restrictions on where this attribute can be placed along |
| 47 | +with the accepted values are: |
| 48 | + |
| 49 | +* Custom packing can only be specified on `struct` declarations for now. |
| 50 | + Specifying a different packing on perhaps `enum` or `type` definitions should |
| 51 | + be a backwards-compatible extension. |
| 52 | +* Packing values must be a power of two. |
| 53 | + |
| 54 | +By specifying this attribute, the alignment of the struct would be the smaller |
| 55 | +of the specified packing and the default alignment of the struct. The alignments |
| 56 | +of each struct field for the purpose of positioning fields would also be the |
| 57 | +smaller of the specified packing and the alignment of the type of that field. If |
| 58 | +the specified packing is greater than or equal to the default alignment of the |
| 59 | +struct, then the alignment and layout of the struct should be unaffected. |
| 60 | + |
| 61 | +When combined with `#[repr(C)]` the size alignment and layout of the struct |
| 62 | +should match the equivalent struct in C. |
| 63 | + |
| 64 | +`#[repr(packed)]` and `#[repr(pack = "1")]` should have identical behavior. |
| 65 | + |
| 66 | +Because this lowers the effective alignment of fields in the same way that |
| 67 | +`#[repr(packed)]` does (which caused [issue #27060][gh27060]), while accessing a |
| 68 | +field should be safe, borrowing a field should be unsafe. |
| 69 | + |
| 70 | +Specifying `#[repr(packed)]` and `#[repr(pack = "N")]` where N is not 1 should |
| 71 | +result in an error. |
| 72 | + |
| 73 | +Specifying `#[repr(pack = "A")]` and `#[repr(align = "B")]` should still pack |
| 74 | +together fields with the packing specified, but then increase the overall |
| 75 | +alignment to the alignment specified. Depends on [RFC #1358][rfc1358] landing. |
| 76 | + |
| 77 | +# Drawbacks |
| 78 | +[drawbacks]: #drawbacks |
| 79 | + |
| 80 | +Duplication in the language where `#[repr(packed)]` and `#[repr(pack = "1")]` |
| 81 | +have identical behavior. |
| 82 | + |
| 83 | +# Alternatives |
| 84 | +[alternatives]: #alternatives |
| 85 | + |
| 86 | +* The alternative is not doing this and forcing people to continue using |
| 87 | + `#[repr(packed)]` with manual padding, although such structs would always have |
| 88 | + an alignment of 1 which is often wrong. |
| 89 | +* Alternatively a new attribute could be used such as `#[pack]`. |
| 90 | +* `#[repr(packed)]` could be extended as either `#[repr(packed(N))]` or |
| 91 | + `#[repr(packed = "N")]`. |
| 92 | + |
| 93 | +# Unresolved questions |
| 94 | +[unresolved]: #unresolved-questions |
| 95 | + |
| 96 | +* The behavior specified here should match the behavior of MSVC at least. Does |
| 97 | + it match the behavior of other C/C++ compilers as well? |
| 98 | +* Should it still be safe to borrow fields whose alignment is less than or equal |
| 99 | + to the specified packing or should all field borrows be unsafe? |
| 100 | + |
| 101 | +[gh27060]: https://github.com/rust-lang/rust/issues/27060 |
| 102 | +[rfc1358]: https://github.com/rust-lang/rfcs/pull/1358 |
0 commit comments