Skip to content

Commit 00062fb

Browse files
committed
[Clang] [C++26] Expansion Statements (Part 9)
1 parent c93a279 commit 00062fb

File tree

7 files changed

+205
-15
lines changed

7 files changed

+205
-15
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3706,6 +3706,12 @@ def err_expansion_stmt_invalid_init : Error<
37063706
"cannot expand expression of type %0">;
37073707
def err_expansion_stmt_lambda : Error<
37083708
"cannot expand lambda closure type">;
3709+
def err_expansion_stmt_case : Error<
3710+
"%select{'case'|'default'}0 belongs to 'switch' outside enclosing expansion statement">;
3711+
def note_enclosing_switch_statement_here : Note<
3712+
"switch statement is here">;
3713+
def err_expansion_stmt_label : Error<
3714+
"labels are not allowed in expansion statements">;
37093715

37103716
def err_attribute_patchable_function_entry_invalid_section
37113717
: Error<"section argument to 'patchable_function_entry' attribute is not "

clang/include/clang/Sema/ScopeInfo.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,11 @@ class FunctionScopeInfo {
202202
public:
203203
/// A SwitchStmt, along with a flag indicating if its list of case statements
204204
/// is incomplete (because we dropped an invalid one while parsing).
205-
using SwitchInfo = llvm::PointerIntPair<SwitchStmt*, 1, bool>;
205+
struct SwitchInfo : llvm::PointerIntPair<SwitchStmt *, 1, bool> {
206+
DeclContext *EnclosingDC;
207+
SwitchInfo(SwitchStmt *Switch, DeclContext *DC)
208+
: PointerIntPair(Switch, false), EnclosingDC(DC) {}
209+
};
206210

207211
/// SwitchStack - This is the current set of active switch statements in the
208212
/// block.

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9519,7 +9519,8 @@ class Sema final : public SemaBase {
95199519
/// of an __label__ label name, otherwise it is a normal label definition
95209520
/// or use.
95219521
LabelDecl *LookupOrCreateLabel(IdentifierInfo *II, SourceLocation IdentLoc,
9522-
SourceLocation GnuLabelLoc = SourceLocation());
9522+
SourceLocation GnuLabelLoc = SourceLocation(),
9523+
bool ForLabelStmt = false);
95239524

95249525
/// Perform a name lookup for a label with the specified name; this does not
95259526
/// create a new label if the lookup fails.

clang/lib/Parse/ParseStmt.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -715,8 +715,9 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs,
715715
// identifier ':' statement
716716
SourceLocation ColonLoc = ConsumeToken();
717717

718-
LabelDecl *LD = Actions.LookupOrCreateLabel(IdentTok.getIdentifierInfo(),
719-
IdentTok.getLocation());
718+
LabelDecl *LD = Actions.LookupOrCreateLabel(
719+
IdentTok.getIdentifierInfo(), IdentTok.getLocation(), /*GnuLabelLoc=*/{},
720+
/*ForLabelStmt=*/true);
720721

721722
// Read label attributes, if present.
722723
StmtResult SubStmt;
@@ -760,6 +761,12 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs,
760761

761762
DiagnoseLabelFollowedByDecl(*this, SubStmt.get());
762763

764+
// If a label cannot appear here, just return the underlying statement.
765+
if (!LD) {
766+
Attrs.clear();
767+
return SubStmt.get();
768+
}
769+
763770
Actions.ProcessDeclAttributeList(Actions.CurScope, LD, Attrs);
764771
Attrs.clear();
765772

clang/lib/Sema/SemaLookup.cpp

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4463,7 +4463,8 @@ LabelDecl *Sema::LookupExistingLabel(IdentifierInfo *II, SourceLocation Loc) {
44634463
}
44644464

44654465
LabelDecl *Sema::LookupOrCreateLabel(IdentifierInfo *II, SourceLocation Loc,
4466-
SourceLocation GnuLabelLoc) {
4466+
SourceLocation GnuLabelLoc,
4467+
bool ForLabelStmt) {
44674468
if (GnuLabelLoc.isValid()) {
44684469
// Local label definitions always shadow existing labels.
44694470
auto *Res = LabelDecl::Create(Context, CurContext, Loc, II, GnuLabelLoc);
@@ -4472,15 +4473,43 @@ LabelDecl *Sema::LookupOrCreateLabel(IdentifierInfo *II, SourceLocation Loc,
44724473
return cast<LabelDecl>(Res);
44734474
}
44744475

4475-
// Not a GNU local label.
4476-
LabelDecl *Res = LookupExistingLabel(II, Loc);
4477-
if (!Res) {
4478-
// If not forward referenced or defined already, create the backing decl.
4479-
Res = LabelDecl::Create(Context, CurContext, Loc, II);
4480-
Scope *S = CurScope->getFnParent();
4481-
assert(S && "Not in a function?");
4482-
PushOnScopeChains(Res, S, true);
4476+
LabelDecl *Existing = LookupExistingLabel(II, Loc);
4477+
4478+
// C++26 [stmt.label]p4 An identifier label shall not be enclosed by an
4479+
// expansion-statement.
4480+
//
4481+
// As an extension, we allow GNU local labels since they are logically
4482+
// scoped to the containing block, which prevents us from ending up with
4483+
// multiple copies of the same label in a function after instantiation.
4484+
//
4485+
// While allowing this is slightly more complicated, it also has the nice
4486+
// side-effect of avoiding otherwise rather horrible diagnostics you'd get
4487+
// when trying to use '__label__' if we didn't support this.
4488+
if (ForLabelStmt && CurContext->isExpansionStmt()) {
4489+
if (Existing && Existing->isGnuLocal())
4490+
return Existing;
4491+
4492+
// Drop the label from the AST as creating it anyway would cause us to
4493+
// either issue various unhelpful diagnostics (if we were to declare
4494+
// it in the function decl context) or shadow a valid label with the
4495+
// same name outside the expansion statement.
4496+
Diag(Loc, diag::err_expansion_stmt_label);
4497+
return nullptr;
44834498
}
4499+
4500+
if (Existing)
4501+
return Existing;
4502+
4503+
// Declare non-local labels outside any expansion statements; this is required
4504+
// to support jumping out of an expansion statement.
4505+
ContextRAII Ctx{*this, CurContext->getEnclosingNonExpansionStatementContext(),
4506+
/*NewThisContext=*/false};
4507+
4508+
// Not a GNU local label. Create the backing decl.
4509+
auto *Res = LabelDecl::Create(Context, CurContext, Loc, II);
4510+
Scope *S = CurScope->getFnParent();
4511+
assert(S && "Not in a function?");
4512+
PushOnScopeChains(Res, S, true);
44844513
return Res;
44854514
}
44864515

clang/lib/Sema/SemaStmt.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,25 @@ Sema::ActOnCaseExpr(SourceLocation CaseLoc, ExprResult Val) {
528528
return CheckAndFinish(Val.get());
529529
}
530530

531+
static bool DiagnoseSwitchCaseInExpansionStmt(Sema &S, SourceLocation KwLoc,
532+
bool IsDefault) {
533+
// C++26 [stmt.expand] The compound-statement of an expansion-statement is a
534+
// control-flow-limited statement.
535+
//
536+
// We diagnose this here rather than in JumpDiagnostics because those run
537+
// after the expansion statement is instantiated, at which point we will have
538+
// have already complained about duplicate case labels, which is not exactly
539+
// great QOI.
540+
if (S.CurContext->isExpansionStmt() &&
541+
S.getCurFunction()->SwitchStack.back().EnclosingDC != S.CurContext) {
542+
S.Diag(KwLoc, diag::err_expansion_stmt_case) << IsDefault;
543+
S.Diag(S.getCurFunction()->SwitchStack.back().getPointer()->getSwitchLoc(),
544+
diag::note_enclosing_switch_statement_here);
545+
return true;
546+
}
547+
return false;
548+
}
549+
531550
StmtResult
532551
Sema::ActOnCaseStmt(SourceLocation CaseLoc, ExprResult LHSVal,
533552
SourceLocation DotDotDotLoc, ExprResult RHSVal,
@@ -547,6 +566,9 @@ Sema::ActOnCaseStmt(SourceLocation CaseLoc, ExprResult LHSVal,
547566
return StmtError();
548567
}
549568

569+
if (DiagnoseSwitchCaseInExpansionStmt(*this, CaseLoc, false))
570+
return StmtError();
571+
550572
if (LangOpts.OpenACC &&
551573
getCurScope()->isInOpenACCComputeConstructScope(Scope::SwitchScope)) {
552574
Diag(CaseLoc, diag::err_acc_branch_in_out_compute_construct)
@@ -572,6 +594,9 @@ Sema::ActOnDefaultStmt(SourceLocation DefaultLoc, SourceLocation ColonLoc,
572594
return SubStmt;
573595
}
574596

597+
if (DiagnoseSwitchCaseInExpansionStmt(*this, DefaultLoc, true))
598+
return StmtError();
599+
575600
if (LangOpts.OpenACC &&
576601
getCurScope()->isInOpenACCComputeConstructScope(Scope::SwitchScope)) {
577602
Diag(DefaultLoc, diag::err_acc_branch_in_out_compute_construct)
@@ -1196,8 +1221,9 @@ StmtResult Sema::ActOnStartOfSwitchStmt(SourceLocation SwitchLoc,
11961221

11971222
auto *SS = SwitchStmt::Create(Context, InitStmt, Cond.get().first, CondExpr,
11981223
LParenLoc, RParenLoc);
1224+
SS->setSwitchLoc(SwitchLoc);
11991225
getCurFunction()->SwitchStack.push_back(
1200-
FunctionScopeInfo::SwitchInfo(SS, false));
1226+
FunctionScopeInfo::SwitchInfo(SS, CurContext));
12011227
return SS;
12021228
}
12031229

@@ -1313,7 +1339,7 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
13131339
BodyStmt = new (Context) NullStmt(BodyStmt->getBeginLoc());
13141340
}
13151341

1316-
SS->setBody(BodyStmt, SwitchLoc);
1342+
SS->setBody(BodyStmt);
13171343

13181344
Expr *CondExpr = SS->getCond();
13191345
if (!CondExpr) return StmtError();
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fblocks -verify
2+
3+
void g(int);
4+
5+
void label() {
6+
template for (auto x : {1, 2}) {
7+
invalid1:; // expected-error {{labels are not allowed in expansion statements}}
8+
invalid2:; // expected-error {{labels are not allowed in expansion statements}}
9+
goto invalid1; // expected-error {{use of undeclared label 'invalid1'}}
10+
}
11+
12+
template for (auto x : {1, 2}) {
13+
(void) [] {
14+
template for (auto x : {1, 2}) {
15+
invalid3:; // expected-error {{labels are not allowed in expansion statements}}
16+
}
17+
ok:;
18+
};
19+
20+
(void) ^{
21+
template for (auto x : {1, 2}) {
22+
invalid4:; // expected-error {{labels are not allowed in expansion statements}}
23+
}
24+
ok:;
25+
};
26+
27+
struct X {
28+
void f() {
29+
ok:;
30+
}
31+
};
32+
}
33+
34+
// GNU local labels are allowed.
35+
template for (auto x : {1, 2}) {
36+
__label__ a;
37+
if (x == 1) goto a;
38+
a:;
39+
if (x == 1) goto a;
40+
}
41+
42+
// Likewise, jumping *out* of an expansion statement is fine.
43+
template for (auto x : {1, 2}) {
44+
if (x == 1) goto lbl;
45+
g(x);
46+
}
47+
lbl:;
48+
template for (auto x : {1, 2}) {
49+
if (x == 1) goto lbl;
50+
g(x);
51+
}
52+
53+
// Jumping into one is not possible, as local labels aren't visible
54+
// outside the block that declares them, and non-local labels are invalid.
55+
goto exp1; // expected-error {{use of undeclared label 'exp1'}}
56+
goto exp3; // expected-error {{use of undeclared label 'exp3'}}
57+
template for (auto x : {1, 2}) {
58+
__label__ exp1, exp2;
59+
exp1:;
60+
exp2:;
61+
exp3:; // expected-error {{labels are not allowed in expansion statements}}
62+
}
63+
goto exp2; // expected-error {{use of undeclared label 'exp2'}}
64+
65+
// Allow jumping from inside an expansion statement to a local label in
66+
// one of its parents.
67+
out1:;
68+
template for (auto x : {1, 2}) {
69+
__label__ x, y;
70+
x:
71+
goto out1;
72+
goto out2;
73+
template for (auto x : {3, 4}) {
74+
goto x;
75+
goto y;
76+
goto out1;
77+
goto out2;
78+
}
79+
y:
80+
}
81+
out2:;
82+
}
83+
84+
85+
void case_default(int i) {
86+
switch (i) { // expected-note 3 {{switch statement is here}}
87+
template for (auto x : {1, 2}) {
88+
case 1:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}}
89+
template for (auto x : {1, 2}) {
90+
case 2:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}}
91+
}
92+
default: // expected-error {{'default' belongs to 'switch' outside enclosing expansion statement}}
93+
switch (i) { // expected-note {{switch statement is here}}
94+
case 3:;
95+
default:
96+
template for (auto x : {1, 2}) {
97+
case 4:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}}
98+
}
99+
}
100+
}
101+
}
102+
103+
template for (auto x : {1, 2}) {
104+
switch (i) {
105+
case 1:;
106+
default:
107+
}
108+
}
109+
110+
// Ensure that we diagnose this even if the statements would be discarded.
111+
switch (i) { // expected-note 2 {{switch statement is here}}
112+
template for (auto x : {}) {
113+
case 1:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}}
114+
default:; // expected-error {{'default' belongs to 'switch' outside enclosing expansion statement}}
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)