On Thu, Jan 25, 2024 at 10:13:10PM -0500, Jason Merrill wrote:
> On 1/25/24 20:36, Marek Polacek wrote:
> > Better version:
> >
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> >
> > -- >8 --
> > Real-world experience shows that -Wdangling-reference triggers for
> > user-defined std::span-like classes a lot. We can easily avoid that
> > by considering classes like
> >
> > template<typename T>
> > struct Span {
> > T* data_;
> > std::size len_;
> > };
> >
> > to be std::span-like, and not warning for them. Unlike the previous
> > patch, this one considers a non-union class template that has a pointer
> > data member and a trivial destructor as std::span-like.
> >
> > PR c++/110358
> > PR c++/109640
> >
> > gcc/cp/ChangeLog:
> >
> > * call.cc (reference_like_class_p): Don't warn for std::span-like
> > classes.
> >
> > gcc/ChangeLog:
> >
> > * doc/invoke.texi: Update -Wdangling-reference description.
> >
> > gcc/testsuite/ChangeLog:
> >
> > * g++.dg/warn/Wdangling-reference18.C: New test.
> > * g++.dg/warn/Wdangling-reference19.C: New test.
> > * g++.dg/warn/Wdangling-reference20.C: New test.
> > ---
> > gcc/cp/call.cc | 18 ++++++++
> > gcc/doc/invoke.texi | 14 +++++++
> > .../g++.dg/warn/Wdangling-reference18.C | 24 +++++++++++
> > .../g++.dg/warn/Wdangling-reference19.C | 25 +++++++++++
> > .../g++.dg/warn/Wdangling-reference20.C | 42 +++++++++++++++++++
> > 5 files changed, 123 insertions(+)
> > create mode 100644 gcc/testsuite/g++.dg/warn/Wdangling-reference18.C
> > create mode 100644 gcc/testsuite/g++.dg/warn/Wdangling-reference19.C
> > create mode 100644 gcc/testsuite/g++.dg/warn/Wdangling-reference20.C
> >
> > diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> > index 9de0d77c423..afd3e1ff024 100644
> > --- a/gcc/cp/call.cc
> > +++ b/gcc/cp/call.cc
> > @@ -14082,6 +14082,24 @@ reference_like_class_p (tree ctype)
> > return true;
> > }
> > + /* Avoid warning if CTYPE looks like std::span: it's a class template,
> > + has a T* member, and a trivial destructor. For example,
> > +
> > + template<typename T>
> > + struct Span {
> > + T* data_;
> > + std::size len_;
> > + };
> > +
> > + is considered std::span-like. */
> > + if (NON_UNION_CLASS_TYPE_P (ctype)
> > + && CLASSTYPE_TEMPLATE_INSTANTIATION (ctype)
> > + && TYPE_HAS_TRIVIAL_DESTRUCTOR (ctype))
> > + for (tree field = next_aggregate_field (TYPE_FIELDS (ctype));
> > + field; field = next_aggregate_field (DECL_CHAIN (field)))
> > + if (TYPE_PTR_P (TREE_TYPE (field)))
> > + 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,
> > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > index 6ec56493e59..e0ff18a86f5 100644
> > --- a/gcc/doc/invoke.texi
> > +++ b/gcc/doc/invoke.texi
> > @@ -3916,6 +3916,20 @@ where @code{std::minmax} returns
> > @code{std::pair<const int&, const int&>}, and
> > both references dangle after the end of the full expression that contains
> > the call to @code{std::minmax}.
> > +The warning does not warn for @code{std::span}-like classes. We consider
> > +classes of the form:
> > +
> > +@smallexample
> > +template<typename T>
> > +struct Span @{
> > + T* data_;
> > + std::size len_;
> > +@};
> > +@end smallexample
> > +
> > +as @code{std::span}-like; that is, the class is a non-union class template
> > +that has a pointer data member and a trivial destructor.
> > +
> > This warning is enabled by @option{-Wall}.
> > @opindex Wdelete-non-virtual-dtor
> > diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference18.C
> > b/gcc/testsuite/g++.dg/warn/Wdangling-reference18.C
> > new file mode 100644
> > index 00000000000..e088c177769
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference18.C
> > @@ -0,0 +1,24 @@
> > +// PR c++/110358
> > +// { dg-do compile { target c++11 } }
> > +// { dg-options "-Wdangling-reference" }
> > +// Don't warn for std::span-like classes.
> > +
> > +template <typename T>
> > +struct Span {
> > + T* data_;
> > + int len_;
> > +
> > + [[nodiscard]] constexpr auto operator[](int n) const noexcept -> T& {
> > return data_[n]; }
> > + [[nodiscard]] constexpr auto front() const noexcept -> T& { return
> > data_[0]; }
> > + [[nodiscard]] constexpr auto back() const noexcept -> T& { return
> > data_[len_ - 1]; }
> > +};
> > +
> > +auto get() -> Span<int>;
> > +
> > +auto f() -> int {
> > + int const& a = get().front(); // { dg-bogus "dangling reference" }
> > + int const& b = get().back(); // { dg-bogus "dangling reference" }
> > + int const& c = get()[0]; // { dg-bogus "dangling reference" }
> > +
> > + return a + b + c;
> > +}
> > diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference19.C
> > b/gcc/testsuite/g++.dg/warn/Wdangling-reference19.C
> > new file mode 100644
> > index 00000000000..053467d822f
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference19.C
> > @@ -0,0 +1,25 @@
> > +// PR c++/110358
> > +// { dg-do compile { target c++11 } }
> > +// { dg-options "-Wdangling-reference" }
> > +// Like Wdangling-reference18.C but not actually a span-like class.
> > +
> > +template <typename T>
> > +struct Span {
> > + T* data_;
> > + int len_;
> > + ~Span ();
> > +
> > + [[nodiscard]] constexpr auto operator[](int n) const noexcept -> T& {
> > return data_[n]; }
> > + [[nodiscard]] constexpr auto front() const noexcept -> T& { return
> > data_[0]; }
> > + [[nodiscard]] constexpr auto back() const noexcept -> T& { return
> > data_[len_ - 1]; }
> > +};
> > +
> > +auto get() -> Span<int>;
> > +
> > +auto f() -> int {
> > + int const& a = get().front(); // { dg-warning "dangling reference" }
> > + int const& b = get().back(); // { dg-warning "dangling reference" }
> > + int const& c = get()[0]; // { dg-warning "dangling reference" }
> > +
> > + return a + b + c;
> > +}
> > diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference20.C
> > b/gcc/testsuite/g++.dg/warn/Wdangling-reference20.C
> > new file mode 100644
> > index 00000000000..463c7380283
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference20.C
> > @@ -0,0 +1,42 @@
> > +// PR c++/109640
> > +// { dg-do compile { target c++20 } }
> > +// { dg-options "-Wdangling-reference" }
> > +// Don't warn for std::span-like classes.
> > +
> > +#include <iterator>
> > +#include <span>
> > +
> > +template <typename T>
> > +struct MySpan
> > +{
> > + MySpan(T* data, std::size_t size) :
> > + data_(data),
> > + size_(size)
> > + {}
> > +
> > + T& operator[](std::size_t idx) { return data_[idx]; }
> > +
> > +private:
> > + T* data_;
> > + std::size_t size_;
> > +};
> > +
> > +template <typename T, std::size_t n>
> > +MySpan<T const> make_my_span(T const(&x)[n])
> > +{
> > + return MySpan(std::begin(x), n);
> > +}
> > +
> > +template <typename T, std::size_t n>
> > +std::span<T const> make_span(T const(&x)[n])
> > +{
> > + return std::span(std::begin(x), n);
> > +}
> > +
> > +int main()
> > +{
> > + int x[10]{};
> > + int const& y{make_my_span(x)[0]};
> > + int const& y2{make_span(x)[0]};
>
> Let's also test that we do warn if the argument to make_*span is a
> temporary. OK with that change, assuming it works as expected.
We do warn then, I've added
using T = int[10];
[[maybe_unused]] int const& y3{make_my_span(T{})[0]};
[[maybe_unused]] int const& y4{make_span(T{})[0]};
and get two warnings. I'll push with that adjusted; thanks.
Marek