Skip to content

Constructing BitFlags in const context #63

@crazyjackel

Description

@crazyjackel

When using the #[bitflags] macro, I encountered an issue where flags must have exactly one set bit. My code is as follows:

#[bitflags]
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq)]
enum AiPrimitiveType {
    Unknown = 0x00,
    Point = 0x01,
    Line = 0x02,
    Triangle = 0x04,
    Polygon = 0x08,
    NgonEncodingFlag = 0x10,
}

This produces the following error:

error: Flags must have exactly one set bit
  --> src/structs/mesh.rs:40:5
   |
40 |     Unknown = 0x00,
   |     ^^^^^^^

The error is caused by the check_flag logic:

if !n.is_power_of_two() {
    Err(syn::Error::new(
        flag.span,
        "Flags must have exactly one set bit",
    ))
} 

However, looking at the macro expansion:

#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq)]
enum AiPrimitiveType {
    Point = 0x01,
    Line = 0x02,
    Triangle = 0x04,
    Polygon = 0x08,
    NgonEncodingFlag = 0x10,
}
impl ::enumflags2::_internal::core::ops::Not for AiPrimitiveType {
    type Output = ::enumflags2::BitFlags<Self>;
    #[inline(always)]
    fn not(self) -> Self::Output {
        use ::enumflags2::BitFlags;
        BitFlags::from_flag(self).not()
    }
}
impl ::enumflags2::_internal::core::ops::BitOr for AiPrimitiveType {
    type Output = ::enumflags2::BitFlags<Self>;
    #[inline(always)]
    fn bitor(self, other: Self) -> Self::Output {
        use ::enumflags2::BitFlags;
        BitFlags::from_flag(self) | other
    }
}
impl ::enumflags2::_internal::core::ops::BitAnd for AiPrimitiveType {
    type Output = ::enumflags2::BitFlags<Self>;
    #[inline(always)]
    fn bitand(self, other: Self) -> Self::Output {
        use ::enumflags2::BitFlags;
        BitFlags::from_flag(self) & other
    }
}
impl ::enumflags2::_internal::core::ops::BitXor for AiPrimitiveType {
    type Output = ::enumflags2::BitFlags<Self>;
    #[inline(always)]
    fn bitxor(self, other: Self) -> Self::Output {
        use ::enumflags2::BitFlags;
        BitFlags::from_flag(self) ^ other
    }
}
unsafe impl ::enumflags2::_internal::RawBitFlags for AiPrimitiveType {
    type Numeric = u8;
    const EMPTY: Self::Numeric = 0;
    const DEFAULT: Self::Numeric = 0;
    const ALL_BITS: Self::Numeric = 0
        | (Self::Point as u8)
        | (Self::Line as u8)
        | (Self::Triangle as u8)
        | (Self::Polygon as u8)
        | (Self::NgonEncodingFlag as u8);
    const BITFLAGS_TYPE_NAME: &'static str = "BitFlags<AiPrimitiveType>";
    fn bits(self) -> Self::Numeric {
        self as u8
    }
}
impl ::enumflags2::BitFlag for AiPrimitiveType {}

This restriction seems unnecessary and limits usability, particularly for flags like Unknown = 0x00 or when defining named combinations. These combinations would naturally follow enum-to-u8 behavior as expected.

The only potential issue I see is with ALL_BITS as currently implemented. This could be resolved by ensuring it rounds up to the largest power of 2, setting all bits appropriately. Could this restriction be relaxed to allow more flexibility?

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions