2e93f2c92f
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`.
166 lines
4.9 KiB
Rust
166 lines
4.9 KiB
Rust
// check-pass
|
|
// compile-flags: -Zunpretty=expanded
|
|
// edition:2021
|
|
//
|
|
// This test checks the code generated for all[*] the builtin derivable traits
|
|
// on a variety of structs and enums. It protects against accidental changes to
|
|
// the generated code, and makes deliberate changes to the generated code
|
|
// easier to review.
|
|
//
|
|
// [*] It excludes `Copy` in some cases, because that changes the code
|
|
// generated for `Clone`.
|
|
//
|
|
// [*] It excludes `RustcEncodable` and `RustDecodable`, which are obsolete and
|
|
// also require the `rustc_serialize` crate.
|
|
|
|
#![crate_type = "lib"]
|
|
#![allow(dead_code)]
|
|
#![allow(deprecated)]
|
|
|
|
// Empty struct.
|
|
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
struct Empty;
|
|
|
|
// A basic struct. Note: because this derives `Copy`, it gets the simple
|
|
// `clone` implemention that just does `*self`.
|
|
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
struct Point {
|
|
x: u32,
|
|
y: u32,
|
|
}
|
|
|
|
// A basic packed struct. Note: because this derives `Copy`, it gets the simple
|
|
// `clone` implemention that just does `*self`.
|
|
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
#[repr(packed)]
|
|
struct PackedPoint {
|
|
x: u32,
|
|
y: u32,
|
|
}
|
|
|
|
// A large struct. Note: because this derives `Copy`, it gets the simple
|
|
// `clone` implemention that just does `*self`.
|
|
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
struct Big {
|
|
b1: u32, b2: u32, b3: u32, b4: u32, b5: u32, b6: u32, b7: u32, b8: u32,
|
|
}
|
|
|
|
// A struct that doesn't impl `Copy`, which means it gets the non-simple
|
|
// `clone` implemention that clones the fields individually.
|
|
#[derive(Clone)]
|
|
struct NonCopy(u32);
|
|
|
|
// A packed struct that doesn't impl `Copy`, which means it gets the non-simple
|
|
// `clone` implemention that clones the fields individually.
|
|
#[derive(Clone)]
|
|
#[repr(packed)]
|
|
struct PackedNonCopy(u32);
|
|
|
|
// A struct that impls `Copy` manually, which means it gets the non-simple
|
|
// `clone` implemention that clones the fields individually.
|
|
#[derive(Clone)]
|
|
struct ManualCopy(u32);
|
|
impl Copy for ManualCopy {}
|
|
|
|
// A packed struct that impls `Copy` manually, which means it gets the
|
|
// non-simple `clone` implemention that clones the fields individually.
|
|
#[derive(Clone)]
|
|
#[repr(packed)]
|
|
struct PackedManualCopy(u32);
|
|
impl Copy for PackedManualCopy {}
|
|
|
|
// A struct with an unsized field. Some derives are not usable in this case.
|
|
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
struct Unsized([u32]);
|
|
|
|
// A packed struct with an unsized `[u8]` field. This is currently allowed, but
|
|
// causes a warning and will be phased out at some point.
|
|
#[derive(Debug, Hash)]
|
|
#[repr(packed)]
|
|
struct PackedUnsizedU8([u8]);
|
|
//~^ WARNING byte slice in a packed struct that derives a built-in trait
|
|
//~^^ WARNING byte slice in a packed struct that derives a built-in trait
|
|
//~^^^ this was previously accepted
|
|
//~^^^^ this was previously accepted
|
|
|
|
trait Trait {
|
|
type A;
|
|
}
|
|
|
|
// A generic struct involving an associated type.
|
|
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
struct Generic<T: Trait, U> {
|
|
t: T,
|
|
ta: T::A,
|
|
u: U,
|
|
}
|
|
|
|
// A packed, generic tuple struct involving an associated type. Because it is
|
|
// packed, a `T: Copy` bound is added to all impls (and where clauses within
|
|
// them) except for `Default`. This is because we must access fields using
|
|
// copies (e.g. `&{self.0}`), instead of using direct references (e.g.
|
|
// `&self.0`) which may be misaligned in a packed struct.
|
|
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
#[repr(packed)]
|
|
struct PackedGeneric<T: Trait, U>(T, T::A, U);
|
|
|
|
// An empty enum.
|
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
enum Enum0 {}
|
|
|
|
// A single-variant enum.
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
enum Enum1 {
|
|
Single { x: u32 }
|
|
}
|
|
|
|
// A C-like, fieldless enum with a single variant.
|
|
#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
enum Fieldless1 {
|
|
#[default]
|
|
A,
|
|
}
|
|
|
|
// A C-like, fieldless enum.
|
|
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
enum Fieldless {
|
|
#[default]
|
|
A,
|
|
B,
|
|
C,
|
|
}
|
|
|
|
// An enum with multiple fieldless and fielded variants.
|
|
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
enum Mixed {
|
|
#[default]
|
|
P,
|
|
Q,
|
|
R(u32),
|
|
S { d1: Option<u32>, d2: Option<i32> },
|
|
}
|
|
|
|
// An enum with no fieldless variants. Note that `Default` cannot be derived
|
|
// for this enum.
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
enum Fielded {
|
|
X(u32),
|
|
Y(bool),
|
|
Z(Option<i32>),
|
|
}
|
|
|
|
// A generic enum. Note that `Default` cannot be derived for this enum.
|
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
enum EnumGeneric<T, U> {
|
|
One(T),
|
|
Two(U),
|
|
}
|
|
|
|
// A union. Most builtin traits are not derivable for unions.
|
|
#[derive(Clone, Copy)]
|
|
pub union Union {
|
|
pub b: bool,
|
|
pub u: u32,
|
|
pub i: i32,
|
|
}
|