https://github.com/bassiounix created 
https://github.com/llvm/llvm-project/pull/198244

Add support for _if declarations_ in `C2y` mode by exploiting existing C++ code.

No API change to the names except a signature change to 
`Parser::ParseCXXCondition` which got a new extra param `isSecondCallForIfCond` 
to indicate what clause are we parsing in the condition. This new param is 
specific to the if condition in `C2y` and doesn't affect any other code path.

I noticed that the usage of `Parser::ParseCXXCondition` when not dealing with 
for loops it sets false-y values to `FRI` and `EnterForConditionScope`, hence 
this is what I depended on for indicating that I'm parsing declaration 
condition using `parsingIfOrSwitchCondition`.

The only new changes to the code is narrowing the code path to `C2y` mode and 
introduce new errors and warnings for some pitfalls with the syntax and what is 
expected from the standard.

It should be noted that the first clause in the standard paper can only be 
declaration. If I understand correctly this means we can't allow expression 
statement in the first clause of the condition, and that's the goal of the new 
warnings.

Sources:
- _if declaration_ standard paper 
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3356.htm

Resourses:
- An old draft of `C2y` 
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3356.htm

---

_TBR_: ~this footnote should be removed before merging!~

There might still be C++-specific code paths that I didn't guard against in the 
code, so please address them if you notice one. I'm still not familiar with 
Clang codebase :)

>From 4b23504a24dd2eed4681159da7d26681e2508443 Mon Sep 17 00:00:00 2001
From: bassiounix <[email protected]>
Date: Mon, 18 May 2026 10:05:00 +0300
Subject: [PATCH] [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'}}
+  }
+}

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

Reply via email to