diff options
author | Marek Polacek <polacek@redhat.com> | 2024-01-25 12:08:14 -0500 |
---|---|---|
committer | Marek Polacek <polacek@redhat.com> | 2024-01-30 13:18:32 -0500 |
commit | f2061b2a9641c2228d4e2d86f19532ad7e93d627 (patch) | |
tree | 2abec8e50144538a94b4f0dba4f05d43c57099a8 /gcc | |
parent | 24d5e0bf19f27a89a13f64b4b8750dbde89bdaa0 (diff) | |
download | gcc-f2061b2a9641c2228d4e2d86f19532ad7e93d627.zip gcc-f2061b2a9641c2228d4e2d86f19532ad7e93d627.tar.gz gcc-f2061b2a9641c2228d4e2d86f19532ad7e93d627.tar.bz2 |
c++: avoid -Wdangling-reference for std::span-like classes [PR110358]
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.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cp/call.cc | 18 | ||||
-rw-r--r-- | gcc/doc/invoke.texi | 14 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/warn/Wdangling-reference18.C | 24 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/warn/Wdangling-reference19.C | 25 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/warn/Wdangling-reference20.C | 44 |
5 files changed, 125 insertions, 0 deletions
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 451a189..42cbd0d 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 819a75d..eb931b9 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 0000000..e088c17 --- /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 0000000..053467d --- /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 0000000..84138f0 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference20.C @@ -0,0 +1,44 @@ +// 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]{}; + [[maybe_unused]] int const& y1{make_my_span(x)[0]}; + [[maybe_unused]] int const& y2{make_span(x)[0]}; + using T = int[10]; + [[maybe_unused]] int const& y3{make_my_span(T{})[0]}; // { dg-warning "dangling reference" } + [[maybe_unused]] int const& y4{make_span(T{})[0]}; // { dg-warning "dangling reference" } +} |