Skip to content

Commit 705a4d2

Browse files
committed
Allow bitflags v1 and v2 to coexist
1 parent c874f24 commit 705a4d2

File tree

9 files changed

+142
-11
lines changed

9 files changed

+142
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2929
- [#843]: `defmt`: Sort IDs of log msgs by severity to allow runtime filtering by severity
3030
- [#822]: `CI`: Run `cargo semver-checks` on every PR
3131
- [#807]: `defmt-print`: Add `watch_elf` flag to allow ELF file reload without restarting `defmt-print`
32+
- [#877]: `defmt`:Allow `bitflags` v1 and v2 to coexist
3233

3334
[#889]: https://github.com/knurling-rs/defmt/pull/889
3435
[#887]: https://github.com/knurling-rs/defmt/pull/887

defmt/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ version = "0.3.8"
2121
alloc = []
2222
avoid-default-panic = []
2323
ip_in_core = []
24+
bitflagsv2 = ["dep:bitflagsv2"]
2425

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

4951
[dev-dependencies]
5052
rustc_version = "0.4"

defmt/src/export/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ use crate::{Format, Formatter, Str};
88
pub use self::integers::*;
99
pub use bitflags::bitflags;
1010

11+
#[cfg(feature = "bitflagsv2")]
12+
pub use bitflagsv2::bitflags as bitflagsv2;
13+
1114
pub trait UnsignedInt {}
1215
impl UnsignedInt for u8 {}
1316
impl UnsignedInt for u16 {}

defmt/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,11 +361,21 @@ pub use defmt_macros::timestamp;
361361
/// const ABC = Self::A.bits | Self::B.bits | Self::C.bits;
362362
/// }
363363
/// }
364-
///
364+
/// #[cfg(feature = "bitflagsv2")]
365+
/// defmt::bitflagsv2! {
366+
/// struct Flags: u32 {
367+
/// const A = 0b00000001;
368+
/// const B = 0b00000010;
369+
/// const C = 0b00000100;
370+
/// const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits()
371+
/// }
372+
/// }
365373
/// defmt::info!("Flags::ABC: {}", Flags::ABC);
366374
/// defmt::info!("Flags::empty(): {}", Flags::empty());
367375
/// ```
368376
pub use defmt_macros::bitflags;
377+
#[cfg(feature = "bitflagsv2")]
378+
pub use defmt_macros::bitflagsv2;
369379

370380
#[doc(hidden)] // documented as the `Format` trait instead
371381
pub use defmt_macros::Format;

firmware/qemu/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ name = "defmt-test"
1111
harness = false
1212

1313
[dependencies]
14-
defmt = { path = "../../defmt" }
14+
defmt = { path = "../../defmt", default-features = true, features = ["bitflagsv2"]}
1515
defmt-semihosting = { path = "../defmt-semihosting" }
1616
defmt-test = { path = "../defmt-test" }
1717
cortex-m = "0.7"
@@ -20,6 +20,7 @@ cortex-m-semihosting = "0.5"
2020
alloc-cortex-m = { version = "0.4", optional = true }
2121
linked_list_allocator = { version = "0.10.2", optional = true }
2222

23+
2324
[features]
2425
alloc = ["defmt/alloc", "alloc-cortex-m", "linked_list_allocator/const_mut_refs"]
2526
ip_in_core = ["defmt/ip_in_core"]

firmware/qemu/src/bin/bitflagsv2.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
INFO Flags::empty(): FLAG_0
2+
INFO Flags::empty(): Flags(0x0) (fmt::Debug)
3+
INFO Flags::all(): FLAG_1 | FLAG_2 | FLAG_7 | FLAG_7_COPY
4+
INFO Flags::all(): Flags(FLAG_1 | FLAG_2 | FLAG_7) (fmt::Debug)
5+
INFO Flags::FLAG_1: FLAG_1
6+
INFO Flags::FLAG_1: Flags(FLAG_1) (fmt::Debug)
7+
INFO Flags::FLAG_7: FLAG_7 | FLAG_7_COPY
8+
INFO Flags::FLAG_7: Flags(FLAG_7) (fmt::Debug)
9+
INFO LargeFlags::ALL: MSB | ALL | NON_LITERAL
10+
INFO LargeFlags::ALL: LargeFlags(MSB | ALL) (fmt::Debug)
11+
INFO LargeFlags::empty(): (empty)
12+
INFO LargeFlags::empty(): LargeFlags(0x0) (fmt::Debug)

firmware/qemu/src/bin/bitflagsv2.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#![no_std]
2+
#![no_main]
3+
#![allow(clippy::bad_bit_mask)]
4+
5+
use cortex_m_rt::entry;
6+
use cortex_m_semihosting::debug;
7+
use defmt::{bitflagsv2, Debug2Format};
8+
9+
use defmt_semihosting as _; // global logger
10+
11+
bitflagsv2! {
12+
#[derive(Debug)]
13+
struct Flags: u8 {
14+
#[cfg(not(never))]
15+
const FLAG_0 = 0b00;
16+
const FLAG_1 = 0b01;
17+
const FLAG_2 = 0b10;
18+
const FLAG_7 = 1 << 7;
19+
20+
const FLAG_7_COPY = Self::FLAG_7.bits();
21+
22+
#[cfg(never)]
23+
const CFGD_OUT = 1;
24+
}
25+
}
26+
27+
bitflagsv2! {
28+
#[derive(Debug)]
29+
struct LargeFlags: u128 {
30+
const MSB = 1 << 127;
31+
const ALL = !0;
32+
const NON_LITERAL = compute_flag_value(0x346934553632462);
33+
}
34+
}
35+
36+
const fn compute_flag_value(x: u128) -> u128 {
37+
x ^ 0xdeadbeef
38+
}
39+
40+
#[entry]
41+
fn main() -> ! {
42+
defmt::info!("Flags::empty(): {}", Flags::empty());
43+
defmt::info!(
44+
"Flags::empty(): {} (fmt::Debug)",
45+
Debug2Format(&Flags::empty())
46+
);
47+
defmt::info!("Flags::all(): {}", Flags::all());
48+
defmt::info!("Flags::all(): {} (fmt::Debug)", Debug2Format(&Flags::all()));
49+
defmt::info!("Flags::FLAG_1: {}", Flags::FLAG_1);
50+
defmt::info!(
51+
"Flags::FLAG_1: {} (fmt::Debug)",
52+
Debug2Format(&Flags::FLAG_1)
53+
);
54+
defmt::info!("Flags::FLAG_7: {}", Flags::FLAG_7);
55+
defmt::info!(
56+
"Flags::FLAG_7: {} (fmt::Debug)",
57+
Debug2Format(&Flags::FLAG_7)
58+
);
59+
60+
defmt::info!("LargeFlags::ALL: {}", LargeFlags::ALL);
61+
defmt::info!(
62+
"LargeFlags::ALL: {} (fmt::Debug)",
63+
Debug2Format(&LargeFlags::ALL)
64+
);
65+
defmt::info!("LargeFlags::empty(): {}", LargeFlags::empty());
66+
defmt::info!(
67+
"LargeFlags::empty(): {} (fmt::Debug)",
68+
Debug2Format(&LargeFlags::empty())
69+
);
70+
71+
loop {
72+
debug::exit(debug::EXIT_SUCCESS)
73+
}
74+
}
75+
76+
// like `panic-semihosting` but doesn't print to stdout (that would corrupt the defmt stream)
77+
#[cfg(target_os = "none")]
78+
#[panic_handler]
79+
fn panic(_: &core::panic::PanicInfo) -> ! {
80+
loop {
81+
debug::exit(debug::EXIT_FAILURE)
82+
}
83+
}

macros/src/items/bitflags.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use proc_macro::TokenStream;
22
use proc_macro2::TokenStream as TokenStream2;
3-
use quote::{quote, ToTokens};
3+
use quote::{format_ident, quote, ToTokens};
44
use syn::parse_macro_input;
55

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

1010
mod input;
1111

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

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

2829
let ident = input.ident();
2930
let ty = input.ty();
30-
let flag_statics = codegen_flag_statics(&input);
31+
let flag_statics = codegen_flag_statics(&input, is_v2);
32+
let bitflag_macro = format_ident!("{bitflags_tag}");
3133
quote!(
3234
const _: () = {
3335
fn assert<T: defmt::export::UnsignedInt>() {}
@@ -36,7 +38,7 @@ pub(crate) fn expand(input: TokenStream) -> TokenStream {
3638
#(#flag_statics)*
3739
};
3840

39-
defmt::export::bitflags! {
41+
defmt::export::#bitflag_macro! {
4042
#bitflags_input
4143
}
4244

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

62-
fn codegen_flag_statics(input: &Input) -> Vec<TokenStream2> {
64+
fn codegen_flag_statics(input: &Input, is_v2: bool) -> Vec<TokenStream2> {
6365
input
6466
.flags()
6567
.enumerate()
@@ -69,11 +71,21 @@ fn codegen_flag_statics(input: &Input) -> Vec<TokenStream2> {
6971
let struct_name = input.ident();
7072
let repr_ty = input.ty();
7173

74+
let _tag = if is_v2 {
75+
"bitflagsv2_value"
76+
} else {
77+
"bitflags_value"
78+
};
7279
let sym_name = construct::mangled_symbol_name(
73-
"bitflags_value",
80+
_tag,
7481
&format!("{}::{i}::{}", input.ident(), flag.ident()),
7582
);
7683

84+
let bits_access = if is_v2 {
85+
quote! { bits() }
86+
} else {
87+
quote! { bits}
88+
};
7789
quote! {
7890
#(#cfg_attrs)*
7991
#[cfg_attr(target_os = "macos", link_section = ".defmt,end")]
@@ -84,7 +96,7 @@ fn codegen_flag_statics(input: &Input) -> Vec<TokenStream2> {
8496
// causes a value such as `1 << 127` to be evaluated as an `i32`, which
8597
// overflows. So we instead coerce (but don't cast) it to the bitflags' raw
8698
// type, and then cast that to u128.
87-
let coerced_value: #repr_ty = #struct_name::#var_name.bits;
99+
let coerced_value: #repr_ty = #struct_name::#var_name.#bits_access;
88100
coerced_value as u128
89101
};
90102
}

macros/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,14 @@ pub fn write(args: TokenStream) -> TokenStream {
199199
#[proc_macro]
200200
#[proc_macro_error]
201201
pub fn bitflags(ts: TokenStream) -> TokenStream {
202-
items::bitflags::expand(ts)
202+
items::bitflags::expand(ts, false)
203+
}
204+
205+
/* # Items */
206+
#[proc_macro]
207+
#[proc_macro_error]
208+
pub fn bitflagsv2(ts: TokenStream) -> TokenStream {
209+
items::bitflags::expand(ts, true)
203210
}
204211

205212
#[proc_macro]

0 commit comments

Comments
 (0)