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/4] [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/4] [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/4] [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/4] [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}} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
