[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
This commit is contained in:
Bruno Cardoso Lopes 2021-06-22 15:14:26 -07:00
parent 5c8659801a
commit c9aaf34b8d
5 changed files with 162 additions and 13 deletions

View File

@ -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(

View File

@ -2817,6 +2817,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
if (!Instantiation->isInvalidDecl()) {
// Perform any dependent diagnostics from the pattern.
if (Pattern->isDependentContext())
PerformDependentDiagnostics(Pattern, TemplateArgs);
// Instantiate any out-of-line class template partial

View File

@ -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);

View File

@ -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);
});
}

View File

@ -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);
}