[clang] [clang][Parser] Warn when the body of expansion statement is not a compound statement (PR #209229)
Yanzuo Liu via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 15 21:19:38 PDT 2026
https://github.com/zwuis updated https://github.com/llvm/llvm-project/pull/209229
>From 6858ff8a03b5f20eb262b2a404974079a9b33c4f Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Mon, 13 Jul 2026 23:57:51 +0800
Subject: [PATCH 1/5] Warn when the body of expansion statement is not a
compound statement
---
.../clang/Basic/DiagnosticParseKinds.td | 3 +
clang/lib/Parse/ParseStmt.cpp | 9 +-
.../Parser/cxx2c-expansion-statements.cpp | 115 +++++++++++++-----
3 files changed, 96 insertions(+), 31 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 55b26deed0750..389900040399a 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -457,6 +457,9 @@ 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_requires_braced_body : ExtWarn<
+ "ISO C++ requires a compound statement to be the body of expansion statement">,
+ InGroup<DiagGroup<"expansion-stmt-braced-body">>, DefaultIgnore;
def err_expected_case_before_expression: Error<
"expected 'case' keyword before expression">;
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index bdaea72cf52a1..bbde87794b652 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2329,6 +2329,10 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// the other parts.
getCurScope()->EnterLoopBody(PrecedingLabel);
+ bool BodyIsCompoundStmt = Tok.is(tok::l_brace);
+ // attribute-specifier without attribute (`[[]]`) isn't in AST.
+ 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.
@@ -2341,7 +2345,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// for-init-statement/condition and a new scope for substatement in C++.
//
ParseScope InnerScope(this, Scope::DeclScope, C99orCXXorObjC,
- Tok.is(tok::l_brace));
+ BodyIsCompoundStmt);
// The body of the for loop has the same local mangling number as the
// for-init-statement.
@@ -2379,6 +2383,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
return StmtError();
}
+ if (!BodyIsCompoundStmt)
+ Diag(BodyBeginLoc, diag::ext_expansion_stmt_requires_braced_body);
+
return Actions.FinishCXXExpansionStmt(ForRangeStmt.get(), Body.get());
}
diff --git a/clang/test/Parser/cxx2c-expansion-statements.cpp b/clang/test/Parser/cxx2c-expansion-statements.cpp
index 736f9fead383c..2ca94270cd60a 100644
--- a/clang/test/Parser/cxx2c-expansion-statements.cpp
+++ b/clang/test/Parser/cxx2c-expansion-statements.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify=expected,brace -Wexpansion-stmt-braced-body
namespace std {
template <typename T>
struct initializer_list {
@@ -14,31 +15,82 @@ void bad() {
template for (;); // expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
template for (;;); // expected-error {{expansion statement must use the syntax of a range-based for loop}}
template for (int x;;); // expected-error {{expansion statement must use the syntax of a range-based for loop}}
- template for (x : {1}); // expected-error {{expansion statement requires type for expansion variable}}
+ template for (x : {1});
+ // expected-error at -1 {{expansion statement requires type for expansion variable}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
template for (: {1}); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
template for (auto y : {1})]; // expected-error {{expected expression}}
- template for (auto y : {1}; // expected-error {{expected ')'}} expected-note {{to match this '('}}
- template for (extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
- template for (register auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'register'}} expected-error {{ISO C++17 does not allow 'register' storage class specifier}}
- template for (__private_extern__ auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
- template for (extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}}
- template for (static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
- template for (thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}}
- template for (static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}}
- template for (__thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}}
- template for (static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
- template for (constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}}
- template for (consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}}
- template for (int x; extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
- template for (int x; extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}}
- template for (int x; static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
- template for (int x; thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}}
- template for (int x; static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}}
- template for (int x; __thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}}
- template for (int x; static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
- template for (int x; constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}}
- template for (int x; consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}}
- template for (auto y : {abc, -+, }); // expected-error {{use of undeclared identifier 'abc'}} expected-error {{expected expression}}
+ template for (auto y : {1};
+ // expected-error at -1 {{expected ')'}}
+ // expected-note at -2 {{to match this '('}}
+ // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (extern auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'extern'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (register auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'register'}}
+ // expected-error at -2 {{ISO C++17 does not allow 'register' storage class specifier}}
+ // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (__private_extern__ auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'extern'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (extern static auto y : {1, 2});
+ // expected-error at -1 {{cannot combine with previous 'extern' declaration specifier}}
+ // expected-error at -2 {{expansion variable 'y' may not be declared 'extern'}}
+ // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (static auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (thread_local auto y : {1, 2});
+ // expected-error at -1 {{'thread_local' variables must have global storage}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (static thread_local auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'thread_local'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (__thread auto y : {1, 2});
+ // expected-error at -1 {{'__thread' variables must have global storage}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (static __thread auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (constinit auto y : {1, 2});
+ // expected-error at -1 {{local variable cannot be declared 'constinit'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (consteval auto y : {1, 2});
+ // expected-error at -1 {{consteval can only be used in function declarations}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; extern auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'extern'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; extern static auto y : {1, 2});
+ // expected-error at -1 {{cannot combine with previous 'extern' declaration specifier}}
+ // expected-error at -2 {{expansion variable 'y' may not be declared 'extern'}}
+ // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; static auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; thread_local auto y : {1, 2});
+ // expected-error at -1 {{'thread_local' variables must have global storage}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; static thread_local auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'thread_local'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; __thread auto y : {1, 2});
+ // expected-error at -1 {{'__thread' variables must have global storage}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; static __thread auto y : {1, 2});
+ // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; constinit auto y : {1, 2});
+ // expected-error at -1 {{local variable cannot be declared 'constinit'}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; consteval auto y : {1, 2});
+ // expected-error at -1 {{consteval can only be used in function declarations}}
+ // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (auto y : {abc, -+, });
+ // expected-error at -1 {{use of undeclared identifier 'abc'}}
+ // expected-error at -2 {{expected expression}}
+ // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
template for (3 : "error") // expected-error {{expansion statement declaration must declare a variable}} \
expected-error {{expansion statement must use the syntax of a range-based for loop}}
;
@@ -46,18 +98,21 @@ void bad() {
; // Semicolon for synchronisation; otherwise, the parser skips over next statement...
template do {} while (true); // expected-error {{expected '<' after 'template'}}
for template (int x : {}) {} // expected-error {{'for template' is invalid; use 'template for' instead}}
+ template for (int x : {1})
+ [ // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ []] {}
}
void good() {
- template for (auto y : {});
- template for (auto y : {1, 2});
- template for (int x; auto y : {1, 2});
- template for (int x; int y : {1, 2});
- template for (int x; constexpr auto y : {1, 2});
- template for (int x; constexpr int y : {1, 2});
+ template for (auto y : {}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (auto y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; auto y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; int y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; constexpr auto y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (int x; constexpr int y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
template for (constexpr int a : {1, 2}) {
template for (constexpr int b : {1, 2}) {
- template for (constexpr int c : {1, 2});
+ template for (constexpr int c : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
}
}
}
>From fac01ce301392ae1b76057cf9935a6b00519a197 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Tue, 14 Jul 2026 16:34:49 +0800
Subject: [PATCH 2/5] Address review comments
---
.../clang/Basic/DiagnosticParseKinds.td | 6 +-
clang/lib/Parse/ParseStmt.cpp | 8 +-
...sion-statements-non-compound-stmt-body.cpp | 31 +++++
.../Parser/cxx2c-expansion-statements.cpp | 115 +++++-------------
4 files changed, 68 insertions(+), 92 deletions(-)
create mode 100644 clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 389900040399a..88d08b3c4a03b 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -457,9 +457,9 @@ 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_requires_braced_body : ExtWarn<
- "ISO C++ requires a compound statement to be the body of expansion statement">,
- InGroup<DiagGroup<"expansion-stmt-braced-body">>, DefaultIgnore;
+def ext_expansion_stmt_missing_braces : Extension<
+ "ISO C++ requires a compound statement to be the body of an expansion statement">,
+ InGroup<DiagGroup<"expansion-stmt-missing-braces">>;
def err_expected_case_before_expression: Error<
"expected 'case' keyword before expression">;
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index bbde87794b652..bebcba038a20e 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2329,8 +2329,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// the other parts.
getCurScope()->EnterLoopBody(PrecedingLabel);
- bool BodyIsCompoundStmt = Tok.is(tok::l_brace);
// attribute-specifier without attribute (`[[]]`) isn't in AST.
+ bool BodyStartsWithSquare = Tok.is(tok::l_square);
SourceLocation BodyBeginLoc = Tok.getLocation();
// C99 6.8.5p5 - In C99, the body of the for statement is a scope, even if
@@ -2345,7 +2345,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// for-init-statement/condition and a new scope for substatement in C++.
//
ParseScope InnerScope(this, Scope::DeclScope, C99orCXXorObjC,
- BodyIsCompoundStmt);
+ Tok.is(tok::l_brace));
// The body of the for loop has the same local mangling number as the
// for-init-statement.
@@ -2383,8 +2383,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
return StmtError();
}
- if (!BodyIsCompoundStmt)
- Diag(BodyBeginLoc, diag::ext_expansion_stmt_requires_braced_body);
+ if (!isa<CompoundStmt>(Body.get()) || BodyStartsWithSquare)
+ Diag(BodyBeginLoc, diag::ext_expansion_stmt_missing_braces);
return Actions.FinishCXXExpansionStmt(ForRangeStmt.get(), Body.get());
}
diff --git a/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp b/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp
new file mode 100644
index 0000000000000..fb434f62da902
--- /dev/null
+++ b/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp
@@ -0,0 +1,31 @@
+// 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 a compound statement to be the body of an expansion statement}}
+ ; // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ template for (int x : {1})
+ if (x) // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ ;
+ template for (int x : {1})
+ switch (x) // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ ;
+ template for (int x : {1})
+ for (;;) // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ ;
+ template for (int x : {1})
+ while (x) // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ ;
+ template for (int x : {1})
+ do // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ ;
+ while (x);
+ template for (int x : {1})
+ return; // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ template for (int x : {1})
+ [ // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ []] {}
+ template for (int x : {1})
+ foo: {} // expected-error {{labels are not allowed in expansion statements}}
+}
diff --git a/clang/test/Parser/cxx2c-expansion-statements.cpp b/clang/test/Parser/cxx2c-expansion-statements.cpp
index 2ca94270cd60a..736f9fead383c 100644
--- a/clang/test/Parser/cxx2c-expansion-statements.cpp
+++ b/clang/test/Parser/cxx2c-expansion-statements.cpp
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify
-// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify=expected,brace -Wexpansion-stmt-braced-body
namespace std {
template <typename T>
struct initializer_list {
@@ -15,82 +14,31 @@ void bad() {
template for (;); // expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
template for (;;); // expected-error {{expansion statement must use the syntax of a range-based for loop}}
template for (int x;;); // expected-error {{expansion statement must use the syntax of a range-based for loop}}
- template for (x : {1});
- // expected-error at -1 {{expansion statement requires type for expansion variable}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (x : {1}); // expected-error {{expansion statement requires type for expansion variable}}
template for (: {1}); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
template for (auto y : {1})]; // expected-error {{expected expression}}
- template for (auto y : {1};
- // expected-error at -1 {{expected ')'}}
- // expected-note at -2 {{to match this '('}}
- // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (extern auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'extern'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (register auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'register'}}
- // expected-error at -2 {{ISO C++17 does not allow 'register' storage class specifier}}
- // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (__private_extern__ auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'extern'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (extern static auto y : {1, 2});
- // expected-error at -1 {{cannot combine with previous 'extern' declaration specifier}}
- // expected-error at -2 {{expansion variable 'y' may not be declared 'extern'}}
- // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (static auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (thread_local auto y : {1, 2});
- // expected-error at -1 {{'thread_local' variables must have global storage}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (static thread_local auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'thread_local'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (__thread auto y : {1, 2});
- // expected-error at -1 {{'__thread' variables must have global storage}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (static __thread auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (constinit auto y : {1, 2});
- // expected-error at -1 {{local variable cannot be declared 'constinit'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (consteval auto y : {1, 2});
- // expected-error at -1 {{consteval can only be used in function declarations}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; extern auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'extern'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; extern static auto y : {1, 2});
- // expected-error at -1 {{cannot combine with previous 'extern' declaration specifier}}
- // expected-error at -2 {{expansion variable 'y' may not be declared 'extern'}}
- // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; static auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; thread_local auto y : {1, 2});
- // expected-error at -1 {{'thread_local' variables must have global storage}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; static thread_local auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'thread_local'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; __thread auto y : {1, 2});
- // expected-error at -1 {{'__thread' variables must have global storage}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; static __thread auto y : {1, 2});
- // expected-error at -1 {{expansion variable 'y' may not be declared 'static'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; constinit auto y : {1, 2});
- // expected-error at -1 {{local variable cannot be declared 'constinit'}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; consteval auto y : {1, 2});
- // expected-error at -1 {{consteval can only be used in function declarations}}
- // brace-warning at -2 {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (auto y : {abc, -+, });
- // expected-error at -1 {{use of undeclared identifier 'abc'}}
- // expected-error at -2 {{expected expression}}
- // brace-warning at -3 {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (auto y : {1}; // expected-error {{expected ')'}} expected-note {{to match this '('}}
+ template for (extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (register auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'register'}} expected-error {{ISO C++17 does not allow 'register' storage class specifier}}
+ template for (__private_extern__ auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+ template for (thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}}
+ template for (static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}}
+ template for (__thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}}
+ template for (static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+ template for (constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}}
+ template for (consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}}
+ template for (int x; extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (int x; extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (int x; static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+ template for (int x; thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}}
+ template for (int x; static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}}
+ template for (int x; __thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}}
+ template for (int x; static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+ template for (int x; constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}}
+ template for (int x; consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}}
+ template for (auto y : {abc, -+, }); // expected-error {{use of undeclared identifier 'abc'}} expected-error {{expected expression}}
template for (3 : "error") // expected-error {{expansion statement declaration must declare a variable}} \
expected-error {{expansion statement must use the syntax of a range-based for loop}}
;
@@ -98,21 +46,18 @@ void bad() {
; // Semicolon for synchronisation; otherwise, the parser skips over next statement...
template do {} while (true); // expected-error {{expected '<' after 'template'}}
for template (int x : {}) {} // expected-error {{'for template' is invalid; use 'template for' instead}}
- template for (int x : {1})
- [ // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
- []] {}
}
void good() {
- template for (auto y : {}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (auto y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; auto y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; int y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; constexpr auto y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
- template for (int x; constexpr int y : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (auto y : {});
+ template for (auto y : {1, 2});
+ template for (int x; auto y : {1, 2});
+ template for (int x; int y : {1, 2});
+ template for (int x; constexpr auto y : {1, 2});
+ template for (int x; constexpr int y : {1, 2});
template for (constexpr int a : {1, 2}) {
template for (constexpr int b : {1, 2}) {
- template for (constexpr int c : {1, 2}); // brace-warning {{ISO C++ requires a compound statement to be the body of expansion statement}}
+ template for (constexpr int c : {1, 2});
}
}
}
>From 6e840d8d898306d4daf30f5bfc9c0264c599f2b9 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Tue, 14 Jul 2026 22:00:15 +0800
Subject: [PATCH 3/5] Move comment to where the variable is used
---
clang/lib/Parse/ParseStmt.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index bebcba038a20e..b72f28e4aac6d 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2329,7 +2329,6 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// the other parts.
getCurScope()->EnterLoopBody(PrecedingLabel);
- // attribute-specifier without attribute (`[[]]`) isn't in AST.
bool BodyStartsWithSquare = Tok.is(tok::l_square);
SourceLocation BodyBeginLoc = Tok.getLocation();
@@ -2383,6 +2382,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
return StmtError();
}
+ // attribute-specifier without attribute (`[[]]`) isn't in AST.
if (!isa<CompoundStmt>(Body.get()) || BodyStartsWithSquare)
Diag(BodyBeginLoc, diag::ext_expansion_stmt_missing_braces);
>From 4079c4fdf25be6bf0dd5e77eb1db7612c7c8236b Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Wed, 15 Jul 2026 18:26:18 +0800
Subject: [PATCH 4/5] Handle `__attribute__(())`
---
clang/lib/Parse/ParseStmt.cpp | 5 +++--
.../cxx2c-expansion-statements-non-compound-stmt-body.cpp | 3 +++
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index b72f28e4aac6d..7cd1c0d7a0a81 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2329,7 +2329,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// the other parts.
getCurScope()->EnterLoopBody(PrecedingLabel);
- bool BodyStartsWithSquare = Tok.is(tok::l_square);
+ 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
@@ -2383,7 +2383,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
}
// attribute-specifier without attribute (`[[]]`) isn't in AST.
- if (!isa<CompoundStmt>(Body.get()) || BodyStartsWithSquare)
+ // `__declspec()` is only applied to declarations, so we can ignore it.
+ if (!isa<CompoundStmt>(Body.get()) || BodyStartsWithAttr)
Diag(BodyBeginLoc, diag::ext_expansion_stmt_missing_braces);
return Actions.FinishCXXExpansionStmt(ForRangeStmt.get(), Body.get());
diff --git a/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp b/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp
index fb434f62da902..869c30d8c6ab8 100644
--- a/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp
+++ b/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp
@@ -26,6 +26,9 @@ void f() {
template for (int x : {1})
[ // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
[]] {}
+ template for (int x : {1})
+ __attribute__ // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ (()) {}
template for (int x : {1})
foo: {} // expected-error {{labels are not allowed in expansion statements}}
}
>From 7b10efecc8c58f9ee85db21c1a27d2bbb96e01a5 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Thu, 16 Jul 2026 12:12:25 +0800
Subject: [PATCH 5/5] Improve diagnostics
---
clang/include/clang/Basic/DiagnosticGroups.td | 1 +
.../clang/Basic/DiagnosticParseKinds.td | 9 +++--
clang/lib/Parse/ParseStmt.cpp | 5 ++-
...sion-statements-non-compound-stmt-body.cpp | 33 +++++++++++++------
4 files changed, 34 insertions(+), 14 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 79583534b9bbd..b7072634cccf3 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -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,
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 88d08b3c4a03b..7671f003c8b80 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -457,9 +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_missing_braces : Extension<
- "ISO C++ requires a compound statement to be the body of an expansion statement">,
- InGroup<DiagGroup<"expansion-stmt-missing-braces">>;
+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>;
def err_expected_case_before_expression: Error<
"expected 'case' keyword before expression">;
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 7cd1c0d7a0a81..44b51a985f2ff 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2385,7 +2385,10 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// 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)
- Diag(BodyBeginLoc, diag::ext_expansion_stmt_missing_braces);
+ 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());
}
diff --git a/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp b/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp
index 869c30d8c6ab8..5aae6a5305db8 100644
--- a/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp
+++ b/clang/test/Parser/cxx2c-expansion-statements-non-compound-stmt-body.cpp
@@ -3,31 +3,44 @@
void f() {
template for (int x : {1})
- template for (int y : {1}) // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
- ; // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ 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 a compound statement to be the body of an expansion statement}}
+ 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 a compound statement to be the body of an expansion statement}}
+ 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 a compound statement to be the body of an expansion statement}}
+ 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 a compound statement to be the body of an expansion statement}}
+ 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 a compound statement to be the body of an expansion statement}}
+ 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 a compound statement to be the body of an expansion statement}}
+ 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 a compound statement to be the body of an expansion statement}}
+ [] {}(); // 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})
- __attribute__ // expected-warning {{ISO C++ requires a compound statement to be the body of an expansion statement}}
+ [ // 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}}
More information about the cfe-commits
mailing list