Rollup merge of #149174 - GrigorenkoPV:const_block_item, r=me,ytmimi

`const` blocks as a `mod` item

Tracking issue: rust-lang/rust#149226

This adds support for writing `const { ... }` as an item in a module. In the current implementation, this is a unique AST item that gets lowered to `const _: () = const { ... };` in HIR.

rustfmt support included.

TODO:
- `pub const { ... }` does not make sense (see rust-lang/rust#147136). Reject it. Should this be rejected by the parser or smth?
- Improve diagnostics (preferably they should not mention the fake `_` ident).
This commit is contained in:
Matthias Krüger 2026-01-24 15:35:08 +01:00 committed by GitHub
commit 0bb15457de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 551 additions and 85 deletions

View file

@ -3626,6 +3626,7 @@ impl Item {
pub fn opt_generics(&self) -> Option<&Generics> {
match &self.kind {
ItemKind::ExternCrate(..)
| ItemKind::ConstBlock(_)
| ItemKind::Use(_)
| ItemKind::Mod(..)
| ItemKind::ForeignMod(_)
@ -3895,6 +3896,17 @@ impl ConstItemRhs {
}
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct ConstBlockItem {
pub id: NodeId,
pub span: Span,
pub block: Box<Block>,
}
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 {
@ -3914,6 +3926,11 @@ pub enum ItemKind {
///
/// E.g., `const FOO: i32 = 42;`.
Const(Box<ConstItem>),
/// 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 { .. }`.
@ -3990,6 +4007,8 @@ impl ItemKind {
| ItemKind::MacroDef(ident, _)
| ItemKind::Delegation(box Delegation { ident, .. }) => Some(ident),
ItemKind::ConstBlock(_) => Some(ConstBlockItem::IDENT),
ItemKind::Use(_)
| ItemKind::ForeignMod(_)
| ItemKind::GlobalAsm(_)
@ -4003,9 +4022,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",
}
}
@ -4016,6 +4035,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",
@ -4045,7 +4065,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,
}
}
}

View file

@ -425,6 +425,7 @@ macro_rules! common_visitor_and_walkers {
ByRef,
Closure,
Const,
ConstBlockItem,
ConstItem,
ConstItemRhs,
Defaultness,
@ -825,6 +826,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) =>

View file

@ -205,6 +205,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
| ItemKind::Use(..)
| ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::ConstBlock(..)
| ItemKind::Mod(..)
| ItemKind::ForeignMod(..)
| ItemKind::GlobalAsm(..)
@ -282,8 +283,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(
@ -302,6 +308,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_define_opaque(hir_id, &define_opaque);
hir::ItemKind::Const(ident, generics, ty, rhs)
}
ItemKind::ConstBlock(ConstBlockItem { span, id, block }) => hir::ItemKind::Const(
self.lower_ident(ConstBlockItem::IDENT),
hir::Generics::empty(),
self.arena.alloc(self.ty_tup(DUMMY_SP, &[])),
hir::ConstItemRhs::Body({
let body = hir::Expr {
hir_id: self.lower_node_id(*id),
kind: hir::ExprKind::Block(self.lower_block(block, false), None),
span: self.lower_span(*span),
};
self.record_body(&[], body)
}),
),
ItemKind::Fn(box Fn {
sig: FnSig { decl, header, span: fn_sig_span },
ident,

View file

@ -537,6 +537,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) {

View file

@ -205,6 +205,17 @@ impl<'a> State<'a> {
define_opaque.as_deref(),
);
}
ast::ItemKind::ConstBlock(ast::ConstBlockItem { id: _, span: _, block }) => {
let ib = self.ibox(INDENT_UNIT);
self.word("const");
self.nbsp();
{
let cb = self.cbox(0);
let ib = self.ibox(0);
self.print_block_with_attrs(block, &[], cb, ib);
}
self.end(ib);
}
ast::ItemKind::Const(box ast::ConstItem {
defaultness,
ident,

View file

@ -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;
@ -110,7 +110,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) => {

View file

@ -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;
@ -168,7 +168,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;

View file

@ -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::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, AllowConstBlockItems::No)
.expect("failed to reparse item")
.expect("an actual item")
}

View file

@ -25,8 +25,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};
@ -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, AllowConstBlockItems::Yes)? {
items.push(item);
}
AstFragment::Items(items)

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::{AllowConstBlockItems, ForceCollect, Parser};
use rustc_session::Session;
use rustc_session::config::ProcMacroExecutionStrategy;
use rustc_span::profiling::SpannedEventArgRecorder;
@ -160,7 +160,10 @@ impl MultiItemModifier for DeriveProcMacro {
let mut items = vec![];
loop {
match parser.parse_item(ForceCollect::No) {
match parser.parse_item(
ForceCollect::No,
if is_stmt { AllowConstBlockItems::No } else { AllowConstBlockItems::Yes },
) {
Ok(None) => break,
Ok(Some(item)) => {
if is_stmt {

View file

@ -414,6 +414,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.

View file

@ -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,

View file

@ -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::Yes,
) {
Ok(Some(item)) => {
// FIXME(#100717)

View file

@ -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);
@ -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,
allow_const_block_items: AllowConstBlockItems,
) -> 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, 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<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 @@ impl<'a> Parser<'a> {
attrs_allowed: bool,
fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
allow_const_block_items: AllowConstBlockItems,
) -> 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, allow_const_block_items)
}) {
let mut item = item.expect("an actual item");
attrs.prepend_to_nt_inner(&mut item.attrs);
return Ok(Some(*item));
@ -163,6 +177,7 @@ impl<'a> Parser<'a> {
let kind = this.parse_item_kind(
&mut attrs,
mac_allowed,
allow_const_block_items,
lo,
&vis,
&mut def,
@ -209,6 +224,7 @@ impl<'a> Parser<'a> {
&mut self,
attrs: &mut AttrVec,
macros_allowed: bool,
allow_const_block_items: AllowConstBlockItems,
lo: Span,
vis: &Visibility,
def: &mut Defaultness,
@ -257,6 +273,15 @@ impl<'a> Parser<'a> {
} else if self.check_impl_frontmatter(0) {
// IMPL ITEM
self.parse_item_impl(attrs, def_(), false)?
} else if let AllowConstBlockItems::Yes | AllowConstBlockItems::DoesNotMatter =
allow_const_block_items
&& self.check_inline_const(0)
{
// CONST BLOCK ITEM
if let AllowConstBlockItems::DoesNotMatter = allow_const_block_items {
debug!("Parsing a const block item that does not matter: {:?}", self.token.span);
};
ItemKind::ConstBlock(self.parse_const_block_item()?)
} else if let Const::Yes(const_span) = self.parse_constness(case) {
// CONST ITEM
self.recover_const_mut(const_span);
@ -316,6 +341,7 @@ impl<'a> Parser<'a> {
return self.parse_item_kind(
attrs,
macros_allowed,
allow_const_block_items,
lo,
vis,
def,
@ -1068,8 +1094,13 @@ impl<'a> Parser<'a> {
fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<Box<AssocItem>>>> {
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 {
@ -1096,8 +1127,7 @@ impl<'a> Parser<'a> {
},
};
Some(Box::new(Item { attrs, id, span, vis, kind, tokens }))
},
))
}))
}
/// Parses a `type` alias with the following grammar:
@ -1320,8 +1350,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 {
@ -1348,8 +1383,7 @@ impl<'a> Parser<'a> {
},
};
Some(Box::new(Item { attrs, id, span, vis, kind, tokens }))
},
))
}))
}
fn error_bad_item_kind<T>(&self, span: Span, kind: &ItemKind, ctx: &'static str) -> Option<T> {
@ -1437,6 +1471,14 @@ impl<'a> Parser<'a> {
}
}
fn parse_const_block_item(&mut self) -> PResult<'a, ConstBlockItem> {
self.expect_keyword(exp!(Const))?;
let const_span = self.prev_token.span;
self.psess.gated_spans.gate(sym::const_block_items, const_span);
let block = self.parse_block()?;
Ok(ConstBlockItem { id: DUMMY_NODE_ID, span: const_span.to(block.span), block })
}
/// Parse a static item with the prefix `"static" "mut"?` already parsed and stored in
/// `mutability`.
///
@ -2394,7 +2436,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);

