[clang] [Clang][C2y] Add support for if declarations (N3356 paper) (PR #198244)

Muhammad Bassiouni via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 14 06:56:17 PDT 2026


https://github.com/bassiounix updated https://github.com/llvm/llvm-project/pull/198244

>From 4b23504a24dd2eed4681159da7d26681e2508443 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Mon, 18 May 2026 10:05:00 +0300
Subject: [PATCH 01/32] [Clang][C2y] Add support for if declaration

---
 .../clang/Basic/DiagnosticParseKinds.td       |  2 +
 clang/include/clang/Parse/Parser.h            | 10 ++--
 clang/lib/Parse/ParseDecl.cpp                 |  5 +-
 clang/lib/Parse/ParseExprCXX.cpp              | 47 +++++++++++----
 clang/lib/Parse/ParseStmt.cpp                 |  2 +-
 clang/lib/Parse/ParseTentative.cpp            |  4 +-
 clang/test/C/C2y/n3267.c                      | 59 +++++++++++++++++++
 7 files changed, 107 insertions(+), 22 deletions(-)
 create mode 100644 clang/test/C/C2y/n3267.c

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 7bcd1870a2600..9c4527764226b 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -220,6 +220,8 @@ def ext_c2y_case_range : Extension<
   "case ranges are a C2y extension">, InGroup<C2y>;
 def err_c2y_labeled_break_continue : Error<
   "named %select{'break'|'continue'}0 is only supported in C2y">;
+def err_c2y_first_condition_clause_is_not_declaration : Error<
+  "first clause in condition must be a declaration">;
 
 // Generic errors.
 def err_expected_expression : Error<"expected expression">;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index dc3dc8a4ae0e9..5182e2849f8d7 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -5039,12 +5039,10 @@ class Parser : public CodeCompletionHandler {
   /// appropriate moment for a 'for' loop.
   ///
   /// \returns The parsed condition.
-  Sema::ConditionResult ParseCXXCondition(StmtResult *InitStmt,
-                                          SourceLocation Loc,
-                                          Sema::ConditionKind CK,
-                                          bool MissingOK,
-                                          ForRangeInfo *FRI = nullptr,
-                                          bool EnterForConditionScope = false);
+  Sema::ConditionResult ParseCXXCondition(
+      StmtResult *InitStmt, SourceLocation Loc, Sema::ConditionKind CK,
+      bool MissingOK, ForRangeInfo *FRI = nullptr,
+      bool EnterForConditionScope = false, bool isSecondCallForIfCond = false);
   DeclGroupPtrTy ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
                                                       ParsedAttributes &Attrs);
 
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 75ad821c245a5..0635508cd0713 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2162,10 +2162,11 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
   ParsedAttributes LocalAttrs(AttrFactory);
   LocalAttrs.takeAllPrependingFrom(Attrs);
   ParsingDeclarator D(*this, DS, LocalAttrs, Context);
-  if (TemplateInfo.TemplateParams)
+  if (!getLangOpts().C2y && TemplateInfo.TemplateParams)
     D.setTemplateParameterLists(*TemplateInfo.TemplateParams);
 
   bool IsTemplateSpecOrInst =
+      !getLangOpts().C2y &&
       (TemplateInfo.Kind == ParsedTemplateKind::ExplicitInstantiation ||
        TemplateInfo.Kind == ParsedTemplateKind::ExplicitSpecialization);
   SuppressAccessChecks SAC(*this, IsTemplateSpecOrInst);
@@ -2185,7 +2186,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
     while (MaybeParseHLSLAnnotations(D))
       ;
 
-  if (Tok.is(tok::kw_requires))
+  if (!getLangOpts().C2y && Tok.is(tok::kw_requires))
     ParseTrailingRequiresClauseWithScope(D);
 
   // Save late-parsed attributes for now; they need to be parsed in the
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 39c61f4b5bf5c..85782255a1b97 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1868,7 +1868,8 @@ Parser::ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
 Sema::ConditionResult
 Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
                           Sema::ConditionKind CK, bool MissingOK,
-                          ForRangeInfo *FRI, bool EnterForConditionScope) {
+                          ForRangeInfo *FRI, bool EnterForConditionScope,
+                          bool isSecondCallForIfCond) {
   // Helper to ensure we always enter a continue/break scope if requested.
   struct ForConditionScopeRAII {
     Scope *S;
@@ -1883,6 +1884,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
         S->setIsConditionVarScope(false);
     }
   } ForConditionScope{EnterForConditionScope ? getCurScope() : nullptr};
+  bool parsingIfOrSwitchCondition = !FRI && !EnterForConditionScope;
 
   ParenBraceBracketBalancer BalancerRAIIObj(*this);
   PreferredType.enterCondition(Actions, Tok.getLocation());
@@ -1898,10 +1900,11 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
   MaybeParseCXX11Attributes(attrs);
 
   const auto WarnOnInit = [this, &CK] {
-    Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
-                                ? diag::warn_cxx14_compat_init_statement
-                                : diag::ext_init_statement)
-        << (CK == Sema::ConditionKind::Switch);
+    if (!getLangOpts().C2y)
+      Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
+                                  ? diag::warn_cxx14_compat_init_statement
+                                  : diag::ext_init_statement)
+          << (CK == Sema::ConditionKind::Switch);
   };
 
   // Determine what kind of thing we have.
@@ -1924,7 +1927,9 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
       }
       ConsumeToken();
       *InitStmt = Actions.ActOnNullStmt(SemiLoc);
-      return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
+      return ParseCXXCondition(nullptr, Loc, CK, MissingOK, FRI,
+                               EnterForConditionScope,
+                               parsingIfOrSwitchCondition);
     }
 
     EnterExpressionEvaluationContext Eval(
@@ -1939,10 +1944,22 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
       return Sema::ConditionError();
 
     if (InitStmt && Tok.is(tok::semi)) {
+      if (getLangOpts().C2y && parsingIfOrSwitchCondition &&
+          !isSecondCallForIfCond)
+        // C2y only permits declaration in the first clause of an if condition,
+        // so it makes sense to error out in other condition. We can stop
+        // parsing here and just report an error but we chose to continue to
+        // generate an error about the second clause of the condition since
+        // there's a ';' found.
+        Diag(Tok.getLocation(),
+             diag::err_c2y_first_condition_clause_is_not_declaration);
+
       WarnOnInit();
       *InitStmt = Actions.ActOnExprStmt(Expr.get());
       ConsumeToken();
-      return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
+      return ParseCXXCondition(nullptr, Loc, CK, MissingOK, FRI,
+                               EnterForConditionScope,
+                               parsingIfOrSwitchCondition);
     }
 
     return Actions.ActOnCondition(getCurScope(), Loc, Expr.get(), CK,
@@ -1953,7 +1970,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
     WarnOnInit();
     DeclGroupPtrTy DG;
     SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
-    if (Tok.is(tok::kw_using))
+    if (!getLangOpts().C2y && Tok.is(tok::kw_using))
       DG = ParseAliasDeclarationInInitStatement(
           DeclaratorContext::SelectionInit, attrs);
     else {
@@ -1962,7 +1979,9 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
                                   attrs, DeclSpecAttrs, /*RequireSemi=*/true);
     }
     *InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
-    return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
+    return ParseCXXCondition(nullptr, Loc, CK, MissingOK, FRI,
+                             EnterForConditionScope,
+                             parsingIfOrSwitchCondition);
   }
 
   case ConditionOrInitStatement::ForRangeDecl: {
@@ -1978,7 +1997,12 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
     return Sema::ConditionResult();
   }
 
-  case ConditionOrInitStatement::ConditionDecl:
+  case ConditionOrInitStatement::ConditionDecl: {
+    if (getLangOpts().C2y && isSecondCallForIfCond) {
+      Diag(Tok.getLocation(), diag::err_expected_expression);
+      return Sema::ConditionError();
+    }
+  } break;
   case ConditionOrInitStatement::Error:
     break;
   }
@@ -2007,7 +2031,8 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
   }
 
   // If attributes are present, parse them.
