c++: -Wdangling-reference and lambda false warning [PR109640]

Message ID 20240120021823.81839-1-polacek@redhat.com
State Accepted
Headers
Series c++: -Wdangling-reference and lambda false warning [PR109640] |

Checks

Context Check Description
snail/gcc-patch-check success Github commit url

Commit Message

Marek Polacek Jan. 20, 2024, 2:18 a.m. UTC
  Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
-Wdangling-reference checks if a function receives a temporary as its
argument, and only warns if any of the arguments was a temporary.  But
we should not warn when the temporary represents a lambda or we generate
false positives as in the attached testcases.

	PR c++/113256
	PR c++/111607
	PR c++/109640

gcc/cp/ChangeLog:

	* call.cc (do_warn_dangling_reference): Don't warn if the temporary
	is of lambda type.

gcc/testsuite/ChangeLog:

	* g++.dg/warn/Wdangling-reference14.C: New test.
	* g++.dg/warn/Wdangling-reference15.C: New test.
	* g++.dg/warn/Wdangling-reference16.C: New test.
---
 gcc/cp/call.cc                                |  9 ++++--
 .../g++.dg/warn/Wdangling-reference14.C       | 22 +++++++++++++
 .../g++.dg/warn/Wdangling-reference15.C       | 31 +++++++++++++++++++
 .../g++.dg/warn/Wdangling-reference16.C       | 13 ++++++++
 4 files changed, 72 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/warn/Wdangling-reference14.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wdangling-reference15.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wdangling-reference16.C


base-commit: 615e25c82de97acc17ab438f88d6788cf7ffe1d6
  

Comments

Jason Merrill Jan. 23, 2024, 9:03 p.m. UTC | #1
On 1/19/24 21:18, Marek Polacek wrote:
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

OK.  A lambda could return a dangling reference, but it's unlikely.

> -- >8 --
> -Wdangling-reference checks if a function receives a temporary as its
> argument, and only warns if any of the arguments was a temporary.  But
> we should not warn when the temporary represents a lambda or we generate
> false positives as in the attached testcases.
> 
> 	PR c++/113256
> 	PR c++/111607
> 	PR c++/109640
> 
> gcc/cp/ChangeLog:
> 
> 	* call.cc (do_warn_dangling_reference): Don't warn if the temporary
> 	is of lambda type.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/warn/Wdangling-reference14.C: New test.
> 	* g++.dg/warn/Wdangling-reference15.C: New test.
> 	* g++.dg/warn/Wdangling-reference16.C: New test.
> ---
>   gcc/cp/call.cc                                |  9 ++++--
>   .../g++.dg/warn/Wdangling-reference14.C       | 22 +++++++++++++
>   .../g++.dg/warn/Wdangling-reference15.C       | 31 +++++++++++++++++++
>   .../g++.dg/warn/Wdangling-reference16.C       | 13 ++++++++
>   4 files changed, 72 insertions(+), 3 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wdangling-reference14.C
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wdangling-reference15.C
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wdangling-reference16.C
> 
> diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> index 1f5ff417c81..77f51bacce3 100644
> --- a/gcc/cp/call.cc
> +++ b/gcc/cp/call.cc
> @@ -14123,7 +14123,10 @@ do_warn_dangling_reference (tree expr, bool arg_p)
>         tree e = expr;
>         while (handled_component_p (e))
>   	e = TREE_OPERAND (e, 0);
> -      if (!reference_like_class_p (TREE_TYPE (e)))
> +      tree type = TREE_TYPE (e);
> +      /* If the temporary represents a lambda, we don't really know
> +	 what's going on here.  */
> +      if (!reference_like_class_p (type) && !LAMBDA_TYPE_P (type))
>   	return expr;
>       }
>   
> @@ -14180,10 +14183,10 @@ do_warn_dangling_reference (tree expr, bool arg_p)
>   	       initializing this reference parameter.  */
>   	    if (do_warn_dangling_reference (arg, /*arg_p=*/true))
>   	      return expr;
> -	  /* Don't warn about member function like:
> +	  /* Don't warn about member functions like:
>   	      std::any a(...);
>   	      S& s = a.emplace<S>({0}, 0);
> -	     which constructs a new object and returns a reference to it, but
> +	     which construct a new object and return a reference to it, but
>   	     we still want to detect:
>   	       struct S { const S& self () { return *this; } };
>   	       const S& s = S().self();
> diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference14.C b/gcc/testsuite/g++.dg/warn/Wdangling-reference14.C
> new file mode 100644
> index 00000000000..92b38a965e0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference14.C
> @@ -0,0 +1,22 @@
> +// PR c++/113256
> +// { dg-do compile { target c++14 } }
> +// { dg-options "-Wdangling-reference" }
> +
> +#include <utility>
> +#include <cassert>
> +
> +template<class M, class T, class A> auto bind(M T::* pm, A)
> +{
> +    return [=]( auto&& x ) -> M const& { return x.*pm; };
> +}
> +
> +template<int I> struct arg {};
> +
> +arg<1> _1;
> +
> +int main()
> +{
> +    std::pair<int, int> pair;
> +    int const& x = bind( &std::pair<int, int>::first, _1 )( pair ); // { dg-bogus "dangling reference" }
> +    assert( &x == &pair.first );
> +}
> diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference15.C b/gcc/testsuite/g++.dg/warn/Wdangling-reference15.C
> new file mode 100644
> index 00000000000..c39577db64a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference15.C
> @@ -0,0 +1,31 @@
> +// PR c++/111607
> +// { dg-do compile { target c++20 } }
> +// { dg-options "-Wdangling-reference" }
> +
> +#include <variant>
> +
> +struct S {
> +	constexpr S(int i_) : i(i_) {}
> +	S(S const &) = delete;
> +	S & operator=(S const &) = delete;
> +	S(S &&) = delete;
> +	S & operator=(S &&) = delete;
> +	int i;
> +};
> +
> +struct A {
> +	S s{0};
> +};
> +
> +using V = std::variant<A>;
> +
> +consteval auto f(V const & v) {
> +  auto const & s = std::visit([](auto const & v) -> S const & { return v.s; }, v); // { dg-bogus "dangling reference" }
> +  return s.i;
> +}
> +
> +int main() {
> +	constexpr V a{std::in_place_type<A>};
> +	constexpr auto i = f(a);
> +	return i;
> +}
> diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference16.C b/gcc/testsuite/g++.dg/warn/Wdangling-reference16.C
> new file mode 100644
> index 00000000000..91996922291
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference16.C
> @@ -0,0 +1,13 @@
> +// PR c++/109640
> +// { dg-do compile { target c++11 } }
> +// { dg-options "-Wdangling-reference" }
> +
> +bool
> +fn0 ()
> +{
> +    int a;
> +    int&& i = [](int& r) -> int&& { return static_cast<int&&>(r); }(a); // { dg-bogus "dangling reference" }
> +    auto const l = [](int& r) -> int&& { return static_cast<int&&>(r); };
> +    int&& j = l(a);
> +    return &i == &j;
> +}
> 
> base-commit: 615e25c82de97acc17ab438f88d6788cf7ffe1d6
  

Patch

diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 1f5ff417c81..77f51bacce3 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -14123,7 +14123,10 @@  do_warn_dangling_reference (tree expr, bool arg_p)
       tree e = expr;
       while (handled_component_p (e))
 	e = TREE_OPERAND (e, 0);
-      if (!reference_like_class_p (TREE_TYPE (e)))
+      tree type = TREE_TYPE (e);
+      /* If the temporary represents a lambda, we don't really know
+	 what's going on here.  */
+      if (!reference_like_class_p (type) && !LAMBDA_TYPE_P (type))
 	return expr;
     }
 
