https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/111173
>From 51f84ce80ddda9e12591f263a24a19238fc69cb8 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Mon, 23 Sep 2024 10:51:21 -0400 Subject: [PATCH 1/2] Reapply "[Clang][Sema] Refactor collection of multi-level template argument lists (#106585)" --- clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/AST/DeclTemplate.h | 66 +- clang/include/clang/Sema/Sema.h | 25 +- clang/lib/AST/DeclTemplate.cpp | 30 +- clang/lib/Sema/SemaConcept.cpp | 29 +- clang/lib/Sema/SemaDecl.cpp | 31 +- clang/lib/Sema/SemaDeclCXX.cpp | 4 +- clang/lib/Sema/SemaTemplate.cpp | 179 +++-- clang/lib/Sema/SemaTemplateDeduction.cpp | 33 +- clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 45 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 712 +++++++++--------- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 46 +- clang/lib/Serialization/ASTReader.cpp | 3 +- clang/lib/Serialization/ASTReaderDecl.cpp | 18 +- clang/lib/Serialization/ASTWriterDecl.cpp | 17 +- .../temp/temp.constr/temp.constr.decl/p4.cpp | 175 +++++ 16 files changed, 762 insertions(+), 654 deletions(-) create mode 100644 clang/test/CXX/temp/temp.constr/temp.constr.decl/p4.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 44d5f348ed2d54..fdb1a5942e4157 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -473,6 +473,9 @@ Bug Fixes to C++ Support containing outer unexpanded parameters were not correctly expanded. (#GH101754) - Fixed a bug in constraint expression comparison where the ``sizeof...`` expression was not handled properly in certain friend declarations. (#GH93099) +- Clang now uses the correct set of template argument lists when comparing the constraints of + out-of-line definitions and member templates explicitly specialized for a given implicit instantiation of + a class template. (#GH102320) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 687715a22e9fd3..05739f39d2a496 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -781,15 +781,11 @@ class RedeclarableTemplateDecl : public TemplateDecl, EntryType *Entry, void *InsertPos); struct CommonBase { - CommonBase() : InstantiatedFromMember(nullptr, false) {} + CommonBase() {} /// The template from which this was most /// directly instantiated (or null). - /// - /// The boolean value indicates whether this template - /// was explicitly specialized. - llvm::PointerIntPair<RedeclarableTemplateDecl*, 1, bool> - InstantiatedFromMember; + RedeclarableTemplateDecl *InstantiatedFromMember = nullptr; /// If non-null, points to an array of specializations (including /// partial specializations) known only by their external declaration IDs. @@ -809,14 +805,19 @@ class RedeclarableTemplateDecl : public TemplateDecl, }; /// Pointer to the common data shared by all declarations of this - /// template. - mutable CommonBase *Common = nullptr; + /// template, and a flag indicating if the template is a member + /// specialization. + mutable llvm::PointerIntPair<CommonBase *, 1, bool> Common; + + CommonBase *getCommonPtrInternal() const { return Common.getPointer(); } /// Retrieves the "common" pointer shared by all (re-)declarations of /// the same template. Calling this routine may implicitly allocate memory /// for the common pointer. CommonBase *getCommonPtr() const; + void setCommonPtr(CommonBase *C) const { Common.setPointer(C); } + virtual CommonBase *newCommon(ASTContext &C) const = 0; // Construct a template decl with name, parameters, and templated element. @@ -857,15 +858,12 @@ class RedeclarableTemplateDecl : public TemplateDecl, /// template<> template<typename T> /// struct X<int>::Inner { /* ... */ }; /// \endcode - bool isMemberSpecialization() const { - return getCommonPtr()->InstantiatedFromMember.getInt(); - } + bool isMemberSpecialization() const { return Common.getInt(); } /// Note that this member template is a specialization. void setMemberSpecialization() { - assert(getCommonPtr()->InstantiatedFromMember.getPointer() && - "Only member templates can be member template specializations"); - getCommonPtr()->InstantiatedFromMember.setInt(true); + assert(!isMemberSpecialization() && "already a member specialization"); + Common.setInt(true); } /// Retrieve the member template from which this template was @@ -905,12 +903,12 @@ class RedeclarableTemplateDecl : public TemplateDecl, /// void X<T>::f(T, U); /// \endcode RedeclarableTemplateDecl *getInstantiatedFromMemberTemplate() const { - return getCommonPtr()->InstantiatedFromMember.getPointer(); + return getCommonPtr()->InstantiatedFromMember; } void setInstantiatedFromMemberTemplate(RedeclarableTemplateDecl *TD) { - assert(!getCommonPtr()->InstantiatedFromMember.getPointer()); - getCommonPtr()->InstantiatedFromMember.setPointer(TD); + assert(!getCommonPtr()->InstantiatedFromMember); + getCommonPtr()->InstantiatedFromMember = TD; } /// Retrieve the "injected" template arguments that correspond to the @@ -1989,6 +1987,8 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl, /// template arguments have been deduced. void setInstantiationOf(ClassTemplatePartialSpecializationDecl *PartialSpec, const TemplateArgumentList *TemplateArgs) { + assert(!isa<ClassTemplatePartialSpecializationDecl>(this) && + "A partial specialization cannot be instantiated from a template"); assert(!SpecializedTemplate.is<SpecializedPartialSpecialization*>() && "Already set to a class template partial specialization!"); auto *PS = new (getASTContext()) SpecializedPartialSpecialization(); @@ -2000,6 +2000,8 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl, /// Note that this class template specialization is an instantiation /// of the given class template. void setInstantiationOf(ClassTemplateDecl *TemplDecl) { + assert(!isa<ClassTemplatePartialSpecializationDecl>(this) && + "A partial specialization cannot be instantiated from a template"); assert(!SpecializedTemplate.is<SpecializedPartialSpecialization*>() && "Previously set to a class template partial specialization!"); SpecializedTemplate = TemplDecl; @@ -2187,18 +2189,11 @@ class ClassTemplatePartialSpecializationDecl /// struct X<int>::Inner<T*> { /* ... */ }; /// \endcode bool isMemberSpecialization() const { - const auto *First = - cast<ClassTemplatePartialSpecializationDecl>(getFirstDecl()); - return First->InstantiatedFromMember.getInt(); + return InstantiatedFromMember.getInt(); } /// Note that this member template is a specialization. - void setMemberSpecialization() { - auto *First = cast<ClassTemplatePartialSpecializationDecl>(getFirstDecl()); - assert(First->InstantiatedFromMember.getPointer() && - "Only member templates can be member template specializations"); - return First->InstantiatedFromMember.setInt(true); - } + void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); } /// Retrieves the injected specialization type for this partial /// specialization. This is not the same as the type-decl-type for @@ -2268,10 +2263,6 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl { return static_cast<Common *>(RedeclarableTemplateDecl::getCommonPtr()); } - void setCommonPtr(Common *C) { - RedeclarableTemplateDecl::Common = C; - } - public: friend class ASTDeclReader; @@ -2754,6 +2745,8 @@ class VarTemplateSpecializationDecl : public VarDecl, /// template arguments have been deduced. void setInstantiationOf(VarTemplatePartialSpecializationDecl *PartialSpec, const TemplateArgumentList *TemplateArgs) { + assert(!isa<VarTemplatePartialSpecializationDecl>(this) && + "A partial specialization cannot be instantiated from a template"); assert(!SpecializedTemplate.is<SpecializedPartialSpecialization *>() && "Already set to a variable template partial specialization!"); auto *PS = new (getASTContext()) SpecializedPartialSpecialization(); @@ -2765,6 +2758,8 @@ class VarTemplateSpecializationDecl : public VarDecl, /// Note that this variable template specialization is an instantiation /// of the given variable template. void setInstantiationOf(VarTemplateDecl *TemplDecl) { + assert(!isa<VarTemplatePartialSpecializationDecl>(this) && + "A partial specialization cannot be instantiated from a template"); assert(!SpecializedTemplate.is<SpecializedPartialSpecialization *>() && "Previously set to a variable template partial specialization!"); SpecializedTemplate = TemplDecl; @@ -2949,18 +2944,11 @@ class VarTemplatePartialSpecializationDecl /// U* X<int>::Inner<T*> = (T*)(0) + 1; /// \endcode bool isMemberSpecialization() const { - const auto *First = - cast<VarTemplatePartialSpecializationDecl>(getFirstDecl()); - return First->InstantiatedFromMember.getInt(); + return InstantiatedFromMember.getInt(); } /// Note that this member template is a specialization. - void setMemberSpecialization() { - auto *First = cast<VarTemplatePartialSpecializationDecl>(getFirstDecl()); - assert(First->InstantiatedFromMember.getPointer() && - "Only member templates can be member template specializations"); - return First->InstantiatedFromMember.setInt(true); - } + void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); } SourceRange getSourceRange() const override LLVM_READONLY; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index bede971ce0191b..7ff9c2754a6fe0 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -11325,9 +11325,9 @@ class Sema final : public SemaBase { CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc, const ParsedAttributesView &Attr, TemplateParameterList *TemplateParams, AccessSpecifier AS, SourceLocation ModulePrivateLoc, - SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists, - TemplateParameterList **OuterTemplateParamLists, - SkipBodyInfo *SkipBody = nullptr); + SourceLocation FriendLoc, + ArrayRef<TemplateParameterList *> OuterTemplateParamLists, + bool IsMemberSpecialization, SkipBodyInfo *SkipBody = nullptr); /// Translates template arguments as provided by the parser /// into template arguments used by semantic analysis. @@ -11366,7 +11366,8 @@ class Sema final : public SemaBase { DeclResult ActOnVarTemplateSpecialization( Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous, SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams, - StorageClass SC, bool IsPartialSpecialization); + StorageClass SC, bool IsPartialSpecialization, + bool IsMemberSpecialization); /// Get the specialization of the given variable template corresponding to /// the specified argument list, or a null-but-valid result if the arguments @@ -13017,28 +13018,14 @@ class Sema final : public SemaBase { /// dealing with a specialization. This is only relevant for function /// template specializations. /// - /// \param Pattern If non-NULL, indicates the pattern from which we will be - /// instantiating the definition of the given declaration, \p ND. This is - /// used to determine the proper set of template instantiation arguments for - /// friend function template specializations. - /// /// \param ForConstraintInstantiation when collecting arguments, /// ForConstraintInstantiation indicates we should continue looking when /// encountering a lambda generic call operator, and continue looking for /// arguments on an enclosing class template. - /// - /// \param SkipForSpecialization when specified, any template specializations - /// in a traversal would be ignored. - /// \param ForDefaultArgumentSubstitution indicates we should continue looking - /// when encountering a specialized member function template, rather than - /// returning immediately. MultiLevelTemplateArgumentList getTemplateInstantiationArgs( const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false, std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt, - bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr, - bool ForConstraintInstantiation = false, - bool SkipForSpecialization = false, - bool ForDefaultArgumentSubstitution = false); + bool RelativeToPrimary = false, bool ForConstraintInstantiation = false); /// RAII object to handle the state changes required to synthesize /// a function body. diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 6fe817c5ef1c6b..d9b67b7bedf5a5 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -309,16 +309,16 @@ bool TemplateDecl::isTypeAlias() const { void RedeclarableTemplateDecl::anchor() {} RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() const { - if (Common) - return Common; + if (CommonBase *C = getCommonPtrInternal()) + return C; // Walk the previous-declaration chain until we either find a declaration // with a common pointer or we run out of previous declarations. SmallVector<const RedeclarableTemplateDecl *, 2> PrevDecls; for (const RedeclarableTemplateDecl *Prev = getPreviousDecl(); Prev; Prev = Prev->getPreviousDecl()) { - if (Prev->Common) { - Common = Prev->Common; + if (CommonBase *C = Prev->getCommonPtrInternal()) { + setCommonPtr(C); break; } @@ -326,18 +326,18 @@ RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() c } // If we never found a common pointer, allocate one now. - if (!Common) { + if (!getCommonPtrInternal()) { // FIXME: If any of the declarations is from an AST file, we probably // need an update record to add the common data. - Common = newCommon(getASTContext()); + setCommonPtr(newCommon(getASTContext())); } // Update any previous declarations we saw with the common pointer. for (const RedeclarableTemplateDecl *Prev : PrevDecls) - Prev->Common = Common; + Prev->setCommonPtr(getCommonPtrInternal()); - return Common; + return getCommonPtrInternal(); } void RedeclarableTemplateDecl::loadLazySpecializationsImpl() const { @@ -463,19 +463,17 @@ void FunctionTemplateDecl::addSpecialization( } void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) { - using Base = RedeclarableTemplateDecl; - // If we haven't created a common pointer yet, then it can just be created // with the usual method. - if (!Base::Common) + if (!getCommonPtrInternal()) return; - Common *ThisCommon = static_cast<Common *>(Base::Common); + Common *ThisCommon = static_cast<Common *>(getCommonPtrInternal()); Common *PrevCommon = nullptr; SmallVector<FunctionTemplateDecl *, 8> PreviousDecls; for (; Prev; Prev = Prev->getPreviousDecl()) { - if (Prev->Base::Common) { - PrevCommon = static_cast<Common *>(Prev->Base::Common); + if (CommonBase *C = Prev->getCommonPtrInternal()) { + PrevCommon = static_cast<Common *>(C); break; } PreviousDecls.push_back(Prev); @@ -485,7 +483,7 @@ void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) { // use this common pointer. if (!PrevCommon) { for (auto *D : PreviousDecls) - D->Base::Common = ThisCommon; + D->setCommonPtr(ThisCommon); return; } @@ -493,7 +491,7 @@ void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) { assert(ThisCommon->Specializations.size() == 0 && "Can't merge incompatible declarations!"); - Base::Common = PrevCommon; + setCommonPtr(PrevCommon); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 998a148a7d24a1..e36ee062213716 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -585,8 +585,8 @@ static bool CheckConstraintSatisfaction( ArrayRef<TemplateArgument> TemplateArgs = TemplateArgsLists.getNumSubstitutedLevels() > 0 - ? TemplateArgsLists.getOutermost() - : ArrayRef<TemplateArgument> {}; + ? TemplateArgsLists.getInnermost() + : ArrayRef<TemplateArgument>{}; Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(), Sema::InstantiatingTemplate::ConstraintsCheck{}, const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange); @@ -834,7 +834,6 @@ Sema::SetupConstraintCheckingTemplateArgumentsAndScope( getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(), /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true); if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope)) return std::nullopt; @@ -910,15 +909,13 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, // Figure out the to-translation-unit depth for this function declaration for // the purpose of seeing if they differ by constraints. This isn't the same as // getTemplateDepth, because it includes already instantiated parents. -static unsigned -CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND, - bool SkipForSpecialization = false) { +static unsigned CalculateTemplateDepthForConstraints(Sema &S, + const NamedDecl *ND) { MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( ND, ND->getLexicalDeclContext(), /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, - /*ForConstraintInstantiation=*/true, SkipForSpecialization); + /*ForConstraintInstantiation=*/true); return MLTAL.getNumLevels(); } @@ -957,8 +954,7 @@ static const Expr *SubstituteConstraintExpressionWithoutSatisfaction( DeclInfo.getDecl(), DeclInfo.getLexicalDeclContext(), /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true, - /*SkipForSpecialization*/ false); + /*ForConstraintInstantiation=*/true); if (MLTAL.getNumSubstitutedLevels() == 0) return ConstrExpr; @@ -1068,16 +1064,16 @@ bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old, bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) { assert(FD->getFriendObjectKind() && "Must be a friend!"); + FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate(); // The logic for non-templates is handled in ASTContext::isSameEntity, so we // don't have to bother checking 'DependsOnEnclosingTemplate' for a // non-function-template. - assert(FD->getDescribedFunctionTemplate() && - "Non-function templates don't need to be checked"); + assert(FTD && "Non-function templates don't need to be checked"); SmallVector<const Expr *, 3> ACs; - FD->getDescribedFunctionTemplate()->getAssociatedConstraints(ACs); + FTD->getAssociatedConstraints(ACs); - unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(*this, FD); + unsigned OldTemplateDepth = FTD->getTemplateParameters()->getDepth(); for (const Expr *Constraint : ACs) if (ConstraintExpressionDependsOnEnclosingTemplate(FD, OldTemplateDepth, Constraint)) @@ -1524,7 +1520,6 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N, CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(), /*Final=*/false, CSE->getTemplateArguments(), /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true); return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL, @@ -1805,8 +1800,8 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, return false; } - unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1, true); - unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2, true); + unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1); + unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2); for (size_t I = 0; I != AC1.size() && I != AC2.size(); ++I) { if (Depth2 > Depth1) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 2bf610746bc317..673b9d69228596 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -4510,10 +4510,10 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) { adjustDeclContextForDeclaratorDecl(New, Old); // Ensure the template parameters are compatible. - if (NewTemplate && - !TemplateParameterListsAreEqual(NewTemplate->getTemplateParameters(), - OldTemplate->getTemplateParameters(), - /*Complain=*/true, TPL_TemplateMatch)) + if (NewTemplate && !TemplateParameterListsAreEqual( + NewTemplate, NewTemplate->getTemplateParameters(), + OldTemplate, OldTemplate->getTemplateParameters(), + /*Complain=*/true, TPL_TemplateMatch)) return New->setInvalidDecl(); // C++ [class.mem]p1: @@ -7663,7 +7663,7 @@ NamedDecl *Sema::ActOnVariableDeclarator( : SourceLocation(); DeclResult Res = ActOnVarTemplateSpecialization( S, D, TInfo, Previous, TemplateKWLoc, TemplateParams, SC, - IsPartialSpecialization); + IsPartialSpecialization, IsMemberSpecialization); if (Res.isInvalid()) return nullptr; NewVD = cast<VarDecl>(Res.get()); @@ -7682,6 +7682,10 @@ NamedDecl *Sema::ActOnVariableDeclarator( VarTemplateDecl::Create(Context, DC, D.getIdentifierLoc(), Name, TemplateParams, NewVD); NewVD->setDescribedVarTemplate(NewTemplate); + // If we are providing an explicit specialization of a static variable + // template, make a note of that. + if (IsMemberSpecialization) + NewTemplate->setMemberSpecialization(); } // If this decl has an auto type in need of deduction, make a note of the @@ -8057,12 +8061,6 @@ NamedDecl *Sema::ActOnVariableDeclarator( ? TPC_ClassTemplateMember : TPC_VarTemplate)) NewVD->setInvalidDecl(); - - // If we are providing an explicit specialization of a static variable - // template, make a note of that. - if (PrevVarTemplate && - PrevVarTemplate->getInstantiatedFromMemberTemplate()) - PrevVarTemplate->setMemberSpecialization(); } } @@ -9869,6 +9867,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, NewFD); FunctionTemplate->setLexicalDeclContext(CurContext); NewFD->setDescribedFunctionTemplate(FunctionTemplate); + if (isMemberSpecialization) + FunctionTemplate->setMemberSpecialization(); // For source fidelity, store the other template param lists. if (TemplateParamLists.size() > 1) { @@ -12015,10 +12015,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, // If this is an explicit specialization of a member that is a function // template, mark it as a member specialization. - if (IsMemberSpecialization && - NewTemplateDecl->getInstantiatedFromMemberTemplate()) { - NewTemplateDecl->setMemberSpecialization(); - assert(OldTemplateDecl->isMemberSpecialization()); + if (IsMemberSpecialization) { // Explicit specializations of a member template do not inherit deleted // status from the parent member template that they are specializing. if (OldFD->isDeleted()) { @@ -17079,8 +17076,8 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, DeclResult Result = CheckClassTemplate( S, TagSpec, TUK, KWLoc, SS, Name, NameLoc, Attrs, TemplateParams, AS, ModulePrivateLoc, - /*FriendLoc*/ SourceLocation(), TemplateParameterLists.size() - 1, - TemplateParameterLists.data(), SkipBody); + /*FriendLoc*/ SourceLocation(), TemplateParameterLists.drop_back(), + isMemberSpecialization, SkipBody); return Result.get(); } else { // The "template<>" header is extraneous. diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 9cb2ed02a3f764..75d82c12e0c1f3 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17416,8 +17416,8 @@ DeclResult Sema::ActOnTemplatedFriendTag( return CheckClassTemplate(S, TagSpec, TagUseKind::Friend, TagLoc, SS, Name, NameLoc, Attr, TemplateParams, AS_public, /*ModulePrivateLoc=*/SourceLocation(), - FriendLoc, TempParamLists.size() - 1, - TempParamLists.data()) + FriendLoc, TempParamLists.drop_back(), + IsMemberSpecialization) .get(); } else { // The "template<>" header is extraneous. diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index eeaa1ebd7ba83a..b1a34edba9150b 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1795,8 +1795,9 @@ DeclResult Sema::CheckClassTemplate( CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc, const ParsedAttributesView &Attr, TemplateParameterList *TemplateParams, AccessSpecifier AS, SourceLocation ModulePrivateLoc, - SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists, - TemplateParameterList **OuterTemplateParamLists, SkipBodyInfo *SkipBody) { + SourceLocation FriendLoc, + ArrayRef<TemplateParameterList *> OuterTemplateParamLists, + bool IsMemberSpecialization, SkipBodyInfo *SkipBody) { assert(TemplateParams && TemplateParams->size() > 0 && "No template parameters"); assert(TUK != TagUseKind::Reference && @@ -1984,19 +1985,6 @@ DeclResult Sema::CheckClassTemplate( } if (PrevClassTemplate) { - // Ensure that the template parameter lists are compatible. Skip this check - // for a friend in a dependent context: the template parameter list itself - // could be dependent. - if (!(TUK == TagUseKind::Friend && CurContext->isDependentContext()) && - !TemplateParameterListsAreEqual( - TemplateCompareNewDeclInfo(SemanticContext ? SemanticContext - : CurContext, - CurContext, KWLoc), - TemplateParams, PrevClassTemplate, - PrevClassTemplate->getTemplateParameters(), /*Complain=*/true, - TPL_TemplateMatch)) - return true; - // C++ [temp.class]p4: // In a redeclaration, partial specialization, explicit // specialization or explicit instantiation of a class template, @@ -2011,30 +1999,6 @@ DeclResult Sema::CheckClassTemplate( Diag(PrevRecordDecl->getLocation(), diag::note_previous_use); Kind = PrevRecordDecl->getTagKind(); } - - // Check for redefinition of this class template. - if (TUK == TagUseKind::Definition) { - if (TagDecl *Def = PrevRecordDecl->getDefinition()) { - // If we have a prior definition that is not visible, treat this as - // simply making that previous definition visible. - NamedDecl *Hidden = nullptr; - if (SkipBody && !hasVisibleDefinition(Def, &Hidden)) { - SkipBody->ShouldSkip = true; - SkipBody->Previous = Def; - auto *Tmpl = cast<CXXRecordDecl>(Hidden)->getDescribedClassTemplate(); - assert(Tmpl && "original definition of a class template is not a " - "class template?"); - makeMergedDefinitionVisible(Hidden); - makeMergedDefinitionVisible(Tmpl); - } else { - Diag(NameLoc, diag::err_redefinition) << Name; - Diag(Def->getLocation(), diag::note_previous_definition); - // FIXME: Would it make sense to try to "forget" the previous - // definition, as part of error recovery? - return true; - } - } - } } else if (PrevDecl) { // C++ [temp]p5: // A class template shall not have the same name as any other @@ -2046,23 +2010,6 @@ DeclResult Sema::CheckClassTemplate( return true; } - // Check the template parameter list of this declaration, possibly - // merging in the template parameter list from the previous class - // template declaration. Skip this check for a friend in a dependent - // context, because the template parameter list might be dependent. - if (!(TUK == TagUseKind::Friend && CurContext->isDependentContext()) && - CheckTemplateParameterList( - TemplateParams, - PrevClassTemplate ? GetTemplateParameterList(PrevClassTemplate) - : nullptr, - (SS.isSet() && SemanticContext && SemanticContext->isRecord() && - SemanticContext->isDependentContext()) - ? TPC_ClassTemplateMember - : TUK == TagUseKind::Friend ? TPC_FriendClassTemplate - : TPC_ClassTemplate, - SkipBody)) - Invalid = true; - if (SS.isSet()) { // If the name of the template was qualified, we must be defining the // template out-of-line. @@ -2089,10 +2036,8 @@ DeclResult Sema::CheckClassTemplate( PrevClassTemplate->getTemplatedDecl() : nullptr, /*DelayTypeCreation=*/true); SetNestedNameSpecifier(*this, NewClass, SS); - if (NumOuterTemplateParamLists > 0) - NewClass->setTemplateParameterListsInfo( - Context, - llvm::ArrayRef(OuterTemplateParamLists, NumOuterTemplateParamLists)); + if (!OuterTemplateParamLists.empty()) + NewClass->setTemplateParameterListsInfo(Context, OuterTemplateParamLists); // Add alignment attributes if necessary; these attributes are checked when // the ASTContext lays out the structure. @@ -2105,7 +2050,10 @@ DeclResult Sema::CheckClassTemplate( = ClassTemplateDecl::Create(Context, SemanticContext, NameLoc, DeclarationName(Name), TemplateParams, NewClass); - + // If we are providing an explicit specialization of a member that is a + // class template, make a note of that. + if (IsMemberSpecialization) + NewTemplate->setMemberSpecialization(); if (ShouldAddRedecl) NewTemplate->setPreviousDecl(PrevClassTemplate); @@ -2120,12 +2068,6 @@ DeclResult Sema::CheckClassTemplate( assert(T->isDependentType() && "Class template type is not dependent?"); (void)T; - // If we are providing an explicit specialization of a member that is a - // class template, make a note of that. - if (PrevClassTemplate && - PrevClassTemplate->getInstantiatedFromMemberTemplate()) - PrevClassTemplate->setMemberSpecialization(); - // Set the access specifier. if (!Invalid && TUK != TagUseKind::Friend && NewTemplate->getDeclContext()->isRecord()) @@ -2135,8 +2077,62 @@ DeclResult Sema::CheckClassTemplate( NewClass->setLexicalDeclContext(CurContext); NewTemplate->setLexicalDeclContext(CurContext); - if (TUK == TagUseKind::Definition && (!SkipBody || !SkipBody->ShouldSkip)) - NewClass->startDefinition(); + // Ensure that the template parameter lists are compatible. Skip this check + // for a friend in a dependent context: the template parameter list itself + // could be dependent. + if (ShouldAddRedecl && PrevClassTemplate && + !TemplateParameterListsAreEqual( + NewTemplate, TemplateParams, PrevClassTemplate, + PrevClassTemplate->getTemplateParameters(), + /*Complain=*/true, TPL_TemplateMatch)) + return true; + + // Check the template parameter list of this declaration, possibly + // merging in the template parameter list from the previous class + // template declaration. Skip this check for a friend in a dependent + // context, because the template parameter list might be dependent. + if (ShouldAddRedecl && + CheckTemplateParameterList( + TemplateParams, + PrevClassTemplate ? PrevClassTemplate->getTemplateParameters() + : nullptr, + (SS.isSet() && SemanticContext && SemanticContext->isRecord() && + SemanticContext->isDependentContext()) + ? TPC_ClassTemplateMember + : TUK == TagUseKind::Friend ? TPC_FriendClassTemplate + : TPC_ClassTemplate, + SkipBody)) + Invalid = true; + + if (TUK == TagUseKind::Definition) { + if (PrevClassTemplate) { + // Check for redefinition of this class template. + if (TagDecl *Def = + PrevClassTemplate->getTemplatedDecl()->getDefinition()) { + // If we have a prior definition that is not visible, treat this as + // simply making that previous definition visible. + NamedDecl *Hidden = nullptr; + if (SkipBody && !hasVisibleDefinition(Def, &Hidden)) { + SkipBody->ShouldSkip = true; + SkipBody->Previous = Def; + auto *Tmpl = cast<CXXRecordDecl>(Hidden)->getDescribedClassTemplate(); + assert(Tmpl && "original definition of a class template is not a " + "class template?"); + makeMergedDefinitionVisible(Hidden); + makeMergedDefinitionVisible(Tmpl); + } else { + Diag(NameLoc, diag::err_redefinition) << Name; + Diag(Def->getLocation(), diag::note_previous_definition); + // FIXME: Would it make sense to try to "forget" the previous + // definition, as part of error recovery? + return true; + } + } + } + + if (!SkipBody || !SkipBody->ShouldSkip) + NewClass->startDefinition(); + } ProcessDeclAttributeList(S, NewClass, Attr); ProcessAPINotes(NewClass); @@ -4133,7 +4129,8 @@ void Sema::CheckDeductionGuideTemplate(FunctionTemplateDecl *TD) { DeclResult Sema::ActOnVarTemplateSpecialization( Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous, SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams, - StorageClass SC, bool IsPartialSpecialization) { + StorageClass SC, bool IsPartialSpecialization, + bool IsMemberSpecialization) { // D must be variable template id. assert(D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId && "Variable template specialization is declared with a template id."); @@ -4251,17 +4248,16 @@ DeclResult Sema::ActOnVarTemplateSpecialization( Context, VarTemplate->getDeclContext(), TemplateKWLoc, TemplateNameLoc, TemplateParams, VarTemplate, DI->getType(), DI, SC, CanonicalConverted); + // If we are providing an explicit specialization of a member variable + // template specialization, make a note of that. + if (IsMemberSpecialization) + Partial->setMemberSpecialization(); Partial->setTemplateArgsAsWritten(TemplateArgs); if (!PrevPartial) VarTemplate->AddPartialSpecialization(Partial, InsertPos); Specialization = Partial; - // If we are providing an explicit specialization of a member variable - // template specialization, make a note of that. - if (PrevPartial && PrevPartial->getInstantiatedFromMember()) - PrevPartial->setMemberSpecialization(); - CheckTemplatePartialSpecialization(Partial); } else { // Create a new class template specialization declaration node for @@ -5779,9 +5775,7 @@ bool Sema::CheckTemplateArgumentList( MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs( Template, NewContext, /*Final=*/false, CanonicalConverted, - /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, - /*ForConceptInstantiation=*/true); + /*RelativeToPrimary=*/true, /*ForConceptInstantiation=*/true); if (EnsureTemplateArgumentListConstraints( Template, MLTAL, SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) { @@ -8452,15 +8446,12 @@ DeclResult Sema::ActOnClassTemplateSpecialization( Diag(TemplateNameLoc, diag::err_partial_spec_args_match_primary_template) << /*class template*/ 0 << (TUK == TagUseKind::Definition) << FixItHint::CreateRemoval(SourceRange(LAngleLoc, RAngleLoc)); - return CheckClassTemplate(S, TagSpec, TUK, KWLoc, SS, - ClassTemplate->getIdentifier(), - TemplateNameLoc, - Attr, - TemplateParams, - AS_none, /*ModulePrivateLoc=*/SourceLocation(), - /*FriendLoc*/SourceLocation(), - TemplateParameterLists.size() - 1, - TemplateParameterLists.data()); + return CheckClassTemplate( + S, TagSpec, TUK, KWLoc, SS, ClassTemplate->getIdentifier(), + TemplateNameLoc, Attr, TemplateParams, AS_none, + /*ModulePrivateLoc=*/SourceLocation(), + /*FriendLoc*/ SourceLocation(), TemplateParameterLists.drop_back(), + isMemberSpecialization); } // Create a new class template partial specialization declaration node. @@ -8470,6 +8461,11 @@ DeclResult Sema::ActOnClassTemplateSpecialization( ClassTemplatePartialSpecializationDecl::Create( Context, Kind, DC, KWLoc, TemplateNameLoc, TemplateParams, ClassTemplate, CanonicalConverted, CanonType, PrevPartial); + + // If we are providing an explicit specialization of a member class + // template specialization, make a note of that. + if (isMemberSpecialization) + Partial->setMemberSpecialization(); Partial->setTemplateArgsAsWritten(TemplateArgs); SetNestedNameSpecifier(*this, Partial, SS); if (TemplateParameterLists.size() > 1 && SS.isSet()) { @@ -8481,11 +8477,6 @@ DeclResult Sema::ActOnClassTemplateSpecialization( ClassTemplate->AddPartialSpecialization(Partial, InsertPos); Specialization = Partial; - // If we are providing an explicit specialization of a member class - // template specialization, make a note of that. - if (PrevPartial && PrevPartial->getInstantiatedFromMember()) - PrevPartial->setMemberSpecialization(); - CheckTemplatePartialSpecialization(Partial); } else { // Create a new class template specialization declaration node for @@ -11284,8 +11275,8 @@ class ExplicitSpecializationVisibilityChecker { template<typename TemplDecl> void checkTemplate(TemplDecl *TD) { - if (TD->isMemberSpecialization()) { - if (!CheckMemberSpecialization(TD)) + if (TD->getMostRecentDecl()->isMemberSpecialization()) { + if (!CheckMemberSpecialization(TD->getMostRecentDecl())) diagnose(TD->getMostRecentDecl(), false); } } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index ce3317db5a8202..8e80ab730ac342 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3197,20 +3197,6 @@ template<> struct IsPartialSpecialization<VarTemplatePartialSpecializationDecl> { static constexpr bool value = true; }; -template <typename TemplateDeclT> -static bool DeducedArgsNeedReplacement(TemplateDeclT *Template) { - return false; -} -template <> -bool DeducedArgsNeedReplacement<VarTemplatePartialSpecializationDecl>( - VarTemplatePartialSpecializationDecl *Spec) { - return !Spec->isClassScopeExplicitSpecialization(); -} -template <> -bool DeducedArgsNeedReplacement<ClassTemplatePartialSpecializationDecl>( - ClassTemplatePartialSpecializationDecl *Spec) { - return !Spec->isClassScopeExplicitSpecialization(); -} template <typename TemplateDeclT> static TemplateDeductionResult @@ -3221,23 +3207,10 @@ CheckDeducedArgumentConstraints(Sema &S, TemplateDeclT *Template, llvm::SmallVector<const Expr *, 3> AssociatedConstraints; Template->getAssociatedConstraints(AssociatedConstraints); - std::optional<ArrayRef<TemplateArgument>> Innermost; - // If we don't need to replace the deduced template arguments, - // we can add them immediately as the inner-most argument list. - if (!DeducedArgsNeedReplacement(Template)) - Innermost = CanonicalDeducedArgs; - MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( - Template, Template->getDeclContext(), /*Final=*/false, Innermost, - /*RelativeToPrimary=*/true, /*Pattern=*/ - nullptr, /*ForConstraintInstantiation=*/true); - - // getTemplateInstantiationArgs picks up the non-deduced version of the - // template args when this is a variable template partial specialization and - // not class-scope explicit specialization, so replace with Deduced Args - // instead of adding to inner-most. - if (!Innermost) - MLTAL.replaceInnermostTemplateArguments(Template, CanonicalDeducedArgs); + Template, Template->getDeclContext(), /*Final=*/false, + /*Innermost=*/CanonicalDeducedArgs, /*RelativeToPrimary=*/true, + /*ForConstraintInstantiation=*/true); if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL, Info.getLocation(), diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index 545da21183c3c4..ca93c840f03215 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -765,7 +765,7 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, } // Template arguments used to transform the template arguments in // DeducedResults. - SmallVector<TemplateArgument> TemplateArgsForBuildingRC( + SmallVector<TemplateArgument> InnerArgsForBuildingRC( F->getTemplateParameters()->size()); // Transform the transformed template args MultiLevelTemplateArgumentList Args; @@ -778,33 +778,30 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, NamedDecl *TP = F->getTemplateParameters()->getParam(Index); MultiLevelTemplateArgumentList Args; Args.setKind(TemplateSubstitutionKind::Rewrite); - Args.addOuterTemplateArguments(TemplateArgsForBuildingRC); + Args.addOuterTemplateArguments(InnerArgsForBuildingRC); // Rebuild the template parameter with updated depth and index. NamedDecl *NewParam = transformTemplateParameter(SemaRef, F->getDeclContext(), TP, Args, /*NewIndex=*/FirstUndeducedParamIdx, getDepthAndIndex(TP).first + AdjustDepth); FirstUndeducedParamIdx += 1; - assert(TemplateArgsForBuildingRC[Index].isNull()); - TemplateArgsForBuildingRC[Index] = - Context.getInjectedTemplateArg(NewParam); + assert(InnerArgsForBuildingRC[Index].isNull()); + InnerArgsForBuildingRC[Index] = Context.getInjectedTemplateArg(NewParam); continue; } TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{}); TemplateArgumentLoc Output; if (!SemaRef.SubstTemplateArgument(Input, Args, Output)) { - assert(TemplateArgsForBuildingRC[Index].isNull() && + assert(InnerArgsForBuildingRC[Index].isNull() && "InstantiatedArgs must be null before setting"); - TemplateArgsForBuildingRC[Index] = Output.getArgument(); + InnerArgsForBuildingRC[Index] = Output.getArgument(); } } - // A list of template arguments for transforming the require-clause of F. - // It must contain the entire set of template argument lists. - MultiLevelTemplateArgumentList ArgsForBuildingRC; - ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite); - ArgsForBuildingRC.addOuterTemplateArguments(TemplateArgsForBuildingRC); + // A list of template arguments for transforming the require-clause using + // the transformed template arguments as the template argument list of F. + // // For 2), if the underlying deduction guide F is nested in a class template, // we need the entire template argument list, as the constraint AST in the // require-clause of F remains completely uninstantiated. @@ -827,25 +824,15 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, // - The occurrence of U in the function parameter is [depth:0, index:0] // - The template parameter of U is [depth:0, index:0] // - // We add the outer template arguments which is [int] to the multi-level arg - // list to ensure that the occurrence U in `C<U>` will be replaced with int - // during the substitution. - // // NOTE: The underlying deduction guide F is instantiated -- either from an // explicitly-written deduction guide member, or from a constructor. - // getInstantiatedFromMemberTemplate() can only handle the former case, so we - // check the DeclContext kind. - if (F->getLexicalDeclContext()->getDeclKind() == - clang::Decl::ClassTemplateSpecialization) { - auto OuterLevelArgs = SemaRef.getTemplateInstantiationArgs( - F, F->getLexicalDeclContext(), - /*Final=*/false, /*Innermost=*/std::nullopt, - /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, - /*ForConstraintInstantiation=*/true); - for (auto It : OuterLevelArgs) - ArgsForBuildingRC.addOuterTemplateArguments(It.Args); - } + MultiLevelTemplateArgumentList ArgsForBuildingRC = + SemaRef.getTemplateInstantiationArgs(F, F->getLexicalDeclContext(), + /*Final=*/false, + /*Innermost=*/InnerArgsForBuildingRC, + /*RelativeToPrimary=*/true, + /*ForConstraintInstantiation=*/true); + ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite); ExprResult E = SemaRef.SubstExpr(RC, ArgsForBuildingRC); if (E.isInvalid()) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 898255ff7c6a32..063c09f479a7c9 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -52,38 +52,6 @@ using namespace sema; //===----------------------------------------------------------------------===/ namespace { -namespace TemplateInstArgsHelpers { -struct Response { - const Decl *NextDecl = nullptr; - bool IsDone = false; - bool ClearRelativeToPrimary = true; - static Response Done() { - Response R; - R.IsDone = true; - return R; - } - static Response ChangeDecl(const Decl *ND) { - Response R; - R.NextDecl = ND; - return R; - } - static Response ChangeDecl(const DeclContext *Ctx) { - Response R; - R.NextDecl = Decl::castFromDeclContext(Ctx); - return R; - } - - static Response UseNextDecl(const Decl *CurDecl) { - return ChangeDecl(CurDecl->getDeclContext()); - } - - static Response DontClearRelativeToPrimaryNextDecl(const Decl *CurDecl) { - Response R = Response::UseNextDecl(CurDecl); - R.ClearRelativeToPrimary = false; - return R; - } -}; - // Retrieve the primary template for a lambda call operator. It's // unfortunate that we only have the mappings of call operators rather // than lambda classes. @@ -171,374 +139,396 @@ bool isLambdaEnclosedByTypeAliasDecl( .TraverseType(Underlying); } -// Add template arguments from a variable template instantiation. -Response -HandleVarTemplateSpec(const VarTemplateSpecializationDecl *VarTemplSpec, - MultiLevelTemplateArgumentList &Result, - bool SkipForSpecialization) { - // For a class-scope explicit specialization, there are no template arguments - // at this level, but there may be enclosing template arguments. - if (VarTemplSpec->isClassScopeExplicitSpecialization()) - return Response::DontClearRelativeToPrimaryNextDecl(VarTemplSpec); - - // We're done when we hit an explicit specialization. - if (VarTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization && - !isa<VarTemplatePartialSpecializationDecl>(VarTemplSpec)) - return Response::Done(); - - // If this variable template specialization was instantiated from a - // specialized member that is a variable template, we're done. - assert(VarTemplSpec->getSpecializedTemplate() && "No variable template?"); - llvm::PointerUnion<VarTemplateDecl *, VarTemplatePartialSpecializationDecl *> - Specialized = VarTemplSpec->getSpecializedTemplateOrPartial(); - if (VarTemplatePartialSpecializationDecl *Partial = - Specialized.dyn_cast<VarTemplatePartialSpecializationDecl *>()) { - if (!SkipForSpecialization) - Result.addOuterTemplateArguments( - Partial, VarTemplSpec->getTemplateInstantiationArgs().asArray(), - /*Final=*/false); - if (Partial->isMemberSpecialization()) - return Response::Done(); - } else { - VarTemplateDecl *Tmpl = Specialized.get<VarTemplateDecl *>(); - if (!SkipForSpecialization) - Result.addOuterTemplateArguments( - Tmpl, VarTemplSpec->getTemplateInstantiationArgs().asArray(), - /*Final=*/false); - if (Tmpl->isMemberSpecialization()) - return Response::Done(); +struct TemplateInstantiationArgumentCollecter + : DeclVisitor<TemplateInstantiationArgumentCollecter, Decl *> { + Sema &S; + MultiLevelTemplateArgumentList &Result; + std::optional<ArrayRef<TemplateArgument>> Innermost; + bool RelativeToPrimary; + bool ForConstraintInstantiation; + + TemplateInstantiationArgumentCollecter( + Sema &S, MultiLevelTemplateArgumentList &Result, + std::optional<ArrayRef<TemplateArgument>> Innermost, + bool RelativeToPrimary, bool ForConstraintInstantiation) + : S(S), Result(Result), Innermost(Innermost), + RelativeToPrimary(RelativeToPrimary), + ForConstraintInstantiation(ForConstraintInstantiation) {} + + Decl *Done() { return nullptr; } + + Decl *ChangeDecl(const Decl *D) { + RelativeToPrimary = false; + return const_cast<Decl *>(D); } - return Response::DontClearRelativeToPrimaryNextDecl(VarTemplSpec); -} -// If we have a template template parameter with translation unit context, -// then we're performing substitution into a default template argument of -// this template template parameter before we've constructed the template -// that will own this template template parameter. In this case, we -// use empty template parameter lists for all of the outer templates -// to avoid performing any substitutions. -Response -HandleDefaultTempArgIntoTempTempParam(const TemplateTemplateParmDecl *TTP, - MultiLevelTemplateArgumentList &Result) { - for (unsigned I = 0, N = TTP->getDepth() + 1; I != N; ++I) - Result.addOuterTemplateArguments(std::nullopt); - return Response::Done(); -} + Decl *ChangeDecl(const DeclContext *DC) { + return ChangeDecl(Decl::castFromDeclContext(DC)); + } -Response HandlePartialClassTemplateSpec( - const ClassTemplatePartialSpecializationDecl *PartialClassTemplSpec, - MultiLevelTemplateArgumentList &Result, bool SkipForSpecialization) { - if (!SkipForSpecialization) - Result.addOuterRetainedLevels(PartialClassTemplSpec->getTemplateDepth()); - return Response::Done(); -} + Decl *UseNextDecl(const Decl *D) { return ChangeDecl(D->getDeclContext()); } -// Add template arguments from a class template instantiation. -Response -HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec, - MultiLevelTemplateArgumentList &Result, - bool SkipForSpecialization) { - if (!ClassTemplSpec->isClassScopeExplicitSpecialization()) { - // We're done when we hit an explicit specialization. - if (ClassTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization && - !isa<ClassTemplatePartialSpecializationDecl>(ClassTemplSpec)) - return Response::Done(); + void AddInnermostTemplateArguments(const Decl *D) { + assert(Innermost); + Result.addOuterTemplateArguments(const_cast<Decl *>(D), *Innermost, + /*Final=*/false); + Innermost.reset(); + } - if (!SkipForSpecialization) - Result.addOuterTemplateArguments( - const_cast<ClassTemplateSpecializationDecl *>(ClassTemplSpec), - ClassTemplSpec->getTemplateInstantiationArgs().asArray(), - /*Final=*/false); + void AddOuterTemplateArguments(const Decl *D, ArrayRef<TemplateArgument> Args, + bool Final) { + Result.addOuterTemplateArguments(const_cast<Decl *>(D), Args, Final); + } - // If this class template specialization was instantiated from a - // specialized member that is a class template, we're done. - assert(ClassTemplSpec->getSpecializedTemplate() && "No class template?"); - if (ClassTemplSpec->getSpecializedTemplate()->isMemberSpecialization()) - return Response::Done(); - - // If this was instantiated from a partial template specialization, we need - // to get the next level of declaration context from the partial - // specialization, as the ClassTemplateSpecializationDecl's - // DeclContext/LexicalDeclContext will be for the primary template. - if (auto *InstFromPartialTempl = ClassTemplSpec->getSpecializedTemplateOrPartial() - .dyn_cast<ClassTemplatePartialSpecializationDecl *>()) - return Response::ChangeDecl(InstFromPartialTempl->getLexicalDeclContext()); + Decl *VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *TTPD) { + if (Innermost) + AddInnermostTemplateArguments(TTPD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(nullptr, std::nullopt, /*Final=*/false); + + for (unsigned Depth = TTPD->getDepth() + 1; Depth--;) + AddOuterTemplateArguments(nullptr, std::nullopt, /*Final=*/false); + + return Done(); } - return Response::UseNextDecl(ClassTemplSpec); -} -Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function, - MultiLevelTemplateArgumentList &Result, - const FunctionDecl *Pattern, bool RelativeToPrimary, - bool ForConstraintInstantiation, - bool ForDefaultArgumentSubstitution) { - // Add template arguments from a function template specialization. - if (!RelativeToPrimary && - Function->getTemplateSpecializationKindForInstantiation() == - TSK_ExplicitSpecialization) - return Response::Done(); - - if (!RelativeToPrimary && - Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) { - // This is an implicit instantiation of an explicit specialization. We - // don't get any template arguments from this function but might get - // some from an enclosing template. - return Response::UseNextDecl(Function); - } else if (const TemplateArgumentList *TemplateArgs = - Function->getTemplateSpecializationArgs()) { - // Add the template arguments for this specialization. - Result.addOuterTemplateArguments(const_cast<FunctionDecl *>(Function), - TemplateArgs->asArray(), - /*Final=*/false); + Decl *VisitFunctionTemplateDecl(FunctionTemplateDecl *FTD) { + assert( + (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && + "outer template not instantiated?"); + + if (Innermost) + AddInnermostTemplateArguments(FTD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(FTD, FTD->getInjectedTemplateArgs(), + /*Final=*/false); + + if (FTD->isMemberSpecialization()) + return Done(); + + if (FTD->getFriendObjectKind()) + return ChangeDecl(FTD->getLexicalDeclContext()); + return UseNextDecl(FTD); + } - if (RelativeToPrimary && - (Function->getTemplateSpecializationKind() == - TSK_ExplicitSpecialization || - (Function->getFriendObjectKind() && - !Function->getPrimaryTemplate()->getFriendObjectKind()))) - return Response::UseNextDecl(Function); - - // If this function was instantiated from a specialized member that is - // a function template, we're done. - assert(Function->getPrimaryTemplate() && "No function template?"); - if (!ForDefaultArgumentSubstitution && - Function->getPrimaryTemplate()->isMemberSpecialization()) - return Response::Done(); - - // If this function is a generic lambda specialization, we are done. - if (!ForConstraintInstantiation && - isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) - return Response::Done(); - - } else if (Function->getDescribedFunctionTemplate()) { + Decl *VisitVarTemplateDecl(VarTemplateDecl *VTD) { assert( (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && - "Outer template not instantiated?"); + "outer template not instantiated?"); + + if (Innermost) + AddInnermostTemplateArguments(VTD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(VTD, VTD->getInjectedTemplateArgs(), + /*Final=*/false); + + if (VTD->isMemberSpecialization()) + return Done(); + + return UseNextDecl(VTD); } - // If this is a friend or local declaration and it declares an entity at - // namespace scope, take arguments from its lexical parent - // instead of its semantic parent, unless of course the pattern we're - // instantiating actually comes from the file's context! - if ((Function->getFriendObjectKind() || Function->isLocalExternDecl()) && - Function->getNonTransparentDeclContext()->isFileContext() && - (!Pattern || !Pattern->getLexicalDeclContext()->isFileContext())) { - return Response::ChangeDecl(Function->getLexicalDeclContext()); + + Decl *VisitVarTemplatePartialSpecializationDecl( + VarTemplatePartialSpecializationDecl *VTPSD) { + assert( + (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && + "outer template not instantiated?"); + + if (Innermost) + AddInnermostTemplateArguments(VTPSD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(VTPSD, VTPSD->getTemplateArgs().asArray(), + /*Final=*/false); + + if (VTPSD->isMemberSpecialization()) + return Done(); + + return UseNextDecl(VTPSD); } - if (ForConstraintInstantiation && Function->getFriendObjectKind()) - return Response::ChangeDecl(Function->getLexicalDeclContext()); - return Response::UseNextDecl(Function); -} + Decl *VisitClassTemplateDecl(ClassTemplateDecl *CTD) { + assert( + (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && + "outer template not instantiated?"); -Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD, - MultiLevelTemplateArgumentList &Result) { - if (!isa<ClassTemplateSpecializationDecl>(FTD->getDeclContext())) { - Result.addOuterTemplateArguments( - const_cast<FunctionTemplateDecl *>(FTD), - const_cast<FunctionTemplateDecl *>(FTD)->getInjectedTemplateArgs(), - /*Final=*/false); - - NestedNameSpecifier *NNS = FTD->getTemplatedDecl()->getQualifier(); - - while (const Type *Ty = NNS ? NNS->getAsType() : nullptr) { - if (NNS->isInstantiationDependent()) { - if (const auto *TSTy = Ty->getAs<TemplateSpecializationType>()) { - ArrayRef<TemplateArgument> Arguments = TSTy->template_arguments(); - // Prefer template arguments from the injected-class-type if possible. - // For example, - // ```cpp - // template <class... Pack> struct S { - // template <class T> void foo(); - // }; - // template <class... Pack> template <class T> - // ^^^^^^^^^^^^^ InjectedTemplateArgs - // They're of kind TemplateArgument::Pack, not of - // TemplateArgument::Type. - // void S<Pack...>::foo() {} - // ^^^^^^^ - // TSTy->template_arguments() (which are of PackExpansionType) - // ``` - // This meets the contract in - // TreeTransform::TryExpandParameterPacks that the template arguments - // for unexpanded parameters should be of a Pack kind. - if (TSTy->isCurrentInstantiation()) { - auto *RD = TSTy->getCanonicalTypeInternal()->getAsCXXRecordDecl(); - if (ClassTemplateDecl *CTD = RD->getDescribedClassTemplate()) - Arguments = CTD->getInjectedTemplateArgs(); - else if (auto *Specialization = - dyn_cast<ClassTemplateSpecializationDecl>(RD)) - Arguments = - Specialization->getTemplateInstantiationArgs().asArray(); - } - Result.addOuterTemplateArguments( - TSTy->getTemplateName().getAsTemplateDecl(), Arguments, - /*Final=*/false); - } - } + if (Innermost) + AddInnermostTemplateArguments(CTD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(CTD, CTD->getInjectedTemplateArgs(), + /*Final=*/false); - NNS = NNS->getPrefix(); - } + if (CTD->isMemberSpecialization()) + return Done(); + + if (CTD->getFriendObjectKind()) + return ChangeDecl(CTD->getLexicalDeclContext()); + return UseNextDecl(CTD); } - return Response::ChangeDecl(FTD->getLexicalDeclContext()); -} + Decl *VisitClassTemplatePartialSpecializationDecl( + ClassTemplatePartialSpecializationDecl *CTPSD) { + assert( + (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && + "outer template not instantiated?"); + + if (Innermost) + AddInnermostTemplateArguments(CTPSD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(CTPSD, CTPSD->getTemplateArgs().asArray(), + /*Final=*/false); -Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec, - MultiLevelTemplateArgumentList &Result, - ASTContext &Context, - bool ForConstraintInstantiation) { - if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) { + if (CTPSD->isMemberSpecialization()) + return Done(); + + return UseNextDecl(CTPSD); + } + + Decl *VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *TATD) { assert( (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && - "Outer template not instantiated?"); - if (ClassTemplate->isMemberSpecialization()) - return Response::Done(); - if (ForConstraintInstantiation) - Result.addOuterTemplateArguments(const_cast<CXXRecordDecl *>(Rec), - ClassTemplate->getInjectedTemplateArgs(), - /*Final=*/false); + "outer template not instantiated?"); + if (Innermost) + AddInnermostTemplateArguments(TATD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(TATD, TATD->getInjectedTemplateArgs(), + /*Final=*/false); + + return UseNextDecl(TATD); } - if (const MemberSpecializationInfo *MSInfo = - Rec->getMemberSpecializationInfo()) - if (MSInfo->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) - return Response::Done(); - - bool IsFriend = Rec->getFriendObjectKind() || - (Rec->getDescribedClassTemplate() && - Rec->getDescribedClassTemplate()->getFriendObjectKind()); - if (ForConstraintInstantiation && IsFriend && - Rec->getNonTransparentDeclContext()->isFileContext()) { - return Response::ChangeDecl(Rec->getLexicalDeclContext()); + Decl *VisitConceptDecl(ConceptDecl *CD) { + assert( + (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && + "outer template not instantiated?"); + if (Innermost) + AddInnermostTemplateArguments(CD); + + return UseNextDecl(CD); } - // This is to make sure we pick up the VarTemplateSpecializationDecl or the - // TypeAliasTemplateDecl that this lambda is defined inside of. - if (Rec->isLambda()) { - if (const Decl *LCD = Rec->getLambdaContextDecl()) - return Response::ChangeDecl(LCD); - // Retrieve the template arguments for a using alias declaration. - // This is necessary for constraint checking, since we always keep - // constraints relative to the primary template. - if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef); - ForConstraintInstantiation && TypeAlias) { - if (isLambdaEnclosedByTypeAliasDecl(Rec->getLambdaCallOperator(), - TypeAlias.PrimaryTypeAliasDecl)) { - Result.addOuterTemplateArguments(TypeAlias.Template, - TypeAlias.AssociatedTemplateArguments, - /*Final=*/false); - // Visit the parent of the current type alias declaration rather than - // the lambda thereof. - // E.g., in the following example: - // struct S { - // template <class> using T = decltype([]<Concept> {} ()); - // }; - // void foo() { - // S::T var; - // } - // The instantiated lambda expression (which we're visiting at 'var') - // has a function DeclContext 'foo' rather than the Record DeclContext - // S. This seems to be an oversight to me that we may want to set a - // Sema Context from the CXXScopeSpec before substituting into T. - return Response::ChangeDecl(TypeAlias.Template->getDeclContext()); + Decl *VisitFunctionDecl(FunctionDecl *FD) { + assert(!FD->getDescribedFunctionTemplate() && + "not for templated declarations"); + + if (!RelativeToPrimary) { + // Add template arguments from a function template specialization. + if (const MemberSpecializationInfo *MSI = + FD->getMemberSpecializationInfo(); + MSI && + MSI->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return Done(); + + // This is an implicit instantiation of an explicit specialization. We + // don't get any template arguments from this function but might get + // some from an enclosing template. + if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return UseNextDecl(FD); + } + + if (const TemplateArgumentList *TemplateArgs = + FD->getTemplateSpecializationArgs()) { + // Add the template arguments for this specialization. + if (Innermost) + AddInnermostTemplateArguments(FD); + else + AddOuterTemplateArguments(FD, TemplateArgs->asArray(), /*Final=*/false); + + if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization || + (FD->getFriendObjectKind() && + !FD->getPrimaryTemplate()->getFriendObjectKind())) + return UseNextDecl(FD); + + // If this function was instantiated from a specialized member that is + // a function template, we're done. + assert(FD->getPrimaryTemplate() && "No function template?"); + if (FD->getPrimaryTemplate()->isMemberSpecialization()) + return Done(); + + // If this function is a generic lambda specialization, we are done. + if (!ForConstraintInstantiation && + isGenericLambdaCallOperatorOrStaticInvokerSpecialization(FD)) + return Done(); + } + + // If this is a friend or local declaration and it declares an entity at + // namespace scope, take arguments from its lexical parent + // instead of its semantic parent, unless of course the pattern we're + // instantiating actually comes from the file's context! + if ((FD->getFriendObjectKind() || FD->isLocalExternDecl()) && + FD->getNonTransparentDeclContext()->isFileContext()) { + return ChangeDecl(FD->getLexicalDeclContext()); + } + + if (ForConstraintInstantiation && FD->getFriendObjectKind()) + return ChangeDecl(FD->getLexicalDeclContext()); + return UseNextDecl(FD); + } + + Decl *VisitCXXRecordDecl(CXXRecordDecl *RD) { + assert(!RD->getDescribedClassTemplate() && + "not for templated declarations"); + + if (const MemberSpecializationInfo *MSI = RD->getMemberSpecializationInfo(); + MSI && + MSI->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return Done(); + + if (ForConstraintInstantiation && RD->getFriendObjectKind() && + RD->getNonTransparentDeclContext()->isFileContext()) { + return ChangeDecl(RD->getLexicalDeclContext()); + } + + // This is to make sure we pick up the VarTemplateSpecializationDecl or the + // TypeAliasTemplateDecl that this lambda is defined inside of. + if (RD->isLambda()) { + if (Decl *LCD = RD->getLambdaContextDecl()) + return ChangeDecl(LCD); + // Retrieve the template arguments for a using alias declaration. + // This is necessary for constraint checking, since we always keep + // constraints relative to the primary template. + if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(S); + ForConstraintInstantiation && TypeAlias) { + if (isLambdaEnclosedByTypeAliasDecl(RD->getLambdaCallOperator(), + TypeAlias.PrimaryTypeAliasDecl)) { + AddOuterTemplateArguments(TypeAlias.Template, + TypeAlias.AssociatedTemplateArguments, + /*Final=*/false); + // Visit the parent of the current type alias declaration rather than + // the lambda thereof. + // E.g., in the following example: + // struct S { + // template <class> using T = decltype([]<Concept> {} ()); + // }; + // void foo() { + // S::T var; + // } + // The instantiated lambda expression (which we're visiting at 'var') + // has a function DeclContext 'foo' rather than the Record DeclContext + // S. This seems to be an oversight to me that we may want to set a + // Sema Context from the CXXScopeSpec before substituting into T. + return ChangeDecl(TypeAlias.Template->getDeclContext()); + } } } + + return UseNextDecl(RD); } - return Response::UseNextDecl(Rec); -} + Decl * + VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *CTSD) { + // For a class-scope explicit specialization, there are no template + // arguments at this level, but there may be enclosing template arguments. + if (CTSD->isClassScopeExplicitSpecialization()) + return UseNextDecl(CTSD); -Response HandleImplicitConceptSpecializationDecl( - const ImplicitConceptSpecializationDecl *CSD, - MultiLevelTemplateArgumentList &Result) { - Result.addOuterTemplateArguments( - const_cast<ImplicitConceptSpecializationDecl *>(CSD), - CSD->getTemplateArguments(), - /*Final=*/false); - return Response::UseNextDecl(CSD); -} + // We're done when we hit an explicit specialization. + if (CTSD->getSpecializationKind() == TSK_ExplicitSpecialization) + return Done(); + + if (Innermost) + AddInnermostTemplateArguments(CTSD); + else + AddOuterTemplateArguments(CTSD, + CTSD->getTemplateInstantiationArgs().asArray(), + /*Final=*/false); + + // If this class template specialization was instantiated from a + // specialized member that is a class template, we're done. + assert(CTSD->getSpecializedTemplate() && "No class template?"); + llvm::PointerUnion<ClassTemplateDecl *, + ClassTemplatePartialSpecializationDecl *> + Specialized = CTSD->getSpecializedTemplateOrPartial(); + if (auto *CTPSD = + Specialized.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) { + if (CTPSD->isMemberSpecialization()) + return Done(); + } else { + auto *CTD = Specialized.get<ClassTemplateDecl *>(); + if (CTD->isMemberSpecialization()) + return Done(); + } + return UseNextDecl(CTSD); + } + + Decl * + VisitVarTemplateSpecializationDecl(VarTemplateSpecializationDecl *VTSD) { + // For a class-scope explicit specialization, there are no template + // arguments at this level, but there may be enclosing template arguments. + if (VTSD->isClassScopeExplicitSpecialization()) + return UseNextDecl(VTSD); + + // We're done when we hit an explicit specialization. + if (VTSD->getSpecializationKind() == TSK_ExplicitSpecialization) + return Done(); + + if (Innermost) + AddInnermostTemplateArguments(VTSD); + else + AddOuterTemplateArguments(VTSD, + VTSD->getTemplateInstantiationArgs().asArray(), + /*Final=*/false); + + // If this variable template specialization was instantiated from a + // specialized member that is a variable template, we're done. + assert(VTSD->getSpecializedTemplate() && "No variable template?"); + llvm::PointerUnion<VarTemplateDecl *, + VarTemplatePartialSpecializationDecl *> + Specialized = VTSD->getSpecializedTemplateOrPartial(); + if (auto *VTPSD = + Specialized.dyn_cast<VarTemplatePartialSpecializationDecl *>()) { + if (VTPSD->isMemberSpecialization()) + return Done(); + } else { + auto *VTD = Specialized.get<VarTemplateDecl *>(); + if (VTD->isMemberSpecialization()) + return Done(); + } + return UseNextDecl(VTSD); + } + + Decl *VisitImplicitConceptSpecializationDecl( + ImplicitConceptSpecializationDecl *ICSD) { + AddOuterTemplateArguments(ICSD, ICSD->getTemplateArguments(), + /*Final=*/false); + return UseNextDecl(ICSD); + } + + Decl *VisitDecl(Decl *D) { + if (D->isFileContextDecl()) + return Done(); + + if (isa<DeclContext>(D)) + RelativeToPrimary = false; + + return UseNextDecl(D); + } + + Decl *Visit(Decl *D) { + if (TemplateDecl *TD = D->getDescribedTemplate()) + D = TD; + return DeclVisitor::Visit(D); + } +}; -Response HandleGenericDeclContext(const Decl *CurDecl) { - return Response::UseNextDecl(CurDecl); -} -} // namespace TemplateInstArgsHelpers } // namespace MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( const NamedDecl *ND, const DeclContext *DC, bool Final, std::optional<ArrayRef<TemplateArgument>> Innermost, bool RelativeToPrimary, - const FunctionDecl *Pattern, bool ForConstraintInstantiation, - bool SkipForSpecialization, bool ForDefaultArgumentSubstitution) { + bool ForConstraintInstantiation) { assert((ND || DC) && "Can't find arguments for a decl if one isn't provided"); // Accumulate the set of template argument lists in this structure. MultiLevelTemplateArgumentList Result; - - using namespace TemplateInstArgsHelpers; const Decl *CurDecl = ND; if (!CurDecl) CurDecl = Decl::castFromDeclContext(DC); - if (Innermost) { - Result.addOuterTemplateArguments(const_cast<NamedDecl *>(ND), *Innermost, - Final); - // Populate placeholder template arguments for TemplateTemplateParmDecls. - // This is essential for the case e.g. - // - // template <class> concept Concept = false; - // template <template <Concept C> class T> void foo(T<int>) - // - // where parameter C has a depth of 1 but the substituting argument `int` - // has a depth of 0. - if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl)) - HandleDefaultTempArgIntoTempTempParam(TTP, Result); - CurDecl = Response::UseNextDecl(CurDecl).NextDecl; - } - - while (!CurDecl->isFileContextDecl()) { - Response R; - if (const auto *VarTemplSpec = - dyn_cast<VarTemplateSpecializationDecl>(CurDecl)) { - R = HandleVarTemplateSpec(VarTemplSpec, Result, SkipForSpecialization); - } else if (const auto *PartialClassTemplSpec = - dyn_cast<ClassTemplatePartialSpecializationDecl>(CurDecl)) { - R = HandlePartialClassTemplateSpec(PartialClassTemplSpec, Result, - SkipForSpecialization); - } else if (const auto *ClassTemplSpec = - dyn_cast<ClassTemplateSpecializationDecl>(CurDecl)) { - R = HandleClassTemplateSpec(ClassTemplSpec, Result, - SkipForSpecialization); - } else if (const auto *Function = dyn_cast<FunctionDecl>(CurDecl)) { - R = HandleFunction(*this, Function, Result, Pattern, RelativeToPrimary, - ForConstraintInstantiation, - ForDefaultArgumentSubstitution); - } else if (const auto *Rec = dyn_cast<CXXRecordDecl>(CurDecl)) { - R = HandleRecordDecl(*this, Rec, Result, Context, - ForConstraintInstantiation); - } else if (const auto *CSD = - dyn_cast<ImplicitConceptSpecializationDecl>(CurDecl)) { - R = HandleImplicitConceptSpecializationDecl(CSD, Result); - } else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(CurDecl)) { - R = HandleFunctionTemplateDecl(FTD, Result); - } else if (const auto *CTD = dyn_cast<ClassTemplateDecl>(CurDecl)) { - R = Response::ChangeDecl(CTD->getLexicalDeclContext()); - } else if (!isa<DeclContext>(CurDecl)) { - R = Response::DontClearRelativeToPrimaryNextDecl(CurDecl); - if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl)) { - R = HandleDefaultTempArgIntoTempTempParam(TTP, Result); - } - } else { - R = HandleGenericDeclContext(CurDecl); - } - - if (R.IsDone) - return Result; - if (R.ClearRelativeToPrimary) - RelativeToPrimary = false; - assert(R.NextDecl); - CurDecl = R.NextDecl; - } - + TemplateInstantiationArgumentCollecter Collecter( + *this, Result, Innermost, RelativeToPrimary, ForConstraintInstantiation); + do { + CurDecl = Collecter.Visit(const_cast<Decl *>(CurDecl)); + } while (CurDecl); return Result; } @@ -1687,10 +1677,8 @@ namespace { CXXRecordDecl::LambdaDependencyKind ComputeLambdaDependency(LambdaScopeInfo *LSI) { - if (auto TypeAlias = - TemplateInstArgsHelpers::getEnclosingTypeAliasTemplateDecl( - getSema()); - TypeAlias && TemplateInstArgsHelpers::isLambdaEnclosedByTypeAliasDecl( + if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(getSema()); + TypeAlias && isLambdaEnclosedByTypeAliasDecl( LSI->CallOperator, TypeAlias.PrimaryTypeAliasDecl)) { unsigned TypeAliasDeclDepth = TypeAlias.Template->getTemplateDepth(); if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels()) @@ -1729,8 +1717,8 @@ namespace { // RecoveryExpr that wraps the uninstantiated default argument so // that downstream diagnostics are omitted. ExprResult ErrorResult = SemaRef.CreateRecoveryExpr( - UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(), - { UninstExpr }, UninstExpr->getType()); + UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(), {UninstExpr}, + UninstExpr->getType()); if (ErrorResult.isUsable()) PVD->setDefaultArg(ErrorResult.get()); } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 1c35c7d288e325..d29434486dcb06 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -12,6 +12,7 @@ #include "TreeTransform.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/ASTLambda.h" #include "clang/AST/ASTMutationListener.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" @@ -4685,6 +4686,36 @@ bool Sema::InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD, ParmVarDecl *Param) { assert(Param->hasUninstantiatedDefaultArg()); + NamedDecl *Pattern = FD; + std::optional<ArrayRef<TemplateArgument>> Innermost; + + // C++ [dcl.fct.default]p4 + // For non-template functions, default arguments can be added in later + // declarations of a function that inhabit the same scope. + // + // C++ [dcl.fct.default]p6 + // Except for member functions of templated classes, the default arguments + // in a member function definition that appears outside of the class + // definition are added to the set of default arguments provided by the + // member function declaration in the class definition; the program is + // ill-formed if a default constructor, copy or move constructor, or copy + // or move assignment operator is so declared. Default arguments for a + // member function of a templated class shall be specified on the initial + // declaration of the member function within the templated class. + // + // We need to collect the template arguments from the context of the function + // where the default argument was defined. For a specialization of a function + // template explicitly specialized for an implicit instantiation of a class + // template, that context is the (implicitly instantiated) declaration in the + // definition of the class template specialization. + if (FD->isCXXClassMember() && + !isGenericLambdaCallOperatorOrStaticInvokerSpecialization(FD)) { + if (FunctionTemplateDecl *FTD = FD->getPrimaryTemplate()) { + Pattern = FTD->getFirstDecl(); + Innermost = FD->getTemplateSpecializationArgs()->asArray(); + } + } + // Instantiate the expression. // // FIXME: Pass in a correct Pattern argument, otherwise @@ -4702,12 +4733,10 @@ bool Sema::InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD, // // template<typename T> // A<T> Foo(int a = A<T>::FooImpl()); - MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs( - FD, FD->getLexicalDeclContext(), - /*Final=*/false, /*Innermost=*/std::nullopt, - /*RelativeToPrimary=*/true, /*Pattern=*/nullptr, - /*ForConstraintInstantiation=*/false, /*SkipForSpecialization=*/false, - /*ForDefaultArgumentSubstitution=*/true); + MultiLevelTemplateArgumentList TemplateArgs = + getTemplateInstantiationArgs(Pattern, Pattern->getLexicalDeclContext(), + /*Final=*/false, Innermost, + /*RelativeToPrimary=*/true); if (SubstDefaultArgument(CallLoc, Param, TemplateArgs, /*ForCallExpr*/ true)) return true; @@ -4748,7 +4777,7 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation, MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(Decl, Decl->getLexicalDeclContext(), /*Final=*/false, /*Innermost=*/std::nullopt, - /*RelativeToPrimary*/ true); + /*RelativeToPrimary=*/true); // FIXME: We can't use getTemplateInstantiationPattern(false) in general // here, because for a non-defining friend declaration in a class template, @@ -5186,8 +5215,7 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, SetDeclDefaulted(Function, PatternDecl->getLocation()); } else { MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs( - Function, Function->getLexicalDeclContext(), /*Final=*/false, - /*Innermost=*/std::nullopt, false, PatternDecl); + Function, Function->getLexicalDeclContext()); // Substitute into the qualifier; we can get a substitution failure here // through evil use of alias templates. diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 97b79bd1381c02..4a20dfc09cd061 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -10003,7 +10003,8 @@ void ASTReader::finishPendingActions() { auto RTD = cast<RedeclarableTemplateDecl>(D)->getCanonicalDecl(); for (auto *R = getMostRecentExistingDecl(RTD); R; R = R->getPreviousDecl()) - cast<RedeclarableTemplateDecl>(R)->Common = RTD->Common; + cast<RedeclarableTemplateDecl>(R)->setCommonPtr( + RTD->getCommonPtrInternal()); } PendingDefinitions.clear(); diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 7cead2728ca938..1ccc810f415eb4 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2416,11 +2416,13 @@ ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { // Make sure we've allocated the Common pointer first. We do this before // VisitTemplateDecl so that getCommonPtr() can be used during initialization. RedeclarableTemplateDecl *CanonD = D->getCanonicalDecl(); - if (!CanonD->Common) { - CanonD->Common = CanonD->newCommon(Reader.getContext()); + if (!CanonD->getCommonPtrInternal()) { + CanonD->setCommonPtr(CanonD->newCommon(Reader.getContext())); Reader.PendingDefinitions.insert(CanonD); } - D->Common = CanonD->Common; + D->setCommonPtr(CanonD->getCommonPtrInternal()); + if (Record.readInt()) + D->setMemberSpecialization(); // If this is the first declaration of the template, fill in the information // for the 'common' pointer. @@ -2429,8 +2431,6 @@ ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { assert(RTD->getKind() == D->getKind() && "InstantiatedFromMemberTemplate kind mismatch"); D->setInstantiatedFromMemberTemplate(RTD); - if (Record.readInt()) - D->setMemberSpecialization(); } } @@ -2562,12 +2562,12 @@ void ASTDeclReader::VisitClassTemplatePartialSpecializationDecl( D->TemplateParams = Params; RedeclarableResult Redecl = VisitClassTemplateSpecializationDeclImpl(D); + D->InstantiatedFromMember.setInt(Record.readInt()); // These are read/set from/to the first declaration. if (ThisDeclID == Redecl.getFirstID()) { D->InstantiatedFromMember.setPointer( - readDeclAs<ClassTemplatePartialSpecializationDecl>()); - D->InstantiatedFromMember.setInt(Record.readInt()); + readDeclAs<ClassTemplatePartialSpecializationDecl>()); } } @@ -2660,12 +2660,12 @@ void ASTDeclReader::VisitVarTemplatePartialSpecializationDecl( D->TemplateParams = Params; RedeclarableResult Redecl = VisitVarTemplateSpecializationDeclImpl(D); + D->InstantiatedFromMember.setInt(Record.readInt()); // These are read/set from/to the first declaration. if (ThisDeclID == Redecl.getFirstID()) { D->InstantiatedFromMember.setPointer( readDeclAs<VarTemplatePartialSpecializationDecl>()); - D->InstantiatedFromMember.setInt(Record.readInt()); } } @@ -2888,7 +2888,7 @@ void ASTDeclReader::mergeRedeclarableTemplate(RedeclarableTemplateDecl *D, // If we merged the template with a prior declaration chain, merge the // common pointer. // FIXME: Actually merge here, don't just overwrite. - D->Common = D->getCanonicalDecl()->Common; + D->setCommonPtr(D->getCanonicalDecl()->getCommonPtrInternal()); } /// "Cast" to type T, asserting if we don't have an implicit conversion. diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index b71684569609ac..f21cbd11b6ab89 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1713,14 +1713,13 @@ void ASTDeclWriter::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) { void ASTDeclWriter::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { VisitRedeclarable(D); + Record.push_back(D->isMemberSpecialization()); + // Emit data to initialize CommonOrPrev before VisitTemplateDecl so that // getCommonPtr() can be used while this is still initializing. - if (D->isFirstDecl()) { + if (D->isFirstDecl()) // This declaration owns the 'common' pointer, so serialize that data now. Record.AddDeclRef(D->getInstantiatedFromMemberTemplate()); - if (D->getInstantiatedFromMemberTemplate()) - Record.push_back(D->isMemberSpecialization()); - } VisitTemplateDecl(D); Record.push_back(D->getIdentifierNamespace()); @@ -1806,11 +1805,10 @@ void ASTDeclWriter::VisitClassTemplatePartialSpecializationDecl( VisitClassTemplateSpecializationDecl(D); + Record.push_back(D->isMemberSpecialization()); // These are read/set from/to the first declaration. - if (D->getPreviousDecl() == nullptr) { + if (D->isFirstDecl()) Record.AddDeclRef(D->getInstantiatedFromMember()); - Record.push_back(D->isMemberSpecialization()); - } Code = serialization::DECL_CLASS_TEMPLATE_PARTIAL_SPECIALIZATION; } @@ -1874,12 +1872,11 @@ void ASTDeclWriter::VisitVarTemplatePartialSpecializationDecl( Record.AddTemplateParameterList(D->getTemplateParameters()); VisitVarTemplateSpecializationDecl(D); + Record.push_back(D->isMemberSpecialization()); // These are read/set from/to the first declaration. - if (D->getPreviousDecl() == nullptr) { + if (D->isFirstDecl()) Record.AddDeclRef(D->getInstantiatedFromMember()); - Record.push_back(D->isMemberSpecialization()); - } Code = serialization::DECL_VAR_TEMPLATE_PARTIAL_SPECIALIZATION; } diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.decl/p4.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.decl/p4.cpp new file mode 100644 index 00000000000000..70064f867e18e3 --- /dev/null +++ b/clang/test/CXX/temp/temp.constr/temp.constr.decl/p4.cpp @@ -0,0 +1,175 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s +// expected-no-diagnostics + +template<typename T> +concept D = true; + +template<typename T> +struct A { + template<typename U, bool V> + void f() requires V; + + template<> + void f<short, true>(); + + template<D U> + void g(); + + template<typename U, bool V> requires V + struct B; + + template<typename U, bool V> requires V + struct B<U*, V>; + + template<> + struct B<short, true>; + + template<D U> + struct C; + + template<D U> + struct C<U*>; + + template<typename U, bool V> requires V + static int x; + + template<typename U, bool V> requires V + static int x<U*, V>; + + template<> + int x<short, true>; + + template<D U> + static int y; + + template<D U> + static int y<U*>; +}; + +template<typename T> +template<typename U, bool V> +void A<T>::f() requires V { } + +template<typename T> +template<D U> +void A<T>::g() { } + +template<typename T> +template<typename U, bool V> requires V +struct A<T>::B { }; + +template<typename T> +template<typename U, bool V> requires V +struct A<T>::B<U*, V> { }; + +template<typename T> +template<typename U, bool V> requires V +struct A<T>::B<U&, V> { }; + +template<typename T> +template<D U> +struct A<T>::C { }; + +template<typename T> +template<D U> +struct A<T>::C<U*> { }; + +template<typename T> +template<typename U, bool V> requires V +int A<T>::x = 0; + +template<typename T> +template<typename U, bool V> requires V +int A<T>::x<U*, V> = 0; + +template<typename T> +template<typename U, bool V> requires V +int A<T>::x<U&, V> = 0; + +template<typename T> +template<D U> +int A<T>::y = 0; + +template<typename T> +template<D U> +int A<T>::y<U*> = 0; + +template<> +template<typename U, bool V> +void A<short>::f() requires V; + +template<> +template<> +void A<short>::f<int, true>(); + +template<> +template<> +void A<void>::f<int, true>(); + +template<> +template<D U> +void A<short>::g(); + +template<> +template<typename U, bool V> requires V +struct A<int>::B; + +template<> +template<> +struct A<int>::B<int, true>; + +template<> +template<> +struct A<void>::B<int, true>; + +template<> +template<typename U, bool V> requires V +struct A<int>::B<U*, V>; + +template<> +template<typename U, bool V> requires V +struct A<int>::B<U&, V>; + +template<> +template<D U> +struct A<int>::C; + +template<> +template<D U> +struct A<int>::C<U*>; + +template<> +template<D U> +struct A<int>::C<U&>; + +template<> +template<typename U, bool V> requires V +int A<long>::x; + +template<> +template<> +int A<long>::x<int, true>; + +template<> +template<> +int A<void>::x<int, true>; + +template<> +template<typename U, bool V> requires V +int A<long>::x<U*, V>; + +template<> +template<typename U, bool V> requires V +int A<long>::x<U&, V>; + +template<> +template<D U> +int A<long>::y; + +template<> +template<D U> +int A<long>::y<U*>; + +template<> +template<D U> +int A<long>::y<U&>; >From dfa5179f07b5a85c1daafd93b9f1d4bed9b4e27b Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Fri, 4 Oct 2024 11:21:39 -0400 Subject: [PATCH 2/2] [FOLD] return most recent declaration of primary --- clang/include/clang/AST/DeclTemplate.h | 16 ++------------ clang/lib/AST/Decl.cpp | 2 +- clang/lib/AST/DeclTemplate.cpp | 29 ++++++++++++++++++++++---- clang/test/AST/ast-dump-decl.cpp | 2 +- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 05739f39d2a496..58ae7420471a6f 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -1955,13 +1955,7 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl, /// specialization which was specialized by this. llvm::PointerUnion<ClassTemplateDecl *, ClassTemplatePartialSpecializationDecl *> - getSpecializedTemplateOrPartial() const { - if (const auto *PartialSpec = - SpecializedTemplate.dyn_cast<SpecializedPartialSpecialization *>()) - return PartialSpec->PartialSpecialization; - - return SpecializedTemplate.get<ClassTemplateDecl*>(); - } + getSpecializedTemplateOrPartial() const; /// Retrieve the set of template arguments that should be used /// to instantiate members of the class template or class template partial @@ -2713,13 +2707,7 @@ class VarTemplateSpecializationDecl : public VarDecl, /// Retrieve the variable template or variable template partial /// specialization which was specialized by this. llvm::PointerUnion<VarTemplateDecl *, VarTemplatePartialSpecializationDecl *> - getSpecializedTemplateOrPartial() const { - if (const auto *PartialSpec = - SpecializedTemplate.dyn_cast<SpecializedPartialSpecialization *>()) - return PartialSpec->PartialSpecialization; - - return SpecializedTemplate.get<VarTemplateDecl *>(); - } + getSpecializedTemplateOrPartial() const; /// Retrieve the set of template arguments that should be used /// to instantiate the initializer of the variable template or variable diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 84ef9f74582ef6..1dbe6ceae97c3a 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4159,7 +4159,7 @@ FunctionTemplateDecl *FunctionDecl::getPrimaryTemplate() const { if (FunctionTemplateSpecializationInfo *Info = TemplateOrSpecialization .dyn_cast<FunctionTemplateSpecializationInfo*>()) { - return Info->getTemplate(); + return Info->getTemplate()->getMostRecentDecl(); } return nullptr; } diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index d9b67b7bedf5a5..2781863bfd9610 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -997,12 +997,23 @@ void ClassTemplateSpecializationDecl::getNameForDiagnostic( } } +llvm::PointerUnion<ClassTemplateDecl *, + ClassTemplatePartialSpecializationDecl *> +ClassTemplateSpecializationDecl::getSpecializedTemplateOrPartial() const { + if (const auto *PartialSpec = + SpecializedTemplate.dyn_cast<SpecializedPartialSpecialization *>()) + return PartialSpec->PartialSpecialization->getMostRecentDecl(); + + return SpecializedTemplate.get<ClassTemplateDecl *>()->getMostRecentDecl(); +} + ClassTemplateDecl * ClassTemplateSpecializationDecl::getSpecializedTemplate() const { if (const auto *PartialSpec = SpecializedTemplate.dyn_cast<SpecializedPartialSpecialization*>()) - return PartialSpec->PartialSpecialization->getSpecializedTemplate(); - return SpecializedTemplate.get<ClassTemplateDecl*>(); + return PartialSpec->PartialSpecialization->getSpecializedTemplate() + ->getMostRecentDecl(); + return SpecializedTemplate.get<ClassTemplateDecl *>()->getMostRecentDecl(); } SourceRange @@ -1410,11 +1421,21 @@ void VarTemplateSpecializationDecl::getNameForDiagnostic( } } +llvm::PointerUnion<VarTemplateDecl *, VarTemplatePartialSpecializationDecl *> +VarTemplateSpecializationDecl::getSpecializedTemplateOrPartial() const { + if (const auto *PartialSpec = + SpecializedTemplate.dyn_cast<SpecializedPartialSpecialization *>()) + return PartialSpec->PartialSpecialization->getMostRecentDecl(); + + return SpecializedTemplate.get<VarTemplateDecl *>()->getMostRecentDecl(); +} + VarTemplateDecl *VarTemplateSpecializationDecl::getSpecializedTemplate() const { if (const auto *PartialSpec = SpecializedTemplate.dyn_cast<SpecializedPartialSpecialization *>()) - return PartialSpec->PartialSpecialization->getSpecializedTemplate(); - return SpecializedTemplate.get<VarTemplateDecl *>(); + return PartialSpec->PartialSpecialization->getSpecializedTemplate() + ->getMostRecentDecl(); + return SpecializedTemplate.get<VarTemplateDecl *>()->getMostRecentDecl(); } SourceRange VarTemplateSpecializationDecl::getSourceRange() const { diff --git a/clang/test/AST/ast-dump-decl.cpp b/clang/test/AST/ast-dump-decl.cpp index e84241cee922f5..7b998f20944f49 100644 --- a/clang/test/AST/ast-dump-decl.cpp +++ b/clang/test/AST/ast-dump-decl.cpp @@ -530,7 +530,7 @@ namespace testCanonicalTemplate { // CHECK-NEXT: | `-ClassTemplateDecl 0x{{.+}} parent 0x{{.+}} <col:5, col:40> col:40 friend_undeclared TestClassTemplate{{$}} // CHECK-NEXT: | |-TemplateTypeParmDecl 0x{{.+}} <col:14, col:23> col:23 typename depth 1 index 0 T2{{$}} // CHECK-NEXT: | `-CXXRecordDecl 0x{{.+}} parent 0x{{.+}} <col:34, col:40> col:40 class TestClassTemplate{{$}} - // CHECK-NEXT: `-ClassTemplateSpecializationDecl 0x{{.+}} <line:[[@LINE-19]]:3, line:[[@LINE-17]]:3> line:[[@LINE-19]]:31 class TestClassTemplate definition implicit_instantiation{{$}} + // CHECK-NEXT: `-ClassTemplateSpecializationDecl 0x{{.+}} <col:5, col:40> line:[[@LINE-19]]:31 class TestClassTemplate definition implicit_instantiation{{$}} // CHECK-NEXT: |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init{{$}} // CHECK-NEXT: | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr{{$}} // CHECK-NEXT: | |-CopyConstructor simple trivial has_const_param implicit_has_const_param{{$}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits