libstdc++: Implement LWG 4027 change to possibly-const-range [PR118083]

Message ID 20250204220634.2710707-1-ppalka@redhat.com
State New
Headers
Series libstdc++: Implement LWG 4027 change to possibly-const-range [PR118083] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Test passed

Commit Message

Patrick Palka Feb. 4, 2025, 10:06 p.m. UTC
  Tested on x86_64-pc-linux-gnu, does this look OK for trunk and perhaps
14 (after a long while)?

-- >8 --

LWG 4027 effectively makes the const range access CPOs ranges::cfoo behave
more consistently across C++23 and C++20 (pre-P2278R4) and more
consistently with the std::cfoo range accessors, as the below testcase
adjustments demonstrate (which mostly consist of reverting workarounds
added by r14-3771-gf12e26f3496275 and r13-7186-g0d94c6df183375).

In passing fix PR118083 which reports that the input_range constraint on
possibly-const-range is missing in our implementation.  A consequence of
this is that the const range access CPOs now consistently require the
type to be a range, and so in some our of tests we need to introduce
otherwise unused begin/end members.

	PR libstdc++/118083

libstdc++-v3/ChangeLog:

	* include/bits/ranges_base.h
	(ranges::__access::__possibly_const_range): Adjust logic as per
	LWG 4027.  Add missing input_range constraint.
	* testsuite/std/ranges/access/cdata.cc: Adjust, simplify and
	consolidate some tests after the above.
	* 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/join.cc: Likewise.
	* testsuite/std/ranges/adaptors/take_while.cc: Likewise.
	* testsuite/std/ranges/adaptors/transform.cc: Likewise.
---
 libstdc++-v3/include/bits/ranges_base.h       |  4 +-
 .../testsuite/std/ranges/access/cdata.cc      | 21 ++++-----
 .../testsuite/std/ranges/access/cend.cc       | 30 ++-----------
 .../testsuite/std/ranges/access/crbegin.cc    | 43 +++++--------------
 .../testsuite/std/ranges/access/crend.cc      | 20 +++++----
 .../testsuite/std/ranges/adaptors/join.cc     |  8 ++--
 .../std/ranges/adaptors/take_while.cc         |  2 -
 .../std/ranges/adaptors/transform.cc          |  4 --
 8 files changed, 42 insertions(+), 90 deletions(-)
  

Comments

Patrick Palka Feb. 4, 2025, 10:22 p.m. UTC | #1
On Tue, 4 Feb 2025, Patrick Palka wrote:

> Tested on x86_64-pc-linux-gnu, does this look OK for trunk and perhaps
> 14 (after a long while)?
> 
> -- >8 --
> 
> LWG 4027 effectively makes the const range access CPOs ranges::cfoo behave
> more consistently across C++23 and C++20 (pre-P2278R4) and more
> consistently with the std::cfoo range accessors, as the below testcase
> adjustments demonstrate (which mostly consist of reverting workarounds
> added by r14-3771-gf12e26f3496275 and r13-7186-g0d94c6df183375).
> 
> In passing fix PR118083 which reports that the input_range constraint on
> possibly-const-range is missing in our implementation.  A consequence of
> this is that the const range access CPOs now consistently require the
> type to be a range, and so in some our of tests we need to introduce
> otherwise unused begin/end members.

... now with the LWG 4027 testcases added (to cbegin.cc, so that it runs
in both C++20 and C++23 mode):

-- >8 --

Subject: [PATCH] libstdc++: Implement LWG 4027 change to possibly-const-range
 [PR118083]

	PR libstdc++/118083

libstdc++-v3/ChangeLog:

	* include/bits/ranges_base.h
	(ranges::__access::__possibly_const_range): Adjust logic as per
	LWG 4027.  Add missing input_range constraint.
	* testsuite/std/ranges/access/cbegin.cc (test05): Verify LWG
	4027 testcases.
	* testsuite/std/ranges/access/cdata.cc: Adjust, simplify and
	consolidate some tests after the above.
	* 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/join.cc: Likewise.
	* testsuite/std/ranges/adaptors/take_while.cc: Likewise.
	* testsuite/std/ranges/adaptors/transform.cc: Likewise.
