c++: Implement -Wdangling-reference [PR106393]
commitd2249cd9adf5ae638577139177a50f7e62d8abd9
authorMarek Polacek <polacek@redhat.com>
Fri, 14 Oct 2022 14:05:57 +0000 (14 10:05 -0400)
committerMarek Polacek <polacek@redhat.com>
Wed, 26 Oct 2022 19:13:04 +0000 (26 15:13 -0400)
tree646929046e4c2d8962270cc50acc17c6bad9f869
parentf896c13489d22b30d01257bc8316ab97b3359d1c
c++: Implement -Wdangling-reference [PR106393]

This patch implements a new experimental warning (enabled by -Wall) to
detect references bound to temporaries whose lifetime has ended.  The
primary motivation is the Note in
<https://en.cppreference.com/w/cpp/algorithm/max>:

  Capturing the result of std::max by reference produces a dangling reference
  if one of the parameters is a temporary and that parameter is returned:

  int n = 1;
  const int& r = std::max(n-1, n+1); // r is dangling

That's because both temporaries for n-1 and n+1 are destroyed at the end
of the full expression.  With this warning enabled, you'll get:

g.C:3:12: warning: possibly dangling reference to a temporary [-Wdangling-reference]
    3 | const int& r = std::max(n-1, n+1);
      |            ^
g.C:3:24: note: the temporary was destroyed at the end of the full expression 'std::max<int>((n - 1), (n + 1))'
    3 | const int& r = std::max(n-1, n+1);
      |                ~~~~~~~~^~~~~~~~~~

The warning works by checking if a reference is initialized with a function
that returns a reference, and at least one parameter of the function is
a reference that is bound to a temporary.  It assumes that such a function
actually returns one of its arguments!  (I added code to check_return_expr
to suppress the warning when we've seen the definition of the function
and we can say that it can return a variable with static storage
duration.)

It warns when the function in question is a member function, but only if
the function is invoked on a temporary object, otherwise the warning
would emit loads of warnings for valid code like obj.emplace<T>({0}, 0).
It does detect the dangling reference in:

  struct S {
    const S& self () { return *this; }
  };
  const S& s = S().self();

It warns in member initializer lists as well:

  const int& f(const int& i) { return i; }
  struct S {
    const int &r;
    S() : r(f(10)) { }
  };

I've run the testsuite/bootstrap with the warning enabled by default.
There were just a few FAILs, all of which look like genuine bugs.
A bootstrap with the warning enabled by default passed as well.

When testing a previous version of the patch, there were many FAILs in
libstdc++'s 22_locale/; all of them because the warning triggered on

  const test_type& obj = std::use_facet<test_type>(std::locale());

but this code looks valid -- std::use_facet doesn't return a reference
to its parameter.  Therefore I added a #pragma and code to suppress the
warning.

PR c++/106393

gcc/c-family/ChangeLog:

* c.opt (Wdangling-reference): New.

gcc/cp/ChangeLog:

* call.cc (expr_represents_temporary_p): New, factored out of...
(conv_binds_ref_to_temporary): ...here.  Don't return false just
because a ck_base is missing.  Use expr_represents_temporary_p.
(do_warn_dangling_reference): New.
(maybe_warn_dangling_reference): New.
(extend_ref_init_temps): Call maybe_warn_dangling_reference.
* cp-tree.h: Adjust comment.
* typeck.cc (check_return_expr): Suppress -Wdangling-reference
warnings.

gcc/ChangeLog:

* doc/invoke.texi: Document -Wdangling-reference.

libstdc++-v3/ChangeLog:

* include/bits/locale_classes.tcc: Add #pragma to disable
-Wdangling-reference with std::use_facet.

gcc/testsuite/ChangeLog:

* g++.dg/cpp23/elision4.C: Use -Wdangling-reference, add dg-warning.
* g++.dg/cpp23/elision7.C: Likewise.
* g++.dg/warn/Wdangling-pointer-2.C: Use -Wno-dangling-reference.
* g++.dg/warn/Wdangling-reference1.C: New test.
* g++.dg/warn/Wdangling-reference2.C: New test.
* g++.dg/warn/Wdangling-reference3.C: New test.
12 files changed:
gcc/c-family/c.opt
gcc/cp/call.cc
gcc/cp/cp-tree.h
gcc/cp/typeck.cc
gcc/doc/invoke.texi
gcc/testsuite/g++.dg/cpp23/elision4.C
gcc/testsuite/g++.dg/cpp23/elision7.C
gcc/testsuite/g++.dg/warn/Wdangling-pointer-2.C
gcc/testsuite/g++.dg/warn/Wdangling-reference1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wdangling-reference2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wdangling-reference3.C [new file with mode: 0644]
libstdc++-v3/include/bits/locale_classes.tcc