Sugar for const _: () =

This commit is contained in:
Pavel Grigorenko
2025-11-10 15:22:13 +03:00
parent 625b63f9e1
commit 8439fda014
16 changed files with 116 additions and 29 deletions
@@ -537,6 +537,7 @@ macro_rules! gate_all {
gate_all!(super_let, "`super let` is experimental");
gate_all!(frontmatter, "frontmatters are experimental");
gate_all!(coroutines, "coroutine syntax is experimental");
gate_all!(const_block_items, "const block items are experimental");
if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {
@@ -9,7 +9,7 @@
use rustc_expand::config::StripUnconfigured;
use rustc_expand::configure;
use rustc_feature::Features;
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::parser::{ConstBlockItemsAllowed, ForceCollect, Parser};
use rustc_session::Session;
use rustc_span::{Span, sym};
use smallvec::SmallVec;
@@ -110,7 +110,8 @@ fn configure_annotatable(mut self, annotatable: Annotatable) -> Annotatable {
let res: PResult<'_, Annotatable> = try {
match annotatable {
Annotatable::Item(_) => {
let item = parser.parse_item(ForceCollect::Yes)?.unwrap();
let item =
parser.parse_item(ForceCollect::Yes, ConstBlockItemsAllowed::No)?.unwrap();
Annotatable::Item(self.flat_map_item(item).pop().unwrap())
}
Annotatable::AssocItem(_, ctxt) => {
@@ -13,7 +13,7 @@
};
use rustc_expand::module::DirOwnership;
use rustc_parse::lexer::StripTokens;
use rustc_parse::parser::ForceCollect;
use rustc_parse::parser::{ConstBlockItemsAllowed, ForceCollect};
use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error};
use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
use rustc_session::parse::ParseSess;
@@ -168,7 +168,7 @@ fn make_expr(self: Box<ExpandInclude<'a>>) -> Option<Box<ast::Expr>> {
));
let mut ret = SmallVec::new();
loop {
match p.parse_item(ForceCollect::No) {
match p.parse_item(ForceCollect::No, ConstBlockItemsAllowed::No) {
Err(err) => {
err.emit();
break;
+2 -2
View File
@@ -22,7 +22,7 @@
use rustc_hir::{Stability, find_attr};
use rustc_lint_defs::RegisteredTools;
use rustc_parse::MACRO_ARGUMENTS;
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::parser::{ConstBlockItemsAllowed, ForceCollect, Parser};
use rustc_session::Session;
use rustc_session::parse::ParseSess;
use rustc_span::def_id::{CrateNum, DefId, LocalDefId};
@@ -1472,7 +1472,7 @@ pub(crate) fn stream_pretty_printing_compatibility_hack(
let mut parser = Parser::new(psess, stream.clone(), None);
// No need to collect tokens for this simple check.
parser
.parse_item(ForceCollect::No)
.parse_item(ForceCollect::No, ConstBlockItemsAllowed::No)
.expect("failed to reparse item")
.expect("an actual item")
}
+3 -3
View File
@@ -25,8 +25,8 @@
use rustc_hir::def::MacroKinds;
use rustc_hir::limit::Limit;
use rustc_parse::parser::{
AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma,
token_descr,
AttemptLocalParseRecovery, CommaRecoveryMode, ConstBlockItemsAllowed, ForceCollect, Parser,
RecoverColon, RecoverComma, token_descr,
};
use rustc_session::Session;
use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS};
@@ -1099,7 +1099,7 @@ pub fn parse_ast_fragment<'a>(
Ok(match kind {
AstFragmentKind::Items => {
let mut items = SmallVec::new();
while let Some(item) = this.parse_item(ForceCollect::No)? {
while let Some(item) = this.parse_item(ForceCollect::No, ConstBlockItemsAllowed::No)? {
items.push(item);
}
AstFragment::Items(items)
+2 -2
View File
@@ -1,7 +1,7 @@
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::ErrorGuaranteed;
use rustc_middle::ty::{self, TyCtxt};
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::parser::{ConstBlockItemsAllowed, ForceCollect, Parser};
use rustc_session::Session;
use rustc_session::config::ProcMacroExecutionStrategy;
use rustc_span::profiling::SpannedEventArgRecorder;
@@ -160,7 +160,7 @@ fn expand(
let mut items = vec![];
loop {
match parser.parse_item(ForceCollect::No) {
match parser.parse_item(ForceCollect::No, ConstBlockItemsAllowed::No) {
Ok(None) => break,
Ok(Some(item)) => {
if is_stmt {
+2
View File
@@ -414,6 +414,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
(unstable, cmse_nonsecure_entry, "1.48.0", Some(75835)),
/// Allows `async {}` expressions in const contexts.
(unstable, const_async_blocks, "1.53.0", Some(85368)),
/// Allows `const { ... }` as a shorthand for `const _: () = const { ... };` for module items.
(unstable, const_block_items, "CURRENT_RUSTC_VERSION", Some(149226)),
/// Allows `const || {}` closures in const contexts.
(incomplete, const_closures, "1.68.0", Some(106003)),
/// Allows using `[const] Destruct` bounds and calling drop impls in const contexts.
+3 -1
View File
@@ -9,7 +9,8 @@
use tracing::debug;
use super::{
AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle, Trailing, UsePreAttrPos,
AttrWrapper, Capturing, ConstBlockItemsAllowed, FnParseMode, ForceCollect, Parser, PathStyle,
Trailing, UsePreAttrPos,
};
use crate::parser::FnContext;
use crate::{errors, exp, fluent_generated as fluent};
@@ -203,6 +204,7 @@ fn annotate_following_item_if_applicable(
false,
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true },
ForceCollect::No,
ConstBlockItemsAllowed::No,
) {
Ok(Some(item)) => {
// FIXME(#100717)
+57 -12
View File
@@ -22,8 +22,8 @@
use super::diagnostics::{ConsumeClosingDelim, dummy_arg};
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{
AttrWrapper, ExpKeywordPair, ExpTokenPair, FollowedByType, ForceCollect, Parser, PathStyle,
Recovered, Trailing, UsePreAttrPos,
AttrWrapper, ConstBlockItemsAllowed, ExpKeywordPair, ExpTokenPair, FollowedByType,
ForceCollect, Parser, PathStyle, Recovered, Trailing, UsePreAttrPos,
};
use crate::errors::{self, FnPointerCannotBeAsync, FnPointerCannotBeConst, MacroExpandsToAdtField};
use crate::{exp, fluent_generated as fluent};
@@ -69,7 +69,7 @@ pub fn parse_mod(
// `parse_item` consumes the appropriate semicolons so any leftover is an error.
loop {
while self.maybe_consume_incorrect_semicolon(items.last().map(|x| &**x)) {} // Eat all bad semicolons
let Some(item) = self.parse_item(ForceCollect::No)? else {
let Some(item) = self.parse_item(ForceCollect::No, ConstBlockItemsAllowed::Yes)? else {
break;
};
items.push(item);
@@ -123,21 +123,34 @@ enum ReuseKind {
}
impl<'a> Parser<'a> {
pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Box<Item>>> {
pub fn parse_item(
&mut self,
force_collect: ForceCollect,
const_block_items_allowed: ConstBlockItemsAllowed,
) -> PResult<'a, Option<Box<Item>>> {
let fn_parse_mode =
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true };
self.parse_item_(fn_parse_mode, force_collect).map(|i| i.map(Box::new))
self.parse_item_(fn_parse_mode, force_collect, const_block_items_allowed)
.map(|i| i.map(Box::new))
}
fn parse_item_(
&mut self,
fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
const_block_items_allowed: ConstBlockItemsAllowed,
) -> PResult<'a, Option<Item>> {
self.recover_vcs_conflict_marker();
let attrs = self.parse_outer_attributes()?;
self.recover_vcs_conflict_marker();
self.parse_item_common(attrs, true, false, fn_parse_mode, force_collect)
self.parse_item_common(
attrs,
true,
false,
fn_parse_mode,
force_collect,
const_block_items_allowed,
)
}
pub(super) fn parse_item_common(
@@ -147,10 +160,11 @@ pub(super) fn parse_item_common(
attrs_allowed: bool,
fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
const_block_items_allowed: ConstBlockItemsAllowed,
) -> PResult<'a, Option<Item>> {
if let Some(item) =
self.eat_metavar_seq(MetaVarKind::Item, |this| this.parse_item(ForceCollect::Yes))
{
if let Some(item) = self.eat_metavar_seq(MetaVarKind::Item, |this| {
this.parse_item(ForceCollect::Yes, const_block_items_allowed)
}) {
let mut item = item.expect("an actual item");
attrs.prepend_to_nt_inner(&mut item.attrs);
return Ok(Some(*item));
@@ -163,6 +177,7 @@ pub(super) fn parse_item_common(
let kind = this.parse_item_kind(
&mut attrs,
mac_allowed,
const_block_items_allowed,
lo,
&vis,
&mut def,
@@ -209,6 +224,7 @@ fn parse_item_kind(
&mut self,
attrs: &mut AttrVec,
macros_allowed: bool,
const_block_items_allowed: ConstBlockItemsAllowed,
lo: Span,
vis: &Visibility,
def: &mut Defaultness,
@@ -257,6 +273,34 @@ fn parse_item_kind(
} else if self.check_impl_frontmatter(0) {
// IMPL ITEM
self.parse_item_impl(attrs, def_(), false)?
} else if let ConstBlockItemsAllowed::Yes = const_block_items_allowed
&& self.token.is_keyword(kw::Const)
&& self.look_ahead(1, |t| *t == token::OpenBrace || t.is_metavar_block())
{
// CONST BLOCK ITEM
self.psess.gated_spans.gate(sym::const_block_items, self.token.span);
let block = self.parse_const_block(DUMMY_SP)?;
ItemKind::Const(Box::new(ConstItem {
defaultness: Defaultness::Final,
ident: Ident { name: kw::Underscore, span: DUMMY_SP },
generics: Generics {
params: thin_vec![],
where_clause: WhereClause {
has_where_token: false,
predicates: thin_vec![],
span: DUMMY_SP,
},
span: DUMMY_SP,
},
ty: Box::new(Ty {
id: DUMMY_NODE_ID,
kind: TyKind::Tup(thin_vec![]),
span: DUMMY_SP,
tokens: None,
}),
rhs: Some(ConstItemRhs::Body(block)),
define_opaque: None,
}))
} else if let Const::Yes(const_span) = self.parse_constness(case) {
// CONST ITEM
self.recover_const_mut(const_span);
@@ -316,6 +360,7 @@ fn parse_item_kind(
return self.parse_item_kind(
attrs,
macros_allowed,
const_block_items_allowed,
lo,
vis,
def,
@@ -1068,7 +1113,7 @@ fn parse_assoc_item(
fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<Box<AssocItem>>>> {
Ok(self.parse_item_(fn_parse_mode, force_collect)?.map(
Ok(self.parse_item_(fn_parse_mode, force_collect, ConstBlockItemsAllowed::No)?.map(
|Item { attrs, id, span, vis, kind, tokens }| {
let kind = match AssocItemKind::try_from(kind) {
Ok(kind) => kind,
@@ -1320,7 +1365,7 @@ pub fn parse_foreign_item(
context: FnContext::Free,
req_body: false,
};
Ok(self.parse_item_(fn_parse_mode, force_collect)?.map(
Ok(self.parse_item_(fn_parse_mode, force_collect, ConstBlockItemsAllowed::No)?.map(
|Item { attrs, id, span, vis, kind, tokens }| {
let kind = match ForeignItemKind::try_from(kind) {
Ok(kind) => kind,
@@ -2394,7 +2439,7 @@ fn recover_nested_adt_item(&mut self, keyword: Symbol) -> PResult<'a, bool> {
{
let kw_token = self.token;
let kw_str = pprust::token_to_string(&kw_token);
let item = self.parse_item(ForceCollect::No)?;
let item = self.parse_item(ForceCollect::No, ConstBlockItemsAllowed::No)?;
let mut item = item.unwrap().span;
if self.token == token::Comma {
item = item.to(self.token.span);
+7
View File
@@ -145,6 +145,13 @@ pub enum ForceCollect {
No,
}
/// Whether to accept `const { ... }` as a shorthand for `const _: () = const { ... }`.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ConstBlockItemsAllowed {
Yes,
No,
}
/// If the next tokens are ill-formed `$ty::` recover them as `<$ty>::`.
#[macro_export]
macro_rules! maybe_recover_from_interpolated_ty_qpath {
@@ -6,7 +6,9 @@
use crate::errors::UnexpectedNonterminal;
use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
use crate::parser::{FollowedByType, ForceCollect, ParseNtResult, Parser, PathStyle};
use crate::parser::{
ConstBlockItemsAllowed, FollowedByType, ForceCollect, ParseNtResult, Parser, PathStyle,
};
impl<'a> Parser<'a> {
/// Checks whether a non-terminal may begin with a particular token.
@@ -118,7 +120,9 @@ pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseN
match kind {
// Note that TT is treated differently to all the others.
NonterminalKind::TT => Ok(ParseNtResult::Tt(self.parse_token_tree())),
NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? {
NonterminalKind::Item => match self
.parse_item(ForceCollect::Yes, ConstBlockItemsAllowed::Yes)?
{
Some(item) => Ok(ParseNtResult::Item(item)),
None => Err(self.dcx().create_err(UnexpectedNonterminal::Item(self.token.span))),
},
+3 -2
View File
@@ -20,8 +20,8 @@
use super::pat::{PatternLocation, RecoverComma};
use super::path::PathStyle;
use super::{
AttrWrapper, BlockMode, FnContext, FnParseMode, ForceCollect, Parser, Restrictions,
SemiColonMode, Trailing, UsePreAttrPos,
AttrWrapper, BlockMode, ConstBlockItemsAllowed, FnContext, FnParseMode, ForceCollect, Parser,
Restrictions, SemiColonMode, Trailing, UsePreAttrPos,
};
use crate::errors::{self, MalformedLoopLabel};
use crate::exp;
@@ -156,6 +156,7 @@ pub fn parse_stmt_without_recovery(
true,
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true },
force_collect,
ConstBlockItemsAllowed::No,
)? {
self.mk_stmt(lo.to(item.span), StmtKind::Item(Box::new(item)))
} else if self.eat(exp!(Semi)) {
+1
View File
@@ -730,6 +730,7 @@
console,
const_allocate,
const_async_blocks,
const_block_items,
const_closures,
const_compare_raw_pointers,
const_constructor,
+4 -1
View File
@@ -539,7 +539,10 @@ fn check_item(item: &ast::Item, info: &mut ParseSourceInfo, crate_name: &Option<
let mut prev_span_hi = 0;
let not_crate_attrs = &[sym::forbid, sym::allow, sym::warn, sym::deny, sym::expect];
let parsed = parser.parse_item(rustc_parse::parser::ForceCollect::No);
let parsed = parser.parse_item(
rustc_parse::parser::ForceCollect::No,
rustc_parse::parser::ConstBlockItemsAllowed::No,
);
let result = match parsed {
Ok(Some(ref item))
@@ -0,0 +1,7 @@
//@ check-fail
const { //~ ERROR: const block items are experimental
assert!(true)
}
fn main() {}
@@ -0,0 +1,13 @@
error[E0658]: const block items are experimental
--> $DIR/feature-gate-const-block-items.rs:3:1
|
LL | const {
| ^^^^^
|
= note: see issue #149226 <https://github.com/rust-lang/rust/issues/149226> for more information
= help: add `#![feature(const_block_items)]` 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 1 previous error
For more information about this error, try `rustc --explain E0658`.