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
23 changes: 21 additions & 2 deletions bitbybit/src/bitfield/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::bitfield::{
const_name, mask_for_width_and_offset, mask_name, setter_name, with_name, ArrayInfo,
BaseDataSize, BitfieldAttributes, CustomType, DefmtVariant, FieldDefinition, BITCOUNT_BOOL,
};
use proc_macro2::{Ident, TokenStream};
use quote::{quote, TokenStreamExt as _};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, quote_spanned, TokenStreamExt as _};
use std::str::FromStr;
use std::{collections::HashSet, ops::Range};
use syn::{LitInt, Type, Visibility};
Expand Down Expand Up @@ -677,6 +677,25 @@ pub fn generate_debug_trait_impl(
debug_trait
}

pub fn generate_from_trait_impl(
struct_name: &Ident,
base_data_type: &Ident,
span: Span,
) -> TokenStream {
quote_spanned! {span=>
impl ::core::convert::From<#base_data_type> for #struct_name {
fn from(item: #base_data_type) -> #struct_name {
#struct_name::new_with_raw_value(item)
}
}
impl ::core::convert::From<#struct_name> for #base_data_type {
fn from(item: #struct_name) -> #base_data_type {
item.raw_value()
}
}
}
}

pub fn generate_defmt_trait_impl(
struct_name: &Ident,
bitfield_attrs: &BitfieldAttributes,
Expand Down
26 changes: 26 additions & 0 deletions bitbybit/src/bitfield/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use quote::{quote, quote_spanned, ToTokens};
use std::ops::Range;
use std::str::FromStr;
use syn::meta::ParseNestedMeta;
use syn::spanned::Spanned;
use syn::LitStr;
use syn::{parse_macro_input, Attribute, Data, DeriveInput, LitInt, Token, Type};

Expand Down Expand Up @@ -148,6 +149,7 @@ struct BitfieldAttributes {
pub default_val: Option<DefaultVal>,
pub forbid_overlaps: bool,
pub debug_trait: bool,
pub from_trait: Option<Ident>,
pub introspect: bool,
pub defmt_trait: Option<DefmtTrait>,
}
Expand Down Expand Up @@ -225,6 +227,23 @@ impl BitfieldAttributes {
});
return Ok(());
}
if meta.path.is_ident("derive") {
meta.parse_nested_meta(|meta| {
if meta.path.is_ident("Default") {
if self.default_val.is_none() {
let zero = DefaultVal::Lit(LitInt::new("0", meta.path.span()));
self.default_val = Some(zero);
}
} else if meta.path.is_ident("Debug") {
self.debug_trait = true;
} else if meta.path.is_ident("From") {
self.from_trait = Some(meta.path.require_ident()?.clone());
} else {
return Err(syn::Error::new(meta.path.span(), "unsupported derive"));
}
Ok(())
})?;
}
Ok(())
}
}
Expand Down Expand Up @@ -349,6 +368,11 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
debug_trait = codegen::generate_debug_trait_impl(struct_name, &field_definitions);
}

let mut from_trait = TokenStream2::new();
if let Some(ref ident) = bitfield_attrs.from_trait {
from_trait = codegen::generate_from_trait_impl(struct_name, base_data_type, ident.span());
}

