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

>From 77537d523bc164a86b46e83651500a4b37c0c3bf Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Tue, 24 Dec 2024 13:06:44 +0800
Subject: [PATCH 1/8] Reapply "[Clang] Improve diagnostics for expansion length
 mismatch"

... and "[Clang] fix missing initialization of original number of expansions"

This reverts commit acecf68c8b7c3c625cfa00f00f8ddc8f15baae44.

Co-authored-by: Matheus Izvekov <mizve...@gmail.com>
---
 clang/include/clang/Sema/Sema.h               |   8 +-
 clang/include/clang/Sema/SemaInternal.h       |   2 +-
 clang/lib/AST/ExprCXX.cpp                     |   2 +-
 clang/lib/Sema/SemaDeclCXX.cpp                |   2 +-
 clang/lib/Sema/SemaTemplateDeduction.cpp      |   7 +-
 clang/lib/Sema/SemaTemplateVariadic.cpp       | 340 +++++++++---------
 clang/lib/Sema/TreeTransform.h                |   1 +
 .../CXX/temp/temp.decls/temp.variadic/p5.cpp  |  50 +++
 .../SemaTemplate/cxx1z-fold-expressions.cpp   |   2 +-
 clang/test/SemaTemplate/pack-deduction.cpp    |  51 ++-
 10 files changed, 285 insertions(+), 180 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 9ac26d8728446..34f99b8adf467 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -232,9 +232,11 @@ 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 
*>,
-                  SourceLocation>
-    UnexpandedParameterPack;
+using UnexpandedParameterPack = std::pair<
+    llvm::PointerUnion<
+        const TemplateTypeParmType *, const SubstTemplateTypeParmPackType *,
+        const SubstNonTypeTemplateParmPackExpr *, const NamedDecl *>,
+    SourceLocation>;
 
 /// Describes whether we've seen any nullability information for the given
 /// file.
