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

>From 9dcbf7a0fc51bb9abec4b91651995d12dba11262 Mon Sep 17 00:00:00 2001
From: bassiounix <[email protected]>
Date: Tue, 20 Oct 2026 19:38:07 +0300
Subject: [PATCH 1/5] [Clang][C2y] Reject declarations in the condition of a
 while loop

N3356 only extends the selection statements (`if` and `switch`), but the
shared condition parsing accepted `while (int x = 12) {}` in C as a C2y
extension. `while` passes a null init-statement into
`ParseParenExprOrCondition`, so use that to diagnose a condition
declaration as "expected expression" (matching GCC) instead of emitting
the compatibility/extension warning.

Also only emit the extension warning for a non-simple declaration in the
first clause when the declaration is actually permitted there.

Fixes #233839

Co-Authored-By: Claude Fable 5.1 <[email protected]>
---
 clang/lib/Parse/ParseExprCXX.cpp      |  5 +++-
 clang/lib/Parse/ParseStmt.cpp         | 16 ++++++++---
 clang/test/C/C2y/n3267.c              | 41 +++++++++++++++++++++++++++
 clang/test/Parser/c2y-if-decl-while.c | 16 +++++++++++
 4 files changed, 73 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/Parser/c2y-if-decl-while.c

diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 3745ea53fc6d0d..9158c1c4ce0a78 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1939,7 +1939,10 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult 
*InitStmt,
   if (!getLangOpts().CPlusPlus) {
     if (isDeclarationStatement() && !isCXXSimpleDeclaration(false)) {
       // Accept a C2y declaration, *only* if it's not a simple declaration.
-      WarnOnInit();
+      // Only warn when the declaration is actually permitted here; otherwise
+      // we diagnose an error below.
+      if (InitStmt != nullptr)
+        WarnOnInit();
       DeclGroupPtrTy DG;
       SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
       ParsedAttributes DeclSpecAttrs(AttrFactory);
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index de82003f109328..b2fb2bd7f0fc7c 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1331,10 +1331,18 @@ bool Parser::ParseParenExprOrCondition(StmtResult 
*InitStmt,
         // 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)
-      // Handle: if (int decl = 0) {}.
-      DiagCompat(Cond.get().first->getBeginLoc(), diag_compat::decl_statement)
-          << (CK == Sema::ConditionKind::Switch);
+    } else if (Cond.get().first != nullptr) {
+      if (InitStmt == nullptr)
+        // C2y only permits a declaration in the condition of an 'if' or
+        // 'switch' statement; a 'while' statement still requires an
+        // expression. Handle: while (int decl = 0) {}.
+        Diag(Cond.get().first->getBeginLoc(), diag::err_expected_expression)
+            << Cond.get().first->getSourceRange();
+      else
+        // Handle: if (int decl = 0) {}.
+        DiagCompat(Cond.get().first->getBeginLoc(), 
diag_compat::decl_statement)
+            << (CK == Sema::ConditionKind::Switch);
+    }
   }
 
   if (Tok.is(tok::comma)) {
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index b517c5d33f42f6..4f08308b690c25 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -211,3 +211,44 @@ int negative_test_switch() {
   switch (int x, y) {default:} /* expected-error {{multiple declarations are 
not allowed}}
                                   expected-error {{variable declaration in 
condition must have an initializer}} */
 }
+
+void negative_test_while() {
+  // N3356 only extends the selection statements ('if' and 'switch'); the
+  // controlling clause of an iteration statement is still an expression.
+  int y = 1;
+  while (int x = 12) {} // expected-error {{expected expression}}
+  while ([[maybe_unused]] int x = 1) {} // expected-error {{expected 
expression}}
+  while (int x [[maybe_unused]] = 1) {} // expected-error {{expected 
expression}}
+  while (__extension__ int x = 1) {} // expected-error {{expected expression}}
+  while (auto x = 1) {} // expected-error {{expected expression}}
+  while (auto x = &y) {} // expected-error {{expected expression}}
+  while (static_assert(true); true) {} // expected-error {{expected 
expression}}
+  while (int a = 1, b = 2) {} /* expected-error {{expected expression}}
+                                 expected-error {{multiple declarations are 
not allowed}} */
+  while (int x) {} /* expected-error {{variable declaration in condition must 
have an initializer}}
+                      expected-error {{expected expression}} */
+  do {} while (int x = 1); // expected-error {{expected expression}}
+  for (int i = 0; int j = i;) {} // expected-error {{expected expression}}
+
+  // Declarations in 'if' and 'switch' nested in a loop (and vice versa) are
+  // still fine.
+  while (y) { if (int x = 1) {} }
+  if (int x = 1) { while (x) {} }
+}
+
+void negative_test_while_struct_init_clause() {
+  // Kept separate because error recovery skips the rest of the enclosing
+  // function.
+  while (struct S { int x; } s = {}; s.x) {} /* expected-error {{expected 
expression}}
+                                                expected-error {{statement 
requires expression of scalar type ('struct S' invalid)}}
+                                                expected-error {{use of 
undeclared identifier 's'}} */
+}
+
+void negative_test_while_init_clause() {
+  // Kept separate because recovery from the missing ')' skips the rest of the
+  // enclosing function.
+  while (int x = 12; x) {} /* expected-error {{expected expression}}
+                              expected-error {{expected ')'}}
+                              expected-note {{to match this '('}}
+                              expected-error {{use of undeclared identifier 
'x'}} */
+}
diff --git a/clang/test/Parser/c2y-if-decl-while.c 
b/clang/test/Parser/c2y-if-decl-while.c
new file mode 100644
index 00000000000000..cb9d2c9913335f
--- /dev/null
+++ b/clang/test/Parser/c2y-if-decl-while.c
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -std=c2y -pedantic -Wpre-c2y-compat -verify %s
+// RUN: %clang_cc1 -std=c23 -pedantic -verify=expected,ext %s
+// RUN: %clang_cc1 -std=c17 -pedantic -verify=expected,ext %s
+
+// N3356 ('if' declarations) only applies to selection statements. A 
declaration
+// in the controlling clause of a 'while' (or 'do'/'while') statement must be
+// rejected rather than accepted as a C2y extension.
+
+void test(void) {
+  if (int x = 1) {} // ext-warning {{'if' declaration statements are a C2y 
extension}}
+  switch (int x = 1) {} // ext-warning {{'switch' declaration statements are a 
C2y extension}}
+
+  while (int x = 12) {} // expected-error {{expected expression}}
+  while (_Static_assert(1, ""); 1) {} // expected-error {{expected expression}}
+  do {} while (int x = 1); // expected-error {{expected expression}}
+}

