diff --git a/Cargo.lock b/Cargo.lock index 73adb762e485b..279f1f5e31a1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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)", ] @@ -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)", ] @@ -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]] @@ -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]] diff --git a/src/libcore/Cargo.toml b/src/libcore/Cargo.toml index fa2ab11243b6c..8508fa4aa3e57 100644 --- a/src/libcore/Cargo.toml +++ b/src/libcore/Cargo.toml @@ -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 diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index ad8ce1af1f6a1..9cfa70f91954a 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -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)] +#[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!` diff --git a/src/libcore/tests/cfg_if.rs b/src/libcore/tests/cfg_if.rs new file mode 100644 index 0000000000000..2e9784d8543d0 --- /dev/null +++ b/src/libcore/tests/cfg_if.rs @@ -0,0 +1,52 @@ +#![feature(cfg_if)] + +cfg_if! { + if #[cfg(test)] { + use core::option::Option as Option2; + fn works1() -> Option2 { Some(1) } + } else { + fn works1() -> Option { 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 { 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()); +} diff --git a/src/libcore/tests/cfg_if_override.rs b/src/libcore/tests/cfg_if_override.rs new file mode 100644 index 0000000000000..16471ec91b1ec --- /dev/null +++ b/src/libcore/tests/cfg_if_override.rs @@ -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(); +} diff --git a/src/libcore/tests/cfg_if_stable_override.rs b/src/libcore/tests/cfg_if_stable_override.rs new file mode 100644 index 0000000000000..2718b9cab9fa1 --- /dev/null +++ b/src/libcore/tests/cfg_if_stable_override.rs @@ -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 { Some(1) } + } else { + fn works1() -> Option { 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 { 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()); +} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 296c4c887274e..dce40160fadac 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -241,6 +241,7 @@ #![feature(bind_by_move_pattern_guards)] #![feature(box_syntax)] #![feature(c_variadic)] +#![feature(cfg_if)] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_thread_local)] #![feature(char_error_internals)] @@ -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; diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index d5dc5f7c4f0df..e00e234997548 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -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)* - } -} diff --git a/src/libstd/tests/cfg_if.rs b/src/libstd/tests/cfg_if.rs new file mode 100644 index 0000000000000..3ecfadba8d3c9 --- /dev/null +++ b/src/libstd/tests/cfg_if.rs @@ -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()); +}