@@ -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_id:: { LocalDefId , LocalModDefId } ;
78use rustc_hir:: intravisit:: { self , Visitor } ;
@@ -11,11 +12,12 @@ use rustc_middle::query::Providers;
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 , LoopMatchBadRhs , LoopMatchBadStatements , LoopMatchMissingAssignment ,
20+ OutsideLoop , OutsideLoopSuggestion , UnlabeledCfInWhileCondition , 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+ /// #[loop_match] loop { state = 'label: { /* ... */ } }
43+ LoopMatch {
44+ /// The label of the labeled block (so not the loop!)
45+ labeled_block : Label ,
46+ } ,
4047}
4148
4249#[ derive( Clone ) ]
@@ -160,7 +167,12 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
160167 }
161168 }
162169 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) ) ;
164176 }
165177 hir:: ExprKind :: Closure ( & hir:: Closure {
166178 ref fn_decl, body, fn_decl_span, kind, ..
@@ -216,6 +228,22 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
216228 Err ( hir:: LoopIdError :: UnresolvedLabel ) => None ,
217229 } ;
218230
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+
219247 if let Some ( Node :: Block ( _) ) = loop_id. map ( |id| self . tcx . hir_node ( id) ) {
220248 return ;
221249 }
@@ -318,7 +346,7 @@ impl<'hir> CheckLoopVisitor<'hir> {
318346 cx_pos : usize ,
319347 ) {
320348 match self . cx_stack [ cx_pos] {
321- LabeledBlock | Loop ( _) => { }
349+ LabeledBlock | Loop ( _) | LoopMatch { .. } => { }
322350 Closure ( closure_span) => {
323351 self . tcx . dcx ( ) . emit_err ( BreakInsideClosure {
324352 span,
@@ -399,4 +427,55 @@ impl<'hir> CheckLoopVisitor<'hir> {
399427 } ) ;
400428 }
401429 }
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+ }
402481}
0 commit comments