[libcxx-commits] [libcxx] [libc++] Fix __segmented_iterator_traits for implicit template instantiation in SFINAE (PR #134304)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Apr 5 06:26:28 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Peng Liu (winner245)
<details>
<summary>Changes</summary>
The general template `__segmented_iterator_traits<_Iterator>` is only declared but not defined. It is explicitly specialized for `std::deque<T>::iterator` and `join_view` iterators. This is problematic when `__segmented_iterator_traits<_Iterator>` is used as a SFINAE constraint, as it may cause implicit instantiation for an arbitrary `_Iterator` type during substitution, leading to a hard error for SFINAE. Here is a [reproducer](https://godbolt.org/z/xP35GWqhj). This issue first came up in #<!-- -->132896, where `__segmented_iterator_traits` is used within `enable_if` to guide the overload resolution of various `std::__for_each_n`.
This patch fixes the issue by defining a default empty template consisting of member types only for `__is_segmented_iterator = false_type`. This ensures safe instantiation for arbitrary non-segmented iterator types. The existing specializations, such as `__segmented_iterator_traits<__deque_iterator<...>>` and `__segmented_iterator_traits<_JoinViewIterator>`, provide concrete definitions for segmented iterators where `__is_segmented_iterator = true_type`.
---
Full diff: https://github.com/llvm/llvm-project/pull/134304.diff
3 Files Affected:
- (modified) libcxx/include/__iterator/segmented_iterator.h (+7-13)
- (modified) libcxx/include/__ranges/join_view.h (+2)
- (added) libcxx/test/libcxx/iterators/segmented_iterator.pass.cpp (+52)
``````````diff
diff --git a/libcxx/include/__iterator/segmented_iterator.h b/libcxx/include/__iterator/segmented_iterator.h
index 7a8e1addeacd9..2533502d8883d 100644
--- a/libcxx/include/__iterator/segmented_iterator.h
+++ b/libcxx/include/__iterator/segmented_iterator.h
@@ -51,28 +51,22 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _Iterator>
-struct __segmented_iterator_traits;
-/* exposition-only:
-{
- using __segment_iterator = ...;
- using __local_iterator = ...;
+struct __segmented_iterator_traits {
+ using __is_segmented_iterator _LIBCPP_NODEBUG = false_type;
+ using __segment_iterator _LIBCPP_NODEBUG = void;
+ using __local_iterator _LIBCPP_NODEBUG = void;
+ /* exposition-only:
static __segment_iterator __segment(_Iterator);
static __local_iterator __local(_Iterator);
static __local_iterator __begin(__segment_iterator);
static __local_iterator __end(__segment_iterator);
static _Iterator __compose(__segment_iterator, __local_iterator);
+ */
};
-*/
-
-template <class _Tp, size_t = 0>
-struct __has_specialization : false_type {};
-
-template <class _Tp>
-struct __has_specialization<_Tp, sizeof(_Tp) * 0> : true_type {};
template <class _Iterator>
-using __is_segmented_iterator _LIBCPP_NODEBUG = __has_specialization<__segmented_iterator_traits<_Iterator> >;
+struct __is_segmented_iterator : __segmented_iterator_traits<_Iterator>::__is_segmented_iterator {};
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index 327b349f476a7..266d6c11394ee 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -31,6 +31,7 @@
#include <__ranges/range_adaptor.h>
#include <__ranges/view_interface.h>
#include <__type_traits/common_type.h>
+#include <__type_traits/integral_constant.h>
#include <__type_traits/maybe_const.h>
#include <__utility/as_lvalue.h>
#include <__utility/empty.h>
@@ -378,6 +379,7 @@ template <class _JoinViewIterator>
__has_random_access_iterator_category<typename _JoinViewIterator::_Outer>::value &&
__has_random_access_iterator_category<typename _JoinViewIterator::_Inner>::value)
struct __segmented_iterator_traits<_JoinViewIterator> {
+ using __is_segmented_iterator _LIBCPP_NODEBUG = true_type;
using __segment_iterator _LIBCPP_NODEBUG =
__iterator_with_data<typename _JoinViewIterator::_Outer, typename _JoinViewIterator::_Parent*>;
using __local_iterator _LIBCPP_NODEBUG = typename _JoinViewIterator::_Inner;
diff --git a/libcxx/test/libcxx/iterators/segmented_iterator.pass.cpp b/libcxx/test/libcxx/iterators/segmented_iterator.pass.cpp
new file mode 100644
index 0000000000000..d2e2f35fe6f45
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/segmented_iterator.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+
+// <iterator>
+
+// __segmented_iterator_traits<_Iter>
+
+// verifies that __segmented_iterator_traits<_Iter> does not result in implicit
+// template instantaition, which may cause hard errors in SFINAE.
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <array>
+#include <deque>
+#include <list>
+#include <ranges>
+#include <vector>
+#include <__iterator/segmented_iterator.h>
+#include <__type_traits/integral_constant.h>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+template <class Iter>
+struct is_segmented_random_access_iterator
+ : std::_BoolConstant<std::__is_segmented_iterator<Iter>::value &&
+ std::__has_random_access_iterator_category<
+ typename std::__segmented_iterator_traits<Iter>::__local_iterator>::value> {};
+
+int main(int, char**) {
+ static_assert(is_segmented_random_access_iterator<std::deque<int>::iterator>::value, "");
+ static_assert(!is_segmented_random_access_iterator<std::vector<int>::iterator>::value, "");
+ static_assert(!is_segmented_random_access_iterator<std::list<int>::iterator>::value, "");
+ static_assert(!is_segmented_random_access_iterator<std::array<int, 0>::iterator>::value, "");
+ static_assert(!is_segmented_random_access_iterator<cpp17_input_iterator<int*> >::value, "");
+ static_assert(!is_segmented_random_access_iterator<forward_iterator<int*> >::value, "");
+ static_assert(!is_segmented_random_access_iterator<random_access_iterator<int*> >::value, "");
+ static_assert(!is_segmented_random_access_iterator<int*>::value, "");
+
+#if TEST_STD_VER >= 20
+ using join_view_iterator = decltype((std::declval<std::vector<std::vector<int > >&>() | std::views::join).begin());
+ static_assert(is_segmented_random_access_iterator<join_view_iterator>::value, "");
+#endif
+
+ return 0;
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/134304
More information about the libcxx-commits
mailing list