public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r14-3771] libstdc++: Fix <ranges> tests that fail in C++23
@ 2023-09-07  7:11 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2023-09-07  7:11 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:f12e26f3496275a432c6f6a326598db3441a9725

commit r14-3771-gf12e26f3496275a432c6f6a326598db3441a9725
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Sep 6 14:13:18 2023 +0100

    libstdc++: Fix <ranges> tests that fail in C++23
    
    The tests for the std::ranges access CPOs (ranges::begin etc) use
    pathological types with ridiculous overload sets for begin/end/data
    members, to exercise all the corner cases in the specification.
    
    Since P2278R4 "cbegin should always return a constant iterator" was
    implemented for C++23 mode, some of the range access CPOs now require
    the argument to satisfy the range concept, which was not previously
    required. The behaviour of the CPO also changes for corner cases where
    the type is a range R for which constant_range<R> is satisfied in
    addition to constant_range<const R> (meaning there's no need to wrap its
    iterators in const_iterator). Adjust the expected results for those
    pathological types that changed meaning in C++23, and add some new types
    to verify other corner cases.
    
    Some other range adaptor tests fail for C++20 because they assert that
    ranges::end and ranges::cend return different types, which is not true
    when the type satisfies constant_range.
    
    This fixes the tests to PASS for both C++20 and C++23 (and later).
    
    libstdc++-v3/ChangeLog:
    
            * testsuite/std/ranges/access/cbegin.cc: Adjust for C++23
            compatibility.
            * testsuite/std/ranges/access/cdata.cc: Likewise.
            * testsuite/std/ranges/access/cend.cc: Likewise.
            * testsuite/std/ranges/access/crbegin.cc: Likewise.
            * testsuite/std/ranges/access/crend.cc: Likewise.
            * testsuite/std/ranges/adaptors/take.cc: Likewise.
            * testsuite/std/ranges/adaptors/take_while.cc: Likewise.
            * testsuite/std/ranges/adaptors/transform.cc: Likewise.

Diff:
---
 libstdc++-v3/testsuite/std/ranges/access/cbegin.cc | 13 +++++++
 libstdc++-v3/testsuite/std/ranges/access/cdata.cc  | 38 +++++++++++++++++++-
 libstdc++-v3/testsuite/std/ranges/access/cend.cc   | 29 ++++++++++++++--
 .../testsuite/std/ranges/access/crbegin.cc         | 40 ++++++++++++++++++++--
 libstdc++-v3/testsuite/std/ranges/access/crend.cc  | 33 ++++++++++++++++--
 libstdc++-v3/testsuite/std/ranges/adaptors/take.cc |  2 ++
 .../testsuite/std/ranges/adaptors/take_while.cc    |  2 ++
 .../testsuite/std/ranges/adaptors/transform.cc     |  4 +++
 8 files changed, 154 insertions(+), 7 deletions(-)

diff --git a/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc b/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc
index 10376040c16e..3667b0d021fb 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc
@@ -48,6 +48,10 @@ struct R
   friend int* begin(R&&); // this function is not defined
   friend const int* begin(const R& r) noexcept { return r.a + 2; }
   friend const int* begin(const R&&); // this function is not defined
+
+#if __cpp_lib_ranges_as_const
+  friend const int* end(const R&) noexcept; // C++23 requires this.
+#endif
 };
 
 struct RV // view on an R
@@ -56,6 +60,10 @@ struct RV // view on an R
 
   friend int* begin(RV&); // this function is not defined
   friend const int* begin(const RV& rv) noexcept { return begin(std::as_const(rv.r)); }
+
+#if __cpp_lib_ranges_as_const
+  friend const int* end(const RV&) noexcept; // C++23 requires this.
+#endif
 };
 
 // Allow ranges::begin to work with RV&&
@@ -88,6 +96,11 @@ struct RR
   friend int* begin(RR&& r) { return r.a + 1; }
   friend const int* begin(const RR& r) { return r.a + 2; }
   friend const int* begin(const RR&& r) noexcept { return r.a + 3; }