-  MaybeParseGNUAttributes(DeclaratorInfo);
+  if (!getLangOpts().C2y)
+    MaybeParseGNUAttributes(DeclaratorInfo);
 
   // Type-check the declaration itself.
   DeclResult Dcl = Actions.ActOnCXXConditionDeclaration(getCurScope(),
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 1a45ed66950be..301898fb3955f 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1264,7 +1264,7 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
   T.consumeOpen();
   SourceLocation Start = Tok.getLocation();
 
-  if (getLangOpts().CPlusPlus) {
+  if (getLangOpts().CPlusPlus || getLangOpts().C2y) {
     Cond = ParseCXXCondition(InitStmt, Loc, CK, false);
   } else {
     ExprResult CondExpr = ParseExpression();
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index f77b1001332fe..8c4b328fe538f 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -453,7 +453,7 @@ Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement,
   ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement,
                                                  CanBeForRangeDecl);
 
-  if (CanBeInitStatement && Tok.is(tok::kw_using))
+  if (!getLangOpts().C2y && CanBeInitStatement && Tok.is(tok::kw_using))
     return ConditionOrInitStatement::InitStmtDecl;
   if (State.update(isCXXDeclarationSpecifier(ImplicitTypenameContext::No)))
     return State.result();
@@ -1065,7 +1065,7 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
 
     // Check for need to substitute AltiVec __vector keyword
     // for "vector" identifier.
-    if (TryAltiVecVectorToken())
+    if (!getLangOpts().C2y && TryAltiVecVectorToken())
       return TPResult::True;
 
     const Token &Next = NextToken();
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
new file mode 100644
index 0000000000000..df3dbc7561ff2
--- /dev/null
+++ b/clang/test/C/C2y/n3267.c
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -std=c2y -verify %s
+
+bool test_if() {
+  if (true) {}
+  if (bool x = true; x) {}
+  if (bool x = false) return x;
+  if ([[maybe_unused]] bool x = true) {}
+  if (bool x [[maybe_unused]] = true) {}
+  if ([[maybe_unused]] int x = 3; x > 0) {}
+  return false;
+}
+
+int test_switch() {
+  int y = 1;
+  switch (y) {}
+
+  switch (int x = 1; x) {
+  default:
+    y += x;
+  }
+
+  switch (int x [[maybe_unused]] = 1) {}
+  switch ([[maybe_unused]] int x = 1) {}
+
+  switch (int x = 1) {
+  default:
+    return y + x;
+  }
+}
+
+bool negative_test_if() {
+  if (true; true) {} /* expected-error {{first clause in condition must be a declaration}}
+                        expected-warning {{expression result unused}}*/
+  if (true; ) {} /* expected-error {{first clause in condition must be a declaration}}
+                    expected-error {{expected expression}}
+                    expected-warning {{expression result unused}} */
+  if (bool x = true; bool y = x) return y; /* expected-error {{expected expression}}
+                                              expected-error {{use of undeclared identifier 'y'}} */
+  if (true; bool y = true) return y; /* expected-error {{first clause in condition must be a declaration}}
+                                        expected-error {{expected expression}}
+                                        expected-error {{use of undeclared identifier 'y'}}
+                                        expected-warning {{expression result unused}}*/
+  return false;
+}
+
+int negative_test_switch() {
+  switch (true; 1) { /* expected-error {{first clause in condition must be a declaration}}
+                        expected-warning {{expression result unused}} */
+  default:
+    break;
+  }
+  switch (true; ) {} /* expected-error {{first clause in condition must be a declaration}}
+                        expected-error {{expected expression}}
+                        expected-warning {{expression result unused}} */
+  switch (int x = 1; int y = x) { // expected-error {{expected expression}}
+  default:
+    return y; // expected-error {{use of undeclared identifier 'y'}}
+  }
+}

>From 0d0f44120e207a70a45151bd407342082bc95970 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Mon, 18 May 2026 11:03:57 +0300
Subject: [PATCH 02/32] add exposure

---
 clang/docs/ReleaseNotes.rst | 32 +++++++++++++++++++++++++++-----
 clang/www/c_status.html     |  2 +-
 2 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4b0eb5b9d8505..cb14655b517d1 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -177,6 +177,28 @@ C Language Changes
 C2y Feature Support
 ^^^^^^^^^^^^^^^^^^^
 
+- Clang now supports C2y's new syntax for if and switch statements with initializer and condition variables, as specified in `N3356 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3356.htm>`_. For example:
+
+  .. code-block:: c
+
+    if (bool x = true; x) {
+      // ...
+    }
+
+    if (bool x = true) {
+      // ...
+    }
+
+    // attribute list on declarations are also supported
+    switch ([[maybe_unused]] int x = 1) {
+    default:
+      // ...
+    }
+
+    if (bool x [[maybe_unused]] = true; x) {
+      // ...
+    }
+
 - Implemented the type-specific C2y ``<stdbit.h>`` rotate functions with constexpr
   evaluation support:
   ``stdc_rotate_left_{uc,us,ui,ul,ull}`` and
@@ -353,7 +375,7 @@ Attribute Changes in Clang
 
 - The ``[[clang::unsafe_buffer_usage]]`` attribute is now supported in API
   notes. For example:
-  
+
   .. code-block:: yaml
 
     Functions:
@@ -751,10 +773,10 @@ clang-format
 ------------
 - Add ``ObjCSpaceAfterMethodDeclarationPrefix`` option to control space between the
   '-'/'+' and the return type in Objective-C method declarations
-- Deprecate the ``BinPackParameters`` and ``BinPackArguments`` options and replace 
-  them with the ``PackParameters`` and ``PackArguments`` structs (respectively) to 
-  unify packing behavior. Add the ``BreakAfter`` option to the structs, allowing 
-  parameter and argument lists to be formatted with one parameter/argument on each 
+- Deprecate the ``BinPackParameters`` and ``BinPackArguments`` options and replace
+  them with the ``PackParameters`` and ``PackArguments`` structs (respectively) to
+  unify packing behavior. Add the ``BreakAfter`` option to the structs, allowing
+  parameter and argument lists to be formatted with one parameter/argument on each
   line if they exceed the specified count.
 - Add ``AfterComma`` value to ``BreakConstructorInitializers`` to allow breaking
   constructor initializers after commas, keeping the colon on the same line.
diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index 5270033471167..3ae0e0a7d3146 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -181,7 +181,7 @@ <h2 id="c2y">C2y implementation status</h2>
     <tr>
       <td>'if' declarations, v2</td>
       <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3356.htm">N3356</a></td>
-      <td class="none" align="center">No</td>
+      <td class="unreleased" align="center">Clang 23</td>
     </tr>
     <tr>
       <td>Allowing stricter alignment for atomic types</td>

>From 0e20732a06e32b6857178c03f2e77670f5cbd3b3 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Tue, 19 May 2026 01:33:12 +0300
Subject: [PATCH 03/32] address diff in release notes

---
 clang/docs/ReleaseNotes.rst | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index cb14655b517d1..afea236bdb012 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -375,7 +375,7 @@ Attribute Changes in Clang
 
 - The ``[[clang::unsafe_buffer_usage]]`` attribute is now supported in API
   notes. For example:
-
+  
   .. code-block:: yaml
 
     Functions:
@@ -773,10 +773,10 @@ clang-format
 ------------
 - Add ``ObjCSpaceAfterMethodDeclarationPrefix`` option to control space between the
   '-'/'+' and the return type in Objective-C method declarations
-- Deprecate the ``BinPackParameters`` and ``BinPackArguments`` options and replace
-  them with the ``PackParameters`` and ``PackArguments`` structs (respectively) to
-  unify packing behavior. Add the ``BreakAfter`` option to the structs, allowing
-  parameter and argument lists to be formatted with one parameter/argument on each
+- Deprecate the ``BinPackParameters`` and ``BinPackArguments`` options and replace 
+  them with the ``PackParameters`` and ``PackArguments`` structs (respectively) to 
+  unify packing behavior. Add the ``BreakAfter`` option to the structs, allowing 
+  parameter and argument lists to be formatted with one parameter/argument on each 
   line if they exceed the specified count.
 - Add ``AfterComma`` value to ``BreakConstructorInitializers`` to allow breaking
   constructor initializers after commas, keeping the colon on the same line.

>From 46da0c5e77131686dab5ce8d090e7a7a99d372de Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Tue, 19 May 2026 01:34:57 +0300
Subject: [PATCH 04/32] rename `isSecondCallForIfCond` to
 `isParsingSecondClauseOfC2yIfCondition`

---
 clang/include/clang/Parse/Parser.h | 10 ++++++----
 clang/lib/Parse/ParseExprCXX.cpp   | 17 +++++++++--------
 2 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 5182e2849f8d7..1500bcef9d434 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -5039,10 +5039,12 @@ class Parser : public CodeCompletionHandler {
   /// appropriate moment for a 'for' loop.
   ///
   /// \returns The parsed condition.
-  Sema::ConditionResult ParseCXXCondition(
-      StmtResult *InitStmt, SourceLocation Loc, Sema::ConditionKind CK,
-      bool MissingOK, ForRangeInfo *FRI = nullptr,
-      bool EnterForConditionScope = false, bool isSecondCallForIfCond = false);
+  Sema::ConditionResult
+  ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
+                    Sema::ConditionKind CK, bool MissingOK,
+                    ForRangeInfo *FRI = nullptr,
+                    bool EnterForConditionScope = false,
+                    bool isParsingSecondClauseOfC2yIfCondition = false);
   DeclGroupPtrTy ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
                                                       ParsedAttributes &Attrs);
 
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 85782255a1b97..3531f62cdfaf2 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1869,7 +1869,7 @@ Sema::ConditionResult
 Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
                           Sema::ConditionKind CK, bool MissingOK,
                           ForRangeInfo *FRI, bool EnterForConditionScope,
-                          bool isSecondCallForIfCond) {
+                          bool isParsingSecondClauseOfC2yIfCondition) {
   // Helper to ensure we always enter a continue/break scope if requested.
   struct ForConditionScopeRAII {
     Scope *S;
@@ -1884,7 +1884,8 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
         S->setIsConditionVarScope(false);
     }
   } ForConditionScope{EnterForConditionScope ? getCurScope() : nullptr};
-  bool parsingIfOrSwitchCondition = !FRI && !EnterForConditionScope;
+  bool parsingC2yIfOrSwitchCondition =
+      getLangOpts().C2y && !FRI && !EnterForConditionScope;
 
   ParenBraceBracketBalancer BalancerRAIIObj(*this);
   PreferredType.enterCondition(Actions, Tok.getLocation());
@@ -1929,7 +1930,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
       *InitStmt = Actions.ActOnNullStmt(SemiLoc);
       return ParseCXXCondition(nullptr, Loc, CK, MissingOK, FRI,
                                EnterForConditionScope,
-                               parsingIfOrSwitchCondition);
+                               parsingC2yIfOrSwitchCondition);
     }
 
     EnterExpressionEvaluationContext Eval(
@@ -1944,8 +1945,8 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
       return Sema::ConditionError();
 
     if (InitStmt && Tok.is(tok::semi)) {
-      if (getLangOpts().C2y && parsingIfOrSwitchCondition &&
-          !isSecondCallForIfCond)
+      if (parsingC2yIfOrSwitchCondition &&
+          !isParsingSecondClauseOfC2yIfCondition)
         // C2y only permits declaration in the first clause of an if condition,
         // so it makes sense to error out in other condition. We can stop
         // parsing here and just report an error but we chose to continue to
@@ -1959,7 +1960,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
       ConsumeToken();
       return ParseCXXCondition(nullptr, Loc, CK, MissingOK, FRI,
                                EnterForConditionScope,
-                               parsingIfOrSwitchCondition);
+                               parsingC2yIfOrSwitchCondition);
     }
 
     return Actions.ActOnCondition(getCurScope(), Loc, Expr.get(), CK,
@@ -1981,7 +1982,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
     *InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
     return ParseCXXCondition(nullptr, Loc, CK, MissingOK, FRI,
                              EnterForConditionScope,
-                             parsingIfOrSwitchCondition);
+                             parsingC2yIfOrSwitchCondition);
   }
 
   case ConditionOrInitStatement::ForRangeDecl: {
@@ -1998,7 +1999,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
   }
 
   case ConditionOrInitStatement::ConditionDecl: {
-    if (getLangOpts().C2y && isSecondCallForIfCond) {
+    if (getLangOpts().C2y && isParsingSecondClauseOfC2yIfCondition) {
       Diag(Tok.getLocation(), diag::err_expected_expression);
       return Sema::ConditionError();
     }

>From 001d432f4743cb2f9eb4a6aebbf295330e18b646 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Tue, 19 May 2026 02:34:23 +0300
Subject: [PATCH 05/32] move checks

---
 clang/lib/Parse/ParseExprCXX.cpp | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 3531f62cdfaf2..2e92ea3c511dd 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1998,13 +1998,12 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
     return Sema::ConditionResult();
   }
 
-  case ConditionOrInitStatement::ConditionDecl: {
+  case ConditionOrInitStatement::ConditionDecl:
+  case ConditionOrInitStatement::Error:
     if (getLangOpts().C2y && isParsingSecondClauseOfC2yIfCondition) {
       Diag(Tok.getLocation(), diag::err_expected_expression);
       return Sema::ConditionError();
     }
-  } break;
-  case ConditionOrInitStatement::Error:
     break;
   }
 
@@ -2032,8 +2031,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
   }
 
   // If attributes are present, parse them.
-  if (!getLangOpts().C2y)
-    MaybeParseGNUAttributes(DeclaratorInfo);
+  MaybeParseGNUAttributes(DeclaratorInfo);
 
   // Type-check the declaration itself.
   DeclResult Dcl = Actions.ActOnCXXConditionDeclaration(getCurScope(),

>From 19ddb57fdf9d91d4536caf9dcadfcb670c7709c4 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Tue, 19 May 2026 20:13:05 +0300
Subject: [PATCH 06/32] change camelCase to PascalCase

---
 clang/include/clang/Parse/Parser.h | 2 +-
 clang/lib/Parse/ParseExprCXX.cpp   | 7 +++----
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 1500bcef9d434..297bdb373bc7d 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -5044,7 +5044,7 @@ class Parser : public CodeCompletionHandler {
                     Sema::ConditionKind CK, bool MissingOK,
                     ForRangeInfo *FRI = nullptr,
                     bool EnterForConditionScope = false,
-                    bool isParsingSecondClauseOfC2yIfCondition = false);
+                    bool ParsingSecondClauseOfC2yIfCondition = false);
   DeclGroupPtrTy ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
                                                       ParsedAttributes &Attrs);
 
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 2e92ea3c511dd..9117e90fa78d8 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1869,7 +1869,7 @@ Sema::ConditionResult
 Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
                           Sema::ConditionKind CK, bool MissingOK,
                           ForRangeInfo *FRI, bool EnterForConditionScope,
