Author: Matheus Izvekov Date: 2025-02-05T14:12:12-03:00 New Revision: 346077aaa6bef5652a72a2f3d9fc134ea8fc6a5b
URL: https://github.com/llvm/llvm-project/commit/346077aaa6bef5652a72a2f3d9fc134ea8fc6a5b DIFF: https://github.com/llvm/llvm-project/commit/346077aaa6bef5652a72a2f3d9fc134ea8fc6a5b.diff LOG: Reland: [clang] Track function template instantiation from definition (#125266) 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 f7feca0089f94d3..21c1ff25d2862ba 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -145,6 +145,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 499d27a9be5a8a4..f305cbbce4c609a 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 3bb82c1572ef9c5..648dae2838e036b 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1780,6 +1780,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) @@ -1830,7 +1832,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 @@ -1841,12 +1843,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; @@ -1860,7 +1862,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 87406b0e030df16..a30ae798a99bc69 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -1011,6 +1011,26 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl { return getTemplatedDecl()->isThisDeclarationADefinition(); } + bool isCompatibleWithDefinition() const { + return getTemplatedDecl()->isInstantiatedFromMemberTemplate() || + isThisDeclarationADefinition(); + } + + // This bit closely tracks 'RedeclarableTemplateDecl::InstantiatedFromMember', + // except this is per declaration, while the redeclarable field is + // per chain. This indicates a template redeclaration which + // is compatible with the definition, in the non-trivial case + // where this is not already a definition. + // This is only really needed for instantiating the definition of friend + // function templates, which can have redeclarations in diff erent template + // contexts. + // The bit is actually stored in the FunctionDecl for space efficiency + // reasons. + 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 7e8a172ae4c37fa..610207cf8b9a4a1 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3065,6 +3065,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 096844f55374bc6..eb70dd743c8be51 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4072,22 +4072,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 85301d696bccddf..d9a47ca3daa20c5 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 595369661917cef..1f42f9500959e5e 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" @@ -5276,9 +5277,31 @@ 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"); + if (FunctionDecl *Def = cast<FunctionTemplateDecl>(*It) + ->getTemplatedDecl() + ->getDefinition()) + DC = Def->getLexicalDeclContext(); + else + 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 28db3d355237ace..df834cd862f737a 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 d8ce3c2fb964a79..b25dadab656b0ea 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 000000000000000..773a84305a0cda8 --- /dev/null +++ b/clang/test/SemaTemplate/GH55509.cpp @@ -0,0 +1,112 @@ +// 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 +namespace regression2 { + template <class> struct A { + template <class T> static void f() { + A<int>::f<T>(); + } + }; + template <> template <class T> void A<int>::f() { + static_assert(__is_same(T, long)); + } + template void A<void>::f<long>(); +} // namespace regression2 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits