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

Add unstable cfg_if! macro to libcore #59443

Closed
wants to merge 12 commits into from
7 changes: 4 additions & 3 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "core"
version = "0.0.0"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

Expand Down Expand Up @@ -2074,7 +2075,7 @@ name = "rand_chacha"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]

Expand All @@ -2096,7 +2097,7 @@ name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
Expand All @@ -2121,7 +2122,7 @@ name = "rand_xorshift"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
Expand Down
1 change: 1 addition & 0 deletions src/libcore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ path = "../libcore/benches/lib.rs"

[dev-dependencies]
rand = "0.6"
cfg-if = "0.1"

[features]
# Make panics and failed asserts immediately abort without formatting any message
Expand Down
81 changes: 81 additions & 0 deletions src/libcore/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,87 @@ macro_rules! uninitialized_array {
});
}

/// A macro for defining `#[cfg]` if-else statements.
///
/// The macro provided by this crate, `cfg_if`, is similar to the `if/elif` C
/// preprocessor macro by allowing definition of a cascade of `#[cfg]` cases,
/// emitting the implementation which matches first.
///
/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code
/// without having to rewrite each clause multiple times.
///
/// # Example
///
/// ```
/// # #![feature(cfg_if)]
/// cfg_if! {
/// if #[cfg(unix)] {
/// fn foo() { /* unix specific functionality */ }
/// } else if #[cfg(target_pointer_width = "32")] {
/// fn foo() { /* non-unix, 32-bit functionality */ }
/// } else {
/// fn foo() { /* fallback implementation */ }
/// }
/// }
///
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
Copy link
Contributor

Choose a reason for hiding this comment

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

Use $crate:: instead?

#[unstable(feature = "cfg_if", issue = "59442")]
macro_rules! cfg_if {
// match if/else chains with a final `else`
($(
if #[cfg($($meta:meta),*)] { $($it:item)* }
) else * else {
$($it2:item)*
}) => {
cfg_if! {
@__items
() ;
$( ( ($($meta),*) ($($it)*) ), )*
( () ($($it2)*) ),
}
};

// match if/else chains lacking a final `else`
(
if #[cfg($($i_met:meta),*)] { $($i_it:item)* }
$(
else if #[cfg($($e_met:meta),*)] { $($e_it:item)* }
)*
) => {
cfg_if! {
@__items
() ;
( ($($i_met),*) ($($i_it)*) ),
$( ( ($($e_met),*) ($($e_it)*) ), )*
( () () ),
}
};

// Internal and recursive macro to emit all the items
//
// Collects all the negated cfgs in a list at the beginning and after the
// semicolon is all the remaining items
(@__items ($($not:meta,)*) ; ) => {};
(@__items ($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => {
// Emit all items within one block, applying an approprate #[cfg]. The
// #[cfg] will require all `$m` matchers specified and must also negate
// all previous matchers.
cfg_if! { @__apply cfg(all($($m,)* not(any($($not),*)))), $($it)* }

// Recurse to emit all other items in `$rest`, and when we do so add all
// our `$m` matchers to the list of `$not` matchers as future emissions
// will have to negate everything we just matched as well.
cfg_if! { @__items ($($not,)* $($m,)*) ; $($rest)* }
};

// Internal macro to Apply a cfg attribute to a list of items
(@__apply $m:meta, $($it:item)*) => {
$(#[$m] $it)*
};
}

/// Built-in macros to the compiler itself.
///
/// These macros do not have any corresponding definition with a `macro_rules!`
Expand Down
52 changes: 52 additions & 0 deletions src/libcore/tests/cfg_if.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#![feature(cfg_if)]

cfg_if! {
if #[cfg(test)] {
use core::option::Option as Option2;
fn works1() -> Option2<u32> { Some(1) }
} else {
fn works1() -> Option<u32> { None }
}
}

cfg_if! {
if #[cfg(foo)] {
fn works2() -> bool { false }
} else if #[cfg(test)] {
fn works2() -> bool { true }
} else {
fn works2() -> bool { false }
}
}

cfg_if! {
if #[cfg(foo)] {
fn works3() -> bool { false }
} else {
fn works3() -> bool { true }
}
}

cfg_if! {
if #[cfg(test)] {
use core::option::Option as Option3;
fn works4() -> Option3<u32> { Some(1) }
}
}

