@@ -2,6 +2,8 @@ use std::collections::BTreeMap;
22use std:: fmt;
33
44use Context :: * ;
5+ use rustc_ast:: Label ;
6+ use rustc_attr_data_structures:: { AttributeKind , find_attr} ;
57use rustc_hir as hir;
68use rustc_hir:: def:: DefKind ;
79use rustc_hir:: def_id:: LocalDefId ;
@@ -14,8 +16,9 @@ use rustc_span::hygiene::DesugaringKind;
1416use rustc_span:: { BytePos , Span } ;
1517
1618use crate :: errors:: {
17- BreakInsideClosure , BreakInsideCoroutine , BreakNonLoop , ContinueLabeledBlock , OutsideLoop ,
18- OutsideLoopSuggestion , UnlabeledCfInWhileCondition , UnlabeledInLabeledBlock ,
19+ BreakInsideClosure , BreakInsideCoroutine , BreakNonLoop , ConstContinueBadLabel ,
20+ ContinueLabeledBlock , OutsideLoop , OutsideLoopSuggestion , UnlabeledCfInWhileCondition ,
21+ UnlabeledInLabeledBlock ,
1922} ;
2023
2124/// The context in which a block is encountered.
@@ -37,6 +40,11 @@ enum Context {
3740 AnonConst ,
3841 /// E.g. `const { ... }`.
3942 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+ } ,
4048}
4149
4250#[ derive( Clone ) ]
@@ -141,7 +149,12 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
141149 }
142150 }
143151 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) ) ;
145158 }
146159 hir:: ExprKind :: Closure ( & hir:: Closure {
147160 ref fn_decl, body, fn_decl_span, kind, ..
@@ -197,6 +210,23 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
197210 Err ( hir:: LoopIdError :: UnresolvedLabel ) => None ,
198211 } ;
199212
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+
200230 if let Some ( Node :: Block ( _) ) = loop_id. map ( |id| self . tcx . hir_node ( id) ) {
201231 return ;
202232 }
@@ -299,7 +329,7 @@ impl<'hir> CheckLoopVisitor<'hir> {
299329 cx_pos : usize ,
300330 ) {
301331 match self . cx_stack [ cx_pos] {
302- LabeledBlock | Loop ( _) => { }
332+ LabeledBlock | Loop ( _) | LoopMatch { .. } => { }
303333 Closure ( closure_span) => {
304334 self . tcx . dcx ( ) . emit_err ( BreakInsideClosure {
305335 span,
@@ -380,4 +410,36 @@ impl<'hir> CheckLoopVisitor<'hir> {
380410 } ) ;
381411 }
382412 }
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+ }
383445}
0 commit comments