llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-codegen

Author: None (Sirraide)

<details>
<summary>Changes</summary>

This implements support for [named 
loops](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3355.htm) for C2y. We 
also make them available in earlier language modes and in C++ as an extension, 
with support for templates and constant evaluation (though I haven’t added 
support for it to the experimental interpreter because this patch is already 
quite long and I’m not too familiar w/ it candidly).

The basic approach is that we treat `break label` and `continue label` more or 
less like `goto label`; that is, we defer all the checking to 
JumpDiagnostics.cpp. This *does* mean that we run the jump checker on the 
entire function if it contains a labeled break/continue. The alternative from 
what I can tell would have been to create the loop statements early before we 
parse their body; that’s what I tried originally, but it was shaping up to be 
quite the large refactor. Also, breaking out of a nested loop isn’t that common 
anyway, so I don’t think running the jump checker on the entire function is 
really that big of a deal because of that.

@<!-- -->AaronBallman mentioned that it would be nice to have a link from e.g. 
a WhileStmt back to any label(s) naming it for AST matchers; I haven’t 
implemented anything to facilitate that yet, but my idea for that would be to 
add a `LabelDecl*` to `WhileStmt`, `ForStmt`, etc. (basically anything that can 
be named by a label), and then when we are done parsing a `LabelStmt`, we check 
if its child is e.g. a `WhileStmt` (skipping any intervening `LabelStmt`s), and 
if so, we store that statement in that `WhileStmt`. That is, if we have `a: b: 
c: while`, then we’d ultimately store the decl for the outermost label (that 
being `a` here) in the `WhileStmt`; the remaining labels can be obtained by 
walking the AST. 

Unfortunately, that does mean that every `WhileStmt` etc. would need to have an 
extra `Decl*`, which would be `nullptr` in the overwhelming majority of cases. 
I guess we could try and sort of pass a `IsNamedByLabel` flag down throughout 
the parser when we start parsing a `LabelStmt`, which might let us put it in 
the trailing data where it can just be omitted entirely if there is no label, 
but I haven’t thought this through in any amount of detail.

Alternatively, we can just not support that and require people to write 
`labelStmt(hasName("b"), whileLoop())` or whatever (I candidly don’t know the 
proper AST matcher syntax for that because I basically never use AST matchers). 
Aaron pointed out that that doesn’t work if there are multiple labels (e.g. if 
we’re trying to match a loop w/ the name `b` in `a: b: c: while`), but I’m not 
sure whether multiple labels in a row naming the same statement is really a use 
case we care to support.

---

Patch is 105.90 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/152870.diff


44 Files Affected:

