Rollup merge of #149667 - Shinonn23:fix-ice-constblock-148138, r=dianne

Fix ICE by rejecting const blocks in patterns during AST lowering (closes #148138)

This PR fixes the ICE reported in rust-lang/rust#148138.

The root cause is that `const` blocks aren’t allowed in pattern position, but the AST lowering logic still attempted to create `PatExprKind::ConstBlock`, allowing invalid HIR to reach type checking and trigger a `span_bug!`.

Following the discussion in the issue, this patch removes the `ConstBlock` lowering path from `lower_expr_within_pat`. Any `ExprKind::ConstBlock` inside a pattern is now handled consistently with other invalid pattern expressions.

A new UI test is included to ensure the compiler reports a proper error and to prevent regressions.

Closes rust-lang/rust#148138.
This commit is contained in:
Jonathan Brouwer 2025-12-28 22:52:31 +01:00 committed by GitHub
commit ef835a83ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 206 additions and 85 deletions

View file

@ -6,6 +6,7 @@ ast_lowering_abi_specified_multiple_times =
ast_lowering_arbitrary_expression_in_pattern =
arbitrary expressions aren't allowed in patterns
.pattern_from_macro_note = the `expr` fragment specifier forces the metavariable's content to be an expression
.const_block_in_pattern_help = use a named `const`-item or an `if`-guard (`x if x == const {"{ ... }"}`) instead
ast_lowering_argument = argument

View file

@ -357,6 +357,8 @@ pub(crate) struct ArbitraryExpressionInPattern {
pub span: Span,
#[note(ast_lowering_pattern_from_macro_note)]
pub pattern_from_macro_note: bool,
#[help(ast_lowering_const_block_in_pattern_help)]
pub const_block_in_pattern_help: bool,
}
#[derive(Diagnostic)]

View file

@ -399,7 +399,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ExprKind::Lit(lit) => {
hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: false }
}
ExprKind::ConstBlock(c) => hir::PatExprKind::ConstBlock(self.lower_const_block(c)),
ExprKind::IncludedBytes(byte_sym) => hir::PatExprKind::Lit {
lit: respan(span, LitKind::ByteStr(*byte_sym, StrStyle::Cooked)),
negated: false,
@ -419,10 +418,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: true }
}
_ => {
let is_const_block = matches!(expr.kind, ExprKind::ConstBlock(_));
let pattern_from_macro = expr.is_approximately_pattern();
let guar = self.dcx().emit_err(ArbitraryExpressionInPattern {
span,
pattern_from_macro_note: pattern_from_macro,
const_block_in_pattern_help: is_const_block,
});
err(guar)
}

View file

@ -1944,7 +1944,6 @@ pub enum PatExprKind<'hir> {
// once instead of matching on unop neg expressions everywhere.
negated: bool,
},
ConstBlock(ConstBlock),
/// A path pattern for a unit struct/variant or a (maybe-associated) constant.
Path(QPath<'hir>),
}

View file

@ -792,7 +792,6 @@ pub fn walk_pat_expr<'v, V: Visitor<'v>>(visitor: &mut V, expr: &'v PatExpr<'v>)
try_visit!(visitor.visit_id(*hir_id));
match kind {
PatExprKind::Lit { lit, negated } => visitor.visit_lit(*hir_id, *lit, *negated),
PatExprKind::ConstBlock(c) => visitor.visit_inline_const(c),
PatExprKind::Path(qpath) => visitor.visit_qpath(qpath, *hir_id, *span),
}
}

View file

@ -1880,7 +1880,6 @@ impl<'a> State<'a> {
}
self.print_literal(lit);
}
hir::PatExprKind::ConstBlock(c) => self.print_inline_const(c),
hir::PatExprKind::Path(qpath) => self.print_qpath(qpath, true),
}
}

View file

@ -925,9 +925,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
ty
}
rustc_hir::PatExprKind::ConstBlock(c) => {
self.check_expr_const_block(c, Expectation::NoExpectation)
}
rustc_hir::PatExprKind::Path(qpath) => {
let (res, opt_ty, segments) =
self.resolve_ty_and_res_fully_qualified_call(qpath, lt.hir_id, lt.span);

View file

@ -13,14 +13,13 @@ use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
use rustc_hir::{self as hir, LangItem, RangeEnd};
use rustc_index::Idx;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir::interpret::LitToConstInput;
use rustc_middle::thir::{
Ascription, DerefPatBorrowMode, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
};
use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment};
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypingMode};
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::def_id::DefId;
use rustc_span::{ErrorGuaranteed, Span};
@ -613,54 +612,8 @@ impl<'tcx> PatCtxt<'tcx> {
pattern
}
/// Lowers an inline const block (e.g. `const { 1 + 1 }`) to a pattern.
fn lower_inline_const(
&mut self,
block: &'tcx hir::ConstBlock,
id: hir::HirId,
span: Span,
) -> PatKind<'tcx> {
let tcx = self.tcx;
let def_id = block.def_id;
let ty = tcx.typeck(def_id).node_type(block.hir_id);
let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id());
let parent_args = ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id);
let args = ty::InlineConstArgs::new(tcx, ty::InlineConstArgsParts { parent_args, ty }).args;
let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), args };
let c = ty::Const::new_unevaluated(self.tcx, ct);
let pattern = self.const_to_pat(c, ty, id, span);
// Apply a type ascription for the inline constant.
let annotation = {
let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
let args = ty::InlineConstArgs::new(
tcx,
ty::InlineConstArgsParts { parent_args, ty: infcx.next_ty_var(span) },
)
.args;
infcx.canonicalize_user_type_annotation(ty::UserType::new(ty::UserTypeKind::TypeOf(
def_id.to_def_id(),
ty::UserArgs { args, user_self_ty: None },
)))
};
let annotation =
CanonicalUserTypeAnnotation { user_ty: Box::new(annotation), span, inferred_ty: ty };
PatKind::AscribeUserType {
subpattern: pattern,
ascription: Ascription {
annotation,
// Note that we use `Contravariant` here. See the `variance` field documentation
// for details.
variance: ty::Contravariant,
},
}
}
/// Lowers the kinds of "expression" that can appear in a HIR pattern:
/// - Paths (e.g. `FOO`, `foo::BAR`, `Option::None`)
/// - Inline const blocks (e.g. `const { 1 + 1 }`)
/// - Literals, possibly negated (e.g. `-128u8`, `"hello"`)
fn lower_pat_expr(
&mut self,
@ -669,9 +622,6 @@ impl<'tcx> PatCtxt<'tcx> {
) -> PatKind<'tcx> {
match &expr.kind {
hir::PatExprKind::Path(qpath) => self.lower_path(qpath, expr.hir_id, expr.span).kind,
hir::PatExprKind::ConstBlock(anon_const) => {
self.lower_inline_const(anon_const, expr.hir_id, expr.span)
}
hir::PatExprKind::Lit { lit, negated } => {
// We handle byte string literal patterns by using the pattern's type instead of the
// literal's type in `const_to_pat`: if the literal `b"..."` matches on a slice reference,

View file

@ -1521,7 +1521,7 @@ impl<'a> Parser<'a> {
},
)
} else if this.check_inline_const(0) {
this.parse_const_block(lo, false)
this.parse_const_block(lo)
} else if this.may_recover() && this.is_do_catch_block() {
this.recover_do_catch()
} else if this.is_try_block() {

View file

@ -1317,7 +1317,7 @@ impl<'a> Parser<'a> {
}
/// Parses inline const expressions.
fn parse_const_block(&mut self, span: Span, pat: bool) -> PResult<'a, Box<Expr>> {
fn parse_const_block(&mut self, span: Span) -> PResult<'a, Box<Expr>> {
self.expect_keyword(exp!(Const))?;
let (attrs, blk) = self.parse_inner_attrs_and_block(None)?;
let anon_const = AnonConst {
@ -1326,18 +1326,7 @@ impl<'a> Parser<'a> {
mgca_disambiguation: MgcaDisambiguation::AnonConst,
};
let blk_span = anon_const.value.span;
let kind = if pat {
let guar = self
.dcx()
.struct_span_err(blk_span, "const blocks cannot be used as patterns")
.with_help(
"use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead",
)
.emit();
ExprKind::Err(guar)
} else {
ExprKind::ConstBlock(anon_const)
};
let kind = ExprKind::ConstBlock(anon_const);
Ok(self.mk_expr_with_attrs(span.to(blk_span), kind, attrs))
}

View file

@ -785,8 +785,10 @@ impl<'a> Parser<'a> {
} else if self.eat_keyword(exp!(Box)) {
self.parse_pat_box()?
} else if self.check_inline_const(0) {
// Parse `const pat`
let const_expr = self.parse_const_block(lo.to(self.token.span), true)?;
// Parse `const pat`.
// NOTE: This will always error later during AST lowering because
// inline const cannot be used as patterns.
let const_expr = self.parse_const_block(lo.to(self.token.span))?;
if let Some(re) = self.parse_range_end() {
self.parse_pat_range_begin_with(const_expr, re)?
@ -1281,7 +1283,7 @@ impl<'a> Parser<'a> {
.then_some(self.prev_token.span);
let bound = if self.check_inline_const(0) {
self.parse_const_block(self.token.span, true)
self.parse_const_block(self.token.span)
} else if self.check_path() {
let lo = self.token.span;
let (qself, path) = if self.eat_lt() {

View file

@ -724,7 +724,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
kind!("Lit {{ ref {lit}, {negated} }}");
self.lit(lit);
},
PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"),
PatExprKind::Path(_) => self.maybe_path(pat),
}
}

View file

@ -578,7 +578,6 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
Some(val)
}
},
PatExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir_body(*body).value),
PatExprKind::Path(qpath) => self.qpath(qpath, pat_expr.hir_id),
}
}

