[clang] [Sema] Preserve ContainsUnexpandedParameterPack in TransformLambdaExpr (PR #86265)

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Mon Aug 5 07:48:41 PDT 2024


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

>From 6e7b38b3e3f781e11db2fa5d552fdfb6123609df Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 22 Mar 2024 17:34:08 +0800
Subject: [PATCH 01/26] [Sema] Preserve ContainsUnexpandedParameterPack in
 TransformLambdaExpr

---
 clang/docs/ReleaseNotes.rst                   |  2 +
 clang/include/clang/Sema/Sema.h               |  8 +++
 clang/lib/Sema/SemaTemplateVariadic.cpp       | 16 +++++-
 clang/lib/Sema/TreeTransform.h                | 18 ++++++
 .../test/SemaTemplate/lambda-capture-pack.cpp | 57 +++++++++++++++++++
 5 files changed, 98 insertions(+), 3 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 690fc7ed271a3..2d11a4b8c092a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -344,6 +344,8 @@ Bug Fixes to C++ Support
   when one of the function had more specialized templates.
   Fixes (`#82509 <https://github.com/llvm/llvm-project/issues/82509>`_)
   and (`#74494 <https://github.com/llvm/llvm-project/issues/74494>`_)
+- Fixed a crash where template parameter packs were not expanded correctly in a lambda being
+  used as a pattern of a folded expression. (#GH56852), (#GH85667)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index cfc1c3b349478..0d1a2e54840e5 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11201,6 +11201,14 @@ class Sema final {
   void collectUnexpandedParameterPacks(
       QualType T, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
 
+  /// Collect the set of unexpanded parameter packs from a lambda call operator.
+  ///
+  /// \param LambdaCall The lambda call operator that will be traversed to find
+  /// unexpanded parameter packs.
+  void collectUnexpandedParameterPacksFromLambda(
+      CXXMethodDecl *LambdaCall,
+      SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
+
   /// Collect the set of unexpanded parameter packs within the given
   /// type.
   ///
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 903fbfd18e779..638ceac4148ae 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -8,14 +8,15 @@
 //  This file implements semantic analysis for C++0x variadic templates.
 //===----------------------------------------------------------------------===/
 
-#include "clang/Sema/Sema.h"
 #include "TypeLocBuilder.h"
+#include "clang/AST/ASTLambda.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/ParsedTemplate.h"
 #include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/Template.h"
 #include <optional>
@@ -61,8 +62,9 @@ namespace {
 
   public:
     explicit CollectUnexpandedParameterPacksVisitor(
-        SmallVectorImpl<UnexpandedParameterPack> &Unexpanded)
-        : Unexpanded(Unexpanded) {}
+        SmallVectorImpl<UnexpandedParameterPack> &Unexpanded,
+        bool InLambda = false)
+        : Unexpanded(Unexpanded), InLambda(InLambda) {}
 
     bool shouldWalkTypesOfTypeLocs() const { return false; }
 
@@ -544,6 +546,14 @@ void Sema::collectUnexpandedParameterPacks(QualType T,
   CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseType(T);
 }
 
+void Sema::collectUnexpandedParameterPacksFromLambda(
+    CXXMethodDecl *LambdaCall,
+    SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) {
+  assert(isLambdaCallOperator(LambdaCall) && "Expected a lambda call operator");
+  CollectUnexpandedParameterPacksVisitor(Unexpanded, /*InLambda=*/true)
+      .TraverseDecl(LambdaCall);
+}
+
 void Sema::collectUnexpandedParameterPacks(TypeLoc TL,
                    SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) {
   CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseTypeLoc(TL);
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 2d22692f3ab75..f5a859c57034a 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -13748,6 +13748,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
         }
         NewVDs.push_back(NewVD);
         getSema().addInitCapture(LSI, NewVD, C->getCaptureKind() == LCK_ByRef);
+        LSI->ContainsUnexpandedParameterPack |=
+            Init.get()->containsUnexpandedParameterPack();
       }
 
       if (Invalid)
@@ -13936,6 +13938,22 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
                                     /*IsInstantiation*/ true);
   SavedContext.pop();
 
+  // The lambda may contain a pack that would be expanded by a fold expression
+  // outside. We should preserve the ContainsUnexpandedParameterPack flag here
+  // because CXXFoldExprs use it for the pattern.
+  // For example,
+  //
+  // []<class... Is>() { ([I = Is()]() {}, ...); }
+  //
+  // forgetting the flag will result in getPattern() of CXXFoldExpr returning
+  // null in terms of the inner lambda.
+  if (!LSICopy.ContainsUnexpandedParameterPack) {
+    llvm::SmallVector<UnexpandedParameterPack> UnexpandedPacks;
+    getSema().collectUnexpandedParameterPacksFromLambda(NewCallOperator,
+                                                        UnexpandedPacks);
+    // Should we call DiagnoseUnexpandedParameterPacks() instead?
+    LSICopy.ContainsUnexpandedParameterPack = !UnexpandedPacks.empty();
+  }
   return getSema().BuildLambdaExpr(E->getBeginLoc(), Body.get()->getEndLoc(),
                                    &LSICopy);
 }
diff --git a/clang/test/SemaTemplate/lambda-capture-pack.cpp b/clang/test/SemaTemplate/lambda-capture-pack.cpp
index 35b2ffcefea35..31d85e1689af1 100644
--- a/clang/test/SemaTemplate/lambda-capture-pack.cpp
+++ b/clang/test/SemaTemplate/lambda-capture-pack.cpp
@@ -23,3 +23,60 @@ namespace PR41576 {
   }
   static_assert(f(3, 4) == 6); // expected-note {{instantiation}}
 }
+
+namespace PR85667 {
+
+template <class T>
+struct identity {
+  using type = T;
+};
+
+template <class = void> void f() {
+
+  static_assert([]<class... Is>(Is... x) {
+    return ([I(x)] {
+      return I;
+    }() + ...);
+  }(1, 2) == 3);
+
+  static_assert([]<class... Is>(Is... x) {
+    return ([](auto y = Is()) { return y + 1; } + ...);
+  }(0, 0, 0) == 3);
+
+  []<class... Is>() {
+    return ([]() noexcept(Is()) { return 0; }() + ...);
+  }.template operator()<int, int>();
+
+  static_assert(__is_same(decltype([]<class... Is>() {
+                            return ([]() -> decltype(Is()) { return {}; }(),
+                                    ...);
+                          }.template operator()<int, char>()),
+                          char));
+
+  []<class... Is>() {
+    return ([]<class... Ts>() -> decltype(Is()) { return Ts(); }() + ...);
+    // expected-error at -1 {{unexpanded parameter pack 'Ts'}}
+  }.template operator()<int, int>();
+
+  // Note that GCC and EDG reject this case currently.
+  // GCC says the fold expression "has no unexpanded parameter packs", while
+  // EDG says the constraint is not allowed on a non-template function.
+  // MSVC is happy with it.
+  []<class... Is>() {
+    ([]()
+       requires(Is())
+     {},
+     ...);
+  }.template operator()<bool, bool>();
+
+  // https://github.com/llvm/llvm-project/issues/56852
+  []<class... Is>(Is...) {
+    ([] {
+      using T = identity<Is>::type;
+    }(), ...);
+  }(1, 2);
+}
+
+template void f();
+
+}

>From e76fedd9fbe557f5c9a9f980a141c2ed31c24424 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 23 Mar 2024 19:39:37 +0800
Subject: [PATCH 02/26] Consider the captured pack variables - gh18873

---
 clang/docs/ReleaseNotes.rst                   |  4 ++--
 clang/lib/Sema/TreeTransform.h                |  4 ++++
 .../test/SemaTemplate/lambda-capture-pack.cpp | 21 +++++++++++++++++++
 3 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2d11a4b8c092a..41529cbc9bd9a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -344,8 +344,8 @@ Bug Fixes to C++ Support
   when one of the function had more specialized templates.
   Fixes (`#82509 <https://github.com/llvm/llvm-project/issues/82509>`_)
   and (`#74494 <https://github.com/llvm/llvm-project/issues/74494>`_)
-- Fixed a crash where template parameter packs were not expanded correctly in a lambda being
-  used as a pattern of a folded expression. (#GH56852), (#GH85667)
+- Fixed a crash where template parameter packs were not expanded correctly in a lambda used
+  as the pattern of a folded expression. (#GH56852), (#GH85667), and partially fixes (#GH18873).
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index f5a859c57034a..fb29e5111fc54 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -13817,6 +13817,10 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
       continue;
     }
 
+    if (auto *PVD = dyn_cast<VarDecl>(CapturedVar);
+        PVD && !C->isPackExpansion())
+      LSI->ContainsUnexpandedParameterPack |= PVD->isParameterPack();
+
     // Capture the transformed variable.
     getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind,
                                  EllipsisLoc);
diff --git a/clang/test/SemaTemplate/lambda-capture-pack.cpp b/clang/test/SemaTemplate/lambda-capture-pack.cpp
index 31d85e1689af1..8a81417b6daee 100644
--- a/clang/test/SemaTemplate/lambda-capture-pack.cpp
+++ b/clang/test/SemaTemplate/lambda-capture-pack.cpp
@@ -75,6 +75,27 @@ template <class = void> void f() {
       using T = identity<Is>::type;
     }(), ...);
   }(1, 2);
+
+  // https://github.com/llvm/llvm-project/issues/18873
+  [](auto ...y) {
+    ([y] { }, ...);
+  }();
+
+  [](auto ...x) {
+    ([&](auto ...y) {
+      // FIXME: This now hits the assertion with `PackIdx != -1 && "found declaration pack but not pack expanding"'
+      // in Sema::FindInstantiatedDecl.
+      // This is because the captured variable x has been expanded while transforming
+      // the outermost lambda call, but the expansion then gets holded off while transforming
+      // the folded expression. Then we would hit the assertion when instantiating the captured variable
+      // in TransformLambdaExpr.
+      // I think this is supposed to be ill-formed, but GCC and MSVC currently accept this.
+      // If x gets expanded with non-empty arguments, then GCC and MSVC will reject it - we probably
+      // miss a diagnostic for it.
+      // ([x, y] { }, ...);
+      ([x..., y] { }, ...);
+    })();
+  }();
 }
 
 template void f();

>From faf5d8710ba0177685cd65c6b81cf79b97223e4b Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 23 Mar 2024 23:00:51 +0800
Subject: [PATCH 03/26] comments

---
 clang/lib/Sema/TreeTransform.h                  |  4 ++++
 clang/test/SemaTemplate/lambda-capture-pack.cpp | 12 ++++++------
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 0e2ea13d64d46..31592e6557f0d 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -13786,6 +13786,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
         }
         NewVDs.push_back(NewVD);
         getSema().addInitCapture(LSI, NewVD, C->getCaptureKind() == LCK_ByRef);
+        // The initializer might be expanded later. This may happen
+        // if the lambda is within a folded expression. 
         LSI->ContainsUnexpandedParameterPack |=
             Init.get()->containsUnexpandedParameterPack();
       }
@@ -13855,6 +13857,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
       continue;
     }
 
+    // The captured variable might be expanded later. This may happen
+    // if the lambda is within a folded expression. 
     if (auto *PVD = dyn_cast<VarDecl>(CapturedVar);
         PVD && !C->isPackExpansion())
       LSI->ContainsUnexpandedParameterPack |= PVD->isParameterPack();
