Index: include/clang/AST/DeclTemplate.h =================================================================== --- include/clang/AST/DeclTemplate.h +++ include/clang/AST/DeclTemplate.h @@ -1005,6 +1005,16 @@ return getTemplatedDecl()->isThisDeclarationADefinition(); } + /// Checks if this template declaration or some of its redeclaration defines + /// the template. + /// \param [out] FT is assigned defining declaration if it exists. + /// + /// The function can find a definition that has no body (as it is not + /// instantiated yet) if there are no other definitions. Call to + /// isThisDeclarationADefinition returns false for such definitions. + /// + bool isDefined(FunctionTemplateDecl *&FT) const; + /// \brief Return the specialization with the provided arguments if it exists, /// otherwise return the insertion point. FunctionDecl *findSpecialization(ArrayRef Args, Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -3245,6 +3245,14 @@ if (auto *MFD = getInstantiatedFromMemberFunction()) return getDefinitionOrSelf(MFD); + if (FunctionTemplateDecl *TD = getDescribedFunctionTemplate()) { + if (TD->getFriendObjectKind() != FOK_None) { + while (FunctionTemplateDecl *FT = TD->getInstantiatedFromMemberTemplate()) + TD = FT; + return TD->getTemplatedDecl(); + } + } + return nullptr; } Index: lib/AST/DeclTemplate.cpp =================================================================== --- lib/AST/DeclTemplate.cpp +++ lib/AST/DeclTemplate.cpp @@ -231,6 +231,30 @@ return CommonPtr; } +bool FunctionTemplateDecl::isDefined(FunctionTemplateDecl *&Def) const { + FunctionTemplateDecl *UninstantiatedDef = nullptr; + for (auto I : redecls()) { + auto FTD = cast(I); + // If this declaration is a definition, return the result. + if (FTD->isThisDeclarationADefinition()) { + Def = FTD; + return true; + } + // If this declaration has uninstantiated body, return it if no other + // definition exists. + if (FunctionTemplateDecl *Orig = FTD->getInstantiatedFromMemberTemplate()) { + FunctionTemplateDecl *D; + if (Orig->isDefined(D)) + UninstantiatedDef = FTD; + } + } + if (UninstantiatedDef) { + Def = UninstantiatedDef; + return true; + } + return false; +} + void FunctionTemplateDecl::LoadLazySpecializations() const { // Grab the most recent declaration to ensure we've loaded any lazy // redeclarations of this template. Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -11874,9 +11874,35 @@ const FunctionDecl *EffectiveDefinition, SkipBodyInfo *SkipBody) { const FunctionDecl *Definition = EffectiveDefinition; - if (!Definition) - if (!FD->isOdrDefined(Definition)) + if (!Definition) { + if (FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate()) { + FunctionTemplateDecl *DefTD = nullptr; + if (!FTD->isDefined(DefTD)) + return; + const FunctionDecl *Def = DefTD->getTemplatedDecl(); + + // If the found definition is a template with uninstantiated body, it + // can be replaced in specialization: + // + // template struct X { + // template void f(T, U) { } + // }; + // template<> template void X::f(int x, U y) { + // x = y; + // } + // + // In this example the specialization 'X' contains declaration of + // 'f', which is considered a definition by 'isDefined' because it is + // obtained by instantiation of 'X::f', which has a body. + // + if (isa(Def->getLexicalDeclContext()) && + !Def->isThisDeclarationADefinition() && + Def->getFriendObjectKind() == Decl::FOK_None) + return; + Definition = Def; + } else if (!FD->isOdrDefined(Definition)) return; + } assert(Definition); if (FD == Definition) Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1792,7 +1792,9 @@ // If the original function was part of a friend declaration, // inherit its namespace state and add it to the owner. if (isFriend) { - PrincipalDecl->setObjectOfFriendDecl(); + Function->setObjectOfFriendDecl(); + if (FunctionTemplate) + FunctionTemplate->setObjectOfFriendDecl(); DC->makeDeclVisibleInContext(PrincipalDecl); bool QueuedInstantiation = false; Index: test/SemaCXX/friend2.cpp =================================================================== --- test/SemaCXX/friend2.cpp +++ test/SemaCXX/friend2.cpp @@ -129,6 +129,83 @@ void func_22() {} // expected-error{{redefinition of 'func_22'}} +// Case of template friend functions. + +template void func_31(T *x); +template +struct C31a { + template friend void func_31(T *x) {} +}; +template +struct C31b { + template friend void func_31(T *x) {} +}; + + +template inline void func_32(T *x) {} +template +struct C32a { + template friend void func_32(T *x) {} +}; +template +struct C32b { + template friend void func_32(T *x) {} +}; + + +template +struct C33a { + template friend void func_33(T *x) {} +}; +template +struct C33b { + template friend void func_33(T *x) {} +}; + + +template inline void func_34(T *x) {} // expected-note{{previous definition is here}} +template +struct C34 { + template friend void func_34(T *x) {} // expected-error{{redefinition of 'func_34'}} +}; + +C34 v34; // expected-note{{in instantiation of template class 'C34' requested here}} + + +template inline void func_35(T *x); +template +struct C35a { + template friend void func_35(T *x) {} // expected-note{{previous definition is here}} +}; +template +struct C35b { + template friend void func_35(T *x) {} // expected-error{{redefinition of 'func_35'}} +}; + +C35a v35a; +C35b v35b; // expected-note{{in instantiation of template class 'C35b' requested here}} + + +template void func_36(T *x); +template +struct C36 { + template friend void func_36(T *x) {} // expected-error{{redefinition of 'func_36'}} + // expected-note@-1{{previous definition is here}} +}; + +C36 v36a; +C36 v36b; //expected-note{{in instantiation of template class 'C36' requested here}} + + +template void func_37(T *x); +template +struct C37 { + template friend void func_37(T *x) {} // expected-note{{previous definition is here}} +}; + +C37 v37; +template void func_37(T *x) {} // expected-error{{redefinition of 'func_37'}} + namespace pr22307 {