diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -13202,6 +13202,16 @@ 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(SpecialMem->getParent())->isLambda()) { + TypeSourceInfo *TSI = + Context.getTrivialTypeSourceInfo(SpecialMem->getType()); + SpecialMem->setTypeSourceInfo(TSI); + } } CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( @@ -14880,12 +14890,18 @@ 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 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -2817,7 +2817,8 @@ 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 --- 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 @@ PrevDecl = cast(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 @@ 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(D->getParent())->isLambda() && + !D->getTypeSourceInfo() && + isa(D)) { + TypeSourceInfo *TSI = + SemaRef.Context.getTrivialTypeSourceInfo(D->getType()); + D->setTypeSourceInfo(TSI); + } + SmallVector Params; TypeSourceInfo *TInfo = SubstFunctionType(D, Params); if (!TInfo) @@ -2395,6 +2417,9 @@ 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(D)) { Method = CXXConversionDecl::Create( SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, @@ -4919,10 +4944,76 @@ 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(Function->getDeclContext()); + const auto *PatternRec = + dyn_cast(PatternDecl->getDeclContext()); + if (!NewRec || !PatternRec) + return; + if (!PatternRec->isLambda()) + return; + + struct SpecialMemberTypeInfoRebuilder + : TreeTransform { + using Base = TreeTransform; + 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 &Exceptions, + bool &Changed) { + return false; + } + + QualType TransformRecordType(TypeLocBuilder &TLB, RecordTypeLoc TL) { + const RecordType *T = TL.getTypePtr(); + RecordDecl *Record = cast_or_null( + 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(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 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -5828,8 +5828,8 @@ 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 --- /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 +void a(F &&f) { + f.template operator()<0>(); +} + +template +void b(F &&f) { + a([=]() { + f.template operator()(); + }); +} + +void c() { + b([&]() { + }); +} + +#endif + +template void a1(F f) { f.operator()(0); } + +template 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); +}