[libcxx-commits] [libcxx] 988682a - [libc++] Fix std::lower_bound with C++20-hostile iterators

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jun 13 03:19:34 PDT 2022


Author: Nikolas Klauser
Date: 2022-06-13T12:19:28+02:00
New Revision: 988682a3892e0d1cd13256ca985d8a8c60ca2bd6

URL: https://github.com/llvm/llvm-project/commit/988682a3892e0d1cd13256ca985d8a8c60ca2bd6
DIFF: https://github.com/llvm/llvm-project/commit/988682a3892e0d1cd13256ca985d8a8c60ca2bd6.diff

LOG: [libc++] Fix std::lower_bound with C++20-hostile iterators

Reviewed By: EricWF, #libc

Spies: sstefan1, libcxx-commits, mgorny

Differential Revision: https://reviews.llvm.org/D127577

Added: 
    libcxx/include/__algorithm/iterator_operations.h

Modified: 
    libcxx/include/CMakeLists.txt
    libcxx/include/__algorithm/equal_range.h
    libcxx/include/__algorithm/lower_bound.h
    libcxx/include/__algorithm/ranges_binary_search.h
    libcxx/include/__algorithm/ranges_lower_bound.h
    libcxx/include/__algorithm/ranges_upper_bound.h
    libcxx/include/__iterator/advance.h
    libcxx/include/__iterator/distance.h
    libcxx/include/module.modulemap.in
    libcxx/test/libcxx/private_headers.verify.cpp
    libcxx/test/std/algorithms/alg.sorting/alg.binary.search/lower.bound/lower_bound.pass.cpp
    libcxx/test/support/test_iterators.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 62d578cf17912..697f396c165ba 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -41,6 +41,7 @@ set(files
   __algorithm/is_sorted.h
   __algorithm/is_sorted_until.h
   __algorithm/iter_swap.h
+  __algorithm/iterator_operations.h
   __algorithm/lexicographical_compare.h
   __algorithm/lower_bound.h
   __algorithm/make_heap.h

diff  --git a/libcxx/include/__algorithm/equal_range.h b/libcxx/include/__algorithm/equal_range.h
index 3e17772091ce5..2a07364bb66f5 100644
--- a/libcxx/include/__algorithm/equal_range.h
+++ b/libcxx/include/__algorithm/equal_range.h
@@ -12,6 +12,7 @@
 #include <__algorithm/comp.h>
 #include <__algorithm/comp_ref_type.h>
 #include <__algorithm/half_positive.h>
+#include <__algorithm/iterator_operations.h>
 #include <__algorithm/lower_bound.h>
 #include <__algorithm/upper_bound.h>
 #include <__config>
@@ -54,7 +55,7 @@ __equal_range(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __va
             _ForwardIterator __mp1 = __m;
             return pair<_ForwardIterator, _ForwardIterator>
                    (
-                      _VSTD::__lower_bound_impl(__first, __m, __value_, __comp, __proj),
+                      _VSTD::__lower_bound_impl<_StdIterOps>(__first, __m, __value_, __comp, __proj),
                       _VSTD::__upper_bound<_Compare>(++__mp1, __last, __value_, __comp)
                    );
         }

diff  --git a/libcxx/include/__algorithm/iterator_operations.h b/libcxx/include/__algorithm/iterator_operations.h
new file mode 100644
index 0000000000000..c02f9bf649dfc
--- /dev/null
+++ b/libcxx/include/__algorithm/iterator_operations.h
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ALGORIHTM_ITERATOR_OPERATIONS_H
+#define _LIBCPP___ALGORIHTM_ITERATOR_OPERATIONS_H
+
+#include <__config>
+#include <__iterator/advance.h>
+#include <__iterator/distance.h>
+#include <__iterator/iterator_traits.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+struct _RangesIterOps {
+  static constexpr auto advance = ranges::advance;
+  static constexpr auto distance = ranges::distance;
+};
+#endif
+
+struct _StdIterOps {
+
+  template <class _Iterator, class _Distance>
+  _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_AFTER_CXX11 void advance(_Iterator& __iter, _Distance __count) {
+    return std::advance(__iter, __count);
+  }
+
+  template <class _Iterator>
+  _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_AFTER_CXX11
+  typename iterator_traits<_Iterator>::
diff erence_type distance(_Iterator __first, _Iterator __last) {
+    return std::distance(__first, __last);
+  }
+
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ALGORIHTM_ITERATOR_OPERATIONS_H

diff  --git a/libcxx/include/__algorithm/lower_bound.h b/libcxx/include/__algorithm/lower_bound.h
index 0334733f76c56..fbcd5c7e908af 100644
--- a/libcxx/include/__algorithm/lower_bound.h
+++ b/libcxx/include/__algorithm/lower_bound.h
@@ -11,6 +11,7 @@
 
 #include <__algorithm/comp.h>
 #include <__algorithm/half_positive.h>
+#include <__algorithm/iterator_operations.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/invoke.h>
@@ -26,15 +27,15 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _Iter, class _Sent, class _Type, class _Proj, class _Comp>
+template <class _IterOps, class _Iter, class _Sent, class _Type, class _Proj, class _Comp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
 _Iter __lower_bound_impl(_Iter __first, _Sent __last, const _Type& __value, _Comp& __comp, _Proj& __proj) {
-  auto __len = std::__ranges_distance(__first, __last);
+  auto __len = _IterOps::distance(__first, __last);
 
   while (__len != 0) {
     auto __l2 = std::__half_positive(__len);
     _Iter __m = __first;
-    std::__ranges_advance(__m, __l2);
+    _IterOps::advance(__m, __l2);
     if (std::__invoke(__comp, std::__invoke(__proj, *__m), __value)) {
       __first = ++__m;
       __len -= __l2 + 1;
@@ -51,7 +52,7 @@ _ForwardIterator lower_bound(_ForwardIterator __first, _ForwardIterator __last,
   static_assert(__is_callable<_Compare, decltype(*__first), const _Tp&>::value,
                 "The comparator has to be callable");
   auto __proj = std::__identity();
-  return std::__lower_bound_impl(__first, __last, __value_, __comp, __proj);
+  return std::__lower_bound_impl<_StdIterOps>(__first, __last, __value_, __comp, __proj);
 }
 
 template <class _ForwardIterator, class _Tp>

diff  --git a/libcxx/include/__algorithm/ranges_binary_search.h b/libcxx/include/__algorithm/ranges_binary_search.h
index 088391f47a5a2..68359fb1388f0 100644
--- a/libcxx/include/__algorithm/ranges_binary_search.h
+++ b/libcxx/include/__algorithm/ranges_binary_search.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___ALGORITHM_RANGES_BINARY_SEARCH_H
 #define _LIBCPP___ALGORITHM_RANGES_BINARY_SEARCH_H
 
+#include <__algorithm/iterator_operations.h>
 #include <__algorithm/lower_bound.h>
 #include <__config>
 #include <__functional/identity.h>
@@ -34,7 +35,7 @@ struct __fn {
             indirect_strict_weak_order<const _Type*, projected<_Iter, _Proj>> _Comp = ranges::less>
   _LIBCPP_HIDE_FROM_ABI constexpr
   bool operator()(_Iter __first, _Sent __last, const _Type& __value, _Comp __comp = {}, _Proj __proj = {}) const {
-    auto __ret = std::__lower_bound_impl(__first, __last, __value, __comp, __proj);
+    auto __ret = std::__lower_bound_impl<_RangesIterOps>(__first, __last, __value, __comp, __proj);
     return __ret != __last && !std::invoke(__comp, __value, std::invoke(__proj, *__first));
   }
 
@@ -44,7 +45,7 @@ struct __fn {
   bool operator()(_Range&& __r, const _Type& __value, _Comp __comp = {}, _Proj __proj = {}) const {
     auto __first = ranges::begin(__r);
     auto __last = ranges::end(__r);
-    auto __ret = std::__lower_bound_impl(__first, __last, __value, __comp, __proj);
+    auto __ret = std::__lower_bound_impl<_RangesIterOps>(__first, __last, __value, __comp, __proj);
     return __ret != __last && !std::invoke(__comp, __value, std::invoke(__proj, *__first));
   }
 };

diff  --git a/libcxx/include/__algorithm/ranges_lower_bound.h b/libcxx/include/__algorithm/ranges_lower_bound.h
index f4d9c262b0efa..a73470465cfda 100644
--- a/libcxx/include/__algorithm/ranges_lower_bound.h
+++ b/libcxx/include/__algorithm/ranges_lower_bound.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___ALGORITHM_RANGES_LOWER_BOUND_H
 #define _LIBCPP___ALGORITHM_RANGES_LOWER_BOUND_H
 
+#include <__algorithm/iterator_operations.h>
 #include <__algorithm/lower_bound.h>
 #include <__config>
 #include <__functional/identity.h>
@@ -38,7 +39,7 @@ struct __fn {
             indirect_strict_weak_order<const _Type*, projected<_Iter, _Proj>> _Comp = ranges::less>
   _LIBCPP_HIDE_FROM_ABI constexpr
   _Iter operator()(_Iter __first, _Sent __last, const _Type& __value, _Comp __comp = {}, _Proj __proj = {}) const {
-    return std::__lower_bound_impl(__first, __last, __value, __comp, __proj);
+    return std::__lower_bound_impl<_RangesIterOps>(__first, __last, __value, __comp, __proj);
   }
 
   template <forward_range _Range, class _Type, class _Proj = identity,
@@ -48,7 +49,7 @@ struct __fn {
                                          const _Type& __value,
                                          _Comp __comp = {},
                                          _Proj __proj = {}) const {
-    return std::__lower_bound_impl(ranges::begin(__r), ranges::end(__r), __value, __comp, __proj);
+    return std::__lower_bound_impl<_RangesIterOps>(ranges::begin(__r), ranges::end(__r), __value, __comp, __proj);
   }
 };
 } // namespace __lower_bound

diff  --git a/libcxx/include/__algorithm/ranges_upper_bound.h b/libcxx/include/__algorithm/ranges_upper_bound.h
index 69842af448d7a..94b5269c86aff 100644
--- a/libcxx/include/__algorithm/ranges_upper_bound.h
+++ b/libcxx/include/__algorithm/ranges_upper_bound.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___ALGORITHM_RANGES_UPPER_BOUND_H
 #define _LIBCPP___ALGORITHM_RANGES_UPPER_BOUND_H
 
+#include <__algorithm/iterator_operations.h>
 #include <__algorithm/lower_bound.h>
 #include <__config>
 #include <__functional/identity.h>
@@ -39,7 +40,7 @@ struct __fn {
       return !std::invoke(__comp, __rhs, __lhs);
     };
 
-    return std::__lower_bound_impl(__first, __last, __value, __comp_lhs_rhs_swapped, __proj);
+    return std::__lower_bound_impl<_RangesIterOps>(__first, __last, __value, __comp_lhs_rhs_swapped, __proj);
   }
 
   template <forward_range _Range, class _Type, class _Proj = identity,
@@ -53,7 +54,11 @@ struct __fn {
       return !std::invoke(__comp, __rhs, __lhs);
     };
 
-    return std::__lower_bound_impl(ranges::begin(__r), ranges::end(__r), __value, __comp_lhs_rhs_swapped, __proj);
+    return std::__lower_bound_impl<_RangesIterOps>(ranges::begin(__r),
+                                                   ranges::end(__r),
+                                                   __value,
+                                                   __comp_lhs_rhs_swapped,
+                                                   __proj);
   }
 };
 } // namespace __upper_bound