>From b0499a4cd8e2a2353817530854e578e8e82258f6 Mon Sep 17 00:00:00 2001
From: bassiounix <[email protected]>
Date: Wed, 21 Oct 2026 16:17:40 +0300
Subject: [PATCH 2/5] [Clang][C2y] Simplify condition-declaration diagnostics
 in C

Key the diagnostics on whether a declaration is permitted as the
condition (only as the sole clause of an 'if' or 'switch'), so the two
"expected expression" emissions collapse into one and the first-clause
check is no longer nested inside the branching. No functional change.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
---
 clang/lib/Parse/ParseStmt.cpp | 42 +++++++++++++++++------------------
 1 file changed, 20 insertions(+), 22 deletions(-)

diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index b2fb2bd7f0fc7c..05ad46aba1e8fb 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1317,30 +1317,28 @@ bool Parser::ParseParenExprOrCondition(StmtResult 
*InitStmt,
   }
 
   if (!getLangOpts().CPlusPlus) {
-    if (InitStmt != nullptr && InitStmt->isUsable()) {
-      // 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(),
-             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.
-        Diag(Cond.get().first->getBeginLoc(), diag::err_expected_expression)
-            << Cond.get().first->getSourceRange();
-    } else if (Cond.get().first != nullptr) {
-      if (InitStmt == nullptr)
-        // C2y only permits a declaration in the condition of an 'if' or
-        // 'switch' statement; a 'while' statement still requires an
-        // expression. Handle: while (int decl = 0) {}.
-        Diag(Cond.get().first->getBeginLoc(), diag::err_expected_expression)
-            << Cond.get().first->getSourceRange();
+    bool HasInitStmt = InitStmt != nullptr && InitStmt->isUsable();
+    // 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 (HasInitStmt &&
+        !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)
+          << InitStmt->get()->getSourceRange();
+
+    if (Decl *CondDecl = Cond.get().first) {
+      // C2y only permits a declaration as the sole clause of an 'if' or
+      // 'switch' condition. Diagnose it as an expected expression when there
+      // is no init-statement slot at all (while (int decl = 0) {}) or when the
+      // declaration is in the second clause (if (int x = 0; int y = 0) {}).
+      if (InitStmt == nullptr || HasInitStmt)
+        Diag(CondDecl->getBeginLoc(), diag::err_expected_expression)
+            << CondDecl->getSourceRange();
       else
         // Handle: if (int decl = 0) {}.
-        DiagCompat(Cond.get().first->getBeginLoc(), 
diag_compat::decl_statement)
+        DiagCompat(CondDecl->getBeginLoc(), diag_compat::decl_statement)
             << (CK == Sema::ConditionKind::Switch);
     }
   }

