diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index 130fbc9a9724cc76f29f4ee9bd0352ad1f193320..d5ae69b0fdfad6be026c9311d78682d517997e0e 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -3243,10 +3243,16 @@ warn_hidden (tree t) continue; tree name = OVL_NAME (fns); + size_t num_fns = 0; /* The number of fndecls in fns. */ auto_vec<tree, 20> base_fndecls; tree base_binfo; tree binfo; unsigned j; + size_t num_overriders = 0; + hash_set<tree> overriden_base_fndecls; + /* base_fndecls that are hidden but not overriden. The "value" + contains the last fndecl we saw that hides the "key". */ + hash_map<tree, tree> hidden_base_fndecls; if (IDENTIFIER_CDTOR_P (name)) continue; @@ -3264,47 +3270,65 @@ 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; - if (TREE_CODE (fndecl) == FUNCTION_DECL - && DECL_VINDEX (fndecl)) + bool template_p = TREE_CODE (fndecl) == TEMPLATE_DECL; + bool fndecl_overrides_p = false; + fndecl = STRIP_TEMPLATE (fndecl); + if (TREE_CODE (fndecl) != FUNCTION_DECL + || fndecl == conv_op_marker) + continue; + num_fns++; + for (size_t k = 0; k < base_fndecls.length (); k++) { - /* 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. */ - 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; + if (IDENTIFIER_CONV_OP_P (name) + && !same_type_p (DECL_CONV_FN_TYPE (fndecl), + DECL_CONV_FN_TYPE (base_fndecls[k]))) + /* If base_fndecl[k] and fndecl are conversion operators + to different types, they're unrelated. */ + ; + else if (!template_p /* Template methods don't override. */ + && same_signature_p (fndecl, base_fndecls[k])) + { + overriden_base_fndecls.add (base_fndecls[k]); + fndecl_overrides_p = true; + } + else + { + /* fndecl hides base_fndecls[k]. */ + hidden_base_fndecls.put (base_fndecls[k], fndecl); + } } - if (!any_override) - seen_non_override = true; + if (fndecl_overrides_p) + ++num_overriders; } - if (!seen_non_override && warn_overloaded_virtual == 1) - /* All the derived fns override base virtuals. */ - return; + if (warn_overloaded_virtual == 1 && num_overriders == 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; + if (!hidden_fndecl + || overriden_base_fndecls.contains (hidden_fndecl)) + continue; + auto_diagnostic_group d; + if (warning_at (location_of (hidden_fndecl), + OPT_Woverloaded_virtual_, + "%qD was hidden", hidden_fndecl)) + { + tree hider = hidden_base_fndecl.second; + inform (location_of (hider), " by %qD", hider); + } + } } } diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index a33afdb3d509871cfcee5f1a886a8d72e4a2119a..72e169585487f922e8fa2398655e2a29d9eeb4b4 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -3402,7 +3402,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 92f8327b9d0573f44e51577384a387b7888d2f14..9091bfabc967aa0ea1d05813cedd4b0c4b72c02d 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-virt10.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt10.C new file mode 100644 index 0000000000000000000000000000000000000000..42b8b0f97886a2a67d72080e99144fbcfff0ecb5 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt10.C @@ -0,0 +1,11 @@ +// PR c++/117114 - Test case from PR c++/117114 +// { dg-do compile { target c++11 } } +// { dg-additional-options -Woverloaded-virtual } + +struct Troops { virtual ~Troops(); }; +struct Control { virtual int GetControl() const = 0; }; +struct Army : Troops, Control { int GetControl() const override; }; + +struct VirtualControl : virtual Control { + int GetControl() const override; +}; diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt11.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt11.C new file mode 100644 index 0000000000000000000000000000000000000000..4d96b318cc2ce21f4bd2122bb7a130be2121f2d5 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt11.C @@ -0,0 +1,25 @@ +// PR c++/109918 - More tests with multiple inheritance +// { dg-do compile { target c++11 } } +// { dg-additional-options -Woverloaded-virtual } + +struct Troops { virtual ~Troops(); }; +struct Control { + virtual int GetControl() const { return 42; } // { dg-warning "was hidden" } +}; +struct Army : Troops, Control { + template<class T> void GetControl() const; // { dg-message "by" } +}; + + +struct A { + virtual void get() const; +}; +struct B { + virtual void get() const; + virtual void get(char) const; // { dg-warning "was hidden" } +}; + +struct C : A, B { + virtual void get() const; // { dg-message "by" } + virtual void get(int) const; +}; diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt12.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt12.C new file mode 100644 index 0000000000000000000000000000000000000000..3cfe22c03ec5f5b21badc82604a0490a35cad580 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt12.C @@ -0,0 +1,23 @@ +// PR c++/109918 - Test covariant return types +// { dg-do compile { target c++11 } } +// { dg-additional-options -Woverloaded-virtual } + +struct Big { virtual ~Big () {} }; +struct Smaller : Big {}; + +// Single inheritance case +struct Foo { + virtual Big* getMe() const; +}; +struct Bar : Foo { + virtual Smaller* getMe() const; +}; + +// Multiple inheritance case +struct Troops { virtual ~Troops(); }; +struct Control { + virtual Big* GetControl() const; +}; +struct Army : Troops, Control { + virtual Smaller* GetControl() const; +}; diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt13.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt13.C new file mode 100644 index 0000000000000000000000000000000000000000..fb421e17dc34d338e06614ab54721b6d81657188 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt13.C @@ -0,0 +1,28 @@ +// PR c++/117114 - Reduced version of another bootstrap error +// Unrelated methods can have the same DECL_VINDEX when the class hierarchy +// depth is 2 or more. +// { dg-do compile { target c++11 } } +// { dg-additional-options -Woverloaded-virtual } + +class HIRFullVisitor; +class HIRTypeVisitor; + +struct FullVisitable { + virtual void accept_vis (HIRFullVisitor &vis) = 0; +}; + +struct Node { + virtual ~Node() {} +}; + +struct Type : Node, FullVisitable +{ + using FullVisitable::accept_vis; + virtual void accept_vis (HIRTypeVisitor &vis) = 0; +}; + +struct TypePath : Type +{ + void accept_vis (HIRFullVisitor &vis) override; + void accept_vis (HIRTypeVisitor &vis) override; +}; 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 0000000000000000000000000000000000000000..01cd562609a8f9e7a0a47578390f4e5bc64e6afc --- /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 0000000000000000000000000000000000000000..c18049e3a92bd040af702a1036fd3d759c1c373f --- /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 0000000000000000000000000000000000000000..39b0899c2d29b8074ff62fa40c1c69b2467bebe1 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt7.C @@ -0,0 +1,37 @@ +// PR c++/109918 - Test different CV-quals, usage of typedefs, and templates +// { 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 "operator char()" } + operator int() { return 43; } +}; + +typedef char MyChar; +struct C : public A { + operator MyChar() { return 'A'; } // { dg-note "operator MyChar()" } + operator int() { return 43; } +}; + +struct D : public A { + template<class T> + operator T() { return T(); } +}; +int d = D(); + +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 0000000000000000000000000000000000000000..51af2dae77ca4facbf77ee94261cd1e02bdae268 --- /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 0000000000000000000000000000000000000000..6d315c63a08e62ea20b9e68bb60e2a5aa53519ea --- /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) {} +};