diff --git a/clang/include/clang/Sema/SemaInternal.h 
b/clang/include/clang/Sema/SemaInternal.h
index 95874077050a9..acf6c8146d70d 100644
--- a/clang/include/clang/Sema/SemaInternal.h
+++ b/clang/include/clang/Sema/SemaInternal.h
@@ -75,7 +75,7 @@ getDepthAndIndex(UnexpandedParameterPack UPP) {
   if (const auto *TTP = dyn_cast<const TemplateTypeParmType *>(UPP.first))
     return std::make_pair(TTP->getDepth(), TTP->getIndex());
 
-  return getDepthAndIndex(cast<NamedDecl *>(UPP.first));
+  return getDepthAndIndex(cast<const NamedDecl *>(UPP.first));
 }
 
 class TypoCorrectionConsumer : public VisibleDeclConsumer {
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index c8d61e2cf3f26..afa3db950284b 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1734,7 +1734,7 @@ PackIndexingExpr *PackIndexingExpr::Create(
 NamedDecl *PackIndexingExpr::getPackDecl() const {
   if (auto *D = dyn_cast<DeclRefExpr>(getPackIdExpression()); D) {
     NamedDecl *ND = dyn_cast<NamedDecl>(D->getDecl());
-    assert(ND && "exected a named decl");
+    assert(ND && "expected a named decl");
     return ND;
   }
   assert(false && "invalid declaration kind in pack indexing expression");
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 96aac7871db1e..756937f44e948 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17589,7 +17589,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
   unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
   for (UnexpandedParameterPack &U : Unexpanded) {
     if (getDepthAndIndex(U).first >= FriendDeclDepth) {
-      auto *ND = dyn_cast<NamedDecl *>(U.first);
+      auto *ND = dyn_cast<const NamedDecl *>(U.first);
       if (!ND)
         ND = cast<const TemplateTypeParmType *>(U.first)->getDecl();
       Diag(U.second, diag::friend_template_decl_malformed_pack_expansion)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp 
b/clang/lib/Sema/SemaTemplateDeduction.cpp
index e6ec4a7178e81..6af842121ae79 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -874,8 +874,11 @@ class PackDeductionScope {
       SmallVector<UnexpandedParameterPack, 2> Unexpanded;
       S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
       for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) {
-        unsigned Depth, Index;
-        std::tie(Depth, Index) = getDepthAndIndex(Unexpanded[I]);
+        UnexpandedParameterPack U = Unexpanded[I];
+        if (isa<const SubstTemplateTypeParmPackType *,
+                const SubstNonTypeTemplateParmPackExpr *>(U.first))
+          continue;
+        auto [Depth, Index] = getDepthAndIndex(U);
         if (Depth == Info.getDeducedDepth())
           AddPack(Index);
       }
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp 
b/clang/lib/Sema/SemaTemplateVariadic.cpp
index d9256dbd07d7a..d6c71ecec13b6 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -38,7 +38,7 @@ class CollectUnexpandedParameterPacksVisitor
   unsigned DepthLimit = (unsigned)-1;
 
 #ifndef NDEBUG
-    bool ContainsIntermediatePacks = false;
+  bool ContainsIntermediatePacks = false;
 #endif
 
     void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
@@ -97,6 +97,33 @@ class CollectUnexpandedParameterPacksVisitor
       return true;
     }
 
+    bool VisitSubstTemplateTypeParmPackTypeLoc(
+        SubstTemplateTypeParmPackTypeLoc TL) override {
+      Unexpanded.push_back({TL.getTypePtr(), TL.getNameLoc()});
+#ifdef NDEBUG
+      ContainsIntermediatePacks = true;
+#endif
+      return true;
+    }
+
+    bool VisitSubstTemplateTypeParmPackType(
+        SubstTemplateTypeParmPackType *T) override {
+      Unexpanded.push_back({T, SourceLocation()});
+#ifdef NDEBUG
+      ContainsIntermediatePacks = true;
+#endif
+      return true;
+    }
+
+    bool VisitSubstNonTypeTemplateParmPackExpr(
+        SubstNonTypeTemplateParmPackExpr *E) override {
+      Unexpanded.push_back({E, E->getParameterPackLocation()});
+#ifdef NDEBUG
+      ContainsIntermediatePacks = true;
+#endif
+      return true;
+    }
+
     /// Record occurrences of function and non-type template
     /// parameter packs in an expression.
     bool VisitDeclRefExpr(DeclRefExpr *E) override {
@@ -315,24 +342,6 @@ class CollectUnexpandedParameterPacksVisitor
       return true;
     }
 
-    bool TraverseSubstNonTypeTemplateParmPackExpr(
-        SubstNonTypeTemplateParmPackExpr *) override {
-      ContainsIntermediatePacks = true;
-      return true;
-    }
-
-    bool VisitSubstTemplateTypeParmPackType(
-        SubstTemplateTypeParmPackType *) override {
-      ContainsIntermediatePacks = true;
-      return true;
-    }
-
-    bool VisitSubstTemplateTypeParmPackTypeLoc(
-        SubstTemplateTypeParmPackTypeLoc) override {
-      ContainsIntermediatePacks = true;
-      return true;
-    }
-
     bool containsIntermediatePacks() const { return ContainsIntermediatePacks; 
}
 #endif
 };
@@ -374,7 +383,8 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
           auto *TTPD = dyn_cast<TemplateTypeParmDecl>(LocalPack);
           return TTPD && TTPD->getTypeForDecl() == TTPT;
         }
-        return declaresSameEntity(cast<NamedDecl *>(Pack.first), LocalPack);
+        return declaresSameEntity(cast<const NamedDecl *>(Pack.first),
+                                  LocalPack);
       };
       if (llvm::any_of(CSI->LocalPacks, DeclaresThisPack))
         ParamPackReferences.push_back(Pack);
@@ -425,8 +435,8 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
     if (const TemplateTypeParmType *TTP
           = Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>())
       Name = TTP->getIdentifier();
-    else if (NamedDecl *ND = Unexpanded[I].first.dyn_cast<NamedDecl *>())
-      Name = ND->getIdentifier();
+    else
+      Name = cast<const NamedDecl *>(Unexpanded[I].first)->getIdentifier();
 
     if (Name && NamesKnown.insert(Name).second)
       Names.push_back(Name);
@@ -500,7 +510,7 @@ bool 
Sema::DiagnoseUnexpandedParameterPackInRequiresExpr(RequiresExpr *RE) {
   llvm::SmallPtrSet<NamedDecl*, 8> ParmSet(Parms.begin(), Parms.end());
   SmallVector<UnexpandedParameterPack, 2> UnexpandedParms;
   for (auto Parm : Unexpanded)
-    if (ParmSet.contains(Parm.first.dyn_cast<NamedDecl *>()))
+    if (ParmSet.contains(dyn_cast<const NamedDecl *>(Parm.first)))
       UnexpandedParms.push_back(Parm);
   if (UnexpandedParms.empty())
     return false;
@@ -749,6 +759,19 @@ ExprResult Sema::CheckPackExpansion(Expr *Pattern, 
SourceLocation EllipsisLoc,
     PackExpansionExpr(Context.DependentTy, Pattern, EllipsisLoc, 
NumExpansions);
 }
 
+static bool IsUnexpandedPackExpansion(const TemplateArgument &TA) {
+  if (!TA.isPackExpansion())
+    return false;
+
+  if (TA.getKind() == TemplateArgument::Type)
+    return !TA.getAsType()->getAs<PackExpansionType>()->getNumExpansions();
+
+  if (TA.getKind() == TemplateArgument::Expression)
+    return !cast<PackExpansionExpr>(TA.getAsExpr())->getNumExpansions();
+
+  return !TA.getNumTemplateExpansions();
+}
+
 bool Sema::CheckParameterPacksForExpansion(
     SourceLocation EllipsisLoc, SourceRange PatternRange,
     ArrayRef<UnexpandedParameterPack> Unexpanded,
@@ -756,125 +779,104 @@ bool Sema::CheckParameterPacksForExpansion(
     bool &RetainExpansion, std::optional<unsigned> &NumExpansions) {
   ShouldExpand = true;
   RetainExpansion = false;
-  std::pair<IdentifierInfo *, SourceLocation> FirstPack;
-  bool HaveFirstPack = false;
-  std::optional<unsigned> NumPartialExpansions;
-  SourceLocation PartiallySubstitutedPackLoc;
-  typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
+  std::pair<const IdentifierInfo *, SourceLocation> FirstPack;
+  std::optional<std::pair<unsigned, SourceLocation>> PartialExpansion;
+  std::optional<unsigned> CurNumExpansions, CurMaximumOfLeastExpansions;
 
-  for (UnexpandedParameterPack ParmPack : Unexpanded) {
+  for (auto [P, Loc] : Unexpanded) {
     // Compute the depth and index for this parameter pack.
-    unsigned Depth = 0, Index = 0;
-    IdentifierInfo *Name;
-    bool IsVarDeclPack = false;
-    FunctionParmPackExpr *BindingPack = nullptr;
-
-    if (const TemplateTypeParmType *TTP =
-            ParmPack.first.dyn_cast<const TemplateTypeParmType *>()) {
-      Depth = TTP->getDepth();
-      Index = TTP->getIndex();
-      Name = TTP->getIdentifier();
-    } else {
-      NamedDecl *ND = cast<NamedDecl *>(ParmPack.first);
-      if (isa<VarDecl>(ND))
-        IsVarDeclPack = true;
-      else if (isa<BindingDecl>(ND)) {
+    std::optional<std::pair<unsigned, unsigned>> Pos;
+    unsigned NewPackSize, PendingPackExpansionSize = 0;
+    const auto *ND = dyn_cast_if_present<const NamedDecl *>(P);
+    if (ND) {
+      if (isa<VarDecl>(ND)) {
+        auto *DAP = dyn_cast<LocalInstantiationScope::DeclArgumentPack *>(
+            *CurrentInstantiationScope->findInstantiationOf(ND));
+        if (!DAP) {
+          // We can't expand this function parameter pack, so we can't expand
+          // the pack expansion.
+          ShouldExpand = false;
+          continue;
+        }
+        NewPackSize = DAP->size();
+      } else if (isa<BindingDecl>(ND)) {
         // Find the instantiated BindingDecl and check it for a resolved pack.
-        llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
-            CurrentInstantiationScope->findInstantiationOf(ND);
+        llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *>
+            *Instantiation = 
CurrentInstantiationScope->findInstantiationOf(ND);
         Decl *B = cast<Decl *>(*Instantiation);
         Expr *BindingExpr = cast<BindingDecl>(B)->getBinding();
-        BindingPack = cast_if_present<FunctionParmPackExpr>(BindingExpr);
+        auto *BindingPack = cast_if_present<FunctionParmPackExpr>(BindingExpr);
         if (!BindingPack) {
           ShouldExpand = false;
           continue;
         }
+        NewPackSize = BindingPack->getNumExpansions();
       } else
-        std::tie(Depth, Index) = getDepthAndIndex(ND);
-
-      Name = ND->getIdentifier();
+        Pos = getDepthAndIndex(ND);
+    } else if (const auto *TTP = dyn_cast<const TemplateTypeParmType *>(P)) {
+      Pos = {TTP->getDepth(), TTP->getIndex()};
+      ND = TTP->getDecl();
+      // FIXME: We either should have some fallback for canonical TTP, or
+      //        never have canonical TTP here.
+    } else if (const auto *STP =
+                   dyn_cast<const SubstTemplateTypeParmPackType *>(P)) {
+      NewPackSize = STP->getNumArgs();
+      PendingPackExpansionSize = llvm::count_if(
+          STP->getArgumentPack().getPackAsArray(), IsUnexpandedPackExpansion);
+      ND = STP->getReplacedParameter();
+    } else {
+      const auto *SEP = cast<const SubstNonTypeTemplateParmPackExpr *>(P);
+      NewPackSize = SEP->getArgumentPack().pack_size();
+      PendingPackExpansionSize = llvm::count_if(
+          SEP->getArgumentPack().getPackAsArray(), IsUnexpandedPackExpansion);
+      ND = SEP->getParameterPack();
     }
 
-    // Determine the size of this argument pack.
-    unsigned NewPackSize, PendingPackExpansionSize = 0;
-    if (IsVarDeclPack) {
-      // Figure out whether we're instantiating to an argument pack or not.
-      llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
-          CurrentInstantiationScope->findInstantiationOf(
-              cast<NamedDecl *>(ParmPack.first));
-      if (isa<DeclArgumentPack *>(*Instantiation)) {
-        // We could expand this function parameter pack.
-        NewPackSize = cast<DeclArgumentPack *>(*Instantiation)->size();
-      } else {
-        // We can't expand this function parameter pack, so we can't expand
-        // the pack expansion.
-        ShouldExpand = false;
-        continue;
-      }
-    } else if (BindingPack) {
-      NewPackSize = BindingPack->getNumExpansions();
-    } else {
+    if (Pos) {
       // 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
       // want to check any parameter packs we *do* have arguments for.
-      if (Depth >= TemplateArgs.getNumLevels() ||
-          !TemplateArgs.hasTemplateArgument(Depth, Index)) {
+      if (Pos->first >= TemplateArgs.getNumLevels() ||
+          !TemplateArgs.hasTemplateArgument(Pos->first, Pos->second)) {
         ShouldExpand = false;
         continue;
       }
-
       // Determine the size of the argument pack.
       ArrayRef<TemplateArgument> Pack =
-          TemplateArgs(Depth, Index).getPackAsArray();
+          TemplateArgs(Pos->first, Pos->second).getPackAsArray();
       NewPackSize = Pack.size();
       PendingPackExpansionSize =
-          llvm::count_if(Pack, [](const TemplateArgument &TA) {
-            if (!TA.isPackExpansion())
-              return false;
-
-            if (TA.getKind() == TemplateArgument::Type)
-              return !TA.getAsType()
-                          ->castAs<PackExpansionType>()
-                          ->getNumExpansions();
-
-            if (TA.getKind() == TemplateArgument::Expression)
-              return !cast<PackExpansionExpr>(TA.getAsExpr())
-                          ->getNumExpansions();
-
-            return !TA.getNumTemplateExpansions();
-          });
-    }
-
-    // C++0x [temp.arg.explicit]p9:
-    //   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 (NamedDecl *PartialPack =
-              CurrentInstantiationScope->getPartiallySubstitutedPack()) {
-        unsigned PartialDepth, PartialIndex;
-        std::tie(PartialDepth, PartialIndex) = getDepthAndIndex(PartialPack);
-        if (PartialDepth == Depth && PartialIndex == Index) {
+          llvm::count_if(Pack, IsUnexpandedPackExpansion);
+      // C++0x [temp.arg.explicit]p9:
+      //   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 (CurrentInstantiationScope)
+        if (const NamedDecl *PartialPack =
+                CurrentInstantiationScope->getPartiallySubstitutedPack();
+            PartialPack && getDepthAndIndex(PartialPack) == *Pos) {
           RetainExpansion = true;
           // We don't actually know the new pack size yet.
-          NumPartialExpansions = NewPackSize;
-          PartiallySubstitutedPackLoc = ParmPack.second;
+          PartialExpansion = {NewPackSize, Loc};
           continue;
         }
-      }
     }
 
-    if (!NumExpansions) {
+    unsigned LeastNewPackSize = NewPackSize - PendingPackExpansionSize;
+    if (PendingPackExpansionSize)
+      CurMaximumOfLeastExpansions =
+          CurMaximumOfLeastExpansions
+              ? std::max(LeastNewPackSize, *CurMaximumOfLeastExpansions)
+              : LeastNewPackSize;
+
+    // FIXME: Workaround for Canonical TTP.
+    const IdentifierInfo *Name = ND ? ND->getIdentifier() : nullptr;
+    if (!CurNumExpansions) {
       // This is the first pack we've seen for which we have an argument.
       // Record it.
-      NumExpansions = NewPackSize;
-      FirstPack.first = Name;
-      FirstPack.second = ParmPack.second;
-      HaveFirstPack = true;
-      continue;
-    }
-
-    if (NewPackSize != *NumExpansions) {
+      CurNumExpansions = NewPackSize;
+      FirstPack = {Name, Loc};
+    } else if (NewPackSize != *CurNumExpansions) {
       // In some cases, we might be handling packs with unexpanded template
       // arguments. For example, this can occur when substituting into a type
       // alias declaration that uses its injected template parameters as
@@ -888,31 +890,40 @@ bool Sema::CheckParameterPacksForExpansion(
       // Pack comes from another template parameter. 'S<int>' is first
       // instantiated, expanding the outer pack 'Outer' to <int>. The alias
       // declaration is accordingly substituted, leaving the template arguments
-      // as unexpanded
-      // '<Pack...>'.
+      // as unexpanded '<Pack...>'.
       //
       // Since we have no idea of the size of '<Pack...>' until its expansion,
       // we shouldn't assume its pack size for validation. However if we are
       // certain that there are extra arguments beyond unexpanded packs, in
       // which case the pack size is already larger than the previous 
expansion,
       // we can complain that before instantiation.
-      unsigned LeastNewPackSize = NewPackSize - PendingPackExpansionSize;
-      if (PendingPackExpansionSize && LeastNewPackSize <= *NumExpansions) {
+      if (PendingPackExpansionSize && LeastNewPackSize <= *CurNumExpansions) {
         ShouldExpand = false;
         continue;
       }
       // C++0x [temp.variadic]p5:
       //   All of the parameter packs expanded by a pack expansion shall have
       //   the same number of arguments specified.
-      if (HaveFirstPack)
-        Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict)
-            << FirstPack.first << Name << *NumExpansions
-            << (LeastNewPackSize != NewPackSize) << LeastNewPackSize
-            << SourceRange(FirstPack.second) << SourceRange(ParmPack.second);
-      else
-        Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_multilevel)
-            << Name << *NumExpansions << (LeastNewPackSize != NewPackSize)
-            << LeastNewPackSize << SourceRange(ParmPack.second);
+      Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict)
+          << FirstPack.first << Name << *CurNumExpansions
+          << (LeastNewPackSize != NewPackSize) << LeastNewPackSize
+          << SourceRange(FirstPack.second) << SourceRange(Loc);
+      return true;
+    }
+  }
+
+  if (NumExpansions && CurNumExpansions &&
+      *NumExpansions != *CurNumExpansions) {
+    if (CurMaximumOfLeastExpansions &&
+        *CurMaximumOfLeastExpansions <= *NumExpansions) {
+      ShouldExpand = false;
+    } else {
+      Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_multilevel)
+          << FirstPack.first << *NumExpansions
+          << bool(CurMaximumOfLeastExpansions)
+          << (CurMaximumOfLeastExpansions ? *CurMaximumOfLeastExpansions
+                                          : *CurNumExpansions)
+          << SourceRange(FirstPack.second);
       return true;
     }
   }
@@ -926,17 +937,18 @@ bool Sema::CheckParameterPacksForExpansion(
   //
   // ... a call to 'A<int, int>().f<int>' should expand the pack once and
   // retain an expansion.
-  if (NumPartialExpansions) {
-    if (NumExpansions && *NumExpansions < *NumPartialExpansions) {
+  if (PartialExpansion) {
+    if (CurNumExpansions && *CurNumExpansions < PartialExpansion->first) {
       NamedDecl *PartialPack =
           CurrentInstantiationScope->getPartiallySubstitutedPack();
       Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_partial)
-          << PartialPack << *NumPartialExpansions << *NumExpansions
-          << SourceRange(PartiallySubstitutedPackLoc);
+          << PartialPack << PartialExpansion->first << *CurNumExpansions
+          << SourceRange(PartialExpansion->second);
       return true;
     }
-
-    NumExpansions = NumPartialExpansions;
+    NumExpansions = PartialExpansion->first;
+  } else {
+    NumExpansions = CurNumExpansions;
   }
 
   return false;
@@ -946,47 +958,47 @@ std::optional<unsigned> 
Sema::getNumArgumentsInExpansionFromUnexpanded(
     llvm::ArrayRef<UnexpandedParameterPack> Unexpanded,
     const MultiLevelTemplateArgumentList &TemplateArgs) {
   std::optional<unsigned> Result;
-  for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) {
-    // Compute the depth and index for this parameter pack.
-    unsigned Depth;
-    unsigned Index;
+  auto SetResultSize = [&Result](unsigned Size) {
+    assert((!Result || *Result == Size) && "inconsistent pack sizes");
+    Result = Size;
+  };
+  auto SetResultPos = [&](const std::pair<unsigned, unsigned> &Pos) -> bool {
+    unsigned Depth = Pos.first, Index = Pos.second;
+    if (Depth >= TemplateArgs.getNumLevels() ||
+        !TemplateArgs.hasTemplateArgument(Depth, Index))
+      // The pattern refers to an unknown template argument. We're not ready to
+      // expand this pack yet.
+      return true;
+    // Determine the size of the argument pack.
+    SetResultSize(TemplateArgs(Depth, Index).pack_size());
+    return false;
+  };
 
-    if (const TemplateTypeParmType *TTP =
-            Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>()) {
-      Depth = TTP->getDepth();
-      Index = TTP->getIndex();
+  for (auto [I, _] : Unexpanded) {
+    if (const auto *TTP = I.dyn_cast<const TemplateTypeParmType *>()) {
+      if (SetResultPos({TTP->getDepth(), TTP->getIndex()}))
+        return std::nullopt;
+    } else if (const auto *STP =
+                   I.dyn_cast<const SubstTemplateTypeParmPackType *>()) {
+      SetResultSize(STP->getNumArgs());
+    } else if (const auto *SEP =
+                   I.dyn_cast<const SubstNonTypeTemplateParmPackExpr *>()) {
+      SetResultSize(SEP->getArgumentPack().pack_size());
     } else {
-      NamedDecl *ND = cast<NamedDecl *>(Unexpanded[I].first);
+      const auto *ND = cast<const NamedDecl *>(I);
+      // Function parameter pack or init-capture pack.
       if (isa<VarDecl>(ND)) {
-        // Function parameter pack or init-capture pack.
-        typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
-
-        llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
-            CurrentInstantiationScope->findInstantiationOf(
-                cast<NamedDecl *>(Unexpanded[I].first));
-        if (isa<Decl *>(*Instantiation))
+        const auto *DAP = dyn_cast<LocalInstantiationScope::DeclArgumentPack 
*>(
+            *CurrentInstantiationScope->findInstantiationOf(ND));
+        if (!DAP)
           // The pattern refers to an unexpanded pack. We're not ready to 
expand
           // this pack yet.
           return std::nullopt;
-
-        unsigned Size = cast<DeclArgumentPack *>(*Instantiation)->size();
-        assert((!Result || *Result == Size) && "inconsistent pack sizes");
-        Result = Size;
-        continue;
+        SetResultSize(DAP->size());
+      } else if (SetResultPos(getDepthAndIndex(ND))) {
+        return std::nullopt;
       }
-
-      std::tie(Depth, Index) = getDepthAndIndex(ND);
     }
-    if (Depth >= TemplateArgs.getNumLevels() ||
-        !TemplateArgs.hasTemplateArgument(Depth, Index))
-      // The pattern refers to an unknown template argument. We're not ready to
-      // expand this pack yet.
-      return std::nullopt;
-
-    // Determine the size of the argument pack.
-    unsigned Size = TemplateArgs(Depth, Index).pack_size();
-    assert((!Result || *Result == Size) && "inconsistent pack sizes");
-    Result = Size;
   }
 
   return Result;
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index a09f623ff8fbf..328a0e7e0b243 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -6275,6 +6275,7 @@ bool TreeTransform<Derived>::TransformFunctionTypeParams(
                                        = dyn_cast<PackExpansionType>(OldType)) 
{
       // We have a function parameter pack that may need to be expanded.
       QualType Pattern = Expansion->getPattern();
+      NumExpansions = Expansion->getNumExpansions();
       SmallVector<UnexpandedParameterPack, 2> Unexpanded;
       getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded);
 
diff --git a/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp 
b/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp
index 16e668e971a21..dc254e0e3e11a 100644
--- a/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp
@@ -517,6 +517,56 @@ int fn() {
 }
 }
 
+namespace GH56094 {
+template <typename... T> struct D {
+  template <typename... U> using B = int(int (*...p)(T, U));
+  // expected-error@-1 {{pack expansion contains parameter packs 'T' and 'U' 
that have different lengths (1 vs. 2)}}
+  template <typename U1, typename U2> D(B<U1, U2> *);
+  // expected-note@-1 {{in instantiation of template type alias 'B' requested 
here}}
+};
+using t1 = D<float>::B<int>;
+// expected-note@-1 {{in instantiation of template class 'GH56094::D<float>' 
requested here}}
+
+template <bool...> struct F {};
+template <class...> struct G {};
+template <bool... I> struct E {
+  template <bool... U> using B = G<F<I, U>...>;
+  // expected-error@-1 {{pack expansion contains parameter packs 'I' and 'U' 
that have different lengths (1 vs. 2)}}
+  template <bool U1, bool U2> E(B<U1, U2> *);
+  // expected-note@-1 {{in instantiation of template type alias 'B' requested 
here}}
+};
+using t2 = E<true>::B<false>;
+// expected-note@-1 {{in instantiation of template class 'GH56094::E<true>' 
requested here}}
+} // namespace GH56094
+
+namespace GH56094 {
+#if __cplusplus >= 201402L
+template <class> struct A; // expected-note {{template is declared here}}
+template <class> using B = char;
+template <class ...Cs> int C{ A<B<Cs>>{}... }; // expected-error {{implicit 
instantiation of undefined template}}
+#endif
+} // namespace GH56094
+
+namespace GH58679 {
+#if __cplusplus >= 201402L
+template <class> constexpr int A = 1;
+
+template <int> struct B;
+template <> struct B<1> { using b1 = void; };
+
+template <class> using C = char;
+
+template <class... Ds> int D{ B<A<C<Ds>>>{}... };
+
+struct E {
+  template <class E1, class = typename B<A<E1>>::b1> E(E1);
+};
+
+template <typename... Es> int F{ E(C<Es>{})... };
+#endif
+} // namespace GH58679
+
+
 namespace GH58452 {
 template <typename... As> struct A {
   template <typename... Bs> using B = void(As...(Bs));
diff --git a/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp 
b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp
index 47a252eb335f6..e357a73e855fa 100644
--- a/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp
+++ b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp
@@ -97,7 +97,7 @@ namespace PR41845 {
   template <int I> struct Constant {};
 
   template <int... Is> struct Sum {
-    template <int... Js> using type = Constant<((Is + Js) + ... + 0)>; // 
expected-error {{pack expansion contains parameter pack 'Js' that has a 
different length (1 vs. 2) from outer parameter packs}}
+    template <int... Js> using type = Constant<((Is + Js) + ... + 0)>; // 
expected-error {{pack expansion contains parameter packs 'Is' and 'Js' that 
have different lengths (1 vs. 2)}}
   };
 
   Sum<1>::type<1, 2> x; // expected-note {{instantiation of}}
diff --git a/clang/test/SemaTemplate/pack-deduction.cpp 
b/clang/test/SemaTemplate/pack-deduction.cpp
index b3104609994a4..c96ea74c9aebc 100644
--- a/clang/test/SemaTemplate/pack-deduction.cpp
+++ b/clang/test/SemaTemplate/pack-deduction.cpp
@@ -134,14 +134,14 @@ namespace partial_full_mix {
   template<typename ...T> struct tuple {};
   template<typename ...T> struct A {
     template<typename ...U> static pair<tuple<T...>, tuple<U...>> f(pair<T, U> 
...p);
-    // expected-note@-1 {{[with U = <char, double, long>]: pack expansion 
contains parameter pack 'U' that has a different length (2 vs. 3) from outer 
parameter packs}}
+    // expected-note@-1 {{[with U = <char, double, long>]: pack expansion 
contains parameter packs 'T' and 'U' that have different lengths (2 vs. 3)}}
     // expected-note@-2 {{[with U = <char, double, void>]: pack expansion 
contains parameter pack 'U' that has a different length (at least 3 vs. 2) from 
outer parameter packs}}
 
     template<typename ...U> static pair<tuple<T...>, tuple<U...>> g(pair<T, U> 
...p, ...);
-    // expected-note@-1 {{[with U = <char, double, long>]: pack expansion 
contains parameter pack 'U' that has a different length (2 vs. 3) from outer 
parameter packs}}
+    // expected-note@-1 {{[with U = <char, double, long>]: pack expansion 
contains parameter packs 'T' and 'U' that have different lengths (2 vs. 3)}}
 
     template<typename ...U> static tuple<U...> h(tuple<pair<T, U>..., 
pair<int, int>>);
-    // expected-note@-1 {{[with U = <int[2]>]: pack expansion contains 
parameter pack 'U' that has a different length (2 vs. 1) from outer parameter 
packs}}
+    // expected-note@-1 {{[with U = <int[2]>]: pack expansion contains 
parameter packs 'T' and 'U' that have different lengths (2 vs. 1)}}
   };
 
   pair<tuple<int, float>, tuple<char, double>> k1 = A<int, 