View file

@ -705,9 +705,8 @@ impl HirEqInterExpr<'_, '_, '_> {
negated: right_neg,
},
) => left_neg == right_neg && left.node == right.node,
(PatExprKind::ConstBlock(left), PatExprKind::ConstBlock(right)) => self.eq_body(left.body, right.body),
(PatExprKind::Path(left), PatExprKind::Path(right)) => self.eq_qpath(left, right),
(PatExprKind::Lit { .. } | PatExprKind::ConstBlock(..) | PatExprKind::Path(..), _) => false,
(PatExprKind::Lit { .. } | PatExprKind::Path(..), _) => false,
}
}
@ -1312,7 +1311,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
lit.node.hash(&mut self.s);
negated.hash(&mut self.s);
},
PatExprKind::ConstBlock(c) => self.hash_body(c.body),
PatExprKind::Path(qpath) => self.hash_qpath(qpath),
}
}

View file

@ -4,8 +4,63 @@
fn main() {
match 1 {
const { 1 + 7 } => {}
//~^ ERROR const blocks cannot be used as patterns
//~^ ERROR arbitrary expressions aren't allowed in patterns
2 => {}
_ => {}
}
match 5 {
const { 1 } ..= 10 => {}
//~^ ERROR arbitrary expressions aren't allowed in patterns
_ => {}
}
match 5 {
1 ..= const { 10 } => {}
//~^ ERROR arbitrary expressions aren't allowed in patterns
_ => {}
}
match 5 {
const { 1 } ..= const { 10 } => {}
//~^ ERROR arbitrary expressions aren't allowed in patterns
//~| ERROR arbitrary expressions aren't allowed in patterns
_ => {}
}
match 5 {
const { 1 } .. 10 => {}
//~^ ERROR arbitrary expressions aren't allowed in patterns
_ => {}
}
match 5 {
1 .. const { 10 } => {}
//~^ ERROR arbitrary expressions aren't allowed in patterns
_ => {}
}
match 5 {
const { 1 + 2 } ..= 10 => {}
//~^ ERROR arbitrary expressions aren't allowed in patterns
_ => {}
}
match 5 {
1 ..= const { 5 + 5 } => {}
//~^ ERROR arbitrary expressions aren't allowed in patterns
_ => {}
}
match 5 {
const { 3 } .. => {}
//~^ ERROR arbitrary expressions aren't allowed in patterns
_ => {}
}
match 5 {
..= const { 7 } => {}
//~^ ERROR arbitrary expressions aren't allowed in patterns
_ => {}
}
}

View file

