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

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 22 03:17:42 PDT 2024


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

None

>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] [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 690fc7ed271a3d..2d11a4b8c092a2 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 cfc1c3b3494788..0d1a2e54840e52 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 903fbfd18e779c..638ceac4148aec 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 2d22692f3ab750..f5a859c57034a5 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 35b2ffcefea355..31d85e1689af15 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();
+
+}



More information about the cfe-commits mailing list