[libcxx-commits] [libcxx] [libc++][ranges][enumerate_view] Fix iterator converting constructor test (PR #212044)
Hristo Hristov via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Aug 14 23:32:29 PDT 2026
https://github.com/Zingam updated https://github.com/llvm/llvm-project/pull/212044
>From a7ca704bbf976908f4c938f89d4ca63488a65cae Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 25 Jul 2026 19:28:15 +0200
Subject: [PATCH 1/8] add: static_assert for convertible_to
---
.../range.enumerate/iterator/ctor.convert.pass.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
index f441de92f6a60..083b01e178daa 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
@@ -43,6 +43,7 @@ constexpr void test() {
};
static_assert(std::is_convertible_v<EnumerateIterator, EnumerateConstIterator>);
+ static_assert(std::convertible_to<EnumerateIterator, EnumerateConstIterator>);
std::array array{0, 84, 2, 3, 4};
auto view = make_enumerate_view(array.begin(), array.end());
>From bb0f70c7a8bef2692d7ba738cc0d5890cc717a9d Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 27 Jul 2026 22:32:20 +0200
Subject: [PATCH 2/8] remove: is_convertinble_v line
---
.../range.enumerate/iterator/ctor.convert.pass.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
index 083b01e178daa..a53700b027b21 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
@@ -42,7 +42,6 @@ constexpr void test() {
return EnumerateView(std::move(view));
};
- static_assert(std::is_convertible_v<EnumerateIterator, EnumerateConstIterator>);
static_assert(std::convertible_to<EnumerateIterator, EnumerateConstIterator>);
std::array array{0, 84, 2, 3, 4};
>From 1cddab5d71fb55ccce6b88b2e6193b58040d5635 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 29 Jul 2026 23:49:54 +0200
Subject: [PATCH 3/8] port: underlying non-const to const not convertible test
case form zip view
---
.../iterator/ctor.convert.pass.cpp | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
index a53700b027b21..eda1087d0b34a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
@@ -27,8 +27,17 @@
#include "test_iterators.h"
+#include "../../range_adaptor_types.h"
#include "../types.h"
+using ConstIterIncompatibleView =
+ BasicView<forward_iterator<int*>,
+ forward_iterator<int*>,
+ random_access_iterator<const int*>,
+ random_access_iterator<const int*>>;
+static_assert(!std::convertible_to<std::ranges::iterator_t<ConstIterIncompatibleView>,
+ std::ranges::iterator_t<const ConstIterIncompatibleView>>);
+
template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
constexpr void test() {
using View = MinimalView<Iterator, Sentinel>;
@@ -64,6 +73,17 @@ constexpr void test() {
assert(index == 1);
assert(value == 84);
}
+ {
+ // underlying non-const to const not convertible
+ int buffer[3] = {1, 2, 3};
+ std::ranges::enumerate_view v(ConstIterIncompatibleView{buffer});
+ auto iter1 = v.begin();
+ auto iter2 = std::as_const(v).begin();
+
+ static_assert(!std::is_same_v<decltype(iter1), decltype(iter2)>);
+ static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
+ static_assert(!std::constructible_from<decltype(iter2), decltype(iter1)>);
+ }
}
constexpr bool tests() {
>From eaf043f7a597c8e831e1a04f3c9a64e75baff845 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 7 Aug 2026 00:31:02 +0200
Subject: [PATCH 4/8] ref: port zip.view tests
---
.../iterator/ctor.convert.pass.cpp | 106 ++++++++++--------
1 file changed, 62 insertions(+), 44 deletions(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
index eda1087d0b34a..954b3d6081941 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
@@ -38,44 +38,72 @@ using ConstIterIncompatibleView =
static_assert(!std::convertible_to<std::ranges::iterator_t<ConstIterIncompatibleView>,
std::ranges::iterator_t<const ConstIterIncompatibleView>>);
-template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
-constexpr void test() {
- using View = MinimalView<Iterator, Sentinel>;
- using EnumerateView = std::ranges::enumerate_view<View>;
- using EnumerateIterator = std::ranges::iterator_t<EnumerateView>;
- using EnumerateConstIterator = std::ranges::iterator_t<const EnumerateView>;
-
- auto make_enumerate_view = [](auto begin, auto end) {
- View view{Iterator(std::to_address(base(begin))), Sentinel(Iterator(std::to_address(base(end))))};
-
- return EnumerateView(std::move(view));
- };
-
- static_assert(std::convertible_to<EnumerateIterator, EnumerateConstIterator>);
-
- std::array array{0, 84, 2, 3, 4};
- auto view = make_enumerate_view(array.begin(), array.end());
- {
- std::same_as<EnumerateIterator> decltype(auto) it = view.begin();
- std::same_as<const Iterator&> decltype(auto) itResult = it.base();
- assert(base(base(itResult)) == std::to_address(base(array.begin())));
-
- auto [index, value] = *(++it);
- assert(index == 1);
- assert(value == 84);
+struct TestIt {
+ template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+ constexpr void operator()() const {
+ using View = MinimalView<Iterator, Sentinel>;
+ using EnumerateView = std::ranges::enumerate_view<View>;
+ using EnumerateIterator = std::ranges::iterator_t<EnumerateView>;
+ using EnumerateConstIterator = std::ranges::iterator_t<const EnumerateView>;
+
+ auto make_enumerate_view = [](auto begin, auto end) {
+ View view{Iterator(std::to_address(base(begin))), Sentinel(Iterator(std::to_address(base(end))))};
+
+ return EnumerateView(std::move(view));
+ };
+
+ static_assert(std::convertible_to<EnumerateIterator, EnumerateConstIterator>);
+
+ std::array array{0, 84, 2, 3, 4};
+ auto view = make_enumerate_view(array.begin(), array.end());
+ {
+ std::same_as<EnumerateIterator> decltype(auto) it = view.begin();
+ std::same_as<const Iterator&> decltype(auto) itResult = it.base();
+ assert(base(base(itResult)) == std::to_address(base(array.begin())));
+
+ auto [index, value] = *(++it);
+ assert(index == 1);
+ assert(value == 84);
+ }
+ {
+ std::same_as<EnumerateConstIterator> decltype(auto) it = view.begin();
+ std::same_as<const Iterator&> decltype(auto) itResult = it.base();
+ assert(base(base(itResult)) == std::to_address(base(array.begin())));
+
+ auto [index, value] = *(++it);
+ assert(index == 1);
+ assert(value == 84);
+ }
}
+};
+
+constexpr bool
+test() {
+ using Iterators =
+ types::type_list< cpp17_input_iterator<int*>,
+ cpp20_input_iterator<int*>,
+ forward_iterator<int*>,
+ bidirectional_iterator<int*>,
+ random_access_iterator<int*>,
+ contiguous_iterator<int*>,
+ int* >;
+
+ types::for_each(Iterators{}, TestIt());
+
+ int buffer[3] = {1, 2, 3};
{
- std::same_as<EnumerateConstIterator> decltype(auto) it = view.begin();
- std::same_as<const Iterator&> decltype(auto) itResult = it.base();
- assert(base(base(itResult)) == std::to_address(base(array.begin())));
+ std::ranges::enumerate_view v(NonSimpleCommon{buffer});
+ auto iter1 = v.begin();
+ std::ranges::iterator_t<const decltype(v)> iter2 = iter1;
+ assert(iter1 == iter2);
- auto [index, value] = *(++it);
- assert(index == 1);
- assert(value == 84);
+ static_assert(!std::is_same_v<decltype(iter1), decltype(iter2)>);
+
+ // We cannot create a non-const iterator from a const iterator.
+ static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
}
{
// underlying non-const to const not convertible
- int buffer[3] = {1, 2, 3};
std::ranges::enumerate_view v(ConstIterIncompatibleView{buffer});
auto iter1 = v.begin();
auto iter2 = std::as_const(v).begin();
@@ -84,23 +112,13 @@ constexpr void test() {
static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
static_assert(!std::constructible_from<decltype(iter2), decltype(iter1)>);
}
-}
-
-constexpr bool tests() {
- test<cpp17_input_iterator<int*>>();
- test<cpp20_input_iterator<int*>>();
- test<forward_iterator<int*>>();
- test<bidirectional_iterator<int*>>();
- test<random_access_iterator<int*>>();
- test<contiguous_iterator<int*>>();
- test<int*>();
return true;
}
int main(int, char**) {
- tests();
- static_assert(tests());
+ test();
+ static_assert(test());
return 0;
}
>From e1dda3258bfcfbc1fa91500b45c2fbea902cf4ec Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 7 Aug 2026 12:48:39 +0200
Subject: [PATCH 5/8] remove: redundant test
---
.../iterator/ctor.convert.pass.cpp | 18 +++++-------------
1 file changed, 5 insertions(+), 13 deletions(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
index 954b3d6081941..fbcefcba52220 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
@@ -57,15 +57,8 @@ struct TestIt {
std::array array{0, 84, 2, 3, 4};
auto view = make_enumerate_view(array.begin(), array.end());
{
- std::same_as<EnumerateIterator> decltype(auto) it = view.begin();
- std::same_as<const Iterator&> decltype(auto) itResult = it.base();
- assert(base(base(itResult)) == std::to_address(base(array.begin())));
-
- auto [index, value] = *(++it);
- assert(index == 1);
- assert(value == 84);
- }
- {
+ // Assigning a non-const iterator to a const-iterator-typed variable invokes
+ // the converting constructor.
std::same_as<EnumerateConstIterator> decltype(auto) it = view.begin();
std::same_as<const Iterator&> decltype(auto) itResult = it.base();
assert(base(base(itResult)) == std::to_address(base(array.begin())));
@@ -77,8 +70,7 @@ struct TestIt {
}
};
-constexpr bool
-test() {
+constexpr bool test() {
using Iterators =
types::type_list< cpp17_input_iterator<int*>,
cpp20_input_iterator<int*>,
@@ -97,7 +89,7 @@ test() {
std::ranges::iterator_t<const decltype(v)> iter2 = iter1;
assert(iter1 == iter2);
- static_assert(!std::is_same_v<decltype(iter1), decltype(iter2)>);
+ static_assert(!std::same_as<decltype(iter1), decltype(iter2)>);
// We cannot create a non-const iterator from a const iterator.
static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
@@ -108,7 +100,7 @@ test() {
auto iter1 = v.begin();
auto iter2 = std::as_const(v).begin();
- static_assert(!std::is_same_v<decltype(iter1), decltype(iter2)>);
+ static_assert(!std::same_as<decltype(iter1), decltype(iter2)>);
static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
static_assert(!std::constructible_from<decltype(iter2), decltype(iter1)>);
}
>From 560a981702e3244b34869386996d56aa5976ff94 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 7 Aug 2026 14:22:54 +0200
Subject: [PATCH 6/8] fix: missing include
---
.../range.enumerate/iterator/ctor.convert.pass.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
index fbcefcba52220..4d7ef2ddab882 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
@@ -22,10 +22,9 @@
#include <concepts>
#include <memory>
#include <ranges>
-#include <type_traits>
#include <utility>
-#include "test_iterators.h"
+#include "type_algorithms.h"
#include "../../range_adaptor_types.h"
#include "../types.h"
>From d5aa8f08ea0570a153e7f96473d50ea66fa42624 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 12 Aug 2026 16:01:47 +0200
Subject: [PATCH 7/8] revert: TestIt functor. fix: tests order
---
.../iterator/ctor.convert.pass.cpp | 107 +++++++++---------
1 file changed, 53 insertions(+), 54 deletions(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
index 4d7ef2ddab882..a339ff75b326e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
@@ -37,51 +37,18 @@ using ConstIterIncompatibleView =
static_assert(!std::convertible_to<std::ranges::iterator_t<ConstIterIncompatibleView>,
std::ranges::iterator_t<const ConstIterIncompatibleView>>);
-struct TestIt {
- template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
- constexpr void operator()() const {
- using View = MinimalView<Iterator, Sentinel>;
- using EnumerateView = std::ranges::enumerate_view<View>;
- using EnumerateIterator = std::ranges::iterator_t<EnumerateView>;
- using EnumerateConstIterator = std::ranges::iterator_t<const EnumerateView>;
-
- auto make_enumerate_view = [](auto begin, auto end) {
- View view{Iterator(std::to_address(base(begin))), Sentinel(Iterator(std::to_address(base(end))))};
-
- return EnumerateView(std::move(view));
- };
-
- static_assert(std::convertible_to<EnumerateIterator, EnumerateConstIterator>);
-
- std::array array{0, 84, 2, 3, 4};
- auto view = make_enumerate_view(array.begin(), array.end());
- {
- // Assigning a non-const iterator to a const-iterator-typed variable invokes
- // the converting constructor.
- std::same_as<EnumerateConstIterator> decltype(auto) it = view.begin();
- std::same_as<const Iterator&> decltype(auto) itResult = it.base();
- assert(base(base(itResult)) == std::to_address(base(array.begin())));
-
- auto [index, value] = *(++it);
- assert(index == 1);
- assert(value == 84);
- }
- }
-};
-
-constexpr bool test() {
- using Iterators =
- types::type_list< cpp17_input_iterator<int*>,
- cpp20_input_iterator<int*>,
- forward_iterator<int*>,
- bidirectional_iterator<int*>,
- random_access_iterator<int*>,
- contiguous_iterator<int*>,
- int* >;
-
- types::for_each(Iterators{}, TestIt());
-
+constexpr void sfinae_test() {
int buffer[3] = {1, 2, 3};
+ {
+ // underlying non-const to const not convertible
+ std::ranges::enumerate_view v(ConstIterIncompatibleView{buffer});
+ auto iter1 = v.begin();
+ auto iter2 = std::as_const(v).begin();
+
+ static_assert(!std::same_as<decltype(iter1), decltype(iter2)>);
+ static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
+ static_assert(!std::constructible_from<decltype(iter2), decltype(iter1)>);
+ }
{
std::ranges::enumerate_view v(NonSimpleCommon{buffer});
auto iter1 = v.begin();
@@ -93,23 +60,55 @@ constexpr bool test() {
// We cannot create a non-const iterator from a const iterator.
static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
}
- {
- // underlying non-const to const not convertible
- std::ranges::enumerate_view v(ConstIterIncompatibleView{buffer});
- auto iter1 = v.begin();
- auto iter2 = std::as_const(v).begin();
+}
- static_assert(!std::same_as<decltype(iter1), decltype(iter2)>);
- static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
- static_assert(!std::constructible_from<decltype(iter2), decltype(iter1)>);
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+ using View = MinimalView<Iterator, Sentinel>;
+ using EnumerateView = std::ranges::enumerate_view<View>;
+ using EnumerateIterator = std::ranges::iterator_t<EnumerateView>;
+ using EnumerateConstIterator = std::ranges::iterator_t<const EnumerateView>;
+
+ auto make_enumerate_view = [](auto begin, auto end) {
+ View view{Iterator(std::to_address(base(begin))), Sentinel(Iterator(std::to_address(base(end))))};
+
+ return EnumerateView(std::move(view));
+ };
+
+ static_assert(std::convertible_to<EnumerateIterator, EnumerateConstIterator>);
+
+ std::array array{0, 84, 2, 3, 4};
+ auto view = make_enumerate_view(array.begin(), array.end());
+ {
+ // Assigning a non-const iterator to a const-iterator-typed variable invokes
+ // the converting constructor.
+ std::same_as<EnumerateConstIterator> decltype(auto) it = view.begin();
+ std::same_as<const Iterator&> decltype(auto) itResult = it.base();
+ assert(base(base(itResult)) == std::to_address(base(array.begin())));
+
+ // verify ++it
+ auto [index, value] = *(++it);
+ assert(index == 1);
+ assert(value == 84);
}
+}
+
+constexpr bool tests() {
+ sfinae_test();
+ test<cpp17_input_iterator<int*>>();
+ test<cpp20_input_iterator<int*>>();
+ test<forward_iterator<int*>>();
+ test<bidirectional_iterator<int*>>();
+ test<random_access_iterator<int*>>();
+ test<contiguous_iterator<int*>>();
+ test<int*>();
return true;
}
int main(int, char**) {
- test();
- static_assert(test());
+ tests();
+ static_assert(tests());
return 0;
}
>From e54405d71e0bbf718bb268a350993037bb426868 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Sat, 15 Aug 2026 09:32:17 +0300
Subject: [PATCH 8/8] Apply suggestions from code review
Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
.../iterator/ctor.convert.pass.cpp | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
index a339ff75b326e..d99ce46a85ae1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
@@ -37,10 +37,10 @@ using ConstIterIncompatibleView =
static_assert(!std::convertible_to<std::ranges::iterator_t<ConstIterIncompatibleView>,
std::ranges::iterator_t<const ConstIterIncompatibleView>>);
-constexpr void sfinae_test() {
+constexpr void test_SFINAE() {
int buffer[3] = {1, 2, 3};
{
- // underlying non-const to const not convertible
+ // Underlying non-const to const not convertible.
std::ranges::enumerate_view v(ConstIterIncompatibleView{buffer});
auto iter1 = v.begin();
auto iter2 = std::as_const(v).begin();
@@ -51,7 +51,8 @@ constexpr void sfinae_test() {
}
{
std::ranges::enumerate_view v(NonSimpleCommon{buffer});
- auto iter1 = v.begin();
+ auto iter1 = v.begin();
+
std::ranges::iterator_t<const decltype(v)> iter2 = iter1;
assert(iter1 == iter2);
@@ -86,15 +87,15 @@ constexpr void test() {
std::same_as<const Iterator&> decltype(auto) itResult = it.base();
assert(base(base(itResult)) == std::to_address(base(array.begin())));
- // verify ++it
+ // Verify ++it
auto [index, value] = *(++it);
assert(index == 1);
assert(value == 84);
}
}
-constexpr bool tests() {
- sfinae_test();
+constexpr bool test() {
+ test_SFINAE();
test<cpp17_input_iterator<int*>>();
test<cpp20_input_iterator<int*>>();
test<forward_iterator<int*>>();
@@ -108,7 +109,8 @@ constexpr bool tests() {
int main(int, char**) {
tests();
- static_assert(tests());
+test();
+static_assert(test());
return 0;
}
More information about the libcxx-commits
mailing list