Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
257 changes: 257 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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::<U3>() })]
pub u: U3,
}
}

fn assert_impls_default<T: Default>() {}

fn check_default() {
assert_impls_default::<S3>();
assert_impls_default::<S4>();
}
}
11 changes: 7 additions & 4 deletions src/new/linux_uapi/linux/can.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -54,28 +54,30 @@ 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,
pub len: u8,
pub flags: u8,
__res0: u8,
__res1: u8,
#[custom_default([0; CANFD_MAX_DLEN])]
pub data: [u8; CANFD_MAX_DLEN],
}
}

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],
}
}
Expand All @@ -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,
}

Expand Down