>From 1f1e8885568c801f505c7c2596bb9b3dfae7ef94 Mon Sep 17 00:00:00 2001
From: bassiounix <[email protected]>
Date: Wed, 21 Oct 2026 16:21:04 +0300
Subject: [PATCH 3/5] [Clang][C2y] Make the 'if' declaration extension warning
 on by default

Declarations in the condition of 'if' and 'switch' statements (N3356)
were previously only diagnosed in pre-C2y modes under -pedantic, since
the C compatibility warnings default to Extension. Promote the
diagnostic to an ExtWarn so users on older language modes are told
they are relying on a C2y extension; -Wno-c2y-extensions silences it.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
---
 clang/include/clang/Basic/DiagnosticParseKinds.td | 3 ++-
 clang/test/Parser/c2y-if-decl-while.c             | 4 ++++
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 7665688686b5d2..c7c70a930372d5 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -24,7 +24,8 @@ defm label_followed_by_declaration : C23Compat<
   "label followed by a declaration is">;
 
 // C2y compatibility with C23 and earlier.
-defm decl_statement : C2yCompat<"'%select{if|switch}0' declaration statements 
are">;
+defm decl_statement : C2yCompat<"'%select{if|switch}0' declaration statements 
are",
+                                /*ext_warn=*/true>;
 defm generic_with_type_arg : C2yCompat<
   "passing a type argument as the first operand to '_Generic' is">;
 
diff --git a/clang/test/Parser/c2y-if-decl-while.c 
b/clang/test/Parser/c2y-if-decl-while.c
index cb9d2c9913335f..3ecfb5ac55a9c3 100644
--- a/clang/test/Parser/c2y-if-decl-while.c
+++ b/clang/test/Parser/c2y-if-decl-while.c
@@ -1,6 +1,10 @@
 // RUN: %clang_cc1 -std=c2y -pedantic -Wpre-c2y-compat -verify %s
 // RUN: %clang_cc1 -std=c23 -pedantic -verify=expected,ext %s
 // RUN: %clang_cc1 -std=c17 -pedantic -verify=expected,ext %s
+// The extension warning is on by default, not only under -pedantic.
+// RUN: %clang_cc1 -std=c23 -verify=expected,ext %s
+// RUN: %clang_cc1 -std=c17 -verify=expected,ext %s
+// RUN: %clang_cc1 -std=c17 -Wno-c2y-extensions -verify %s
 
 // N3356 ('if' declarations) only applies to selection statements. A 
declaration
 // in the controlling clause of a 'while' (or 'do'/'while') statement must be

>From 4c740d281fd4a23e69a295c2f7e6c0c6ce29dccb Mon Sep 17 00:00:00 2001
From: bassiounix <[email protected]>
Date: Wed, 21 Oct 2026 13:50:03 +0300
Subject: [PATCH 4/5] [Clang][C2y] Fix pre-c2y-compat expectations in
 c2y-if-decl-while.c

The -std=c2y run line passes -Wpre-c2y-compat, so the compat form of the
'if'/'switch' declaration diagnostic fires. Give that run a 'compat'
prefix and expect the warnings.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
---
 clang/test/Parser/c2y-if-decl-while.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/clang/test/Parser/c2y-if-decl-while.c 
b/clang/test/Parser/c2y-if-decl-while.c
index 3ecfb5ac55a9c3..c169e71a6ebd83 100644
--- a/clang/test/Parser/c2y-if-decl-while.c
+++ b/clang/test/Parser/c2y-if-decl-while.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c2y -pedantic -Wpre-c2y-compat -verify %s
+// RUN: %clang_cc1 -std=c2y -pedantic -Wpre-c2y-compat -verify=expected,compat 
%s
 // RUN: %clang_cc1 -std=c23 -pedantic -verify=expected,ext %s
 // RUN: %clang_cc1 -std=c17 -pedantic -verify=expected,ext %s
 // The extension warning is on by default, not only under -pedantic.