float>().f<char>(pair<int, char>(), pair<float, double>());
@@ -211,7 +211,7 @@ using any_pairs_list = X<int, int>::Y<T...>; // 
#any_pairs_list
 
 template <class... T>
 using any_pairs_list_2 = X<int, int>::Y<>;
-// expected-error@#GH17042_Y {{different length (2 vs. 0)}} \
+// expected-error@#GH17042_Y {{different lengths (2 vs. 0)}} \
 // expected-note@-1 {{requested here}}
 
 template <class A, class B, class... P>
@@ -219,20 +219,20 @@ using any_pairs_list_3 = X<int, int>::Y<A, B, P...>; // 
#any_pairs_list_3
 
 template <class A, class B, class C, class... P>
 using any_pairs_list_4 = X<int, int>::Y<A, B, C, P...>;
-// expected-error@#GH17042_Y {{different length (2 vs. at least 3)}} \
+// expected-error@#GH17042_Y {{different lengths (2 vs. at least 3)}} \
 // expected-note@-1 {{requested here}}
 
 static_assert(__is_same(any_pairs_list<char, char>, X<void(int, char), 
void(int, char)>), "");
 
 static_assert(!__is_same(any_pairs_list<char, char, char>, X<void(int, char), 
void(int, char)>), "");
-// expected-error@#GH17042_Y {{different length (2 vs. 3)}} \
+// expected-error@#GH17042_Y {{different lengths (2 vs. 3)}} \
 // expected-note@#any_pairs_list {{requested here}} \
 // expected-note@-1 {{requested here}}
 
 static_assert(__is_same(any_pairs_list_3<char, char>, X<void(int, char), 
void(int, char)>), "");
 
 static_assert(!__is_same(any_pairs_list_3<char, char, float>, X<void(int, 
char), void(int, char)>), "");
-// expected-error@#GH17042_Y {{different length (2 vs. 3)}} \
+// expected-error@#GH17042_Y {{different lengths (2 vs. 3)}} \
 // expected-note@#any_pairs_list_3 {{requested here}} \
 // expected-note@-1 {{requested here}}
 
