Skip to content

Commit e4a1b5f

Browse files
Sirraidesteakhal
andauthored
[Clang] [C2y] Implement N3355 ‘Named Loops’ (#152870)
This implements support for [named loops](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3355.htm) for C2y. When parsing a `LabelStmt`, we create the `LabeDecl` early before we parse the substatement; this label is then passed down to `ParseWhileStatement()` and friends, which then store it in the loop’s (or switch statement’s) `Scope`; when we encounter a `break/continue` statement, we perform a lookup for the label (and error if it doesn’t exist), and then walk the scope stack and check if there is a scope whose preceding label is the target label, which identifies the jump target. The feature is only supported in C2y mode, though a cc1-only option exists for testing (`-fnamed-loops`), which is mostly intended to try and make sure that we don’t have to refactor this entire implementation when/if we start supporting it in C++. --------- Co-authored-by: Balazs Benics <[email protected]>
1 parent 83f3908 commit e4a1b5f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2209
-174
lines changed

clang-tools-extra/clangd/XRefs.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,11 +1106,11 @@ class FindControlFlow : public RecursiveASTVisitor<FindControlFlow> {
11061106
return true;
11071107
}
11081108
bool VisitBreakStmt(BreakStmt *B) {
1109-
found(Break, B->getBreakLoc());
1109+
found(Break, B->getKwLoc());
11101110
return true;
11111111
}
11121112
bool VisitContinueStmt(ContinueStmt *C) {
1113-
found(Continue, C->getContinueLoc());
1113+
found(Continue, C->getKwLoc());
11141114
return true;
11151115
}
11161116
bool VisitSwitchCase(SwitchCase *C) {

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ C Language Changes
134134

135135
C2y Feature Support
136136
^^^^^^^^^^^^^^^^^^^
137+
- Clang now supports `N3355 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3355.htm>`_ Named Loops.
137138

138139
C23 Feature Support
139140
^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/JSONNodeDumper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ class JSONNodeDumper
333333
void VisitStringLiteral(const StringLiteral *SL);
334334
void VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *BLE);
335335

336+
void VisitLoopControlStmt(const LoopControlStmt *LS);
336337
void VisitIfStmt(const IfStmt *IS);
337338
void VisitSwitchStmt(const SwitchStmt *SS);
338339
void VisitCaseStmt(const CaseStmt *CS);

clang/include/clang/AST/Stmt.h

Lines changed: 80 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -277,24 +277,14 @@ class alignas(void *) Stmt {
277277
SourceLocation GotoLoc;
278278
};
279279

280-
class ContinueStmtBitfields {
281-
friend class ContinueStmt;
280+
class LoopControlStmtBitfields {
281+
friend class LoopControlStmt;
282282

283283
LLVM_PREFERRED_TYPE(StmtBitfields)
284284
unsigned : NumStmtBits;
285285

286-
/// The location of the "continue".
287-
SourceLocation ContinueLoc;
288-
};
289-
290-
class BreakStmtBitfields {
291-
friend class BreakStmt;
292-
293-
LLVM_PREFERRED_TYPE(StmtBitfields)
294-
unsigned : NumStmtBits;
295-
296-
/// The location of the "break".
297-
SourceLocation BreakLoc;
286+
/// The location of the "continue"/"break".
287+
SourceLocation KwLoc;
298288
};
299289

300290
class ReturnStmtBitfields {
@@ -1325,8 +1315,7 @@ class alignas(void *) Stmt {
13251315
DoStmtBitfields DoStmtBits;
13261316
ForStmtBitfields ForStmtBits;
13271317
GotoStmtBitfields GotoStmtBits;
1328-
ContinueStmtBitfields ContinueStmtBits;
1329-
BreakStmtBitfields BreakStmtBits;
1318+
LoopControlStmtBitfields LoopControlStmtBits;
13301319
ReturnStmtBitfields ReturnStmtBits;
13311320
SwitchCaseBitfields SwitchCaseBits;
13321321

@@ -2184,6 +2173,14 @@ class LabelStmt : public ValueStmt {
21842173
SourceLocation getBeginLoc() const { return getIdentLoc(); }
21852174
SourceLocation getEndLoc() const LLVM_READONLY { return SubStmt->getEndLoc();}
21862175

2176+
/// Look through nested labels and return the first non-label statement; e.g.
2177+
/// if this is 'a:' in 'a: b: c: for(;;)', this returns the for loop.
2178+
const Stmt *getInnermostLabeledStmt() const;
2179+
Stmt *getInnermostLabeledStmt() {
2180+
return const_cast<Stmt *>(
2181+
const_cast<const LabelStmt *>(this)->getInnermostLabeledStmt());
2182+
}
2183+
21872184
child_range children() { return child_range(&SubStmt, &SubStmt + 1); }
21882185

21892186
const_child_range children() const {
@@ -3056,26 +3053,53 @@ class IndirectGotoStmt : public Stmt {
30563053
}
30573054
};
30583055

3059-
/// ContinueStmt - This represents a continue.
3060-
class ContinueStmt : public Stmt {
3061-
public:
3062-
ContinueStmt(SourceLocation CL) : Stmt(ContinueStmtClass) {
3063-
setContinueLoc(CL);
3056+
/// Base class for BreakStmt and ContinueStmt.
3057+
class LoopControlStmt : public Stmt {
3058+
/// If this is a named break/continue, the label whose statement we're
3059+
/// targeting, as well as the source location of the label after the
3060+
/// keyword; for example:
3061+
///
3062+
/// a: // <-- TargetLabel
3063+
/// for (;;)
3064+
/// break a; // <-- LabelLoc
3065+
///
3066+
LabelDecl *TargetLabel = nullptr;
3067+
SourceLocation LabelLoc;
3068+
3069+
protected:
3070+
LoopControlStmt(StmtClass Class, SourceLocation Loc, SourceLocation LabelLoc,
3071+
LabelDecl *Target)
3072+
: Stmt(Class), TargetLabel(Target), LabelLoc(LabelLoc) {
3073+
setKwLoc(Loc);
30643074
}
30653075

3066-
/// Build an empty continue statement.
3067-
explicit ContinueStmt(EmptyShell Empty) : Stmt(ContinueStmtClass, Empty) {}
3076+
LoopControlStmt(StmtClass Class, SourceLocation Loc)
3077+
: LoopControlStmt(Class, Loc, SourceLocation(), nullptr) {}
30683078

3069-
SourceLocation getContinueLoc() const { return ContinueStmtBits.ContinueLoc; }
3070-
void setContinueLoc(SourceLocation L) { ContinueStmtBits.ContinueLoc = L; }
3079+
LoopControlStmt(StmtClass Class, EmptyShell ES) : Stmt(Class, ES) {}
30713080

3072-
SourceLocation getBeginLoc() const { return getContinueLoc(); }
3073-
SourceLocation getEndLoc() const { return getContinueLoc(); }
3081+
public:
3082+
SourceLocation getKwLoc() const { return LoopControlStmtBits.KwLoc; }
3083+
void setKwLoc(SourceLocation L) { LoopControlStmtBits.KwLoc = L; }
30743084

3075-
static bool classof(const Stmt *T) {
3076-
return T->getStmtClass() == ContinueStmtClass;
3085+
SourceLocation getBeginLoc() const { return getKwLoc(); }
3086+
SourceLocation getEndLoc() const {
3087+
return hasLabelTarget() ? getLabelLoc() : getKwLoc();
30773088
}
30783089

3090+
bool hasLabelTarget() const { return TargetLabel != nullptr; }
3091+
3092+
SourceLocation getLabelLoc() const { return LabelLoc; }
3093+
void setLabelLoc(SourceLocation L) { LabelLoc = L; }
3094+
3095+
LabelDecl *getLabelDecl() { return TargetLabel; }
3096+
const LabelDecl *getLabelDecl() const { return TargetLabel; }
3097+
void setLabelDecl(LabelDecl *S) { TargetLabel = S; }
3098+
3099+
/// If this is a named break/continue, get the loop or switch statement
3100+
/// that this targets.
3101+
const Stmt *getNamedLoopOrSwitch() const;
3102+
30793103
// Iterators
30803104
child_range children() {
30813105
return child_range(child_iterator(), child_iterator());
@@ -3084,35 +3108,42 @@ class ContinueStmt : public Stmt {
30843108
const_child_range children() const {
30853109
return const_child_range(const_child_iterator(), const_child_iterator());
30863110
}
3087-
};
30883111

3089-
/// BreakStmt - This represents a break.
3090-
class BreakStmt : public Stmt {
3091-
public:
3092-
BreakStmt(SourceLocation BL) : Stmt(BreakStmtClass) {
3093-
setBreakLoc(BL);
3112+
static bool classof(const Stmt *T) {
3113+
StmtClass Class = T->getStmtClass();
3114+
return Class == ContinueStmtClass || Class == BreakStmtClass;
30943115
}
3116+
};
30953117

3096-
/// Build an empty break statement.
3097-
explicit BreakStmt(EmptyShell Empty) : Stmt(BreakStmtClass, Empty) {}
3098-
3099-
SourceLocation getBreakLoc() const { return BreakStmtBits.BreakLoc; }
3100-
void setBreakLoc(SourceLocation L) { BreakStmtBits.BreakLoc = L; }
3118+
/// ContinueStmt - This represents a continue.
3119+
class ContinueStmt : public LoopControlStmt {
3120+
public:
3121+
ContinueStmt(SourceLocation CL) : LoopControlStmt(ContinueStmtClass, CL) {}
3122+
ContinueStmt(SourceLocation CL, SourceLocation LabelLoc, LabelDecl *Target)
3123+
: LoopControlStmt(ContinueStmtClass, CL, LabelLoc, Target) {}
31013124

3102-
SourceLocation getBeginLoc() const { return getBreakLoc(); }
3103-
SourceLocation getEndLoc() const { return getBreakLoc(); }
3125+
/// Build an empty continue statement.
3126+
explicit ContinueStmt(EmptyShell Empty)
3127+
: LoopControlStmt(ContinueStmtClass, Empty) {}
31043128

31053129
static bool classof(const Stmt *T) {
3106-
return T->getStmtClass() == BreakStmtClass;
3130+
return T->getStmtClass() == ContinueStmtClass;
31073131
}
3132+
};
31083133

3109-
// Iterators
3110-
child_range children() {
3111-
return child_range(child_iterator(), child_iterator());
3112-
}
3134+
/// BreakStmt - This represents a break.
3135+
class BreakStmt : public LoopControlStmt {
3136+
public:
3137+
BreakStmt(SourceLocation BL) : LoopControlStmt(BreakStmtClass, BL) {}
3138+
BreakStmt(SourceLocation CL, SourceLocation LabelLoc, LabelDecl *Target)
3139+
: LoopControlStmt(BreakStmtClass, CL, LabelLoc, Target) {}
31133140

3114-
const_child_range children() const {
3115-
return const_child_range(const_child_iterator(), const_child_iterator());
3141+
/// Build an empty break statement.
3142+
explicit BreakStmt(EmptyShell Empty)
3143+
: LoopControlStmt(BreakStmtClass, Empty) {}
3144+
3145+
static bool classof(const Stmt *T) {
3146+
return T->getStmtClass() == BreakStmtClass;
31163147
}
31173148
};
31183149

clang/include/clang/AST/TextNodeDumper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ class TextNodeDumper
255255
void VisitExpressionTemplateArgument(const TemplateArgument &TA);
256256
void VisitPackTemplateArgument(const TemplateArgument &TA);
257257

258+
void VisitLoopControlStmt(const LoopControlStmt *L);
258259
void VisitIfStmt(const IfStmt *Node);
259260
void VisitSwitchStmt(const SwitchStmt *Node);
260261
void VisitWhileStmt(const WhileStmt *Node);

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ def warn_c23_compat_case_range : Warning<
215215
DefaultIgnore, InGroup<CPre2yCompat>;
216216
def ext_c2y_case_range : Extension<
217217
"case ranges are a C2y extension">, InGroup<C2y>;
218+
def err_c2y_labeled_break_continue : Error<
219+
"named %select{'break'|'continue'}0 is only supported in C2y">;
218220

219221
// Generic errors.
220222
def err_expected_expression : Error<"expected expression">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10824,6 +10824,11 @@ def err_continue_not_in_loop : Error<
1082410824
"'continue' statement not in loop statement">;
1082510825
def err_break_not_in_loop_or_switch : Error<
1082610826
"'break' statement not in loop or switch statement">;
10827+
def err_break_continue_label_not_found : Error<
10828+
"'%select{break|continue}0' label does not name an enclosing "
10829+
"%select{loop or 'switch'|loop}0">;
10830+
def err_continue_switch : Error<
10831+
"label of 'continue' refers to a switch statement">;
1082710832
def warn_loop_ctrl_binds_to_inner : Warning<
1082810833
"'%0' is bound to current loop, GCC binds it to the enclosing loop">,
1082910834
InGroup<GccCompat>;

clang/include/clang/Basic/LangOptions.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ LANGOPT(NoHonorInfs , 1, 0, Benign, "Permit Floating Point optimization wi
193193
LANGOPT(NoSignedZero , 1, 0, Benign, "Permit Floating Point optimization without regard to signed zeros")
194194
LANGOPT(AllowRecip , 1, 0, Benign, "Permit Floating Point reciprocal")
195195
LANGOPT(ApproxFunc , 1, 0, Benign, "Permit Floating Point approximation")
196+
LANGOPT(NamedLoops , 1, 0, Benign, "Permit named break/continue")
196197

197198
ENUM_LANGOPT(ComplexRange, ComplexRangeKind, 3, CX_None, NotCompatible, "Enable use of range reduction for complex arithmetics.")
198199

clang/include/clang/Basic/StmtNodes.td

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ def DoStmt : StmtNode<Stmt>;
1616
def ForStmt : StmtNode<Stmt>;
1717
def GotoStmt : StmtNode<Stmt>;
1818
def IndirectGotoStmt : StmtNode<Stmt>;
19-
def ContinueStmt : StmtNode<Stmt>;
20-
def BreakStmt : StmtNode<Stmt>;
2119
def ReturnStmt : StmtNode<Stmt>;
2220
def DeclStmt : StmtNode<Stmt>;
2321
def SwitchCase : StmtNode<Stmt, 1>;
@@ -26,6 +24,11 @@ def DefaultStmt : StmtNode<SwitchCase>;
2624
def CapturedStmt : StmtNode<Stmt>;
2725
def SYCLKernelCallStmt : StmtNode<Stmt>;
2826

27+
// Break/continue.
28+
def LoopControlStmt : StmtNode<Stmt, 1>;
29+
def ContinueStmt : StmtNode<LoopControlStmt>;
30+
def BreakStmt : StmtNode<LoopControlStmt>;
31+
2932
// Statements that might produce a value (for example, as the last non-null
3033
// statement in a GNU statement-expression).
3134
def ValueStmt : StmtNode<Stmt, 1>;

clang/include/clang/Driver/Options.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1642,6 +1642,17 @@ defm auto_import : BoolFOption<"auto-import",
16421642
def offload_EQ : CommaJoined<["--"], "offload=">, Flags<[NoXarchOption]>, Alias<offload_targets_EQ>,
16431643
HelpText<"Specify comma-separated list of offloading target triples (CUDA and HIP only)">;
16441644

1645+
// This flag is only here so we can test the named loops implementation
1646+
// in C++ mode and C language modes before C2y to make sure it actually
1647+
// works; it should be removed once the syntax of the feature is stable
1648+
// enough to backport it to earlier language modes (and to C++ if it ever
1649+
// gets standardised as a C++ feature).
1650+
defm named_loops
1651+
: BoolFOption<
1652+
"named-loops", LangOpts<"NamedLoops">, DefaultFalse,
1653+
PosFlag<SetTrue, [], [CC1Option], "Enable support for named loops">,
1654+
NegFlag<SetFalse>>;
1655+
16451656
// C++ Coroutines
16461657
defm coroutines : BoolFOption<"coroutines",
16471658
LangOpts<"Coroutines">, Default<cpp20.KeyPath>,

0 commit comments

Comments
 (0)