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

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@345434 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Eric Fiselier
2018-10-26 22:54:46 +00:00
parent 924b8f34f6
commit 293b83d6e6
2 changed files with 51 additions and 12 deletions

View File

@@ -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>

View File

@@ -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();
}