Skip to content

Commit 3686dcc

Browse files
committed
loop match: error on #[const_continue] outside #[loop_match]
1 parent 29a5872 commit 3686dcc

File tree

12 files changed

+130
-36
lines changed

12 files changed

+130
-36
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,9 +1890,9 @@ dependencies = [
18901890

18911891
[[package]]
18921892
name = "ipc-channel"
1893-
version = "0.20.0"
1893+
version = "0.20.1"
18941894
source = "registry+https://github.com/rust-lang/crates.io-index"
1895-
checksum = "5b1c98b70019c830a1fc39cecfe1f60ff99c4122f0a189697c810c90ec545c14"
1895+
checksum = "1700f6b8b9f00cdd675f32fbb3a5be882213140dfe045805273221ca266c43f8"
18961896
dependencies = [
18971897
"bincode",
18981898
"crossbeam-channel",

compiler/rustc_hir/src/hir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3000,7 +3000,7 @@ impl fmt::Display for LoopIdError {
30003000
}
30013001
}
30023002

3003-
#[derive(Copy, Clone, Debug, HashStable_Generic)]
3003+
#[derive(Copy, Clone, Debug, PartialEq, HashStable_Generic)]
30043004
pub struct Destination {
30053005
/// This is `Some(_)` iff there is an explicit user-specified 'label
30063006
pub label: Option<Label>,

compiler/rustc_hir_typeck/src/loops.rs

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use std::collections::BTreeMap;
22
use std::fmt;
33

44
use Context::*;
5-
use rustc_ast::Label;
65
use rustc_attr_data_structures::{AttributeKind, find_attr};
76
use rustc_hir as hir;
87
use rustc_hir::def::DefKind;
@@ -42,8 +41,8 @@ enum Context {
4241
ConstBlock,
4342
/// E.g. `#[loop_match] loop { state = 'label: { /* ... */ } }`.
4443
LoopMatch {
45-
/// The label of the labeled block (not of the loop itself).
46-
labeled_block: Label,
44+
/// The destination pointing to the labeled block (not to the loop itself).
45+
labeled_block: Destination,
4746
},
4847
}
4948

@@ -186,18 +185,18 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
186185
{
187186
self.with_context(UnlabeledBlock(b.span.shrink_to_lo()), |v| v.visit_block(b));
188187
}
189-
hir::ExprKind::Break(break_label, ref opt_expr) => {
188+
hir::ExprKind::Break(break_destination, ref opt_expr) => {
190189
if let Some(e) = opt_expr {
191190
self.visit_expr(e);
192191
}
193192

194-
if self.require_label_in_labeled_block(e.span, &break_label, "break") {
193+
if self.require_label_in_labeled_block(e.span, &break_destination, "break") {
195194
// If we emitted an error about an unlabeled break in a labeled
196195
// block, we don't need any further checking for this break any more
197196
return;
198197
}
199198

200-
let loop_id = match break_label.target_id {
199+
let loop_id = match break_destination.target_id {
201200
Ok(loop_id) => Some(loop_id),
202201
Err(hir::LoopIdError::OutsideLoopScope) => None,
203202
Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
@@ -212,18 +211,25 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
212211

213212
// A `#[const_continue]` must break to a block in a `#[loop_match]`.
214213
if find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::ConstContinue(_)) {
215-
if let Some(break_label) = break_label.label {
216-
let is_target_label = |cx: &Context| match cx {
217-
Context::LoopMatch { labeled_block } => {
218-
break_label.ident.name == labeled_block.ident.name
219-
}
220-
_ => false,
221-
};
214+
let Some(label) = break_destination.label else {
215+
let span = e.span;
216+
self.tcx.dcx().emit_fatal(ConstContinueBadLabel { span });
217+
};
222218

223-
if !self.cx_stack.iter().rev().any(is_target_label) {
224-
let span = break_label.ident.span;
225-
self.tcx.dcx().emit_fatal(ConstContinueBadLabel { span });
219+
let is_target_label = |cx: &Context| match cx {
220+
Context::LoopMatch { labeled_block } => {
221+
// NOTE: with macro expansion, the label's span might be different here
222+
// even though it does still refer to the same HIR node. A block
223+
// can't have two labels, so the hir_id is a unique identifier.
224+
assert!(labeled_block.target_id.is_ok()); // see `is_loop_match`.
225+
break_destination.target_id == labeled_block.target_id
226226
}
227+
_ => false,
228+
};
229+
230+
if !self.cx_stack.iter().rev().any(is_target_label) {
231+
let span = label.ident.span;
232+
self.tcx.dcx().emit_fatal(ConstContinueBadLabel { span });
227233
}
228234
}
229235

@@ -249,7 +255,7 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
249255
Some(kind) => {
250256
let suggestion = format!(
251257
"break{}",
252-
break_label
258+
break_destination
253259
.label
254260
.map_or_else(String::new, |l| format!(" {}", l.ident))
255261
);
@@ -259,7 +265,7 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
259265
kind: kind.name(),
260266
suggestion,
261267
loop_label,
262-
break_label: break_label.label,
268+
break_label: break_destination.label,
263269
break_expr_kind: &break_expr.kind,
264270
break_expr_span: break_expr.span,
265271
});
@@ -268,7 +274,7 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
268274
}
269275

270276
let sp_lo = e.span.with_lo(e.span.lo() + BytePos("break".len() as u32));
271-
let label_sp = match break_label.label {
277+
let label_sp = match break_destination.label {
272278
Some(label) => sp_lo.with_hi(label.ident.span.hi()),
273279
None => sp_lo.shrink_to_lo(),
274280
};
@@ -416,7 +422,7 @@ impl<'hir> CheckLoopVisitor<'hir> {
416422
&self,
417423
e: &'hir hir::Expr<'hir>,
418424
body: &'hir hir::Block<'hir>,
419-
) -> Option<Label> {
425+
) -> Option<Destination> {
420426
if !find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::LoopMatch(_)) {
421427
return None;
422428
}
@@ -438,8 +444,8 @@ impl<'hir> CheckLoopVisitor<'hir> {
438444

439445
let hir::ExprKind::Assign(_, rhs_expr, _) = loop_body_expr.kind else { return None };
440446

441-
let hir::ExprKind::Block(_, label) = rhs_expr.kind else { return None };
447+
let hir::ExprKind::Block(block, label) = rhs_expr.kind else { return None };
442448

443-
label
449+
Some(Destination { label, target_id: Ok(block.hir_id) })
444450
}
445451
}

compiler/rustc_mir_build/messages.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ 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_missing_value = a `#[const_continue]` must break to a label with a value
91+
mir_build_const_continue_missing_label_or_value = a `#[const_continue]` must break to a label with a value
9292
9393
mir_build_const_continue_unknown_jump_target = the target of this `#[const_continue]` is not statically known
9494
.label = this value must be a literal or a monomorphic const

compiler/rustc_mir_build/src/errors.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,8 +1222,8 @@ pub(crate) struct ConstContinueBadConst {
12221222
}
12231223

12241224
#[derive(Diagnostic)]
1225-
#[diag(mir_build_const_continue_missing_value)]
1226-
pub(crate) struct ConstContinueMissingValue {
1225+
#[diag(mir_build_const_continue_missing_label_or_value)]
1226+
pub(crate) struct ConstContinueMissingLabelOrValue {
12271227
#[primary_span]
12281228
pub span: Span,
12291229
}

compiler/rustc_mir_build/src/thir/cx/expr.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -851,9 +851,9 @@ impl<'tcx> ThirBuildCx<'tcx> {
851851
if find_attr!(self.tcx.hir_attrs(expr.hir_id), AttributeKind::ConstContinue(_)) {
852852
match dest.target_id {
853853
Ok(target_id) => {
854-
let Some(value) = value else {
854+
let (Some(value), Some(_)) = (value, dest.label) else {
855855
let span = expr.span;
856-
self.tcx.dcx().emit_fatal(ConstContinueMissingValue { span })
856+
self.tcx.dcx().emit_fatal(ConstContinueMissingLabelOrValue { span })
857857
};
858858

859859
ExprKind::ConstContinue {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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 main() {
8+
loop {
9+
#[const_continue]
10+
break ();
11+
//~^ ERROR `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
12+
}
13+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
2+
--> $DIR/const-continue-outside-loop-match.rs:10:9
3+
|
4+
LL | break ();
5+
| ^^^^^^^^
6+
7+
error: aborting due to 1 previous error
8+

tests/ui/loop-match/const-continue-to-block.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,24 @@ fn const_continue_to_block() -> u8 {
2424
}
2525
}
2626
}
27+
28+
fn const_continue_to_shadowed_block() -> u8 {
29+
let state = 0;
30+
#[loop_match]
31+
loop {
32+
state = 'blk: {
33+
match state {
34+
0 => {
35+
#[const_continue]
36+
break 'blk 1;
37+
}
38+
_ => 'blk: {
39+
//~^ WARN label name `'blk` shadows a label name that is already in scope
40+
#[const_continue]
41+
break 'blk 2;
42+
//~^ ERROR `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
43+
}
44+
}
45+
}
46+
}
47+
}
Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
1+
warning: label name `'blk` shadows a label name that is already in scope
2+
--> $DIR/const-continue-to-block.rs:38:22
3+
|
4+
LL | state = 'blk: {
5+
| ---- first declared here
6+
...
7+
LL | _ => 'blk: {
8+
| ^^^^ label `'blk` already in scope
9+
110
error: `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
211
--> $DIR/const-continue-to-block.rs:20:27
312
|
413
LL | break 'b 2;
514
| ^^
615

7-
error: aborting due to 1 previous error
16+
error: `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
17+
--> $DIR/const-continue-to-block.rs:41:27
18+
|
19+
LL | break 'blk 2;
20+
| ^^^^
21+
22+
error: aborting due to 2 previous errors; 1 warning emitted
823

0 commit comments

Comments
 (0)