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 LoopScope final { public: using LabelTy = typename Compiler::LabelTy; using OptLabelTy = typename Compiler::OptLabelTy; + using LabelInfo = typename Compiler::LabelInfo; - LoopScope(Compiler *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 *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 *Ctx; - OptLabelTy OldBreakLabel; - OptLabelTy OldContinueLabel; - VariableScope *OldBreakVarScope; - VariableScope *OldContinueVarScope; }; // Sets the context for a switch scope, mapping labels. @@ -147,32 +141,30 @@ template class SwitchScope final { using LabelTy = typename Compiler::LabelTy; using OptLabelTy = typename Compiler::OptLabelTy; using CaseMap = typename Compiler::CaseMap; + using LabelInfo = typename Compiler::LabelInfo; + + SwitchScope(Compiler *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 *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 *Ctx; - OptLabelTy OldBreakLabel; - OptLabelTy OldDefaultLabel; CaseMap OldCaseLabels; - VariableScope *OldLabelVarScope; }; template class StmtExprScope final { @@ -5682,7 +5674,8 @@ bool Compiler::visitWhileStmt(const WhileStmt *S) { LabelTy CondLabel = this->getLabel(); // Label before the condition. LabelTy EndLabel = this->getLabel(); // Label after the loop. - LoopScope LS(this, EndLabel, CondLabel); + LocalScope WholeLoopScope(this); + LoopScope LS(this, S, EndLabel, CondLabel); this->fallthrough(CondLabel); this->emitLabel(CondLabel); @@ -5712,8 +5705,7 @@ bool Compiler::visitWhileStmt(const WhileStmt *S) { return false; this->fallthrough(EndLabel); this->emitLabel(EndLabel); - - return true; + return WholeLoopScope.destroyLocals(); } template bool Compiler::visitDoStmt(const DoStmt *S) { @@ -5723,7 +5715,8 @@ template bool Compiler::visitDoStmt(const DoStmt *S) { LabelTy StartLabel = this->getLabel(); LabelTy EndLabel = this->getLabel(); LabelTy CondLabel = this->getLabel(); - LoopScope LS(this, EndLabel, CondLabel); + LocalScope WholeLoopScope(this); + LoopScope LS(this, S, EndLabel, CondLabel); this->fallthrough(StartLabel); this->emitLabel(StartLabel); @@ -5745,7 +5738,7 @@ template bool Compiler::visitDoStmt(const DoStmt *S) { this->fallthrough(EndLabel); this->emitLabel(EndLabel); - return true; + return WholeLoopScope.destroyLocals(); } template @@ -5759,19 +5752,21 @@ bool Compiler::visitForStmt(const ForStmt *S) { LabelTy EndLabel = this->getLabel(); LabelTy CondLabel = this->getLabel(); LabelTy IncLabel = this->getLabel(); - LoopScope LS(this, EndLabel, IncLabel); + LocalScope 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 CondScope(this); - if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt()) + LoopScope 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::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 @@ -5815,7 +5810,8 @@ bool Compiler::visitCXXForRangeStmt(const CXXForRangeStmt *S) { LabelTy EndLabel = this->getLabel(); LabelTy CondLabel = this->getLabel(); LabelTy IncLabel = this->getLabel(); - LoopScope LS(this, EndLabel, IncLabel); + LocalScope WholeLoopScope(this); + LoopScope LS(this, S, EndLabel, IncLabel); // Emit declarations needed in the loop. if (Init && !this->visitStmt(Init)) @@ -5854,29 +5850,78 @@ bool Compiler::visitCXXForRangeStmt(const CXXForRangeStmt *S) { this->fallthrough(EndLabel); this->emitLabel(EndLabel); - return true; + return WholeLoopScope.destroyLocals(); } template bool Compiler::visitBreakStmt(const BreakStmt *S) { - if (!BreakLabel) + if (LabelInfoStack.empty()) return false; - for (VariableScope *C = VarScope; C != BreakVarScope; + OptLabelTy TargetLabel = std::nullopt; + const Stmt *TargetLoop = S->getNamedLoopOrSwitch(); + const VariableScope *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 *C = this->VarScope; C != BreakScope; C = C->getParent()) C->emitDestruction(); - return this->jump(*BreakLabel); + + return this->jump(*TargetLabel); } template bool Compiler::visitContinueStmt(const ContinueStmt *S) { - if (!ContinueLabel) + if (LabelInfoStack.empty()) return false; - for (VariableScope *C = VarScope; - C && C->getParent() != ContinueVarScope; C = C->getParent()) + OptLabelTy TargetLabel = std::nullopt; + const Stmt *TargetLoop = S->getNamedLoopOrSwitch(); + const VariableScope *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 *C = VarScope; C != ContinueScope; + C = C->getParent()) C->emitDestruction(); - return this->jump(*ContinueLabel); + + return this->jump(*TargetLabel); } template @@ -5889,7 +5934,7 @@ bool Compiler::visitSwitchStmt(const SwitchStmt *S) { LocalScope 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::visitSwitchStmt(const SwitchStmt *S) { return false; } - SwitchScope SS(this, std::move(CaseLabels), EndLabel, DefaultLabel); + SwitchScope SS(this, S, std::move(CaseLabels), EndLabel, + DefaultLabel); if (!this->visitStmt(S->getBody())) return false; this->emitLabel(EndLabel); @@ -5966,7 +6012,18 @@ bool Compiler::visitCaseStmt(const CaseStmt *S) { template bool Compiler::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, bool>, // Aliases for types defined in the emitter. using LabelTy = typename Emitter::LabelTy; using AddrTy = typename Emitter::AddrTy; - using OptLabelTy = std::optional; + using OptLabelTy = UnsignedOrNone; using CaseMap = llvm::DenseMap; + struct LabelInfo { + const Stmt *Name; + const VariableScope *BreakOrContinueScope; + OptLabelTy BreakLabel; + OptLabelTy ContinueLabel; + OptLabelTy DefaultLabel; + LabelInfo(const Stmt *Name, OptLabelTy BreakLabel, OptLabelTy ContinueLabel, + OptLabelTy DefaultLabel, + const VariableScope *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, bool>, /// Switch case mapping. CaseMap CaseLabels; - - /// Scope to cleanup until when we see a break statement. - VariableScope *BreakVarScope = nullptr; - /// Point to break to. - OptLabelTy BreakLabel; - /// Scope to cleanup until when we see a continue statement. - VariableScope *ContinueVarScope = nullptr; - /// Point to continue to. - OptLabelTy ContinueLabel; - /// Default case label. - OptLabelTy DefaultLabel; + /// Stack of label information for loops and switch statements. + llvm::SmallVector 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 {