---
 libstdc++-v3/include/bits/ranges_base.h       |  4 +-
 .../testsuite/std/ranges/access/cbegin.cc     | 17 ++++++++
 .../testsuite/std/ranges/access/cdata.cc      | 21 ++++-----
 .../testsuite/std/ranges/access/cend.cc       | 30 ++-----------
 .../testsuite/std/ranges/access/crbegin.cc    | 43 +++++--------------
 .../testsuite/std/ranges/access/crend.cc      | 20 +++++----
 .../testsuite/std/ranges/adaptors/join.cc     |  8 ++--
 .../std/ranges/adaptors/take_while.cc         |  2 -
 .../std/ranges/adaptors/transform.cc          |  4 --
 9 files changed, 59 insertions(+), 90 deletions(-)

diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
index 4dcfbf66d51..28fe64a9e9d 100644
--- a/libstdc++-v3/include/bits/ranges_base.h
+++ b/libstdc++-v3/include/bits/ranges_base.h
@@ -642,11 +642,11 @@ namespace ranges
   namespace __access
   {
 #if __glibcxx_ranges_as_const // >= C++23
-    template<typename _Range>
+    template<input_range _Range>
       constexpr auto&
       __possibly_const_range(_Range& __r) noexcept
       {
-	if constexpr (constant_range<const _Range> && !constant_range<_Range>)
+	if constexpr (input_range<const _Range>)
 	  return const_cast<const _Range&>(__r);
 	else
 	  return __r;
diff --git a/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc b/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc
index 5423e782428..66b28f41877 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc
@@ -116,10 +116,27 @@ test04()
   VERIFY(std::ranges::cbegin(std::move(c)) == std::ranges::begin(c));
 }
 
+void
+test05()
+{
+  // LWG 4027 - possibly-const-range should prefer returning const R&
+  auto r = std::views::single(0)
+    | std::views::transform([](int) { return 0; });
+  using C1 = decltype(std::ranges::cbegin(r));
+  using C1 = decltype(std::cbegin(r));
+
+  [] (auto x) {
+    auto r = std::views::single(x) | std::views::lazy_split(0);
+    static_assert( ! requires { (*std::ranges::cbegin(r)).front() = 42; });
+    static_assert( ! requires { (*std::cbegin(r)).front() = 42; });
+  }(0);
+}
+
 int
 main()
 {
   test01();
   test03();
   test04();
+  test05();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
index 62c347be43d..f474ab7ec99 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
@@ -34,20 +34,21 @@ test01()
   {
     int i = 0;
     int j = 0;
+
+#if __cpp_lib_ranges_as_const
+    // These overloads mean that range<R> and range<const R> are satisfied.
+    const int* begin() const { throw; }
+    const int* end() const { throw; }
+#endif
+
     int* data() { return &j; }
     const R* data() const noexcept { return nullptr; }
   };
   static_assert( has_cdata<R&> );
   static_assert( has_cdata<const R&> );
   R 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)) );
@@ -58,11 +59,11 @@ test01()
 
   struct R2
   {
+#if __cpp_lib_ranges_as_const
     // These overloads mean that range<R2> and range<const R2> are satisfied.
-    int* begin();
-    int* end();
-    const int* begin() const;
-    const int* end() const;
+    const int* begin() const { throw; }
+    const int* end() const { throw; }
+#endif
 
     int i = 0;
     int j = 0;
diff --git a/libstdc++-v3/testsuite/std/ranges/access/cend.cc b/libstdc++-v3/testsuite/std/ranges/access/cend.cc
index 6194fe7d866..6903c4558a1 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/cend.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/cend.cc
@@ -52,15 +52,6 @@ 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;
@@ -79,26 +70,11 @@ 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};
-#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) );
@@ -107,7 +83,7 @@ test03()
 struct RR
 {
   short s = 0;
-  long l = 0;
+  short l = 0;
   int a[4] = { 0, 1, 2, 3 };
 
   const void* begin() const; // return type not an iterator
@@ -115,8 +91,8 @@ struct RR
   friend int* end(RR&) { throw 1; }
   short* end() noexcept { return &s; }
 
-  friend const long* begin(const RR&) noexcept;
-  const long* end() const { return &l; }
+  friend const short* begin(const RR&) noexcept;
+  const short* end() const { return &l; }
 
   friend int* begin(RR&&) noexcept;
   friend int* end(RR&& r) { return r.a + 1; }
diff --git a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
index 9a07f0b3874..c283ee4e33c 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
@@ -28,6 +28,11 @@ struct R1
   int i = 0;
   int j = 0;
 
+#if __cpp_lib_ranges_as_const
+  const int *begin() const;
+  const int *end() const;
+#endif
+
   const int* rbegin() const { return &i; }
   friend const int* rbegin(const R1&& r) { return &r.j; }
 };
