Skip to content

[C23] Complete support for WG14 N2508 #71398

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ C23 Feature Support
- Clang now supports ``<stdckdint.h>`` which defines several macros for performing
checked integer arithmetic. It is also exposed in pre-C23 modes.

- Completed the implementation of
`N2508 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2508.pdf>`_. We
previously implemented allowing a label at the end of a compound statement,
and now we've implemented allowing a label to be followed by a declaration
instead of a statement.

Non-comprehensive list of changes in this release
-------------------------------------------------

Expand Down Expand Up @@ -566,6 +572,23 @@ Bug Fixes in This Version
Fixes (`#67687 <https://github.com/llvm/llvm-project/issues/67687>`_)
- Fix crash from constexpr evaluator evaluating uninitialized arrays as rvalue.
Fixes (`#67317 <https://github.com/llvm/llvm-project/issues/67317>`_)
- Clang now properly diagnoses use of stand-alone OpenMP directives after a
label (including ``case`` or ``default`` labels).

Before:

.. code-block:: c++

label:
#pragma omp barrier // ok

After:

.. code-block:: c++

label:
#pragma omp barrier // error: '#pragma omp barrier' cannot be an immediate substatement

- Fixed an issue that a benign assertion might hit when instantiating a pack expansion
inside a lambda. (`#61460 <https://github.com/llvm/llvm-project/issues/61460>`_)

Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,12 @@ def note_missing_selector_name : Note<
def note_force_empty_selector_name : Note<
"or insert whitespace before ':' to use %0 as parameter name "
"and have an empty entry in the selector">;
def ext_c_label_followed_by_declaration : ExtWarn<
"label followed by a declaration is a C23 extension">,
InGroup<C23>;
def warn_c23_compat_label_followed_by_declaration : Warning<
"label followed by a declaration is incompatible with C standards before "
"C23">, InGroup<CPre23Compat>, DefaultIgnore;
def ext_c_label_end_of_compound_statement : ExtWarn<
"label at end of compound statement is a C23 extension">,
InGroup<C23>;
Expand Down
9 changes: 3 additions & 6 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -415,18 +415,15 @@ class Parser : public CodeCompletionHandler {

/// Flags describing a context in which we're parsing a statement.
enum class ParsedStmtContext {
/// This context permits declarations in language modes where declarations
/// are not statements.
AllowDeclarationsInC = 0x1,
/// This context permits standalone OpenMP directives.
AllowStandaloneOpenMPDirectives = 0x2,
AllowStandaloneOpenMPDirectives = 0x1,
/// This context is at the top level of a GNU statement expression.
InStmtExpr = 0x4,
InStmtExpr = 0x2,

/// The context of a regular substatement.
SubStmt = 0,
/// The context of a compound-statement.
Compound = AllowDeclarationsInC | AllowStandaloneOpenMPDirectives,
Compound = AllowStandaloneOpenMPDirectives,

LLVM_MARK_AS_BITMASK_ENUM(InStmtExpr)
};
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Parse/ParseOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2670,7 +2670,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
}
case OMPD_threadprivate: {
// FIXME: Should this be permitted in C++?
if ((StmtCtx & ParsedStmtContext::AllowDeclarationsInC) ==
if ((StmtCtx & ParsedStmtContext::AllowStandaloneOpenMPDirectives) ==
ParsedStmtContext()) {
Diag(Tok, diag::err_omp_immediate_directive)
<< getOpenMPDirectiveName(DKind) << 0;
Expand All @@ -2689,7 +2689,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
}
case OMPD_allocate: {
// FIXME: Should this be permitted in C++?
if ((StmtCtx & ParsedStmtContext::AllowDeclarationsInC) ==
if ((StmtCtx & ParsedStmtContext::AllowStandaloneOpenMPDirectives) ==
ParsedStmtContext()) {
Diag(Tok, diag::err_omp_immediate_directive)
<< getOpenMPDirectiveName(DKind) << 0;
Expand Down
42 changes: 29 additions & 13 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,7 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
auto IsStmtAttr = [](ParsedAttr &Attr) { return Attr.isStmtAttr(); };
bool AllAttrsAreStmtAttrs = llvm::all_of(CXX11Attrs, IsStmtAttr) &&
llvm::all_of(GNUAttrs, IsStmtAttr);
if ((getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt ||
(StmtCtx & ParsedStmtContext::AllowDeclarationsInC) !=
ParsedStmtContext()) &&
((GNUAttributeLoc.isValid() && !(HaveAttrs && AllAttrsAreStmtAttrs)) ||
if (((GNUAttributeLoc.isValid() && !(HaveAttrs && AllAttrsAreStmtAttrs)) ||
isDeclarationStatement())) {
SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
DeclGroupPtrTy Decl;
Expand Down Expand Up @@ -701,6 +698,18 @@ StmtResult Parser::ParseSEHLeaveStatement() {
return Actions.ActOnSEHLeaveStmt(LeaveLoc, getCurScope());
}

static void DiagnoseLabelFollowedByDecl(Parser &P, const Stmt *SubStmt) {
// When in C mode (but not Microsoft extensions mode), diagnose use of a
// label that is followed by a declaration rather than a statement.
if (!P.getLangOpts().CPlusPlus && !P.getLangOpts().MicrosoftExt &&
isa<DeclStmt>(SubStmt)) {
P.Diag(SubStmt->getBeginLoc(),
P.getLangOpts().C23
? diag::warn_c23_compat_label_followed_by_declaration
: diag::ext_c_label_followed_by_declaration);
}
}

/// ParseLabeledStatement - We have an identifier and a ':' after it.
///
/// label:
Expand All @@ -715,9 +724,10 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs,
assert(Tok.is(tok::identifier) && Tok.getIdentifierInfo() &&
"Not an identifier!");

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this deserves a comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


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

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

LabelDecl *LD = Actions.LookupOrCreateLabel(IdentTok.getIdentifierInfo(),
IdentTok.getLocation());
Actions.ProcessDeclAttributeList(Actions.CurScope, LD, Attrs);
Expand All @@ -784,9 +796,10 @@ StmtResult Parser::ParseCaseStatement(ParsedStmtContext StmtCtx,
bool MissingCase, ExprResult Expr) {
assert((MissingCase || Tok.is(tok::kw_case)) && "Not a case stmt!");

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

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

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

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

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

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

DiagnoseLabelFollowedByDecl(*this, SubStmt.get());
return Actions.ActOnDefaultStmt(DefaultLoc, ColonLoc,
SubStmt.get(), getCurScope());
}
Expand Down
39 changes: 33 additions & 6 deletions clang/test/C/C2x/n2508.c
Original file line number Diff line number Diff line change
@@ -1,23 +1,50 @@
// RUN: %clang_cc1 -verify -std=c2x %s
// RUN: %clang_cc1 -verify -std=c23 %s
// RUN: %clang_cc1 -verify=pedantic -std=c11 -pedantic %s
// RUN: %clang_cc1 -verify=compat -std=c23 -Wpre-c23-compat %s

// expected-no-diagnostics

/* WG14 N2508: yes
* Free positioning of labels inside compound statements
*/
void test() {
void test(void) {
{
inner:
}
} /* pedantic-warning {{label at end of compound statement is a C23 extension}}
compat-warning {{label at end of compound statement is incompatible with C standards before C23}}
*/

switch (1) {
case 1:
}
} /* pedantic-warning {{label at end of compound statement is a C23 extension}}
compat-warning {{label at end of compound statement is incompatible with C standards before C23}}
*/

{
multiple: labels: on: a: line:
}
} /* pedantic-warning {{label at end of compound statement is a C23 extension}}
compat-warning {{label at end of compound statement is incompatible with C standards before C23}}
*/

final:
}
} /* pedantic-warning {{label at end of compound statement is a C23 extension}}
compat-warning {{label at end of compound statement is incompatible with C standards before C23}}
*/

void test_labels(void) {
label:
int i = 0; /* pedantic-warning {{label followed by a declaration is a C23 extension}}
compat-warning {{label followed by a declaration is incompatible with C standards before C23}}
*/

switch (i) {
case 1:
_Static_assert(1, ""); /* pedantic-warning {{label followed by a declaration is a C23 extension}}
compat-warning {{label followed by a declaration is incompatible with C standards before C23}}
*/
default:
_Static_assert(1, ""); /* pedantic-warning {{label followed by a declaration is a C23 extension}}
compat-warning {{label followed by a declaration is incompatible with C standards before C23}}
*/
}
}
24 changes: 16 additions & 8 deletions clang/test/OpenMP/barrier_ast_print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ T tmain(T argc) {
static T a;
#pragma omp barrier
switch (argc) {
case 0:
case 0: {
#pragma omp barrier
break;
default:
} break;
default: {
#pragma omp barrier
#pragma omp barrier
break;
} break;
}
return a + argc;
}
Expand All @@ -35,11 +35,15 @@ T tmain(T argc) {
// CHECK-NEXT: #pragma omp barrier
// CHECK-NEXT: switch (argc) {
// CHECK-NEXT: case 0:
// CHECK-NEXT: {
// CHECK-NEXT: #pragma omp barrier
// CHECK-NEXT: }
// CHECK-NEXT: break;
// CHECK-NEXT: default:
// CHECK-NEXT: {
// CHECK-NEXT: #pragma omp barrier
// CHECK-NEXT: #pragma omp barrier
// CHECK-NEXT: }
// CHECK-NEXT: break;
// CHECK-NEXT: }

Expand All @@ -49,21 +53,25 @@ int main(int argc, char **argv) {
#pragma omp barrier
// CHECK-NEXT: #pragma omp barrier
switch (argc) {
case 0:
case 0: {
#pragma omp barrier
#pragma omp barrier
break;
default:
} break;
default: {
#pragma omp barrier
break;
} break;
}
// CHECK-NEXT: switch (argc) {
// CHECK-NEXT: case 0:
// CHECK-NEXT: {
// CHECK-NEXT: #pragma omp barrier
// CHECK-NEXT: #pragma omp barrier
// CHECK-NEXT: }
// CHECK-NEXT: break;
// CHECK-NEXT: default:
// CHECK-NEXT: {
// CHECK-NEXT: #pragma omp barrier
// CHECK-NEXT: }
// CHECK-NEXT: break;
// CHECK-NEXT: }
return tmain(argc) + tmain(argv[0][0]) + a;
Expand Down
8 changes: 4 additions & 4 deletions clang/test/OpenMP/barrier_messages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ T tmain(T argc) {
switch (argc) {
#pragma omp barrier
case 1:
#pragma omp barrier
#pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}}
break;
default: {
#pragma omp barrier
Expand All @@ -50,7 +50,7 @@ T tmain(T argc) {
#pragma omp barrier
}
label:
#pragma omp barrier
#pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}}
label1 : {
#pragma omp barrier
}
Expand Down Expand Up @@ -92,7 +92,7 @@ int main(int argc, char **argv) {
switch (argc) {
#pragma omp barrier
case 1:
#pragma omp barrier
#pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}}
break;
default: {
#pragma omp barrier
Expand All @@ -104,7 +104,7 @@ int main(int argc, char **argv) {
#pragma omp barrier
}
label:
#pragma omp barrier
#pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}}
label1 : {
#pragma omp barrier
}
Expand Down
6 changes: 4 additions & 2 deletions clang/test/OpenMP/cancel_messages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ int main(int argc, char **argv) {
switch (argc) {
#pragma omp cancel taskgroup // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
case 1:
#pragma omp cancel parallel // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
#pragma omp cancel parallel // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}} \
expected-error {{'#pragma omp cancel' cannot be an immediate substatement}}
break;
default: {
#pragma omp cancel sections // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
Expand All @@ -83,7 +84,8 @@ int main(int argc, char **argv) {
#pragma omp cancel taskgroup // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
}
label:
#pragma omp cancel parallel // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
#pragma omp cancel parallel // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}} \
expected-error {{'#pragma omp cancel' cannot be an immediate substatement}}
label1 : {
#pragma omp cancel sections // expected-error {{orphaned 'omp cancel' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
}
Expand Down
6 changes: 4 additions & 2 deletions clang/test/OpenMP/cancellation_point_messages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ int main(int argc, char **argv) {
switch (argc) {
#pragma omp cancellation point taskgroup // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
case 1:
#pragma omp cancellation point parallel // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
#pragma omp cancellation point parallel // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}} \
expected-error {{'#pragma omp cancellation point' cannot be an immediate substatement}}
break;
default: {
#pragma omp cancellation point sections // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
Expand All @@ -83,7 +84,8 @@ int main(int argc, char **argv) {
#pragma omp cancellation point taskgroup // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
}
label:
#pragma omp cancellation point parallel // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
#pragma omp cancellation point parallel // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}} \
expected-error {{'#pragma omp cancellation point' cannot be an immediate substatement}}
label1 : {
#pragma omp cancellation point sections // expected-error {{orphaned 'omp cancellation point' directives are prohibited; perhaps you forget to enclose the directive into a region?}}
}
Expand Down
Loading