Skip to content

Commit 8bd06d5

Browse files
authored
[C23] Complete support for WG14 N2508 (#71398)
In Clang 16, we implemented the ability to add a label at the end of a compound statement. These changes complete the implementation by allowing a label to be followed by a declaration in C. Note, this seems to have fixed an issue with some OpenMP stand-alone directives not being properly diagnosed as per: https://www.openmp.org/spec-html/5.1/openmpsu19.html#x34-330002.1.3 (The same requirement exists in OpenMP 5.2 as well.)
1 parent 37fa327 commit 8bd06d5

17 files changed

+151
-68
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,12 @@ C23 Feature Support
209209
- Clang now supports ``<stdckdint.h>`` which defines several macros for performing
210210
checked integer arithmetic. It is also exposed in pre-C23 modes.
211211

212+
- Completed the implementation of
213+
`N2508 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2508.pdf>`_. We
214+
previously implemented allowing a label at the end of a compound statement,
215+
and now we've implemented allowing a label to be followed by a declaration
216+
instead of a statement.
217+
212218
Non-comprehensive list of changes in this release
213219
-------------------------------------------------
214220

@@ -566,6 +572,23 @@ Bug Fixes in This Version
566572
Fixes (`#67687 <https://github.com/llvm/llvm-project/issues/67687>`_)
567573
- Fix crash from constexpr evaluator evaluating uninitialized arrays as rvalue.
568574
Fixes (`#67317 <https://github.com/llvm/llvm-project/issues/67317>`_)
575+
- Clang now properly diagnoses use of stand-alone OpenMP directives after a
576+
label (including ``case`` or ``default`` labels).
577+
578+
Before:
579+
580+
.. code-block:: c++
581+
582+
label:
583+
#pragma omp barrier // ok
584+
585+
After:
586+
587+
.. code-block:: c++
588+
589+
label:
590+
#pragma omp barrier // error: '#pragma omp barrier' cannot be an immediate substatement
591+
569592
- Fixed an issue that a benign assertion might hit when instantiating a pack expansion
570593
inside a lambda. (`#61460 <https://github.com/llvm/llvm-project/issues/61460>`_)
571594

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,12 @@ def note_missing_selector_name : Note<
299299
def note_force_empty_selector_name : Note<
300300
"or insert whitespace before ':' to use %0 as parameter name "
301301
"and have an empty entry in the selector">;
302+
def ext_c_label_followed_by_declaration : ExtWarn<
303+
"label followed by a declaration is a C23 extension">,
304+
InGroup<C23>;
305+
def warn_c23_compat_label_followed_by_declaration : Warning<
306+
"label followed by a declaration is incompatible with C standards before "
307+
"C23">, InGroup<CPre23Compat>, DefaultIgnore;
302308
def ext_c_label_end_of_compound_statement : ExtWarn<
303309
"label at end of compound statement is a C23 extension">,
304310
InGroup<C23>;

clang/include/clang/Parse/Parser.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -415,18 +415,15 @@ class Parser : public CodeCompletionHandler {
415415

416416
/// Flags describing a context in which we're parsing a statement.
417417
enum class ParsedStmtContext {
418-
/// This context permits declarations in language modes where declarations
419-
/// are not statements.
420-
AllowDeclarationsInC = 0x1,
421418
/// This context permits standalone OpenMP directives.
422-
AllowStandaloneOpenMPDirectives = 0x2,
419+
AllowStandaloneOpenMPDirectives = 0x1,
423420
/// This context is at the top level of a GNU statement expression.
424-
InStmtExpr = 0x4,
421+
InStmtExpr = 0x2,
425422

426423
/// The context of a regular substatement.
427424
SubStmt = 0,
428425
/// The context of a compound-statement.
429-
Compound = AllowDeclarationsInC | AllowStandaloneOpenMPDirectives,
426+
Compound = AllowStandaloneOpenMPDirectives,
430427

431428
LLVM_MARK_AS_BITMASK_ENUM(InStmtExpr)
432429
};

clang/lib/Parse/ParseOpenMP.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2670,7 +2670,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
26702670
}
26712671
case OMPD_threadprivate: {
26722672
// FIXME: Should this be permitted in C++?
2673-
if ((StmtCtx & ParsedStmtContext::AllowDeclarationsInC) ==
2673+
if ((StmtCtx & ParsedStmtContext::AllowStandaloneOpenMPDirectives) ==
26742674
ParsedStmtContext()) {
26752675
Diag(Tok, diag::err_omp_immediate_directive)
26762676
<< getOpenMPDirectiveName(DKind) << 0;
@@ -2689,7 +2689,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
26892689
}
26902690
case OMPD_allocate: {
26912691
// FIXME: Should this be permitted in C++?
2692-
if ((StmtCtx & ParsedStmtContext::AllowDeclarationsInC) ==
2692+
if ((StmtCtx & ParsedStmtContext::AllowStandaloneOpenMPDirectives) ==
26932693
ParsedStmtContext()) {
26942694
Diag(Tok, diag::err_omp_immediate_directive)
26952695
<< getOpenMPDirectiveName(DKind) << 0;

clang/lib/Parse/ParseStmt.cpp

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -235,10 +235,7 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
235235
auto IsStmtAttr = [](ParsedAttr &Attr) { return Attr.isStmtAttr(); };
236236
bool AllAttrsAreStmtAttrs = llvm::all_of(CXX11Attrs, IsStmtAttr) &&
237237
llvm::all_of(GNUAttrs, IsStmtAttr);
238-
if ((getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt ||
239-
(StmtCtx & ParsedStmtContext::AllowDeclarationsInC) !=
240-
ParsedStmtContext()) &&
241-
((GNUAttributeLoc.isValid() && !(HaveAttrs && AllAttrsAreStmtAttrs)) ||
238+
if (((GNUAttributeLoc.isValid() && !(HaveAttrs && AllAttrsAreStmtAttrs)) ||
242239
isDeclarationStatement())) {
243240
SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
244241
DeclGroupPtrTy Decl;
@@ -701,6 +698,18 @@ StmtResult Parser::ParseSEHLeaveStatement() {
701698
return Actions.ActOnSEHLeaveStmt(LeaveLoc, getCurScope());
702699
}
703700

701+
static void DiagnoseLabelFollowedByDecl(Parser &P, const Stmt *SubStmt) {
702+
// When in C mode (but not Microsoft extensions mode), diagnose use of a
703+
// label that is followed by a declaration rather than a statement.
704+
if (!P.getLangOpts().CPlusPlus && !P.getLangOpts().MicrosoftExt &&
705+
isa<DeclStmt>(SubStmt)) {
706+
P.Diag(SubStmt->getBeginLoc(),
707+
P.getLangOpts().C23
708+
? diag::warn_c23_compat_label_followed_by_declaration
709+
: diag::ext_c_label_followed_by_declaration);
710+
}
711+
}
712+
704713
/// ParseLabeledStatement - We have an identifier and a ':' after it.
705714
///
706715
/// label:
@@ -715,9 +724,10 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs,
715724
assert(Tok.is(tok::identifier) && Tok.getIdentifierInfo() &&
716725
"Not an identifier!");
717726

718-
// The substatement is always a 'statement', not a 'declaration', but is
719-
// otherwise in the same context as the labeled-statement.
720-
StmtCtx &= ~ParsedStmtContext::AllowDeclarationsInC;
727+
// [OpenMP 5.1] 2.1.3: A stand-alone directive may not be used in place of a
728+
// substatement in a selection statement, in place of the loop body in an
729+
// iteration statement, or in place of the statement that follows a label.
730+
StmtCtx &= ~ParsedStmtContext::AllowStandaloneOpenMPDirectives;
721731

722732
Token IdentTok = Tok; // Save the whole token.
723733
ConsumeToken(); // eat the identifier.
@@ -766,6 +776,8 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs,
766776
if (SubStmt.isInvalid())
767777
SubStmt = Actions.ActOnNullStmt(ColonLoc);
768778

779+
DiagnoseLabelFollowedByDecl(*this, SubStmt.get());
780+
769781
LabelDecl *LD = Actions.LookupOrCreateLabel(IdentTok.getIdentifierInfo(),
770782
IdentTok.getLocation());
771783
Actions.ProcessDeclAttributeList(Actions.CurScope, LD, Attrs);
@@ -784,9 +796,10 @@ StmtResult Parser::ParseCaseStatement(ParsedStmtContext StmtCtx,
784796
bool MissingCase, ExprResult Expr) {
785797
assert((MissingCase || Tok.is(tok::kw_case)) && "Not a case stmt!");
786798

787-
// The substatement is always a 'statement', not a 'declaration', but is
788-
// otherwise in the same context as the labeled-statement.
789-
StmtCtx &= ~ParsedStmtContext::AllowDeclarationsInC;
799+
// [OpenMP 5.1] 2.1.3: A stand-alone directive may not be used in place of a
800+
// substatement in a selection statement, in place of the loop body in an
801+
// iteration statement, or in place of the statement that follows a label.
802+
StmtCtx &= ~ParsedStmtContext::AllowStandaloneOpenMPDirectives;
790803

791804
// It is very common for code to contain many case statements recursively
792805
// nested, as in (but usually without indentation):
@@ -912,6 +925,7 @@ StmtResult Parser::ParseCaseStatement(ParsedStmtContext StmtCtx,
912925
// Broken sub-stmt shouldn't prevent forming the case statement properly.
913926
if (SubStmt.isInvalid())
914927
SubStmt = Actions.ActOnNullStmt(SourceLocation());
928+
DiagnoseLabelFollowedByDecl(*this, SubStmt.get());
915929
Actions.ActOnCaseStmtBody(DeepestParsedCaseStmt, SubStmt.get());
916930
}
917931

@@ -927,9 +941,10 @@ StmtResult Parser::ParseCaseStatement(ParsedStmtContext StmtCtx,
927941
StmtResult Parser::ParseDefaultStatement(ParsedStmtContext StmtCtx) {
928942
assert(Tok.is(tok::kw_default) && "Not a default stmt!");
929943

930-
// The substatement is always a 'statement', not a 'declaration', but is
931-
// otherwise in the same context as the labeled-statement.
932-
StmtCtx &= ~ParsedStmtContext::AllowDeclarationsInC;
944+
// [OpenMP 5.1] 2.1.3: A stand-alone directive may not be used in place of a
945+
// substatement in a selection statement, in place of the loop body in an
946+
// iteration statement, or in place of the statement that follows a label.
947+
StmtCtx &= ~ParsedStmtContext::AllowStandaloneOpenMPDirectives;
933948

934949
SourceLocation DefaultLoc = ConsumeToken(); // eat the 'default'.
935950

@@ -963,6 +978,7 @@ StmtResult Parser::ParseDefaultStatement(ParsedStmtContext StmtCtx) {
963978
if (SubStmt.isInvalid())
964979
SubStmt = Actions.ActOnNullStmt(ColonLoc);
965980

981+
DiagnoseLabelFollowedByDecl(*this, SubStmt.get());
966982
return Actions.ActOnDefaultStmt(DefaultLoc, ColonLoc,
967983
SubStmt.get(), getCurScope());
968984
}

clang/test/C/C2x/n2508.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,50 @@
1-
// RUN: %clang_cc1 -verify -std=c2x %s
1+
// RUN: %clang_cc1 -verify -std=c23 %s
2+
// RUN: %clang_cc1 -verify=pedantic -std=c11 -pedantic %s
3+
// RUN: %clang_cc1 -verify=compat -std=c23 -Wpre-c23-compat %s
24

35
// expected-no-diagnostics
46

57
/* WG14 N2508: yes
68
* Free positioning of labels inside compound statements
79
*/
8-
void test() {
10+
void test(void) {
911
{
1012
inner:
11-
}
13+
} /* pedantic-warning {{label at end of compound statement is a C23 extension}}
14+
compat-warning {{label at end of compound statement is incompatible with C standards before C23}}
15+
*/
1216

1317
switch (1) {
1418
case 1:
15-
}
19+
} /* pedantic-warning {{label at end of compound statement is a C23 extension}}
20+
compat-warning {{label at end of compound statement is incompatible with C standards before C23}}
21+
*/
1622

1723
{
1824
multiple: labels: on: a: line:
19-
}
25+
} /* pedantic-warning {{label at end of compound statement is a C23 extension}}
26+
compat-warning {{label at end of compound statement is incompatible with C standards before C23}}
27+
*/
2028

2129
final:
22-
}
30+
} /* pedantic-warning {{label at end of compound statement is a C23 extension}}
31+
compat-warning {{label at end of compound statement is incompatible with C standards before C23}}
32+
*/
2333

34+
void test_labels(void) {
35+
label:
36+
int i = 0; /* pedantic-warning {{label followed by a declaration is a C23 extension}}
37+
compat-warning {{label followed by a declaration is incompatible with C standards before C23}}
38+
*/
39+
40+
switch (i) {
41+
case 1:
42+
_Static_assert(1, ""); /* pedantic-warning {{label followed by a declaration is a C23 extension}}
43+
compat-warning {{label followed by a declaration is incompatible with C standards before C23}}
44+
*/
45+
default:
46+
_Static_assert(1, ""); /* pedantic-warning {{label followed by a declaration is a C23 extension}}
47+
compat-warning {{label followed by a declaration is incompatible with C standards before C23}}
48+
*/
49+
}
50+
}

clang/test/OpenMP/barrier_ast_print.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ T tmain(T argc) {
1717
static T a;
1818
#pragma omp barrier
1919
switch (argc) {
20-
case 0:
20+
case 0: {
2121
#pragma omp barrier
22-
break;
23-
default:
22+
} break;
23+
default: {
2424
#pragma omp barrier
2525
#pragma omp barrier
26-
break;
26+
} break;
2727
}
2828
return a + argc;
2929
}
@@ -35,11 +35,15 @@ T tmain(T argc) {
3535
// CHECK-NEXT: #pragma omp barrier
3636
// CHECK-NEXT: switch (argc) {
3737
// CHECK-NEXT: case 0:
38+
// CHECK-NEXT: {
3839
// CHECK-NEXT: #pragma omp barrier
40+
// CHECK-NEXT: }
3941
// CHECK-NEXT: break;
4042
// CHECK-NEXT: default:
43+
// CHECK-NEXT: {
4144
// CHECK-NEXT: #pragma omp barrier
4245
// CHECK-NEXT: #pragma omp barrier
46+
// CHECK-NEXT: }
4347
// CHECK-NEXT: break;
4448
// CHECK-NEXT: }
4549

@@ -49,21 +53,25 @@ int main(int argc, char **argv) {
4953
#pragma omp barrier
5054
// CHECK-NEXT: #pragma omp barrier
5155
switch (argc) {
52-
case 0:
56+
case 0: {
5357
#pragma omp barrier
5458
#pragma omp barrier
55-
break;
56-
default:
59+
} break;
60+
default: {
5761
#pragma omp barrier
58-
break;
62+
} break;
5963
}
6064
// CHECK-NEXT: switch (argc) {
6165
// CHECK-NEXT: case 0:
66+
// CHECK-NEXT: {
6267
// CHECK-NEXT: #pragma omp barrier
6368
// CHECK-NEXT: #pragma omp barrier
69+
// CHECK-NEXT: }
6470
// CHECK-NEXT: break;
6571
// CHECK-NEXT: default:
72+
// CHECK-NEXT: {
6673
// CHECK-NEXT: #pragma omp barrier
74+
// CHECK-NEXT: }
6775
// CHECK-NEXT: break;
6876
// CHECK-NEXT: }
6977
return tmain(argc) + tmain(argv[0][0]) + a;

clang/test/OpenMP/barrier_messages.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ T tmain(T argc) {
3838
switch (argc) {
3939
#pragma omp barrier
4040
case 1:
41-
#pragma omp barrier
41+
#pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}}
4242
break;
4343
default: {
4444
#pragma omp barrier
@@ -50,7 +50,7 @@ T tmain(T argc) {
5050
#pragma omp barrier
5151
}
5252
label:
53-
#pragma omp barrier
53+
#pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}}
5454
label1 : {
5555
#pragma omp barrier
5656
}
@@ -92,7 +92,7 @@ int main(int argc, char **argv) {
9292
switch (argc) {
9393
#pragma omp barrier
9494
case 1:
95-
#pragma omp barrier
95+
#pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}}
9696
break;
9797
default: {
9898
#pragma omp barrier
@@ -104,7 +104,7 @@ int main(int argc, char **argv) {
104104
#pragma omp barrier
105105
}
106106
label:
107-
#pragma omp barrier
107+
#pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}}
108108
label1 : {
109109
#pragma omp barrier
110110
}

clang/test/OpenMP/cancel_messages.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ int main(int argc, char **argv) {
7171
switch (argc) {
7272
#pragma omp cancel taskgroup // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
7373
case 1:
74-
#pragma omp cancel parallel // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
74+
#pragma omp cancel parallel // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}} \
75+
expected-error {{'#pragma omp cancel' cannot be an immediate substatement}}
7576
break;
7677
default: {
7778
#pragma omp cancel sections // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
@@ -83,7 +84,8 @@ int main(int argc, char **argv) {
8384
#pragma omp cancel taskgroup // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
8485
}
8586
label:
86-
#pragma omp cancel parallel // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
87+
#pragma omp cancel parallel // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}} \
88+
expected-error {{'#pragma omp cancel' cannot be an immediate substatement}}
8789
label1 : {
8890
#pragma omp cancel sections // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
8991
}

clang/test/OpenMP/cancellation_point_messages.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ int main(int argc, char **argv) {
7171
switch (argc) {
7272
#pragma omp cancellation point taskgroup // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
7373
case 1:
74-
#pragma omp cancellation point parallel // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
74+
#pragma omp cancellation point parallel // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}} \
75+
expected-error {{'#pragma omp cancellation point' cannot be an immediate substatement}}
7576
break;
7677
default: {
7778
#pragma omp cancellation point sections // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
@@ -83,7 +84,8 @@ int main(int argc, char **argv) {
8384
#pragma omp cancellation point taskgroup // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
8485
}
8586
label:
86-
#pragma omp cancellation point parallel // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
87+
#pragma omp cancellation point parallel // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}} \
88+
expected-error {{'#pragma omp cancellation point' cannot be an immediate substatement}}
8789
label1 : {
8890
#pragma omp cancellation point sections // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
8991
}

0 commit comments

Comments
 (0)