Skip to content

loop_match: suggest extracting to a const item #143585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,16 @@ mir_build_confused = missing patterns are not covered because `{$variable}` is i

mir_build_const_continue_bad_const = could not determine the target branch for this `#[const_continue]`
.label = this value is too generic
.note = the value must be a literal or a monomorphic const

mir_build_const_continue_missing_value = a `#[const_continue]` must break to a label with a value

mir_build_const_continue_not_const = could not determine the target branch for this `#[const_continue]`
.help = try extracting the expression into a `const` item

mir_build_const_continue_not_const_const_block = `const` blocks may use generics, and are not evaluated early enough
mir_build_const_continue_not_const_const_other = this value must be a literal or a monomorphic const
mir_build_const_continue_not_const_constant_parameter = constant parameters may use generics, and are not evaluated early enough

mir_build_const_continue_unknown_jump_target = the target of this `#[const_continue]` is not statically known
.label = this value must be a literal or a monomorphic const

Expand Down
29 changes: 26 additions & 3 deletions compiler/rustc_mir_build/src/builder/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ use tracing::{debug, instrument};

use super::matches::BuiltMatchTree;
use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
use crate::errors::{ConstContinueBadConst, ConstContinueUnknownJumpTarget};
use crate::errors::{
ConstContinueBadConst, ConstContinueNotMonomorphicConst, ConstContinueUnknownJumpTarget,
};

#[derive(Debug)]
pub(crate) struct Scopes<'tcx> {
Expand Down Expand Up @@ -867,7 +869,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
span_bug!(span, "break value must be a scope")
};

let constant = match &self.thir[value].kind {
let expr = &self.thir[value];
let constant = match &expr.kind {
ExprKind::Adt(box AdtExpr { variant_index, fields, base, .. }) => {
assert!(matches!(base, AdtExprBase::None));
assert!(fields.is_empty());
Expand All @@ -887,7 +890,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
),
}
}
_ => self.as_constant(&self.thir[value]),

ExprKind::Literal { .. }
| ExprKind::NonHirLiteral { .. }
| ExprKind::ZstLiteral { .. }
| ExprKind::NamedConst { .. } => self.as_constant(&self.thir[value]),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic constant items will have the same problem, as you could specify them with arguments.

Please add a test for them and for associated consts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure what you mean, I added some extra test cases for the associated const scenarios I could think of. Did you have anything else in mind?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Associated consts of traits can use generics (either by being GATs or by having generic params on the trait). Associated consts of types can use generics I'd the type is generic

Generic const items are an unstable feature but can also explicitly use generics

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, well the examples I can come up with all fail well before any const evaluation would actually take place. Anyway, I've added what I think you mean.


other => {
use crate::errors::ConstContinueNotMonomorphicConstReason as Reason;

let span = expr.span;
let reason = match other {
ExprKind::ConstParam { .. } => Reason::ConstantParameter { span },
ExprKind::ConstBlock { .. } => Reason::ConstBlock { span },
_ => Reason::Other { span },
};

self.tcx
.dcx()
.emit_err(ConstContinueNotMonomorphicConst { span: expr.span, reason });
return block.unit();
}
};

let break_index = self
Expand Down
32 changes: 32 additions & 0 deletions compiler/rustc_mir_build/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1213,6 +1213,38 @@ pub(crate) struct LoopMatchArmWithGuard {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(mir_build_const_continue_not_const)]
#[help]
pub(crate) struct ConstContinueNotMonomorphicConst {
#[primary_span]
pub span: Span,

#[subdiagnostic]
pub reason: ConstContinueNotMonomorphicConstReason,
}

#[derive(Subdiagnostic)]
pub(crate) enum ConstContinueNotMonomorphicConstReason {
#[label(mir_build_const_continue_not_const_constant_parameter)]
ConstantParameter {
#[primary_span]
span: Span,
},

#[label(mir_build_const_continue_not_const_const_block)]
ConstBlock {
#[primary_span]
span: Span,
},

#[label(mir_build_const_continue_not_const_const_other)]
Other {
#[primary_span]
span: Span,
},
}

#[derive(Diagnostic)]
#[diag(mir_build_const_continue_bad_const)]
pub(crate) struct ConstContinueBadConst {
Expand Down
174 changes: 174 additions & 0 deletions tests/ui/loop-match/suggest-const-item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#![allow(incomplete_features)]
#![feature(loop_match)]
#![feature(generic_const_items)]
#![crate_type = "lib"]

const fn const_fn() -> i32 {
1
}

#[unsafe(no_mangle)]
fn suggest_const_block<const N: i32>() -> i32 {
let mut state = 0;
#[loop_match]
loop {
state = 'blk: {
match state {
0 => {
#[const_continue]
break 'blk const_fn();
//~^ ERROR could not determine the target branch for this `#[const_continue]`
}
1 => {
#[const_continue]
break 'blk const { const_fn() };
//~^ ERROR could not determine the target branch for this `#[const_continue]`
}
2 => {
#[const_continue]
break 'blk N;
//~^ ERROR could not determine the target branch for this `#[const_continue]`
}
_ => {
#[const_continue]
break 'blk 1 + 1;
//~^ ERROR could not determine the target branch for this `#[const_continue]`
}
}
}
}
state
}

