sepavloff created this revision. sepavloff added a subscriber: cfe-commits.
Declaration of friend function may depend on template parameters, however it does not become a template function: template<typename T> class C1 { friend void func(T x); }; It may be not obvious for user, so compiler could emit a warning in such case. This patch implements appropriate warning, the wording is taken from GCC message. The patch fixes PR23342. http://reviews.llvm.org/D16579 Files: include/clang/Basic/DiagnosticGroups.td include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/SemaDecl.cpp test/CXX/drs/dr3xx.cpp test/CXX/drs/dr5xx.cpp test/CXX/temp/temp.decls/temp.friend/p1.cpp test/PCH/cxx-templates.cpp test/PCH/cxx-templates.h test/SemaCXX/friend.cpp test/SemaCXX/overload-call.cpp
Index: test/SemaCXX/overload-call.cpp =================================================================== --- test/SemaCXX/overload-call.cpp +++ test/SemaCXX/overload-call.cpp @@ -566,13 +566,15 @@ // Ensure that overload resolution attempts to complete argument types when // performing ADL. template<typename T> struct S { - friend int f(const S&); + friend int f(const S&); // expected-warning{{friend declaration 'IncompleteArg::f' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} }; extern S<int> s; int k = f(s); template<typename T> struct Op { - friend bool operator==(const Op &, const Op &); + friend bool operator==(const Op &, const Op &); // expected-warning{{friend declaration 'IncompleteArg::operator==' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} }; extern Op<char> op; bool b = op == op; Index: test/SemaCXX/friend.cpp =================================================================== --- test/SemaCXX/friend.cpp +++ test/SemaCXX/friend.cpp @@ -21,7 +21,8 @@ template <class T> struct Outer { void foo(T); struct Inner { - friend void Outer::foo(T); + friend void Outer::foo(T); // expected-warning{{friend declaration 'test1::Outer::Inner::foo' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} }; }; @@ -363,3 +364,47 @@ f_pr6954(5); // expected-error{{undeclared identifier 'f_pr6954'}} } + +template<typename T> void pr23342_func(T x); +template<typename T> +struct pr23342_C1 { + friend void pr23342_func<>(T x); + friend bool func(T x); // expected-warning{{friend declaration 'func' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} + friend bool func2(int x); + template<typename T2> friend bool func3(T2 x); + friend T func4(); // expected-warning{{friend declaration 'func4' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} +}; + +namespace pr23342 { + +template<typename T> +struct C1 { + friend void pr23342_func<>(T x); + friend bool func(T x); // expected-warning{{friend declaration 'pr23342::func' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} + friend bool func2(int x); + template<typename T2> friend bool func3(T2 x); + friend T func4(); // expected-warning{{friend declaration 'pr23342::func4' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}}}; +}; + +template <typename T> +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!=' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}}}; +}; +template <typename T> +bool operator!=(const Arg<T>& lhs, T rhs) { + return true; +} +bool foo() { + Arg<int> arg; + return (arg == 42) || (arg != 42); +} + +} Index: test/PCH/cxx-templates.h =================================================================== --- test/PCH/cxx-templates.h +++ test/PCH/cxx-templates.h @@ -1,4 +1,5 @@ // Header for PCH test cxx-templates.cpp +#pragma clang diagnostic ignored "-Wnon-template-friend" template <typename T1, typename T2> struct S; Index: test/PCH/cxx-templates.cpp =================================================================== --- test/PCH/cxx-templates.cpp +++ test/PCH/cxx-templates.cpp @@ -104,7 +104,7 @@ } #ifndef NO_ERRORS -// expected-error@cxx-templates.h:305 {{incomplete}} +// expected-error@cxx-templates.h:306 {{incomplete}} template int local_extern::f<int[]>(); // expected-note {{in instantiation of}} #endif template int local_extern::g<int[]>(); Index: test/CXX/temp/temp.decls/temp.friend/p1.cpp =================================================================== --- test/CXX/temp/temp.decls/temp.friend/p1.cpp +++ test/CXX/temp/temp.decls/temp.friend/p1.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -verify -emit-llvm-only %s +// RUN: %clang_cc1 -verify -emit-llvm-only -Wno-non-template-friend %s namespace test0 { template <typename T> struct Num { Index: test/CXX/drs/dr5xx.cpp =================================================================== --- test/CXX/drs/dr5xx.cpp +++ test/CXX/drs/dr5xx.cpp @@ -581,8 +581,10 @@ namespace dr557 { // dr557: yes template<typename T> struct S { - friend void f(S<T> *); - friend void g(S<S<T> > *); + friend void f(S<T> *); // expected-warning{{friend declaration 'dr557::f' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} + friend void g(S<S<T> > *); // expected-warning{{friend declaration 'dr557::g' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} }; void x(S<int> *p, S<S<int> > *q) { f(p); Index: test/CXX/drs/dr3xx.cpp =================================================================== --- test/CXX/drs/dr3xx.cpp +++ test/CXX/drs/dr3xx.cpp @@ -291,7 +291,8 @@ template void g(N::A<0>::B<0>); namespace N { - template<typename> struct I { friend bool operator==(const I&, const I&); }; + template<typename> struct I { friend bool operator==(const I&, const I&); }; // expected-warning{{friend declaration 'dr321::N::operator==' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} } N::I<int> i, j; bool x = i == j; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -8312,6 +8312,15 @@ AddToScope = false; } + if (isFriend && !NewFD->isInvalidDecl()) { + if (R->isDependentType() && TemplateParamLists.empty() && + !isFunctionTemplateSpecialization && + (D.getFunctionDefinitionKind() == FDK_Declaration)) { + Diag(NameInfo.getLoc(), diag::warn_non_template_friend) << NewFD; + Diag(NameInfo.getLoc(), diag::note_non_template_friend); + } + } + return NewFD; } Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -1142,6 +1142,11 @@ "enclosing namespace is a Microsoft extension; add a nested name specifier">, InGroup<MicrosoftUnqualifiedFriend>; def err_pure_friend : Error<"friend declaration cannot have a pure-specifier">; +def warn_non_template_friend : Warning<"friend declaration %q0 declares a " + "non-template function">, InGroup<NonTemplateFriend>; +def note_non_template_friend : Note<"if this is not what you intended, make " + "sure the function template has already been declared and add <> after the " + "function name here">; def err_invalid_member_in_interface : Error< "%select{data member |non-public member function |static member function |" Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -273,6 +273,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">;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits