[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