[clang] [Clang][P1061] Add stuctured binding packs (PR #121417)

Jason Rice via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 15 22:13:09 PST 2025


https://github.com/ricejasonf updated https://github.com/llvm/llvm-project/pull/121417

>From 3c81c5bd989f26331917f1401becc1cfa7f4a454 Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Thu, 22 Jul 2021 16:46:33 -0700
Subject: [PATCH 01/14] [Clang][P1061] stuctured binding packs

---
 clang/include/clang/AST/Decl.h                |   8 +-
 clang/include/clang/AST/DeclCXX.h             |  22 +-
 clang/include/clang/AST/ExprCXX.h             |  48 ++++
 clang/include/clang/AST/RecursiveASTVisitor.h |   1 +
 .../clang/Basic/DiagnosticParseKinds.td       |   5 +
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 clang/include/clang/Basic/StmtNodes.td        |   1 +
 clang/include/clang/Sema/DeclSpec.h           |   1 +
 clang/include/clang/Sema/Sema.h               |   4 +-
 .../include/clang/Serialization/ASTBitCodes.h |   1 +
 clang/lib/AST/ASTContext.cpp                  |  14 +-
 clang/lib/AST/ASTImporter.cpp                 |   2 +-
 clang/lib/AST/Decl.cpp                        |  11 +-
 clang/lib/AST/DeclBase.cpp                    |   6 +-
 clang/lib/AST/DeclCXX.cpp                     |  60 ++++-
 clang/lib/AST/Expr.cpp                        |   5 +
 clang/lib/AST/ExprCXX.cpp                     |  48 ++++
 clang/lib/AST/ExprClassification.cpp          |   7 +
 clang/lib/AST/ExprConstant.cpp                |   5 +-
 clang/lib/AST/ItaniumMangle.cpp               |   2 +-
 clang/lib/AST/StmtPrinter.cpp                 |  11 +
 clang/lib/AST/StmtProfile.cpp                 |   4 +
 clang/lib/CodeGen/CGDebugInfo.cpp             |   4 +-
 clang/lib/CodeGen/CGDecl.cpp                  |   5 +-
 clang/lib/CodeGen/CodeGenModule.cpp           |   4 +-
 clang/lib/Parse/ParseDecl.cpp                 |  28 ++-
 clang/lib/Sema/SemaDecl.cpp                   |  10 +
 clang/lib/Sema/SemaDeclCXX.cpp                | 209 ++++++++++++++----
 clang/lib/Sema/SemaExceptionSpec.cpp          |   7 +-
 clang/lib/Sema/SemaLambda.cpp                 |   1 +
 clang/lib/Sema/SemaStmt.cpp                   |  14 +-
 clang/lib/Sema/SemaTemplate.cpp               |   7 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |  40 +++-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  32 ++-
 clang/lib/Sema/SemaTemplateVariadic.cpp       |  73 +++++-
 clang/lib/Sema/TreeTransform.h                |   7 +
 clang/lib/Serialization/ASTReaderStmt.cpp     |  11 +
 clang/lib/Serialization/ASTWriter.cpp         |   1 +
 clang/lib/Serialization/ASTWriterStmt.cpp     |  11 +
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |   1 +
 clang/test/Parser/cxx2c-binding-pack.cpp      |   7 +
 .../cxx2c-binding-pack-nontemplate.cpp        |  12 +
 clang/test/SemaCXX/cxx2c-binding-pack.cpp     |  82 +++++++
 clang/test/SemaCXX/typo-correction-crash.cpp  |   3 +-
 clang/tools/libclang/CXCursor.cpp             |   1 +
 45 files changed, 728 insertions(+), 111 deletions(-)
 create mode 100644 clang/test/Parser/cxx2c-binding-pack.cpp
 create mode 100644 clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp
 create mode 100644 clang/test/SemaCXX/cxx2c-binding-pack.cpp

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 67ee0bb412692a..bdf6c81732d0bc 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -698,6 +698,10 @@ class ValueDecl : public NamedDecl {
     return const_cast<ValueDecl *>(this)->getPotentiallyDecomposedVarDecl();
   }
 
+  /// Determine whether this value is actually a function parameter pack,
+  /// init-capture pack, or structured binding pack
+  bool isParameterPack() const;
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K >= firstValue && K <= lastValue; }
@@ -1527,10 +1531,6 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
     NonParmVarDeclBits.IsInitCapture = IC;
   }
 
-  /// Determine whether this variable is actually a function parameter pack or
-  /// init-capture pack.
-  bool isParameterPack() const;
-
   /// Whether this local extern variable declaration's previous declaration
   /// was declared in the same block scope. Only correct in C++.
   bool isPreviousDeclInSameBlockScope() const {
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index c232556edeff70..12002db17fb3ad 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4131,8 +4131,9 @@ class BindingDecl : public ValueDecl {
   /// binding).
   Expr *Binding = nullptr;
 
-  BindingDecl(DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id)
-      : ValueDecl(Decl::Binding, DC, IdLoc, Id, QualType()) {}
+  BindingDecl(DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id,
+              QualType T)
+      : ValueDecl(Decl::Binding, DC, IdLoc, Id, T) {}
 
   void anchor() override;
 
@@ -4140,7 +4141,8 @@ class BindingDecl : public ValueDecl {
   friend class ASTDeclReader;
 
   static BindingDecl *Create(ASTContext &C, DeclContext *DC,
-                             SourceLocation IdLoc, IdentifierInfo *Id);
+                             SourceLocation IdLoc, IdentifierInfo *Id,
+                             QualType T);
   static BindingDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
 
   /// Get the expression to which this declaration is bound. This may be null
@@ -4152,10 +4154,6 @@ class BindingDecl : public ValueDecl {
   /// decomposition of.
   ValueDecl *getDecomposedDecl() const { return Decomp; }
 
-  /// Get the variable (if any) that holds the value of evaluating the binding.
-  /// Only present for user-defined bindings for tuple-like types.
-  VarDecl *getHoldingVar() const;
-
   /// Set the binding for this BindingDecl, along with its declared type (which
   /// should be a possibly-cv-qualified form of the type of the binding, or a
   /// reference to such a type).
@@ -4167,6 +4165,9 @@ class BindingDecl : public ValueDecl {
   /// Set the decomposed variable for this BindingDecl.
   void setDecomposedDecl(ValueDecl *Decomposed) { Decomp = Decomposed; }
 
+  VarDecl *getHoldingVar() const;
+  static VarDecl *getHoldingVar(Expr *E);
+
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == Decl::Binding; }
 };
@@ -4219,6 +4220,13 @@ class DecompositionDecl final
 
   void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
 
+  /// Visit the variables (if any) that hold the values of evaluating the
+  /// binding. Only present for user-defined bindings for tuple-like types.
+  void VisitHoldingVars(llvm::function_ref<void(VarDecl *)> F) const;
+
+  // Visit the concrete bindings. (workaround)
+  void VisitBindings(llvm::function_ref<void(BindingDecl *)> F) const;
+
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == Decomposition; }
 };
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 4cec89c979f775..6162c712c90dc3 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5321,6 +5321,54 @@ class BuiltinBitCastExpr final
   }
 };
 
+class ResolvedUnexpandedPackExpr final
+    : public Expr,
+      private llvm::TrailingObjects<ResolvedUnexpandedPackExpr, Stmt *> {
+  friend TrailingObjects;
+
+  SourceLocation BeginLoc;
+  unsigned NumExprs;
+
+  ResolvedUnexpandedPackExpr(SourceLocation BL, QualType QT, unsigned NumExprs);
+
+public:
+  static ResolvedUnexpandedPackExpr *CreateDeserialized(ASTContext &C,
+                                                        unsigned NumExprs);
+  static ResolvedUnexpandedPackExpr *
+  Create(ASTContext &C, SourceLocation BeginLoc, QualType T, unsigned NumExprs);
+  static ResolvedUnexpandedPackExpr *Create(ASTContext &C,
+                                            SourceLocation BeginLoc, QualType T,
+                                            llvm::ArrayRef<Expr *> Exprs);
+
+  unsigned getNumExprs() const { return NumExprs; }
+
+  Expr **getExprs() {
+    return reinterpret_cast<Expr **>(getTrailingObjects<Stmt *>());
+  }
+  Expr *const *getExprs() const {
+    return reinterpret_cast<Expr *const *>(getTrailingObjects<Stmt *>());
+  }
+
+  Expr *getExpansion(unsigned Idx) { return getExprs()[Idx]; }
+  Expr *getExpansion(unsigned Idx) const { return getExprs()[Idx]; }
+
+  // Iterators
+  child_range children() {
+    return child_range(getTrailingObjects<Stmt *>(),
+                       getTrailingObjects<Stmt *>() + getNumExprs());
+  }
+
+  SourceLocation getBeginLoc() const LLVM_READONLY { return BeginLoc; }
+  SourceLocation getEndLoc() const LLVM_READONLY { return BeginLoc; }
+
+  // Returns the resolved pack of a decl or nullptr
+  static ResolvedUnexpandedPackExpr *getFromDecl(Decl *);
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == ResolvedUnexpandedPackExprClass;
+  }
+};
+
 } // namespace clang
 
 #endif // LLVM_CLANG_AST_EXPRCXX_H
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index f5b32ed51698e0..9f8a8f2a8348f2 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2936,6 +2936,7 @@ DEF_TRAVERSE_STMT(FunctionParmPackExpr, {})
 DEF_TRAVERSE_STMT(CXXFoldExpr, {})
 DEF_TRAVERSE_STMT(AtomicExpr, {})
 DEF_TRAVERSE_STMT(CXXParenListInitExpr, {})
+DEF_TRAVERSE_STMT(ResolvedUnexpandedPackExpr, {})
 
 DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {
   if (S->getLifetimeExtendedTemporaryDecl()) {
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 86fcae209c40db..5ee6f9ff28c4c8 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1099,6 +1099,11 @@ def err_lambda_capture_misplaced_ellipsis : Error<
   "the name of the capture">;
 def err_lambda_capture_multiple_ellipses : Error<
   "multiple ellipses in pack capture">;
+def err_binding_multiple_ellipses : Error<
+  "multiple ellipses in structured binding declaration">;
+def warn_cxx2c_binding_pack : Warning<
+  "structured binding pack is incompatible with C++ standards before C++2c">,
+  DefaultIgnore, InGroup<CXXPre26Compat>;
 def err_capture_default_first : Error<
   "capture default must be first">;
 def ext_decl_attrs_on_lambda : ExtWarn<
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 330ae045616aba..22012fb2663534 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5906,6 +5906,9 @@ def warn_cxx23_pack_indexing : Warning<
   "pack indexing is incompatible with C++ standards before C++2c">,
   DefaultIgnore, InGroup<CXXPre26Compat>;
 
+def err_pack_outside_template : Error<
+  "pack declaration outside of template">;
+
 def err_fold_expression_packs_both_sides : Error<
   "binary fold expression has unexpanded parameter packs in both operands">;
 def err_fold_expression_empty : Error<
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index 31280df93e4c6e..a5ac8eba371f2f 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -162,6 +162,7 @@ def MaterializeTemporaryExpr : StmtNode<Expr>;
 def LambdaExpr : StmtNode<Expr>;
 def CXXFoldExpr : StmtNode<Expr>;
 def CXXParenListInitExpr: StmtNode<Expr>;
+def ResolvedUnexpandedPackExpr : StmtNode<Expr>;
 
 // C++ Coroutines expressions
 def CoroutineSuspendExpr : StmtNode<Expr, 1>;
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 06243f2624876f..5f5df3a45d41dc 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -1795,6 +1795,7 @@ class DecompositionDeclarator {
     IdentifierInfo *Name;
     SourceLocation NameLoc;
     std::optional<ParsedAttributes> Attrs;
+    SourceLocation EllipsisLoc;
   };
 
 private:
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5ee7ea48cc983c..24e13278cb2a89 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -230,7 +230,8 @@ void threadSafetyCleanup(BeforeSet *Cache);
 
 // FIXME: No way to easily map from TemplateTypeParmTypes to
 // TemplateTypeParmDecls, so we have this horrible PointerUnion.
-typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType *, NamedDecl *>,
+typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType *, NamedDecl *,
+                                     ResolvedUnexpandedPackExpr *>,
                   SourceLocation>
     UnexpandedParameterPack;
 
@@ -6012,6 +6013,7 @@ class Sema final : public SemaBase {
                                          RecordDecl *ClassDecl,
                                          const IdentifierInfo *Name);
 
+  unsigned GetDecompositionElementCount(QualType DecompType);
   void CheckCompleteDecompositionDeclaration(DecompositionDecl *DD);
 
   /// Stack containing information needed when in C++2a an 'auto' is encountered
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index dfd82afad40070..bab63be73e58e8 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1890,6 +1890,7 @@ enum StmtCode {
   EXPR_PACK_EXPANSION,                    // PackExpansionExpr
   EXPR_PACK_INDEXING,                     // PackIndexingExpr
   EXPR_SIZEOF_PACK,                       // SizeOfPackExpr
+  EXPR_RESOLVED_UNEXPANDED_PACK,          // ResolvedUnexpandedPackExpr
   EXPR_SUBST_NON_TYPE_TEMPLATE_PARM,      // SubstNonTypeTemplateParmExpr
   EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK, // SubstNonTypeTemplateParmPackExpr
   EXPR_FUNCTION_PARM_PACK,                // FunctionParmPackExpr
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 8b4ae58e8427a9..6216d896a88ac3 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -12726,11 +12726,15 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
 
   // Likewise, variables with tuple-like bindings are required if their
   // bindings have side-effects.
-  if (const auto *DD = dyn_cast<DecompositionDecl>(VD))
-    for (const auto *BD : DD->bindings())
-      if (const auto *BindingVD = BD->getHoldingVar())
-        if (DeclMustBeEmitted(BindingVD))
-          return true;
+  if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) {
+    bool BindingResult = false;
+    DD->VisitHoldingVars([&](VarDecl *BindingVD) {
+      if (DeclMustBeEmitted(BindingVD))
+        BindingResult = true;
+    });
+    if (BindingResult)
+      return true;
+  }
 
   return false;
 }
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 26d33b0d94795f..33b4fe0b8fecc9 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -2552,7 +2552,7 @@ ExpectedDecl ASTNodeImporter::VisitBindingDecl(BindingDecl *D) {
 
   BindingDecl *ToD;
   if (GetImportedOrCreateDecl(ToD, D, Importer.getToContext(), DC, Loc,
-                              Name.getAsIdentifierInfo()))
+                              Name.getAsIdentifierInfo(), D->getType()))
     return ToD;
 
   Error Err = Error::success();
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 741e908cf9bc56..76c208bef6031c 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2659,10 +2659,6 @@ bool VarDecl::checkForConstantInitialization(
   return Eval->HasConstantInitialization;
 }
 
-bool VarDecl::isParameterPack() const {
-  return isa<PackExpansionType>(getType());
-}
-
 template<typename DeclT>
 static DeclT *getDefinitionOrSelf(DeclT *D) {
   assert(D);
@@ -5397,6 +5393,13 @@ bool ValueDecl::isInitCapture() const {
   return false;
 }
 
+bool ValueDecl::isParameterPack() const {
+  if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(this))
+    return NTTP->isParameterPack();
+
+  return isa_and_nonnull<PackExpansionType>(getType().getTypePtrOrNull());
+}
+
 void ImplicitParamDecl::anchor() {}
 
 ImplicitParamDecl *ImplicitParamDecl::Create(ASTContext &C, DeclContext *DC,
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index fb701f76231bcd..6a5662f9d074e4 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -237,10 +237,12 @@ bool Decl::isTemplateParameterPack() const {
 }
 
 bool Decl::isParameterPack() const {
-  if (const auto *Var = dyn_cast<VarDecl>(this))
+  if (isTemplateParameterPack())
+    return true;
+  if (const auto *Var = dyn_cast<ValueDecl>(this))
     return Var->isParameterPack();
 
-  return isTemplateParameterPack();
+  return false;
 }
 
 FunctionDecl *Decl::getAsFunction() {
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index af73c658d6a0c5..371bf5dcf02206 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3395,26 +3395,37 @@ VarDecl *ValueDecl::getPotentiallyDecomposedVarDecl() {
   if (auto *Var = llvm::dyn_cast<VarDecl>(this))
     return Var;
   if (auto *BD = llvm::dyn_cast<BindingDecl>(this))
-    return llvm::dyn_cast<VarDecl>(BD->getDecomposedDecl());
+    return llvm::dyn_cast_or_null<VarDecl>(BD->getDecomposedDecl());
   return nullptr;
 }
 
 void BindingDecl::anchor() {}
 
 BindingDecl *BindingDecl::Create(ASTContext &C, DeclContext *DC,
-                                 SourceLocation IdLoc, IdentifierInfo *Id) {
-  return new (C, DC) BindingDecl(DC, IdLoc, Id);
+                                 SourceLocation IdLoc, IdentifierInfo *Id,
+                                 QualType T) {
+  return new (C, DC) BindingDecl(DC, IdLoc, Id, T);
 }
 
 BindingDecl *BindingDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
-  return new (C, ID) BindingDecl(nullptr, SourceLocation(), nullptr);
+  return new (C, ID)
+      BindingDecl(nullptr, SourceLocation(), nullptr, QualType());
 }
 
 VarDecl *BindingDecl::getHoldingVar() const {
   Expr *B = getBinding();
   if (!B)
     return nullptr;
-  auto *DRE = dyn_cast<DeclRefExpr>(B->IgnoreImplicit());
+  return getHoldingVar(B);
+}
+
+VarDecl *BindingDecl::getHoldingVar(Expr *E) {
+  auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImplicit());
+  if (!DRE)
+    return nullptr;
+  if (auto *BD = dyn_cast<BindingDecl>(DRE->getDecl())) {
+    DRE = dyn_cast<DeclRefExpr>(BD->getBinding());
+  }
   if (!DRE)
     return nullptr;
 
@@ -3423,6 +3434,45 @@ VarDecl *BindingDecl::getHoldingVar() const {
   return VD;
 }
 
+void DecompositionDecl::VisitHoldingVars(
+    llvm::function_ref<void(VarDecl *)> F) const {
+  for (BindingDecl *B : bindings()) {
+    Expr *BE = B->getBinding();
+    // All BindingDecls will contain holding vars or none will
+    if (!BE)
+      return;
+
+    llvm::ArrayRef<Expr *> Exprs;
+    if (auto *RP = dyn_cast<ResolvedUnexpandedPackExpr>(BE))
+      Exprs = llvm::ArrayRef(RP->getExprs(), RP->getNumExprs());
+    else
+      Exprs = BE;
+
+    for (Expr *E : Exprs) {
+      VarDecl *VD = BindingDecl::getHoldingVar(E);
+      if (!VD)
+        return;
+      F(VD);
+    }
+  }
+}
+
+void DecompositionDecl::VisitBindings(
+    llvm::function_ref<void(BindingDecl *)> F) const {
+  for (BindingDecl *B : bindings()) {
+    llvm::ArrayRef<Expr *> Exprs;
+    if (B->isParameterPack()) {
+      auto *RP = cast<ResolvedUnexpandedPackExpr>(B->getBinding());
+      Exprs = llvm::ArrayRef(RP->getExprs(), RP->getNumExprs());
+      for (Expr *E : Exprs) {
+        auto *DRE = cast<DeclRefExpr>(E);
+        F(cast<BindingDecl>(DRE->getDecl()));
+      }
+    } else
+      F(B);
+  }
+}
+
 void DecompositionDecl::anchor() {}
 
 DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC,
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 8c8ccdb61dc01c..39f02ebf85b2ce 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3656,6 +3656,11 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
     // These never have a side-effect.
     return false;
 
+  // ResolvedUnexpandedPackExpr is currently only used for
+  // structed bindings which have no side effects
+  case ResolvedUnexpandedPackExprClass:
+    return false;
+
   case ConstantExprClass:
     // FIXME: Move this into the "return false;" block above.
     return cast<ConstantExpr>(this)->getSubExpr()->HasSideEffects(
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index fc09d24fc30cb4..190af789d306ed 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1965,3 +1965,51 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee,
   SubExprs[SubExpr::RHS] = RHS;
   setDependence(computeDependence(this));
 }
+
+ResolvedUnexpandedPackExpr::ResolvedUnexpandedPackExpr(SourceLocation BL,
+                                                       QualType QT,
+                                                       unsigned NumExprs)
+    : Expr(ResolvedUnexpandedPackExprClass, QT, VK_PRValue, OK_Ordinary),
+      BeginLoc(BL), NumExprs(NumExprs) {
+  setDependence(ExprDependence::TypeValueInstantiation |
+                ExprDependence::UnexpandedPack);
+}
+
+ResolvedUnexpandedPackExpr *
+ResolvedUnexpandedPackExpr::CreateDeserialized(ASTContext &Ctx,
+                                               unsigned NumExprs) {
+  void *Mem = Ctx.Allocate(totalSizeToAlloc<Stmt *>(NumExprs),
+                           alignof(ResolvedUnexpandedPackExpr));
+  return new (Mem)
+      ResolvedUnexpandedPackExpr(SourceLocation(), QualType(), NumExprs);
+}
+
+ResolvedUnexpandedPackExpr *
+ResolvedUnexpandedPackExpr::Create(ASTContext &Ctx, SourceLocation BL,
+                                   QualType T, unsigned NumExprs) {
+  void *Mem = Ctx.Allocate(totalSizeToAlloc<Stmt *>(NumExprs),
+                           alignof(ResolvedUnexpandedPackExpr));
+  ResolvedUnexpandedPackExpr *New =
+      new (Mem) ResolvedUnexpandedPackExpr(BL, T, NumExprs);
+
+  auto Exprs = llvm::MutableArrayRef(New->getExprs(), New->getNumExprs());
+  std::fill(Exprs.begin(), Exprs.end(), nullptr);
+
+  return New;
+}
+
+ResolvedUnexpandedPackExpr *
+ResolvedUnexpandedPackExpr::Create(ASTContext &Ctx, SourceLocation BL,
+                                   QualType T, ArrayRef<Expr *> Exprs) {
+  auto *New = Create(Ctx, BL, T, Exprs.size());
+  std::copy(Exprs.begin(), Exprs.end(), New->getExprs());
+  return New;
+}
+
+ResolvedUnexpandedPackExpr *ResolvedUnexpandedPackExpr::getFromDecl(Decl *D) {
+  // TODO P1858: Extend to VarDecls for P1858
+  if (auto *BD = dyn_cast<BindingDecl>(D)) {
+    return dyn_cast_or_null<ResolvedUnexpandedPackExpr>(BD->getBinding());
+  }
+  return nullptr;
+}
diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp
index 3f37d06cc8f3a0..29a869bd76ca37 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -451,6 +451,13 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
   case Expr::PackExpansionExprClass:
     return ClassifyInternal(Ctx, cast<PackExpansionExpr>(E)->getPattern());
 
+  case Expr::ResolvedUnexpandedPackExprClass: {
+    if (cast<ResolvedUnexpandedPackExpr>(E)->getNumExprs() > 0)
+      return ClassifyInternal(
+          Ctx, cast<ResolvedUnexpandedPackExpr>(E)->getExpansion(0));
+    return Cl::CL_PRValue;
+  }
+
   case Expr::MaterializeTemporaryExprClass:
     return cast<MaterializeTemporaryExpr>(E)->isBoundToLvalueReference()
               ? Cl::CL_LValue
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index dd75dca647540a..b7ff7aa1d61ecc 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5155,9 +5155,7 @@ static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
     OK &= EvaluateVarDecl(Info, VD);
 
   if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D))
-    for (auto *BD : DD->bindings())
-      if (auto *VD = BD->getHoldingVar())
-        OK &= EvaluateDecl(Info, VD);
+    DD->VisitHoldingVars([&](VarDecl *HD) { OK &= EvaluateDecl(Info, HD); });
 
   return OK;
 }
@@ -17122,6 +17120,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
   case Expr::SYCLUniqueStableNameExprClass:
   case Expr::CXXParenListInitExprClass:
   case Expr::HLSLOutArgExprClass:
+  case Expr::ResolvedUnexpandedPackExprClass:
     return ICEDiag(IK_NotICE, E->getBeginLoc());
 
   case Expr::InitListExprClass: {
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 47aa9b40dab845..ffa9703536b7f5 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -4926,7 +4926,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
   case Expr::SourceLocExprClass:
   case Expr::EmbedExprClass:
   case Expr::BuiltinBitCastExprClass:
-  {
+  case Expr::ResolvedUnexpandedPackExprClass: {
     NotPrimaryExpr();
     if (!NullOut) {
       // As bad as this diagnostic is, it's better than crashing.
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index c5d19f70fc6ea0..50bf880a4d37ea 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -44,9 +44,11 @@
 #include "clang/Basic/TypeTraits.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/raw_ostream.h"
@@ -2556,6 +2558,15 @@ void StmtPrinter::VisitPackIndexingExpr(PackIndexingExpr *E) {
   OS << "]";
 }
 
+void StmtPrinter::VisitResolvedUnexpandedPackExpr(
+    ResolvedUnexpandedPackExpr *E) {
+  OS << "<<resolved pack(";
+  llvm::interleave(
+      E->getExprs(), E->getExprs() + E->getNumExprs(),
+      [this](auto *X) { PrintExpr(X); }, [this] { OS << ", "; });
+  OS << ")>>";
+}
+
 void StmtPrinter::VisitSubstNonTypeTemplateParmPackExpr(
                                        SubstNonTypeTemplateParmPackExpr *Node) {
   OS << *Node->getParameterPack();
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 27313f9ae12752..1424f03ebf78bc 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2276,6 +2276,10 @@ void StmtProfiler::VisitSizeOfPackExpr(const SizeOfPackExpr *S) {
     ID.AddInteger(0);
   }
 }
+void StmtProfiler::VisitResolvedUnexpandedPackExpr(
+    const ResolvedUnexpandedPackExpr *S) {
+  VisitExpr(S);
+}
 
 void StmtProfiler::VisitPackIndexingExpr(const PackIndexingExpr *E) {
   VisitExpr(E);
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index f29ddece5dbc95..db424dabc0575f 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -5067,10 +5067,10 @@ CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *VD, llvm::Value *Storage,
   assert(CGM.getCodeGenOpts().hasReducedDebugInfo());
 
   if (auto *DD = dyn_cast<DecompositionDecl>(VD)) {
-    for (auto *B : DD->bindings()) {
+    DD->VisitBindings([&](BindingDecl *B) {
       EmitDeclare(B, Storage, std::nullopt, Builder,
                   VD->getType()->isReferenceType());
-    }
+    });
     // Don't emit an llvm.dbg.declare for the composite storage as it doesn't
     // correspond to a user variable.
     return nullptr;
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 47b21bc9f63f04..06b4f39e1e3d81 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -104,7 +104,6 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
   case Decl::Binding:
   case Decl::UnresolvedUsingIfExists:
   case Decl::HLSLBuffer:
-    llvm_unreachable("Declaration should not be in declstmts!");
   case Decl::Record:    // struct/union/class X;
   case Decl::CXXRecord: // struct/union/class X; [C++]
     if (CGDebugInfo *DI = getDebugInfo())
@@ -163,9 +162,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))
-      for (auto *B : DD->bindings())
-        if (auto *HD = B->getHoldingVar())
-          EmitVarDecl(*HD);
+      DD->VisitHoldingVars([&](VarDecl *HD) { EmitVarDecl(*HD); });
     return;
   }
 
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index c49f7631488285..bfd459dabafe10 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6987,9 +6987,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
   case Decl::VarTemplateSpecialization:
     EmitGlobal(cast<VarDecl>(D));
     if (auto *DD = dyn_cast<DecompositionDecl>(D))
-      for (auto *B : DD->bindings())
-        if (auto *HD = B->getHoldingVar())
-          EmitGlobal(HD);
+      DD->VisitHoldingVars([&](VarDecl *HD) { EmitGlobal(HD); });
     break;
 
   // Indirect fields from global anonymous structs and unions can be
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 937a94b02458c6..c298825fb88280 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -7308,15 +7308,16 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
 
   // If this doesn't look like a structured binding, maybe it's a misplaced
   // array declarator.
-  if (!(Tok.is(tok::identifier) &&
+  if (!(Tok.isOneOf(tok::identifier, tok::ellipsis) &&
         NextToken().isOneOf(tok::comma, tok::r_square, tok::kw_alignas,
-                            tok::l_square)) &&
+                            tok::identifier, tok::l_square)) &&
       !(Tok.is(tok::r_square) &&
         NextToken().isOneOf(tok::equal, tok::l_brace))) {
     PA.Revert();
     return ParseMisplacedBracketDeclarator(D);
   }
 
+  bool HasEllipsis = false;
   SmallVector<DecompositionDeclarator::Binding, 32> Bindings;
   while (Tok.isNot(tok::r_square)) {
     if (!Bindings.empty()) {
@@ -7331,11 +7332,12 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
           Diag(Tok, diag::err_expected_comma_or_rsquare);
         }
 
-        SkipUntil(tok::r_square, tok::comma, tok::identifier,
-                  StopAtSemi | StopBeforeMatch);
+        // I don't know why this skipping was here
+        // SkipUntil(tok::r_square, tok::comma, tok::identifier,
+        //          StopAtSemi | StopBeforeMatch);
         if (Tok.is(tok::comma))
           ConsumeToken();
-        else if (Tok.isNot(tok::identifier))
+        else if (Tok.is(tok::r_square))
           break;
       }
     }
@@ -7343,6 +7345,20 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
     if (isCXX11AttributeSpecifier())
       DiagnoseAndSkipCXX11Attributes();
 
+    SourceLocation EllipsisLoc = {};
+
+    if (Tok.is(tok::ellipsis)) {
+      if (!getLangOpts().CPlusPlus26)
+        Diag(Tok, diag::warn_cxx2c_binding_pack);
+      if (HasEllipsis) {
+        Diag(Tok, diag::err_binding_multiple_ellipses);
+        break;
+      }
+      HasEllipsis = true;
+      EllipsisLoc = Tok.getLocation();
+      ConsumeToken();
+    }
+
     if (Tok.isNot(tok::identifier)) {
       Diag(Tok, diag::err_expected) << tok::identifier;
       break;
@@ -7360,7 +7376,7 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
       MaybeParseCXX11Attributes(Attrs);
     }
 
-    Bindings.push_back({II, Loc, std::move(Attrs)});
+    Bindings.push_back({II, Loc, std::move(Attrs), EllipsisLoc});
   }
 
   if (Tok.isNot(tok::r_square))
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 4001c4d263f1d2..ddb677dc1e70f2 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -9780,6 +9780,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
   QualType R = TInfo->getType();
 
   assert(R->isFunctionType());
+  bool NeedsExpansion = false;
+
   if (R.getCanonicalType()->castAs<FunctionType>()->getCmseNSCallAttr())
     Diag(D.getIdentifierLoc(), diag::err_function_decl_cmse_ns_call);
 
@@ -10964,6 +10966,14 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
       D.getFunctionDefinitionKind() == FunctionDefinitionKind::Declaration)
     ExternalDeclarations.push_back(NewFD);
 
+  if (NeedsExpansion) {
+    CodeSynthesisContext SynthCtx{};
+    pushCodeSynthesisContext(SynthCtx);
+    NewFD = dyn_cast_or_null<FunctionDecl>(
+        SubstDecl(NewFD, DC, MultiLevelTemplateArgumentList{}));
+    popCodeSynthesisContext();
+  }
+
   return NewFD;
 }
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index c5a72cf812ebc9..adabca19107f2b 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -888,7 +888,15 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
       Previous.clear();
     }
 
-    auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, VarName);
+    QualType QT = Context.DependentTy;
+    if (B.EllipsisLoc.isValid()) {
+      if (!cast<Decl>(DC)->isTemplated())
+        Diag(B.EllipsisLoc, diag::err_pack_outside_template);
+      QT = Context.getPackExpansionType(QT, std::nullopt,
+                                        /*ExpectsPackInType=*/false);
+    }
+
+    auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, B.Name, QT);
 
     ProcessDeclAttributeList(S, BD, *B.Attrs);
 
@@ -951,28 +959,130 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
   return New;
 }
 
