[clang] [Clang] Implement P0963R3 "Structured binding declaration as a condition" (PR #130228)

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Mon Mar 10 05:21:41 PDT 2025


https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/130228

>From 295b8173b6913d9014c5786eb4af0112384afa65 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 7 Mar 2025 11:38:11 +0800
Subject: [PATCH 1/9] [Clang] Implement P0963R3 "Structured binding declaration
 as a condition"

---
 clang/include/clang/AST/DeclCXX.h             | 23 +++++++++++-------
 .../clang/Basic/DiagnosticSemaKinds.td        |  8 +++++--
 clang/lib/AST/ASTImporter.cpp                 |  7 +++---
 clang/lib/AST/DeclCXX.cpp                     | 21 +++++++++-------
 clang/lib/AST/ExprConstant.cpp                | 24 +++++++++++++++----
 clang/lib/CodeGen/CGDecl.cpp                  | 13 ++++++----
 clang/lib/CodeGen/CGStmt.cpp                  | 17 ++++++++++---
 clang/lib/CodeGen/CodeGenFunction.cpp         |  7 +++++-
 clang/lib/CodeGen/CodeGenFunction.h           |  5 +++-
 clang/lib/Sema/SemaDecl.cpp                   |  6 ++---
 clang/lib/Sema/SemaDeclCXX.cpp                | 17 +++++++------
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  8 ++++---
 clang/lib/Serialization/ASTReaderDecl.cpp     |  9 ++++---
 clang/lib/Serialization/ASTWriterDecl.cpp     |  1 +
 clang/test/AST/ByteCode/if.cpp                |  2 +-
 clang/test/Parser/cxx1z-decomposition.cpp     | 12 +++++-----
 clang/test/Parser/decomposed-condition.cpp    | 24 +++++++++----------
 clang/test/SemaCXX/decomposed-condition.cpp   |  4 ++--
 18 files changed, 136 insertions(+), 72 deletions(-)

diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index dbd02ef7f8011..414a5aea32566 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4224,15 +4224,18 @@ class DecompositionDecl final
     : public VarDecl,
       private llvm::TrailingObjects<DecompositionDecl, BindingDecl *> {
   /// The number of BindingDecl*s following this object.
-  unsigned NumBindings;
+  unsigned NumBindings : 31;
+
+  LLVM_PREFERRED_TYPE(bool)
+  unsigned IsDecisionVariable : 1;
 
   DecompositionDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
                     SourceLocation LSquareLoc, QualType T,
                     TypeSourceInfo *TInfo, StorageClass SC,
-                    ArrayRef<BindingDecl *> Bindings)
+                    ArrayRef<BindingDecl *> Bindings, bool IsDecisionVariable)
       : VarDecl(Decomposition, C, DC, StartLoc, LSquareLoc, nullptr, T, TInfo,
                 SC),
-        NumBindings(Bindings.size()) {
+        NumBindings(Bindings.size()), IsDecisionVariable(IsDecisionVariable) {
     std::uninitialized_copy(Bindings.begin(), Bindings.end(),
                             getTrailingObjects<BindingDecl *>());
     for (auto *B : Bindings) {
@@ -4253,12 +4256,14 @@ class DecompositionDecl final
 
   static DecompositionDecl *Create(ASTContext &C, DeclContext *DC,
                                    SourceLocation StartLoc,
-                                   SourceLocation LSquareLoc,
-                                   QualType T, TypeSourceInfo *TInfo,
-                                   StorageClass S,
-                                   ArrayRef<BindingDecl *> Bindings);
+                                   SourceLocation LSquareLoc, QualType T,
+                                   TypeSourceInfo *TInfo, StorageClass S,
+                                   ArrayRef<BindingDecl *> Bindings,
+                                   bool IsDecisionVariable);
+
   static DecompositionDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
-                                               unsigned NumBindings);
+                                               unsigned NumBindings,
+                                               bool IsDecisionVariable);
 
   // Provide the range of bindings which may have a nested pack.
   llvm::ArrayRef<BindingDecl *> bindings() const {
@@ -4285,6 +4290,8 @@ class DecompositionDecl final
                                             std::move(Bindings));
   }
 
+  bool isDecisionVariable() const { return IsDecisionVariable; }
+
   void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
 
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 0b121c04cd3c0..fbc91f2eb8dd6 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -529,8 +529,12 @@ def warn_cxx14_compat_decomp_decl : Warning<
 def ext_decomp_decl : ExtWarn<
   "decomposition declarations are a C++17 extension">, InGroup<CXX17>;
 def ext_decomp_decl_cond : ExtWarn<
-  "ISO C++17 does not permit structured binding declaration in a condition">,
-  InGroup<DiagGroup<"binding-in-condition">>;
+  "structured binding declaration in a condition is a C++2c extenstion">,
+  InGroup<CXX26>;
+def warn_cxx26_decomp_decl_cond : Warning<
+  "structured binding declaration in a condition is incompatible with "
+  "C++ standards before C++2c">,
+  InGroup<CXXPre26Compat>, DefaultIgnore;
 def err_decomp_decl_spec : Error<
   "decomposition declaration cannot be declared "
   "%plural{1:'%1'|:with '%1' specifiers}0">;
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 82180486f3c7c..95ca085c5b746 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4614,9 +4614,10 @@ ExpectedDecl ASTNodeImporter::VisitVarDecl(VarDecl *D) {
             ImportArrayChecked(FromDecomp->bindings(), Bindings.begin()))
       return std::move(Err);
     DecompositionDecl *ToDecomp;
-    if (GetImportedOrCreateDecl(
-            ToDecomp, FromDecomp, Importer.getToContext(), DC, ToInnerLocStart,
-            Loc, ToType, ToTypeSourceInfo, D->getStorageClass(), Bindings))
+    if (GetImportedOrCreateDecl(ToDecomp, FromDecomp, Importer.getToContext(),
+                                DC, ToInnerLocStart, Loc, ToType,
+                                ToTypeSourceInfo, D->getStorageClass(),
+                                Bindings, FromDecomp->isDecisionVariable()))
       return ToDecomp;
     ToVar = ToDecomp;
   } else {
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 7eff776882629..fdad1150c8b1a 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3520,21 +3520,24 @@ DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC,
                                              SourceLocation LSquareLoc,
                                              QualType T, TypeSourceInfo *TInfo,
                                              StorageClass SC,
-                                             ArrayRef<BindingDecl *> Bindings) {
+                                             ArrayRef<BindingDecl *> Bindings,
+                                             bool IsDecisionVariable) {
   size_t Extra = additionalSizeToAlloc<BindingDecl *>(Bindings.size());
-  return new (C, DC, Extra)
-      DecompositionDecl(C, DC, StartLoc, LSquareLoc, T, TInfo, SC, Bindings);
+  return new (C, DC, Extra) DecompositionDecl(
+      C, DC, StartLoc, LSquareLoc, T, TInfo, SC, Bindings, IsDecisionVariable);
 }
 
-DecompositionDecl *DecompositionDecl::CreateDeserialized(ASTContext &C,
-                                                         GlobalDeclID ID,
-                                                         unsigned NumBindings) {
+DecompositionDecl *
+DecompositionDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+                                      unsigned NumBindings,
+                                      bool IsDecisionVariable) {
   size_t Extra = additionalSizeToAlloc<BindingDecl *>(NumBindings);
-  auto *Result = new (C, ID, Extra)
-      DecompositionDecl(C, nullptr, SourceLocation(), SourceLocation(),
-                        QualType(), nullptr, StorageClass(), {});
+  auto *Result = new (C, ID, Extra) DecompositionDecl(
+      C, nullptr, SourceLocation(), SourceLocation(), QualType(), nullptr,
+      StorageClass(), {}, IsDecisionVariable);
   // Set up and clean out the bindings array.
   Result->NumBindings = NumBindings;
+  Result->IsDecisionVariable = IsDecisionVariable;
   auto *Trail = Result->getTrailingObjects<BindingDecl *>();
   for (unsigned I = 0; I != NumBindings; ++I)
     new (Trail + I) BindingDecl*(nullptr);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 7244120d1be51..564a47a839336 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5218,16 +5218,28 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
   return true;
 }
 
+static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
+                                          const DecompositionDecl *DD);
+
 static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
   bool OK = true;
 
   if (const VarDecl *VD = dyn_cast<VarDecl>(D))
     OK &= EvaluateVarDecl(Info, VD);
 
-  if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D))
-    for (auto *BD : DD->flat_bindings())
-      if (auto *VD = BD->getHoldingVar())
-        OK &= EvaluateDecl(Info, VD);
+  if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D);
+      DD && !DD->isDecisionVariable())
+    OK &= EvaluateDecompositionDeclInit(Info, DD);
+
+  return OK;
+}
+
+static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
+                                          const DecompositionDecl *DD) {
+  bool OK = true;
+  for (auto *BD : DD->flat_bindings())
+    if (auto *VD = BD->getHoldingVar())
+      OK &= EvaluateDecl(Info, VD);
 
   return OK;
 }
@@ -5251,6 +5263,10 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
     return false;
   if (!EvaluateAsBooleanCondition(Cond, Result, Info))
     return false;
+  if (auto *DD = dyn_cast_if_present<DecompositionDecl>(CondDecl);
+      DD && DD->isDecisionVariable() &&
+      !EvaluateDecompositionDeclInit(Info, DD))
+    return false;
   return Scope.destroy();
 }
 
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 9cd5885aaae51..44d787a85f691 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -164,10 +164,9 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
     assert(VD.isLocalVarDecl() &&
            "Should not see file-scope variables inside a function!");
     EmitVarDecl(VD);
-    if (auto *DD = dyn_cast<DecompositionDecl>(&VD))
-      for (auto *B : DD->flat_bindings())
-        if (auto *HD = B->getHoldingVar())
-          EmitVarDecl(*HD);
+    if (auto *DD = dyn_cast<DecompositionDecl>(&VD);
+        DD && !DD->isDecisionVariable())
+      EmitDecompositionVarInit(*DD);
 
     return;
   }
@@ -2057,6 +2056,12 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
                         /*IsAutoInit=*/false);
 }
 