@@ -11,8 +11,10 @@
 // rejected rather than accepted as a C2y extension.
 
 void test(void) {
-  if (int x = 1) {} // ext-warning {{'if' declaration statements are a C2y 
extension}}
-  switch (int x = 1) {} // ext-warning {{'switch' declaration statements are a 
C2y extension}}
+  if (int x = 1) {} // ext-warning {{'if' declaration statements are a C2y 
extension}} \
+                    // compat-warning {{'if' declaration statements are 
incompatible with C standards before C2y}}
+  switch (int x = 1) {} // ext-warning {{'switch' declaration statements are a 
C2y extension}} \
+                        // compat-warning {{'switch' declaration statements 
are incompatible with C standards before C2y}}
 
   while (int x = 12) {} // expected-error {{expected expression}}
   while (_Static_assert(1, ""); 1) {} // expected-error {{expected expression}}

>From 273365e97fb01331da33ccd0cd3de3383ac7f106 Mon Sep 17 00:00:00 2001
From: bassiounix <[email protected]>
Date: Wed, 21 Oct 2026 16:35:43 +0300
Subject: [PATCH 5/5] [Clang][C2y] Diagnose declarations in a 'while' condition
 directly

Instead of the generic "expected expression", a declaration in the
controlling clause of a 'while' statement in C is now rejected with
"ISO C does not allow a declaration in a 'while' statement".

Co-Authored-By: Claude Fable 5.1 <[email protected]>
---
 .../clang/Basic/DiagnosticParseKinds.td       |  2 ++
 clang/include/clang/Parse/Parser.h            |  7 +++-
 clang/lib/Parse/ParseExprCXX.cpp              | 36 ++++++++++++++-----
 clang/lib/Parse/ParseStmt.cpp                 | 10 +++---
 clang/test/C/C2y/n3267.c                      | 25 ++++++-------
 clang/test/Parser/c2y-if-decl-while.c         |  4 +--
 6 files changed, 55 insertions(+), 29 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index c7c70a930372d5..b73af2f88f5b7e 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -285,6 +285,8 @@ def err_c2y_labeled_break_continue : Error<
 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">;
+def err_c_decl_in_while_statement : Error<
+  "ISO C does not allow a declaration in a 'while' statement">;
 
 // 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 7f91c7d4670ff1..2565cee24bf1c7 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -5069,10 +5069,15 @@ class Parser : public CodeCompletionHandler {
   /// present will be parsed and stored here, and a null result will be
   /// returned.
   ///
+  /// \param AfterInitStmt Whether an init-statement has already been parsed
+  /// for this condition, so a null \p InitStmt means "the init-statement slot
+  /// is used up" rather than "no init-statement is permitted here".
+  ///
   /// \returns The parsed condition.
   Sema::ConditionResult ParseCondition(StmtResult *InitStmt, SourceLocation 
Loc,
                                        Sema::ConditionKind CK, bool MissingOK,
-                                       ForRangeInfo *FRI = nullptr);
+                                       ForRangeInfo *FRI = nullptr,
+                                       bool AfterInitStmt = false);
   DeclGroupPtrTy ParseAliasDeclarationInInitStatement(DeclaratorContext 
Context,
                                                       ParsedAttributes &Attrs);
 
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 9158c1c4ce0a78..19d485a89c5557 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1894,8 +1894,8 @@ 
Parser::ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
 Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
                                              SourceLocation Loc,
                                              Sema::ConditionKind CK,
-                                             bool MissingOK,
-                                             ForRangeInfo *FRI) {
+                                             bool MissingOK, ForRangeInfo *FRI,
+                                             bool AfterInitStmt) {
   ParenBraceBracketBalancer BalancerRAIIObj(*this);
   PreferredType.enterCondition(Actions, Tok.getLocation());
 
@@ -1951,14 +1951,20 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult 
*InitStmt,
                             DeclSpecAttrs);
       StmtResult DeclStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
       if (InitStmt == nullptr) {
+        // Either the init-statement slot is already used up (the second
+        // clause of an 'if' or 'switch' must be an expression), or there is
+        // no such slot at all (a 'while' statement).
+        unsigned DiagID = AfterInitStmt ? diag::err_expected_expression
+                                        : diag::err_c_decl_in_while_statement;
         if (DeclStmt.isUsable())
-          Diag(DeclStmt.get()->getBeginLoc(), diag::err_expected_expression)
+          Diag(DeclStmt.get()->getBeginLoc(), DiagID)
               << DeclStmt.get()->getSourceRange();
         else
-          Diag(DeclStart, diag::err_expected_expression);
+          Diag(DeclStart, DiagID);
       } else
         *InitStmt = DeclStmt;
