Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap generated flags types in a newtype #264

Closed
wants to merge 7 commits into from

Conversation

KodrAus
Copy link
Member

@KodrAus KodrAus commented Nov 2, 2021

Part of #262

This lets us support:

It introduces an internal unnamable type as the single field of a BitFlags type that holds all the trait implementations. So instead of us having to commit to implementing traits on your generated flags type, we can instead add them to this internal field type and you can choose to derive them on the flags type based on that implementation.

Given an input like:

bitflags! {
    /// baz
    pub struct Flags: u32 {
        const A = 0b00000001;
        #[doc = "bar"]
        const B = 0b00000010;
        const C = 0b00000100;
        #[doc = "foo"]
        const ABC = Flags::A.bits() | Flags::B.bits() | Flags::C.bits();
    }
}

we'll generate code that looks something like:

use bitflags::bitflags;
/// baz
pub struct Flags(<Self as ::bitflags::__private::BitFlagsField>::Field);
const _: () = {
    #[repr(transparent)]
    pub struct __Field(u32);
    #[automatically_derived]
    #[allow(unused_qualifications)]
    impl ::core::clone::Clone for __Field {
        #[inline]
        fn clone(&self) -> __Field {
            {
                let _: ::core::clone::AssertParamIsClone<u32>;
                *self
            }
        }
    }
    #[automatically_derived]
    #[allow(unused_qualifications)]
    impl ::core::marker::Copy for __Field {}
    impl ::core::marker::StructuralPartialEq for __Field {}
    #[automatically_derived]
    #[allow(unused_qualifications)]
    impl ::core::cmp::PartialEq for __Field {
        #[inline]
        fn eq(&self, other: &__Field) -> bool {
            match *other {
                __Field(ref __self_1_0) => match *self {
                    __Field(ref __self_0_0) => (*__self_0_0) == (*__self_1_0),
                },
            }
        }
        #[inline]
        fn ne(&self, other: &__Field) -> bool {
            match *other {
                __Field(ref __self_1_0) => match *self {
                    __Field(ref __self_0_0) => (*__self_0_0) != (*__self_1_0),
                },
            }
        }
    }
    impl ::core::marker::StructuralEq for __Field {}
    #[automatically_derived]
    #[allow(unused_qualifications)]
    impl ::core::cmp::Eq for __Field {
        #[inline]
        #[doc(hidden)]
        #[no_coverage]
        fn assert_receiver_is_total_eq(&self) -> () {
            {
                let _: ::core::cmp::AssertParamIsEq<u32>;
            }
        }
    }
    #[automatically_derived]
    #[allow(unused_qualifications)]
    impl ::core::cmp::PartialOrd for __Field {
        #[inline]
        fn partial_cmp(&self, other: &__Field) -> ::core::option::Option<::core::cmp::Ordering> {
            match *other {
                __Field(ref __self_1_0) => match *self {
                    __Field(ref __self_0_0) => {
                        match ::core::cmp::PartialOrd::partial_cmp(&(*__self_0_0), &(*__self_1_0)) {
                            ::core::option::Option::Some(::core::cmp::Ordering::Equal) => {
                                ::core::option::Option::Some(::core::cmp::Ordering::Equal)
                            }
                            cmp => cmp,
                        }
                    }
                },
            }
        }
    }
    #[automatically_derived]
    #[allow(unused_qualifications)]
    impl ::core::cmp::Ord for __Field {
        #[inline]
        fn cmp(&self, other: &__Field) -> ::core::cmp::Ordering {
            match *other {
                __Field(ref __self_1_0) => match *self {
                    __Field(ref __self_0_0) => {
                        match ::core::cmp::Ord::cmp(&(*__self_0_0), &(*__self_1_0)) {
                            ::core::cmp::Ordering::Equal => ::core::cmp::Ordering::Equal,
                            cmp => cmp,
                        }
                    }
                },
            }
        }
    }
    #[automatically_derived]
    #[allow(unused_qualifications)]
    impl ::core::hash::Hash for __Field {
        fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
            match *self {
                __Field(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state),
            }
        }
    }
    impl ::bitflags::__private::BitFlagsField for Flags {
        type Field = __Field;
    }
    impl ::bitflags::_core::fmt::Debug for __Field {
        fn fmt(&self, f: &mut ::bitflags::_core::fmt::Formatter) -> ::bitflags::_core::fmt::Result {
            #[allow(non_snake_case)]
            trait __BitFlags {
                #[inline]
                fn A(&self) -> bool {
                    false
                }
                #[inline]
                fn B(&self) -> bool {
                    false
                }
                #[inline]
                fn C(&self) -> bool {
                    false
                }
                #[inline]
                fn ABC(&self) -> bool {
                    false
                }
            }
            #[allow(non_snake_case)]
            impl __BitFlags for Flags {
                #[allow(deprecated)]
                #[inline]
                fn A(&self) -> bool {
                    if Self::A.bits() == 0 && self.bits() != 0 {
                        false
                    } else {
                        self.bits() & Self::A.bits() == Self::A.bits()
                    }
                }
                #[allow(deprecated)]
                #[inline]
                fn B(&self) -> bool {
                    if Self::B.bits() == 0 && self.bits() != 0 {
                        false
                    } else {
                        self.bits() & Self::B.bits() == Self::B.bits()
                    }
                }
                #[allow(deprecated)]
                #[inline]
                fn C(&self) -> bool {
                    if Self::C.bits() == 0 && self.bits() != 0 {
                        false
                    } else {
                        self.bits() & Self::C.bits() == Self::C.bits()
                    }
                }
                #[allow(deprecated)]
                #[inline]
                fn ABC(&self) -> bool {
                    if Self::ABC.bits() == 0 && self.bits() != 0 {
                        false
                    } else {
                        self.bits() & Self::ABC.bits() == Self::ABC.bits()
                    }
                }
            }
            let mut first = true;
            if <Flags as __BitFlags>::A(&Flags(*self)) {
                if !first {
                    f.write_str(" | ")?;
                }
                first = false;
                f.write_str("A")?;
            }
            if <Flags as __BitFlags>::B(&Flags(*self)) {
                if !first {
                    f.write_str(" | ")?;
                }
                first = false;
                f.write_str("B")?;
            }
            if <Flags as __BitFlags>::C(&Flags(*self)) {
                if !first {
                    f.write_str(" | ")?;
                }
                first = false;
                f.write_str("C")?;
            }
            if <Flags as __BitFlags>::ABC(&Flags(*self)) {
                if !first {
                    f.write_str(" | ")?;
                }
                first = false;
                f.write_str("ABC")?;
            }
            let extra_bits = Flags(*self).bits() & !Flags::all().bits();
            if extra_bits != 0 {
                if !first {
                    f.write_str(" | ")?;
                }
                first = false;
                f.write_str("0x")?;
                ::bitflags::_core::fmt::LowerHex::fmt(&extra_bits, f)?;
            }
            if first {
                f.write_str("(empty)")?;
            }
            Ok(())
        }
    }
    impl ::bitflags::_core::fmt::Binary for __Field {
        fn fmt(&self, f: &mut ::bitflags::_core::fmt::Formatter) -> ::bitflags::_core::fmt::Result {
            ::bitflags::_core::fmt::Binary::fmt(&self.0, f)
        }
    }
    impl ::bitflags::_core::fmt::Octal for __Field {
        fn fmt(&self, f: &mut ::bitflags::_core::fmt::Formatter) -> ::bitflags::_core::fmt::Result {
            ::bitflags::_core::fmt::Octal::fmt(&self.0, f)
        }
    }
    impl ::bitflags::_core::fmt::LowerHex for __Field {
        fn fmt(&self, f: &mut ::bitflags::_core::fmt::Formatter) -> ::bitflags::_core::fmt::Result {
            ::bitflags::_core::fmt::LowerHex::fmt(&self.0, f)
        }
    }
    impl ::bitflags::_core::fmt::UpperHex for __Field {
        fn fmt(&self, f: &mut ::bitflags::_core::fmt::Formatter) -> ::bitflags::_core::fmt::Result {
            ::bitflags::_core::fmt::UpperHex::fmt(&self.0, f)
        }
    }
    impl ::bitflags::_core::fmt::Binary for Flags {
        fn fmt(&self, f: &mut ::bitflags::_core::fmt::Formatter) -> ::bitflags::_core::fmt::Result {
            ::bitflags::_core::fmt::Binary::fmt(&self.0, f)
        }
    }
    impl ::bitflags::_core::fmt::Octal for Flags {
        fn fmt(&self, f: &mut ::bitflags::_core::fmt::Formatter) -> ::bitflags::_core::fmt::Result {
            ::bitflags::_core::fmt::Octal::fmt(&self.0, f)
        }
    }
    impl ::bitflags::_core::fmt::LowerHex for Flags {
        fn fmt(&self, f: &mut ::bitflags::_core::fmt::Formatter) -> ::bitflags::_core::fmt::Result {
            ::bitflags::_core::fmt::LowerHex::fmt(&self.0, f)
        }
    }
    impl ::bitflags::_core::fmt::UpperHex for Flags {
        fn fmt(&self, f: &mut ::bitflags::_core::fmt::Formatter) -> ::bitflags::_core::fmt::Result {
            ::bitflags::_core::fmt::UpperHex::fmt(&self.0, f)
        }
    }
    #[allow(dead_code)]
    impl Flags {
        pub const A: Self = Self(__Field(0b00000001));
        #[doc = "bar"]
        pub const B: Self = Self(__Field(0b00000010));
        pub const C: Self = Self(__Field(0b00000100));
        #[doc = "foo"]
        pub const ABC: Self = Self(__Field(Flags::A.bits() | Flags::B.bits() | Flags::C.bits()));
        #[doc = " Returns an empty set of flags."]
        #[inline]
        pub const fn empty() -> Self {
            Self(__Field(0))
        }
        #[doc = " Returns the set containing all flags."]
        #[inline]
        pub const fn all() -> Self {
            #[allow(non_snake_case)]
            trait __BitFlags {
                const A: u32 = 0;
                const B: u32 = 0;
                const C: u32 = 0;
                const ABC: u32 = 0;
            }
            #[allow(non_snake_case)]
            impl __BitFlags for Flags {
                #[allow(deprecated)]
                const A: u32 = Self::A.bits();
                #[allow(deprecated)]
                const B: u32 = Self::B.bits();
                #[allow(deprecated)]
                const C: u32 = Self::C.bits();
                #[allow(deprecated)]
                const ABC: u32 = Self::ABC.bits();
            }
            Self::from_bits_preserve(
                <Self as __BitFlags>::A
                    | <Self as __BitFlags>::B
                    | <Self as __BitFlags>::C
                    | <Self as __BitFlags>::ABC,
            )
        }
        #[doc = " Returns the raw value of the flags currently stored."]
        #[inline]
        pub const fn bits(&self) -> u32 {
            (self.0).0
        }
        #[doc = " Convert from underlying bit representation, unless that"]
        #[doc = " representation contains bits that do not correspond to a flag."]
        #[inline]
        pub const fn from_bits(bits: u32) -> ::bitflags::_core::option::Option<Self> {
            if (bits & !Self::all().bits()) == 0 {
                ::bitflags::_core::option::Option::Some(Self::from_bits_preserve(bits))
            } else {
                ::bitflags::_core::option::Option::None
            }
        }
        #[doc = " Convert from underlying bit representation, dropping any bits"]
        #[doc = " that do not correspond to flags."]
        #[inline]
        pub const fn from_bits_truncate(bits: u32) -> Self {
            Self::from_bits_preserve(bits & Self::all().bits())
        }
        #[doc = " Convert from underlying bit representation, preserving all"]
        #[doc = " bits (even those not corresponding to a defined flag)."]
        #[doc = ""]
        #[doc = " # Safety"]
        #[doc = ""]
        #[doc = " The caller of the `bitflags!` macro can chose to allow or"]
        #[doc = " disallow extra bits for their bitflags type."]
        #[doc = ""]
        #[doc = " The caller of `from_bits_preserve()` has to ensure that"]
        #[doc = " all bits correspond to a defined flag or that extra bits"]
        #[doc = " are valid for this bitflags type."]
        #[inline]
        pub const fn from_bits_preserve(bits: u32) -> Self {
            Self(__Field(bits))
        }
        #[doc = " Returns `true` if no flags are currently stored."]
        #[inline]
        pub const fn is_empty(&self) -> bool {
            self.bits() == Self::empty().bits()
        }
        #[doc = " Returns `true` if all flags are currently set."]
        #[inline]
        pub const fn is_all(&self) -> bool {
            Self::all().bits() | self.bits() == self.bits()
        }
        #[doc = " Returns `true` if there are flags common to both `self` and `other`."]
        #[inline]
        pub const fn intersects(&self, other: Self) -> bool {
            !(Self::from_bits_preserve(self.bits() & other.bits())).is_empty()
        }
        #[doc = " Returns `true` if all of the flags in `other` are contained within `self`."]
        #[inline]
        pub const fn contains(&self, other: Self) -> bool {
            (self.bits() & other.bits()) == other.bits()
        }
        #[doc = " Inserts the specified flags in-place."]
        #[inline]
        pub fn insert(&mut self, other: Self) {
            (self.0).0 |= other.bits();
        }
        #[doc = " Removes the specified flags in-place."]
        #[inline]
        pub fn remove(&mut self, other: Self) {
            (self.0).0 &= !other.bits();
        }
        #[doc = " Toggles the specified flags in-place."]
        #[inline]
        pub fn toggle(&mut self, other: Self) {
            (self.0).0 ^= other.bits();
        }
        #[doc = " Inserts or removes the specified flags depending on the passed value."]
        #[inline]
        pub fn set(&mut self, other: Self, value: bool) {
            if value {
                self.insert(other);
            } else {
                self.remove(other);
            }
        }
        #[doc = " Returns the intersection between the flags in `self` and"]
        #[doc = " `other`."]
        #[doc = ""]
        #[doc = " Specifically, the returned set contains only the flags which are"]
        #[doc = " present in *both* `self` *and* `other`."]
        #[doc = ""]
        #[doc = " This is equivalent to using the `&` operator (e.g."]
        #[doc = " [`ops::BitAnd`]), as in `flags & other`."]
        #[doc = ""]
        #[doc = " [`ops::BitAnd`]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html"]
        #[inline]
        #[must_use]
        pub const fn intersection(self, other: Self) -> Self {
            Self::from_bits_preserve(self.bits() & other.bits())
        }
        #[doc = " Returns the union of between the flags in `self` and `other`."]
        #[doc = ""]
        #[doc = " Specifically, the returned set contains all flags which are"]
        #[doc = " present in *either* `self` *or* `other`, including any which are"]
        #[doc = " present in both (see [`Self::symmetric_difference`] if that"]
        #[doc = " is undesirable)."]
        #[doc = ""]
        #[doc = " This is equivalent to using the `|` operator (e.g."]
        #[doc = " [`ops::BitOr`]), as in `flags | other`."]
        #[doc = ""]
        #[doc = " [`ops::BitOr`]: https://doc.rust-lang.org/std/ops/trait.BitOr.html"]
        #[inline]
        #[must_use]
        pub const fn union(self, other: Self) -> Self {
            Self::from_bits_preserve(self.bits() | other.bits())
        }
        #[doc = " Returns the difference between the flags in `self` and `other`."]
        #[doc = ""]
        #[doc = " Specifically, the returned set contains all flags present in"]
        #[doc = " `self`, except for the ones present in `other`."]
        #[doc = ""]
        #[doc = " It is also conceptually equivalent to the \"bit-clear\" operation:"]
        #[doc = " `flags & !other` (and this syntax is also supported)."]
        #[doc = ""]
        #[doc = " This is equivalent to using the `-` operator (e.g."]
        #[doc = " [`ops::Sub`]), as in `flags - other`."]
        #[doc = ""]
        #[doc = " [`ops::Sub`]: https://doc.rust-lang.org/std/ops/trait.Sub.html"]
        #[inline]
        #[must_use]
        pub const fn difference(self, other: Self) -> Self {
            Self::from_bits_preserve(self.bits() & !other.bits())
        }
        #[doc = " Returns the [symmetric difference][sym-diff] between the flags"]
        #[doc = " in `self` and `other`."]
        #[doc = ""]
        #[doc = " Specifically, the returned set contains the flags present which"]
        #[doc = " are present in `self` or `other`, but that are not present in"]
        #[doc = " both. Equivalently, it contains the flags present in *exactly"]
        #[doc = " one* of the sets `self` and `other`."]
        #[doc = ""]
        #[doc = " This is equivalent to using the `^` operator (e.g."]
        #[doc = " [`ops::BitXor`]), as in `flags ^ other`."]
        #[doc = ""]
        #[doc = " [sym-diff]: https://en.wikipedia.org/wiki/Symmetric_difference"]
        #[doc = " [`ops::BitXor`]: https://doc.rust-lang.org/std/ops/trait.BitXor.html"]
        #[inline]
        #[must_use]
        pub const fn symmetric_difference(self, other: Self) -> Self {
            Self::from_bits_preserve(self.bits() ^ other.bits())
        }
        #[doc = " Returns the complement of this set of flags."]
        #[doc = ""]
        #[doc = " Specifically, the returned set contains all the flags which are"]
        #[doc = " not set in `self`, but which are allowed for this type."]
        #[doc = ""]
        #[doc = " Alternatively, it can be thought of as the set difference"]
        #[doc = " between [`Self::all()`] and `self` (e.g. `Self::all() - self`)"]
        #[doc = ""]
        #[doc = " This is equivalent to using the `!` operator (e.g."]
        #[doc = " [`ops::Not`]), as in `!flags`."]
        #[doc = ""]
        #[doc = " [`Self::all()`]: Self::all"]
        #[doc = " [`ops::Not`]: https://doc.rust-lang.org/std/ops/trait.Not.html"]
        #[inline]
        #[must_use]
        pub const fn complement(self) -> Self {
            Self::from_bits_truncate(!self.bits())
        }
    }
    impl ::bitflags::_core::ops::BitOr for Flags {
        type Output = Self;
        #[doc = " Returns the union of the two sets of flags."]
        #[inline]
        fn bitor(self, other: Flags) -> Self {
            Self::from_bits_preserve(self.bits() | other.bits())
        }
    }
    impl ::bitflags::_core::ops::BitOrAssign for Flags {
        #[doc = " Adds the set of flags."]
        #[inline]
        fn bitor_assign(&mut self, other: Self) {
            (self.0).0 |= other.bits();
        }
    }
    impl ::bitflags::_core::ops::BitXor for Flags {
        type Output = Self;
        #[doc = " Returns the left flags, but with all the right flags toggled."]
        #[inline]
        fn bitxor(self, other: Self) -> Self {
            Self::from_bits_preserve(self.bits() ^ other.bits())
        }
    }
    impl ::bitflags::_core::ops::BitXorAssign for Flags {
        #[doc = " Toggles the set of flags."]
        #[inline]
        fn bitxor_assign(&mut self, other: Self) {
            (self.0).0 ^= other.bits();
        }
    }
    impl ::bitflags::_core::ops::BitAnd for Flags {
        type Output = Self;
        #[doc = " Returns the intersection between the two sets of flags."]
        #[inline]
        fn bitand(self, other: Self) -> Self {
            Self::from_bits_preserve(self.bits() & other.bits())
        }
    }
    impl ::bitflags::_core::ops::BitAndAssign for Flags {
        #[doc = " Disables all flags disabled in the set."]
        #[inline]
        fn bitand_assign(&mut self, other: Self) {
            (self.0).0 &= other.bits();
        }
    }
    impl ::bitflags::_core::ops::Sub for Flags {
        type Output = Self;
        #[doc = " Returns the set difference of the two sets of flags."]
        #[inline]
        fn sub(self, other: Self) -> Self {
            Self::from_bits_preserve(self.bits() & !other.bits())
        }
    }
    impl ::bitflags::_core::ops::SubAssign for Flags {
        #[doc = " Disables all flags enabled in the set."]
        #[inline]
        fn sub_assign(&mut self, other: Self) {
            (self.0).0 &= !other.bits();
        }
    }
    impl ::bitflags::_core::ops::Not for Flags {
        type Output = Self;
        #[doc = " Returns the complement of this set of flags."]
        #[inline]
        fn not(self) -> Self {
            Self::from_bits_preserve(!self.bits()) & Self::all()
        }
    }
    impl ::bitflags::_core::iter::Extend<Flags> for Flags {
        fn extend<T: ::bitflags::_core::iter::IntoIterator<Item = Self>>(&mut self, iterator: T) {
            for item in iterator {
                self.insert(item)
            }
        }
    }
    impl ::bitflags::_core::iter::FromIterator<Flags> for Flags {
        fn from_iter<T: ::bitflags::_core::iter::IntoIterator<Item = Self>>(iterator: T) -> Self {
            let mut result = Self::empty();
            result.extend(iterator);
            result
        }
    }
    impl ::bitflags::BitFlags for Flags {
        type Bits = u32;
        fn empty() -> Self {
            Flags::empty()
        }
        fn all() -> Self {
            Flags::all()
        }
        fn bits(&self) -> u32 {
            Flags::bits(self)
        }
        fn from_bits(bits: u32) -> ::bitflags::_core::option::Option<Flags> {
            Flags::from_bits(bits)
        }
        fn from_bits_truncate(bits: u32) -> Flags {
            Flags::from_bits_truncate(bits)
        }
        fn from_bits_preserve(bits: u32) -> Flags {
            Flags::from_bits_preserve(bits)
        }
        fn is_empty(&self) -> bool {
            Flags::is_empty(self)
        }
        fn is_all(&self) -> bool {
            Flags::is_all(self)
        }
        fn intersects(&self, other: Flags) -> bool {
            Flags::intersects(self, other)
        }
        fn contains(&self, other: Flags) -> bool {
            Flags::contains(self, other)
        }
        fn insert(&mut self, other: Flags) {
            Flags::insert(self, other)
        }
        fn remove(&mut self, other: Flags) {
            Flags::remove(self, other)
        }
        fn toggle(&mut self, other: Flags) {
            Flags::toggle(self, other)
        }
        fn set(&mut self, other: Flags, value: bool) {
            Flags::set(self, other, value)
        }
    }
    impl ::bitflags::__private::ImplementedByBitFlagsMacro for Flags {}
};

Note that this is a breaking change. You now can't access the .bits field directly because there isn't one. You instead need to use .bits(). I've made this a newtype instead of a struct with a single named field so custom derives will treat it like its inner field, instead of like a struct.

@KodrAus
Copy link
Member Author

KodrAus commented Nov 16, 2021

The current stumbling block now is how #[derive(Debug)] works. Previously, we'd implement it as A | B, but now we end up with Flags(A | B), because of the semantics of #[derive(Debug)]. This is a bit unfortunate, but could be livable if we can't think of a better approach.

@KodrAus KodrAus mentioned this pull request Nov 18, 2021
@KodrAus
Copy link
Member Author

KodrAus commented May 4, 2022

I'll close this one since things have moved along to the point that we may as well re-implement it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant