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 @@ -35,10 +35,23 @@ user-provided comparator to be evaluated up to twice as many times as it would be without the debug mode, and causes the library to violate some of the Standard's complexity clauses. -Iterator debugging checks -------------------------- -The library contains various assertions to check the validity of iterators used -by the program. The following containers and classes support iterator debugging: +Iterator bounds checking +------------------------ +The library provides iterators that ensure they are within the bounds of their container when dereferenced. +Arithmetic can be performed on these iterators to create out-of-bounds iterators, but they cannot be dereferenced +when out-of-bounds. The following classes currently provide iterators that have bounds checking: + +- ``std::string`` +- ``std::vector`` (``T != bool``) +- ``std::span`` + +.. TODO: Add support for iterator bounds checking in ``std::string_view`` and ``std::array`` + +Iterator ownership checking +--------------------------- +The library provides iterator ownership checking, which allows catching cases where e.g. +an iterator from container ``X`` is used as a position to insert into container ``Y``. +The following classes support iterator ownership checking: - ``std::string`` - ``std::vector`` (``T != bool``) @@ -48,9 +61,6 @@ - ``std::unordered_set`` - ``std::unordered_multiset`` -The remaining containers do not currently support iterator debugging. -Patches welcome. - Randomizing unspecified behavior -------------------------------- The library supports the randomization of unspecified behavior. For example, randomizing diff --git a/libcxx/include/__debug b/libcxx/include/__debug --- a/libcxx/include/__debug +++ b/libcxx/include/__debug @@ -23,6 +23,10 @@ # define _LIBCPP_DEBUG_RANDOMIZE_UNSPECIFIED_STABILITY #endif +#if defined(_LIBCPP_ENABLE_DEBUG_MODE) && !defined(_LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING) +# define _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING +#endif + #ifdef _LIBCPP_ENABLE_DEBUG_MODE # define _LIBCPP_DEBUG_ASSERT(x, m) _LIBCPP_ASSERT(::std::__libcpp_is_constant_evaluated() || (x), m) #else diff --git a/libcxx/include/span b/libcxx/include/span --- a/libcxx/include/span +++ b/libcxx/include/span @@ -211,7 +211,7 @@ using const_pointer = const _Tp *; using reference = _Tp &; using const_reference = const _Tp &; -#ifdef _LIBCPP_ENABLE_DEBUG_MODE +#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING using iterator = __bounded_iter; #else using iterator = __wrap_iter; @@ -355,14 +355,14 @@ // [span.iter], span iterator support _LIBCPP_INLINE_VISIBILITY constexpr iterator begin() const noexcept { -#ifdef _LIBCPP_ENABLE_DEBUG_MODE +#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING return std::__make_bounded_iter(data(), data(), data() + size()); #else return iterator(this, data()); #endif } _LIBCPP_INLINE_VISIBILITY constexpr iterator end() const noexcept { -#ifdef _LIBCPP_ENABLE_DEBUG_MODE +#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING return std::__make_bounded_iter(data() + size(), data(), data() + size()); #else return iterator(this, data() + size()); @@ -394,7 +394,7 @@ using const_pointer = const _Tp *; using reference = _Tp &; using const_reference = const _Tp &; -#ifdef _LIBCPP_ENABLE_DEBUG_MODE +#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING using iterator = __bounded_iter; #else using iterator = __wrap_iter; @@ -522,14 +522,14 @@ // [span.iter], span iterator support _LIBCPP_INLINE_VISIBILITY constexpr iterator begin() const noexcept { -#ifdef _LIBCPP_ENABLE_DEBUG_MODE +#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING return std::__make_bounded_iter(data(), data(), data() + size()); #else return iterator(this, data()); #endif } _LIBCPP_INLINE_VISIBILITY constexpr iterator end() const noexcept { -#ifdef _LIBCPP_ENABLE_DEBUG_MODE +#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING return std::__make_bounded_iter(data() + size(), data(), data() + size()); #else return iterator(this, data() + size()); diff --git a/libcxx/include/string b/libcxx/include/string --- a/libcxx/include/string +++ b/libcxx/include/string @@ -682,6 +682,7 @@ "[allocator.requirements] states that rebinding an allocator to the same type should result in the " "original allocator"); + // TODO: Implement iterator bounds checking without requiring the global database. typedef __wrap_iter iterator; typedef __wrap_iter const_iterator; typedef std::reverse_iterator reverse_iterator; diff --git a/libcxx/include/vector b/libcxx/include/vector --- a/libcxx/include/vector +++ b/libcxx/include/vector @@ -350,6 +350,7 @@ typedef typename __alloc_traits::difference_type difference_type; typedef typename __alloc_traits::pointer pointer; typedef typename __alloc_traits::const_pointer const_pointer; + // TODO: Implement iterator bounds checking without requiring the global database. typedef __wrap_iter iterator; typedef __wrap_iter const_iterator; typedef std::reverse_iterator reverse_iterator;