@@ -2,6 +2,7 @@ use std::collections::BTreeMap;
22use std:: fmt;
33
44use Context :: * ;
5+ use rustc_ast:: Label ;
56use rustc_hir as hir;
67use rustc_hir:: def:: DefKind ;
78use rustc_hir:: def_id:: LocalDefId ;
@@ -11,11 +12,12 @@ use rustc_middle::hir::nested_filter;
1112use rustc_middle:: span_bug;
1213use rustc_middle:: ty:: TyCtxt ;
1314use rustc_span:: hygiene:: DesugaringKind ;
14- use rustc_span:: { BytePos , Span } ;
15+ use rustc_span:: { BytePos , Span , sym } ;
1516
1617use crate :: errors:: {
17- BreakInsideClosure , BreakInsideCoroutine , BreakNonLoop , ContinueLabeledBlock , OutsideLoop ,
18- OutsideLoopSuggestion , UnlabeledCfInWhileCondition , UnlabeledInLabeledBlock ,
18+ BreakInsideClosure , BreakInsideCoroutine , BreakNonLoop , ConstContinueBadLabel ,
19+ ContinueLabeledBlock , OutsideLoop , OutsideLoopSuggestion , UnlabeledCfInWhileCondition ,
20+ UnlabeledInLabeledBlock ,
1921} ;
2022
2123/// The context in which a block is encountered.
@@ -37,6 +39,11 @@ enum Context {
3739 AnonConst ,
3840 /// E.g. `const { ... }`.
3941 ConstBlock ,
42+ /// E.g. `#[loop_match] loop { state = 'label: { /* ... */ } }`.
43+ LoopMatch {
44+ /// The label of the labeled block (not of the loop itself).
45+ labeled_block : Label ,
46+ } ,
4047}
4148
4249#[ derive( Clone ) ]
@@ -141,7 +148,12 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
141148 }
142149 }
143150 hir:: ExprKind :: Loop ( ref b, _, source, _) => {
144- self . with_context ( Loop ( source) , |v| v. visit_block ( b) ) ;
151+ let cx = match self . is_loop_match ( e, b) {
152+ Some ( labeled_block) => LoopMatch { labeled_block } ,
153+ None => Loop ( source) ,
154+ } ;
155+
156+ self . with_context ( cx, |v| v. visit_block ( b) ) ;
145157 }
146158 hir:: ExprKind :: Closure ( & hir:: Closure {
147159 ref fn_decl, body, fn_decl_span, kind, ..
@@ -197,6 +209,24 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
197209 Err ( hir:: LoopIdError :: UnresolvedLabel ) => None ,
198210 } ;
199211
212+ // A `#[const_continue]` must break to a block in a `#[loop_match]`.
213+ let attrs = self . tcx . hir_attrs ( e. hir_id ) ;
214+ if attrs. iter ( ) . any ( |attr| attr. has_name ( sym:: const_continue) ) {
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 !self . tcx . hir_attrs ( e. hir_id ) . iter ( ) . any ( |attr| attr. has_name ( sym:: loop_match) ) {
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