+
+#if __cpp_lib_ranges_as_const
+  short* end() noexcept { return &s + 1; }   // C++23 requires this.
+  const long* end() const { return &l + 1; } // C++23 requires this.
+#endif
 };
 
 // N.B. this is a lie, cbegin on an RR rvalue will return a dangling pointer.
diff --git a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
index f0f45eeb6bf2..d69b04c8f744 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
@@ -41,15 +41,43 @@ test01()
   static_assert( has_cdata<R&> );
   static_assert( has_cdata<const R&> );
   R r;
-  const R& c = r;
+#if ! __cpp_lib_ranges_as_const
   VERIFY( std::ranges::cdata(r) == (R*)nullptr );
   static_assert( noexcept(std::ranges::cdata(r)) );
+#else
+  // constant_range<const R> is not satisfied, so cdata(r) == data(r).
+  VERIFY( std::ranges::cdata(r) == &r.j );
+  static_assert( ! noexcept(std::ranges::cdata(r)) );
+#endif
+  const R& c = r;
   VERIFY( std::ranges::cdata(c) == (R*)nullptr );
   static_assert( noexcept(std::ranges::cdata(c)) );
 
   // not lvalues and not borrowed ranges
   static_assert( !has_cdata<R> );
   static_assert( !has_cdata<const R> );
+
+  struct R2
+  {
+    // These overloads mean that range<R2> and range<const R2> are satisfied.
+    int* begin();
+    int* end();
+    const int* begin() const;
+    const int* end() const;
+
+    int i = 0;
+    int j = 0;
+    int* data() { return &j; }
+    const R2* data() const noexcept { return nullptr; }
+  };
+  static_assert( has_cdata<R2&> );
+  static_assert( has_cdata<const R2&> );
+  R2 r2;
+  VERIFY( std::ranges::cdata(r2) == (R2*)nullptr );
+  static_assert( noexcept(std::ranges::cdata(r2)) );
+  const R2& c2 = r2;
+  VERIFY( std::ranges::cdata(c2) == (R2*)nullptr );
+  static_assert( noexcept(std::ranges::cdata(c2)) );
 }
 
 void
@@ -71,6 +99,14 @@ struct R3
   friend long* begin(R3&& r); // not defined
   friend const long* begin(const R3& r) { return &r.l; }
   friend const short* begin(const R3&&); // not defined
+
+#if __cpp_lib_ranges_as_const
+  // C++23 needs these so that range<R3> is satisfied and so that
+  // possibly-const-range<R3> is not the same type as R3.
+  friend long* begin(R3&);
+  friend long* end(R3&);
+  friend const long* end(const R3& r);
+#endif
 };
 
 template<> constexpr bool std::ranges::enable_borrowed_range<R3> = true;
diff --git a/libstdc++-v3/testsuite/std/ranges/access/cend.cc b/libstdc++-v3/testsuite/std/ranges/access/cend.cc
index f47bd9d91358..3726ebbf1185 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/cend.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/cend.cc
@@ -42,7 +42,7 @@ struct R
   int a[4] = { 0, 1, 2, 3 };
 
   const int* begin() const { return nullptr; }
-  friend const int* begin(const R&& r) noexcept { return nullptr; }
+  friend const int* begin(const R&&) noexcept { return nullptr; }
 
   // Should be ignored because it doesn't return a sentinel for int*
   const long* end() const { return nullptr; }
@@ -53,6 +53,15 @@ struct R
   friend const int* end(const R&& r) noexcept { return r.a + 3; }
 };
 
+#if __cpp_lib_ranges_as_const
+struct R2 : R
+{
+  // This overload means constant_range<const R2> will be satisfied:
+  friend const int* begin(const R2&) noexcept;
+  friend const int* end(const R2& r2) noexcept { return r2.a + 2; }
+};
+#endif
+
 struct RV // view on an R
 {
   R& r;
@@ -71,12 +80,28 @@ test03()
 {
   R r;
   const R& c = r;
+#if ! __cpp_lib_ranges_as_const
   VERIFY( std::ranges::cend(r) == std::ranges::end(c) );
+#else
+  // constant_range<const R> is not satisfied, so cend(r) == end(r) instead.
+  VERIFY( std::ranges::cend(r) == std::ranges::end(r) );
+  R2 r2;
+  const R& c2 = r2;
+  // But constant_range<const R2> is satisfied, so cend(r2) == end(c2).
+  VERIFY( std::ranges::cend(r2) == std::ranges::end(c2) );
+  VERIFY( std::ranges::cend(r2) == std::ranges::end((const R&)c2) );
+#endif
   VERIFY( std::ranges::cend(c) == std::ranges::end(c) );
 
   RV v{r};
-  const RV cv{r};
+#if ! __cpp_lib_ranges_as_const
   VERIFY( std::ranges::cend(std::move(v)) == std::ranges::end(c) );
+#else
+  // constant_range<RV> is already satisfied, so cend(v) == end(r) instead.
+  VERIFY( std::ranges::cend(std::move(v)) == std::ranges::end(r) );
+#endif
+
+  const RV cv{r};
   VERIFY( std::ranges::cend(std::move(cv)) == std::ranges::end(c) );
 }
 
diff --git a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
index db58d9577d90..95b4607fdf1b 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
@@ -37,13 +37,33 @@ struct R1V // view on an R1
 {
   R1& r;
 
-  friend const long* rbegin(R1V&); // this is not defined
+  friend const long* rbegin(R1V&) { return nullptr; }
   friend const int* rbegin(const R1V& rv) noexcept { return rv.r.rbegin(); }
 };
 
 // Allow ranges::end to work with R1V&&
 template<> constexpr bool std::ranges::enable_borrowed_range<R1V> = true;
 
+#if __cpp_lib_ranges_as_const
+struct R1VC // view on an R1
+{
+  R1& r;
+
+  friend const long* rbegin(R1VC&); // this is not defined
+  friend const int* rbegin(const R1VC& rv) noexcept { return rv.r.rbegin(); }
+
+  // The following ensure that the following are satisfied:
+  // constant_range<const R1VC> && ! constant_range<R1VC>
+  friend int* begin(R1VC&);
+  friend int* end(R1VC&);
+  friend const int* begin(const R1VC&);
+  friend const int* end(const R1VC&);
+};
+
+// Allow ranges::end to work with R1VC&&
+template<> constexpr bool std::ranges::enable_borrowed_range<R1VC> = true;
+#endif
+
 void
 test01()
 {
@@ -53,8 +73,24 @@ test01()
   VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) );
 
   R1V v{r};
-  const R1V cv{r};
+#if ! __cpp_lib_ranges_as_const
+  VERIFY( std::ranges::crbegin(v) == std::ranges::rbegin(c) );
   VERIFY( std::ranges::crbegin(std::move(v)) == std::ranges::rbegin(c) );
+#else
+  // constant_range<const R1V> is not satisfied, so crbegin(v) == rbegin(v).
+  VERIFY( std::ranges::crbegin(v) == (long*)nullptr );
+  VERIFY( std::ranges::crbegin(std::move(v)) == (long*)nullptr );
+  R1VC v2{r};
+  // But constant_range<const R1VC> is satisfied:
+  VERIFY( std::ranges::crbegin(v2) == std::ranges::rbegin(c) );
+  VERIFY( std::ranges::crbegin(std::move(v2)) == std::ranges::rbegin(c) );
+  const R1VC cv2{r};
+  VERIFY( std::ranges::crbegin(cv2) == std::ranges::rbegin(c) );
+  VERIFY( std::ranges::crbegin(std::move(cv2)) == std::ranges::rbegin(c) );
+#endif
+
+  const R1V cv{r};
+  VERIFY( std::ranges::crbegin(cv) == std::ranges::rbegin(c) );
   VERIFY( std::ranges::crbegin(std::move(cv)) == std::ranges::rbegin(c) );
 }
 
diff --git a/libstdc++-v3/testsuite/std/ranges/access/crend.cc b/libstdc++-v3/testsuite/std/ranges/access/crend.cc
index a7e033fc3171..2e4c0f3197a0 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/crend.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/crend.cc
@@ -86,18 +86,47 @@ struct R3
   friend const int* rend(const R3& r) { return &r.i; }
 };
 