- (modified) clang-tools-extra/clangd/XRefs.cpp (+2-2) 
- (modified) clang/docs/LanguageExtensions.rst (+1) 
- (modified) clang/docs/ReleaseNotes.rst (+2) 
- (modified) clang/include/clang/AST/JSONNodeDumper.h (+1) 
- (modified) clang/include/clang/AST/Stmt.h (+66-47) 
- (modified) clang/include/clang/AST/TextNodeDumper.h (+1) 
- (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+8) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+5) 
- (modified) clang/include/clang/Basic/StmtNodes.td (+5-2) 
- (modified) clang/include/clang/Parse/Parser.h (+4) 
- (modified) clang/include/clang/Sema/ScopeInfo.h (+10-3) 
- (modified) clang/include/clang/Sema/Sema.h (+4-2) 
- (modified) clang/lib/AST/ASTImporter.cpp (+18-8) 
- (modified) clang/lib/AST/ExprConstant.cpp (+66-17) 
- (modified) clang/lib/AST/JSONNodeDumper.cpp (+7) 
- (modified) clang/lib/AST/Stmt.cpp (+7) 
- (modified) clang/lib/AST/StmtPrinter.cpp (+11-2) 
- (modified) clang/lib/AST/TextNodeDumper.cpp (+20) 
- (modified) clang/lib/CodeGen/CGObjC.cpp (+1-1) 
- (modified) clang/lib/CodeGen/CGStmt.cpp (+21-7) 
- (modified) clang/lib/CodeGen/CGStmtOpenMP.cpp (+3-3) 
- (modified) clang/lib/CodeGen/CodeGenFunction.h (+6-2) 
- (modified) clang/lib/Parse/ParseStmt.cpp (+20-4) 
- (modified) clang/lib/Sema/JumpDiagnostics.cpp (+140-26) 
- (modified) clang/lib/Sema/SemaStmt.cpp (+28-6) 
- (modified) clang/lib/Sema/TreeTransform.h (+20-2) 
- (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+10-5) 
- (modified) clang/lib/Serialization/ASTWriterStmt.cpp (+12-4) 
- (modified) clang/lib/Tooling/Syntax/BuildTree.cpp (+2-4) 
- (added) clang/test/AST/ast-dump-labeled-break-continue-json.c (+326) 
- (added) clang/test/AST/ast-dump-labeled-break-continue.c (+41) 
- (added) clang/test/AST/ast-print-labeled-break-continue.c (+29) 
- (added) clang/test/CodeGen/labeled-break-continue.c (+281) 
- (added) clang/test/CodeGenCXX/labeled-break-continue.cpp (+221) 
- (added) clang/test/CodeGenObjC/labeled-break-continue.m (+174) 
- (modified) clang/test/OpenMP/for_loop_messages.cpp (+20) 
- (added) clang/test/Parser/labeled-break-continue.c (+10) 
- (modified) clang/test/Sema/__try.c (+16) 
- (added) clang/test/Sema/labeled-break-continue.c (+146) 
- (added) clang/test/SemaCXX/labeled-break-continue-constexpr.cpp (+169) 
- (added) clang/test/SemaCXX/labeled-break-continue.cpp (+51) 
- (added) clang/test/SemaObjC/labeled-break-continue.m (+39) 
- (modified) clang/test/SemaOpenACC/no-branch-in-out.c (+23) 
- (modified) clang/www/c_status.html (+1-1) 


``````````diff
diff --git a/clang-tools-extra/clangd/XRefs.cpp 
b/clang-tools-extra/clangd/XRefs.cpp
index 83a8b7289aec3..a98b17bd33475 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -1106,11 +1106,11 @@ class FindControlFlow : public 
RecursiveASTVisitor<FindControlFlow> {
     return true;
   }
   bool VisitBreakStmt(BreakStmt *B) {
-    found(Break, B->getBreakLoc());
+    found(Break, B->getKwLoc());
     return true;
   }
   bool VisitContinueStmt(ContinueStmt *C) {
-    found(Continue, C->getContinueLoc());
+    found(Continue, C->getKwLoc());
     return true;
   }
   bool VisitSwitchCase(SwitchCase *C) {
diff --git a/clang/docs/LanguageExtensions.rst 
b/clang/docs/LanguageExtensions.rst
index b5bb198ca637a..7949a6adfb5f6 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1709,6 +1709,7 @@ Attributes (N2335)                                        
                     C
 ``#embed`` (N3017)                                                             
C23           C89, C++
 Octal literals prefixed with ``0o`` or ``0O``                                  
C2y           C89, C++
 ``_Countof`` (N3369, N3469)                                                    
C2y           C89
+Named Loops (N3355)                                                            
C2y           C89, C++
 ============================================= ================================ 
============= =============
 
 Builtin type aliases
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0e9fcaa5fac6a..4d8ea6a15e30b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -103,6 +103,8 @@ C Language Changes
 
 C2y Feature Support
 ^^^^^^^^^^^^^^^^^^^
+- Clang now supports `N3355 
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3355.htm>`_ Named Loops. 
This feature
+  is also available in earlier language modes and in C++ as an extension.
 
 C23 Feature Support
 ^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/JSONNodeDumper.h 
b/clang/include/clang/AST/JSONNodeDumper.h
index 570662b58ccf0..1c0467a45b36a 100644
--- a/clang/include/clang/AST/JSONNodeDumper.h
+++ b/clang/include/clang/AST/JSONNodeDumper.h
@@ -334,6 +334,7 @@ class JSONNodeDumper
   void VisitStringLiteral(const StringLiteral *SL);
   void VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *BLE);
 
