Skip to content

Commit

Permalink
Allow bitflags v1 and v2 to coexist
Browse files Browse the repository at this point in the history
  • Loading branch information
manushT committed Nov 29, 2024
1 parent aa9c647 commit b923d69
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ We have several packages which live in this repository. Changes are tracked sepa
* [#845] `decoder`: fix println!() records being printed with formatting
* [#843] `defmt`: Sort IDs of log msgs by severity to allow runtime filtering by severity
* [#822] `CI`: Run `cargo semver-checks` on every PR
* [#877]:`defmt`: Allow `bitflags` v1 and v2 to coexist

### [defmt-v0.3.8] - 2024-05-17

Expand Down
2 changes: 2 additions & 0 deletions defmt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ version = "0.3.9"
alloc = []
avoid-default-panic = []
ip_in_core = []
bitflagsv2 = ["dep:bitflagsv2"]

# Encoding feature flags. These should only be set by end-user crates, not by library crates.
#
Expand All @@ -45,6 +46,7 @@ unstable-test = [ "defmt-macros/unstable-test" ]
[dependencies]
defmt-macros = { path = "../macros", version = "0.3.10" }
bitflags = "1"
bitflagsv2 = { package = "bitflags", version = "2", optional = true }

[dev-dependencies]
rustc_version = "0.4"
Expand Down
3 changes: 3 additions & 0 deletions defmt/src/export/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ use crate::{Format, Formatter, Str};
pub use self::integers::*;
pub use bitflags::bitflags;

#[cfg(feature = "bitflagsv2")]
pub use bitflagsv2::bitflags as bitflagsv2;

pub trait UnsignedInt {}
impl UnsignedInt for u8 {}
impl UnsignedInt for u16 {}
Expand Down
12 changes: 11 additions & 1 deletion defmt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,11 +361,21 @@ pub use defmt_macros::timestamp;
/// const ABC = Self::A.bits | Self::B.bits | Self::C.bits;
/// }
/// }
///
/// #[cfg(feature = "bitflagsv2")]
/// defmt::bitflagsv2! {
/// struct Flags: u32 {
/// const A = 0b00000001;
/// const B = 0b00000010;
/// const C = 0b00000100;
/// const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits()
/// }
/// }
/// defmt::info!("Flags::ABC: {}", Flags::ABC);
/// defmt::info!("Flags::empty(): {}", Flags::empty());
/// ```
pub use defmt_macros::bitflags;
#[cfg(feature = "bitflagsv2")]
pub use defmt_macros::bitflagsv2;

#[doc(hidden)] // documented as the `Format` trait instead
pub use defmt_macros::Format;
Expand Down
2 changes: 1 addition & 1 deletion firmware/qemu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ name = "defmt-test"
harness = false

[dependencies]
defmt = { path = "../../defmt" }
defmt = { path = "../../defmt", default-features = true, features = ["bitflagsv2"]}
defmt-semihosting = { path = "../defmt-semihosting" }
defmt-test = { path = "../defmt-test" }
cortex-m = "0.7"
Expand Down
12 changes: 12 additions & 0 deletions firmware/qemu/src/bin/bitflagsv2.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
INFO Flags::empty(): FLAG_0
INFO Flags::empty(): Flags(0x0) (fmt::Debug)
INFO Flags::all(): FLAG_1 | FLAG_2 | FLAG_7 | FLAG_7_COPY
INFO Flags::all(): Flags(FLAG_1 | FLAG_2 | FLAG_7) (fmt::Debug)
INFO Flags::FLAG_1: FLAG_1
INFO Flags::FLAG_1: Flags(FLAG_1) (fmt::Debug)
INFO Flags::FLAG_7: FLAG_7 | FLAG_7_COPY
INFO Flags::FLAG_7: Flags(FLAG_7) (fmt::Debug)
INFO LargeFlags::ALL: MSB | ALL | NON_LITERAL
INFO LargeFlags::ALL: LargeFlags(MSB | ALL) (fmt::Debug)
INFO LargeFlags::empty(): (empty)
INFO LargeFlags::empty(): LargeFlags(0x0) (fmt::Debug)
81 changes: 81 additions & 0 deletions firmware/qemu/src/bin/bitflagsv2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#![no_std]
#![no_main]
#![allow(clippy::bad_bit_mask)]

use cortex_m_rt::entry;
use cortex_m_semihosting::debug;
use defmt::{bitflagsv2, Debug2Format};

use defmt_semihosting as _; // global logger

bitflagsv2! {
#[derive(Debug)]
struct Flags: u8 {
#[cfg(not(never))]
const FLAG_0 = 0b00;
const FLAG_1 = 0b01;
const FLAG_2 = 0b10;
const FLAG_7 = 1 << 7;
const FLAG_7_COPY = Self::FLAG_7.bits();
#[cfg(never)]
const CFGD_OUT = 1;
}
}

bitflagsv2! {
#[derive(Debug)]
struct LargeFlags: u128 {
const MSB = 1 << 127;
const ALL = !0;
const NON_LITERAL = compute_flag_value(0x346934553632462);
}
}

const fn compute_flag_value(x: u128) -> u128 {
x ^ 0xdeadbeef
}

#[entry]
fn main() -> ! {
defmt::info!("Flags::empty(): {}", Flags::empty());
defmt::info!(
"Flags::empty(): {} (fmt::Debug)",
Debug2Format(&Flags::empty())
);
defmt::info!("Flags::all(): {}", Flags::all());
defmt::info!("Flags::all(): {} (fmt::Debug)", Debug2Format(&Flags::all()));
defmt::info!("Flags::FLAG_1: {}", Flags::FLAG_1);
defmt::info!(
"Flags::FLAG_1: {} (fmt::Debug)",
Debug2Format(&Flags::FLAG_1)
);
defmt::info!("Flags::FLAG_7: {}", Flags::FLAG_7);
defmt::info!(
"Flags::FLAG_7: {} (fmt::Debug)",
Debug2Format(&Flags::FLAG_7)
);

defmt::info!("LargeFlags::ALL: {}", LargeFlags::ALL);
defmt::info!(
"LargeFlags::ALL: {} (fmt::Debug)",
Debug2Format(&LargeFlags::ALL)
);
defmt::info!("LargeFlags::empty(): {}", LargeFlags::empty());
defmt::info!(
"LargeFlags::empty(): {} (fmt::Debug)",
Debug2Format(&LargeFlags::empty())
);

loop {
debug::exit(debug::EXIT_SUCCESS)
}
}

// like `panic-semihosting` but doesn't print to stdout (that would corrupt the defmt stream)
#[cfg(target_os = "none")]
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
loop {
debug::exit(debug::EXIT_FAILURE)
}
}
28 changes: 20 additions & 8 deletions macros/src/items/bitflags.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, ToTokens};
use quote::{format_ident, quote, ToTokens};
use syn::parse_macro_input;

use crate::{cargo, construct};
Expand All @@ -9,7 +9,7 @@ use self::input::Input;

mod input;

pub(crate) fn expand(input: TokenStream) -> TokenStream {
pub(crate) fn expand(input: TokenStream, is_v2: bool) -> TokenStream {
let bitflags_input = TokenStream2::from(input.clone());
let input = parse_macro_input!(input as Input);

Expand All @@ -23,11 +23,13 @@ pub(crate) fn expand(input: TokenStream) -> TokenStream {
construct::crate_local_disambiguator(),
cargo::crate_name(),
);
let format_tag = construct::interned_string(&format_string, "bitflags", false, None);
let bitflags_tag = if is_v2 { "bitflagsv2" } else { "bitflags" };
let format_tag = construct::interned_string(&format_string, bitflags_tag, false, None);

let ident = input.ident();
let ty = input.ty();
let flag_statics = codegen_flag_statics(&input);
let flag_statics = codegen_flag_statics(&input, is_v2);
let bitflag_macro = format_ident!("{bitflags_tag}");
quote!(
const _: () = {
fn assert<T: defmt::export::UnsignedInt>() {}
Expand All @@ -36,7 +38,7 @@ pub(crate) fn expand(input: TokenStream) -> TokenStream {
#(#flag_statics)*
};

defmt::export::bitflags! {
defmt::export::#bitflag_macro! {
#bitflags_input
}

Expand All @@ -59,7 +61,7 @@ pub(crate) fn expand(input: TokenStream) -> TokenStream {
.into()
}

fn codegen_flag_statics(input: &Input) -> Vec<TokenStream2> {
fn codegen_flag_statics(input: &Input, is_v2: bool) -> Vec<TokenStream2> {
input
.flags()
.enumerate()
Expand All @@ -69,11 +71,21 @@ fn codegen_flag_statics(input: &Input) -> Vec<TokenStream2> {
let struct_name = input.ident();
let repr_ty = input.ty();

let _tag = if is_v2 {
"bitflagsv2_value"
} else {
"bitflags_value"
};
let sym_name = construct::mangled_symbol_name(
"bitflags_value",
_tag,
&format!("{}::{i}::{}", input.ident(), flag.ident()),
);

let bits_access = if is_v2 {
quote! { bits() }
} else {
quote! { bits}
};
quote! {
#(#cfg_attrs)*
#[cfg_attr(target_os = "macos", link_section = ".defmt,end")]
Expand All @@ -84,7 +96,7 @@ fn codegen_flag_statics(input: &Input) -> Vec<TokenStream2> {
// causes a value such as `1 << 127` to be evaluated as an `i32`, which
// overflows. So we instead coerce (but don't cast) it to the bitflags' raw
// type, and then cast that to u128.
let coerced_value: #repr_ty = #struct_name::#var_name.bits;
let coerced_value: #repr_ty = #struct_name::#var_name.#bits_access;
coerced_value as u128
};
}
Expand Down
9 changes: 8 additions & 1 deletion macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,14 @@ pub fn write(args: TokenStream) -> TokenStream {
#[proc_macro]
#[proc_macro_error]
pub fn bitflags(ts: TokenStream) -> TokenStream {
items::bitflags::expand(ts)
items::bitflags::expand(ts, false)
}

/* # Items */
#[proc_macro]
#[proc_macro_error]
pub fn bitflagsv2(ts: TokenStream) -> TokenStream {
items::bitflags::expand(ts, true)
}

#[proc_macro]
Expand Down

0 comments on commit b923d69

Please sign in to comment.