-// N.B. this is a lie, rend on an R3 rvalue will return a dangling pointer.
-template<> constexpr bool std::ranges::enable_borrowed_range<R3> = true;
+struct R4
+{
+  int i = 0;
+
+  // These members mean that range<R4> and range<const R4> are satisfied.
+  const short* begin() const { return 0; }
+  const short* end() const { return 0; }
+
+  const int* rbegin() const noexcept { return &i + 1; }
+  const long* rend() const noexcept { return nullptr; } // not a sentinel for rbegin()
+
+  friend const long* rbegin(const R4&) noexcept { return nullptr; }
+  friend const int* rend(const R4& r) { return &r.i; }
+};
 
 void
 test03()
 {
   R3 r;
   const R3& c = r;
+#if ! __cpp_lib_ranges_as_const
   VERIFY( std::ranges::crend(r) == std::ranges::rend(c) );
   static_assert( !noexcept(std::ranges::crend(r)) );
+#else
+  // constant_range<const R3> is not satisfied, so crend(r) is equivalent
+  // to const_sentinel{rend(r)}, which is ill-formed because range<R3>
+  // is not satisfied.
+  static_assert( not std::ranges::range<R3> );
+  static_assert( not std::ranges::range<const R3> );
+#endif
   VERIFY( std::ranges::crend(c) == std::ranges::rend(c) );
   static_assert( !noexcept(std::ranges::crend(c)) );
+
+  R4 r4;
+  const R4& c4 = r4;
+  auto b = std::ranges::rbegin(r4);
+  auto s0 = std::ranges::rend(r4);
+  static_assert( std::ranges::__access::__adl_rend<R4&> );
+  auto s = std::ranges::crend(r4);
+  auto s2 = std::ranges::rend(c4);
+  // VERIFY( std::ranges::crend(r4) == std::ranges::rend(c4) );
 }
 
 void
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
index 290b5c2bba59..a1f6068bc09b 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
@@ -94,8 +94,10 @@ test05()
 
   // Verify that _Sentinel<false> is implicitly convertible to _Sentinel<true>.
   static_assert(!ranges::common_range<decltype(v)>);
+#if ! __cpp_lib_ranges_as_const
   static_assert(!std::same_as<decltype(ranges::end(v)),
 			      decltype(ranges::cend(v))>);
+#endif
   auto b = ranges::cend(v);
   b = ranges::end(v);
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
index 46060771483d..098091edc8b6 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
@@ -64,8 +64,10 @@ test03()
 
   // Verify that _Sentinel<false> is implicitly convertible to _Sentinel<true>.
   static_assert(!ranges::common_range<decltype(v)>);
+#if ! __cpp_lib_ranges_as_const
   static_assert(!std::same_as<decltype(ranges::end(v)),
 			      decltype(ranges::cend(v))>);
+#endif
   auto b = ranges::cend(v);
   b = ranges::end(v);
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
index 9d52cb01dadb..d9801d5abb39 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
@@ -108,16 +108,20 @@ test05()
   auto r = ranges::subrange{i, std::default_sentinel};
   auto v = r | views::transform(std::negate{});
 
+#if ! __cpp_lib_ranges_as_const
   // Verify that _Iterator<false> is implicitly convertible to _Iterator<true>.
   static_assert(!std::same_as<decltype(ranges::begin(v)),
 			      decltype(ranges::cbegin(v))>);
+#endif
   auto a = ranges::cbegin(v);
   a = ranges::begin(v);
 
+#if ! __cpp_lib_ranges_as_const
   // Verify that _Sentinel<false> is implicitly convertible to _Sentinel<true>.
   static_assert(!ranges::common_range<decltype(v)>);
   static_assert(!std::same_as<decltype(ranges::end(v)),
 			      decltype(ranges::cend(v))>);
+#endif
   auto b = ranges::cend(v);
   b = ranges::end(v);
 }

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2023-09-07  7:11 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-09-07  7:11 [gcc r14-3771] libstdc++: Fix <ranges> tests that fail in C++23 Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).