View file

@ -145,6 +145,14 @@ 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,
}
/// If the next tokens are ill-formed `$ty::` recover them as `<$ty>::`.
#[macro_export]
macro_rules! maybe_recover_from_interpolated_ty_qpath {

View file

@ -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))),
},

View file

@ -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)) {

View file

@ -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 {
@ -2233,7 +2233,7 @@ fn parse_item_from_source_str(
psess: &ParseSess,
) -> PResult<'_, Option<Box<ast::Item>>> {
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`.
@ -2248,7 +2248,9 @@ fn string_to_expr(source_str: String) -> Box<ast::Expr> {
/// Parses a string, returns an item.
fn string_to_item(source_str: String) -> Option<Box<ast::Item>> {
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]

View file

@ -572,6 +572,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
Use,
Static,
Const,
ConstBlock,
Fn,
Mod,
ForeignMod,

View file

@ -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,

View file

@ -959,7 +959,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
}
// These items do not add names to modules.
ItemKind::Impl { .. } | ItemKind::ForeignMod(..) | ItemKind::GlobalAsm(..) => {}
ItemKind::Impl { .. }
| ItemKind::ForeignMod(..)
| ItemKind::GlobalAsm(..)
| ItemKind::ConstBlock(..) => {}
ItemKind::MacroDef(..) | ItemKind::MacCall(_) | ItemKind::DelegationMac(..) => {
unreachable!()

View file

@ -131,7 +131,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();

View file

@ -275,6 +275,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(..)

View file

@ -2727,8 +2727,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),
@ -2739,7 +2739,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),
@ -2751,19 +2751,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,
@ -2776,7 +2770,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,
@ -2795,7 +2789,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,
@ -2835,13 +2829,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);
@ -2849,7 +2837,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);
@ -2857,11 +2845,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: _,
}) => {
let is_type_const = attr::contains_name(&item.attrs, sym::type_const);
self.with_generic_param_rib(
@ -2903,15 +2891,36 @@ 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 { id: _, span: _, block }) => 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.resolve_labeled_block(None, block.id, block),
)
},
);
},
),
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),
@ -2921,7 +2930,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 {
@ -2945,7 +2954,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(
&[],
@ -5477,6 +5486,7 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> {
ItemKind::Mod(..)
| ItemKind::Static(..)
| ItemKind::ConstBlock(..)
| ItemKind::Use(..)
| ItemKind::ExternCrate(..)
| ItemKind::MacroDef(..)

View file

@ -731,6 +731,7 @@ symbols! {
console,
const_allocate,
const_async_blocks,
const_block_items,
const_closures,
const_compare_raw_pointers,
const_constructor,

View file

@ -539,7 +539,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::No,
);
let result = match parsed {
Ok(Some(ref item))

View file

@ -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::Yes) {
Ok(Some(item_ptr)) => *item_ptr,
Ok(None) => continue,
Err(err) => {

View file

@ -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<MacroArg> {
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<Box<ast::Item>>| x
);

View file

@ -7,7 +7,9 @@ use rustc_span::{BytePos, Ident, Pos, Span, symbol};
use tracing::debug;
use crate::attr::*;
use crate::comment::{CodeCharKind, CommentCodeSlices, contains_comment, rewrite_comment};
use crate::comment::{
CodeCharKind, CommentCodeSlices, contains_comment, recover_comment_removed, rewrite_comment,
};
use crate::config::{BraceStyle, Config, MacroSelector, StyleEdition};
use crate::coverage::transform_missing_snippet;
use crate::items::{
@ -533,6 +535,28 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => {
self.visit_static(&StaticParts::from_item(item));
}
ast::ItemKind::ConstBlock(ast::ConstBlockItem {
id: _,
span,
ref block,
}) => {
let context = &self.get_context();
let offset = self.block_indent;
self.push_rewrite(
item.span,
block
.rewrite(
context,
Shape::legacy(
context.budget(offset.block_indent),
offset.block_only(),
),
)
.map(|rhs| {
recover_comment_removed(format!("const {rhs}"), span, context)
}),
);
}
ast::ItemKind::Fn(ref fn_kind) => {
let ast::Fn {
defaultness,

View file

@ -0,0 +1,24 @@
#![feature(const_block_items)]
const {
assert!(true)
}
#[cfg(false)] const { assert!(false) }
#[cfg(false)]
// foo
const
{
// bar
assert!(false)
// baz
} // 123
#[expect(unused)]
pub const { let a = 1; assert!(true); }

View file

@ -0,0 +1,20 @@
#![feature(const_block_items)]
const { assert!(true) }
#[cfg(false)]
const { assert!(false) }
#[cfg(false)]
// foo
const {
// bar
assert!(false)
// baz
} // 123
#[expect(unused)]
const {
let a = 1;
assert!(true);
}

View file

@ -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() {}

View file

@ -0,0 +1,15 @@
error[E0080]: evaluation panicked: assertion failed: false
--> $DIR/assert-fail.rs:5:9
|
LL | const { assert!(false) }
| ^^^^^^^^^^^^^^ evaluation of `_` failed here
error[E0080]: evaluation panicked: assertion failed: 2 + 2 == 5
--> $DIR/assert-fail.rs:7:9
|
LL | const { assert!(2 + 2 == 5) }
| ^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0080`.

View file

@ -0,0 +1,7 @@
//@ check-pass
#![feature(const_block_items)]
const { assert!(true) }
const { assert!(2 + 2 == 4) }
fn main() {}

View file

@ -0,0 +1,10 @@
//@ build-pass
//@ compile-flags: -Zunpretty=hir
#![feature(const_block_items)]
const {
// foo
}
fn main() { }

View file

@ -0,0 +1,14 @@
//@ build-pass
//@ compile-flags: -Zunpretty=hir
#![feature(const_block_items)]
extern crate std;
#[prelude_import]
use ::std::prelude::rust_2015::*;
const _: () =
{
// foo
};
fn main() { }

View file

@ -0,0 +1,20 @@
//@ check-fail
#![feature(const_block_items)]
const {
assert!(true);
2 + 2 //~ ERROR: mismatched types [E0308]
}
const fn id<T>(t: T) -> T {
t
}
const { id(2) }
//~^ ERROR: mismatched types [E0308]
const { id(()) }
fn main() {}

View file

@ -0,0 +1,30 @@
error[E0308]: mismatched types
--> $DIR/typecheck.rs:7:5
|
LL | 2 + 2
| ^^^^^ expected `()`, found integer
error[E0308]: mismatched types
--> $DIR/typecheck.rs:15:12
|
LL | const { id(2) }
| -- ^ expected `()`, found integer
| |
| arguments to this function are incorrect
|
help: the return type of this call is `{integer}` due to the type of the argument passed
--> $DIR/typecheck.rs:15:9
|
LL | const { id(2) }
| ^^^-^
| |
| this argument influences the return type of `id`
note: function defined here
--> $DIR/typecheck.rs:11:10
|
LL | const fn id<T>(t: T) -> T {
| ^^ ----
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.

View file

@ -1,5 +1,5 @@
warning: trait `Value` is never used
--> $DIR/const-block-item.rs:5:15
--> $DIR/static.rs:5:15
|
LL | pub trait Value {
| ^^^^^

View file

@ -0,0 +1,7 @@
//@ check-fail
const { //~ ERROR: const block items are experimental
assert!(true)
}
fn main() {}

View file

@ -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`.

View file

@ -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() {} ],

View file

@ -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() {}

View file

@ -0,0 +1,12 @@
#![feature(const_block_items)]
// ATTENTION: if we ever start accepting inner attributes here, make sure `rustfmt` can handle them.
// see: https://github.com/rust-lang/rustfmt/issues/6158
const {
#![expect(unused)] //~ ERROR: an inner attribute is not permitted in this context
let a = 1;
assert!(true);
}
fn main() {}

View file

@ -0,0 +1,13 @@
error: an inner attribute is not permitted in this context
--> $DIR/inner-attr.rs:7:5
|
LL | #![expect(unused)]
| ^^^^^^^^^^^^^^^^^^
...
LL | fn main() {}
| ------------ the inner attribute doesn't annotate this function
|
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
error: aborting due to 1 previous error

View file

@ -0,0 +1,12 @@
//@ check-pass
#![feature(const_block_items)]
macro_rules! foo {
($item:item) => {
$item
};
}
foo!(const {});
fn main() {}

View file

@ -0,0 +1,14 @@
//@ check-fail
#![feature(const_block_items)]
macro_rules! foo {
($item:item) => {
$item
//~^ ERROR: expected expression, found ``
};
}
fn main() {
foo!(const {});
}

View file

@ -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

View file

@ -0,0 +1,9 @@
//@ check-pass
#![feature(const_block_items)]
fn main() {
mod foo {
const { assert!(true) }
}
}

View file

@ -0,0 +1,10 @@
#![feature(const_block_items)]
//@ check-pass
// FIXME(const_block_items): `pub`` is useless here
pub const {
assert!(true);
}
fn main() { }

View file

@ -0,0 +1,10 @@
#![feature(const_block_items)]
const unsafe fn foo() -> bool {
true
}
const unsafe { assert!(foo()) }
//~^ ERROR: expected one of `extern` or `fn`, found `{`
fn main() { }

View file

@ -0,0 +1,8 @@
error: expected one of `extern` or `fn`, found `{`
--> $DIR/unsafe.rs:7:14
|
LL | const unsafe { assert!(foo()) }
| ^ expected one of `extern` or `fn`
error: aborting due to 1 previous error