[clang] [Clang][C2y] Reject declarations in the condition of a while loop (PR #233908)
Muhammad Bassiouni via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 21 06:58:57 PDT 2026
https://github.com/bassiounix updated https://github.com/llvm/llvm-project/pull/233908
>From 9dcbf7a0fc51bb9abec4b91651995d12dba11262 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
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 <noreply at anthropic.com>
---
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 <muhammad.m.bassiouni at gmail.com>
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 <noreply at anthropic.com>
---
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 <muhammad.m.bassiouni at gmail.com>
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 <noreply at anthropic.com>
---
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 <muhammad.m.bassiouni at gmail.com>
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 <noreply at anthropic.com>
---
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 3a2f5b668df043c3097a858d53cf51c4b3fd1d30 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
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 <noreply at anthropic.com>
---
.../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..2de79e1958beed 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}}
}
More information about the cfe-commits
mailing list