Skip to content
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
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ def ExtraTokens : DiagGroup<"extra-tokens">;
def CXX98CompatExtraSemi : DiagGroup<"c++98-compat-extra-semi">;
def CXX11ExtraSemi : DiagGroup<"c++11-extra-semi">;
def EmptyInitStatement : DiagGroup<"empty-init-stmt">;
def ExpansionStmtBody : DiagGroup<"expansion-stmt-missing-braces">;
def ExportUnnamed : DiagGroup<"export-unnamed">;
def ExtraSemiStmt : DiagGroup<"extra-semi-stmt", [EmptyInitStatement]>;
def ExtraSemi : DiagGroup<"extra-semi", [CXX98CompatExtraSemi,
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 @@ -457,6 +457,12 @@ def err_expansion_stmt_requires_cxx2c : Error<
"expansion statements are only supported in C++2c">;
def err_for_template : Error<
"'for template' is invalid; use 'template for' instead">;
def ext_expansion_stmt_body_not_compound_stmt : Extension<
"ISO C++ requires the body of an expansion statement to be a compound statement">,
InGroup<ExpansionStmtBody>;
def ext_expansion_stmt_body_attr : Extension<
"ISO C++ forbids attributes before the compound statement of an expansion statement">,
InGroup<ExpansionStmtBody>;
Comment thread
cor3ntin marked this conversation as resolved.

def err_expected_case_before_expression: Error<
"expected 'case' keyword before expression">;
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2329,6 +2329,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// the other parts.
getCurScope()->EnterLoopBody(PrecedingLabel);

bool BodyStartsWithAttr = Tok.isOneOf(tok::l_square, tok::kw___attribute);
SourceLocation BodyBeginLoc = Tok.getLocation();

// C99 6.8.5p5 - In C99, the body of the for statement is a scope, even if
// there is no compound stmt. C90 does not have this clause. We only do this
// if the body isn't a compound statement to avoid push/pop in common cases.
Expand Down Expand Up @@ -2379,6 +2382,14 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
return StmtError();
}

// attribute-specifier without attribute (`[[]]`) isn't in AST.
// `__declspec()` is only applied to declarations, so we can ignore it.
if (!isa<CompoundStmt>(Body.get()) || BodyStartsWithAttr)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Whelp, I think this logic is correct but I really don't agree with the design in the standard and think there's another extension hiding in here:

int main() {
  template for (int x : {1}) [[clang::atomic(remote_memory)]] {}
}

That's invalid per the standard because the standard requires a compound-statement and that grammar production cannot have a leading attribute. If WG21 used statement instead, then a leading attribute would be accepted as you'd expect.

I think the diagnostic text will be confusing in that situation because there is a compound statement. That suggests we want to handle attributes with their own diagnostic, but that would mean we'd need to get much better about recognizing attributes instead of looking for a single token.

Ideally, we'd have a CompoundStmt or an AttributedStmt which wraps a CompoundStmt, but there are two scenarios I think that won't cover:

template for (int x : {1}) [[]] { } // Empty attributes don't make an AttributedStmt
template for (int x : {1}) [[unknown]] {} // Unknown attributes don't make an AttributedStmt either

Other test cases to consider would be Objective-C++ or lambdas in C++, so it's not just attributes:

template for (int x : {1}) [obj msg];
template for (int x : {1}) [](){}();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I mean, our logic could actually warn by checking the next token is { - that cover all the cases, i think
https://eel.is/c++draft/stmt.block#nt:compound-statement

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Possibly. The cases I was worried about for token lookahead also involved lexer stuff like

template for (int x : {1})
#pragma clang diagnostic ...
{}

and whether anything can usefully produce an annotation token before the compound statement.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, I also think we should just check if the next token is {; but also, all of these issues really make it seem like the wording should just say statement instead...

whether anything can usefully produce an annotation token before the compound statement.

Code completion maybe?

Diag(BodyBeginLoc,
isa<CompoundStmt>(Body.get()->stripLabelLikeStatements())
? diag::ext_expansion_stmt_body_attr
: diag::ext_expansion_stmt_body_not_compound_stmt);

return Actions.FinishCXXExpansionStmt(ForRangeStmt.get(), Body.get());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -Wexpansion-stmt-missing-braces -verify
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -Wpedantic -verify

void f() {
template for (int x : {1})
template for (int y : {1}) // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
; // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
template for (int x : {1})
if (x) // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
;
template for (int x : {1})
switch (x) // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
;
template for (int x : {1})
for (;;) // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
;
template for (int x : {1})
while (x) // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
;
template for (int x : {1})
do // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
;
while (x);
template for (int x : {1})
return; // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
template for (int x : {1})
[] {}(); // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
template for (int x : {1})
[ // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
[]] if (x)
;
template for (int x : {1})
[ // expected-warning {{ISO C++ requires the body of an expansion statement to be a compound statement}}
[likely]] if (x)
;
template for (int x : {1})
[ // expected-warning {{ISO C++ forbids attributes before the compound statement of an expansion statement}}
[]] {}
template for (int x : {1})
[ // expected-warning {{ISO C++ forbids attributes before the compound statement of an expansion statement}}
[likely]] {}
template for (int x : {1})
__attribute__ // expected-warning {{ISO C++ forbids attributes before the compound statement of an expansion statement}}
(()) {}
template for (int x : {1})
foo: {} // expected-error {{labels are not allowed in expansion statements}}
}
Loading