[libcxx-commits] [libcxx] [libc++][ranges][enumerate_view] Fix sentinel converting constructor test (PR #217365)

Lucas Mellone via libcxx-commits libcxx-commits at lists.llvm.org
Thu Aug 20 01:54:14 PDT 2026


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/217365

>From a5d6d8feb899d759277c82e318b10d2585b3ae55 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 18 Aug 2026 16:07:44 +0200
Subject: [PATCH 1/6] add: sentinel wrapper and 
 NonSimpleNonCommonConvertibleView

---
 .../sentinel/ctor.convert.pass.cpp            | 54 ++++++++++++++-----
 1 file changed, 42 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
index b3bee42685a9a..f03f1c60b849f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
@@ -29,6 +29,32 @@
 
 #include "../types.h"
 
+template <class T>
+struct convertible_sentinel_wrapper {
+    explicit convertible_sentinel_wrapper() = default;
+    contexpr convertible_sentinel_wrapper(const T& it) : it_(it) {}
+
+    template <class U>
+        requires std::convertible_to<const U&, T>
+    constexpr convertible_sentinel_wrapper(const convertible_sentinel_wrapper<U>& other) : it_(other.it_) {}
+
+    constexpr friend bool operator==(convertible_sentinel_wrapper const& self, const T& other) {
+        return self.it_ == other;
+    }
+    T it_;
+};
+
+struct NonSimpleNonCommonConvertibleView : IntBufferView {
+    using IntBufferView::IntBufferView;
+
+    constexpr int* begin() { return buffer_; }
+    constexpr const int* begin() const { return buffer_; }
+    constexpr convertible_sentinel_wrapper<int*> end() { return convertible_sentinel_wrapper<int*>(buffer_ + size_; }
+    constexpr convertible_sentinel_wrapper<const int*> end() const {
+        return convertible_sentinel_wrapper<const int*>(buffer_ + size_);
+    }
+};
+
 template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
 constexpr void test() {
   using View                   = MinimalView<Iterator, Sentinel>;
@@ -46,18 +72,22 @@ constexpr void test() {
 
   std::array array{0, 1, 2, 3, 84};
   auto view = make_enumerate_view(array.begin(), array.end());
-
-  std::same_as<EnumerateSentinel> decltype(auto) s = view.end();
-  std::same_as<Sentinel> decltype(auto) sResult    = s.base();
-  assert(base(base(sResult)) == std::to_address(base(array.end())));
-
-  // Test assignment
-  EnumerateConstSentinel cs                      = s;
-  std::same_as<Sentinel> decltype(auto) csResult = cs.base();
-  assert(base(base(csResult)) == std::to_address(base(array.end())));
+  
+  {
+    // Assigning a non-const sentinel to a const-sentinel-typed variable invokes
+    // the converting constructor.
+    std::same_as<EnumerateSentinel> decltype(auto) s = view.end();
+    std::same_as<Sentinel> decltype(auto) sResult    = s.base();
+    assert(base(base(sResult)) == std::to_address(base(array.end())));
+
+    // Verify assignment
+    EnumerateConstSentinel cs                      = s;
+    std::same_as<Sentinel> decltype(auto) csResult = cs.base();
+    assert(base(base(csResult)) == std::to_address(base(array.end())));
+  }
 }
 
-constexpr bool tests() {
+constexpr bool test() {
   test<cpp17_input_iterator<int*>>();
   test<cpp20_input_iterator<int*>>();
   test<forward_iterator<int*>>();
@@ -70,8 +100,8 @@ constexpr bool tests() {
 }
 
 int main(int, char**) {
-  tests();
-  static_assert(tests());
+  test();
+  static_assert(test());
 
   return 0;
 }

>From 69d0bd155ceaf64e12fcef7565e5a0f810d35b09 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 19 Aug 2026 01:00:00 +0200
Subject: [PATCH 2/6] wip: add Underlying non-const to const not convertible
 test case

---
 .../sentinel/ctor.convert.pass.cpp            | 60 +++++++++++++------
 1 file changed, 42 insertions(+), 18 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
index f03f1c60b849f..a5d255f75bb29 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
@@ -28,33 +28,55 @@
 #include "test_iterators.h"
 
 #include "../types.h"
+#include "../../range_adaptor_types.h"
 
 template <class T>
 struct convertible_sentinel_wrapper {
-    explicit convertible_sentinel_wrapper() = default;
-    contexpr convertible_sentinel_wrapper(const T& it) : it_(it) {}
+  explicit convertible_sentinel_wrapper() = default;
+  constexpr convertible_sentinel_wrapper(const T& it) : it_(it) {}
 
-    template <class U>
-        requires std::convertible_to<const U&, T>
-    constexpr convertible_sentinel_wrapper(const convertible_sentinel_wrapper<U>& other) : it_(other.it_) {}
+  template <class U>
+    requires std::convertible_to<const U&, T>
+  constexpr convertible_sentinel_wrapper(const convertible_sentinel_wrapper<U>& other) : it_(other.it_) {}
 
-    constexpr friend bool operator==(convertible_sentinel_wrapper const& self, const T& other) {
-        return self.it_ == other;
-    }
-    T it_;
+  constexpr friend bool operator==(convertible_sentinel_wrapper const& self, const T& other) {
+    return self.it_ == other;
+  }
+  T it_;
 };
 
 struct NonSimpleNonCommonConvertibleView : IntBufferView {
-    using IntBufferView::IntBufferView;
-
-    constexpr int* begin() { return buffer_; }
-    constexpr const int* begin() const { return buffer_; }
-    constexpr convertible_sentinel_wrapper<int*> end() { return convertible_sentinel_wrapper<int*>(buffer_ + size_; }
-    constexpr convertible_sentinel_wrapper<const int*> end() const {
-        return convertible_sentinel_wrapper<const int*>(buffer_ + size_);
-    }
+  using IntBufferView::IntBufferView;
+
+  constexpr int* begin() { return buffer_; }
+  constexpr const int* begin() const { return buffer_; }
+  constexpr convertible_sentinel_wrapper<int*> end() { return convertible_sentinel_wrapper<int*>(buffer_ + size_); }
+  constexpr convertible_sentinel_wrapper<const int*> end() const {
+    return convertible_sentinel_wrapper<const int*>(buffer_ + size_);
+  }
 };
 
+// convertible_to<sentinel<false>, sentinel<Const>>
+static_assert(std::convertible_to< //
+              std::ranges::sentinel_t<std::ranges::enumerate_view<NonSimpleNonCommonConvertibleView>>,
+              std::ranges::sentinel_t<std::ranges::enumerate_view<NonSimpleNonCommonConvertibleView> const>>);
+
+constexpr void test_SFINAE() {
+  int buffer[3] = {1, 2, 3};
+
+  // Underlying non-const to const not convertible.
+  {
+    std::ranges::enumerate_view v{NonSimpleNonCommonConvertibleView(buffer)};
+    auto sent1 = v.end();
+    auto sent2 = std::as_const(v).end();
+
+    static_assert(!std::same_as<decltype(sent1), decltype(sent2)>);
+
+    static_assert(!std::is_constructible_v<decltype(sent1), decltype(sent2)>);
+    static_assert(!std::is_constructible_v<decltype(sent2), decltype(sent1)>); // this assert fails
+  }
+}
+
 template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
 constexpr void test() {
   using View                   = MinimalView<Iterator, Sentinel>;
@@ -72,7 +94,7 @@ constexpr void test() {
 
   std::array array{0, 1, 2, 3, 84};
   auto view = make_enumerate_view(array.begin(), array.end());
-  
+
   {
     // Assigning a non-const sentinel to a const-sentinel-typed variable invokes
     // the converting constructor.
@@ -88,6 +110,8 @@ constexpr void test() {
 }
 
 constexpr bool test() {
+  test_SFINAE();
+
   test<cpp17_input_iterator<int*>>();
   test<cpp20_input_iterator<int*>>();
   test<forward_iterator<int*>>();

>From 8f3af3e95e1981d5750ed1c7a3a82cd9ebd21739 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 19 Aug 2026 16:26:51 +0200
Subject: [PATCH 3/6] add: test case for const st to non-const st

---
 .../range.enumerate/sentinel/ctor.convert.pass.cpp     | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
index a5d255f75bb29..43da253f34934 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
@@ -75,6 +75,16 @@ constexpr void test_SFINAE() {
     static_assert(!std::is_constructible_v<decltype(sent1), decltype(sent2)>);
     static_assert(!std::is_constructible_v<decltype(sent2), decltype(sent1)>); // this assert fails
   }
+  {
+    std::ranges::enumerate_view v{NonSimpleNonCommonConvertibleView(buffer)};
+    auto sent1 = v.end();
+    std::ranges::sentinel_t<const decltype(v)> sent2 = sent1;
+
+    static_assert(!std::same_as<decltype(sent1), decltype(sent2)>);
+
+    // We cannot create a non-const sentinel from a const sentinel.
+    static_assert(!std::is_constructible_v<decltype(sent1), decltype(sent2)>);
+  }
 }
 
 template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>

>From 71696d1d1278f03384c9297a70c60e7278c47c11 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 19 Aug 2026 17:25:50 +0200
Subject: [PATCH 4/6] fix assertions

---
 .../range.enumerate/sentinel/ctor.convert.pass.cpp           | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
index 43da253f34934..e376b64cc979d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
@@ -72,8 +72,11 @@ constexpr void test_SFINAE() {
 
     static_assert(!std::same_as<decltype(sent1), decltype(sent2)>);
 
+    // We cannot create a non-const sentinel from a const sentinel.
     static_assert(!std::is_constructible_v<decltype(sent1), decltype(sent2)>);
-    static_assert(!std::is_constructible_v<decltype(sent2), decltype(sent1)>); // this assert fails
+
+    // We can create a const sentinel from a non-const sentinel.
+    static_assert(std::is_constructible_v<decltype(sent2), decltype(sent1)>);
   }
   {
     std::ranges::enumerate_view v{NonSimpleNonCommonConvertibleView(buffer)};

>From 2bf68e00a8609a3417ca1edbe4a573c4c45c69c0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 19 Aug 2026 17:35:44 +0200
Subject: [PATCH 5/6] fix: clang-format

---
 .../range.enumerate/sentinel/ctor.convert.pass.cpp              | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
index e376b64cc979d..ee5934a9da07a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
@@ -80,7 +80,7 @@ constexpr void test_SFINAE() {
   }
   {
     std::ranges::enumerate_view v{NonSimpleNonCommonConvertibleView(buffer)};
-    auto sent1 = v.end();
+    auto sent1                                       = v.end();
     std::ranges::sentinel_t<const decltype(v)> sent2 = sent1;
 
     static_assert(!std::same_as<decltype(sent1), decltype(sent2)>);

>From 473a0d03bc379c36218053614dfe4658a52f124b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 20 Aug 2026 10:54:02 +0200
Subject: [PATCH 6/6] Apply suggestions from code review

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 .../range.enumerate/sentinel/ctor.convert.pass.cpp     | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
index ee5934a9da07a..37b1df1734f4a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
@@ -27,8 +27,8 @@
 
 #include "test_iterators.h"
 
-#include "../types.h"
 #include "../../range_adaptor_types.h"
+#include "../types.h"
 
 template <class T>
 struct convertible_sentinel_wrapper {
@@ -57,7 +57,7 @@ struct NonSimpleNonCommonConvertibleView : IntBufferView {
 };
 
 // convertible_to<sentinel<false>, sentinel<Const>>
-static_assert(std::convertible_to< //
+static_assert(std::convertible_to<
               std::ranges::sentinel_t<std::ranges::enumerate_view<NonSimpleNonCommonConvertibleView>>,
               std::ranges::sentinel_t<std::ranges::enumerate_view<NonSimpleNonCommonConvertibleView> const>>);
 
@@ -67,8 +67,8 @@ constexpr void test_SFINAE() {
   // Underlying non-const to const not convertible.
   {
     std::ranges::enumerate_view v{NonSimpleNonCommonConvertibleView(buffer)};
-    auto sent1 = v.end();
-    auto sent2 = std::as_const(v).end();
+    auto st= v.end();
+    auto const_st= std::as_const(v).end();
 
     static_assert(!std::same_as<decltype(sent1), decltype(sent2)>);
 
@@ -111,7 +111,7 @@ constexpr void test() {
   {
     // Assigning a non-const sentinel to a const-sentinel-typed variable invokes
     // the converting constructor.
-    std::same_as<EnumerateSentinel> decltype(auto) s = view.end();
+    std::same_as<EnumerateSentinel> decltype(auto) st = view.end();
     std::same_as<Sentinel> decltype(auto) sResult    = s.base();
     assert(base(base(sResult)) == std::to_address(base(array.end())));
 



More information about the libcxx-commits mailing list