Skip to content

Commit 2e93f2c

Browse files
committed
Allow more deriving on packed structs.
Currently, deriving on packed structs has some non-trivial limitations, related to the fact that taking references on unaligned fields is UB. The current approach to field accesses in derived code: - Normal case: `&self.0` - In a packed struct that derives `Copy`: `&{self.0}` - In a packed struct that doesn't derive `Copy`: `&self.0` Plus, we disallow deriving any builtin traits other than `Default` for any packed generic type, because it's possible that there might be misaligned fields. This is a fairly broad restriction. Plus, we disallow deriving any builtin traits other than `Default` for most packed types that don't derive `Copy`. (The exceptions are those where the alignments inherently satisfy the packing, e.g. in a type with `repr(packed(N))` where all the fields have alignments of `N` or less anyway. Such types are pretty strange, because the `packed` attribute is not having any effect.) This commit introduces a new, simpler approach to field accesses: - Normal case: `&self.0` - In a packed struct: `&{self.0}` In the latter case, this requires that all fields impl `Copy`, which is a new restriction. This means that the following example compiles under the old approach and doesn't compile under the new approach. ``` #[derive(Debug)] struct NonCopy(u8); #[derive(Debug) #[repr(packed)] struct MyType(NonCopy); ``` (Note that the old approach's support for cases like this was brittle. Changing the `u8` to a `u16` would be enough to stop it working. So not much capability is lost here.) However, the other constraints from the old rules are removed. We can now derive builtin traits for packed generic structs like this: ``` trait Trait { type A; } #[derive(Hash)] #[repr(packed)] pub struct Foo<T: Trait>(T, T::A); ``` To allow this, we add a `T: Copy` bound in the derived impl and a `T::A: Copy` bound in where clauses. So `T` and `T::A` must impl `Copy`. We can now also derive builtin traits for packed structs that don't derive `Copy`, so long as the fields impl `Copy`: ``` #[derive(Hash)] #[repr(packed)] pub struct Foo(u32); ``` This includes types that hand-impl `Copy` rather than deriving it, such as the following, that show up in winapi-0.2: ``` #[derive(Clone)] #[repr(packed)] struct MyType(i32); impl Copy for MyType {} ``` The new approach is simpler to understand and implement, and it avoids the need for the `unsafe_derive_on_repr_packed` check. One exception is required for backwards-compatibility: we allow `[u8]` fields for now. There is a new lint for this, `byte_slice_in_packed_struct_with_derive`.
1 parent c7bf469 commit 2e93f2c

File tree

25 files changed

+823
-298
lines changed

25 files changed

+823
-298
lines changed

compiler/rustc_builtin_macros/src/deriving/bounds.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub fn expand_deriving_copy(
1717
span,
1818
path: path_std!(marker::Copy),
1919
skip_path_as_bound: false,
20+
needs_copy_as_bound_if_packed: false,
2021
additional_bounds: Vec::new(),
2122
supports_unions: true,
2223
methods: Vec::new(),

compiler/rustc_builtin_macros/src/deriving/clone.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub fn expand_deriving_clone(
7373
span,
7474
path: path_std!(clone::Clone),
7575
skip_path_as_bound: false,
76+
needs_copy_as_bound_if_packed: true,
7677
additional_bounds: bounds,
7778
supports_unions: true,
7879
methods: vec![MethodDef {

compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub fn expand_deriving_eq(
2727
span,
2828
path: path_std!(cmp::Eq),
2929
skip_path_as_bound: false,
30+
needs_copy_as_bound_if_packed: true,
3031
additional_bounds: Vec::new(),
3132
supports_unions: true,
3233
methods: vec![MethodDef {

compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub fn expand_deriving_ord(
2020
span,
2121
path: path_std!(cmp::Ord),
2222
skip_path_as_bound: false,
23+
needs_copy_as_bound_if_packed: true,
2324
additional_bounds: Vec::new(),
2425
supports_unions: false,
2526
methods: vec![MethodDef {

compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ pub fn expand_deriving_partial_eq(
8484
span,
8585
path: path_std!(cmp::PartialEq),
8686
skip_path_as_bound: false,
87+
needs_copy_as_bound_if_packed: true,
8788
additional_bounds: Vec::new(),
8889
supports_unions: false,
8990
methods,

compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub fn expand_deriving_partial_ord(
5959
span,
6060
path: path_std!(cmp::PartialOrd),
6161
skip_path_as_bound: false,
62+
needs_copy_as_bound_if_packed: true,
6263
additional_bounds: vec![],
6364
supports_unions: false,
6465
methods: vec![partial_cmp_def],

compiler/rustc_builtin_macros/src/deriving/debug.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub fn expand_deriving_debug(
2323
span,
2424
path: path_std!(fmt::Debug),
2525
skip_path_as_bound: false,
26+
needs_copy_as_bound_if_packed: true,
2627
additional_bounds: Vec::new(),
2728
supports_unions: false,
2829
methods: vec![MethodDef {

compiler/rustc_builtin_macros/src/deriving/decodable.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub fn expand_deriving_rustc_decodable(
2525
span,
2626
path: Path::new_(vec![krate, sym::Decodable], vec![], PathKind::Global),
2727
skip_path_as_bound: false,
28+
needs_copy_as_bound_if_packed: true,
2829
additional_bounds: Vec::new(),
2930
supports_unions: false,
3031
methods: vec![MethodDef {

compiler/rustc_builtin_macros/src/deriving/default.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub fn expand_deriving_default(
2525
span,
2626
path: Path::new(vec![kw::Default, sym::Default]),
2727
skip_path_as_bound: has_a_default_variant(item),
28+
needs_copy_as_bound_if_packed: false,
2829
additional_bounds: Vec::new(),
2930
supports_unions: false,
3031
methods: vec![MethodDef {

compiler/rustc_builtin_macros/src/deriving/encodable.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pub fn expand_deriving_rustc_encodable(
109109
span,
110110
path: Path::new_(vec![krate, sym::Encodable], vec![], PathKind::Global),
111111
skip_path_as_bound: false,
112+
needs_copy_as_bound_if_packed: true,
112113
additional_bounds: Vec::new(),
113114
supports_unions: false,
114115
methods: vec![MethodDef {

0 commit comments

Comments
 (0)