diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs new file mode 100644 index 000000000000..805f9d0634c0 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -0,0 +1,29 @@ +use crate::util::check_builtin_macro_attribute; + +use rustc_ast::{self as ast, AstLike}; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_expand::config::StripUnconfigured; +use rustc_span::symbol::sym; +use rustc_span::Span; + +pub fn expand( + ecx: &mut ExtCtxt<'_>, + _span: Span, + meta_item: &ast::MetaItem, + item: Annotatable, +) -> Vec { + check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval); + + let mut visitor = + StripUnconfigured { sess: ecx.sess, features: ecx.ecfg.features, modified: false }; + let mut item = visitor.fully_configure(item); + if visitor.modified { + // Erase the tokens if cfg-stripping modified the item + // This will cause us to synthesize fake tokens + // when `nt_to_tokenstream` is called on this item. + if let Some(tokens) = item.tokens_mut() { + *tokens = None; + } + } + vec![item] +} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 9a3c914337ca..1017b23e5675 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -24,6 +24,7 @@ mod asm; mod assert; mod cfg; mod cfg_accessible; +mod cfg_eval; mod compile_error; mod concat; mod concat_idents; @@ -89,6 +90,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { register_attr! { bench: test::expand_bench, cfg_accessible: cfg_accessible::Expander, + cfg_eval: cfg_eval::expand, derive: derive::Expander, global_allocator: global_allocator::expand, test: test::expand_test, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9663760cba18..507eb1e1cbe8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -344,6 +344,7 @@ symbols! { cfg_attr, cfg_attr_multi, cfg_doctest, + cfg_eval, cfg_panic, cfg_sanitize, cfg_target_feature, diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 3e70ba81d499..28fed9b8a14c 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1452,6 +1452,18 @@ pub(crate) mod builtin { /* compiler built-in */ } + /// Expands all `#[cfg]` and `#[cfg_attr]` attributes in the code fragment it's applied to. + #[cfg(not(bootstrap))] + #[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" + )] + #[rustc_builtin_macro] + pub macro cfg_eval($($tt:tt)*) { + /* compiler built-in */ + } + /// Unstable implementation detail of the `rustc` compiler, do not use. #[rustc_builtin_macro] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index a1fbd8dec750..5e8a8d252a23 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -81,3 +81,12 @@ pub use crate::macros::builtin::derive; )] #[doc(no_inline)] pub use crate::macros::builtin::cfg_accessible; + +#[cfg(not(bootstrap))] +#[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" +)] +#[doc(no_inline)] +pub use crate::macros::builtin::cfg_eval; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 72b86338d2c9..acdf7550fe71 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -234,6 +234,7 @@ #![feature(box_syntax)] #![feature(c_variadic)] #![feature(cfg_accessible)] +#![cfg_attr(not(bootstrap), feature(cfg_eval))] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_thread_local)] #![feature(char_error_internals)] diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index ef9aec54a4ca..7181dc6e710e 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -67,6 +67,15 @@ pub use core::prelude::v1::derive; #[doc(hidden)] pub use core::prelude::v1::cfg_accessible; +#[cfg(not(bootstrap))] +#[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" +)] +#[doc(hidden)] +pub use core::prelude::v1::cfg_eval; + // The file so far is equivalent to src/libcore/prelude/v1.rs, // and below to src/liballoc/prelude.rs. // Those files are duplicated rather than using glob imports diff --git a/src/test/ui/proc-macro/cfg-eval-fail.rs b/src/test/ui/proc-macro/cfg-eval-fail.rs new file mode 100644 index 000000000000..379491f3126b --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval-fail.rs @@ -0,0 +1,9 @@ +#![feature(cfg_eval)] +#![feature(stmt_expr_attributes)] + +fn main() { + let _ = #[cfg_eval] #[cfg(FALSE)] 0; + //~^ ERROR removing an expression is not supported in this position + //~| ERROR removing an expression is not supported in this position + //~| ERROR removing an expression is not supported in this position +} diff --git a/src/test/ui/proc-macro/cfg-eval-fail.stderr b/src/test/ui/proc-macro/cfg-eval-fail.stderr new file mode 100644 index 000000000000..010ac006b0be --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval-fail.stderr @@ -0,0 +1,20 @@ +error: removing an expression is not supported in this position + --> $DIR/cfg-eval-fail.rs:5:25 + | +LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0; + | ^^^^^^^^^^^^^ + +error: removing an expression is not supported in this position + --> $DIR/cfg-eval-fail.rs:5:25 + | +LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0; + | ^^^^^^^^^^^^^ + +error: removing an expression is not supported in this position + --> $DIR/cfg-eval-fail.rs:5:25 + | +LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0; + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/proc-macro/cfg-eval.rs b/src/test/ui/proc-macro/cfg-eval.rs new file mode 100644 index 000000000000..ea397df54526 --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval.rs @@ -0,0 +1,32 @@ +// check-pass +// compile-flags: -Z span-debug +// aux-build:test-macros.rs + +#![feature(cfg_eval)] +#![feature(proc_macro_hygiene)] +#![feature(stmt_expr_attributes)] + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] +extern crate test_macros; + +#[cfg_eval] +#[print_attr] +struct S1 { + #[cfg(FALSE)] + field_false: u8, + #[cfg(all(/*true*/))] + #[cfg_attr(FALSE, unknown_attr)] + #[cfg_attr(all(/*true*/), allow())] + field_true: u8, +} + +#[cfg_eval] +#[cfg(FALSE)] +struct S2 {} + +fn main() { + let _ = #[cfg_eval] #[print_attr](#[cfg(FALSE)] 0, #[cfg(all(/*true*/))] 1); +} diff --git a/src/test/ui/proc-macro/cfg-eval.stdout b/src/test/ui/proc-macro/cfg-eval.stdout new file mode 100644 index 000000000000..b98e8961bfea --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval.stdout @@ -0,0 +1,135 @@ +PRINT-ATTR INPUT (DISPLAY): struct S1 { #[cfg(all())] #[allow()] field_true : u8, } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "struct", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Ident { + ident: "S1", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "cfg", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "all", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + ], + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + ], + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "allow", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + ], + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Ident { + ident: "field_true", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Ident { + ident: "u8", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + ], + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): (#[cfg(all())] 1,) +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "cfg", + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "all", + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + ], + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + ], + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + ], + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, +]