Index: include/__debug =================================================================== --- include/__debug +++ include/__debug @@ -18,12 +18,41 @@ #endif #if _LIBCPP_DEBUG_LEVEL >= 1 -# include -# include # include +# include # ifndef _LIBCPP_ASSERT -# define _LIBCPP_ASSERT(x, m) ((x) ? (void)0 : (_VSTD::fprintf(stderr, "%s\n", m), _VSTD::abort())) +# define _LIBCPP_ASSERT(x, m) ((x) ? (void)0 : (_VSTD::__debug_assert(__FILE__, __func__, __LINE__, m))) # endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +struct __debug_assertion_info { + const char* __file_; + const char* __func_; + unsigned long __line_; + const char* __msg_; + + _LIBCPP_INLINE_VISIBILITY + void __print() const { + _VSTD::fprintf(stderr, "%s:%lu: %s: %s\n", __file_, __line_, __func_, __msg_); + } +}; + +typedef void (*__debug_assertion_handler_t)(__debug_assertion_info const&); + +_LIBCPP_FUNC_VIS +__debug_assertion_handler_t __debug_assertion_handler(__debug_assertion_handler_t = nullptr); + +_LIBCPP_INLINE_VISIBILITY +inline void __debug_assert(const char* __file, const char* __func, + unsigned long __line, const char* __msg) +{ + __debug_assertion_info __info = {__file, __func, __line, __msg}; + __debug_assertion_handler()(__info); +} + +_LIBCPP_END_NAMESPACE_STD + #endif #ifndef _LIBCPP_ASSERT Index: include/algorithm =================================================================== --- include/algorithm +++ include/algorithm @@ -738,6 +738,17 @@ #ifdef _LIBCPP_DEBUG +template +struct __debug_is_comparable : false_type {}; + +template +struct __debug_is_comparable<_Compare, _LHS, _RHS, + typename __void_t()(_VSTD::declval<_LHS>(), _VSTD::declval<_RHS>()) + )>::type +> : true_type {}; + + template struct __debug_less { @@ -746,11 +757,22 @@ template bool operator()(const _Tp& __x, const _Up& __y) { + typedef typename __debug_is_comparable<_Compare, _Up const&, _Tp const&>::type + _CanCompare; bool __r = __comp_(__x, __y); if (__r) - _LIBCPP_ASSERT(!__comp_(__y, __x), "Comparator does not induce a strict weak ordering"); + __do_compare_assert(__y, __x, _CanCompare()); return __r; } + + template + void __do_compare_assert(_LHS const&, _RHS const&, false_type) {} + + template + void __do_compare_assert(_LHS const& __l, _RHS const& __r, true_type) { + _LIBCPP_ASSERT(!__comp_(__l, __r), "Comparator does not induce a strict weak ordering"); + } + }; #endif // _LIBCPP_DEBUG Index: src/debug.cpp =================================================================== --- src/debug.cpp +++ src/debug.cpp @@ -14,9 +14,23 @@ #include "algorithm" #include "__hash_table" #include "mutex" +#include "cstdlib" _LIBCPP_BEGIN_NAMESPACE_STD +void __default_debug_handler(__debug_assertion_info const& info) { + info.__print(); + _VSTD::abort(); +} + +__debug_assertion_handler_t __debug_assertion_handler(__debug_assertion_handler_t new_handler) { + static __debug_assertion_handler_t handle = &__default_debug_handler; + __debug_assertion_handler_t old = handle; + if (new_handler) handle = new_handler; + return old; +} + + _LIBCPP_FUNC_VIS __libcpp_db* __get_db() Index: test/libcxx/algorithms/debug_less.pass.cpp =================================================================== --- /dev/null +++ test/libcxx/algorithms/debug_less.pass.cpp @@ -0,0 +1,164 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// + +// template struct __debug_less + +// __debug_less checks that a comparator actually provides a strict-weak ordering. + +#define _LIBCPP_DEBUG 0 +#include "debug_mode.h" + +#include +#include + +template +struct MyType { + int value; + explicit MyType(int xvalue = 0) : value(xvalue) {} +}; + +template +bool operator<(MyType const& LHS, MyType const& RHS) { + return LHS.value < RHS.value; +} + +struct CompareBase { + static int called; + static void reset() { + called = 0; + } +}; + +int CompareBase::called = 0; + +template +struct GoodComparator : public CompareBase { + bool operator()(ValueType const& lhs, ValueType const& rhs) const { + ++CompareBase::called; + return lhs < rhs; + } +}; + +template +struct BadComparator : public CompareBase { + bool operator()(ValueType const&, ValueType const&) const { + ++CompareBase::called; + return true; + } +}; + +template +struct TwoWayHomoComparator : public CompareBase { + bool operator()(T1 const& lhs, T2 const& rhs) const { + ++CompareBase::called; + return lhs < rhs; + } + + bool operator()(T2 const& lhs, T1 const& rhs) const { + ++CompareBase::called; + return lhs < rhs; + } +}; + +template +struct OneWayHomoComparator : public CompareBase { + bool operator()(T1 const& lhs, T2 const& rhs) const { + ++CompareBase::called; + return lhs < rhs; + } +}; + +using std::__debug_less; + +typedef MyType<0> MT0; +typedef MyType<1> MT1; + +void test_passing() { + int& called = CompareBase::called; + called = 0; + MT0 one(1); + MT0 two(2); + MT1 three(3); + MT1 four(4); + + { + typedef GoodComparator C; + typedef __debug_less D; + + C c; + D d(c); + + assert(d(one, two) == true); + assert(called == 2); + called = 0; + + assert(d(one, one) == false); + assert(called == 1); + called = 0; + + assert(d(two, one) == false); + assert(called == 1); + called = 0; + } + { + typedef TwoWayHomoComparator C; + typedef __debug_less D; + C c; + D d(c); + + assert(d(one, three) == true); + assert(called == 2); + called = 0; + + assert(d(three, one) == false); + assert(called == 1); + called = 0; + } + { + typedef OneWayHomoComparator C; + typedef __debug_less D; + C c; + D d(c); + + assert(d(one, three) == true); + assert(called == 1); + called = 0; + } +} + +void test_failing() { + int& called = CompareBase::called; + called = 0; + MT0 one(1); + MT0 two(2); + + { + typedef BadComparator C; + typedef __debug_less D; + C c; + D d(c); + + try { + d(one, two); + assert(false); + } catch (DebugException const&) { + } + + assert(called == 2); + called = 0; + } +} + +int main() { + getDebugHandler(&throwingDebugHandler); + test_passing(); + test_failing(); +} \ No newline at end of file Index: test/support/debug_mode.h =================================================================== --- /dev/null +++ test/support/debug_mode.h @@ -0,0 +1,34 @@ +#ifndef SUPPORT_DEBUG_MODE_H +#define SUPPORT_DEBUG_MODE_H + +#ifndef _LIBCPP_DEBUG +#error _LIBCPP_DEBUG must be defined before including the header +#endif +#ifdef _LIBCPP_CONFIG +#error debug_mode.h must be included before any libc++ files +#endif + +#include <__debug> +#include + +typedef std::__debug_assertion_handler_t DebugHandlerT; +typedef std::__debug_assertion_info DebugInfo; + +inline DebugHandlerT getDebugHandler(DebugHandlerT new_h = nullptr) { + return std::__debug_assertion_handler(new_h); +} + +void exitZeroHandler(DebugInfo const&) { + std::exit(0); +} + +struct DebugException { + DebugInfo info; + DebugException(DebugInfo xinfo) : info(xinfo) {} +}; + +inline void throwingDebugHandler(DebugInfo const& info) { + throw DebugException(info); +} + +#endif // SUPPORT_DEBUG_MODE_H