@@ -2,6 +2,7 @@ use std::collections::BTreeMap;
2
2
use std:: fmt;
3
3
4
4
use Context :: * ;
5
+ use rustc_ast:: Label ;
5
6
use rustc_hir as hir;
6
7
use rustc_hir:: def_id:: { LocalDefId , LocalModDefId } ;
7
8
use rustc_hir:: intravisit:: { self , Visitor } ;
@@ -11,11 +12,12 @@ use rustc_middle::query::Providers;
11
12
use rustc_middle:: span_bug;
12
13
use rustc_middle:: ty:: TyCtxt ;
13
14
use rustc_span:: hygiene:: DesugaringKind ;
14
- use rustc_span:: { BytePos , Span } ;
15
+ use rustc_span:: { BytePos , Span , sym } ;
15
16
16
17
use crate :: errors:: {
17
- BreakInsideClosure , BreakInsideCoroutine , BreakNonLoop , ContinueLabeledBlock , OutsideLoop ,
18
- OutsideLoopSuggestion , UnlabeledCfInWhileCondition , UnlabeledInLabeledBlock ,
18
+ BreakInsideClosure , BreakInsideCoroutine , BreakNonLoop , ConstContinueBadLabel ,
19
+ ContinueLabeledBlock , LoopMatchBadRhs , LoopMatchBadStatements , LoopMatchMissingAssignment ,
20
+ OutsideLoop , OutsideLoopSuggestion , UnlabeledCfInWhileCondition , UnlabeledInLabeledBlock ,
19
21
} ;
20
22
21
23
/// The context in which a block is encountered.
@@ -37,6 +39,11 @@ enum Context {
37
39
AnonConst ,
38
40
/// E.g. `const { ... }`.
39
41
ConstBlock ,
42
+ /// #[loop_match] loop { state = 'label: { /* ... */ } }
43
+ LoopMatch {
44
+ /// The label of the labeled block (so not the loop!)
45
+ labeled_block : Label ,
46
+ } ,
40
47
}
41
48
42
49
#[ derive( Clone ) ]
@@ -160,7 +167,12 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
160
167
}
161
168
}
162
169
hir:: ExprKind :: Loop ( ref b, _, source, _) => {
163
- self . with_context ( Loop ( source) , |v| v. visit_block ( b) ) ;
170
+ let cx = match self . is_loop_match ( e, b) {
171
+ Some ( labeled_block) => LoopMatch { labeled_block } ,
172
+ None => Loop ( source) ,
173
+ } ;
174
+
175
+ self . with_context ( cx, |v| v. visit_block ( b) ) ;
164
176
}
165
177
hir:: ExprKind :: Closure ( & hir:: Closure {
166
178
ref fn_decl, body, fn_decl_span, kind, ..
@@ -216,6 +228,22 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
216
228
Err ( hir:: LoopIdError :: UnresolvedLabel ) => None ,
217
229
} ;
218
230
231
+ // a #[const_continue] must be to a block that participates in #[loop_match]
232
+ let attrs = self . tcx . hir_attrs ( e. hir_id ) ;
233
+ if attrs. iter ( ) . any ( |attr| attr. has_name ( sym:: const_continue) ) {
234
+ if let Some ( break_label) = break_label. label {
235
+ let is_target_label = |cx : & Context | match cx {
236
+ Context :: LoopMatch { labeled_block } => break_label == * labeled_block,
237
+ _ => false ,
238
+ } ;
239
+
240
+ if !self . cx_stack . iter ( ) . rev ( ) . any ( is_target_label) {
241
+ let span = break_label. ident . span ;
242
+ self . tcx . dcx ( ) . emit_fatal ( ConstContinueBadLabel { span } ) ;
243
+ }
244
+ }
245
+ }
246
+
219
247
if let Some ( Node :: Block ( _) ) = loop_id. map ( |id| self . tcx . hir_node ( id) ) {
220
248
return ;
221
249
}
@@ -318,7 +346,7 @@ impl<'hir> CheckLoopVisitor<'hir> {
318
346
cx_pos : usize ,
319
347
) {
320
348
match self . cx_stack [ cx_pos] {
321
- LabeledBlock | Loop ( _) => { }
349
+ LabeledBlock | Loop ( _) | LoopMatch { .. } => { }
322
350
Closure ( closure_span) => {
323
351
self . tcx . dcx ( ) . emit_err ( BreakInsideClosure {
324
352
span,
@@ -399,4 +427,55 @@ impl<'hir> CheckLoopVisitor<'hir> {
399
427
} ) ;
400
428
}
401
429
}
430
+
431
+ /// Is this a loop annotated with #[loop_match] that looks syntactically sound?
432
+ fn is_loop_match (
433
+ & self ,
434
+ e : & ' hir hir:: Expr < ' hir > ,
435
+ body : & ' hir hir:: Block < ' hir > ,
436
+ ) -> Option < Label > {
437
+ if !self . tcx . hir_attrs ( e. hir_id ) . iter ( ) . any ( |attr| attr. has_name ( sym:: loop_match) ) {
438
+ return None ;
439
+ }
440
+
441
+ let dcx = self . tcx . dcx ( ) ;
442
+
443
+ // accept either `state = expr` or `state = expr;`
444
+ let loop_body_expr = match body. stmts {
445
+ [ ] => match body. expr {
446
+ Some ( expr) => expr,
447
+ None => {
448
+ dcx. emit_err ( LoopMatchMissingAssignment { span : body. span } ) ;
449
+ return None ;
450
+ }
451
+ } ,
452
+ [ single] if body. expr . is_none ( ) => match single. kind {
453
+ hir:: StmtKind :: Expr ( expr) | hir:: StmtKind :: Semi ( expr) => expr,
454
+ _ => {
455
+ dcx. emit_err ( LoopMatchMissingAssignment { span : body. span } ) ;
456
+ return None ;
457
+ }
458
+ } ,
459
+ [ first @ last] | [ first, .., last] => {
460
+ dcx. emit_err ( LoopMatchBadStatements { span : first. span . to ( last. span ) } ) ;
461
+ return None ;
462
+ }
463
+ } ;
464
+
465
+ let hir:: ExprKind :: Assign ( _, rhs_expr, _) = loop_body_expr. kind else {
466
+ dcx. emit_err ( LoopMatchMissingAssignment { span : loop_body_expr. span } ) ;
467
+ return None ;
468
+ } ;
469
+
470
+ let hir:: ExprKind :: Block ( _, label) = rhs_expr. kind else {
471
+ dcx. emit_err ( LoopMatchBadRhs { span : rhs_expr. span } ) ;
472
+ return None ;
473
+ } ;
474
+
475
+ if label. is_none ( ) {
476
+ todo ! ( )
477
+ }
478
+
479
+ label
480
+ }
402
481
}
0 commit comments