Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -306,6 +306,7 @@ def InitializerOverrides : DiagGroup<"initializer-overrides">; def NonNull : DiagGroup<"nonnull">; def NonPODVarargs : DiagGroup<"non-pod-varargs">; +def NonTemplateFriend : DiagGroup<"non-template-friend">; def ClassVarargs : DiagGroup<"class-varargs", [NonPODVarargs]>; def : DiagGroup<"nonportable-cfstrings">; def NonVirtualDtor : DiagGroup<"non-virtual-dtor">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -1271,6 +1271,13 @@ "enclosing namespace is a Microsoft extension; add a nested name specifier">, InGroup; def err_pure_friend : Error<"friend declaration cannot have a pure-specifier">; +def warn_non_template_friend : Warning<"friend declaration %q0 depends on " + "template parameter but is not a function template">, + InGroup; +def note_add_template_friend_decl : Note<"declare function outside class " + "template to suppress this warning">; +def note_befriend_template : Note<"to befriend a template specialization, " + "make sure the function template has already been declared and use '<>'">; def err_invalid_member_in_interface : Error< "%select{data member |non-public member function |static member function |" Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -10318,6 +10318,27 @@ /// attempts to add itself into the container void CheckObjCCircularContainer(ObjCMessageExpr *Message); + /// Set of file level friend function declared in template classes and + /// dependent on the template parameter. + /// + /// It is kept to check, if friend function declaration in the code like: + /// \code + /// template class C { + /// friend void func(T &x); + /// }; + /// \endcode + /// + /// is actually intended to be: + /// + /// \code + /// template friend void func(T2 &x); + /// \endcode + SmallVector FriendsOfTemplates; + + /// Check dependent friend functions for misinterpretation as function + /// templates. + void checkDependentFriends(); + void AnalyzeDeleteExprMismatch(const CXXDeleteExpr *DE); void AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation DeleteLoc, bool DeleteWasArrayForm); Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -764,6 +764,7 @@ LateTemplateParserCleanup(OpaqueParser); CheckDelayedMemberExceptionSpecs(); + checkDependentFriends(); } DiagnoseUnterminatedPragmaAttribute(); Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -12191,3 +12191,203 @@ _2, _3, _4)); } + +namespace { +/// Helper class used to check if a friend declaration may refer to another +/// function declaration. +/// +/// The class is used to compare two function declarations, one is a friend +/// function declared in template class, the other is a function with the same +/// name declared at file level. +/// +/// The class is used to produce appropriate warning if friend function declared +/// in a template class depends on that class template parameters. +/// +class FunctionMatcher { + ASTContext &Ctx; + std::map TemplParamMapping; + + bool match(QualType TFriend, QualType TFunc) { + if (TFriend == TFunc) + return true; + if (TFriend.getQualifiers() != TFunc.getQualifiers()) + return false; + if (auto FrT = TFriend->getAs()) { + if (auto FT = TFunc->getAs()) + return FrT->getDepth() == FT->getDepth() && + FrT->getIndex() == FT->getIndex(); + + const TemplateTypeParmType *CanonFrTParm = cast( + FrT->getCanonicalTypeUnqualified().getTypePtr()); + CanQualType CanonFT = TFunc->getCanonicalTypeUnqualified(); + auto Ptr = TemplParamMapping.find(CanonFrTParm); + if (Ptr != TemplParamMapping.end()) + return Ptr->second == CanonFT; + TemplParamMapping[CanonFrTParm] = CanonFT; + return true; + } + if (auto FrPT = TFriend->getAs()) { + if (auto PT = TFunc->getAs()) + return match(FrPT->getPointeeType(), PT->getPointeeType()); + return false; + } + if (auto FrLR = TFriend->getAs()) { + if (auto LR = TFunc->getAs()) + return match(FrLR->getPointeeType(), LR->getPointeeType()); + return false; + } + if (auto FrRR = TFriend->getAs()) { + if (auto RR = TFunc->getAs()) + return match(FrRR->getPointeeType(), RR->getPointeeType()); + return false; + } + if (auto FrT = TFriend->getAs()) { + QualType FrT2 = Ctx.getQualifiedType(FrT->getInjectedSpecializationType(), + TFriend.getQualifiers()); + return match(FrT2, TFunc); + } + if (auto FrT = TFriend->getAs()) { + if (auto FT = TFunc->getAs()) { + if (FrT->getNumParams() != FT->getNumParams()) + return false; + for (unsigned I = 0, E = FT->getNumParams(); I < E; ++I) { + if (!match(FrT->getParamType(I), FT->getParamType(I))) + return false; + } + return match(FrT->getReturnType(), FT->getReturnType()); + } + return false; + } + if (auto *FrT = TFriend->getAs()) { + if (auto FT = TFunc->getAs()) { + TemplateName FrTN = FrT->getTemplateName(); + TemplateName FTN = FT->getTemplateName(); + if (FrTN.getKind() != TemplateName::Template || + FrTN.getKind() != FTN.getKind()) + return false; + if (FrTN.isInstantiationDependent() || FTN.isInstantiationDependent()) + return false; + const Decl *FrD = FrTN.getAsTemplateDecl()->getCanonicalDecl(); + const Decl *FD = FTN.getAsTemplateDecl()->getCanonicalDecl(); + if (FrD != FD) + return false; + + auto IFrT = FrT->begin(), EFrT = FrT->end(); + auto IFT = FT->begin(); + for (; IFrT != EFrT; ++IFrT, ++IFT) { + assert(IFT != FT->end()); + const TemplateArgument &FrTA = *IFrT; + const TemplateArgument &FTA = *IFT; + if (FrTA.getKind() != FTA.getKind()) + return false; + if (FrTA.getKind() == TemplateArgument::Type) { + QualType FrTy = FrTA.getAsType(); + QualType FTy = FTA.getAsType(); + if (!match(FrTy, FTy)) + return false; + } else if (FrTA.getKind() == TemplateArgument::Integral) { + if (FrTA.getIntegralType() != FTA.getIntegralType()) + return false; + llvm::APSInt FrV = FrTA.getAsIntegral(); + llvm::APSInt FV = FTA.getAsIntegral(); + if (FrV != FV) + return false; + } else + return false; + } + return true; + } + return false; + } + + return false; + } + +public: + FunctionMatcher (ASTContext &Ctx) : Ctx(Ctx) {} + + bool isPossibleMatch(FunctionDecl *FrD, FunctionDecl *F) { + TemplParamMapping.clear(); + return match(FrD->getType(), F->getType()); + } + + bool isPossibleMatch(FunctionDecl *FrD, FunctionTemplateDecl *F) { + TemplParamMapping.clear(); + FunctionDecl *Pattern = F->getTemplatedDecl(); + return match(FrD->getType(), Pattern->getType()); + } +}; +} + +/// Given a friend function declaration checks if it might be misused. +static void checkDependentFriend(Sema &S, FunctionDecl *FriendD) { + if (FriendD->isInvalidDecl()) + return; + + // Scan all function and function template declarations with the same name as + // the specified friend function to see if some of them could be referenced in + // the friend declaration. + LookupResult FRes(S, FriendD->getNameInfo(), Sema::LookupOrdinaryName); + if (S.LookupQualifiedName(FRes, FriendD->getDeclContext())) { + + // First check for suitable functions that uses particular specialization of + // parameter type, for example: + // + // template class C { + // friend void func(T &x); + // }; + // void func(int &x); + // + // Presence of such function can be considered as an evidence that the + // friend function is dependent intentionally. No warning in this case. + for (NamedDecl *D : FRes) { + if (D->isInvalidDecl()) + continue; + if (auto *FD = dyn_cast(D)) { + if (FunctionMatcher(S.getASTContext()).isPossibleMatch(FriendD, FD)) + return; + } + } + + // Next check if there is suitable function template, like: + // + // template class C { + // friend void func(T &x); + // }; + // template void func(T &x); + // + // If such template exists, issue warning and propose the template as a + // friend. + for (NamedDecl *D : FRes) { + if (D->isInvalidDecl()) + continue; + if (auto *FTD = dyn_cast(D)) + if (FunctionMatcher(S.getASTContext()).isPossibleMatch(FriendD, FTD)) { + // Appropriate function template is found. + S.Diag(FriendD->getLocation(), diag::warn_non_template_friend) + << FriendD; + SourceLocation NameLoc = FriendD->getNameInfo().getLocEnd(); + SourceLocation PastName = S.getLocForEndOfToken(NameLoc); + S.Diag(PastName, diag::note_befriend_template) + << FixItHint::CreateInsertion(PastName, "<>"); + return; + } + } + } + + // No appropriate function or function template was found, so issue generic + // warning. + S.Diag(FriendD->getLocation(), diag::warn_non_template_friend) << FriendD; + S.Diag(FriendD->getLocation(), diag::note_add_template_friend_decl); + S.Diag(FriendD->getLocation(), diag::note_befriend_template); +} + + +void Sema::checkDependentFriends() { + for (FunctionDecl *FriendD : FriendsOfTemplates) { + if (FriendD->getType()->isDependentType() && + !FriendD->isThisDeclarationADefinition()) + checkDependentFriend(*this, FriendD); + } + FriendsOfTemplates.clear(); +} Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -9155,6 +9155,25 @@ AddToScope = false; } + // If this is a friend function declaration inside a template class and the + // function depends on the template parameter as in: + // + // template class C { + // friend void func(T &x); + // }; + // + // store it in the Sema. Later on such declaration will be checked if it is + // a misprint of a function template. + if (isFriend && !NewFD->isInvalidDecl()) { + if (TemplateParamLists.empty() && + !DC->isRecord() && + !isFunctionTemplateSpecialization && + NewFD->getType()->isDependentType() && + !NewFD->isThisDeclarationADefinition()) { + FriendsOfTemplates.push_back(NewFD); + } + } + return NewFD; } Index: test/CXX/drs/dr3xx.cpp =================================================================== --- test/CXX/drs/dr3xx.cpp +++ test/CXX/drs/dr3xx.cpp @@ -291,7 +291,9 @@ template void g(N::A<0>::B<0>); namespace N { - template struct I { friend bool operator==(const I&, const I&); }; + template struct I { friend bool operator==(const I&, const I&); }; // expected-warning{{friend declaration 'dr321::N::operator==' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} } N::I i, j; bool x = i == j; Index: test/CXX/drs/dr5xx.cpp =================================================================== --- test/CXX/drs/dr5xx.cpp +++ test/CXX/drs/dr5xx.cpp @@ -587,8 +587,12 @@ namespace dr557 { // dr557: yes template struct S { - friend void f(S *); - friend void g(S > *); + friend void f(S *); // expected-warning{{friend declaration 'dr557::f' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} + friend void g(S > *); // expected-warning{{friend declaration 'dr557::g' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} }; void x(S *p, S > *q) { f(p); Index: test/CXX/temp/temp.res/p3.cpp =================================================================== --- test/CXX/temp/temp.res/p3.cpp +++ test/CXX/temp/temp.res/p3.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -verify %s -std=c++11 +// RUN: %clang_cc1 -verify %s -std=c++11 -Wno-non-template-friend template struct A { template struct B; Index: test/SemaCXX/friend.cpp =================================================================== --- test/SemaCXX/friend.cpp +++ test/SemaCXX/friend.cpp @@ -388,3 +388,116 @@ friend void f(void *p = 0) {} // expected-error {{must be the only}} }; } + +template void pr23342_func(T x); +template +struct pr23342_C1 { + friend void pr23342_func<>(T x); + friend bool func(T x); // expected-warning{{friend declaration 'func' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} + friend bool func2(int x); + template friend bool func3(T2 x); + friend T func4(); // expected-warning{{friend declaration 'func4' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; + +namespace pr23342 { + +template +struct C1 { + friend void pr23342_func<>(T x); + friend bool func(T x); // expected-warning{{friend declaration 'pr23342::func' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} + friend bool func2(int x); + template friend bool func3(T2 x); + friend T func4(); // expected-warning{{friend declaration 'pr23342::func4' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; + +template +struct Arg { + friend bool operator==(const Arg& lhs, T rhs) { + return false; + } + friend bool operator!=(const Arg& lhs, T rhs); // expected-warning{{friend declaration 'pr23342::operator!=' depends on template parameter but is not a function template}} + // expected-note@-1{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; +template +bool operator!=(const Arg& lhs, T rhs) { + return true; +} +bool foo() { + Arg arg; + return (arg == 42) || (arg != 42); +} + + +template class C0 { + friend void func0(C0 &); // expected-warning{{friend declaration 'pr23342::func0' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; + +template class C0a { + friend void func0a(C0a &); // expected-warning{{friend declaration 'pr23342::func0a' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; +void func0a(C0a x); +void func0a(C0a *x); +void func0a(const C0a &x); +int func0a(C0a &x); + +template class C0b { + friend void func0b(C0b &x); +}; +void func0b(C0b &x); + + +template class C1a { + friend void func1a(T x, C1a &y); // expected-warning{{friend declaration 'pr23342::func1a' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; +void func1a(char x, C1a &y); + +template class C1b { + friend void func1b(T x, C1b &y); +}; +void func1b(int x, C1b &y); + + +template class C2a { + friend void func2a(C2a &); // expected-warning{{friend declaration 'pr23342::func2a' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; +template +void func2a(const C2a &x); + + +template class C2b { + friend void func2b(C2b &); // expected-warning{{friend declaration 'pr23342::func2b' depends on template parameter but is not a function template}} + // expected-note@-1{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}}; +}; +template +void func2b(C2b &x); + +template class C2c; +template +void func2c(C2c &x); +template class C2c { + friend void func2c<>(C2c &); +}; + +template class C3 { + friend void func3(T &); +}; +void func3(int &); +template void func3(T &); + +} Index: test/SemaCXX/overload-call.cpp =================================================================== --- test/SemaCXX/overload-call.cpp +++ test/SemaCXX/overload-call.cpp @@ -574,13 +574,17 @@ // Ensure that overload resolution attempts to complete argument types when // performing ADL. template struct S { - friend int f(const S&); + friend int f(const S&); // expected-warning{{friend declaration 'IncompleteArg::f' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} }; extern S s; int k = f(s); template struct Op { - friend bool operator==(const Op &, const Op &); + friend bool operator==(const Op &, const Op &); // expected-warning{{friend declaration 'IncompleteArg::operator==' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} }; extern Op op; bool b = op == op;