From: Marek Polacek Date: Fri, 17 Mar 2023 18:36:10 +0000 (-0400) Subject: c++: further -Wdangling-reference refinement [PR107532] X-Git-Tag: basepoints/gcc-14~377 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=59bfdd5f467292a368d0d628084a4b65d1bb06bb;p=thirdparty%2Fgcc.git c++: further -Wdangling-reference refinement [PR107532] Based on , 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(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. --- diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index d5e8ccc07d3d..5df0f7ddee93 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 000000000000..667618e7196f --- /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(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 000000000000..85e01f01a506 --- /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 + +int main() +{ + int i = 42; + auto const& v = std::get<0>(std::tuple(i)); // { dg-bogus "dangling reference" } + (void) v; +}