Skip to content

Commit 8f4ff04

Browse files
committed
loop match: error on #[const_continue] outside #[loop_match]
1 parent 6268d0a commit 8f4ff04

File tree

7 files changed

+190
-13
lines changed

7 files changed

+190
-13
lines changed

compiler/rustc_mir_build/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,12 @@ mir_build_const_continue_bad_const = could not determine the target branch for t
8888
.label = this value is too generic
8989
.note = the value must be a literal or a monomorphic const
9090
91+
mir_build_const_continue_invalid_jump = a `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
92+
9193
mir_build_const_continue_missing_value = a `#[const_continue]` must break to a label with a value
9294
95+
mir_build_const_continue_not_in_loop_match = a `#[const_continue]` can only occur in a `#[loop_match]` loop
96+
9397
mir_build_const_continue_unknown_jump_target = the target of this `#[const_continue]` is not statically known
9498
.label = this value must be a literal or a monomorphic const
9599

compiler/rustc_mir_build/src/builder/scope.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,10 @@ use tracing::{debug, instrument};
100100

101101
use super::matches::BuiltMatchTree;
102102
use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
103-
use crate::errors::{ConstContinueBadConst, ConstContinueUnknownJumpTarget};
103+
use crate::errors::{
104+
ConstContinueBadConst, ConstContinueInvalidJump, ConstContinueNotInLoopMatch,
105+
ConstContinueUnknownJumpTarget,
106+
};
104107

