Author: Matheus Izvekov Date: 2025-01-29T17:23:36-03:00 New Revision: 07a0e2be86f33beb6d519a3d466b95c2257e93cb
URL: https://github.com/llvm/llvm-project/commit/07a0e2be86f33beb6d519a3d466b95c2257e93cb DIFF: https://github.com/llvm/llvm-project/commit/07a0e2be86f33beb6d519a3d466b95c2257e93cb.diff LOG: [clang] Track function template instantiation from definition (#112241) This fixes instantiation of definition for friend function templates, when the declaration found and the one containing the definition have different template contexts. In these cases, the the function declaration corresponding to the definition is not available; it may not even be instantiated at all. So this patch adds a bit which tracks which function template declaration was instantiated from the member template. It's used to find which primary template serves as a context for the purpose of obtainining the template arguments needed to instantiate the definition. Fixes #55509 Relanding patch, with no changes, after it was reverted due to revert of commit this patch depended on. Added: clang/test/SemaTemplate/GH55509.cpp Modified: clang/docs/ReleaseNotes.rst clang/include/clang/AST/Decl.h clang/include/clang/AST/DeclBase.h clang/include/clang/AST/DeclTemplate.h clang/lib/AST/Decl.cpp clang/lib/Sema/SemaTemplateDeduction.cpp clang/lib/Sema/SemaTemplateInstantiate.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/lib/Serialization/ASTReaderDecl.cpp clang/lib/Serialization/ASTWriterDecl.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 6397d43616fdde..2b56ce974289d5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -126,6 +126,8 @@ Bug Fixes to Attribute Support Bug Fixes to C++ Support ^^^^^^^^^^^^^^^^^^^^^^^^ +- Clang is now better at keeping track of friend function template instance contexts. (#GH55509) + Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 9593bab576412a..362a2741a0cddd 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2298,6 +2298,13 @@ class FunctionDecl : public DeclaratorDecl, FunctionDeclBits.IsLateTemplateParsed = ILT; } + bool isInstantiatedFromMemberTemplate() const { + return FunctionDeclBits.IsInstantiatedFromMemberTemplate; + } + void setInstantiatedFromMemberTemplate(bool Val = true) { + FunctionDeclBits.IsInstantiatedFromMemberTemplate = Val; + } + /// Whether this function is "trivial" in some specialized C++ senses. /// Can only be true for default constructors, copy constructors, /// copy assignment operators, and destructors. Not meaningful until diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 2c0c3a8dc2f9d5..3a13309a6100ee 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1777,6 +1777,8 @@ class DeclContext { uint64_t HasImplicitReturnZero : 1; LLVM_PREFERRED_TYPE(bool) uint64_t IsLateTemplateParsed : 1; + LLVM_PREFERRED_TYPE(bool) + uint64_t IsInstantiatedFromMemberTemplate : 1; /// Kind of contexpr specifier as defined by ConstexprSpecKind. LLVM_PREFERRED_TYPE(ConstexprSpecKind) @@ -1827,7 +1829,7 @@ class DeclContext { }; /// Number of inherited and non-inherited bits in FunctionDeclBitfields. - enum { NumFunctionDeclBits = NumDeclContextBits + 31 }; + enum { NumFunctionDeclBits = NumDeclContextBits + 32 }; /// Stores the bits used by CXXConstructorDecl. If modified /// NumCXXConstructorDeclBits and the accessor @@ -1838,12 +1840,12 @@ class DeclContext { LLVM_PREFERRED_TYPE(FunctionDeclBitfields) uint64_t : NumFunctionDeclBits; - /// 20 bits to fit in the remaining available space. + /// 19 bits to fit in the remaining available space. /// Note that this makes CXXConstructorDeclBitfields take /// exactly 64 bits and thus the width of NumCtorInitializers /// will need to be shrunk if some bit is added to NumDeclContextBitfields, /// NumFunctionDeclBitfields or CXXConstructorDeclBitfields. - uint64_t NumCtorInitializers : 17; + uint64_t NumCtorInitializers : 16; LLVM_PREFERRED_TYPE(bool) uint64_t IsInheritingConstructor : 1; @@ -1857,7 +1859,7 @@ class DeclContext { }; /// Number of inherited and non-inherited bits in CXXConstructorDeclBitfields. - enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 20 }; + enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 19 }; /// Stores the bits used by ObjCMethodDecl. /// If modified NumObjCMethodDeclBits and the accessor diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 9ecff2c898acd5..0c706036ff7022 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -1011,6 +1011,15 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl { return getTemplatedDecl()->isThisDeclarationADefinition(); } + bool isCompatibleWithDefinition() const { + return getTemplatedDecl()->isInstantiatedFromMemberTemplate() || + isThisDeclarationADefinition(); + } + void setInstantiatedFromMemberTemplate(FunctionTemplateDecl *D) { + getTemplatedDecl()->setInstantiatedFromMemberTemplate(); + RedeclarableTemplateDecl::setInstantiatedFromMemberTemplate(D); + } + /// Return the specialization with the provided arguments if it exists, /// otherwise return the insertion point. FunctionDecl *findSpecialization(ArrayRef<TemplateArgument> Args, diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 0bd4d64b54a0f2..ba77c748815d56 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3069,6 +3069,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, FunctionDeclBits.IsIneligibleOrNotSelected = false; FunctionDeclBits.HasImplicitReturnZero = false; FunctionDeclBits.IsLateTemplateParsed = false; + FunctionDeclBits.IsInstantiatedFromMemberTemplate = false; FunctionDeclBits.ConstexprKind = static_cast<uint64_t>(ConstexprKind); FunctionDeclBits.BodyContainsImmediateEscalatingExpression = false; FunctionDeclBits.InstantiationIsPending = false; diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 137942f0c30bfe..6aaf86a6a6ff36 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4074,22 +4074,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( if (FunctionTemplate->getFriendObjectKind()) Owner = FunctionTemplate->getLexicalDeclContext(); FunctionDecl *FD = FunctionTemplate->getTemplatedDecl(); - // additional check for inline friend, - // ``` - // template <class F1> int foo(F1 X); - // template <int A1> struct A { - // template <class F1> friend int foo(F1 X) { return A1; } - // }; - // template struct A<1>; - // int a = foo(1.0); - // ``` - const FunctionDecl *FDFriend; - if (FD->getFriendObjectKind() == Decl::FriendObjectKind::FOK_None && - FD->isDefined(FDFriend, /*CheckForPendingFriendDefinition*/ true) && - FDFriend->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) { - FD = const_cast<FunctionDecl *>(FDFriend); - Owner = FD->getLexicalDeclContext(); - } + MultiLevelTemplateArgumentList SubstArgs( FunctionTemplate, CanonicalDeducedArgumentList->asArray(), /*Final=*/false); diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index c45d3ffe2508b2..b4fa23ddf049d5 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -479,9 +479,6 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( using namespace TemplateInstArgsHelpers; const Decl *CurDecl = ND; - if (!CurDecl) - CurDecl = Decl::castFromDeclContext(DC); - if (Innermost) { Result.addOuterTemplateArguments(const_cast<NamedDecl *>(ND), *Innermost, Final); @@ -495,8 +492,10 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( // has a depth of 0. if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl)) HandleDefaultTempArgIntoTempTempParam(TTP, Result); - CurDecl = Response::UseNextDecl(CurDecl).NextDecl; - } + CurDecl = DC ? Decl::castFromDeclContext(DC) + : Response::UseNextDecl(CurDecl).NextDecl; + } else if (!CurDecl) + CurDecl = Decl::castFromDeclContext(DC); while (!CurDecl->isFileContextDecl()) { Response R; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 131f5c8ad1a09d..f540431a73445f 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/DependentDiagnostic.h" @@ -5245,9 +5246,26 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, RebuildTypeSourceInfoForDefaultSpecialMembers(); SetDeclDefaulted(Function, PatternDecl->getLocation()); } else { + NamedDecl *ND = Function; + DeclContext *DC = ND->getLexicalDeclContext(); + std::optional<ArrayRef<TemplateArgument>> Innermost; + if (auto *Primary = Function->getPrimaryTemplate(); + Primary && + !isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function) && + Function->getTemplateSpecializationKind() != + TSK_ExplicitSpecialization) { + auto It = llvm::find_if(Primary->redecls(), + [](const RedeclarableTemplateDecl *RTD) { + return cast<FunctionTemplateDecl>(RTD) + ->isCompatibleWithDefinition(); + }); + assert(It != Primary->redecls().end() && + "Should't get here without a definition"); + DC = (*It)->getLexicalDeclContext(); + Innermost.emplace(Function->getTemplateSpecializationArgs()->asArray()); + } MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs( - Function, Function->getLexicalDeclContext(), /*Final=*/false, - /*Innermost=*/std::nullopt, false, PatternDecl); + Function, DC, /*Final=*/false, Innermost, false, PatternDecl); // Substitute into the qualifier; we can get a substitution failure here // through evil use of alias templates. diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 8210eb2143acf5..7a15e60d87d83a 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1064,6 +1064,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) { FD->setHasImplicitReturnZero(FunctionDeclBits.getNextBit()); FD->setIsMultiVersion(FunctionDeclBits.getNextBit()); FD->setLateTemplateParsed(FunctionDeclBits.getNextBit()); + FD->setInstantiatedFromMemberTemplate(FunctionDeclBits.getNextBit()); FD->setFriendConstraintRefersToEnclosingTemplate( FunctionDeclBits.getNextBit()); FD->setUsesSEHTry(FunctionDeclBits.getNextBit()); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index fa2294da95de8d..6a79444bdb9892 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -679,7 +679,7 @@ void ASTDeclWriter::VisitDeclaratorDecl(DeclaratorDecl *D) { } void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { - static_assert(DeclContext::NumFunctionDeclBits == 44, + static_assert(DeclContext::NumFunctionDeclBits == 45, "You need to update the serializer after you change the " "FunctionDeclBits"); @@ -785,6 +785,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { FunctionDeclBits.addBit(D->hasImplicitReturnZero()); FunctionDeclBits.addBit(D->isMultiVersion()); FunctionDeclBits.addBit(D->isLateTemplateParsed()); + FunctionDeclBits.addBit(D->isInstantiatedFromMemberTemplate()); FunctionDeclBits.addBit(D->FriendConstraintRefersToEnclosingTemplate()); FunctionDeclBits.addBit(D->usesSEHTry()); Record.push_back(FunctionDeclBits); diff --git a/clang/test/SemaTemplate/GH55509.cpp b/clang/test/SemaTemplate/GH55509.cpp new file mode 100644 index 00000000000000..f95833fbed7b19 --- /dev/null +++ b/clang/test/SemaTemplate/GH55509.cpp @@ -0,0 +1,101 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++26 %s + +namespace t1 { + template<int N> struct A { + template<class C> friend auto cica(const A<N-1>&, C) { + return N; + } + }; + + template<> struct A<0> { + template<class C> friend auto cica(const A<0>&, C); + // expected-note@-1 {{declared here}} + }; + + void test() { + cica(A<0>{}, 0); + // expected-error@-1 {{function 'cica<int>' with deduced return type cannot be used before it is defined}} + + (void)A<1>{}; + cica(A<0>{}, 0); + } +} // namespace t1 +namespace t2 { + template<int N> struct A { + template<class C> friend auto cica(const A<N-1>&, C) { + return N; + } + }; + + template<> struct A<0> { + template<class C> friend auto cica(const A<0>&, C); + }; + + template <int N, class = decltype(cica(A<N>{}, nullptr))> + void MakeCica(); + // expected-note@-1 {{candidate function}} + + template <int N> void MakeCica(A<N+1> = {}); + // expected-note@-1 {{candidate function}} + + void test() { + MakeCica<0>(); + + MakeCica<0>(); + // expected-error@-1 {{call to 'MakeCica' is ambiguous}} + } +} // namespace t2 +namespace t3 { + template<int N> struct A { + template<class C> friend auto cica(const A<N-1>&, C) { + return N-1; + } + }; + + template<> struct A<0> { + template<class C> friend auto cica(const A<0>&, C); + }; + + template <int N, class AT, class = decltype(cica(AT{}, nullptr))> + static constexpr bool MakeCica(int); + + template <int N, class AT> + static constexpr bool MakeCica(short, A<N+1> = {}); + + template <int N, class AT = A<N>, class Val = decltype(MakeCica<N, AT>(0))> + static constexpr bool has_cica = Val{}; + + constexpr bool cica2 = has_cica<0> || has_cica<0>; +} // namespace t3 +namespace t4 { + template<int N> struct A { + template<class C> friend auto cica(const A<N-1>&, C); + }; + + template<> struct A<0> { + template<class C> friend auto cica(const A<0>&, C) { + C a; + } + }; + + template struct A<1>; + + void test() { + cica(A<0>{}, 0); + } +} // namespace t4 +namespace regression1 { + template <class> class A; + + template <class T> [[gnu::abi_tag("TAG")]] void foo(A<T>); + + template <class> struct A { + friend void foo <>(A); + }; + + template struct A<int>; + + template <class T> [[gnu::abi_tag("TAG")]] void foo(A<T>) {} + + template void foo<int>(A<int>); +} // namespace regression1 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits