@@ -2,6 +2,8 @@ use std::collections::BTreeMap;
2
2
use std:: fmt;
3
3
4
4
use Context :: * ;
5
+ use rustc_ast:: Label ;
6
+ use rustc_attr_data_structures:: { AttributeKind , find_attr} ;
5
7
use rustc_hir as hir;
6
8
use rustc_hir:: def:: DefKind ;
7
9
use rustc_hir:: def_id:: LocalDefId ;
@@ -14,8 +16,9 @@ use rustc_span::hygiene::DesugaringKind;
14
16
use rustc_span:: { BytePos , Span } ;
15
17
16
18
use crate :: errors:: {
17
- BreakInsideClosure , BreakInsideCoroutine , BreakNonLoop , ContinueLabeledBlock , OutsideLoop ,
18
- OutsideLoopSuggestion , UnlabeledCfInWhileCondition , UnlabeledInLabeledBlock ,
19
+ BreakInsideClosure , BreakInsideCoroutine , BreakNonLoop , ConstContinueBadLabel ,
20
+ ContinueLabeledBlock , OutsideLoop , OutsideLoopSuggestion , UnlabeledCfInWhileCondition ,
21
+ UnlabeledInLabeledBlock ,
19
22
} ;
20
23
21
24
/// The context in which a block is encountered.
@@ -37,6 +40,11 @@ enum Context {
37
40
AnonConst ,
38
41
/// E.g. `const { ... }`.
39
42
ConstBlock ,
43
+ /// E.g. `#[loop_match] loop { state = 'label: { /* ... */ } }`.
44
+ LoopMatch {
45
+ /// The label of the labeled block (not of the loop itself).
46
+ labeled_block : Label ,
47
+ } ,
40
48
}
41
49
42
50
#[ derive( Clone ) ]
@@ -141,7 +149,12 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
141
149
}
142
150
}
143
151
hir:: ExprKind :: Loop ( ref b, _, source, _) => {
144
- self . with_context ( Loop ( source) , |v| v. visit_block ( b) ) ;
152
+ let cx = match self . is_loop_match ( e, b) {
153
+ Some ( labeled_block) => LoopMatch { labeled_block } ,
154
+ None => Loop ( source) ,
155
+ } ;
156
+
157
+ self . with_context ( cx, |v| v. visit_block ( b) ) ;
145
158
}
146
159
hir:: ExprKind :: Closure ( & hir:: Closure {
147
160
ref fn_decl, body, fn_decl_span, kind, ..
@@ -197,6 +210,23 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
197
210
Err ( hir:: LoopIdError :: UnresolvedLabel ) => None ,
198
211
} ;
199
212
213
+ // A `#[const_continue]` must break to a block in a `#[loop_match]`.
214
+ 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
+ } ;
222
+
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 } ) ;
226
+ }
227
+ }
228
+ }
229
+
200
230
if let Some ( Node :: Block ( _) ) = loop_id. map ( |id| self . tcx . hir_node ( id) ) {
201
231
return ;
202
232
}
@@ -299,7 +329,7 @@ impl<'hir> CheckLoopVisitor<'hir> {
299
329
cx_pos : usize ,
300
330
) {
301
331
match self . cx_stack [ cx_pos] {
302
- LabeledBlock | Loop ( _) => { }
332
+ LabeledBlock | Loop ( _) | LoopMatch { .. } => { }
303
333
Closure ( closure_span) => {
304
334
self . tcx . dcx ( ) . emit_err ( BreakInsideClosure {
305
335
span,
@@ -380,4 +410,36 @@ impl<'hir> CheckLoopVisitor<'hir> {
380
410
} ) ;
381
411
}
382
412
}
413
+
414
+ /// Is this a loop annotated with `#[loop_match]` that looks syntactically sound?
415
+ fn is_loop_match (
416
+ & self ,
417
+ e : & ' hir hir:: Expr < ' hir > ,
418
+ body : & ' hir hir:: Block < ' hir > ,
419
+ ) -> Option < Label > {
420
+ if !find_attr ! ( self . tcx. hir_attrs( e. hir_id) , AttributeKind :: LoopMatch ( _) ) {
421
+ return None ;
422
+ }
423
+
424
+ // NOTE: Diagnostics are emitted during MIR construction.
425
+
426
+ // Accept either `state = expr` or `state = expr;`.
427
+ let loop_body_expr = match body. stmts {
428
+ [ ] => match body. expr {
429
+ Some ( expr) => expr,
430
+ None => return None ,
431
+ } ,
432
+ [ single] if body. expr . is_none ( ) => match single. kind {
433
+ hir:: StmtKind :: Expr ( expr) | hir:: StmtKind :: Semi ( expr) => expr,
434
+ _ => return None ,
435
+ } ,
436
+ [ ..] => return None ,
437
+ } ;
438
+
439
+ let hir:: ExprKind :: Assign ( _, rhs_expr, _) = loop_body_expr. kind else { return None } ;
440
+
441
+ let hir:: ExprKind :: Block ( _, label) = rhs_expr. kind else { return None } ;
442
+
443
+ label
444
+ }
383
445
}
0 commit comments