diff --git a/src/macros.rs b/src/macros.rs index baea69e6783c0..3a0818049cddd 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -244,6 +244,194 @@ macro_rules! s_no_extra_traits { ); } +/// Emit a struct with the given derive attributes plus a generated `Default` impl. +/// +/// Fields default to `Default::default()`. A field whose default can't be derived must carry +/// `#[custom_default(EXPR)]` as its *first* attribute, and `EXPR` is used instead. +/// +/// This works by scanning each field for `#[custom_default]` attributes. If one exists, the +/// attribute's contents are added to `processed_field_defaults` and will be used in the expansion +/// for `Default`. If it does not exist, `Default::default()` is used instead. In either case, the +/// field is added to `processed_fields` with `#[custom_default]` stripped if necessary, and +/// `impl_default` is invoked again with the remaining fields. +macro_rules! impl_default { + // entry; `attrs` is the attribute block the caller wants on the struct (repr, derives, etc.), + // which is merged with the struct's own attributes. + ( + attrs: { $($attrs:tt)* } + $(#[$attr:meta])* + $vis:vis struct $name:ident { $($body:tt)* } + ) => { + impl_default! { + @struct + attrs: { $($attrs)* $(#[$attr])* } + vis: { $vis } + name: { $name } + processed_fields: { } + processed_field_defaults: { } + remaining_fields: { $($body)* } + } + }; + + // field led by #[custom_default(...)] + ( + @struct + attrs: { $($attrs:tt)* } + vis: { $vis:vis } + name: { $name:ident } + processed_fields: { $($processed_fields:tt)* } + processed_field_defaults: { $($processed_field_defaults:tt)* } + remaining_fields: { + #[custom_default($default:expr)] + $(#[$fattr:meta])* + $fvis:vis $fname:ident: $fty:ty, + $($tail:tt)* + } + ) => { + impl_default! { + @struct + attrs: { $($attrs)* } + vis: { $vis } + name: { $name } + processed_fields: { $($processed_fields)* $(#[$fattr])* $fvis $fname: $fty, } + processed_field_defaults: { + $($processed_field_defaults)* + $(#[$fattr])* $fname: $default, + } + remaining_fields: { $($tail)* } + } + }; + + // plain field + ( + @struct + attrs: { $($attrs:tt)* } + vis: { $vis:vis } + name: { $name:ident } + processed_fields: { $($processed_fields:tt)* } + processed_field_defaults: { $($processed_field_defaults:tt)* } + remaining_fields: { + $(#[$fattr:meta])* + $fvis:vis $fname:ident: $fty:ty, + $($tail:tt)* + } + ) => { + impl_default! { + @struct + attrs: { $($attrs)* } + vis: { $vis } + name: { $name } + processed_fields: { $($processed_fields)* $(#[$fattr])* $fvis $fname: $fty, } + processed_field_defaults: { + $($processed_field_defaults)* + $(#[$fattr])* $fname: ::core::default::Default::default(), + } + remaining_fields: { $($tail)* } + } + }; + + // done + ( + @struct + attrs: { $($attrs:tt)* } + vis: { $vis:vis } + name: { $name:ident } + processed_fields: { $($processed_fields:tt)* } + processed_field_defaults: { $($processed_field_defaults:tt)* } + remaining_fields: { } + ) => { + $($attrs)* + $vis struct $name { $($processed_fields)* } + + impl ::core::default::Default for $name { + // Field doc comments get forwarded to the initializer alongside `#[cfg]` + // they're harmless there but the lint fires, so silence it. + #[allow(unused_doc_comments)] + fn default() -> Self { + Self { $($processed_field_defaults)* } + } + } + }; +} + +/// Like [`s`], but also generates a `Default` impl for every struct in the block. +macro_rules! s_with_default { + ($( + $(#[$attr:meta])* + $pub:vis $t:ident $i:ident { $($field:tt)* } + )*) => ($( + s_with_default!(it: $(#[$attr])* $pub $t $i { $($field)* }); + )*); + + (it: $(#[$attr:meta])* $pub:vis union $i:ident { $($field:tt)* }) => ( + compile_error!( + "unions cannot derive extra traits, use s_no_extra_traits_with_default instead" + ); + ); + + (it: $(#[$attr:meta])* $pub:vis struct $i:ident { $($field:tt)* }) => ( + impl_default! { + attrs: { + #[repr(C)] + #[::core::prelude::v1::derive( + ::core::clone::Clone, + ::core::marker::Copy, + ::core::fmt::Debug, + )] + #[cfg_attr( + feature = "extra_traits", + ::core::prelude::v1::derive(PartialEq, Eq, Hash) + )] + #[allow(deprecated)] + } + $(#[$attr])* $pub struct $i { $($field)* } + } + ); +} + +/// Like [`s_no_extra_traits`], but also generates a `Default` impl for every struct in the block. +/// +/// Unions are emitted as by `s_no_extra_traits!`; no `Default` is generated for them. A struct +/// field of union type supplies its own default via `#[custom_default(...)]`. +macro_rules! s_no_extra_traits_with_default { + ($( + $(#[$attr:meta])* + $pub:vis $t:ident $i:ident { $($field:tt)* } + )*) => ($( + s_no_extra_traits_with_default!(it: $(#[$attr])* $pub $t $i { $($field)* }); + )*); + + (it: $(#[$attr:meta])* $pub:vis union $i:ident { $($field:tt)* }) => ( + #[repr(C)] + #[::core::prelude::v1::derive( + ::core::clone::Clone, + ::core::marker::Copy, + )] + $(#[$attr])* + $pub union $i { $($field)* } + + impl ::core::fmt::Debug for $i { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_struct(::core::stringify!($i)).finish_non_exhaustive() + } + } + ); + + (it: $(#[$attr:meta])* $pub:vis struct $i:ident { $($field:tt)* }) => ( + impl_default! { + attrs: { + #[repr(C)] + #[::core::prelude::v1::derive( + ::core::clone::Clone, + ::core::marker::Copy, + ::core::fmt::Debug, + )] + } + $(#[$attr])* $pub struct $i { $($field)* } + } + ); +} + /// Create an uninhabited type that can't be constructed. It implements `Debug`, `Clone`, /// and `Copy`, but these aren't meaningful for extern types so they should eventually /// be removed. @@ -583,6 +771,42 @@ mod tests { assert_eq!(core::mem::offset_of!(Off1, c), offset_of!(Off1, c)); assert_eq!(core::mem::offset_of!(Off1, d), offset_of!(Off1, d)); } + + #[test] + fn s_with_default_uses_custom_default() { + // A non-default value proves `custom_default` is used rather than a derived default. + s_with_default! { + struct CustomDefault { + a: u32, + #[custom_default([1; 64])] + buf: [u8; 64], + } + } + + let s = CustomDefault::default(); + assert_eq!(s.a, 0); + assert_eq!(s.buf, [1u8; 64]); + } + + #[test] + fn s_with_default_keeps_field_attrs() { + // If `custom_default` stripping ate other field attributes, the two `a` fields would + // collide. Checked across arches since CI runs many. + s_with_default! { + struct FieldAttrs { + #[cfg(target_arch = "x86_64")] + a: u8, + #[cfg(not(target_arch = "x86_64"))] + a: u64, + } + } + + let s = FieldAttrs::default(); + #[cfg(target_arch = "x86_64")] + assert_eq!(s.a, 0u8); + #[cfg(not(target_arch = "x86_64"))] + assert_eq!(s.a, 0u64); + } } #[cfg(test)] @@ -626,4 +850,37 @@ mod macro_checks { type Foo; pub type Bar; } + + s_with_default! { + pub struct S3 { + pub a: u32, + #[custom_default([1; 64])] + pub buf: [u8; 64], + } + + struct S3Priv { + pub a: u32, + b: u32, + } + } + + s_no_extra_traits_with_default! { + pub union U3 { + pub a: u32, + b: f32, + } + + pub struct S4 { + pub a: u32, + #[custom_default(unsafe { ::core::mem::zeroed::() })] + pub u: U3, + } + } + + fn assert_impls_default() {} + + fn check_default() { + assert_impls_default::(); + assert_impls_default::(); + } } diff --git a/src/new/linux_uapi/linux/can.rs b/src/new/linux_uapi/linux/can.rs index 604e107a51220..c2631bd55fa4b 100644 --- a/src/new/linux_uapi/linux/can.rs +++ b/src/new/linux_uapi/linux/can.rs @@ -37,7 +37,7 @@ pub const CANXL_MAX_DLC_MASK: c_int = 0x07FF; pub const CANXL_MIN_DLEN: usize = 1; pub const CANXL_MAX_DLEN: usize = 2048; -s! { +s_with_default! { #[repr(align(8))] pub struct can_frame { pub can_id: canid_t, @@ -54,7 +54,7 @@ pub const CANFD_BRS: c_int = 0x01; pub const CANFD_ESI: c_int = 0x02; pub const CANFD_FDF: c_int = 0x04; -s! { +s_with_default! { #[repr(align(8))] pub struct canfd_frame { pub can_id: canid_t, @@ -62,6 +62,7 @@ s! { pub flags: u8, __res0: u8, __res1: u8, + #[custom_default([0; CANFD_MAX_DLEN])] pub data: [u8; CANFD_MAX_DLEN], } } @@ -69,13 +70,14 @@ s! { pub const CANXL_XLF: c_int = 0x80; pub const CANXL_SEC: c_int = 0x01; -s! { +s_with_default! { pub struct canxl_frame { pub prio: canid_t, pub flags: u8, pub sdt: u8, pub len: u16, pub af: u32, + #[custom_default([0; CANXL_MAX_DLEN])] pub data: [u8; CANXL_MAX_DLEN], } } @@ -101,10 +103,11 @@ pub const CAN_NPROTO: c_int = 8; pub const SOL_CAN_BASE: c_int = 100; -s_no_extra_traits! { +s_no_extra_traits_with_default! { pub struct sockaddr_can { pub can_family: crate::sa_family_t, pub can_ifindex: c_int, + #[custom_default(unsafe { mem::zeroed::<__c_anonymous_sockaddr_can_can_addr>() })] pub can_addr: __c_anonymous_sockaddr_can_can_addr, }