@@ -257,4 +257,41 @@ template <int... Args1> struct Nttp {
 template <int... Args> using Alias = Nttp<1, 2, 3>::B<Args...>;
 }
 
+// This test case comes from https://bugs.llvm.org/show_bug.cgi?id=16668
+namespace PR16668 {
+  template<int, typename> struct P {};
+
+  template<int ...A> struct S {
+     template<int ...B> using sum = S<A + B ...>; // expected-error 
{{different lengths (3 vs. 4)}}
+
+     template<typename ...Ts> void f(P<A, Ts> ...);
+
+#if 0
+     // FIXME: Substituting into Fn resulted in rejects-valid.
+     template<typename ...Ts> using Fn = void(S::*)(P<A, Ts>...);
+     template<typename ...Ts> void g(Fn<Ts...>);
+#endif
+   };
+
+   template<int ...C> using X = S<1, 2, 3>::sum<4, C ...>; // expected-note 
{{here}}
+
+   using Y = X<5, 6>;
+   using Y = S<5, 7, 9>;
+
+   using Z = X<5, 6, 7>; // expected-note {{here}}
+
+   void g() {
+     P<0, int> p0;
+     P<1, char> p1;
+     P<2, double> p2;
+     using S = S<0, 1, 2>;
+     S s;
+     s.f(p0, p1, p2);
+#if 0
+     s.g(&S::f<int, char, double>);
+     s.g<int, char, double>(&S::f);
+#endif
+   }
+}
+
 }

