diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -312,6 +312,7 @@ __concepts/totally_ordered.h __condition_variable/condition_variable.h __config + __config_hardening __coroutine/coroutine_handle.h __coroutine/coroutine_traits.h __coroutine/noop_coroutine_handle.h @@ -418,6 +419,7 @@ __fwd/subrange.h __fwd/tuple.h __hash_table + __hardening.h __ios/fpos.h __iterator/access.h __iterator/advance.h diff --git a/libcxx/include/__config_hardening b/libcxx/include/__config_hardening new file mode 100644 --- /dev/null +++ b/libcxx/include/__config_hardening @@ -0,0 +1,124 @@ +// -*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___CONFIG_HARDENING +#define _LIBCPP___CONFIG_HARDENING + +#include <__config> +#include <__hardening.h> + +#ifndef _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER +# pragma GCC system_header +#endif + +// Available ABI-breaking checks: + +// Changes the iterator type of select containers (see below) to a bounded iterator that: +// - keeps track of which container it refers to; +// - keeps track of whether it's valid or not and ASSERTS validity on every dereference; +// - allows enabling the check that a given range is valid (sentinel is reachable from the begin iterator, etc.) in +// constant time. +// +// ABI impact: increases the size and changes the layout of containers (to store additional information that is used by +// bounded iterators), changes the return type of container functions that return iterators. +// +// Supported containers: +// - `span`; +// - `string_view`; +// - `array`. +// #define _LIBCPP_ASSERTIONS_ENABLE_HARDENING_BOUNDED_ITERATORS + +#ifdef _LIBCPP_ENABLE_HARDENING_MODE_ABI_BREAK +# define _LIBCPP_ASSERTIONS_ENABLE_BOUNDED_ITERATORS +// TODO: more checks... +#endif // _LIBCPP_ENABLE_HARDENING_MODE_ABI_BREAK + +// Available checks: + +// Checks that ranges (whether expressed as an iterator pair, an iterator and a count, or a `std::range`) given as +// input to library functions are valid: +// - both iterators refer to the same container; +// - the sentinel is reachable from the begin iterator; +// - where applicable, the input range and the output range do not overlap. +// +// The level of support depends on whether bounded iterators are enabled in the ABI. If bounded iterators are not +// enabled, the library falls back to more basic checks. +// #define _LIBCPP_ASSERTIONS_ENABLE_CHECK_INVALID_RANGE + +// Checks any calls into a container that attempt to access a non-existent element. Types like `optional` and `function` +// are considered one-element containers for the purposes of this check. This check only applies to member functions of +// containers, not any access made purely via iterators. +// #define _LIBCPP_ASSERTIONS_ENABLE_CHECK_BAD_CONTAINER_ACCESS + +// TODO: +//#define _LIBCPP_ASSERTIONS_ENABLE_DEBUG_RANDOMIZE_UNSPECIFIED_BEHAVIOR +//#define _LIBCPP_ASSERTIONS_ENABLE_DEBUG_CHECK_UNSORTED_ETC_INPUT + +#if defined(_LIBCPP_ENABLE_HARDENING_MODE) + +# define _LIBCPP_ENABLE_HARDENING_ASSERTIONS + +# define _LIBCPP_ASSERTIONS_ENABLE_CHECK_INVALID_RANGE +# define _LIBCPP_ASSERTIONS_ENABLE_CHECK_BAD_CONTAINER_ACCESS +// TODO: more checks to be added here... + +#elif defined(_LIBCPP_ENABLE_DEBUG_MODE) + +# define _LIBCPP_ENABLE_HARDENING_ASSERTIONS + +# define _LIBCPP_ASSERTIONS_ENABLE_CHECK_INVALID_RANGE +# define _LIBCPP_ASSERTIONS_ENABLE_CHECK_BAD_CONTAINER_ACCESS +// TODO: +//# define _LIBCPP_ASSERTIONS_ENABLE_DEBUG_RANDOMIZE_UNSPECIFIED_BEHAVIOR +//# define _LIBCPP_ASSERTIONS_ENABLE_DEBUG_CHECK_UNSORTED_ETC_INPUT +// TODO: more checks to be added here... + +#endif + +// The assertions machinery. + +#ifdef _LIBCPP_ENABLE_HARDENING_ASSERTIONS +# define _LIBCPP_HARDENING_ASSERT(expression, message) \ + (__builtin_expect(static_cast(expression), 1) \ + ? (void)0 \ + : _LIBCPP_VERBOSE_ABORT( \ + "%s:%d: assertion %s failed: %s", __builtin_FILE(), __builtin_LINE(), #expression, message)) + +#else +# define _LIBCPP_HARDENING_ASSERT(expression, message) ((void)0) +#endif + +// Definitions of different categories of assertions. + +#ifdef _LIBCPP_ASSERTIONS_ENABLE_CHECK_INVALID_RANGE + +# define _LIBCPP_ASSERT_CHECK_INVALID_RANGE1(range, message) \ + _LIBCPP_HARDENING_ASSERT(__hardening::_CheckInvalidRange(range), message); +# define _LIBCPP_ASSERT_CHECK_INVALID_RANGE2(first, last, message) \ + _LIBCPP_HARDENING_ASSERT(__hardening::_CheckInvalidRange(first, last), message); +// TODO: _LIBCPP_ASSERT_CHECK_OVERLAPPING_RANGES(range1, range2, message); + +#else + +# define _LIBCPP_ASSERT_CHECK_INVALID_RANGE1(range, message) ((void)0) +# define _LIBCPP_ASSERT_CHECK_INVALID_RANGE2(first, last, message) ((void)0) + +#endif // _LIBCPP_ASSERTIONS_ENABLE_CHECK_INVALID_RANGE + +#ifdef _LIBCPP_ASSERTIONS_ENABLE_CHECK_BAD_CONTAINER_ACCESS +# define _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(condition, message) \ + _LIBCPP_HARDENING_ASSERT(condition, message); +#else +# define _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(condition, message) ((void)0) +#endif // _LIBCPP_ASSERTIONS_ENABLE_CHECK_BAD_CONTAINER_ACCESS + +// TODO: +// _LIBCPP_ASSERT_CHECK_FOREIGN_ITERATOR(container, iter, message); + +#endif // _LIBCPP___CONFIG_HARDENING diff --git a/libcxx/include/__hardening.h b/libcxx/include/__hardening.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__hardening.h @@ -0,0 +1,51 @@ +// -*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___HARDENING_H +#define _LIBCPP___HARDENING_H + +#include <__config> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__utility/forward.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +template +_LIBCPP_HIDE_FROM_ABI +constexpr bool __refer_to_same_range(const _Iter&, const _Iter&) { + return true; +} + +namespace __hardening { + +// TODO: specialization for random access iterators. + +template +_LIBCPP_HIDE_FROM_ABI +constexpr bool _CheckInvalidRange(const _Iter& __first, const _Iter& __last) { + return std::__refer_to_same_range(__first, __last) && __first <= __last; +} + +template +_LIBCPP_HIDE_FROM_ABI +constexpr bool _CheckInvalidRange(_Range&& __range) { + return __hardening::_CheckInvalidRange( + ranges::begin(std::forward<_Range>(__range)), ranges::end(std::forward<_Range>(__range))); +} + +} // __hardening + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___HARDENING_H diff --git a/libcxx/include/__iterator/bounded_iter.h b/libcxx/include/__iterator/bounded_iter.h --- a/libcxx/include/__iterator/bounded_iter.h +++ b/libcxx/include/__iterator/bounded_iter.h @@ -12,6 +12,7 @@ #include <__assert> #include <__config> +#include <__config_hardening> #include <__iterator/iterator_traits.h> #include <__memory/pointer_traits.h> #include <__type_traits/enable_if.h> @@ -210,6 +211,12 @@ return __bounded_iter<_It>(std::move(__it), std::move(__begin), std::move(__end)); } +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR +bool __refer_to_same_range(const __bounded_iter<_It>& __iter1, const __bounded_iter<_It>& __iter2) { + return __iter1.__begin_ == __iter2.__begin_ && __iter1.__end_ == __iter2.__end_; +} + #if _LIBCPP_STD_VER <= 17 template struct __libcpp_is_contiguous_iterator<__bounded_iter<_Iterator> > : true_type {}; diff --git a/libcxx/include/span b/libcxx/include/span --- a/libcxx/include/span +++ b/libcxx/include/span @@ -129,6 +129,7 @@ #include <__assert> // all public C++ headers provide the assertion handler #include <__config> +#include <__config_hardening> #include <__debug> #include <__fwd/span.h> #include <__iterator/bounded_iter.h> @@ -214,7 +215,7 @@ using const_pointer = const _Tp *; using reference = _Tp &; using const_reference = const _Tp &; -#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING +#if defined(_LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING) || defined(_LIBCPP_ASSERTIONS_ENABLE_BOUNDED_ITERATORS) using iterator = __bounded_iter; #else using iterator = __wrap_iter; @@ -236,6 +237,8 @@ : __data_{_VSTD::to_address(__first)} { (void)__count; _LIBCPP_ASSERT(_Extent == __count, "size mismatch in span's constructor (iterator, len)"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(_Extent == __count, "span::span(Iter, Sent)"); + _LIBCPP_ASSERT_CHECK_INVALID_RANGE2(__first, __count, "span::span(Iter, size_type)"); } template <__span_compatible_iterator _It, __span_compatible_sentinel_for<_It> _End> @@ -247,6 +250,8 @@ _LIBCPP_ASSERT(__dist >= 0, "invalid range in span's constructor (iterator, sentinel)"); _LIBCPP_ASSERT( __dist == _Extent, "invalid range in span's constructor (iterator, sentinel): last - first != extent"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(_Extent == __last - __first, "span::span(Iter, Sent)"); + _LIBCPP_ASSERT_CHECK_INVALID_RANGE2(__first, __last, "span::span(Iter, Sent)"); } _LIBCPP_INLINE_VISIBILITY constexpr span(type_identity_t (&__arr)[_Extent]) noexcept : __data_{__arr} {} @@ -264,6 +269,8 @@ _LIBCPP_INLINE_VISIBILITY constexpr explicit span(_Range&& __r) : __data_{ranges::data(__r)} { _LIBCPP_ASSERT(ranges::size(__r) == _Extent, "size mismatch in span's constructor (range)"); + _LIBCPP_ASSERT_CHECK_INVALID_RANGE1(std::forward<_Range>(__r), "span::span(Range)"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(ranges::size(__r) == _Extent, "span::span(Range)"); } template <__span_array_convertible _OtherElementType> @@ -274,7 +281,10 @@ template <__span_array_convertible _OtherElementType> _LIBCPP_INLINE_VISIBILITY constexpr explicit span(const span<_OtherElementType, dynamic_extent>& __other) noexcept - : __data_{__other.data()} { _LIBCPP_ASSERT(_Extent == __other.size(), "size mismatch in span's constructor (other span)"); } + : __data_{__other.data()} { + _LIBCPP_ASSERT(_Extent == __other.size(), "size mismatch in span's constructor (other span)"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(_Extent == __other.size(), "span::span(Range)"); + } // ~span() noexcept = default; @@ -299,6 +309,8 @@ constexpr span first(size_type __count) const noexcept { _LIBCPP_ASSERT(__count <= size(), "span::first(count): count out of range"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(__count <= size(), "span::first(size_type)"); + return {data(), __count}; } @@ -306,6 +318,7 @@ constexpr span last(size_type __count) const noexcept { _LIBCPP_ASSERT(__count <= size(), "span::last(count): count out of range"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(__count <= size(), "span::last(size_type)"); return {data() + size() - __count, __count}; } @@ -327,9 +340,11 @@ subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept { _LIBCPP_ASSERT(__offset <= size(), "span::subspan(offset, count): offset out of range"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(__offset <= size(), "span::subspan(offset, count)"); if (__count == dynamic_extent) return {data() + __offset, size() - __offset}; _LIBCPP_ASSERT(__count <= size() - __offset, "span::subspan(offset, count): offset + count out of range"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(__count <= size() - __offset, "span::subspan(offset, count)"); return {data() + __offset, __count}; } @@ -340,18 +355,21 @@ _LIBCPP_INLINE_VISIBILITY constexpr reference operator[](size_type __idx) const noexcept { _LIBCPP_ASSERT(__idx < size(), "span::operator[](index): index out of range"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(__idx < size(), "span::operator[]"); return __data_[__idx]; } _LIBCPP_INLINE_VISIBILITY constexpr reference front() const noexcept { _LIBCPP_ASSERT(!empty(), "span::front() on empty span"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(!empty(), "span::front()"); return __data_[0]; } _LIBCPP_INLINE_VISIBILITY constexpr reference back() const noexcept { _LIBCPP_ASSERT(!empty(), "span::back() on empty span"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(!empty(), "span::back()"); return __data_[size()-1]; } @@ -359,14 +377,14 @@ // [span.iter], span iterator support _LIBCPP_INLINE_VISIBILITY constexpr iterator begin() const noexcept { -#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING +#if defined(_LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING) || defined(_LIBCPP_ASSERTIONS_ENABLE_BOUNDED_ITERATORS) 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_DEBUG_ITERATOR_BOUNDS_CHECKING +#if defined(_LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING) || defined(_LIBCPP_ASSERTIONS_ENABLE_BOUNDED_ITERATORS) return std::__make_bounded_iter(data() + size(), data(), data() + size()); #else return iterator(this, data() + size()); @@ -398,7 +416,7 @@ using const_pointer = const _Tp *; using reference = _Tp &; using const_reference = const _Tp &; -#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING +#if defined(_LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING) || defined(_LIBCPP_ASSERTIONS_ENABLE_BOUNDED_ITERATORS) using iterator = __bounded_iter; #else using iterator = __wrap_iter; @@ -416,12 +434,15 @@ template <__span_compatible_iterator _It> _LIBCPP_INLINE_VISIBILITY constexpr span(_It __first, size_type __count) - : __data_{_VSTD::to_address(__first)}, __size_{__count} {} + : __data_{_VSTD::to_address(__first)}, __size_{__count} { + _LIBCPP_ASSERT_CHECK_INVALID_RANGE2(__first, __count, "span::span(Iter, size_type)"); + } template <__span_compatible_iterator _It, __span_compatible_sentinel_for<_It> _End> _LIBCPP_INLINE_VISIBILITY constexpr span(_It __first, _End __last) : __data_(_VSTD::to_address(__first)), __size_(__last - __first) { _LIBCPP_ASSERT(__last - __first >= 0, "invalid range in span's constructor (iterator, sentinel)"); + _LIBCPP_ASSERT_CHECK_INVALID_RANGE2(__first, __last, "span::span(Iter, Sent)"); } template @@ -439,7 +460,9 @@ template <__span_compatible_range _Range> _LIBCPP_INLINE_VISIBILITY - constexpr span(_Range&& __r) : __data_(ranges::data(__r)), __size_{ranges::size(__r)} {} + constexpr span(_Range&& __r) : __data_(ranges::data(__r)), __size_{ranges::size(__r)} { + _LIBCPP_ASSERT_CHECK_INVALID_RANGE1(std::forward<_Range>(__r), "span::span(Range)"); + } template <__span_array_convertible _OtherElementType, size_t _OtherExtent> _LIBCPP_INLINE_VISIBILITY @@ -453,6 +476,7 @@ constexpr span first() const noexcept { _LIBCPP_ASSERT(_Count <= size(), "span::first(): Count out of range"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(_Count <= size(), "span::first()"); return span{data(), _Count}; } @@ -461,6 +485,7 @@ constexpr span last() const noexcept { _LIBCPP_ASSERT(_Count <= size(), "span::last(): Count out of range"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(_Count <= size(), "span::last()"); return span{data() + size() - _Count, _Count}; } @@ -468,6 +493,7 @@ constexpr span first(size_type __count) const noexcept { _LIBCPP_ASSERT(__count <= size(), "span::first(count): count out of range"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(__count <= size(), "span::first(size_type)"); return {data(), __count}; } @@ -475,6 +501,7 @@ constexpr span last (size_type __count) const noexcept { _LIBCPP_ASSERT(__count <= size(), "span::last(count): count out of range"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(__count <= size(), "span::last(size_type)"); return {data() + size() - __count, __count}; } @@ -484,6 +511,9 @@ { _LIBCPP_ASSERT(_Offset <= size(), "span::subspan(): Offset out of range"); _LIBCPP_ASSERT(_Count == dynamic_extent || _Count <= size() - _Offset, "span::subspan(): Offset + Count out of range"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(_Offset <= size(), "span::subspan"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(_Count == dynamic_extent || _Count <= size() - _Offset, "span::subspan"); + return span{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count}; } @@ -492,9 +522,11 @@ subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept { _LIBCPP_ASSERT(__offset <= size(), "span::subspan(offset, count): offset out of range"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(__offset <= size(), "span::subspan(offset, count)"); if (__count == dynamic_extent) return {data() + __offset, size() - __offset}; _LIBCPP_ASSERT(__count <= size() - __offset, "span::subspan(offset, count): offset + count out of range"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(__count <= size() - __offset, "span::subspan(offset, count)"); return {data() + __offset, __count}; } @@ -505,18 +537,21 @@ _LIBCPP_INLINE_VISIBILITY constexpr reference operator[](size_type __idx) const noexcept { _LIBCPP_ASSERT(__idx < size(), "span::operator[](index): index out of range"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(__idx < size(), "span::operator[]"); return __data_[__idx]; } _LIBCPP_INLINE_VISIBILITY constexpr reference front() const noexcept { _LIBCPP_ASSERT(!empty(), "span::front() on empty span"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(!empty(), "span::front()"); return __data_[0]; } _LIBCPP_INLINE_VISIBILITY constexpr reference back() const noexcept { _LIBCPP_ASSERT(!empty(), "span::back() on empty span"); + _LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(!empty(), "span::back()"); return __data_[size()-1]; } @@ -525,14 +560,14 @@ // [span.iter], span iterator support _LIBCPP_INLINE_VISIBILITY constexpr iterator begin() const noexcept { -#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING +#if defined(_LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING) || defined(_LIBCPP_ASSERTIONS_ENABLE_BOUNDED_ITERATORS) 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_DEBUG_ITERATOR_BOUNDS_CHECKING +#if defined(_LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING) || defined(_LIBCPP_ASSERTIONS_ENABLE_BOUNDED_ITERATORS) return std::__make_bounded_iter(data() + size(), data(), data() + size()); #else return iterator(this, data() + size()); diff --git a/libcxx/include/vector b/libcxx/include/vector --- a/libcxx/include/vector +++ b/libcxx/include/vector @@ -468,6 +468,8 @@ template <_ContainerCompatibleRange<_Tp> _Range> _LIBCPP_HIDE_FROM_ABI constexpr vector(from_range_t, _Range&& __range, const allocator_type& __alloc = allocator_type()) : __end_cap_(nullptr, __alloc) { + //_LIBCPP_ASSERT_CHECK_INVALID_RANGE1(std::forward<_Range>(__range), "vector::vector(from_range_t)"); + if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) { auto __n = static_cast(ranges::distance(__range)); __init_with_size(ranges::begin(__range), ranges::end(__range), __n); @@ -547,6 +549,9 @@ template <_ContainerCompatibleRange<_Tp> _Range> _LIBCPP_HIDE_FROM_ABI constexpr void assign_range(_Range&& __range) { + //_LIBCPP_ASSERT_CHECK_INVALID_RANGE1(std::forward<_Range>(__range), "vector::assign_range"); + //_LIBCPP_ASSERT_CHECK_OVERLAPPING_RANGES2(*this, std::forward<_Range>(__range), "vector::assign_range"); + if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) { auto __n = static_cast(ranges::distance(__range)); __assign_with_size(ranges::begin(__range), ranges::end(__range), __n); @@ -621,21 +626,25 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference front() _NOEXCEPT { _LIBCPP_ASSERT(!empty(), "front() called on an empty vector"); + //_LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(!empty(), "vector::front()"); return *this->__begin_; } _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference front() const _NOEXCEPT { _LIBCPP_ASSERT(!empty(), "front() called on an empty vector"); + //_LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(!empty(), "vector::front()"); return *this->__begin_; } _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference back() _NOEXCEPT { _LIBCPP_ASSERT(!empty(), "back() called on an empty vector"); + //_LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(!empty(), "vector::back()"); return *(this->__end_ - 1); } _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference back() const _NOEXCEPT { _LIBCPP_ASSERT(!empty(), "back() called on an empty vector"); + //_LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(!empty(), "vector::back()"); return *(this->__end_ - 1); } @@ -663,6 +672,8 @@ template <_ContainerCompatibleRange<_Tp> _Range> _LIBCPP_HIDE_FROM_ABI constexpr void append_range(_Range&& __range) { + //_LIBCPP_ASSERT_CHECK_INVALID_RANGE1(std::forward<_Range>(__range), "vector::append_range"); + //_LIBCPP_ASSERT_CHECK_OVERLAPPING_RANGES1(*this, std::forward<_Range>(__range), "vector::append_range"); insert_range(end(), std::forward<_Range>(__range)); } #endif @@ -690,6 +701,10 @@ template <_ContainerCompatibleRange<_Tp> _Range> _LIBCPP_HIDE_FROM_ABI constexpr iterator insert_range(const_iterator __position, _Range&& __range) { + //_LIBCPP_ASSERT_CHECK_INVALID_RANGE1(std::forward<_Range>(__range), "vector::insert_range"); + //_LIBCPP_ASSERT_CHECK_OVERLAPPING_RANGES1(*this, std::forward<_Range>(__range), "vector::insert_range"); + //_LIBCPP_ASSERT_CHECK_FOREIGN_ITERATOR(*this, __position, "vector::insert_range"); + if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) { auto __n = static_cast(ranges::distance(__range)); return __insert_with_size(__position, ranges::begin(__range), ranges::end(__range), __n); @@ -1253,6 +1268,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<_Tp, _Allocator>::vector(_InputIterator __first, _InputIterator __last) { + //_LIBCPP_ASSERT_CHECK_INVALID_RANGE2(__first, __last, "vector::vector(Iter, Iter)"); __init_with_sentinel(__first, __last); } @@ -1264,6 +1280,7 @@ vector<_Tp, _Allocator>::vector(_InputIterator __first, _InputIterator __last, const allocator_type& __a) : __end_cap_(nullptr, __a) { + //_LIBCPP_ASSERT_CHECK_INVALID_RANGE2(__first, __last, "vector::vector(Iter, Iter, Alloc)"); __init_with_sentinel(__first, __last); } @@ -1274,6 +1291,8 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<_Tp, _Allocator>::vector(_ForwardIterator __first, _ForwardIterator __last) { + //_LIBCPP_ASSERT_CHECK_INVALID_RANGE2(__first, __last, "vector::vector(Iter, Iter)"); + size_type __n = static_cast(std::distance(__first, __last)); __init_with_size(__first, __last, __n); } @@ -1286,6 +1305,8 @@ vector<_Tp, _Allocator>::vector(_ForwardIterator __first, _ForwardIterator __last, const allocator_type& __a) : __end_cap_(nullptr, __a) { + //_LIBCPP_ASSERT_CHECK_INVALID_RANGE2(__first, __last, "vector::vector(Iter, Iter, Alloc)"); + size_type __n = static_cast(std::distance(__first, __last)); __init_with_size(__first, __last, __n); } @@ -1447,6 +1468,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::assign(_InputIterator __first, _InputIterator __last) { + //_LIBCPP_ASSERT_CHECK_INVALID_RANGE2(__first, __last, "vector::assign()"); + //_LIBCPP_ASSERT_CHECK_OVERLAPPING_RANGES2(begin(), end(), __first, __last, "vector::assign"); + __assign_with_sentinel(__first, __last); } @@ -1466,6 +1490,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::assign(_ForwardIterator __first, _ForwardIterator __last) { + //_LIBCPP_ASSERT_CHECK_INVALID_RANGE2(__first, __last, "vector::assign()"); + //_LIBCPP_ASSERT_CHECK_OVERLAPPING_RANGES2(begin(), end(), __first, __last, "vector::assign"); + __assign_with_size(__first, __last, std::distance(__first, __last)); } @@ -1502,6 +1529,9 @@ void vector<_Tp, _Allocator>::assign(size_type __n, const_reference __u) { + // TODO: _LIBCPP_ASSERT_CHECK_OVERLAPPING_RANGES2( + // begin(), end(), __make_iter(__u), __make_iter(__u) + 1, "vector::assign"); + if (__n <= capacity()) { size_type __s = size(); @@ -1563,6 +1593,7 @@ vector<_Tp, _Allocator>::operator[](size_type __n) _NOEXCEPT { _LIBCPP_ASSERT(__n < size(), "vector[] index out of bounds"); + //_LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(__n < size(), "vector::operator[]"); return this->__begin_[__n]; } @@ -1573,6 +1604,7 @@ vector<_Tp, _Allocator>::operator[](size_type __n) const _NOEXCEPT { _LIBCPP_ASSERT(__n < size(), "vector[] index out of bounds"); + //_LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(__n < size(), "vector::operator[]"); return this->__begin_[__n]; } @@ -1719,6 +1751,7 @@ vector<_Tp, _Allocator>::pop_back() { _LIBCPP_ASSERT(!empty(), "vector::pop_back called on an empty vector"); + //_LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(!empty(), "vector::pop_back()"); this->__destruct_at_end(this->__end_ - 1); } @@ -1732,6 +1765,9 @@ "vector::erase(iterator) called with an iterator not referring to this vector"); _LIBCPP_ASSERT(__position != end(), "vector::erase(iterator) called with a non-dereferenceable iterator"); + //_LIBCPP_ASSERT_CHECK_BAD_CONTAINER_ACCESS(__position < end(), "vector::erase(Iter)"); + //_LIBCPP_ASSERT_CHECK_FOREIGN_ITERATOR(*this, __position, "vector::erase(Iter)"); + difference_type __ps = __position - cbegin(); pointer __p = this->__begin_ + __ps; this->__destruct_at_end(std::move(__p + 1, this->__end_, __p)); @@ -1751,6 +1787,9 @@ "vector::erase(iterator, iterator) called with an iterator not referring to this vector"); _LIBCPP_ASSERT(__first <= __last, "vector::erase(first, last) called with invalid range"); + //_LIBCPP_ASSERT_CHECK_INVALID_RANGE2(__first, __last, "vector::erase(Iter, Iter)"); + //_LIBCPP_ASSERT_CHECK_FOREIGN_ITERATOR(*this, __first, "vector::erase(Iter, Iter)"); + pointer __p = this->__begin_ + (__first - begin()); if (__first != __last) { this->__destruct_at_end(std::move(__p + (__last - __first), this->__end_, __p)); @@ -1787,6 +1826,8 @@ { _LIBCPP_DEBUG_ASSERT(__get_const_db()->__find_c_from_i(std::addressof(__position)) == this, "vector::insert(iterator, x) called with an iterator not referring to this vector"); + //_LIBCPP_ASSERT_CHECK_FOREIGN_ITERATOR(this, __position, "vector::insert(const_iterator, const_reference)"); + pointer __p = this->__begin_ + (__position - begin()); // We can't compare unrelated pointers inside constant expressions if (!__libcpp_is_constant_evaluated() && this->__end_ < this->__end_cap()) @@ -1821,6 +1862,8 @@ { _LIBCPP_DEBUG_ASSERT(__get_const_db()->__find_c_from_i(std::addressof(__position)) == this, "vector::insert(iterator, x) called with an iterator not referring to this vector"); + //_LIBCPP_ASSERT_CHECK_FOREIGN_ITERATOR(this, __position, "vector::insert(const_iterator, value_type&&)"); + pointer __p = this->__begin_ + (__position - begin()); if (this->__end_ < this->__end_cap()) { @@ -1852,6 +1895,8 @@ { _LIBCPP_DEBUG_ASSERT(__get_const_db()->__find_c_from_i(std::addressof(__position)) == this, "vector::emplace(iterator, x) called with an iterator not referring to this vector"); + //_LIBCPP_ASSERT_CHECK_FOREIGN_ITERATOR(*this, __position, "vector::emplace(const_iterator, Args&&...)"); + pointer __p = this->__begin_ + (__position - begin()); if (this->__end_ < this->__end_cap()) { @@ -1883,6 +1928,9 @@ { _LIBCPP_DEBUG_ASSERT(__get_const_db()->__find_c_from_i(std::addressof(__position)) == this, "vector::insert(iterator, n, x) called with an iterator not referring to this vector"); + //_LIBCPP_ASSERT_CHECK_FOREIGN_ITERATOR( + //*this, __position, "vector::insert(const_iterator, size_type, const_reference)"); + pointer __p = this->__begin_ + (__position - begin()); if (__n > 0) { @@ -1923,6 +1971,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator vector<_Tp, _Allocator>::insert(const_iterator __position, _InputIterator __first, _InputIterator __last) { + //_LIBCPP_ASSERT_CHECK_INVALID_RANGE2(__first, __last, "vector::insert(const_iterator, Iter, Iter)"); + //_LIBCPP_ASSERT_CHECK_FOREIGN_ITERATOR(*this, __position, "vector::insert(const_iterator, Iter, Iter)"); + return __insert_with_sentinel(__position, __first, __last); } @@ -1977,6 +2028,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator vector<_Tp, _Allocator>::insert(const_iterator __position, _ForwardIterator __first, _ForwardIterator __last) { + //_LIBCPP_ASSERT_CHECK_INVALID_RANGE2(__first, __last, "vector::insert(const_iterator, Iter, Iter)"); + //_LIBCPP_ASSERT_CHECK_FOREIGN_ITERATOR(this, __position, "vector::insert(const_iterator, Iter, Iter)"); + return __insert_with_size(__position, __first, __last, std::distance(__first, __last)); }