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 <[email protected]>
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@-1 {{expansion statement requires type for expansion 
variable}}
+  // brace-warning@-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@-1 {{expected ')'}}
+  // expected-note@-2 {{to match this '('}}
+  // brace-warning@-3 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
+  template for (extern auto y : {1, 2});
+  // expected-error@-1 {{expansion variable 'y' may not be declared 'extern'}}
+  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
+  template for (register auto y : {1, 2});
+  // expected-error@-1 {{expansion variable 'y' may not be declared 
'register'}}
+  // expected-error@-2 {{ISO C++17 does not allow 'register' storage class 
specifier}}
+  // brace-warning@-3 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
+  template for (__private_extern__ auto y : {1, 2});
+  // expected-error@-1 {{expansion variable 'y' may not be declared 'extern'}}
+  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
+  template for (extern static auto y : {1, 2});
+  // expected-error@-1 {{cannot combine with previous 'extern' declaration 
specifier}}
+  // expected-error@-2 {{expansion variable 'y' may not be declared 'extern'}}
+  // brace-warning@-3 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
+  template for (static auto y : {1, 2});
+  // expected-error@-1 {{expansion variable 'y' may not be declared 'static'}}
+  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
+  template for (thread_local auto y : {1, 2});
+  // expected-error@-1 {{'thread_local' variables must have global storage}}
+  // brace-warning@-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@-1 {{expansion variable 'y' may not be declared 
'thread_local'}}
+  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
+  template for (__thread auto y : {1, 2});
+  // expected-error@-1 {{'__thread' variables must have global storage}}
+  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
+  template for (static __thread auto y : {1, 2});
+  // expected-error@-1 {{expansion variable 'y' may not be declared 'static'}}
+  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
+  template for (constinit auto y : {1, 2});
+  // expected-error@-1 {{local variable cannot be declared 'constinit'}}
+  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
+  template for (consteval auto y : {1, 2});
+  // expected-error@-1 {{consteval can only be used in function declarations}}
+  // brace-warning@-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@-1 {{expansion variable 'y' may not be declared 'extern'}}
+  // brace-warning@-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@-1 {{cannot combine with previous 'extern' declaration 
specifier}}
+  // expected-error@-2 {{expansion variable 'y' may not be declared 'extern'}}
+  // brace-warning@-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@-1 {{expansion variable 'y' may not be declared 'static'}}
+  // brace-warning@-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@-1 {{'thread_local' variables must have global storage}}
+  // brace-warning@-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@-1 {{expansion variable 'y' may not be declared 
'thread_local'}}
+  // brace-warning@-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@-1 {{'__thread' variables must have global storage}}
+  // brace-warning@-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@-1 {{expansion variable 'y' may not be declared 'static'}}
+  // brace-warning@-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@-1 {{local variable cannot be declared 'constinit'}}
+  // brace-warning@-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@-1 {{consteval can only be used in function declarations}}
+  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
+  template for (auto y : {abc, -+, });
+  // expected-error@-1 {{use of undeclared identifier 'abc'}}
+  // expected-error@-2 {{expected expression}}
+  // brace-warning@-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 <[email protected]>
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@-1 {{expansion statement requires type for expansion 
variable}}
-  // brace-warning@-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@-1 {{expected ')'}}
-  // expected-note@-2 {{to match this '('}}
-  // brace-warning@-3 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
-  template for (extern auto y : {1, 2});
-  // expected-error@-1 {{expansion variable 'y' may not be declared 'extern'}}
-  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
-  template for (register auto y : {1, 2});
-  // expected-error@-1 {{expansion variable 'y' may not be declared 
'register'}}
-  // expected-error@-2 {{ISO C++17 does not allow 'register' storage class 
specifier}}
-  // brace-warning@-3 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
-  template for (__private_extern__ auto y : {1, 2});
-  // expected-error@-1 {{expansion variable 'y' may not be declared 'extern'}}
-  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
-  template for (extern static auto y : {1, 2});
-  // expected-error@-1 {{cannot combine with previous 'extern' declaration 
specifier}}
-  // expected-error@-2 {{expansion variable 'y' may not be declared 'extern'}}
-  // brace-warning@-3 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
-  template for (static auto y : {1, 2});
-  // expected-error@-1 {{expansion variable 'y' may not be declared 'static'}}
-  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
-  template for (thread_local auto y : {1, 2});
-  // expected-error@-1 {{'thread_local' variables must have global storage}}
-  // brace-warning@-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@-1 {{expansion variable 'y' may not be declared 
'thread_local'}}
-  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
-  template for (__thread auto y : {1, 2});
-  // expected-error@-1 {{'__thread' variables must have global storage}}
-  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
-  template for (static __thread auto y : {1, 2});
-  // expected-error@-1 {{expansion variable 'y' may not be declared 'static'}}
-  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
-  template for (constinit auto y : {1, 2});
-  // expected-error@-1 {{local variable cannot be declared 'constinit'}}
-  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
-  template for (consteval auto y : {1, 2});
-  // expected-error@-1 {{consteval can only be used in function declarations}}
-  // brace-warning@-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@-1 {{expansion variable 'y' may not be declared 'extern'}}
-  // brace-warning@-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@-1 {{cannot combine with previous 'extern' declaration 
specifier}}
-  // expected-error@-2 {{expansion variable 'y' may not be declared 'extern'}}
-  // brace-warning@-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@-1 {{expansion variable 'y' may not be declared 'static'}}
-  // brace-warning@-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@-1 {{'thread_local' variables must have global storage}}
-  // brace-warning@-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@-1 {{expansion variable 'y' may not be declared 
'thread_local'}}
-  // brace-warning@-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@-1 {{'__thread' variables must have global storage}}
-  // brace-warning@-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@-1 {{expansion variable 'y' may not be declared 'static'}}
-  // brace-warning@-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@-1 {{local variable cannot be declared 'constinit'}}
-  // brace-warning@-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@-1 {{consteval can only be used in function declarations}}
-  // brace-warning@-2 {{ISO C++ requires a compound statement to be the body 
of expansion statement}}
-  template for (auto y : {abc, -+, });
-  // expected-error@-1 {{use of undeclared identifier 'abc'}}
-  // expected-error@-2 {{expected expression}}
-  // brace-warning@-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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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}}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to