[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:
parent
5c8659801a
commit
c9aaf34b8d
|
@ -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(
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue