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

BitFlags trait #220

Merged
merged 8 commits into from Oct 23, 2021
Merged

BitFlags trait #220

merged 8 commits into from Oct 23, 2021

Conversation

arturoc
Copy link

@arturoc arturoc commented Jun 18, 2020

Adds a BitFlags trait that is implemented by every bitflags.

The trait includes documentation so it the current hack to document methods through an example could be removed

Closes #154

Adds a BitFlags trait that is implemented by every bitflags.

The trait includes documentation so it the current hack to document methods through an example could be removed

Closes bitflags#154
@Adam-Gleave
Copy link

Any updates on this? I've been looking for something like this for a project, and the changes look reasonable.

use core as _core;

pub trait BitFlags<T>:
_core::ops::BitOr<Output = Self>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be better for the BitFlags trait not to include these additional supertraits and let consumers add them as additional bounds if they need them.

On the one hand it seems reasonable that if you've got flags you'll want to perform bitwise operations on them, but on the other it's a bit of a grab-bag snapshot of our current functionality that we won't be able to extend (since we can't add new supertraits without breakage).

It might be best to stick with just the current set of methods on the trait which cover:

  • Construction from bits
  • Conversion back into bits
  • Set operations on pairs of flags

That should be enough to support a wide range of fundamental use-cases.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about Clone + Copy? I need those to implement the iterator which is based on a value and removes the flags as it advances through it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the BitFlags type itself to be Copy? Or just the underlying bits? If I understand right, we could just perform shifts and masks on the bits themselves and use BitFlags::from_bits_unchecked to return just that flag.

fn from_bits(bits: T) -> _core::option::Option<Self>;
/// Convert from underlying bit representation, dropping any bits
/// that do not correspond to flags.
unsafe fn from_bits_truncate(bits: T) -> Self;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method doesn't need to be unsafe.

@KodrAus
Copy link
Member

KodrAus commented May 16, 2021

Thanks for your patience on this one everybody! I've had a look through this now and left some thoughts on what direction we might want to take it in 🙇

@@ -0,0 +1,68 @@
use core as _core;

pub trait BitFlags<T>:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably make this an associated type, since an implementation of BitFlags only corresponds to a single underlying integer type.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the lines in this comment are wrong? do you mean perhaps empty and all?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry, I mean making the trait look like:

pub trait BitFlags {
    type Bits;

    ..
}

instead of:

pub trait BitFlags<T> {
    ..
}

@arturoc
Copy link
Author

arturoc commented May 17, 2021

I've implemented all the changes and left out Copy and Clone by now since they are not really needed for this PR, once this is merged I'll look into what's the minimum needed for the iterators PR.

Two of the tests are failing but doesn't seem related to this, the 1.32 test seems to be related to unsupported features in const function because that function is not wrapped with __fn_bitflags! I'll send another PR to fix that. The nightly tests are failing cause the output format has changed so the comparison with the generated private_flags.stderr is not equal anymore

@bradleyharden
Copy link

@arturoc, we ran into a case where this would be useful for the atsamd-hal crate. I'm looking forward to seeing it get merged. Thanks for working on it.

@KodrAus
Copy link
Member

KodrAus commented Aug 3, 2021

Hey @bradleyharden! 👋

Would you be happy to share some more details of your usecase? Would the trait in its current shape without any supertraits be sufficient for you?

@KodrAus
Copy link
Member

KodrAus commented Aug 3, 2021

Looks like we just need a rebase on the current main, which should fix up those build failures.

Thanks again for persisting with this @arturoc

@@ -0,0 +1,46 @@
use core as _core;
Copy link
Contributor

@konsumlamm konsumlamm Aug 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
use core as _core;

This is only necessary for use in macros, so that types get resolved correctly. For the definition of the trait, you can just write Option etc.

@konsumlamm
Copy link
Contributor

We may also want to add the functions added in #244 as trait methods (which would also eliminate the need for the supertraits for ops). I was worried a bit about the names conflicting with the respective const fns (since trait methods can't be const currently, sadly), but there seem to be no problems.

@KodrAus
Copy link
Member

KodrAus commented Aug 4, 2021

I was worried a bit about the names conflicting with the respective const fns (since trait methods can't be const currently, sadly), but there seem to be no problems.

Ah yes, inherent methods are resolved over methods from traits or deref, so we'd be safe adding them to the trait.

We may also want to use some private supertrait to discourage callers from implementing this trait directly (we can't technically prevent it since expansion happens in their crates). That way we could mark the trait methods as const in the future when it's possible without needing a major version bump. Maybe something like:

#[doc(hidden)]
pub mod _private {
    pub trait ProducedByMacro {}
}

pub trait BitFlags: ProducedByMacro { .. }

@konsumlamm
Copy link
Contributor

We may also want to use some private supertrait to discourage callers from implementing this trait directly (we can't technically prevent it since expansion happens in their crates). That way we could mark the trait methods as const in the future when it's possible without needing a major version bump. Maybe something like:

#[doc(hidden)]
pub mod _private {
    pub trait ProducedByMacro {}
}

pub trait BitFlags: ProducedByMacro { .. }

Why would this private supertrait have to be generated by a macro? It could just be defined as a normal trait (as is the case for the BitFlags trait in this PR). That way, we would have a truly sealed trait.

fn bits(&self) -> Self::Bits;
/// Convert from underlying bit representation, unless that
/// representation contains bits that do not correspond to a flag.
fn from_bits(bits: Self::Bits) -> _core::option::Option<Self>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fn from_bits(bits: Self::Bits) -> _core::option::Option<Self>
fn from_bits(bits: Self::Bits) -> Option<Self>

Comment on lines +2 to +3

pub trait BitFlags {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub trait BitFlags {
/// A trait that is automatically implemented for all bitflags.
pub trait BitFlags {

@KodrAus
Copy link
Member

KodrAus commented Aug 4, 2021

Why would this private supertrait have to be generated by a macro? It could just be defined as a normal trait

Ah the pattern would be almost the same as the usual private::Sealed approach. I just pulled the name ProducedByMacro out to suggest to anyone looking at the docs that our requirement on implementing ProducedByMacro is that it came from the bitflags! macro.

The sealed trait (whether it's called ProducedByMacro, Sealed, or anything else) would live in bitflags, but unlike in the usual pattern we'd still need it to be publicly exported so that callers of bitflags! in external crates could still implement it along with BitFlags. We just don't want anyone implementing that sealed trait directly. And if they do they run the risk of breakage when we tweak BitFlags.

@arturoc
Copy link
Author

arturoc commented Aug 23, 2021

jjust found a moment to rebase on current main, every test seems to be passing now without problem

@KodrAus
Copy link
Member

KodrAus commented Oct 23, 2021

Thanks for working on this @arturoc! I'll go ahead and merge this in and send a follow-up that includes a few of the other things we were looking at.

@arturoc
Copy link
Author

arturoc commented Oct 23, 2021

That's great! Btw, probably the generated example that serves as documentation is not necesary anymore since the trait documents everything already

impl $crate::BitFlags for $BitFlags {
type Bits = $T;

fn empty() -> Self {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably want to put inline on most methods here, to enable cross-crate inlining if called via a trait.

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.

Add BitFlags trait implemented by every bit flags
6 participants