const functions

This adds const functions for every non mutable function in the crate.
This commit is contained in:
Arturo Castro 2019-03-11 17:36:38 +01:00
parent bb53c1999b
commit d038a14537
3 changed files with 218 additions and 49 deletions

View File

@ -21,6 +21,7 @@ exclude = [
"appveyor.yml",
"bors.toml"
]
build = "build.rs"
[badges]
travis-ci = { repository = "bitflags/bitflags" }

44
build.rs Normal file
View File

@ -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()
}

View File

@ -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 {
@ -507,21 +547,24 @@ macro_rules! __impl_bitflags {
pub const $Flag: $BitFlags = $BitFlags { bits: $value };
)+
/// Returns an empty set of flags.
__fn_bitflags! {
/// Returns an empty set of flags
#[inline]
pub fn empty() -> $BitFlags {
pub const fn empty() -> $BitFlags {
$BitFlags { bits: 0 }
}
}
__fn_bitflags! {
/// Returns the set containing all flags.
#[inline]
pub fn all() -> $BitFlags {
pub const fn all() -> $BitFlags {
// See `Debug::fmt` for why this approach is taken.
#[allow(non_snake_case)]
trait __BitFlags {
$(
#[inline]
fn $Flag() -> $T { 0 }
const $Flag: $T = 0;
)+
}
impl __BitFlags for $BitFlags {
@ -530,18 +573,21 @@ macro_rules! __impl_bitflags {
#[allow(deprecated)]
#[inline]
$(? #[$attr $($args)*])*
fn $Flag() -> $T { Self::$Flag.bits }
const $Flag: $T = Self::$Flag.bits;
}
)+
}
$BitFlags { bits: $(<$BitFlags as __BitFlags>::$Flag())|+ }
$BitFlags { bits: $(<$BitFlags as __BitFlags>::$Flag)|+ }
}
}
__fn_bitflags! {
/// Returns the raw value of the flags currently stored.
#[inline]
pub fn bits(&self) -> $T {
pub const fn bits(&self) -> $T {
self.bits
}
}
/// Convert from underlying bit representation, unless that
/// representation contains bits that do not correspond to a flag.
@ -554,35 +600,45 @@ macro_rules! __impl_bitflags {
}
}
__fn_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()
pub const fn from_bits_truncate(bits: $T) -> $BitFlags {
$BitFlags { bits: bits & $BitFlags::all().bits }
}
}
__fn_bitflags! {
/// Returns `true` if no flags are currently stored.
#[inline]
pub fn is_empty(&self) -> bool {
*self == $BitFlags::empty()
pub const fn is_empty(&self) -> bool {
self.bits() == $BitFlags::empty().bits()
}
}
__fn_bitflags! {
/// Returns `true` if all flags are currently set.
#[inline]
pub fn is_all(&self) -> bool {
*self == $BitFlags::all()
pub const fn is_all(&self) -> bool {
self.bits == $BitFlags::all().bits
}
}
__fn_bitflags! {
/// Returns `true` if there are flags common to both `self` and `other`.
#[inline]
pub fn intersects(&self, other: $BitFlags) -> bool {
!(*self & other).is_empty()
pub const fn intersects(&self, other: $BitFlags) -> bool {
!$BitFlags{ bits: self.bits & other.bits}.is_empty()
}
}
__fn_bitflags! {
/// Returns `true` all of the flags in `other` are contained within `self`.
#[inline]
pub fn contains(&self, other: $BitFlags) -> bool {
(*self & other) == other
pub const fn contains(&self, other: $BitFlags) -> bool {
(self.bits & other.bits) == other.bits
}
}
/// Inserts the specified flags in-place.
@ -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
@ -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;