From 57b9b1f9745a56943cc0aebc4c3ec487945f044e Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 18 Sep 2024 17:44:32 +0200 Subject: [PATCH 1/5] Use `ast::NestedMetaItem` when evaluating cfg predicate --- compiler/rustc_attr/src/builtin.rs | 35 +++++++++++++------ compiler/rustc_builtin_macros/src/cfg.rs | 9 +++-- compiler/rustc_codegen_ssa/src/lib.rs | 2 +- compiler/rustc_expand/src/config.rs | 12 ++++--- compiler/rustc_metadata/src/native_libs.rs | 9 +++-- compiler/rustc_parse/src/lib.rs | 4 +-- compiler/rustc_parse/src/parser/attr.rs | 8 +++-- compiler/rustc_session/src/cstore.rs | 2 +- .../traits/on_unimplemented.rs | 12 +++---- tests/ui/macros/cfg.rs | 2 +- tests/ui/macros/cfg.stderr | 4 +-- 11 files changed, 62 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 762ebc47ca9d0..37f70c218698c 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -523,7 +523,7 @@ pub struct Condition { /// Tests if a cfg-pattern matches the cfg set pub fn cfg_matches( - cfg: &ast::MetaItem, + cfg: &ast::NestedMetaItem, sess: &Session, lint_node_id: NodeId, features: Option<&Features>, @@ -594,12 +594,26 @@ pub fn parse_version(s: Symbol) -> Option { /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to /// evaluate individual items. pub fn eval_condition( - cfg: &ast::MetaItem, + cfg: &ast::NestedMetaItem, sess: &Session, features: Option<&Features>, eval: &mut impl FnMut(Condition) -> bool, ) -> bool { let dcx = sess.dcx(); + + let cfg = match cfg { + ast::NestedMetaItem::MetaItem(meta_item) => meta_item, + _ => { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: cfg.span(), + reason: UnsupportedLiteralReason::Generic, + is_bytestr: false, + start_point_span: sess.source_map().start_point(cfg.span()), + }); + return false; + } + }; + match &cfg.kind { ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => { try_gate_cfg(sym::version, cfg.span, sess, features); @@ -653,23 +667,19 @@ pub fn eval_condition( .iter() // We don't use any() here, because we want to evaluate all cfg condition // as eval_condition can (and does) extra checks - .fold(false, |res, mi| { - res | eval_condition(mi.meta_item().unwrap(), sess, features, eval) - }), + .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)), sym::all => mis .iter() // We don't use all() here, because we want to evaluate all cfg condition // as eval_condition can (and does) extra checks - .fold(true, |res, mi| { - res & eval_condition(mi.meta_item().unwrap(), sess, features, eval) - }), + .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)), sym::not => { let [mi] = mis.as_slice() else { dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); return false; }; - !eval_condition(mi.meta_item().unwrap(), sess, features, eval) + !eval_condition(mi, sess, features, eval) } sym::target => { if let Some(features) = features @@ -690,7 +700,12 @@ pub fn eval_condition( seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name)); } - res & eval_condition(&mi, sess, features, eval) + res & eval_condition( + &ast::NestedMetaItem::MetaItem(mi), + sess, + features, + eval, + ) }) } _ => { diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index cf1d5c68eadd0..940c94b1cfc89 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -6,7 +6,6 @@ use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; -use rustc_parse::parser::attr::AllowLeadingUnsafe; use rustc_span::Span; use {rustc_ast as ast, rustc_attr as attr}; @@ -36,14 +35,18 @@ pub(crate) fn expand_cfg( }) } -fn parse_cfg<'a>(cx: &ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> { +fn parse_cfg<'a>( + cx: &ExtCtxt<'a>, + span: Span, + tts: TokenStream, +) -> PResult<'a, ast::NestedMetaItem> { let mut p = cx.new_parser_from_tts(tts); if p.token == token::Eof { return Err(cx.dcx().create_err(errors::RequiresCfgPattern { span })); } - let cfg = p.parse_meta_item(AllowLeadingUnsafe::No)?; + let cfg = p.parse_meta_item_inner()?; let _ = p.eat(&token::Comma); diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 162d14272a555..3129b9ac20344 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -156,7 +156,7 @@ pub struct NativeLib { pub kind: NativeLibKind, pub name: Symbol, pub filename: Option, - pub cfg: Option, + pub cfg: Option, pub verbatim: bool, pub dll_imports: Vec, } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 3208837427736..8ea4f901368ec 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -5,7 +5,9 @@ use rustc_ast::token::{Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{ AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree, }; -use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, NodeId}; +use rustc_ast::{ + self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, NestedMetaItem, NodeId, +}; use rustc_attr as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_feature::{ @@ -449,7 +451,7 @@ impl<'a> StripUnconfigured<'a> { } } -pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItem> { +pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a NestedMetaItem> { let span = meta_item.span; match meta_item.meta_item_list() { None => { @@ -464,9 +466,9 @@ pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a Meta sess.dcx().emit_err(InvalidCfg::MultiplePredicates { span: l.span() }); None } - Some([single]) => match single.meta_item() { - Some(meta_item) => Some(meta_item), - None => { + Some([single]) => match single.is_meta_item() { + true => Some(single), + false => { sess.dcx().emit_err(InvalidCfg::PredicateLiteral { span: single.span() }); None } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 82b907d2501fe..373b109189e7b 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use std::path::{Path, PathBuf}; -use rustc_ast::{CRATE_NODE_ID, NestedMetaItem}; +use rustc_ast::CRATE_NODE_ID; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; use rustc_middle::query::LocalCrate; @@ -304,7 +304,12 @@ impl<'tcx> Collector<'tcx> { sess.dcx().emit_err(errors::LinkCfgForm { span: item.span() }); continue; }; - let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else { + let [link_cfg] = link_cfg else { + sess.dcx() + .emit_err(errors::LinkCfgSinglePredicate { span: item.span() }); + continue; + }; + if !link_cfg.is_meta_item() { sess.dcx() .emit_err(errors::LinkCfgSinglePredicate { span: item.span() }); continue; diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index f7a8b8780edc4..da02f98d0e5bd 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -18,7 +18,7 @@ use std::path::Path; use rustc_ast as ast; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{AttrItem, Attribute, MetaItem, token}; +use rustc_ast::{AttrItem, Attribute, NestedMetaItem, token}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diag, FatalError, PResult}; @@ -160,7 +160,7 @@ pub fn fake_token_stream_for_crate(psess: &ParseSess, krate: &ast::Crate) -> Tok pub fn parse_cfg_attr( cfg_attr: &Attribute, psess: &ParseSess, -) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> { +) -> Option<(NestedMetaItem, Vec<(AttrItem, Span)>)> { const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]"; const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ "; diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index c65cf3f40f658..4aa56cb7624a6 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -356,8 +356,10 @@ impl<'a> Parser<'a> { } /// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited. - pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> { - let cfg_predicate = self.parse_meta_item(AllowLeadingUnsafe::No)?; + pub fn parse_cfg_attr( + &mut self, + ) -> PResult<'a, (ast::NestedMetaItem, Vec<(ast::AttrItem, Span)>)> { + let cfg_predicate = self.parse_meta_item_inner()?; self.expect(&token::Comma)?; // Presumably, the majority of the time there will only be one attr. @@ -452,7 +454,7 @@ impl<'a> Parser<'a> { /// ```ebnf /// MetaItemInner = UNSUFFIXED_LIT | MetaItem ; /// ``` - fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> { + pub fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> { match self.parse_unsuffixed_meta_item_lit() { Ok(lit) => return Ok(ast::NestedMetaItem::Lit(lit)), Err(err) => err.cancel(), // we provide a better error below diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index 66ad70d8d18be..b936a299b0973 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -72,7 +72,7 @@ pub struct NativeLib { pub name: Symbol, /// If packed_bundled_libs enabled, actual filename of library is stored. pub filename: Option, - pub cfg: Option, + pub cfg: Option, pub foreign_module: Option, pub verbatim: Option, pub dll_imports: Vec, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 2c7ca50f9549c..f2efc5ee47d2b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -1,7 +1,7 @@ use std::iter; use std::path::PathBuf; -use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, MetaItem, NestedMetaItem}; +use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; @@ -282,7 +282,7 @@ pub struct OnUnimplementedFormatString { #[derive(Debug)] pub struct OnUnimplementedDirective { - pub condition: Option, + pub condition: Option, pub subcommands: Vec, pub message: Option, pub label: Option, @@ -413,9 +413,7 @@ impl<'tcx> OnUnimplementedDirective { } else { let cond = item_iter .next() - .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))? - .meta_item() - .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClauseInOnUnimplemented { span }))?; + .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))?; attr::eval_condition(cond, &tcx.sess, Some(tcx.features()), &mut |cfg| { if let Some(value) = cfg.value && let Err(guar) = parse_value(value, cfg.span) @@ -558,8 +556,8 @@ impl<'tcx> OnUnimplementedDirective { IgnoredDiagnosticOption::maybe_emit_warning( tcx, item_def_id, - directive.condition.as_ref().map(|i| i.span), - aggr.condition.as_ref().map(|i| i.span), + directive.condition.as_ref().map(|i| i.span()), + aggr.condition.as_ref().map(|i| i.span()), "condition", ); IgnoredDiagnosticOption::maybe_emit_warning( diff --git a/tests/ui/macros/cfg.rs b/tests/ui/macros/cfg.rs index 2aac50a9d011a..387cc0ba8e25c 100644 --- a/tests/ui/macros/cfg.rs +++ b/tests/ui/macros/cfg.rs @@ -1,6 +1,6 @@ fn main() { cfg!(); //~ ERROR macro requires a cfg-pattern - cfg!(123); //~ ERROR expected identifier + cfg!(123); //~ ERROR unsupported literal cfg!(foo = 123); //~ ERROR literal in `cfg` predicate value must be a string cfg!(foo, bar); //~ ERROR expected 1 cfg-pattern } diff --git a/tests/ui/macros/cfg.stderr b/tests/ui/macros/cfg.stderr index 2633d5f720d70..5c21497dc5dd6 100644 --- a/tests/ui/macros/cfg.stderr +++ b/tests/ui/macros/cfg.stderr @@ -6,11 +6,11 @@ LL | cfg!(); | = note: this error originates in the macro `cfg` (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected identifier, found `123` +error[E0565]: unsupported literal --> $DIR/cfg.rs:3:10 | LL | cfg!(123); - | ^^^ expected identifier + | ^^^ error[E0565]: literal in `cfg` predicate value must be a string --> $DIR/cfg.rs:4:16 From c99f29b29fa4115436c352a921f9963b173b5608 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 19 Sep 2024 10:13:14 +0200 Subject: [PATCH 2/5] Implement boolean lit support in cfg predicates --- compiler/rustc_ast/src/attr/mod.rs | 10 +++++++ compiler/rustc_attr/src/builtin.rs | 3 +- compiler/rustc_expand/src/config.rs | 6 ++-- compiler/rustc_metadata/src/native_libs.rs | 2 +- .../traits/on_unimplemented.rs | 4 ++- tests/ui/cfg/true-false.rs | 29 +++++++++++++++++++ 6 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 tests/ui/cfg/true-false.rs diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 124d0acfa4912..338d50cb39424 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -527,6 +527,16 @@ impl NestedMetaItem { } } + /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem` or if it's + /// `NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(_), .. })`. + pub fn meta_item_or_bool(&self) -> Option<&NestedMetaItem> { + match self { + NestedMetaItem::MetaItem(_item) => Some(self), + NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(_), .. }) => Some(self), + _ => None, + } + } + /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`. pub fn meta_item(&self) -> Option<&MetaItem> { match self { diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 37f70c218698c..930fdeb975d87 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -603,6 +603,7 @@ pub fn eval_condition( let cfg = match cfg { ast::NestedMetaItem::MetaItem(meta_item) => meta_item, + ast::NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => return *b, _ => { dcx.emit_err(session_diagnostics::UnsupportedLiteral { span: cfg.span(), @@ -649,7 +650,7 @@ pub fn eval_condition( } ast::MetaItemKind::List(mis) => { for mi in mis.iter() { - if !mi.is_meta_item() { + if mi.meta_item_or_bool().is_none() { dcx.emit_err(session_diagnostics::UnsupportedLiteral { span: mi.span(), reason: UnsupportedLiteralReason::Generic, diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 8ea4f901368ec..af56169fd60c9 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -466,9 +466,9 @@ pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a Nest sess.dcx().emit_err(InvalidCfg::MultiplePredicates { span: l.span() }); None } - Some([single]) => match single.is_meta_item() { - true => Some(single), - false => { + Some([single]) => match single.meta_item_or_bool() { + Some(meta_item) => Some(meta_item), + None => { sess.dcx().emit_err(InvalidCfg::PredicateLiteral { span: single.span() }); None } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 373b109189e7b..c7953d50406ac 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -309,7 +309,7 @@ impl<'tcx> Collector<'tcx> { .emit_err(errors::LinkCfgSinglePredicate { span: item.span() }); continue; }; - if !link_cfg.is_meta_item() { + let Some(link_cfg) = link_cfg.meta_item_or_bool() else { sess.dcx() .emit_err(errors::LinkCfgSinglePredicate { span: item.span() }); continue; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index f2efc5ee47d2b..e9a2c5b8d8e06 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -413,7 +413,9 @@ impl<'tcx> OnUnimplementedDirective { } else { let cond = item_iter .next() - .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))?; + .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))? + .meta_item_or_bool() + .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClauseInOnUnimplemented { span }))?; attr::eval_condition(cond, &tcx.sess, Some(tcx.features()), &mut |cfg| { if let Some(value) = cfg.value && let Err(guar) = parse_value(value, cfg.span) diff --git a/tests/ui/cfg/true-false.rs b/tests/ui/cfg/true-false.rs new file mode 100644 index 0000000000000..0bd1cf427fafd --- /dev/null +++ b/tests/ui/cfg/true-false.rs @@ -0,0 +1,29 @@ +//@ run-pass + +#![feature(link_cfg)] + +#[cfg(true)] +fn foo() -> bool { + cfg!(true) +} + +#[cfg(false)] +fn foo() -> bool { + cfg!(false) +} + +#[cfg_attr(true, cfg(false))] +fn foo() {} + +#[link(name = "foo", cfg(false))] +extern "C" {} + +fn main() { + assert!(foo()); + assert!(cfg!(true)); + assert!(!cfg!(false)); + assert!(cfg!(not(false))); + assert!(cfg!(all(true))); + assert!(cfg!(any(true))); + assert!(!cfg!(not(true))); +} From 62ef411631efb25134e29da76fcdb7802aa94bd4 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 19 Sep 2024 11:19:35 +0200 Subject: [PATCH 3/5] Feature gate boolean lit support in cfg predicates --- compiler/rustc_attr/src/builtin.rs | 20 ++++++++- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_span/src/symbol.rs | 1 + .../language-features/cfg-boolean-literals.md | 22 ++++++++++ tests/ui/cfg/raw-true-false.rs | 19 ++++++++ tests/ui/cfg/true-false.rs | 1 + .../feature-gate-cfg-boolean-literals.rs | 10 +++++ .../feature-gate-cfg-boolean-literals.stderr | 43 +++++++++++++++++++ 8 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/cfg-boolean-literals.md create mode 100644 tests/ui/cfg/raw-true-false.rs create mode 100644 tests/ui/feature-gates/feature-gate-cfg-boolean-literals.rs create mode 100644 tests/ui/feature-gates/feature-gate-cfg-boolean-literals.stderr diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 930fdeb975d87..a3c6132d484d2 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -18,7 +18,7 @@ use rustc_session::parse::feature_err; use rustc_session::{RustcVersion, Session}; use rustc_span::Span; use rustc_span::hygiene::Transparency; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::symbol::{Symbol, kw, sym}; use crate::fluent_generated; use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; @@ -603,7 +603,23 @@ pub fn eval_condition( let cfg = match cfg { ast::NestedMetaItem::MetaItem(meta_item) => meta_item, - ast::NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => return *b, + ast::NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { + if let Some(features) = features { + // we can't use `try_gate_cfg` as symbols don't differentiate between `r#true` + // and `true`, and we want to keep the former working without feature gate + gate_cfg( + &(( + if *b { kw::True } else { kw::False }, + sym::cfg_boolean_literals, + |features: &Features| features.cfg_boolean_literals, + )), + cfg.span(), + sess, + features, + ); + } + return *b; + } _ => { dcx.emit_err(session_diagnostics::UnsupportedLiteral { span: cfg.span(), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 1ffd35dbf91d1..643a95e6ea1ef 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -371,6 +371,8 @@ declare_features! ( (unstable, async_for_loop, "1.77.0", Some(118898)), /// Allows using C-variadics. (unstable, c_variadic, "1.34.0", Some(44930)), + /// Allows the use of `#[cfg()]`. + (unstable, cfg_boolean_literals, "CURRENT_RUSTC_VERSION", Some(131204)), /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. (unstable, cfg_overflow_checks, "1.71.0", Some(111466)), /// Provides the relocation model information as cfg entry diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8f226b26befda..ea8c5f135abf7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -543,6 +543,7 @@ symbols! { cfg_accessible, cfg_attr, cfg_attr_multi, + cfg_boolean_literals, cfg_doctest, cfg_eval, cfg_fmt_debug, diff --git a/src/doc/unstable-book/src/language-features/cfg-boolean-literals.md b/src/doc/unstable-book/src/language-features/cfg-boolean-literals.md new file mode 100644 index 0000000000000..ad795ff9d9b26 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/cfg-boolean-literals.md @@ -0,0 +1,22 @@ +# `cfg_boolean_literals` + +The tracking issue for this feature is: [#131204] + +[#131204]: https://github.com/rust-lang/rust/issues/131204 + +------------------------ + +The `cfg_boolean_literals` feature makes it possible to use the `true`/`false` +literal as cfg predicate. They always evaluate to true/false respectively. + +## Examples + +```rust +#![feature(cfg_boolean_literals)] + +#[cfg(true)] +const A: i32 = 5; + +#[cfg(all(false))] +const A: i32 = 58 * 89; +``` diff --git a/tests/ui/cfg/raw-true-false.rs b/tests/ui/cfg/raw-true-false.rs new file mode 100644 index 0000000000000..4cb8bb71c924b --- /dev/null +++ b/tests/ui/cfg/raw-true-false.rs @@ -0,0 +1,19 @@ +//@ check-pass +//@ compile-flags: --cfg false --check-cfg=cfg(r#false) + +#![deny(warnings)] + +#[expect(unexpected_cfgs)] +mod a { + #[cfg(r#true)] + pub fn foo() {} +} + +mod b { + #[cfg(r#false)] + pub fn bar() {} +} + +fn main() { + b::bar() +} diff --git a/tests/ui/cfg/true-false.rs b/tests/ui/cfg/true-false.rs index 0bd1cf427fafd..03d96fbafecbe 100644 --- a/tests/ui/cfg/true-false.rs +++ b/tests/ui/cfg/true-false.rs @@ -1,6 +1,7 @@ //@ run-pass #![feature(link_cfg)] +#![feature(cfg_boolean_literals)] #[cfg(true)] fn foo() -> bool { diff --git a/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.rs b/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.rs new file mode 100644 index 0000000000000..6784b4450490b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.rs @@ -0,0 +1,10 @@ +#[cfg(true)] //~ ERROR `cfg(true)` is experimental +fn foo() {} + +#[cfg_attr(true, cfg(false))] //~ ERROR `cfg(true)` is experimental +//~^ ERROR `cfg(false)` is experimental +fn foo() {} + +fn main() { + cfg!(false); //~ ERROR `cfg(false)` is experimental +} diff --git a/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.stderr b/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.stderr new file mode 100644 index 0000000000000..64491464f1d43 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.stderr @@ -0,0 +1,43 @@ +error[E0658]: `cfg(true)` is experimental and subject to change + --> $DIR/feature-gate-cfg-boolean-literals.rs:1:7 + | +LL | #[cfg(true)] + | ^^^^ + | + = note: see issue #131204 for more information + = help: add `#![feature(cfg_boolean_literals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `cfg(true)` is experimental and subject to change + --> $DIR/feature-gate-cfg-boolean-literals.rs:4:12 + | +LL | #[cfg_attr(true, cfg(false))] + | ^^^^ + | + = note: see issue #131204 for more information + = help: add `#![feature(cfg_boolean_literals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `cfg(false)` is experimental and subject to change + --> $DIR/feature-gate-cfg-boolean-literals.rs:4:22 + | +LL | #[cfg_attr(true, cfg(false))] + | ^^^^^ + | + = note: see issue #131204 for more information + = help: add `#![feature(cfg_boolean_literals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `cfg(false)` is experimental and subject to change + --> $DIR/feature-gate-cfg-boolean-literals.rs:9:10 + | +LL | cfg!(false); + | ^^^^^ + | + = note: see issue #131204 for more information + = help: add `#![feature(cfg_boolean_literals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. From 781f1840cd37348ce4d76fff2b04e1ada531864e Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 29 Sep 2024 23:40:06 +0200 Subject: [PATCH 4/5] Adjust rustdoc for literal boolean support --- src/librustdoc/clean/cfg.rs | 10 +++-- src/librustdoc/clean/cfg/tests.rs | 51 ++++++++++++++++--------- src/librustdoc/clean/types.rs | 5 ++- src/librustdoc/visit_ast.rs | 2 +- tests/rustdoc-ui/cfg-boolean-literal.rs | 19 +++++++++ 5 files changed, 64 insertions(+), 23 deletions(-) create mode 100644 tests/rustdoc-ui/cfg-boolean-literal.rs diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 267392190858c..53830016a80bb 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -6,7 +6,7 @@ use std::fmt::{self, Write}; use std::{mem, ops}; -use rustc_ast::{LitKind, MetaItem, MetaItemKind, NestedMetaItem}; +use rustc_ast::{LitKind, MetaItem, MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_data_structures::fx::FxHashSet; use rustc_feature::Features; use rustc_session::parse::ParseSess; @@ -48,6 +48,10 @@ impl Cfg { ) -> Result, InvalidCfgError> { match nested_cfg { NestedMetaItem::MetaItem(ref cfg) => Cfg::parse_without(cfg, exclude), + NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => match *b { + true => Ok(Some(Cfg::True)), + false => Ok(Some(Cfg::False)), + }, NestedMetaItem::Lit(ref lit) => { Err(InvalidCfgError { msg: "unexpected literal", span: lit.span }) } @@ -120,8 +124,8 @@ impl Cfg { /// /// If the content is not properly formatted, it will return an error indicating what and where /// the error is. - pub(crate) fn parse(cfg: &MetaItem) -> Result { - Self::parse_without(cfg, &FxHashSet::default()).map(|ret| ret.unwrap()) + pub(crate) fn parse(cfg: &NestedMetaItem) -> Result { + Self::parse_nested(cfg, &FxHashSet::default()).map(|ret| ret.unwrap()) } /// Checks whether the given configuration can be matched in the current session. diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 0ab655103e293..d4b11451c8941 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -1,4 +1,5 @@ -use rustc_ast::{MetaItemLit, Path, Safety, StrStyle}; +use rustc_ast::ast::LitIntType; +use rustc_ast::{MetaItemLit, NestedMetaItem, Path, Safety, StrStyle}; use rustc_span::symbol::{Ident, kw}; use rustc_span::{DUMMY_SP, create_default_session_globals_then}; use thin_vec::thin_vec; @@ -13,52 +14,52 @@ fn name_value_cfg(name: &str, value: &str) -> Cfg { Cfg::Cfg(Symbol::intern(name), Some(Symbol::intern(value))) } -fn dummy_meta_item_word(name: &str) -> MetaItem { - MetaItem { +fn dummy_lit(symbol: Symbol, kind: LitKind) -> NestedMetaItem { + NestedMetaItem::Lit(MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP }) +} + +fn dummy_meta_item_word(name: &str) -> NestedMetaItem { + NestedMetaItem::MetaItem(MetaItem { unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(name)), kind: MetaItemKind::Word, span: DUMMY_SP, - } + }) } -fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> MetaItem { +fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> NestedMetaItem { let lit = MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP }; - MetaItem { + NestedMetaItem::MetaItem(MetaItem { unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(name)), kind: MetaItemKind::NameValue(lit), span: DUMMY_SP, - } + }) } macro_rules! dummy_meta_item_list { ($name:ident, [$($list:ident),* $(,)?]) => { - MetaItem { + NestedMetaItem::MetaItem(MetaItem { unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(stringify!($name))), kind: MetaItemKind::List(thin_vec![ $( - NestedMetaItem::MetaItem( - dummy_meta_item_word(stringify!($list)), - ), + dummy_meta_item_word(stringify!($list)), )* ]), span: DUMMY_SP, - } + }) }; ($name:ident, [$($list:expr),* $(,)?]) => { - MetaItem { + NestedMetaItem::MetaItem(MetaItem { unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(stringify!($name))), kind: MetaItemKind::List(thin_vec![ - $( - NestedMetaItem::MetaItem($list), - )* + $($list,)* ]), span: DUMMY_SP, - } + }) }; } @@ -251,6 +252,14 @@ fn test_cfg_or() { #[test] fn test_parse_ok() { create_default_session_globals_then(|| { + let r#true = Symbol::intern("true"); + let mi = dummy_lit(r#true, LitKind::Bool(true)); + assert_eq!(Cfg::parse(&mi), Ok(Cfg::True)); + + let r#false = Symbol::intern("false"); + let mi = dummy_lit(r#false, LitKind::Bool(false)); + assert_eq!(Cfg::parse(&mi), Ok(Cfg::False)); + let mi = dummy_meta_item_word("all"); assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all"))); @@ -309,6 +318,14 @@ fn test_parse_err() { let mi = dummy_meta_item_list!(not, [dummy_meta_item_list!(foo, []),]); assert!(Cfg::parse(&mi).is_err()); + + let c = Symbol::intern("e"); + let mi = dummy_lit(c, LitKind::Char('e')); + assert!(Cfg::parse(&mi).is_err()); + + let five = Symbol::intern("5"); + let mi = dummy_lit(five, LitKind::Int(5.into(), LitIntType::Unsuffixed)); + assert!(Cfg::parse(&mi).is_err()); }) } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index b9c5e8e787b29..7e822011a67d0 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -5,6 +5,7 @@ use std::sync::{Arc, OnceLock as OnceCell}; use std::{fmt, iter}; use arrayvec::ArrayVec; +use rustc_ast::NestedMetaItem; use rustc_ast_pretty::pprust; use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel, StableSince}; use rustc_const_eval::const_eval::is_unstable_const_fn; @@ -1016,7 +1017,7 @@ pub(crate) trait AttributesExt { .peekable(); if doc_cfg.peek().is_some() && doc_cfg_active { doc_cfg - .filter_map(|attr| Cfg::parse(attr.meta_item()?).ok()) + .filter_map(|attr| Cfg::parse(&attr).ok()) .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) } else if doc_auto_cfg_active { // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because @@ -1072,7 +1073,7 @@ pub(crate) trait AttributesExt { let mut meta = attr.meta_item().unwrap().clone(); meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature)); - if let Ok(feat_cfg) = Cfg::parse(&meta) { + if let Ok(feat_cfg) = Cfg::parse(&NestedMetaItem::MetaItem(meta)) { cfg &= feat_cfg; } } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 2cf703f57c0b6..2fcb147688dc9 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -164,7 +164,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { .unwrap_or(&[]) .iter() .filter_map(|attr| { - Cfg::parse(attr.meta_item()?) + Cfg::parse(attr) .map_err(|e| self.cx.sess().dcx().span_err(e.span, e.msg)) .ok() }) diff --git a/tests/rustdoc-ui/cfg-boolean-literal.rs b/tests/rustdoc-ui/cfg-boolean-literal.rs new file mode 100644 index 0000000000000..4d4e599bfeef2 --- /dev/null +++ b/tests/rustdoc-ui/cfg-boolean-literal.rs @@ -0,0 +1,19 @@ +//@ check-pass + +#![feature(cfg_boolean_literals)] +#![feature(doc_cfg)] + +#[doc(cfg(false))] +pub fn foo() {} + +#[doc(cfg(true))] +pub fn bar() {} + +#[doc(cfg(any(true)))] +pub fn zoo() {} + +#[doc(cfg(all(true)))] +pub fn toy() {} + +#[doc(cfg(not(true)))] +pub fn nay() {} From a3ffa1eae507809628decc6250b28db6ab167b11 Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 1 Oct 2024 10:25:18 +0200 Subject: [PATCH 5/5] Improve non-boolean literal error in cfg predicate --- compiler/rustc_attr/messages.ftl | 2 ++ compiler/rustc_attr/src/builtin.rs | 3 ++- compiler/rustc_attr/src/session_diagnostics.rs | 1 + tests/ui/macros/cfg.rs | 2 +- tests/ui/macros/cfg.stderr | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_attr/messages.ftl b/compiler/rustc_attr/messages.ftl index 5d9ac23ec490e..adabf18ca85d6 100644 --- a/compiler/rustc_attr/messages.ftl +++ b/compiler/rustc_attr/messages.ftl @@ -107,6 +107,8 @@ attr_unknown_version_literal = attr_unstable_cfg_target_compact = compact `cfg(target(..))` is experimental and subject to change +attr_unsupported_literal_cfg_boolean = + literal in `cfg` predicate value must be a boolean attr_unsupported_literal_cfg_string = literal in `cfg` predicate value must be a string attr_unsupported_literal_deprecated_kv_pair = diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index a3c6132d484d2..af73281c3160a 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -36,6 +36,7 @@ pub fn is_builtin_attr(attr: &Attribute) -> bool { pub(crate) enum UnsupportedLiteralReason { Generic, CfgString, + CfgBoolean, DeprecatedString, DeprecatedKvPair, } @@ -623,7 +624,7 @@ pub fn eval_condition( _ => { dcx.emit_err(session_diagnostics::UnsupportedLiteral { span: cfg.span(), - reason: UnsupportedLiteralReason::Generic, + reason: UnsupportedLiteralReason::CfgBoolean, is_bytestr: false, start_point_span: sess.source_map().start_point(cfg.span()), }); diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs index 959a5a4bba752..626840aa6a3cd 100644 --- a/compiler/rustc_attr/src/session_diagnostics.rs +++ b/compiler/rustc_attr/src/session_diagnostics.rs @@ -206,6 +206,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral { let mut diag = Diag::new(dcx, level, match self.reason { UnsupportedLiteralReason::Generic => fluent::attr_unsupported_literal_generic, UnsupportedLiteralReason::CfgString => fluent::attr_unsupported_literal_cfg_string, + UnsupportedLiteralReason::CfgBoolean => fluent::attr_unsupported_literal_cfg_boolean, UnsupportedLiteralReason::DeprecatedString => { fluent::attr_unsupported_literal_deprecated_string } diff --git a/tests/ui/macros/cfg.rs b/tests/ui/macros/cfg.rs index 387cc0ba8e25c..50998572274ec 100644 --- a/tests/ui/macros/cfg.rs +++ b/tests/ui/macros/cfg.rs @@ -1,6 +1,6 @@ fn main() { cfg!(); //~ ERROR macro requires a cfg-pattern - cfg!(123); //~ ERROR unsupported literal + cfg!(123); //~ ERROR literal in `cfg` predicate value must be a boolean cfg!(foo = 123); //~ ERROR literal in `cfg` predicate value must be a string cfg!(foo, bar); //~ ERROR expected 1 cfg-pattern } diff --git a/tests/ui/macros/cfg.stderr b/tests/ui/macros/cfg.stderr index 5c21497dc5dd6..5332691486562 100644 --- a/tests/ui/macros/cfg.stderr +++ b/tests/ui/macros/cfg.stderr @@ -6,7 +6,7 @@ LL | cfg!(); | = note: this error originates in the macro `cfg` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0565]: unsupported literal +error[E0565]: literal in `cfg` predicate value must be a boolean --> $DIR/cfg.rs:3:10 | LL | cfg!(123);