diff --git a/clang/test/SemaTemplate/lambda-capture-pack.cpp b/clang/test/SemaTemplate/lambda-capture-pack.cpp
index 8a81417b6daee..506902bc99dc6 100644
--- a/clang/test/SemaTemplate/lambda-capture-pack.cpp
+++ b/clang/test/SemaTemplate/lambda-capture-pack.cpp
@@ -83,15 +83,15 @@ template <class = void> void f() {
 
   [](auto ...x) {
     ([&](auto ...y) {
-      // FIXME: This now hits the assertion with `PackIdx != -1 && "found declaration pack but not pack expanding"'
+      // FIXME: This now hits assertion `PackIdx != -1 && "found declaration pack but not pack expanding"'
       // in Sema::FindInstantiatedDecl.
       // This is because the captured variable x has been expanded while transforming
-      // the outermost lambda call, but the expansion then gets holded off while transforming
-      // the folded expression. Then we would hit the assertion when instantiating the captured variable
-      // in TransformLambdaExpr.
+      // the outermost lambda call, but the expansion is held off while transforming
+      // the folded expression. Then, we would hit the assertion when instantiating the
+      // captured variable in TransformLambdaExpr.
       // I think this is supposed to be ill-formed, but GCC and MSVC currently accept this.
-      // If x gets expanded with non-empty arguments, then GCC and MSVC will reject it - we probably
-      // miss a diagnostic for it.
+      // However, if x gets expanded with non-empty arguments, then GCC and MSVC will reject it -
+      // we probably need a diagnostic for it.
       // ([x, y] { }, ...);
       ([x..., y] { }, ...);
     })();

>From c5765ec62f1d32b1cd817c8c78ef7b2d21bf296e Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 24 Mar 2024 00:15:14 +0800
Subject: [PATCH 04/26] format

---
 clang/lib/Sema/TreeTransform.h | 34 ++++++++++++++++++++++++----------
 1 file changed, 24 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 31592e6557f0d..e2ec9b7085737 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -13786,8 +13786,9 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
         }
         NewVDs.push_back(NewVD);
         getSema().addInitCapture(LSI, NewVD, C->getCaptureKind() == LCK_ByRef);
-        // The initializer might be expanded later. This may happen
-        // if the lambda is within a folded expression. 
+        // If the lambda is written within a fold expression, the initializer
+        // may be expanded later. Preserve the ContainsUnexpandedParameterPack
+        // flag because CXXFoldExpr uses it for the pattern.
         LSI->ContainsUnexpandedParameterPack |=
             Init.get()->containsUnexpandedParameterPack();
       }
@@ -13848,6 +13849,17 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
 
       EllipsisLoc = C->getEllipsisLoc();
     }
+#if 0
+    else if (auto *PVD = cast<VarDecl>(C->getCapturedVar()); PVD->isParameterPack()) {
+      // If the lambda is written within a fold expression, the captured
+      // variable may be expanded later. Preserve the
+      // ContainsUnexpandedParameterPack flag because CXXFoldExpr uses it for the
+      // pattern.
+      LSI->ContainsUnexpandedParameterPack |= true;
+      getSema().tryCaptureVariable(PVD, C->getLocation(), Kind, EllipsisLoc);
+      continue;
+    }
+#endif
 
     // Transform the captured variable.
     auto *CapturedVar = cast_or_null<ValueDecl>(
@@ -13857,11 +13869,15 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
       continue;
     }
 
-    // The captured variable might be expanded later. This may happen
-    // if the lambda is within a folded expression. 
+    // If the lambda is written within a fold expression, the captured
+    // variable may be expanded later. Preserve the
+    // ContainsUnexpandedParameterPack flag because CXXFoldExpr uses it for the
+    // pattern.
+#if 1
     if (auto *PVD = dyn_cast<VarDecl>(CapturedVar);
         PVD && !C->isPackExpansion())
       LSI->ContainsUnexpandedParameterPack |= PVD->isParameterPack();
+#endif
 
     // Capture the transformed variable.
     getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind,
@@ -13984,12 +14000,10 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
                                     /*IsInstantiation*/ true);
   SavedContext.pop();
 
-  // The lambda may contain a pack that would be expanded by a fold expression
-  // outside. We should preserve the ContainsUnexpandedParameterPack flag here
-  // because CXXFoldExprs use it for the pattern.
-  // For example,
+  // The lambda function might contain a pack that would be expanded by a fold
+  // expression outside. For example,
   //
-  // []<class... Is>() { ([I = Is()]() {}, ...); }
+  // []<class... Is>() { ([](auto P = Is()) {}, ...); }
   //
   // forgetting the flag will result in getPattern() of CXXFoldExpr returning
   // null in terms of the inner lambda.
@@ -13997,7 +14011,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
     llvm::SmallVector<UnexpandedParameterPack> UnexpandedPacks;
     getSema().collectUnexpandedParameterPacksFromLambda(NewCallOperator,
                                                         UnexpandedPacks);
-    // Should we call DiagnoseUnexpandedParameterPacks() instead?
+    // FIXME: Should we call DiagnoseUnexpandedParameterPacks() instead?
     LSICopy.ContainsUnexpandedParameterPack = !UnexpandedPacks.empty();
   }
   return getSema().BuildLambdaExpr(E->getBeginLoc(), Body.get()->getEndLoc(),

>From 6c2d311938839092f8fd1c5334673c6e35fe4651 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 3 May 2024 21:50:42 +0800
Subject: [PATCH 05/26] Retain the 'unexpanded' Decls

---
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  8 ++++++
 .../test/SemaTemplate/lambda-capture-pack.cpp | 26 +++++++++----------
 2 files changed, 21 insertions(+), 13 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 787a485e0b2f8..ac96449221384 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -6221,6 +6221,14 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
           return cast<NamedDecl>(FD);
 
         int PackIdx = ArgumentPackSubstitutionIndex;
+        // FIXME: Move it elsewhere e.g. TreeTransform::TransformDecl.
+        // This is for #18873.
+        if (PackIdx == -1) {
+          LocalInstantiationScope LIS(*this, /*CombineWithOuterScope=*/false);
+          MultiLevelTemplateArgumentList FakeArgs;
+          FakeArgs.addOuterRetainedLevels(D->getTemplateDepth() + 1);
+          return cast_if_present<NamedDecl>(SubstDecl(D, CurContext, FakeArgs));
+        }
         assert(PackIdx != -1 &&
                "found declaration pack but not pack expanding");
         typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
diff --git a/clang/test/SemaTemplate/lambda-capture-pack.cpp b/clang/test/SemaTemplate/lambda-capture-pack.cpp
index 506902bc99dc6..12cb2ba6cb86e 100644
--- a/clang/test/SemaTemplate/lambda-capture-pack.cpp
+++ b/clang/test/SemaTemplate/lambda-capture-pack.cpp
@@ -83,21 +83,21 @@ template <class = void> void f() {
 
   [](auto ...x) {
     ([&](auto ...y) {
-      // FIXME: This now hits assertion `PackIdx != -1 && "found declaration pack but not pack expanding"'
-      // in Sema::FindInstantiatedDecl.
-      // This is because the captured variable x has been expanded while transforming
-      // the outermost lambda call, but the expansion is held off while transforming
-      // the folded expression. Then, we would hit the assertion when instantiating the
-      // captured variable in TransformLambdaExpr.
-      // I think this is supposed to be ill-formed, but GCC and MSVC currently accept this.
-      // However, if x gets expanded with non-empty arguments, then GCC and MSVC will reject it -
-      // we probably need a diagnostic for it.
-      // ([x, y] { }, ...);
       ([x..., y] { }, ...);
-    })();
-  }();
+    })(1);
+  }(2, 'b');
+
+  [](auto ...x) {  // #outer
+    ([&](auto ...y) { // #inner
+      ([x, y] { }, ...);
+      // expected-error at -1 {{parameter pack 'y' that has a different length (4 vs. 3) from outer parameter packs}}
+      // expected-note-re@#inner {{function template specialization {{.*}} requested here}}
+      // expected-note-re@#outer {{function template specialization {{.*}} requested here}}
+      // expected-note-re@#instantiate-f {{function template specialization {{.*}} requested here}}
+    })('a', 'b', 'c');
+  }(0, 1, 2, 3);
 }
 
-template void f();
+template void f(); // #instantiate-f
 
 }

>From 1ac5ca73a1dd6084cae9ec54546f56fc18d7e0bc Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 3 May 2024 22:11:49 +0800
Subject: [PATCH 06/26] Update ReleaseNotes

