diff options
author | Marek Polacek <polacek@redhat.com> | 2022-10-14 10:05:57 -0400 |
---|---|---|
committer | Marek Polacek <polacek@redhat.com> | 2022-10-26 15:13:04 -0400 |
commit | d2249cd9adf5ae638577139177a50f7e62d8abd9 (patch) | |
tree | 646929046e4c2d8962270cc50acc17c6bad9f869 /gcc/doc | |
parent | f896c13489d22b30d01257bc8316ab97b3359d1c (diff) | |
download | gcc-d2249cd9adf5ae638577139177a50f7e62d8abd9.zip gcc-d2249cd9adf5ae638577139177a50f7e62d8abd9.tar.gz gcc-d2249cd9adf5ae638577139177a50f7e62d8abd9.tar.bz2 |
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.
Diffstat (limited to 'gcc/doc')
-rw-r--r-- | gcc/doc/invoke.texi | 51 |
1 files changed, 50 insertions, 1 deletions
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 64f77e8..9f0e546 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -249,7 +249,8 @@ in the following sections. -Wno-class-conversion -Wclass-memaccess @gol -Wcomma-subscript -Wconditionally-supported @gol -Wno-conversion-null -Wctad-maybe-unsupported @gol --Wctor-dtor-privacy -Wno-delete-incomplete @gol +-Wctor-dtor-privacy -Wdangling-reference @gol +-Wno-delete-incomplete @gol -Wdelete-non-virtual-dtor -Wno-deprecated-array-compare @gol -Wdeprecated-copy -Wdeprecated-copy-dtor @gol -Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion @gol @@ -3627,6 +3628,54 @@ public static member functions. Also warn if there are no non-private methods, and there's at least one private member function that isn't a constructor or destructor. +@item -Wdangling-reference @r{(C++ and Objective-C++ only)} +@opindex Wdangling-reference +@opindex Wno-dangling-reference +Warn when a reference is bound to a temporary whose lifetime has ended. +For example: + +@smallexample +int n = 1; +const int& r = std::max(n - 1, n + 1); // r is dangling +@end smallexample + +In the example above, two temporaries are created, one for each +argument, and a reference to one of the temporaries is returned. +However, both temporaries are destroyed at the end of the full +expression, so the reference @code{r} is dangling. This warning +also detects dangling references in member initializer lists: + +@smallexample +const int& f(const int& i) @{ return i; @} +struct S @{ + const int &r; // r is dangling + S() : r(f(10)) @{ @} +@}; +@end smallexample + +Member functions are checked as well, but only their object argument: + +@smallexample +struct S @{ + const S& self () @{ return *this; @} +@}; +const S& s = S().self(); // s is dangling +@end smallexample + +Certain functions are safe in this respect, for example @code{std::use_facet}: +they take and return a reference, but they don't return one of its arguments, +which can fool the warning. Such functions can be excluded from the warning +by wrapping them in a @code{#pragma}: + +@smallexample +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdangling-reference" +const T& foo (const T&) @{ @dots{} @} +#pragma GCC diagnostic pop +@end smallexample + +This warning is enabled by @option{-Wall}. + @item -Wdelete-non-virtual-dtor @r{(C++ and Objective-C++ only)} @opindex Wdelete-non-virtual-dtor @opindex Wno-delete-non-virtual-dtor |