diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -246,6 +246,9 @@ so the [[maybe_unused]] attribute may be applied to a label to silence an ``-Wunused-label`` warning. +- Implemented `WG14 N508 `_, + so labels can placed everywhere inside a compound statement. + C++ Language Changes in Clang ----------------------------- @@ -294,6 +297,8 @@ C++2b Feature Support ^^^^^^^^^^^^^^^^^^^^^ +- Support label at end of compound statement (`P2324 `_). + CUDA/HIP Language Changes in Clang ---------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -295,8 +295,20 @@ 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 err_label_end_of_compound_statement : Error< - "label at end of compound statement: expected statement">; +def err_switch_label_end_of_compound_statement : Error< + "label at end of switch compound statement: expected statement">; +def ext_c_label_end_of_compound_statement : ExtWarn< + "label at end of compound statement is a C2x extension">, + InGroup; +def ext_cxx_label_end_of_compound_statement : ExtWarn< + "label at end of compound statement is a C++2b extension">, + InGroup; +def warn_c2x_compat_label_end_of_compound_statement : Warning< + "label at end of compound statement is incompatible with C standards before C2x">, + InGroup, DefaultIgnore; +def warn_cxx20_compat_label_end_of_compound_statement : Warning< + "label at end of compound statement is incompatible with C++ standards before C++2b">, + InGroup, DefaultIgnore; def err_address_of_label_outside_fn : Error< "use of address-of-label extension outside of a function body">; def err_asm_operand_wide_string_literal : Error< diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -679,9 +679,12 @@ /// ParseLabeledStatement - We have an identifier and a ':' after it. /// +/// label: +/// identifier ':' +/// [GNU] identifier ':' attributes[opt] +/// /// labeled-statement: -/// identifier ':' statement -/// [GNU] identifier ':' attributes[opt] statement +/// label statement /// StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs, ParsedStmtContext StmtCtx) { @@ -725,6 +728,20 @@ } } + // The label may have no statement following it + if (SubStmt.isUnset() && Tok.is(tok::r_brace)) { + if (getLangOpts().CPlusPlus) { + Diag(Tok, getLangOpts().CPlusPlus2b + ? diag::warn_cxx20_compat_label_end_of_compound_statement + : diag::ext_cxx_label_end_of_compound_statement); + } else { + Diag(Tok, getLangOpts().C2x + ? diag::warn_c2x_compat_label_end_of_compound_statement + : diag::ext_c_label_end_of_compound_statement); + } + SubStmt = Actions.ActOnNullStmt(ColonLoc); + } + // If we've not parsed a statement yet, parse one now. if (!SubStmt.isInvalid() && !SubStmt.isUsable()) SubStmt = ParseStatement(nullptr, StmtCtx); @@ -873,8 +890,8 @@ // another parsing error, so avoid producing extra diagnostics. if (ColonLoc.isValid()) { SourceLocation AfterColonLoc = PP.getLocForEndOfToken(ColonLoc); - Diag(AfterColonLoc, diag::err_label_end_of_compound_statement) - << FixItHint::CreateInsertion(AfterColonLoc, " ;"); + Diag(AfterColonLoc, diag::err_switch_label_end_of_compound_statement) + << FixItHint::CreateInsertion(AfterColonLoc, " ;"); } SubStmt = StmtError(); } @@ -928,8 +945,8 @@ // Diagnose the common error "switch (X) {... default: }", which is // not valid. SourceLocation AfterColonLoc = PP.getLocForEndOfToken(ColonLoc); - Diag(AfterColonLoc, diag::err_label_end_of_compound_statement) - << FixItHint::CreateInsertion(AfterColonLoc, " ;"); + Diag(AfterColonLoc, diag::err_switch_label_end_of_compound_statement) + << FixItHint::CreateInsertion(AfterColonLoc, " ;"); SubStmt = true; } @@ -1096,10 +1113,10 @@ return Actions.ActOnExprStmt(E, /*DiscardedValue=*/!IsStmtExprResult); } -/// ParseCompoundStatementBody - Parse a sequence of statements and invoke the -/// ActOnCompoundStmt action. This expects the '{' to be the current token, and -/// consume the '}' at the end of the block. It does not manipulate the scope -/// stack. +/// ParseCompoundStatementBody - Parse a sequence of statements optionally +/// followed by a label and invoke the ActOnCompoundStmt action. This expects +/// the '{' to be the current token, and consume the '}' at the end of the +/// block. It does not manipulate the scope stack. StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { PrettyStackTraceLoc CrashInfo(PP.getSourceManager(), Tok.getLocation(), diff --git a/clang/test/AST/ast-dump-stmt.c b/clang/test/AST/ast-dump-stmt.c --- a/clang/test/AST/ast-dump-stmt.c +++ b/clang/test/AST/ast-dump-stmt.c @@ -161,6 +161,10 @@ // CHECK-NEXT: ImplicitCastExpr // CHECK-NEXT: ImplicitCastExpr // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'void *' lvalue Var 0x{{[^ ]*}} 'ptr' 'void *' + +label3: + // CHECK-NEXT: LabelStmt 0x{{[^ ]*}} 'label3' + // CHECK-NEXT: NullStmt 0x{{[^ ]*}} } void TestSwitch(int i) { diff --git a/clang/test/Parser/c2x-label.c b/clang/test/Parser/c2x-label.c new file mode 100644 --- /dev/null +++ b/clang/test/Parser/c2x-label.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c17 -Wc2x-compat -verify=c17 %s +// RUN: %clang_cc1 -fsyntax-only -std=c2x -Wpre-c2x-compat -verify=c2x %s + +void foo() { + int x; +label1: + x = 1; +label2: label3: label4: +} // c17-warning {{label at end of compound statement is a C2x extension}} \ + c2x-warning {{label at end of compound statement is incompatible with C standards before C2x}} diff --git a/clang/test/Parser/cxx2b-label.cpp b/clang/test/Parser/cxx2b-label.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Parser/cxx2b-label.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx2b -std=c++2b -Wpre-c++2b-compat %s +// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx20 -std=c++20 %s + +void foo() { +label1: + int x; +label2: + x = 1; +label3: label4: label5: +} // cxx20-warning {{label at end of compound statement is a C++2b extension}} \ + cxx2b-warning {{label at end of compound statement is incompatible with C++ standards before C++2b}} diff --git a/clang/test/Parser/switch-recovery.cpp b/clang/test/Parser/switch-recovery.cpp --- a/clang/test/Parser/switch-recovery.cpp +++ b/clang/test/Parser/switch-recovery.cpp @@ -160,14 +160,14 @@ void missing_statement_case(int x) { switch (x) { case 1: - case 0: // expected-error {{label at end of compound statement: expected statement}} + case 0: // expected-error {{label at end of switch compound statement: expected statement}} } } void missing_statement_default(int x) { switch (x) { case 0: - default: // expected-error {{label at end of compound statement: expected statement}} + default: // expected-error {{label at end of switch compound statement: expected statement}} } } @@ -179,7 +179,7 @@ void pr19022_1a(int x) { switch(x) { case 1 // expected-error{{expected ':' after 'case'}} \ - // expected-error{{label at end of compound statement: expected statement}} + // expected-error{{label at end of switch compound statement: expected statement}} } } diff --git a/clang/www/c_status.html b/clang/www/c_status.html --- a/clang/www/c_status.html +++ b/clang/www/c_status.html @@ -763,7 +763,7 @@ Free positioning of labels inside compound statements N2508 - No + Clang 16 Clarification request for C17 example of undefined behavior diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1456,7 +1456,7 @@ Labels at the end of compound statements P2324R2 - No + Clang 16 Delimited escape sequences