-      return ParseCondition(nullptr, Loc, CK, MissingOK);
+      return ParseCondition(nullptr, Loc, CK, MissingOK, /*FRI=*/nullptr,
+                            /*AfterInitStmt=*/true);
     }
 
     // Handle '(; expr)', '([[...]]; expr)' and '(__attribute__((...)); expr)'
@@ -1971,7 +1977,8 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult 
*InitStmt,
       } else
         Diag(Null.get()->getBeginLoc(),
              diag::err_c2y_first_condition_clause_is_not_declaration);
-      return ParseCondition(nullptr, Loc, CK, MissingOK);
+      return ParseCondition(nullptr, Loc, CK, MissingOK, /*FRI=*/nullptr,
+                            /*AfterInitStmt=*/true);
     }
   }
 
@@ -1992,7 +1999,8 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult 
*InitStmt,
       }
       ConsumeToken();
       *InitStmt = Actions.ActOnNullStmt(SemiLoc);
-      return ParseCondition(nullptr, Loc, CK, MissingOK);
+      return ParseCondition(nullptr, Loc, CK, MissingOK, /*FRI=*/nullptr,
+                            /*AfterInitStmt=*/true);
     }
 
     EnterExpressionEvaluationContext Eval(
@@ -2010,7 +2018,8 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult 
*InitStmt,
       WarnOnInit();
       *InitStmt = Actions.ActOnExprStmt(Expr.get());
       ConsumeToken();
-      return ParseCondition(nullptr, Loc, CK, MissingOK);
+      return ParseCondition(nullptr, Loc, CK, MissingOK, /*FRI=*/nullptr,
+                            /*AfterInitStmt=*/true);
     }
 
     return Actions.ActOnCondition(getCurScope(), Loc, Expr.get(), CK,
@@ -2030,7 +2039,8 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult 
*InitStmt,
                                   attrs, DeclSpecAttrs, /*RequireSemi=*/true);
     }
     *InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
-    return ParseCondition(nullptr, Loc, CK, MissingOK);
+    return ParseCondition(nullptr, Loc, CK, MissingOK, /*FRI=*/nullptr,
+                            /*AfterInitStmt=*/true);
   }
 
   case ConditionOrInitStatement::ForRangeDecl: {
@@ -2059,6 +2069,14 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult 
*InitStmt,
   Declarator DeclaratorInfo(DS, attrs, DeclaratorContext::Condition);
   ParseDeclarator(DeclaratorInfo);
 
+  // In C, only 'if' and 'switch' permit a declaration as the condition; a
+  // 'while' statement has no init-statement slot. Diagnose this before Sema
+  // gets a chance to complain about the declaration itself. (A declaration in
+  // the second clause of an 'if' or 'switch' is diagnosed by the caller.)
+  if (!getLangOpts().CPlusPlus && InitStmt == nullptr && !AfterInitStmt)
+    Diag(DeclaratorInfo.getBeginLoc(), diag::err_c_decl_in_while_statement)
+        << DeclaratorInfo.getSourceRange();
+
   // simple-asm-expr[opt]
   if (Tok.is(tok::kw_asm)) {
     SourceLocation Loc;
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 05ad46aba1e8fb..4c3c4e5117813d 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1328,12 +1328,12 @@ bool Parser::ParseParenExprOrCondition(StmtResult 
*InitStmt,
            diag::err_c2y_first_condition_clause_is_not_declaration)
           << InitStmt->get()->getSourceRange();
 
-    if (Decl *CondDecl = Cond.get().first) {
+    if (Decl *CondDecl = Cond.get().first; CondDecl && InitStmt != nullptr) {
       // C2y only permits a declaration as the sole clause of an 'if' or
-      // 'switch' condition. Diagnose it as an expected expression when there
-      // is no init-statement slot at all (while (int decl = 0) {}) or when the
-      // declaration is in the second clause (if (int x = 0; int y = 0) {}).
-      if (InitStmt == nullptr || HasInitStmt)
+      // 'switch' condition; one in the second clause (if (int x = 0; int y =
+      // 0) {}) must be an expression. A declaration in a 'while' condition
+      // (InitStmt == nullptr) has already been diagnosed by ParseCondition.
+      if (HasInitStmt)
         Diag(CondDecl->getBeginLoc(), diag::err_expected_expression)
             << CondDecl->getSourceRange();
       else
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index 4f08308b690c25..2afe7bb14b9901 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -216,17 +216,17 @@ void negative_test_while() {
   // N3356 only extends the selection statements ('if' and 'switch'); the
   // controlling clause of an iteration statement is still an expression.
   int y = 1;
-  while (int x = 12) {} // expected-error {{expected expression}}
-  while ([[maybe_unused]] int x = 1) {} // expected-error {{expected 
expression}}
-  while (int x [[maybe_unused]] = 1) {} // expected-error {{expected 
expression}}
-  while (__extension__ int x = 1) {} // expected-error {{expected expression}}
-  while (auto x = 1) {} // expected-error {{expected expression}}
-  while (auto x = &y) {} // expected-error {{expected expression}}
-  while (static_assert(true); true) {} // expected-error {{expected 
expression}}
-  while (int a = 1, b = 2) {} /* expected-error {{expected expression}}
+  while (int x = 12) {} // expected-error {{ISO C does not allow a declaration 
in a 'while' statement}}
+  while ([[maybe_unused]] int x = 1) {} // expected-error {{ISO C does not 
allow a declaration in a 'while' statement}}
+  while (int x [[maybe_unused]] = 1) {} // expected-error {{ISO C does not 
allow a declaration in a 'while' statement}}
+  while (__extension__ int x = 1) {} // expected-error {{ISO C does not allow 
a declaration in a 'while' statement}}
+  while (auto x = 1) {} // expected-error {{ISO C does not allow a declaration 
in a 'while' statement}}
+  while (auto x = &y) {} // expected-error {{ISO C does not allow a 
declaration in a 'while' statement}}
+  while (static_assert(true); true) {} // expected-error {{ISO C does not 
allow a declaration in a 'while' statement}}
+  while (int a = 1, b = 2) {} /* expected-error {{ISO C does not allow a 
declaration in a 'while' statement}}
                                  expected-error {{multiple declarations are 
not allowed}} */
-  while (int x) {} /* expected-error {{variable declaration in condition must 
have an initializer}}
-                      expected-error {{expected expression}} */
+  while (int x) {} /* expected-error {{ISO C does not allow a declaration in a 
'while' statement}}
+                      expected-error {{variable declaration in condition must 
have an initializer}} */
   do {} while (int x = 1); // expected-error {{expected expression}}
   for (int i = 0; int j = i;) {} // expected-error {{expected expression}}
 
