diff --git a/libcxx/docs/DesignDocs/DebugMode.rst b/libcxx/docs/DesignDocs/DebugMode.rst --- a/libcxx/docs/DesignDocs/DebugMode.rst +++ b/libcxx/docs/DesignDocs/DebugMode.rst @@ -80,3 +80,66 @@ str.insert(bad_it, '!'); // causes debug assertion // control flow doesn't return } + +The above example overrides the bug handler at run-time, but it is also possible +to override it at compile time using the ``_LIBCPP_DEBUG_FUNCTION_INVOCATION`` +macro. This macro takes 2 arguments: the expression ``x`` to trigger the +assertion, and an error message ``m``. The default invocation uses these +arguments to construct the ``std::__libcpp_debug_info`` argument, but you can do +whatever you like with them (including nothing): + +.. code-block:: cpp + + #define _LIBCPP_DEBUG_FUNCTION_INVOCATION(x, m) abort() + #include + int main(int, char**) { + std::string::iterator bad_it; + std::string str("hello world"); + str.insert(bad_it, '!'); // causes debug assertion + // control flow doesn't return + } + +Debug Mode Checks +================= + +Libc++'s debug mode offers two levels of checking. The first enables various +precondition checks throughout libc++. The second additionally enables +"iterator debugging" which checks the validity of iterators used by the program. + +Basic Checks +============ + +These checks are enabled when ``_LIBCPP_DEBUG`` is defined to either 0 or 1. + +The following checks are enabled by ``_LIBCPP_DEBUG``: + + * Many algorithms, such as ``binary_search``, ``merge``, ``next_permutation``, and ``sort``, + wrap the user-provided comparator to assert that `!comp(y, x)` whenever + `comp(x, y)`. This can cause the user-provided comparator to be evaluated + up to twice as many times as it would be without ``_LIBCPP_DEBUG``, and + causes the library to violate some of the Standard's complexity clauses. + + * Bounds checks in containers (e.g ``operator[]``) + + * Validity of dereferencing (e.g. ``operator*``, ``operator->``) + + * Invariants and preconditions (e.g. ``has_value`` for ``std::optional``) + +Iterator Debugging Checks +========================= + +These checks are enabled when ``_LIBCPP_DEBUG`` is defined to 1. + +The following containers and STL classes support iterator debugging: + + * ``std::string`` + * ``std::vector`` (``T != bool``) + * ``std::list`` + * ``std::locale`` + * ``std::unordered_map`` + * ``std::unordered_multimap`` + * ``std::unordered_set`` + * ``std::unordered_multiset`` + +The remaining includes do not currently support iterator debugging. +Patches welcome. diff --git a/libcxx/include/__debug b/libcxx/include/__debug --- a/libcxx/include/__debug +++ b/libcxx/include/__debug @@ -27,15 +27,19 @@ # include #endif +#if !defined(_LIBCPP_DEBUG_FUNCTION_INVOCATION) +# define _LIBCPP_DEBUG_FUNCTION_INVOCATION(x, m) _VSTD::__libcpp_debug_function(_VSTD::__libcpp_debug_info(__FILE__, __LINE__, x, m)) +#endif + #if _LIBCPP_DEBUG_LEVEL == 0 # define _LIBCPP_DEBUG_ASSERT(x, m) ((void)0) # define _LIBCPP_ASSERT_IMPL(x, m) ((void)0) #elif _LIBCPP_DEBUG_LEVEL == 1 # define _LIBCPP_DEBUG_ASSERT(x, m) ((void)0) -# define _LIBCPP_ASSERT_IMPL(x, m) ((x) ? (void)0 : _VSTD::__libcpp_debug_function(_VSTD::__libcpp_debug_info(__FILE__, __LINE__, #x, m))) +# define _LIBCPP_ASSERT_IMPL(x, m) ((x) ? (void)0 : _LIBCPP_DEBUG_FUNCTION_INVOCATION(#x, m)) #elif _LIBCPP_DEBUG_LEVEL == 2 # define _LIBCPP_DEBUG_ASSERT(x, m) _LIBCPP_ASSERT(x, m) -# define _LIBCPP_ASSERT_IMPL(x, m) ((x) ? (void)0 : _VSTD::__libcpp_debug_function(_VSTD::__libcpp_debug_info(__FILE__, __LINE__, #x, m))) +# define _LIBCPP_ASSERT_IMPL(x, m) ((x) ? (void)0 : _LIBCPP_DEBUG_FUNCTION_INVOCATION(#x, m)) #else # error _LIBCPP_DEBUG_LEVEL must be one of 0, 1, 2 #endif diff --git a/libcxx/test/libcxx/debug/debug_function_invocation_macro.pass.cpp b/libcxx/test/libcxx/debug/debug_function_invocation_macro.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/debug/debug_function_invocation_macro.pass.cpp @@ -0,0 +1,25 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DEBUG=1 +// UNSUPPORTED: libcxx-no-debug-mode + +// Test that the _LIBCPP_DEBUG_FUNCTION_INVOCATION macro works. + +// Define macro so that it doesn't abort. +#define _LIBCPP_DEBUG_FUNCTION_INVOCATION(x, m) std::_Exit(EXIT_SUCCESS) + +#include + +int main(int, char**) { + std::string::iterator bad_it; + std::string str("hello world"); + str.insert(bad_it, '!'); + return EXIT_FAILURE; +}