cfg_if! {
if #[cfg(foo)] {
fn works5() -> bool { false }
} else if #[cfg(test)] {
fn works5() -> bool { true }
}
}

#[test]
fn it_works() {
assert!(works1().is_some());
assert!(works2());
assert!(works3());
assert!(works4().is_some());
assert!(works5());
}
17 changes: 17 additions & 0 deletions src/libcore/tests/cfg_if_override.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! Test that overriding cfg_if with our own cfg_if macro does not break
//! anything.

macro_rules! cfg_if {
(()) => {
mod foo {
fn foo() {}
}
}
}

cfg_if!{}

#[test]
fn it_works() {
foo::foo();
}
55 changes: 55 additions & 0 deletions src/libcore/tests/cfg_if_stable_override.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//! Test that using cfg_if from the cfg_if crate does not break anything.

#[macro_use(cfg_if)]
extern crate cfg_if;

cfg_if! {
if #[cfg(test)] {
use core::option::Option as Option2;
fn works1() -> Option2<u32> { Some(1) }
} else {
fn works1() -> Option<u32> { None }
}
}

cfg_if! {
if #[cfg(foo)] {
fn works2() -> bool { false }
} else if #[cfg(test)] {
fn works2() -> bool { true }
} else {
fn works2() -> bool { false }
}
}

cfg_if! {
if #[cfg(foo)] {
fn works3() -> bool { false }
} else {
fn works3() -> bool { true }
}
}

cfg_if! {
if #[cfg(test)] {
use core::option::Option as Option3;
fn works4() -> Option3<u32> { Some(1) }
}
}

cfg_if! {
if #[cfg(foo)] {
fn works5() -> bool { false }
} else if #[cfg(test)] {
fn works5() -> bool { true }
}
}

#[test]
fn it_works() {
assert!(works1().is_some());
assert!(works2());
assert!(works3());
assert!(works4().is_some());
assert!(works5());
}
5 changes: 5 additions & 0 deletions src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@
#![feature(bind_by_move_pattern_guards)]
#![feature(box_syntax)]
#![feature(c_variadic)]
#![feature(cfg_if)]
gnzlbg marked this conversation as resolved.
Show resolved Hide resolved
#![feature(cfg_target_has_atomic)]
#![feature(cfg_target_thread_local)]
#![feature(char_error_internals)]
Expand Down Expand Up @@ -326,6 +327,10 @@ pub use core::{assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::{unreachable, unimplemented, write, writeln, r#try, todo};

#[unstable(feature = "cfg_if", issue = "59442")]
pub use core::cfg_if;


#[allow(unused_imports)] // macros from `alloc` are not used on all platforms
#[macro_use]
extern crate alloc as alloc_crate;
Expand Down
36 changes: 0 additions & 36 deletions src/libstd/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -943,39 +943,3 @@ mod builtin {
($cond:expr, $($arg:tt)+) => ({ /* compiler built-in */ });
}
}

/// A macro for defining `#[cfg]` if-else statements.
///
/// This is similar to the `if/elif` C preprocessor macro by allowing definition
/// of a cascade of `#[cfg]` cases, emitting the implementation which matches
/// first.
///
/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code
/// without having to rewrite each clause multiple times.
macro_rules! cfg_if {
($(
if #[cfg($($meta:meta),*)] { $($it:item)* }
) else * else {
$($it2:item)*
}) => {
__cfg_if_items! {
() ;
$( ( ($($meta),*) ($($it)*) ), )*
( () ($($it2)*) ),
}
}
}

macro_rules! __cfg_if_items {
(($($not:meta,)*) ; ) => {};
(($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => {
__cfg_if_apply! { cfg(all(not(any($($not),*)), $($m,)*)), $($it)* }
__cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* }
}
}

macro_rules! __cfg_if_apply {
($m:meta, $($it:item)*) => {
$(#[$m] $it)*
}
}
15 changes: 15 additions & 0 deletions src/libstd/tests/cfg_if.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#![feature(cfg_if)]
pub use std::cfg_if;

cfg_if! {
if #[cfg(test)] {
fn foo() -> bool { true }
} else {
fn foo() -> bool { false }
}
}

#[test]
fn cfg_if_test() {
assert!(foo());
}