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