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

This is an implementation of P1061 Structure Bindings Introduce a Pack without  
the ability to use packs outside of templates. There is a couple of ways the 
AST could have been sliced so let me know what you think. The only part of this 
change that I am unsure of is the serialization/deserialization stuff. I 
followed the implementation of other Exprs, but I do not really know how it is 
tested. Thank you for your time considering this.

>From 3c81c5bd989f26331917f1401becc1cfa7f4a454 Mon Sep 17 00:00:00 2001
From: Jason Rice <ricejas...@gmail.com>
Date: Thu, 22 Jul 2021 16:46:33 -0700
Subject: [PATCH] [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;
 

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to