>From 01ab5f63944dcfd16806ee09f9d6f26c745babb1 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Thu, 13 Mar 2025 13:31:44 +0800
Subject: [PATCH 2/8] Changelogs, which were taken from D128095 and GH56094

---
 clang/docs/ReleaseNotes.rst | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e16cfc69590fa..1ea37cb8f1aa0 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -247,6 +247,8 @@ Improvements to Clang's diagnostics
 - The ``-Wsign-compare`` warning now treats expressions with bitwise not(~) 
and minus(-) as signed integers 
   except for the case where the operand is an unsigned integer
   and throws warning if they are compared with unsigned integers (##18878).
+- When diagnosing multi-level pack expansions of mismatched lengths, Clang will
+  now, in most cases, be able to point to the relevant outer parameter.
 
 - Improve the diagnostics for chained comparisons to report actual expressions 
and operators (#GH129069).
 
@@ -308,6 +310,7 @@ Bug Fixes to C++ Support
   not in the last position.
 - Clang now correctly parses ``if constexpr`` expressions in immediate 
function context. (#GH123524)
 - Fixed an assertion failure affecting code that uses C++23 "deducing this". 
(#GH130272)
+- Fixed multi-level pack expansion of undeclared function parameters. 
(#GH56094)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^

>From 03ba8d4d3aa7cdcbec4904b4408b3e94d9dc04b8 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Fri, 14 Mar 2025 13:58:25 +0800
Subject: [PATCH 3/8] Explanation, assertions

---
 clang/include/clang/Sema/SemaInternal.h |  3 ++
 clang/lib/Sema/SemaConcept.cpp          |  3 ++
 clang/lib/Sema/SemaDeclCXX.cpp          |  4 ++
 clang/lib/Sema/SemaTemplateVariadic.cpp | 49 ++++++++++++++++---------
 4 files changed, 41 insertions(+), 18 deletions(-)

diff --git a/clang/include/clang/Sema/SemaInternal.h 
b/clang/include/clang/Sema/SemaInternal.h
index acf6c8146d70d..26c14646192cd 100644
--- a/clang/include/clang/Sema/SemaInternal.h
+++ b/clang/include/clang/Sema/SemaInternal.h
@@ -72,6 +72,9 @@ inline std::pair<unsigned, unsigned> getDepthAndIndex(const 
NamedDecl *ND) {
 /// Retrieve the depth and index of an unexpanded parameter pack.
 inline std::pair<unsigned, unsigned>
 getDepthAndIndex(UnexpandedParameterPack UPP) {
+  assert(!(isa<SubstTemplateTypeParmPackType, 
SubstNonTypeTemplateParmPackExpr>(
+             UPP.first)) &&
+         "Couldn't find depths for Subst* pack expansions!");
   if (const auto *TTP = dyn_cast<const TemplateTypeParmType *>(UPP.first))
     return std::make_pair(TTP->getDepth(), TTP->getIndex());
 
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 57dc4154a537f..854d3aa43b2a3 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1716,6 +1716,9 @@ bool FoldExpandedConstraint::AreCompatibleForSubsumption(
   Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.Pattern), BPacks);
 
   for (const UnexpandedParameterPack &APack : APacks) {
+    assert(!(isa<const SubstTemplateTypeParmPackType *,
+                 const SubstNonTypeTemplateParmPackExpr *>(APack.first)) &&
+           "The pattern of CXXFoldExpr contains a Subst* node?");
     std::pair<unsigned, unsigned> DepthAndIndex = getDepthAndIndex(APack);
     auto it = llvm::find_if(BPacks, [&](const UnexpandedParameterPack &BPack) {
       return getDepthAndIndex(BPack) == DepthAndIndex;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 756937f44e948..74377b2b3d724 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17588,6 +17588,10 @@ DeclResult Sema::ActOnTemplatedFriendTag(
   collectUnexpandedParameterPacks(QualifierLoc, Unexpanded);
   unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
   for (UnexpandedParameterPack &U : Unexpanded) {
+    assert(
+        !(isa<SubstTemplateTypeParmPackType, SubstNonTypeTemplateParmPackExpr>(
+            U.first)) &&
+        "Shouldn't see Subst* nodes from the parser");
     if (getDepthAndIndex(U).first >= FriendDeclDepth) {
       auto *ND = dyn_cast<const NamedDecl *>(U.first);
       if (!ND)
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp 
b/clang/lib/Sema/SemaTemplateVariadic.cpp
index d6c71ecec13b6..d8a15fc6e0d03 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -759,19 +759,6 @@ ExprResult Sema::CheckPackExpansion(Expr *Pattern, 
SourceLocation EllipsisLoc,
     PackExpansionExpr(Context.DependentTy, Pattern, EllipsisLoc, 
NumExpansions);
 }
 
-static bool IsUnexpandedPackExpansion(const TemplateArgument &TA) {
-  if (!TA.isPackExpansion())
-    return false;
-
-  if (TA.getKind() == TemplateArgument::Type)
-    return !TA.getAsType()->getAs<PackExpansionType>()->getNumExpansions();
-
-  if (TA.getKind() == TemplateArgument::Expression)
-    return !cast<PackExpansionExpr>(TA.getAsExpr())->getNumExpansions();
-
-  return !TA.getNumTemplateExpansions();
-}
-
 bool Sema::CheckParameterPacksForExpansion(
     SourceLocation EllipsisLoc, SourceRange PatternRange,
     ArrayRef<UnexpandedParameterPack> Unexpanded,
@@ -782,6 +769,22 @@ bool Sema::CheckParameterPacksForExpansion(
   std::pair<const IdentifierInfo *, SourceLocation> FirstPack;
   std::optional<std::pair<unsigned, SourceLocation>> PartialExpansion;
   std::optional<unsigned> CurNumExpansions, CurMaximumOfLeastExpansions;
+  typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
+
+  // Determine if a pack expansion type contains an unresolved pack expansion.
+  // (i.e. the length of the expanded size is unknown at this point.)
+  static auto IsUnexpandedPackExpansion = [](const TemplateArgument &TA) {
+    if (!TA.isPackExpansion())
+      return false;
+
+    if (TA.getKind() == TemplateArgument::Type)
+      return !TA.getAsType()->getAs<PackExpansionType>()->getNumExpansions();
+
+    if (TA.getKind() == TemplateArgument::Expression)
+      return !cast<PackExpansionExpr>(TA.getAsExpr())->getNumExpansions();
+
+    return !TA.getNumTemplateExpansions();
+  };
 
   for (auto [P, Loc] : Unexpanded) {
     // Compute the depth and index for this parameter pack.
@@ -790,7 +793,7 @@ bool Sema::CheckParameterPacksForExpansion(
     const auto *ND = dyn_cast_if_present<const NamedDecl *>(P);
     if (ND) {
       if (isa<VarDecl>(ND)) {
-        auto *DAP = dyn_cast<LocalInstantiationScope::DeclArgumentPack *>(
+        auto *DAP = dyn_cast<DeclArgumentPack *>(
             *CurrentInstantiationScope->findInstantiationOf(ND));
         if (!DAP) {
           // We can't expand this function parameter pack, so we can't expand
@@ -801,8 +804,8 @@ bool Sema::CheckParameterPacksForExpansion(
         NewPackSize = DAP->size();
       } else if (isa<BindingDecl>(ND)) {
         // Find the instantiated BindingDecl and check it for a resolved pack.
-        llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *>
-            *Instantiation = 
CurrentInstantiationScope->findInstantiationOf(ND);
+        llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
+            CurrentInstantiationScope->findInstantiationOf(ND);
         Decl *B = cast<Decl *>(*Instantiation);
         Expr *BindingExpr = cast<BindingDecl>(B)->getBinding();
         auto *BindingPack = cast_if_present<FunctionParmPackExpr>(BindingExpr);
@@ -863,6 +866,9 @@ bool Sema::CheckParameterPacksForExpansion(
     }
 
     unsigned LeastNewPackSize = NewPackSize - PendingPackExpansionSize;
+    // This maintains the maximum length of the least pack expansion at the
+    // current level, where the least pack expansion contains only resolvable
+    // packs at this point.
     if (PendingPackExpansionSize)
       CurMaximumOfLeastExpansions =
           CurMaximumOfLeastExpansions
@@ -872,8 +878,8 @@ bool Sema::CheckParameterPacksForExpansion(
     // FIXME: Workaround for Canonical TTP.
     const IdentifierInfo *Name = ND ? ND->getIdentifier() : nullptr;
     if (!CurNumExpansions) {
-      // This is the first pack we've seen for which we have an argument.
-      // Record it.
+      // This is the first pack we've seen at this level for which it has a
+      // length. Record it.
       CurNumExpansions = NewPackSize;
       FirstPack = {Name, Loc};
     } else if (NewPackSize != *CurNumExpansions) {
@@ -912,8 +918,15 @@ bool Sema::CheckParameterPacksForExpansion(
     }
   }
 
+  // We have tried our best in the for loop to find out which outer pack
+  // expansion has a different length than the current one (by checking those
+  // Subst* nodes). However, if we fail to determine that, we'll have to
+  // complain the difference in a vague manner.
   if (NumExpansions && CurNumExpansions &&
       *NumExpansions != *CurNumExpansions) {
+    // If the current pack expansion contains any unresolved packs, and since
+    // they might eventually expand to match the outer expansion, we don't
+    // complain it too early.
     if (CurMaximumOfLeastExpansions &&
         *CurMaximumOfLeastExpansions <= *NumExpansions) {
       ShouldExpand = false;

>From b68abb2f66e4ed8d53e07b2ac00f89b30ae2b79f Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Fri, 14 Mar 2025 14:25:22 +0800
Subject: [PATCH 4/8] Fix build failures

---
 clang/include/clang/Sema/SemaInternal.h | 4 ++--
 clang/lib/Sema/SemaDeclCXX.cpp          | 7 +++----
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Sema/SemaInternal.h 
b/clang/include/clang/Sema/SemaInternal.h
index 26c14646192cd..36ac3717cc1e1 100644
--- a/clang/include/clang/Sema/SemaInternal.h
+++ b/clang/include/clang/Sema/SemaInternal.h
@@ -72,8 +72,8 @@ inline std::pair<unsigned, unsigned> getDepthAndIndex(const 
NamedDecl *ND) {
 /// Retrieve the depth and index of an unexpanded parameter pack.
 inline std::pair<unsigned, unsigned>
 getDepthAndIndex(UnexpandedParameterPack UPP) {
-  assert(!(isa<SubstTemplateTypeParmPackType, 
SubstNonTypeTemplateParmPackExpr>(
-             UPP.first)) &&
+  assert(!(isa<const SubstTemplateTypeParmPackType *,
+               const SubstNonTypeTemplateParmPackExpr *>(UPP.first)) &&
          "Couldn't find depths for Subst* pack expansions!");
   if (const auto *TTP = dyn_cast<const TemplateTypeParmType *>(UPP.first))
     return std::make_pair(TTP->getDepth(), TTP->getIndex());
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 74377b2b3d724..d98ec9096cd3d 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17588,10 +17588,9 @@ DeclResult Sema::ActOnTemplatedFriendTag(
   collectUnexpandedParameterPacks(QualifierLoc, Unexpanded);
   unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
   for (UnexpandedParameterPack &U : Unexpanded) {
-    assert(
-        !(isa<SubstTemplateTypeParmPackType, SubstNonTypeTemplateParmPackExpr>(
-            U.first)) &&
-        "Shouldn't see Subst* nodes from the parser");
+    assert(!(isa<const SubstTemplateTypeParmPackType *,
+                 const SubstNonTypeTemplateParmPackExpr *>(U.first)) &&
+           "Shouldn't see Subst* nodes from the parser");
     if (getDepthAndIndex(U).first >= FriendDeclDepth) {
       auto *ND = dyn_cast<const NamedDecl *>(U.first);
       if (!ND)

>From 3d39be17e1e386f159245b95e787308364ca8cac Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Mon, 31 Mar 2025 20:10:05 +0800
Subject: [PATCH 5/8] Address feedback

---
 clang/lib/Sema/SemaTemplateDeduction.cpp |  3 +
 clang/lib/Sema/SemaTemplateVariadic.cpp  | 80 ++++++++++++------------
 2 files changed, 43 insertions(+), 40 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp 
b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c015636fe890d..60adb579602bc 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -875,6 +875,9 @@ class PackDeductionScope {
       S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
       for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) {
         UnexpandedParameterPack U = Unexpanded[I];
+        // We're only interested in undeduced packs during argument deduction.
+        // However, collectUnexpandedParameterPacks() may collect Subst* nodes,
+        // so we ignore those.
         if (isa<const SubstTemplateTypeParmPackType *,
                 const SubstNonTypeTemplateParmPackExpr *>(U.first))
           continue;
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp 
b/clang/lib/Sema/SemaTemplateVariadic.cpp
index d8a15fc6e0d03..53cafe35539e4 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -37,27 +37,25 @@ class CollectUnexpandedParameterPacksVisitor
   bool InLambdaOrBlock = false;
   unsigned DepthLimit = (unsigned)-1;
 
-#ifndef NDEBUG
   bool ContainsIntermediatePacks = false;
-#endif
 
-    void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
-      if (auto *VD = dyn_cast<VarDecl>(ND)) {
-        // For now, the only problematic case is a generic lambda's templated
-        // call operator, so we don't need to look for all the other ways we
-        // could have reached a dependent parameter pack.
-        auto *FD = dyn_cast<FunctionDecl>(VD->getDeclContext());
-        auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr;
-        if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
-          return;
-      } else if (ND->isTemplateParameterPack() &&
-                 getDepthAndIndex(ND).first >= DepthLimit) {
+  void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
+    if (auto *VD = dyn_cast<VarDecl>(ND)) {
+      // For now, the only problematic case is a generic lambda's templated
+      // call operator, so we don't need to look for all the other ways we
+      // could have reached a dependent parameter pack.
+      auto *FD = dyn_cast<FunctionDecl>(VD->getDeclContext());
+      auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr;
+      if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
         return;
-      }
-
-      Unexpanded.push_back({ND, Loc});
+    } else if (ND->isTemplateParameterPack() &&
+               getDepthAndIndex(ND).first >= DepthLimit) {
+      return;
     }
 
+    Unexpanded.push_back({ND, Loc});
+  }
+
     void addUnexpanded(const TemplateTypeParmType *T,
                        SourceLocation Loc = SourceLocation()) {
       if (T->getDepth() < DepthLimit)
@@ -100,27 +98,21 @@ class CollectUnexpandedParameterPacksVisitor
     bool VisitSubstTemplateTypeParmPackTypeLoc(
         SubstTemplateTypeParmPackTypeLoc TL) override {
       Unexpanded.push_back({TL.getTypePtr(), TL.getNameLoc()});
-#ifdef NDEBUG
       ContainsIntermediatePacks = true;
-#endif
       return true;
     }
 
     bool VisitSubstTemplateTypeParmPackType(
         SubstTemplateTypeParmPackType *T) override {
       Unexpanded.push_back({T, SourceLocation()});
-#ifdef NDEBUG
       ContainsIntermediatePacks = true;
-#endif
       return true;
     }
 
     bool VisitSubstNonTypeTemplateParmPackExpr(
         SubstNonTypeTemplateParmPackExpr *E) override {
       Unexpanded.push_back({E, E->getParameterPackLocation()});
-#ifdef NDEBUG
       ContainsIntermediatePacks = true;
-#endif
       return true;
     }
 
@@ -141,10 +133,8 @@ class CollectUnexpandedParameterPacksVisitor
           addUnexpanded(TTP);
       }
 
-#ifndef NDEBUG
       ContainsIntermediatePacks |=
           (bool)Template.getAsSubstTemplateTemplateParmPack();
-#endif
 
       return DynamicRecursiveASTVisitor::TraverseTemplateName(Template);
     }
@@ -336,14 +326,12 @@ class CollectUnexpandedParameterPacksVisitor
       return DynamicRecursiveASTVisitor::TraverseLambdaCapture(Lambda, C, 
Init);
     }
 
-#ifndef NDEBUG
     bool TraverseFunctionParmPackExpr(FunctionParmPackExpr *) override {
       ContainsIntermediatePacks = true;
       return true;
     }
 
     bool containsIntermediatePacks() const { return ContainsIntermediatePacks; 
}
-#endif
 };
 }
 
@@ -766,8 +754,13 @@ bool Sema::CheckParameterPacksForExpansion(
     bool &RetainExpansion, std::optional<unsigned> &NumExpansions) {
   ShouldExpand = true;
   RetainExpansion = false;
+  // Track the first pack we encounter that has a known length, used for
+  // diagnostics.
   std::pair<const IdentifierInfo *, SourceLocation> FirstPack;
+  // Track the partially expanded packs at the current level, used for
+  // diagnostics.
   std::optional<std::pair<unsigned, SourceLocation>> PartialExpansion;
+  // The pack size and the resolvable pack size at the current level.
   std::optional<unsigned> CurNumExpansions, CurMaximumOfLeastExpansions;
   typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
 
@@ -821,18 +814,25 @@ bool Sema::CheckParameterPacksForExpansion(
       ND = TTP->getDecl();
       // FIXME: We either should have some fallback for canonical TTP, or
       //        never have canonical TTP here.
-    } else if (const auto *STP =
-                   dyn_cast<const SubstTemplateTypeParmPackType *>(P)) {
-      NewPackSize = STP->getNumArgs();
-      PendingPackExpansionSize = llvm::count_if(
-          STP->getArgumentPack().getPackAsArray(), IsUnexpandedPackExpansion);
-      ND = STP->getReplacedParameter();
     } else {
-      const auto *SEP = cast<const SubstNonTypeTemplateParmPackExpr *>(P);
-      NewPackSize = SEP->getArgumentPack().pack_size();
-      PendingPackExpansionSize = llvm::count_if(
-          SEP->getArgumentPack().getPackAsArray(), IsUnexpandedPackExpansion);
-      ND = SEP->getParameterPack();
+      // The Subst* nodes aren't used for pack expansion, so we don't set Pos.
+      // Instead, their presence indicates that we've encountered a pack that's
+      // been substituted but not yet expanded. In this case, we try to
+      // recover their identifiers to improve our diagnostics by highlighting
+      // which parameters conflict with the current expansion.
+      if (const auto *STP =
+              dyn_cast<const SubstTemplateTypeParmPackType *>(P)) {
+        NewPackSize = STP->getNumArgs();
+        PendingPackExpansionSize = llvm::count_if(
+            STP->getArgumentPack().getPackAsArray(), 
IsUnexpandedPackExpansion);
+        ND = STP->getReplacedParameter();
+      } else {
+        const auto *SEP = cast<const SubstNonTypeTemplateParmPackExpr *>(P);
+        NewPackSize = SEP->getArgumentPack().pack_size();
+        PendingPackExpansionSize = llvm::count_if(
+            SEP->getArgumentPack().getPackAsArray(), 
IsUnexpandedPackExpansion);
+        ND = SEP->getParameterPack();
+      }
     }
 
     if (Pos) {
@@ -920,13 +920,13 @@ bool Sema::CheckParameterPacksForExpansion(
 
   // We have tried our best in the for loop to find out which outer pack
   // expansion has a different length than the current one (by checking those
-  // Subst* nodes). However, if we fail to determine that, we'll have to
-  // complain the difference in a vague manner.
+  // Subst* nodes).  Here we display a more generic diagnostic if we could not
+  // find the outer pack.
   if (NumExpansions && CurNumExpansions &&
       *NumExpansions != *CurNumExpansions) {
     // If the current pack expansion contains any unresolved packs, and since
     // they might eventually expand to match the outer expansion, we don't
-    // complain it too early.
+    // diagnose it now.
     if (CurMaximumOfLeastExpansions &&
         *CurMaximumOfLeastExpansions <= *NumExpansions) {
       ShouldExpand = false;

>From 2928f92f06ab5832d9d8323f880b9454bc765611 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Mon, 31 Mar 2025 20:13:26 +0800
Subject: [PATCH 6/8] Don't format addUnexpanded()

---
 clang/lib/Sema/SemaTemplateVariadic.cpp | 28 ++++++++++++-------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp 
b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 53cafe35539e4..2c99b0e00c842 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -39,22 +39,22 @@ class CollectUnexpandedParameterPacksVisitor
 
   bool ContainsIntermediatePacks = false;
 
-  void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
-    if (auto *VD = dyn_cast<VarDecl>(ND)) {
-      // For now, the only problematic case is a generic lambda's templated
-      // call operator, so we don't need to look for all the other ways we
-      // could have reached a dependent parameter pack.
-      auto *FD = dyn_cast<FunctionDecl>(VD->getDeclContext());
-      auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr;
-      if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
+    void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
+      if (auto *VD = dyn_cast<VarDecl>(ND)) {
+        // For now, the only problematic case is a generic lambda's templated
+        // call operator, so we don't need to look for all the other ways we
+        // could have reached a dependent parameter pack.
+        auto *FD = dyn_cast<FunctionDecl>(VD->getDeclContext());
+        auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr;
+        if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
+          return;
+      } else if (ND->isTemplateParameterPack() &&
+                getDepthAndIndex(ND).first >= DepthLimit) {
         return;
-    } else if (ND->isTemplateParameterPack() &&
-               getDepthAndIndex(ND).first >= DepthLimit) {
-      return;
-    }
+      }
 
-    Unexpanded.push_back({ND, Loc});
-  }
+      Unexpanded.push_back({ND, Loc});
+    }
 
     void addUnexpanded(const TemplateTypeParmType *T,
                        SourceLocation Loc = SourceLocation()) {

>From 376897b7aca25f575cdbb0ffeef50e3fee345fd1 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Mon, 31 Mar 2025 20:15:08 +0800
Subject: [PATCH 7/8] Again, revert unrelated format changes

---
 clang/lib/Sema/SemaTemplateVariadic.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp 
b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 2c99b0e00c842..9e9a3ffdae806 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -49,7 +49,7 @@ class CollectUnexpandedParameterPacksVisitor
         if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
           return;
       } else if (ND->isTemplateParameterPack() &&
-                getDepthAndIndex(ND).first >= DepthLimit) {
+                 getDepthAndIndex(ND).first >= DepthLimit) {
         return;
       }
 

>From a6871bb38a61d6fdb26b9cad7305af1f73117448 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Mon, 31 Mar 2025 20:17:27 +0800
Subject: [PATCH 8/8] Address one more comment

---
 clang/lib/Sema/SemaTemplateVariadic.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp 
b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 9e9a3ffdae806..14f65d76c0166 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -865,10 +865,10 @@ bool Sema::CheckParameterPacksForExpansion(
         }
     }
 
-    unsigned LeastNewPackSize = NewPackSize - PendingPackExpansionSize;
     // This maintains the maximum length of the least pack expansion at the
     // current level, where the least pack expansion contains only resolvable
     // packs at this point.
+    unsigned LeastNewPackSize = NewPackSize - PendingPackExpansionSize;
     if (PendingPackExpansionSize)
       CurMaximumOfLeastExpansions =
           CurMaximumOfLeastExpansions

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

Reply via email to