[clang] [Clang] Don't assert on substituted-but-yet-expanded packs for nested lambdas (PR #112896)

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 18 06:15:38 PDT 2024


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

>From 6caf16d6ce8eb939c91ed87493c2ccab82d38411 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 18 Oct 2024 20:22:08 +0800
Subject: [PATCH 1/2] [Clang] Don't assert on substituted-but-yet-expanded
 packs for nested lambdas

---
 clang/docs/ReleaseNotes.rst                  |  2 +-
 clang/lib/Sema/SemaTemplateVariadic.cpp      | 49 ++++++++++++++------
 clang/test/SemaCXX/lambda-pack-expansion.cpp | 40 ++++++++++++++++
 3 files changed, 75 insertions(+), 16 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b430b2b0ee3187..d071c480f36dc7 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -486,7 +486,7 @@ Bug Fixes to C++ Support
 - Clang no longer tries to capture non-odr used default arguments of template parameters of generic lambdas (#GH107048)
 - Fixed a bug where defaulted comparison operators would remove ``const`` from base classes. (#GH102588)
 - Fix a crash when using ``source_location`` in the trailing return type of a lambda expression. (#GH67134)
-- A follow-up fix was added for (#GH61460), as the previous fix was not entirely correct. (#GH86361)
+- A follow-up fix was added for (#GH61460), as the previous fix was not entirely correct. (#GH86361), (#GH112352)
 - Fixed a crash in the typo correction of an invalid CTAD guide. (#GH107887)
 - Fixed a crash when clang tries to subtitute parameter pack while retaining the parameter
   pack. (#GH63819), (#GH107560)
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 19bd4547665835..151b328872bd75 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -41,7 +41,7 @@ namespace {
     unsigned DepthLimit = (unsigned)-1;
 
 #ifndef NDEBUG
-    bool ContainsFunctionParmPackExpr = false;
+    bool ContainsIntermediatePacks = false;
 #endif
 
     void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
@@ -114,6 +114,11 @@ namespace {
           addUnexpanded(TTP);
       }
 
+#ifndef NDEBUG
+      ContainsIntermediatePacks |=
+          (bool)Template.getAsSubstTemplateTemplateParmPack();
+#endif
+
       return inherited::TraverseTemplateName(Template);
     }
 
@@ -297,13 +302,28 @@ namespace {
 
 #ifndef NDEBUG
     bool TraverseFunctionParmPackExpr(FunctionParmPackExpr *) {
-      ContainsFunctionParmPackExpr = true;
+      ContainsIntermediatePacks = true;
+      return true;
+    }
+
+    bool TraverseSubstNonTypeTemplateParmPackExpr(
+        SubstNonTypeTemplateParmPackExpr *) {
+      ContainsIntermediatePacks = true;
+      return true;
+    }
+
+    bool VisitSubstTemplateTypeParmPackType(SubstTemplateTypeParmPackType *) {
+      ContainsIntermediatePacks = true;
       return true;
     }
 
-    bool containsFunctionParmPackExpr() const {
-      return ContainsFunctionParmPackExpr;
+    bool
+    VisitSubstTemplateTypeParmPackTypeLoc(SubstTemplateTypeParmPackTypeLoc) {
+      ContainsIntermediatePacks = true;
+      return true;
     }
+
+    bool containsIntermediatePacks() const { return ContainsIntermediatePacks; }
 #endif
   };
 }
@@ -439,21 +459,20 @@ bool Sema::DiagnoseUnexpandedParameterPack(Expr *E,
   if (!E->containsUnexpandedParameterPack())
     return false;
 
-  // FunctionParmPackExprs are special:
-  //
-  // 1) they're used to model DeclRefExprs to packs that have been expanded but
-  // had that expansion held off in the process of transformation.
-  //
-  // 2) they always have the unexpanded dependencies but don't introduce new
-  // unexpanded packs.
-  //
-  // We might encounter a FunctionParmPackExpr being a full expression, which a
-  // larger CXXFoldExpr would expand.
   SmallVector<UnexpandedParameterPack, 2> Unexpanded;
   CollectUnexpandedParameterPacksVisitor Visitor(Unexpanded);
   Visitor.TraverseStmt(E);
-  assert((!Unexpanded.empty() || Visitor.containsFunctionParmPackExpr()) &&
+#ifndef NDEBUG
+  // The expression might contain a type/subexpression that has been substituted
+  // but has the expansion held off, e.g. a FunctionParmPackExpr which a larger
+  // CXXFoldExpr would expand. It's only possible when expanding a lambda as a
+  // pattern of a fold expression, so don't fire on an empty result in that
+  // case.
+  bool LambdaReferencingOuterPacks =
+      getEnclosingLambdaOrBlock() && Visitor.containsIntermediatePacks();
+  assert((!Unexpanded.empty() || LambdaReferencingOuterPacks) &&
          "Unable to find unexpanded parameter packs");
+#endif
   return DiagnoseUnexpandedParameterPacks(E->getBeginLoc(), UPPC, Unexpanded);
 }
 
diff --git a/clang/test/SemaCXX/lambda-pack-expansion.cpp b/clang/test/SemaCXX/lambda-pack-expansion.cpp
index 0e60ecd8756600..7f7dd3bb095d57 100644
--- a/clang/test/SemaCXX/lambda-pack-expansion.cpp
+++ b/clang/test/SemaCXX/lambda-pack-expansion.cpp
@@ -94,3 +94,43 @@ template <typename... Ts> void g2(Ts... p1s) {
 void f1() { g(); }
 
 } // namespace GH61460
+
+namespace GH112352 {
+
+template <class>
+constexpr bool foo = false;
+
+template <int>
+constexpr bool bar = false;
+
+template <template<class> class>
+constexpr bool baz = false;
+
+struct S {
+  template <typename... Types, int... Values> void foldExpr1() {
+    (void)[]<int... Is> {
+      ([] {
+        Is;
+        foo<Types>;
+        bar<Values>;
+      } &&
+       ...);
+    };
+  }
+
+  template <template<class> class... TTPs> void foldExpr2() {
+    (void)[]<int... Is> {
+      ([] {
+        Is;
+        baz<TTPs>;
+      } && ...);
+    };
+  }
+};
+
+void use() {
+  S().foldExpr1();
+  S().foldExpr2();
+}
+
+} // namespace GH112352

>From c2d5cc751b37c4aa6f5472e17906bd4eee3723c7 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 18 Oct 2024 21:14:56 +0800
Subject: [PATCH 2/2] Propagate up ContainsUnexpandedParameterPack for VarDecls

---
 clang/lib/Sema/TreeTransform.h               | 21 ++++++++++++--------
 clang/test/SemaCXX/lambda-pack-expansion.cpp |  2 ++
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 45e8b3cf6bd8fc..ca04fb7f71be00 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -8385,14 +8385,19 @@ 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();
+    if (LSI) {
+      if (auto *TD = dyn_cast<TypeDecl>(Transformed))
+        LSI->ContainsUnexpandedParameterPack |=
+            getSema()
+                .getASTContext()
+                .getTypeDeclType(TD)
+                .getCanonicalType()
+                ->containsUnexpandedParameterPack();
+
+      if (auto *VD = dyn_cast<VarDecl>(Transformed))
+        LSI->ContainsUnexpandedParameterPack |=
+            VD->getType()->containsUnexpandedParameterPack();
+    }
 
     Decls.push_back(Transformed);
   }
diff --git a/clang/test/SemaCXX/lambda-pack-expansion.cpp b/clang/test/SemaCXX/lambda-pack-expansion.cpp
index 7f7dd3bb095d57..3d724c5e4d168b 100644
--- a/clang/test/SemaCXX/lambda-pack-expansion.cpp
+++ b/clang/test/SemaCXX/lambda-pack-expansion.cpp
@@ -111,6 +111,8 @@ struct S {
     (void)[]<int... Is> {
       ([] {
         Is;
+        // Propagate up the flag ContainsUnexpandedParameterPack from VarDecl.
+        S var(foo<Types>);
         foo<Types>;
         bar<Values>;
       } &&



More information about the cfe-commits mailing list