about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2024-10-04 15:42:53 +0200
committerGitHub <noreply@github.com>2024-10-04 15:42:53 +0200
commit2ceeeb159dc5aa13cd090ade1b013c51461949a5 (patch)
treeabfd471990fd393fbd7cc4801180085415a07d6d
parent3002af6cb643138839537f6fd0265162610fdbbe (diff)
parenta3ffa1eae507809628decc6250b28db6ab167b11 (diff)
downloadrust-2ceeeb159dc5aa13cd090ade1b013c51461949a5.tar.gz
rust-2ceeeb159dc5aa13cd090ade1b013c51461949a5.zip
Rollup merge of #131034 - Urgau:cfg-true-false, r=nnethercote
Implement RFC3695 Allow boolean literals as cfg predicates

This PR implements https://github.com/rust-lang/rfcs/pull/3695: allow boolean literals as cfg predicates, i.e. `cfg(true)` and `cfg(false)`.

r? `@nnethercote` *(or anyone with parser knowledge)*
cc `@clubby789`
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs10
-rw-r--r--compiler/rustc_attr/messages.ftl2
-rw-r--r--compiler/rustc_attr/src/builtin.rs57
-rw-r--r--compiler/rustc_attr/src/session_diagnostics.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/cfg.rs9
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs2
-rw-r--r--compiler/rustc_expand/src/config.rs8
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs9
-rw-r--r--compiler/rustc_parse/src/lib.rs4
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs8
-rw-r--r--compiler/rustc_session/src/cstore.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs10
-rw-r--r--src/doc/unstable-book/src/language-features/cfg-boolean-literals.md22
-rw-r--r--src/librustdoc/clean/cfg.rs10
-rw-r--r--src/librustdoc/clean/cfg/tests.rs51
-rw-r--r--src/librustdoc/clean/types.rs5
-rw-r--r--src/librustdoc/visit_ast.rs2
-rw-r--r--tests/rustdoc-ui/cfg-boolean-literal.rs19
-rw-r--r--tests/ui/cfg/raw-true-false.rs19
-rw-r--r--tests/ui/cfg/true-false.rs30
-rw-r--r--tests/ui/feature-gates/feature-gate-cfg-boolean-literals.rs10
-rw-r--r--tests/ui/feature-gates/feature-gate-cfg-boolean-literals.stderr43
-rw-r--r--tests/ui/macros/cfg.rs2
-rw-r--r--tests/ui/macros/cfg.stderr4
26 files changed, 284 insertions, 58 deletions
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 124d0acfa49..338d50cb394 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/messages.ftl b/compiler/rustc_attr/messages.ftl
index 5d9ac23ec49..adabf18ca85 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 28d73fbe9f3..d6a7dbad12d 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};
@@ -36,6 +36,7 @@ pub fn is_builtin_attr(attr: &Attribute) -> bool {
 pub(crate) enum UnsupportedLiteralReason {
     Generic,
     CfgString,
+    CfgBoolean,
     DeprecatedString,
     DeprecatedKvPair,
 }
@@ -533,7 +534,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>,
@@ -604,12 +605,43 @@ pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
 /// 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,
+        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(),
+                reason: UnsupportedLiteralReason::CfgBoolean,
+                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);
@@ -645,7 +677,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,
@@ -663,23 +695,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
@@ -700,7 +728,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_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs
index 959a5a4bba7..626840aa6a3 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/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs
index cf1d5c68ead..940c94b1cfc 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 162d14272a5..3129b9ac203 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<Symbol>,
-    pub cfg: Option<ast::MetaItem>,
+    pub cfg: Option<ast::NestedMetaItem>,
     pub verbatim: bool,
     pub dll_imports: Vec<cstore::DllImport>,
 }
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 32088374277..af56169fd60 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,7 +466,7 @@ 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([single]) => match single.meta_item_or_bool() {
             Some(meta_item) => Some(meta_item),
             None => {
                 sess.dcx().emit_err(InvalidCfg::PredicateLiteral { span: single.span() });
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index c5530097e96..380e36fe405 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(<true/false>)]`.
+    (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_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index 82b907d2501..c7953d50406 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;
+                        };
+                        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_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index f7a8b8780ed..da02f98d0e5 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 \
         <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>";
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index c65cf3f40f6..4aa56cb7624 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 66ad70d8d18..b936a299b09 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<Symbol>,
-    pub cfg: Option<ast::MetaItem>,
+    pub cfg: Option<ast::NestedMetaItem>,
     pub foreign_module: Option<DefId>,
     pub verbatim: Option<bool>,
     pub dll_imports: Vec<DllImport>,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index e8aa129c6cd..1527600e764 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -544,6 +544,7 @@ symbols! {
         cfg_accessible,
         cfg_attr,
         cfg_attr_multi,
+        cfg_boolean_literals,
         cfg_doctest,
         cfg_eval,
         cfg_fmt_debug,
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 2c7ca50f954..e9a2c5b8d8e 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<MetaItem>,
+    pub condition: Option<NestedMetaItem>,
     pub subcommands: Vec<OnUnimplementedDirective>,
     pub message: Option<OnUnimplementedFormatString>,
     pub label: Option<OnUnimplementedFormatString>,
@@ -414,7 +414,7 @@ impl<'tcx> OnUnimplementedDirective {
             let cond = item_iter
                 .next()
                 .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))?
-                .meta_item()
+                .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
@@ -558,8 +558,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/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 00000000000..ad795ff9d9b
--- /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/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index 26739219085..53830016a80 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<Option<Cfg>, 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<Cfg, InvalidCfgError> {
-        Self::parse_without(cfg, &FxHashSet::default()).map(|ret| ret.unwrap())
+    pub(crate) fn parse(cfg: &NestedMetaItem) -> Result<Cfg, InvalidCfgError> {
+        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 0ab655103e2..d4b11451c89 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 a3277e8ca92..ec31a51a81e 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, StableSince};
 use rustc_const_eval::const_eval::is_unstable_const_fn;
@@ -986,7 +987,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
@@ -1042,7 +1043,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 c44e5ecaba8..6defe91d383 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 00000000000..4d4e599bfee
--- /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() {}
diff --git a/tests/ui/cfg/raw-true-false.rs b/tests/ui/cfg/raw-true-false.rs
new file mode 100644
index 00000000000..4cb8bb71c92
--- /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
new file mode 100644
index 00000000000..03d96fbafec
--- /dev/null
+++ b/tests/ui/cfg/true-false.rs
@@ -0,0 +1,30 @@
+//@ run-pass
+
+#![feature(link_cfg)]
+#![feature(cfg_boolean_literals)]
+
+#[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)));
+}
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 00000000000..6784b445049
--- /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 00000000000..64491464f1d
--- /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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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`.
diff --git a/tests/ui/macros/cfg.rs b/tests/ui/macros/cfg.rs
index 2aac50a9d01..50998572274 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 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 2633d5f720d..53326914865 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]: literal in `cfg` predicate value must be a boolean
   --> $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