@ -1,10 +1,90 @@
error: const blocks cannot be used as patterns
--> $DIR/in-pat-recovery.rs:6:15
error: arbitrary expressions aren't allowed in patterns
--> $DIR/in-pat-recovery.rs:6:9
|
LL | const { 1 + 7 } => {}
| ^^^^^^^^^
| ^^^^^^^^^^^^^^^
|
= help: use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead
error: aborting due to 1 previous error
error: arbitrary expressions aren't allowed in patterns
--> $DIR/in-pat-recovery.rs:13:9
|
LL | const { 1 } ..= 10 => {}
| ^^^^^^^^^^^
|
= help: use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead
error: arbitrary expressions aren't allowed in patterns
--> $DIR/in-pat-recovery.rs:19:15
|
LL | 1 ..= const { 10 } => {}
| ^^^^^^^^^^^^
|
= help: use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead
error: arbitrary expressions aren't allowed in patterns
--> $DIR/in-pat-recovery.rs:25:9
|
LL | const { 1 } ..= const { 10 } => {}
| ^^^^^^^^^^^
|
= help: use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead
error: arbitrary expressions aren't allowed in patterns
--> $DIR/in-pat-recovery.rs:25:25
|
LL | const { 1 } ..= const { 10 } => {}
| ^^^^^^^^^^^^
|
= help: use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead
error: arbitrary expressions aren't allowed in patterns
--> $DIR/in-pat-recovery.rs:32:9
|
LL | const { 1 } .. 10 => {}
| ^^^^^^^^^^^
|
= help: use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead
error: arbitrary expressions aren't allowed in patterns
--> $DIR/in-pat-recovery.rs:38:14
|
LL | 1 .. const { 10 } => {}
| ^^^^^^^^^^^^
|
= help: use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead
error: arbitrary expressions aren't allowed in patterns
--> $DIR/in-pat-recovery.rs:44:9
|
LL | const { 1 + 2 } ..= 10 => {}
| ^^^^^^^^^^^^^^^
|
= help: use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead
error: arbitrary expressions aren't allowed in patterns
--> $DIR/in-pat-recovery.rs:50:15
|
LL | 1 ..= const { 5 + 5 } => {}
| ^^^^^^^^^^^^^^^
|
= help: use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead
error: arbitrary expressions aren't allowed in patterns
--> $DIR/in-pat-recovery.rs:56:9
|
LL | const { 3 } .. => {}
| ^^^^^^^^^^^
|
= help: use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead
error: arbitrary expressions aren't allowed in patterns
--> $DIR/in-pat-recovery.rs:62:13
|
LL | ..= const { 7 } => {}
| ^^^^^^^^^^^
|
= help: use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead
error: aborting due to 11 previous errors

View file

@ -0,0 +1,10 @@
#![feature(deref_patterns)]
#![expect(incomplete_features)]
fn main() {
let vec![const { vec![] }]: Vec<usize> = vec![];
//~^ ERROR expected a pattern, found a function call
//~| ERROR usage of qualified paths in this context is experimental
//~| ERROR expected tuple struct or tuple variant
//~| ERROR arbitrary expressions aren't allowed in patterns
}

View file

@ -0,0 +1,41 @@
error[E0532]: expected a pattern, found a function call
--> $DIR/ice-adjust-mode-unimplemented-for-constblock.rs:5:9
|
LL | let vec![const { vec![] }]: Vec<usize> = vec![];
| ^^^^^^^^^^^^^^^^^^^^^^ not a tuple struct or tuple variant
|
= note: function calls are not allowed in patterns: <https://doc.rust-lang.org/book/ch19-00-patterns.html>
= note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0658]: usage of qualified paths in this context is experimental
--> $DIR/ice-adjust-mode-unimplemented-for-constblock.rs:5:9
|
LL | let vec![const { vec![] }]: Vec<usize> = vec![];
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #86935 <https://github.com/rust-lang/rust/issues/86935> for more information
= help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
error: arbitrary expressions aren't allowed in patterns
--> $DIR/ice-adjust-mode-unimplemented-for-constblock.rs:5:14
|
LL | let vec![const { vec![] }]: Vec<usize> = vec![];
| ^^^^^^^^^^^^^^^^
|
= help: use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead
error[E0164]: expected tuple struct or tuple variant, found associated function `<[_]>::into_vec`
--> $DIR/ice-adjust-mode-unimplemented-for-constblock.rs:5:9
|
LL | let vec![const { vec![] }]: Vec<usize> = vec![];
| ^^^^^^^^^^^^^^^^^^^^^^ `fn` calls are not allowed in patterns
|
= help: for more information, visit https://doc.rust-lang.org/book/ch19-00-patterns.html
= note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0164, E0532, E0658.
For more information about an error, try `rustc --explain E0164`.