diff options
author | Eric Fiselier <eric@efcs.ca> | 2018-10-26 22:54:46 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2018-10-26 22:54:46 +0000 |
commit | e503a1c3b818d66346f3c0198b43280049fc4b7a (patch) | |
tree | 7ad5b2bec3413da7518b9f00d7e58c1cf6d072df | |
parent | eebecb3214eef1137c97d269d7f1691250bd5560 (diff) | |
download | bcm5719-llvm-e503a1c3b818d66346f3c0198b43280049fc4b7a.tar.gz bcm5719-llvm-e503a1c3b818d66346f3c0198b43280049fc4b7a.zip |
Fix PR39458 _LIBCPP_DEBUG breaks heterogeneous compare.
The types/comparators passed to std::upper_bound and std::lower_bound
are not required to provided to provide an operator</comp(...) which
accepts the arguments in reverse order. Nor are the ranges required
to have a strict weak ordering.
However, in debug mode we attempted to check the result of a comparison
with the arguments reversed, which may not compiler.
This patch removes the use of the debug comparator for upper_bound
and lower_bound.
equal_range et al still use debug comparators when they call
__upper_bound and __lower_bound.
See llvm.org/PR39458
llvm-svn: 345434
-rw-r--r-- | libcxx/include/algorithm | 12 | ||||
-rw-r--r-- | libcxx/test/libcxx/algorithms/debug_less.pass.cpp | 51 |
2 files changed, 51 insertions, 12 deletions
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm index 9ce6aa011db..beee6b5b837 100644 --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -4088,14 +4088,8 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator lower_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value_, _Compare __comp) { -#ifdef _LIBCPP_DEBUG - typedef typename add_lvalue_reference<__debug_less<_Compare> >::type _Comp_ref; - __debug_less<_Compare> __c(__comp); - return __lower_bound<_Comp_ref>(__first, __last, __value_, __c); -#else // _LIBCPP_DEBUG typedef typename add_lvalue_reference<_Compare>::type _Comp_ref; return __lower_bound<_Comp_ref>(__first, __last, __value_, __comp); -#endif // _LIBCPP_DEBUG } template <class _ForwardIterator, class _Tp> @@ -4136,14 +4130,8 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator upper_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value_, _Compare __comp) { -#ifdef _LIBCPP_DEBUG - typedef typename add_lvalue_reference<__debug_less<_Compare> >::type _Comp_ref; - __debug_less<_Compare> __c(__comp); - return __upper_bound<_Comp_ref>(__first, __last, __value_, __c); -#else // _LIBCPP_DEBUG typedef typename add_lvalue_reference<_Compare>::type _Comp_ref; return __upper_bound<_Comp_ref>(__first, __last, __value_, __comp); -#endif // _LIBCPP_DEBUG } template <class _ForwardIterator, class _Tp> diff --git a/libcxx/test/libcxx/algorithms/debug_less.pass.cpp b/libcxx/test/libcxx/algorithms/debug_less.pass.cpp index e030f64e5dd..302c5a8376c 100644 --- a/libcxx/test/libcxx/algorithms/debug_less.pass.cpp +++ b/libcxx/test/libcxx/algorithms/debug_less.pass.cpp @@ -161,7 +161,58 @@ void test_failing() { } } +template <int> +struct Tag { + explicit Tag(int v) : value(v) {} + int value; +}; + +template <class = void> +struct FooImp { + explicit FooImp(int x) : x_(x) {} + int x_; +}; + +template <class T> +inline bool operator<(FooImp<T> const& x, Tag<0> y) { + return x.x_ < y.value; +} + +template <class T> +inline bool operator<(Tag<0>, FooImp<T> const&) { + static_assert(sizeof(FooImp<T>) != sizeof(FooImp<T>), "should not be instantiated"); +} + +template <class T> +inline bool operator<(Tag<1> x, FooImp<T> const& y) { + return x.value < y.x_; +} + +template <class T> +inline bool operator<(FooImp<T> const&, Tag<1>) { + static_assert(sizeof(FooImp<T>) != sizeof(FooImp<T>), "should not be instantiated"); +} + +typedef FooImp<> Foo; + +// Test that we don't attempt to call the comparator with the arguments reversed +// for upper_bound and lower_bound since the comparator or type is not required +// to support it, nor does it require the range to have a strict weak ordering. +// See llvm.org/PR39458 +void test_upper_and_lower_bound() { + Foo table[] = {Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)}; + { + Foo* iter = std::lower_bound(std::begin(table), std::end(table), Tag<0>(3)); + assert(iter == (table + 2)); + } + { + Foo* iter = std::upper_bound(std::begin(table), std::end(table), Tag<1>(3)); + assert(iter == (table + 3)); + } +} + int main() { test_passing(); test_failing(); + test_upper_and_lower_bound(); } |