Message ID | 20240120021823.81839-1-polacek@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: -Wdangling-reference and lambda false warning [PR109640] | expand |
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
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; +}