[clang] [Clang] Introduce FunctionParmPackDecl for expanded lambda captures (PR #107995)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 10 02:59:23 PDT 2024
https://github.com/zyn0217 created https://github.com/llvm/llvm-project/pull/107995
This patch continues the effort of #86265, fixing another issue involving expanded captures that were not able to held off in the process of the inner lambda's transformation.
Similar to FunctionParmPackExpr, FunctionParmPackDecl is introduced to model expanded parameters (particularly, lambda parameters) in the situation where the expansion shouldn't occur in the subsequent transformation. This node acts as an intermediate Decl and thus should not show up in the eventual AST.
Fixes #18873
>From c97f213bc412dd2a4d6ee5c8e58a82f04dbbd03b Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 10 Sep 2024 17:23:55 +0800
Subject: [PATCH] [Clang] Introduce FunctionParmPackDecl for expanded lambda
captures
---
clang/docs/ReleaseNotes.rst | 2 +-
clang/include/clang/AST/DeclTemplate.h | 37 +++++++++++++++++++
clang/include/clang/AST/RecursiveASTVisitor.h | 2 +
clang/include/clang/Basic/DeclNodes.td | 1 +
clang/include/clang/Sema/Template.h | 3 ++
.../include/clang/Serialization/ASTBitCodes.h | 5 ++-
clang/lib/AST/DeclBase.cpp | 1 +
clang/lib/AST/DeclTemplate.cpp | 37 +++++++++++++++++++
clang/lib/CodeGen/CGDecl.cpp | 1 +
clang/lib/Sema/SemaTemplateInstantiate.cpp | 25 ++++++++++++-
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 5 +++
clang/lib/Sema/TreeTransform.h | 14 +++++--
clang/lib/Serialization/ASTCommon.cpp | 1 +
clang/lib/Serialization/ASTReaderDecl.cpp | 11 ++++++
clang/lib/Serialization/ASTWriterDecl.cpp | 10 +++++
.../SemaCXX/fold_lambda_with_variadics.cpp | 31 ++++------------
clang/tools/libclang/CIndex.cpp | 1 +
17 files changed, 157 insertions(+), 30 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 250821a9f9c45c..f39c39f8646d08 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -344,7 +344,7 @@ Bug Fixes to C++ Support
module imports in those situations. (#GH60336)
- Fix init-capture packs having a size of one before being instantiated. (#GH63677)
- Clang now preserves the unexpanded flag in a lambda transform used for pack expansion. (#GH56852), (#GH85667),
- (#GH99877).
+ (#GH99877), (#GH18873).
- Fixed a bug when diagnosing ambiguous explicit specializations of constrained member functions.
- Fixed an assertion failure when selecting a function from an overload set that includes a
specialization of a conversion function template.
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 687715a22e9fd3..3730f5bee09d2a 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3220,6 +3220,43 @@ class ImplicitConceptSpecializationDecl final
friend class ASTDeclReader;
};
+class FunctionParmPackDecl final
+ : public Decl,
+ private llvm::TrailingObjects<FunctionParmPackDecl, VarDecl *> {
+ friend TrailingObjects;
+ friend class ASTDeclReader;
+
+ /// The function parameter pack which was referenced.
+ NamedDecl *Pattern;
+
+ unsigned NumExpansions;
+
+ FunctionParmPackDecl(DeclContext *DC, SourceLocation StartLoc,
+ NamedDecl *Pattern, ArrayRef<VarDecl *> ExpandedParams);
+
+ void setExpandedParams(ArrayRef<VarDecl *> ExpandedParams);
+
+public:
+ static FunctionParmPackDecl *Create(ASTContext &C, DeclContext *DC,
+ SourceLocation StartLoc,
+ NamedDecl *Pattern,
+ ArrayRef<VarDecl *> ExpandedParams);
+
+ static FunctionParmPackDecl *
+ CreateDeserialized(ASTContext &C, GlobalDeclID ID, unsigned NumExpansions);
+
+ ArrayRef<VarDecl *> getExpandedParams() const {
+ return ArrayRef<VarDecl *>(getTrailingObjects<VarDecl *>(), NumExpansions);
+ }
+
+ unsigned getNumExpansions() const { return NumExpansions; }
+
+ NamedDecl *getPattern() const { return Pattern; }
+
+ static bool classofKind(Kind K) { return K == FunctionParmPack; }
+ static bool classof(const Decl *D) { return classofKind(D->getKind()); }
+};
+
/// A template parameter object.
///
/// Template parameter objects represent values of class type used as template
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 3389670a2ab9d9..3297e9eb009678 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2353,6 +2353,8 @@ DEF_TRAVERSE_DECL(ImplicitConceptSpecializationDecl, {
TRY_TO(TraverseTemplateArguments(D->getTemplateArguments()));
})
+DEF_TRAVERSE_DECL(FunctionParmPackDecl, {})
+
#undef DEF_TRAVERSE_DECL
// ----------------- Stmt traversal -----------------
diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index 48396e85c5adac..694d3d8b79a183 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -91,6 +91,7 @@ def Named : DeclNode<Decl, "named declarations", 1>;
def ObjCProperty : DeclNode<Named, "Objective-C properties">;
def ObjCCompatibleAlias : DeclNode<Named>;
def ImplicitConceptSpecialization : DeclNode<Decl>;
+def FunctionParmPack : DeclNode<Decl>;
def LinkageSpec : DeclNode<Decl>, DeclContext;
def Export : DeclNode<Decl>, DeclContext;
def ObjCPropertyImpl : DeclNode<Decl>;
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 0340c23fd170d6..5de4bf0148652c 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -515,6 +515,9 @@ enum class TemplateSubstitutionKind : char {
llvm::PointerUnion<Decl *, DeclArgumentPack *> *
findInstantiationOf(const Decl *D);
+ llvm::PointerUnion<Decl *, DeclArgumentPack *> *
+ findInstantiationUnsafe(const Decl *D);
+
void InstantiatedLocal(const Decl *D, Decl *Inst);
void InstantiatedLocalPackArg(const Decl *D, VarDecl *Inst);
void MakeInstantiatedLocalArgPack(const Decl *D);
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 4410df296d8efc..3f23cea4591b40 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1493,7 +1493,10 @@ enum DeclCode {
/// An ImplicitConceptSpecializationDecl record.
DECL_IMPLICIT_CONCEPT_SPECIALIZATION,
- DECL_LAST = DECL_IMPLICIT_CONCEPT_SPECIALIZATION
+ /// A FunctionParmPackDecl record.
+ DECL_FUNCTION_PARM_PACK,
+
+ DECL_LAST = DECL_FUNCTION_PARM_PACK
};
/// Record codes for each kind of statement or expression.
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index f42857f20efc44..728b20b39d1635 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -986,6 +986,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case LifetimeExtendedTemporary:
case RequiresExprBody:
case ImplicitConceptSpecialization:
+ case FunctionParmPack:
// Never looked up by name.
return 0;
}
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 976b3a3e1ecedb..34d60298929c2c 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1132,6 +1132,43 @@ void ImplicitConceptSpecializationDecl::setTemplateArguments(
getTrailingObjects<TemplateArgument>());
}
+//===----------------------------------------------------------------------===//
+// FunctionParmPackDecl Implementation
+//===----------------------------------------------------------------------===//
+FunctionParmPackDecl::FunctionParmPackDecl(DeclContext *DC,
+ SourceLocation StartLoc,
+ NamedDecl *Pattern,
+ ArrayRef<VarDecl *> ExpandedParams)
+ : Decl(FunctionParmPack, DC, StartLoc), Pattern(Pattern),
+ NumExpansions(ExpandedParams.size()) {
+ std::uninitialized_copy(ExpandedParams.begin(), ExpandedParams.end(),
+ getTrailingObjects<VarDecl *>());
+}
+
+FunctionParmPackDecl *
+FunctionParmPackDecl::Create(ASTContext &C, DeclContext *DC,
+ SourceLocation StartLoc, NamedDecl *Pattern,
+ ArrayRef<VarDecl *> ExpandedParams) {
+ return new (C, DC, additionalSizeToAlloc<VarDecl *>(ExpandedParams.size()))
+ FunctionParmPackDecl(DC, StartLoc, Pattern, ExpandedParams);
+}
+
+FunctionParmPackDecl *
+FunctionParmPackDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+ unsigned NumExpansions) {
+ return new (C, ID, additionalSizeToAlloc<VarDecl *>(NumExpansions))
+ FunctionParmPackDecl(/*DC=*/nullptr, SourceLocation(),
+ /*Pattern=*/nullptr,
+ SmallVector<VarDecl *>(NumExpansions, nullptr));
+}
+
+void FunctionParmPackDecl::setExpandedParams(
+ ArrayRef<VarDecl *> ExpandedParams) {
+ assert(ExpandedParams.size() == NumExpansions);
+ std::uninitialized_copy(ExpandedParams.begin(), ExpandedParams.end(),
+ getTrailingObjects<VarDecl *>());
+}
+
//===----------------------------------------------------------------------===//
// ClassTemplatePartialSpecializationDecl Implementation
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 563f728e29d781..8269a2f9e0fb6f 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -134,6 +134,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
case Decl::ImplicitConceptSpecialization:
case Decl::LifetimeExtendedTemporary:
case Decl::RequiresExprBody:
+ case Decl::FunctionParmPack:
// None of these decls require codegen support.
return;
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index c42cc250bb904a..b09e42356a1c9c 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1855,6 +1855,21 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
// template parameter.
}
+ if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(D);
+ PVD && SemaRef.ArgumentPackSubstitutionIndex == -1) {
+ if (auto *Found =
+ SemaRef.CurrentInstantiationScope->findInstantiationUnsafe(D)) {
+ using DeclArgumentPack = LocalInstantiationScope::DeclArgumentPack;
+ if (auto *Pack = Found->dyn_cast<DeclArgumentPack *>()) {
+ assert(SemaRef.getCurLambda() &&
+ "Only lambdas can hold off an expanded pack expansion");
+ return FunctionParmPackDecl::Create(SemaRef.getASTContext(),
+ PVD->getDeclContext(),
+ PVD->getLocation(), PVD, *Pack);
+ }
+ }
+ }
+
return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs);
}
@@ -4369,9 +4384,8 @@ static const Decl *getCanonicalParmVarDecl(const Decl *D) {
return D;
}
-
llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *
-LocalInstantiationScope::findInstantiationOf(const Decl *D) {
+LocalInstantiationScope::findInstantiationUnsafe(const Decl *D) {
D = getCanonicalParmVarDecl(D);
for (LocalInstantiationScope *Current = this; Current;
Current = Current->Outer) {
@@ -4395,6 +4409,13 @@ LocalInstantiationScope::findInstantiationOf(const Decl *D) {
if (!Current->CombineWithOuterScope)
break;
}
+ return nullptr;
+}
+
+llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *
+LocalInstantiationScope::findInstantiationOf(const Decl *D) {
+ if (auto *Result = findInstantiationUnsafe(D))
+ return Result;
// If we're performing a partial substitution during template argument
// deduction, we may not have values for template parameters yet.
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 6df412cbb09c83..6f2a301847c317 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4119,6 +4119,11 @@ Decl *TemplateDeclInstantiator::VisitImplicitConceptSpecializationDecl(
llvm_unreachable("Concept specializations cannot reside inside a template");
}
+Decl *
+TemplateDeclInstantiator::VisitFunctionParmPackDecl(FunctionParmPackDecl *D) {
+ llvm_unreachable("Function param packs cannot reside inside a template");
+}
+
Decl *
TemplateDeclInstantiator::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) {
return RequiresExprBodyDecl::Create(SemaRef.Context, D->getDeclContext(),
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 0daf620b4123e4..f8b7cca66bc83f 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -14722,12 +14722,20 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
}
// Transform the captured variable.
- auto *CapturedVar = cast_or_null<ValueDecl>(
- getDerived().TransformDecl(C->getLocation(), C->getCapturedVar()));
- if (!CapturedVar || CapturedVar->isInvalidDecl()) {
+ Decl *NewCapturedDecl =
+ getDerived().TransformDecl(C->getLocation(), C->getCapturedVar());
+ if (!NewCapturedDecl || NewCapturedDecl->isInvalidDecl()) {
Invalid = true;
continue;
}
+ if (auto *FPPD = dyn_cast<FunctionParmPackDecl>(NewCapturedDecl)) {
+ LSI->ContainsUnexpandedParameterPack = true;
+ for (VarDecl *Expanded : FPPD->getExpandedParams())
+ getSema().tryCaptureVariable(Expanded, C->getLocation(), Kind,
+ EllipsisLoc);
+ continue;
+ }
+ auto *CapturedVar = cast<ValueDecl>(NewCapturedDecl);
// This is not an init-capture; however it contains an unexpanded pack e.g.
// ([Pack] {}(), ...)
diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp
index f30642f513ae4a..151bfbf145c515 100644
--- a/clang/lib/Serialization/ASTCommon.cpp
+++ b/clang/lib/Serialization/ASTCommon.cpp
@@ -453,6 +453,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
case Decl::RequiresExprBody:
case Decl::UnresolvedUsingIfExists:
case Decl::HLSLBuffer:
+ case Decl::FunctionParmPack:
return false;
// These indirectly derive from Redeclarable<T> but are not actually
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 9272e23c7da3fc..c20532601ebbfc 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -389,6 +389,7 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
void VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D);
void VisitDeclaratorDecl(DeclaratorDecl *DD);
void VisitFunctionDecl(FunctionDecl *FD);
+ void VisitFunctionParmPackDecl(FunctionParmPackDecl *D);
void VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *GD);
void VisitCXXMethodDecl(CXXMethodDecl *D);
void VisitCXXConstructorDecl(CXXConstructorDecl *D);
@@ -2406,6 +2407,13 @@ void ASTDeclReader::VisitImplicitConceptSpecializationDecl(
D->setTemplateArguments(Args);
}
+void ASTDeclReader::VisitFunctionParmPackDecl(FunctionParmPackDecl *D) {
+ SmallVector<VarDecl *, 4> Expanded;
+ for (unsigned I = 0; I < D->getNumExpansions(); ++I)
+ Expanded.push_back(cast<VarDecl>(Record.readDeclRef()));
+ D->setExpandedParams(Expanded);
+}
+
void ASTDeclReader::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) {
}
@@ -4158,6 +4166,9 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
D = ImplicitConceptSpecializationDecl::CreateDeserialized(Context, ID,
Record.readInt());
break;
+ case DECL_FUNCTION_PARM_PACK:
+ D = FunctionParmPackDecl::CreateDeserialized(Context, ID, Record.readInt());
+ break;
}
assert(D && "Unknown declaration reading AST file");
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 555f6325da646b..a803d8d8c9611e 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -90,6 +90,7 @@ namespace clang {
void VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D);
void VisitDeclaratorDecl(DeclaratorDecl *D);
void VisitFunctionDecl(FunctionDecl *D);
+ void VisitFunctionParmPackDecl(FunctionParmPackDecl *D);
void VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D);
void VisitCXXMethodDecl(CXXMethodDecl *D);
void VisitCXXConstructorDecl(CXXConstructorDecl *D);
@@ -1700,6 +1701,15 @@ void ASTDeclWriter::VisitImplicitConceptSpecializationDecl(
Code = serialization::DECL_IMPLICIT_CONCEPT_SPECIALIZATION;
}
+void ASTDeclWriter::VisitFunctionParmPackDecl(FunctionParmPackDecl *D) {
+ Record.push_back(D->getNumExpansions());
+ VisitDecl(D);
+ Record.AddDeclRef(D->getPattern());
+ for (VarDecl *VD : D->getExpandedParams())
+ Record.AddDeclRef(VD);
+ Code = serialization::DECL_FUNCTION_PARM_PACK;
+}
+
void ASTDeclWriter::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) {
Code = serialization::DECL_REQUIRES_EXPR_BODY;
}
diff --git a/clang/test/SemaCXX/fold_lambda_with_variadics.cpp b/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
index 14e242f009dc51..d86cc72ae09249 100644
--- a/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
+++ b/clang/test/SemaCXX/fold_lambda_with_variadics.cpp
@@ -57,28 +57,14 @@ template <class = void> void f() {
})(1);
}(2, 'b');
-#if 0
- // FIXME: 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!
+ // https://github.com/llvm/llvm-project/issues/18873
+ static_assert([]<auto... z>(auto ...x) {
+ return [&](auto ...y) {
+ return ([x, y] {
+ return x + y + z;
+ }() + ...);
+ }(1, 'a');
+ }.template operator()<2, 'b'>(3, 'c') == 1 + 'a' + 2 + 'b' + 3 + 'c');
[](auto ...x) { // #outer
([&](auto ...y) { // #inner
@@ -89,7 +75,6 @@ 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
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index e821c5e4c588b6..432c9924e37d1f 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -7068,6 +7068,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
case Decl::LifetimeExtendedTemporary:
case Decl::RequiresExprBody:
case Decl::UnresolvedUsingIfExists:
+ case Decl::FunctionParmPack:
return C;
// Declaration kinds that don't make any sense here, but are
More information about the cfe-commits
mailing list