Skip to content

Commit

Permalink
Merge #175
Browse files Browse the repository at this point in the history
175: Const fn r=KodrAus a=arturoc

This adds const functions for every non mutable function in the crate.



Co-authored-by: Arturo Castro <arturo@rin.rs>
  • Loading branch information
bors[bot] and arturoc committed Apr 23, 2019
2 parents 766297e + 907a53d commit 30fbf77
Show file tree
Hide file tree
Showing 3 changed files with 218 additions and 49 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -21,6 +21,7 @@ exclude = [
"appveyor.yml",
"bors.toml"
]
build = "build.rs"

[badges]
travis-ci = { repository = "bitflags/bitflags" }
Expand Down
44 changes: 44 additions & 0 deletions build.rs
@@ -0,0 +1,44 @@
use std::env;
use std::process::Command;
use std::str::{self, FromStr};

fn main(){
let minor = match rustc_minor_version() {
Some(minor) => minor,
None => return,
};

// const fn stabilized in Rust 1.31:
if minor >= 31 {
println!("cargo:rustc-cfg=bitflags_const_fn");
}
}

fn rustc_minor_version() -> Option<u32> {
let rustc = match env::var_os("RUSTC") {
Some(rustc) => rustc,
None => return None,
};

let output = match Command::new(rustc).arg("--version").output() {
Ok(output) => output,
Err(_) => return None,
};

let version = match str::from_utf8(&output.stdout) {
Ok(version) => version,
Err(_) => return None,
};

let mut pieces = version.split('.');
if pieces.next() != Some("rustc 1") {
return None;
}

let next = match pieces.next() {
Some(next) => next,
None => return None,
};

u32::from_str(next).ok()
}
222 changes: 173 additions & 49 deletions src/lib.rs
Expand Up @@ -415,6 +415,46 @@ macro_rules! __bitflags {
};
}

