[clang] c9aaf34 - [SemaCXX] Handle lack of TypeSourceInfo on special member functions in templated lambdas
Bruno Cardoso Lopes via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 22 17:27:56 PDT 2021
Author: Bruno Cardoso Lopes
Date: 2021-06-22T17:26:05-07:00
New Revision: c9aaf34b8db884faa3d3ced4d2fb88fd45697408
URL: https://github.com/llvm/llvm-project/commit/c9aaf34b8db884faa3d3ced4d2fb88fd45697408
DIFF: https://github.com/llvm/llvm-project/commit/c9aaf34b8db884faa3d3ced4d2fb88fd45697408.diff
LOG: [SemaCXX] Handle lack of TypeSourceInfo on special member functions in templated lambdas
During template instantiation involving templated lambdas, clang
could hit an assertion in `TemplateDeclInstantiator::SubstFunctionType`
since the functions are not associated with any `TypeSourceInfo`:
`assert(OldTInfo && "substituting function without type source info");`
This path is triggered when using templated lambdas like the one added as
a test to this patch. To fix this:
- Create `TypeSourceInfo`s for special members and make sure the template
instantiator can get through all patterns.
- Introduce a `SpecialMemberTypeInfoRebuilder` tree transform to rewrite
such member function arguments. Without this, we get errors like:
`error: only special member functions and comparison operators may be defaulted`
since `getDefaultedFunctionKind` can't properly recognize these functions
as special members as part of `SetDeclDefaulted`.
Fixes PR45828 and PR44848
Differential Revision: https://reviews.llvm.org/D88327
Added:
clang/test/SemaCXX/lambdas-implicit-explicit-template.cpp
Modified:
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Sema/TreeTransform.h
Removed:
################################################################################
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 6d2710f1774ce..a68a06eb4d270 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -13202,6 +13202,16 @@ void Sema::setupImplicitSpecialMemberType(CXXMethodDecl *SpecialMem,
auto QT = Context.getFunctionType(ResultTy, Args, EPI);
SpecialMem->setType(QT);
+
+ // During template instantiation of implicit special member functions we need
+ // a reliable TypeSourceInfo for the function prototype in order to allow
+ // functions to be substituted.
+ if (inTemplateInstantiation() &&
+ cast<CXXRecordDecl>(SpecialMem->getParent())->isLambda()) {
+ TypeSourceInfo *TSI =
+ Context.getTrivialTypeSourceInfo(SpecialMem->getType());
+ SpecialMem->setTypeSourceInfo(TSI);
+ }
}
CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
@@ -14880,12 +14890,18 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
setupImplicitSpecialMemberType(CopyConstructor, Context.VoidTy, ArgType);
+ // During template instantiation of special member functions we need a
+ // reliable TypeSourceInfo for the parameter types in order to allow functions
+ // to be substituted.
+ TypeSourceInfo *TSI = nullptr;
+ if (inTemplateInstantiation() && ClassDecl->isLambda())
+ TSI = Context.getTrivialTypeSourceInfo(ArgType);
+
// Add the parameter to the constructor.
- ParmVarDecl *FromParam = ParmVarDecl::Create(Context, CopyConstructor,
- ClassLoc, ClassLoc,
- /*IdentifierInfo=*/nullptr,
- ArgType, /*TInfo=*/nullptr,
- SC_None, nullptr);
+ ParmVarDecl *FromParam =
+ ParmVarDecl::Create(Context, CopyConstructor, ClassLoc, ClassLoc,
+ /*IdentifierInfo=*/nullptr, ArgType,
+ /*TInfo=*/TSI, SC_None, nullptr);
CopyConstructor->setParams(FromParam);
CopyConstructor->setTrivial(
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index f9d40a2ed4b7b..f18f77d3442a1 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2817,7 +2817,8 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
if (!Instantiation->isInvalidDecl()) {
// Perform any dependent diagnostics from the pattern.
- PerformDependentDiagnostics(Pattern, TemplateArgs);
+ if (Pattern->isDependentContext())
+ PerformDependentDiagnostics(Pattern, TemplateArgs);
// Instantiate any out-of-line class template partial
// specializations now.
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 0fa42c5494213..be4c519307898 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -9,6 +9,7 @@
//
//===----------------------------------------------------------------------===/
+#include "TreeTransform.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTMutationListener.h"
@@ -1825,9 +1826,16 @@ Decl *TemplateDeclInstantiator::VisitCXXRecordDecl(CXXRecordDecl *D) {
PrevDecl = cast<CXXRecordDecl>(Prev);
}
- CXXRecordDecl *Record = CXXRecordDecl::Create(
- SemaRef.Context, D->getTagKind(), Owner, D->getBeginLoc(),
- D->getLocation(), D->getIdentifier(), PrevDecl);
+ CXXRecordDecl *Record = nullptr;
+ if (D->isLambda())
+ Record = CXXRecordDecl::CreateLambda(
+ SemaRef.Context, Owner, D->getLambdaTypeInfo(), D->getLocation(),
+ D->isDependentLambda(), D->isGenericLambda(),
+ D->getLambdaCaptureDefault());
+ else
+ Record = CXXRecordDecl::Create(SemaRef.Context, D->getTagKind(), Owner,
+ D->getBeginLoc(), D->getLocation(),
+ D->getIdentifier(), PrevDecl);
// Substitute the nested name specifier, if any.
if (SubstQualifier(D, Record))
@@ -2306,6 +2314,20 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
if (InstantiatedExplicitSpecifier.isInvalid())
return nullptr;
+ // Implicit destructors/constructors created for local classes in
+ // DeclareImplicit* (see SemaDeclCXX.cpp) might not have an associated TSI.
+ // Unfortunately there isn't enough context in those functions to
+ // conditionally populate the TSI without breaking non-template related use
+ // cases. Populate TSIs prior to calling SubstFunctionType to make sure we get
+ // a proper transformation.
+ if (cast<CXXRecordDecl>(D->getParent())->isLambda() &&
+ !D->getTypeSourceInfo() &&
+ isa<CXXConstructorDecl, CXXDestructorDecl>(D)) {
+ TypeSourceInfo *TSI =
+ SemaRef.Context.getTrivialTypeSourceInfo(D->getType());
+ D->setTypeSourceInfo(TSI);
+ }
+
SmallVector<ParmVarDecl *, 4> Params;
TypeSourceInfo *TInfo = SubstFunctionType(D, Params);
if (!TInfo)
@@ -2395,6 +2417,9 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
Destructor->isInlineSpecified(), false, Destructor->getConstexprKind(),
TrailingRequiresClause);
Method->setRangeEnd(Destructor->getEndLoc());
+ Method->setDeclName(SemaRef.Context.DeclarationNames.getCXXDestructorName(
+ SemaRef.Context.getCanonicalType(
+ SemaRef.Context.getTypeDeclType(Record))));
} else if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(D)) {
Method = CXXConversionDecl::Create(
SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo,
@@ -4919,10 +4944,76 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
Rec->isLocalClass() && !Function->isFunctionTemplateSpecialization();
LocalInstantiationScope Scope(*this, MergeWithParentScope);
+ auto RebuildTypeSourceInfoForDefaultSpecialMembers = [&]() {
+ // Special members might get their TypeSourceInfo set up w.r.t the
+ // PatternDecl context, in which case parameters could still be pointing
+ // back to the original class, make sure arguments are bound to the
+ // instantiated record instead.
+ assert(PatternDecl->isDefaulted() &&
+ "Special member needs to be defaulted");
+ auto PatternSM = getDefaultedFunctionKind(PatternDecl).asSpecialMember();
+ if (!(PatternSM == Sema::CXXCopyConstructor ||
+ PatternSM == Sema::CXXCopyAssignment ||
+ PatternSM == Sema::CXXMoveConstructor ||
+ PatternSM == Sema::CXXMoveAssignment))
+ return;
- if (PatternDecl->isDefaulted())
+ auto *NewRec = dyn_cast<CXXRecordDecl>(Function->getDeclContext());
+ const auto *PatternRec =
+ dyn_cast<CXXRecordDecl>(PatternDecl->getDeclContext());
+ if (!NewRec || !PatternRec)
+ return;
+ if (!PatternRec->isLambda())
+ return;
+
+ struct SpecialMemberTypeInfoRebuilder
+ : TreeTransform<SpecialMemberTypeInfoRebuilder> {
+ using Base = TreeTransform<SpecialMemberTypeInfoRebuilder>;
+ const CXXRecordDecl *OldDecl;
+ CXXRecordDecl *NewDecl;
+
+ SpecialMemberTypeInfoRebuilder(Sema &SemaRef, const CXXRecordDecl *O,
+ CXXRecordDecl *N)
+ : TreeTransform(SemaRef), OldDecl(O), NewDecl(N) {}
+
+ bool TransformExceptionSpec(SourceLocation Loc,
+ FunctionProtoType::ExceptionSpecInfo &ESI,
+ SmallVectorImpl<QualType> &Exceptions,
+ bool &Changed) {
+ return false;
+ }
+
+ QualType TransformRecordType(TypeLocBuilder &TLB, RecordTypeLoc TL) {
+ const RecordType *T = TL.getTypePtr();
+ RecordDecl *Record = cast_or_null<RecordDecl>(
+ getDerived().TransformDecl(TL.getNameLoc(), T->getDecl()));
+ if (Record != OldDecl)
+ return Base::TransformRecordType(TLB, TL);
+
+ QualType Result = getDerived().RebuildRecordType(NewDecl);
+ if (Result.isNull())
+ return QualType();
+
+ RecordTypeLoc NewTL = TLB.push<RecordTypeLoc>(Result);
+ NewTL.setNameLoc(TL.getNameLoc());
+ return Result;
+ }
+ } IR{*this, PatternRec, NewRec};
+
+ TypeSourceInfo *NewSI = IR.TransformType(Function->getTypeSourceInfo());
+ Function->setType(NewSI->getType());
+ Function->setTypeSourceInfo(NewSI);
+
+ ParmVarDecl *Parm = Function->getParamDecl(0);
+ TypeSourceInfo *NewParmSI = IR.TransformType(Parm->getTypeSourceInfo());
+ Parm->setType(NewParmSI->getType());
+ Parm->setTypeSourceInfo(NewParmSI);
+ };
+
+ if (PatternDecl->isDefaulted()) {
+ RebuildTypeSourceInfoForDefaultSpecialMembers();
SetDeclDefaulted(Function, PatternDecl->getLocation());
- else {
+ } else {
MultiLevelTemplateArgumentList TemplateArgs =
getTemplateInstantiationArgs(Function, nullptr, false, PatternDecl);
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index b4648efa229f8..7b9f6a85260f1 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -5828,8 +5828,8 @@ TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB,
return getDerived().TransformFunctionProtoType(
TLB, TL, nullptr, Qualifiers(),
[&](FunctionProtoType::ExceptionSpecInfo &ESI, bool &Changed) {
- return This->TransformExceptionSpec(TL.getBeginLoc(), ESI,
- ExceptionStorage, Changed);
+ return This->getDerived().TransformExceptionSpec(
+ TL.getBeginLoc(), ESI, ExceptionStorage, Changed);
});
}
diff --git a/clang/test/SemaCXX/lambdas-implicit-explicit-template.cpp b/clang/test/SemaCXX/lambdas-implicit-explicit-template.cpp
new file mode 100644
index 0000000000000..13fe12abe9e9d
--- /dev/null
+++ b/clang/test/SemaCXX/lambdas-implicit-explicit-template.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -std=c++20 -DEXPLICIT -verify %s
+// RUN: %clang_cc1 -std=c++17 -DEXPLICIT -verify -Wno-c++20-extensions %s
+// RUN: %clang_cc1 -std=c++14 -verify %s
+
+// expected-no-diagnostics
+
+#ifdef EXPLICIT
+
+template <typename F>
+void a(F &&f) {
+ f.template operator()<0>();
+}
+
+template <typename F>
+void b(F &&f) {
+ a([=]<int i>() {
+ f.template operator()<i>();
+ });
+}
+
+void c() {
+ b([&]<int i>() {
+ });
+}
+
+#endif
+
+template <typename F> void a1(F f) { f.operator()(0); }
+
+template <typename F> void b1(F f) {
+ a1([=](auto i) { f.operator()(i); });
+}
+
+void c1() {
+ b1([&](auto i) {});
+}
+
+void c2() {
+ const auto lambda = [&](auto arg1) {};
+ [&](auto arg2) { lambda.operator()(arg2); }(0);
+}
More information about the cfe-commits
mailing list