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.

        PR c++/110358
        PR c++/109640

gcc/cp/ChangeLog:

        * call.cc (span_like_class_p): New.
        (do_warn_dangling_reference): Use it.

gcc/ChangeLog:

        * doc/invoke.texi: Update -Wdangling-reference documentation.

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                                | 38 ++++++++++++++++-
 gcc/doc/invoke.texi                           | 15 +++++++
 .../g++.dg/warn/Wdangling-reference18.C       | 24 +++++++++++
 .../g++.dg/warn/Wdangling-reference19.C       | 25 +++++++++++
 .../g++.dg/warn/Wdangling-reference20.C       | 42 +++++++++++++++++++
 5 files changed, 143 insertions(+), 1 deletion(-)
 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 77f51bacce3..d6bdb3cc9bd 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -14082,6 +14082,40 @@ reference_like_class_p (tree ctype)
   return false;
 }
 
+/* Return true if class TYPE looks like std::span: it's a class template
+   and has a T* member followed by a field of integral type.  For example,
+
+    template<typename T>
+    struct Span {
+      T* data_;
+      std::size len_;
+    };
+
+   is considered std::span-like.  */
+
+static bool
+span_like_class_p (tree type)
+{
+  if (!NON_UNION_CLASS_TYPE_P (type)
+      || !CLASSTYPE_TEMPLATE_INSTANTIATION (type))
+    return false;
+
+  tree args = CLASSTYPE_TI_ARGS (type);
+  if (TREE_VEC_LENGTH (args) != 1)
+    return false;
+
+  tree f = next_aggregate_field (TYPE_FIELDS (type));
+  if (f && TYPE_PTR_P (TREE_TYPE (f)))
+    {
+      f = next_aggregate_field (DECL_CHAIN (f));
+      if (f && INTEGRAL_TYPE_P (TREE_TYPE (f))
+         && !next_aggregate_field (DECL_CHAIN (f)))
+       return true;
+    }
+
+  return false;
+}
+
 /* Helper for maybe_warn_dangling_reference to find a problematic CALL_EXPR
    that initializes the LHS (and at least one of its arguments represents
    a temporary, as outlined in maybe_warn_dangling_reference), or NULL_TREE
@@ -14126,7 +14160,9 @@ do_warn_dangling_reference (tree expr, bool arg_p)
       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))
+      if (!reference_like_class_p (type)
+         && !LAMBDA_TYPE_P (type)
+         && !span_like_class_p (type))
        return expr;
     }
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 278c931b6a3..509779c8fd8 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3914,6 +3914,21 @@ 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 class template that
+has a pointer data member followed by an integral data member, and does
+not have any other data members.
+
 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..3f74ab701c1
--- /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_;
+    T foo_;
+
+    [[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]};
+  (void) y, (void) y2;
+}

base-commit: c596ce03120cc22e141186401c6656009ddebdaa
prerequisite-patch-id: 5929438e96b89b465c26c2fbd5b92d2444d1901d
-- 
2.43.0

Reply via email to