-                          bool isParsingSecondClauseOfC2yIfCondition) {
+                          bool ParsingSecondClauseOfC2yIfCondition) {
   // Helper to ensure we always enter a continue/break scope if requested.
   struct ForConditionScopeRAII {
     Scope *S;
@@ -1945,8 +1945,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
       return Sema::ConditionError();
 
     if (InitStmt && Tok.is(tok::semi)) {
-      if (parsingC2yIfOrSwitchCondition &&
-          !isParsingSecondClauseOfC2yIfCondition)
+      if (parsingC2yIfOrSwitchCondition && !ParsingSecondClauseOfC2yIfCondition)
         // C2y only permits declaration in the first clause of an if condition,
         // so it makes sense to error out in other condition. We can stop
         // parsing here and just report an error but we chose to continue to
@@ -2000,7 +1999,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
 
   case ConditionOrInitStatement::ConditionDecl:
   case ConditionOrInitStatement::Error:
-    if (getLangOpts().C2y && isParsingSecondClauseOfC2yIfCondition) {
+    if (getLangOpts().C2y && ParsingSecondClauseOfC2yIfCondition) {
       Diag(Tok.getLocation(), diag::err_expected_expression);
       return Sema::ConditionError();
     }

>From 946d453d11c9cf7cb119ed4fc9adda35a6fbfea3 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Tue, 19 May 2026 20:25:51 +0300
Subject: [PATCH 07/32] remove unnecessary checks

---
 clang/lib/Parse/ParseDecl.cpp      | 5 ++---
 clang/lib/Parse/ParseExprCXX.cpp   | 4 ++--
 clang/lib/Parse/ParseTentative.cpp | 4 ++--
 3 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 0635508cd0713..75ad821c245a5 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2162,11 +2162,10 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
   ParsedAttributes LocalAttrs(AttrFactory);
   LocalAttrs.takeAllPrependingFrom(Attrs);
   ParsingDeclarator D(*this, DS, LocalAttrs, Context);
-  if (!getLangOpts().C2y && TemplateInfo.TemplateParams)
+  if (TemplateInfo.TemplateParams)
     D.setTemplateParameterLists(*TemplateInfo.TemplateParams);
 
   bool IsTemplateSpecOrInst =
-      !getLangOpts().C2y &&
       (TemplateInfo.Kind == ParsedTemplateKind::ExplicitInstantiation ||
        TemplateInfo.Kind == ParsedTemplateKind::ExplicitSpecialization);
   SuppressAccessChecks SAC(*this, IsTemplateSpecOrInst);
@@ -2186,7 +2185,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
     while (MaybeParseHLSLAnnotations(D))
       ;
 
-  if (!getLangOpts().C2y && Tok.is(tok::kw_requires))
+  if (Tok.is(tok::kw_requires))
     ParseTrailingRequiresClauseWithScope(D);
 
   // Save late-parsed attributes for now; they need to be parsed in the
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 9117e90fa78d8..4a9f671ef72ab 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1885,7 +1885,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
     }
   } ForConditionScope{EnterForConditionScope ? getCurScope() : nullptr};
   bool parsingC2yIfOrSwitchCondition =
-      getLangOpts().C2y && !FRI && !EnterForConditionScope;
+      getLangOpts().C2y && !EnterForConditionScope;
 
   ParenBraceBracketBalancer BalancerRAIIObj(*this);
   PreferredType.enterCondition(Actions, Tok.getLocation());
@@ -1970,7 +1970,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
     WarnOnInit();
     DeclGroupPtrTy DG;
     SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
-    if (!getLangOpts().C2y && Tok.is(tok::kw_using))
+    if (Tok.is(tok::kw_using))
       DG = ParseAliasDeclarationInInitStatement(
           DeclaratorContext::SelectionInit, attrs);
     else {
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 8c4b328fe538f..f77b1001332fe 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -453,7 +453,7 @@ Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement,
   ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement,
                                                  CanBeForRangeDecl);
 
-  if (!getLangOpts().C2y && CanBeInitStatement && Tok.is(tok::kw_using))
+  if (CanBeInitStatement && Tok.is(tok::kw_using))
     return ConditionOrInitStatement::InitStmtDecl;
   if (State.update(isCXXDeclarationSpecifier(ImplicitTypenameContext::No)))
     return State.result();
@@ -1065,7 +1065,7 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
 
     // Check for need to substitute AltiVec __vector keyword
     // for "vector" identifier.
-    if (!getLangOpts().C2y && TryAltiVecVectorToken())
+    if (TryAltiVecVectorToken())
       return TPResult::True;
 
     const Token &Next = NextToken();

>From 403880ed7de08fab16a49d3c62fe4af7a7611126 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Wed, 20 May 2026 05:28:08 +0300
Subject: [PATCH 08/32] rethink parsing

---
 clang/docs/LanguageExtensions.rst             |  1 +
 .../clang/Basic/DiagnosticParseKinds.td       |  6 +++
 clang/include/clang/Parse/Parser.h            | 12 +++---
 clang/lib/Parse/ParseExprCXX.cpp              | 37 +++++--------------
 clang/lib/Parse/ParseStmt.cpp                 | 34 +++++++++++------
 clang/test/C/C2y/n3267.c                      |  7 ++--
 6 files changed, 48 insertions(+), 49 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 03cb02deb5e7f..33f07f86e1e23 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -2014,6 +2014,7 @@ Octal literals prefixed with ``0o`` or ``0O``                                  C
 ``_Generic`` with a type operand (N3260)                                       C2y           C89, C++
 ``++``/``--`` on ``_Complex`` value (N3259)                                    C2y           C89, C++
 ``__COUNTER__`` (N3457)                                                        C2y           C89, C++
+If declarations (N3356)                                                        C2y           C99
 ============================================= ================================ ============= =============
 
 Builtin type aliases
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 9c4527764226b..092fe1ed5344d 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -169,6 +169,12 @@ def ext_c2y_generic_with_type_arg : Extension<
 def warn_c2y_compat_generic_with_type_arg : Warning<
   "passing a type argument as the first operand to '_Generic' is incompatible "
   "with C standards before C2y">, InGroup<CPre2yCompat>, DefaultIgnore;
+def warn_c2y_compat_init_statement : Warning<
+  "%select{if|switch}0 initialization statements are incompatible with "
+  "C standards before C2y">, DefaultIgnore, InGroup<CPre2yCompat>;
+def ext_c2y_init_statement : ExtWarn<
+  "'%select{if|switch}0' initialization statements are a C2y extension">,
+  InGroup<C2y>;
 
 def ext_c99_feature : Extension<
   "'%0' is a C99 extension">, InGroup<C99>;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 297bdb373bc7d..dc3dc8a4ae0e9 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -5039,12 +5039,12 @@ class Parser : public CodeCompletionHandler {
   /// appropriate moment for a 'for' loop.
   ///
   /// \returns The parsed condition.
-  Sema::ConditionResult
-  ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
-                    Sema::ConditionKind CK, bool MissingOK,
-                    ForRangeInfo *FRI = nullptr,
-                    bool EnterForConditionScope = false,
-                    bool ParsingSecondClauseOfC2yIfCondition = false);
+  Sema::ConditionResult ParseCXXCondition(StmtResult *InitStmt,
+                                          SourceLocation Loc,
+                                          Sema::ConditionKind CK,
+                                          bool MissingOK,
+                                          ForRangeInfo *FRI = nullptr,
+                                          bool EnterForConditionScope = false);
   DeclGroupPtrTy ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
                                                       ParsedAttributes &Attrs);
 
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 4a9f671ef72ab..d23c0ab1588d3 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1868,8 +1868,7 @@ Parser::ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
 Sema::ConditionResult
 Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
                           Sema::ConditionKind CK, bool MissingOK,
-                          ForRangeInfo *FRI, bool EnterForConditionScope,
-                          bool ParsingSecondClauseOfC2yIfCondition) {
+                          ForRangeInfo *FRI, bool EnterForConditionScope) {
   // Helper to ensure we always enter a continue/break scope if requested.
   struct ForConditionScopeRAII {
     Scope *S;
@@ -1884,8 +1883,6 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
         S->setIsConditionVarScope(false);
     }
   } ForConditionScope{EnterForConditionScope ? getCurScope() : nullptr};
-  bool parsingC2yIfOrSwitchCondition =
-      getLangOpts().C2y && !EnterForConditionScope;
 
   ParenBraceBracketBalancer BalancerRAIIObj(*this);
   PreferredType.enterCondition(Actions, Tok.getLocation());
@@ -1901,11 +1898,16 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
   MaybeParseCXX11Attributes(attrs);
 
   const auto WarnOnInit = [this, &CK] {
-    if (!getLangOpts().C2y)
+    if (getLangOpts().CPlusPlus)
       Diag(Tok.getLocation(), getLangOpts().CPlusPlus17
                                   ? diag::warn_cxx14_compat_init_statement
                                   : diag::ext_init_statement)
           << (CK == Sema::ConditionKind::Switch);
+    else
+      Diag(Tok.getLocation(), getLangOpts().C2y
+                                  ? diag::warn_c2y_compat_init_statement
+                                  : diag::ext_c2y_init_statement)
+          << (CK == Sema::ConditionKind::Switch);
   };
 
   // Determine what kind of thing we have.
@@ -1928,9 +1930,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
       }
       ConsumeToken();
       *InitStmt = Actions.ActOnNullStmt(SemiLoc);
-      return ParseCXXCondition(nullptr, Loc, CK, MissingOK, FRI,
-                               EnterForConditionScope,
-                               parsingC2yIfOrSwitchCondition);
+      return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
     }
 
     EnterExpressionEvaluationContext Eval(
@@ -1945,21 +1945,10 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
       return Sema::ConditionError();
 
     if (InitStmt && Tok.is(tok::semi)) {
-      if (parsingC2yIfOrSwitchCondition && !ParsingSecondClauseOfC2yIfCondition)
-        // C2y only permits declaration in the first clause of an if condition,
-        // so it makes sense to error out in other condition. We can stop
-        // parsing here and just report an error but we chose to continue to
-        // generate an error about the second clause of the condition since
-        // there's a ';' found.
-        Diag(Tok.getLocation(),
-             diag::err_c2y_first_condition_clause_is_not_declaration);
-
       WarnOnInit();
       *InitStmt = Actions.ActOnExprStmt(Expr.get());
       ConsumeToken();
-      return ParseCXXCondition(nullptr, Loc, CK, MissingOK, FRI,
-                               EnterForConditionScope,
-                               parsingC2yIfOrSwitchCondition);
+      return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
     }
 
     return Actions.ActOnCondition(getCurScope(), Loc, Expr.get(), CK,
@@ -1979,9 +1968,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
                                   attrs, DeclSpecAttrs, /*RequireSemi=*/true);
     }
     *InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
-    return ParseCXXCondition(nullptr, Loc, CK, MissingOK, FRI,
-                             EnterForConditionScope,
-                             parsingC2yIfOrSwitchCondition);
+    return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
   }
 
   case ConditionOrInitStatement::ForRangeDecl: {
@@ -1999,10 +1986,6 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
 
   case ConditionOrInitStatement::ConditionDecl:
   case ConditionOrInitStatement::Error:
-    if (getLangOpts().C2y && ParsingSecondClauseOfC2yIfCondition) {
-      Diag(Tok.getLocation(), diag::err_expected_expression);
-      return Sema::ConditionError();
-    }
     break;
   }
 
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 301898fb3955f..13b549ad6e2d6 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1264,18 +1264,7 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
   T.consumeOpen();
   SourceLocation Start = Tok.getLocation();
 
-  if (getLangOpts().CPlusPlus || getLangOpts().C2y) {
-    Cond = ParseCXXCondition(InitStmt, Loc, CK, false);
-  } else {
-    ExprResult CondExpr = ParseExpression();
-
-    // If required, convert to a boolean value.
-    if (CondExpr.isInvalid())
-      Cond = Sema::ConditionError();
-    else
-      Cond = Actions.ActOnCondition(getCurScope(), Loc, CondExpr.get(), CK,
-                                    /*MissingOK=*/false);
-  }
+  Cond = ParseCXXCondition(InitStmt, Loc, CK, false);
 
   // If the parser was confused by the condition and we don't have a ')', try to
   // recover by skipping ahead to a semi and bailing out.  If condexp is
@@ -1297,6 +1286,27 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
                                     /*MissingOK=*/false);
   }
 
+  if (getLangOpts().C99 && InitStmt->get() != nullptr &&
+      InitStmt->get()->getStmtClass() != Stmt::DeclStmtClass){
+    // C2y only permits declaration in the first clause of an if condition,
+    // so it makes sense to error out in other conditions.
+    Diag(InitStmt->get()->getBeginLoc(),
+         diag::err_c2y_first_condition_clause_is_not_declaration)
+        << InitStmt->get()->getSourceRange();
+    Cond = Sema::ConditionError();
+    return true;
+  }
+
+  if (getLangOpts().C99 && InitStmt->get() != nullptr &&
+      Cond.get().first != nullptr){
+    // C2y only permits expression in the second clause of an if condition,
+    // so it makes sense to error out in other conditions.
+    Diag(Cond.get().first->getBeginLoc(), diag::err_expected_expression)
+        << Cond.get().first->getSourceRange();
+    Cond = Sema::ConditionError();
+    return true;
+  }
+
   // Either the condition is valid or the rparen is present.
   T.consumeClose();
   LParenLoc = T.getOpenLocation();
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index df3dbc7561ff2..6cc2bdf4bc4a4 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -34,11 +34,10 @@ bool negative_test_if() {
   if (true; ) {} /* expected-error {{first clause in condition must be a declaration}}
                     expected-error {{expected expression}}
                     expected-warning {{expression result unused}} */
-  if (bool x = true; bool y = x) return y; /* expected-error {{expected expression}}
-                                              expected-error {{use of undeclared identifier 'y'}} */
+  if (bool x = true; bool y = x) return y; // expected-error {{expected expression}}
+
   if (true; bool y = true) return y; /* expected-error {{first clause in condition must be a declaration}}
                                         expected-error {{expected expression}}
-                                        expected-error {{use of undeclared identifier 'y'}}
                                         expected-warning {{expression result unused}}*/
   return false;
 }
@@ -54,6 +53,6 @@ int negative_test_switch() {
                         expected-warning {{expression result unused}} */
   switch (int x = 1; int y = x) { // expected-error {{expected expression}}
   default:
-    return y; // expected-error {{use of undeclared identifier 'y'}}
+    return y;
   }
 }

>From 52998b10b7f6c1f3031c546d67cdbd6b14a64ee0 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Wed, 20 May 2026 05:29:41 +0300
Subject: [PATCH 09/32] format