@@ -239,15 +239,16 @@ void negative_test_while() {
 void negative_test_while_struct_init_clause() {
   // Kept separate because error recovery skips the rest of the enclosing
   // function.
-  while (struct S { int x; } s = {}; s.x) {} /* expected-error {{expected 
expression}}
+  while (struct S { int x; } s = {}; s.x) {} /* expected-error {{ISO C does 
not allow a declaration in a 'while' statement}}
                                                 expected-error {{statement 
requires expression of scalar type ('struct S' invalid)}}
+                                                expected-error {{expected 
expression}}
                                                 expected-error {{use of 
undeclared identifier 's'}} */
 }
 
 void negative_test_while_init_clause() {
   // Kept separate because recovery from the missing ')' skips the rest of the
   // enclosing function.
-  while (int x = 12; x) {} /* expected-error {{expected expression}}
+  while (int x = 12; x) {} /* expected-error {{ISO C does not allow a 
declaration in a 'while' statement}}
                               expected-error {{expected ')'}}
                               expected-note {{to match this '('}}
                               expected-error {{use of undeclared identifier 
'x'}} */
diff --git a/clang/test/Parser/c2y-if-decl-while.c 
b/clang/test/Parser/c2y-if-decl-while.c
index c169e71a6ebd83..e21bc12ec745b6 100644
--- a/clang/test/Parser/c2y-if-decl-while.c
+++ b/clang/test/Parser/c2y-if-decl-while.c
@@ -16,7 +16,7 @@ void test(void) {
   switch (int x = 1) {} // ext-warning {{'switch' declaration statements are a 
C2y extension}} \
                         // compat-warning {{'switch' declaration statements 
are incompatible with C standards before C2y}}
 
-  while (int x = 12) {} // expected-error {{expected expression}}
-  while (_Static_assert(1, ""); 1) {} // expected-error {{expected expression}}
+  while (int x = 12) {} // expected-error {{ISO C does not allow a declaration 
in a 'while' statement}}
+  while (_Static_assert(1, ""); 1) {} // expected-error {{ISO C does not allow 
a declaration in a 'while' statement}}
   do {} while (int x = 1); // expected-error {{expected expression}}
 }

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

Reply via email to