struct S;

impl S {
const M: usize = 42;

fn g() {
let mut state = 0;
#[loop_match]
loop {
state = 'blk: {
match state {
0 => {
#[const_continue]
break 'blk Self::M;
}
_ => panic!(),
}
}
}
}
}

trait T {
const N: usize;

fn f() {
let mut state = 0;
#[loop_match]
loop {
state = 'blk: {
match state {
0 => {
#[const_continue]
break 'blk Self::N;
//~^ ERROR could not determine the target branch for this `#[const_continue]`
}
_ => panic!(),
}
}
}
}
}

impl T for S {
const N: usize = 1;
}

impl S {
fn h() {
let mut state = 0;
#[loop_match]
loop {
state = 'blk: {
match state {
0 => {
#[const_continue]
break 'blk Self::N;
}
_ => panic!(),
}
}
}
}
}

trait T2<U> {
const L: u32;

fn p() {
let mut state = 0;
#[loop_match]
loop {
state = 'blk: {
match state {
0 => {
#[const_continue]
break 'blk Self::L;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another test could be the same function as a free (generic) function, but using <() as Trait<T>>::L which should probably always fail, but it may succeed if L in the impl<T> Trait<T> for () {} does not use This for its value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that does appear to be the current behavior.

//~^ ERROR could not determine the target branch for this `#[const_continue]`
}
_ => panic!(),
}
}
}
}
}

const SIZE_OF<T>: usize = size_of::<T>();

fn q<T>() {
let mut state = 0;
#[loop_match]
loop {
state = 'blk: {
match state {
0 => {
#[const_continue]
break 'blk SIZE_OF::<T>;
//~^ ERROR could not determine the target branch for this `#[const_continue]`
}
_ => panic!(),
}
}
}
}

trait Trait<T> {
const X: usize = 9000;
const Y: usize = size_of::<T>();
}

impl<T> Trait<T> for () {}

fn r<T>() {
let mut state = 0;
#[loop_match]
loop {
state = 'blk: {
match state {
0 => {
#[const_continue]
break 'blk <() as Trait<T>>::X;
}
1 => {
#[const_continue]
break 'blk <() as Trait<T>>::Y;
//~^ ERROR could not determine the target branch for this `#[const_continue]`
}
_ => panic!(),
}
}
}
}
58 changes: 58 additions & 0 deletions tests/ui/loop-match/suggest-const-item.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
error: could not determine the target branch for this `#[const_continue]`
--> $DIR/suggest-const-item.rs:19:32
|
LL | break 'blk const_fn();
| ^^^^^^^^^^ this value must be a literal or a monomorphic const
|
= help: try extracting the expression into a `const` item

error: could not determine the target branch for this `#[const_continue]`
--> $DIR/suggest-const-item.rs:24:32
|
LL | break 'blk const { const_fn() };
| ^^^^^^^^^^^^^^^^^^^^ `const` blocks may use generics, and are not evaluated early enough
|
= help: try extracting the expression into a `const` item

error: could not determine the target branch for this `#[const_continue]`
--> $DIR/suggest-const-item.rs:29:32
|
LL | break 'blk N;
| ^ constant parameters may use generics, and are not evaluated early enough
|
= help: try extracting the expression into a `const` item

error: could not determine the target branch for this `#[const_continue]`
--> $DIR/suggest-const-item.rs:34:32
|
LL | break 'blk 1 + 1;
| ^^^^^ this value must be a literal or a monomorphic const
|
= help: try extracting the expression into a `const` item

error: could not determine the target branch for this `#[const_continue]`
--> $DIR/suggest-const-item.rs:76:36
|
LL | break 'blk Self::N;
| ^^^^^^^ this value is too generic

error: could not determine the target branch for this `#[const_continue]`
--> $DIR/suggest-const-item.rs:119:36
|
LL | break 'blk Self::L;
| ^^^^^^^ this value is too generic

error: could not determine the target branch for this `#[const_continue]`
--> $DIR/suggest-const-item.rs:139:32
|
LL | break 'blk SIZE_OF::<T>;
| ^^^^^^^^^^^^ this value is too generic

error: could not determine the target branch for this `#[const_continue]`
--> $DIR/suggest-const-item.rs:167:32
|
LL | break 'blk <() as Trait<T>>::Y;
| ^^^^^^^^^^^^^^^^^^^ this value is too generic

error: aborting due to 8 previous errors

Loading