diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index bd86fff6dd03f..37b892c4e1655 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -472,6 +472,7 @@ Bug Fixes to C++ Support - Fixed an assertion failure in debug mode, and potential crashes in release mode, when diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter. - Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326) +- Clang is now better at keeping track of friend function template instance contexts. (#GH55509) - Fixed an issue deducing non-type template arguments of reference type. (#GH73460) - Fixed an issue in constraint evaluation, where type constraints on the lambda expression containing outer unexpanded parameters were not correctly expanded. (#GH101754) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 7ff35d73df599..6afc86710a813 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2299,6 +2299,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 ee662ed73d7e0..eb67dc03157e6 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1763,6 +1763,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) @@ -1813,7 +1815,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 @@ -1824,12 +1826,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; @@ -1843,7 +1845,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 05739f39d2a49..2fb49ec1aea0d 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -1008,6 +1008,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 Args, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7ff9c2754a6fe..043456438b6d0 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13027,6 +13027,12 @@ class Sema final : public SemaBase { std::optional> Innermost = std::nullopt, bool RelativeToPrimary = false, bool ForConstraintInstantiation = false); + void getTemplateInstantiationArgs( + MultiLevelTemplateArgumentList &Result, const NamedDecl *D, + const DeclContext *DC = nullptr, bool Final = false, + std::optional> Innermost = std::nullopt, + bool RelativeToPrimary = false, bool ForConstraintInstantiation = false); + /// RAII object to handle the state changes required to synthesize /// a function body. class SynthesizedFunctionScope { diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 58d11a0312c50..8f54b5f1589d4 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3067,6 +3067,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(ConstexprKind); FunctionDeclBits.BodyContainsImmediateEscalatingExpression = false; FunctionDeclBits.InstantiationIsPending = false; diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 8e80ab730ac34..f9a8d2d9ff0b1 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4007,22 +4007,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( if (FunctionTemplate->getFriendObjectKind()) Owner = FunctionTemplate->getLexicalDeclContext(); FunctionDecl *FD = FunctionTemplate->getTemplatedDecl(); - // additional check for inline friend, - // ``` - // template int foo(F1 X); - // template struct A { - // template 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(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 f2007fc5d85a5..9c5b3e7c9066c 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -512,13 +512,13 @@ struct TemplateInstantiationArgumentCollecter } // namespace -MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( - const NamedDecl *ND, const DeclContext *DC, bool Final, +void Sema::getTemplateInstantiationArgs( + MultiLevelTemplateArgumentList &Result, const NamedDecl *ND, + const DeclContext *DC, bool Final, std::optional> Innermost, bool RelativeToPrimary, 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; const Decl *CurDecl = ND; if (!CurDecl) @@ -529,6 +529,17 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( do { CurDecl = Collecter.Visit(const_cast(CurDecl)); } while (CurDecl); +} + +MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( + const NamedDecl *ND, const DeclContext *DC, bool Final, + std::optional> Innermost, bool RelativeToPrimary, + 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; + getTemplateInstantiationArgs(Result, ND, DC, Final, Innermost, + RelativeToPrimary, ForConstraintInstantiation); return Result; } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index d29434486dcb0..17d167b3a5e0c 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5214,8 +5214,26 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, RebuildTypeSourceInfoForDefaultSpecialMembers(); SetDeclDefaulted(Function, PatternDecl->getLocation()); } else { - MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs( - Function, Function->getLexicalDeclContext()); + DeclContext *DC = Function; + MultiLevelTemplateArgumentList TemplateArgs; + if (auto *Primary = Function->getPrimaryTemplate(); + Primary && + !isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) { + auto It = llvm::find_if(Primary->redecls(), + [](const RedeclarableTemplateDecl *RTD) { + return cast(RTD) + ->isCompatibleWithDefinition(); + }); + assert(It != Primary->redecls().end() && + "Should't get here without a definition"); + DC = (*It)->getLexicalDeclContext(); + if (Function->getTemplateSpecializationKind() != + TSK_ExplicitSpecialization) + TemplateArgs.addOuterTemplateArguments( + Function, Function->getTemplateSpecializationArgs()->asArray(), + /*Final=*/false); + } + getTemplateInstantiationArgs(TemplateArgs, /*D=*/nullptr, DC); // 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 1ccc810f415eb..a44df84a8bcef 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1087,6 +1087,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 f21cbd11b6ab8..dec93317dc7b3 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -626,7 +626,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"); @@ -732,6 +732,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 0000000000000..f95833fbed7b1 --- /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 struct A { + template friend auto cica(const A&, C) { + return N; + } + }; + + template<> struct A<0> { + template friend auto cica(const A<0>&, C); + // expected-note@-1 {{declared here}} + }; + + void test() { + cica(A<0>{}, 0); + // expected-error@-1 {{function 'cica' with deduced return type cannot be used before it is defined}} + + (void)A<1>{}; + cica(A<0>{}, 0); + } +} // namespace t1 +namespace t2 { + template struct A { + template friend auto cica(const A&, C) { + return N; + } + }; + + template<> struct A<0> { + template friend auto cica(const A<0>&, C); + }; + + template {}, nullptr))> + void MakeCica(); + // expected-note@-1 {{candidate function}} + + template void MakeCica(A = {}); + // expected-note@-1 {{candidate function}} + + void test() { + MakeCica<0>(); + + MakeCica<0>(); + // expected-error@-1 {{call to 'MakeCica' is ambiguous}} + } +} // namespace t2 +namespace t3 { + template struct A { + template friend auto cica(const A&, C) { + return N-1; + } + }; + + template<> struct A<0> { + template friend auto cica(const A<0>&, C); + }; + + template + static constexpr bool MakeCica(int); + + template + static constexpr bool MakeCica(short, A = {}); + + template , class Val = decltype(MakeCica(0))> + static constexpr bool has_cica = Val{}; + + constexpr bool cica2 = has_cica<0> || has_cica<0>; +} // namespace t3 +namespace t4 { + template struct A { + template friend auto cica(const A&, C); + }; + + template<> struct A<0> { + template 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 A; + + template [[gnu::abi_tag("TAG")]] void foo(A); + + template struct A { + friend void foo <>(A); + }; + + template struct A; + + template [[gnu::abi_tag("TAG")]] void foo(A) {} + + template void foo(A); +} // namespace regression1