Skip to content

Commit 859e8a6

Browse files
authored
[clang][bytecode] Implement C23 named loops (#156856)
1 parent e2a067e commit 859e8a6

File tree

3 files changed

+131
-68
lines changed

3 files changed

+131
-68
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 113 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -114,31 +114,25 @@ template <class Emitter> class LoopScope final {
114114
public:
115115
using LabelTy = typename Compiler<Emitter>::LabelTy;
116116
using OptLabelTy = typename Compiler<Emitter>::OptLabelTy;
117+
using LabelInfo = typename Compiler<Emitter>::LabelInfo;
117118

118-
LoopScope(Compiler<Emitter> *Ctx, LabelTy BreakLabel, LabelTy ContinueLabel)
119-
: Ctx(Ctx), OldBreakLabel(Ctx->BreakLabel),
120-
OldContinueLabel(Ctx->ContinueLabel),
121-
OldBreakVarScope(Ctx->BreakVarScope),
122-
OldContinueVarScope(Ctx->ContinueVarScope) {
123-
this->Ctx->BreakLabel = BreakLabel;
124-
this->Ctx->ContinueLabel = ContinueLabel;
125-
this->Ctx->BreakVarScope = this->Ctx->VarScope;
126-
this->Ctx->ContinueVarScope = this->Ctx->VarScope;
127-
}
119+
LoopScope(Compiler<Emitter> *Ctx, const Stmt *Name, LabelTy BreakLabel,
120+
LabelTy ContinueLabel)
121+
: Ctx(Ctx) {
122+
#ifndef NDEBUG
123+
for (const LabelInfo &LI : Ctx->LabelInfoStack)
124+
assert(LI.Name != Name);
125+
#endif
128126

129-
~LoopScope() {
130-
this->Ctx->BreakLabel = OldBreakLabel;
131-
this->Ctx->ContinueLabel = OldContinueLabel;
132-
this->Ctx->ContinueVarScope = OldContinueVarScope;
133-
this->Ctx->BreakVarScope = OldBreakVarScope;
127+
this->Ctx->LabelInfoStack.emplace_back(Name, BreakLabel, ContinueLabel,
128+
/*DefaultLabel=*/std::nullopt,
129+
Ctx->VarScope);
134130
}
135131

132+
~LoopScope() { this->Ctx->LabelInfoStack.pop_back(); }
133+
136134
private:
137135
Compiler<Emitter> *Ctx;
138-
OptLabelTy OldBreakLabel;
139-
OptLabelTy OldContinueLabel;
140-
VariableScope<Emitter> *OldBreakVarScope;
141-
VariableScope<Emitter> *OldContinueVarScope;
142136
};
143137

144138
// Sets the context for a switch scope, mapping labels.
@@ -147,32 +141,30 @@ template <class Emitter> class SwitchScope final {
147141
using LabelTy = typename Compiler<Emitter>::LabelTy;
148142
using OptLabelTy = typename Compiler<Emitter>::OptLabelTy;
149143
using CaseMap = typename Compiler<Emitter>::CaseMap;
144+
using LabelInfo = typename Compiler<Emitter>::LabelInfo;
145+
146+
SwitchScope(Compiler<Emitter> *Ctx, const Stmt *Name, CaseMap &&CaseLabels,
147+
LabelTy BreakLabel, OptLabelTy DefaultLabel)
148+
: Ctx(Ctx), OldCaseLabels(std::move(this->Ctx->CaseLabels)) {
149+
#ifndef NDEBUG
150+
for (const LabelInfo &LI : Ctx->LabelInfoStack)
151+
assert(LI.Name != Name);
152+
#endif
150153

151-
SwitchScope(Compiler<Emitter> *Ctx, CaseMap &&CaseLabels, LabelTy BreakLabel,
152-
OptLabelTy DefaultLabel)
153-
: Ctx(Ctx), OldBreakLabel(Ctx->BreakLabel),
154-
OldDefaultLabel(this->Ctx->DefaultLabel),
155-
OldCaseLabels(std::move(this->Ctx->CaseLabels)),
156-
OldLabelVarScope(Ctx->BreakVarScope) {
157-
this->Ctx->BreakLabel = BreakLabel;
158-
this->Ctx->DefaultLabel = DefaultLabel;
159154
this->Ctx->CaseLabels = std::move(CaseLabels);
160-
this->Ctx->BreakVarScope = this->Ctx->VarScope;
155+
this->Ctx->LabelInfoStack.emplace_back(Name, BreakLabel,
156+
/*ContinueLabel=*/std::nullopt,
157+
DefaultLabel, Ctx->VarScope);
161158
}
162159

163160
~SwitchScope() {
164-
this->Ctx->BreakLabel = OldBreakLabel;
165-
this->Ctx->DefaultLabel = OldDefaultLabel;
166161
this->Ctx->CaseLabels = std::move(OldCaseLabels);
167-
this->Ctx->BreakVarScope = OldLabelVarScope;
162+
this->Ctx->LabelInfoStack.pop_back();
168163
}
169164

170165
private:
171166
Compiler<Emitter> *Ctx;
172-
OptLabelTy OldBreakLabel;
173-
OptLabelTy OldDefaultLabel;
174167
CaseMap OldCaseLabels;
175-
VariableScope<Emitter> *OldLabelVarScope;
176168
};
177169

178170
template <class Emitter> class StmtExprScope final {
@@ -5685,7 +5677,8 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
56855677

56865678
LabelTy CondLabel = this->getLabel(); // Label before the condition.
56875679
LabelTy EndLabel = this->getLabel(); // Label after the loop.
5688-
LoopScope<Emitter> LS(this, EndLabel, CondLabel);
5680+
LocalScope<Emitter> WholeLoopScope(this);
5681+
LoopScope<Emitter> LS(this, S, EndLabel, CondLabel);
56895682

56905683
this->fallthrough(CondLabel);
56915684
this->emitLabel(CondLabel);
@@ -5715,8 +5708,7 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
57155708
return false;
57165709
this->fallthrough(EndLabel);
57175710
this->emitLabel(EndLabel);
5718-
5719-
return true;
5711+
return WholeLoopScope.destroyLocals();
57205712
}
57215713

57225714
template <class Emitter> bool Compiler<Emitter>::visitDoStmt(const DoStmt *S) {
@@ -5726,7 +5718,8 @@ template <class Emitter> bool Compiler<Emitter>::visitDoStmt(const DoStmt *S) {
57265718
LabelTy StartLabel = this->getLabel();
57275719
LabelTy EndLabel = this->getLabel();
57285720
LabelTy CondLabel = this->getLabel();
5729-
LoopScope<Emitter> LS(this, EndLabel, CondLabel);
5721+
LocalScope<Emitter> WholeLoopScope(this);
5722+
LoopScope<Emitter> LS(this, S, EndLabel, CondLabel);
57305723

57315724
this->fallthrough(StartLabel);
57325725
this->emitLabel(StartLabel);
@@ -5748,7 +5741,7 @@ template <class Emitter> bool Compiler<Emitter>::visitDoStmt(const DoStmt *S) {
57485741

57495742
this->fallthrough(EndLabel);
57505743
this->emitLabel(EndLabel);
5751-
return true;
5744+
return WholeLoopScope.destroyLocals();
57525745
}
57535746

57545747
template <class Emitter>
@@ -5762,19 +5755,21 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
57625755
LabelTy EndLabel = this->getLabel();
57635756
LabelTy CondLabel = this->getLabel();
57645757
LabelTy IncLabel = this->getLabel();
5765-
LoopScope<Emitter> LS(this, EndLabel, IncLabel);
57665758

5759+
LocalScope<Emitter> WholeLoopScope(this);
57675760
if (Init && !this->visitStmt(Init))
57685761
return false;
57695762

5763+
// Start of the loop body {
57705764
this->fallthrough(CondLabel);
57715765
this->emitLabel(CondLabel);
57725766

5773-
// Start of loop body.
57745767
LocalScope<Emitter> CondScope(this);
5775-
if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
5768+
LoopScope<Emitter> LS(this, S, EndLabel, IncLabel);
5769+
if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt()) {
57765770
if (!visitDeclStmt(CondDecl))
57775771
return false;
5772+
}
57785773

57795774
if (Cond) {
57805775
if (!this->visitBool(Cond))
@@ -5797,12 +5792,12 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
57975792
return false;
57985793
if (!this->jump(CondLabel))
57995794
return false;
5800-
// End of loop body.
5795+
// } End of loop body.
58015796

58025797
this->emitLabel(EndLabel);
58035798
// If we jumped out of the loop above, we still need to clean up the condition
58045799
// scope.
5805-
return CondScope.destroyLocals();
5800+
return CondScope.destroyLocals() && WholeLoopScope.destroyLocals();
58065801
}
58075802

58085803
template <class Emitter>
@@ -5818,7 +5813,8 @@ bool Compiler<Emitter>::visitCXXForRangeStmt(const CXXForRangeStmt *S) {
58185813
LabelTy EndLabel = this->getLabel();
58195814
LabelTy CondLabel = this->getLabel();
58205815
LabelTy IncLabel = this->getLabel();
5821-
LoopScope<Emitter> LS(this, EndLabel, IncLabel);
5816+
LocalScope<Emitter> WholeLoopScope(this);
5817+
LoopScope<Emitter> LS(this, S, EndLabel, IncLabel);
58225818

58235819
// Emit declarations needed in the loop.
58245820
if (Init && !this->visitStmt(Init))
@@ -5857,29 +5853,78 @@ bool Compiler<Emitter>::visitCXXForRangeStmt(const CXXForRangeStmt *S) {
58575853

58585854
this->fallthrough(EndLabel);
58595855
this->emitLabel(EndLabel);
5860-
return true;
5856+
return WholeLoopScope.destroyLocals();
58615857
}
58625858

58635859
template <class Emitter>
58645860
bool Compiler<Emitter>::visitBreakStmt(const BreakStmt *S) {
5865-
if (!BreakLabel)
5861+
if (LabelInfoStack.empty())
58665862
return false;
58675863

5868-
for (VariableScope<Emitter> *C = VarScope; C != BreakVarScope;
5864+
OptLabelTy TargetLabel = std::nullopt;
5865+
const Stmt *TargetLoop = S->getNamedLoopOrSwitch();
5866+
const VariableScope<Emitter> *BreakScope = nullptr;
5867+
5868+
if (!TargetLoop) {
5869+
for (const auto &LI : llvm::reverse(LabelInfoStack)) {
5870+
if (LI.BreakLabel) {
5871+
TargetLabel = *LI.BreakLabel;
5872+
BreakScope = LI.BreakOrContinueScope;
5873+
break;
5874+
}
5875+
}
5876+
} else {
5877+
for (auto LI : LabelInfoStack) {
5878+
if (LI.Name == TargetLoop) {
5879+
TargetLabel = *LI.BreakLabel;
5880+
BreakScope = LI.BreakOrContinueScope;
5881+
break;
5882+
}
5883+
}
5884+
}
5885+
5886+
assert(TargetLabel);
5887+
5888+
for (VariableScope<Emitter> *C = this->VarScope; C != BreakScope;
58695889
C = C->getParent())
58705890
C->emitDestruction();
5871-
return this->jump(*BreakLabel);
5891+
5892+
return this->jump(*TargetLabel);
58725893
}
58735894

58745895
template <class Emitter>
58755896
bool Compiler<Emitter>::visitContinueStmt(const ContinueStmt *S) {
5876-
if (!ContinueLabel)
5897+
if (LabelInfoStack.empty())
58775898
return false;
58785899

5879-
for (VariableScope<Emitter> *C = VarScope;
5880-
C && C->getParent() != ContinueVarScope; C = C->getParent())
5900+
OptLabelTy TargetLabel = std::nullopt;
5901+
const Stmt *TargetLoop = S->getNamedLoopOrSwitch();
5902+
const VariableScope<Emitter> *ContinueScope = nullptr;
5903+
5904+
if (!TargetLoop) {
5905+
for (const auto &LI : llvm::reverse(LabelInfoStack)) {
5906+
if (LI.ContinueLabel) {
5907+
TargetLabel = *LI.ContinueLabel;
5908+
ContinueScope = LI.BreakOrContinueScope;
5909+
break;
5910+
}
5911+
}
5912+
} else {
5913+
for (auto LI : LabelInfoStack) {
5914+
if (LI.Name == TargetLoop) {
5915+
TargetLabel = *LI.ContinueLabel;
5916+
ContinueScope = LI.BreakOrContinueScope;
5917+
break;
5918+
}
5919+
}
5920+
}
5921+
assert(TargetLabel);
5922+
5923+
for (VariableScope<Emitter> *C = VarScope; C != ContinueScope;
5924+
C = C->getParent())
58815925
C->emitDestruction();
5882-
return this->jump(*ContinueLabel);
5926+
5927+
return this->jump(*TargetLabel);
58835928
}
58845929

58855930
template <class Emitter>
@@ -5892,7 +5937,7 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
58925937
LocalScope<Emitter> LS(this);
58935938

58945939
LabelTy EndLabel = this->getLabel();
5895-
OptLabelTy DefaultLabel = std::nullopt;
5940+
UnsignedOrNone DefaultLabel = std::nullopt;
58965941
unsigned CondVar =
58975942
this->allocateLocalPrimitive(Cond, CondT, /*IsConst=*/true);
58985943

@@ -5953,7 +5998,8 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
59535998
return false;
59545999
}
59556000

5956-
SwitchScope<Emitter> SS(this, std::move(CaseLabels), EndLabel, DefaultLabel);
6001+
SwitchScope<Emitter> SS(this, S, std::move(CaseLabels), EndLabel,
6002+
DefaultLabel);
59576003
if (!this->visitStmt(S->getBody()))
59586004
return false;
59596005
this->emitLabel(EndLabel);
@@ -5969,7 +6015,18 @@ bool Compiler<Emitter>::visitCaseStmt(const CaseStmt *S) {
59696015

59706016
template <class Emitter>
59716017
bool Compiler<Emitter>::visitDefaultStmt(const DefaultStmt *S) {
5972-
this->emitLabel(*DefaultLabel);
6018+
if (LabelInfoStack.empty())
6019+
return false;
6020+
6021+
LabelTy DefaultLabel;
6022+
for (const LabelInfo &LI : llvm::reverse(LabelInfoStack)) {
6023+
if (LI.DefaultLabel) {
6024+
DefaultLabel = *LI.DefaultLabel;
6025+
break;
6026+
}
6027+
}
6028+
6029+
this->emitLabel(DefaultLabel);
59736030
return this->visitStmt(S->getSubStmt());
59746031
}
59756032

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,23 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
112112
// Aliases for types defined in the emitter.
113113
using LabelTy = typename Emitter::LabelTy;
114114
using AddrTy = typename Emitter::AddrTy;
115-
using OptLabelTy = std::optional<LabelTy>;
115+
using OptLabelTy = UnsignedOrNone;
116116
using CaseMap = llvm::DenseMap<const SwitchCase *, LabelTy>;
117117

118+
struct LabelInfo {
119+
const Stmt *Name;
120+
const VariableScope<Emitter> *BreakOrContinueScope;
121+
OptLabelTy BreakLabel;
122+
OptLabelTy ContinueLabel;
123+
OptLabelTy DefaultLabel;
124+
LabelInfo(const Stmt *Name, OptLabelTy BreakLabel, OptLabelTy ContinueLabel,
125+
OptLabelTy DefaultLabel,
126+
const VariableScope<Emitter> *BreakOrContinueScope)
127+
: Name(Name), BreakOrContinueScope(BreakOrContinueScope),
128+
BreakLabel(BreakLabel), ContinueLabel(ContinueLabel),
129+
DefaultLabel(DefaultLabel) {}
130+
};
131+
118132
/// Current compilation context.
119133
Context &Ctx;
120134
/// Program to link to.
@@ -443,17 +457,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
443457

444458
/// Switch case mapping.
445459
CaseMap CaseLabels;
446-
447-
/// Scope to cleanup until when we see a break statement.
448-
VariableScope<Emitter> *BreakVarScope = nullptr;
449-
/// Point to break to.
450-
OptLabelTy BreakLabel;
451-
/// Scope to cleanup until when we see a continue statement.
452-
VariableScope<Emitter> *ContinueVarScope = nullptr;
453-
/// Point to continue to.
454-
OptLabelTy ContinueLabel;
455-
/// Default case label.
456-
OptLabelTy DefaultLabel;
460+
/// Stack of label information for loops and switch statements.
461+
llvm::SmallVector<LabelInfo> LabelInfoStack;
457462

458463
const FunctionDecl *CompilingFunction = nullptr;
459464
};

clang/test/SemaCXX/labeled-break-continue-constexpr.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -fnamed-loops -std=c++23 -fsyntax-only -verify %s
2+
// RUN: %clang_cc1 -fnamed-loops -std=c++23 -fsyntax-only -verify %s -fexperimental-new-constant-interpreter
23
// expected-no-diagnostics
34

45
struct Tracker {

0 commit comments

Comments
 (0)