@@ -36,6 +41,11 @@ struct R1V // view on an R1
 {
   R1& r;
 
+#if __cpp_lib_ranges_as_const
+  const int *begin() const;
+  const int *end() const;
+#endif
+
   friend const long* rbegin(R1V&) { return nullptr; }
   friend const int* rbegin(const R1V& rv) noexcept { return rv.r.rbegin(); }
 };
@@ -43,26 +53,6 @@ struct R1V // view on an R1
 // 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()
 {
@@ -72,21 +62,8 @@ test01()
   VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) );
 
   R1V v{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) );
diff --git a/libstdc++-v3/testsuite/std/ranges/access/crend.cc b/libstdc++-v3/testsuite/std/ranges/access/crend.cc
index 6f7dce28200..d4530e530e1 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/crend.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/crend.cc
@@ -28,6 +28,11 @@ struct R1
   int i = 0;
   int j = 0;
 
+#if __cpp_lib_ranges_as_const
+  const int *begin() const;
+  const int *end() const;
+#endif
+
   constexpr const int* rbegin() const { return &i; }
   constexpr const int* rend() const { return &i + 1; }
   friend constexpr const int* rbegin(const R1&& r) { return &r.j; }
@@ -78,6 +83,11 @@ struct R3
 {
   int i = 0;
 
+#if __cpp_lib_ranges_as_const
+  const int *begin() const;
+  const int *end() const;
+#endif
+
   const int* rbegin() const noexcept { return &i + 1; }
   const long* rend() const noexcept { return nullptr; } // not a sentinel for rbegin()
 
@@ -89,9 +99,11 @@ struct R4
 {
   int i = 0;
 
+#if __cpp_lib_ranges_as_const
   // These members mean that range<R4> and range<const R4> are satisfied.
   const short* begin() const { return 0; }
   const short* end() const { return 0; }
+#endif
 
   const int* rbegin() const noexcept { return &i + 1; }
   const long* rend() const noexcept { return nullptr; } // not a sentinel for rbegin()
@@ -105,16 +117,8 @@ 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)) );
 
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
index 9e1c526b4ca..2861115c22a 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
@@ -113,15 +113,15 @@ test06()
 
   // Verify that _Iterator<false> is implicitly convertible to _Iterator<true>.
   static_assert(!std::same_as<decltype(ranges::begin(v)),
-			      decltype(std::as_const(v).begin())>);
-  auto a = std::as_const(v).begin();
+			      decltype(ranges::cbegin(v))>);
+  auto a = std::cbegin(v);
   a = ranges::begin(v);
 
   // 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(std::as_const(v).end())>);
-  auto b = std::as_const(v).end();
+			      decltype(ranges::cend(v))>);
+  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 38757d27d48..f09919bedc4 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
@@ -63,10 +63,8 @@ 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 934d2f65dcf..1788db1ce8d 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
@@ -107,20 +107,16 @@ 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);
 }
  

Patch

diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
index 4dcfbf66d51..28fe64a9e9d 100644
--- a/libstdc++-v3/include/bits/ranges_base.h
+++ b/libstdc++-v3/include/bits/ranges_base.h
@@ -642,11 +642,11 @@  namespace ranges
   namespace __access
   {
 #if __glibcxx_ranges_as_const // >= C++23
-    template<typename _Range>
+    template<input_range _Range>
       constexpr auto&
       __possibly_const_range(_Range& __r) noexcept
       {
-	if constexpr (constant_range<const _Range> && !constant_range<_Range>)
+	if constexpr (input_range<const _Range>)
 	  return const_cast<const _Range&>(__r);
 	else
 	  return __r;
diff --git a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
index 62c347be43d..f474ab7ec99 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
@@ -34,20 +34,21 @@  test01()
   {
     int i = 0;
     int j = 0;
+
+#if __cpp_lib_ranges_as_const
+    // These overloads mean that range<R> and range<const R> are satisfied.
+    const int* begin() const { throw; }
+    const int* end() const { throw; }
+#endif
+
     int* data() { return &j; }
     const R* data() const noexcept { return nullptr; }
   };
   static_assert( has_cdata<R&> );
   static_assert( has_cdata<const R&> );
   R 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)) );