---
 clang/lib/Parse/ParseStmt.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 13b549ad6e2d6..690bee8e1c4bc 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1287,7 +1287,7 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
   }
 
   if (getLangOpts().C99 && InitStmt->get() != nullptr &&
-      InitStmt->get()->getStmtClass() != Stmt::DeclStmtClass){
+      InitStmt->get()->getStmtClass() != Stmt::DeclStmtClass) {
     // C2y only permits declaration in the first clause of an if condition,
     // so it makes sense to error out in other conditions.
     Diag(InitStmt->get()->getBeginLoc(),
@@ -1298,7 +1298,7 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
   }
 
   if (getLangOpts().C99 && InitStmt->get() != nullptr &&
-      Cond.get().first != nullptr){
+      Cond.get().first != nullptr) {
     // C2y only permits expression in the second clause of an if condition,
     // so it makes sense to error out in other conditions.
     Diag(Cond.get().first->getBeginLoc(), diag::err_expected_expression)

>From 7108d370b462cf7b5db3b21c8fd30a9b7de9f0e6 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Wed, 20 May 2026 08:47:39 +0300
Subject: [PATCH 10/32] fix ci

---
 clang/lib/Parse/ParseStmt.cpp | 40 ++++++++++++++++++-----------------
 1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 690bee8e1c4bc..38899baaf50a7 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1286,25 +1286,27 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
                                     /*MissingOK=*/false);
   }
 
-  if (getLangOpts().C99 && InitStmt->get() != nullptr &&
-      InitStmt->get()->getStmtClass() != Stmt::DeclStmtClass) {
-    // C2y only permits declaration in the first clause of an if condition,
-    // so it makes sense to error out in other conditions.
-    Diag(InitStmt->get()->getBeginLoc(),
-         diag::err_c2y_first_condition_clause_is_not_declaration)
-        << InitStmt->get()->getSourceRange();
-    Cond = Sema::ConditionError();
-    return true;
-  }
-
-  if (getLangOpts().C99 && InitStmt->get() != nullptr &&
-      Cond.get().first != nullptr) {
-    // C2y only permits expression in the second clause of an if condition,
-    // so it makes sense to error out in other conditions.
-    Diag(Cond.get().first->getBeginLoc(), diag::err_expected_expression)
-        << Cond.get().first->getSourceRange();
-    Cond = Sema::ConditionError();
-    return true;
+  if (getLangOpts().C99) {
+    if (InitStmt != nullptr && InitStmt->isUsable()) {
+      // handle the 2 clauses of declaration: (clause1; clause2)
+      if (InitStmt->get()->getStmtClass() != Stmt::DeclStmtClass)
+        // C2y only permits declaration in the first clause of an if condition,
+        // so it makes sense to error out in other conditions.
+        Diag(InitStmt->get()->getBeginLoc(),
+            diag::err_c2y_first_condition_clause_is_not_declaration)
+            << InitStmt->get()->getSourceRange();
+
+      if (Cond.get().first != nullptr)
+        // C2y only permits expression in the second clause of an if condition,
+        // so it makes sense to error out in other conditions.
+        Diag(Cond.get().first->getBeginLoc(), diag::err_expected_expression)
+            << Cond.get().first->getSourceRange();
+    } else if (Cond.get().first != nullptr)
+      // handle: if (int decl = 0) {}
+      Diag(Cond.get().first->getBeginLoc(),
+           getLangOpts().C2y ? diag::warn_c2y_compat_init_statement
+                             : diag::ext_c2y_init_statement)
+          << (CK == Sema::ConditionKind::Switch);
   }
 
   // Either the condition is valid or the rparen is present.

>From aab7a00164a0c1e71e51d192618acc23f16f803d Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Wed, 20 May 2026 09:51:01 +0300
Subject: [PATCH 11/32] format

---
 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 38899baaf50a7..2def5572593c2 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1293,7 +1293,7 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
         // C2y only permits declaration in the first clause of an if condition,
         // so it makes sense to error out in other conditions.
         Diag(InitStmt->get()->getBeginLoc(),
-            diag::err_c2y_first_condition_clause_is_not_declaration)
+             diag::err_c2y_first_condition_clause_is_not_declaration)
             << InitStmt->get()->getSourceRange();
 
       if (Cond.get().first != nullptr)

>From f68e79f89338d9caec8ca20bc6c6ed18a363e46b Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Wed, 20 May 2026 18:31:56 +0300
Subject: [PATCH 12/32] Drop CXX from ParseCXXCondition

---
 clang/include/clang/Parse/Parser.h | 10 ++++------
 clang/lib/Parse/ParseExprCXX.cpp   | 15 ++++++++-------
 clang/lib/Parse/ParseStmt.cpp      |  4 ++--
 3 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index dc3dc8a4ae0e9..e836c23ef424a 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -5039,12 +5039,10 @@ class Parser : public CodeCompletionHandler {
   /// appropriate moment for a 'for' loop.
   ///
   /// \returns The parsed condition.
-  Sema::ConditionResult ParseCXXCondition(StmtResult *InitStmt,
-                                          SourceLocation Loc,
-                                          Sema::ConditionKind CK,
-                                          bool MissingOK,
-                                          ForRangeInfo *FRI = nullptr,
-                                          bool EnterForConditionScope = false);
+  Sema::ConditionResult ParseCondition(StmtResult *InitStmt, SourceLocation Loc,
+                                       Sema::ConditionKind CK, bool MissingOK,
+                                       ForRangeInfo *FRI = nullptr,
+                                       bool EnterForConditionScope = false);
   DeclGroupPtrTy ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
                                                       ParsedAttributes &Attrs);
 
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index d23c0ab1588d3..de36ef9b5b0be 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1865,10 +1865,11 @@ Parser::ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
   return DG;
 }
 
-Sema::ConditionResult
-Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
-                          Sema::ConditionKind CK, bool MissingOK,
-                          ForRangeInfo *FRI, bool EnterForConditionScope) {
+Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
+                                             SourceLocation Loc,
+                                             Sema::ConditionKind CK,
+                                             bool MissingOK, ForRangeInfo *FRI,
+                                             bool EnterForConditionScope) {
   // Helper to ensure we always enter a continue/break scope if requested.
   struct ForConditionScopeRAII {
     Scope *S;
@@ -1930,7 +1931,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
       }
       ConsumeToken();
       *InitStmt = Actions.ActOnNullStmt(SemiLoc);
-      return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
+      return ParseCondition(nullptr, Loc, CK, MissingOK);
     }
 
     EnterExpressionEvaluationContext Eval(
@@ -1948,7 +1949,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
       WarnOnInit();
       *InitStmt = Actions.ActOnExprStmt(Expr.get());
       ConsumeToken();
-      return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
+      return ParseCondition(nullptr, Loc, CK, MissingOK);
     }
 
     return Actions.ActOnCondition(getCurScope(), Loc, Expr.get(), CK,
@@ -1968,7 +1969,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
                                   attrs, DeclSpecAttrs, /*RequireSemi=*/true);
     }
     *InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