#[macro_export(local_inner_macros)]
#[doc(hidden)]
#[cfg(bitflags_const_fn)]
macro_rules! __fn_bitflags {
(
$(# $attr_args:tt)*
const fn $($item:tt)*
) => {
$(# $attr_args)*
const fn $($item)*
};
(
$(# $attr_args:tt)*
pub const fn $($item:tt)*
) => {
$(# $attr_args)*
pub const fn $($item)*
};
}

#[macro_export(local_inner_macros)]
#[doc(hidden)]
#[cfg(not(bitflags_const_fn))]
macro_rules! __fn_bitflags {
(
$(# $attr_args:tt)*
const fn $($item:tt)*
) => {
$(# $attr_args)*
fn $($item)*
};
(
$(# $attr_args:tt)*
pub const fn $($item:tt)*
) => {
$(# $attr_args)*
pub fn $($item)*
};
}

#[macro_export(local_inner_macros)]
#[doc(hidden)]
macro_rules! __impl_bitflags {
Expand Down Expand Up @@ -507,40 +547,46 @@ macro_rules! __impl_bitflags {
pub const $Flag: $BitFlags = $BitFlags { bits: $value };
)+

/// Returns an empty set of flags.
#[inline]
pub fn empty() -> $BitFlags {
$BitFlags { bits: 0 }
__fn_bitflags! {
/// Returns an empty set of flags
#[inline]
pub const fn empty() -> $BitFlags {
$BitFlags { bits: 0 }
}
}

/// Returns the set containing all flags.
#[inline]
pub fn all() -> $BitFlags {
// See `Debug::fmt` for why this approach is taken.
#[allow(non_snake_case)]
trait __BitFlags {
$(
#[inline]
fn $Flag() -> $T { 0 }
)+
}
impl __BitFlags for $BitFlags {
$(
__impl_bitflags! {
#[allow(deprecated)]
__fn_bitflags! {
/// Returns the set containing all flags.
#[inline]
pub const fn all() -> $BitFlags {
// See `Debug::fmt` for why this approach is taken.
#[allow(non_snake_case)]
trait __BitFlags {
$(
#[inline]
$(? #[$attr $($args)*])*
fn $Flag() -> $T { Self::$Flag.bits }
}
)+
const $Flag: $T = 0;
)+
}
impl __BitFlags for $BitFlags {
$(
__impl_bitflags! {
#[allow(deprecated)]
#[inline]
$(? #[$attr $($args)*])*
const $Flag: $T = Self::$Flag.bits;
}
)+
}
$BitFlags { bits: $(<$BitFlags as __BitFlags>::$Flag)|+ }
}
$BitFlags { bits: $(<$BitFlags as __BitFlags>::$Flag())|+ }
}

/// Returns the raw value of the flags currently stored.
#[inline]
pub fn bits(&self) -> $T {
self.bits
__fn_bitflags! {
/// Returns the raw value of the flags currently stored.
#[inline]
pub const fn bits(&self) -> $T {
self.bits
}
}

/// Convert from underlying bit representation, unless that
Expand All @@ -554,35 +600,45 @@ macro_rules! __impl_bitflags {
}
}

/// Convert from underlying bit representation, dropping any bits
/// that do not correspond to flags.
#[inline]
pub fn from_bits_truncate(bits: $T) -> $BitFlags {
$BitFlags { bits } & $BitFlags::all()
__fn_bitflags! {
/// Convert from underlying bit representation, dropping any bits
/// that do not correspond to flags.
#[inline]
pub const fn from_bits_truncate(bits: $T) -> $BitFlags {
$BitFlags { bits: bits & $BitFlags::all().bits }
}
}

/// Returns `true` if no flags are currently stored.
#[inline]
pub fn is_empty(&self) -> bool {
*self == $BitFlags::empty()
__fn_bitflags! {
/// Returns `true` if no flags are currently stored.
#[inline]
pub const fn is_empty(&self) -> bool {
self.bits() == $BitFlags::empty().bits()
}
}

/// Returns `true` if all flags are currently set.
#[inline]
pub fn is_all(&self) -> bool {
*self == $BitFlags::all()
__fn_bitflags! {
/// Returns `true` if all flags are currently set.
#[inline]
pub const fn is_all(&self) -> bool {
self.bits == $BitFlags::all().bits
}
}

/// Returns `true` if there are flags common to both `self` and `other`.
#[inline]
pub fn intersects(&self, other: $BitFlags) -> bool {
!(*self & other).is_empty()
__fn_bitflags! {
/// Returns `true` if there are flags common to both `self` and `other`.
#[inline]
pub const fn intersects(&self, other: $BitFlags) -> bool {
!$BitFlags{ bits: self.bits & other.bits}.is_empty()
}
}

/// Returns `true` all of the flags in `other` are contained within `self`.
#[inline]
pub fn contains(&self, other: $BitFlags) -> bool {
(*self & other) == other
__fn_bitflags! {
/// Returns `true` all of the flags in `other` are contained within `self`.
#[inline]
pub const fn contains(&self, other: $BitFlags) -> bool {
(self.bits & other.bits) == other.bits
}
}

/// Inserts the specified flags in-place.
Expand Down Expand Up @@ -769,6 +825,61 @@ macro_rules! __impl_bitflags {
$(#[$filtered])*
fn $($item)*
};

// Every attribute that the user writes on a const is applied to the
// corresponding const that we generate, but within the implementation of
// Debug and all() we want to ignore everything but #[cfg] attributes. In
// particular, including a #[deprecated] attribute on those items would fail
// to compile.
// https://github.com/bitflags/bitflags/issues/109
//
// const version
//
// Input:
//
// ? #[cfg(feature = "advanced")]
// ? #[deprecated(note = "Use somthing else.")]
// ? #[doc = r"High quality documentation."]
// const f: i32 { /* ... */ }
//
// Output:
//
// #[cfg(feature = "advanced")]
// const f: i32 { /* ... */ }
(
$(#[$filtered:meta])*
? #[cfg $($cfgargs:tt)*]
$(? #[$rest:ident $($restargs:tt)*])*
const $($item:tt)*
) => {
__impl_bitflags! {
$(#[$filtered])*
#[cfg $($cfgargs)*]
$(? #[$rest $($restargs)*])*
const $($item)*
}
};
(
$(#[$filtered:meta])*
// $next != `cfg`
? #[$next:ident $($nextargs:tt)*]
$(? #[$rest:ident $($restargs:tt)*])*
const $($item:tt)*
) => {
__impl_bitflags! {
$(#[$filtered])*
// $next filtered out
$(? #[$rest $($restargs)*])*
const $($item)*
}
};
(
$(#[$filtered:meta])*
const $($item:tt)*
) => {
$(#[$filtered])*
const $($item)*
};
}

// Same as std::stringify but callable from __impl_bitflags, which needs to use
Expand Down Expand Up @@ -996,6 +1107,19 @@ mod tests {
assert_eq!(m1, e1);
}


#[cfg(bitflags_const_fn)]
#[test]
fn test_const_fn() {
const M1: Flags = Flags::empty();

const M2: Flags = Flags::A;
assert_eq!(M2, Flags::A);

const M3: Flags = Flags::C;
assert_eq!(M3, Flags::C);
}

#[test]
fn test_extend() {
let mut flags;
Expand Down

0 comments on commit 30fbf77

Please sign in to comment.