@@ -58,11 +59,11 @@  test01()
 
   struct R2
   {
+#if __cpp_lib_ranges_as_const
     // These overloads mean that range<R2> and range<const R2> are satisfied.
-    int* begin();
-    int* end();
-    const int* begin() const;
-    const int* end() const;
+    const int* begin() const { throw; }
+    const int* end() const { throw; }
+#endif
 
     int i = 0;
     int j = 0;
diff --git a/libstdc++-v3/testsuite/std/ranges/access/cend.cc b/libstdc++-v3/testsuite/std/ranges/access/cend.cc
index 6194fe7d866..6903c4558a1 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/cend.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/cend.cc
@@ -52,15 +52,6 @@  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;
@@ -79,26 +70,11 @@  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};
-#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) );
@@ -107,7 +83,7 @@  test03()
 struct RR
 {
   short s = 0;
-  long l = 0;
+  short l = 0;
   int a[4] = { 0, 1, 2, 3 };
 
   const void* begin() const; // return type not an iterator
@@ -115,8 +91,8 @@  struct RR
   friend int* end(RR&) { throw 1; }
   short* end() noexcept { return &s; }
 
-  friend const long* begin(const RR&) noexcept;
-  const long* end() const { return &l; }
+  friend const short* begin(const RR&) noexcept;
+  const short* end() const { return &l; }
 
   friend int* begin(RR&&) noexcept;
   friend int* end(RR&& r) { return r.a + 1; }
diff --git a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
index 9a07f0b3874..c283ee4e33c 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
@@ -28,6 +28,11 @@  struct R1
   int i = 0;
   int j = 0;
 
+#if __cpp_lib_ranges_as_const
+  const int *begin() const;
+  const int *end() const;
+#endif
+
   const int* rbegin() const { return &i; }
   friend const int* rbegin(const R1&& r) { return &r.j; }
 };
@@ -36,6 +41,11 @@  struct R1V // view on an R1
 {
   R1& r;
 
+#if __cpp_lib_ranges_as_const
+  const int *begin() const;
+  const int *end() const;
+#endif
+
   friend const long* rbegin(R1V&) { return nullptr; }
   friend const int* rbegin(const R1V& rv) noexcept { return rv.r.rbegin(); }
 };
@@ -43,26 +53,6 @@  struct R1V // view on an R1
 // 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()
 {
@@ -72,21 +62,8 @@  test01()
   VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) );
 
   R1V v{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) );
diff --git a/libstdc++-v3/testsuite/std/ranges/access/crend.cc b/libstdc++-v3/testsuite/std/ranges/access/crend.cc
index 6f7dce28200..d4530e530e1 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/crend.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/crend.cc
@@ -28,6 +28,11 @@  struct R1
   int i = 0;
   int j = 0;
 
+#if __cpp_lib_ranges_as_const
+  const int *begin() const;
+  const int *end() const;
+#endif
+
   constexpr const int* rbegin() const { return &i; }
   constexpr const int* rend() const { return &i + 1; }
   friend constexpr const int* rbegin(const R1&& r) { return &r.j; }
@@ -78,6 +83,11 @@  struct R3
 {
   int i = 0;
 
+#if __cpp_lib_ranges_as_const
+  const int *begin() const;
+  const int *end() const;
+#endif
+
   const int* rbegin() const noexcept { return &i + 1; }
   const long* rend() const noexcept { return nullptr; } // not a sentinel for rbegin()
 
@@ -89,9 +99,11 @@  struct R4
 {
   int i = 0;
 
+#if __cpp_lib_ranges_as_const
   // These members mean that range<R4> and range<const R4> are satisfied.
   const short* begin() const { return 0; }
   const short* end() const { return 0; }
+#endif
 
   const int* rbegin() const noexcept { return &i + 1; }
   const long* rend() const noexcept { return nullptr; } // not a sentinel for rbegin()
@@ -105,16 +117,8 @@  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)) );
 
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
index 9e1c526b4ca..2861115c22a 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
@@ -113,15 +113,15 @@  test06()
 
   // Verify that _Iterator<false> is implicitly convertible to _Iterator<true>.
   static_assert(!std::same_as<decltype(ranges::begin(v)),
-			      decltype(std::as_const(v).begin())>);
-  auto a = std::as_const(v).begin();
+			      decltype(ranges::cbegin(v))>);
+  auto a = std::cbegin(v);
   a = ranges::begin(v);
 
   // 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(std::as_const(v).end())>);
-  auto b = std::as_const(v).end();
+			      decltype(ranges::cend(v))>);
+  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 38757d27d48..f09919bedc4 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
@@ -63,10 +63,8 @@  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 934d2f65dcf..1788db1ce8d 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
@@ -107,20 +107,16 @@  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);
 }