105108
#[derive(Debug)]
106109
pub(crate) struct Scopes<'tcx> {
@@ -862,6 +865,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
862865
) -> BlockAnd<()> {
863866
let span = source_info.span;
864867

868+
let opt_break_index = self
869+
.scopes
870+
.const_continuable_scopes
871+
.iter()
872+
.rposition(|const_continuable_scope| const_continuable_scope.region_scope == scope);
873+
874+
let Some(break_index) = opt_break_index else {
875+
if self.scopes.const_continuable_scopes.is_empty() {
876+
self.tcx.dcx().emit_fatal(ConstContinueNotInLoopMatch { span })
877+
} else {
878+
self.tcx.dcx().emit_fatal(ConstContinueInvalidJump { span })
879+
}
880+
};
881+
865882
// A break can only break out of a scope, so the value should be a scope.
866883
let rustc_middle::thir::ExprKind::Scope { value, .. } = self.thir[value].kind else {
867884
span_bug!(span, "break value must be a scope")
@@ -890,13 +907,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
890907
_ => self.as_constant(&self.thir[value]),
891908
};
892909

893-
let break_index = self
894-
.scopes
895-
.const_continuable_scopes
896-
.iter()
897-
.rposition(|const_continuable_scope| const_continuable_scope.region_scope == scope)
898-
.unwrap_or_else(|| span_bug!(span, "no enclosing const-continuable scope found"));
899-
900910
let scope = &self.scopes.const_continuable_scopes[break_index];
901911

902912
let state_decl = &self.local_decls[scope.state_place.as_local().unwrap()];

compiler/rustc_mir_build/src/errors.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,20 @@ pub(crate) struct ConstContinueBadConst {
12211221
pub span: Span,
12221222
}
12231223

1224+
#[derive(Diagnostic)]
1225+
#[diag(mir_build_const_continue_invalid_jump)]
1226+
pub(crate) struct ConstContinueInvalidJump {
1227+
#[primary_span]
1228+
pub span: Span,
1229+
}
1230+
1231+
#[derive(Diagnostic)]
1232+
#[diag(mir_build_const_continue_not_in_loop_match)]
1233+
pub(crate) struct ConstContinueNotInLoopMatch {
1234+
#[primary_span]
1235+
pub span: Span,
1236+
}
1237+
12241238
#[derive(Diagnostic)]
12251239
#[diag(mir_build_const_continue_missing_value)]
12261240
pub(crate) struct ConstContinueMissingValue {
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Test that the correct error is emitted when `#[const_continue]` occurs outside of
2+
// a loop match. See also https://github.com/rust-lang/rust/issues/143165.
3+
#![allow(incomplete_features)]
4+
#![feature(loop_match)]
5+
#![crate_type = "lib"]
6+
7+
fn main1() {
8+
let mut state;
9+
loop {
10+
state = 'state: {
11+
#[const_continue]
12+
break break;
13+
//~^ ERROR a `#[const_continue]` can only occur in a `#[loop_match]` loop
14+
//~| ERROR unlabeled `break` inside of a labeled block
15+
//~| ERROR unlabeled `break` inside of a labeled block
16+
}
17+
}
18+
}
19+
20+
enum State2 {
21+
X,
22+
Z,
23+
}
24+
25+
fn main2() {
26+
let mut state1;
27+
let mut state2;
28+
let mut first;
29+
'a: loop {
30+
state1 = 'blk1: {
31+
match state1 {
32+
State1A => {
33+
#[loop_match]
34+
loop {
35+
state2 = 'blk2: {
36+
match state2 {
37+
State2X => {
38+
break 'blk2 {
39+
//~^ WARN this labeled break expression is easy to confuse with an unlabeled break
40+
if first {
41+
'blk2: {
42+
//~^ WARN label name `'blk2` shadows a label name that is already in scope
43+
match state2 {
44+
State2X => {
45+
#[const_continue]
46+
break 'blk2 State2::Z;
47+
//~^ ERROR a `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
48+
}
49+
}
50+
};
51+
}
52+
break 'blk2 State2::X;
53+
};
54+
}
55+
}
56+
}
57+
}
58+
}
59+
}
60+
}
61+
}
62+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
warning: label name `'blk2` shadows a label name that is already in scope
2+
--> $DIR/const-continue-outside-loop-match.rs:41:45
3+
|
4+
LL | state2 = 'blk2: {
5+
| ----- first declared here
6+
...
7+
LL | 'blk2: {
8+
| ^^^^^ label `'blk2` already in scope
9+
10+
warning: this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression
11+
--> $DIR/const-continue-outside-loop-match.rs:38:37
12+
|
13+
LL | / ... break 'blk2 {
14+
LL | | ...
15+
LL | | ... if first {
16+
LL | | ... 'blk2: {
17+
... |
18+
LL | | ... break 'blk2 State2::X;
19+
LL | | ... };
20+
| |_______________________^
21+
|
22+
= note: `#[warn(break_with_label_and_loop)]` on by default
23+
help: wrap this expression in parentheses
24+
|
25+
LL ~ break 'blk2 ({
26+
LL |
27+
...
28+
LL | break 'blk2 State2::X;
29+
LL ~ });
30+
|
31+
32+
error[E0695]: unlabeled `break` inside of a labeled block
33+
--> $DIR/const-continue-outside-loop-match.rs:12:19
34+
|
35+
LL | break break;
36+
| ^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label
37+
38+
error[E0695]: unlabeled `break` inside of a labeled block
39+
--> $DIR/const-continue-outside-loop-match.rs:12:13
40+
|
41+
LL | break break;
42+
| ^^^^^^^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label
43+
44+
error: a `#[const_continue]` can only occur in a `#[loop_match]` loop
45+
--> $DIR/const-continue-outside-loop-match.rs:12:13
46+
|
47+
LL | break break;
48+
| ^^^^^^^^^^^
49+
50+
error: a `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
51+
--> $DIR/const-continue-outside-loop-match.rs:46:57
52+
|
53+
LL | ... break 'blk2 State2::Z;
54+
| ^^^^^^^^^^^^^^^^^^^^^
55+
56+
error: aborting due to 4 previous errors; 2 warnings emitted
57+
58+
For more information about this error, try `rustc --explain E0695`.

tests/ui/loop-match/invalid.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,23 @@ fn break_without_value_unit() {
142142
}
143143
}
144144

145+
fn break_without_label() {
146+
let mut state = State::A;
147+
#[loop_match]
148+
'a: loop {
149+
state = 'blk: {
150+
match state {
151+
_ => {
152+
#[const_continue]
153+
break State::A;
154+
//~^ ERROR unlabeled `break` inside of a labeled block
155+
//~| ERROR mismatched types
156+
}
157+
}
158+
}
159+
}
160+
}
161+
145162
fn arm_has_guard(cond: bool) {
146163
let mut state = State::A;
147164
#[loop_match]

tests/ui/loop-match/invalid.stderr

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ help: give the `break` a value of the expected type
99
LL | break 'blk /* value */;
1010
| +++++++++++
1111

12+
error[E0695]: unlabeled `break` inside of a labeled block
13+
--> $DIR/invalid.rs:153:21
14+
|
15+
LL | break State::A;
16+
| ^^^^^^^^^^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label
17+
18+
error[E0308]: mismatched types
19+
--> $DIR/invalid.rs:153:27
20+
|
21+
LL | break State::A;
22+
| ^^^^^^^^ expected `()`, found `State`
23+
1224
error: invalid update of the `#[loop_match]` state
1325
--> $DIR/invalid.rs:18:9
1426
|
@@ -81,13 +93,13 @@ LL | break 'blk;
8193
| ^^^^^^^^^^
8294

8395
error: match arms that are part of a `#[loop_match]` cannot have guards
84-
--> $DIR/invalid.rs:155:29
96+
--> $DIR/invalid.rs:172:29
8597
|
8698
LL | State::B if cond => break 'a,
8799
| ^^^^
88100

89101
error[E0004]: non-exhaustive patterns: `State::B` and `State::C` not covered
90-
--> $DIR/invalid.rs:168:19
102+
--> $DIR/invalid.rs:185:19
91103
|
92104
LL | match state {
93105
| ^^^^^ patterns `State::B` and `State::C` not covered
@@ -110,12 +122,12 @@ LL ~ State::B | State::C => todo!(),
110122
|
111123

112124
error[E0579]: lower range bound must be less than upper
113-
--> $DIR/invalid.rs:185:17
125+
--> $DIR/invalid.rs:202:17
114126
|
115127
LL | 4.0..3.0 => {
116128
| ^^^^^^^^
117129

118-
error: aborting due to 14 previous errors
130+
error: aborting due to 16 previous errors
119131

120-
Some errors have detailed explanations: E0004, E0308, E0579.
132+
Some errors have detailed explanations: E0004, E0308, E0579, E0695.
121133
For more information about an error, try `rustc --explain E0004`.

0 commit comments

Comments
 (0)