+void CodeGenFunction::EmitDecompositionVarInit(const DecompositionDecl &DD) {
+  for (auto *B : DD.flat_bindings())
+    if (auto *HD = B->getHoldingVar())
+      EmitVarDecl(*HD);
+}
+
 /// Emit an expression as an initializer for an object (variable, field, etc.)
 /// at the given location.  The expression is not necessarily the normal
 /// initializer for the object, and the address is not necessarily
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index abe799af32c6e..1d71f22dc2d47 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -913,6 +913,11 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
       if (CondConstant)
         incrementProfileCounter(&S);
       if (Executed) {
+        if (auto *DD = dyn_cast_if_present<DecompositionDecl>(
+                S.getConditionVariable())) {
+          assert(DD->isDecisionVariable());
+          EmitDecompositionVarInit(*DD);
+        }
         RunCleanupsScope ExecutedScope(*this);
         EmitStmt(Executed);
       }
@@ -952,10 +957,16 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
   // there is a 'return' within the body, but this is particularly beneficial
   // when one if-stmt is nested within another if-stmt so that all of the MC/DC
   // updates are kept linear and consistent.
-  if (!CGM.getCodeGenOpts().MCDCCoverage)
-    EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH);
-  else {
+  if (!CGM.getCodeGenOpts().MCDCCoverage) {
+    EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH,
+                         nullptr, S.getConditionVariable());
+  } else {
     llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
+    if (auto *DD =
+            dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable())) {
+      assert(DD->isDecisionVariable());
+      EmitDecompositionVarInit(*DD);
+    }
     Builder.CreateCondBr(BoolCondVal, ThenBlock, ElseBlock);
   }
 
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 08165e0b28406..c17a7d308bca8 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1846,7 +1846,8 @@ void CodeGenFunction::EmitBranchToCounterBlock(
 /// LHS and RHS nodes.
 void CodeGenFunction::EmitBranchOnBoolExpr(
     const Expr *Cond, llvm::BasicBlock *TrueBlock, llvm::BasicBlock *FalseBlock,
-    uint64_t TrueCount, Stmt::Likelihood LH, const Expr *ConditionalOp) {
+    uint64_t TrueCount, Stmt::Likelihood LH, const Expr *ConditionalOp,
+    const VarDecl *ConditionalDecl) {
   Cond = Cond->IgnoreParens();
 
   if (const BinaryOperator *CondBOp = dyn_cast<BinaryOperator>(Cond)) {
@@ -2047,6 +2048,10 @@ void CodeGenFunction::EmitBranchOnBoolExpr(
     CondV = EvaluateExprAsBool(Cond);
   }
 
+  if (auto *DD = dyn_cast_if_present<DecompositionDecl>(ConditionalDecl);
+      DD && DD->isDecisionVariable())
+    EmitDecompositionVarInit(*DD);
+
   // If not at the top of the logical operator nest, update MCDC temp with the
   // boolean result of the evaluated condition.
   if (!MCDCLogOpStack.empty()) {
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 018fc66b72a1e..3586594429da8 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3474,6 +3474,8 @@ class CodeGenFunction : public CodeGenTypeCache {
   void emitAutoVarTypeCleanup(const AutoVarEmission &emission,
                               QualType::DestructionKind dtorKind);
 
+  void EmitDecompositionVarInit(const DecompositionDecl &var);
+
   /// Emits the alloca and debug information for the size expressions for each
   /// dimension of an array. It registers the association of its (1-dimensional)
   /// QualTypes and size expression's debug node, so that CGDebugInfo can
@@ -5180,7 +5182,8 @@ class CodeGenFunction : public CodeGenTypeCache {
   void EmitBranchOnBoolExpr(const Expr *Cond, llvm::BasicBlock *TrueBlock,
                             llvm::BasicBlock *FalseBlock, uint64_t TrueCount,
                             Stmt::Likelihood LH = Stmt::LH_None,
-                            const Expr *ConditionalOp = nullptr);
+                            const Expr *ConditionalOp = nullptr,
+                            const VarDecl *ConditionalDecl = nullptr);
 
   /// Given an assignment `*LHS = RHS`, emit a test that checks if \p RHS is
   /// nonnull, if \p LHS is marked _Nonnull.
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 36de02d91e151..28215332f5ca3 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7768,9 +7768,9 @@ NamedDecl *Sema::ActOnVariableDeclarator(
       NewVD = cast<VarDecl>(Res.get());
       AddToScope = false;
     } else if (D.isDecompositionDeclarator()) {
-      NewVD = DecompositionDecl::Create(Context, DC, D.getBeginLoc(),
-                                        D.getIdentifierLoc(), R, TInfo, SC,
-                                        Bindings);
+      NewVD = DecompositionDecl::Create(
+          Context, DC, D.getBeginLoc(), D.getIdentifierLoc(), R, TInfo, SC,
+          Bindings, D.getContext() == DeclaratorContext::Condition);
     } else
       NewVD = VarDecl::Create(Context, DC, D.getBeginLoc(),
                               D.getIdentifierLoc(), II, R, TInfo, SC);
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index a3a028b9485d6..0e6017b75cfd8 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -740,13 +740,16 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
     return nullptr;
   }
 
-  Diag(Decomp.getLSquareLoc(),
-       !getLangOpts().CPlusPlus17
-           ? diag::ext_decomp_decl
-           : D.getContext() == DeclaratorContext::Condition
-                 ? diag::ext_decomp_decl_cond
-                 : diag::warn_cxx14_compat_decomp_decl)
-      << Decomp.getSourceRange();
+  unsigned DiagID;
+  if (!getLangOpts().CPlusPlus17)
+    DiagID = diag::ext_decomp_decl;
+  else if (D.getContext() == DeclaratorContext::Condition) {
+    DiagID = getLangOpts().CPlusPlus26 ? diag::warn_cxx26_decomp_decl_cond
+                                       : diag::ext_decomp_decl_cond;
+  } else
+    DiagID = diag::warn_cxx14_compat_decomp_decl;
+
+  Diag(Decomp.getLSquareLoc(), DiagID) << Decomp.getSourceRange();
 
   // The semantic context is always just the current context.
   DeclContext *const DC = CurContext;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index dd894df851488..c87ce2ae15ae9 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -14,6 +14,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTLambda.h"
 #include "clang/AST/ASTMutationListener.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DependentDiagnostic.h"
 #include "clang/AST/Expr.h"
@@ -1473,9 +1474,10 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D,
   // Build the instantiated declaration.
   VarDecl *Var;
   if (Bindings)
-    Var = DecompositionDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(),
-                                    D->getLocation(), DI->getType(), DI,
-                                    D->getStorageClass(), *Bindings);
+    Var = DecompositionDecl::Create(
+        SemaRef.Context, DC, D->getInnerLocStart(), D->getLocation(),
+        DI->getType(), DI, D->getStorageClass(), *Bindings,
+        cast<DecompositionDecl>(D)->isDecisionVariable());
   else
     Var = VarDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(),
                           D->getLocation(), D->getIdentifier(), DI->getType(),
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 2a580c44b94e5..113ed96ea2021 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -4115,9 +4115,12 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
   case DECL_PARM_VAR:
     D = ParmVarDecl::CreateDeserialized(Context, ID);
     break;
-  case DECL_DECOMPOSITION:
-    D = DecompositionDecl::CreateDeserialized(Context, ID, Record.readInt());
-    break;
+  case DECL_DECOMPOSITION: {
+    unsigned NumBindings = Record.readInt();
+    bool IsDecisionVariable = Record.readBool();
+    D = DecompositionDecl::CreateDeserialized(Context, ID, NumBindings,
+                                              IsDecisionVariable);
+  } break;
   case DECL_BINDING:
     D = BindingDecl::CreateDeserialized(Context, ID);
     break;
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index a1810003f5425..c11866319f2b0 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1327,6 +1327,7 @@ void ASTDeclWriter::VisitParmVarDecl(ParmVarDecl *D) {
 void ASTDeclWriter::VisitDecompositionDecl(DecompositionDecl *D) {
   // Record the number of bindings first to simplify deserialization.
   Record.push_back(D->bindings().size());
+  Record.push_back(D->isDecisionVariable());
 
   VisitVarDecl(D);
   for (auto *B : D->bindings())
diff --git a/clang/test/AST/ByteCode/if.cpp b/clang/test/AST/ByteCode/if.cpp
index c48b2b8d378c8..909e08a22a283 100644
--- a/clang/test/AST/ByteCode/if.cpp
+++ b/clang/test/AST/ByteCode/if.cpp
@@ -54,7 +54,7 @@ namespace InitDecl {
 constexpr char g(char const (&x)[2]) {
     return 'x';
   if (auto [a, b] = x) // both-error {{an array type is not allowed here}} \
-                       // both-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+                       // both-warning {{structured binding declaration in a condition is a C++2c extenstion}}
     ;
 }
 static_assert(g("x") == 'x');
diff --git a/clang/test/Parser/cxx1z-decomposition.cpp b/clang/test/Parser/cxx1z-decomposition.cpp
index acf3f99069185..96e25e8d9593b 100644
--- a/clang/test/Parser/cxx1z-decomposition.cpp
+++ b/clang/test/Parser/cxx1z-decomposition.cpp
@@ -37,12 +37,12 @@ namespace OtherDecl {
   void g() {
     // A condition is allowed as a Clang extension.
     // See commentary in test/Parser/decomposed-condition.cpp
-    for (; auto [a, b, c] = S(); ) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
-    if (auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
-    if (int n; auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
-    switch (auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{statement requires expression of integer type ('S' invalid)}}
-    switch (int n; auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{statement requires expression of integer type ('S' invalid)}}
-    while (auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
+    for (; auto [a, b, c] = S(); ) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
+    if (auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
+    if (int n; auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
+    switch (auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{statement requires expression of integer type ('S' invalid)}}
+    switch (int n; auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{statement requires expression of integer type ('S' invalid)}}
+    while (auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
 
     // An exception-declaration is not a simple-declaration.
     try {}
diff --git a/clang/test/Parser/decomposed-condition.cpp b/clang/test/Parser/decomposed-condition.cpp
index 4c635c4735db8..37a24f8f95093 100644
--- a/clang/test/Parser/decomposed-condition.cpp
+++ b/clang/test/Parser/decomposed-condition.cpp
@@ -33,33 +33,33 @@ Na g();
 
 namespace CondInIf {
 int h() {
-  if (auto [ok, d] = f()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+  if (auto [ok, d] = f()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
     ;
-  if (auto [ok, d] = g()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
+  if (auto [ok, d] = g()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
     ;
-  if (auto [value] = Get()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+  if (auto [value] = Get()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
     return value;
 }
 } // namespace CondInIf
 
 namespace CondInWhile {
 int h() {
-  while (auto [ok, d] = f()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+  while (auto [ok, d] = f()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
     ;
-  while (auto [ok, d] = g()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
+  while (auto [ok, d] = g()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
     ;
-  while (auto [value] = Get()) // expected-warning{{ISO C++17 does not permit structured binding declaration in a condition}}
+  while (auto [value] = Get()) // expected-warning{{structured binding declaration in a condition is a C++2c extenstion}}
     return value;
 }
 } // namespace CondInWhile
 
 namespace CondInFor {
 int h() {
-  for (; auto [ok, d] = f();) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+  for (; auto [ok, d] = f();) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
     ;
-  for (; auto [ok, d] = g();) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
+  for (; auto [ok, d] = g();) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
     ;
-  for (; auto [value] = Get();) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+  for (; auto [value] = Get();) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
     return value;
 }
 } // namespace CondInFor
@@ -74,11 +74,11 @@ struct IntegerLike {
 
 namespace CondInSwitch {
 int h(IntegerLike x) {
-  switch (auto [ok, d] = x) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+  switch (auto [ok, d] = x) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
     ;
-  switch (auto [ok, d] = g()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{statement requires expression of integer type ('Na' invalid)}}
+  switch (auto [ok, d] = g()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{statement requires expression of integer type ('Na' invalid)}}
     ;
-  switch (auto [value] = Get()) {// expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+  switch (auto [value] = Get()) {// expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
   // expected-warning at -1{{switch condition has boolean value}}
   case 1:
     return value;
diff --git a/clang/test/SemaCXX/decomposed-condition.cpp b/clang/test/SemaCXX/decomposed-condition.cpp
index e55bbee3134ca..32ab3a34f567b 100644
--- a/clang/test/SemaCXX/decomposed-condition.cpp
+++ b/clang/test/SemaCXX/decomposed-condition.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -std=c++1z -Wno-binding-in-condition -verify %s
-// RUN: %clang_cc1 -std=c++1z -Wno-binding-in-condition -verify %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -std=c++1z -Wno-c++26-extensions -verify %s
+// RUN: %clang_cc1 -std=c++1z -Wno-c++26-extensions -verify %s -fexperimental-new-constant-interpreter
 
 struct X {
   bool flag;

>From 5cd91f8adf02d633e7503b144a2d2b264d50dcaf Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 9 Mar 2025 23:29:02 +0800
Subject: [PATCH 2/9] Add a simple CG test, release note, comments for decision
 variable [dcl.struct.bind]

---
 clang/docs/ReleaseNotes.rst       |   2 +
 clang/include/clang/AST/DeclCXX.h |   4 ++
 clang/lib/CodeGen/CGStmt.cpp      |  21 ++++++
 clang/lib/Sema/SemaDecl.cpp       |   3 +-
 clang/test/CodeGen/p0963r3.cpp    | 108 ++++++++++++++++++++++++++++++
 clang/www/cxx_status.html         |   2 +-
 6 files changed, 138 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGen/p0963r3.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 37ea963bf337d..bea43ebcedb2f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -76,6 +76,8 @@ C++2c Feature Support
 
 - Implemented `P1061R10 Structured Bindings can introduce a Pack <https://wg21.link/P1061R10>`_.
 
+- Implemented `P0963R3 Structured binding declaration as a condition <https://wg21.link/P0963R3>`_.
+
 C++23 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 414a5aea32566..d82d9900945a1 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4290,6 +4290,10 @@ class DecompositionDecl final
                                             std::move(Bindings));
   }
 
+  /// The decision variable of a condition that is a structured binding
+  /// declaration is specified in [dcl.struct.bind]p4:
+  ///   If a structured binding declaration appears as a condition, the decision
+  ///   variable of the condition is e.
   bool isDecisionVariable() const { return IsDecisionVariable; }
 
   void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 1d71f22dc2d47..0a49eadbf33d1 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -1110,6 +1110,11 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S,
   // execution of the loop body.
   llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
 
+  if (auto *DD =
+          dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
+      DD && DD->isDecisionVariable())
+    EmitDecompositionVarInit(*DD);
+
   // while(1) is common, avoid extra exit blocks.  Be sure
   // to correctly handle break/continue though.
   llvm::ConstantInt *C = dyn_cast<llvm::ConstantInt>(BoolCondVal);
@@ -1343,6 +1348,12 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S,
     // C99 6.8.5p2/p4: The first substatement is executed if the expression
     // compares unequal to 0.  The condition must be a scalar type.
     llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
+
+    if (auto *DD =
+            dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
+        DD && DD->isDecisionVariable())
+      EmitDecompositionVarInit(*DD);
+
     llvm::MDNode *Weights =
         createProfileWeightsForLoop(S.getCond(), getProfileCount(S.getBody()));
     if (!Weights && CGM.getCodeGenOpts().OptimizationLevel)
@@ -2240,6 +2251,11 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
       if (S.getConditionVariable())
         EmitDecl(*S.getConditionVariable());
 
+      if (auto *DD =
+              dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
+          DD && DD->isDecisionVariable())
+        EmitDecompositionVarInit(*DD);
+
       // At this point, we are no longer "within" a switch instance, so
       // we can temporarily enforce this to ensure that any embedded case
       // statements are not emitted.
@@ -2271,6 +2287,11 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
     EmitDecl(*S.getConditionVariable());
   llvm::Value *CondV = EmitScalarExpr(S.getCond());
 
+  if (auto *DD =
+          dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
+      DD && DD->isDecisionVariable())
+    EmitDecompositionVarInit(*DD);
+
   // Create basic block to hold stuff that comes after switch
   // statement. We also need to create a default block now so that
   // explicit case ranges tests can have a place to jump to on
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 28215332f5ca3..675a928551fcb 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7770,7 +7770,8 @@ NamedDecl *Sema::ActOnVariableDeclarator(
     } else if (D.isDecompositionDeclarator()) {
       NewVD = DecompositionDecl::Create(
           Context, DC, D.getBeginLoc(), D.getIdentifierLoc(), R, TInfo, SC,
-          Bindings, D.getContext() == DeclaratorContext::Condition);
+          Bindings, /*IsDecisionVariable=*/D.getContext() ==
+                        DeclaratorContext::Condition);
     } else
       NewVD = VarDecl::Create(Context, DC, D.getBeginLoc(),
                               D.getIdentifierLoc(), II, R, TInfo, SC);
diff --git a/clang/test/CodeGen/p0963r3.cpp b/clang/test/CodeGen/p0963r3.cpp
new file mode 100644
index 0000000000000..64f97feb28ae1
--- /dev/null
+++ b/clang/test/CodeGen/p0963r3.cpp
@@ -0,0 +1,108 @@
+// RUN: %clang_cc1 -std=c++2c -verify -emit-llvm %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+namespace std {
+
+template <typename T> struct tuple_size;
+
+template <int, typename> struct tuple_element;
+
+} // namespace std
+
+namespace Case1 {
+
+struct S {
+  int a, b;
+  bool called_operator_bool = false;
+
+  operator bool() {
+    called_operator_bool = true;
+    return a != b;
+  }
+
+  template <int I> int get() {
+    if (!called_operator_bool)
+      return a + b;
+    return I == 0 ? a : b;
+  }
+};
+
+} // namespace Case1
+
+template <> struct std::tuple_size<Case1::S> {
+  static const int value = 2;
+};
+
+template <int I> struct std::tuple_element<I, Case1::S> {
+  using type = int;
+};
+
+namespace Case1 {
+
+void foo() {
+  if (S s(1, 2); auto [a, b] = s) {
+    __builtin_assume(a == 1);
+    __builtin_assume(b == 2);
+  }
+// CHECK: %[[call:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call]], label {{.*}}, label {{.*}}
+
+  if (auto [a, b] = S(1, 2)) {
+    __builtin_assume(a == 1);
+    __builtin_assume(b == 2);
+  }
+// CHECK: %[[call2:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call2]], label {{.*}}, label {{.*}}
+
+  if (S s(3, 4); auto& [a, b] = s) {
+    __builtin_assume(a == 3);
+    __builtin_assume(b == 4);
+  }
+// CHECK: %[[call3:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call3]], label {{.*}}, label {{.*}}
+
+  while (auto [i, j] = S(5, 6))
+    break;
+
+// CHECK: while.cond{{.*}}:
+// CHECK: %[[call4:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call4]], label {{.*}}, label {{.*}}
+
+  S s(7, 8);
+  while (auto& [i, j] = s)
+    break;
+
+// CHECK: while.cond{{.*}}:
+// CHECK: %[[call5:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call5]], label {{.*}}, label {{.*}}
+
+  for (int k = 0; auto [i, j] = S(24, 42); ++k)
+    break;
+
+// CHECK: for.cond{{.*}}:
+// CHECK: %[[call6:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call6]], label {{.*}}, label {{.*}}
+
+  for (S s(114, 514); auto& [i, j] = s; ++i)
+    break;
+
+// CHECK: for.cond{{.*}}:
+// CHECK: %[[call7:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call7]], label {{.*}}, label {{.*}}
+}
+
+} // namespace Case1
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 8570592101cc5..3f652c4291ddd 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -223,7 +223,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
  <tr>
   <td>Structured binding declaration as a condition</td>
   <td><a href="https://wg21.link/P0963R3">P0963R3</a></td>
-  <td class="none" align="center">No</td>
+  <td class="unreleased" align="center">Clang 21</td>
  </tr>
  <!--Poland, Fall 2024-->
  <tr>

>From ffe3922efc996e017b9425f22fe7953f352e12b6 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 9 Mar 2025 23:38:06 +0800
Subject: [PATCH 3/9] Test -Wpre-c++26-compat

---
 clang/test/SemaCXX/decomposed-condition.cpp | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/clang/test/SemaCXX/decomposed-condition.cpp b/clang/test/SemaCXX/decomposed-condition.cpp
index 32ab3a34f567b..14a23af3f5658 100644
--- a/clang/test/SemaCXX/decomposed-condition.cpp
+++ b/clang/test/SemaCXX/decomposed-condition.cpp
@@ -1,5 +1,7 @@
-// RUN: %clang_cc1 -std=c++1z -Wno-c++26-extensions -verify %s
-// RUN: %clang_cc1 -std=c++1z -Wno-c++26-extensions -verify %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -std=c++17 -Wno-c++26-extensions -verify %s
+// RUN: %clang_cc1 -std=c++17 -Wno-c++26-extensions -verify %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -std=c++2c -Wpre-c++26-compat -verify=cxx26,expected %s
+// RUN: %clang_cc1 -std=c++2c -Wpre-c++26-compat -verify=cxx26,expected %s -fexperimental-new-constant-interpreter
 
 struct X {
   bool flag;
@@ -14,7 +16,7 @@ struct X {
 
 namespace CondInIf {
 constexpr int f(X x) {
-  if (auto [ok, d] = x)
+  if (auto [ok, d] = x) // cxx26-warning {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
     return d + int(ok);
   else
     return d * int(ok);
@@ -26,12 +28,13 @@ static_assert(f({true, 2}) == 3);
 static_assert(f({false, 2}) == 0);
 
 constexpr char g(char const (&x)[2]) {
-  if (auto &[a, b] = x)
+  if (auto &[a, b] = x) // cxx26-warning {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
     return a;
   else
     return b;
 
-  if (auto [a, b] = x) // expected-error {{an array type is not allowed here}}
+  if (auto [a, b] = x) // expected-error {{an array type is not allowed here}} \
+                       // cxx26-warning {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
     ;
 }
 
@@ -41,6 +44,7 @@ static_assert(g("x") == 'x');
 namespace CondInSwitch {
 constexpr int f(int n) {
   switch (X s = {true, n}; auto [ok, d] = s) {
+    // cxx26-warning at -1 {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
     s = {};
   case 0:
     return int(ok);
@@ -65,6 +69,7 @@ namespace CondInWhile {
 constexpr int f(int n) {
   int m = 1;
   while (auto [ok, d] = X{n > 1, n}) {
+    // cxx26-warning at -1 {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
     m *= d;
     --n;
   }
@@ -81,6 +86,7 @@ namespace CondInFor {
 constexpr int f(int n) {
   int a = 1, b = 1;
   for (X x = {true, n}; auto &[ok, d] = x; --d) {
+    // cxx26-warning at -1 {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
     if (d < 2)
       ok = false;
     else {

>From 2ae72b4710e036a3b19ac06b0feee96d13a68092 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Mar 2025 13:56:32 +0800
Subject: [PATCH 4/9] Add more tests; handle switch (..) case .. in constant
 evaluator

---
 clang/lib/AST/ExprConstant.cpp |   6 ++
 clang/test/CodeGen/p0963r3.cpp | 145 ++++++++++++++++++++++++++++-----
 2 files changed, 130 insertions(+), 21 deletions(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 564a47a839336..898f8a2fce769 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5351,6 +5351,12 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
     if (!EvaluateInteger(SS->getCond(), Value, Info))
       return ESR_Failed;
 
+    if (auto *DD =
+            dyn_cast_if_present<DecompositionDecl>(SS->getConditionVariable());
+        DD && DD->isDecisionVariable() &&
+        !EvaluateDecompositionDeclInit(Info, DD))
+      return ESR_Failed;
+
     if (!CondScope.destroy())
       return ESR_Failed;
   }
diff --git a/clang/test/CodeGen/p0963r3.cpp b/clang/test/CodeGen/p0963r3.cpp
index 64f97feb28ae1..b356e78adfa83 100644
--- a/clang/test/CodeGen/p0963r3.cpp
+++ b/clang/test/CodeGen/p0963r3.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++2c -verify -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -std=c++2c -verify -emit-llvm -triple=x86_64-pc-linux-gnu %s -o - | FileCheck %s
 // expected-no-diagnostics
 
 namespace std {
@@ -13,16 +13,26 @@ namespace Case1 {
 
 struct S {
   int a, b;
-  bool called_operator_bool = false;
+  bool flag = false;
 
-  operator bool() {
-    called_operator_bool = true;
+  constexpr explicit operator bool() {
+    flag = true;
     return a != b;
   }
 
-  template <int I> int get() {
-    if (!called_operator_bool)
-      return a + b;
+  constexpr operator int() {
+    flag = true;
+    return a * b;
+  }
+
+  constexpr bool operator==(S rhs) const {
+    return a == rhs.a && b == rhs.b;
+  }
+
+  template <int I>
+  constexpr int& get() {
+    if (!flag)
+      return a = a + b;
     return I == 0 ? a : b;
   }
 };
@@ -45,8 +55,8 @@ void foo() {
     __builtin_assume(b == 2);
   }
 // CHECK: %[[call:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
 // CHECK: br i1 %[[call]], label {{.*}}, label {{.*}}
 
   if (auto [a, b] = S(1, 2)) {
@@ -54,8 +64,8 @@ void foo() {
     __builtin_assume(b == 2);
   }
 // CHECK: %[[call2:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
 // CHECK: br i1 %[[call2]], label {{.*}}, label {{.*}}
 
   if (S s(3, 4); auto& [a, b] = s) {
@@ -63,8 +73,8 @@ void foo() {
     __builtin_assume(b == 4);
   }
 // CHECK: %[[call3:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
 // CHECK: br i1 %[[call3]], label {{.*}}, label {{.*}}
 
   while (auto [i, j] = S(5, 6))
@@ -72,8 +82,8 @@ void foo() {
 
 // CHECK: while.cond{{.*}}:
 // CHECK: %[[call4:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
 // CHECK: br i1 %[[call4]], label {{.*}}, label {{.*}}
 
   S s(7, 8);
@@ -82,8 +92,8 @@ void foo() {
 
 // CHECK: while.cond{{.*}}:
 // CHECK: %[[call5:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
 // CHECK: br i1 %[[call5]], label {{.*}}, label {{.*}}
 
   for (int k = 0; auto [i, j] = S(24, 42); ++k)
@@ -91,8 +101,8 @@ void foo() {
 
 // CHECK: for.cond{{.*}}:
 // CHECK: %[[call6:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
 // CHECK: br i1 %[[call6]], label {{.*}}, label {{.*}}
 
   for (S s(114, 514); auto& [i, j] = s; ++i)
@@ -100,9 +110,102 @@ void foo() {
 
 // CHECK: for.cond{{.*}}:
 // CHECK: %[[call7:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
 // CHECK: br i1 %[[call7]], label {{.*}}, label {{.*}}
+
+  switch (S s(10, 11); auto& [i, j] = s) {
+    case 10 * 11:
+      __builtin_assume(i == 10);
+      __builtin_assume(j == 11);
+      break;
+    default:
+      break;
+  }
+
+// CHECK: %[[call8:.+]] = call {{.*}} i32 @_ZN5Case11ScviEv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
+// CHECK: switch i32 %[[call8]], label {{.*}}
+
 }
 
+constexpr int bar(auto) {
+  constexpr auto value = [] {
+    if (S s(1, 2); auto [i, j] = s)
+      return S(i, j);
+    return S(0, 0);
+  }();
+  static_assert(value == S(1, 2));
+
+  // FIXME: The diagnostic message adds a trailing comma "static assertion failed due to requirement 'value == Case1::S((0, 1, ))'"
+  // static_assert(value == S(0, 1));
+
+  constexpr auto value2 = [] {
+    if (auto [a, b] = S(1, 2))
+      return S(a, b);
+    return S(0, 0);
+  }();
+  static_assert(value2 == S(1, 2));
+
+  constexpr auto value3 = [] {
+    if (auto&& [a, b] = S(3, 4))
+      return S(a, b);
+    return S(0, 0);
+  }();
+  static_assert(value3 == S(3, 4));
+
+  constexpr auto value4 = [] {
+    S s(7, 8);
+    int cnt = 0;
+    while (auto& [i, j] = s) {
+      s.flag = false;
+      ++i, ++j;
+      if (++cnt == 10)
+        break;
+    }
+    return s;
+  }();
+  static_assert(value4 == S(17, 18));
+
+  constexpr auto value5 = [] {
+    S s(3, 4);
+    for (int cnt = 0; auto& [x, y] = s; s.flag = false, ++cnt) {
+      if (cnt == 3)
+        break;
+      ++x, ++y;
+    }
+    return s;
+  }();
+  static_assert(value5 == S(6, 7));
+
+  constexpr auto value6 = [] {
+    switch (auto [x, y] = S(3, 4)) {
+      case 3 * 4:
+        return S(x, y);
+      default:
+        return S(y, x);
+    }
+  }();
+  static_assert(value6 == S(3, 4));
+
+  return 42;
+}
+
+constexpr int value = bar(1);
+
+#if 0
+
+// FIXME: This causes clang to ICE, though this is not a regression.
+constexpr int ice(auto) {
+  if constexpr (S s(1, 2); auto [i, j] = s) {
+    static_assert(i == 1);
+  }
+  return 42;
+}
+
+constexpr int value2 = ice(1);
+
+#endif
+
 } // namespace Case1

>From 8dd6f8245c7aed0d0f6fb40e05227160db3ebbed Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Mar 2025 16:06:21 +0800
Subject: [PATCH 5/9] Enable fexperimental-new-constant-interpreter tests

---
 clang/lib/AST/ByteCode/Compiler.cpp | 45 +++++++++++++++++++++++++----
 clang/lib/AST/ByteCode/Compiler.h   |  1 +
 clang/test/CodeGen/p0963r3.cpp      |  1 +
 3 files changed, 41 insertions(+), 6 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 394e39e99a106..213e1a965ff44 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5104,6 +5104,16 @@ bool Compiler<Emitter>::visitCompoundStmt(const CompoundStmt *S) {
   return Scope.destroyLocals();
 }
 
+template <class Emitter>
+bool Compiler<Emitter>::emitDecompositionVarInit(const DecompositionDecl *DD) {
+  for (auto *BD : DD->bindings())
+    if (auto *KD = BD->getHoldingVar()) {
+      if (!this->visitVarDecl(KD))
+        return false;
+    }
+  return true;
+}
+
 template <class Emitter>
 bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
   for (const auto *D : DS->decls()) {
@@ -5118,12 +5128,10 @@ bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
       return false;
 
     // Register decomposition decl holding vars.
-    if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) {
-      for (auto *BD : DD->bindings())
-        if (auto *KD = BD->getHoldingVar()) {
-          if (!this->visitVarDecl(KD))
-            return false;
-        }
+    if (const auto *DD = dyn_cast<DecompositionDecl>(VD);
+        DD && !DD->isDecisionVariable()) {
+      if (!this->emitDecompositionVarInit(DD))
+        return false;
     }
   }
 
@@ -5189,6 +5197,12 @@ template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
       return false;
   }
 
+  if (auto *DD =
+          dyn_cast_if_present<DecompositionDecl>(IS->getConditionVariable());
+      DD && DD->isDecisionVariable())
+    if (!this->emitDecompositionVarInit(DD))
+      return false;
+
   if (const Stmt *Else = IS->getElse()) {
     LabelTy LabelElse = this->getLabel();
     LabelTy LabelEnd = this->getLabel();
@@ -5249,6 +5263,13 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
 
     if (!this->visitBool(Cond))
       return false;
+
+    if (auto *DD =
+            dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable());
+        DD && DD->isDecisionVariable())
+      if (!this->emitDecompositionVarInit(DD))
+        return false;
+
     if (!this->jumpFalse(EndLabel))
       return false;
 
@@ -5330,6 +5351,12 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
         return false;
     }
 
+    if (auto *DD =
+            dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable());
+        DD && DD->isDecisionVariable())
+      if (!this->emitDecompositionVarInit(DD))
+        return false;
+
     if (Body && !this->visitStmt(Body))
       return false;
 
@@ -5452,6 +5479,12 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
   if (!this->emitSetLocal(CondT, CondVar, S))
     return false;
 
+  if (auto *DD =
+          dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable());
+      DD && DD->isDecisionVariable())
+    if (!this->emitDecompositionVarInit(DD))
+      return false;
+
   CaseMap CaseLabels;
   // Create labels and comparison ops for all case statements.
   for (const SwitchCase *SC = S->getSwitchCaseList(); SC;
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 77fcc3d1b41ce..9bb0ba6e52f16 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -386,6 +386,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
   bool compileUnionAssignmentOperator(const CXXMethodDecl *MD);
 
   bool checkLiteralType(const Expr *E);
+  bool emitDecompositionVarInit(const DecompositionDecl *DD);
 
 protected:
   /// Variable to storage mapping.
diff --git a/clang/test/CodeGen/p0963r3.cpp b/clang/test/CodeGen/p0963r3.cpp
index b356e78adfa83..b48b5294e093e 100644
--- a/clang/test/CodeGen/p0963r3.cpp
+++ b/clang/test/CodeGen/p0963r3.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -std=c++2c -verify -emit-llvm -triple=x86_64-pc-linux-gnu %s -o - | FileCheck %s
+// RUN: %clang_cc1 -std=c++2c -verify -fsyntax-only -fexperimental-new-constant-interpreter -triple=x86_64-pc-linux-gnu %s
 // expected-no-diagnostics
 
 namespace std {

>From da94831f805f1a6ef0f71cfa89910878c93ead80 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Mar 2025 19:33:52 +0800
Subject: [PATCH 6/9] Remove isDecisionVariable flag

---
 clang/docs/LanguageExtensions.rst             |  1 +
 clang/include/clang/AST/DeclCXX.h             | 26 +++++----------
 clang/lib/AST/ASTImporter.cpp                 |  7 ++--
 clang/lib/AST/ByteCode/Compiler.cpp           | 27 +++++++--------
 clang/lib/AST/ByteCode/Compiler.h             |  2 +-
 clang/lib/AST/DeclCXX.cpp                     | 21 +++++-------
 clang/lib/AST/ExprConstant.cpp                | 25 +++++++-------
 clang/lib/CodeGen/CGDecl.cpp                  |  6 ++--
 clang/lib/CodeGen/CGStmt.cpp                  | 33 ++++++++-----------
 clang/lib/CodeGen/CodeGenFunction.cpp         |  3 +-
 clang/lib/CodeGen/CodeGenFunction.h           |  2 +-
 clang/lib/Sema/SemaDecl.cpp                   |  7 ++--
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  8 ++---
 clang/lib/Serialization/ASTReaderDecl.cpp     |  9 ++---
 clang/lib/Serialization/ASTWriterDecl.cpp     |  1 -
 15 files changed, 74 insertions(+), 104 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 578ee02f09b9b..8fbbee0f1586f 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1641,6 +1641,7 @@ Conditional ``explicit``                     __cpp_conditional_explicit       C+
 Attributes on Lambda-Expressions                                              C++23         C++11
 Attributes on Structured Bindings            __cpp_structured_bindings        C++26         C++03
 Packs in Structured Bindings                 __cpp_structured_bindings        C++26         C++03
+Structured binding declaration as a condition                                 C++26         C++11
 Static assert with user-generated message    __cpp_static_assert >= 202306L   C++26         C++11
 Pack Indexing                                __cpp_pack_indexing              C++26         C++03
 ``= delete ("should have a reason");``       __cpp_deleted_function           C++26         C++03
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index d82d9900945a1..61be7fb50ff87 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4224,18 +4224,15 @@ class DecompositionDecl final
     : public VarDecl,
       private llvm::TrailingObjects<DecompositionDecl, BindingDecl *> {
   /// The number of BindingDecl*s following this object.
-  unsigned NumBindings : 31;
-
-  LLVM_PREFERRED_TYPE(bool)
-  unsigned IsDecisionVariable : 1;
+  unsigned NumBindings;
 
   DecompositionDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
                     SourceLocation LSquareLoc, QualType T,
                     TypeSourceInfo *TInfo, StorageClass SC,
-                    ArrayRef<BindingDecl *> Bindings, bool IsDecisionVariable)
+                    ArrayRef<BindingDecl *> Bindings)
       : VarDecl(Decomposition, C, DC, StartLoc, LSquareLoc, nullptr, T, TInfo,
                 SC),
-        NumBindings(Bindings.size()), IsDecisionVariable(IsDecisionVariable) {
+        NumBindings(Bindings.size()) {
     std::uninitialized_copy(Bindings.begin(), Bindings.end(),
                             getTrailingObjects<BindingDecl *>());
     for (auto *B : Bindings) {
@@ -4256,14 +4253,13 @@ class DecompositionDecl final
 
   static DecompositionDecl *Create(ASTContext &C, DeclContext *DC,
                                    SourceLocation StartLoc,
-                                   SourceLocation LSquareLoc, QualType T,
-                                   TypeSourceInfo *TInfo, StorageClass S,
-                                   ArrayRef<BindingDecl *> Bindings,
-                                   bool IsDecisionVariable);
+                                   SourceLocation LSquareLoc,
+                                   QualType T, TypeSourceInfo *TInfo,
+                                   StorageClass S,
+                                   ArrayRef<BindingDecl *> Bindings);
 
   static DecompositionDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
-                                               unsigned NumBindings,
-                                               bool IsDecisionVariable);
+                                               unsigned NumBindings);
 
   // Provide the range of bindings which may have a nested pack.
   llvm::ArrayRef<BindingDecl *> bindings() const {
@@ -4290,12 +4286,6 @@ class DecompositionDecl final
                                             std::move(Bindings));
   }
 
-  /// The decision variable of a condition that is a structured binding
-  /// declaration is specified in [dcl.struct.bind]p4:
-  ///   If a structured binding declaration appears as a condition, the decision
-  ///   variable of the condition is e.
-  bool isDecisionVariable() const { return IsDecisionVariable; }
-
   void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
 
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 95ca085c5b746..82180486f3c7c 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4614,10 +4614,9 @@ ExpectedDecl ASTNodeImporter::VisitVarDecl(VarDecl *D) {
             ImportArrayChecked(FromDecomp->bindings(), Bindings.begin()))
       return std::move(Err);
     DecompositionDecl *ToDecomp;
-    if (GetImportedOrCreateDecl(ToDecomp, FromDecomp, Importer.getToContext(),
-                                DC, ToInnerLocStart, Loc, ToType,
-                                ToTypeSourceInfo, D->getStorageClass(),
-                                Bindings, FromDecomp->isDecisionVariable()))
+    if (GetImportedOrCreateDecl(
+            ToDecomp, FromDecomp, Importer.getToContext(), DC, ToInnerLocStart,
+            Loc, ToType, ToTypeSourceInfo, D->getStorageClass(), Bindings))
       return ToDecomp;
     ToVar = ToDecomp;
   } else {
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 213e1a965ff44..4848d6a6e012e 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5051,7 +5051,7 @@ template <class Emitter> bool Compiler<Emitter>::visitStmt(const Stmt *S) {
   case Stmt::CompoundStmtClass:
     return visitCompoundStmt(cast<CompoundStmt>(S));
   case Stmt::DeclStmtClass:
-    return visitDeclStmt(cast<DeclStmt>(S));
+    return visitDeclStmt(cast<DeclStmt>(S), /*EvaluateConditionDecl=*/true);
   case Stmt::ReturnStmtClass:
     return visitReturnStmt(cast<ReturnStmt>(S));
   case Stmt::IfStmtClass:
@@ -5115,7 +5115,8 @@ bool Compiler<Emitter>::emitDecompositionVarInit(const DecompositionDecl *DD) {
 }
 
 template <class Emitter>
-bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
+bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS,
+                                      bool EvaluateConditionDecl) {
   for (const auto *D : DS->decls()) {
     if (isa<StaticAssertDecl, TagDecl, TypedefNameDecl, BaseUsingDecl,
             FunctionDecl, NamespaceAliasDecl, UsingDirectiveDecl>(D))
@@ -5129,7 +5130,7 @@ bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
 
     // Register decomposition decl holding vars.
     if (const auto *DD = dyn_cast<DecompositionDecl>(VD);
-        DD && !DD->isDecisionVariable()) {
+        EvaluateConditionDecl && DD) {
       if (!this->emitDecompositionVarInit(DD))
         return false;
     }
@@ -5180,7 +5181,7 @@ template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
       return false;
 
   if (const DeclStmt *CondDecl = IS->getConditionVariableDeclStmt())
-    if (!visitDeclStmt(CondDecl))
+    if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
       return false;
 
   // Compile condition.
@@ -5198,8 +5199,7 @@ template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
   }
 
   if (auto *DD =
-          dyn_cast_if_present<DecompositionDecl>(IS->getConditionVariable());
-      DD && DD->isDecisionVariable())
+          dyn_cast_if_present<DecompositionDecl>(IS->getConditionVariable()))
     if (!this->emitDecompositionVarInit(DD))
       return false;
 
@@ -5258,15 +5258,14 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
   {
     LocalScope<Emitter> CondScope(this);
     if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
-      if (!visitDeclStmt(CondDecl))
+      if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
         return false;
 
     if (!this->visitBool(Cond))
       return false;
 
     if (auto *DD =
-            dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable());
-        DD && DD->isDecisionVariable())
+            dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable()))
       if (!this->emitDecompositionVarInit(DD))
         return false;
 
@@ -5341,7 +5340,7 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
   {
     LocalScope<Emitter> CondScope(this);
     if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
-      if (!visitDeclStmt(CondDecl))
+      if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
         return false;
 
     if (Cond) {
@@ -5352,8 +5351,7 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
     }
 
     if (auto *DD =
-            dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable());
-        DD && DD->isDecisionVariable())
+            dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable()))
       if (!this->emitDecompositionVarInit(DD))
         return false;
 
@@ -5470,7 +5468,7 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
       return false;
 
   if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
-    if (!visitDeclStmt(CondDecl))
+    if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
       return false;
 
   // Initialize condition variable.
@@ -5480,8 +5478,7 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
     return false;
 
   if (auto *DD =
-          dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable());
-      DD && DD->isDecisionVariable())
+          dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable()))
     if (!this->emitDecompositionVarInit(DD))
       return false;
 
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 9bb0ba6e52f16..f59f5a3b91d59 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -213,7 +213,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
 
   // Statements.
   bool visitCompoundStmt(const CompoundStmt *S);
-  bool visitDeclStmt(const DeclStmt *DS);
+  bool visitDeclStmt(const DeclStmt *DS, bool EvaluateConditionDecl);
   bool visitReturnStmt(const ReturnStmt *RS);
   bool visitIfStmt(const IfStmt *IS);
   bool visitWhileStmt(const WhileStmt *S);
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index fdad1150c8b1a..7eff776882629 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3520,24 +3520,21 @@ DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC,
                                              SourceLocation LSquareLoc,
                                              QualType T, TypeSourceInfo *TInfo,
                                              StorageClass SC,
-                                             ArrayRef<BindingDecl *> Bindings,
-                                             bool IsDecisionVariable) {
+                                             ArrayRef<BindingDecl *> Bindings) {
   size_t Extra = additionalSizeToAlloc<BindingDecl *>(Bindings.size());
-  return new (C, DC, Extra) DecompositionDecl(
-      C, DC, StartLoc, LSquareLoc, T, TInfo, SC, Bindings, IsDecisionVariable);
+  return new (C, DC, Extra)
+      DecompositionDecl(C, DC, StartLoc, LSquareLoc, T, TInfo, SC, Bindings);
 }
 
-DecompositionDecl *
-DecompositionDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
-                                      unsigned NumBindings,
-                                      bool IsDecisionVariable) {
+DecompositionDecl *DecompositionDecl::CreateDeserialized(ASTContext &C,
+                                                         GlobalDeclID ID,
+                                                         unsigned NumBindings) {
   size_t Extra = additionalSizeToAlloc<BindingDecl *>(NumBindings);
-  auto *Result = new (C, ID, Extra) DecompositionDecl(
-      C, nullptr, SourceLocation(), SourceLocation(), QualType(), nullptr,
-      StorageClass(), {}, IsDecisionVariable);
+  auto *Result = new (C, ID, Extra)
+      DecompositionDecl(C, nullptr, SourceLocation(), SourceLocation(),
+                        QualType(), nullptr, StorageClass(), {});
   // Set up and clean out the bindings array.
   Result->NumBindings = NumBindings;
-  Result->IsDecisionVariable = IsDecisionVariable;
   auto *Trail = Result->getTrailingObjects<BindingDecl *>();
   for (unsigned I = 0; I != NumBindings; ++I)
     new (Trail + I) BindingDecl*(nullptr);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 898f8a2fce769..32652aaef226e 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5221,14 +5221,15 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
 static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
                                           const DecompositionDecl *DD);
 
-static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
+static bool EvaluateDecl(EvalInfo &Info, const Decl *D,
+                         bool EvaluateConditionDecl) {
   bool OK = true;
 
   if (const VarDecl *VD = dyn_cast<VarDecl>(D))
     OK &= EvaluateVarDecl(Info, VD);
 
   if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D);
-      DD && !DD->isDecisionVariable())
+      EvaluateConditionDecl && DD)
     OK &= EvaluateDecompositionDeclInit(Info, DD);
 
   return OK;
@@ -5236,12 +5237,12 @@ static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
 
 static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
                                           const DecompositionDecl *DD) {
-  bool OK = true;
   for (auto *BD : DD->flat_bindings())
     if (auto *VD = BD->getHoldingVar())
-      OK &= EvaluateDecl(Info, VD);
+      if (!EvaluateDecl(Info, VD, /*EvaluateConditionDecl=*/true))
+        return false;
 
-  return OK;
+  return true;
 }
 
 static bool EvaluateDependentExpr(const Expr *E, EvalInfo &Info) {
@@ -5259,13 +5260,12 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
   if (Cond->isValueDependent())
     return false;
   FullExpressionRAII Scope(Info);
-  if (CondDecl && !EvaluateDecl(Info, CondDecl))
+  if (CondDecl && !EvaluateDecl(Info, CondDecl, /*EvaluateConditionDecl=*/false))
     return false;
   if (!EvaluateAsBooleanCondition(Cond, Result, Info))
     return false;
   if (auto *DD = dyn_cast_if_present<DecompositionDecl>(CondDecl);
-      DD && DD->isDecisionVariable() &&
-      !EvaluateDecompositionDeclInit(Info, DD))
+      DD && !EvaluateDecompositionDeclInit(Info, DD))
     return false;
   return Scope.destroy();
 }
@@ -5341,7 +5341,8 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
 
     FullExpressionRAII CondScope(Info);
     if (SS->getConditionVariable() &&
-        !EvaluateDecl(Info, SS->getConditionVariable()))
+        !EvaluateDecl(Info, SS->getConditionVariable(),
+                      /*EvaluateConditionDecl=*/false))
       return ESR_Failed;
     if (SS->getCond()->isValueDependent()) {
       // We don't know what the value is, and which branch should jump to.
@@ -5353,8 +5354,7 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
 
     if (auto *DD =
             dyn_cast_if_present<DecompositionDecl>(SS->getConditionVariable());
-        DD && DD->isDecisionVariable() &&
-        !EvaluateDecompositionDeclInit(Info, DD))
+        DD && !EvaluateDecompositionDeclInit(Info, DD))
       return ESR_Failed;
 
     if (!CondScope.destroy())
@@ -5581,7 +5581,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
         return ESR_Failed;
       // Each declaration initialization is its own full-expression.
       FullExpressionRAII Scope(Info);
-      if (!EvaluateDecl(Info, D) && !Info.noteFailure())
+      if (!EvaluateDecl(Info, D, /*EvaluateConditionDecl=*/true) &&
+          !Info.noteFailure())
         return ESR_Failed;
       if (!Scope.destroy())
         return ESR_Failed;
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 44d787a85f691..8de51c3af39f3 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -48,7 +48,7 @@ using namespace CodeGen;
 static_assert(clang::Sema::MaximumAlignment <= llvm::Value::MaximumAlignment,
               "Clang max alignment greater than what LLVM supports?");
 
-void CodeGenFunction::EmitDecl(const Decl &D) {
+void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) {
   switch (D.getKind()) {
   case Decl::BuiltinTemplate:
   case Decl::TranslationUnit:
@@ -152,7 +152,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
     return;
   case Decl::UsingPack:
     for (auto *Using : cast<UsingPackDecl>(D).expansions())
-      EmitDecl(*Using);
+      EmitDecl(*Using, /*EvaluateConditionDecl=*/EvaluateConditionDecl);
     return;
   case Decl::UsingDirective: // using namespace X; [C++]
     if (CGDebugInfo *DI = getDebugInfo())
@@ -165,7 +165,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
            "Should not see file-scope variables inside a function!");
     EmitVarDecl(VD);
     if (auto *DD = dyn_cast<DecompositionDecl>(&VD);
-        DD && !DD->isDecisionVariable())
+        EvaluateConditionDecl && DD)
       EmitDecompositionVarInit(*DD);
 
     return;
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 0a49eadbf33d1..08dd38b23ddef 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -894,7 +894,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
     EmitStmt(S.getInit());
 
   if (S.getConditionVariable())
-    EmitDecl(*S.getConditionVariable());
+    EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
 
   // If the condition constant folds and can be elided, try to avoid emitting
   // the condition and the dead arm of the if/else.
@@ -914,10 +914,8 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
         incrementProfileCounter(&S);
       if (Executed) {
         if (auto *DD = dyn_cast_if_present<DecompositionDecl>(
-                S.getConditionVariable())) {
-          assert(DD->isDecisionVariable());
+                S.getConditionVariable()))
           EmitDecompositionVarInit(*DD);
-        }
         RunCleanupsScope ExecutedScope(*this);
         EmitStmt(Executed);
       }
@@ -959,14 +957,13 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
   // updates are kept linear and consistent.
   if (!CGM.getCodeGenOpts().MCDCCoverage) {
     EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH,
-                         nullptr, S.getConditionVariable());
+                         /*ConditionalOp=*/nullptr,
+                         /*ConditionalDecl=*/S.getConditionVariable());
   } else {
     llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
     if (auto *DD =
-            dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable())) {
-      assert(DD->isDecisionVariable());
+            dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
       EmitDecompositionVarInit(*DD);
-    }
     Builder.CreateCondBr(BoolCondVal, ThenBlock, ElseBlock);
   }
 
@@ -1103,7 +1100,7 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S,
   RunCleanupsScope ConditionScope(*this);
 
   if (S.getConditionVariable())
-    EmitDecl(*S.getConditionVariable());
+    EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
 
   // Evaluate the conditional in the while header.  C99 6.8.5.1: The
   // evaluation of the controlling expression takes place before each
@@ -1111,8 +1108,7 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S,
   llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
 
   if (auto *DD =
-          dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
-      DD && DD->isDecisionVariable())
+          dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
     EmitDecompositionVarInit(*DD);
 
   // while(1) is common, avoid extra exit blocks.  Be sure
@@ -1323,7 +1319,7 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S,
     // If the for statement has a condition scope, emit the local variable
     // declaration.
     if (S.getConditionVariable()) {
-      EmitDecl(*S.getConditionVariable());
+      EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
 
       // We have entered the condition variable's scope, so we're now able to
       // jump to the continue block.
@@ -1350,8 +1346,7 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S,
     llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
 
     if (auto *DD =
-            dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
-        DD && DD->isDecisionVariable())
+            dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
       EmitDecompositionVarInit(*DD);
 
     llvm::MDNode *Weights =
@@ -2249,11 +2244,10 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
       // Emit the condition variable if needed inside the entire cleanup scope
       // used by this special case for constant folded switches.
       if (S.getConditionVariable())
-        EmitDecl(*S.getConditionVariable());
+        EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
 
       if (auto *DD =
-              dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
-          DD && DD->isDecisionVariable())
+              dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
         EmitDecompositionVarInit(*DD);
 
       // At this point, we are no longer "within" a switch instance, so
@@ -2284,12 +2278,11 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
     EmitStmt(S.getInit());
 
   if (S.getConditionVariable())
-    EmitDecl(*S.getConditionVariable());
+    EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
   llvm::Value *CondV = EmitScalarExpr(S.getCond());
 
   if (auto *DD =
-          dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
-      DD && DD->isDecisionVariable())
+          dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
     EmitDecompositionVarInit(*DD);
 
   // Create basic block to hold stuff that comes after switch
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index c17a7d308bca8..8c4edb809ebc8 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -2048,8 +2048,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(
     CondV = EvaluateExprAsBool(Cond);
   }
 
-  if (auto *DD = dyn_cast_if_present<DecompositionDecl>(ConditionalDecl);
-      DD && DD->isDecisionVariable())
+  if (auto *DD = dyn_cast_if_present<DecompositionDecl>(ConditionalDecl))
     EmitDecompositionVarInit(*DD);
 
   // If not at the top of the logical operator nest, update MCDC temp with the
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 3586594429da8..d1be95d0b982b 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3377,7 +3377,7 @@ class CodeGenFunction : public CodeGenTypeCache {
   /// EmitDecl - Emit a declaration.
   ///
   /// This function can be called with a null (unreachable) insert point.
-  void EmitDecl(const Decl &D);
+  void EmitDecl(const Decl &D, bool EvaluateConditionDecl = true);
 
   /// EmitVarDecl - Emit a local variable declaration.
   ///
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 675a928551fcb..36de02d91e151 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7768,10 +7768,9 @@ NamedDecl *Sema::ActOnVariableDeclarator(
       NewVD = cast<VarDecl>(Res.get());
       AddToScope = false;
     } else if (D.isDecompositionDeclarator()) {
-      NewVD = DecompositionDecl::Create(
-          Context, DC, D.getBeginLoc(), D.getIdentifierLoc(), R, TInfo, SC,
-          Bindings, /*IsDecisionVariable=*/D.getContext() ==
-                        DeclaratorContext::Condition);
+      NewVD = DecompositionDecl::Create(Context, DC, D.getBeginLoc(),
+                                        D.getIdentifierLoc(), R, TInfo, SC,
+                                        Bindings);
     } else
       NewVD = VarDecl::Create(Context, DC, D.getBeginLoc(),
                               D.getIdentifierLoc(), II, R, TInfo, SC);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index c87ce2ae15ae9..dd894df851488 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -14,7 +14,6 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTLambda.h"
 #include "clang/AST/ASTMutationListener.h"
-#include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DependentDiagnostic.h"
 #include "clang/AST/Expr.h"
@@ -1474,10 +1473,9 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D,
   // Build the instantiated declaration.
   VarDecl *Var;
   if (Bindings)
-    Var = DecompositionDecl::Create(
-        SemaRef.Context, DC, D->getInnerLocStart(), D->getLocation(),
-        DI->getType(), DI, D->getStorageClass(), *Bindings,
-        cast<DecompositionDecl>(D)->isDecisionVariable());
+    Var = DecompositionDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(),
+                                    D->getLocation(), DI->getType(), DI,
+                                    D->getStorageClass(), *Bindings);
   else
     Var = VarDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(),
                           D->getLocation(), D->getIdentifier(), DI->getType(),
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 113ed96ea2021..2a580c44b94e5 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -4115,12 +4115,9 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
   case DECL_PARM_VAR:
     D = ParmVarDecl::CreateDeserialized(Context, ID);
     break;
-  case DECL_DECOMPOSITION: {
-    unsigned NumBindings = Record.readInt();
-    bool IsDecisionVariable = Record.readBool();
-    D = DecompositionDecl::CreateDeserialized(Context, ID, NumBindings,
-                                              IsDecisionVariable);
-  } break;
+  case DECL_DECOMPOSITION:
+    D = DecompositionDecl::CreateDeserialized(Context, ID, Record.readInt());
+    break;
   case DECL_BINDING:
     D = BindingDecl::CreateDeserialized(Context, ID);
     break;
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index c11866319f2b0..a1810003f5425 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1327,7 +1327,6 @@ void ASTDeclWriter::VisitParmVarDecl(ParmVarDecl *D) {
 void ASTDeclWriter::VisitDecompositionDecl(DecompositionDecl *D) {
   // Record the number of bindings first to simplify deserialization.
   Record.push_back(D->bindings().size());
-  Record.push_back(D->isDecisionVariable());
 
   VisitVarDecl(D);
   for (auto *B : D->bindings())

>From c37a324aa39c37f1162b0d4a157d47d5dae55b33 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Mar 2025 19:36:27 +0800
Subject: [PATCH 7/9] Early returns

---
 clang/lib/AST/ExprConstant.cpp | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 32652aaef226e..edb840e9fed89 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5223,16 +5223,16 @@ static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
 
 static bool EvaluateDecl(EvalInfo &Info, const Decl *D,
                          bool EvaluateConditionDecl) {
-  bool OK = true;
-
   if (const VarDecl *VD = dyn_cast<VarDecl>(D))
-    OK &= EvaluateVarDecl(Info, VD);
+    if (!EvaluateVarDecl(Info, VD))
+      return false;
 
   if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D);
       EvaluateConditionDecl && DD)
-    OK &= EvaluateDecompositionDeclInit(Info, DD);
+    if (!EvaluateDecompositionDeclInit(Info, DD))
+      return false;
 
-  return OK;
+  return true;
 }
 
 static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
@@ -5260,7 +5260,8 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
   if (Cond->isValueDependent())
     return false;
   FullExpressionRAII Scope(Info);
-  if (CondDecl && !EvaluateDecl(Info, CondDecl, /*EvaluateConditionDecl=*/false))
+  if (CondDecl &&
+      !EvaluateDecl(Info, CondDecl, /*EvaluateConditionDecl=*/false))
     return false;
   if (!EvaluateAsBooleanCondition(Cond, Result, Info))
     return false;

>From 7ca3012be794b65324b62d7eaf8bebaf326bcd5e Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Mar 2025 19:48:38 +0800
Subject: [PATCH 8/9] Fix documents

---
 clang/docs/LanguageExtensions.rst | 96 +++++++++++++++----------------
 1 file changed, 48 insertions(+), 48 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 8fbbee0f1586f..8d20217ee2c28 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1604,54 +1604,54 @@ More information could be found `here <https://clang.llvm.org/docs/Modules.html>
 Language Extensions Back-ported to Previous Standards
 =====================================================
 
-============================================ ================================ ============= =============
-Feature                                      Feature Test Macro               Introduced In Backported To
-============================================ ================================ ============= =============
-variadic templates                           __cpp_variadic_templates         C++11         C++03
-Alias templates                              __cpp_alias_templates            C++11         C++03
-Non-static data member initializers          __cpp_nsdmi                      C++11         C++03
-Range-based ``for`` loop                     __cpp_range_based_for            C++11         C++03
-RValue references                            __cpp_rvalue_references          C++11         C++03
-Attributes                                   __cpp_attributes                 C++11         C++03
-Lambdas                                      __cpp_lambdas                    C++11         C++03
-Generalized lambda captures                  __cpp_init_captures              C++14         C++03
-Generic lambda expressions                   __cpp_generic_lambdas            C++14         C++03
-variable templates                           __cpp_variable_templates         C++14         C++03
-Binary literals                              __cpp_binary_literals            C++14         C++03
-Relaxed constexpr                            __cpp_constexpr                  C++14         C++11
-Static assert with no message                __cpp_static_assert >= 201411L   C++17         C++11
-Pack expansion in generalized lambda-capture __cpp_init_captures              C++17         C++03
-``if constexpr``                             __cpp_if_constexpr               C++17         C++11
-fold expressions                             __cpp_fold_expressions           C++17         C++03
-Lambda capture of \*this by value            __cpp_capture_star_this          C++17         C++03
-Attributes on enums                          __cpp_enumerator_attributes      C++17         C++03
-Guaranteed copy elision                      __cpp_guaranteed_copy_elision    C++17         C++03
-Hexadecimal floating literals                __cpp_hex_float                  C++17         C++03
-``inline`` variables                         __cpp_inline_variables           C++17         C++03
-Attributes on namespaces                     __cpp_namespace_attributes       C++17         C++11
-Structured bindings                          __cpp_structured_bindings        C++17         C++03
-template template arguments                  __cpp_template_template_args     C++17         C++03
-Familiar template syntax for generic lambdas __cpp_generic_lambdas            C++20         C++03
-``static operator[]``                        __cpp_multidimensional_subscript C++20         C++03
-Designated initializers                      __cpp_designated_initializers    C++20         C++03
-Conditional ``explicit``                     __cpp_conditional_explicit       C++20         C++03
-``using enum``                               __cpp_using_enum                 C++20         C++03
-``if consteval``                             __cpp_if_consteval               C++23         C++20
-``static operator()``                        __cpp_static_call_operator       C++23         C++03
-Attributes on Lambda-Expressions                                              C++23         C++11
-Attributes on Structured Bindings            __cpp_structured_bindings        C++26         C++03
-Packs in Structured Bindings                 __cpp_structured_bindings        C++26         C++03
-Structured binding declaration as a condition                                 C++26         C++11
-Static assert with user-generated message    __cpp_static_assert >= 202306L   C++26         C++11
-Pack Indexing                                __cpp_pack_indexing              C++26         C++03
-``= delete ("should have a reason");``       __cpp_deleted_function           C++26         C++03
-Variadic Friends                             __cpp_variadic_friend            C++26         C++03
--------------------------------------------- -------------------------------- ------------- -------------
-Designated initializers (N494)                                                C99           C89
-Array & element qualification (N2607)                                         C23           C89
-Attributes (N2335)                                                            C23           C89
-``#embed`` (N3017)                                                            C23           C89, C++
-============================================ ================================ ============= =============
+============================================= ================================ ============= =============
+Feature                                       Feature Test Macro               Introduced In Backported To
+============================================= ================================ ============= =============
+variadic templates                            __cpp_variadic_templates         C++11         C++03
+Alias templates                               __cpp_alias_templates            C++11         C++03
+Non-static data member initializers           __cpp_nsdmi                      C++11         C++03
+Range-based ``for`` loop                      __cpp_range_based_for            C++11         C++03
+RValue references                             __cpp_rvalue_references          C++11         C++03
+Attributes                                    __cpp_attributes                 C++11         C++03
+Lambdas                                       __cpp_lambdas                    C++11         C++03
+Generalized lambda captures                   __cpp_init_captures              C++14         C++03
+Generic lambda expressions                    __cpp_generic_lambdas            C++14         C++03
+variable templates                            __cpp_variable_templates         C++14         C++03
+Binary literals                               __cpp_binary_literals            C++14         C++03
+Relaxed constexpr                             __cpp_constexpr                  C++14         C++11
+Static assert with no message                 __cpp_static_assert >= 201411L   C++17         C++11
+Pack expansion in generalized lambda-capture  __cpp_init_captures              C++17         C++03
+``if constexpr``                              __cpp_if_constexpr               C++17         C++11
+fold expressions                              __cpp_fold_expressions           C++17         C++03
+Lambda capture of \*this by value             __cpp_capture_star_this          C++17         C++03
+Attributes on enums                           __cpp_enumerator_attributes      C++17         C++03
+Guaranteed copy elision                       __cpp_guaranteed_copy_elision    C++17         C++03
+Hexadecimal floating literals                 __cpp_hex_float                  C++17         C++03
+``inline`` variables                          __cpp_inline_variables           C++17         C++03
+Attributes on namespaces                      __cpp_namespace_attributes       C++17         C++11
+Structured bindings                           __cpp_structured_bindings        C++17         C++03
+template template arguments                   __cpp_template_template_args     C++17         C++03
+Familiar template syntax for generic lambdas  __cpp_generic_lambdas            C++20         C++03
+``static operator[]``                         __cpp_multidimensional_subscript C++20         C++03
+Designated initializers                       __cpp_designated_initializers    C++20         C++03
+Conditional ``explicit``                      __cpp_conditional_explicit       C++20         C++03
+``using enum``                                __cpp_using_enum                 C++20         C++03
+``if consteval``                              __cpp_if_consteval               C++23         C++20
+``static operator()``                         __cpp_static_call_operator       C++23         C++03
+Attributes on Lambda-Expressions                                               C++23         C++11
+Attributes on Structured Bindings             __cpp_structured_bindings        C++26         C++03
+Packs in Structured Bindings                  __cpp_structured_bindings        C++26         C++03
+Structured binding declaration as a condition                                  C++26         C++11
+Static assert with user-generated message     __cpp_static_assert >= 202306L   C++26         C++11
+Pack Indexing                                 __cpp_pack_indexing              C++26         C++03
+``= delete ("should have a reason");``        __cpp_deleted_function           C++26         C++03
+Variadic Friends                              __cpp_variadic_friend            C++26         C++03
+--------------------------------------------- -------------------------------- ------------- -------------
+Designated initializers (N494)                                                 C99           C89
+Array & element qualification (N2607)                                          C23           C89
+Attributes (N2335)                                                             C23           C89
+``#embed`` (N3017)                                                             C23           C89, C++
+============================================= ================================ ============= =============
 
 Builtin type aliases
 ====================

>From ecb03db718cf4c62635f7f6d11d10f395f9eceb3 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Mar 2025 20:16:53 +0800
Subject: [PATCH 9/9] Default evaluation flag to false

---
 clang/lib/AST/ByteCode/Compiler.cpp |  8 ++++----
 clang/lib/AST/ByteCode/Compiler.h   |  2 +-
 clang/lib/AST/ExprConstant.cpp      |  8 +++-----
 clang/lib/CodeGen/CGStmt.cpp        | 12 ++++++------
 clang/lib/CodeGen/CodeGenFunction.h |  2 +-
 5 files changed, 15 insertions(+), 17 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 4848d6a6e012e..fde601f2b5418 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5181,7 +5181,7 @@ template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
       return false;
 
   if (const DeclStmt *CondDecl = IS->getConditionVariableDeclStmt())
-    if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
+    if (!visitDeclStmt(CondDecl))
       return false;
 
   // Compile condition.
@@ -5258,7 +5258,7 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
   {
     LocalScope<Emitter> CondScope(this);
     if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
-      if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
+      if (!visitDeclStmt(CondDecl))
         return false;
 
     if (!this->visitBool(Cond))
@@ -5340,7 +5340,7 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
   {
     LocalScope<Emitter> CondScope(this);
     if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
-      if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
+      if (!visitDeclStmt(CondDecl))
         return false;
 
     if (Cond) {
@@ -5468,7 +5468,7 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
       return false;
 
   if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
-    if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
+    if (!visitDeclStmt(CondDecl))
       return false;
 
   // Initialize condition variable.
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index f59f5a3b91d59..bc991535bddfc 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -213,7 +213,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
 
   // Statements.
   bool visitCompoundStmt(const CompoundStmt *S);
-  bool visitDeclStmt(const DeclStmt *DS, bool EvaluateConditionDecl);
+  bool visitDeclStmt(const DeclStmt *DS, bool EvaluateConditionDecl = false);
   bool visitReturnStmt(const ReturnStmt *RS);
   bool visitIfStmt(const IfStmt *IS);
   bool visitWhileStmt(const WhileStmt *S);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index edb840e9fed89..cd2a196454677 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5222,7 +5222,7 @@ static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
                                           const DecompositionDecl *DD);
 
 static bool EvaluateDecl(EvalInfo &Info, const Decl *D,
-                         bool EvaluateConditionDecl) {
+                         bool EvaluateConditionDecl = false) {
   if (const VarDecl *VD = dyn_cast<VarDecl>(D))
     if (!EvaluateVarDecl(Info, VD))
       return false;
@@ -5260,8 +5260,7 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
   if (Cond->isValueDependent())
     return false;
   FullExpressionRAII Scope(Info);
-  if (CondDecl &&
-      !EvaluateDecl(Info, CondDecl, /*EvaluateConditionDecl=*/false))
+  if (CondDecl && !EvaluateDecl(Info, CondDecl))
     return false;
   if (!EvaluateAsBooleanCondition(Cond, Result, Info))
     return false;
@@ -5342,8 +5341,7 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
 
     FullExpressionRAII CondScope(Info);
     if (SS->getConditionVariable() &&
-        !EvaluateDecl(Info, SS->getConditionVariable(),
-                      /*EvaluateConditionDecl=*/false))
+        !EvaluateDecl(Info, SS->getConditionVariable()))
       return ESR_Failed;
     if (SS->getCond()->isValueDependent()) {
       // We don't know what the value is, and which branch should jump to.
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 08dd38b23ddef..809d0fbebe255 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -894,7 +894,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
     EmitStmt(S.getInit());
 
   if (S.getConditionVariable())
-    EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
+    EmitDecl(*S.getConditionVariable());
 
   // If the condition constant folds and can be elided, try to avoid emitting
   // the condition and the dead arm of the if/else.
@@ -1100,7 +1100,7 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S,
   RunCleanupsScope ConditionScope(*this);
 
   if (S.getConditionVariable())
-    EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
+    EmitDecl(*S.getConditionVariable());
 
   // Evaluate the conditional in the while header.  C99 6.8.5.1: The
   // evaluation of the controlling expression takes place before each
@@ -1319,7 +1319,7 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S,
     // If the for statement has a condition scope, emit the local variable
     // declaration.
     if (S.getConditionVariable()) {
-      EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
+      EmitDecl(*S.getConditionVariable());
 
       // We have entered the condition variable's scope, so we're now able to
       // jump to the continue block.
@@ -1679,7 +1679,7 @@ void CodeGenFunction::EmitDeclStmt(const DeclStmt &S) {
     EmitStopPoint(&S);
 
   for (const auto *I : S.decls())
-    EmitDecl(*I);
+    EmitDecl(*I, /*EvaluateConditionDecl=*/true);
 }
 
 void CodeGenFunction::EmitBreakStmt(const BreakStmt &S) {
@@ -2244,7 +2244,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
       // Emit the condition variable if needed inside the entire cleanup scope
       // used by this special case for constant folded switches.
       if (S.getConditionVariable())
-        EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
+        EmitDecl(*S.getConditionVariable());
 
       if (auto *DD =
               dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
@@ -2278,7 +2278,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
     EmitStmt(S.getInit());
 
   if (S.getConditionVariable())
-    EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
+    EmitDecl(*S.getConditionVariable());
   llvm::Value *CondV = EmitScalarExpr(S.getCond());
 
   if (auto *DD =
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index d1be95d0b982b..07560666d1b40 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3377,7 +3377,7 @@ class CodeGenFunction : public CodeGenTypeCache {
   /// EmitDecl - Emit a declaration.
   ///
   /// This function can be called with a null (unreachable) insert point.
-  void EmitDecl(const Decl &D, bool EvaluateConditionDecl = true);
+  void EmitDecl(const Decl &D, bool EvaluateConditionDecl = false);
 
   /// EmitVarDecl - Emit a local variable declaration.
   ///



More information about the cfe-commits mailing list