Attached is a patch adding a -Wlong-distance-friends flag to diagnose long distance (cross module) friendship.
2020-08-14 Jeff Chapman II <jchap...@lock3software.com> gcc/c-family/ * c.opt (Wlong-distance-friends): New. gcc/cp/ * cp-tree.h (module_friendship_compatible): New. * friend.c (add_friend): Warn when befriending a function owned by a different module. (make_friend_class): Warn when befriending a class owned by a different module. * module.cc (module_friendship_compatible): New function. Returns true if a decl may be befriended by another without issuing a long distance friend warning. gcc/testsuite/ * g++.dg/modules/long-distance-friend-1_[ab].C: New test. * g++.dg/modules/long-distance-friend-2_a.[Ch]: Ditto. * g++.dg/modules/long-distance-friend-3_[ab].[Ch]: Ditto. * g++.dg/modules/redecl-3_b.C: Add case for -Wlong-distance-friends. Thanks, Jeff Chapman II
From 3d229111dcbe4bc3438207a500452c4420fba527 Mon Sep 17 00:00:00 2001 From: Jeff Chapman II <jchap...@lock3software.com> Date: Fri, 20 Dec 2019 11:02:20 -0500 Subject: [PATCH 2/2] c++: Add warning flag for cross module friendship 2020-08-14 Jeff Chapman II <jchap...@lock3software.com> gcc/c-family/ * c.opt (Wlong-distance-friends): New. gcc/cp/ * cp-tree.h (module_friendship_compatible): New. * friend.c (add_friend): Warn when befriending a function owned by a different module. (make_friend_class): Warn when befriending a class owned by a different module. * module.cc (module_friendship_compatible): New function. Returns true if a decl may be befriended by another without issuing a long distance friend warning. gcc/testsuite/ * g++.dg/modules/long-distance-friend-1_[ab].C: New test. * g++.dg/modules/long-distance-friend-2_a.[Ch]: Ditto. * g++.dg/modules/long-distance-friend-3_[ab].[Ch]: Ditto. * g++.dg/modules/redecl-3_b.C: Add case for -Wlong-distance-friends. --- gcc/c-family/c.opt | 4 ++ gcc/cp/cp-tree.h | 1 + gcc/cp/friend.c | 24 ++++++++++++ gcc/cp/module.cc | 38 +++++++++++++++++++ .../g++.dg/modules/long-distance-friend-1_a.C | 17 +++++++++ .../g++.dg/modules/long-distance-friend-1_b.C | 24 ++++++++++++ .../g++.dg/modules/long-distance-friend-2_a.C | 18 +++++++++ .../g++.dg/modules/long-distance-friend-2_a.h | 7 ++++ .../g++.dg/modules/long-distance-friend-3_a.C | 9 +++++ .../g++.dg/modules/long-distance-friend-3_b.C | 9 +++++ .../g++.dg/modules/long-distance-friend-3_b.h | 15 ++++++++ gcc/testsuite/g++.dg/modules/redecl-3_b.C | 4 +- 12 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.dg/modules/long-distance-friend-1_a.C create mode 100644 gcc/testsuite/g++.dg/modules/long-distance-friend-1_b.C create mode 100644 gcc/testsuite/g++.dg/modules/long-distance-friend-2_a.C create mode 100644 gcc/testsuite/g++.dg/modules/long-distance-friend-2_a.h create mode 100644 gcc/testsuite/g++.dg/modules/long-distance-friend-3_a.C create mode 100644 gcc/testsuite/g++.dg/modules/long-distance-friend-3_b.C create mode 100644 gcc/testsuite/g++.dg/modules/long-distance-friend-3_b.h diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 506d091e5f2..2d39ac5f974 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -739,6 +739,10 @@ Wlogical-not-parentheses C ObjC C++ ObjC++ Var(warn_logical_not_paren) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall) Warn when logical not is used on the left hand side operand of a comparison. +Wlong-distance-friends +C++ Var(warn_long_distance_friends) Warning LangEnabledBy(C++,Wall) +Warn when friends are declared across module boundaries. + Wlong-long C ObjC C++ ObjC++ CPP(cpp_warn_long_long) CppReason(CPP_W_LONG_LONG) Var(warn_long_long) Init(-1) Warning LangEnabledBy(C ObjC,Wc90-c99-compat) Do not warn about using \"long long\" when -pedantic. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index d633b4d5c70..9299625f33b 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7071,6 +7071,7 @@ inline bool module_exporting_p () extern module_state *get_module (tree name, module_state *parent = NULL, bool partition = false); extern bool module_may_redeclare (tree decl); +extern bool module_friendship_compatible (tree decl1, tree decl2); extern int module_initializer_kind (); extern void module_add_import_initializers (); diff --git a/gcc/cp/friend.c b/gcc/cp/friend.c index 9b27d7d340a..1e6e746a3a5 100644 --- a/gcc/cp/friend.c +++ b/gcc/cp/friend.c @@ -173,6 +173,17 @@ add_friend (tree type, tree decl, bool complain) if (decl == error_mark_node) return; + if (modules_p () + && complain + && warn_long_distance_friends + && !module_friendship_compatible (TYPE_NAME (type), decl)) + { + warning (OPT_Wlong_distance_friends, + "%q#T is not visible to befriended declaration %q#D", + type, decl); + inform (DECL_SOURCE_LOCATION (decl), "import declared %q#D here", decl); + } + typedecl = TYPE_MAIN_DECL (type); list = DECL_FRIENDLIST (typedecl); name = DECL_NAME (decl); @@ -249,6 +260,19 @@ add_friend (tree type, tree decl, bool complain) void make_friend_class (tree type, tree friend_type, bool complain) { + if (modules_p () + && complain + && warn_long_distance_friends + && !module_friendship_compatible (TYPE_NAME (type), + TYPE_NAME (friend_type))) + { + warning (OPT_Wlong_distance_friends, + "%q#T is not visible to befriended declaration %q#T", + type, friend_type); + inform (location_of (friend_type), "import declared %q#T here", + friend_type); + } + tree classes; /* CLASS_TEMPLATE_DEPTH counts the number of template headers for diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index f743e851848..583c72abe6b 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -17718,6 +17718,44 @@ module_may_redeclare (tree decl) return me && get_primary (them) == get_primary (me); } +/* Is it possible for two decls are friendship compatible -- that is, can they + reasonably use the other's definition. */ + +bool +module_friendship_compatible (tree decl1, tree decl2) +{ + int our_origin = get_originating_module (decl1); + int their_origin = get_originating_module (decl2); + + module_state *them = (*modules)[their_origin]; + module_state *me = (*modules)[our_origin]; + + if (them->is_header ()) + /* If it came from a header, it's in the global module. */ + return (me->is_header () + || !module_purview_p ()); + + if (!their_origin) + return ((DECL_LANG_SPECIFIC (decl2) && DECL_MODULE_PURVIEW_P (decl2)) + == module_purview_p ()); + + if (!me->name) + me = me->parent; + + if (me && get_primary (them) == get_primary (me)) + return true; + + /* It's never ok to friend a template declaration. */ + if (TREE_CODE (decl2) == TEMPLATE_DECL) + return false; + + /* It's always ok to friend a template specialization. */ + if (DECL_FUNCTION_TEMPLATE_P (decl2) && DECL_TEMPLATE_INFO (decl2)) + return true; + + return me && get_primary (them) == get_primary (me); +} + /* DECL is being created by this TU. Record it came from here. We record module purview, so we can see if partial or explicit specialization needs to be written out, even though its purviewness diff --git a/gcc/testsuite/g++.dg/modules/long-distance-friend-1_a.C b/gcc/testsuite/g++.dg/modules/long-distance-friend-1_a.C new file mode 100644 index 00000000000..8e1848ea064 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/long-distance-friend-1_a.C @@ -0,0 +1,17 @@ +// { dg-additional-options -fmodules-ts } +export module foo; +// { dg-module-cmi foo } + +export int foo() { return 0; } +export struct Foo; +export struct FooComplete {}; + +export template<typename T> int templateFoo(T t) { return 0; } +export template<> int templateFoo(double t) { return 0; } + +export template<typename T> struct TemplateFoo; + +export template<typename T> struct TemplateFooComplete { T t; }; +export template<> struct TemplateFooComplete<double> { double t; double f; }; + + diff --git a/gcc/testsuite/g++.dg/modules/long-distance-friend-1_b.C b/gcc/testsuite/g++.dg/modules/long-distance-friend-1_b.C new file mode 100644 index 00000000000..a2d9e4c7c41 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/long-distance-friend-1_b.C @@ -0,0 +1,24 @@ +// { dg-additional-options "-fmodules-ts -Wlong-distance-friends" } +import foo; + +struct Bar +{ + friend int foo(); // { dg-warning "is not visible to befriended decl" } + friend class Foo; // { dg-warning "is not visible to befriended decl" } + friend class FooComplete; // { dg-warning "is not visible to befriended decl" } +}; + +struct Bar2 +{ + friend int templateFoo<int>(int); // { dg-warning "is not visible to befriended decl" } + + friend int templateFoo<double>(double); // { dg-warning "is not visible to befriended decl" } + + friend class TemplateFoo<int>; // { dg-warning "is not visible to befriended decl" } + + + friend class TemplateFooComplete<int>; // { dg-warning "is not visible to befriended decl" } + + friend class TemplateFooComplete<double>; // { dg-warning "is not visible to befriended decl" } +}; + diff --git a/gcc/testsuite/g++.dg/modules/long-distance-friend-2_a.C b/gcc/testsuite/g++.dg/modules/long-distance-friend-2_a.C new file mode 100644 index 00000000000..f24860cc44d --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/long-distance-friend-2_a.C @@ -0,0 +1,18 @@ +// { dg-additional-options "-fmodules-ts -Wlong-distance-friends" } +module; +#include "long-distance-friend-2_a.h" +export module foo; +// { dg-module-cmi foo } + +class B +{ + friend A; // { dg-warning "is not visible to befriended decl" } + friend class A2; // { dg-warning "is not visible to befriended decl" } +}; + +class B2 +{ + friend A3; // { dg-warning "is not visible to befriended decl" } + friend class A4; // { dg-warning "is not visible to befriended decl" } +}; + diff --git a/gcc/testsuite/g++.dg/modules/long-distance-friend-2_a.h b/gcc/testsuite/g++.dg/modules/long-distance-friend-2_a.h new file mode 100644 index 00000000000..c8958d8a41c --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/long-distance-friend-2_a.h @@ -0,0 +1,7 @@ +#pragma once + +class A; +class A2; +class A3; +class A4; + diff --git a/gcc/testsuite/g++.dg/modules/long-distance-friend-3_a.C b/gcc/testsuite/g++.dg/modules/long-distance-friend-3_a.C new file mode 100644 index 00000000000..9ea9e0c1905 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/long-distance-friend-3_a.C @@ -0,0 +1,9 @@ +// { dg-additional-options "-fmodules-ts -Wlong-distance-friends" } +export module foo; +// { dg-module-cmi foo } + +export class Foo; +export class FooComplete {}; +class FooPrivate; +class FooPrivateComplete {}; + diff --git a/gcc/testsuite/g++.dg/modules/long-distance-friend-3_b.C b/gcc/testsuite/g++.dg/modules/long-distance-friend-3_b.C new file mode 100644 index 00000000000..ca11d7261c0 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/long-distance-friend-3_b.C @@ -0,0 +1,9 @@ +// { dg-additional-options "-fmodules-ts -Wlong-distance-friends" } +#include "long-distance-friend-3_b.h" + +// { dg-warning "is not visible to befriended decl" "" { target *-*-* } 6 } +// { dg-warning "is not visible to befriended decl" "" { target *-*-* } 7 } + +// { dg-error "does not name a type" "" { target *-*-* } 12 } +// { dg-error "does not name a type" "" { target *-*-* } 13 } + diff --git a/gcc/testsuite/g++.dg/modules/long-distance-friend-3_b.h b/gcc/testsuite/g++.dg/modules/long-distance-friend-3_b.h new file mode 100644 index 00000000000..3361b60270d --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/long-distance-friend-3_b.h @@ -0,0 +1,15 @@ +#pragma once +import foo; + +class A +{ + friend Foo; + friend FooComplete; +}; + +class A2 +{ + friend FooPrivate; + friend FooPrivateComplete; +}; + diff --git a/gcc/testsuite/g++.dg/modules/redecl-3_b.C b/gcc/testsuite/g++.dg/modules/redecl-3_b.C index 35efe7b1d37..f98d9efccb2 100644 --- a/gcc/testsuite/g++.dg/modules/redecl-3_b.C +++ b/gcc/testsuite/g++.dg/modules/redecl-3_b.C @@ -1,9 +1,9 @@ -// { dg-additional-options "-fmodules-ts" } +// { dg-additional-options "-fmodules-ts -Wlong-distance-friends" } import foo; struct Bar { - friend int foo(); + friend int foo(); // { dg-warning "is not visible to befriended decl" } }; int foo() // { dg-error "conflicts with import" } -- 2.27.0