@@ -14180,10 +14183,10 @@  do_warn_dangling_reference (tree expr, bool arg_p)
 	       initializing this reference parameter.  */
 	    if (do_warn_dangling_reference (arg, /*arg_p=*/true))
 	      return expr;
-	  /* Don't warn about member function like:
+	  /* Don't warn about member functions like:
 	      std::any a(...);
 	      S& s = a.emplace<S>({0}, 0);
-	     which constructs a new object and returns a reference to it, but
+	     which construct a new object and return a reference to it, but
 	     we still want to detect:
 	       struct S { const S& self () { return *this; } };
 	       const S& s = S().self();
diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference14.C b/gcc/testsuite/g++.dg/warn/Wdangling-reference14.C
new file mode 100644
index 00000000000..92b38a965e0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference14.C
@@ -0,0 +1,22 @@ 
+// PR c++/113256
+// { dg-do compile { target c++14 } }
+// { dg-options "-Wdangling-reference" }
+
+#include <utility>
+#include <cassert>
+
+template<class M, class T, class A> auto bind(M T::* pm, A)
+{
+    return [=]( auto&& x ) -> M const& { return x.*pm; };
+}
+
+template<int I> struct arg {};
+
+arg<1> _1;
+
+int main()
+{
+    std::pair<int, int> pair;
+    int const& x = bind( &std::pair<int, int>::first, _1 )( pair ); // { dg-bogus "dangling reference" }
+    assert( &x == &pair.first );
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference15.C b/gcc/testsuite/g++.dg/warn/Wdangling-reference15.C
new file mode 100644
index 00000000000..c39577db64a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference15.C
@@ -0,0 +1,31 @@ 
+// PR c++/111607
+// { dg-do compile { target c++20 } }
+// { dg-options "-Wdangling-reference" }
+
+#include <variant>
+
+struct S {
+	constexpr S(int i_) : i(i_) {}
+	S(S const &) = delete;
+	S & operator=(S const &) = delete;
+	S(S &&) = delete;
+	S & operator=(S &&) = delete;
+	int i;
+};
+
+struct A {
+	S s{0};
+};
+
+using V = std::variant<A>;
+
+consteval auto f(V const & v) {
+  auto const & s = std::visit([](auto const & v) -> S const & { return v.s; }, v); // { dg-bogus "dangling reference" }
+  return s.i;
+}
+
+int main() {
+	constexpr V a{std::in_place_type<A>};
+	constexpr auto i = f(a);
+	return i;
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference16.C b/gcc/testsuite/g++.dg/warn/Wdangling-reference16.C
new file mode 100644
index 00000000000..91996922291
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference16.C
@@ -0,0 +1,13 @@ 
+// PR c++/109640
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wdangling-reference" }
+
+bool
+fn0 ()
+{
+    int a;
+    int&& i = [](int& r) -> int&& { return static_cast<int&&>(r); }(a); // { dg-bogus "dangling reference" }
+    auto const l = [](int& r) -> int&& { return static_cast<int&&>(r); };
+    int&& j = l(a);
+    return &i == &j;
+}