---
 clang/docs/ReleaseNotes.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 81a6d67f42f7e..dd488f7ec17d1 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -561,7 +561,7 @@ Bug Fixes to C++ Support
 - Fixed a crash when trying to evaluate a user-defined ``static_assert`` message whose ``size()``
   function returns a large or negative value. Fixes (#GH89407).
 - Fixed a crash where template parameter packs were not expanded correctly in a lambda used
-  as the pattern of a folded expression. (#GH56852), (#GH85667), and partially fixed (#GH18873).
+  as the pattern of a folded expression. (#GH56852), (#GH85667) and (#GH18873).
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^

>From db36bc87d2a5bb64a918a53636f3f97524f883ad Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 8 May 2024 09:50:01 +0800
Subject: [PATCH 07/26] Cleanup & silence the unused-expression warning

---
 clang/lib/Sema/TreeTransform.h                 | 18 ++----------------
 .../test/SemaTemplate/lambda-capture-pack.cpp  | 12 +++++++++---
 2 files changed, 11 insertions(+), 19 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index ec14129259f83..e765d25ee606f 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -14092,17 +14092,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
 
       EllipsisLoc = C->getEllipsisLoc();
     }
-#if 0
-    else if (auto *PVD = cast<VarDecl>(C->getCapturedVar()); PVD->isParameterPack()) {
-      // If the lambda is written within a fold expression, the captured
-      // variable may be expanded later. Preserve the
-      // ContainsUnexpandedParameterPack flag because CXXFoldExpr uses it for the
-      // pattern.
-      LSI->ContainsUnexpandedParameterPack |= true;
-      getSema().tryCaptureVariable(PVD, C->getLocation(), Kind, EllipsisLoc);
-      continue;
-    }
-#endif
 
     // Transform the captured variable.
     auto *CapturedVar = cast_or_null<ValueDecl>(
@@ -14116,11 +14105,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
     // variable may be expanded later. Preserve the
     // ContainsUnexpandedParameterPack flag because CXXFoldExpr uses it for the
     // pattern.
-#if 1
-    if (auto *PVD = dyn_cast<VarDecl>(CapturedVar);
-        PVD && !C->isPackExpansion())
-      LSI->ContainsUnexpandedParameterPack |= PVD->isParameterPack();
-#endif
+    LSI->ContainsUnexpandedParameterPack |=
+        cast<VarDecl>(CapturedVar)->isParameterPack();
 
     // Capture the transformed variable.
     getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind,
diff --git a/clang/test/SemaTemplate/lambda-capture-pack.cpp b/clang/test/SemaTemplate/lambda-capture-pack.cpp
index 12cb2ba6cb86e..7b22c724eb20e 100644
--- a/clang/test/SemaTemplate/lambda-capture-pack.cpp
+++ b/clang/test/SemaTemplate/lambda-capture-pack.cpp
@@ -78,18 +78,24 @@ template <class = void> void f() {
 
   // https://github.com/llvm/llvm-project/issues/18873
   [](auto ...y) {
-    ([y] { }, ...);
+    ([y] { }(), ...);
   }();
 
   [](auto ...x) {
     ([&](auto ...y) {
-      ([x..., y] { }, ...);
+      ([x..., y] { }(), ...);
     })(1);
   }(2, 'b');
 
+  [](auto ...x) {
+    ([&](auto ...y) {
+      ([x, y] { }(), ...);
+    })(1, 'a');
+  }(2, 'b');
+
   [](auto ...x) {  // #outer
     ([&](auto ...y) { // #inner
-      ([x, y] { }, ...);
+      ([x, y] { }(), ...);
       // expected-error at -1 {{parameter pack 'y' that has a different length (4 vs. 3) from outer parameter packs}}
       // expected-note-re@#inner {{function template specialization {{.*}} requested here}}
       // expected-note-re@#outer {{function template specialization {{.*}} requested here}}

>From 66acffb605aa724f59a2667bc3e542a23163e1e3 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 24 May 2024 13:33:20 +0800
Subject: [PATCH 08/26] Fix the CI

---
 clang/lib/Sema/TreeTransform.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index e765d25ee606f..3be68d8d251f3 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -14105,8 +14105,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
     // variable may be expanded later. Preserve the
     // ContainsUnexpandedParameterPack flag because CXXFoldExpr uses it for the
     // pattern.
-    LSI->ContainsUnexpandedParameterPack |=
-        cast<VarDecl>(CapturedVar)->isParameterPack();
+    if (auto *VD = dyn_cast<VarDecl>(CapturedVar); VD && !C->isPackExpansion())
+      LSI->ContainsUnexpandedParameterPack |= VD->isParameterPack();
 
     // Capture the transformed variable.
     getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind,

>From 5285c104ed526311edc4a54842ca7844410d4780 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 24 May 2024 13:34:00 +0800
Subject: [PATCH 09/26] Fix typos

---
 clang/lib/Sema/SemaTemplateDeduction.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 0b6375001f532..2af3c44f49b3c 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4296,7 +4296,7 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
 
   // Deduce an argument of type ParamType from an expression with index ArgIdx.
   auto DeduceCallArgument = [&](QualType ParamType, unsigned ArgIdx,
-                                bool ExplicitObjetArgument) {
+                                bool ExplicitObjectArgument) {
     // C++ [demp.deduct.call]p1: (DR1391)
     //   Template argument deduction is done by comparing each function template
     //   parameter that contains template-parameters that participate in
@@ -4304,7 +4304,7 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
     if (!hasDeducibleTemplateParameters(*this, FunctionTemplate, ParamType))
       return TemplateDeductionResult::Success;
 
-    if (ExplicitObjetArgument) {
+    if (ExplicitObjectArgument) {
       //   ... with the type of the corresponding argument
       return DeduceTemplateArgumentsFromCallArgument(
           *this, TemplateParams, FirstInnerIndex, ParamType, ObjectType,

>From b2d28364d1729b3f90b37905b91554b9a5d75423 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 18 Jul 2024 14:28:52 +0800
Subject: [PATCH 10/26] Fixup

---
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  8 -----
 clang/lib/Sema/TreeTransform.h                | 25 +++++++---------
 .../test/SemaTemplate/lambda-capture-pack.cpp | 29 +++++++++++++++----
 3 files changed, 34 insertions(+), 28 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 896e38d45326e..01432301633ed 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -6113,14 +6113,6 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
           return cast<NamedDecl>(FD);
 
         int PackIdx = ArgumentPackSubstitutionIndex;
-        // FIXME: Move it elsewhere e.g. TreeTransform::TransformDecl.
-        // This is for #18873.
-        if (PackIdx == -1) {
-          LocalInstantiationScope LIS(*this, /*CombineWithOuterScope=*/false);
-          MultiLevelTemplateArgumentList FakeArgs;
-          FakeArgs.addOuterRetainedLevels(D->getTemplateDepth() + 1);
-          return cast_if_present<NamedDecl>(SubstDecl(D, CurContext, FakeArgs));
-        }
         assert(PackIdx != -1 &&
                "found declaration pack but not pack expanding");
         typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index ba074bc636361..1afe05d9ad524 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -14443,9 +14443,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
         }
         NewVDs.push_back(NewVD);
         getSema().addInitCapture(LSI, NewVD, C->getCaptureKind() == LCK_ByRef);
-        // If the lambda is written within a fold expression, the initializer
-        // may be expanded later. Preserve the ContainsUnexpandedParameterPack
-        // flag because CXXFoldExpr uses it for the pattern.
+        // The Init expression might be expanded by an outer fold expression.
         LSI->ContainsUnexpandedParameterPack |=
             Init.get()->containsUnexpandedParameterPack();
       }
@@ -14515,10 +14513,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
       continue;
     }
 
-    // If the lambda is written within a fold expression, the captured
-    // variable may be expanded later. Preserve the
-    // ContainsUnexpandedParameterPack flag because CXXFoldExpr uses it for the
-    // pattern.
+    // The captured pack might be expanded by an outer fold expression.
     if (auto *VD = dyn_cast<VarDecl>(CapturedVar); VD && !C->isPackExpansion())
       LSI->ContainsUnexpandedParameterPack |= VD->isParameterPack();
 
@@ -14574,6 +14569,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
 
     if (NewCallOpType.isNull())
       return ExprError();
+    LSI->ContainsUnexpandedParameterPack |=
+        NewCallOpType->containsUnexpandedParameterPack();
     NewCallOpTSI =
         NewCallOpTLBuilder.getTypeSourceInfo(getSema().Context, NewCallOpType);
   }
@@ -14648,18 +14645,18 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
                                     /*IsInstantiation*/ true);
   SavedContext.pop();
 
-  // The lambda function might contain a pack that would be expanded by a fold
-  // expression outside. For example,
+  // Parts other than the capture e.g. the lambda body might still contain a
+  // pattern that an outer fold expression would expand.
   //
-  // []<class... Is>() { ([](auto P = Is()) {}, ...); }
-  //
-  // forgetting the flag will result in getPattern() of CXXFoldExpr returning
-  // null in terms of the inner lambda.
+  // We don't have a way to propagate up the ContainsUnexpandedParameterPack
+  // flag from a Stmt, so we have to revisit the lambda.
   if (!LSICopy.ContainsUnexpandedParameterPack) {
     llvm::SmallVector<UnexpandedParameterPack> UnexpandedPacks;
     getSema().collectUnexpandedParameterPacksFromLambda(NewCallOperator,
                                                         UnexpandedPacks);
-    // FIXME: Should we call DiagnoseUnexpandedParameterPacks() instead?
+    // FIXME: Should we call Sema::DiagnoseUnexpandedParameterPacks() instead?
+    // Unfortunately, that requires the LambdaScopeInfo to exist, which has been
+    // removed by ActOnFinishFunctionBody().
     LSICopy.ContainsUnexpandedParameterPack = !UnexpandedPacks.empty();
   }
   // Recompute the dependency of the lambda so that we can defer the lambda call
diff --git a/clang/test/SemaTemplate/lambda-capture-pack.cpp b/clang/test/SemaTemplate/lambda-capture-pack.cpp
index 7b22c724eb20e..6d2ab50827759 100644
--- a/clang/test/SemaTemplate/lambda-capture-pack.cpp
+++ b/clang/test/SemaTemplate/lambda-capture-pack.cpp
@@ -76,7 +76,6 @@ template <class = void> void f() {
     }(), ...);
   }(1, 2);
 
-  // https://github.com/llvm/llvm-project/issues/18873
   [](auto ...y) {
     ([y] { }(), ...);
   }();
@@ -87,11 +86,28 @@ template <class = void> void f() {
     })(1);
   }(2, 'b');
 
-  [](auto ...x) {
-    ([&](auto ...y) {
-      ([x, y] { }(), ...);
-    })(1, 'a');
-  }(2, 'b');
+#if 0
+  // https://github.com/llvm/llvm-project/issues/18873
+  [](auto ...x) { // #1
+    ([&](auto ...y) {  // #2
+      ([x, y] { }(), ...); // #3
+    })(1, 'a');  // #4
+  }(2, 'b');  // #5
+
+  // We run into another crash for the above lambda because of the absence of a
+  // mechanism that rebuilds an unexpanded pack from an expanded Decls.
+  //
+  // Basically, this happens after `x` at #1 being expanded when the template
+  // arguments at #5, deduced as <int, char>, are ready. When we want to
+  // instantiate the body of #1, we first instantiate the CallExpr at #4, which
+  // boils down to the lambda's instantiation at #2. To that end, we have to
+  // instantiate the body of it, which turns out to be #3. #3 is a CXXFoldExpr,
+  // and we immediately have to hold off on the expansion because we don't have
+  // corresponding template arguments for it. Therefore, we want to rebuild a
+  // CXXFoldExpr, which requires another pattern transformation of the lambda
+  // inside #3. Then we need to find an unexpanded form of such a Decl of x at
+  // the time of transforming the capture, which is impossible because the
+  // instantiated form has been expanded at #1!
 
   [](auto ...x) {  // #outer
     ([&](auto ...y) { // #inner
@@ -102,6 +118,7 @@ template <class = void> void f() {
       // expected-note-re@#instantiate-f {{function template specialization {{.*}} requested here}}
     })('a', 'b', 'c');
   }(0, 1, 2, 3);
+#endif
 }
 
 template void f(); // #instantiate-f

>From 26872a6fecbc4ac4f83f6b694ef241a9bf17c4ed Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 18 Jul 2024 15:28:36 +0800
Subject: [PATCH 11/26] Fix release notes

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

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9e5bee1e7989b..02cf2e1919d7f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -1034,13 +1034,13 @@ Bug Fixes to C++ Support
 - Fixed a CTAD substitution bug involving type aliases that reference outer template parameters. (#GH94614).
 - Clang now correctly handles unexpanded packs in the template parameter list of a generic lambda expression
   (#GH48937)
+- Clang now correctly handles unexpanded packs in a lambda used as the pattern of a folded expression. (#GH56852), 
+  (#GH85667).
 - Fix a crash when parsing an invalid type-requirement in a requires expression. (#GH51868)
 - Fix parsing of built-in type-traits such as ``__is_pointer`` in libstdc++ headers. (#GH95598)
 - Fixed failed assertion when resolving context of defaulted comparison method outside of struct. (#GH96043).
 - Clang now diagnoses explicit object parameters in member pointers and other contexts where they should not appear.
   Fixes (#GH85992).
-- Fixed a crash where template parameter packs were not expanded correctly in a lambda used
-  as the pattern of a folded expression. (#GH56852), (#GH85667) and (#GH18873).
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^

>From 10b9d8b1e74a741cb23c9b4b207519a2f38da95e Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 18 Jul 2024 15:31:52 +0800
Subject: [PATCH 12/26] clarify a bit more

---
 clang/test/SemaTemplate/lambda-capture-pack.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang/test/SemaTemplate/lambda-capture-pack.cpp b/clang/test/SemaTemplate/lambda-capture-pack.cpp
index 6d2ab50827759..ebfec69e19a7a 100644
--- a/clang/test/SemaTemplate/lambda-capture-pack.cpp
+++ b/clang/test/SemaTemplate/lambda-capture-pack.cpp
@@ -103,11 +103,11 @@ template <class = void> void f() {
   // boils down to the lambda's instantiation at #2. To that end, we have to
   // instantiate the body of it, which turns out to be #3. #3 is a CXXFoldExpr,
   // and we immediately have to hold off on the expansion because we don't have
-  // corresponding template arguments for it. Therefore, we want to rebuild a
-  // CXXFoldExpr, which requires another pattern transformation of the lambda
-  // inside #3. Then we need to find an unexpanded form of such a Decl of x at
-  // the time of transforming the capture, which is impossible because the
-  // instantiated form has been expanded at #1!
+  // corresponding template arguments (arguments at #4 are not transformed yet) for it.
+  // Therefore, we want to rebuild a CXXFoldExpr, which requires another pattern
+  // transformation of the lambda inside #3. Then we need to find an unexpanded form
+  // of such a Decl of x at the time of transforming the capture, which is impossible
+  // because the instantiated form has been expanded at #1!
 
   [](auto ...x) {  // #outer
     ([&](auto ...y) { // #inner

>From 54af6a1cdcdedebe1f3c218d9defc06a74965f06 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 22 Jul 2024 13:41:40 +0800
Subject: [PATCH 13/26] Typo

---
 clang/docs/ReleaseNotes.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 02cf2e1919d7f..8d520e859bee4 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -1034,7 +1034,7 @@ Bug Fixes to C++ Support
 - Fixed a CTAD substitution bug involving type aliases that reference outer template parameters. (#GH94614).
 - Clang now correctly handles unexpanded packs in the template parameter list of a generic lambda expression
   (#GH48937)
-- Clang now correctly handles unexpanded packs in a lambda used as the pattern of a folded expression. (#GH56852), 
+- Clang now correctly handles unexpanded packs in a lambda used as the pattern of a fold expression. (#GH56852), 
   (#GH85667).
 - Fix a crash when parsing an invalid type-requirement in a requires expression. (#GH51868)
 - Fix parsing of built-in type-traits such as ``__is_pointer`` in libstdc++ headers. (#GH95598)

>From 81fd4c9bdf768c268386b68c2d5d32e6f000351a Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Mon, 22 Jul 2024 15:19:07 +0200
Subject: [PATCH 14/26] [Sema] Default arguments for template parameters affect
 ContainsUnexpandedPacks

This addresses the FIXME in the code. There are tests for the new
behavior in a follow up fix for #99877, which also addresses other bugs
that prevent exposing the wrong results of `ContainsUnexpandedPacks` in
the outputs of the compiler without crashes.
---
 clang/lib/AST/DeclTemplate.cpp | 26 ++++++++++++++++++--------
 1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 722c7fcf0b0df..5a47b4e646355 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -44,6 +44,13 @@ using namespace clang;
 // TemplateParameterList Implementation
 //===----------------------------------------------------------------------===//
 
+namespace {
+template <class TemplateParam>
+bool DefaultArgumentContainsUnexpandedPack(const TemplateParam &P) {
+  return P.hasDefaultArgument() &&
+         P.getDefaultArgument().getArgument().containsUnexpandedParameterPack();
+}
+} // namespace
 
 TemplateParameterList::TemplateParameterList(const ASTContext& C,
                                              SourceLocation TemplateLoc,
@@ -61,27 +68,30 @@ TemplateParameterList::TemplateParameterList(const ASTContext& C,
 
     bool IsPack = P->isTemplateParameterPack();
     if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) {
-      if (!IsPack && NTTP->getType()->containsUnexpandedParameterPack())
+      if (!IsPack && (NTTP->getType()->containsUnexpandedParameterPack() ||
+                      DefaultArgumentContainsUnexpandedPack(*NTTP)))
         ContainsUnexpandedParameterPack = true;
       if (NTTP->hasPlaceholderTypeConstraint())
         HasConstrainedParameters = true;
     } else if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(P)) {
       if (!IsPack &&
-          TTP->getTemplateParameters()->containsUnexpandedParameterPack())
+          (TTP->getTemplateParameters()->containsUnexpandedParameterPack() ||
+           DefaultArgumentContainsUnexpandedPack(*TTP))) {
         ContainsUnexpandedParameterPack = true;
+      }
     } else if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(P)) {
-      if (const TypeConstraint *TC = TTP->getTypeConstraint()) {
-        if (TC->getImmediatelyDeclaredConstraint()
-            ->containsUnexpandedParameterPack())
-          ContainsUnexpandedParameterPack = true;
+      if (!IsPack && DefaultArgumentContainsUnexpandedPack(*TTP)) {
+        ContainsUnexpandedParameterPack = true;
+      } else if (const TypeConstraint *TC = TTP->getTypeConstraint();
+                 TC && TC->getImmediatelyDeclaredConstraint()
+                           ->containsUnexpandedParameterPack()) {
+        ContainsUnexpandedParameterPack = true;
       }
       if (TTP->hasTypeConstraint())
         HasConstrainedParameters = true;
     } else {
       llvm_unreachable("unexpected template parameter type");
     }
-    // FIXME: If a default argument contains an unexpanded parameter pack, the
-    // template parameter list does too.
   }
 
   if (HasRequiresClause) {

>From 97372b6820ccba9263cee4ee3fbd87e6edb2e6c3 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 23 Jul 2024 23:06:07 +0800
Subject: [PATCH 15/26] Merge tests from ilya's 99882 and handle template
 parameter cases

Co-authored-by: Ilya Biryukov <ibiryukov at google.com>
---
 clang/lib/Sema/TreeTransform.h | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 1afe05d9ad524..cd72bff376e9a 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -14528,9 +14528,14 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
   auto TPL = getDerived().TransformTemplateParameterList(
       E->getTemplateParameterList());
   LSI->GLTemplateParameterList = TPL;
-  if (TPL)
+  if (TPL) {
     getSema().AddTemplateParametersToLambdaCallOperator(NewCallOperator, Class,
                                                         TPL);
+    // The parameter list might reference to a pack that an outer fold
+    // expression would expand.
+    LSI->ContainsUnexpandedParameterPack |=
+        TPL->containsUnexpandedParameterPack();
+  }
 
   // Transform the type of the original lambda's call operator.
   // The transformation MUST be done in the CurrentInstantiationScope since

>From d1cc8a89b58327e1318b33c9add6b51a9980f110 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 25 Jul 2024 21:48:35 +0800
Subject: [PATCH 16/26] Handle more cases e.g. constraints, DeclStmt, etc.

---
 clang/include/clang/Sema/Sema.h               |  17 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |  40 ++--
 clang/lib/Sema/SemaTemplateVariadic.cpp       |  55 ++++-
 clang/lib/Sema/TreeTransform.h                |  93 ++++++--
 .../SemaCXX/fold_lambda_with_variadics.cpp    | 201 ++++++++++++++++++
 .../test/SemaTemplate/lambda-capture-pack.cpp | 101 ---------
 6 files changed, 350 insertions(+), 157 deletions(-)
 create mode 100644 clang/test/SemaCXX/fold_lambda_with_variadics.cpp

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 74cde2bcff1c2..6712392c2b03c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14035,6 +14035,15 @@ class Sema final : public SemaBase {
       TemplateArgument Arg,
       SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
 
+  /// Collect the set of unexpanded parameter packs within the given
+  /// template argument.
+  ///
+  /// \param Arg The template argument that will be traversed to find
+  /// unexpanded parameter packs.
+  void collectUnexpandedParameterPacksForFoldExprs(
+      Expr *E, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded,
+      SmallVectorImpl<UnexpandedParameterPack> &UnexpandedFromConstraints);
+
   /// Collect the set of unexpanded parameter packs within the given
   /// template argument.
   ///
@@ -14052,14 +14061,6 @@ class Sema final : public SemaBase {
   void collectUnexpandedParameterPacks(
       QualType T, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
 
-  /// Collect the set of unexpanded parameter packs from a lambda call operator.
-  ///
-  /// \param LambdaCall The lambda call operator that will be traversed to find
-  /// unexpanded parameter packs.
-  void collectUnexpandedParameterPacksFromLambda(
-      CXXMethodDecl *LambdaCall,
-      SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
-
   /// Collect the set of unexpanded parameter packs within the given
   /// type.
   ///
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index a7bc6749c5852..13f6d6cbeb757 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -39,6 +39,7 @@
 #include "llvm/ADT/STLForwardCompat.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/SaveAndRestore.h"
 #include "llvm/Support/TimeProfiler.h"
 #include <optional>
 
@@ -1394,7 +1395,22 @@ namespace {
                                  SourceRange PatternRange,
                                  ArrayRef<UnexpandedParameterPack> Unexpanded,
                                  bool &ShouldExpand, bool &RetainExpansion,
-                                 std::optional<unsigned> &NumExpansions) {
+                                 std::optional<unsigned> &NumExpansions,
+                                 bool ForConstraints = false) {
+      if (ForConstraints) {
+        LambdaScopeInfo *LSI = getSema().getCurLambda();
+        if (LSI) {
+          MultiLevelTemplateArgumentList MLTAL =
+              getSema().getTemplateInstantiationArgs(
+                  LSI->CallOperator, /*DC=*/nullptr, /*Final=*/false,
+                  /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true,
+                  /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true);
+          return getSema().CheckParameterPacksForExpansion(
+              EllipsisLoc, PatternRange, Unexpanded, MLTAL, ShouldExpand,
+              RetainExpansion, NumExpansions);
+        }
+      }
+
       return getSema().CheckParameterPacksForExpansion(EllipsisLoc,
                                                        PatternRange, Unexpanded,
                                                        TemplateArgs,
@@ -1656,11 +1672,12 @@ namespace {
       LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
       Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
 
-      ExprResult Result = inherited::TransformLambdaExpr(E);
-      if (Result.isInvalid())
-        return Result;
+      return inherited::TransformLambdaExpr(E);
+    }
 
-      CXXMethodDecl *MD = Result.getAs<LambdaExpr>()->getCallOperator();
+    void RebuildLambdaExprImpl(SourceLocation StartLoc, SourceLocation EndLoc,
+                               LambdaScopeInfo *LSI) {
+      CXXMethodDecl *MD = LSI->CallOperator;
       for (ParmVarDecl *PVD : MD->parameters()) {
         assert(PVD && "null in a parameter list");
         if (!PVD->hasDefaultArg())
@@ -1673,14 +1690,12 @@ namespace {
           // RecoveryExpr that wraps the uninstantiated default argument so
           // that downstream diagnostics are omitted.
           ExprResult ErrorResult = SemaRef.CreateRecoveryExpr(
-              UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(),
-              { UninstExpr }, UninstExpr->getType());
+              UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(), {UninstExpr},
+              UninstExpr->getType());
           if (ErrorResult.isUsable())
             PVD->setDefaultArg(ErrorResult.get());
         }
       }
-
-      return Result;
     }
 
     StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body) {
@@ -1693,11 +1708,8 @@ namespace {
       // `true` to temporarily fix this issue.
       // FIXME: This temporary fix can be removed after fully implementing
       // p0588r1.
-      bool Prev = EvaluateConstraints;
-      EvaluateConstraints = true;
-      StmtResult Stmt = inherited::TransformLambdaBody(E, Body);
-      EvaluateConstraints = Prev;
-      return Stmt;
+      llvm::SaveAndRestore _(EvaluateConstraints, true);
+      return inherited::TransformLambdaBody(E, Body);
     }
 
     ExprResult TransformRequiresExpr(RequiresExpr *E) {
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 9a92fd682133f..7c7f5d233cbbc 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/Support/SaveAndRestore.h"
 #include <optional>
 
 using namespace clang;
@@ -40,6 +41,10 @@ namespace {
     bool InLambda = false;
     unsigned DepthLimit = (unsigned)-1;
 
+    FunctionDecl *CurrentFunction = nullptr;
+    bool InConstraint = false;
+    SmallVectorImpl<UnexpandedParameterPack> *UnexpandedFromConstraints;
+
     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
@@ -52,19 +57,31 @@ namespace {
       } else if (getDepthAndIndex(ND).first >= DepthLimit)
         return;
 
+      if (InConstraint && UnexpandedFromConstraints) {
+        UnexpandedFromConstraints->push_back({ND, Loc});
+        return;
+      }
+
       Unexpanded.push_back({ND, Loc});
     }
     void addUnexpanded(const TemplateTypeParmType *T,
                        SourceLocation Loc = SourceLocation()) {
-      if (T->getDepth() < DepthLimit)
+      if (T->getDepth() < DepthLimit) {
+        if (InConstraint && UnexpandedFromConstraints) {
+          UnexpandedFromConstraints->push_back({T, Loc});
+          return;
+        }
         Unexpanded.push_back({T, Loc});
+      }
     }
 
   public:
     explicit CollectUnexpandedParameterPacksVisitor(
         SmallVectorImpl<UnexpandedParameterPack> &Unexpanded,
-        bool InLambda = false)
-        : Unexpanded(Unexpanded), InLambda(InLambda) {}
+        SmallVectorImpl<UnexpandedParameterPack> *UnexpandedFromConstraints =
+            nullptr)
+        : Unexpanded(Unexpanded),
+          UnexpandedFromConstraints(UnexpandedFromConstraints) {}
 
     bool shouldWalkTypesOfTypeLocs() const { return false; }
 
@@ -138,6 +155,11 @@ namespace {
     /// do not contain unexpanded parameter packs.
     bool TraverseStmt(Stmt *S) {
       Expr *E = dyn_cast_or_null<Expr>(S);
+
+      llvm::SaveAndRestore _(InConstraint);
+      if (CurrentFunction && CurrentFunction->getTrailingRequiresClause() == S)
+        InConstraint = true;
+
       if ((E && E->containsUnexpandedParameterPack()) || InLambda)
         return inherited::TraverseStmt(S);
 
@@ -172,6 +194,17 @@ namespace {
       if (D && D->isParameterPack())
         return true;
 
+      if (D && D->isFunctionOrFunctionTemplate()) {
+        FunctionDecl *FD;
+        if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
+          FD = FTD->getTemplatedDecl();
+        else
+          FD = cast<FunctionDecl>(D);
+
+        llvm::SaveAndRestore _(CurrentFunction, FD);
+        return inherited::TraverseDecl(D);
+      }
+
       return inherited::TraverseDecl(D);
     }
 
@@ -538,6 +571,14 @@ void Sema::collectUnexpandedParameterPacks(TemplateArgument Arg,
     .TraverseTemplateArgument(Arg);
 }
 
+void Sema::collectUnexpandedParameterPacksForFoldExprs(
+    Expr *E, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded,
+    SmallVectorImpl<UnexpandedParameterPack> &UnexpandedFromConstraints) {
+  CollectUnexpandedParameterPacksVisitor Visitor(Unexpanded,
+                                                 &UnexpandedFromConstraints);
+  Visitor.TraverseTemplateArgument(E);
+}
+
 void Sema::collectUnexpandedParameterPacks(TemplateArgumentLoc Arg,
                    SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) {
   CollectUnexpandedParameterPacksVisitor(Unexpanded)
@@ -549,14 +590,6 @@ void Sema::collectUnexpandedParameterPacks(QualType T,
   CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseType(T);
 }
 
-void Sema::collectUnexpandedParameterPacksFromLambda(
-    CXXMethodDecl *LambdaCall,
-    SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) {
-  assert(isLambdaCallOperator(LambdaCall) && "Expected a lambda call operator");
-  CollectUnexpandedParameterPacksVisitor(Unexpanded, /*InLambda=*/true)
-      .TraverseDecl(LambdaCall);
-}
-
 void Sema::collectUnexpandedParameterPacks(TypeLoc TL,
                    SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) {
   CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseTypeLoc(TL);
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index cd72bff376e9a..8280e166706b2 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -288,7 +288,8 @@ class TreeTransform {
                                SourceRange PatternRange,
                                ArrayRef<UnexpandedParameterPack> Unexpanded,
                                bool &ShouldExpand, bool &RetainExpansion,
-                               std::optional<unsigned> &NumExpansions) {
+                               std::optional<unsigned> &NumExpansions,
+                               bool ForConstraints = false) {
     ShouldExpand = false;
     return false;
   }
@@ -4006,6 +4007,37 @@ class TreeTransform {
                                       NumExpansions);
   }
 
+  void RebuildLambdaExprImpl(SourceLocation StartLoc, SourceLocation EndLoc,
+                             LambdaScopeInfo *LSI) {}
+
+  ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
+                               LambdaScopeInfo *LSI) {
+    CXXRecordDecl *Class = LSI->Lambda;
+    CXXMethodDecl *CallOperator = LSI->CallOperator;
+    CallOperator->setLexicalDeclContext(Class);
+    Decl *TemplateOrNonTemplateCallOperatorDecl =
+        CallOperator->getDescribedFunctionTemplate()
+            ? CallOperator->getDescribedFunctionTemplate()
+            : cast<Decl>(CallOperator);
+    // FIXME: Is this really the best choice? Keeping the lexical decl context
+    // set as CurContext seems more faithful to the source.
+    TemplateOrNonTemplateCallOperatorDecl->setLexicalDeclContext(Class);
+
+    getDerived().RebuildLambdaExprImpl(StartLoc, EndLoc, LSI);
+
+    // Default arguments might contain unexpanded packs that would expand later.
+    for (ParmVarDecl *PVD : LSI->CallOperator->parameters()) {
+      if (Expr *Init = PVD->getInit())
+        LSI->ContainsUnexpandedParameterPack |=
+            Init->containsUnexpandedParameterPack();
+      else if (PVD->hasUninstantiatedDefaultArg())
+        LSI->ContainsUnexpandedParameterPack |=
+            PVD->getUninstantiatedDefaultArg()
+                ->containsUnexpandedParameterPack();
+    }
+    return getSema().BuildLambdaExpr(StartLoc, EndLoc, LSI);
+  }
+
   /// Build an empty C++1z fold-expression with the given operator.
   ///
   /// By default, produces the fallback value for the fold-expression, or
@@ -8257,6 +8289,7 @@ StmtResult
 TreeTransform<Derived>::TransformDeclStmt(DeclStmt *S) {
   bool DeclChanged = false;
   SmallVector<Decl *, 4> Decls;
+  LambdaScopeInfo *LSI = getSema().getCurLambda();
   for (auto *D : S->decls()) {
     Decl *Transformed = getDerived().TransformDefinition(D->getLocation(), D);
     if (!Transformed)
@@ -8265,6 +8298,15 @@ TreeTransform<Derived>::TransformDeclStmt(DeclStmt *S) {
     if (Transformed != D)
       DeclChanged = true;
 
+    if (LSI && isa<TypeDecl>(Transformed))
+      LSI->ContainsUnexpandedParameterPack |=
+          getSema()
+              .getASTContext()
+              .getTypeDeclType(cast<TypeDecl>(Transformed))
+              .getCanonicalType()
+              .getTypePtr()
+              ->containsUnexpandedParameterPack();
+
     Decls.push_back(Transformed);
   }
 
@@ -14588,11 +14630,20 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
     Params = FPTL.getParams();
   }
 
+  Expr *TrailingRequiresExpr =
+      E->getCallOperator()->getTrailingRequiresClause();
+  if (TrailingRequiresExpr) {
+    // If we're in an expansion, do not propagate up this flag. Otherwise we
+    // would fail to unexpand the surrounding CXXFoldExpr.
+    if (getSema().ArgumentPackSubstitutionIndex == -1)
+      LSI->ContainsUnexpandedParameterPack |=
+          TrailingRequiresExpr->containsUnexpandedParameterPack();
+  }
+
   getSema().CompleteLambdaCallOperator(
       NewCallOperator, E->getCallOperator()->getLocation(),
-      E->getCallOperator()->getInnerLocStart(),
-      E->getCallOperator()->getTrailingRequiresClause(), NewCallOpTSI,
-      E->getCallOperator()->getConstexprKind(),
+      E->getCallOperator()->getInnerLocStart(), TrailingRequiresExpr,
+      NewCallOpTSI, E->getCallOperator()->getConstexprKind(),
       E->getCallOperator()->getStorageClass(), Params,
       E->hasExplicitResultType());
 
@@ -14650,20 +14701,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
                                     /*IsInstantiation*/ true);
   SavedContext.pop();
 
-  // Parts other than the capture e.g. the lambda body might still contain a
-  // pattern that an outer fold expression would expand.
-  //
-  // We don't have a way to propagate up the ContainsUnexpandedParameterPack
-  // flag from a Stmt, so we have to revisit the lambda.
-  if (!LSICopy.ContainsUnexpandedParameterPack) {
-    llvm::SmallVector<UnexpandedParameterPack> UnexpandedPacks;
-    getSema().collectUnexpandedParameterPacksFromLambda(NewCallOperator,
-                                                        UnexpandedPacks);
-    // FIXME: Should we call Sema::DiagnoseUnexpandedParameterPacks() instead?
-    // Unfortunately, that requires the LambdaScopeInfo to exist, which has been
-    // removed by ActOnFinishFunctionBody().
-    LSICopy.ContainsUnexpandedParameterPack = !UnexpandedPacks.empty();
-  }
   // Recompute the dependency of the lambda so that we can defer the lambda call
   // construction until after we have all the necessary template arguments. For
   // example, given
@@ -14704,8 +14741,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
   Class->setTypeForDecl(nullptr);
   getSema().Context.getTypeDeclType(Class);
 
-  return getSema().BuildLambdaExpr(E->getBeginLoc(), Body.get()->getEndLoc(),
-                                   &LSICopy);
+  return RebuildLambdaExpr(E->getBeginLoc(), Body.get()->getEndLoc(), &LSICopy);
 }
 
 template<typename Derived>
@@ -15244,9 +15280,11 @@ TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
 
   Expr *Pattern = E->getPattern();
 
-  SmallVector<UnexpandedParameterPack, 2> Unexpanded;
-  getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded);
-  assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
+  SmallVector<UnexpandedParameterPack, 2> Unexpanded, UnexpandedFromConstraints;
+  getSema().collectUnexpandedParameterPacksForFoldExprs(
+      Pattern, Unexpanded, UnexpandedFromConstraints);
+  assert((!Unexpanded.empty() || !UnexpandedFromConstraints.empty()) &&
+         "Pack expansion without parameter packs?");
 
   // Determine whether the set of unexpanded parameter packs can and should
   // be expanded.
@@ -15261,6 +15299,15 @@ TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
                                            NumExpansions))
     return true;
 
+  // Only constraints contain unexpanded packs.
+  if (Unexpanded.empty() && !UnexpandedFromConstraints.empty()) {
+    if (getDerived().TryExpandParameterPacks(
+            E->getEllipsisLoc(), Pattern->getSourceRange(),
+            UnexpandedFromConstraints, Expand, RetainExpansion, NumExpansions,
+            /*ForConstraints=*/true))
+      return true;
+  }
+
   if (!Expand) {
     // Do not expand any packs here, just transform and rebuild a fold
     // expression.
diff --git a/clang/test/SemaCXX/fold_lambda_with_variadics.cpp b/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
new file mode 100644
index 0000000000000..f4ecfe9807e5d
--- /dev/null
+++ b/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
@@ -0,0 +1,201 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+
+namespace GH85667 {
+
+template <class T>
+struct identity {
+  using type = T;
+};
+
+template <class... T>
+concept C = false; // #concept-C
+
+template <class = void> void f() {
+
+  static_assert([]<class... Is>(Is... x) {
+    return ([I(x)] {
+      return I;
+    }() + ...);
+  }(1, 2) == 3);
+
+  []<class... Is>(Is... x) {
+    return ([](auto y = Is()) { return y + 1; }() + ...); // expected-error {{no matching function}}                     \
+                                                          // expected-note {{couldn't infer template argument 'y:auto'}} \
+                                                          // expected-note at -1 {{requested here}}
+                                                          // expected-note@#instantiate-f {{requested here}}
+  }(1);
+
+  []<class... Is>() {
+    ([]()               // expected-error {{no matching function}}
+       requires C<Is>   // expected-note {{because 'float' does not satisfy 'C'}} \
+                        // expected-note at -1 {{constraints not satisfied}}         \
+                        // expected-note@#concept-C {{evaluated to false}}        \
+                        // expected-note@#instantiate-f {{requested here}}
+     {}(),
+     ...);
+  }.template operator()<char, int, float>(); // expected-note {{requested here}}
+
+  []<class... Is>() {
+    ([](Is)
+       requires (!C<Is>)
+     {}(Is()),
+     ...);
+  }.template operator()<char, int, float>();
+
+  []<class... Is>() {
+    ([]<class = Is>(Is)
+       noexcept(bool(Is())) requires (!C<Is>)
+     {}(Is()),
+     ...);
+  }.template operator()<char, int, float>();
+
+  static_assert(__is_same(decltype([]<class... Is>() {
+                            return ([]() -> decltype(Is()) { return {}; }(),
+                                    ...);
+                          }.template operator()<int, char>()),
+                          char));
+
+  []<class... Is>() {
+    return ([]<class... Ts>() -> decltype(Is()) { return Ts(); }() + ...);
+    // expected-error at -1 {{unexpanded parameter pack 'Ts'}}
+  }.template operator()<int, int>();
+
+  // https://github.com/llvm/llvm-project/issues/56852
+  []<class... Is>(Is...) {
+    ([] {
+      using T = identity<Is>::type;
+    }(), ...);
+  }(1, 2);
+
+  [](auto ...y) {
+    ([y] { }(), ...);
+  }();
+
+  [](auto ...x) {
+    ([&](auto ...y) {
+      ([x..., y] { }(), ...);
+    })(1);
+  }(2, 'b');
+
+#if 0
+  // https://github.com/llvm/llvm-project/issues/18873
+  [](auto ...x) { // #1
+    ([&](auto ...y) {  // #2
+      ([x, y] { }(), ...); // #3
+    })(1, 'a');  // #4
+  }(2, 'b');  // #5
+
+  // We run into another crash for the above lambda because of the absence of a
+  // mechanism that rebuilds an unexpanded pack from an expanded Decls.
+  //
+  // Basically, this happens after `x` at #1 being expanded when the template
+  // arguments at #5, deduced as <int, char>, are ready. When we want to
+  // instantiate the body of #1, we first instantiate the CallExpr at #4, which
+  // boils down to the lambda's instantiation at #2. To that end, we have to
+  // instantiate the body of it, which turns out to be #3. #3 is a CXXFoldExpr,
+  // and we immediately have to hold off on the expansion because we don't have
+  // corresponding template arguments (arguments at #4 are not transformed yet) for it.
+  // Therefore, we want to rebuild a CXXFoldExpr, which requires another pattern
+  // transformation of the lambda inside #3. Then we need to find an unexpanded form
+  // of such a Decl of x at the time of transforming the capture, which is impossible
+  // because the instantiated form has been expanded at #1!
+
+  [](auto ...x) {  // #outer
+    ([&](auto ...y) { // #inner
+      ([x, y] { }(), ...);
+      // expected-error at -1 {{parameter pack 'y' that has a different length (4 vs. 3) from outer parameter packs}}
+      // expected-note-re@#inner {{function template specialization {{.*}} requested here}}
+      // expected-note-re@#outer {{function template specialization {{.*}} requested here}}
+      // expected-note-re@#instantiate-f {{function template specialization {{.*}} requested here}}
+    })('a', 'b', 'c');
+  }(0, 1, 2, 3);
+#endif
+}
+
+template void f(); // #instantiate-f
+
+} // namespace GH85667
+
+namespace GH99877 {
+
+struct tuple {
+  int x[3];
+};
+
+template <class F> int apply(F f, tuple v) { return f(v.x[0], v.x[1], v.x[2]); }
+
+int Cartesian1(auto x, auto y) {
+  return apply(
+      [&](auto... xs) {
+        return (apply([xs](auto... ys) { return (ys + ...); }, y) + ...);
+      },
+      x);
+}
+
+int Cartesian2(auto x, auto y) {
+  return apply(
+      [&](auto... xs) {
+        return (apply([zs = xs](auto... ys) { return (ys + ...); }, y) + ...);
+      },
+      x);
+}
+
+template <int...> struct Ints {};
+template <int> struct Choose {
+  template <class> struct Templ;
+};
+template <int... x> int Cartesian3(auto y) {
+  return [&]<int... xs>(Ints<xs...>) {
+    // check in default template arguments for
+    // - type template parameters,
+    (void)(apply([]<class = decltype(xs)>(auto... ys) { return (ys + ...); },
+                 y) +
+           ...);
+    // - template template parameters.
+    (void)(apply([]<template <class> class = Choose<xs>::template Templ>(
+                     auto... ys) { return (ys + ...); },
+                 y) +
+           ...);
+    // - non-type template parameters,
+    return (apply([]<int = xs>(auto... ys) { return (ys + ...); }, y) + ...);
+  }(Ints<x...>());
+}
+
+template <int... x> int Cartesian4(auto y) {
+  return [&]<int... xs>(Ints<xs...>) {
+    return (
+        apply([]<decltype(xs) xx = 1>(auto... ys) { return (ys + ...); }, y) +
+        ...);
+  }(Ints<x...>());
+}
+
+// FIXME: Attributes should preserve the ContainsUnexpandedPack flag.
+#if 0
+
+int Cartesian5(auto x, auto y) {
+  return apply(
+      [&](auto... xs) {
+        return (apply([](auto... ys) __attribute__((
+                          diagnose_if(!__is_same(decltype(xs), int), "message",
+                                      "error"))) { return (ys + ...); },
+                      y) +
+                ...);
+      },
+      x);
+}
+
+#endif
+
+void foo() {
+  auto x = tuple({1, 2, 3});
+  auto y = tuple({4, 5, 6});
+  Cartesian1(x, y);
+  Cartesian2(x, y);
+  Cartesian3<1, 2, 3>(y);
+  Cartesian4<1, 2, 3>(y);
+#if 0
+  Cartesian5(x, y);
+#endif
+}
+
+} // namespace GH99877
diff --git a/clang/test/SemaTemplate/lambda-capture-pack.cpp b/clang/test/SemaTemplate/lambda-capture-pack.cpp
index ebfec69e19a7a..35b2ffcefea35 100644
--- a/clang/test/SemaTemplate/lambda-capture-pack.cpp
+++ b/clang/test/SemaTemplate/lambda-capture-pack.cpp
@@ -23,104 +23,3 @@ namespace PR41576 {
   }
   static_assert(f(3, 4) == 6); // expected-note {{instantiation}}
 }
-
-namespace PR85667 {
-
-template <class T>
-struct identity {
-  using type = T;
-};
-
-template <class = void> void f() {
-
-  static_assert([]<class... Is>(Is... x) {
-    return ([I(x)] {
-      return I;
-    }() + ...);
-  }(1, 2) == 3);
-
-  static_assert([]<class... Is>(Is... x) {
-    return ([](auto y = Is()) { return y + 1; } + ...);
-  }(0, 0, 0) == 3);
-
-  []<class... Is>() {
-    return ([]() noexcept(Is()) { return 0; }() + ...);
-  }.template operator()<int, int>();
-
-  static_assert(__is_same(decltype([]<class... Is>() {
-                            return ([]() -> decltype(Is()) { return {}; }(),
-                                    ...);
-                          }.template operator()<int, char>()),
-                          char));
-
-  []<class... Is>() {
-    return ([]<class... Ts>() -> decltype(Is()) { return Ts(); }() + ...);
-    // expected-error at -1 {{unexpanded parameter pack 'Ts'}}
-  }.template operator()<int, int>();
-
-  // Note that GCC and EDG reject this case currently.
-  // GCC says the fold expression "has no unexpanded parameter packs", while
-  // EDG says the constraint is not allowed on a non-template function.
-  // MSVC is happy with it.
-  []<class... Is>() {
-    ([]()
-       requires(Is())
-     {},
-     ...);
-  }.template operator()<bool, bool>();
-
-  // https://github.com/llvm/llvm-project/issues/56852
-  []<class... Is>(Is...) {
-    ([] {
-      using T = identity<Is>::type;
-    }(), ...);
-  }(1, 2);
-
-  [](auto ...y) {
-    ([y] { }(), ...);
-  }();
-
-  [](auto ...x) {
-    ([&](auto ...y) {
-      ([x..., y] { }(), ...);
-    })(1);
-  }(2, 'b');
-
-#if 0
-  // https://github.com/llvm/llvm-project/issues/18873
-  [](auto ...x) { // #1
-    ([&](auto ...y) {  // #2
-      ([x, y] { }(), ...); // #3
-    })(1, 'a');  // #4
-  }(2, 'b');  // #5
-
-  // We run into another crash for the above lambda because of the absence of a
-  // mechanism that rebuilds an unexpanded pack from an expanded Decls.
-  //
-  // Basically, this happens after `x` at #1 being expanded when the template
-  // arguments at #5, deduced as <int, char>, are ready. When we want to
-  // instantiate the body of #1, we first instantiate the CallExpr at #4, which
-  // boils down to the lambda's instantiation at #2. To that end, we have to
-  // instantiate the body of it, which turns out to be #3. #3 is a CXXFoldExpr,
-  // and we immediately have to hold off on the expansion because we don't have
-  // corresponding template arguments (arguments at #4 are not transformed yet) for it.
-  // Therefore, we want to rebuild a CXXFoldExpr, which requires another pattern
-  // transformation of the lambda inside #3. Then we need to find an unexpanded form
-  // of such a Decl of x at the time of transforming the capture, which is impossible
-  // because the instantiated form has been expanded at #1!
-
-  [](auto ...x) {  // #outer
-    ([&](auto ...y) { // #inner
-      ([x, y] { }(), ...);
-      // expected-error at -1 {{parameter pack 'y' that has a different length (4 vs. 3) from outer parameter packs}}
-      // expected-note-re@#inner {{function template specialization {{.*}} requested here}}
-      // expected-note-re@#outer {{function template specialization {{.*}} requested here}}
-      // expected-note-re@#instantiate-f {{function template specialization {{.*}} requested here}}
-    })('a', 'b', 'c');
-  }(0, 1, 2, 3);
-#endif
-}
-
-template void f(); // #instantiate-f
-
-}

>From ce2bf931b07ab95f0f7da5e5923bd74578385544 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 25 Jul 2024 22:15:21 +0800
Subject: [PATCH 17/26] Co-author

Co-authored-by: Ilya Biryukov <809452+ilya-biryukov at users.noreply.github.com>
---
 clang/test/SemaCXX/fold_lambda_with_variadics.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/test/SemaCXX/fold_lambda_with_variadics.cpp b/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
index f4ecfe9807e5d..8539674b57190 100644
--- a/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
+++ b/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
@@ -35,6 +35,7 @@ template <class = void> void f() {
      ...);
   }.template operator()<char, int, float>(); // expected-note {{requested here}}
 
+
   []<class... Is>() {
     ([](Is)
        requires (!C<Is>)

>From c2a0bf947718e63442f448cba5bb7fa5b71ebc86 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 26 Jul 2024 23:27:11 +0800
Subject: [PATCH 18/26] Fix ReleaseNotes

---
 clang/docs/ReleaseNotes.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index caf998aa2d7af..c34c66268cc08 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -152,7 +152,7 @@ Bug Fixes to C++ Support
 - Fixed a crash when an expression with a dependent ``__typeof__`` type is used as the operand of a unary operator. (#GH97646)
 - Fixed a failed assertion when checking invalid delete operator declaration. (#GH96191)
 - Clang now correctly handles unexpanded packs in a lambda used as the pattern of a fold expression. (#GH56852), 
-  (#GH85667), (#99877).
+  (#GH85667), (#GH99877).
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^

>From ea94d6d802da0123acffc051e54f9529d59c6da9 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 30 Jul 2024 16:34:27 +0800
Subject: [PATCH 19/26] Use CurContext

---
 clang/lib/Sema/SemaTemplateInstantiate.cpp | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 42f11ed345963..4f8b261353124 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1398,17 +1398,15 @@ namespace {
                                  std::optional<unsigned> &NumExpansions,
                                  bool ForConstraints = false) {
       if (ForConstraints) {
-        LambdaScopeInfo *LSI = getSema().getCurLambda();
-        if (LSI) {
-          MultiLevelTemplateArgumentList MLTAL =
-              getSema().getTemplateInstantiationArgs(
-                  LSI->CallOperator, /*DC=*/nullptr, /*Final=*/false,
-                  /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true,
-                  /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true);
-          return getSema().CheckParameterPacksForExpansion(
-              EllipsisLoc, PatternRange, Unexpanded, MLTAL, ShouldExpand,
-              RetainExpansion, NumExpansions);
-        }
+        MultiLevelTemplateArgumentList MLTAL =
+            getSema().getTemplateInstantiationArgs(
+                cast<NamedDecl>(getSema().CurContext), /*DC=*/nullptr,
+                /*Final=*/false,
+                /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true,
+                /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true);
+        return getSema().CheckParameterPacksForExpansion(
+            EllipsisLoc, PatternRange, Unexpanded, MLTAL, ShouldExpand,
+            RetainExpansion, NumExpansions);
       }
 
       return getSema().CheckParameterPacksForExpansion(EllipsisLoc,

>From 412bf5ec0026a5ff899481f050ec748344e25131 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 30 Jul 2024 20:22:36 +0800
Subject: [PATCH 20/26] Remove the fixes for constraints

---
 clang/include/clang/Sema/Sema.h               |  9 ----
 clang/lib/Sema/SemaTemplateInstantiate.cpp    | 19 ++-----
 clang/lib/Sema/SemaTemplateVariadic.cpp       | 50 ++-----------------
 clang/lib/Sema/TreeTransform.h                | 20 ++------
 .../SemaCXX/fold_lambda_with_variadics.cpp    | 23 +--------
 5 files changed, 12 insertions(+), 109 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f92e2b3d0bd98..7bfdaaae45a93 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14037,15 +14037,6 @@ class Sema final : public SemaBase {
       TemplateArgument Arg,
       SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
 
-  /// Collect the set of unexpanded parameter packs within the given
-  /// template argument.
-  ///
-  /// \param Arg The template argument that will be traversed to find
-  /// unexpanded parameter packs.
-  void collectUnexpandedParameterPacksForFoldExprs(
-      Expr *E, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded,
-      SmallVectorImpl<UnexpandedParameterPack> &UnexpandedFromConstraints);
-
   /// Collect the set of unexpanded parameter packs within the given
   /// template argument.
   ///
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 4f8b261353124..077787b45a141 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1395,20 +1395,7 @@ namespace {
                                  SourceRange PatternRange,
                                  ArrayRef<UnexpandedParameterPack> Unexpanded,
                                  bool &ShouldExpand, bool &RetainExpansion,
-                                 std::optional<unsigned> &NumExpansions,
-                                 bool ForConstraints = false) {
-      if (ForConstraints) {
-        MultiLevelTemplateArgumentList MLTAL =
-            getSema().getTemplateInstantiationArgs(
-                cast<NamedDecl>(getSema().CurContext), /*DC=*/nullptr,
-                /*Final=*/false,
-                /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true,
-                /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true);
-        return getSema().CheckParameterPacksForExpansion(
-            EllipsisLoc, PatternRange, Unexpanded, MLTAL, ShouldExpand,
-            RetainExpansion, NumExpansions);
-      }
-
+                                 std::optional<unsigned> &NumExpansions) {
       return getSema().CheckParameterPacksForExpansion(EllipsisLoc,
                                                        PatternRange, Unexpanded,
                                                        TemplateArgs,
@@ -1688,8 +1675,8 @@ namespace {
           // RecoveryExpr that wraps the uninstantiated default argument so
           // that downstream diagnostics are omitted.
           ExprResult ErrorResult = SemaRef.CreateRecoveryExpr(
-              UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(), {UninstExpr},
-              UninstExpr->getType());
+              UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(),
+              { UninstExpr }, UninstExpr->getType());
           if (ErrorResult.isUsable())
             PVD->setDefaultArg(ErrorResult.get());
         }
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 168fc10342594..2966af847145e 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -8,18 +8,16 @@
 //  This file implements semantic analysis for C++0x variadic templates.
 //===----------------------------------------------------------------------===/
 
+#include "clang/Sema/Sema.h"
 #include "TypeLocBuilder.h"
-#include "clang/AST/ASTLambda.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/ParsedTemplate.h"
 #include "clang/Sema/ScopeInfo.h"
-#include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/Template.h"
-#include "llvm/Support/SaveAndRestore.h"
 #include <optional>
 
 using namespace clang;
@@ -41,10 +39,6 @@ namespace {
     bool InLambda = false;
     unsigned DepthLimit = (unsigned)-1;
 
-    FunctionDecl *CurrentFunction = nullptr;
-    bool InConstraint = false;
-    SmallVectorImpl<UnexpandedParameterPack> *UnexpandedFromConstraints;
-
     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
@@ -57,31 +51,18 @@ namespace {
       } else if (getDepthAndIndex(ND).first >= DepthLimit)
         return;
 
-      if (InConstraint && UnexpandedFromConstraints) {
-        UnexpandedFromConstraints->push_back({ND, Loc});
-        return;
-      }
-
       Unexpanded.push_back({ND, Loc});
     }
     void addUnexpanded(const TemplateTypeParmType *T,
                        SourceLocation Loc = SourceLocation()) {
-      if (T->getDepth() < DepthLimit) {
-        if (InConstraint && UnexpandedFromConstraints) {
-          UnexpandedFromConstraints->push_back({T, Loc});
-          return;
-        }
+      if (T->getDepth() < DepthLimit)
         Unexpanded.push_back({T, Loc});
-      }
     }
 
   public:
     explicit CollectUnexpandedParameterPacksVisitor(
-        SmallVectorImpl<UnexpandedParameterPack> &Unexpanded,
-        SmallVectorImpl<UnexpandedParameterPack> *UnexpandedFromConstraints =
-            nullptr)
-        : Unexpanded(Unexpanded),
-          UnexpandedFromConstraints(UnexpandedFromConstraints) {}
+        SmallVectorImpl<UnexpandedParameterPack> &Unexpanded)
+        : Unexpanded(Unexpanded) {}
 
     bool shouldWalkTypesOfTypeLocs() const { return false; }
 
@@ -156,10 +137,6 @@ namespace {
     bool TraverseStmt(Stmt *S) {
       Expr *E = dyn_cast_or_null<Expr>(S);
 
-      llvm::SaveAndRestore _(InConstraint);
-      if (CurrentFunction && CurrentFunction->getTrailingRequiresClause() == S)
-        InConstraint = true;
-
       if ((E && E->containsUnexpandedParameterPack()) || InLambda)
         return inherited::TraverseStmt(S);
 
@@ -194,17 +171,6 @@ namespace {
       if (D && D->isParameterPack())
         return true;
 
-      if (D && D->isFunctionOrFunctionTemplate()) {
-        FunctionDecl *FD;
-        if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
-          FD = FTD->getTemplatedDecl();
-        else
-          FD = cast<FunctionDecl>(D);
-
-        llvm::SaveAndRestore _(CurrentFunction, FD);
-        return inherited::TraverseDecl(D);
-      }
-
       return inherited::TraverseDecl(D);
     }
 
@@ -571,14 +537,6 @@ void Sema::collectUnexpandedParameterPacks(TemplateArgument Arg,
     .TraverseTemplateArgument(Arg);
 }
 
-void Sema::collectUnexpandedParameterPacksForFoldExprs(
-    Expr *E, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded,
-    SmallVectorImpl<UnexpandedParameterPack> &UnexpandedFromConstraints) {
-  CollectUnexpandedParameterPacksVisitor Visitor(Unexpanded,
-                                                 &UnexpandedFromConstraints);
-  Visitor.TraverseTemplateArgument(E);
-}
-
 void Sema::collectUnexpandedParameterPacks(TemplateArgumentLoc Arg,
                    SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) {
   CollectUnexpandedParameterPacksVisitor(Unexpanded)
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 3de7b69c5e6b3..ce7320121b4ef 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -288,8 +288,7 @@ class TreeTransform {
                                SourceRange PatternRange,
                                ArrayRef<UnexpandedParameterPack> Unexpanded,
                                bool &ShouldExpand, bool &RetainExpansion,
-                               std::optional<unsigned> &NumExpansions,
-                               bool ForConstraints = false) {
+                               std::optional<unsigned> &NumExpansions) {
     ShouldExpand = false;
     return false;
   }
@@ -15306,11 +15305,9 @@ TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
 
   Expr *Pattern = E->getPattern();
 
-  SmallVector<UnexpandedParameterPack, 2> Unexpanded, UnexpandedFromConstraints;
-  getSema().collectUnexpandedParameterPacksForFoldExprs(
-      Pattern, Unexpanded, UnexpandedFromConstraints);
-  assert((!Unexpanded.empty() || !UnexpandedFromConstraints.empty()) &&
-         "Pack expansion without parameter packs?");
+  SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+  getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded);
+  assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
 
   // Determine whether the set of unexpanded parameter packs can and should
   // be expanded.
@@ -15325,15 +15322,6 @@ TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
                                            NumExpansions))
     return true;
 
-  // Only constraints contain unexpanded packs.
-  if (Unexpanded.empty() && !UnexpandedFromConstraints.empty()) {
-    if (getDerived().TryExpandParameterPacks(
-            E->getEllipsisLoc(), Pattern->getSourceRange(),
-            UnexpandedFromConstraints, Expand, RetainExpansion, NumExpansions,
-            /*ForConstraints=*/true))
-      return true;
-  }
-
   if (!Expand) {
     // Do not expand any packs here, just transform and rebuild a fold
     // expression.
diff --git a/clang/test/SemaCXX/fold_lambda_with_variadics.cpp b/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
index 8539674b57190..200a19d8bc354 100644
--- a/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
+++ b/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
@@ -7,9 +7,6 @@ struct identity {
   using type = T;
 };
 
-template <class... T>
-concept C = false; // #concept-C
-
 template <class = void> void f() {
 
   static_assert([]<class... Is>(Is... x) {
@@ -25,27 +22,9 @@ template <class = void> void f() {
                                                           // expected-note@#instantiate-f {{requested here}}
   }(1);
 
-  []<class... Is>() {
-    ([]()               // expected-error {{no matching function}}
-       requires C<Is>   // expected-note {{because 'float' does not satisfy 'C'}} \
-                        // expected-note at -1 {{constraints not satisfied}}         \
-                        // expected-note@#concept-C {{evaluated to false}}        \
-                        // expected-note@#instantiate-f {{requested here}}
-     {}(),
-     ...);
-  }.template operator()<char, int, float>(); // expected-note {{requested here}}
-
-
-  []<class... Is>() {
-    ([](Is)
-       requires (!C<Is>)
-     {}(Is()),
-     ...);
-  }.template operator()<char, int, float>();
-
   []<class... Is>() {
     ([]<class = Is>(Is)
-       noexcept(bool(Is())) requires (!C<Is>)
+       noexcept(bool(Is()))
      {}(Is()),
      ...);
   }.template operator()<char, int, float>();

>From de76522ea2ed1021576a321f683ec7c69ab406e1 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 30 Jul 2024 20:25:34 +0800
Subject: [PATCH 21/26] Remove extra blanks

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

diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 2966af847145e..3d4ccaf68c700 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -136,7 +136,6 @@ namespace {
     /// do not contain unexpanded parameter packs.
     bool TraverseStmt(Stmt *S) {
       Expr *E = dyn_cast_or_null<Expr>(S);
-
       if ((E && E->containsUnexpandedParameterPack()) || InLambda)
         return inherited::TraverseStmt(S);
 

>From 930e8f9eda87fd8dbac42bde6a5de66aed5b52f9 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 30 Jul 2024 20:28:12 +0800
Subject: [PATCH 22/26] Remove changes for constraints in TransformLambdaExpr

---
 clang/lib/Sema/TreeTransform.h | 15 +++------------
 1 file changed, 3 insertions(+), 12 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index ce7320121b4ef..49e6f79eedccc 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -14655,20 +14655,11 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
     Params = FPTL.getParams();
   }
 
-  Expr *TrailingRequiresExpr =
-      E->getCallOperator()->getTrailingRequiresClause();
-  if (TrailingRequiresExpr) {
-    // If we're in an expansion, do not propagate up this flag. Otherwise we
-    // would fail to unexpand the surrounding CXXFoldExpr.
-    if (getSema().ArgumentPackSubstitutionIndex == -1)
-      LSI->ContainsUnexpandedParameterPack |=
-          TrailingRequiresExpr->containsUnexpandedParameterPack();
-  }
-
   getSema().CompleteLambdaCallOperator(
       NewCallOperator, E->getCallOperator()->getLocation(),
-      E->getCallOperator()->getInnerLocStart(), TrailingRequiresExpr,
-      NewCallOpTSI, E->getCallOperator()->getConstexprKind(),
+      E->getCallOperator()->getInnerLocStart(),
+      E->getCallOperator()->getTrailingRequiresClause(), NewCallOpTSI,
+      E->getCallOperator()->getConstexprKind(),
       E->getCallOperator()->getStorageClass(), Params,
       E->hasExplicitResultType());
 

>From 9ce3250477317ff65a053f542271919149a86b23 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 1 Aug 2024 22:06:51 +0800
Subject: [PATCH 23/26] Simplify the RebuildLambdaExpr()

---
 clang/lib/Sema/SemaLambda.cpp              |  4 ++++
 clang/lib/Sema/SemaTemplateInstantiate.cpp |  5 +++--
 clang/lib/Sema/TreeTransform.h             | 20 ++------------------
 3 files changed, 9 insertions(+), 20 deletions(-)

diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 601077e9f3334..b697918120806 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1021,6 +1021,8 @@ void Sema::CompleteLambdaCallOperator(
       getGenericLambdaTemplateParameterList(LSI, *this);
 
   DeclContext *DC = Method->getLexicalDeclContext();
+  // DeclContext::addDecl() assumes that the DeclContext we're adding to is the
+  // lexical context of the Method. Do so.
   Method->setLexicalDeclContext(LSI->Lambda);
   if (TemplateParams) {
     FunctionTemplateDecl *TemplateMethod =
@@ -1105,6 +1107,8 @@ void Sema::ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
 
   CXXMethodDecl *Method = CreateLambdaCallOperator(Intro.Range, Class);
   LSI->CallOperator = Method;
+  // Temporarily set the lexical declaration context to the current
+  // context, so that the Scope stack matches the lexical nesting.
   Method->setLexicalDeclContext(CurContext);
 
   PushDeclContext(CurScope, Method);
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 077787b45a141..6bd8cbbba23b2 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1660,8 +1660,8 @@ namespace {
       return inherited::TransformLambdaExpr(E);
     }
 
-    void RebuildLambdaExprImpl(SourceLocation StartLoc, SourceLocation EndLoc,
-                               LambdaScopeInfo *LSI) {
+    ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
+                                 LambdaScopeInfo *LSI) {
       CXXMethodDecl *MD = LSI->CallOperator;
       for (ParmVarDecl *PVD : MD->parameters()) {
         assert(PVD && "null in a parameter list");
@@ -1681,6 +1681,7 @@ namespace {
             PVD->setDefaultArg(ErrorResult.get());
         }
       }
+      return inherited::RebuildLambdaExpr(StartLoc, EndLoc, LSI);
     }
 
     StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body) {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 49e6f79eedccc..d79ffc61fb785 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -4006,24 +4006,8 @@ class TreeTransform {
                                       NumExpansions);
   }
 
-  void RebuildLambdaExprImpl(SourceLocation StartLoc, SourceLocation EndLoc,
-                             LambdaScopeInfo *LSI) {}
-
   ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
                                LambdaScopeInfo *LSI) {
-    CXXRecordDecl *Class = LSI->Lambda;
-    CXXMethodDecl *CallOperator = LSI->CallOperator;
-    CallOperator->setLexicalDeclContext(Class);
-    Decl *TemplateOrNonTemplateCallOperatorDecl =
-        CallOperator->getDescribedFunctionTemplate()
-            ? CallOperator->getDescribedFunctionTemplate()
-            : cast<Decl>(CallOperator);
-    // FIXME: Is this really the best choice? Keeping the lexical decl context
-    // set as CurContext seems more faithful to the source.
-    TemplateOrNonTemplateCallOperatorDecl->setLexicalDeclContext(Class);
-
-    getDerived().RebuildLambdaExprImpl(StartLoc, EndLoc, LSI);
-
     // Default arguments might contain unexpanded packs that would expand later.
     for (ParmVarDecl *PVD : LSI->CallOperator->parameters()) {
       if (Expr *Init = PVD->getInit())
@@ -14442,7 +14426,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
 
   CXXMethodDecl *NewCallOperator =
       getSema().CreateLambdaCallOperator(E->getIntroducerRange(), Class);
-  NewCallOperator->setLexicalDeclContext(getSema().CurContext);
 
   // Enter the scope of the lambda.
   getSema().buildLambdaScope(LSI, NewCallOperator, E->getIntroducerRange(),
@@ -14757,7 +14740,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
   Class->setTypeForDecl(nullptr);
   getSema().Context.getTypeDeclType(Class);
 
-  return RebuildLambdaExpr(E->getBeginLoc(), Body.get()->getEndLoc(), &LSICopy);
+  return getDerived().RebuildLambdaExpr(E->getBeginLoc(),
+                                        Body.get()->getEndLoc(), &LSICopy);
 }
 
 template<typename Derived>

>From e74a968b706c60c031e987eec3a20036c0e5380c Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 5 Aug 2024 22:22:47 +0800
Subject: [PATCH 24/26] Remove most self-explanatory comments

---
 clang/lib/Sema/TreeTransform.h | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 0cab574f84867..0a38af269d3ee 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -4030,7 +4030,7 @@ class TreeTransform {
 
   ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
                                LambdaScopeInfo *LSI) {
-    // Default arguments might contain unexpanded packs that would expand later.
+    // Default arguments might still contain unexpanded packs that would expand later.
     for (ParmVarDecl *PVD : LSI->CallOperator->parameters()) {
       if (Expr *Init = PVD->getInit())
         LSI->ContainsUnexpandedParameterPack |=
@@ -14615,7 +14615,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
         }
         NewVDs.push_back(NewVD);
         getSema().addInitCapture(LSI, NewVD, C->getCaptureKind() == LCK_ByRef);
-        // The Init expression might be expanded by an outer fold expression.
         LSI->ContainsUnexpandedParameterPack |=
             Init.get()->containsUnexpandedParameterPack();
       }
@@ -14685,7 +14684,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
       continue;
     }
 
-    // The captured pack might be expanded by an outer fold expression.
     if (auto *VD = dyn_cast<VarDecl>(CapturedVar); VD && !C->isPackExpansion())
       LSI->ContainsUnexpandedParameterPack |= VD->isParameterPack();
 
@@ -14703,8 +14701,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
   if (TPL) {
     getSema().AddTemplateParametersToLambdaCallOperator(NewCallOperator, Class,
                                                         TPL);
-    // The parameter list might reference to a pack that an outer fold
-    // expression would expand.
     LSI->ContainsUnexpandedParameterPack |=
         TPL->containsUnexpandedParameterPack();
   }

>From 27bd832788deb21274459129b5e6760320b5169c Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 5 Aug 2024 22:34:25 +0800
Subject: [PATCH 25/26] Remove one more gratuitous comment

---
 clang/lib/Sema/TreeTransform.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 0a38af269d3ee..19bbed0a378cf 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -4030,7 +4030,6 @@ class TreeTransform {
 
   ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
                                LambdaScopeInfo *LSI) {
-    // Default arguments might still contain unexpanded packs that would expand later.
     for (ParmVarDecl *PVD : LSI->CallOperator->parameters()) {
       if (Expr *Init = PVD->getInit())
         LSI->ContainsUnexpandedParameterPack |=

>From ac899374ecd21ba29880d9e4b5c7ecb54e7cd78d Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 5 Aug 2024 22:48:21 +0800
Subject: [PATCH 26/26] FIXME

Co-authored-by: cor3ntin <corentinjabot at gmail.com>
---
 clang/test/SemaCXX/fold_lambda_with_variadics.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/SemaCXX/fold_lambda_with_variadics.cpp b/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
index 200a19d8bc354..14e242f009dc5 100644
--- a/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
+++ b/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
@@ -58,7 +58,7 @@ template <class = void> void f() {
   }(2, 'b');
 
 #if 0
-  // https://github.com/llvm/llvm-project/issues/18873
+  // FIXME: https://github.com/llvm/llvm-project/issues/18873
   [](auto ...x) { // #1
     ([&](auto ...y) {  // #2
       ([x, y] { }(), ...); // #3



More information about the cfe-commits mailing list