https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/156856
None >From 9c3e449ddf0aaa392547024041a8605b635cd988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Thu, 4 Sep 2025 12:31:24 +0200 Subject: [PATCH] [clang][bytecode] Implement C23 named loops --- clang/lib/AST/ByteCode/Compiler.cpp | 169 ++++++++++++------ clang/lib/AST/ByteCode/Compiler.h | 29 +-- .../labeled-break-continue-constexpr.cpp | 1 + 3 files changed, 131 insertions(+), 68 deletions(-) diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 56552f3969216..d97f8ad2add73 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -114,31 +114,25 @@ template <class Emitter> class LoopScope final { public: using LabelTy = typename Compiler<Emitter>::LabelTy; using OptLabelTy = typename Compiler<Emitter>::OptLabelTy; + using LabelInfo = typename Compiler<Emitter>::LabelInfo; - LoopScope(Compiler<Emitter> *Ctx, LabelTy BreakLabel, LabelTy ContinueLabel) - : Ctx(Ctx), OldBreakLabel(Ctx->BreakLabel), - OldContinueLabel(Ctx->ContinueLabel), - OldBreakVarScope(Ctx->BreakVarScope), - OldContinueVarScope(Ctx->ContinueVarScope) { - this->Ctx->BreakLabel = BreakLabel; - this->Ctx->ContinueLabel = ContinueLabel; - this->Ctx->BreakVarScope = this->Ctx->VarScope; - this->Ctx->ContinueVarScope = this->Ctx->VarScope; - } + LoopScope(Compiler<Emitter> *Ctx, const Stmt *Name, LabelTy BreakLabel, + LabelTy ContinueLabel) + : Ctx(Ctx) { +#ifndef NDEBUG + for (const LabelInfo &LI : Ctx->LabelInfoStack) + assert(LI.Name != Name); +#endif - ~LoopScope() { - this->Ctx->BreakLabel = OldBreakLabel; - this->Ctx->ContinueLabel = OldContinueLabel; - this->Ctx->ContinueVarScope = OldContinueVarScope; - this->Ctx->BreakVarScope = OldBreakVarScope; + this->Ctx->LabelInfoStack.emplace_back(Name, BreakLabel, ContinueLabel, + /*DefaultLabel=*/std::nullopt, + Ctx->VarScope); } + ~LoopScope() { this->Ctx->LabelInfoStack.pop_back(); } + private: Compiler<Emitter> *Ctx; - OptLabelTy OldBreakLabel; - OptLabelTy OldContinueLabel; - VariableScope<Emitter> *OldBreakVarScope; - VariableScope<Emitter> *OldContinueVarScope; }; // Sets the context for a switch scope, mapping labels. @@ -147,32 +141,30 @@ template <class Emitter> class SwitchScope final { using LabelTy = typename Compiler<Emitter>::LabelTy; using OptLabelTy = typename Compiler<Emitter>::OptLabelTy; using CaseMap = typename Compiler<Emitter>::CaseMap; + using LabelInfo = typename Compiler<Emitter>::LabelInfo; + + SwitchScope(Compiler<Emitter> *Ctx, const Stmt *Name, CaseMap &&CaseLabels, + LabelTy BreakLabel, OptLabelTy DefaultLabel) + : Ctx(Ctx), OldCaseLabels(std::move(this->Ctx->CaseLabels)) { +#ifndef NDEBUG + for (const LabelInfo &LI : Ctx->LabelInfoStack) + assert(LI.Name != Name); +#endif - SwitchScope(Compiler<Emitter> *Ctx, CaseMap &&CaseLabels, LabelTy BreakLabel, - OptLabelTy DefaultLabel) - : Ctx(Ctx), OldBreakLabel(Ctx->BreakLabel), - OldDefaultLabel(this->Ctx->DefaultLabel), - OldCaseLabels(std::move(this->Ctx->CaseLabels)), - OldLabelVarScope(Ctx->BreakVarScope) { - this->Ctx->BreakLabel = BreakLabel; - this->Ctx->DefaultLabel = DefaultLabel; this->Ctx->CaseLabels = std::move(CaseLabels); - this->Ctx->BreakVarScope = this->Ctx->VarScope; + this->Ctx->LabelInfoStack.emplace_back(Name, BreakLabel, + /*ContinueLabel=*/std::nullopt, + DefaultLabel, Ctx->VarScope); } ~SwitchScope() { - this->Ctx->BreakLabel = OldBreakLabel; - this->Ctx->DefaultLabel = OldDefaultLabel; this->Ctx->CaseLabels = std::move(OldCaseLabels); - this->Ctx->BreakVarScope = OldLabelVarScope; + this->Ctx->LabelInfoStack.pop_back(); } private: Compiler<Emitter> *Ctx; - OptLabelTy OldBreakLabel; - OptLabelTy OldDefaultLabel; CaseMap OldCaseLabels; - VariableScope<Emitter> *OldLabelVarScope; }; template <class Emitter> class StmtExprScope final { @@ -5682,7 +5674,8 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) { LabelTy CondLabel = this->getLabel(); // Label before the condition. LabelTy EndLabel = this->getLabel(); // Label after the loop. - LoopScope<Emitter> LS(this, EndLabel, CondLabel); + LocalScope<Emitter> WholeLoopScope(this); + LoopScope<Emitter> LS(this, S, EndLabel, CondLabel); this->fallthrough(CondLabel); this->emitLabel(CondLabel); @@ -5712,8 +5705,7 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) { return false; this->fallthrough(EndLabel); this->emitLabel(EndLabel); - - return true; + return WholeLoopScope.destroyLocals(); } template <class Emitter> bool Compiler<Emitter>::visitDoStmt(const DoStmt *S) { @@ -5723,7 +5715,8 @@ template <class Emitter> bool Compiler<Emitter>::visitDoStmt(const DoStmt *S) { LabelTy StartLabel = this->getLabel(); LabelTy EndLabel = this->getLabel(); LabelTy CondLabel = this->getLabel(); - LoopScope<Emitter> LS(this, EndLabel, CondLabel); + LocalScope<Emitter> WholeLoopScope(this); + LoopScope<Emitter> LS(this, S, EndLabel, CondLabel); this->fallthrough(StartLabel); this->emitLabel(StartLabel); @@ -5745,7 +5738,7 @@ template <class Emitter> bool Compiler<Emitter>::visitDoStmt(const DoStmt *S) { this->fallthrough(EndLabel); this->emitLabel(EndLabel); - return true; + return WholeLoopScope.destroyLocals(); } template <class Emitter> @@ -5759,19 +5752,21 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) { LabelTy EndLabel = this->getLabel(); LabelTy CondLabel = this->getLabel(); LabelTy IncLabel = this->getLabel(); - LoopScope<Emitter> LS(this, EndLabel, IncLabel); + LocalScope<Emitter> WholeLoopScope(this); if (Init && !this->visitStmt(Init)) return false; + // Start of the loop body { this->fallthrough(CondLabel); this->emitLabel(CondLabel); - // Start of loop body. LocalScope<Emitter> CondScope(this); - if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt()) + LoopScope<Emitter> LS(this, S, EndLabel, IncLabel); + if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt()) { if (!visitDeclStmt(CondDecl)) return false; + } if (Cond) { if (!this->visitBool(Cond)) @@ -5794,12 +5789,12 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) { return false; if (!this->jump(CondLabel)) return false; - // End of loop body. + // } End of loop body. this->emitLabel(EndLabel); // If we jumped out of the loop above, we still need to clean up the condition // scope. - return CondScope.destroyLocals(); + return CondScope.destroyLocals() && WholeLoopScope.destroyLocals(); } template <class Emitter> @@ -5815,7 +5810,8 @@ bool Compiler<Emitter>::visitCXXForRangeStmt(const CXXForRangeStmt *S) { LabelTy EndLabel = this->getLabel(); LabelTy CondLabel = this->getLabel(); LabelTy IncLabel = this->getLabel(); - LoopScope<Emitter> LS(this, EndLabel, IncLabel); + LocalScope<Emitter> WholeLoopScope(this); + LoopScope<Emitter> LS(this, S, EndLabel, IncLabel); // Emit declarations needed in the loop. if (Init && !this->visitStmt(Init)) @@ -5854,29 +5850,78 @@ bool Compiler<Emitter>::visitCXXForRangeStmt(const CXXForRangeStmt *S) { this->fallthrough(EndLabel); this->emitLabel(EndLabel); - return true; + return WholeLoopScope.destroyLocals(); } template <class Emitter> bool Compiler<Emitter>::visitBreakStmt(const BreakStmt *S) { - if (!BreakLabel) + if (LabelInfoStack.empty()) return false; - for (VariableScope<Emitter> *C = VarScope; C != BreakVarScope; + OptLabelTy TargetLabel = std::nullopt; + const Stmt *TargetLoop = S->getNamedLoopOrSwitch(); + const VariableScope<Emitter> *BreakScope = nullptr; + + if (!TargetLoop) { + for (const auto &LI : llvm::reverse(LabelInfoStack)) { + if (LI.BreakLabel) { + TargetLabel = *LI.BreakLabel; + BreakScope = LI.BreakOrContinueScope; + break; + } + } + } else { + for (auto LI : LabelInfoStack) { + if (LI.Name == TargetLoop) { + TargetLabel = *LI.BreakLabel; + BreakScope = LI.BreakOrContinueScope; + break; + } + } + } + + assert(TargetLabel); + + for (VariableScope<Emitter> *C = this->VarScope; C != BreakScope; C = C->getParent()) C->emitDestruction(); - return this->jump(*BreakLabel); + + return this->jump(*TargetLabel); } template <class Emitter> bool Compiler<Emitter>::visitContinueStmt(const ContinueStmt *S) { - if (!ContinueLabel) + if (LabelInfoStack.empty()) return false; - for (VariableScope<Emitter> *C = VarScope; - C && C->getParent() != ContinueVarScope; C = C->getParent()) + OptLabelTy TargetLabel = std::nullopt; + const Stmt *TargetLoop = S->getNamedLoopOrSwitch(); + const VariableScope<Emitter> *ContinueScope = nullptr; + + if (!TargetLoop) { + for (const auto &LI : llvm::reverse(LabelInfoStack)) { + if (LI.ContinueLabel) { + TargetLabel = *LI.ContinueLabel; + ContinueScope = LI.BreakOrContinueScope; + break; + } + } + } else { + for (auto LI : LabelInfoStack) { + if (LI.Name == TargetLoop) { + TargetLabel = *LI.ContinueLabel; + ContinueScope = LI.BreakOrContinueScope; + break; + } + } + } + assert(TargetLabel); + + for (VariableScope<Emitter> *C = VarScope; C != ContinueScope; + C = C->getParent()) C->emitDestruction(); - return this->jump(*ContinueLabel); + + return this->jump(*TargetLabel); } template <class Emitter> @@ -5889,7 +5934,7 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) { LocalScope<Emitter> LS(this); LabelTy EndLabel = this->getLabel(); - OptLabelTy DefaultLabel = std::nullopt; + UnsignedOrNone DefaultLabel = std::nullopt; unsigned CondVar = this->allocateLocalPrimitive(Cond, CondT, /*IsConst=*/true); @@ -5950,7 +5995,8 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) { return false; } - SwitchScope<Emitter> SS(this, std::move(CaseLabels), EndLabel, DefaultLabel); + SwitchScope<Emitter> SS(this, S, std::move(CaseLabels), EndLabel, + DefaultLabel); if (!this->visitStmt(S->getBody())) return false; this->emitLabel(EndLabel); @@ -5966,7 +6012,18 @@ bool Compiler<Emitter>::visitCaseStmt(const CaseStmt *S) { template <class Emitter> bool Compiler<Emitter>::visitDefaultStmt(const DefaultStmt *S) { - this->emitLabel(*DefaultLabel); + if (LabelInfoStack.empty()) + return false; + + LabelTy DefaultLabel; + for (const LabelInfo &LI : llvm::reverse(LabelInfoStack)) { + if (LI.DefaultLabel) { + DefaultLabel = *LI.DefaultLabel; + break; + } + } + + this->emitLabel(DefaultLabel); return this->visitStmt(S->getSubStmt()); } diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index 475faee4d3fde..63e0fdada0393 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -112,9 +112,23 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>, // Aliases for types defined in the emitter. using LabelTy = typename Emitter::LabelTy; using AddrTy = typename Emitter::AddrTy; - using OptLabelTy = std::optional<LabelTy>; + using OptLabelTy = UnsignedOrNone; using CaseMap = llvm::DenseMap<const SwitchCase *, LabelTy>; + struct LabelInfo { + const Stmt *Name; + const VariableScope<Emitter> *BreakOrContinueScope; + OptLabelTy BreakLabel; + OptLabelTy ContinueLabel; + OptLabelTy DefaultLabel; + LabelInfo(const Stmt *Name, OptLabelTy BreakLabel, OptLabelTy ContinueLabel, + OptLabelTy DefaultLabel, + const VariableScope<Emitter> *BreakOrContinueScope) + : Name(Name), BreakOrContinueScope(BreakOrContinueScope), + BreakLabel(BreakLabel), ContinueLabel(ContinueLabel), + DefaultLabel(DefaultLabel) {} + }; + /// Current compilation context. Context &Ctx; /// Program to link to. @@ -443,17 +457,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>, /// Switch case mapping. CaseMap CaseLabels; - - /// Scope to cleanup until when we see a break statement. - VariableScope<Emitter> *BreakVarScope = nullptr; - /// Point to break to. - OptLabelTy BreakLabel; - /// Scope to cleanup until when we see a continue statement. - VariableScope<Emitter> *ContinueVarScope = nullptr; - /// Point to continue to. - OptLabelTy ContinueLabel; - /// Default case label. - OptLabelTy DefaultLabel; + /// Stack of label information for loops and switch statements. + llvm::SmallVector<LabelInfo> LabelInfoStack; const FunctionDecl *CompilingFunction = nullptr; }; diff --git a/clang/test/SemaCXX/labeled-break-continue-constexpr.cpp b/clang/test/SemaCXX/labeled-break-continue-constexpr.cpp index bec6c582a1f0d..d1b57adca91c1 100644 --- a/clang/test/SemaCXX/labeled-break-continue-constexpr.cpp +++ b/clang/test/SemaCXX/labeled-break-continue-constexpr.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fnamed-loops -std=c++23 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fnamed-loops -std=c++23 -fsyntax-only -verify %s -fexperimental-new-constant-interpreter // expected-no-diagnostics struct Tracker { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits