diff --git a/bitbybit-tests/src/bitfield_tests.rs b/bitbybit-tests/src/bitfield_tests.rs index bcb468c..70dc20b 100644 --- a/bitbybit-tests/src/bitfield_tests.rs +++ b/bitbybit-tests/src/bitfield_tests.rs @@ -431,10 +431,13 @@ fn builder_available_in_const_context() { a: u16, } - assert_eq!(const { Test::builder().with_a(123).build().raw_value() }, 123); + assert_eq!( + const { Test::builder().with_a(123).build().raw_value() }, + 123 + ); const { let raw = Test::builder().with_a(123).build().raw_value(); - if raw != 123 { + if raw != 123 { panic!("builder didn't build the right value `123`"); } } @@ -1181,6 +1184,86 @@ fn reserved_identifiers() { ); } +#[test] +fn builder_with_overlapping_fields() { + /// Floating Point Status Register + #[bitfield(u32, default = 0)] + #[derive(PartialEq, Eq, Debug)] + pub struct FPSR { + #[bit(24, rw)] + flush_denorm_to_zero: bool, + + #[bits(12..=13, rw)] + maskable_cause_bits: u2, + + #[bit(13, rw)] + cause_underflow: bool, + #[bit(12, rw)] + cause_inexact_operation: bool, + + #[bits(7..=8, rw)] + enable_bits: u2, + + #[bit(8, rw)] + enable_underflow: bool, + #[bit(7, rw)] + enable_inexact_operation: bool, + } + + FPSR::builder() + .with_flush_denorm_to_zero(true) + .with_enable_inexact_operation(false) + .with_enable_underflow(false) + .with_maskable_cause_bits(u2::ZERO) + .build(); +} + +#[test] +fn builder_with_overlapping_fields_long() { + #[bitfield(u128)] + #[derive(PartialEq, Eq, Debug)] + pub struct Long { + #[bits(0..=119, rw)] + rest: u120, + + #[bits(120..=121, rw)] + x: u2, + #[bits(122..=123, rw)] + y: u2, + #[bits(124..=125, rw)] + z: u2, + #[bits(126..=127, rw)] + q: u2, + + #[bit(120, rw)] + a: bool, + #[bit(121, rw)] + c: bool, + #[bit(122, rw)] + d: bool, + #[bit(123, rw)] + e: bool, + #[bit(124, rw)] + f: bool, + #[bit(125, rw)] + g: bool, + #[bit(126, rw)] + h: bool, + #[bit(127, rw)] + i: bool, + } + + Long::builder() + .with_h(true) + .with_i(true) + .with_z(u2::new(1)) + .with_e(false) + .with_d(false) + .with_x(u2::new(1)) + .with_rest(u120::ZERO) + .build(); +} + #[test] fn new_with_construction1() { #[bitfield(u128, default = 0)] diff --git a/bitbybit/src/bitfield/codegen.rs b/bitbybit/src/bitfield/codegen.rs index 570d3fc..93305c4 100644 --- a/bitbybit/src/bitfield/codegen.rs +++ b/bitbybit/src/bitfield/codegen.rs @@ -4,8 +4,8 @@ use crate::bitfield::{ }; use proc_macro2::{Ident, TokenStream}; use quote::{quote, TokenStreamExt as _}; +use std::ops::Range; use std::str::FromStr; -use std::{collections::HashSet, ops::Range}; use syn::{LitInt, Type, Visibility}; /// Performs the codegen for the bitfield. @@ -473,8 +473,6 @@ pub fn make_builder( } }); - let mut set_params: HashSet> = HashSet::default(); - let mut any_overlaps = false; let masks: Vec<_> = definitions .clone() .map(|def| { @@ -496,6 +494,9 @@ pub fn make_builder( } }) .collect(); + + let fully_initialized = masks.iter().fold(0, |acc, val| acc | val); + for (i, field_definition) in definitions.clone().enumerate() { if let Some(setter_type) = field_definition.setter_type.as_ref() { let field_name = &field_definition.field_name; @@ -543,7 +544,6 @@ pub fn make_builder( if overlaps { names.push(quote!(false)); result.push(quote!(false)); - any_overlaps = true; } else { let name = syn::parse_str::(format!("{}", def.field_name).as_str()) .unwrap(); @@ -553,7 +553,6 @@ pub fn make_builder( } } } - set_params.insert(builder_params); let doc_comment = &field_definition.doc_comment; new_with_builder_chain.push(quote! { @@ -570,24 +569,54 @@ pub fn make_builder( } } + let mut set_params = Vec::with_capacity(masks.len()); + fn param_combinations(agg: Vec, len: usize, sum: &mut Vec>, masks: &[u128], fully_initialized: u128) { + if agg.len() == len { + if masks.iter().enumerate().zip(agg.iter()).filter_map(|(mask, present)| { + if *present { + Some(mask) + } else { + None + } + }).fold(0, |acc, mask| acc ^ mask.1) == fully_initialized { + sum.push(agg); + } + return; + } + for val in [true, false] { + let mut agg = agg.clone(); + agg.push(val); + param_combinations(agg, len, sum, masks, fully_initialized); + } + } + param_combinations(Vec::with_capacity(masks.len()), masks.len(), &mut set_params, &masks, fully_initialized); + set_params.retain(|vals| { + let present = vals + .iter() + .zip(masks.iter()) + .filter(|(present, _)| **present) + .map(|(_, mask)| *mask) + .collect::>(); + for (i, mask) in present.iter().enumerate() { + if !present + .iter() + .enumerate() + .all(|(j, m)| j == i || mask & m == 0) + { + return false; + } + } + let acc = present.iter().fold(0, |acc, mask| acc | mask); + acc == fully_initialized + }); + let unset_params = definitions.map(|_| quote! { false }).collect::>(); let builder_struct_name_str = builder_struct_name.to_string(); let mut is_buildable = false; for set_params in set_params { - if any_overlaps && set_params.iter().all(|p| *p) { - // Do not create an uncallable `PartialFoo::build()` as it can't be - // constructed. This is only to avoid including it in the list of valid types in E0599. - continue; - } - let mut mask = 0; - for (i, ¶m) in set_params.iter().enumerate() { - if param { - mask |= masks[i]; - } - } if !has_default - && (mask + && (fully_initialized | u128::MAX .overflowing_shl(base_data_size.exposed.try_into().unwrap()) .0) diff --git a/bitbybit/tests/no_compile/overlapping_bitfield_u8_fields.stderr b/bitbybit/tests/no_compile/overlapping_bitfield_u8_fields.stderr index a4bb1e3..5f20ed5 100644 --- a/bitbybit/tests/no_compile/overlapping_bitfield_u8_fields.stderr +++ b/bitbybit/tests/no_compile/overlapping_bitfield_u8_fields.stderr @@ -66,9 +66,7 @@ error[E0599]: no method named `build` found for struct `PartialTestAllowed` - - `PartialTestAllowed` - `PartialTestAllowed` - - `PartialTestAllowed` error[E0599]: no method named `build` found for struct `PartialTestAllowed` in the current scope --> tests/no_compile/overlapping_bitfield_u8_fields.rs:52:38 @@ -81,9 +79,7 @@ error[E0599]: no method named `build` found for struct `PartialTestAllowed` - - `PartialTestAllowed` - `PartialTestAllowed` - - `PartialTestAllowed` error[E0599]: no function or associated item named `builder` found for struct `TestSometimesUnconstructable` in the current scope --> tests/no_compile/overlapping_bitfield_u8_fields.rs:54:35