diff  --git a/libcxx/include/__iterator/advance.h b/libcxx/include/__iterator/advance.h
index 8a43e00968dde..4b8b0dc970a17 100644
--- a/libcxx/include/__iterator/advance.h
+++ b/libcxx/include/__iterator/advance.h
@@ -194,15 +194,6 @@ inline namespace __cpo {
 
 #endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
 
-template <class _Iter, class _Sent>
-_LIBCPP_CONSTEXPR_AFTER_CXX11 void __ranges_advance(_Iter& __first, _Sent& __last) {
-#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
-  return ranges::advance(__first, __last);
-#else
-  return std::advance(__first, __last);
-#endif
-}
-
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ITERATOR_ADVANCE_H

diff  --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index 0870e7c8fa04a..8819078958d38 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -102,16 +102,6 @@ inline namespace __cpo {
 
 #endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
 
-template <class _Iter, class _Sent>
-_LIBCPP_CONSTEXPR_AFTER_CXX11
-typename iterator_traits<_Iter>::
diff erence_type __ranges_distance(_Iter __first, _Sent __second) {
-#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
-  return ranges::distance(__first, __second);
-#else
-  return std::distance(__first, __second);
-#endif
-}
-
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ITERATOR_DISTANCE_H

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index f146703e54301..a12802463684f 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -280,6 +280,7 @@ module std [system] {
       module is_sorted                { private header "__algorithm/is_sorted.h" }
       module is_sorted_until          { private header "__algorithm/is_sorted_until.h" }
       module iter_swap                { private header "__algorithm/iter_swap.h" }
+      module iterator_operations      { private header "__algorithm/iterator_operations.h" }
       module lexicographical_compare  { private header "__algorithm/lexicographical_compare.h" }
       module lower_bound              { private header "__algorithm/lower_bound.h" }
       module make_heap                { private header "__algorithm/make_heap.h" }

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index b210d5c8ad173..484d01c197fe2 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -78,6 +78,7 @@ END-SCRIPT
 #include <__algorithm/is_sorted.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/is_sorted.h'}}
 #include <__algorithm/is_sorted_until.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/is_sorted_until.h'}}
 #include <__algorithm/iter_swap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/iter_swap.h'}}
+#include <__algorithm/iterator_operations.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/iterator_operations.h'}}
 #include <__algorithm/lexicographical_compare.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/lexicographical_compare.h'}}
 #include <__algorithm/lower_bound.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/lower_bound.h'}}
 #include <__algorithm/make_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/make_heap.h'}}

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/lower.bound/lower_bound.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/lower.bound/lower_bound.pass.cpp
index ce9b71c062424..589e748b3e7f6 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/lower.bound/lower_bound.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/lower.bound/lower_bound.pass.cpp
@@ -21,6 +21,7 @@
 #include "test_iterators.h"
 
 #if TEST_STD_VER > 17
+
 TEST_CONSTEXPR bool eq(int a, int b) { return a == b; }
 
 TEST_CONSTEXPR bool test_constexpr() {
@@ -64,6 +65,11 @@ test()
         test(Iter(v.data()), Iter(v.data()+v.size()), x);
 }
 
+void test_instantiation() {
+    auto iter = Cpp20HostileIterator<int*>();
+    std::lower_bound(iter, iter, 0);
+}
+
 int main(int, char**)
 {
     int d[] = {0, 1, 2, 3};

diff  --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 6d3a8e2699edd..e41852d8e28fc 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -794,4 +794,59 @@ class Iterator {
 
 #endif // TEST_STD_VER > 17
 
+template <class Sub, class Iterator>
+struct IteratorAdaptorBase {
+  using OutTraits = std::iterator_traits<Iterator>;
+  using iterator_category = typename OutTraits::iterator_category;
+  using value_type = typename OutTraits::value_type;
+  using pointer = typename OutTraits::pointer;
+  using reference = typename OutTraits::reference;
+  using 
diff erence_type = typename OutTraits::
diff erence_type;
+
+  IteratorAdaptorBase() {}
+  IteratorAdaptorBase(Iterator) {}
+
+  Sub& sub() { return static_cast<Sub&>(*this); }
+  const Sub& sub() const { return static_cast<Sub&>(*this); }
+
+  const Iterator& base() const { return it_; }
+
+  reference get() const { return *it_; }
+  reference operator*() const { return *it_; }
+  pointer operator->() const { return it_; }
+  reference operator[](
diff erence_type) const { return *it_; }
+
+  Sub& operator++() { return static_cast<Sub&>(*this); }
+  Sub& operator--() { return static_cast<Sub&>(*this); }
+  Sub operator++(int) { return static_cast<Sub&>(*this); }
+  Sub operator--(int) { return static_cast<Sub&>(*this); }
+
+  Sub& operator+=(
diff erence_type) { return static_cast<Sub&>(*this); }
+  Sub& operator-=(
diff erence_type) { return static_cast<Sub&>(*this); }
+  bool operator==(Sub) const { return false; }
+  bool operator!=(Sub) const { return false; }
+  bool operator==(Iterator b) const { return *this == Sub(b); }
+  bool operator!=(Iterator b) const { return *this != Sub(b); }
+
+  friend Sub operator+(Sub, 
diff erence_type) { Sub(); }
+  friend Sub operator+(
diff erence_type, Sub) { Sub(); }
+  friend Sub operator-(Sub, 
diff erence_type) { Sub(); }
+  friend 
diff erence_type operator-(Sub, Sub) { return 0; }
+
+  friend bool operator<(Sub, Sub) { return false; }
+  friend bool operator>(Sub, Sub) { return false; }
+  friend bool operator<=(Sub, Sub) { return false; }
+  friend bool operator>=(Sub, Sub) { return false; }
+
+ private:
+  Iterator it_;
+};
+
+template <typename It>
+struct Cpp20HostileIterator
+    : IteratorAdaptorBase<Cpp20HostileIterator<It>, It> {
+  Cpp20HostileIterator() {}
+  Cpp20HostileIterator(It it);
+};
+
 #endif // SUPPORT_TEST_ITERATORS_H


        


More information about the libcxx-commits mailing list