+namespace {
+// CheckBindingsCount
+//  - Checks the arity of the structured bindings
+//  - Creates the resolved pack expr if there is
+//    one
+bool CheckBindingsCount(Sema &S, DecompositionDecl *DD, QualType DecompType,
+                        ArrayRef<BindingDecl *> Bindings,
+                        unsigned MemberCount) {
+  auto BindingWithPackItr =
+      std::find_if(Bindings.begin(), Bindings.end(),
+                   [](BindingDecl *D) -> bool { return D->isParameterPack(); });
+  bool HasPack = BindingWithPackItr != Bindings.end();
+  bool IsValid;
+  if (!HasPack) {
+    IsValid = Bindings.size() == MemberCount;
+  } else {
+    // there may not be more members than non-pack bindings
+    IsValid = MemberCount >= Bindings.size() - 1;
+  }
+
+  if (IsValid && HasPack) {
+    TemplateTypeParmDecl *DummyTemplateParam = TemplateTypeParmDecl::Create(
+        S.Context, S.Context.getTranslationUnitDecl(),
+        /*KeyLoc*/ SourceLocation(), /*NameLoc*/ SourceLocation(),
+        /*TemplateDepth*/ 0, /*AutoParameterPosition*/ 0,
+        /*Identifier*/ nullptr, false, /*IsParameterPack*/ true);
+
+    // create the pack expr and assign it to the binding
+    unsigned PackSize = MemberCount - Bindings.size() + 1;
+    QualType PackType = S.Context.getPackExpansionType(
+        QualType(DummyTemplateParam->getTypeForDecl(), 0), PackSize);
+    (*BindingWithPackItr)
+        ->setBinding(PackType,
+                     ResolvedUnexpandedPackExpr::Create(
+                         S.Context, DD->getBeginLoc(), DecompType, PackSize));
+  }
+
+  if (IsValid)
+    return false;
+
+  S.Diag(DD->getLocation(), diag::err_decomp_decl_wrong_number_bindings)
+      << DecompType << (unsigned)Bindings.size() << MemberCount << MemberCount
+      << (MemberCount < Bindings.size());
+  return true;
+}
+
+// BindingInitWalker
+//  - This implements a forward iterating flattened view
+//    of structured bindings that may have a nested pack.
+//    It allows the user to set the init expr for either the
+//    BindingDecl or its ResolvedUnexpandedPackExpr
+struct BindingInitWalker {
+  using BindingItrTy = typename ArrayRef<BindingDecl *>::iterator;
+  using PackExprItrTy = typename MutableArrayRef<Expr *>::iterator;
+  Sema &SemaRef;
+  ArrayRef<BindingDecl *> Bindings;
+  ResolvedUnexpandedPackExpr *PackExpr = nullptr;
+  MutableArrayRef<Expr *> PackExprNodes;
+  BindingItrTy BindingItr;
+  PackExprItrTy PackExprItr;
+
+  BindingInitWalker(Sema &S, ArrayRef<BindingDecl *> Bs)
+      : SemaRef(S), Bindings(Bs), BindingItr(Bindings.begin()) {}
+
+  BindingDecl *get() { return *BindingItr; }
+
+  void commitAndAdvance(QualType T, Expr *E) {
+    BindingDecl *B = *BindingItr;
+    bool IsPackExpr =
+        isa_and_nonnull<ResolvedUnexpandedPackExpr>(B->getBinding());
+    if (IsPackExpr && !PackExpr) {
+      PackExpr = cast<ResolvedUnexpandedPackExpr>(B->getBinding());
+      PackExprNodes =
+          llvm::MutableArrayRef(PackExpr->getExprs(), PackExpr->getNumExprs());
+      PackExprItr = PackExprNodes.begin();
+    }
+
+    if (IsPackExpr) {
+      // Build a nested BindingDecl with a DeclRefExpr
+      auto *NestedBD =
+          BindingDecl::Create(SemaRef.Context, B->getDeclContext(),
+                              B->getLocation(), B->getIdentifier(), T);
+
+      NestedBD->setBinding(T, E);
+      NestedBD->setDecomposedDecl(nullptr);
+      auto *DE = SemaRef.BuildDeclRefExpr(NestedBD, T.getNonReferenceType(),
+                                          VK_LValue, B->getLocation());
+      *PackExprItr = DE;
+      if (++PackExprItr != PackExprNodes.end())
+        return;
+      // If we hit the end of the pack exprs then
+      // continue to advance BindingItr
+    } else {
+      (*BindingItr)->setBinding(T, E);
+    }
+
+    ++BindingItr;
+  }
+};
+} // namespace
+
 static bool checkSimpleDecomposition(
     Sema &S, ArrayRef<BindingDecl *> Bindings, ValueDecl *Src,
-    QualType DecompType, const llvm::APSInt &NumElems, QualType ElemType,
+    QualType DecompType, const llvm::APSInt &NumElemsAPS, QualType ElemType,
     llvm::function_ref<ExprResult(SourceLocation, Expr *, unsigned)> GetInit) {
-  if ((int64_t)Bindings.size() != NumElems) {
-    S.Diag(Src->getLocation(), diag::err_decomp_decl_wrong_number_bindings)
-        << DecompType << (unsigned)Bindings.size()
-        << (unsigned)NumElems.getLimitedValue(UINT_MAX)
-        << toString(NumElems, 10) << (NumElems < Bindings.size());
+  unsigned NumElems = (unsigned)NumElemsAPS.getLimitedValue(UINT_MAX);
+
+  if (CheckBindingsCount(S, cast<DecompositionDecl>(Src), DecompType, Bindings,
+                         NumElems)) {
+
     return true;
   }
 
-  unsigned I = 0;
-  for (auto *B : Bindings) {
+  auto Walker = BindingInitWalker(S, Bindings);
+  for (unsigned I = 0; I < NumElems; I++) {
+    BindingDecl *B = Walker.get();
     SourceLocation Loc = B->getLocation();
     ExprResult E = S.BuildDeclRefExpr(Src, DecompType, VK_LValue, Loc);
     if (E.isInvalid())
       return true;
-    E = GetInit(Loc, E.get(), I++);
+    E = GetInit(Loc, E.get(), I);
     if (E.isInvalid())
       return true;
-    B->setBinding(ElemType, E.get());
+    Walker.commitAndAdvance(ElemType, E.get());
   }
 
   return false;
@@ -1210,11 +1320,9 @@ static bool checkTupleLikeDecomposition(Sema &S,
                                         ArrayRef<BindingDecl *> Bindings,
                                         VarDecl *Src, QualType DecompType,
                                         const llvm::APSInt &TupleSize) {
-  if ((int64_t)Bindings.size() != TupleSize) {
-    S.Diag(Src->getLocation(), diag::err_decomp_decl_wrong_number_bindings)
-        << DecompType << (unsigned)Bindings.size()
-        << (unsigned)TupleSize.getLimitedValue(UINT_MAX)
-        << toString(TupleSize, 10) << (TupleSize < Bindings.size());
+  unsigned NumElems = (unsigned)TupleSize.getLimitedValue(UINT_MAX);
+  if (CheckBindingsCount(S, cast<DecompositionDecl>(Src), DecompType, Bindings,
+                         NumElems)) {
     return true;
   }
 
@@ -1249,8 +1357,9 @@ static bool checkTupleLikeDecomposition(Sema &S,
     }
   }
 
-  unsigned I = 0;
-  for (auto *B : Bindings) {
+  auto Walker = BindingInitWalker(S, Bindings);
+  for (unsigned I = 0; I < NumElems; I++) {
+    BindingDecl *B = Walker.get();
     InitializingBinding InitContext(S, B);
     SourceLocation Loc = B->getLocation();
 
@@ -1336,8 +1445,7 @@ static bool checkTupleLikeDecomposition(Sema &S,
     if (E.isInvalid())
       return true;
 
-    B->setBinding(T, E.get());
-    I++;
+    Walker.commitAndAdvance(T, E.get());
   }
 
   return false;
@@ -1433,20 +1541,18 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings,
   QualType BaseType = S.Context.getQualifiedType(S.Context.getRecordType(RD),
                                                  DecompType.getQualifiers());
 
-  auto DiagnoseBadNumberOfBindings = [&]() -> bool {
-    unsigned NumFields = llvm::count_if(
-        RD->fields(), [](FieldDecl *FD) { return !FD->isUnnamedBitField(); });
-    assert(Bindings.size() != NumFields);
-    S.Diag(Src->getLocation(), diag::err_decomp_decl_wrong_number_bindings)
-        << DecompType << (unsigned)Bindings.size() << NumFields << NumFields
-        << (NumFields < Bindings.size());
+  unsigned NumFields = llvm::count_if(
+      RD->fields(), [](FieldDecl *FD) { return !FD->isUnnamedBitField(); });
+  if (CheckBindingsCount(S, cast<DecompositionDecl>(Src), DecompType, Bindings,
+                         NumFields)) {
     return true;
-  };
+  }
+
+  auto Walker = BindingInitWalker(S, Bindings);
 
   //   all of E's non-static data members shall be [...] well-formed
   //   when named as e.name in the context of the structured binding,
   //   E shall not have an anonymous union member, ...
-  unsigned I = 0;
   for (auto *FD : RD->fields()) {
     if (FD->isUnnamedBitField())
       continue;
@@ -1471,9 +1577,7 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings,
     }
 
     // We have a real field to bind.
-    if (I >= Bindings.size())
-      return DiagnoseBadNumberOfBindings();
-    auto *B = Bindings[I++];
+    BindingDecl *B = Walker.get();
     SourceLocation Loc = B->getLocation();
 
     // The field must be accessible in the context of the structured binding.
@@ -1508,23 +1612,52 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings,
     Qualifiers Q = DecompType.getQualifiers();
     if (FD->isMutable())
       Q.removeConst();
-    B->setBinding(S.BuildQualifiedType(FD->getType(), Loc, Q), E.get());
+    Walker.commitAndAdvance(S.BuildQualifiedType(FD->getType(), Loc, Q),
+                            E.get());
   }
 
-  if (I != Bindings.size())
-    return DiagnoseBadNumberOfBindings();
-
   return false;
 }
 
+unsigned Sema::GetDecompositionElementCount(QualType DecompType) {
+  assert(!DecompType->isDependentType() && "expecting non-dependent type");
+  SourceLocation Loc = SourceLocation(); // FIXME
+  DecompType = DecompType.getNonReferenceType();
+  if (auto *CAT = Context.getAsConstantArrayType(DecompType))
+    return CAT->getSize().getLimitedValue(UINT_MAX);
+  if (auto *VT = DecompType->getAs<VectorType>())
+    return VT->getNumElements();
+  if (auto *CT = DecompType->getAs<ComplexType>())
+    return 2;
+  llvm::APSInt TupleSize(32);
+  if (IsTupleLike TL = isTupleLike(*this, Loc, DecompType, TupleSize);
+      TL == IsTupleLike::TupleLike)
+    return (unsigned)TupleSize.getLimitedValue(UINT_MAX);
+
+  if (CXXRecordDecl *RD = DecompType->getAsCXXRecordDecl();
+      RD && !RD->isUnion()) {
+    CXXCastPath BasePath;
+    DeclAccessPair BasePair =
+        findDecomposableBaseClass(*this, Loc, RD, BasePath);
+    RD = cast_or_null<CXXRecordDecl>(BasePair.getDecl());
+    if (RD)
+      return llvm::count_if(
+          RD->fields(), [](FieldDecl *FD) { return !FD->isUnnamedBitField(); });
+  }
+
+  llvm_unreachable("unknown type for decomposition");
+}
+
 void Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) {
   QualType DecompType = DD->getType();
 
   // If the type of the decomposition is dependent, then so is the type of
   // each binding.
   if (DecompType->isDependentType()) {
-    for (auto *B : DD->bindings())
-      B->setType(Context.DependentTy);
+    for (auto *B : DD->bindings()) {
+      if (B->getType().isNull())
+        B->setType(Context.DependentTy);
+    }
     return;
   }
 
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index ac3666394d0e86..079ba597ed0cf1 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1068,9 +1068,9 @@ static CanThrowResult canVarDeclThrow(Sema &Self, const VarDecl *VD) {
 
   // If this is a decomposition declaration, bindings might throw.
   if (auto *DD = dyn_cast<DecompositionDecl>(VD))
-    for (auto *B : DD->bindings())
-      if (auto *HD = B->getHoldingVar())
-        CT = mergeCanThrow(CT, canVarDeclThrow(Self, HD));
+    DD->VisitHoldingVars([&](VarDecl *HD) {
+      CT = mergeCanThrow(CT, canVarDeclThrow(Self, HD));
+    });
 
   return CT;
 }
@@ -1278,6 +1278,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Expr::ConvertVectorExprClass:
   case Expr::VAArgExprClass:
   case Expr::CXXParenListInitExprClass:
+  case Expr::ResolvedUnexpandedPackExprClass:
     return canSubStmtsThrow(*this, S);
 
   case Expr::CompoundLiteralExprClass:
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index a67c0b2b367d1a..e008a26ff7f062 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1526,6 +1526,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
     // - a member of a templated entity,
     // - an enumerator for an enumeration that is a templated entity, or
     // - the closure type of a lambda-expression ([expr.prim.lambda.closure])
+    // - an entity defined with an implicit template region
     // appearing in the declaration of a templated entity. [Note 6: A local
     // class, a local or block variable, or a friend function defined in a
     // templated entity is a templated entity.  — end note]
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index d9149f7ee40bbf..2d7ba56657a0c2 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -755,7 +755,7 @@ bool Sema::checkMustTailAttr(const Stmt *St, const Attr &MTA) {
     return true;
   };
 
-  const auto *CallerDecl = dyn_cast<FunctionDecl>(CurContext);
+  const auto *CallerDecl = getCurFunctionDecl();
 
   // Find caller function signature.
   if (!CallerDecl) {
@@ -1010,8 +1010,7 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc,
     bool Immediate = ExprEvalContexts.back().Context ==
                      ExpressionEvaluationContext::ImmediateFunctionContext;
     if (CurContext->isFunctionOrMethod()) {
-      const auto *FD =
-          dyn_cast<FunctionDecl>(Decl::castFromDeclContext(CurContext));
+      const auto *FD = getCurFunctionDecl();
       if (FD && FD->isImmediateFunction())
         Immediate = true;
     }
@@ -2670,8 +2669,11 @@ StmtResult Sema::BuildCXXForRangeStmt(
     // them in properly when we instantiate the loop.
     if (!LoopVar->isInvalidDecl() && Kind != BFRK_Check) {
       if (auto *DD = dyn_cast<DecompositionDecl>(LoopVar))
-        for (auto *Binding : DD->bindings())
-          Binding->setType(Context.DependentTy);
+        for (auto *Binding : DD->bindings()) {
+          if (!Binding->isParameterPack()) {
+            Binding->setType(Context.DependentTy);
+          }
+        }
       LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType()));
     }
   } else if (!BeginDeclStmt.get()) {
@@ -3916,7 +3918,7 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
   // deduction.
   if (getLangOpts().CPlusPlus14) {
     if (AutoType *AT = FnRetType->getContainedAutoType()) {
-      FunctionDecl *FD = cast<FunctionDecl>(CurContext);
+      FunctionDecl *FD = getCurFunctionDecl();
       // If we've already decided this function is invalid, e.g. because
       // we saw a `return` whose expression had an error, don't keep
       // trying to deduce its return type.
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 5e7a3c8484c88f..ef19d0d52213b2 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -928,9 +928,10 @@ static TemplateArgumentLoc translateTemplateArgument(Sema &SemaRef,
 
 void Sema::translateTemplateArguments(const ASTTemplateArgsPtr &TemplateArgsIn,
                                       TemplateArgumentListInfo &TemplateArgs) {
- for (unsigned I = 0, Last = TemplateArgsIn.size(); I != Last; ++I)
-   TemplateArgs.addArgument(translateTemplateArgument(*this,
-                                                      TemplateArgsIn[I]));
+  for (unsigned I = 0, Last = TemplateArgsIn.size(); I != Last; ++I) {
+    TemplateArgs.addArgument(
+        translateTemplateArgument(*this, TemplateArgsIn[I]));
+  }
 }
 
 static void maybeDiagnoseTemplateParameterShadow(Sema &SemaRef, Scope *S,
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index fb0f38df62a744..ca11a1b7c910fa 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -805,7 +805,6 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
           SemaRef, CodeSynthesisContext::BuildingDeductionGuides,
           PointOfInstantiation, InstantiationRange, Entity) {}
 
-
 void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) {
   Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext;
   InNonInstantiationSFINAEContext = false;
@@ -1569,6 +1568,10 @@ namespace {
     /// pack.
     ExprResult TransformFunctionParmPackExpr(FunctionParmPackExpr *E);
 
+    // Transform a ResolvedUnexpandedPackExpr
+    ExprResult
+    TransformResolvedUnexpandedPackExpr(ResolvedUnexpandedPackExpr *E);
+
     QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
                                         FunctionProtoTypeLoc TL) {
       // Call the base version; it will forward to our overridden version below.
@@ -1851,7 +1854,8 @@ bool TemplateInstantiator::AlreadyTransformed(QualType T) {
   if (T.isNull())
     return true;
 
-  if (T->isInstantiationDependentType() || T->isVariablyModifiedType())
+  if (T->isInstantiationDependentType() || T->isVariablyModifiedType() ||
+      T->containsUnexpandedParameterPack())
     return false;
 
   getSema().MarkDeclarationsReferencedInType(Loc, T);
@@ -2484,6 +2488,14 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) {
     if (PD->isParameterPack())
       return TransformFunctionParmPackRefExpr(E, PD);
 
+  if (BindingDecl *BD = dyn_cast<BindingDecl>(D); BD && BD->isParameterPack()) {
+    BD = cast<BindingDecl>(TransformDecl(BD->getLocation(), BD));
+    if (auto *RP =
+            dyn_cast_or_null<ResolvedUnexpandedPackExpr>(BD->getBinding())) {
+      return TransformResolvedUnexpandedPackExpr(RP);
+    }
+  }
+
   return inherited::TransformDeclRefExpr(E);
 }
 
@@ -2648,6 +2660,30 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
   return Result;
 }
 
+ExprResult TemplateInstantiator::TransformResolvedUnexpandedPackExpr(
+    ResolvedUnexpandedPackExpr *E) {
+  if (getSema().ArgumentPackSubstitutionIndex != -1) {
+    assert(static_cast<unsigned>(getSema().ArgumentPackSubstitutionIndex) <
+               E->getNumExprs() &&
+           "ArgumentPackSubstitutionIndex is out of range");
+    return TransformExpr(
+        E->getExpansion(getSema().ArgumentPackSubstitutionIndex));
+  }
+
+  if (!AlwaysRebuild())
+    return E;
+
+  SmallVector<Expr *, 12> NewExprs;
+  if (TransformExprs(E->getExprs(), E->getNumExprs(),
+                     /*IsCall=*/false, NewExprs))
+    return ExprError();
+
+  // NOTE: The type is just a superficial PackExpansionType
+  //       that needs no substitution.
+  return ResolvedUnexpandedPackExpr::Create(SemaRef.Context, E->getBeginLoc(),
+                                            E->getType(), NewExprs);
+}
+
 QualType TemplateInstantiator::TransformSubstTemplateTypeParmPackType(
     TypeLocBuilder &TLB, SubstTemplateTypeParmPackTypeLoc TL,
     bool SuppressObjCLifetime) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index e058afe81da589..68d090c3a558ad 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1166,17 +1166,24 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
 
 Decl *TemplateDeclInstantiator::VisitBindingDecl(BindingDecl *D) {
   auto *NewBD = BindingDecl::Create(SemaRef.Context, Owner, D->getLocation(),
-                                    D->getIdentifier());
+                                    D->getIdentifier(), D->getType());
   NewBD->setReferenced(D->isReferenced());
   SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, NewBD);
+
   return NewBD;
 }
 
 Decl *TemplateDeclInstantiator::VisitDecompositionDecl(DecompositionDecl *D) {
   // Transform the bindings first.
+  // The transformed DD will have all of the concrete BindingDecls.
   SmallVector<BindingDecl*, 16> NewBindings;
-  for (auto *OldBD : D->bindings())
+  ResolvedUnexpandedPackExpr *OldResolvedPack = nullptr;
+  for (auto *OldBD : D->bindings()) {
+    Expr *BindingExpr = OldBD->getBinding();
+    if (auto *RP = dyn_cast_or_null<ResolvedUnexpandedPackExpr>(BindingExpr))
+      OldResolvedPack = RP;
     NewBindings.push_back(cast<BindingDecl>(VisitBindingDecl(OldBD)));
+  }
   ArrayRef<BindingDecl*> NewBindingArray = NewBindings;
 
   auto *NewDD = cast_or_null<DecompositionDecl>(
@@ -1186,6 +1193,27 @@ Decl *TemplateDeclInstantiator::VisitDecompositionDecl(DecompositionDecl *D) {
     for (auto *NewBD : NewBindings)
       NewBD->setInvalidDecl();
 
+  if (OldResolvedPack) {
+    // Mark the holding vars (if any) in the pack as instantiated since
+    // they are created implicitly.
+    auto Bindings = NewDD->bindings();
+    auto BPack = std::find_if(
+        Bindings.begin(), Bindings.end(),
+        [](BindingDecl *D) -> bool { return D->isParameterPack(); });
+    auto *NewResolvedPack =
+        cast<ResolvedUnexpandedPackExpr>((*BPack)->getBinding());
+    Expr **OldExprs = OldResolvedPack->getExprs();
+    Expr **NewExprs = NewResolvedPack->getExprs();
+    for (unsigned I = 0; I < OldResolvedPack->getNumExprs(); I++) {
+      DeclRefExpr *OldDRE = cast<DeclRefExpr>(OldExprs[I]);
+      BindingDecl *OldNestedBD = cast<BindingDecl>(OldDRE->getDecl());
+      DeclRefExpr *NewDRE = cast<DeclRefExpr>(NewExprs[I]);
+      BindingDecl *NewNestedBD = cast<BindingDecl>(NewDRE->getDecl());
+      SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldNestedBD,
+                                                           NewNestedBD);
+    }
+  }
+
   return NewDD;
 }
 
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index c8452db6bc9014..c4e6e090705104 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -19,6 +19,7 @@
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/Template.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/SaveAndRestore.h"
 #include <optional>
 
@@ -50,16 +51,28 @@ class CollectUnexpandedParameterPacksVisitor
         auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr;
         if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
           return;
-      } else if (getDepthAndIndex(ND).first >= DepthLimit)
+      } else if (ND->isTemplateParameterPack() &&
+                 getDepthAndIndex(ND).first >= DepthLimit) {
         return;
+      } else if (auto *BD = dyn_cast<BindingDecl>(ND)) {
+        Expr *E = BD->getBinding();
+        if (auto *RP = dyn_cast_or_null<ResolvedUnexpandedPackExpr>(E)) {
+          addUnexpanded(RP);
+          return;
+        }
+      }
 
       Unexpanded.push_back({ND, Loc});
     }
+
     void addUnexpanded(const TemplateTypeParmType *T,
                        SourceLocation Loc = SourceLocation()) {
       if (T->getDepth() < DepthLimit)
         Unexpanded.push_back({T, Loc});
     }
+    void addUnexpanded(ResolvedUnexpandedPackExpr *E) {
+      Unexpanded.push_back({E, E->getBeginLoc()});
+    }
 
   public:
     explicit CollectUnexpandedParameterPacksVisitor(
@@ -103,6 +116,12 @@ class CollectUnexpandedParameterPacksVisitor
       return true;
     }
 
+    bool
+    VisitResolvedUnexpandedPackExpr(ResolvedUnexpandedPackExpr *E) override {
+      addUnexpanded(E);
+      return true;
+    }
+
     /// Record occurrences of template template parameter packs.
     bool TraverseTemplateName(TemplateName Template) override {
       if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>(
@@ -210,6 +229,10 @@ class CollectUnexpandedParameterPacksVisitor
     bool TraversePackIndexingTypeLoc(PackIndexingTypeLoc TL) override {
       return DynamicRecursiveASTVisitor::TraverseStmt(TL.getIndexExpr());
     }
+    bool
+    TraverseResolvedUnexpandedPackExpr(ResolvedUnexpandedPackExpr *E) override {
+      return true;
+    }
 
     ///@}
 
@@ -422,8 +445,8 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
     if (const TemplateTypeParmType *TTP
           = Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>())
       Name = TTP->getIdentifier();
-    else
-      Name = cast<NamedDecl *>(Unexpanded[I].first)->getIdentifier();
+    else if (NamedDecl *ND = Unexpanded[I].first.dyn_cast<NamedDecl *>())
+      Name = ND->getIdentifier();
 
     if (Name && NamesKnown.insert(Name).second)
       Names.push_back(Name);
@@ -757,23 +780,42 @@ bool Sema::CheckParameterPacksForExpansion(
   bool HaveFirstPack = false;
   std::optional<unsigned> NumPartialExpansions;
   SourceLocation PartiallySubstitutedPackLoc;
+  typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
 
   for (UnexpandedParameterPack ParmPack : Unexpanded) {
     // Compute the depth and index for this parameter pack.
     unsigned Depth = 0, Index = 0;
     IdentifierInfo *Name;
     bool IsVarDeclPack = false;
+    ResolvedUnexpandedPackExpr *ResolvedPack = nullptr;
 
     if (const TemplateTypeParmType *TTP =
             ParmPack.first.dyn_cast<const TemplateTypeParmType *>()) {
       Depth = TTP->getDepth();
       Index = TTP->getIndex();
       Name = TTP->getIdentifier();
+    } else if (auto *RP =
+                   ParmPack.first.dyn_cast<ResolvedUnexpandedPackExpr *>()) {
+      ResolvedPack = RP;
     } else {
       NamedDecl *ND = cast<NamedDecl *>(ParmPack.first);
       if (isa<VarDecl>(ND))
         IsVarDeclPack = true;
-      else
+      else if (isa<BindingDecl>(ND)) {
+        // find the instantiated BindingDecl and check it for a resolved pack
+        llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
+            CurrentInstantiationScope->findInstantiationOf(ND);
+        if (Decl *B = Instantiation->dyn_cast<Decl *>()) {
+          Expr *BindingExpr = cast<BindingDecl>(B)->getBinding();
+          if (auto *RP = dyn_cast<ResolvedUnexpandedPackExpr>(BindingExpr)) {
+            ResolvedPack = RP;
+          }
+        }
+        if (!ResolvedPack) {
+          ShouldExpand = false;
+          continue;
+        }
+      } else
         std::tie(Depth, Index) = getDepthAndIndex(ND);
 
       Name = ND->getIdentifier();
@@ -797,6 +839,8 @@ bool Sema::CheckParameterPacksForExpansion(
         ShouldExpand = false;
         continue;
       }
+    } else if (ResolvedPack) {
+      NewPackSize = ResolvedPack->getNumExprs();
     } else {
       // If we don't have a template argument at this depth/index, then we
       // cannot expand the pack expansion. Make a note of this, but we still
@@ -833,7 +877,7 @@ bool Sema::CheckParameterPacksForExpansion(
     //   Template argument deduction can extend the sequence of template
     //   arguments corresponding to a template parameter pack, even when the
     //   sequence contains explicitly specified template arguments.
-    if (!IsVarDeclPack && CurrentInstantiationScope) {
+    if (!IsVarDeclPack && !ResolvedPack && CurrentInstantiationScope) {
       if (NamedDecl *PartialPack =
               CurrentInstantiationScope->getPartiallySubstitutedPack()) {
         unsigned PartialDepth, PartialIndex;
@@ -939,6 +983,12 @@ std::optional<unsigned> Sema::getNumArgumentsInExpansionFromUnexpanded(
             Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>()) {
       Depth = TTP->getDepth();
       Index = TTP->getIndex();
+    } else if (auto *PE = Unexpanded[I]
+                              .first.dyn_cast<ResolvedUnexpandedPackExpr *>()) {
+      unsigned Size = PE->getNumExprs();
+      assert((!Result || *Result == Size) && "inconsistent pack sizes");
+      Result = Size;
+      continue;
     } else {
       NamedDecl *ND = cast<NamedDecl *>(Unexpanded[I].first);
       if (isa<VarDecl>(ND)) {
@@ -1167,8 +1217,13 @@ ExprResult Sema::ActOnSizeofParameterPackExpr(Scope *S,
 
   MarkAnyDeclReferenced(OpLoc, ParameterPack, true);
 
+  std::optional<unsigned> Length;
+  if (auto *RP = ResolvedUnexpandedPackExpr::getFromDecl(ParameterPack)) {
+    Length = RP->getNumExprs();
+  }
+
   return SizeOfPackExpr::Create(Context, OpLoc, ParameterPack, NameLoc,
-                                RParenLoc);
+                                RParenLoc, Length);
 }
 
 static bool isParameterPack(Expr *PackExpression) {
@@ -1439,8 +1494,10 @@ ExprResult Sema::ActOnCXXFoldExpr(Scope *S, SourceLocation LParenLoc, Expr *LHS,
     }
   }
 
-  return BuildCXXFoldExpr(ULE, LParenLoc, LHS, Opc, EllipsisLoc, RHS, RParenLoc,
-                          std::nullopt);
+  ExprResult Result = BuildCXXFoldExpr(ULE, LParenLoc, LHS, Opc, EllipsisLoc,
+                                       RHS, RParenLoc, std::nullopt);
+
+  return Result;
 }
 
 ExprResult Sema::BuildCXXFoldExpr(UnresolvedLookupExpr *Callee,
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 5d43d98ce49e46..7af74c6d469a55 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -15991,6 +15991,13 @@ TreeTransform<Derived>::TransformFunctionParmPackExpr(FunctionParmPackExpr *E) {
   return E;
 }
 
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformResolvedUnexpandedPackExpr(
+    ResolvedUnexpandedPackExpr *E) {
+  // Default behavior is to do nothing with this transformation.
+  return E;
+}
+
 template<typename Derived>
 ExprResult
 TreeTransform<Derived>::TransformMaterializeTemporaryExpr(
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 9e8cf19a6f0f72..f1ed2d81676bf2 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2201,6 +2201,11 @@ void ASTStmtReader::VisitPackIndexingExpr(PackIndexingExpr *E) {
     Exprs[I] = Record.readExpr();
 }
 
+void ASTStmtReader::VisitResolvedUnexpandedPackExpr(
+    ResolvedUnexpandedPackExpr *E) {
+  VisitExpr(E);
+}
+
 void ASTStmtReader::VisitSubstNonTypeTemplateParmExpr(
                                               SubstNonTypeTemplateParmExpr *E) {
   VisitExpr(E);
@@ -4270,6 +4275,12 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
           /*TransformedExprs=*/Record[ASTStmtReader::NumExprFields]);
       break;
 
+    case EXPR_RESOLVED_UNEXPANDED_PACK:
+      S = ResolvedUnexpandedPackExpr::CreateDeserialized(
+          Context,
+          /*NumExprs=*/Record[ASTStmtReader::NumExprFields]);
+      break;
+
     case EXPR_SUBST_NON_TYPE_TEMPLATE_PARM:
       S = new (Context) SubstNonTypeTemplateParmExpr(Empty);
       break;
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 4a6027943072c0..2b531659f1920f 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -874,6 +874,7 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream,
   RECORD(EXPR_PACK_EXPANSION);
   RECORD(EXPR_SIZEOF_PACK);
   RECORD(EXPR_PACK_INDEXING);
+  RECORD(EXPR_RESOLVED_UNEXPANDED_PACK);
   RECORD(EXPR_SUBST_NON_TYPE_TEMPLATE_PARM);
   RECORD(EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK);
   RECORD(EXPR_FUNCTION_PARM_PACK);
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 1d42b43c3e2ca0..26967160d40b4e 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2202,6 +2202,17 @@ void ASTStmtWriter::VisitPackIndexingExpr(PackIndexingExpr *E) {
   Code = serialization::EXPR_PACK_INDEXING;
 }
 
+void ASTStmtWriter::VisitResolvedUnexpandedPackExpr(
+    ResolvedUnexpandedPackExpr *E) {
+  VisitExpr(E);
+  Record.push_back(E->getNumExprs());
+  Record.AddSourceLocation(E->getBeginLoc());
+  auto SubExprs = llvm::ArrayRef(E->getExprs(), E->getNumExprs());
+  for (Expr *Sub : SubExprs)
+    Record.AddStmt(Sub);
+  Code = serialization::EXPR_RESOLVED_UNEXPANDED_PACK;
+}
+
 void ASTStmtWriter::VisitSubstNonTypeTemplateParmExpr(
                                               SubstNonTypeTemplateParmExpr *E) {
   VisitExpr(E);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index db385e891e762f..312c7870948cd9 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1743,6 +1743,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::DependentCoawaitExprClass:
     case Stmt::CoreturnStmtClass:
     case Stmt::CoyieldExprClass:
+    case Stmt::ResolvedUnexpandedPackExprClass:
     case Stmt::SEHTryStmtClass:
     case Stmt::SEHExceptStmtClass:
     case Stmt::SEHLeaveStmtClass:
diff --git a/clang/test/Parser/cxx2c-binding-pack.cpp b/clang/test/Parser/cxx2c-binding-pack.cpp
new file mode 100644
index 00000000000000..ab8bdd54935f91
--- /dev/null
+++ b/clang/test/Parser/cxx2c-binding-pack.cpp
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -std=c++2c -verify -fsyntax-only %s
+
+template <unsigned N>
+void decompose_array() {
+  int arr[4] = {1, 2, 3, 5};
+  auto [x, ...rest, ...more_rest] = arr; // expected-error{{multiple ellipses in structured binding declaration}}
+}
diff --git a/clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp b/clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp
new file mode 100644
index 00000000000000..9c02083cf88f7e
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++2c %s -verify
+
+void decompose_array() {
+  int arr[4] = {1, 2, 3, 6};
+  auto [x, ...rest, y] = arr; // expected-error{{pack declaration outside of template}}
+}
+
+template <unsigned N>
+void decompose_array_2() {
+  int arr[4] = {1, 2, 3, N};
+  auto [x, ...rest, ...y] = arr; // expected-error{{multiple ellipses in structured binding declaration}}
+}
diff --git a/clang/test/SemaCXX/cxx2c-binding-pack.cpp b/clang/test/SemaCXX/cxx2c-binding-pack.cpp
new file mode 100644
index 00000000000000..ee9466e3b12c70
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-binding-pack.cpp
@@ -0,0 +1,82 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++2b %s -verify
+// expected-no-diagnostics
+
+template <typename T>
+struct type_ { };
+
+template <typename ...T>
+auto sum(T... t) { return (t + ...); }
+
+struct my_struct {
+	int a;
+	int b;
+	int c;
+  int d;
+};
+
+struct fake_tuple {
+  int arr[4] = {1, 2, 3, 6};
+
+  template <unsigned i>
+  int get() {
+    return arr[i];
+  }
+};
+
+namespace std {
+  template <typename T>
+  struct tuple_size;
+  template <unsigned i, typename T>
+  struct tuple_element;
+
+  template <>
+  struct tuple_size<fake_tuple> {
+    static constexpr unsigned value = 4;
+  };
+
+  template <unsigned i>
+  struct tuple_element<i, fake_tuple> {
+    using type = int;
+  };
+}
+
+
+template <typename T>
+void decompose_tuple() {
+  auto tup = T{{1, 2, 3, 6}};
+  auto&& [x, ...rest, y] = tup;
+
+  ((void)type_<int>(type_<decltype(rest)>{}), ...);
+
+  T arrtup[2] = {T{{1, 2, 3, 6}},
+                 T{{7, 9, 10, 11}}};
+  int sum = 0;
+  for (auto [...xs] : arrtup) {
+    sum += (xs + ...);
+  }
+}
+
+template <typename T>
+void decompose_struct() {
+  T obj{1, 2, 3, 6};
+  auto [x, ...rest, y] = obj;
+}
+
+template <typename T>
+void decompose_array() {
+  // previously unable to use non-dependent array here
+  // Fixes https://bugs.llvm.org/show_bug.cgi?id=45964
+  int arr[4] = {1, 2, 3, 6};
+  auto [x, ...rest, y] = arr;
+
+  static_assert(sizeof...(rest) == 2);
+  int size = sizeof...(rest);
+  T arr2[sizeof...(rest)] = {rest...};
+  auto [...pack] = arr2;
+}
+
+int main() {
+  decompose_array<int>();
+  decompose_tuple<fake_tuple>();
+  decompose_struct<my_struct>();
+}
diff --git a/clang/test/SemaCXX/typo-correction-crash.cpp b/clang/test/SemaCXX/typo-correction-crash.cpp
index 2a77c9df505e8f..9ba2d92fa8bd79 100644
--- a/clang/test/SemaCXX/typo-correction-crash.cpp
+++ b/clang/test/SemaCXX/typo-correction-crash.cpp
@@ -32,7 +32,8 @@ FooRecord::NestedNamespace::type x; // expected-error {{no member named 'NestedN
 
 void cast_expr(int g) { +int(n)(g); } // expected-error {{undeclared identifier 'n'}}
 
-void bind() { for (const auto& [test,_] : _test_) { }; } // expected-error {{undeclared identifier '_test_'}}
+void bind() { for (const auto& [test,_] : _test_) { }; } // expected-error {{undeclared identifier '_test_'}} \
+                                                         // expected-note {{'test' declared here}}
 
 namespace NoCrash {
 class S {
diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
index f56e77b42f9d73..694d86a2ce9aec 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -338,6 +338,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
   case Stmt::EmbedExprClass:
   case Stmt::HLSLOutArgExprClass:
   case Stmt::OpenACCAsteriskSizeExprClass:
+  case Stmt::ResolvedUnexpandedPackExprClass:
     K = CXCursor_UnexposedExpr;
     break;
 

>From 116aff1e444b6bfb0d61bcf8ece24303808db0fe Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Thu, 2 Jan 2025 14:13:49 -0800
Subject: [PATCH 02/14] [Clang][P1061] Improve visiting HoldingVars; Improve
 parsing diagnostics of binding packs

---
 clang/include/clang/AST/DeclCXX.h             |  3 +-
 clang/include/clang/AST/ExprCXX.h             |  2 ++
 .../clang/Basic/DiagnosticParseKinds.td       |  6 ++--
 clang/lib/AST/DeclBase.cpp                    |  4 +--
 clang/lib/AST/DeclCXX.cpp                     | 35 ++++---------------
 clang/lib/AST/Expr.cpp                        |  6 +---
 clang/lib/AST/ExprCXX.cpp                     |  9 ++---
 clang/lib/AST/ExprClassification.cpp          |  2 +-
 clang/lib/Parse/ParseDecl.cpp                 | 25 ++++++++-----
 clang/lib/Sema/SemaDecl.cpp                   |  9 -----
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |  2 +-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  4 +--
 clang/lib/Sema/SemaTemplateVariadic.cpp       |  2 +-
 clang/test/Parser/cxx2c-binding-pack.cpp      |  6 +++-
 .../cxx2c-binding-pack-nontemplate.cpp        | 12 +++----
 clang/test/SemaCXX/cxx2c-binding-pack.cpp     |  4 +--
 16 files changed, 51 insertions(+), 80 deletions(-)

diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 12002db17fb3ad..6ac913e8b85b92 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4166,7 +4166,6 @@ class BindingDecl : public ValueDecl {
   void setDecomposedDecl(ValueDecl *Decomposed) { Decomp = Decomposed; }
 
   VarDecl *getHoldingVar() const;
-  static VarDecl *getHoldingVar(Expr *E);
 
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == Decl::Binding; }
@@ -4224,7 +4223,7 @@ class DecompositionDecl final
   /// binding. Only present for user-defined bindings for tuple-like types.
   void VisitHoldingVars(llvm::function_ref<void(VarDecl *)> F) const;
 
-  // Visit the concrete bindings. (workaround)
+  // Visit the concrete bindings.
   void VisitBindings(llvm::function_ref<void(BindingDecl *)> F) const;
 
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 6162c712c90dc3..371da5caf41a5c 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5321,6 +5321,8 @@ class BuiltinBitCastExpr final
   }
 };
 
+// Represents an unexpanded pack where the list of expressions are
+// known. These are used when structured bindings introduce a pack.
 class ResolvedUnexpandedPackExpr final
     : public Expr,
       private llvm::TrailingObjects<ResolvedUnexpandedPackExpr, Stmt *> {
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 5ee6f9ff28c4c8..3ed90a261f7e4a 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1101,9 +1101,11 @@ def err_lambda_capture_multiple_ellipses : Error<
   "multiple ellipses in pack capture">;
 def err_binding_multiple_ellipses : Error<
   "multiple ellipses in structured binding declaration">;
-def warn_cxx2c_binding_pack : Warning<
+def note_previous_ellipsis : Note<
+  "previous ellipsis specified here">;
+def ext_cxx_binding_pack : ExtWarn<
   "structured binding pack is incompatible with C++ standards before C++2c">,
-  DefaultIgnore, InGroup<CXXPre26Compat>;
+  InGroup<CXX26>;
 def err_capture_default_first : Error<
   "capture default must be first">;
 def ext_decl_attrs_on_lambda : ExtWarn<
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 6a5662f9d074e4..29bb3c0b6816b3 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -237,12 +237,10 @@ bool Decl::isTemplateParameterPack() const {
 }
 
 bool Decl::isParameterPack() const {
-  if (isTemplateParameterPack())
-    return true;
   if (const auto *Var = dyn_cast<ValueDecl>(this))
     return Var->isParameterPack();
 
-  return false;
+  return isTemplateParameterPack();
 }
 
 FunctionDecl *Decl::getAsFunction() {
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 371bf5dcf02206..4c9a069f15ce32 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3395,7 +3395,7 @@ VarDecl *ValueDecl::getPotentiallyDecomposedVarDecl() {
   if (auto *Var = llvm::dyn_cast<VarDecl>(this))
     return Var;
   if (auto *BD = llvm::dyn_cast<BindingDecl>(this))
-    return llvm::dyn_cast_or_null<VarDecl>(BD->getDecomposedDecl());
+    return llvm::dyn_cast_if_present<VarDecl>(BD->getDecomposedDecl());
   return nullptr;
 }
 
@@ -3416,16 +3416,7 @@ VarDecl *BindingDecl::getHoldingVar() const {
   Expr *B = getBinding();
   if (!B)
     return nullptr;
-  return getHoldingVar(B);
-}
-
-VarDecl *BindingDecl::getHoldingVar(Expr *E) {
-  auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImplicit());
-  if (!DRE)
-    return nullptr;
-  if (auto *BD = dyn_cast<BindingDecl>(DRE->getDecl())) {
-    DRE = dyn_cast<DeclRefExpr>(BD->getBinding());
-  }
+  auto *DRE = dyn_cast<DeclRefExpr>(B->IgnoreImplicit());
   if (!DRE)
     return nullptr;
 
@@ -3434,27 +3425,13 @@ VarDecl *BindingDecl::getHoldingVar(Expr *E) {
   return VD;
 }
 
+
 void DecompositionDecl::VisitHoldingVars(
     llvm::function_ref<void(VarDecl *)> F) const {
-  for (BindingDecl *B : bindings()) {
-    Expr *BE = B->getBinding();
-    // All BindingDecls will contain holding vars or none will
-    if (!BE)
-      return;
-
-    llvm::ArrayRef<Expr *> Exprs;
-    if (auto *RP = dyn_cast<ResolvedUnexpandedPackExpr>(BE))
-      Exprs = llvm::ArrayRef(RP->getExprs(), RP->getNumExprs());
-    else
-      Exprs = BE;
-
-    for (Expr *E : Exprs) {
-      VarDecl *VD = BindingDecl::getHoldingVar(E);
-      if (!VD)
-        return;
+  VisitBindings([&](BindingDecl* BD) {
+    if (VarDecl* VD = BD->getHoldingVar())
       F(VD);
-    }
-  }
+  });
 }
 
 void DecompositionDecl::VisitBindings(
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 39f02ebf85b2ce..26a227814388ff 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3653,12 +3653,8 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
   case PackIndexingExprClass:
   case HLSLOutArgExprClass:
   case OpenACCAsteriskSizeExprClass:
-    // These never have a side-effect.
-    return false;
-
-  // ResolvedUnexpandedPackExpr is currently only used for
-  // structed bindings which have no side effects
   case ResolvedUnexpandedPackExprClass:
+    // These never have a side-effect.
     return false;
 
   case ConstantExprClass:
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 190af789d306ed..7c710b703f38c1 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1971,6 +1971,9 @@ ResolvedUnexpandedPackExpr::ResolvedUnexpandedPackExpr(SourceLocation BL,
                                                        unsigned NumExprs)
     : Expr(ResolvedUnexpandedPackExprClass, QT, VK_PRValue, OK_Ordinary),
       BeginLoc(BL), NumExprs(NumExprs) {
+  // C++ [temp.dep.expr]p3
+  // An id-expression is type-dependent if it is
+  //    - associated by name lookup with a pack
   setDependence(ExprDependence::TypeValueInstantiation |
                 ExprDependence::UnexpandedPack);
 }
@@ -2007,9 +2010,7 @@ ResolvedUnexpandedPackExpr::Create(ASTContext &Ctx, SourceLocation BL,
 }
 
 ResolvedUnexpandedPackExpr *ResolvedUnexpandedPackExpr::getFromDecl(Decl *D) {
-  // TODO P1858: Extend to VarDecls for P1858
-  if (auto *BD = dyn_cast<BindingDecl>(D)) {
-    return dyn_cast_or_null<ResolvedUnexpandedPackExpr>(BD->getBinding());
-  }
+  if (auto *BD = dyn_cast<BindingDecl>(D))
+    return dyn_cast_if_present<ResolvedUnexpandedPackExpr>(BD->getBinding());
   return nullptr;
 }
diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp
index 29a869bd76ca37..5225c3ca773ad4 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -455,7 +455,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
     if (cast<ResolvedUnexpandedPackExpr>(E)->getNumExprs() > 0)
       return ClassifyInternal(
           Ctx, cast<ResolvedUnexpandedPackExpr>(E)->getExpansion(0));
-    return Cl::CL_PRValue;
+    return Cl::CL_LValue;
   }
 
   case Expr::MaterializeTemporaryExprClass:
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index c298825fb88280..3e480415996996 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -7310,14 +7310,14 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
   // array declarator.
   if (!(Tok.isOneOf(tok::identifier, tok::ellipsis) &&
         NextToken().isOneOf(tok::comma, tok::r_square, tok::kw_alignas,
-                            tok::identifier, tok::l_square)) &&
+                            tok::identifier, tok::l_square, tok::ellipsis)) &&
       !(Tok.is(tok::r_square) &&
         NextToken().isOneOf(tok::equal, tok::l_brace))) {
     PA.Revert();
     return ParseMisplacedBracketDeclarator(D);
   }
 
-  bool HasEllipsis = false;
+  SourceLocation PrevEllipsisLoc;
   SmallVector<DecompositionDeclarator::Binding, 32> Bindings;
   while (Tok.isNot(tok::r_square)) {
     if (!Bindings.empty()) {
@@ -7332,9 +7332,8 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
           Diag(Tok, diag::err_expected_comma_or_rsquare);
         }
 
-        // I don't know why this skipping was here
-        // SkipUntil(tok::r_square, tok::comma, tok::identifier,
-        //          StopAtSemi | StopBeforeMatch);
+        SkipUntil({tok::r_square, tok::comma, tok::identifier, tok::ellipsis},
+                  StopAtSemi | StopBeforeMatch);
         if (Tok.is(tok::comma))
           ConsumeToken();
         else if (Tok.is(tok::r_square))
@@ -7345,17 +7344,18 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
     if (isCXX11AttributeSpecifier())
       DiagnoseAndSkipCXX11Attributes();
 
-    SourceLocation EllipsisLoc = {};
+    SourceLocation EllipsisLoc;
 
     if (Tok.is(tok::ellipsis)) {
       if (!getLangOpts().CPlusPlus26)
-        Diag(Tok, diag::warn_cxx2c_binding_pack);
-      if (HasEllipsis) {
+        Diag(Tok, diag::ext_cxx_binding_pack);
+      if (PrevEllipsisLoc.isValid()) {
         Diag(Tok, diag::err_binding_multiple_ellipses);
+        Diag(PrevEllipsisLoc, diag::note_previous_ellipsis);
         break;
       }
-      HasEllipsis = true;
       EllipsisLoc = Tok.getLocation();
+      PrevEllipsisLoc = EllipsisLoc;
       ConsumeToken();
     }
 
@@ -7368,6 +7368,13 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
     SourceLocation Loc = Tok.getLocation();
     ConsumeToken();
 
+    if (Tok.is(tok::ellipsis) && !PrevEllipsisLoc.isValid()) {
+      DiagnoseMisplacedEllipsis(Tok.getLocation(), Loc,
+                                EllipsisLoc.isValid(), true);
+      EllipsisLoc = Tok.getLocation();
+      ConsumeToken();
+    }
+
     ParsedAttributes Attrs(AttrFactory);
     if (isCXX11AttributeSpecifier()) {
       Diag(Tok, getLangOpts().CPlusPlus26
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index ddb677dc1e70f2..4ba78818f19836 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -9780,7 +9780,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
   QualType R = TInfo->getType();
 
   assert(R->isFunctionType());
-  bool NeedsExpansion = false;
 
   if (R.getCanonicalType()->castAs<FunctionType>()->getCmseNSCallAttr())
     Diag(D.getIdentifierLoc(), diag::err_function_decl_cmse_ns_call);
@@ -10966,14 +10965,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
       D.getFunctionDefinitionKind() == FunctionDefinitionKind::Declaration)
     ExternalDeclarations.push_back(NewFD);
 
-  if (NeedsExpansion) {
-    CodeSynthesisContext SynthCtx{};
-    pushCodeSynthesisContext(SynthCtx);
-    NewFD = dyn_cast_or_null<FunctionDecl>(
-        SubstDecl(NewFD, DC, MultiLevelTemplateArgumentList{}));
-    popCodeSynthesisContext();
-  }
-
   return NewFD;
 }
 
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index ca11a1b7c910fa..f94c22185d399b 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2491,7 +2491,7 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) {
   if (BindingDecl *BD = dyn_cast<BindingDecl>(D); BD && BD->isParameterPack()) {
     BD = cast<BindingDecl>(TransformDecl(BD->getLocation(), BD));
     if (auto *RP =
-            dyn_cast_or_null<ResolvedUnexpandedPackExpr>(BD->getBinding())) {
+            dyn_cast_if_present<ResolvedUnexpandedPackExpr>(BD->getBinding())) {
       return TransformResolvedUnexpandedPackExpr(RP);
     }
   }
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 68d090c3a558ad..4ae29854a58725 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1180,13 +1180,13 @@ Decl *TemplateDeclInstantiator::VisitDecompositionDecl(DecompositionDecl *D) {
   ResolvedUnexpandedPackExpr *OldResolvedPack = nullptr;
   for (auto *OldBD : D->bindings()) {
     Expr *BindingExpr = OldBD->getBinding();
-    if (auto *RP = dyn_cast_or_null<ResolvedUnexpandedPackExpr>(BindingExpr))
+    if (auto *RP = dyn_cast_if_present<ResolvedUnexpandedPackExpr>(BindingExpr))
       OldResolvedPack = RP;
     NewBindings.push_back(cast<BindingDecl>(VisitBindingDecl(OldBD)));
   }
   ArrayRef<BindingDecl*> NewBindingArray = NewBindings;
 
-  auto *NewDD = cast_or_null<DecompositionDecl>(
+  auto *NewDD = cast_if_present<DecompositionDecl>(
       VisitVarDecl(D, /*InstantiatingVarTemplate=*/false, &NewBindingArray));
 
   if (!NewDD || NewDD->isInvalidDecl())
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index c4e6e090705104..94cdc99217d306 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -56,7 +56,7 @@ class CollectUnexpandedParameterPacksVisitor
         return;
       } else if (auto *BD = dyn_cast<BindingDecl>(ND)) {
         Expr *E = BD->getBinding();
-        if (auto *RP = dyn_cast_or_null<ResolvedUnexpandedPackExpr>(E)) {
+        if (auto *RP = dyn_cast_if_present<ResolvedUnexpandedPackExpr>(E)) {
           addUnexpanded(RP);
           return;
         }
diff --git a/clang/test/Parser/cxx2c-binding-pack.cpp b/clang/test/Parser/cxx2c-binding-pack.cpp
index ab8bdd54935f91..400630b6b7d7df 100644
--- a/clang/test/Parser/cxx2c-binding-pack.cpp
+++ b/clang/test/Parser/cxx2c-binding-pack.cpp
@@ -3,5 +3,9 @@
 template <unsigned N>
 void decompose_array() {
   int arr[4] = {1, 2, 3, 5};
-  auto [x, ...rest, ...more_rest] = arr; // expected-error{{multiple ellipses in structured binding declaration}}
+  auto [x, ... // #1
+    rest, ...more_rest] = arr; // expected-error{{multiple ellipses in structured binding declaration}}
+                               // expected-note@#1{{previous ellipsis specified here}}
+                               //
+  auto [y...] = arr; // expected-error{{'...' must immediately precede declared identifier}}
 }
diff --git a/clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp b/clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp
index 9c02083cf88f7e..3ce405937861b9 100644
--- a/clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp
+++ b/clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp
@@ -1,12 +1,8 @@
-// RUN: %clang_cc1 -fsyntax-only -std=c++2c %s -verify
+// RUN: %clang_cc1 -fsyntax-only -std=c++26 %s -verify=nontemplate
+// RUN: %clang_cc1 -fsyntax-only %s -verify=nontemplate,compat
 
 void decompose_array() {
   int arr[4] = {1, 2, 3, 6};
-  auto [x, ...rest, y] = arr; // expected-error{{pack declaration outside of template}}
-}
-
-template <unsigned N>
-void decompose_array_2() {
-  int arr[4] = {1, 2, 3, N};
-  auto [x, ...rest, ...y] = arr; // expected-error{{multiple ellipses in structured binding declaration}}
+  auto [x, ...rest, y] = arr; // nontemplate-error{{pack declaration outside of template}} \
+  // compat-warning{{structured binding pack is incompatible with C++ standards before C++2c}}
 }
diff --git a/clang/test/SemaCXX/cxx2c-binding-pack.cpp b/clang/test/SemaCXX/cxx2c-binding-pack.cpp
index ee9466e3b12c70..7e06d63543841a 100644
--- a/clang/test/SemaCXX/cxx2c-binding-pack.cpp
+++ b/clang/test/SemaCXX/cxx2c-binding-pack.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -std=c++2b %s -verify
+// RUN: %clang_cc1 -fsyntax-only -std=c++26 %s -verify
 // expected-no-diagnostics
 
 template <typename T>
@@ -64,8 +64,6 @@ void decompose_struct() {
 
 template <typename T>
 void decompose_array() {
-  // previously unable to use non-dependent array here
-  // Fixes https://bugs.llvm.org/show_bug.cgi?id=45964
   int arr[4] = {1, 2, 3, 6};
   auto [x, ...rest, y] = arr;
 

>From 66ff7c679f1e48cd80e02bf23be51bcb84e65b98 Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Thu, 2 Jan 2025 15:22:42 -0800
Subject: [PATCH 03/14] [Clang][P1061] Remove unrelated code changes; Address
 style issues

---
 clang/lib/AST/DeclCXX.cpp               |  5 ++--
 clang/lib/Parse/ParseDecl.cpp           |  4 +--
 clang/lib/Sema/SemaDeclCXX.cpp          | 39 ++++---------------------
 clang/lib/Sema/SemaLambda.cpp           |  1 -
 clang/lib/Sema/SemaStmt.cpp             |  7 +++--
 clang/lib/Sema/SemaTemplate.cpp         |  3 +-
 clang/lib/Sema/SemaTemplateVariadic.cpp | 14 ++++-----
 7 files changed, 19 insertions(+), 54 deletions(-)

diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 4c9a069f15ce32..d5a986bfd588cd 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3425,11 +3425,10 @@ VarDecl *BindingDecl::getHoldingVar() const {
   return VD;
 }
 
-
 void DecompositionDecl::VisitHoldingVars(
     llvm::function_ref<void(VarDecl *)> F) const {
-  VisitBindings([&](BindingDecl* BD) {
-    if (VarDecl* VD = BD->getHoldingVar())
+  VisitBindings([&](BindingDecl *BD) {
+    if (VarDecl *VD = BD->getHoldingVar())
       F(VD);
   });
 }
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 3e480415996996..234da2ed13bd11 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -7369,8 +7369,8 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
     ConsumeToken();
 
     if (Tok.is(tok::ellipsis) && !PrevEllipsisLoc.isValid()) {
-      DiagnoseMisplacedEllipsis(Tok.getLocation(), Loc,
-                                EllipsisLoc.isValid(), true);
+      DiagnoseMisplacedEllipsis(Tok.getLocation(), Loc, EllipsisLoc.isValid(),
+                                true);
       EllipsisLoc = Tok.getLocation();
       ConsumeToken();
     }
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index adabca19107f2b..2f657bdd2eada1 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -959,14 +959,15 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
   return New;
 }
 
-namespace {
 // CheckBindingsCount
 //  - Checks the arity of the structured bindings
 //  - Creates the resolved pack expr if there is
 //    one
-bool CheckBindingsCount(Sema &S, DecompositionDecl *DD, QualType DecompType,
-                        ArrayRef<BindingDecl *> Bindings,
-                        unsigned MemberCount) {
+
+static bool CheckBindingsCount(Sema &S, DecompositionDecl *DD,
+                               QualType DecompType,
+                               ArrayRef<BindingDecl *> Bindings,
+                               unsigned MemberCount) {
   auto BindingWithPackItr =
       std::find_if(Bindings.begin(), Bindings.end(),
                    [](BindingDecl *D) -> bool { return D->isParameterPack(); });
@@ -1058,7 +1059,6 @@ struct BindingInitWalker {
     ++BindingItr;
   }
 };
-} // namespace
 
 static bool checkSimpleDecomposition(
     Sema &S, ArrayRef<BindingDecl *> Bindings, ValueDecl *Src,
@@ -1619,35 +1619,6 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings,
   return false;
 }
 
-unsigned Sema::GetDecompositionElementCount(QualType DecompType) {
-  assert(!DecompType->isDependentType() && "expecting non-dependent type");
-  SourceLocation Loc = SourceLocation(); // FIXME
-  DecompType = DecompType.getNonReferenceType();
-  if (auto *CAT = Context.getAsConstantArrayType(DecompType))
-    return CAT->getSize().getLimitedValue(UINT_MAX);
-  if (auto *VT = DecompType->getAs<VectorType>())
-    return VT->getNumElements();
-  if (auto *CT = DecompType->getAs<ComplexType>())
-    return 2;
-  llvm::APSInt TupleSize(32);
-  if (IsTupleLike TL = isTupleLike(*this, Loc, DecompType, TupleSize);
-      TL == IsTupleLike::TupleLike)
-    return (unsigned)TupleSize.getLimitedValue(UINT_MAX);
-
-  if (CXXRecordDecl *RD = DecompType->getAsCXXRecordDecl();
-      RD && !RD->isUnion()) {
-    CXXCastPath BasePath;
-    DeclAccessPair BasePair =
-        findDecomposableBaseClass(*this, Loc, RD, BasePath);
-    RD = cast_or_null<CXXRecordDecl>(BasePair.getDecl());
-    if (RD)
-      return llvm::count_if(
-          RD->fields(), [](FieldDecl *FD) { return !FD->isUnnamedBitField(); });
-  }
-
-  llvm_unreachable("unknown type for decomposition");
-}
-
 void Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) {
   QualType DecompType = DD->getType();
 
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index e008a26ff7f062..a67c0b2b367d1a 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1526,7 +1526,6 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
     // - a member of a templated entity,
     // - an enumerator for an enumeration that is a templated entity, or
     // - the closure type of a lambda-expression ([expr.prim.lambda.closure])
-    // - an entity defined with an implicit template region
     // appearing in the declaration of a templated entity. [Note 6: A local
     // class, a local or block variable, or a friend function defined in a
     // templated entity is a templated entity.  — end note]
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 2d7ba56657a0c2..250035e2e5df1e 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -755,7 +755,7 @@ bool Sema::checkMustTailAttr(const Stmt *St, const Attr &MTA) {
     return true;
   };
 
-  const auto *CallerDecl = getCurFunctionDecl();
+  const auto *CallerDecl = dyn_cast<FunctionDecl>(CurContext);
 
   // Find caller function signature.
   if (!CallerDecl) {
@@ -1010,7 +1010,8 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc,
     bool Immediate = ExprEvalContexts.back().Context ==
                      ExpressionEvaluationContext::ImmediateFunctionContext;
     if (CurContext->isFunctionOrMethod()) {
-      const auto *FD = getCurFunctionDecl();
+      const auto *FD =
+          dyn_cast<FunctionDecl>(Decl::castFromDeclContext(CurContext));
       if (FD && FD->isImmediateFunction())
         Immediate = true;
     }
@@ -3918,7 +3919,7 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
   // deduction.
   if (getLangOpts().CPlusPlus14) {
     if (AutoType *AT = FnRetType->getContainedAutoType()) {
-      FunctionDecl *FD = getCurFunctionDecl();
+      FunctionDecl *FD = cast<FunctionDecl>(CurContext);
       // If we've already decided this function is invalid, e.g. because
       // we saw a `return` whose expression had an error, don't keep
       // trying to deduce its return type.
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index ef19d0d52213b2..f4dbb296ac7ded 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -928,10 +928,9 @@ static TemplateArgumentLoc translateTemplateArgument(Sema &SemaRef,
 
 void Sema::translateTemplateArguments(const ASTTemplateArgsPtr &TemplateArgsIn,
                                       TemplateArgumentListInfo &TemplateArgs) {
-  for (unsigned I = 0, Last = TemplateArgsIn.size(); I != Last; ++I) {
+  for (unsigned I = 0, Last = TemplateArgsIn.size(); I != Last; ++I)
     TemplateArgs.addArgument(
         translateTemplateArgument(*this, TemplateArgsIn[I]));
-  }
 }
 
 static void maybeDiagnoseTemplateParameterShadow(Sema &SemaRef, Scope *S,
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 94cdc99217d306..945ff8900ce9dc 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -19,7 +19,6 @@
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/Template.h"
-#include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/SaveAndRestore.h"
 #include <optional>
 
@@ -70,6 +69,7 @@ class CollectUnexpandedParameterPacksVisitor
       if (T->getDepth() < DepthLimit)
         Unexpanded.push_back({T, Loc});
     }
+
     void addUnexpanded(ResolvedUnexpandedPackExpr *E) {
       Unexpanded.push_back({E, E->getBeginLoc()});
     }
@@ -807,9 +807,8 @@ bool Sema::CheckParameterPacksForExpansion(
             CurrentInstantiationScope->findInstantiationOf(ND);
         if (Decl *B = Instantiation->dyn_cast<Decl *>()) {
           Expr *BindingExpr = cast<BindingDecl>(B)->getBinding();
-          if (auto *RP = dyn_cast<ResolvedUnexpandedPackExpr>(BindingExpr)) {
+          if (auto *RP = dyn_cast<ResolvedUnexpandedPackExpr>(BindingExpr))
             ResolvedPack = RP;
-          }
         }
         if (!ResolvedPack) {
           ShouldExpand = false;
@@ -1218,9 +1217,8 @@ ExprResult Sema::ActOnSizeofParameterPackExpr(Scope *S,
   MarkAnyDeclReferenced(OpLoc, ParameterPack, true);
 
   std::optional<unsigned> Length;
-  if (auto *RP = ResolvedUnexpandedPackExpr::getFromDecl(ParameterPack)) {
+  if (auto *RP = ResolvedUnexpandedPackExpr::getFromDecl(ParameterPack))
     Length = RP->getNumExprs();
-  }
 
   return SizeOfPackExpr::Create(Context, OpLoc, ParameterPack, NameLoc,
                                 RParenLoc, Length);
@@ -1494,10 +1492,8 @@ ExprResult Sema::ActOnCXXFoldExpr(Scope *S, SourceLocation LParenLoc, Expr *LHS,
     }
   }
 
-  ExprResult Result = BuildCXXFoldExpr(ULE, LParenLoc, LHS, Opc, EllipsisLoc,
-                                       RHS, RParenLoc, std::nullopt);
-
-  return Result;
+  return BuildCXXFoldExpr(ULE, LParenLoc, LHS, Opc, EllipsisLoc, RHS, RParenLoc,
+                          std::nullopt);
 }
 
 ExprResult Sema::BuildCXXFoldExpr(UnresolvedLookupExpr *Callee,

>From 5720bd3a86f9ed240f110ace4d8b040dad509324 Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Fri, 3 Jan 2025 11:13:38 -0800
Subject: [PATCH 04/14] [Clang][P1061] Remove vestigial changes

---
 clang/lib/Sema/SemaDecl.cpp                | 1 -
 clang/lib/Sema/SemaTemplateInstantiate.cpp | 1 +
 clang/lib/Sema/SemaTemplateVariadic.cpp    | 9 ++-------
 3 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 4ba78818f19836..4001c4d263f1d2 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -9780,7 +9780,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
   QualType R = TInfo->getType();
 
   assert(R->isFunctionType());
-
   if (R.getCanonicalType()->castAs<FunctionType>()->getCmseNSCallAttr())
     Diag(D.getIdentifierLoc(), diag::err_function_decl_cmse_ns_call);
 
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index f94c22185d399b..84fd945813fa25 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -805,6 +805,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
           SemaRef, CodeSynthesisContext::BuildingDeductionGuides,
           PointOfInstantiation, InstantiationRange, Entity) {}
 
+
 void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) {
   Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext;
   InNonInstantiationSFINAEContext = false;
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 945ff8900ce9dc..6936577160f30c 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -50,15 +50,14 @@ class CollectUnexpandedParameterPacksVisitor
         auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr;
         if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
           return;
-      } else if (ND->isTemplateParameterPack() &&
-                 getDepthAndIndex(ND).first >= DepthLimit) {
-        return;
       } else if (auto *BD = dyn_cast<BindingDecl>(ND)) {
         Expr *E = BD->getBinding();
         if (auto *RP = dyn_cast_if_present<ResolvedUnexpandedPackExpr>(E)) {
           addUnexpanded(RP);
           return;
         }
+      } else if (getDepthAndIndex(ND).first >= DepthLimit) {
+        return;
       }
 
       Unexpanded.push_back({ND, Loc});
@@ -229,10 +228,6 @@ class CollectUnexpandedParameterPacksVisitor
     bool TraversePackIndexingTypeLoc(PackIndexingTypeLoc TL) override {
       return DynamicRecursiveASTVisitor::TraverseStmt(TL.getIndexExpr());
     }
-    bool
-    TraverseResolvedUnexpandedPackExpr(ResolvedUnexpandedPackExpr *E) override {
-      return true;
-    }
 
     ///@}
 

>From ffefb6774e13540eb658cc4351d2cb32ff24db0c Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Fri, 3 Jan 2025 14:39:10 -0800
Subject: [PATCH 05/14] [Clang][P1061] Simplify unwrapping ResolvedPacks; other
 style fixes

---
 clang/lib/Sema/SemaTemplate.cpp         |  6 +++---
 clang/lib/Sema/SemaTemplateVariadic.cpp | 10 ++++------
 2 files changed, 7 insertions(+), 9 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index f4dbb296ac7ded..5e7a3c8484c88f 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -928,9 +928,9 @@ static TemplateArgumentLoc translateTemplateArgument(Sema &SemaRef,
 
 void Sema::translateTemplateArguments(const ASTTemplateArgsPtr &TemplateArgsIn,
                                       TemplateArgumentListInfo &TemplateArgs) {
-  for (unsigned I = 0, Last = TemplateArgsIn.size(); I != Last; ++I)
-    TemplateArgs.addArgument(
-        translateTemplateArgument(*this, TemplateArgsIn[I]));
+ for (unsigned I = 0, Last = TemplateArgsIn.size(); I != Last; ++I)
+   TemplateArgs.addArgument(translateTemplateArgument(*this,
+                                                      TemplateArgsIn[I]));
 }
 
 static void maybeDiagnoseTemplateParameterShadow(Sema &SemaRef, Scope *S,
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 6936577160f30c..656e94ab774df2 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -797,14 +797,12 @@ bool Sema::CheckParameterPacksForExpansion(
       if (isa<VarDecl>(ND))
         IsVarDeclPack = true;
       else if (isa<BindingDecl>(ND)) {
-        // find the instantiated BindingDecl and check it for a resolved pack
+        // Find the instantiated BindingDecl and check it for a resolved pack.
         llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
             CurrentInstantiationScope->findInstantiationOf(ND);
-        if (Decl *B = Instantiation->dyn_cast<Decl *>()) {
-          Expr *BindingExpr = cast<BindingDecl>(B)->getBinding();
-          if (auto *RP = dyn_cast<ResolvedUnexpandedPackExpr>(BindingExpr))
-            ResolvedPack = RP;
-        }
+        Decl *B = cast<Decl *>(*Instantiation);
+        Expr *BindingExpr = cast<BindingDecl>(B)->getBinding();
+        ResolvedPack = dyn_cast<ResolvedUnexpandedPackExpr>(BindingExpr);
         if (!ResolvedPack) {
           ShouldExpand = false;
           continue;

>From 685f947f64bcc1d0da4094c74a9ec868c9eb43e0 Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Fri, 3 Jan 2025 20:22:58 -0800
Subject: [PATCH 06/14] [Clang][P1061] Add ASTReader implementation

---
 clang/include/clang/AST/ExprCXX.h         |  2 +
 clang/lib/Serialization/ASTReaderStmt.cpp |  5 ++
 clang/test/AST/ast-dump-binding-pack.cpp  | 58 +++++++++++++++++++++++
 3 files changed, 65 insertions(+)
 create mode 100644 clang/test/AST/ast-dump-binding-pack.cpp

diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 371da5caf41a5c..980f12bbf7e009 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5326,6 +5326,8 @@ class BuiltinBitCastExpr final
 class ResolvedUnexpandedPackExpr final
     : public Expr,
       private llvm::TrailingObjects<ResolvedUnexpandedPackExpr, Stmt *> {
+  friend class ASTStmtReader;
+  friend class ASTStmtWriter;
   friend TrailingObjects;
 
   SourceLocation BeginLoc;
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index f1ed2d81676bf2..9cf663cc41dded 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2204,6 +2204,11 @@ void ASTStmtReader::VisitPackIndexingExpr(PackIndexingExpr *E) {
 void ASTStmtReader::VisitResolvedUnexpandedPackExpr(
     ResolvedUnexpandedPackExpr *E) {
   VisitExpr(E);
+  E->NumExprs = Record.readInt();
+  E->BeginLoc = readSourceLocation();
+  auto **Exprs = E->getExprs();
+  for (unsigned I = 0; I < E->NumExprs; ++I)
+    Exprs[I] = Record.readExpr();
 }
 
 void ASTStmtReader::VisitSubstNonTypeTemplateParmExpr(
diff --git a/clang/test/AST/ast-dump-binding-pack.cpp b/clang/test/AST/ast-dump-binding-pack.cpp
new file mode 100644
index 00000000000000..fc1eeab895628f
--- /dev/null
+++ b/clang/test/AST/ast-dump-binding-pack.cpp
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -ast-dump -std=c++26 %s | FileCheck %s
+
+// Test this with PCH.
+// RUN: %clang_cc1 %s -std=c++26 -emit-pch -o %t %s
+// RUN: %clang_cc1 %s -std=c++26 -include-pch %t -ast-dump-all | FileCheck %s
+
+#ifndef PCH_HELPER
+#define PCH_HELPER
+
+template <unsigned N>
+void foo() {
+  int arr[4] = {1, 2, 3, 4};
+  auto [x, ...rest, y] = arr;
+  int arr_2 = {rest...};
+};
+
+// CHECK-LABEL: FunctionTemplateDecl {{.*}} foo
+// CHECK-NEXT: NonTypeTemplateParmDecl
+// CHECK-NEXT: FunctionDecl {{.*}} foo
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: DeclStmt
+// CHECK-NEXT: VarDecl
+// CHECK-NEXT: InitListExpr
+// CHECK-NEXT: IntegerLiteral
+// CHECK-NEXT: IntegerLiteral
+// CHECK-NEXT: IntegerLiteral
+// CHECK-NEXT: IntegerLiteral
+// CHECK-NEXT: DeclStmt
+// CHECK-NEXT: DecompositionDecl {{.*}} 'int[4]'
+// CHECK-NEXT: ArrayInitLoopExpr {{.*}} 'int[4]'
+// CHECK-NEXT: OpaqueValueExpr
+// CHECK-NEXT: DeclRefExpr
+// CHECK-NEXT: ImplicitCastExpr
+// CHECK-NEXT: ArraySubscriptExpr
+// CHECK-NEXT: ImplicitCastExpr
+// CHECK-NEXT: OpaqueValueExpr
+// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Var {{.*}} 'arr' 'int[4]'
+// CHECK-NEXT: ArrayInitIndexExpr {{.*}} 'unsigned long'
+// CHECK-NEXT: BindingDecl {{.*}} x 'int'
+// CHECK-NEXT: ArraySubscriptExpr {{.*}} 'int' lvalue
+// CHECK-NEXT: ImplicitCastExpr
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int[4]' lvalue Decomposition {{.*}} 'int[4]'
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0
+// CHECK-NEXT: BindingDecl {{.*}} rest 'type-parameter-0-0...'
+// CHECK-NEXT: ResolvedUnexpandedPackExpr {{.*}} 'int[4]'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue Binding {{.*}} 'rest' 'int'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue Binding {{.*}} 'rest' 'int'
+// CHECK-NEXT: BindingDecl {{.*}} y 'int'
+// CHECK-NEXT: ArraySubscriptExpr {{.*}} 'int' lvalue
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int *'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int[4]' lvalue Decomposition {{.*}} 'int[4]'
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3
+// CHECK-NEXT: DeclStmt
+// CHECK-NEXT: VarDecl {{.*}} arr_2 'int'
+// CHECK-NEXT: InitListExpr {{.*}} 'void'
+// CHECK-NEXT: PackExpansionExpr {{.*}} '<dependent type>' lvalue
+// CHECK-NEXT: DeclRefExpr {{.*}} 'type-parameter-0-0' lvalue Binding {{.*}} 'rest'
+#endif

>From d957783750fee77440ea24b775b3dcf2da25534b Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Sat, 4 Jan 2025 11:38:10 -0800
Subject: [PATCH 07/14] [Clang][P1061] Improve AST dump testing

---
 clang/test/AST/ast-dump-binding-pack.cpp | 92 ++++++++++++++----------
 1 file changed, 54 insertions(+), 38 deletions(-)

diff --git a/clang/test/AST/ast-dump-binding-pack.cpp b/clang/test/AST/ast-dump-binding-pack.cpp
index fc1eeab895628f..81c75a1268730f 100644
--- a/clang/test/AST/ast-dump-binding-pack.cpp
+++ b/clang/test/AST/ast-dump-binding-pack.cpp
@@ -10,49 +10,65 @@
 template <unsigned N>
 void foo() {
   int arr[4] = {1, 2, 3, 4};
-  auto [x, ...rest, y] = arr;
-  int arr_2 = {rest...};
+  auto [binding_1, ...binding_rest, binding_4] = arr;
+  int arr_2[] = {binding_rest...};
 };
 
 // CHECK-LABEL: FunctionTemplateDecl {{.*}} foo
-// CHECK-NEXT: NonTypeTemplateParmDecl
-// CHECK-NEXT: FunctionDecl {{.*}} foo
-// CHECK-NEXT: CompoundStmt
-// CHECK-NEXT: DeclStmt
-// CHECK-NEXT: VarDecl
-// CHECK-NEXT: InitListExpr
-// CHECK-NEXT: IntegerLiteral
-// CHECK-NEXT: IntegerLiteral
-// CHECK-NEXT: IntegerLiteral
-// CHECK-NEXT: IntegerLiteral
-// CHECK-NEXT: DeclStmt
-// CHECK-NEXT: DecompositionDecl {{.*}} 'int[4]'
-// CHECK-NEXT: ArrayInitLoopExpr {{.*}} 'int[4]'
-// CHECK-NEXT: OpaqueValueExpr
-// CHECK-NEXT: DeclRefExpr
-// CHECK-NEXT: ImplicitCastExpr
-// CHECK-NEXT: ArraySubscriptExpr
-// CHECK-NEXT: ImplicitCastExpr
-// CHECK-NEXT: OpaqueValueExpr
-// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Var {{.*}} 'arr' 'int[4]'
-// CHECK-NEXT: ArrayInitIndexExpr {{.*}} 'unsigned long'
-// CHECK-NEXT: BindingDecl {{.*}} x 'int'
-// CHECK-NEXT: ArraySubscriptExpr {{.*}} 'int' lvalue
+// CHECK-LABEL: BindingDecl {{.*}} binding_1
+// CHECK-NEXT: ArraySubscriptExpr {{.*}}
 // CHECK-NEXT: ImplicitCastExpr
-// CHECK-NEXT: DeclRefExpr {{.*}} 'int[4]' lvalue Decomposition {{.*}} 'int[4]'
+// CHECK-NEXT: DeclRefExpr {{.*}}
 // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0
-// CHECK-NEXT: BindingDecl {{.*}} rest 'type-parameter-0-0...'
-// CHECK-NEXT: ResolvedUnexpandedPackExpr {{.*}} 'int[4]'
-// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue Binding {{.*}} 'rest' 'int'
-// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue Binding {{.*}} 'rest' 'int'
-// CHECK-NEXT: BindingDecl {{.*}} y 'int'
-// CHECK-NEXT: ArraySubscriptExpr {{.*}} 'int' lvalue
-// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int *'
-// CHECK-NEXT: DeclRefExpr {{.*}} 'int[4]' lvalue Decomposition {{.*}} 'int[4]'
+// CHECK-NOT: BindingDecl
+// CHECK-LABEL: BindingDecl {{.*}} binding_rest
+// CHECK-NEXT: ResolvedUnexpandedPackExpr
+// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Binding {{.*}} 'binding_rest'
+// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Binding {{.*}} 'binding_rest'
+// CHECK-NOT: BindingDecl
+// CHECK-LABEL: BindingDecl {{.*}} binding_4
+// CHECK-NEXT: ArraySubscriptExpr
+// CHECK-NEXT: ImplicitCastExpr {{.*}}
+// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Decomposition {{.*}}
 // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3
-// CHECK-NEXT: DeclStmt
-// CHECK-NEXT: VarDecl {{.*}} arr_2 'int'
-// CHECK-NEXT: InitListExpr {{.*}} 'void'
+// CHECK-NOT: BindingDecl
+// CHECK-LABEL: VarDecl {{.*}} arr_2
+// CHECK-NEXT: InitListExpr
 // CHECK-NEXT: PackExpansionExpr {{.*}} '<dependent type>' lvalue
-// CHECK-NEXT: DeclRefExpr {{.*}} 'type-parameter-0-0' lvalue Binding {{.*}} 'rest'
+// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Binding {{.*}} 'binding_rest'
+
+struct tag_t { };
+template <unsigned N>
+void bar() {
+  auto [...empty_binding_pack] = tag_t{};
+  static_assert(sizeof...(empty_binding_pack) == 0);
+};
+
+// CHECK-LABEL: FunctionTemplateDecl {{.*}} bar
+// CHECK-NOT: BindingDecl
+// CHECK-LABEL: BindingDecl {{.*}} empty_binding_pack
+// CHECK-NEXT: ResolvedUnexpandedPackExpr
+// CHECK-NOT: DeclRefExpr {{.*}} 'empty_binding_pack'
+// CHECK-NOT: BindingDecl
+// CHECK: DeclStmt
+
+struct int_pair { int x; int y; };
+template <typename T>
+void baz() {
+  auto [binding_1, binding_2, ...empty_binding_pack] = T{};
+  static_assert(sizeof...(empty_binding_pack) == 0);
+};
+
+void(*f)() = baz<int_pair>;
+
+// CHECK-LABEL: FunctionDecl {{.*}} baz {{.*}} implicit_instantiation
+// CHECK-NEXT: TemplateArgument type 'int_pair'
+// CHECK: BindingDecl {{.*}} binding_1
+// CHECK: BindingDecl {{.*}} binding_2
+// CHECK-NOT: BindingDecl
+// CHECK-LABEL: BindingDecl {{.*}} empty_binding_pack
+// CHECK-NEXT: ResolvedUnexpandedPackExpr
+// CHECK-NOT: DeclRefExpr {{.*}} 'empty_binding_pack'
+// CHECK-NOT: BindingDecl
+// CHECK: DeclStmt
 #endif

>From 91ca5b4566f49f532ab7d0f6da1ab4a13c014df8 Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Thu, 9 Jan 2025 19:50:04 -0800
Subject: [PATCH 08/14] [Clang][P1061] Add flat_binding_iterator

---
 clang/include/clang/AST/DeclCXX.h             | 85 ++++++++++++++++++-
 clang/include/clang/AST/ExprCXX.h             | 15 ++--
 clang/lib/AST/DeclCXX.cpp                     | 27 +++++-
 clang/lib/AST/ExprCXX.cpp                     |  8 +-
 clang/lib/AST/StmtPrinter.cpp                 |  2 +-
 clang/lib/Sema/SemaDeclCXX.cpp                |  3 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |  2 +-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  4 +-
 clang/lib/Serialization/ASTReaderStmt.cpp     |  2 +-
 clang/lib/Serialization/ASTWriterStmt.cpp     |  3 +-
 10 files changed, 126 insertions(+), 25 deletions(-)

diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 6ac913e8b85b92..021fddcc836ef1 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4165,6 +4165,8 @@ class BindingDecl : public ValueDecl {
   /// Set the decomposed variable for this BindingDecl.
   void setDecomposedDecl(ValueDecl *Decomposed) { Decomp = Decomposed; }
 
+  /// Get the variable (if any) that holds the value of evaluating the binding.
+  /// Only present for user-defined bindings for tuple-like types.
   VarDecl *getHoldingVar() const;
 
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
@@ -4213,10 +4215,19 @@ class DecompositionDecl final
   static DecompositionDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
                                                unsigned NumBindings);
 
-  ArrayRef<BindingDecl *> bindings() const {
-    return llvm::ArrayRef(getTrailingObjects<BindingDecl *>(), NumBindings);
+  // Provide the range of bindings which may have a nested pack.
+  llvm::ArrayRef<BindingDecl *> bindings() const {
+    return {getTrailingObjects<BindingDecl *>(), NumBindings};
   }
 
+  // Provide a flattened range to visit each binding directly.
+  class flat_binding_iterator;
+  using flat_binding_range = llvm::iterator_range<flat_binding_iterator>;
+
+  flat_binding_range flat_bindings() const;
+  flat_binding_iterator flat_bindings_begin() const;
+  flat_binding_iterator flat_bindings_end() const;
+
   void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
 
   /// Visit the variables (if any) that hold the values of evaluating the
@@ -4230,6 +4241,76 @@ class DecompositionDecl final
   static bool classofKind(Kind K) { return K == Decomposition; }
 };
 
+/// Iterate bindings that may be contain a nested pack of bindings.
+class DecompositionDecl::flat_binding_iterator {
+  friend class DecompositionDecl;
+
+  using ItrTy = llvm::ArrayRef<BindingDecl *>::const_iterator;
+  using PackItrTy = llvm::ArrayRef<Expr *>::const_iterator;
+
+  llvm::ArrayRef<BindingDecl *> Bindings;
+  llvm::ArrayRef<Expr *> PackExprs;
+  ItrTy Itr = nullptr;
+  PackItrTy PackItr = nullptr;
+
+  explicit flat_binding_iterator(llvm::ArrayRef<BindingDecl *> Bindings,
+                                 ItrTy Itr, llvm::ArrayRef<Expr *> PackExprs,
+                                 PackItrTy PackItr)
+      : Bindings(Bindings), PackExprs(PackExprs), Itr(Itr), PackItr(PackItr) {
+    // If Itr is not a parameter pack then PackItr should not be set.
+    assert(((!(*Itr)->isParameterPack() && PackItr == nullptr) ||
+            PackItr != PackExprs.end()) &&
+           "pack must have initial iterator within the range");
+  }
+
+public:
+  flat_binding_iterator() = default;
+
+  using value_type = BindingDecl *;
+  using reference = BindingDecl *;
+  using pointer = BindingDecl *;
+  using iterator_category = std::forward_iterator_tag;
+
+  reference operator*() const {
+    if (!(*Itr)->isParameterPack())
+      return *Itr;
+
+    auto *DRE = cast<DeclRefExpr>(*PackItr);
+    return cast<BindingDecl>(DRE->getDecl());
+  }
+
+  flat_binding_iterator &operator++() {
+    if ((*Itr)->isParameterPack()) {
+      ++PackItr;
+      if (PackItr != PackExprs.end())
+        return *this;
+      else
+        PackItr = nullptr;
+    }
+    ++Itr;
+    return *this;
+  }
+
+  flat_binding_iterator operator++(int) {
+    flat_binding_iterator Temp = *this;
+    ++*this;
+    return Temp;
+  }
+
+  bool operator==(const flat_binding_iterator &Other) const {
+    return Itr == Other.Itr && PackItr == Other.PackItr;
+  }
+
+  bool operator!=(const flat_binding_iterator &Other) const {
+    return Itr != Other.Itr || PackItr != Other.PackItr;
+  }
+};
+
+inline DecompositionDecl::flat_binding_range
+DecompositionDecl::flat_bindings() const {
+  return flat_binding_range(flat_bindings_begin(), flat_bindings_end());
+}
+
 /// An instance of this class represents the declaration of a property
 /// member.  This is a Microsoft extension to C++, first introduced in
 /// Visual Studio .NET 2003 as a parallel to similar features in C#
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 980f12bbf7e009..09d10087add718 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5325,7 +5325,7 @@ class BuiltinBitCastExpr final
 // known. These are used when structured bindings introduce a pack.
 class ResolvedUnexpandedPackExpr final
     : public Expr,
-      private llvm::TrailingObjects<ResolvedUnexpandedPackExpr, Stmt *> {
+      private llvm::TrailingObjects<ResolvedUnexpandedPackExpr, Expr *> {
   friend class ASTStmtReader;
   friend class ASTStmtWriter;
   friend TrailingObjects;
@@ -5346,11 +5346,12 @@ class ResolvedUnexpandedPackExpr final
 
   unsigned getNumExprs() const { return NumExprs; }
 
-  Expr **getExprs() {
-    return reinterpret_cast<Expr **>(getTrailingObjects<Stmt *>());
+  llvm::MutableArrayRef<Expr *> getExprs() {
+    return {getTrailingObjects<Expr *>(), NumExprs};
   }
-  Expr *const *getExprs() const {
-    return reinterpret_cast<Expr *const *>(getTrailingObjects<Stmt *>());
+
+  llvm::ArrayRef<Expr *> getExprs() const {
+    return {getTrailingObjects<Expr *>(), NumExprs};
   }
 
   Expr *getExpansion(unsigned Idx) { return getExprs()[Idx]; }
@@ -5358,8 +5359,8 @@ class ResolvedUnexpandedPackExpr final
 
   // Iterators
   child_range children() {
-    return child_range(getTrailingObjects<Stmt *>(),
-                       getTrailingObjects<Stmt *>() + getNumExprs());
+    return child_range((Stmt **)getTrailingObjects<Expr *>(),
+                       (Stmt **)getTrailingObjects<Expr *>() + getNumExprs());
   }
 
   SourceLocation getBeginLoc() const LLVM_READONLY { return BeginLoc; }
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index d5a986bfd588cd..083bb77f31af19 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3436,11 +3436,9 @@ void DecompositionDecl::VisitHoldingVars(
 void DecompositionDecl::VisitBindings(
     llvm::function_ref<void(BindingDecl *)> F) const {
   for (BindingDecl *B : bindings()) {
-    llvm::ArrayRef<Expr *> Exprs;
     if (B->isParameterPack()) {
       auto *RP = cast<ResolvedUnexpandedPackExpr>(B->getBinding());
-      Exprs = llvm::ArrayRef(RP->getExprs(), RP->getNumExprs());
-      for (Expr *E : Exprs) {
+      for (Expr *E : RP->getExprs()) {
         auto *DRE = cast<DeclRefExpr>(E);
         F(cast<BindingDecl>(DRE->getDecl()));
       }
@@ -3449,6 +3447,29 @@ void DecompositionDecl::VisitBindings(
   }
 }
 
+DecompositionDecl::flat_binding_iterator
+DecompositionDecl::flat_bindings_begin() const {
+  auto Bindings = bindings();
+  BindingDecl *First = *Bindings.begin();
+  llvm::ArrayRef<Expr *> PackExprs;
+
+  if (First->isParameterPack()) {
+    auto *RP = cast<ResolvedUnexpandedPackExpr>(First->getBinding());
+    PackExprs = RP->getExprs();
+  }
+
+  return flat_binding_iterator(Bindings, Bindings.begin(), PackExprs,
+                               PackExprs.begin());
+}
+
+DecompositionDecl::flat_binding_iterator
+DecompositionDecl::flat_bindings_end() const {
+  auto Bindings = bindings();
+  llvm::ArrayRef<Expr *> PackExprs;
+
+  return flat_binding_iterator(Bindings, Bindings.end(), PackExprs, nullptr);
+}
+
 void DecompositionDecl::anchor() {}
 
 DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC,
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 7c710b703f38c1..107ec3dcd9ac65 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1981,7 +1981,7 @@ ResolvedUnexpandedPackExpr::ResolvedUnexpandedPackExpr(SourceLocation BL,
 ResolvedUnexpandedPackExpr *
 ResolvedUnexpandedPackExpr::CreateDeserialized(ASTContext &Ctx,
                                                unsigned NumExprs) {
-  void *Mem = Ctx.Allocate(totalSizeToAlloc<Stmt *>(NumExprs),
+  void *Mem = Ctx.Allocate(totalSizeToAlloc<Expr *>(NumExprs),
                            alignof(ResolvedUnexpandedPackExpr));
   return new (Mem)
       ResolvedUnexpandedPackExpr(SourceLocation(), QualType(), NumExprs);
@@ -1990,12 +1990,12 @@ ResolvedUnexpandedPackExpr::CreateDeserialized(ASTContext &Ctx,
 ResolvedUnexpandedPackExpr *
 ResolvedUnexpandedPackExpr::Create(ASTContext &Ctx, SourceLocation BL,
                                    QualType T, unsigned NumExprs) {
-  void *Mem = Ctx.Allocate(totalSizeToAlloc<Stmt *>(NumExprs),
+  void *Mem = Ctx.Allocate(totalSizeToAlloc<Expr *>(NumExprs),
                            alignof(ResolvedUnexpandedPackExpr));
   ResolvedUnexpandedPackExpr *New =
       new (Mem) ResolvedUnexpandedPackExpr(BL, T, NumExprs);
 
-  auto Exprs = llvm::MutableArrayRef(New->getExprs(), New->getNumExprs());
+  auto Exprs = New->getExprs();
   std::fill(Exprs.begin(), Exprs.end(), nullptr);
 
   return New;
@@ -2005,7 +2005,7 @@ ResolvedUnexpandedPackExpr *
 ResolvedUnexpandedPackExpr::Create(ASTContext &Ctx, SourceLocation BL,
                                    QualType T, ArrayRef<Expr *> Exprs) {
   auto *New = Create(Ctx, BL, T, Exprs.size());
-  std::copy(Exprs.begin(), Exprs.end(), New->getExprs());
+  std::copy(Exprs.begin(), Exprs.end(), New->getExprs().begin());
   return New;
 }
 
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 50bf880a4d37ea..64955553108f78 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2562,7 +2562,7 @@ void StmtPrinter::VisitResolvedUnexpandedPackExpr(
     ResolvedUnexpandedPackExpr *E) {
   OS << "<<resolved pack(";
   llvm::interleave(
-      E->getExprs(), E->getExprs() + E->getNumExprs(),
+      E->getExprs().begin(), E->getExprs().end(),
       [this](auto *X) { PrintExpr(X); }, [this] { OS << ", "; });
   OS << ")>>";
 }
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 2f657bdd2eada1..cf78ea197f77c7 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1032,8 +1032,7 @@ struct BindingInitWalker {
         isa_and_nonnull<ResolvedUnexpandedPackExpr>(B->getBinding());
     if (IsPackExpr && !PackExpr) {
       PackExpr = cast<ResolvedUnexpandedPackExpr>(B->getBinding());
-      PackExprNodes =
-          llvm::MutableArrayRef(PackExpr->getExprs(), PackExpr->getNumExprs());
+      PackExprNodes = PackExpr->getExprs();
       PackExprItr = PackExprNodes.begin();
     }
 
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 84fd945813fa25..dea44fb3910f78 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2675,7 +2675,7 @@ ExprResult TemplateInstantiator::TransformResolvedUnexpandedPackExpr(
     return E;
 
   SmallVector<Expr *, 12> NewExprs;
-  if (TransformExprs(E->getExprs(), E->getNumExprs(),
+  if (TransformExprs(E->getExprs().begin(), E->getNumExprs(),
                      /*IsCall=*/false, NewExprs))
     return ExprError();
 
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 4ae29854a58725..5593b2c7a87bf5 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1202,8 +1202,8 @@ Decl *TemplateDeclInstantiator::VisitDecompositionDecl(DecompositionDecl *D) {
         [](BindingDecl *D) -> bool { return D->isParameterPack(); });
     auto *NewResolvedPack =
         cast<ResolvedUnexpandedPackExpr>((*BPack)->getBinding());
-    Expr **OldExprs = OldResolvedPack->getExprs();
-    Expr **NewExprs = NewResolvedPack->getExprs();
+    auto OldExprs = OldResolvedPack->getExprs();
+    auto NewExprs = NewResolvedPack->getExprs();
     for (unsigned I = 0; I < OldResolvedPack->getNumExprs(); I++) {
       DeclRefExpr *OldDRE = cast<DeclRefExpr>(OldExprs[I]);
       BindingDecl *OldNestedBD = cast<BindingDecl>(OldDRE->getDecl());
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 9cf663cc41dded..cb26228a37fcde 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2206,7 +2206,7 @@ void ASTStmtReader::VisitResolvedUnexpandedPackExpr(
   VisitExpr(E);
   E->NumExprs = Record.readInt();
   E->BeginLoc = readSourceLocation();
-  auto **Exprs = E->getExprs();
+  auto **Exprs = E->getTrailingObjects<Expr *>();
   for (unsigned I = 0; I < E->NumExprs; ++I)
     Exprs[I] = Record.readExpr();
 }
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 26967160d40b4e..60470ea2f367a6 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2207,8 +2207,7 @@ void ASTStmtWriter::VisitResolvedUnexpandedPackExpr(
   VisitExpr(E);
   Record.push_back(E->getNumExprs());
   Record.AddSourceLocation(E->getBeginLoc());
-  auto SubExprs = llvm::ArrayRef(E->getExprs(), E->getNumExprs());
-  for (Expr *Sub : SubExprs)
+  for (Expr *Sub : E->getExprs())
     Record.AddStmt(Sub);
   Code = serialization::EXPR_RESOLVED_UNEXPANDED_PACK;
 }

>From abab5cf53e86ee3d9d6afe53ac36967d516f1eb6 Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Fri, 10 Jan 2025 10:22:05 -0800
Subject: [PATCH 09/14] [Clang][P1061] Remove Decomp Visit functions

---
 clang/include/clang/AST/DeclCXX.h    | 24 ++++++------------------
 clang/lib/AST/ASTContext.cpp         | 11 ++++-------
 clang/lib/AST/DeclCXX.cpp            | 27 ++++-----------------------
 clang/lib/AST/ExprConstant.cpp       |  4 +++-
 clang/lib/CodeGen/CGDebugInfo.cpp    |  3 +--
 clang/lib/CodeGen/CGDecl.cpp         |  5 ++++-
 clang/lib/CodeGen/CodeGenModule.cpp  |  5 ++++-
 clang/lib/Sema/SemaExceptionSpec.cpp |  6 +++---
 8 files changed, 29 insertions(+), 56 deletions(-)

diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 021fddcc836ef1..be564b4118dc11 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4230,13 +4230,6 @@ class DecompositionDecl final
 
   void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
 
-  /// Visit the variables (if any) that hold the values of evaluating the
-  /// binding. Only present for user-defined bindings for tuple-like types.
-  void VisitHoldingVars(llvm::function_ref<void(VarDecl *)> F) const;
-
-  // Visit the concrete bindings.
-  void VisitBindings(llvm::function_ref<void(BindingDecl *)> F) const;
-
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == Decomposition; }
 };
@@ -4245,8 +4238,8 @@ class DecompositionDecl final
 class DecompositionDecl::flat_binding_iterator {
   friend class DecompositionDecl;
 
-  using ItrTy = llvm::ArrayRef<BindingDecl *>::const_iterator;
-  using PackItrTy = llvm::ArrayRef<Expr *>::const_iterator;
+  using ItrTy = llvm::ArrayRef<BindingDecl *>::iterator;
+  using PackItrTy = llvm::ArrayRef<Expr *>::iterator;
 
   llvm::ArrayRef<BindingDecl *> Bindings;
   llvm::ArrayRef<Expr *> PackExprs;
@@ -4256,22 +4249,17 @@ class DecompositionDecl::flat_binding_iterator {
   explicit flat_binding_iterator(llvm::ArrayRef<BindingDecl *> Bindings,
                                  ItrTy Itr, llvm::ArrayRef<Expr *> PackExprs,
                                  PackItrTy PackItr)
-      : Bindings(Bindings), PackExprs(PackExprs), Itr(Itr), PackItr(PackItr) {
-    // If Itr is not a parameter pack then PackItr should not be set.
-    assert(((!(*Itr)->isParameterPack() && PackItr == nullptr) ||
-            PackItr != PackExprs.end()) &&
-           "pack must have initial iterator within the range");
-  }
+      : Bindings(Bindings), PackExprs(PackExprs), Itr(Itr), PackItr(PackItr) {}
 
 public:
   flat_binding_iterator() = default;
 
   using value_type = BindingDecl *;
-  using reference = BindingDecl *;
-  using pointer = BindingDecl *;
+  using reference = BindingDecl *&;
+  using pointer = BindingDecl **;
   using iterator_category = std::forward_iterator_tag;
 
-  reference operator*() const {
+  BindingDecl *operator*() {
     if (!(*Itr)->isParameterPack())
       return *Itr;
 
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 6216d896a88ac3..c340d6aeebc600 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -12727,13 +12727,10 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
   // Likewise, variables with tuple-like bindings are required if their
   // bindings have side-effects.
   if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) {
-    bool BindingResult = false;
-    DD->VisitHoldingVars([&](VarDecl *BindingVD) {
-      if (DeclMustBeEmitted(BindingVD))
-        BindingResult = true;
-    });
-    if (BindingResult)
-      return true;
+    for (const auto *BD : DD->flat_bindings())
+      if (const auto *BindingVD = BD->getHoldingVar())
+        if (DeclMustBeEmitted(BindingVD))
+          return true;
   }
 
   return false;
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 083bb77f31af19..6d9213b5d8037f 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3425,35 +3425,16 @@ VarDecl *BindingDecl::getHoldingVar() const {
   return VD;
 }
 
-void DecompositionDecl::VisitHoldingVars(
-    llvm::function_ref<void(VarDecl *)> F) const {
-  VisitBindings([&](BindingDecl *BD) {
-    if (VarDecl *VD = BD->getHoldingVar())
-      F(VD);
-  });
-}
-
-void DecompositionDecl::VisitBindings(
-    llvm::function_ref<void(BindingDecl *)> F) const {
-  for (BindingDecl *B : bindings()) {
-    if (B->isParameterPack()) {
-      auto *RP = cast<ResolvedUnexpandedPackExpr>(B->getBinding());
-      for (Expr *E : RP->getExprs()) {
-        auto *DRE = cast<DeclRefExpr>(E);
-        F(cast<BindingDecl>(DRE->getDecl()));
-      }
-    } else
-      F(B);
-  }
-}
-
 DecompositionDecl::flat_binding_iterator
 DecompositionDecl::flat_bindings_begin() const {
+  if (NumBindings == 0)
+    return flat_bindings_end();
+
   auto Bindings = bindings();
   BindingDecl *First = *Bindings.begin();
   llvm::ArrayRef<Expr *> PackExprs;
 
-  if (First->isParameterPack()) {
+  if (NumBindings > 0 && First->isParameterPack()) {
     auto *RP = cast<ResolvedUnexpandedPackExpr>(First->getBinding());
     PackExprs = RP->getExprs();
   }
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index b7ff7aa1d61ecc..c767ea93065840 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5155,7 +5155,9 @@ static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
     OK &= EvaluateVarDecl(Info, VD);
 
   if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D))
-    DD->VisitHoldingVars([&](VarDecl *HD) { OK &= EvaluateDecl(Info, HD); });
+    for (auto *BD : DD->flat_bindings())
+      if (auto *VD = BD->getHoldingVar())
+        OK &= EvaluateDecl(Info, VD);
 
   return OK;
 }
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index db424dabc0575f..df23ce6d48cd71 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -5067,10 +5067,9 @@ CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *VD, llvm::Value *Storage,
   assert(CGM.getCodeGenOpts().hasReducedDebugInfo());
 
   if (auto *DD = dyn_cast<DecompositionDecl>(VD)) {
-    DD->VisitBindings([&](BindingDecl *B) {
+    for (BindingDecl *B : DD->flat_bindings())
       EmitDeclare(B, Storage, std::nullopt, Builder,
                   VD->getType()->isReferenceType());
-    });
     // Don't emit an llvm.dbg.declare for the composite storage as it doesn't
     // correspond to a user variable.
     return nullptr;
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 06b4f39e1e3d81..34c33ef586ba37 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -162,7 +162,10 @@ 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->VisitHoldingVars([&](VarDecl *HD) { EmitVarDecl(*HD); });
+      for (auto *B : DD->flat_bindings())
+        if (auto *HD = B->getHoldingVar())
+          EmitVarDecl(*HD);
+
     return;
   }
 
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index bfd459dabafe10..a5bd54dd41e6b6 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6987,7 +6987,10 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
   case Decl::VarTemplateSpecialization:
     EmitGlobal(cast<VarDecl>(D));
     if (auto *DD = dyn_cast<DecompositionDecl>(D))
-      DD->VisitHoldingVars([&](VarDecl *HD) { EmitGlobal(HD); });
+      for (auto *B : DD->flat_bindings())
+        if (auto *HD = B->getHoldingVar())
+          EmitGlobal(HD);
+
     break;
 
   // Indirect fields from global anonymous structs and unions can be
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index 079ba597ed0cf1..4cd277e3821633 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1068,9 +1068,9 @@ static CanThrowResult canVarDeclThrow(Sema &Self, const VarDecl *VD) {
 
   // If this is a decomposition declaration, bindings might throw.
   if (auto *DD = dyn_cast<DecompositionDecl>(VD))
-    DD->VisitHoldingVars([&](VarDecl *HD) {
-      CT = mergeCanThrow(CT, canVarDeclThrow(Self, HD));
-    });
+    for (auto *B : DD->flat_bindings())
+      if (auto *HD = B->getHoldingVar())
+        CT = mergeCanThrow(CT, canVarDeclThrow(Self, HD));
 
   return CT;
 }

>From 80acb717f7617b1983cfde72d62461c2533fc6e8 Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Tue, 14 Jan 2025 17:01:10 -0800
Subject: [PATCH 10/14] [Clang][P1061] Use range concat for flat_bindings

---
 clang/include/clang/AST/DeclCXX.h | 96 +++++++++----------------------
 clang/lib/AST/DeclCXX.cpp         | 27 +--------
 2 files changed, 29 insertions(+), 94 deletions(-)

diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index be564b4118dc11..2b3adb04664377 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4150,6 +4150,9 @@ class BindingDecl : public ValueDecl {
   /// decomposition declaration, and when the initializer is type-dependent.
   Expr *getBinding() const { return Binding; }
 
+  // Get the array of Exprs when the binding represents a pack.
+  llvm::ArrayRef<Expr *> getBindingPackExprs() const;
+
   /// Get the decomposition declaration that this binding represents a
   /// decomposition of.
   ValueDecl *getDecomposedDecl() const { return Decomp; }
@@ -4220,84 +4223,37 @@ class DecompositionDecl final
     return {getTrailingObjects<BindingDecl *>(), NumBindings};
   }
 
-  // Provide a flattened range to visit each binding directly.
-  class flat_binding_iterator;
-  using flat_binding_range = llvm::iterator_range<flat_binding_iterator>;
-
-  flat_binding_range flat_bindings() const;
-  flat_binding_iterator flat_bindings_begin() const;
-  flat_binding_iterator flat_bindings_end() const;
-
-  void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
+  // Provide a flattened range to visit each binding.
+  auto flat_bindings() const {
+    llvm::ArrayRef<BindingDecl *> Bindings = bindings();
+    llvm::ArrayRef<Expr *> PackExprs;
 
-  static bool classof(const Decl *D) { return classofKind(D->getKind()); }
-  static bool classofKind(Kind K) { return K == Decomposition; }
-};
-
-/// Iterate bindings that may be contain a nested pack of bindings.
-class DecompositionDecl::flat_binding_iterator {
-  friend class DecompositionDecl;
-
-  using ItrTy = llvm::ArrayRef<BindingDecl *>::iterator;
-  using PackItrTy = llvm::ArrayRef<Expr *>::iterator;
-
-  llvm::ArrayRef<BindingDecl *> Bindings;
-  llvm::ArrayRef<Expr *> PackExprs;
-  ItrTy Itr = nullptr;
-  PackItrTy PackItr = nullptr;
-
-  explicit flat_binding_iterator(llvm::ArrayRef<BindingDecl *> Bindings,
-                                 ItrTy Itr, llvm::ArrayRef<Expr *> PackExprs,
-                                 PackItrTy PackItr)
-      : Bindings(Bindings), PackExprs(PackExprs), Itr(Itr), PackItr(PackItr) {}
-
-public:
-  flat_binding_iterator() = default;
+    // Split the bindings into subranges split by the pack.
+    auto S1 = Bindings.take_until(
+        [](BindingDecl *BD) { return BD->isParameterPack(); });
 
-  using value_type = BindingDecl *;
-  using reference = BindingDecl *&;
-  using pointer = BindingDecl **;
-  using iterator_category = std::forward_iterator_tag;
-
-  BindingDecl *operator*() {
-    if (!(*Itr)->isParameterPack())
-      return *Itr;
-
-    auto *DRE = cast<DeclRefExpr>(*PackItr);
-    return cast<BindingDecl>(DRE->getDecl());
-  }
-
-  flat_binding_iterator &operator++() {
-    if ((*Itr)->isParameterPack()) {
-      ++PackItr;
-      if (PackItr != PackExprs.end())
-        return *this;
-      else
-        PackItr = nullptr;
+    Bindings = Bindings.drop_front(S1.size());
+    if (!Bindings.empty()) {
+      PackExprs = Bindings.front()->getBindingPackExprs();
+      Bindings = Bindings.drop_front();
     }
-    ++Itr;
-    return *this;
-  }
 
-  flat_binding_iterator operator++(int) {
-    flat_binding_iterator Temp = *this;
-    ++*this;
-    return Temp;
-  }
+    auto S2 = llvm::map_range(PackExprs, [](Expr *E) {
+      auto *DRE = cast<DeclRefExpr>(E);
+      return cast<BindingDecl>(DRE->getDecl());
+    });
 
-  bool operator==(const flat_binding_iterator &Other) const {
-    return Itr == Other.Itr && PackItr == Other.PackItr;
+    // llvm::concat must take temporaries or it will capture
+    // references.
+    return llvm::concat<BindingDecl *>(std::move(S1), std::move(S2),
+                                       std::move(Bindings));
   }
 
-  bool operator!=(const flat_binding_iterator &Other) const {
-    return Itr != Other.Itr || PackItr != Other.PackItr;
-  }
-};
+  void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
 
-inline DecompositionDecl::flat_binding_range
-DecompositionDecl::flat_bindings() const {
-  return flat_binding_range(flat_bindings_begin(), flat_bindings_end());
-}
+  static bool classof(const Decl *D) { return classofKind(D->getKind()); }
+  static bool classofKind(Kind K) { return K == Decomposition; }
+};
 
 /// An instance of this class represents the declaration of a property
 /// member.  This is a Microsoft extension to C++, first introduced in
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 6d9213b5d8037f..7c286a42e338fb 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3425,30 +3425,9 @@ VarDecl *BindingDecl::getHoldingVar() const {
   return VD;
 }
 
-DecompositionDecl::flat_binding_iterator
-DecompositionDecl::flat_bindings_begin() const {
-  if (NumBindings == 0)
-    return flat_bindings_end();
-
-  auto Bindings = bindings();
-  BindingDecl *First = *Bindings.begin();
-  llvm::ArrayRef<Expr *> PackExprs;
-
-  if (NumBindings > 0 && First->isParameterPack()) {
-    auto *RP = cast<ResolvedUnexpandedPackExpr>(First->getBinding());
-    PackExprs = RP->getExprs();
-  }
-
-  return flat_binding_iterator(Bindings, Bindings.begin(), PackExprs,
-                               PackExprs.begin());
-}
-
-DecompositionDecl::flat_binding_iterator
-DecompositionDecl::flat_bindings_end() const {
-  auto Bindings = bindings();
-  llvm::ArrayRef<Expr *> PackExprs;
-
-  return flat_binding_iterator(Bindings, Bindings.end(), PackExprs, nullptr);
+llvm::ArrayRef<Expr *> BindingDecl::getBindingPackExprs() const {
+  auto *RP = cast<ResolvedUnexpandedPackExpr>(Binding);
+  return RP->getExprs();
 }
 
 void DecompositionDecl::anchor() {}

>From 360708d3d03c337b613cc910a368488a979c66db Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Wed, 15 Jan 2025 12:23:44 -0800
Subject: [PATCH 11/14] [Clang][P1061] Unremove llvm_unreachable in EmitDecl

---
 clang/lib/CodeGen/CGDecl.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 34c33ef586ba37..55f6b7c882c914 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -104,6 +104,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
   case Decl::Binding:
   case Decl::UnresolvedUsingIfExists:
   case Decl::HLSLBuffer:
+    llvm_unreachable("Declaration should not be in declstmts!");
   case Decl::Record:    // struct/union/class X;
   case Decl::CXXRecord: // struct/union/class X; [C++]
     if (CGDebugInfo *DI = getDebugInfo())

>From ea249ac2527045d7a77902977d5f296c4ca10ab3 Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Wed, 15 Jan 2025 15:05:10 -0800
Subject: [PATCH 12/14] [Clang][P1061] Move rebuild of resolved packs into
 TreeTransform

---
 clang/lib/Sema/SemaTemplateInstantiate.cpp | 17 ++++-------------
 clang/lib/Sema/TreeTransform.h             | 22 ++++++++++++++++++++--
 2 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index dea44fb3910f78..1c001ac5054d14 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2490,7 +2490,9 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) {
       return TransformFunctionParmPackRefExpr(E, PD);
 
   if (BindingDecl *BD = dyn_cast<BindingDecl>(D); BD && BD->isParameterPack()) {
-    BD = cast<BindingDecl>(TransformDecl(BD->getLocation(), BD));
+    BD = cast_or_null<BindingDecl>(TransformDecl(BD->getLocation(), BD));
+    if (!BD)
+      return ExprError();
     if (auto *RP =
             dyn_cast_if_present<ResolvedUnexpandedPackExpr>(BD->getBinding())) {
       return TransformResolvedUnexpandedPackExpr(RP);
@@ -2671,18 +2673,7 @@ ExprResult TemplateInstantiator::TransformResolvedUnexpandedPackExpr(
         E->getExpansion(getSema().ArgumentPackSubstitutionIndex));
   }
 
-  if (!AlwaysRebuild())
-    return E;
-
-  SmallVector<Expr *, 12> NewExprs;
-  if (TransformExprs(E->getExprs().begin(), E->getNumExprs(),
-                     /*IsCall=*/false, NewExprs))
-    return ExprError();
-
-  // NOTE: The type is just a superficial PackExpansionType
-  //       that needs no substitution.
-  return ResolvedUnexpandedPackExpr::Create(SemaRef.Context, E->getBeginLoc(),
-                                            E->getType(), NewExprs);
+  return inherited::TransformResolvedUnexpandedPackExpr(E);
 }
 
 QualType TemplateInstantiator::TransformSubstTemplateTypeParmPackType(
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 7af74c6d469a55..8bc4eaf60f8550 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3676,6 +3676,13 @@ class TreeTransform {
                                            FullySubstituted);
   }
 
+  ExprResult RebuildResolvedUnexpandedPackExpr(SourceLocation BeginLoc,
+                                               QualType T,
+                                               ArrayRef<Expr *> Exprs) {
+    return ResolvedUnexpandedPackExpr::Create(SemaRef.Context, BeginLoc, T,
+                                              Exprs);
+  }
+
   /// Build a new expression representing a call to a source location
   ///  builtin.
   ///
@@ -15994,8 +16001,19 @@ TreeTransform<Derived>::TransformFunctionParmPackExpr(FunctionParmPackExpr *E) {
 template <typename Derived>
 ExprResult TreeTransform<Derived>::TransformResolvedUnexpandedPackExpr(
     ResolvedUnexpandedPackExpr *E) {
-  // Default behavior is to do nothing with this transformation.
-  return E;
+  bool ArgumentChanged = false;
+  SmallVector<Expr *, 12> NewExprs;
+  if (TransformExprs(E->getExprs().begin(), E->getNumExprs(),
+                     /*IsCall=*/false, NewExprs, &ArgumentChanged))
+    return ExprError();
+
+  if (!AlwaysRebuild() && !ArgumentChanged)
+    return E;
+
+  // NOTE: The type is just a superficial PackExpansionType
+  //       that needs no substitution.
+  return RebuildResolvedUnexpandedPackExpr(E->getBeginLoc(), E->getType(),
+                                           NewExprs);
 }
 
 template<typename Derived>

>From dda9d89e7520047508732e6fcd43516ae28c8b71 Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Wed, 15 Jan 2025 21:01:28 -0800
Subject: [PATCH 13/14] [Clang][P1061] Fix crash

---
 clang/include/clang/AST/DeclCXX.h | 10 +++++++++-
 clang/lib/AST/DeclCXX.cpp         |  2 ++
 clang/lib/AST/ExprCXX.cpp         |  4 ++--
 clang/lib/Sema/SemaDeclCXX.cpp    | 16 +++++-----------
 4 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 2b3adb04664377..e5cd7bbf958243 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4199,8 +4199,16 @@ class DecompositionDecl final
         NumBindings(Bindings.size()) {
     std::uninitialized_copy(Bindings.begin(), Bindings.end(),
                             getTrailingObjects<BindingDecl *>());
-    for (auto *B : Bindings)
+    for (auto *B : Bindings) {
       B->setDecomposedDecl(this);
+      if (B->isParameterPack()) {
+        for (Expr *E : B->getBindingPackExprs()) {
+          auto *DRE = cast<DeclRefExpr>(E);
+          auto *NestedB = cast<BindingDecl>(DRE->getDecl());
+          NestedB->setDecomposedDecl(this);
+        }
+      }
+    }
   }
 
   void anchor() override;
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 7c286a42e338fb..6d6cb3a0ef18de 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3426,6 +3426,8 @@ VarDecl *BindingDecl::getHoldingVar() const {
 }
 
 llvm::ArrayRef<Expr *> BindingDecl::getBindingPackExprs() const {
+  if (!Binding)
+    return {};
   auto *RP = cast<ResolvedUnexpandedPackExpr>(Binding);
   return RP->getExprs();
 }
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 107ec3dcd9ac65..a1620376d1a40f 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1996,7 +1996,7 @@ ResolvedUnexpandedPackExpr::Create(ASTContext &Ctx, SourceLocation BL,
       new (Mem) ResolvedUnexpandedPackExpr(BL, T, NumExprs);
 
   auto Exprs = New->getExprs();
-  std::fill(Exprs.begin(), Exprs.end(), nullptr);
+  std::uninitialized_fill(Exprs.begin(), Exprs.end(), nullptr);
 
   return New;
 }
@@ -2005,7 +2005,7 @@ ResolvedUnexpandedPackExpr *
 ResolvedUnexpandedPackExpr::Create(ASTContext &Ctx, SourceLocation BL,
                                    QualType T, ArrayRef<Expr *> Exprs) {
   auto *New = Create(Ctx, BL, T, Exprs.size());
-  std::copy(Exprs.begin(), Exprs.end(), New->getExprs().begin());
+  std::uninitialized_copy(Exprs.begin(), Exprs.end(), New->getExprs().begin());
   return New;
 }
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index cf78ea197f77c7..945fddd29e6d51 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -981,20 +981,14 @@ static bool CheckBindingsCount(Sema &S, DecompositionDecl *DD,
   }
 
   if (IsValid && HasPack) {
-    TemplateTypeParmDecl *DummyTemplateParam = TemplateTypeParmDecl::Create(
-        S.Context, S.Context.getTranslationUnitDecl(),
-        /*KeyLoc*/ SourceLocation(), /*NameLoc*/ SourceLocation(),
-        /*TemplateDepth*/ 0, /*AutoParameterPosition*/ 0,
-        /*Identifier*/ nullptr, false, /*IsParameterPack*/ true);
-
     // create the pack expr and assign it to the binding
     unsigned PackSize = MemberCount - Bindings.size() + 1;
     QualType PackType = S.Context.getPackExpansionType(
-        QualType(DummyTemplateParam->getTypeForDecl(), 0), PackSize);
-    (*BindingWithPackItr)
-        ->setBinding(PackType,
-                     ResolvedUnexpandedPackExpr::Create(
-                         S.Context, DD->getBeginLoc(), DecompType, PackSize));
+        S.Context.DependentTy, std::nullopt, /*ExpectsPackInType=*/false);
+    BindingDecl *BD = (*BindingWithPackItr);
+    BD->setBinding(PackType,
+                   ResolvedUnexpandedPackExpr::Create(
+                       S.Context, DD->getBeginLoc(), DecompType, PackSize));
   }
 
   if (IsValid)

>From 0b83b9936c87d96574e9983db838ee9123a7956f Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejasonf at gmail.com>
Date: Wed, 15 Jan 2025 22:11:54 -0800
Subject: [PATCH 14/14] [Clang][P1061] Fix crash (part 2)

---
 clang/lib/Sema/SemaDeclCXX.cpp            | 14 ++++++++------
 clang/test/SemaCXX/cxx2c-binding-pack.cpp | 16 ++++++++++++++++
 2 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 945fddd29e6d51..e596e452712907 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1009,14 +1009,16 @@ struct BindingInitWalker {
   using BindingItrTy = typename ArrayRef<BindingDecl *>::iterator;
   using PackExprItrTy = typename MutableArrayRef<Expr *>::iterator;
   Sema &SemaRef;
+  DecompositionDecl *DecompDecl;
   ArrayRef<BindingDecl *> Bindings;
   ResolvedUnexpandedPackExpr *PackExpr = nullptr;
   MutableArrayRef<Expr *> PackExprNodes;
   BindingItrTy BindingItr;
   PackExprItrTy PackExprItr;
 
-  BindingInitWalker(Sema &S, ArrayRef<BindingDecl *> Bs)
-      : SemaRef(S), Bindings(Bs), BindingItr(Bindings.begin()) {}
+  BindingInitWalker(Sema &S, DecompositionDecl *DD, ArrayRef<BindingDecl *> Bs)
+      : SemaRef(S), DecompDecl(DD), Bindings(Bs), BindingItr(Bindings.begin()) {
+  }
 
   BindingDecl *get() { return *BindingItr; }
 
@@ -1037,7 +1039,7 @@ struct BindingInitWalker {
                               B->getLocation(), B->getIdentifier(), T);
 
       NestedBD->setBinding(T, E);
-      NestedBD->setDecomposedDecl(nullptr);
+      NestedBD->setDecomposedDecl(DecompDecl);
       auto *DE = SemaRef.BuildDeclRefExpr(NestedBD, T.getNonReferenceType(),
                                           VK_LValue, B->getLocation());
       *PackExprItr = DE;
@@ -1065,7 +1067,7 @@ static bool checkSimpleDecomposition(
     return true;
   }
 
-  auto Walker = BindingInitWalker(S, Bindings);
+  auto Walker = BindingInitWalker(S, cast<DecompositionDecl>(Src), Bindings);
   for (unsigned I = 0; I < NumElems; I++) {
     BindingDecl *B = Walker.get();
     SourceLocation Loc = B->getLocation();
@@ -1350,7 +1352,7 @@ static bool checkTupleLikeDecomposition(Sema &S,
     }
   }
 
-  auto Walker = BindingInitWalker(S, Bindings);
+  auto Walker = BindingInitWalker(S, cast<DecompositionDecl>(Src), Bindings);
   for (unsigned I = 0; I < NumElems; I++) {
     BindingDecl *B = Walker.get();
     InitializingBinding InitContext(S, B);
@@ -1541,7 +1543,7 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings,
     return true;
   }
 
-  auto Walker = BindingInitWalker(S, Bindings);
+  auto Walker = BindingInitWalker(S, cast<DecompositionDecl>(Src), Bindings);
 
   //   all of E's non-static data members shall be [...] well-formed
   //   when named as e.name in the context of the structured binding,
diff --git a/clang/test/SemaCXX/cxx2c-binding-pack.cpp b/clang/test/SemaCXX/cxx2c-binding-pack.cpp
index 7e06d63543841a..58a82b6da79fee 100644
--- a/clang/test/SemaCXX/cxx2c-binding-pack.cpp
+++ b/clang/test/SemaCXX/cxx2c-binding-pack.cpp
@@ -73,8 +73,24 @@ void decompose_array() {
   auto [...pack] = arr2;
 }
 
+// Test case by Younan Zhang.
+template <unsigned... P>
+struct S {
+  template <unsigned... Q>
+  struct N {
+    void foo() {
+      int arr[] = {P..., Q...};
+      auto [x, y, ...rest] = arr;
+      [&]() {
+        static_assert(sizeof...(rest) + 2 == sizeof...(P) + sizeof...(Q));
+      }();
+    }
+  };
+};
+
 int main() {
   decompose_array<int>();
   decompose_tuple<fake_tuple>();
   decompose_struct<my_struct>();
+  S<1, 2, 3, 4>::N<5, 6>().foo();
 }



More information about the cfe-commits mailing list