[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