diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index db7cace49ae8f..ca29181cc52e9 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3551,6 +3551,7 @@ impl Item { pub fn opt_generics(&self) -> Option<&Generics> { match &self.kind { ItemKind::ExternCrate(..) + | ItemKind::ConstBlock(_) | ItemKind::Use(_) | ItemKind::Mod(..) | ItemKind::ForeignMod(_) @@ -3792,6 +3793,15 @@ impl ConstItemRhs { } } +#[derive(Clone, Encodable, Decodable, Debug, Walkable)] +pub struct ConstBlockItem { + pub body: Box, +} + +impl ConstBlockItem { + pub const IDENT: Ident = Ident { name: kw::Underscore, span: DUMMY_SP }; +} + // Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum ItemKind { @@ -3811,6 +3821,11 @@ pub enum ItemKind { /// /// E.g., `const FOO: i32 = 42;`. Const(Box), + /// A module-level const block. + /// Equivalent to `const _: () = const { ... };`. + /// + /// E.g., `const { assert!(true) }`. + ConstBlock(ConstBlockItem), /// A function declaration (`fn`). /// /// E.g., `fn foo(bar: usize) -> usize { .. }`. @@ -3887,6 +3902,8 @@ impl ItemKind { | ItemKind::MacroDef(ident, _) | ItemKind::Delegation(box Delegation { ident, .. }) => Some(ident), + ItemKind::ConstBlock(_) => Some(ConstBlockItem::IDENT), + ItemKind::Use(_) | ItemKind::ForeignMod(_) | ItemKind::GlobalAsm(_) @@ -3900,9 +3917,9 @@ impl ItemKind { pub fn article(&self) -> &'static str { use ItemKind::*; match self { - Use(..) | Static(..) | Const(..) | Fn(..) | Mod(..) | GlobalAsm(..) | TyAlias(..) - | Struct(..) | Union(..) | Trait(..) | TraitAlias(..) | MacroDef(..) - | Delegation(..) | DelegationMac(..) => "a", + Use(..) | Static(..) | Const(..) | ConstBlock(..) | Fn(..) | Mod(..) + | GlobalAsm(..) | TyAlias(..) | Struct(..) | Union(..) | Trait(..) | TraitAlias(..) + | MacroDef(..) | Delegation(..) | DelegationMac(..) => "a", ExternCrate(..) | ForeignMod(..) | MacCall(..) | Enum(..) | Impl { .. } => "an", } } @@ -3913,6 +3930,7 @@ impl ItemKind { ItemKind::Use(..) => "`use` import", ItemKind::Static(..) => "static item", ItemKind::Const(..) => "constant item", + ItemKind::ConstBlock(..) => "const block", ItemKind::Fn(..) => "function", ItemKind::Mod(..) => "module", ItemKind::ForeignMod(..) => "extern block", @@ -3942,7 +3960,18 @@ impl ItemKind { | Self::Trait(box Trait { generics, .. }) | Self::TraitAlias(box TraitAlias { generics, .. }) | Self::Impl(Impl { generics, .. }) => Some(generics), - _ => None, + + Self::ExternCrate(..) + | Self::Use(..) + | Self::Static(..) + | Self::ConstBlock(..) + | Self::Mod(..) + | Self::ForeignMod(..) + | Self::GlobalAsm(..) + | Self::MacCall(..) + | Self::MacroDef(..) + | Self::Delegation(..) + | Self::DelegationMac(..) => None, } } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index dde773fd147da..40c458661320c 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -422,6 +422,7 @@ macro_rules! common_visitor_and_walkers { ByRef, Closure, Const, + ConstBlockItem, ConstItem, ConstItemRhs, Defaultness, @@ -819,6 +820,8 @@ macro_rules! common_visitor_and_walkers { visit_visitable!($($mut)? vis, use_tree), ItemKind::Static(item) => visit_visitable!($($mut)? vis, item), + ItemKind::ConstBlock(item) => + visit_visitable!($($mut)? vis, item), ItemKind::Const(item) => visit_visitable!($($mut)? vis, item), ItemKind::Mod(safety, ident, mod_kind) => diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index f5b7065247a08..00a8fde918abf 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -183,8 +183,13 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_define_opaque(hir_id, define_opaque); hir::ItemKind::Static(*m, ident, ty, body_id) } - ItemKind::Const(box ast::ConstItem { - ident, generics, ty, rhs, define_opaque, .. + ItemKind::Const(box ConstItem { + defaultness: _, + ident, + generics, + ty, + rhs, + define_opaque, }) => { let ident = self.lower_ident(*ident); let (generics, (ty, rhs)) = self.lower_generics( @@ -201,6 +206,15 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_define_opaque(hir_id, &define_opaque); hir::ItemKind::Const(ident, generics, ty, rhs) } + ItemKind::ConstBlock(ConstBlockItem { body }) => hir::ItemKind::Const( + self.lower_ident(ConstBlockItem::IDENT), + hir::Generics::empty(), + self.arena.alloc(self.ty_tup(DUMMY_SP, &[])), + hir::ConstItemRhs::Body({ + let body = self.lower_expr_mut(body); + self.record_body(&[], body) + }), + ), ItemKind::Fn(box Fn { sig: FnSig { decl, header, span: fn_sig_span }, ident, diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 2d87d8c84d7c9..af520bc94a9f0 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -525,6 +525,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { 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_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index c7cbf34dedb9b..2e7f40e7ad7ca 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -205,6 +205,9 @@ impl<'a> State<'a> { define_opaque.as_deref(), ); } + ast::ItemKind::ConstBlock(ast::ConstBlockItem { body }) => { + self.print_expr(body, FixupContext::default()) + } ast::ItemKind::Const(box ast::ConstItem { defaultness, ident, diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index dd770fe5f1a13..5a38c91fb3f2f 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::base::{Annotatable, ExtCtxt}; use rustc_expand::config::StripUnconfigured; use rustc_expand::configure; use rustc_feature::Features; -use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser}; use rustc_session::Session; use rustc_span::{Span, sym}; use smallvec::SmallVec; @@ -113,7 +113,8 @@ impl CfgEval<'_> { let res: PResult<'_, Annotatable> = try { match annotatable { Annotatable::Item(_) => { - let item = parser.parse_item(ForceCollect::Yes)?.unwrap(); + let item = + parser.parse_item(ForceCollect::Yes, AllowConstBlockItems::Yes)?.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 16adaab15c525..92ce870c65e18 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::base::{ }; use rustc_expand::module::DirOwnership; use rustc_parse::lexer::StripTokens; -use rustc_parse::parser::ForceCollect; +use rustc_parse::parser::{AllowConstBlockItems, 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; @@ -173,7 +173,7 @@ pub(crate) fn expand_include<'cx>( )); let mut ret = SmallVec::new(); loop { - match p.parse_item(ForceCollect::No) { + match p.parse_item(ForceCollect::No, AllowConstBlockItems::Yes) { Err(err) => { err.emit(); break; diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 946f17943fe3d..68c7e5491f1ea 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -22,7 +22,7 @@ use rustc_hir::limit::Limit; 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::{AllowConstBlockItems, ForceCollect, Parser}; use rustc_session::Session; use rustc_session::config::CollapseMacroDebuginfo; use rustc_session::parse::ParseSess; @@ -1497,7 +1497,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, AllowConstBlockItems::FIXME) .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 6d048c120a211..e56239748a26f 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -21,8 +21,8 @@ use rustc_hir::Target; use rustc_hir::def::MacroKinds; use rustc_hir::limit::Limit; use rustc_parse::parser::{ - AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma, - token_descr, + AllowConstBlockItems, AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, + RecoverColon, RecoverComma, token_descr, }; use rustc_session::Session; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; @@ -1096,7 +1096,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, AllowConstBlockItems::Yes)? { 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 9bfda8764f552..634495f9f0703 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -156,7 +156,7 @@ impl MultiItemModifier for DeriveProcMacro { let mut items = vec![]; loop { - match parser.parse_item(ForceCollect::No) { + match parser.parse_item(ForceCollect::No, (!is_stmt).into()) { 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 3ef4eb00c3546..e7ddb21b3807d 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -443,6 +443,8 @@ declare_features! ( (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_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index 87953321af3fc..b76c48fbe8ac2 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -171,6 +171,7 @@ impl Target { ast::ItemKind::Use(..) => Target::Use, ast::ItemKind::Static { .. } => Target::Static, ast::ItemKind::Const(..) => Target::Const, + ast::ItemKind::ConstBlock(..) => Target::Const, ast::ItemKind::Fn { .. } => Target::Fn, ast::ItemKind::Mod(..) => Target::Mod, ast::ItemKind::ForeignMod { .. } => Target::ForeignMod, diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 63109c7ba5cbf..46eb40107e3f0 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -9,7 +9,8 @@ use thin_vec::ThinVec; use tracing::debug; use super::{ - AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle, Trailing, UsePreAttrPos, + AllowConstBlockItems, AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle, + Trailing, UsePreAttrPos, }; use crate::parser::FnContext; use crate::{errors, exp, fluent_generated as fluent}; @@ -203,6 +204,7 @@ impl<'a> Parser<'a> { false, FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true }, ForceCollect::No, + AllowConstBlockItems::FIXME, ) { Ok(Some(item)) => { // FIXME(#100717) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 86991f047e46d..798d3dadc604d 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -22,8 +22,8 @@ use tracing::debug; use super::diagnostics::{ConsumeClosingDelim, dummy_arg}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ - AttrWrapper, ExpKeywordPair, ExpTokenPair, FollowedByType, ForceCollect, Parser, PathStyle, - Recovered, Trailing, UsePreAttrPos, + AllowConstBlockItems, AttrWrapper, 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 @@ impl<'a> Parser<'a> { // `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, AllowConstBlockItems::Yes)? else { break; }; items.push(item); @@ -118,21 +118,34 @@ impl<'a> Parser<'a> { } 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, + allow_const_block_items: AllowConstBlockItems, + ) -> 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, allow_const_block_items) + .map(|i| i.map(Box::new)) } fn parse_item_( &mut self, fn_parse_mode: FnParseMode, force_collect: ForceCollect, + const_block_items_allowed: AllowConstBlockItems, ) -> 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( @@ -142,10 +155,11 @@ impl<'a> Parser<'a> { attrs_allowed: bool, fn_parse_mode: FnParseMode, force_collect: ForceCollect, + const_block_items_allowed: AllowConstBlockItems, ) -> 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)); @@ -158,6 +172,7 @@ impl<'a> Parser<'a> { let kind = this.parse_item_kind( &mut attrs, mac_allowed, + const_block_items_allowed, lo, &vis, &mut def, @@ -204,6 +219,7 @@ impl<'a> Parser<'a> { &mut self, attrs: &mut AttrVec, macros_allowed: bool, + const_block_items_allowed: AllowConstBlockItems, lo: Span, vis: &Visibility, def: &mut Defaultness, @@ -253,6 +269,17 @@ impl<'a> Parser<'a> { } else if self.check_impl_frontmatter() { // IMPL ITEM self.parse_item_impl(attrs, def_())? + } else if let AllowConstBlockItems::Yes | AllowConstBlockItems::DoesNotMatter = + 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); + if let AllowConstBlockItems::DoesNotMatter = const_block_items_allowed { + debug!("Parsing a const block item that does not matter: {:?}", self.token.span); + }; + ItemKind::ConstBlock(ConstBlockItem { body: self.parse_expr()? }) } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { // CONST ITEM self.recover_const_mut(const_span); @@ -312,6 +339,7 @@ impl<'a> Parser<'a> { return self.parse_item_kind( attrs, macros_allowed, + const_block_items_allowed, lo, vis, def, @@ -992,8 +1020,13 @@ impl<'a> Parser<'a> { fn_parse_mode: FnParseMode, force_collect: ForceCollect, ) -> PResult<'a, Option>>> { - Ok(self.parse_item_(fn_parse_mode, force_collect)?.map( - |Item { attrs, id, span, vis, kind, tokens }| { + Ok(self + .parse_item_( + fn_parse_mode, + force_collect, + AllowConstBlockItems::DoesNotMatter, // due to `AssocItemKind::try_from` below + )? + .map(|Item { attrs, id, span, vis, kind, tokens }| { let kind = match AssocItemKind::try_from(kind) { Ok(kind) => kind, Err(kind) => match kind { @@ -1020,8 +1053,7 @@ impl<'a> Parser<'a> { }, }; Some(Box::new(Item { attrs, id, span, vis, kind, tokens })) - }, - )) + })) } /// Parses a `type` alias with the following grammar: @@ -1244,8 +1276,13 @@ impl<'a> Parser<'a> { context: FnContext::Free, req_body: false, }; - Ok(self.parse_item_(fn_parse_mode, force_collect)?.map( - |Item { attrs, id, span, vis, kind, tokens }| { + Ok(self + .parse_item_( + fn_parse_mode, + force_collect, + AllowConstBlockItems::DoesNotMatter, // due to `ForeignItemKind::try_from` below + )? + .map(|Item { attrs, id, span, vis, kind, tokens }| { let kind = match ForeignItemKind::try_from(kind) { Ok(kind) => kind, Err(kind) => match kind { @@ -1272,8 +1309,7 @@ impl<'a> Parser<'a> { }, }; Some(Box::new(Item { attrs, id, span, vis, kind, tokens })) - }, - )) + })) } fn error_bad_item_kind(&self, span: Span, kind: &ItemKind, ctx: &'static str) -> Option { @@ -2300,7 +2336,10 @@ impl<'a> Parser<'a> { { 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, + AllowConstBlockItems::DoesNotMatter, // self.token != kw::Const + )?; 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 14a738fb9d247..92caf79478e0f 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -145,6 +145,24 @@ pub enum ForceCollect { No, } +/// Whether to accept `const { ... }` as a shorthand for `const _: () = const { ... }`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum AllowConstBlockItems { + Yes, + No, + DoesNotMatter, + FIXME, // FIXME(const_block_items): get rid of this variant +} + +impl From for AllowConstBlockItems { + fn from(value: bool) -> Self { + match value { + true => Self::Yes, + false => Self::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 ea2b896f5fb9c..706b454835fab 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -6,7 +6,9 @@ use rustc_span::{Ident, kw}; use crate::errors::UnexpectedNonterminal; use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; -use crate::parser::{FollowedByType, ForceCollect, ParseNtResult, Parser, PathStyle}; +use crate::parser::{ + AllowConstBlockItems, FollowedByType, ForceCollect, ParseNtResult, Parser, PathStyle, +}; impl<'a> Parser<'a> { /// Checks whether a non-terminal may begin with a particular token. @@ -118,7 +120,9 @@ impl<'a> Parser<'a> { 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, AllowConstBlockItems::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 26393bf61a32e..3c6629572ae50 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -20,8 +20,8 @@ use super::diagnostics::AttemptLocalParseRecovery; use super::pat::{PatternLocation, RecoverComma}; use super::path::PathStyle; use super::{ - AttrWrapper, BlockMode, FnContext, FnParseMode, ForceCollect, Parser, Restrictions, - SemiColonMode, Trailing, UsePreAttrPos, + AllowConstBlockItems, AttrWrapper, BlockMode, FnContext, FnParseMode, ForceCollect, Parser, + Restrictions, SemiColonMode, Trailing, UsePreAttrPos, }; use crate::errors::{self, MalformedLoopLabel}; use crate::exp; @@ -156,6 +156,7 @@ impl<'a> Parser<'a> { true, FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true }, force_collect, + AllowConstBlockItems::No, )? { self.mk_stmt(lo.to(item.span), StmtKind::Item(Box::new(item))) } else if self.eat(exp!(Semi)) { diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 9b157cb6c7bf9..9819489e9bba7 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -22,7 +22,7 @@ use rustc_span::{ }; use crate::lexer::StripTokens; -use crate::parser::{ForceCollect, Parser}; +use crate::parser::{AllowConstBlockItems, ForceCollect, Parser}; use crate::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; fn psess() -> ParseSess { @@ -2239,7 +2239,7 @@ fn parse_item_from_source_str( psess: &ParseSess, ) -> PResult<'_, Option>> { unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source, StripTokens::Nothing)) - .parse_item(ForceCollect::No) + .parse_item(ForceCollect::No, AllowConstBlockItems::Yes) } // Produces a `rustc_span::span`. @@ -2254,7 +2254,9 @@ fn string_to_expr(source_str: String) -> Box { /// Parses a string, returns an item. fn string_to_item(source_str: String) -> Option> { - with_error_checking_parse(source_str, &psess(), |p| p.parse_item(ForceCollect::No)) + with_error_checking_parse(source_str, &psess(), |p| { + p.parse_item(ForceCollect::No, AllowConstBlockItems::Yes) + }) } #[test] diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 35101624bd68e..23fbb9ab3a2b7 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -572,6 +572,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Use, Static, Const, + ConstBlock, Fn, Mod, ForeignMod, diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 141a60a8ec3f9..b9417af13b113 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -276,7 +276,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { ast::ItemKind::ExternCrate(..) => Target::ExternCrate, ast::ItemKind::Use(_) => Target::Use, ast::ItemKind::Static(_) => Target::Static, - ast::ItemKind::Const(_) => Target::Const, + ast::ItemKind::Const(_) | ast::ItemKind::ConstBlock(_) => Target::Const, ast::ItemKind::Fn(_) | ast::ItemKind::Delegation(..) => Target::Fn, ast::ItemKind::Mod(..) => Target::Mod, ast::ItemKind::ForeignMod(_) => Target::ForeignFn, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index cd12d5ad10cf7..9394ae170583c 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -835,6 +835,15 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { | ItemKind::Static(box StaticItem { ident, .. }) => { self.r.define_local(parent, ident, ValueNS, res, vis, sp, expansion); } + ItemKind::ConstBlock(_) => self.r.define_local( + parent, + ast::ConstBlockItem::IDENT, + ValueNS, + res, + vis, + sp, + expansion, + ), ItemKind::Fn(box Fn { ident, .. }) => { self.r.define_local(parent, ident, ValueNS, res, vis, sp, expansion); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index ea64a1e6c64dd..a4782b2c52d9b 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -121,7 +121,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { mutability: s.mutability, nested: false, }, - ItemKind::Const(..) => DefKind::Const, + ItemKind::Const(..) | ItemKind::ConstBlock(..) => DefKind::Const, ItemKind::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn, ItemKind::MacroDef(ident, def) => { let edition = i.span.edition(); diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index fe6e5b8e6eb6a..77c20948c1374 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -273,6 +273,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> | ast::ItemKind::Use(..) | ast::ItemKind::Static(..) | ast::ItemKind::Const(..) + | ast::ItemKind::ConstBlock(..) | ast::ItemKind::GlobalAsm(..) | ast::ItemKind::TyAlias(..) | ast::ItemKind::TraitAlias(..) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index df620ddb66529..bec3f51b14099 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -2702,8 +2702,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { debug!("(resolving item) resolving {:?} ({:?})", item.kind.ident(), item.kind); let def_kind = self.r.local_def_kind(item.id); - match item.kind { - ItemKind::TyAlias(box TyAlias { ref generics, .. }) => { + match &item.kind { + ItemKind::TyAlias(box TyAlias { generics, .. }) => { self.with_generic_param_rib( &generics.params, RibKind::Item(HasGenericParams::Yes(generics.span), def_kind), @@ -2714,7 +2714,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ); } - ItemKind::Fn(box Fn { ref generics, ref define_opaque, .. }) => { + ItemKind::Fn(box Fn { generics, define_opaque, .. }) => { self.with_generic_param_rib( &generics.params, RibKind::Item(HasGenericParams::Yes(generics.span), def_kind), @@ -2726,19 +2726,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.resolve_define_opaques(define_opaque); } - ItemKind::Enum(_, ref generics, _) - | ItemKind::Struct(_, ref generics, _) - | ItemKind::Union(_, ref generics, _) => { + ItemKind::Enum(_, generics, _) + | ItemKind::Struct(_, generics, _) + | ItemKind::Union(_, generics, _) => { self.resolve_adt(item, generics); } - ItemKind::Impl(Impl { - ref generics, - ref of_trait, - ref self_ty, - items: ref impl_items, - .. - }) => { + ItemKind::Impl(Impl { generics, of_trait, self_ty, items: impl_items, .. }) => { self.diag_metadata.current_impl_items = Some(impl_items); self.resolve_implementation( &item.attrs, @@ -2751,7 +2745,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.diag_metadata.current_impl_items = None; } - ItemKind::Trait(box Trait { ref generics, ref bounds, ref items, .. }) => { + ItemKind::Trait(box Trait { generics, bounds, items, .. }) => { // Create a new rib for the trait-wide type parameters. self.with_generic_param_rib( &generics.params, @@ -2770,7 +2764,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ); } - ItemKind::TraitAlias(box TraitAlias { ref generics, ref bounds, .. }) => { + ItemKind::TraitAlias(box TraitAlias { generics, bounds, .. }) => { // Create a new rib for the trait-wide type parameters. self.with_generic_param_rib( &generics.params, @@ -2810,13 +2804,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.parent_scope.module = orig_module; } - ItemKind::Static(box ast::StaticItem { - ident, - ref ty, - ref expr, - ref define_opaque, - .. - }) => { + ItemKind::Static(box ast::StaticItem { ident, ty, expr, define_opaque, .. }) => { self.with_static_rib(def_kind, |this| { this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { this.visit_ty(ty); @@ -2824,7 +2812,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { if let Some(expr) = expr { // We already forbid generic params because of the above item rib, // so it doesn't matter whether this is a trivial constant. - this.resolve_static_body(expr, Some((ident, ConstantItemKind::Static))); + this.resolve_static_body(expr, Some((*ident, ConstantItemKind::Static))); } }); self.resolve_define_opaques(define_opaque); @@ -2832,11 +2820,11 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ItemKind::Const(box ast::ConstItem { ident, - ref generics, - ref ty, - ref rhs, - ref define_opaque, - .. + generics, + ty, + rhs, + define_opaque, + defaultness: _, }) => { self.with_generic_param_rib( &generics.params, @@ -2862,15 +2850,32 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { if let Some(rhs) = rhs { this.resolve_const_item_rhs( rhs, - Some((ident, ConstantItemKind::Const)), + Some((*ident, ConstantItemKind::Const)), ); } }, ); self.resolve_define_opaques(define_opaque); } + ItemKind::ConstBlock(ConstBlockItem { body }) => self.with_generic_param_rib( + &[], + RibKind::Item(HasGenericParams::No, def_kind), + item.id, + LifetimeBinderKind::ConstItem, + DUMMY_SP, + |this| { + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + this.with_constant_rib( + IsRepeatExpr::No, + ConstantHasGenerics::Yes, + Some((ConstBlockItem::IDENT, ConstantItemKind::Const)), + |this| this.visit_expr(body), + ); + }) + }, + ), - ItemKind::Use(ref use_tree) => { + ItemKind::Use(use_tree) => { let maybe_exported = match use_tree.kind { UseTreeKind::Simple(_) | UseTreeKind::Glob => MaybeExported::Ok(item.id), UseTreeKind::Nested { .. } => MaybeExported::NestedUse(&item.vis), @@ -2880,7 +2885,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.future_proof_import(use_tree); } - ItemKind::MacroDef(_, ref macro_def) => { + ItemKind::MacroDef(_, macro_def) => { // Maintain macro_rules scopes in the same way as during early resolution // for diagnostics and doc links. if macro_def.macro_rules { @@ -2893,7 +2898,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { visit::walk_item(self, item); } - ItemKind::Delegation(ref delegation) => { + ItemKind::Delegation(delegation) => { let span = delegation.path.segments.last().unwrap().ident.span; self.with_generic_param_rib( &[], @@ -5338,6 +5343,7 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { ItemKind::Mod(..) | ItemKind::Static(..) + | ItemKind::ConstBlock(..) | ItemKind::Use(..) | ItemKind::ExternCrate(..) | ItemKind::MacroDef(..) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f2b13dad1fd90..54e2130e85330 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -715,6 +715,7 @@ symbols! { 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 e9f5024e494d1..04ba887b1a2a2 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -529,7 +529,10 @@ fn parse_source( 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::AllowConstBlockItems::FIXME, + ); let result = match parsed { Ok(Some(ref item)) diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 975f9be44e4d3..0a8039968c6ae 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -2177,7 +2177,7 @@ pub(crate) fn rewrite_assign_rhs_with_comments, R: Rewrite + Spa } else { shape }; - let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; + let rhs: String = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; if contains_comment { let rhs = rhs.trim_start(); combine_strs_with_missing_comments(context, &lhs, rhs, between_span, shape, allow_extend) diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index d1778f324c6aa..dff9704202f4f 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -1999,7 +1999,7 @@ pub(crate) fn rewrite_struct_field( combine_strs_with_missing_comments(context, &attrs_str, field_str, missing_span, shape, false) } -pub(crate) struct StaticParts<'a> { +pub(crate) struct StaticPartsLhs<'a> { prefix: &'a str, safety: ast::Safety, vis: &'a ast::Visibility, @@ -2007,48 +2007,52 @@ pub(crate) struct StaticParts<'a> { generics: Option<&'a ast::Generics>, ty: &'a ast::Ty, mutability: ast::Mutability, - expr_opt: Option<&'a ast::Expr>, defaultness: Option, +} + +pub(crate) struct StaticParts<'a> { + lhs: Option>, + rhs: Option<&'a ast::Expr>, span: Span, } impl<'a> StaticParts<'a> { pub(crate) fn from_item(item: &'a ast::Item) -> Self { - let (defaultness, prefix, safety, ident, ty, mutability, expr_opt, generics) = - match &item.kind { - ast::ItemKind::Static(s) => ( - None, - "static", - s.safety, - s.ident, - &s.ty, - s.mutability, - s.expr.as_deref(), - None, - ), - ast::ItemKind::Const(c) => ( - Some(c.defaultness), - "const", - ast::Safety::Default, - c.ident, - &c.ty, - ast::Mutability::Not, - c.rhs.as_ref().map(|rhs| rhs.expr()), - Some(&c.generics), - ), - _ => unreachable!(), - }; - StaticParts { - prefix, - safety, - vis: &item.vis, - ident, - generics, - ty, - mutability, - expr_opt, - defaultness, - span: item.span, + match &item.kind { + ast::ItemKind::Static(s) => StaticParts { + lhs: Some(StaticPartsLhs { + prefix: "static", + safety: s.safety, + vis: &item.vis, + ident: s.ident, + generics: None, + ty: &s.ty, + mutability: s.mutability, + defaultness: None, + }), + rhs: s.expr.as_deref(), + span: item.span, + }, + ast::ItemKind::Const(c) => StaticParts { + lhs: Some(StaticPartsLhs { + prefix: "const", + safety: ast::Safety::Default, + vis: &item.vis, + ident: c.ident, + generics: Some(&c.generics), + ty: &c.ty, + mutability: ast::Mutability::Not, + defaultness: Some(c.defaultness), + }), + rhs: c.rhs.as_ref().map(|rhs| rhs.expr()), + span: item.span, + }, + ast::ItemKind::ConstBlock(b) => StaticParts { + lhs: None, + rhs: Some(&b.body), + span: item.span, + }, + _ => unreachable!(), } } @@ -2063,15 +2067,17 @@ impl<'a> StaticParts<'a> { _ => unreachable!(), }; StaticParts { - prefix: "const", - safety: ast::Safety::Default, - vis: &ti.vis, - ident, - generics, - ty, - mutability: ast::Mutability::Not, - expr_opt, - defaultness: Some(defaultness), + lhs: Some(StaticPartsLhs { + prefix: "const", + safety: ast::Safety::Default, + vis: &ti.vis, + ident, + generics, + ty, + mutability: ast::Mutability::Not, + defaultness: Some(defaultness), + }), + rhs: expr_opt, span: ti.span, } } @@ -2087,48 +2093,57 @@ impl<'a> StaticParts<'a> { _ => unreachable!(), }; StaticParts { - prefix: "const", - safety: ast::Safety::Default, - vis: &ii.vis, - ident, - generics, - ty, - mutability: ast::Mutability::Not, - expr_opt, - defaultness: Some(defaultness), + lhs: Some(StaticPartsLhs { + prefix: "const", + safety: ast::Safety::Default, + vis: &ii.vis, + ident, + generics, + ty, + mutability: ast::Mutability::Not, + defaultness: Some(defaultness), + }), + rhs: expr_opt, span: ii.span, } } } -fn rewrite_static( +fn rewrite_static_lhs( context: &RewriteContext<'_>, - static_parts: &StaticParts<'_>, + lhs: &StaticPartsLhs<'_>, offset: Indent, -) -> Option { +) -> Option<[String; 2]> { + let StaticPartsLhs { + prefix, + safety, + vis, + ident, + generics, + ty, + mutability, + defaultness, + } = *lhs; + // For now, if this static (or const) has generics, then bail. - if static_parts - .generics - .is_some_and(|g| !g.params.is_empty() || !g.where_clause.is_empty()) - { + if generics.is_some_and(|g| !g.params.is_empty() || !g.where_clause.is_empty()) { return None; } let colon = colon_spaces(context.config); let mut prefix = format!( "{}{}{}{} {}{}{}", - format_visibility(context, static_parts.vis), - static_parts.defaultness.map_or("", format_defaultness), - format_safety(static_parts.safety), - static_parts.prefix, - format_mutability(static_parts.mutability), - rewrite_ident(context, static_parts.ident), + format_visibility(context, vis), + defaultness.map_or("", format_defaultness), + format_safety(safety), + prefix, + format_mutability(mutability), + rewrite_ident(context, ident), colon, ); - // 2 = " =".len() - let ty_shape = - Shape::indented(offset.block_only(), context.config).offset_left(prefix.len() + 2)?; - let ty_str = match static_parts.ty.rewrite(context, ty_shape) { + let ty_shape = Shape::indented(offset.block_only(), context.config) + .offset_left(prefix.len() + const { " =".len() })?; + let ty_str = match ty.rewrite(context, ty_shape) { Some(ty_str) => ty_str, None => { if prefix.ends_with(' ') { @@ -2136,7 +2151,7 @@ fn rewrite_static( } let nested_indent = offset.block_indent(context.config); let nested_shape = Shape::indented(nested_indent, context.config); - let ty_str = static_parts.ty.rewrite(context, nested_shape)?; + let ty_str = ty.rewrite(context, nested_shape)?; format!( "{}{}", nested_indent.to_string_with_newline(context.config), @@ -2144,31 +2159,76 @@ fn rewrite_static( ) } }; + Some([prefix, ty_str]) +} - if let Some(expr) = static_parts.expr_opt { - let comments_lo = context.snippet_provider.span_after(static_parts.span, "="); - let expr_lo = expr.span.lo(); - let comments_span = mk_sp(comments_lo, expr_lo); +fn rewrite_static_rhs( + context: &RewriteContext<'_>, + lhs: String, + rhs: &ast::Expr, + span: Span, + offset: Indent, + end: &'static str, +) -> Option { + let comments_lo = context.snippet_provider.span_after(span, "="); + let expr_lo = rhs.span.lo(); + let comments_span = mk_sp(comments_lo, expr_lo); - let lhs = format!("{prefix}{ty_str} ="); + let remaining_width = context.budget(offset.block_indent + end.len()); + rewrite_assign_rhs_with_comments( + context, + &lhs, + rhs, + Shape::legacy(remaining_width, offset.block_only()), + &RhsAssignKind::Expr(&rhs.kind, rhs.span), + RhsTactics::Default, + comments_span, + true, + ) + .ok() + .map(|res| recover_comment_removed(res, span, context)) + .map(|s| if s.ends_with(end) { s } else { s + end }) +} - // 1 = ; - let remaining_width = context.budget(offset.block_indent + 1); - rewrite_assign_rhs_with_comments( - context, - &lhs, - expr, - Shape::legacy(remaining_width, offset.block_only()), - &RhsAssignKind::Expr(&expr.kind, expr.span), - RhsTactics::Default, - comments_span, - true, - ) - .ok() - .map(|res| recover_comment_removed(res, static_parts.span, context)) - .map(|s| if s.ends_with(';') { s } else { s + ";" }) - } else { - Some(format!("{prefix}{ty_str};")) +fn rewrite_static( + context: &RewriteContext<'_>, + static_parts: &StaticParts<'_>, + offset: Indent, +) -> Option { + match *static_parts { + StaticParts { + lhs: Some(ref lhs), + rhs: None, + span: _, + } => { + let [prefix, ty] = rewrite_static_lhs(context, lhs, offset)?; + Some(format!("{prefix}{ty};")) + } + StaticParts { + lhs: Some(ref lhs), + rhs: Some(rhs), + span, + } => { + let [prefix, ty] = rewrite_static_lhs(context, lhs, offset)?; + let lhs = format!("{prefix}{ty} ="); + rewrite_static_rhs(context, lhs, rhs, span, offset, ";") + } + StaticParts { + lhs: None, + rhs: Some(rhs), + span, + } => rhs + .rewrite_result( + context, + Shape::legacy(context.budget(offset.block_indent), offset.block_only()), + ) + .ok() + .map(|res| recover_comment_removed(res, span, context)), + StaticParts { + lhs: None, + rhs: None, + span: _, + } => unreachable!(), } } diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs index 26bf6c5326f5f..8dfb749f4b9f6 100644 --- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs +++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs @@ -3,7 +3,7 @@ use std::panic::{AssertUnwindSafe, catch_unwind}; use rustc_ast::ast; use rustc_ast::token::TokenKind; use rustc_parse::exp; -use rustc_parse::parser::ForceCollect; +use rustc_parse::parser::{AllowConstBlockItems, ForceCollect}; use rustc_span::symbol::kw; use crate::parse::macros::build_stream_parser; @@ -61,7 +61,7 @@ fn parse_cfg_if_inner<'a>( } while parser.token != TokenKind::CloseBrace && parser.token.kind != TokenKind::Eof { - let item = match parser.parse_item(ForceCollect::No) { + let item = match parser.parse_item(ForceCollect::No, AllowConstBlockItems::FIXME) { Ok(Some(item_ptr)) => *item_ptr, Ok(None) => continue, Err(err) => { diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index c063990dfa0b1..552300b1b8284 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -2,7 +2,7 @@ use rustc_ast::ast; use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_parse::MACRO_ARGUMENTS; -use rustc_parse::parser::{ForceCollect, Parser, Recovery}; +use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser, Recovery}; use rustc_session::parse::ParseSess; use rustc_span::symbol; @@ -67,7 +67,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { parse_macro_arg!( Item, NonterminalKind::Item, - |parser: &mut Parser<'b>| parser.parse_item(ForceCollect::No), + |parser: &mut Parser<'b>| parser.parse_item(ForceCollect::No, AllowConstBlockItems::Yes), |x: Option>| x ); diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs index c521b497a2d99..79dd311fdbd71 100644 --- a/src/tools/rustfmt/src/visitor.rs +++ b/src/tools/rustfmt/src/visitor.rs @@ -530,7 +530,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { self.format_missing_with_indent(source!(self, item.span).lo()); self.format_foreign_mod(foreign_mod, item.span); } - ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => { + ast::ItemKind::Static(..) + | ast::ItemKind::Const(..) + | ast::ItemKind::ConstBlock(..) => { self.visit_static(&StaticParts::from_item(item)); } ast::ItemKind::Fn(ref fn_kind) => { diff --git a/src/tools/rustfmt/tests/source/const-block-items.rs b/src/tools/rustfmt/tests/source/const-block-items.rs new file mode 100644 index 0000000000000..77cfe8b1c55fb --- /dev/null +++ b/src/tools/rustfmt/tests/source/const-block-items.rs @@ -0,0 +1,24 @@ +#![feature(const_block_items)] + + const { + + + assert!(true) + } + + #[cfg(false)] const { assert!(false) } + + + #[cfg(false)] +// foo + const + + { + // bar + assert!(false) + // baz + } + + + #[expect(unused)] +const { let a = 1; assert!(true); } diff --git a/src/tools/rustfmt/tests/target/const-block-items.rs b/src/tools/rustfmt/tests/target/const-block-items.rs new file mode 100644 index 0000000000000..d68ea7d9c7265 --- /dev/null +++ b/src/tools/rustfmt/tests/target/const-block-items.rs @@ -0,0 +1,20 @@ +#![feature(const_block_items)] + +const { assert!(true) } + +#[cfg(false)] +const { assert!(false) } + +#[cfg(false)] +// foo +const { + // bar + assert!(false) + // baz +} + +#[expect(unused)] +const { + let a = 1; + assert!(true); +} diff --git a/tests/ui/consts/const-block-items/assert-fail.rs b/tests/ui/consts/const-block-items/assert-fail.rs new file mode 100644 index 0000000000000..a7e3478666ef8 --- /dev/null +++ b/tests/ui/consts/const-block-items/assert-fail.rs @@ -0,0 +1,10 @@ +//@ check-fail + +#![feature(const_block_items)] + +const { assert!(false) } +//~^ ERROR: evaluation panicked: assertion failed: false [E0080] +const { assert!(2 + 2 == 5) } +//~^ ERROR: evaluation panicked: assertion failed: 2 + 2 == 5 [E0080] + +fn main() {} diff --git a/tests/ui/consts/const-block-items/assert-fail.stderr b/tests/ui/consts/const-block-items/assert-fail.stderr new file mode 100644 index 0000000000000..78bd89478541e --- /dev/null +++ b/tests/ui/consts/const-block-items/assert-fail.stderr @@ -0,0 +1,27 @@ +error[E0080]: evaluation panicked: assertion failed: false + --> $DIR/assert-fail.rs:5:9 + | +LL | const { assert!(false) } + | ^^^^^^^^^^^^^^ evaluation of `_::{constant#0}` failed here + +note: erroneous constant encountered + --> $DIR/assert-fail.rs:5:1 + | +LL | const { assert!(false) } + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0080]: evaluation panicked: assertion failed: 2 + 2 == 5 + --> $DIR/assert-fail.rs:7:9 + | +LL | const { assert!(2 + 2 == 5) } + | ^^^^^^^^^^^^^^^^^^^ evaluation of `_::{constant#0}` failed here + +note: erroneous constant encountered + --> $DIR/assert-fail.rs:7:1 + | +LL | const { assert!(2 + 2 == 5) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-block-items/assert-pass.rs b/tests/ui/consts/const-block-items/assert-pass.rs new file mode 100644 index 0000000000000..c409cc5b91f7d --- /dev/null +++ b/tests/ui/consts/const-block-items/assert-pass.rs @@ -0,0 +1,7 @@ +//@ check-pass +#![feature(const_block_items)] + +const { assert!(true) } +const { assert!(2 + 2 == 4) } + +fn main() {} 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 0000000000000..5523633197bba --- /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 0000000000000..60a724f907120 --- /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`. diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index fa06da5cbfbc6..0367b8c2d023c 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -5,6 +5,7 @@ #![allow(incomplete_features)] #![feature(auto_traits)] #![feature(box_patterns)] +#![feature(const_block_items)] #![feature(const_trait_impl)] #![feature(coroutines)] #![feature(decl_macro)] @@ -369,6 +370,9 @@ fn test_item() { c1!(item, [ pub const S: () = {}; ], "pub const S: () = {};"); c1!(item, [ const S: (); ], "const S: ();"); + // ItemKind::ConstBlock + c1!(item, [ const {} ], "const {}"); + // ItemKind::Fn c1!(item, [ pub default const async unsafe extern "C" fn f() {} ], diff --git a/tests/ui/parser/const-block-items/attrs.rs b/tests/ui/parser/const-block-items/attrs.rs new file mode 100644 index 0000000000000..eb7a0554ea9c6 --- /dev/null +++ b/tests/ui/parser/const-block-items/attrs.rs @@ -0,0 +1,14 @@ +//@ check-pass + +#![feature(const_block_items)] + +#[cfg(false)] +const { assert!(false) } + +#[expect(unused)] +const { + let a = 1; + assert!(true); +} + +fn main() {} diff --git a/tests/ui/parser/const-block-items/macro-item.rs b/tests/ui/parser/const-block-items/macro-item.rs new file mode 100644 index 0000000000000..0c07bfc203d25 --- /dev/null +++ b/tests/ui/parser/const-block-items/macro-item.rs @@ -0,0 +1,12 @@ +//@ check-pass +#![feature(const_block_items)] + +macro_rules! foo { + ($item:item) => { + $item + }; +} + +foo!(const {}); + +fn main() {} diff --git a/tests/ui/parser/const-block-items/macro-stmt.rs b/tests/ui/parser/const-block-items/macro-stmt.rs new file mode 100644 index 0000000000000..38632d2eec337 --- /dev/null +++ b/tests/ui/parser/const-block-items/macro-stmt.rs @@ -0,0 +1,14 @@ +//@ check-fail + +#![feature(const_block_items)] + +macro_rules! foo { + ($item:item) => { + $item + //~^ ERROR: expected expression, found `` + }; +} + +fn main() { + foo!(const {}); +} diff --git a/tests/ui/parser/const-block-items/macro-stmt.stderr b/tests/ui/parser/const-block-items/macro-stmt.stderr new file mode 100644 index 0000000000000..dce90e5daa4ca --- /dev/null +++ b/tests/ui/parser/const-block-items/macro-stmt.stderr @@ -0,0 +1,13 @@ +error: expected expression, found `` + --> $DIR/macro-stmt.rs:7:9 + | +LL | $item + | ^^^^^ expected expression +... +LL | foo!(const {}); + | -------------- in this macro invocation + | + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/const-block-items/mod-in-fn.rs b/tests/ui/parser/const-block-items/mod-in-fn.rs new file mode 100644 index 0000000000000..ec98f95a84485 --- /dev/null +++ b/tests/ui/parser/const-block-items/mod-in-fn.rs @@ -0,0 +1,9 @@ +//@ check-pass + +#![feature(const_block_items)] + +fn main() { + mod foo { + const { assert!(true) } + } +}