diff options
author | Marek Polacek <polacek@redhat.com> | 2023-03-17 14:36:10 -0400 |
---|---|---|
committer | Marek Polacek <polacek@redhat.com> | 2023-03-23 09:25:41 -0400 |
commit | 59bfdd5f467292a368d0d628084a4b65d1bb06bb (patch) | |
tree | f50a5eaa6ce601ea9751a42f0cb8cb7eae11d367 /gcc | |
parent | 3b97715af0e848ef8703ac04665bde562b2ac159 (diff) | |
download | gcc-59bfdd5f467292a368d0d628084a4b65d1bb06bb.zip gcc-59bfdd5f467292a368d0d628084a4b65d1bb06bb.tar.gz gcc-59bfdd5f467292a368d0d628084a4b65d1bb06bb.tar.bz2 |
c++: further -Wdangling-reference refinement [PR107532]
Based on <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107532#c24>,
it seems like we should treat *any* class with a reference member
as a reference wrapper. To suppress the warning in
int i = 42;
auto const& v = std::get<0>(std::tuple<int&>(i));
we have to look into base classes as well. For std::tuple, this means
that we have to check the _Head_base subobject, which is a non-direct
base class of std::tuple. So I've employed a DFS walk.
PR c++/107532
gcc/cp/ChangeLog:
* call.cc (class_has_reference_member_p): New.
(class_has_reference_member_p_r): New.
(reference_like_class_p): Don't look for a specific constructor.
Use a DFS walk with class_has_reference_member_p_r.
gcc/testsuite/ChangeLog:
* g++.dg/warn/Wdangling-reference11.C: New test.
* g++.dg/warn/Wdangling-reference12.C: New test.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cp/call.cc | 63 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/warn/Wdangling-reference11.C | 23 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/warn/Wdangling-reference12.C | 12 |
3 files changed, 72 insertions, 26 deletions
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index d5e8ccc..5df0f7d 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -13785,8 +13785,31 @@ std_pair_ref_ref_p (tree t) /* Return true if a class CTYPE is either std::reference_wrapper or std::ref_view, or a reference wrapper class. We consider a class - a reference wrapper class if it has a reference member and a - constructor taking the same reference type. */ + a reference wrapper class if it has a reference member. We no + longer check that it has a constructor taking the same reference type + since that approach still generated too many false positives. */ + +static bool +class_has_reference_member_p (tree t) +{ + for (tree fields = TYPE_FIELDS (t); + fields; + fields = DECL_CHAIN (fields)) + if (TREE_CODE (fields) == FIELD_DECL + && !DECL_ARTIFICIAL (fields) + && TYPE_REF_P (TREE_TYPE (fields))) + return true; + return false; +} + +/* A wrapper for the above suitable as a callback for dfs_walk_once. */ + +static tree +class_has_reference_member_p_r (tree binfo, void *) +{ + return (class_has_reference_member_p (BINFO_TYPE (binfo)) + ? integer_one_node : NULL_TREE); +} static bool reference_like_class_p (tree ctype) @@ -13802,31 +13825,19 @@ reference_like_class_p (tree ctype) if (decl_in_std_namespace_p (tdecl)) { tree name = DECL_NAME (tdecl); - return (name - && (id_equal (name, "reference_wrapper") - || id_equal (name, "span") - || id_equal (name, "ref_view"))); - } - for (tree fields = TYPE_FIELDS (ctype); - fields; - fields = DECL_CHAIN (fields)) - { - if (TREE_CODE (fields) != FIELD_DECL || DECL_ARTIFICIAL (fields)) - continue; - tree type = TREE_TYPE (fields); - if (!TYPE_REF_P (type)) - continue; - /* OK, the field is a reference member. Do we have a constructor - taking its type? */ - for (tree fn : ovl_range (CLASSTYPE_CONSTRUCTORS (ctype))) - { - tree args = FUNCTION_FIRST_USER_PARMTYPE (fn); - if (args - && same_type_p (TREE_VALUE (args), type) - && TREE_CHAIN (args) == void_list_node) - return true; - } + if (name + && (id_equal (name, "reference_wrapper") + || id_equal (name, "span") + || id_equal (name, "ref_view"))) + return true; } + + /* Some classes, such as std::tuple, have the reference member in its + (non-direct) base class. */ + if (dfs_walk_once (TYPE_BINFO (ctype), class_has_reference_member_p_r, + nullptr, nullptr)) + return true; + return false; } diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference11.C b/gcc/testsuite/g++.dg/warn/Wdangling-reference11.C new file mode 100644 index 0000000..667618e --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference11.C @@ -0,0 +1,23 @@ +// PR c++/107532 +// { dg-do compile { target c++11 } } +// { dg-options "-Wdangling-reference" } + +struct R +{ + int& r; + int& get() { return r; } + int&& rget() { return static_cast<int&&>(r); } +}; + +int main() +{ + int i = 42; + int& l = R{i}.get(); // { dg-bogus "dangling reference" } + int const& cl = R{i}.get(); // { dg-bogus "dangling reference" } + int&& r = R{i}.rget(); // { dg-bogus "dangling reference" } + int const&& cr = R{i}.rget(); // { dg-bogus "dangling reference" } + (void) l; + (void) r; + (void) cr; + (void) cl; +} diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference12.C b/gcc/testsuite/g++.dg/warn/Wdangling-reference12.C new file mode 100644 index 0000000..85e01f0 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference12.C @@ -0,0 +1,12 @@ +// PR c++/107532 +// { dg-do compile { target c++11 } } +// { dg-options "-Wdangling-reference" } + +#include <tuple> + +int main() +{ + int i = 42; + auto const& v = std::get<0>(std::tuple<int&>(i)); // { dg-bogus "dangling reference" } + (void) v; +} |