Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
5 changes: 5 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,11 @@ Bug Fixes in This Version
- Fixed an infinite recursion when checking constexpr destructors. (#GH141789)
- Fixed a crash when a malformed using declaration appears in a ``constexpr`` function. (#GH144264)
- Fixed a bug when use unicode character name in macro concatenation. (#GH145240)
- In C23, something like ``[[/*possible attributes*/]];`` is an attribute
declaration, not a statement. So it is not allowed by the syntax in places
where a statement is required, specifically as the secondary block of a
selection or iteration statement. This differs from C++, since C++ allows
declaration statements. Clang now emits a warning for these patterns. (#GH141659)

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ def BuiltinRequiresHeader : DiagGroup<"builtin-requires-header">;
def C99Compat : DiagGroup<"c99-compat">;
def C23Compat : DiagGroup<"c23-compat">;
def : DiagGroup<"c2x-compat", [C23Compat]>;
def CAttributeExtensions : DiagGroup<"c-attribute-extension">;

def CppKeywordInC : DiagGroup<"c++-keyword">;
def DuplicateDeclSpecifier : DiagGroup<"duplicate-decl-specifier">;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,9 @@ def err_expected_while : Error<"expected 'while' in do/while loop">;

def err_expected_semi_after_stmt : Error<"expected ';' after %0 statement">;
def err_expected_semi_after_expr : Error<"expected ';' after expression">;
def warn_attr_in_secondary_block : ExtWarn<
"ISO C does not allow an attribute list to appear here">,
InGroup<CAttributeExtensions>;
def err_extraneous_token_before_semi : Error<"extraneous '%0' before ';'">;

def err_expected_semi_after_method_proto : Error<
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -7168,13 +7168,16 @@ class Parser : public CodeCompletionHandler {
AllowStandaloneOpenMPDirectives = 0x2,
/// This context is at the top level of a GNU statement expression.
InStmtExpr = 0x4,
/// This context is the C99 secondary-block in selection or iteration
/// statement.
SecondaryBlockInC = 0x8,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why does AllowDeclarationsInC not suffice? I would expect that bit to not be set in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea. We can just reuse AllowDeclarationsInC . I have removed SecondaryBlockInC .


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

LLVM_MARK_AS_BITMASK_ENUM(InStmtExpr)
LLVM_MARK_AS_BITMASK_ENUM(SecondaryBlockInC)
};

/// Act on an expression statement that might be the last statement in a
Expand Down
40 changes: 30 additions & 10 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts,
// at the start of the statement. Thus, we're not using MaybeParseAttributes
// here because we don't want to allow arbitrary orderings.
ParsedAttributes CXX11Attrs(AttrFactory);
MaybeParseCXX11Attributes(CXX11Attrs, /*MightBeObjCMessageSend*/ true);
bool HasStdAttr =
MaybeParseCXX11Attributes(CXX11Attrs, /*MightBeObjCMessageSend*/ true);
ParsedAttributes GNUOrMSAttrs(AttrFactory);
if (getLangOpts().OpenCL)
MaybeParseGNUAttributes(GNUOrMSAttrs);
Expand All @@ -80,6 +81,12 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts,
assert((CXX11Attrs.empty() || Res.isInvalid() || Res.isUsable()) &&
"attributes on empty statement");

if (HasStdAttr && getLangOpts().C23 &&
(StmtCtx & ParsedStmtContext::SecondaryBlockInC) != ParsedStmtContext{} &&
isa_and_present<NullStmt>(Res.get()))
Diag(CXX11Attrs.Range.getBegin(), diag::warn_attr_in_secondary_block)
<< CXX11Attrs.Range;

if (CXX11Attrs.empty() || Res.isInvalid())
return Res;

Expand Down Expand Up @@ -1491,6 +1498,10 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {

SourceLocation InnerStatementTrailingElseLoc;
StmtResult ThenStmt;
ParsedStmtContext StmtCtx = getLangOpts().C99
? ParsedStmtContext::SecondaryBlockInC
: ParsedStmtContext::SubStmt;

{
bool ShouldEnter = ConstexprCondition && !*ConstexprCondition;
Sema::ExpressionEvaluationContext Context =
Expand All @@ -1503,7 +1514,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
EnterExpressionEvaluationContext PotentiallyDiscarded(
Actions, Context, nullptr,
Sema::ExpressionEvaluationContextRecord::EK_Other, ShouldEnter);
ThenStmt = ParseStatement(&InnerStatementTrailingElseLoc);
ThenStmt = ParseStatement(&InnerStatementTrailingElseLoc, StmtCtx);
}

if (Tok.isNot(tok::kw_else))
Expand Down Expand Up @@ -1548,7 +1559,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
EnterExpressionEvaluationContext PotentiallyDiscarded(
Actions, Context, nullptr,
Sema::ExpressionEvaluationContextRecord::EK_Other, ShouldEnter);
ElseStmt = ParseStatement();
ElseStmt = ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx);

if (ElseStmt.isUsable())
MIChecker.Check();
Expand Down Expand Up @@ -1684,8 +1695,11 @@ StmtResult Parser::ParseSwitchStatement(SourceLocation *TrailingElseLoc) {
if (C99orCXX)
getCurScope()->decrementMSManglingNumber();

ParsedStmtContext StmtCtx = getLangOpts().C99
? ParsedStmtContext::SecondaryBlockInC
: ParsedStmtContext::SubStmt;
// Read the body statement.
StmtResult Body(ParseStatement(TrailingElseLoc));
StmtResult Body(ParseStatement(TrailingElseLoc, StmtCtx));

// Pop the scopes.
InnerScope.Exit();
Expand Down Expand Up @@ -1754,9 +1768,11 @@ StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) {
ParseScope InnerScope(this, Scope::DeclScope, C99orCXX, Tok.is(tok::l_brace));

MisleadingIndentationChecker MIChecker(*this, MSK_while, WhileLoc);

ParsedStmtContext StmtCtx = getLangOpts().C99
? ParsedStmtContext::SecondaryBlockInC
: ParsedStmtContext::SubStmt;
// Read the body statement.
StmtResult Body(ParseStatement(TrailingElseLoc));
StmtResult Body(ParseStatement(TrailingElseLoc, StmtCtx));

if (Body.isUsable())
MIChecker.Check();
Expand Down Expand Up @@ -1799,9 +1815,11 @@ StmtResult Parser::ParseDoStatement() {
//
bool C99orCXX = getLangOpts().C99 || getLangOpts().CPlusPlus;
ParseScope InnerScope(this, Scope::DeclScope, C99orCXX, Tok.is(tok::l_brace));

ParsedStmtContext StmtCtx = getLangOpts().C99
? ParsedStmtContext::SecondaryBlockInC
: ParsedStmtContext::SubStmt;
// Read the body statement.
StmtResult Body(ParseStatement());
StmtResult Body(ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx));

// Pop the body scope if needed.
InnerScope.Exit();
Expand Down Expand Up @@ -2221,9 +2239,11 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
getCurScope()->decrementMSManglingNumber();

MisleadingIndentationChecker MIChecker(*this, MSK_for, ForLoc);

ParsedStmtContext StmtCtx = getLangOpts().C99
? ParsedStmtContext::SecondaryBlockInC
: ParsedStmtContext::SubStmt;
// Read the body statement.
StmtResult Body(ParseStatement(TrailingElseLoc));
StmtResult Body(ParseStatement(TrailingElseLoc, StmtCtx));

if (Body.isUsable())
MIChecker.Check();
Expand Down
30 changes: 30 additions & 0 deletions clang/test/Parser/statements.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s -Wno-unreachable-code
// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify %s -Wno-unreachable-code

void test1(void) {
{ ; { ;;}} ;;
Expand Down Expand Up @@ -77,3 +78,32 @@ int test9(void) {

return 4, // expected-error {{expected ';' after return statement}}
}

#if __STDC_VERSION__ >= 202311L
void attr_decl_in_selection_statement(int n) {
if (1)
[[]]; // expected-warning {{ISO C does not allow an attribute list to appear here}}

if (1) {

} else
[[]]; // expected-warning {{ISO C does not allow an attribute list to appear here}}


switch (n)
[[]]; // expected-warning {{ISO C does not allow an attribute list to appear here}}
}

void attr_decl_in_iteration_statement(int n) {
int i;
for (i = 0; i < n; ++i)
[[]]; // expected-warning {{ISO C does not allow an attribute list to appear here}}

while (i > 0)
[[]]; // expected-warning {{ISO C does not allow an attribute list to appear here}}

do
[[]]; // expected-warning {{ISO C does not allow an attribute list to appear here}}
while (i > 0);
}
#endif // __STDC_VERSION__ >= 202311L
17 changes: 11 additions & 6 deletions clang/test/Sema/c2x-fallthrough.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -std=c2x -verify %s
// RUN: %clang_cc1 -fsyntax-only -std=c23 -verify %s

// This is the latest version of fallthrough that we support.
_Static_assert(__has_c_attribute(fallthrough) == 201910L);
Expand All @@ -16,17 +16,22 @@ void f(int n) {
}
case 2:
for (int n = 0; n != 10; ++n)
[[fallthrough]]; // expected-error {{does not directly precede switch label}}
[[fallthrough]]; // expected-error {{does not directly precede switch label}} \
// expected-warning {{ISO C does not allow an attribute list to appear here}}
case 3:
while (1)
[[fallthrough]]; // expected-error {{does not directly precede switch label}}
[[fallthrough]]; // expected-error {{does not directly precede switch label}} \
// expected-warning {{ISO C does not allow an attribute list to appear here}}
case 4:
while (0)
[[fallthrough]]; // expected-error {{does not directly precede switch label}}
[[fallthrough]]; // expected-error {{does not directly precede switch label}} \
// expected-warning {{ISO C does not allow an attribute list to appear here}}
case 5:
do [[fallthrough]]; while (1); // expected-error {{does not directly precede switch label}}
do [[fallthrough]]; while (1); // expected-error {{does not directly precede switch label}} \
// expected-warning {{ISO C does not allow an attribute list to appear here}}
case 6:
do [[fallthrough]]; while (0); // expected-error {{does not directly precede switch label}}
do [[fallthrough]]; while (0); // expected-error {{does not directly precede switch label}} \
// expected-warning {{ISO C does not allow an attribute list to appear here}}
case 7:
switch (n) {
case 0:
Expand Down
Loading