+  void VisitLoopControlStmt(const LoopControlStmt *LS);
   void VisitIfStmt(const IfStmt *IS);
   void VisitSwitchStmt(const SwitchStmt *SS);
   void VisitCaseStmt(const CaseStmt *CS);
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index a5b0d5053003f..45930eef4f91c 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -277,24 +277,14 @@ class alignas(void *) Stmt {
     SourceLocation GotoLoc;
   };
 
-  class ContinueStmtBitfields {
-    friend class ContinueStmt;
+  class LoopControlStmtBitfields {
+    friend class LoopControlStmt;
 
     LLVM_PREFERRED_TYPE(StmtBitfields)
     unsigned : NumStmtBits;
 
-    /// The location of the "continue".
-    SourceLocation ContinueLoc;
-  };
-
-  class BreakStmtBitfields {
-    friend class BreakStmt;
-
-    LLVM_PREFERRED_TYPE(StmtBitfields)
-    unsigned : NumStmtBits;
-
-    /// The location of the "break".
-    SourceLocation BreakLoc;
+    /// The location of the "continue"/"break".
+    SourceLocation KwLoc;
   };
 
   class ReturnStmtBitfields {
@@ -1325,8 +1315,7 @@ class alignas(void *) Stmt {
     DoStmtBitfields DoStmtBits;
     ForStmtBitfields ForStmtBits;
     GotoStmtBitfields GotoStmtBits;
-    ContinueStmtBitfields ContinueStmtBits;
-    BreakStmtBitfields BreakStmtBits;
+    LoopControlStmtBitfields LoopControlStmtBits;
     ReturnStmtBitfields ReturnStmtBits;
     SwitchCaseBitfields SwitchCaseBits;
 
@@ -3056,25 +3045,42 @@ class IndirectGotoStmt : public Stmt {
   }
 };
 
-/// ContinueStmt - This represents a continue.
-class ContinueStmt : public Stmt {
+/// Base class for BreakStmt and ContinueStmt.
+class LoopControlStmt : public Stmt {
+  /// If this is a labeled break/continue, the label whose statement we're
+  /// targeting.
+  LabelDecl *TargetLabel = nullptr;
+
+  /// Location of the label, if any.
+  SourceLocation Label;
+
+protected:
+  LoopControlStmt(StmtClass Class, SourceLocation Loc) : Stmt(Class) {
+    setKwLoc(Loc);
+  }
+
+  LoopControlStmt(StmtClass Class, EmptyShell ES) : Stmt(Class, ES) {}
+
 public:
-  ContinueStmt(SourceLocation CL) : Stmt(ContinueStmtClass) {
-    setContinueLoc(CL);
+  SourceLocation getKwLoc() const { return LoopControlStmtBits.KwLoc; }
+  void setKwLoc(SourceLocation L) { LoopControlStmtBits.KwLoc = L; }
+
+  SourceLocation getBeginLoc() const { return getKwLoc(); }
+  SourceLocation getEndLoc() const {
+    return isLabeled() ? getLabelLoc() : getKwLoc();
   }
 
-  /// Build an empty continue statement.
-  explicit ContinueStmt(EmptyShell Empty) : Stmt(ContinueStmtClass, Empty) {}
+  bool isLabeled() const { return TargetLabel; }
 
-  SourceLocation getContinueLoc() const { return ContinueStmtBits.ContinueLoc; 
}
-  void setContinueLoc(SourceLocation L) { ContinueStmtBits.ContinueLoc = L; }
+  SourceLocation getLabelLoc() const { return Label; }
+  void setLabelLoc(SourceLocation L) { Label = L; }
 
-  SourceLocation getBeginLoc() const { return getContinueLoc(); }
-  SourceLocation getEndLoc() const { return getContinueLoc(); }
+  LabelDecl *getLabelDecl() const { return TargetLabel; }
+  void setLabelDecl(LabelDecl *S) { TargetLabel = S; }
 
-  static bool classof(const Stmt *T) {
-    return T->getStmtClass() == ContinueStmtClass;
-  }
+  /// If this is a labeled break/continue, get the loop or switch statement
+  /// that this targets.
+  Stmt *getLabelTarget() const;
 
   // Iterators
   child_range children() {
@@ -3084,35 +3090,48 @@ class ContinueStmt : public Stmt {
   const_child_range children() const {
     return const_child_range(const_child_iterator(), const_child_iterator());
   }
+
+  static bool classof(const Stmt *T) {
+    StmtClass Class = T->getStmtClass();
+    return Class == ContinueStmtClass || Class == BreakStmtClass;
+  }
 };
 
-/// BreakStmt - This represents a break.
-class BreakStmt : public Stmt {
+/// ContinueStmt - This represents a continue.
+class ContinueStmt : public LoopControlStmt {
 public:
-  BreakStmt(SourceLocation BL) : Stmt(BreakStmtClass) {
-    setBreakLoc(BL);
+  ContinueStmt(SourceLocation CL) : LoopControlStmt(ContinueStmtClass, CL) {}
+  ContinueStmt(SourceLocation CL, SourceLocation LabelLoc, LabelDecl *Target)
+      : LoopControlStmt(ContinueStmtClass, CL) {
+    setLabelLoc(LabelLoc);
+    setLabelDecl(Target);
   }
 
-  /// Build an empty break statement.
-  explicit BreakStmt(EmptyShell Empty) : Stmt(BreakStmtClass, Empty) {}
-
-  SourceLocation getBreakLoc() const { return BreakStmtBits.BreakLoc; }
-  void setBreakLoc(SourceLocation L) { BreakStmtBits.BreakLoc = L; }
-
-  SourceLocation getBeginLoc() const { return getBreakLoc(); }
-  SourceLocation getEndLoc() const { return getBreakLoc(); }
+  /// Build an empty continue statement.
+  explicit ContinueStmt(EmptyShell Empty)
+      : LoopControlStmt(ContinueStmtClass, Empty) {}
 
   static bool classof(const Stmt *T) {
-    return T->getStmtClass() == BreakStmtClass;
+    return T->getStmtClass() == ContinueStmtClass;
   }
+};
 
-  // Iterators
-  child_range children() {
-    return child_range(child_iterator(), child_iterator());
+/// BreakStmt - This represents a break.
+class BreakStmt : public LoopControlStmt {
+public:
+  BreakStmt(SourceLocation BL) : LoopControlStmt(BreakStmtClass, BL) {}
+  BreakStmt(SourceLocation CL, SourceLocation LabelLoc, LabelDecl *Target)
+      : LoopControlStmt(BreakStmtClass, CL) {
+    setLabelLoc(LabelLoc);
+    setLabelDecl(Target);
   }
 
-  const_child_range children() const {
-    return const_child_range(const_child_iterator(), const_child_iterator());
+  /// Build an empty break statement.
+  explicit BreakStmt(EmptyShell Empty)
+      : LoopControlStmt(BreakStmtClass, Empty) {}
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == BreakStmtClass;
   }
 };
 
diff --git a/clang/include/clang/AST/TextNodeDumper.h 
b/clang/include/clang/AST/TextNodeDumper.h
index 1917a8ac29f05..324d9bc26aae0 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -255,6 +255,7 @@ class TextNodeDumper
   void VisitExpressionTemplateArgument(const TemplateArgument &TA);
   void VisitPackTemplateArgument(const TemplateArgument &TA);
 
+  void VisitLoopControlStmt(const LoopControlStmt *L);
   void VisitIfStmt(const IfStmt *Node);
   void VisitSwitchStmt(const SwitchStmt *Node);
   void VisitWhileStmt(const WhileStmt *Node);
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 0042afccba2c8..6f2498d3bc7c3 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -215,6 +215,14 @@ def warn_c23_compat_case_range : Warning<
   DefaultIgnore, InGroup<CPre2yCompat>;
 def ext_c2y_case_range : Extension<
   "case ranges are a C2y extension">, InGroup<C2y>;
+def warn_c2y_labeled_break_continue
+    : Warning<"labeled %select{'break'|'continue'}0 is incompatible with C "
+              "standards before C2y">,
+      DefaultIgnore,
+      InGroup<CPre2yCompat>;
+def ext_c2y_labeled_break_continue
+    : Extension<"labeled %select{'break'|'continue'}0 is a C2y extension">,
+      InGroup<C2y>;
 
 // Generic errors.
 def err_expected_expression : Error<"expected expression">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index cf23594201143..94647a033d497 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10796,6 +10796,11 @@ def err_continue_not_in_loop : Error<
   "'continue' statement not in loop statement">;
 def err_break_not_in_loop_or_switch : Error<
   "'break' statement not in loop or switch statement">;
+def err_break_continue_label_not_found
+    : Error<"'%select{continue|break}0' label does not name an enclosing "
+            "%select{loop|loop or 'switch'}0">;
+def err_continue_switch
+    : Error<"label of 'continue' refers to a switch statement">;
 def warn_loop_ctrl_binds_to_inner : Warning<
   "'%0' is bound to current loop, GCC binds it to the enclosing loop">,
   InGroup<GccCompat>;
diff --git a/clang/include/clang/Basic/StmtNodes.td 
b/clang/include/clang/Basic/StmtNodes.td
index c9c173f5c7469..046ef4f30e232 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -16,8 +16,6 @@ def DoStmt : StmtNode<Stmt>;
 def ForStmt : StmtNode<Stmt>;
 def GotoStmt : StmtNode<Stmt>;
 def IndirectGotoStmt : StmtNode<Stmt>;
-def ContinueStmt : StmtNode<Stmt>;
-def BreakStmt : StmtNode<Stmt>;
 def ReturnStmt : StmtNode<Stmt>;
 def DeclStmt  : StmtNode<Stmt>;
 def SwitchCase : StmtNode<Stmt, 1>;
@@ -26,6 +24,11 @@ def DefaultStmt : StmtNode<SwitchCase>;
 def CapturedStmt : StmtNode<Stmt>;
 def SYCLKernelCallStmt : StmtNode<Stmt>;
 
+// Break/continue.
+def LoopControlStmt : StmtNode<Stmt, 1>;
+def ContinueStmt : StmtNode<LoopControlStmt>;
+def BreakStmt : StmtNode<LoopControlStmt>;
+
 // Statements that might produce a value (for example, as the last non-null
 // statement in a GNU statement-expression).
 def ValueStmt : StmtNode<Stmt, 1>;
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index e9437e6d46366..7add07c79fc64 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -7458,6 +7458,7 @@ class Parser : public CodeCompletionHandler {
   /// \verbatim
   ///       jump-statement:
   ///         'continue' ';'
+  /// [C2y]   'continue' identifier ';'
   /// \endverbatim
   ///
   /// Note: this lets the caller parse the end ';'.
@@ -7468,6 +7469,7 @@ class Parser : public CodeCompletionHandler {
   /// \verbatim
   ///       jump-statement:
   ///         'break' ';'
+  /// [C2y]   'break' identifier ';'
   /// \endverbatim
   ///
   /// Note: this lets the caller parse the end ';'.
@@ -7484,6 +7486,8 @@ class Parser : public CodeCompletionHandler {
   /// \endverbatim
   StmtResult ParseReturnStatement();
 
+  StmtResult ParseBreakOrContinueStatement(bool IsContinue);
+
   StmtResult ParsePragmaLoopHint(StmtVector &Stmts, ParsedStmtContext StmtCtx,
                                  SourceLocation *TrailingElseLoc,
                                  ParsedAttributes &Attrs);
diff --git a/clang/include/clang/Sema/ScopeInfo.h 
b/clang/include/clang/Sema/ScopeInfo.h
index 94b247a689c2d..2a46edc478591 100644
--- a/clang/include/clang/Sema/ScopeInfo.h
+++ b/clang/include/clang/Sema/ScopeInfo.h
@@ -124,6 +124,9 @@ class FunctionScopeInfo {
   /// Whether this function contains any indirect gotos.
   bool HasIndirectGoto : 1;
 
+  /// Whether this function contains any labeled break or continue statements.
+  bool HasLabeledBreakOrContinue : 1;
+
   /// Whether this function contains any statement marked with
   /// \c [[clang::musttail]].
   bool HasMustTail : 1;
@@ -391,7 +394,8 @@ class FunctionScopeInfo {
 public:
   FunctionScopeInfo(DiagnosticsEngine &Diag)
       : Kind(SK_Function), HasBranchProtectedScope(false),
-        HasBranchIntoScope(false), HasIndirectGoto(false), HasMustTail(false),
+        HasBranchIntoScope(false), HasIndirectGoto(false),
+        HasLabeledBreakOrContinue(false), HasMustTail(false),
         HasDroppedStmt(false), HasOMPDeclareReductionCombiner(false),
         HasFallthroughStmt(false), UsesFPIntrin(false),
         HasPotentialAvailabilityViolations(false), ObjCShouldCallSuper(false),
@@ -436,6 +440,8 @@ class FunctionScopeInfo {
     HasBranchIntoScope = true;
   }
 
+  void setHasLabeledBreakOrContinue() { HasLabeledBreakOrContinue = true; }
+
   void setHasBranchProtectedScope() {
     HasBranchProtectedScope = true;
   }
@@ -485,8 +491,9 @@ class FunctionScopeInfo {
   }
 
   bool NeedsScopeChecking() const {
-    return !HasDroppedStmt && (HasIndirectGoto || HasMustTail ||
-                               (HasBranchProtectedScope && 
HasBranchIntoScope));
+    return !HasDroppedStmt &&
+           (HasIndirectGoto || HasMustTail || HasLabeledBreakOrContinue ||
+            (HasBranchProtectedScope && HasBranchIntoScope));
   }
 
   // Add a block introduced in this function.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5211373367677..7db36a64679d3 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11033,8 +11033,10 @@ class Sema final : public SemaBase {
                            LabelDecl *TheDecl);
   StmtResult ActOnIndirectGotoStmt(SourceLocation GotoLoc,
                                    SourceLocation StarLoc, Expr *DestExp);
-  StmtResult ActOnContinueStmt(SourceLocation ContinueLoc, Scope *CurScope);
-  StmtResult ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope);
+  StmtResult ActOnContinueStmt(SourceLocation ContinueLoc, Scope *CurScope,
+                               LabelDecl *Label, SourceLocation LabelLoc);
+  StmtResult ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope,
+                            LabelDecl *Label, SourceLocation LabelLoc);
 
   struct NamedReturnInfo {
     const VarDecl *Candidate;
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 8e2927bdc8d6f..79583b68b4112 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -7407,18 +7407,28 @@ ExpectedStmt 
ASTNodeImporter::VisitIndirectGotoStmt(IndirectGotoStmt *S) {
       ToGotoLoc, ToStarLoc, ToTarget);
 }
 
+template <typename StmtClass>
+static ExpectedStmt ImportLoopControlStmt(ASTNodeImporter &NodeImporter,
+                                          ASTImporter &Importer, StmtClass *S) 
{
+  Error Err = Error::success();
+  auto ToLoc = NodeImporter.importChecked(Err, S->getKwLoc());
+  auto ToLabelLoc = S->isLabeled()
+                        ? NodeImporter.importChecked(Err, S->getLabelLoc())
+                        : SourceLocation();
+  auto ToDecl = S->isLabeled()
+                    ? NodeImporter.importChecked(Err, S->getLabelDecl())
+                    : nullptr;
+  if (Err)
+    return std::move(Err);
+  return new (Importer.getToContext()) StmtClass(ToLoc, ToLabelLoc, ToDecl);
+}
+
 ExpectedStmt ASTNodeImporter::VisitContinueStmt(ContinueStmt *S) {
-  ExpectedSLoc ToContinueLocOrErr = import(S->getContinueLoc());
-  if (!ToContinueLocOrErr)
-    return ToContinueLocOrErr.takeError();
-  return new (Importer.getToContext()) ContinueStmt(*ToContinueLocOrErr);
+  return ImportLoopControlStmt(*this, Importer, S);
 }
 
 ExpectedStmt ASTNodeImporter::VisitBreakStmt(BreakStmt *S) {
-  auto ToBreakLocOrErr = import(S->getBreakLoc());
-  if (!ToBreakLocOrErr)
-    return ToBreakLocOrErr.takeError();
-  return new (Importer.getToContext()) BreakStmt(*ToBreakLocOrErr);
+  return ImportLoopControlStmt(*this, Importer, S);
 }
 
 ExpectedStmt ASTNodeImporter::VisitReturnStmt(ReturnStmt *S) {
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 3679327da7b0c..264153f7508d7 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -894,6 +894,11 @@ namespace {
     /// declaration whose initializer is being evaluated, if any.
     APValue *EvaluatingDeclValue;
 
+    /// Stack of loops and 'switch' statements which we're currently
+    /// breaking/continuing; null entries are used to mark unlabeled
+    /// break/continue.
+    SmallVector<Stmt *> BreakContinueStack;
+
     /// Set of objects that are currently being constructed.
     llvm::DenseMap<ObjectUnderConstruction, ConstructionPhase>
         ObjectsUnderConstruction;
@@ -5385,6 +5390,44 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, 
EvalInfo &Info,
                                    const Stmt *S,
                                    const SwitchCase *SC = nullptr);
 
+/// Helper to implement labeled break/continue. Returns 'true' if the 
evaluation
+/// result should be propagated up. Otherwise, it sets the evaluation result
+/// to either Continue to continue the current loop, or Succeeded to break it.
+static bool ShouldPropagateBreakContinue(EvalInfo &Info,
+                                         const Stmt *LoopOrSwitch,
+                                         ArrayRef<BlockScopeRAII *> Scopes,
+                                         EvalStmtResult &ESR) {
+  bool IsSwitch = isa<SwitchStmt>(LoopOrSwitch);
+
+  // For loops, map Succeeded to Continue so we don't have to check for both.
+  if (!IsSwitch && ESR == ESR_Succeeded) {
+    ESR = ESR_Continue;
+    return false;
+  }
+
+  if (ESR != ESR_Break && ESR != ESR_Continue)
+    return false;
+
+  // Are we breaking out of or continuing this statement?
+  bool CanBreakOrContinue = !IsSwitch || ESR == ESR_Break;
+  Stmt *StackTop = Info.BreakContinueStack.back();
+  if (CanBreakOrContinue && (StackTop == nullptr || StackTop == LoopOrSwitch)) 
{
+    Info.BreakContinueStack.pop_back();
+    if (ESR == ESR_Break)
+      ESR = ESR_Succeeded;
+    return false;
+  }
+
+  // We're not. Propagate the result up.
+  for (BlockScopeRAII *S : Scopes) {
+    if (!S->destroy()) {
+      ESR = ESR_Failed;
+      break;
+    }
+  }
+  return true;
+}
+
 /// Evaluate the body of a loop, and translate the result as appropriate.
 static EvalStmtResult EvaluateLoopBody(StmtResult &Result, EvalInfo &Info,
                                        const Stmt *Body,
@@ -5395,18 +5438,7 @@ static EvalStmtResult EvaluateLoopBody(StmtResult 
&Result, EvalInfo &Info,
   if (ESR != ESR_Failed && ESR != ESR_CaseNotFound && !Scope.destroy())
     ESR = ESR_Failed;
 
-  switch (ESR) {
-  case ESR_Break:
-    return ESR_Succeeded;
-  case ESR_Succeeded:
-  case ESR_Continue:
-    return ESR_Continue;
-  case ESR_Failed:
-  case ESR_Returned:
-  case ESR_CaseNotFound:
-    return ESR;
-  }
-  llvm_unreachable("Invalid EvalStmtResult!");
+  return ESR;
 }
 
 /// Evaluate a switch statement.
@@ -5472,10 +5504,12 @@ static EvalStmtResult EvaluateSwitch(StmtResult 
&Result, EvalInfo &Info,
   EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found);
   if (ESR != ESR_Failed && ESR ...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/152870
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to