[clang] [Clang][C2y] Add support for if declarations (N3356 paper) (PR #198244)
Muhammad Bassiouni via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 14 07:02:03 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/33] [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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] update backport mode
---
clang/docs/LanguageExtensions.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 33f07f86e1e23..b0cbeb54f2d55 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -2014,7 +2014,7 @@ Octal literals prefixed with ``0o`` or ``0O`` C
``_Generic`` with a type operand (N3260) C2y C89, C++
``++``/``--`` on ``_Complex`` value (N3259) C2y C89, C++
``__COUNTER__`` (N3457) C2y C89, C++
-If declarations (N3356) C2y C99
+If declarations (N3356) C2y C89
============================================= ================================ ============= =============
Builtin type aliases
>From 1bada04c074cab4e22e6ddeef5c7aea6a9bd8049 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Tue, 26 May 2026 18:09:01 +0300
Subject: [PATCH 22/33] address review comments
---
.../clang/Basic/DiagnosticParseKinds.td | 3 ---
clang/lib/Parse/ParseExprCXX.cpp | 18 +++++-------------
clang/test/C/C2y/n3267.c | 4 ++--
3 files changed, 7 insertions(+), 18 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 4d00d87def455..092fe1ed5344d 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -169,9 +169,6 @@ def ext_c2y_generic_with_type_arg : Extension<
def warn_c2y_compat_generic_with_type_arg : Warning<
"passing a type argument as the first operand to '_Generic' is incompatible "
"with C standards before C2y">, InGroup<CPre2yCompat>, DefaultIgnore;
-def warn_c2y_empty_declaration_statement : Warning<
- "empty declaration statement of '%select{if|switch}0' has no effect">,
- InGroup<C2y>;
def warn_c2y_compat_init_statement : Warning<
"%select{if|switch}0 initialization statements are incompatible with "
"C standards before C2y">, DefaultIgnore, InGroup<CPre2yCompat>;
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 6aaec052daf7e..f058e895eac0d 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1881,7 +1881,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
}
ParsedAttributes attrs(AttrFactory);
- bool ParsedAttrs = MaybeParseCXX11Attributes(attrs);
+ MaybeParseCXX11Attributes(attrs);
const auto WarnOnInit = [this, &CK] {
if (getLangOpts().CPlusPlus)
@@ -1897,7 +1897,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
};
if (!getLangOpts().CPlusPlus) {
- if (isDeclarationStatement() && !isCXXSimpleDeclaration(false)) {
+ if (Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
WarnOnInit();
DeclGroupPtrTy DG;
SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
@@ -1909,19 +1909,11 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
return ParseCondition(nullptr, Loc, CK, MissingOK);
}
- if (Tok.is(tok::semi) && ParsedAttrs) {
+ if (Tok.is(tok::semi)) {
// Parse if ([[...]]; true).
WarnOnInit();
- if (attrs.empty()) {
- SourceLocation SemiLoc = Tok.getLocation();
- Diag(SemiLoc, diag::warn_c2y_empty_declaration_statement)
- << (CK == Sema::ConditionKind::Switch)
- << FixItHint::CreateRemoval(SemiLoc);
- ConsumeToken();
- InitStmt = nullptr;
- } else
- *InitStmt = Actions.ActOnAttributedStmt(
- attrs, Actions.ActOnNullStmt(ConsumeToken()).get());
+ *InitStmt = Actions.ActOnAttributedStmt(
+ attrs, Actions.ActOnNullStmt(ConsumeToken()).get());
return ParseCondition(nullptr, Loc, CK, MissingOK);
}
}
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index 3b56060c99edc..a08b0c3f8ce63 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -55,7 +55,7 @@ bool negative_test_if() {
expected-error {{expected expression}}
expected-warning {{expression result unused}}*/
if (int x) {} // expected-error {{variable declaration in condition must have an initializer}}
- if ([[]]; true) {} // expected-warning {{empty declaration statement of 'if' has no effect}}
+ if ([[]]; true) {} // expected-error {{first clause in condition must be a declaration}}
return false;
}
@@ -72,7 +72,7 @@ int negative_test_switch() {
expected-error {{expected expression}}
expected-warning {{expression result unused}} */
- switch ([[]]; 1) { // expected-warning {{empty declaration statement of 'switch' has no effect}}
+ switch ([[]]; 1) { // expected-error {{first clause in condition must be a declaration}}
default:
}
>From 6da23bd5a15b40125298dd0ef9024fd864eb97ae Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Tue, 26 May 2026 21:33:10 +0300
Subject: [PATCH 23/33] handle edge cases with parsing
---
clang/lib/Parse/ParseExprCXX.cpp | 10 ++++++++--
clang/lib/Parse/ParseStmt.cpp | 3 +--
clang/test/C/C2y/n3267.c | 9 +++++++--
3 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index f058e895eac0d..a0f52ce2d79ed 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1881,7 +1881,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
}
ParsedAttributes attrs(AttrFactory);
- MaybeParseCXX11Attributes(attrs);
+ bool ParsedAttrs = MaybeParseCXX11Attributes(attrs);
const auto WarnOnInit = [this, &CK] {
if (getLangOpts().CPlusPlus)
@@ -1909,7 +1909,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
return ParseCondition(nullptr, Loc, CK, MissingOK);
}
- if (Tok.is(tok::semi)) {
+ if (Tok.is(tok::semi) && ParsedAttrs) {
// Parse if ([[...]]; true).
WarnOnInit();
*InitStmt = Actions.ActOnAttributedStmt(
@@ -1927,6 +1927,12 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
// if (; true);
if (InitStmt && Tok.is(tok::semi)) {
WarnOnInit();
+ if (!getLangOpts().CPlusPlus && !InitStmt->get()) {
+ Diag(Tok.getLocation(),
+ diag::err_c2y_first_condition_clause_is_not_declaration);
+ Actions.ActOnNullStmt(ConsumeToken());
+ return ParseCondition(nullptr, Loc, CK, MissingOK);
+ }
SourceLocation SemiLoc = Tok.getLocation();
if (!Tok.hasLeadingEmptyMacro() && !SemiLoc.isMacroID()) {
Diag(SemiLoc, diag::warn_empty_init_statement)
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 2e3a0e027995b..dd5e82eca59ba 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1289,8 +1289,7 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
if (!getLangOpts().CPlusPlus) {
if (InitStmt != nullptr && InitStmt->isUsable()) {
// Handle the 2 clauses of declaration: (clause1; clause2).
- if (!isa<DeclStmt>(InitStmt->get()) &&
- !isa<AttributedStmt>(InitStmt->get()))
+ if (!isa<DeclStmt, AttributedStmt, NullStmt>(InitStmt->get()))
// C2y only permits declaration in the first clause of an if condition.
Diag(InitStmt->get()->getBeginLoc(),
diag::err_c2y_first_condition_clause_is_not_declaration)
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index a08b0c3f8ce63..53154cc418449 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -12,6 +12,7 @@ bool test_if() {
if (auto x = 1; x) {}
if (static_assert(true); true) {}
if ([[clang::assume(1 > 0)]]; true) {}
+ if ([[]]; true) {}
return false;
}
@@ -37,6 +38,10 @@ int test_switch() {
default:
}
+ switch ([[]]; 1) {
+ default:
+ }
+
switch (int x = 1) {
default:
return y + x;
@@ -55,7 +60,7 @@ bool negative_test_if() {
expected-error {{expected expression}}
expected-warning {{expression result unused}}*/
if (int x) {} // expected-error {{variable declaration in condition must have an initializer}}
- if ([[]]; true) {} // expected-error {{first clause in condition must be a declaration}}
+ if (; true) {} // expected-error {{first clause in condition must be a declaration}}
return false;
}
@@ -72,7 +77,7 @@ int negative_test_switch() {
expected-error {{expected expression}}
expected-warning {{expression result unused}} */
- switch ([[]]; 1) { // expected-error {{first clause in condition must be a declaration}}
+ switch (; 1) { // expected-error {{first clause in condition must be a declaration}}
default:
}
>From f4352c95b392cfb55bd0ee0817f494e589e31cd8 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Wed, 27 May 2026 02:45:14 +0300
Subject: [PATCH 24/33] reorder parsing logic
---
clang/lib/Parse/ParseExprCXX.cpp | 20 +++++++++-----------
clang/lib/Parse/ParseStmt.cpp | 4 +++-
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index a0f52ce2d79ed..f74165e1b0bee 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1909,11 +1909,15 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
return ParseCondition(nullptr, Loc, CK, MissingOK);
}
- if (Tok.is(tok::semi) && ParsedAttrs) {
- // Parse if ([[...]]; true).
- WarnOnInit();
- *InitStmt = Actions.ActOnAttributedStmt(
- attrs, Actions.ActOnNullStmt(ConsumeToken()).get());
+ // Handle 'if (; true)' and 'if ([[...]]; true)'.
+ if (Tok.is(tok::semi)) {
+ StmtResult Null = Actions.ActOnNullStmt(ConsumeToken());
+ if (ParsedAttrs) {
+ WarnOnInit();
+ *InitStmt = Actions.ActOnAttributedStmt(attrs, Null.get());
+ } else
+ Diag(Null.get()->getBeginLoc(),
+ diag::err_c2y_first_condition_clause_is_not_declaration);
return ParseCondition(nullptr, Loc, CK, MissingOK);
}
}
@@ -1927,12 +1931,6 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
// if (; true);
if (InitStmt && Tok.is(tok::semi)) {
WarnOnInit();
- if (!getLangOpts().CPlusPlus && !InitStmt->get()) {
- Diag(Tok.getLocation(),
- diag::err_c2y_first_condition_clause_is_not_declaration);
- Actions.ActOnNullStmt(ConsumeToken());
- return ParseCondition(nullptr, Loc, CK, MissingOK);
- }
SourceLocation SemiLoc = Tok.getLocation();
if (!Tok.hasLeadingEmptyMacro() && !SemiLoc.isMacroID()) {
Diag(SemiLoc, diag::warn_empty_init_statement)
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index dd5e82eca59ba..6ad8c230c5ba6 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1288,7 +1288,9 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
if (!getLangOpts().CPlusPlus) {
if (InitStmt != nullptr && InitStmt->isUsable()) {
- // Handle the 2 clauses of declaration: (clause1; clause2).
+ // Handle the 2 clauses of declaration: (clause1; clause2). We need to
+ // allow NullStmt because that’s what we end up with if we have an empty
+ // attribute-specifier-sequence, which is valid: if ([[]]; true).
if (!isa<DeclStmt, AttributedStmt, NullStmt>(InitStmt->get()))
// C2y only permits declaration in the first clause of an if condition.
Diag(InitStmt->get()->getBeginLoc(),
>From 40d0700036b99f894c2129bef64287b3b55d3277 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Thu, 28 May 2026 15:49:25 +0300
Subject: [PATCH 25/33] address review
---
clang/docs/ReleaseNotes.rst | 5 +++-
.../clang/Basic/DiagnosticParseKinds.td | 10 ++++----
clang/lib/Parse/ParseExprCXX.cpp | 14 ++++++++---
clang/lib/Parse/ParseStmt.cpp | 4 +--
clang/test/C/C2y/n3267.c | 25 +++++++++++++++++++
5 files changed, 46 insertions(+), 12 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 596166810a401..1e75389588b15 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -219,7 +219,10 @@ C Language Changes
C2y Feature Support
^^^^^^^^^^^^^^^^^^^
-- Clang now supports C2y's new syntax for if and switch statements with initializer and condition variables, as specified in `N3356 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3356.htm>`_. For example:
+- Clang now supports C2y's new syntax for ``if`` and ``switch`` statements with
+ initializer and condition variables, as specified in
+ `N3356 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3356.htm>`_. For
+ example:
.. code-block:: c
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 092fe1ed5344d..fecadf0451382 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -169,11 +169,11 @@ def ext_c2y_generic_with_type_arg : Extension<
def warn_c2y_compat_generic_with_type_arg : Warning<
"passing a type argument as the first operand to '_Generic' is incompatible "
"with C standards before C2y">, InGroup<CPre2yCompat>, DefaultIgnore;
-def warn_c2y_compat_init_statement : Warning<
- "%select{if|switch}0 initialization statements are incompatible with "
+def warn_c2y_compat_decl_statement : Warning<
+ "%select{if|switch}0 declaration statements are incompatible with "
"C standards before C2y">, DefaultIgnore, InGroup<CPre2yCompat>;
-def ext_c2y_init_statement : ExtWarn<
- "'%select{if|switch}0' initialization statements are a C2y extension">,
+def ext_c2y_decl_statement : ExtWarn<
+ "'%select{if|switch}0' declaration statements are a C2y extension">,
InGroup<C2y>;
def ext_c99_feature : Extension<
@@ -731,7 +731,7 @@ def ext_init_statement : ExtWarn<
"'%select{if|switch}0' initialization statements are a C++17 extension">,
InGroup<CXX17>;
def warn_cxx14_compat_init_statement : Warning<
- "%select{if|switch}0 initialization statements are incompatible with "
+ "'%select{if|switch}0' initialization statements are incompatible with "
"C++ standards before C++17">, DefaultIgnore, InGroup<CXXPre17Compat>;
def ext_for_range_init_stmt : ExtWarn<
"range-based for loop initialization statements are a C++20 extension">,
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index f74165e1b0bee..44dc283c8d14e 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1891,13 +1891,14 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
<< (CK == Sema::ConditionKind::Switch);
else
Diag(Tok.getLocation(), getLangOpts().C2y
- ? diag::warn_c2y_compat_init_statement
- : diag::ext_c2y_init_statement)
+ ? diag::warn_c2y_compat_decl_statement
+ : diag::ext_c2y_decl_statement)
<< (CK == Sema::ConditionKind::Switch);
};
if (!getLangOpts().CPlusPlus) {
- if (Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
+ if (isDeclarationStatement() && !isCXXSimpleDeclaration(false)) {
+ // Accept a C2y declaration, *only* if it's not a simple declaration.
WarnOnInit();
DeclGroupPtrTy DG;
SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
@@ -1905,7 +1906,12 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
// C2y replaces the init-statement in C++17 to be a declaration instead.
DG = ParseDeclaration(DeclaratorContext::SelectionInit, DeclEnd, attrs,
DeclSpecAttrs);
- *InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
+ StmtResult DeclStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
+ if (InitStmt == nullptr)
+ Diag(DeclStmt.get()->getBeginLoc(), diag::err_expected_expression)
+ << DeclStmt.get()->getSourceRange();
+ else
+ *InitStmt = DeclStmt;
return ParseCondition(nullptr, Loc, CK, MissingOK);
}
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 6ad8c230c5ba6..7681ad9dff209 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1304,8 +1304,8 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
} else if (Cond.get().first != nullptr)
// Handle: if (int decl = 0) {}.
Diag(Cond.get().first->getBeginLoc(),
- getLangOpts().C2y ? diag::warn_c2y_compat_init_statement
- : diag::ext_c2y_init_statement)
+ getLangOpts().C2y ? diag::warn_c2y_compat_decl_statement
+ : diag::ext_c2y_decl_statement)
<< (CK == Sema::ConditionKind::Switch);
}
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index 53154cc418449..d44c4b37c3d86 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -13,6 +13,11 @@ bool test_if() {
if (static_assert(true); true) {}
if ([[clang::assume(1 > 0)]]; true) {}
if ([[]]; true) {}
+ if (auto x = 3) {}
+ if (auto x = 3; x == 3) {}
+ int y = 1;
+ if (auto x = &y) {}
+ if (auto x = &y; *x == 1) {}
return false;
}
@@ -42,6 +47,10 @@ int test_switch() {
default:
}
+ switch (auto x = 3) {default:}
+ switch (auto x = 3; x) {default:}
+ switch (auto x = &y; *x) {default:}
+
switch (int x = 1) {
default:
return y + x;
@@ -55,12 +64,19 @@ bool negative_test_if() {
expected-error {{expected expression}}
expected-warning {{expression result unused}} */
if (bool x = true; bool y = x) return y; // expected-error {{expected expression}}
+ if (bool x = true; bool y = x; y) return y; /*expected-error {{expected expression}}
+ expected-error {{expected ')'}}
+ expected-note {{to match this '('}}
+ expected-error {{use of undeclared identifier 'y'}} */
if (true; bool y = true) return y; /* expected-error {{first clause in condition must be a declaration}}
expected-error {{expected expression}}
expected-warning {{expression result unused}}*/
if (int x) {} // expected-error {{variable declaration in condition must have an initializer}}
if (; true) {} // expected-error {{first clause in condition must be a declaration}}
+ if (static_assert(1); static_assert(1); static_assert(1); static_assert(1); 1) {} /* expected-error {{expected expression}}
+ expected-error {{expected expression}}
+ expected-error {{expected expression}} */
return false;
}
@@ -81,6 +97,15 @@ int negative_test_switch() {
default:
}
+ switch (static_assert(1); static_assert(1); static_assert(1); static_assert(1); 1) { /* expected-error {{expected expression}}
+ expected-error {{expected expression}}
+ expected-error {{expected expression}} */
+ default:
+ }
+
+ int y = 1;
+ switch (auto x = &y) {default:} // expected-error {{statement requires expression of integer type ('int *' invalid)}}
+
switch (int x = 1; int y = x) { // expected-error {{expected expression}}
default:
return y;
>From b405ce2563062598b4086aff3cd4e7fd783ccad6 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Thu, 28 May 2026 17:42:08 +0300
Subject: [PATCH 26/33] fix ci
---
clang/include/clang/Basic/DiagnosticParseKinds.td | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index fecadf0451382..a07d50e007e1a 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -170,7 +170,7 @@ def warn_c2y_compat_generic_with_type_arg : Warning<
"passing a type argument as the first operand to '_Generic' is incompatible "
"with C standards before C2y">, InGroup<CPre2yCompat>, DefaultIgnore;
def warn_c2y_compat_decl_statement : Warning<
- "%select{if|switch}0 declaration statements are incompatible with "
+ "'%select{if|switch}0' declaration statements are incompatible with "
"C standards before C2y">, DefaultIgnore, InGroup<CPre2yCompat>;
def ext_c2y_decl_statement : ExtWarn<
"'%select{if|switch}0' declaration statements are a C2y extension">,
@@ -731,7 +731,7 @@ def ext_init_statement : ExtWarn<
"'%select{if|switch}0' initialization statements are a C++17 extension">,
InGroup<CXX17>;
def warn_cxx14_compat_init_statement : Warning<
- "'%select{if|switch}0' initialization statements are incompatible with "
+ "%select{if|switch}0 initialization statements are incompatible with "
"C++ standards before C++17">, DefaultIgnore, InGroup<CXXPre17Compat>;
def ext_for_range_init_stmt : ExtWarn<
"range-based for loop initialization statements are a C++20 extension">,
>From c5fe7ee4e86aa6152897fa71dec50a42ce9fb4d1 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Thu, 28 May 2026 17:51:01 +0300
Subject: [PATCH 27/33] Change ext_c2y_decl_statement from ExtWarn to Extension
---
clang/include/clang/Basic/DiagnosticParseKinds.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index a07d50e007e1a..fa306933d1b9f 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -172,7 +172,7 @@ def warn_c2y_compat_generic_with_type_arg : Warning<
def warn_c2y_compat_decl_statement : Warning<
"'%select{if|switch}0' declaration statements are incompatible with "
"C standards before C2y">, DefaultIgnore, InGroup<CPre2yCompat>;
-def ext_c2y_decl_statement : ExtWarn<
+def ext_c2y_decl_statement : Extension<
"'%select{if|switch}0' declaration statements are a C2y extension">,
InGroup<C2y>;
>From 26228888a81c18482752eee590c77eed6137ca7d Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Thu, 28 May 2026 19:40:49 +0300
Subject: [PATCH 28/33] add support for GNU attributes
---
clang/lib/Parse/ParseExprCXX.cpp | 9 +++++++++
clang/test/C/C2y/n3267.c | 16 ++++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 44dc283c8d14e..2d00bcb47cc5e 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1882,6 +1882,8 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
ParsedAttributes attrs(AttrFactory);
bool ParsedAttrs = MaybeParseCXX11Attributes(attrs);
+ if (!getLangOpts().CPlusPlus)
+ ParsedAttrs = MaybeParseGNUAttributes(attrs) || ParsedAttrs;
const auto WarnOnInit = [this, &CK] {
if (getLangOpts().CPlusPlus)
@@ -2008,6 +2010,13 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
DeclSpec DS(AttrFactory);
ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_condition);
+ // The Declarator's DeclarationAttrs only accepts [[]] and keyword attributes;
+ // move any GNU attributes onto the DeclSpec instead.
+ if (!getLangOpts().CPlusPlus)
+ for (ParsedAttr &AL : attrs)
+ if (AL.isGNUAttribute())
+ DS.getAttributes().takeOneFrom(attrs, &AL);
+
// declarator
Declarator DeclaratorInfo(DS, attrs, DeclaratorContext::Condition);
ParseDeclarator(DeclaratorInfo);
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index d44c4b37c3d86..05abd1062e273 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -13,6 +13,14 @@ bool test_if() {
if (static_assert(true); true) {}
if ([[clang::assume(1 > 0)]]; true) {}
if ([[]]; true) {}
+ if (__attribute__((assume(1 > 0))); true) {}
+ if (__attribute__(()); true) {}
+ if (__attribute__((deprecated)) auto x = 3) {}
+ if (auto x __attribute__((deprecated)) = 3) {}
+ if (__attribute__((deprecated)) auto x = 3) {x += 1;} /* expected-warning {{'x' is deprecated}}
+ expected-note {{'x' has been explicitly marked deprecated here}} */
+ if (int x __attribute__((deprecated)) = 3; x) {} /* expected-warning {{'x' is deprecated}}
+ expected-note {{'x' has been explicitly marked deprecated here}} */
if (auto x = 3) {}
if (auto x = 3; x == 3) {}
int y = 1;
@@ -32,6 +40,14 @@ int test_switch() {
switch (int x [[maybe_unused]] = 1) {}
switch ([[maybe_unused]] int x = 1) {}
+ switch (__attribute__((assume(1 > 0))); 1) {default:}
+ switch (__attribute__(()); 1) {default:}
+ switch (__attribute__((deprecated)) auto x = 3) {default:}
+ switch (auto x __attribute__((deprecated)) = 3) {default:}
+ switch (__attribute__((deprecated)) auto x = 3) {default: x += 1;} /* expected-warning {{'x' is deprecated}}
+ expected-note {{'x' has been explicitly marked deprecated here}} */
+ switch (int x __attribute__((deprecated)) = 3; x) {default:} /* expected-warning {{'x' is deprecated}}
+ expected-note {{'x' has been explicitly marked deprecated here}} */
switch (struct A { int x;} a = {.x = 1}; a.x) {}
switch (int arr[] = {1,2,3}; arr[1]) {}
>From 260c14872b6970e27d5ccebf9739434217223e70 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Fri, 29 May 2026 03:29:47 +0300
Subject: [PATCH 29/33] Implement support for __extension__ and enhance related
tests
---
clang/lib/Parse/ParseExprCXX.cpp | 14 ++++++++++
clang/test/C/C2y/n3267.c | 45 ++++++++++++++++++++++++++++++++
2 files changed, 59 insertions(+)
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 2d00bcb47cc5e..e87eacd3018a8 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1880,6 +1880,20 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
return Sema::ConditionError();
}
+ // In C, the first clause of a condition may be a declaration used as an
+ // init-statement (C2y), and that declaration may be prefixed by one or more
+ // __extension__ markers. Consume them up front -- mirroring block-statement
+ // parsing -- so the disambiguation below sees the real start of the
+ // declaration. The markers also silence extension diagnostics for the rest of
+ // the condition, including the diagnostic for the init-statement extension
+ // itself.
+ std::optional<ExtensionRAIIObject> ExtensionGuard;
+ if (!getLangOpts().CPlusPlus && Tok.is(tok::kw___extension__)) {
+ ExtensionGuard.emplace(Diags);
+ while (TryConsumeToken(tok::kw___extension__))
+ ;
+ }
+
ParsedAttributes attrs(AttrFactory);
bool ParsedAttrs = MaybeParseCXX11Attributes(attrs);
if (!getLangOpts().CPlusPlus)
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index 05abd1062e273..309ed424c668a 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -21,11 +21,36 @@ bool test_if() {
expected-note {{'x' has been explicitly marked deprecated here}} */
if (int x __attribute__((deprecated)) = 3; x) {} /* expected-warning {{'x' is deprecated}}
expected-note {{'x' has been explicitly marked deprecated here}} */
+ if (__extension__ int x = 3; x) {}
+ if (__extension__ int x = 3) {}
+ if (__extension__ __extension__ auto x = 1; x) {}
+ if (__extension__ [[maybe_unused]] int x = 3; x > 0) {}
+ if (__extension__ static_assert(true); true) {}
+ if (__extension__ struct ExtTag { int x; } a = {.x = 1}; a.x) {}
+ // __extension__ does not silence ordinary (non-extension) diagnostics.
+ if (__extension__ int dx __attribute__((deprecated)) = 3; dx) {} /* expected-warning {{'dx' is deprecated}}
+ expected-note {{'dx' has been explicitly marked deprecated here}} */
+ if (__extension__ [[]]; true) {}
+ if (__extension__ enum ExtEnum { EA } e = EA; e) {}
+ // The second selection-header form is 'declaration expression', whose first
+ // clause is the full C 'declaration' grammar -- so these declaration
+ // varieties are all valid in it. (Only the third form, the simple-declaration
+ // that is itself the controlling expression, is limited to a single
+ // declarator with a mandatory initializer.)
+ if (int a = 1, b = 2; a + b) {} // multiple declarators
+ if (static int s = 0; s) {} // storage-class specifier
+ if (int fn(void); true) {} // function declaration
+ if (typedef int LocalT; true) {} // typedef declaration
+ if (_Static_assert(1, ""); true) {} // _Static_assert declaration
if (auto x = 3) {}
if (auto x = 3; x == 3) {}
int y = 1;
if (auto x = &y) {}
if (auto x = &y; *x == 1) {}
+ // The classic 'T * x' ambiguity resolves to a declaration when T names a type.
+ typedef int MyInt;
+ if (MyInt * p = &y; p) {} // declaration of a pointer
+ if (MyInt (q) = 3; q) {} // parenthesized declarator
return false;
}
@@ -48,6 +73,10 @@ int test_switch() {
expected-note {{'x' has been explicitly marked deprecated here}} */
switch (int x __attribute__((deprecated)) = 3; x) {default:} /* expected-warning {{'x' is deprecated}}
expected-note {{'x' has been explicitly marked deprecated here}} */
+ switch (__extension__ int x = 1; x) {default:}
+ switch (__extension__ static_assert(true); 1) {default:}
+ switch (__extension__ enum SwEnum { SA, SB } e = SA; e) {default:}
+ switch (int a = 1, b = 2; a + b) {default:}
switch (struct A { int x;} a = {.x = 1}; a.x) {}
switch (int arr[] = {1,2,3}; arr[1]) {}
@@ -89,7 +118,19 @@ bool negative_test_if() {
expected-error {{expected expression}}
expected-warning {{expression result unused}}*/
if (int x) {} // expected-error {{variable declaration in condition must have an initializer}}
+ // The third form (the declaration that is itself the controlling expression)
+ // is limited to a single declarator, so a declarator-list is rejected.
+ if (int x = 1, y = 2) {} /* expected-error {{expected ')'}}
+ expected-note {{to match this '('}} */
if (; true) {} // expected-error {{first clause in condition must be a declaration}}
+ if (__extension__; true) {} // expected-error {{first clause in condition must be a declaration}}
+ if (__extension__ true; true) {} /* expected-error {{first clause in condition must be a declaration}}
+ expected-warning {{expression result unused}} */
+ if (struct Incomplete s; true) {} /* expected-error {{variable has incomplete type 'struct Incomplete'}}
+ expected-note {{forward declaration of 'struct Incomplete'}} */
+ int a = 2;
+ if (a * 1; true) {} /* expected-error {{first clause in condition must be a declaration}}
+ expected-warning {{expression result unused}} */
if (static_assert(1); static_assert(1); static_assert(1); static_assert(1); 1) {} /* expected-error {{expected expression}}
expected-error {{expected expression}}
expected-error {{expected expression}} */
@@ -113,6 +154,10 @@ int negative_test_switch() {
default:
}
+ switch (__extension__; 1) { // expected-error {{first clause in condition must be a declaration}}
+ default:
+ }
+
switch (static_assert(1); static_assert(1); static_assert(1); static_assert(1); 1) { /* expected-error {{expected expression}}
expected-error {{expected expression}}
expected-error {{expected expression}} */
>From bec1b049175def0f3e9a1370f566609aeca82fa9 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Fri, 29 May 2026 03:31:56 +0300
Subject: [PATCH 30/33] scope ExtensionGuard
---
clang/lib/Parse/ParseExprCXX.cpp | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index e87eacd3018a8..93e8b31eb171d 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1880,15 +1880,15 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
return Sema::ConditionError();
}
- // In C, the first clause of a condition may be a declaration used as an
- // init-statement (C2y), and that declaration may be prefixed by one or more
- // __extension__ markers. Consume them up front -- mirroring block-statement
- // parsing -- so the disambiguation below sees the real start of the
- // declaration. The markers also silence extension diagnostics for the rest of
- // the condition, including the diagnostic for the init-statement extension
- // itself.
- std::optional<ExtensionRAIIObject> ExtensionGuard;
if (!getLangOpts().CPlusPlus && Tok.is(tok::kw___extension__)) {
+ // In C, the first clause of a condition may be a declaration used as an
+ // init-statement (C2y), and that declaration may be prefixed by one or more
+ // __extension__ markers. Consume them up front -- mirroring block-statement
+ // parsing -- so the disambiguation below sees the real start of the
+ // declaration. The markers also silence extension diagnostics for the rest
+ // of the condition, including the diagnostic for the init-statement
+ // extension itself.
+ std::optional<ExtensionRAIIObject> ExtensionGuard;
ExtensionGuard.emplace(Diags);
while (TryConsumeToken(tok::kw___extension__))
;
>From e4bacc6c8a6ea551133c3353979bf2c368e80148 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Mon, 15 Jun 2026 18:38:49 +0300
Subject: [PATCH 31/33] address code review
---
.../clang/Basic/DiagnosticParseKinds.td | 1 +
clang/lib/Parse/ParseExprCXX.cpp | 15 ++++++++------
clang/lib/Parse/ParseStmt.cpp | 6 ++++++
clang/test/C/C2y/n3267.c | 20 +++++++++++++++++--
4 files changed, 34 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index fa306933d1b9f..b48c3e541c9a4 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -228,6 +228,7 @@ def err_c2y_labeled_break_continue : Error<
"named %select{'break'|'continue'}0 is only supported in C2y">;
def err_c2y_first_condition_clause_is_not_declaration : Error<
"first clause in condition must be a declaration">;
+def err_c2y_multiple_declarations : Error<"multiple declarations are not allowed">;
// Generic errors.
def err_expected_expression : Error<"expected expression">;
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 93e8b31eb171d..ca4d6385f91ed 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1880,7 +1880,7 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
return Sema::ConditionError();
}
- if (!getLangOpts().CPlusPlus && Tok.is(tok::kw___extension__)) {
+ if (Tok.is(tok::kw___extension__)) {
// In C, the first clause of a condition may be a declaration used as an
// init-statement (C2y), and that declaration may be prefixed by one or more
// __extension__ markers. Consume them up front -- mirroring block-statement
@@ -1923,15 +1923,18 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
DG = ParseDeclaration(DeclaratorContext::SelectionInit, DeclEnd, attrs,
DeclSpecAttrs);
StmtResult DeclStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
- if (InitStmt == nullptr)
- Diag(DeclStmt.get()->getBeginLoc(), diag::err_expected_expression)
- << DeclStmt.get()->getSourceRange();
- else
+ if (InitStmt == nullptr) {
+ if (DeclStmt.isUsable())
+ Diag(DeclStmt.get()->getBeginLoc(), diag::err_expected_expression)
+ << DeclStmt.get()->getSourceRange();
+ else
+ Diag(DeclStart, diag::err_expected_expression);
+ } else
*InitStmt = DeclStmt;
return ParseCondition(nullptr, Loc, CK, MissingOK);
}
- // Handle 'if (; true)' and 'if ([[...]]; true)'.
+ // Handle '(; expr)', '(__attribute__((...)); expr)' and '([[...]]; expr)'.
if (Tok.is(tok::semi)) {
StmtResult Null = Actions.ActOnNullStmt(ConsumeToken());
if (ParsedAttrs) {
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 7681ad9dff209..5860ab8a747f2 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1309,6 +1309,12 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
<< (CK == Sema::ConditionKind::Switch);
}
+ if (Tok.is(tok::comma)) {
+ Diag(Tok, diag::err_c2y_multiple_declarations);
+ // Skip until the next token is ')' (stop when current token is r_paren)
+ while (Tok.isNot(tok::r_paren) && !Tok.is(tok::eof))
+ ConsumeAnyToken();
+ }
// Either the condition is valid or the rparen is present.
T.consumeClose();
LParenLoc = T.getOpenLocation();
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index 309ed424c668a..e154534c64d31 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -51,6 +51,20 @@ bool test_if() {
typedef int MyInt;
if (MyInt * p = &y; p) {} // declaration of a pointer
if (MyInt (q) = 3; q) {} // parenthesized declarator
+ if (int scope_check = 12; scope_check) {
+ } else {
+ scope_check = 100; // This should be fine
+ }
+ scope_check = 100; // expected-error {{use of undeclared identifier 'scope_check'}}
+
+ if (struct ScopeCheck { int x; } s = {}; s.x == 0) {
+ } else if (false) {
+ struct ScopeCheck works; // okay
+ } else {
+ struct ScopeCheck works; // okay
+ }
+ struct ScopeCheck nope; /* expected-error {{variable has incomplete type 'struct ScopeCheck'}}
+ expected-note {{forward declaration of 'struct ScopeCheck'}}*/
return false;
}
@@ -120,8 +134,7 @@ bool negative_test_if() {
if (int x) {} // expected-error {{variable declaration in condition must have an initializer}}
// The third form (the declaration that is itself the controlling expression)
// is limited to a single declarator, so a declarator-list is rejected.
- if (int x = 1, y = 2) {} /* expected-error {{expected ')'}}
- expected-note {{to match this '('}} */
+ if (int x = 1, y = 2) {} // expected-error {{multiple declarations are not allowed}}
if (; true) {} // expected-error {{first clause in condition must be a declaration}}
if (__extension__; true) {} // expected-error {{first clause in condition must be a declaration}}
if (__extension__ true; true) {} /* expected-error {{first clause in condition must be a declaration}}
@@ -171,4 +184,7 @@ int negative_test_switch() {
default:
return y;
}
+
+ switch (int x, y) {default:} /* expected-error {{multiple declarations are not allowed}}
+ expected-error {{variable declaration in condition must have an initializer}} */
}
>From 7a1d957a68c760e00f84b223565b199bd365bcbd Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Mon, 13 Jul 2026 21:58:50 +0300
Subject: [PATCH 32/33] remove custom path to GNU attributes with a FIXME
comment waiting for GCC to finalize it
---
clang/lib/Parse/ParseExprCXX.cpp | 16 ++++++----------
clang/test/C/C2y/n3267.c | 16 ++++++++++++----
2 files changed, 18 insertions(+), 14 deletions(-)
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 0b5c57d9b107a..dac7deccde4d5 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1894,10 +1894,12 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
;
}
+ // FIXME(#198244): We need to support GNU attributes in C2y. We had a
+ // discussion about it and decided to wait and see what GCC would end up doing
+ // because as of now GCC does not support it either as an attribute
+ // declaration.
ParsedAttributes attrs(AttrFactory);
bool ParsedAttrs = MaybeParseCXX11Attributes(attrs);
- if (!getLangOpts().CPlusPlus)
- ParsedAttrs = MaybeParseGNUAttributes(attrs) || ParsedAttrs;
const auto WarnOnInit = [this, &CK] {
if (getLangOpts().CPlusPlus)
@@ -1934,7 +1936,8 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
return ParseCondition(nullptr, Loc, CK, MissingOK);
}
- // Handle '(; expr)', '(__attribute__((...)); expr)' and '([[...]]; expr)'.
+ // Handle '(; expr)', '([[...]]; expr)' and '(__attribute__((...)); expr)'
+ // when GNU-style attributes are finalized.
if (Tok.is(tok::semi)) {
StmtResult Null = Actions.ActOnNullStmt(ConsumeToken());
if (ParsedAttrs) {
@@ -2027,13 +2030,6 @@ Sema::ConditionResult Parser::ParseCondition(StmtResult *InitStmt,
DeclSpec DS(AttrFactory);
ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_condition);
- // The Declarator's DeclarationAttrs only accepts [[]] and keyword attributes;
- // move any GNU attributes onto the DeclSpec instead.
- if (!getLangOpts().CPlusPlus)
- for (ParsedAttr &AL : attrs)
- if (AL.isGNUAttribute())
- DS.getAttributes().takeOneFrom(attrs, &AL);
-
// declarator
Declarator DeclaratorInfo(DS, attrs, DeclaratorContext::Condition);
ParseDeclarator(DeclaratorInfo);
diff --git a/clang/test/C/C2y/n3267.c b/clang/test/C/C2y/n3267.c
index e154534c64d31..41682289ebc96 100644
--- a/clang/test/C/C2y/n3267.c
+++ b/clang/test/C/C2y/n3267.c
@@ -13,8 +13,12 @@ bool test_if() {
if (static_assert(true); true) {}
if ([[clang::assume(1 > 0)]]; true) {}
if ([[]]; true) {}
- if (__attribute__((assume(1 > 0))); true) {}
- if (__attribute__(()); true) {}
+ // FIXME(#198244): We need to support GNU attributes in C2y. We had a
+ // discussion about it and decided to wait and see what GCC would end up doing
+ // because as of now GCC does not support it either as an attribute
+ // declaration.
+ // if (__attribute__((assume(1 > 0))); true) {}
+ // if (__attribute__(()); true) {}
if (__attribute__((deprecated)) auto x = 3) {}
if (auto x __attribute__((deprecated)) = 3) {}
if (__attribute__((deprecated)) auto x = 3) {x += 1;} /* expected-warning {{'x' is deprecated}}
@@ -79,8 +83,12 @@ int test_switch() {
switch (int x [[maybe_unused]] = 1) {}
switch ([[maybe_unused]] int x = 1) {}
- switch (__attribute__((assume(1 > 0))); 1) {default:}
- switch (__attribute__(()); 1) {default:}
+ // FIXME(#198244): We need to support GNU attributes in C2y. We had a
+ // discussion about it and decided to wait and see what GCC would end up doing
+ // because as of now GCC does not support it either as an attribute
+ // declaration.
+ // switch (__attribute__((assume(1 > 0))); 1) {default:}
+ // switch (__attribute__(()); 1) {default:}
switch (__attribute__((deprecated)) auto x = 3) {default:}
switch (auto x __attribute__((deprecated)) = 3) {default:}
switch (__attribute__((deprecated)) auto x = 3) {default: x += 1;} /* expected-warning {{'x' is deprecated}}
>From f17aa035faadc6f83b1892070faa4dffbfab8540 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Tue, 14 Jul 2026 17:01:26 +0300
Subject: [PATCH 33/33] bump up clang version
---
clang/www/c_status.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index 49b80b431220d..f478a56857fa3 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="unreleased" align="center">Clang 23</td>
+ <td class="unreleased" align="center">Clang 24</td>
</tr>
<tr>
<td>Allowing stricter alignment for atomic types</td>
More information about the cfe-commits
mailing list