let mut defmt_trait = TokenStream2::new();
if bitfield_attrs.defmt_trait.is_some() {
defmt_trait = codegen::generate_defmt_trait_impl(
Expand Down Expand Up @@ -439,6 +463,8 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {

#debug_trait

#from_trait

#defmt_trait

#( #new_with_builder_chain )*
Expand Down
113 changes: 113 additions & 0 deletions bitbybit/tests/derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use arbitrary_int::u31;
use bitbybit::bitfield;
use core::ptr::NonNull;

// native inner type

#[bitfield(u32, default = 42, derive(Default, Debug, From))]
#[derive(PartialEq)] // NOTE works because `raw_value` exists after #[bitfield(...)]
struct TestNative {}

fn test_native_int() {
let t: TestNative = Default::default();
assert_eq!(42, t.raw_value);

let debug = format!("{t:?}");
assert!(debug.starts_with("TestNative"));

let t = TestNative::from(42);
assert_eq!(42, t.raw_value);

let value = u32::from(t);
assert_eq!(42, value);

let t: TestNative = value.into();
assert_eq!(42, t.raw_value);

let value: u32 = t.into();
assert_eq!(42, value);
}

// arbitrary_int inner type

#[bitfield(u31, default = 42, derive(Default, Debug, From))]
struct TestArbitrary {}

fn test_arbitrary_int() {
const _42: u31 = u31::from_u32(42);

let t: TestArbitrary = Default::default();
assert_eq!(_42, t.raw_value());

let debug = format!("{t:?}");
assert!(debug.starts_with("TestArbitrary"));

let t = TestArbitrary::from(_42);
assert_eq!(_42, t.raw_value());

let value = u31::from(t);
assert_eq!(_42, value);

let t: TestArbitrary = value.into();
assert_eq!(_42, t.raw_value());

let value: u31 = t.into();
assert_eq!(_42, value);
}

// generic conversion to/from raw types

trait NonNullExt<T> {
unsafe fn read_volatile_le(self) -> T;
unsafe fn read_volatile_be(self) -> T;
unsafe fn write_volatile_le(self, value: T);
unsafe fn write_volatile_be(self, value: T);
}

impl<T> NonNullExt<T> for NonNull<T>
where
T: From<u32> + Into<u32>, // NOTE works on any #[bitfield(u32, derive(From))]
{
unsafe fn read_volatile_le(self) -> T {
assert_eq!(size_of::<u32>(), size_of::<T>());
unsafe { u32::from_le(self.cast::<u32>().read_volatile()).into() }
}
unsafe fn read_volatile_be(self) -> T {
assert_eq!(size_of::<u32>(), size_of::<T>());
unsafe { u32::from_be(self.cast::<u32>().read_volatile()).into() }
}
unsafe fn write_volatile_le(self, val: T) {
assert_eq!(size_of::<u32>(), size_of::<T>());
unsafe { self.cast::<u32>().write_volatile(val.into().to_le()) };
}
unsafe fn write_volatile_be(self, val: T) {
assert_eq!(size_of::<u32>(), size_of::<T>());
unsafe { self.cast::<u32>().write_volatile(val.into().to_be()) };
}
}

fn test_generic_conversion() {
let mut le = TestNative::from(1u32.to_le());
let mut be = TestNative::from(1u32.to_be());
assert_ne!(le, be);

let ptr_le = NonNull::from_mut(&mut le);
assert_eq!(1u32.to_le(), le.raw_value());
assert_eq!(1u32, unsafe { ptr_le.read_volatile_le().raw_value() });

let ptr_be = NonNull::from_mut(&mut be);
assert_eq!(1u32.to_be(), be.raw_value());
assert_eq!(1u32, unsafe { ptr_be.read_volatile_be().raw_value() });

unsafe { ptr_le.write_volatile_le(Default::default()) };
assert_eq!(42u32.to_le(), le.raw_value());

unsafe { ptr_be.write_volatile_be(Default::default()) };
assert_eq!(42u32.to_be(), be.raw_value());
}

fn main() {
test_native_int();
test_arbitrary_int();
test_generic_conversion();
}
12 changes: 5 additions & 7 deletions bitbybit/tests/no_compile/invalid_field_range.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ error[E0599]: no method named `with_c` found for struct `PartialTest<true, true,
30 | Test::builder().with_a(u4::new(1)).with_b(u4::new(1)).with_c(true).build();
| ^^^^^^ method not found in `PartialTest<true, true, false>`
|
= note: the method was found for
- `PartialTest<false, b, false>`
= note: the method was found for `PartialTest<false, b, false>`
help: one of the expressions' fields has a method of the same name
|
30 | Test::builder().with_a(u4::new(1)).with_b(u4::new(1)).0.with_c(true).build();
Expand All @@ -29,17 +28,16 @@ error[E0599]: no method named `build` found for struct `PartialTest<false, false
31 | Test::builder().with_c(true).build();
| ^^^^^ method not found in `PartialTest<false, false, true>`
|
= note: the method was found for
- `PartialTest<true, true, false>`
= note: the method was found for `PartialTest<true, true, false>`

error[E0599]: no function or associated item named `builder` found for struct `Test2` in the current scope
error[E0599]: no associated function or constant named `builder` found for struct `Test2` in the current scope
--> tests/no_compile/invalid_field_range.rs:32:12
|
20 | #[bitfield(u8)]
| --------------- function or associated item `builder` not found for this struct
| --------------- associated function or constant `builder` not found for this struct
...
32 | Test2::builder().with_a(u4::new(1)).with_b(u4::new(1)).build();
| ^^^^^^^ function or associated item not found in `Test2`
| ^^^^^^^ associated function or constant not found in `Test2`
|
note: if you're trying to build a new `Test2`, consider using `Test2::new_with_raw_value` which returns `Test2`
--> tests/no_compile/invalid_field_range.rs:21:8
Expand Down
9 changes: 3 additions & 6 deletions bitbybit/tests/no_compile/missing_fields_in_builder.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ error[E0599]: no method named `build` found for struct `PartialTest<true, false>
12 | Test::builder().with_foo(1).build();
| ^^^^^ method not found in `PartialTest<true, false>`
|
= note: the method was found for
- `PartialTest<true, true>`
= note: the method was found for `PartialTest<true, true>`

error[E0599]: no method named `build` found for struct `PartialTest<false, true>` in the current scope
--> tests/no_compile/missing_fields_in_builder.rs:13:33
Expand All @@ -19,8 +18,7 @@ error[E0599]: no method named `build` found for struct `PartialTest<false, true>
13 | Test::builder().with_bar(1).build();
| ^^^^^ method not found in `PartialTest<false, true>`
|
= note: the method was found for
- `PartialTest<true, true>`
= note: the method was found for `PartialTest<true, true>`

error[E0599]: no method named `with_bar` found for struct `PartialTest<true, true>` in the current scope
--> tests/no_compile/missing_fields_in_builder.rs:15:45
Expand All @@ -31,8 +29,7 @@ error[E0599]: no method named `with_bar` found for struct `PartialTest<true, tru
15 | Test::builder().with_bar(1).with_foo(1).with_bar(2).build();
| ^^^^^^^^ method not found in `PartialTest<true, true>`
|
= note: the method was found for
- `PartialTest<foo, false>`
= note: the method was found for `PartialTest<foo, false>`
help: one of the expressions' fields has a method of the same name
|
15 | Test::builder().with_bar(1).with_foo(1).0.with_bar(2).build();
Expand Down
3 changes: 1 addition & 2 deletions bitbybit/tests/no_compile/overlapping_bitfield_bits.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ error[E0599]: no method named `with_b` found for struct `PartialTestExhaustiveOv
| |_________|
|
|
= note: the method was found for
- `PartialTestExhaustiveOverlap<false, false>`
= note: the method was found for `PartialTestExhaustiveOverlap<false, false>`
help: one of the expressions' fields has a method of the same name
|
48 | .0.with_b(0)
Expand Down
33 changes: 16 additions & 17 deletions bitbybit/tests/no_compile/overlapping_bitfield_u8_fields.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ error: bitfield!: Detected overlap of field `lower_bits` with other field
10 | | }
| |_^

error[E0599]: no function or associated item named `builder` found for struct `TestSometimesUnconstructable` in the current scope
error[E0599]: no associated function or constant named `builder` found for struct `TestSometimesUnconstructable` in the current scope
--> tests/no_compile/overlapping_bitfield_u8_fields.rs:46:35
|
12 | #[bitfield(u16)]
| ---------------- function or associated item `builder` not found for this struct
| ---------------- associated function or constant `builder` not found for this struct
...
46 | TestSometimesUnconstructable::builder().with_lower_bits(0).with_bit_upper(u4::new(0)).build();
| ^^^^^^^ function or associated item not found in `TestSometimesUnconstructable`
| ^^^^^^^ associated function or constant not found in `TestSometimesUnconstructable`
|
note: if you're trying to build a new `TestSometimesUnconstructable`, consider using `TestSometimesUnconstructable::new_with_raw_value` which returns `TestSometimesUnconstructable`
--> tests/no_compile/overlapping_bitfield_u8_fields.rs:13:8
Expand All @@ -33,21 +33,20 @@ error[E0599]: no method named `with_a` found for struct `PartialTestAllowed<true
48 | TestAllowed::builder().with_upper(u4::new(0)).with_lower(u12::new(0)).with_a(u2::new(0)).build();
| ^^^^^^ method not found in `PartialTestAllowed<true, true, false, false, false>`
|
= note: the method was found for
- `PartialTestAllowed<upper, false, c, b, false>`
= note: the method was found for `PartialTestAllowed<upper, false, c, b, false>`
help: one of the expressions' fields has a method of the same name
|
48 | TestAllowed::builder().with_upper(u4::new(0)).with_lower(u12::new(0)).0.with_a(u2::new(0)).build();
| ++

error[E0599]: no function or associated item named `builder` found for struct `TestSometimesUnconstructable` in the current scope
error[E0599]: no associated function or constant named `builder` found for struct `TestSometimesUnconstructable` in the current scope
--> tests/no_compile/overlapping_bitfield_u8_fields.rs:49:35
|
12 | #[bitfield(u16)]
| ---------------- function or associated item `builder` not found for this struct
| ---------------- associated function or constant `builder` not found for this struct
...
49 | TestSometimesUnconstructable::builder().with_lower_bits(0).with_bit_upper(u4::new(0)).with_middle_bits(0).build();
| ^^^^^^^ function or associated item not found in `TestSometimesUnconstructable`
| ^^^^^^^ associated function or constant not found in `TestSometimesUnconstructable`
|
note: if you're trying to build a new `TestSometimesUnconstructable`, consider using `TestSometimesUnconstructable::new_with_raw_value` which returns `TestSometimesUnconstructable`
--> tests/no_compile/overlapping_bitfield_u8_fields.rs:13:8
Expand Down Expand Up @@ -85,44 +84,44 @@ error[E0599]: no method named `build` found for struct `PartialTestAllowed<false
- `PartialTestAllowed<true, true, false, false, false>`
- `PartialTestAllowed<true, true, false, true, true>`

error[E0599]: no function or associated item named `builder` found for struct `TestSometimesUnconstructable` in the current scope
error[E0599]: no associated function or constant named `builder` found for struct `TestSometimesUnconstructable` in the current scope
--> tests/no_compile/overlapping_bitfield_u8_fields.rs:54:35
|
12 | #[bitfield(u16)]
| ---------------- function or associated item `builder` not found for this struct
| ---------------- associated function or constant `builder` not found for this struct
...
54 | TestSometimesUnconstructable::builder().with_lower_bits(0).build();
| ^^^^^^^ function or associated item not found in `TestSometimesUnconstructable`
| ^^^^^^^ associated function or constant not found in `TestSometimesUnconstructable`
|
note: if you're trying to build a new `TestSometimesUnconstructable`, consider using `TestSometimesUnconstructable::new_with_raw_value` which returns `TestSometimesUnconstructable`
--> tests/no_compile/overlapping_bitfield_u8_fields.rs:13:8
|
13 | struct TestSometimesUnconstructable {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0599]: no function or associated item named `builder` found for struct `TestSometimesUnconstructable` in the current scope
error[E0599]: no associated function or constant named `builder` found for struct `TestSometimesUnconstructable` in the current scope
--> tests/no_compile/overlapping_bitfield_u8_fields.rs:55:35
|
12 | #[bitfield(u16)]
| ---------------- function or associated item `builder` not found for this struct
| ---------------- associated function or constant `builder` not found for this struct
...
55 | TestSometimesUnconstructable::builder().with_middle_bits(0).build();
| ^^^^^^^ function or associated item not found in `TestSometimesUnconstructable`
| ^^^^^^^ associated function or constant not found in `TestSometimesUnconstructable`
|
note: if you're trying to build a new `TestSometimesUnconstructable`, consider using `TestSometimesUnconstructable::new_with_raw_value` which returns `TestSometimesUnconstructable`
--> tests/no_compile/overlapping_bitfield_u8_fields.rs:13:8
|
13 | struct TestSometimesUnconstructable {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0599]: no function or associated item named `builder` found for struct `TestUnconstructable` in the current scope
error[E0599]: no associated function or constant named `builder` found for struct `TestUnconstructable` in the current scope
--> tests/no_compile/overlapping_bitfield_u8_fields.rs:56:26
|
21 | #[bitfield(u16)]
| ---------------- function or associated item `builder` not found for this struct
| ---------------- associated function or constant `builder` not found for this struct
...
56 | TestUnconstructable::builder().with_bits(0).build();
| ^^^^^^^ function or associated item not found in `TestUnconstructable`
| ^^^^^^^ associated function or constant not found in `TestUnconstructable`
|
note: if you're trying to build a new `TestUnconstructable`, consider using `TestUnconstructable::new_with_raw_value` which returns `TestUnconstructable`
--> tests/no_compile/overlapping_bitfield_u8_fields.rs:22:8
Expand Down
1 change: 1 addition & 0 deletions bitbybit/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ fn all_tests() {
t.pass("tests/with_fields.rs");
t.pass("tests/correct.rs");
t.pass("tests/builder_value_field.rs");
t.pass("tests/derive.rs");

t.compile_fail("tests/no_compile/exhaustive_bitenum.rs");
t.compile_fail("tests/no_compile/non_exhaustive_bitenum.rs");
Expand Down