-    return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
+    return ParseCondition(nullptr, Loc, CK, MissingOK);
   }
 
   case ConditionOrInitStatement::ForRangeDecl: {
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 2def5572593c2..a06df4734f5c9 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1264,7 +1264,7 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
   T.consumeOpen();
   SourceLocation Start = Tok.getLocation();
 
-  Cond = ParseCXXCondition(InitStmt, Loc, CK, false);
+  Cond = ParseCondition(InitStmt, Loc, CK, false);
 
   // If the parser was confused by the condition and we don't have a ')', try to
   // recover by skipping ahead to a semi and bailing out.  If condexp is
@@ -2121,7 +2121,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
         ColonProtectionRAIIObject ColonProtection(*this, MightBeForRangeStmt);
         SourceLocation SecondPartStart = Tok.getLocation();
         Sema::ConditionKind CK = Sema::ConditionKind::Boolean;
-        SecondPart = ParseCXXCondition(
+        SecondPart = ParseCondition(
             /*InitStmt=*/nullptr, ForLoc, CK,
             // FIXME: recovery if we don't see another semi!
             /*MissingOK=*/true, MightBeForRangeStmt ? &ForRangeInfo : nullptr,

>From 37a3f35260961ca4780a6ec8ece8e46851c5f58d Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Wed, 20 May 2026 19:41:32 +0300
Subject: [PATCH 13/32] apply code review

---
 clang/lib/Parse/ParseStmt.cpp | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index a06df4734f5c9..09ff368388a77 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1286,19 +1286,17 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
                                     /*MissingOK=*/false);
   }
 
-  if (getLangOpts().C99) {
+  if (!getLangOpts().CPlusPlus) {
     if (InitStmt != nullptr && InitStmt->isUsable()) {
-      // handle the 2 clauses of declaration: (clause1; clause2)
-      if (InitStmt->get()->getStmtClass() != Stmt::DeclStmtClass)
-        // C2y only permits declaration in the first clause of an if condition,
-        // so it makes sense to error out in other conditions.
+      // Handle the 2 clauses of declaration: (clause1; clause2).
+      if (!isa<DeclStmt>(InitStmt->get()))
+        // C2y only permits declaration in the first clause of an if condition.
         Diag(InitStmt->get()->getBeginLoc(),
              diag::err_c2y_first_condition_clause_is_not_declaration)
             << InitStmt->get()->getSourceRange();
 
       if (Cond.get().first != nullptr)
-        // C2y only permits expression in the second clause of an if condition,
-        // so it makes sense to error out in other conditions.
+        // C2y only permits expression in the second clause of an if condition.
         Diag(Cond.get().first->getBeginLoc(), diag::err_expected_expression)
             << Cond.get().first->getSourceRange();
     } else if (Cond.get().first != nullptr)

>From 59acedbcce9ed6f00486e752d6b7a97de5352b01 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Wed, 20 May 2026 19:41:51 +0300
Subject: [PATCH 14/32] add more negative tests

---
 clang/test/C/C2y/n3267.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index 6cc2bdf4bc4a4..17e26561f10f8 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -39,6 +39,7 @@ bool negative_test_if() {
   if (true; bool y = true) return y; /* expected-error {{first clause in condition must be a declaration}}
                                         expected-error {{expected expression}}
                                         expected-warning {{expression result unused}}*/
+  if (int x) {} // expected-error {{variable declaration in condition must have an initializer}}
   return false;
 }
 
@@ -48,6 +49,9 @@ int negative_test_switch() {
   default:
     break;
   }
+
+  switch (int x) {} // expected-error {{variable declaration in condition must have an initializer}}
+
   switch (true; ) {} /* expected-error {{first clause in condition must be a declaration}}
                         expected-error {{expected expression}}
                         expected-warning {{expression result unused}} */

>From 825b89e8cc3dc79dd534455ba5795e0223984d26 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Wed, 20 May 2026 19:56:21 +0300
Subject: [PATCH 15/32] nit: comment style

---
 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 09ff368388a77..50b53389e5889 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1300,7 +1300,7 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
         Diag(Cond.get().first->getBeginLoc(), diag::err_expected_expression)
             << Cond.get().first->getSourceRange();
     } else if (Cond.get().first != nullptr)
-      // handle: if (int decl = 0) {}
+      // Handle: if (int decl = 0) {}.
       Diag(Cond.get().first->getBeginLoc(),
            getLangOpts().C2y ? diag::warn_c2y_compat_init_statement
                              : diag::ext_c2y_init_statement)

>From b59adaadd225267a496ceeb90a2a4080d6ea003e Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Thu, 21 May 2026 07:06:58 +0300
Subject: [PATCH 16/32] add more positive tests

---
 clang/test/C/C2y/n3267.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index 17e26561f10f8..1a3f94724739e 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -7,6 +7,9 @@ bool test_if() {
   if ([[maybe_unused]] bool x = true) {}
   if (bool x [[maybe_unused]] = true) {}
   if ([[maybe_unused]] int x = 3; x > 0) {}
+  if (struct A { int x;} a = {.x = 1}; a.x) {}
+  if (int arr[] = {1,2,3}; arr[1]) {}
+  if (auto x = 1; x) {}
   return false;
 }
 
@@ -22,6 +25,10 @@ int test_switch() {
   switch (int x [[maybe_unused]] = 1) {}
   switch ([[maybe_unused]] int x = 1) {}
 
+  switch (struct A { int x;} a = {.x = 1}; a.x) {}
+  switch (int arr[] = {1,2,3}; arr[1]) {}
+  switch (auto x = 1; x) {}
+
   switch (int x = 1) {
   default:
     return y + x;

>From 348bde9dda19fced4e88b3c031dd5d3c692db0a2 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Thu, 21 May 2026 07:15:19 +0300
Subject: [PATCH 17/32] fix name diverge from main

---
 clang/include/clang/Parse/Parser.h | 10 ++++------
 clang/lib/Parse/ParseExprCXX.cpp   | 10 +++++-----
 clang/lib/Parse/ParseOpenACC.cpp   |  6 +++---
 3 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index c6c492b4980af..476752fdd3b97 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -5005,7 +5005,7 @@ class Parser : public CodeCompletionHandler {
   //===--------------------------------------------------------------------===//
   // C++ if/switch/while/for condition expression.
 
-  /// ParseCXXCondition - if/switch/while condition expression.
+  /// ParseCondition - if/switch/while condition expression.
   ///
   /// \verbatim
   ///       condition:
@@ -5036,11 +5036,9 @@ class Parser : public CodeCompletionHandler {
   /// returned.
   ///
   /// \returns The parsed condition.
-  Sema::ConditionResult ParseCXXCondition(StmtResult *InitStmt,
-                                          SourceLocation Loc,
-                                          Sema::ConditionKind CK,
-                                          bool MissingOK,
-                                          ForRangeInfo *FRI = nullptr);
+  Sema::ConditionResult ParseCondition(StmtResult *InitStmt, SourceLocation Loc,
+                                       Sema::ConditionKind CK, bool MissingOK,
+                                       ForRangeInfo *FRI = nullptr);
   DeclGroupPtrTy ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
                                                       ParsedAttributes &Attrs);
 
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 8ca244d5f123b..03dd1683e2301 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1865,11 +1865,11 @@ Parser::ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
   return DG;
 }
 
-Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt,
-                                                SourceLocation Loc,
-                                                Sema::ConditionKind CK,
-                                                bool MissingOK,
-                                                ForRangeInfo *FRI) {
+Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
+                                             SourceLocation Loc,
+                                             Sema::ConditionKind CK,
+                                             bool MissingOK,
+                                             ForRangeInfo *FRI) {
   ParenBraceBracketBalancer BalancerRAIIObj(*this);
   PreferredType.enterCondition(Actions, Tok.getLocation());
 
diff --git a/clang/lib/Parse/ParseOpenACC.cpp b/clang/lib/Parse/ParseOpenACC.cpp
index a95c5730a001c..760525659154c 100644
--- a/clang/lib/Parse/ParseOpenACC.cpp
+++ b/clang/lib/Parse/ParseOpenACC.cpp
@@ -656,9 +656,9 @@ Parser::OpenACCClauseParseResult Parser::OpenACCSuccess(OpenACCClause *Clause) {
 
 ExprResult Parser::ParseOpenACCConditionExpr() {
   // FIXME: It isn't clear if the spec saying 'condition' means the same as
-  // it does in an if/while/etc (See ParseCXXCondition), however as it was
-  // written with Fortran/C in mind, we're going to assume it just means an
-  // 'expression evaluating to boolean'.
+  // it does in an if/while/etc (See ParseCondition), however as it was written
+  // with Fortran/C in mind, we're going to assume it just means an 'expression
+  // evaluating to boolean'.
   ExprResult ER = ParseExpression();
 
   if (!ER.isUsable())

>From 83022276053da9124ac13e2fb0466654aa8fbe22 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Fri, 22 May 2026 06:46:33 +0300
Subject: [PATCH 18/32] add support for `static_assert-decl` and
 `attribute-specifier-sequence` with respective warnings

---
 .../clang/Basic/DiagnosticParseKinds.td       |  3 ++
 clang/lib/Parse/ParseExprCXX.cpp              | 32 ++++++++++++++++++-
 clang/lib/Parse/ParseStmt.cpp                 |  3 +-
 clang/test/C/C2y/n3267.c                      | 14 ++++++++
 4 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 092fe1ed5344d..4d00d87def455 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -169,6 +169,9 @@ def ext_c2y_generic_with_type_arg : Extension<
 def warn_c2y_compat_generic_with_type_arg : Warning<
   "passing a type argument as the first operand to '_Generic' is incompatible "
   "with C standards before C2y">, InGroup<CPre2yCompat>, DefaultIgnore;
+def warn_c2y_empty_declaration_statement : Warning<
+  "empty declaration statement of '%select{if|switch}0' has no effect">,
+  InGroup<C2y>;
 def warn_c2y_compat_init_statement : Warning<
   "%select{if|switch}0 initialization statements are incompatible with "
   "C standards before C2y">, DefaultIgnore, InGroup<CPre2yCompat>;
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 03dd1683e2301..be680f1cdeb22 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1881,7 +1881,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
   }
 
   ParsedAttributes attrs(AttrFactory);
-  MaybeParseCXX11Attributes(attrs);
+  bool parsedAttrs = MaybeParseCXX11Attributes(attrs);
 
   const auto WarnOnInit = [this, &CK] {
     if (getLangOpts().CPlusPlus)
@@ -1896,6 +1896,36 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
           << (CK == Sema::ConditionKind::Switch);
   };
 
+  if (!getLangOpts().CPlusPlus) {
+    if (isDeclarationStatement() && !isCXXSimpleDeclaration(false)) {
+      WarnOnInit();
+      DeclGroupPtrTy DG;
+      SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
+      ParsedAttributes DeclSpecAttrs(AttrFactory);
+      // C2y replaces the init-statement in C++17 to be a declaration instead.
+      DG = ParseDeclaration(DeclaratorContext::SelectionInit, DeclEnd, attrs,
+                            DeclSpecAttrs);
+      *InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
+      return ParseCondition(nullptr, Loc, CK, MissingOK);
+    }
+
+    if (Tok.is(tok::semi) && parsedAttrs) {
+      // Parse if ([[...]]; true).
+      WarnOnInit();
+      if (attrs.empty()) {
+        SourceLocation SemiLoc = Tok.getLocation();
+        Diag(SemiLoc, diag::warn_c2y_empty_declaration_statement)
+            << (CK == Sema::ConditionKind::Switch)
+            << FixItHint::CreateRemoval(SemiLoc);
+        ConsumeToken();
+        InitStmt = nullptr;
+      } else
+        *InitStmt = Actions.ActOnAttributedStmt(
+            attrs, Actions.ActOnNullStmt(ConsumeToken()).get());
+      return ParseCondition(nullptr, Loc, CK, MissingOK);
+    }
+  }
+
   // Determine what kind of thing we have.
   switch (isCXXConditionDeclarationOrInitStatement(InitStmt, FRI)) {
   case ConditionOrInitStatement::Expression: {
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 45366e1f4e508..2e3a0e027995b 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1289,7 +1289,8 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
   if (!getLangOpts().CPlusPlus) {
     if (InitStmt != nullptr && InitStmt->isUsable()) {
       // Handle the 2 clauses of declaration: (clause1; clause2).
-      if (!isa<DeclStmt>(InitStmt->get()))
+      if (!isa<DeclStmt>(InitStmt->get()) &&
+          !isa<AttributedStmt>(InitStmt->get()))
         // C2y only permits declaration in the first clause of an if condition.
         Diag(InitStmt->get()->getBeginLoc(),
              diag::err_c2y_first_condition_clause_is_not_declaration)
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index 1a3f94724739e..3b56060c99edc 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -10,6 +10,8 @@ bool test_if() {
   if (struct A { int x;} a = {.x = 1}; a.x) {}
   if (int arr[] = {1,2,3}; arr[1]) {}
   if (auto x = 1; x) {}
+  if (static_assert(true); true) {}
+  if ([[clang::assume(1 > 0)]]; true) {}
   return false;
 }
 
@@ -28,6 +30,12 @@ int test_switch() {
   switch (struct A { int x;} a = {.x = 1}; a.x) {}
   switch (int arr[] = {1,2,3}; arr[1]) {}
   switch (auto x = 1; x) {}
+  switch (static_assert(true); 1) {
+  default:
+  }
+  switch ([[clang::assume(1 > 0)]]; 1) {
+  default:
+  }
 
   switch (int x = 1) {
   default:
@@ -47,6 +55,7 @@ bool negative_test_if() {
                                         expected-error {{expected expression}}
                                         expected-warning {{expression result unused}}*/
   if (int x) {} // expected-error {{variable declaration in condition must have an initializer}}
+  if ([[]]; true) {} // expected-warning {{empty declaration statement of 'if' has no effect}}
   return false;
 }
 
@@ -62,6 +71,11 @@ int negative_test_switch() {
   switch (true; ) {} /* expected-error {{first clause in condition must be a declaration}}
                         expected-error {{expected expression}}
                         expected-warning {{expression result unused}} */
+
+  switch ([[]]; 1) { // expected-warning {{empty declaration statement of 'switch' has no effect}}
+  default:
+  }
+
   switch (int x = 1; int y = x) { // expected-error {{expected expression}}
   default:
     return y;

>From 89c0b905d39a3d2f2440a20d42c2c913dde210d0 Mon Sep 17 00:00:00 2001
From: Muhammad Bassiouni <60100307+bassiounix at users.noreply.github.com>
Date: Fri, 22 May 2026 07:26:30 +0300
Subject: [PATCH 19/32] Update clang/lib/Parse/ParseExprCXX.cpp

Co-authored-by: Timm Baeder <tbaeder at redhat.com>
---
 clang/lib/Parse/ParseExprCXX.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index be680f1cdeb22..db2d1301f0cbe 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1881,7 +1881,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
   }
 
   ParsedAttributes attrs(AttrFactory);
-  bool parsedAttrs = MaybeParseCXX11Attributes(attrs);
+  bool ParsedAttrs = MaybeParseCXX11Attributes(attrs);
 
   const auto WarnOnInit = [this, &CK] {
     if (getLangOpts().CPlusPlus)

>From 17659bcc6c1fe1cc11a5c35f77ebd96a69272cbe Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Fri, 22 May 2026 07:58:00 +0300
Subject: [PATCH 20/32] fix ci

---
 clang/lib/Parse/ParseExprCXX.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index db2d1301f0cbe..6aaec052daf7e 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1909,7 +1909,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
       return ParseCondition(nullptr, Loc, CK, MissingOK);
     }
 
-    if (Tok.is(tok::semi) && parsedAttrs) {
+    if (Tok.is(tok::semi) && ParsedAttrs) {
       // Parse if ([[...]]; true).
       WarnOnInit();
       if (attrs.empty()) {

>From 79ba0d24c038795642ebe7dfbc58d7348171dbce Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Fri, 22 May 2026 14:50:11 +0300
Subject: [PATCH 21/32] update backport mode

---
 clang/docs/LanguageExtensions.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 33f07f86e1e23..b0cbeb54f2d55 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -2014,7 +2014,7 @@ Octal literals prefixed with ``0o`` or ``0O``                                  C
 ``_Generic`` with a type operand (N3260)                                       C2y           C89, C++
 ``++``/``--`` on ``_Complex`` value (N3259)                                    C2y           C89, C++
 ``__COUNTER__`` (N3457)                                                        C2y           C89, C++
-If declarations (N3356)                                                        C2y           C99
+If declarations (N3356)                                                        C2y           C89
 ============================================= ================================ ============= =============
 
 Builtin type aliases

>From 1bada04c074cab4e22e6ddeef5c7aea6a9bd8049 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Tue, 26 May 2026 18:09:01 +0300
Subject: [PATCH 22/32] address review comments

---
 .../clang/Basic/DiagnosticParseKinds.td        |  3 ---
 clang/lib/Parse/ParseExprCXX.cpp               | 18 +++++-------------
 clang/test/C/C2y/n3267.c                       |  4 ++--
 3 files changed, 7 insertions(+), 18 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 4d00d87def455..092fe1ed5344d 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -169,9 +169,6 @@ def ext_c2y_generic_with_type_arg : Extension<
 def warn_c2y_compat_generic_with_type_arg : Warning<
   "passing a type argument as the first operand to '_Generic' is incompatible "
   "with C standards before C2y">, InGroup<CPre2yCompat>, DefaultIgnore;
-def warn_c2y_empty_declaration_statement : Warning<
-  "empty declaration statement of '%select{if|switch}0' has no effect">,
-  InGroup<C2y>;
 def warn_c2y_compat_init_statement : Warning<
   "%select{if|switch}0 initialization statements are incompatible with "
   "C standards before C2y">, DefaultIgnore, InGroup<CPre2yCompat>;
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 6aaec052daf7e..f058e895eac0d 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1881,7 +1881,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
   }
 
   ParsedAttributes attrs(AttrFactory);
-  bool ParsedAttrs = MaybeParseCXX11Attributes(attrs);
+  MaybeParseCXX11Attributes(attrs);
 
   const auto WarnOnInit = [this, &CK] {
     if (getLangOpts().CPlusPlus)
@@ -1897,7 +1897,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
   };
 
   if (!getLangOpts().CPlusPlus) {
-    if (isDeclarationStatement() && !isCXXSimpleDeclaration(false)) {
+    if (Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
       WarnOnInit();
       DeclGroupPtrTy DG;
       SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
@@ -1909,19 +1909,11 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
       return ParseCondition(nullptr, Loc, CK, MissingOK);
     }
 
-    if (Tok.is(tok::semi) && ParsedAttrs) {
+    if (Tok.is(tok::semi)) {
       // Parse if ([[...]]; true).
       WarnOnInit();
-      if (attrs.empty()) {
-        SourceLocation SemiLoc = Tok.getLocation();
-        Diag(SemiLoc, diag::warn_c2y_empty_declaration_statement)
-            << (CK == Sema::ConditionKind::Switch)
-            << FixItHint::CreateRemoval(SemiLoc);
-        ConsumeToken();
-        InitStmt = nullptr;
-      } else
-        *InitStmt = Actions.ActOnAttributedStmt(
-            attrs, Actions.ActOnNullStmt(ConsumeToken()).get());
+      *InitStmt = Actions.ActOnAttributedStmt(
+          attrs, Actions.ActOnNullStmt(ConsumeToken()).get());
       return ParseCondition(nullptr, Loc, CK, MissingOK);
     }
   }
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index 3b56060c99edc..a08b0c3f8ce63 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -55,7 +55,7 @@ bool negative_test_if() {
                                         expected-error {{expected expression}}
                                         expected-warning {{expression result unused}}*/
   if (int x) {} // expected-error {{variable declaration in condition must have an initializer}}
-  if ([[]]; true) {} // expected-warning {{empty declaration statement of 'if' has no effect}}
+  if ([[]]; true) {} // expected-error {{first clause in condition must be a declaration}}
   return false;
 }
 
@@ -72,7 +72,7 @@ int negative_test_switch() {
                         expected-error {{expected expression}}
                         expected-warning {{expression result unused}} */
 
-  switch ([[]]; 1) { // expected-warning {{empty declaration statement of 'switch' has no effect}}
+  switch ([[]]; 1) { // expected-error {{first clause in condition must be a declaration}}
   default:
   }
 

>From 6da23bd5a15b40125298dd0ef9024fd864eb97ae Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Tue, 26 May 2026 21:33:10 +0300
Subject: [PATCH 23/32] handle edge cases with parsing

---
 clang/lib/Parse/ParseExprCXX.cpp | 10 ++++++++--
 clang/lib/Parse/ParseStmt.cpp    |  3 +--
 clang/test/C/C2y/n3267.c         |  9 +++++++--
 3 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index f058e895eac0d..a0f52ce2d79ed 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1881,7 +1881,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
   }
 
   ParsedAttributes attrs(AttrFactory);
-  MaybeParseCXX11Attributes(attrs);
+  bool ParsedAttrs = MaybeParseCXX11Attributes(attrs);
 
   const auto WarnOnInit = [this, &CK] {
     if (getLangOpts().CPlusPlus)
@@ -1909,7 +1909,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
       return ParseCondition(nullptr, Loc, CK, MissingOK);
     }
 
-    if (Tok.is(tok::semi)) {
+    if (Tok.is(tok::semi) && ParsedAttrs) {
       // Parse if ([[...]]; true).
       WarnOnInit();
       *InitStmt = Actions.ActOnAttributedStmt(
@@ -1927,6 +1927,12 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
     //   if (; true);
     if (InitStmt && Tok.is(tok::semi)) {
       WarnOnInit();
+      if (!getLangOpts().CPlusPlus && !InitStmt->get()) {
+        Diag(Tok.getLocation(),
+             diag::err_c2y_first_condition_clause_is_not_declaration);
+        Actions.ActOnNullStmt(ConsumeToken());
+        return ParseCondition(nullptr, Loc, CK, MissingOK);
+      }
       SourceLocation SemiLoc = Tok.getLocation();
       if (!Tok.hasLeadingEmptyMacro() && !SemiLoc.isMacroID()) {
         Diag(SemiLoc, diag::warn_empty_init_statement)
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 2e3a0e027995b..dd5e82eca59ba 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1289,8 +1289,7 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
   if (!getLangOpts().CPlusPlus) {
     if (InitStmt != nullptr && InitStmt->isUsable()) {
       // Handle the 2 clauses of declaration: (clause1; clause2).
-      if (!isa<DeclStmt>(InitStmt->get()) &&
-          !isa<AttributedStmt>(InitStmt->get()))
+      if (!isa<DeclStmt, AttributedStmt, NullStmt>(InitStmt->get()))
         // C2y only permits declaration in the first clause of an if condition.
         Diag(InitStmt->get()->getBeginLoc(),
              diag::err_c2y_first_condition_clause_is_not_declaration)
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index a08b0c3f8ce63..53154cc418449 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -12,6 +12,7 @@ bool test_if() {
   if (auto x = 1; x) {}
   if (static_assert(true); true) {}
   if ([[clang::assume(1 > 0)]]; true) {}
+  if ([[]]; true) {}
   return false;
 }
 
@@ -37,6 +38,10 @@ int test_switch() {
   default:
   }
 
+  switch ([[]]; 1) {
+  default:
+  }
+
   switch (int x = 1) {
   default:
     return y + x;
@@ -55,7 +60,7 @@ bool negative_test_if() {
                                         expected-error {{expected expression}}
                                         expected-warning {{expression result unused}}*/
   if (int x) {} // expected-error {{variable declaration in condition must have an initializer}}
-  if ([[]]; true) {} // expected-error {{first clause in condition must be a declaration}}
+  if (; true) {} // expected-error {{first clause in condition must be a declaration}}
   return false;
 }
 
@@ -72,7 +77,7 @@ int negative_test_switch() {
                         expected-error {{expected expression}}
                         expected-warning {{expression result unused}} */
 
-  switch ([[]]; 1) { // expected-error {{first clause in condition must be a declaration}}
+  switch (; 1) { // expected-error {{first clause in condition must be a declaration}}
   default:
   }
 

>From f4352c95b392cfb55bd0ee0817f494e589e31cd8 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Wed, 27 May 2026 02:45:14 +0300
Subject: [PATCH 24/32] reorder parsing logic

---
 clang/lib/Parse/ParseExprCXX.cpp | 20 +++++++++-----------
 clang/lib/Parse/ParseStmt.cpp    |  4 +++-
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index a0f52ce2d79ed..f74165e1b0bee 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1909,11 +1909,15 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
       return ParseCondition(nullptr, Loc, CK, MissingOK);
     }
 
-    if (Tok.is(tok::semi) && ParsedAttrs) {
-      // Parse if ([[...]]; true).
-      WarnOnInit();
-      *InitStmt = Actions.ActOnAttributedStmt(
-          attrs, Actions.ActOnNullStmt(ConsumeToken()).get());
+    // Handle 'if (; true)' and 'if ([[...]]; true)'.
+    if (Tok.is(tok::semi)) {
+      StmtResult Null = Actions.ActOnNullStmt(ConsumeToken());
+      if (ParsedAttrs) {
+        WarnOnInit();
+        *InitStmt = Actions.ActOnAttributedStmt(attrs, Null.get());
+      } else
+        Diag(Null.get()->getBeginLoc(),
+             diag::err_c2y_first_condition_clause_is_not_declaration);
       return ParseCondition(nullptr, Loc, CK, MissingOK);
     }
   }
@@ -1927,12 +1931,6 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
     //   if (; true);
     if (InitStmt && Tok.is(tok::semi)) {
       WarnOnInit();
-      if (!getLangOpts().CPlusPlus && !InitStmt->get()) {
-        Diag(Tok.getLocation(),
-             diag::err_c2y_first_condition_clause_is_not_declaration);
-        Actions.ActOnNullStmt(ConsumeToken());
-        return ParseCondition(nullptr, Loc, CK, MissingOK);
-      }
       SourceLocation SemiLoc = Tok.getLocation();
       if (!Tok.hasLeadingEmptyMacro() && !SemiLoc.isMacroID()) {
         Diag(SemiLoc, diag::warn_empty_init_statement)
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index dd5e82eca59ba..6ad8c230c5ba6 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1288,7 +1288,9 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
 
   if (!getLangOpts().CPlusPlus) {
     if (InitStmt != nullptr && InitStmt->isUsable()) {
-      // Handle the 2 clauses of declaration: (clause1; clause2).
+      // Handle the 2 clauses of declaration: (clause1; clause2). We need to
+      // allow NullStmt because that’s what we end up with if we have an empty
+      // attribute-specifier-sequence, which is valid: if ([[]]; true).
       if (!isa<DeclStmt, AttributedStmt, NullStmt>(InitStmt->get()))
         // C2y only permits declaration in the first clause of an if condition.
         Diag(InitStmt->get()->getBeginLoc(),

>From 40d0700036b99f894c2129bef64287b3b55d3277 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Thu, 28 May 2026 15:49:25 +0300
Subject: [PATCH 25/32] address review

---
 clang/docs/ReleaseNotes.rst                   |  5 +++-
 .../clang/Basic/DiagnosticParseKinds.td       | 10 ++++----
 clang/lib/Parse/ParseExprCXX.cpp              | 14 ++++++++---
 clang/lib/Parse/ParseStmt.cpp                 |  4 +--
 clang/test/C/C2y/n3267.c                      | 25 +++++++++++++++++++
 5 files changed, 46 insertions(+), 12 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 596166810a401..1e75389588b15 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -219,7 +219,10 @@ C Language Changes
 C2y Feature Support
 ^^^^^^^^^^^^^^^^^^^
 
-- Clang now supports C2y's new syntax for if and switch statements with initializer and condition variables, as specified in `N3356 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3356.htm>`_. For example:
+- Clang now supports C2y's new syntax for ``if`` and ``switch`` statements with
+  initializer and condition variables, as specified in
+  `N3356 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3356.htm>`_. For
+  example:
 
   .. code-block:: c
 
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 092fe1ed5344d..fecadf0451382 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -169,11 +169,11 @@ def ext_c2y_generic_with_type_arg : Extension<
 def warn_c2y_compat_generic_with_type_arg : Warning<
   "passing a type argument as the first operand to '_Generic' is incompatible "
   "with C standards before C2y">, InGroup<CPre2yCompat>, DefaultIgnore;
-def warn_c2y_compat_init_statement : Warning<
-  "%select{if|switch}0 initialization statements are incompatible with "
+def warn_c2y_compat_decl_statement : Warning<
+  "%select{if|switch}0 declaration statements are incompatible with "
   "C standards before C2y">, DefaultIgnore, InGroup<CPre2yCompat>;
-def ext_c2y_init_statement : ExtWarn<
-  "'%select{if|switch}0' initialization statements are a C2y extension">,
+def ext_c2y_decl_statement : ExtWarn<
+  "'%select{if|switch}0' declaration statements are a C2y extension">,
   InGroup<C2y>;
 
 def ext_c99_feature : Extension<
@@ -731,7 +731,7 @@ def ext_init_statement : ExtWarn<
   "'%select{if|switch}0' initialization statements are a C++17 extension">,
   InGroup<CXX17>;
 def warn_cxx14_compat_init_statement : Warning<
-  "%select{if|switch}0 initialization statements are incompatible with "
+  "'%select{if|switch}0' initialization statements are incompatible with "
   "C++ standards before C++17">, DefaultIgnore, InGroup<CXXPre17Compat>;
 def ext_for_range_init_stmt : ExtWarn<
   "range-based for loop initialization statements are a C++20 extension">,
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index f74165e1b0bee..44dc283c8d14e 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1891,13 +1891,14 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
           << (CK == Sema::ConditionKind::Switch);
     else
       Diag(Tok.getLocation(), getLangOpts().C2y
-                                  ? diag::warn_c2y_compat_init_statement
-                                  : diag::ext_c2y_init_statement)
+                                  ? diag::warn_c2y_compat_decl_statement
+                                  : diag::ext_c2y_decl_statement)
           << (CK == Sema::ConditionKind::Switch);
   };
 
   if (!getLangOpts().CPlusPlus) {
-    if (Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
+    if (isDeclarationStatement() && !isCXXSimpleDeclaration(false)) {
+      // Accept a C2y declaration, *only* if it's not a simple declaration.
       WarnOnInit();
       DeclGroupPtrTy DG;
       SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
@@ -1905,7 +1906,12 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
       // C2y replaces the init-statement in C++17 to be a declaration instead.
       DG = ParseDeclaration(DeclaratorContext::SelectionInit, DeclEnd, attrs,
                             DeclSpecAttrs);
-      *InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
+      StmtResult DeclStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
+      if (InitStmt == nullptr)
+        Diag(DeclStmt.get()->getBeginLoc(), diag::err_expected_expression)
+            << DeclStmt.get()->getSourceRange();
+      else
+        *InitStmt = DeclStmt;
       return ParseCondition(nullptr, Loc, CK, MissingOK);
     }
 
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 6ad8c230c5ba6..7681ad9dff209 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1304,8 +1304,8 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
     } else if (Cond.get().first != nullptr)
       // Handle: if (int decl = 0) {}.
       Diag(Cond.get().first->getBeginLoc(),
-           getLangOpts().C2y ? diag::warn_c2y_compat_init_statement
-                             : diag::ext_c2y_init_statement)
+           getLangOpts().C2y ? diag::warn_c2y_compat_decl_statement
+                             : diag::ext_c2y_decl_statement)
           << (CK == Sema::ConditionKind::Switch);
   }
 
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index 53154cc418449..d44c4b37c3d86 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -13,6 +13,11 @@ bool test_if() {
   if (static_assert(true); true) {}
   if ([[clang::assume(1 > 0)]]; true) {}
   if ([[]]; true) {}
+  if (auto x = 3) {}
+  if (auto x = 3; x == 3) {}
+  int y = 1;
+  if (auto x = &y) {}
+  if (auto x = &y; *x == 1) {}
   return false;
 }
 
@@ -42,6 +47,10 @@ int test_switch() {
   default:
   }
 
+  switch (auto x = 3) {default:}
+  switch (auto x = 3; x) {default:}
+  switch (auto x = &y; *x) {default:}
+
   switch (int x = 1) {
   default:
     return y + x;
@@ -55,12 +64,19 @@ bool negative_test_if() {
                     expected-error {{expected expression}}
                     expected-warning {{expression result unused}} */
   if (bool x = true; bool y = x) return y; // expected-error {{expected expression}}
+  if (bool x = true; bool y = x; y) return y; /*expected-error {{expected expression}}
+                                                expected-error {{expected ')'}}
+                                                expected-note {{to match this '('}}
+                                                expected-error {{use of undeclared identifier 'y'}} */
 
   if (true; bool y = true) return y; /* expected-error {{first clause in condition must be a declaration}}
                                         expected-error {{expected expression}}
                                         expected-warning {{expression result unused}}*/
   if (int x) {} // expected-error {{variable declaration in condition must have an initializer}}
   if (; true) {} // expected-error {{first clause in condition must be a declaration}}
+  if (static_assert(1); static_assert(1); static_assert(1); static_assert(1); 1) {} /* expected-error {{expected expression}}
+                                                                                       expected-error {{expected expression}}
+                                                                                       expected-error {{expected expression}} */
   return false;
 }
 
@@ -81,6 +97,15 @@ int negative_test_switch() {
   default:
   }
 
+  switch (static_assert(1); static_assert(1); static_assert(1); static_assert(1); 1) { /* expected-error {{expected expression}}
+                                                                                           expected-error {{expected expression}}
+                                                                                           expected-error {{expected expression}} */
+  default:
+  }
+
+  int y = 1;
+  switch (auto x = &y) {default:} // expected-error {{statement requires expression of integer type ('int *' invalid)}}
+
   switch (int x = 1; int y = x) { // expected-error {{expected expression}}
   default:
     return y;

>From b405ce2563062598b4086aff3cd4e7fd783ccad6 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Thu, 28 May 2026 17:42:08 +0300
Subject: [PATCH 26/32] fix ci

---
 clang/include/clang/Basic/DiagnosticParseKinds.td | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index fecadf0451382..a07d50e007e1a 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -170,7 +170,7 @@ def warn_c2y_compat_generic_with_type_arg : Warning<
   "passing a type argument as the first operand to '_Generic' is incompatible "
   "with C standards before C2y">, InGroup<CPre2yCompat>, DefaultIgnore;
 def warn_c2y_compat_decl_statement : Warning<
-  "%select{if|switch}0 declaration statements are incompatible with "
+  "'%select{if|switch}0' declaration statements are incompatible with "
   "C standards before C2y">, DefaultIgnore, InGroup<CPre2yCompat>;
 def ext_c2y_decl_statement : ExtWarn<
   "'%select{if|switch}0' declaration statements are a C2y extension">,
@@ -731,7 +731,7 @@ def ext_init_statement : ExtWarn<
   "'%select{if|switch}0' initialization statements are a C++17 extension">,
   InGroup<CXX17>;
 def warn_cxx14_compat_init_statement : Warning<
-  "'%select{if|switch}0' initialization statements are incompatible with "
+  "%select{if|switch}0 initialization statements are incompatible with "
   "C++ standards before C++17">, DefaultIgnore, InGroup<CXXPre17Compat>;
 def ext_for_range_init_stmt : ExtWarn<
   "range-based for loop initialization statements are a C++20 extension">,

>From c5fe7ee4e86aa6152897fa71dec50a42ce9fb4d1 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Thu, 28 May 2026 17:51:01 +0300
Subject: [PATCH 27/32] Change ext_c2y_decl_statement from ExtWarn to Extension

---
 clang/include/clang/Basic/DiagnosticParseKinds.td | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index a07d50e007e1a..fa306933d1b9f 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -172,7 +172,7 @@ def warn_c2y_compat_generic_with_type_arg : Warning<
 def warn_c2y_compat_decl_statement : Warning<
   "'%select{if|switch}0' declaration statements are incompatible with "
   "C standards before C2y">, DefaultIgnore, InGroup<CPre2yCompat>;
-def ext_c2y_decl_statement : ExtWarn<
+def ext_c2y_decl_statement : Extension<
   "'%select{if|switch}0' declaration statements are a C2y extension">,
   InGroup<C2y>;
 

>From 26228888a81c18482752eee590c77eed6137ca7d Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Thu, 28 May 2026 19:40:49 +0300
Subject: [PATCH 28/32] add support for GNU attributes

---
 clang/lib/Parse/ParseExprCXX.cpp |  9 +++++++++
 clang/test/C/C2y/n3267.c         | 16 ++++++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 44dc283c8d14e..2d00bcb47cc5e 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1882,6 +1882,8 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
 
   ParsedAttributes attrs(AttrFactory);
   bool ParsedAttrs = MaybeParseCXX11Attributes(attrs);
+  if (!getLangOpts().CPlusPlus)
+    ParsedAttrs = MaybeParseGNUAttributes(attrs) || ParsedAttrs;
 
   const auto WarnOnInit = [this, &CK] {
     if (getLangOpts().CPlusPlus)
@@ -2008,6 +2010,13 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
   DeclSpec DS(AttrFactory);
   ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_condition);
 
+  // The Declarator's DeclarationAttrs only accepts [[]] and keyword attributes;
+  // move any GNU attributes onto the DeclSpec instead.
+  if (!getLangOpts().CPlusPlus)
+    for (ParsedAttr &AL : attrs)
+      if (AL.isGNUAttribute())
+        DS.getAttributes().takeOneFrom(attrs, &AL);
+
   // declarator
   Declarator DeclaratorInfo(DS, attrs, DeclaratorContext::Condition);
   ParseDeclarator(DeclaratorInfo);
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index d44c4b37c3d86..05abd1062e273 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -13,6 +13,14 @@ bool test_if() {
   if (static_assert(true); true) {}
   if ([[clang::assume(1 > 0)]]; true) {}
   if ([[]]; true) {}
+  if (__attribute__((assume(1 > 0))); true) {}
+  if (__attribute__(()); true) {}
+  if (__attribute__((deprecated)) auto x = 3) {}
+  if (auto x __attribute__((deprecated)) = 3) {}
+  if (__attribute__((deprecated)) auto x = 3) {x += 1;} /* expected-warning {{'x' is deprecated}}
+                                                           expected-note {{'x' has been explicitly marked deprecated here}} */
+  if (int x __attribute__((deprecated)) = 3; x) {} /* expected-warning {{'x' is deprecated}}
+                                                      expected-note {{'x' has been explicitly marked deprecated here}} */
   if (auto x = 3) {}
   if (auto x = 3; x == 3) {}
   int y = 1;
@@ -32,6 +40,14 @@ int test_switch() {
 
   switch (int x [[maybe_unused]] = 1) {}
   switch ([[maybe_unused]] int x = 1) {}
+  switch (__attribute__((assume(1 > 0))); 1) {default:}
+  switch (__attribute__(()); 1) {default:}
+  switch (__attribute__((deprecated)) auto x = 3) {default:}
+  switch (auto x __attribute__((deprecated)) = 3) {default:}
+  switch (__attribute__((deprecated)) auto x = 3) {default: x += 1;} /* expected-warning {{'x' is deprecated}}
+                                                                        expected-note {{'x' has been explicitly marked deprecated here}} */
+  switch (int x __attribute__((deprecated)) = 3; x) {default:} /* expected-warning {{'x' is deprecated}}
+                                                                  expected-note {{'x' has been explicitly marked deprecated here}} */
 
   switch (struct A { int x;} a = {.x = 1}; a.x) {}
   switch (int arr[] = {1,2,3}; arr[1]) {}

>From 260c14872b6970e27d5ccebf9739434217223e70 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Fri, 29 May 2026 03:29:47 +0300
Subject: [PATCH 29/32] Implement support for __extension__ and enhance related
 tests

---
 clang/lib/Parse/ParseExprCXX.cpp | 14 ++++++++++
 clang/test/C/C2y/n3267.c         | 45 ++++++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+)

diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 2d00bcb47cc5e..e87eacd3018a8 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1880,6 +1880,20 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
     return Sema::ConditionError();
   }
 
+  // In C, the first clause of a condition may be a declaration used as an
+  // init-statement (C2y), and that declaration may be prefixed by one or more
+  // __extension__ markers. Consume them up front -- mirroring block-statement
+  // parsing -- so the disambiguation below sees the real start of the
+  // declaration. The markers also silence extension diagnostics for the rest of
+  // the condition, including the diagnostic for the init-statement extension
+  // itself.
+  std::optional<ExtensionRAIIObject> ExtensionGuard;
+  if (!getLangOpts().CPlusPlus && Tok.is(tok::kw___extension__)) {
+    ExtensionGuard.emplace(Diags);
+    while (TryConsumeToken(tok::kw___extension__))
+      ;
+  }
+
   ParsedAttributes attrs(AttrFactory);
   bool ParsedAttrs = MaybeParseCXX11Attributes(attrs);
   if (!getLangOpts().CPlusPlus)
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index 05abd1062e273..309ed424c668a 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -21,11 +21,36 @@ bool test_if() {
                                                            expected-note {{'x' has been explicitly marked deprecated here}} */
   if (int x __attribute__((deprecated)) = 3; x) {} /* expected-warning {{'x' is deprecated}}
                                                       expected-note {{'x' has been explicitly marked deprecated here}} */
+  if (__extension__ int x = 3; x) {}
+  if (__extension__ int x = 3) {}
+  if (__extension__ __extension__ auto x = 1; x) {}
+  if (__extension__ [[maybe_unused]] int x = 3; x > 0) {}
+  if (__extension__ static_assert(true); true) {}
+  if (__extension__ struct ExtTag { int x; } a = {.x = 1}; a.x) {}
+  // __extension__ does not silence ordinary (non-extension) diagnostics.
+  if (__extension__ int dx __attribute__((deprecated)) = 3; dx) {} /* expected-warning {{'dx' is deprecated}}
+                                                                      expected-note {{'dx' has been explicitly marked deprecated here}} */
+  if (__extension__ [[]]; true) {}
+  if (__extension__ enum ExtEnum { EA } e = EA; e) {}
+  // The second selection-header form is 'declaration expression', whose first
+  // clause is the full C 'declaration' grammar -- so these declaration
+  // varieties are all valid in it. (Only the third form, the simple-declaration
+  // that is itself the controlling expression, is limited to a single
+  // declarator with a mandatory initializer.)
+  if (int a = 1, b = 2; a + b) {}      // multiple declarators
+  if (static int s = 0; s) {}          // storage-class specifier
+  if (int fn(void); true) {}           // function declaration
+  if (typedef int LocalT; true) {}     // typedef declaration
+  if (_Static_assert(1, ""); true) {}  // _Static_assert declaration
   if (auto x = 3) {}
   if (auto x = 3; x == 3) {}
   int y = 1;
   if (auto x = &y) {}
   if (auto x = &y; *x == 1) {}
+  // The classic 'T * x' ambiguity resolves to a declaration when T names a type.
+  typedef int MyInt;
+  if (MyInt * p = &y; p) {}            // declaration of a pointer
+  if (MyInt (q) = 3; q) {}             // parenthesized declarator
   return false;
 }
 
@@ -48,6 +73,10 @@ int test_switch() {
                                                                         expected-note {{'x' has been explicitly marked deprecated here}} */
   switch (int x __attribute__((deprecated)) = 3; x) {default:} /* expected-warning {{'x' is deprecated}}
                                                                   expected-note {{'x' has been explicitly marked deprecated here}} */
+  switch (__extension__ int x = 1; x) {default:}
+  switch (__extension__ static_assert(true); 1) {default:}
+  switch (__extension__ enum SwEnum { SA, SB } e = SA; e) {default:}
+  switch (int a = 1, b = 2; a + b) {default:}
 
   switch (struct A { int x;} a = {.x = 1}; a.x) {}
   switch (int arr[] = {1,2,3}; arr[1]) {}
@@ -89,7 +118,19 @@ bool negative_test_if() {
                                         expected-error {{expected expression}}
                                         expected-warning {{expression result unused}}*/
   if (int x) {} // expected-error {{variable declaration in condition must have an initializer}}
+  // The third form (the declaration that is itself the controlling expression)
+  // is limited to a single declarator, so a declarator-list is rejected.
+  if (int x = 1, y = 2) {} /* expected-error {{expected ')'}}
+                              expected-note {{to match this '('}} */
   if (; true) {} // expected-error {{first clause in condition must be a declaration}}
+  if (__extension__; true) {} // expected-error {{first clause in condition must be a declaration}}
+  if (__extension__ true; true) {} /* expected-error {{first clause in condition must be a declaration}}
+                                      expected-warning {{expression result unused}} */
+  if (struct Incomplete s; true) {} /* expected-error {{variable has incomplete type 'struct Incomplete'}}
+                                       expected-note {{forward declaration of 'struct Incomplete'}} */
+  int a = 2;
+  if (a * 1; true) {} /* expected-error {{first clause in condition must be a declaration}}
+                         expected-warning {{expression result unused}} */
   if (static_assert(1); static_assert(1); static_assert(1); static_assert(1); 1) {} /* expected-error {{expected expression}}
                                                                                        expected-error {{expected expression}}
                                                                                        expected-error {{expected expression}} */
@@ -113,6 +154,10 @@ int negative_test_switch() {
   default:
   }
 
+  switch (__extension__; 1) { // expected-error {{first clause in condition must be a declaration}}
+  default:
+  }
+
   switch (static_assert(1); static_assert(1); static_assert(1); static_assert(1); 1) { /* expected-error {{expected expression}}
                                                                                            expected-error {{expected expression}}
                                                                                            expected-error {{expected expression}} */

>From bec1b049175def0f3e9a1370f566609aeca82fa9 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Fri, 29 May 2026 03:31:56 +0300
Subject: [PATCH 30/32] scope ExtensionGuard

---
 clang/lib/Parse/ParseExprCXX.cpp | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index e87eacd3018a8..93e8b31eb171d 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1880,15 +1880,15 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
     return Sema::ConditionError();
   }
 
-  // In C, the first clause of a condition may be a declaration used as an
-  // init-statement (C2y), and that declaration may be prefixed by one or more
-  // __extension__ markers. Consume them up front -- mirroring block-statement
-  // parsing -- so the disambiguation below sees the real start of the
-  // declaration. The markers also silence extension diagnostics for the rest of
-  // the condition, including the diagnostic for the init-statement extension
-  // itself.
-  std::optional<ExtensionRAIIObject> ExtensionGuard;
   if (!getLangOpts().CPlusPlus && Tok.is(tok::kw___extension__)) {
+    // In C, the first clause of a condition may be a declaration used as an
+    // init-statement (C2y), and that declaration may be prefixed by one or more
+    // __extension__ markers. Consume them up front -- mirroring block-statement
+    // parsing -- so the disambiguation below sees the real start of the
+    // declaration. The markers also silence extension diagnostics for the rest
+    // of the condition, including the diagnostic for the init-statement
+    // extension itself.
+    std::optional<ExtensionRAIIObject> ExtensionGuard;
     ExtensionGuard.emplace(Diags);
     while (TryConsumeToken(tok::kw___extension__))
       ;

>From e4bacc6c8a6ea551133c3353979bf2c368e80148 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Mon, 15 Jun 2026 18:38:49 +0300
Subject: [PATCH 31/32] address code review

---
 .../clang/Basic/DiagnosticParseKinds.td       |  1 +
 clang/lib/Parse/ParseExprCXX.cpp              | 15 ++++++++------
 clang/lib/Parse/ParseStmt.cpp                 |  6 ++++++
 clang/test/C/C2y/n3267.c                      | 20 +++++++++++++++++--
 4 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index fa306933d1b9f..b48c3e541c9a4 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -228,6 +228,7 @@ def err_c2y_labeled_break_continue : Error<
   "named %select{'break'|'continue'}0 is only supported in C2y">;
 def err_c2y_first_condition_clause_is_not_declaration : Error<
   "first clause in condition must be a declaration">;
+def err_c2y_multiple_declarations : Error<"multiple declarations are not allowed">;
 
 // Generic errors.
 def err_expected_expression : Error<"expected expression">;
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 93e8b31eb171d..ca4d6385f91ed 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1880,7 +1880,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
     return Sema::ConditionError();
   }
 
-  if (!getLangOpts().CPlusPlus && Tok.is(tok::kw___extension__)) {
+  if (Tok.is(tok::kw___extension__)) {
     // In C, the first clause of a condition may be a declaration used as an
     // init-statement (C2y), and that declaration may be prefixed by one or more
     // __extension__ markers. Consume them up front -- mirroring block-statement
@@ -1923,15 +1923,18 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
       DG = ParseDeclaration(DeclaratorContext::SelectionInit, DeclEnd, attrs,
                             DeclSpecAttrs);
       StmtResult DeclStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
-      if (InitStmt == nullptr)
-        Diag(DeclStmt.get()->getBeginLoc(), diag::err_expected_expression)
-            << DeclStmt.get()->getSourceRange();
-      else
+      if (InitStmt == nullptr) {
+        if (DeclStmt.isUsable())
+          Diag(DeclStmt.get()->getBeginLoc(), diag::err_expected_expression)
+              << DeclStmt.get()->getSourceRange();
+        else
+          Diag(DeclStart, diag::err_expected_expression);
+      } else
         *InitStmt = DeclStmt;
       return ParseCondition(nullptr, Loc, CK, MissingOK);
     }
 
-    // Handle 'if (; true)' and 'if ([[...]]; true)'.
+    // Handle '(; expr)', '(__attribute__((...)); expr)' and '([[...]]; expr)'.
     if (Tok.is(tok::semi)) {
       StmtResult Null = Actions.ActOnNullStmt(ConsumeToken());
       if (ParsedAttrs) {
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 7681ad9dff209..5860ab8a747f2 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1309,6 +1309,12 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
           << (CK == Sema::ConditionKind::Switch);
   }
 
+  if (Tok.is(tok::comma)) {
+    Diag(Tok, diag::err_c2y_multiple_declarations);
+    // Skip until the next token is ')' (stop when current token is r_paren)
+    while (Tok.isNot(tok::r_paren) && !Tok.is(tok::eof))
+      ConsumeAnyToken();
+  }
   // Either the condition is valid or the rparen is present.
   T.consumeClose();
   LParenLoc = T.getOpenLocation();
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index 309ed424c668a..e154534c64d31 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -51,6 +51,20 @@ bool test_if() {
   typedef int MyInt;
   if (MyInt * p = &y; p) {}            // declaration of a pointer
   if (MyInt (q) = 3; q) {}             // parenthesized declarator
+  if (int scope_check = 12; scope_check) {
+  } else {
+    scope_check = 100; // This should be fine
+  }
+  scope_check = 100; // expected-error {{use of undeclared identifier 'scope_check'}}
+
+  if (struct ScopeCheck { int x; } s = {}; s.x == 0) {
+  } else if (false) {
+    struct ScopeCheck works; // okay
+  } else {
+    struct ScopeCheck works; // okay
+  }
+  struct ScopeCheck nope; /* expected-error {{variable has incomplete type 'struct ScopeCheck'}}
+                             expected-note {{forward declaration of 'struct ScopeCheck'}}*/
   return false;
 }
 
@@ -120,8 +134,7 @@ bool negative_test_if() {
   if (int x) {} // expected-error {{variable declaration in condition must have an initializer}}
   // The third form (the declaration that is itself the controlling expression)
   // is limited to a single declarator, so a declarator-list is rejected.
-  if (int x = 1, y = 2) {} /* expected-error {{expected ')'}}
-                              expected-note {{to match this '('}} */
+  if (int x = 1, y = 2) {} // expected-error {{multiple declarations are not allowed}}
   if (; true) {} // expected-error {{first clause in condition must be a declaration}}
   if (__extension__; true) {} // expected-error {{first clause in condition must be a declaration}}
   if (__extension__ true; true) {} /* expected-error {{first clause in condition must be a declaration}}
@@ -171,4 +184,7 @@ int negative_test_switch() {
   default:
     return y;
   }
+
+  switch (int x, y) {default:} /* expected-error {{multiple declarations are not allowed}}
+                                  expected-error {{variable declaration in condition must have an initializer}} */
 }

>From 7a1d957a68c760e00f84b223565b199bd365bcbd Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Mon, 13 Jul 2026 21:58:50 +0300
Subject: [PATCH 32/32] remove custom path to GNU attributes with a FIXME
 comment waiting for GCC to finalize it

---
 clang/lib/Parse/ParseExprCXX.cpp | 16 ++++++----------
 clang/test/C/C2y/n3267.c         | 16 ++++++++++++----
 2 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 0b5c57d9b107a..dac7deccde4d5 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1894,10 +1894,12 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
       ;
   }
 
+  // FIXME(#198244): We need to support GNU attributes in C2y. We had a
+  // discussion about it and decided to wait and see what GCC would end up doing
+  // because as of now GCC does not support it either as an attribute
+  // declaration.
   ParsedAttributes attrs(AttrFactory);
   bool ParsedAttrs = MaybeParseCXX11Attributes(attrs);
-  if (!getLangOpts().CPlusPlus)
-    ParsedAttrs = MaybeParseGNUAttributes(attrs) || ParsedAttrs;
 
   const auto WarnOnInit = [this, &CK] {
     if (getLangOpts().CPlusPlus)
@@ -1934,7 +1936,8 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
       return ParseCondition(nullptr, Loc, CK, MissingOK);
     }
 
-    // Handle '(; expr)', '(__attribute__((...)); expr)' and '([[...]]; expr)'.
+    // Handle '(; expr)', '([[...]]; expr)' and '(__attribute__((...)); expr)'
+    // when GNU-style attributes are finalized.
     if (Tok.is(tok::semi)) {
       StmtResult Null = Actions.ActOnNullStmt(ConsumeToken());
       if (ParsedAttrs) {
@@ -2027,13 +2030,6 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
   DeclSpec DS(AttrFactory);
   ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_condition);
 
-  // The Declarator's DeclarationAttrs only accepts [[]] and keyword attributes;
-  // move any GNU attributes onto the DeclSpec instead.
-  if (!getLangOpts().CPlusPlus)
-    for (ParsedAttr &AL : attrs)
-      if (AL.isGNUAttribute())
-        DS.getAttributes().takeOneFrom(attrs, &AL);
-
   // declarator
   Declarator DeclaratorInfo(DS, attrs, DeclaratorContext::Condition);
   ParseDeclarator(DeclaratorInfo);
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index e154534c64d31..41682289ebc96 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -13,8 +13,12 @@ bool test_if() {
   if (static_assert(true); true) {}
   if ([[clang::assume(1 > 0)]]; true) {}
   if ([[]]; true) {}
-  if (__attribute__((assume(1 > 0))); true) {}
-  if (__attribute__(()); true) {}
+  // FIXME(#198244): We need to support GNU attributes in C2y. We had a
+  // discussion about it and decided to wait and see what GCC would end up doing
+  // because as of now GCC does not support it either as an attribute
+  // declaration.
+  // if (__attribute__((assume(1 > 0))); true) {}
+  // if (__attribute__(()); true) {}
   if (__attribute__((deprecated)) auto x = 3) {}
   if (auto x __attribute__((deprecated)) = 3) {}
   if (__attribute__((deprecated)) auto x = 3) {x += 1;} /* expected-warning {{'x' is deprecated}}
@@ -79,8 +83,12 @@ int test_switch() {
 
   switch (int x [[maybe_unused]] = 1) {}
   switch ([[maybe_unused]] int x = 1) {}
-  switch (__attribute__((assume(1 > 0))); 1) {default:}
-  switch (__attribute__(()); 1) {default:}
+  // FIXME(#198244): We need to support GNU attributes in C2y. We had a
+  // discussion about it and decided to wait and see what GCC would end up doing
+  // because as of now GCC does not support it either as an attribute
+  // declaration.
+  // switch (__attribute__((assume(1 > 0))); 1) {default:}
+  // switch (__attribute__(()); 1) {default:}
   switch (__attribute__((deprecated)) auto x = 3) {default:}
   switch (auto x __attribute__((deprecated)) = 3) {default:}
   switch (__attribute__((deprecated)) auto x = 3) {default: x += 1;} /* expected-warning {{'x' is deprecated}}



More information about the cfe-commits mailing list