diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index dd0e27948bec..6376cab364db 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -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) { diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index bca3f0f10ea3..ef5eb4617372 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -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) => { diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 0f31de60a8a4..82398d04855f 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -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>) -> Option> { )); 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; diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index e2d62bdc5453..f62a202c35f9 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -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") } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 20cdffac2d67..578746bed4a3 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -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) diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 5e7a272bcba4..90fd07f84120 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -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 { diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8959bc586af7..a921ecc195ec 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -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. diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 1cb9a1b65d65..4295e761c9fa 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -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) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index c7d09b834540..1612923c6691 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -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>> { + pub fn parse_item( + &mut self, + force_collect: ForceCollect, + const_block_items_allowed: ConstBlockItemsAllowed, + ) -> PResult<'a, Option>> { 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> { 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> { - 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>>> { - 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); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 2b711b2f43ef..d7317d743fba 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -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 { diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index ea2b896f5fb9..e0b6be49942e 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -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))), }, diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 26393bf61a32..2641b4f378d9 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -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)) { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d6d6ebb49e10..ccd7bf45294d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -730,6 +730,7 @@ console, const_allocate, const_async_blocks, + const_block_items, const_closures, const_compare_raw_pointers, const_constructor, diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 569206d6ec88..ca9aaa955875 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -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)) diff --git a/tests/ui/feature-gates/feature-gate-const-block-items.rs b/tests/ui/feature-gates/feature-gate-const-block-items.rs new file mode 100644 index 000000000000..5523633197bb --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-const-block-items.rs @@ -0,0 +1,7 @@ +//@ check-fail + +const { //~ ERROR: const block items are experimental + assert!(true) +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-const-block-items.stderr b/tests/ui/feature-gates/feature-gate-const-block-items.stderr new file mode 100644 index 000000000000..60a724f90712 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-const-block-items.stderr @@ -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 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`.