[clang] [Clang][C2y] Add support for if declarations (N3356 paper) (PR #198244)
Muhammad Bassiouni via cfe-commits
cfe-commits at lists.llvm.org
Fri May 22 04:50:39 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/21] [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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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
More information about the cfe-commits
mailing list