Index: lib/AST/DeclTemplate.cpp =================================================================== --- lib/AST/DeclTemplate.cpp +++ lib/AST/DeclTemplate.cpp @@ -167,7 +167,20 @@ llvm::FoldingSetNodeID ID; EntryType::Profile(ID,Args, getASTContext()); EntryType *Entry = Specs.FindNodeOrInsertPos(ID, InsertPos); - return Entry ? SETraits::getDecl(Entry)->getMostRecentDecl() : nullptr; + if (!Entry) + return nullptr; + auto *SpecDecl = SETraits::getDecl(Entry)->getMostRecentDecl(); + // If we have specializations used in friend declarations and at file level, + // the latter are preferred. + if (SpecDecl->getFriendObjectKind() != FOK_None) { + for (auto *D : SpecDecl->redecls()) { + if (SpecDecl->getFriendObjectKind() == FOK_None) { + SpecDecl = static_cast::DeclType *>(D); + break; + } + } + } + return SpecDecl; } template Index: lib/Sema/SemaAccess.cpp =================================================================== --- lib/Sema/SemaAccess.cpp +++ lib/Sema/SemaAccess.cpp @@ -492,10 +492,11 @@ const EffectiveContext &EC, FunctionDecl *Friend) { AccessResult OnFailure = AR_inaccessible; + Friend = Friend->getCanonicalDecl(); for (SmallVectorImpl::const_iterator I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) { - if (Friend == *I) + if (Friend == (*I)->getCanonicalDecl()) return AR_accessible; if (EC.isDependent() && MightInstantiateTo(S, *I, Friend)) Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1617,13 +1617,21 @@ Innermost), /*InsertPos=*/nullptr); } else if (isFriend) { - // Note, we need this connection even if the friend doesn't have a body. - // Its body may exist but not have been attached yet due to deferred - // parsing. - // FIXME: It might be cleaner to set this when attaching the body to the - // friend function declaration, however that would require finding all the - // instantiations and modifying them. - Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation); + if (const FunctionTemplateSpecializationInfo *FTSI = + D->getTemplateSpecializationInfo()) { + Function->setFunctionTemplateSpecialization(FTSI->getTemplate(), + TemplateArgumentList::CreateCopy(SemaRef.Context, + FTSI->TemplateArguments->asArray()), + /*InsertPos=*/nullptr); + } else { + // Note, we need this connection even if the friend doesn't have a body. + // Its body may exist but not have been attached yet due to deferred + // parsing. + // FIXME: It might be cleaner to set this when attaching the body to the + // friend function declaration, however that would require finding all the + // instantiations and modifying them. + Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation); + } } if (InitFunctionInstantiation(Function, D)) @@ -1668,6 +1676,13 @@ isExplicitSpecialization = true; + } else if (const FunctionTemplateSpecializationInfo *FTSI = + D->getTemplateSpecializationInfo()) { + void *InsertPos = nullptr; + FunctionDecl *Prev = FTSI->getTemplate()->findSpecialization( + FTSI->TemplateArguments->asArray(), InsertPos); + if (Prev) + Function->setPreviousDeclaration(Prev); } else if (TemplateParams || !FunctionTemplate) { // Look only into the namespace where the friend would be declared to // find a previous declaration. This is the innermost enclosing namespace, Index: test/SemaCXX/friend-spec.cpp =================================================================== --- /dev/null +++ test/SemaCXX/friend-spec.cpp @@ -0,0 +1,96 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -DCHECK_DIAGS %s +// RUN: %clang_cc1 -triple i386-pc-linux-gnu -emit-llvm %s + +// Tests for befriended function specializations + + +#ifdef CHECK_DIAGS + +namespace test1 { + template + void func_01(T); + + template + class C1 { + static void private_func(); + friend void func_01<>(int); + }; + + template + void func_01(T) { + C1::private_func(); + } + + template void func_01<>(int); +} + +namespace test2 { + template + void func_01(T); + + template + class C1 { + static void private_func(); // expected-note{{implicitly declared private here}} + friend void func_01<>(int); + }; + + template + void func_01(T) { + C1::private_func(); // expected-error{{'private_func' is a private member of 'test2::C1'}} + } + + template void func_01<>(int*); // expected-note{{in instantiation of function template specialization 'test2::func_01' requested here}} +} + +namespace test3 { + template + void func_01(T, int); + template + void func_01(T, char); + + template + class C1 { + static void private_func(); // expected-note{{implicitly declared private here}} + friend void func_01<>(int, T); + }; + + template + void func_01(T, int) { + C1::private_func(); + } + + template + void func_01(T, char) { + C1::private_func(); // expected-error{{'private_func' is a private member of 'test3::C1'}} + } + + template void func_01<>(int, int); + template void func_01<>(int, char); // expected-note{{in instantiation of function template specialization 'test3::func_01' requested here}} +} + +#endif + +namespace pr12994 { + template + void PrintThis(cls &obj); + + class Xxx; + + template + class Test { + friend void PrintThis<>(Xxx&); + void printme() {} + }; + + class Aimpl {}; + + class Xxx : public Test {}; + + template + void PrintThis(cls &obj) { + obj.printme(); + } + + template void PrintThis(Xxx&); +} +