diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index 646072d4f20..f754886a60a 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -3243,10 +3243,15 @@ warn_hidden (tree t) continue; tree name = OVL_NAME (fns); + size_t num_fns = 0; /* The number of fndecls in fns. */ auto_vec base_fndecls; tree base_binfo; tree binfo; unsigned j; + hash_set overriden_base_fndecls; + /* base_fndecls that are hidden but not overriden. The "value" + contains the fndecl that hides the "key". */ + hash_map hidden_base_fndecls; if (IDENTIFIER_CDTOR_P (name)) continue; @@ -3264,47 +3269,63 @@ warn_hidden (tree t) if (base_fndecls.is_empty ()) continue; - /* Remove any overridden functions. */ - bool seen_non_override = false; + /* Find all the base_fndecls that are overridden, as well as those + that are hidden, in T. */ for (tree fndecl : ovl_range (fns)) { - bool any_override = false; + fndecl = STRIP_TEMPLATE (fndecl); if (TREE_CODE (fndecl) == FUNCTION_DECL - && DECL_VINDEX (fndecl)) + && fndecl != conv_op_marker) { - /* If the method from the base class has the same - signature as the method from the derived class, it - has been overridden. Note that we can't move on - after finding one match: fndecl might override - multiple base fns. */ + num_fns++; + tree fndecl_vindex = DECL_VINDEX (fndecl); + /* If the method from the base class has the same DECL_VINDEX + as the method from the derived, it has been overridden. + Note that we can't move on after finding one match: fndecl + might override multiple base fns. */ for (size_t k = 0; k < base_fndecls.length (); k++) - if (base_fndecls[k] - && same_signature_p (fndecl, base_fndecls[k])) - { - base_fndecls[k] = NULL_TREE; - any_override = true; - } + { + if (!base_fndecls[k] || !DECL_VINDEX (base_fndecls[k])) + continue; + tree base_fndecl_vindex = DECL_VINDEX (base_fndecls[k]); + if (fndecl_vindex + && !tree_int_cst_compare (fndecl_vindex, + base_fndecl_vindex)) + overriden_base_fndecls.add (base_fndecls[k]); + else if (IDENTIFIER_CONV_OP_P (name) + && (DECL_NAME (fndecl) + != DECL_NAME (base_fndecls[k]))) + /* If base_fndecl[k] and fndecl are conversion operators + to different types, they're unrelated. */ + ; + else + hidden_base_fndecls.put (base_fndecls[k], fndecl); + } } - if (!any_override) - seen_non_override = true; } - if (!seen_non_override && warn_overloaded_virtual == 1) - /* All the derived fns override base virtuals. */ - return; + if (warn_overloaded_virtual == 1 + && overriden_base_fndecls.elements () == num_fns) + /* All the fns override a base virtual. */ + continue; - /* Now give a warning for all base functions without overriders, - as they are hidden. */ - for (tree base_fndecl : base_fndecls) - if (base_fndecl) - { - auto_diagnostic_group d; - /* Here we know it is a hider, and no overrider exists. */ - if (warning_at (location_of (base_fndecl), - OPT_Woverloaded_virtual_, - "%qD was hidden", base_fndecl)) - inform (location_of (fns), " by %qD", fns); - } + /* Now give a warning for all hidden methods. Note that a method that + is both in hidden_base_fndecls and overriden_base_fndecls is not + hidden. */ + for (auto hidden_base_fndecl : hidden_base_fndecls) + { + tree hidden_fndecl = hidden_base_fndecl.first; + tree hider = hidden_base_fndecl.second; + if (!hidden_fndecl + || overriden_base_fndecls.contains (hidden_fndecl)) + continue; + gcc_assert (hider); + auto_diagnostic_group d; + if (warning_at (location_of (hidden_fndecl), + OPT_Woverloaded_virtual_, + "%qD was hidden", hidden_fndecl)) + inform (location_of (hider), " by %qD", hider); + } } } diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index 65f70c595cf..3d0dad4ee59 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -3397,7 +3397,8 @@ location_of (tree t) return input_location; } else if (TREE_CODE (t) == OVERLOAD) - t = OVL_FIRST (t); + t = OVL_FIRST (t) != conv_op_marker ? OVL_FIRST (t) + : OVL_FIRST (OVL_CHAIN (t)); if (DECL_P (t)) return DECL_SOURCE_LOCATION (t); diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt1.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt1.C index 92f8327b9d0..9091bfabc96 100644 --- a/gcc/testsuite/g++.dg/warn/Woverloaded-virt1.C +++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt1.C @@ -5,10 +5,12 @@ class Foo { public: virtual void f(int); // { dg-warning "hidden" } + void g(int); // Not virtual, so no warning }; class Bar : public Foo { public: virtual void f(short); // { dg-message "by" } + virtual void g(short); }; diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt5.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt5.C new file mode 100644 index 00000000000..01cd562609a --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt5.C @@ -0,0 +1,12 @@ +// PR c++/109918 - Exact PR testcase +// { dg-do compile } +// { dg-additional-options -Wall } + +struct A { + virtual operator int() { return 42; } + virtual operator char() = 0; +}; + +struct B : public A { + operator char() { return 'A'; } +}; diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt6.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt6.C new file mode 100644 index 00000000000..c18049e3a92 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt6.C @@ -0,0 +1,12 @@ +// PR c++/109918 - PR testcase with -Woverloaded-virtual=2 +// { dg-do compile } +// { dg-additional-options -Woverloaded-virtual=2 } + +struct A { + virtual operator int() { return 42; } + virtual operator char() = 0; +}; + +struct B : public A { + operator char() { return 'A'; } +}; diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt7.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt7.C new file mode 100644 index 00000000000..be50c890d20 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt7.C @@ -0,0 +1,25 @@ +// PR c++/109918 - Test different CV-quals +// { dg-do compile } +// { dg-additional-options -Woverloaded-virtual } + +struct A { + virtual operator char() { return 'a'; } + virtual operator char() const { return 'b'; } // { dg-warning "was hidden" } + virtual operator int() { return 42; } +}; + +struct B : public A { + operator char() { return 'A'; } // { dg-note "by" } + operator int() { return 43; } +}; + +struct AA { + virtual char func(char) { return 'a'; } + virtual char func(char) const { return 'b'; } // { dg-warning "was hidden" } + virtual int func(int) { return 42; } +}; + +struct BB : public AA { + char func(char) { return 'A'; } // { dg-note "by" } + int func(int) { return 43; } +}; diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt8.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt8.C new file mode 100644 index 00000000000..51af2dae77c --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt8.C @@ -0,0 +1,15 @@ +// Identified when investigating PR c++/109918: no warning was emitted due to +// an incorrect early return in warn_hidden. +// { dg-additional-options -Wall } + +struct Foo +{ + virtual void f(int); // { dg-warning "hidden" } + virtual void g() {} +}; + +struct Bar : Foo +{ + virtual void f(short); // { dg-message "by" } + virtual void g() {} +}; diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt9.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt9.C new file mode 100644 index 00000000000..6d315c63a08 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt9.C @@ -0,0 +1,14 @@ +// PR c++/109918: Non virtual overloads in derived classes that don't override +// anything shouldn't cause warnings, even at -Woverloaded-virtual=2 +// { dg-additional-options -Woverloaded-virtual=2 } + +struct Foo +{ + virtual void g() {} +}; + +struct Bar : Foo +{ + virtual void g() {} + void g(int) {} +};