diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index d5ae2b7bded..177a76eb974 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -185,6 +185,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/spanstream ${CMAKE_CURRENT_LIST_DIR}/inc/sstream ${CMAKE_CURRENT_LIST_DIR}/inc/stack + ${CMAKE_CURRENT_LIST_DIR}/inc/static_vector ${CMAKE_CURRENT_LIST_DIR}/inc/stdatomic.h ${CMAKE_CURRENT_LIST_DIR}/inc/stdexcept ${CMAKE_CURRENT_LIST_DIR}/inc/stop_token diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index 0c282412695..13452107d1b 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -118,6 +118,7 @@ #include #include #include +#include #include #include #include diff --git a/stl/inc/ranges b/stl/inc/ranges index 11a4d3e51fb..c49f62a0f27 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -49,9 +49,6 @@ namespace ranges { && (is_pointer_v<_It> || _Has_member_arrow<_It&>); // clang-format on - template - using _Maybe_const = conditional_t<_IsConst, const _Ty, _Ty>; - template using _Maybe_wrapped = conditional_t<_IsWrapped, _Ty, _Unwrapped_t<_Ty>>; diff --git a/stl/inc/static_vector b/stl/inc/static_vector new file mode 100644 index 00000000000..72e434b1f18 --- /dev/null +++ b/stl/inc/static_vector @@ -0,0 +1,1042 @@ +// static_vector standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _STATIC_VECTOR_ +#define _STATIC_VECTOR_ +#include +#if _STL_COMPILER_PREPROCESSOR + +#if !_HAS_CXX23 +#pragma message("The contents of are available only with C++23 or later.") +#else // ^^^ !_HAS_CXX23 / _HAS_CXX23 vvv + +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_STD_BEGIN +// clang-format off +template + requires (_Capacity != 0) +class static_vector; +// clang-format on + +template +class _Static_vector_iterator { +private: + template + using _OtherIter = _Static_vector_iterator<_Ty, _Capacity, _OtherConst, _Checked>; + + template + friend class _Static_vector_iterator; + + friend class static_vector<_Ty, _Capacity>; + + _Maybe_const<_Const, remove_cv_t<_Ty>>* _Current{}; // current position in array +#if _ITERATOR_DEBUG_LEVEL != 0 + const static_vector<_Ty, _Capacity>* _Parent{}; +#endif // _ITERATOR_DEBUG_LEVEL != 0 + +public: +#ifdef __cpp_lib_concepts + using iterator_concept = contiguous_iterator_tag; +#endif // __cpp_lib_concepts + using iterator_category = random_access_iterator_tag; + using value_type = _Ty; + using difference_type = ptrdiff_t; + using pointer = _Maybe_const<_Const, _Ty>*; + using reference = _Maybe_const<_Const, _Ty>&; + + _Static_vector_iterator() = default; + + _Static_vector_iterator(const static_vector<_Ty, _Capacity>& _Parent_, pointer _Current_) noexcept +#if _ITERATOR_DEBUG_LEVEL == 0 + : _Current(_Current_) { + (void) _Parent_; + } +#else // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ / vvv _ITERATOR_DEBUG_LEVEL != 0 vvv + : _Current(_Current_), _Parent(_STD addressof(_Parent_)) { + _STL_VERIFY( + _Parent->_Unchecked_begin() <= _Current, "cannot create static_static_vector iterator outside of storage"); + _STL_VERIFY(_Current <= _Parent->_End, "cannot create static_static_vector iterator outside of storage"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + _Static_vector_iterator(_OtherIter _It) requires _Const +#if _ITERATOR_DEBUG_LEVEL == 0 + : _Current(_It._Current) { + } +#else // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ / vvv _ITERATOR_DEBUG_LEVEL != 0 vvv + : _Current(_It._Current), + _Parent(_It._Parent) { + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + _NODISCARD reference operator*() const noexcept { +#if _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (_Checked) { + _STL_VERIFY(_Current, "cannot dereference value-initialized static_vector iterator"); + _STL_VERIFY(_Parent->_Unchecked_begin() <= _Current && _Current < _Parent->_End, + "cannot dereference out of range static_vector iterator"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return *_Current; + } + + _NODISCARD pointer operator->() const noexcept { +#if _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (_Checked) { + _STL_VERIFY(_Current, "cannot dereference value-initialized static_vector iterator"); + _STL_VERIFY(_Parent->_Unchecked_begin() <= _Current && _Current < _Parent->_End, + "cannot dereference out of range static_vector iterator"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Current; + } + + _NODISCARD _CONSTEXPR17 reference operator[](const ptrdiff_t _Off) const noexcept { +#if _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (_Checked) { + _Verify_offset(_Off); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Current[_Off]; + } + + _Static_vector_iterator& operator++() noexcept { +#if _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (_Checked) { + _STL_VERIFY(_Current, "cannot increment value-initialized static_vector iterator"); + _STL_VERIFY(_Current < _Parent->_End, "cannot increment static_vector iterator past end"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + ++_Current; + return *this; + } + + _Static_vector_iterator operator++(int) noexcept { +#if _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (_Checked) { + _STL_VERIFY(_Current, "cannot increment value-initialized static_vector iterator"); + _STL_VERIFY(_Current < _Parent->_End, "cannot increment static_vector iterator past end"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Static_vector_iterator _Tmp = *this; + ++_Current; + return _Tmp; + } + + _Static_vector_iterator& operator--() noexcept { +#if _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (_Checked) { + _STL_VERIFY(_Current, "cannot decrement value-initialized static_vector iterator"); + _STL_VERIFY(_Parent->_Unchecked_begin() < _Current, "cannot decrement static_vector iterator before begin"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + --_Current; + return *this; + } + + _Static_vector_iterator operator--(int) noexcept { +#if _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (_Checked) { + _STL_VERIFY(_Current, "cannot decrement value-initialized static_vector iterator"); + _STL_VERIFY(_Parent->_Unchecked_begin() < _Current, "cannot decrement static_vector iterator before begin"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Static_vector_iterator _Tmp = *this; + --_Current; + return _Tmp; + } + + _Static_vector_iterator& operator+=(const difference_type _Off) noexcept { +#if _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (_Checked) { + _Verify_offset(_Off); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Current += _Off; + return *this; + } + + _NODISCARD _Static_vector_iterator operator+(const difference_type _Off) const noexcept { + _Static_vector_iterator _Tmp{*this}; + return _Tmp += _Off; + } + + _Static_vector_iterator& operator-=(const difference_type _Off) noexcept { +#if _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (_Checked) { + _Verify_offset(-_Off); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Current -= _Off; + return *this; + } + + _NODISCARD _Static_vector_iterator operator-(const difference_type _Off) const noexcept { + _Static_vector_iterator _Tmp{*this}; + return _Tmp -= _Off; + } + + _NODISCARD friend _Static_vector_iterator operator+( + const difference_type _Off, _Static_vector_iterator _Next) noexcept { + return _Next += _Off; + } + + template + _NODISCARD difference_type operator-(const _OtherIter<_OtherConst>& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (_Checked) { + _STL_VERIFY(_Parent == _Right._Parent, "cannot subtract incompatible static_vector iterators"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Current - _Right._Current; + } + + template + _NODISCARD bool operator==(const _OtherIter<_OtherConst>& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (_Checked) { + _STL_VERIFY(_Parent == _Right._Parent, "cannot compare incompatible static_vector iterators for equality"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Current == _Right._Current; + } + + template + _NODISCARD strong_ordering operator<=>(const _OtherIter<_OtherConst>& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL != 0 + if constexpr (_Checked) { + _STL_VERIFY(_Parent == _Right._Parent, "cannot compare incompatible static_vector iterators"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Current <=> _Right._Current; + } + +#if _ITERATOR_DEBUG_LEVEL != 0 + void _Verify_offset([[maybe_unused]] const difference_type _Off) const noexcept { + if constexpr (_Checked) { + if (_Off != 0) { + _STL_VERIFY(_Parent, "cannot seek value-initialized static_vector iterator"); + } + + if (_Off < 0) { + _STL_VERIFY( + _Off >= _Parent->_Unchecked_begin() - _Current, "cannot seek static_vector iterator before begin"); + } + + if (_Off > 0) { + _STL_VERIFY(_Off <= _Parent->_End - _Current, "cannot seek static_vector iterator after end"); + } + } + } + + template + friend void _Verify_range(const _Static_vector_iterator& _First, const _OtherIter<_OtherConst>& _Last) noexcept { + if constexpr (_Checked) { + _STL_VERIFY(_First._Parent == _Last._Parent, + "static_vector iterators from different static_vector do not form a range"); + _STL_VERIFY(_First._Current <= _Last._Current, "static_vector iterator range transposed"); + } + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + using _Prevent_inheriting_unwrap = _Static_vector_iterator; + + _NODISCARD pointer _Unwrapped() const noexcept { + return _Current; + } + + static constexpr bool _Unwrap_when_unverified = _ITERATOR_DEBUG_LEVEL == 0; + + void _Seek_to(const pointer _It) noexcept { + _Current = _It; + } +}; + +// CLASS TEMPLATE static_vector +// clang-format off +template + requires (_Capacity != 0) +class static_vector { + // clang-format on +private: + template + friend class _Static_vector_iterator; + + alignas(_Ty) unsigned char _Elems[_Capacity * sizeof(_Ty)]; + _Ty* _End{reinterpret_cast<_Ty*>(_Elems)}; + + consteval static auto _Determine_size_type() noexcept { + if constexpr (_Capacity <= numeric_limits::max) { + return std::uint8_t{}; + } else if constexpr (_Capacity <= numeric_limits::max) { + return std::uint16_t{}; + } else if constexpr (_Capacity <= numeric_limits::max) { + return std::uint32_t{}; + } else if constexpr (_Capacity <= numeric_limits::max) { + return std::uint64_t{}; + } else { + _STL_INTERNAL_STATIC_ASSERT(_Always_false<_Ty>, "Too large capacity"); + } + } + +public: + using value_type = _Ty; + using size_type = typename decltype(_Determine_size_type()); + using difference_type = ptrdiff_t; + using pointer = _Ty*; + using const_pointer = const _Ty*; + using reference = _Ty&; + using const_reference = const _Ty&; + + using iterator = _Static_vector_iterator<_Ty, _Capacity, false, true>; + using const_iterator = _Static_vector_iterator<_Ty, _Capacity, true, true>; + + using reverse_iterator = _STD reverse_iterator; + using const_reverse_iterator = _STD reverse_iterator; + + // static_vector.constructors + _NODISCARD static_vector() noexcept = default; + + _NODISCARD static_vector(const static_vector& _Other) noexcept(is_nothrow_copy_constructible_v<_Ty>) { + _End = _Uninitialized_copy_unchecked(_Other._Unchecked_begin(), _Other._Unchecked_end(), _Unchecked_begin()); + } + + _NODISCARD static_vector(static_vector&& _Other) noexcept(is_nothrow_move_constructible_v<_Ty>) { + _End = _Uninitialized_move_unchecked(_Other._Unchecked_begin(), _Other._Unchecked_end(), _Unchecked_begin()); + } + + _NODISCARD explicit static_vector(const size_type _Count) noexcept(is_nothrow_default_constructible_v<_Ty>) { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_Count <= _Capacity, "static_vector(n) out of capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + _End = _Uninitialized_value_construct_n_unchecked1(_Unchecked_begin(), _Count); + } + + _NODISCARD static_vector(const size_type _Count, const _Ty& _Val) noexcept(is_nothrow_copy_constructible_v<_Ty>) { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_Count <= _Capacity, "static_vector(n, val) out of capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + const auto _Newend = _Unchecked_begin() + _Count; + _STD uninitialized_fill(_Unchecked_begin(), _Newend, _Val); + _End = _Newend; + } + + template , int> = 0> + _NODISCARD static_vector(_Iter _First, _Iter _Last) noexcept(is_nothrow_copy_constructible_v<_Ty>) { + _Adl_verify_range(_First, _Last); +#if _CONTAINER_DEBUG_LEVEL != 0 + if constexpr (_Is_fwd_iter_v<_Iter>) { + _STL_VERIFY(static_cast(_STD distance(_First, _Last)) <= _Capacity, + "static_vector(first, last) out of capacity"); + } +#endif // _CONTAINER_DEBUG_LEVEL != 0 + _End = _Uninitialized_copy_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Unchecked_begin()); + } + + _NODISCARD static_vector(initializer_list<_Ty> _Ilist) noexcept(is_nothrow_move_constructible_v<_Ty>) { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_Ilist.size() <= _Capacity, "static_vector(initializer_list) out of capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + _End = _Uninitialized_copy_unchecked(_Ilist.begin(), _Ilist.end(), _Unchecked_begin()); + } + + ~static_vector() noexcept { + _Destroy_range(_Unchecked_begin(), _End); + } + + // static_vector.assignment + static_vector& operator=(const static_vector& _Other) noexcept(is_nothrow_copy_assignable_v<_Ty>) { + if (_Other.size() < size()) { + auto _Newend = _Copy_unchecked(_Other._Unchecked_begin(), _Other._Unchecked_end(), _Unchecked_begin()); + _Destroy_range(_Newend, _End); + _End = _Newend; + } else { + const auto _Mid = _Other._Unchecked_begin() + size(); + auto _Temp = _Copy_unchecked(_Other._Unchecked_begin(), _Mid, _Unchecked_begin()); + _End = _Uninitialized_copy_unchecked(_Mid, _Other._Unchecked_end(), _Temp); + } + return *this; + } + + static_vector& operator=(static_vector&& _Other) noexcept(is_nothrow_move_assignable_v<_Ty>) { + if (_Other.size() < size()) { + auto _Newend = _Move_unchecked(_Other._Unchecked_begin(), _Other._Unchecked_end(), _Unchecked_begin()); + _Destroy_range(_Newend, _End); + _End = _Newend; + } else { + const auto _Mid = _Other._Unchecked_begin() + size(); + auto _Temp = _Move_unchecked(_Other._Unchecked_begin(), _Mid, _Unchecked_begin()); + _End = _Uninitialized_move_unchecked(_Mid, _Other._Unchecked_end(), _Temp); + } + return *this; + } + + static_vector& operator=(initializer_list<_Ty> _Ilist) noexcept(is_nothrow_move_assignable_v<_Ty>) { + const auto _Count = _Ilist.size(); +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_Ilist.size() <= _Capacity, "static_vector& operator=(initializer_list) out of capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + if (_Count < size()) { + auto _Newend = _Copy_unchecked(_Ilist.begin(), _Ilist.end(), _Unchecked_begin()); + _Destroy_range(_Newend, _End); + _End = _Newend; + } else { + const auto _Mid = _Ilist.begin() + size(); + auto _Temp = _Copy_unchecked(_Ilist.begin(), _Mid, _Unchecked_begin()); + _End = _Uninitialized_copy_unchecked(_Mid, _Ilist.end(), _Temp); + } + return *this; + } + + // static_vector.assign + void assign(const size_type _Count, const _Ty& _Val) noexcept(is_nothrow_copy_constructible_v<_Ty>) { + auto _Newend = _Unchecked_begin() + _Count; +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_Newend <= _Unchecked_begin() + _Capacity, "static_vector::assign(n, val) out of capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + if (_Newend <= _End) { + _STD fill(_Unchecked_begin(), _Newend, _Val); + _Destroy_range(_Newend, _End); + } else { + _STD fill(_Unchecked_begin(), _End, _Val); + _STD uninitialized_fill(_End, _Newend, _Val); + } + _End = _Newend; + } + + template , int> = 0> + void assign(_Iter _First, _Iter _Last) noexcept(is_nothrow_copy_constructible_v<_Ty>) { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_unwrapped(_STD move(_Last)); + if constexpr (_Is_fwd_iter_v<_Iter>) { + const auto _Count = static_cast(_STD distance(_UFirst, _ULast)); +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_Count <= _Capacity, "static_vector::assign(first, last) out of capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + + auto _Newend = _Unchecked_begin() + _Count; + if (_Newend <= _End) { + _Copy_unchecked(_STD move(_UFirst), _STD move(_ULast), _Unchecked_begin()); + _Destroy_range(_Newend, _End); + } else { + for (auto _Current = _Unchecked_begin(); _Current != _End; ++_Current, (void) ++_UFirst) { + *_Current = *_UFirst; + } + + _Uninitialized_copy_unchecked(_STD move(_UFirst), _STD move(_ULast), _End); + } + + _End = _Newend; + } else { + auto _Newend = _Unchecked_begin(); + for (; _UFirst != _ULast && _Newend != _End; ++_Newend, (void) ++_UFirst) { + *_Newend = *_UFirst; + } + + if (_Newend != _End) { // NOTE: Inequality to ensure that _UFirst == _ULast + _Destroy_range(_Newend, _End); + _End = _Newend; + } else { + _Uninitialized_copy_unchecked(_STD move(_UFirst), _STD move(_ULast), _End); + } + + _End = _Newend; + } + } + + void assign(initializer_list<_Ty> _Ilist) noexcept(is_nothrow_copy_constructible_v<_Ty>) { + const auto _Count = _Ilist.size(); +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_Count <= _Capacity, "static_vector::assign(initializer_list) out of capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + + auto _Newend = _Unchecked_begin() + _Count; + if (_Newend <= _End) { + _Copy_unchecked(_Ilist.begin(), _Ilist.end(), _Unchecked_begin()); + _Destroy_range(_Newend, _End); + } else { + auto _UFirst = _Ilist.begin(); + for (auto _Current = _Unchecked_begin(); _Current != _End; ++_Current, (void) ++_UFirst) { + *_Current = *_UFirst; + } + + _Uninitialized_copy_unchecked(_STD move(_UFirst), _Ilist.end(), _End); + } + + _End = _Newend; + } + + // static_vector.access + _NODISCARD reference operator[](const size_type _Off) noexcept { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_Unchecked_begin() + _Off < _End, "static_vector subscript out of range"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + return *(_Unchecked_begin() + _Off); + } + + _NODISCARD const_reference operator[](const size_type _Off) const noexcept { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_Unchecked_begin() + _Off < _End, "static_vector subscript out of range"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + return *(_Unchecked_begin() + _Off); + } + + _NODISCARD reference front() noexcept { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_End != _Unchecked_begin(), "front() called on empty static_vector"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + return *_Unchecked_begin(); + } + + _NODISCARD const_reference front() const noexcept { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_End != _Unchecked_begin(), "front() called on empty static_vector"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + return *_Unchecked_begin(); + } + + _NODISCARD reference back() noexcept { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_End != _Unchecked_begin(), "back() called on empty static_vector"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + return *(_End - 1); + } + + _NODISCARD const_reference back() const noexcept { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_End != _Unchecked_begin(), "back() called on empty static_vector"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + return *(_End - 1); + } + + _NODISCARD _Ty* data() noexcept { + return _Unchecked_begin(); + } + + _NODISCARD const _Ty* data() const noexcept { + return _Unchecked_begin(); + } + + // static_vector.iterators + _NODISCARD auto begin() noexcept { + return iterator{*this, _Unchecked_begin()}; + } + + _NODISCARD auto begin() const noexcept { + return const_iterator{*this, _Unchecked_begin()}; + } + + _NODISCARD auto end() noexcept { + return iterator{*this, _End}; + } + + _NODISCARD auto end() const noexcept { + return const_iterator{*this, _End}; + } + + _NODISCARD auto rbegin() noexcept { + return reverse_iterator(iterator{*this, _End}); + } + + _NODISCARD auto rbegin() const noexcept { + return const_reverse_iterator(const_iterator{*this, _End}); + } + + _NODISCARD auto rend() noexcept { + return reverse_iterator(iterator{*this, _Unchecked_begin()}); + } + + _NODISCARD auto rend() const noexcept { + return const_reverse_iterator{const_iterator{*this, _Unchecked_begin()}}; + } + + _NODISCARD auto cbegin() const noexcept { + return const_iterator{*this, _Unchecked_begin()}; + } + + _NODISCARD auto cend() const noexcept { + return const_iterator{*this, _End}; + } + + _NODISCARD auto crbegin() const noexcept { + return const_reverse_iterator(const_iterator{*this, _End}); + } + + _NODISCARD auto crend() const noexcept { + return const_reverse_iterator(const_iterator{*this, _Unchecked_begin()}); + } + + _NODISCARD _Ty* _Unchecked_begin() noexcept { + return reinterpret_cast<_Ty*>(_Elems); + } + + _NODISCARD const _Ty* _Unchecked_begin() const noexcept { + return reinterpret_cast(_Elems); + } + + _NODISCARD _Ty* _Unchecked_end() noexcept { + return _End; + } + + _NODISCARD const _Ty* _Unchecked_end() const noexcept { + return _End; + } + + // static_vector.state + _NODISCARD bool empty() const noexcept { + return _End == _Unchecked_begin(); + } + + _NODISCARD size_type size() const noexcept { + return static_cast(_End - _Unchecked_begin()); + } + + _NODISCARD size_type max_size() const noexcept { + return _Capacity; + } + + _NODISCARD size_type capacity() const noexcept { + return _Capacity; + } + + // static_vector.modifiers + iterator insert(const_iterator _Where, const _Ty& _Val) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(this == _Where._Parent, "static_vector::insert(where, value) iterator outside range"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_End != _Unchecked_begin() + _Capacity, "static_vector::insert(where, value) beyond capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + return emplace(_Where, _Val); + } + + iterator insert(const_iterator _Where, _Ty&& _Val) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(this == _Where._Parent, "static_vector::insert(where, value&&) iterator outside range"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_End != _Unchecked_begin() + _Capacity, "static_vector::insert(where, value) beyond capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + return emplace(_Where, _STD move(_Val)); + } + + iterator insert(const_iterator _Where, _CRT_GUARDOVERFLOW const size_type _Count, const _Ty& _Val) { + // insert _Count * _Val at _Where +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(this == _Where._Parent, "static_vector::insert(count, value) iterator outside range"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + const auto _Whereoff = static_cast(_Where._Current - _Unchecked_begin()); + const auto _Whereptr = _Unchecked_begin() + _Whereoff; + + if (_Count == 0) { + return iterator{*this, _Whereptr}; + } else if (_Count == 1) { + return emplace(_Where, _Val); + } + + const auto _Newend = _End + _Count; + const auto _Middle = _Whereptr + _Count; +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_Newend <= _Unchecked_begin() + _Capacity, "static_vector::insert(count, value) beyond capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + + if (_Whereptr == _End) { + _STD uninitialized_fill(_End, _Newend, _Val); + _End = _Newend; + return iterator{*this, _Whereptr}; + } + + _TRY_BEGIN + if (_End <= _Middle) { // new elements spill into uninitialized memory + _Uninitialized_move_unchecked(_Whereptr, _End, _Middle); + _TRY_BEGIN + _STD uninitialized_fill(_End, _Middle, _Val); + _STD fill(_Whereptr, _End, _Val); + _CATCH_ALL + // We need to destroy the disconnected range in [_Middle, _Newend) + _Destroy_range(_Middle, _Newend); + _RERAISE; + _CATCH_END + } else { // new elements in filled storage + const auto _First1 = _End - _Count; + _Uninitialized_move_unchecked(_First1, _End, _Middle); + _Move_backward_unchecked(_Whereptr, _First1, _End); + _STD fill(_Whereptr, _Middle, _Val); + } + _CATCH_ALL + // We do not know which elements are valid anymore, destroy them + _Destroy_range(_Whereptr, _End); + _End = _Whereptr; + _RERAISE; + _CATCH_END + + _End = _Newend; + return iterator{*this, _Whereptr}; + } + + template , int> = 0> + iterator insert(const_iterator _Where, _Iter _First, _Iter _Last) { + // insert range [_First, _Last) at _Where + _Adl_verify_range(_First, _Last); +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(this == _Where._Parent, "static_vector::insert(first, last) iterator outside range"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + const auto _Whereoff = static_cast(_Where._Current - _Unchecked_begin()); + const auto _Whereptr = _Unchecked_begin() + _Whereoff; + if (_First == _Last) { + return iterator{*this, _Whereptr}; + } + + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_unwrapped(_STD move(_Last)); + if (_Whereptr == _End) { +#if _CONTAINER_DEBUG_LEVEL != 0 + if constexpr (_Is_fwd_iter_v<_Iter>) { + const auto _Count = static_cast(_STD distance(_UFirst, _ULast)); + _STL_VERIFY(_Count <= _Capacity - size(), "static_vector::insert(first, last) beyond capacity"); + } +#endif // _CONTAINER_DEBUG_LEVEL != 0 + _End = _Uninitialized_copy_unchecked(_STD move(_UFirst), _STD move(_ULast), _End); + return iterator{*this, _Whereptr}; + } + + if constexpr (_Is_fwd_iter_v<_Iter>) { + const auto _Count = static_cast(_STD distance(_UFirst, _ULast)); +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_Count <= _Capacity - size(), "static_vector::insert(first, last) beyond capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + + const auto _Newend = _End + _Count; + const auto _Middle = _Whereptr + _Count; + _TRY_BEGIN + if (_End <= _Middle) { // new elements spill into uninitialized memory + const auto _Affected_elements = _End - _Whereptr; + _Uninitialized_move_unchecked(_Whereptr, _End, _Middle); + _TRY_BEGIN + _STD copy_n(_UFirst, _Affected_elements, _Whereptr); + _Uninitialized_copy_unchecked(_STD next(_UFirst, _Affected_elements), _STD move(_ULast), _End); + _CATCH_ALL + // We need to destroy the disconnected range in [_Middle, _Newend) + _Destroy_range(_Middle, _Newend); + _RERAISE; + _CATCH_END + } else { // new elements in filled storage + const auto _First1 = _End - _Count; + _Uninitialized_move_unchecked(_First1, _End, _Middle); + _Move_backward_unchecked(_Whereptr, _First1, _End); + _Copy_unchecked(_STD move(_First), _STD move(_Last), _Whereptr); + } + _CATCH_ALL + // We do not know which elements are valid anymore, destroy them + _Destroy_range(_Whereptr, _End); + _End = _Whereptr; + _RERAISE; + _CATCH_END + + _End = _Newend; + } else { + auto _Oldend = _End; + for (; _UFirst != _ULast; ++_UFirst) { + emplace_back(*_UFirst); + } + + _STD rotate(_Whereptr, _Oldend, _End); + } + + return iterator{*this, _Whereptr}; + } + + iterator insert(const_iterator _Where, initializer_list<_Ty> _Ilist) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(this == _Where._Parent, "static_vector::insert(initializer_list) iterator outside range"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + const auto _Count = _Ilist.size(); + const auto _Whereoff = static_cast(_Where._Current - _Unchecked_begin()); + const auto _Whereptr = _Unchecked_begin() + _Whereoff; + if (_Count == 0) { + return iterator{*this, _Whereptr}; + } + + const auto _Newend = _End + _Count; + const auto _Middle = _Whereptr + _Count; +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY( + _Newend <= _Unchecked_begin() + _Capacity, "static_vector::insert(initializer_list) beyond capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + if (_Whereptr == _End) { + _End = _Uninitialized_copy_unchecked(_Ilist.begin(), _Ilist.end(), _End); + return iterator{*this, _Whereptr}; + } + + _TRY_BEGIN + if (_End <= _Middle) { // new elements spill into uninitialized memory + const auto _Affected_elements = _End - _Whereptr; + _Uninitialized_move_unchecked(_Whereptr, _End, _Middle); + _TRY_BEGIN + _STD copy_n(_Ilist.begin(), _Affected_elements, _Whereptr); + _Uninitialized_copy_unchecked(_STD next(_Ilist.begin(), _Affected_elements), _Ilist.end(), _End); + _CATCH_ALL + // We need to destroy the disconnected range in [_Middle, _Newend) + _Destroy_range(_Middle, _Newend); + _RERAISE; + _CATCH_END + } else { // new elements in filled storage + const auto _First1 = _End - _Count; + _Uninitialized_move_unchecked(_First1, _End, _Middle); + _Move_backward_unchecked(_Whereptr, _First1, _End); + _Copy_unchecked(_Ilist.begin(), _Ilist.end(), _Whereptr); + } + _CATCH_ALL + // We do not know which elements are valid anymore, destroy them + _Destroy_range(_Whereptr, _End); + _End = _Whereptr; + _RERAISE; + _CATCH_END + + _End = _Newend; + return iterator{*this, _Whereptr}; + } + + template + iterator emplace(const_iterator _Where, _Args&&... _Arg) noexcept( + is_nothrow_constructible_v<_Ty, _Args...>&& is_nothrow_move_assignable_v<_Ty>) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(this == _Where._Parent, "static_vector::emplace iterator outside range"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_End != _Unchecked_begin() + _Capacity, "static_vector::emplace beyond capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + + const auto _Whereoff = static_cast(_Where._Current - _Unchecked_begin()); + const auto _Whereptr = _Unchecked_begin() + _Whereoff; + if (_Whereptr == _End) { + _STD construct_at(_End, _STD forward<_Args>(_Arg)...); + ++_End; + } else { + const auto _Oldend = _End; + _Ty _New_elem{_STD forward<_Args>(_Arg)...}; // Basic guarantee from here on + _STD construct_at(_End, _STD move(*(_End - 1))); + ++_End; + _Move_backward_unchecked(_Whereptr, _Oldend - 1, _Oldend); + *_Whereptr = _STD move(_New_elem); + } + + return iterator{*this, _Whereptr}; + } + + template + reference emplace_back(_Args&&... _Arg) noexcept(is_nothrow_constructible_v<_Ty, _Args...>) /* strengthened */ { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_End != _Unchecked_begin() + _Capacity, "static_vector::emplace_back beyond capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + _STD construct_at(_End, _STD forward<_Args>(_Arg)...); + return *(_End++); + } + + reference push_back(const _Ty& _Val) noexcept(is_nothrow_copy_constructible_v<_Ty>) /* strengthened */ { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_End != _Unchecked_begin() + _Capacity, "static_vector::push_back(const value&) beyond capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + _STD construct_at(_End, _Val); + return *(_End++); + } + + reference push_back(_Ty&& _Val) noexcept(is_nothrow_move_constructible_v<_Ty>) /* strengthened */ { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_End != _Unchecked_begin() + _Capacity, "static_vector::push_back(value&&) beyond capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + _STD construct_at(_End, _STD move(_Val)); + return *(_End++); + } + + void pop_back() noexcept /* strengthened */ { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_End != _Unchecked_begin(), "static_vector::pop_back when empty"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + --_End; + destroy_at(_End); + } + + iterator erase(const_iterator _Where) noexcept(is_nothrow_move_assignable_v<_Ty>) /* strengthened */ { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(this == _Where._Parent, "static_vector::erase(where) iterator outside range"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_End != _Unchecked_begin(), "static_vector::erase(where) when empty"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + const auto _Whereoff = static_cast(_Where._Current - _Unchecked_begin()); + const auto _Whereptr = _Unchecked_begin() + _Whereoff; + _Move_unchecked(_Whereptr + 1, _End, _Whereptr); + --_End; + destroy_at(_End); + + return iterator{*this, _Whereptr}; + } + + iterator erase(const_iterator _First, const_iterator _Last) noexcept( + is_nothrow_move_assignable_v<_Ty>) /* strengthened */ { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(this == _First._Parent, "static_vector::erase(first, last) iterator outside range"); + _STL_VERIFY(this == _Last._Parent, "static_vector::erase(first, last) iterator outside range"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + const auto _Firstoff = static_cast(_First._Current - _Unchecked_begin()); + const auto _Firstptr = _Unchecked_begin() + _Firstoff; + if (_First != _Last) { + const auto _Lastoff = static_cast(_Last._Current - _Unchecked_begin()); + const auto _Lastptr = _Unchecked_begin() + _Lastoff; + + const auto _Oldend = _End; + _End = _Move_unchecked(_Lastptr, _End, _Firstptr); + _Destroy_range(_End, _Oldend); + } + + return iterator{*this, _Firstptr}; + } + + void clear() noexcept { + auto _Begin = _Unchecked_begin(); + _Destroy_range(_Begin, _End); + _End = _Begin; + } + + void resize(const size_type _Count) noexcept(is_nothrow_default_constructible_v<_Ty>) { + const auto _Newend = _Unchecked_begin() + _Count; + if (_Newend <= _End) { + _Destroy_range(_Newend, _End); + } else { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY(_Newend <= _Unchecked_begin() + _Capacity, "static_vector::resize(count) beyond capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + _Uninitialized_value_construct_n_unchecked1(_End, _Count - size()); + } + + _End = _Newend; + } + + void resize(const size_type _Count, const _Ty& _Val) noexcept(is_nothrow_copy_constructible_v<_Ty>) { + const auto _Newend = _Unchecked_begin() + _Count; + if (_Newend <= _End) { + _Destroy_range(_Newend, _End); + } else { +#if _CONTAINER_DEBUG_LEVEL != 0 + _STL_VERIFY( + _Newend <= _Unchecked_begin() + _Capacity, "static_vector::resize(count, value) beyond capacity"); +#endif // _CONTAINER_DEBUG_LEVEL != 0 + _STD uninitialized_fill(_End, _Newend, _Val); + } + + _End = _Newend; + } + + void swap(static_vector& _Other) noexcept(is_nothrow_swappable_v<_Ty>&& + is_nothrow_move_constructible_v<_Ty>) requires(is_swappable_v<_Ty>&& is_move_constructible_v<_Ty>) { + const auto _Size = size(); + const auto _Other_size = _Other.size(); + if (_Size < _Other_size) { + const auto _Diff = _Other_size - _Size; + const auto _Mid = _Other._End - _Diff; + _Swap_ranges_unchecked(_Other._Unchecked_begin(), _Mid, _Unchecked_begin()); + _Uninitialized_move_unchecked(_Mid, _Other._End, _End); + _Destroy_range(_Mid, _Other._End); + _End += _Diff; + _Other._End -= _Diff; + } else { + const auto _Diff = _Size - _Other_size; + const auto _Mid = _End - _Diff; + _Swap_ranges_unchecked(_Unchecked_begin(), _Mid, _Other._Unchecked_begin()); + _Uninitialized_move_unchecked(_Mid, _End, _Other._End); + _Destroy_range(_Mid, _End); + _End -= _Diff; + _Other._End += _Diff; + } + } + + // static_vector.comparison + _NODISCARD friend bool operator==(const static_vector& _Left, const static_vector& _Right) noexcept( + noexcept(_STD equal( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()))) { + return _STD equal( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); + } + + _NODISCARD friend strong_ordering operator<=>(const static_vector& _Left, const static_vector& _Right) noexcept( + noexcept(_STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()))) { + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); + } + + template + size_type _Erase(const _Uty& _Val) noexcept { + // erase each element matching _Val + const auto _Oldend = _End; + _End = _STD remove(_Unchecked_begin(), _End, _Val); + _Destroy_range(_Oldend, _End); + return static_cast(_Oldend - _End); + } + + template + size_type _Erase_if(_Pr _Pred) noexcept { + // erase each element satisfying _Pred + const auto _Oldend = _End; + _End = _STD remove_if(_Unchecked_begin(), _End, _STD move(_Pred)); + _Destroy_range(_Oldend, _End); + return static_cast(_Oldend - _End); + } +}; + +template +struct pointer_traits<_Static_vector_iterator<_Ty, _Capacity, _Const, _Checked>> { + using pointer = _Static_vector_iterator<_Ty, _Capacity, _Const, _Checked>; + using element_type = _Maybe_const<_Const, _Ty>; + using difference_type = ptrdiff_t; + + _NODISCARD static element_type* to_address(const pointer _Iter) noexcept { + return _Iter._Unwrapped(); + } +}; + +// clang-format off +template + void swap(static_vector<_Ty, _Capacity>& _Left, static_vector<_Ty, _Capacity>& _Right) noexcept( + noexcept(_Left.swap(_Right))) { + return _Left.swap(_Right); +} +// clang-format on + +template +auto erase(static_vector<_Ty, _Capacity>& _Cont, const _Uty& _Val) { + return _Cont._Erase(_Val); +} + +template +auto erase_if(static_vector<_Ty, _Capacity>& _Cont, _Pr _Pred) { + return _Cont._Erase_if(_Pass_fn(_Pred)); +} + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // _HAS_CXX23 +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _STATIC_VECTOR_ diff --git a/stl/inc/xutility b/stl/inc/xutility index d988b4eced2..08e00bf8553 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -119,6 +119,9 @@ struct _Get_rebind_alias<_Ty, _Other, void_t; }; +template +using _Maybe_const = conditional_t<_IsConst, const _Ty, _Ty>; + template _NODISCARD constexpr void* _Voidify_iter(_Iter _It) noexcept { if constexpr (is_pointer_v<_Iter>) { diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index fb4adb47f77..a2be9969050 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -274,6 +274,7 @@ // _HAS_CXX23 directly controls: // P0401R6 Providing Size Feedback In The Allocator Interface // P0448R4 +// P0843R2 Implement static_vector // P0943R6 Supporting C Atomics In C++ // P1048R1 is_scoped_enum // P1132R7 out_ptr(), inout_ptr() diff --git a/tests/std/test.lst b/tests/std/test.lst index ceb95122ef5..43a23bf85ea 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -297,6 +297,9 @@ tests\P0769R2_shift_left_shift_right tests\P0784R7_library_machinery tests\P0784R7_library_support_for_more_constexpr_containers tests\P0811R3_midpoint_lerp +tests\P0843R2_static_vector +tests\P0843R2_static_vector_death +tests\P0843R2_static_vector_exception tests\P0896R4_common_iterator tests\P0896R4_common_iterator_death tests\P0896R4_counted_iterator diff --git a/tests/std/tests/P0843R2_static_vector/env.lst b/tests/std/tests/P0843R2_static_vector/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0843R2_static_vector/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0843R2_static_vector/test.cpp b/tests/std/tests/P0843R2_static_vector/test.cpp new file mode 100644 index 00000000000..fc930deeb74 --- /dev/null +++ b/tests/std/tests/P0843R2_static_vector/test.cpp @@ -0,0 +1,504 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +using namespace std; + +static constexpr int long_input[] = {0, 1, 2, 3, 4, 5}; +static constexpr int short_input[] = {1, 2, 3, 4}; +static constexpr size_t vector_capacity = 6; +using vec = static_vector; + +static_assert(sizeof(vec) == sizeof(size_t) + 6 * sizeof(int)); + +// TRANSITION, EDG concepts support +template +constexpr bool equalRanges(const Range1& range1, const Range2& range2) noexcept { + return equal(begin(range1), end(range1), begin(range2), end(range2)); +} + +bool test_interface() { +#ifdef __cpp_lib_concepts + static_assert(ranges::contiguous_range); +#endif // __cpp_lib_concepts + + { // constructors + // range constructor + vec construct_iter_range(begin(long_input), end(long_input)); + assert(equalRanges(construct_iter_range, long_input)); + + // special member functions + vec default_construct; + assert(default_construct.empty()); + + vec copy_construct(construct_iter_range); + assert(equalRanges(copy_construct, long_input)); + + vec move_construct(move(copy_construct)); + assert(equalRanges(move_construct, long_input)); + assert(move_construct.size() == copy_construct.size()); + + vec copy_assignment_grow(3, 4); + copy_assignment_grow = construct_iter_range; + assert(equalRanges(copy_assignment_grow, long_input)); + + vec move_assignment_grow(3, 4); + move_assignment_grow = move(copy_assignment_grow); + assert(equalRanges(move_assignment_grow, long_input)); + assert(move_assignment_grow.size() == copy_assignment_grow.size()); + + vec copy_assignment_shrink(6, 5); + vec short_range(begin(short_input), end(short_input)); + copy_assignment_shrink = short_range; + assert(equalRanges(copy_assignment_shrink, short_input)); + + vec move_assignment_shrink(6, 5); + move_assignment_shrink = move(copy_assignment_shrink); + assert(equalRanges(move_assignment_shrink, short_input)); + assert(move_assignment_shrink.size() == copy_assignment_shrink.size()); + + // other constructors + vec construct_size(5); + assert(construct_size.size() == 5); + assert(all_of(construct_size.begin(), construct_size.end(), [](const int val) { return val == 0; })); + + vec construct_size_value(5, 7); + assert(construct_size_value.size() == 5); + assert( + all_of(construct_size_value.begin(), construct_size_value.end(), [](const int val) { return val == 7; })); + + vec contruct_initializer_list({0, 1, 2, 3, 4, 5}); + assert(equalRanges(contruct_initializer_list, long_input)); + + vec assignment_initializer_list_grow(3, 4); + assignment_initializer_list_grow = {0, 1, 2, 3, 4, 5}; + assert(equalRanges(assignment_initializer_list_grow, long_input)); + + vec assignment_initializer_list_shrink(6, 5); + assignment_initializer_list_shrink = {1, 2, 3, 4}; + assert(equalRanges(assignment_initializer_list_shrink, short_input)); + } + + { // assign + vec assign_count_val_grow(3, 4); + assign_count_val_grow.assign(5, 1); + assert(equalRanges(assign_count_val_grow, vec{1, 1, 1, 1, 1})); + + vec assign_count_val_shrink(3, 4); + assign_count_val_shrink.assign(2, 1); + assert(equalRanges(assign_count_val_shrink, vec{1, 1})); + + vec assign_iter_range_grow(3, 4); + assign_iter_range_grow.assign(begin(long_input), end(long_input)); + assert(equalRanges(assign_iter_range_grow, long_input)); + + vec assign_iter_range_shrink(6, 5); + assign_iter_range_shrink.assign(begin(short_input), end(short_input)); + assert(equalRanges(assign_iter_range_shrink, short_input)); + + vec assign_initializer_list_grow(3, 4); + assign_initializer_list_grow.assign({0, 1, 2, 3, 4, 5}); + assert(equalRanges(assign_initializer_list_grow, long_input)); + + vec assign_initializer_list_shrink(6, 5); + assign_initializer_list_shrink.assign({1, 2, 3, 4}); + assert(equalRanges(assign_initializer_list_shrink, short_input)); + } + + { // iterators + vec range(begin(long_input), end(long_input)); + const vec const_range(begin(long_input), end(long_input)); + + const auto b = range.begin(); + static_assert(is_same_v); + assert(*b == 0); + + const auto cb = range.cbegin(); + static_assert(is_same_v); + assert(*cb == 0); + + const auto cb2 = const_range.begin(); + static_assert(is_same_v); + assert(*cb2 == 0); + + const auto e = range.end(); + static_assert(is_same_v); + assert(*prev(e) == 5); + + const auto ce = range.cend(); + static_assert(is_same_v); + assert(*prev(ce) == 5); + + const auto ce2 = const_range.end(); + static_assert(is_same_v); + assert(*prev(ce2) == 5); + + const auto rb = range.rbegin(); + static_assert(is_same_v); + assert(*rb == 5); + + const auto crb = range.crbegin(); + static_assert(is_same_v); + assert(*crb == 5); + + const auto crb2 = const_range.rbegin(); + static_assert(is_same_v); + assert(*crb2 == 5); + + const auto re = range.rend(); + static_assert(is_same_v); + assert(*prev(re) == 0); + + const auto cre = range.crend(); + static_assert(is_same_v); + assert(*prev(cre) == 0); + + const auto cre2 = const_range.rend(); + static_assert(is_same_v); + assert(*prev(cre2) == 0); + } + + { // access + vec range(begin(long_input), end(long_input)); + const vec const_range(begin(long_input), end(long_input)); + + auto&& op = range[3]; + static_assert(is_same_v); + assert(op == 3); + + range[3] = 4; + auto&& op2 = range[3]; + static_assert(is_same_v); + assert(op2 == 4); + + auto&& cop = const_range[3]; + static_assert(is_same_v); + assert(cop == 3); + + auto&& f = range.front(); + static_assert(is_same_v); + assert(f == 0); + + auto&& cf = const_range.front(); + static_assert(is_same_v); + assert(cf == 0); + + auto&& b = range.back(); + static_assert(is_same_v); + assert(b == 5); + + auto&& cb = const_range.back(); + static_assert(is_same_v); + assert(cb == 5); + + auto d = range.data(); + static_assert(is_same_v); + assert(*d == 0); + + auto cd = const_range.data(); + static_assert(is_same_v); + assert(*cd == 0); + } + + { // capacity + vec range(begin(long_input), end(long_input)); + const vec const_range(begin(long_input), end(long_input)); + + const auto e = range.empty(); + static_assert(is_same_v); + assert(!e); + + const auto ec = const_range.empty(); + static_assert(is_same_v); + assert(!ec); + + const auto s = range.size(); + static_assert(is_same_v); + assert(s == size(long_input)); + + const auto sc = const_range.size(); + static_assert(is_same_v); + assert(sc == size(long_input)); + + const auto ms = range.max_size(); + static_assert(is_same_v); + assert(ms == vector_capacity); + + const auto msc = const_range.max_size(); + static_assert(is_same_v); + assert(msc == vector_capacity); + + const auto c = range.capacity(); + static_assert(is_same_v); + assert(c == vector_capacity); + + const auto cc = const_range.capacity(); + static_assert(is_same_v); + assert(cc == vector_capacity); + } + + { // insert + const int expected_insert[] = {0, 3, 5}; + const int to_be_inserted = 3; + + vec insert_lvalue = {0, 5}; + const auto res_insert_lvalue = insert_lvalue.insert(insert_lvalue.begin() + 1, to_be_inserted); + static_assert(is_same_v); + assert(equalRanges(insert_lvalue, expected_insert)); + assert(res_insert_lvalue == insert_lvalue.begin() + 1); + + vec insert_lvalue_const = {0, 5}; + const auto res_insert_lvalue_const = + insert_lvalue_const.insert(insert_lvalue_const.cbegin() + 1, to_be_inserted); + static_assert(is_same_v); + assert(equalRanges(insert_lvalue_const, expected_insert)); + assert(res_insert_lvalue_const == insert_lvalue_const.cbegin() + 1); + + vec insert_rvalue = {0, 5}; + const auto res_insert_rvalue = insert_rvalue.insert(insert_rvalue.begin() + 1, 3); + static_assert(is_same_v); + assert(equalRanges(insert_rvalue, expected_insert)); + assert(res_insert_rvalue == insert_rvalue.begin() + 1); + + vec insert_rvalue_const = {0, 5}; + const auto res_insert_rvalue_const = insert_rvalue_const.insert(insert_rvalue_const.cbegin() + 1, 3); + static_assert(is_same_v); + assert(equalRanges(insert_rvalue_const, expected_insert)); + assert(res_insert_rvalue_const == insert_rvalue_const.cbegin() + 1); + + vec insert_iter_range = {0, 5}; + const auto res_insert_iter_range = + insert_iter_range.insert(insert_iter_range.begin() + 1, begin(short_input), end(short_input)); + static_assert(is_same_v); + assert(equalRanges(insert_iter_range, long_input)); + assert(res_insert_iter_range == insert_iter_range.begin() + 1); + + vec insert_iter_range_const = {0, 5}; + const auto res_insert_iter_range_const = + insert_iter_range_const.insert(insert_iter_range_const.cbegin() + 1, begin(short_input), end(short_input)); + static_assert(is_same_v); + assert(equalRanges(insert_iter_range_const, long_input)); + assert(res_insert_iter_range_const == insert_iter_range_const.cbegin() + 1); + + vec insert_initializer = {0, 5}; + const auto res_insert_initializer = insert_initializer.insert(insert_initializer.begin() + 1, {1, 2, 3, 4}); + static_assert(is_same_v); + assert(equalRanges(insert_initializer, long_input)); + assert(res_insert_initializer == insert_initializer.begin() + 1); + + vec insert_initializer_const = {0, 5}; + const auto res_insert_initializer_const = + insert_initializer_const.insert(insert_initializer_const.cbegin() + 1, {1, 2, 3, 4}); + static_assert(is_same_v); + assert(equalRanges(insert_initializer_const, long_input)); + assert(res_insert_initializer_const == insert_initializer_const.cbegin() + 1); + } + + { // emplace + vec emplace = {0, 1, 2, 4, 5}; + const auto res_emplaced = emplace.emplace(emplace.begin() + 3, 3); + static_assert(is_same_v); + assert(equalRanges(emplace, long_input)); + assert(res_emplaced == emplace.begin() + 3); + + vec emplace_const = {0, 1, 2, 4, 5}; + const auto res_emplace_const = emplace_const.emplace(emplace_const.cbegin() + 3, 3); + static_assert(is_same_v); + assert(equalRanges(emplace_const, long_input)); + assert(res_emplace_const == emplace_const.cbegin() + 3); + + vec emplace_back = {0, 1, 2, 3, 4}; + auto&& res_emplace_back = emplace_back.emplace_back(5); + static_assert(is_same_v); + assert(equalRanges(emplace_back, long_input)); + assert(res_emplace_back++ == 5); + assert(res_emplace_back == 6); + + vec push_back_lvalue = {0, 1, 2, 3, 4}; + const int to_be_pushed = 5; + auto&& res_push_back_lvalue = push_back_lvalue.push_back(to_be_pushed); + static_assert(is_same_v); + assert(equalRanges(push_back_lvalue, long_input)); + assert(res_push_back_lvalue++ == 5); + assert(res_push_back_lvalue == 6); + + vec push_back_rvalue = {0, 1, 2, 3, 4}; + auto&& res_push_back_rvalue = push_back_rvalue.push_back(5); + static_assert(is_same_v); + assert(equalRanges(push_back_rvalue, long_input)); + assert(res_push_back_rvalue++ == 5); + assert(res_push_back_rvalue == 6); + } + + { // resize + vec resize_shrink(begin(long_input), end(long_input)); + resize_shrink.resize(1); + assert(equalRanges(resize_shrink, vec{0})); + + vec resize_value_shrink(begin(long_input), end(long_input)); + resize_value_shrink.resize(1, 5); + assert(equalRanges(resize_value_shrink, vec{0})); + + vec resize_grow(3, 4); + resize_grow.resize(5); + assert(equalRanges(resize_grow, vec{4, 4, 4, 0, 0})); + + vec resize_value_grow(3, 4); + resize_value_grow.resize(5, 2); + assert(equalRanges(resize_value_grow, vec{4, 4, 4, 2, 2})); + } + + { // other modifiers + vec clear(begin(long_input), end(long_input)); + clear.clear(); + assert(clear.empty()); + + vec pop_back(begin(long_input), end(long_input)); + pop_back.pop_back(); + assert(equalRanges(pop_back, vec{0, 1, 2, 3, 4})); + + vec swap_first(begin(long_input), end(long_input)); + vec swap_second{begin(short_input), end(short_input)}; + + swap_first.swap(swap_second); + assert(equalRanges(swap_first, short_input)); + assert(equalRanges(swap_second, long_input)); + + swap(swap_first, swap_second); + assert(equalRanges(swap_first, long_input)); + assert(equalRanges(swap_second, short_input)); + } + + { // erase + vec erased{1, 2, 3, 4, 2, 3}; + erase(erased, 2); + assert(equalRanges(erased, vec{1, 3, 4, 3})); + + vec erased_if{1, 2, 3, 4, 2, 3}; + erase_if(erased_if, [](const int val) { return val < 4; }); + assert(equalRanges(erased_if, vec{4})); + } + + { // comparison + vec first(begin(long_input), end(long_input)); + vec second(begin(long_input), end(long_input)); + vec third(begin(short_input), end(short_input)); + + const auto e = first == second; + assert(e); + + const auto ne = first != third; + assert(ne); + } + return true; +} + +bool test_iterators() { +#ifdef __cpp_lib_concepts + static_assert(contiguous_iterator); + static_assert(contiguous_iterator); +#endif // __cpp_lib_concepts + + vec range(begin(long_input), end(long_input)); + + { // increment + auto it = range.begin(); + assert(*++it == 1); + assert(*it++ == 1); + assert(*it == 2); + + auto cit = range.cbegin(); + assert(*++cit == 1); + assert(*cit++ == 1); + assert(*cit == 2); + } + + { // advance + auto it = range.begin() + 2; + assert(*it == 2); + it += 2; + assert(*it == 4); + + auto cit = range.cbegin() + 2; + assert(*cit == 2); + cit += 2; + assert(*cit == 4); + } + + { // decrement + auto it = range.end(); + assert(*--it == 5); + assert(*it-- == 5); + assert(*it == 4); + + auto cit = range.cend(); + assert(*--cit == 5); + assert(*cit-- == 5); + assert(*cit == 4); + } + + { // advance back + auto it = range.end() - 2; + assert(*it == 4); + it -= 2; + assert(*it == 2); + + auto cit = range.cend() - 2; + assert(*cit == 4); + cit -= 2; + assert(*cit == 2); + } + + { // difference + const auto it1 = range.begin(); + const auto it2 = range.end(); + assert(it2 - it1 == ssize(long_input)); + + const auto cit1 = range.cbegin(); + const auto cit2 = range.cend(); + assert(cit2 - cit1 == ssize(long_input)); + + assert(it2 - cit1 == ssize(long_input)); + assert(cit2 - it1 == ssize(long_input)); + } + + { // comparison + const auto it1 = range.begin(); + const auto it2 = range.begin(); + const auto it3 = range.end(); + + assert(it1 == it2); + assert(it1 != it3); + assert(it1 < it3); + assert(it1 <= it3); + assert(it3 > it1); + assert(it3 >= it1); + } + + { // access + const auto it = range.begin() + 2; + it[2] = 3; + assert(range[4] == 3); + + const auto cit = range.cbegin() + 2; + assert(cit[2] == 3); + + static_vector, 2> vec2 = {{1, 2}, {2, 3}}; + const auto it2 = vec2.begin(); + assert(it2->second == 2); + + const auto cit2 = vec2.cbegin(); + assert(cit2->first == 1); + } + + return true; +} + +int main() { + test_interface(); + test_iterators(); +} diff --git a/tests/std/tests/P0843R2_static_vector_death/env.lst b/tests/std/tests/P0843R2_static_vector_death/env.lst new file mode 100644 index 00000000000..1e9f4b47966 --- /dev/null +++ b/tests/std/tests/P0843R2_static_vector_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_winsdk_concepts_latest_matrix.lst diff --git a/tests/std/tests/P0843R2_static_vector_death/test.cpp b/tests/std/tests/P0843R2_static_vector_death/test.cpp new file mode 100644 index 00000000000..7dc4b3116ed --- /dev/null +++ b/tests/std/tests/P0843R2_static_vector_death/test.cpp @@ -0,0 +1,417 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include +#include + +#include +using namespace std; + +using SV = static_vector; +using IT = typename SV::iterator; + +SV empty_vector; +SV full_vector = {1, 2, 3, 4, 5}; + +void test_case_operator_dereference_value_initialized_iterator() { + IT it; // note: for IDL to work correctly, default-init and value-init are equivalent + (void) *it; // cannot dereference value-initialized static_vector iterator +} + +void test_case_operator_dereference_end_iterator() { + SV sv(full_vector); + IT it = sv.end(); + (void) *it; // cannot dereference end static_vector iterator +} + +void test_case_operator_arrow_value_initialized_iterator() { + IT it; + (void) it.operator->(); // cannot dereference value-initialized static_vector iterator +} + +void test_case_operator_arrow_end_iterator() { + SV sv(full_vector); + IT it = sv.end(); + (void) it.operator->(); // cannot dereference end static_vector iterator +} + +void test_case_operator_preincrement_value_initialized_iterator() { + IT it; + ++it; // cannot increment value-initialized static_vector iterator +} + +void test_case_operator_preincrement_after_end() { + SV sv(full_vector); + IT it = sv.end(); + ++it; // cannot increment static_vector iterator past end +} + +void test_case_operator_predecrement_value_initialized_iterator() { + IT it; + --it; // cannot decrement value-initialized static_vector iterator +} + +void test_case_operator_predecrement_before_begin() { + SV sv(full_vector); + IT it = sv.begin(); + --it; // cannot decrement static_vector iterator before begin +} + +void test_case_operator_advance_value_initialized_iterator() { + IT it; + it += 1; // cannot seek value-initialized static_vector iterator +} + +void test_case_operator_advance_value_initialized_iterator_zero() { + IT it; + it += 0; // OK +} + +void test_case_operator_advance_before_begin() { + SV sv(full_vector); + IT it = sv.begin(); + it += -1; // cannot seek static_vector iterator before begin +} + +void test_case_operator_advance_after_end() { + SV sv(full_vector); + IT it = sv.end(); + it += 1; // cannot seek static_vector iterator after end +} + +void test_case_operator_retreat_value_initialized_iterator() { + IT it; + it -= 1; // cannot seek value-initialized static_vector iterator +} + +void test_case_operator_retreat_value_initialized_iterator_zero() { + IT it; + it -= 0; // OK +} + +void test_case_operator_retreat_before_begin() { + SV sv(full_vector); + IT it = sv.begin(); + it -= 1; // cannot seek static_vector iterator before begin +} + +void test_case_operator_retreat_after_end() { + SV sv(full_vector); + IT it = sv.end(); + it -= -1; // cannot seek static_vector iterator after end +} + +void test_case_operator_subtract_incompatible_different_data() { + SV sp1(full_vector); + SV sp2(empty_vector); + (void) (sp1.begin() - sp2.begin()); // cannot subtract incompatible static_vector iterators +} + +void test_case_operator_subtract_incompatible_value_initialized() { + SV sv(full_vector); + (void) (sv.begin() - IT{}); // cannot subtract incompatible static_vector iterators +} + +void test_case_operator_equal_incompatible_different_data() { + SV sp1(full_vector); + SV sp2(empty_vector); + (void) (sp1.begin() == sp2.begin()); // cannot compare incompatible static_vector iterators for equality +} + +void test_case_operator_equal_incompatible_value_initialized() { + SV sv(full_vector); + (void) (sv.begin() == IT{}); // cannot compare incompatible static_vector iterators for equality +} + +void test_case_operator_less_incompatible_different_data() { + SV sp1(full_vector); + SV sp2(empty_vector); + (void) (sp1.begin() < sp2.begin()); // cannot compare incompatible static_vector iterators +} + +void test_case_operator_less_incompatible_value_initialized() { + SV sv(full_vector); + (void) (sv.begin() < IT{}); // cannot compare incompatible static_vector iterators +} + +void test_case_algorithm_incompatible_different_data() { + SV sp1(full_vector); + SV sp2(empty_vector); + (void) find(sp1.begin(), sp2.begin(), -1); // static_vector iterators from different views do not form a range +} + +void test_case_algorithm_incompatible_value_initialized() { + SV sv(full_vector); + (void) find(sv.begin(), IT{}, -1); // static_vector iterators from different views do not form a range +} + +void test_case_algorithm_incompatible_transposed() { + SV sv(full_vector); + (void) find(sv.end(), sv.begin(), -1); // static_vector iterator range transposed +} + +void test_case_constructor_overflow_count() { + SV sv(8); // static_vector out of capacity +} + +void test_case_constructor_overflow_count_val() { + SV sv(8, 6); // static_vector out of capacity +} + +void test_case_constructor_overflow_iterator_range() { + const int values[] = {1, 2, 3, 4, 5, 6, 7, 8}; + SV sv(values, values + 8); // static_vector out of capacity +} + +void test_case_constructor_overflow_initializer() { + SV sv({1, 2, 3, 4, 5, 6, 7, 8}); // static_vector out of capacity +} + +void test_case_assignment_overflow_initializer() { + SV sv; + sv = {1, 2, 3, 4, 5, 6, 7, 8}; // static_vector out of capacity +} + +void test_case_assign_overflow_count_val() { + SV sv; + sv.assign(8, 6); // static_vector out of capacity +} + +void test_case_assign_overflow_iterator_range() { + SV sv; + const int values[] = {1, 2, 3, 4, 5, 6, 7, 8}; + sv.assign(values, values + 8); // static_vector out of capacity +} + +void test_case_assign_overflow_initializer() { + SV sv; + sv.assign({1, 2, 3, 4, 5, 6, 7, 8}); // static_vector out of capacity +} + +void test_case_operator_subscript_out_of_range() { + SV sv(empty_vector); + (void) sv[1]; // static_vector index out of range +} + +void test_case_front_empty() { + SV sv{}; + (void) sv.front(); // front of empty static_vector +} + +void test_case_front_const_empty() { + const SV sv{}; + (void) sv.front(); // front of empty static_vector +} + +void test_case_back_empty() { + SV sv{}; + (void) sv.back(); // back of empty static_vector +} + +void test_case_back_const_empty() { + const SV sv{}; + (void) sv.back(); // back of empty static_vector +} + +void test_case_insert_invalid_iterator_lvalue() { + SV sv1; + SV sv2; + const int val = 5; + (void) sv1.insert(sv2.begin(), val); // insert iterator outside range +} + +void test_case_insert_invalid_iterator_rvalue() { + SV sv1; + SV sv2; + (void) sv1.insert(sv2.begin(), 5); // insert iterator outside range +} + +void test_case_insert_invalid_iterator_count_value() { + SV sv1; + SV sv2; + (void) sv1.insert(sv2.begin(), 5, 4); // insert iterator outside range +} + +void test_case_insert_invalid_iterator_iterator_range() { + SV sv1; + SV sv2; + const int values[] = {1, 2, 3}; + (void) sv1.insert(sv2.begin(), values, values + 3); // insert iterator outside range +} + +void test_case_insert_invalid_iterator_initalizer() { + SV sv1; + SV sv2; + (void) sv1.insert(sv2.begin(), {1, 2, 3}); // insert iterator outside range +} + +void test_case_insert_overflow_lvalue() { + SV sv = {1, 2, 3, 4, 5}; + const int val = 6; + sv.insert(sv.end(), val); // static_vector out of capacity +} + +void test_case_insert_overflow_rvalue() { + SV sv = {1, 2, 3, 4, 5}; + sv.insert(sv.end(), 6); // static_vector out of capacity +} + +void test_case_insert_overflow_count() { + SV sv = {1, 2, 3, 4, 5}; + sv.insert(sv.end(), 1, 6); // static_vector out of capacity +} + +void test_case_insert_overflow_iterator_range() { + SV sv = {1, 2, 3, 4, 5}; + const int values[] = {6, 7, 8}; + sv.insert(sv.end(), values, values + 3); // static_vector out of capacity +} + +void test_case_insert_overflow_initializer() { + SV sv = {1, 2, 3, 4, 5}; + sv.insert(sv.end(), {6, 7, 8}); // static_vector out of capacity +} + +void test_case_emplace_invalid_iterator() { + SV sv1; + SV sv2; + (void) sv1.emplace(sv2.begin(), 5); // emplace iterator outside range +} + +void test_case_emplace_overflow() { + SV sv = {1, 2, 3, 4, 5}; + sv.emplace(sv.end(), 6); // static_vector out of capacity +} + +void test_case_emplace_back_overflow() { + SV sv = {1, 2, 3, 4, 5}; + sv.emplace_back(6); // static_vector out of capacity +} + +void test_case_push_back_overflow_lvalue() { + SV sv = {1, 2, 3, 4, 5}; + const int val = 6; + sv.push_back(val); // static_vector out of capacity +} + +void test_case_push_back_overflow_rvalue() { + SV sv = {1, 2, 3, 4, 5}; + sv.push_back(6); // static_vector out of capacity +} + +void test_case_resize_overflow_count() { + SV sv = {1, 2, 3, 4, 5}; + sv.resize(8); // static_vector resize beyond capacity +} + +void test_case_resize_overflow_count_val() { + SV sv = {1, 2, 3, 4, 5}; + sv.resize(8, 6); // static_vector resize beyond capacity +} + +void test_case_pop_back_empty() { + SV sv; + sv.pop_back(); // static_vector pop_back of empty range +} + +void test_case_erase_empty() { + SV sv; + sv.erase(sv.begin()); // static_vector erase of empty range +} + +void test_case_erase_invalid_iterator() { + SV sv1 = {1, 2, 3}; + SV sv2; + (void) sv1.erase(sv2.begin()); // erase iterator outside range +} + +void test_case_erase_invalid_iterator_range1() { + SV sv1 = {1, 2, 3}; + SV sv2; + (void) sv1.erase(sv2.begin(), sv1.begin() + 2); // erase iterator outside range +} + +void test_case_erase_invalid_iterator_range2() { + SV sv1 = {1, 2, 3}; + SV sv2; + (void) sv1.erase(sv1.begin(), sv2.begin()); // erase iterator outside range +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec([] { + test_case_operator_advance_value_initialized_iterator_zero(); + test_case_operator_retreat_value_initialized_iterator_zero(); + }); + +#if _ITERATOR_DEBUG_LEVEL != 0 + exec.add_death_tests({ + test_case_operator_dereference_value_initialized_iterator, + test_case_operator_dereference_end_iterator, + test_case_operator_arrow_value_initialized_iterator, + test_case_operator_arrow_end_iterator, + test_case_operator_preincrement_value_initialized_iterator, + test_case_operator_preincrement_after_end, + test_case_operator_predecrement_value_initialized_iterator, + test_case_operator_predecrement_before_begin, + test_case_operator_advance_value_initialized_iterator, + test_case_operator_advance_before_begin, + test_case_operator_advance_after_end, + test_case_operator_retreat_value_initialized_iterator, + test_case_operator_retreat_before_begin, + test_case_operator_retreat_after_end, + test_case_operator_subtract_incompatible_different_data, + test_case_operator_subtract_incompatible_value_initialized, + test_case_operator_equal_incompatible_different_data, + test_case_operator_equal_incompatible_value_initialized, + test_case_operator_less_incompatible_different_data, + test_case_operator_less_incompatible_value_initialized, + test_case_insert_invalid_iterator_lvalue, + test_case_insert_invalid_iterator_rvalue, + test_case_insert_invalid_iterator_count_value, + test_case_insert_invalid_iterator_iterator_range, + test_case_insert_invalid_iterator_initalizer, + test_case_insert_overflow_lvalue, + test_case_insert_overflow_rvalue, + test_case_insert_overflow_count, + test_case_insert_overflow_iterator_range, + test_case_insert_overflow_initializer, + test_case_emplace_invalid_iterator, + test_case_erase_invalid_iterator, + test_case_erase_invalid_iterator_range1, + test_case_erase_invalid_iterator_range2, + test_case_algorithm_incompatible_different_data, + test_case_algorithm_incompatible_value_initialized, + test_case_algorithm_incompatible_transposed, + }); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + // _CONTAINER_DEBUG_LEVEL tests + exec.add_death_tests({ + test_case_constructor_overflow_count, + test_case_constructor_overflow_count_val, + test_case_constructor_overflow_iterator_range, + test_case_constructor_overflow_initializer, + test_case_assignment_overflow_initializer, + test_case_assign_overflow_count_val, + test_case_assign_overflow_iterator_range, + test_case_assign_overflow_initializer, + test_case_operator_subscript_out_of_range, + test_case_front_empty, + test_case_front_const_empty, + test_case_back_empty, + test_case_back_const_empty, + test_case_emplace_overflow, + test_case_emplace_back_overflow, + test_case_push_back_overflow_lvalue, + test_case_push_back_overflow_rvalue, + test_case_resize_overflow_count, + test_case_resize_overflow_count_val, + test_case_pop_back_empty, + test_case_erase_empty, + }); + + return exec.run(argc, argv); +} diff --git a/tests/std/tests/P0843R2_static_vector_exception/env.lst b/tests/std/tests/P0843R2_static_vector_exception/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0843R2_static_vector_exception/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0843R2_static_vector_exception/test.cpp b/tests/std/tests/P0843R2_static_vector_exception/test.cpp new file mode 100644 index 00000000000..6c7007f39c5 --- /dev/null +++ b/tests/std/tests/P0843R2_static_vector_exception/test.cpp @@ -0,0 +1,651 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +using namespace std; + +int default_construction_count{0}; +int implicit_construction_count{0}; +int copy_construction_count{0}; +int move_construction_count{0}; +int copy_assignment_count{0}; +int move_assignment_count{0}; +int destruction_count{0}; + +[[nodiscard]] constexpr bool nothing_leaked() { + const int num_constructions = exchange(default_construction_count, 0) // + + exchange(implicit_construction_count, 0) // + + exchange(copy_construction_count, 0) // + + exchange(move_construction_count, 0); + const int num_destructions = exchange(destruction_count, 0); + return num_constructions == num_destructions; +} + +enum ThrowType { + DefaultConstructed = 0, + ImplictConstructed = 1, + CopyConstructed = 2, + MoveConstructed = 3, + CopyAssigned = 4, + MoveAssigned = 5, +}; + +template +struct Evil { + Evil() { + if constexpr (ThrowAtDefaultConstruction >= 0) { + if (default_construction_count == ThrowAtDefaultConstruction) { + throw ThrowType::DefaultConstructed; + } + } + ++default_construction_count; + } + Evil(const int v) : value(v) { + if constexpr (ThrowAtImplicitConstruction >= 0) { + if (implicit_construction_count == ThrowAtImplicitConstruction) { + throw ThrowType::ImplictConstructed; + } + } + ++implicit_construction_count; + } + Evil(const Evil& v) : value(v.value) { + if constexpr (ThrowAtCopyConstruction >= 0) { + if (copy_construction_count == ThrowAtCopyConstruction) { + throw ThrowType::CopyConstructed; + } + } + ++copy_construction_count; + } + Evil(Evil&& v) : value(v.value) { + if constexpr (ThrowAtMoveConstruction >= 0) { + if (move_construction_count == ThrowAtMoveConstruction) { + throw ThrowType::MoveConstructed; + } + } + ++move_construction_count; + } + Evil& operator=(const Evil& v) { + if constexpr (ThrowAtCopyAssignment >= 0) { + if (copy_assignment_count == ThrowAtCopyAssignment) { + throw ThrowType::CopyAssigned; + } + } + ++copy_assignment_count; + value = v.value; + return *this; + } + Evil& operator=(Evil&& v) { + if constexpr (ThrowAtMoveAssignment >= 0) { + if (move_assignment_count == ThrowAtMoveAssignment) { + throw ThrowType::MoveAssigned; + } + } + ++move_assignment_count; + value = v.value; + return *this; + } + ~Evil() { + ++destruction_count; + } + bool operator==(const Evil& right) const = default; + int value = 0; +}; + +static constexpr int input[] = {0, 1, 2, 3, 4, 5}; +static constexpr size_t vector_capacity = 6; + +template +using vec = static_vector, + vector_capacity>; + +void test_construction() { + try { + vec<0> default_construct{}; + } catch (...) { + assert(false && "default construction failed"); + } + assert(nothing_leaked() && "default construction leaked"); + + try { + vec<0, -1> construct_range(begin(input), end(input)); + [[maybe_unused]] vec<0, -1> copy_construct(construct_range); + assert(false && "copy construction no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyConstructed && "copy construction wrong exception"); + } catch (...) { + assert(false && "copy construction wrong exception type"); + } + assert(nothing_leaked() && "copy construction leaked"); + + try { + vec<0, -1> construct_range(begin(input), end(input)); + [[maybe_unused]] vec<0, -1> move_construct(move(construct_range)); + assert(false && "move construction no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::MoveConstructed && "move construction wrong exception"); + } catch (...) { + assert(false && "move construction wrong exception type"); + } + assert(nothing_leaked() && "move construction leaked"); + + // other constructors + try { + [[maybe_unused]] vec<3> construct_size(5); + assert(false && "size construction no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::DefaultConstructed && "size construction wrong exception"); + } catch (...) { + assert(false && "size construction wrong exception type"); + } + assert(nothing_leaked() && "size construction leaked"); + + try { + [[maybe_unused]] vec<0> construct_size_value(5, 7); + assert(false && "size value construction no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::ImplictConstructed && "size value construction wrong exception"); + } catch (...) { + assert(false && "size value construction wrong exception type"); + } + assert(nothing_leaked() && "size value construction leaked"); + + try { + [[maybe_unused]] vec<0> construct_iter_range(begin(input), end(input)); + assert(false && "iter range construction no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::ImplictConstructed && "iter range construction wrong exception"); + } catch (...) { + assert(false && "iter range construction wrong exception type"); + } + assert(nothing_leaked() && "iter range construction leaked"); + + try { + [[maybe_unused]] vec<0> contruct_initializer_list({0, 1, 2, 3, 4, 5}); + assert(false && "initializer construction no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::ImplictConstructed && "initializer construction wrong exception"); + } catch (...) { + assert(false && "initializer construction no exception type"); + } + assert(nothing_leaked() && "initializer construction leaked"); +} + +void test_assignment() { + { // operator= + try { + vec<0, -1> construct_range(begin(input), end(input)); + vec<0, -1> copy_assigned(begin(input), end(input)); + copy_assigned = construct_range; + assert(false && "copy assignment no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyAssigned && "copy assignment wrong exception"); + } catch (...) { + assert(false && "copy assignment no exception type"); + } + assert(nothing_leaked() && "copy assignment leaked"); + + try { + vec<0, -1> construct_range(begin(input), end(input)); + vec<0, -1> copy_assigned(begin(input), end(input)); + copy_assigned = move(construct_range); + assert(false && "move assignment no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::MoveAssigned && "move assignment wrong exception"); + } catch (...) { + assert(false && "move assignment wrong exception type"); + } + assert(nothing_leaked() && "move assignment leaked"); + + try { + vec<0, -1> assignment_initializer_list(begin(input), end(input)); + assignment_initializer_list = {0, 1, 2, 3, 4, 5}; + assert(false && "initializer assignment no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyAssigned && "initializer assignment wrong exception"); + } catch (...) { + assert(false && "initializer assignment wrong exception type"); + } + assert(nothing_leaked() && "initializer assignment leaked"); + } + + { // assign + try { + vec<-1, -1, 3> assign_count_val{3, 4}; + assign_count_val.assign(5, 1); + assert(false && "assign count value no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyAssigned && "assign count value wrong exception"); + } catch (...) { + assert(false && "assign count value wrong exception type"); + } + assert(nothing_leaked() && "assign count value leaked"); + + try { + vec<-1, -1, -1> assign_iter_range{3, 4}; + vec<-1, -1, -1> assign_input(begin(input), end(input)); + assign_iter_range.assign(begin(assign_input), end(assign_input)); + assert(false && "assign iter range no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyAssigned && "assign iter range wrong exception"); + } catch (...) { + assert(false && "assign iter range wrong exception type"); + } + assert(nothing_leaked() && "assign iter range leaked"); + + try { + vec<-1, -1, -1> assign_initializer_list{3, 4}; + assign_initializer_list.assign({0, 1, 2, 3, 4, 5}); + assert(false && "assign initializer no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyAssigned && "assign initializer wrong exception"); + } catch (...) { + assert(false && "assign initializer wrong exception type"); + } + assert(nothing_leaked() && "assign initializer leaked"); + } +} + +void test_emplace() { + try { + vec<-1, 2, -1> emplace_temporary_construction{3, 4}; + emplace_temporary_construction.emplace(emplace_temporary_construction.begin(), 5); + assert(false && "emplace during temporary construction no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::ImplictConstructed && "emplace during temporary construction wrong exception"); + } catch (...) { + assert(false && "emplace during temporary construction wrong exception type"); + } + assert(nothing_leaked() && "emplace temporary during construction leaked"); + + try { + vec<-1, -1, -1> emplace_during_move_back{3, 4}; + emplace_during_move_back.emplace(emplace_during_move_back.begin(), 5); + assert(false && "emplace during move back no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::MoveConstructed && "emplace during move back wrong exception"); + } catch (...) { + assert(false && "emplace during move back wrong exception type"); + } + assert(nothing_leaked() && "emplace during move back leaked"); + + try { + vec<-1, -1, -1, -1> emplace_during_move_assignment{3, 4}; + emplace_during_move_assignment.emplace(emplace_during_move_assignment.begin(), 5); + assert(false && "emplace during move assignment no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::MoveAssigned && "emplace during move back assignment exception"); + } catch (...) { + assert(false && "emplace during move back assignment exception type"); + } + assert(nothing_leaked() && "emplace during move assignment leaked"); + + { + vec<-1, 2, -1> emplace_at_end_strong_guarantee{3, 4}; + const auto end = emplace_at_end_strong_guarantee.end(); + try { + emplace_at_end_strong_guarantee.emplace(end, 5); + assert(false && "emplace at end no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::ImplictConstructed && "emplace at end wrong exception"); + } catch (...) { + assert(false && "emplace at end wrong exception type"); + } + + // Check strong guarantee + implicit_construction_count -= 2; + const vec<-1, 2, -1> expected{3, 4}; + implicit_construction_count += 2; + assert(end == emplace_at_end_strong_guarantee.end()); + assert(emplace_at_end_strong_guarantee == expected); + } + assert(nothing_leaked() && "emplace at end leaked"); + + { + vec<-1, 2, -1> emplace_back_strong_guarantee{3, 4}; + const auto end = emplace_back_strong_guarantee.end(); + try { + emplace_back_strong_guarantee.emplace_back(5); + assert(false && "emplace_back no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::ImplictConstructed && "emplace_back wrong exception"); + } catch (...) { + assert(false && "emplace_back wrong exception type"); + } + + // Check strong guarantee + implicit_construction_count -= 2; + const vec<-1, 2, -1> expected{3, 4}; + implicit_construction_count += 2; + assert(end == emplace_back_strong_guarantee.end()); + assert(emplace_back_strong_guarantee == expected); + } + assert(nothing_leaked() && "emplace_back leaked"); + + { + const Evil<-1, -1, 2> to_be_inserted{3}; + vec<-1, -1, 2> push_back_lvalue_strong_guarantee{3, 4}; + const auto end = push_back_lvalue_strong_guarantee.end(); + try { + push_back_lvalue_strong_guarantee.push_back(to_be_inserted); + assert(false && "push_back lvalue no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyConstructed && "push_back lvalue wrong exception"); + } catch (...) { + assert(false && "push_back lvalue wrong exception type"); + } + + // Check strong guarantee + copy_construction_count -= 2; + const vec<-1, -1, 2> expected{3, 4}; + copy_construction_count += 2; + assert(end == push_back_lvalue_strong_guarantee.end()); + assert(push_back_lvalue_strong_guarantee == expected); + } + assert(nothing_leaked() && "push_back lvalue leaked"); + + { + const Evil<-1, -1, 2> to_be_inserted{3}; + vec<-1, -1, 2> push_back_rvalue_strong_guarantee{3, 4}; + const auto end = push_back_rvalue_strong_guarantee.end(); + try { + push_back_rvalue_strong_guarantee.push_back(Evil<-1, -1, 2>{5}); + assert(false && "push_back rvalue no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::MoveConstructed && "push_back rvalue wrong exception"); + } catch (...) { + assert(false && "push_back rvalue wrong exception type"); + } + + // Check strong guarantee + copy_construction_count -= 2; + const vec<-1, -1, 2> expected{3, 4}; + copy_construction_count += 2; + assert(end == push_back_rvalue_strong_guarantee.end()); + assert(push_back_rvalue_strong_guarantee == expected); + } + assert(nothing_leaked() && "push_back rvalue leaked"); +} + +void test_insert() { + { // insert value + { + vec<-1, -1, 2> insert_value_strong_guarantee{3, 4}; + const auto end = insert_value_strong_guarantee.end(); + try { + const Evil<-1, -1, 2> to_be_inserted{3}; + insert_value_strong_guarantee.insert(insert_value_strong_guarantee.end(), to_be_inserted); + assert(false && "insert value end no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyConstructed && "insert value end wrong exception"); + } catch (...) { + assert(false && "insert value end wrong exception type"); + } + + // Check strong guarantee + copy_construction_count -= 2; + const vec<-1, -1, 2> expected{3, 4}; + copy_construction_count += 2; + assert(end == insert_value_strong_guarantee.end()); + assert(insert_value_strong_guarantee == expected); + } + assert(nothing_leaked() && "insert value end leaked"); + + try { + const Evil<-1, -1, 2> to_be_inserted{3}; + vec<-1, -1, 2> insert_value_temporary_construction{3, 4}; + insert_value_temporary_construction.insert(insert_value_temporary_construction.begin() + 1, to_be_inserted); + assert(false && "insert value temporary no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyConstructed && "insert value temporary wrong exception"); + } catch (...) { + assert(false && "insert value temporary wrong exception type"); + } + assert(nothing_leaked() && "insert value temporary leaked"); + + try { + const Evil<-1, -1, -1> to_be_inserted{3}; + vec<-1, -1, -1> insert_value_move_construction{3, 4}; + insert_value_move_construction.insert(insert_value_move_construction.begin() + 1, to_be_inserted); + assert(false && "insert value move construction no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::MoveConstructed && "insert value move construction wrong exception"); + } catch (...) { + assert(false && "insert value move construction wrong exception type"); + } + assert(nothing_leaked() && "insert value construction move leaked"); + + try { + const Evil<-1, -1, -1, -1> to_be_inserted{3}; + vec<-1, -1, -1, -1> insert_value_move_assignment{3, 4}; + insert_value_move_assignment.insert(insert_value_move_assignment.begin() + 1, to_be_inserted); + assert(false && "insert value move assignment no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::MoveAssigned && "insert value move assignment wrong exception"); + } catch (...) { + assert(false && "insert value move assignment wrong exception type"); + } + assert(nothing_leaked() && "insert value move assignment leaked"); + } + + { // insert count + { + const Evil<-1, -1, 4> to_be_inserted{5}; + vec<-1, -1, 4> insert_count_strong_guarantee{3, 4, 5}; + const auto end = insert_count_strong_guarantee.end(); + try { + insert_count_strong_guarantee.insert(insert_count_strong_guarantee.end(), 2, to_be_inserted); + assert(false && "insert count end no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyConstructed && "insert count end wrong exception"); + } catch (...) { + assert(false && "insert count end wrong exception type"); + } + + // Check strong guarantee + copy_construction_count -= 3; + const vec<-1, -1, 4> expected{3, 4, 5}; + copy_construction_count += 3; + assert(end == insert_count_strong_guarantee.end()); + assert(insert_count_strong_guarantee == expected); + } + assert(nothing_leaked() && "insert count end leaked"); + + try { + // existing elements are pushed fully into uninitialized memory + const Evil<-1, -1, 4> to_be_inserted{5}; + vec<-1, -1, 4> insert_count_move_back_uninitialized{3, 4, 5}; + insert_count_move_back_uninitialized.insert( + insert_count_move_back_uninitialized.begin() + 1, 2, to_be_inserted); + assert(false && "insert count move back uninitialized no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::MoveConstructed && "insert count move back uninitialized wrong exception"); + } catch (...) { + assert(false && "insert count move back uninitialized wrong exception type"); + } + assert(nothing_leaked() && "insert count move back uninitialized leaked"); + + try { + // existing elements are pushed partially into uninitialized memory + const Evil<-1, -1, 4> to_be_inserted{5}; + vec<-1, -1, 4> insert_count_move_back_mixed{3, 4, 5}; + insert_count_move_back_mixed.insert(insert_count_move_back_mixed.begin(), 2, to_be_inserted); + assert(false && "insert count move back mixed no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::MoveConstructed && "insert count move back mixed wrong exception"); + } catch (...) { + assert(false && "insert count move back mixed wrong exception type"); + } + assert(nothing_leaked() && "insert count move back mixed leaked"); + + try { + // new elements are copied into existing memory + const Evil<-1, -1, 4, -1> to_be_inserted{5}; + vec<-1, -1, 4, -1> insert_count_copy_assignment_uninitialized{3, 4, 5}; + insert_count_copy_assignment_uninitialized.insert( + insert_count_copy_assignment_uninitialized.begin() + 1, 2, to_be_inserted); + assert(false && "insert count copy assignment uninitialized no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyAssigned && "insert count copy assignment uninitialized wrong exception"); + } catch (...) { + assert(false && "insert count copy assignment uninitialized wrong exception type"); + } + assert(nothing_leaked() && "insert count copy assignment uninitialized leaked"); + + try { + // new elements are copied into uninitialized memory + const Evil<-1, -1, 4> to_be_inserted{5}; + vec<-1, -1, 4> insert_count_copy_assignment_mixed{3, 4, 5}; + insert_count_copy_assignment_mixed.insert( + insert_count_copy_assignment_mixed.begin() + 2, 2, to_be_inserted); + assert(false && "insert count copy construction mixed no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::MoveConstructed && "insert count copy construction mixed wrong exception"); + } catch (...) { + assert(false && "insert count copy construction mixed wrong exception type"); + } + assert(nothing_leaked() && "insert count construction copy mixed leaked"); + } + + { // insert range + { + vec<-1, -1, 5> insert_range_strong_guarantee{3, 4}; + const auto end = insert_range_strong_guarantee.end(); + try { + const vec<-1, -1, 5> to_be_inserted{5, 6}; + insert_range_strong_guarantee.insert( + insert_range_strong_guarantee.end(), to_be_inserted.begin(), to_be_inserted.end()); + assert(false && "insert range end no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyConstructed && "insert range end wrong exception"); + } catch (...) { + assert(false && "insert range end wrong exception type"); + } + + // Check strong guarantee + copy_construction_count -= 2; + const vec<-1, -1, 5> expected{3, 4}; + copy_construction_count += 2; + assert(end == insert_range_strong_guarantee.end()); + assert(insert_range_strong_guarantee == expected); + } + assert(nothing_leaked() && "insert range end leaked"); + + try { + // existing elements are pushed fully into uninitialized memory + const vec<-1, -1, 5> to_be_inserted{5, 6}; + vec<-1, -1, 5> insert_range_move_back_uninitialized{3, 4, 5}; + insert_range_move_back_uninitialized.insert( + insert_range_move_back_uninitialized.begin() + 1, to_be_inserted.begin(), to_be_inserted.end()); + assert(false && "insert range move back uninitialized no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::MoveConstructed && "insert range move back uninitialized wrong exception"); + } catch (...) { + assert(false && "insert range move back uninitialized wrong exception type"); + } + assert(nothing_leaked() && "insert range move back uninitialized leaked"); + + try { + // existing elements are pushed partially into uninitialized memory + const vec<-1, -1, 5> to_be_inserted{5, 6}; + vec<-1, -1, 5> insert_range_move_back_mixed{3, 4, 5}; + insert_range_move_back_mixed.insert( + insert_range_move_back_mixed.begin(), to_be_inserted.begin(), to_be_inserted.end()); + assert(false && "insert range move back mixed no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::MoveConstructed && "insert range move back mixed wrong exception"); + } catch (...) { + assert(false && "insert range move back mixed wrong exception type"); + } + assert(nothing_leaked() && "insert range move back mixed leaked"); + + try { + // new elements are copied into existing memory + const vec<-1, -1, 5, -1> to_be_inserted{5, 6}; + vec<-1, -1, 5, -1> insert_range_copy_assignment_uninitialized{3, 4, 5}; + insert_range_copy_assignment_uninitialized.insert( + insert_range_copy_assignment_uninitialized.begin() + 1, to_be_inserted.begin(), to_be_inserted.end()); + assert(false && "insert range copy assignment uninitialized no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyAssigned && "insert range copy assignment uninitialized wrong exception"); + } catch (...) { + assert(false && "insert range copy assignment uninitialized wrong exception type"); + } + assert(nothing_leaked() && "insert range copy assignment uninitialized leaked"); + + try { + // new elements are copied into uninitialized memory + const vec<-1, -1, 5> to_be_inserted{5, 6}; + vec<-1, -1, 5> insert_range_copy_assignment_mixed{3, 4, 5}; + insert_range_copy_assignment_mixed.insert( + insert_range_copy_assignment_mixed.begin() + 2, to_be_inserted.begin(), to_be_inserted.end()); + assert(false && "insert range copy construction mixed no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::MoveConstructed && "insert range copy construction mixed wrong exception"); + } catch (...) { + assert(false && "insert range copy construction mixed wrong exception type"); + } + assert(nothing_leaked() && "insert range construction copy mixed leaked"); + } +} + +void test_resize() { + { + vec<2, -1, 2> resize_strong_guarantee{3, 4}; + const auto end = resize_strong_guarantee.end(); + try { + resize_strong_guarantee.resize(5); + assert(false && "resize count no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::DefaultConstructed && "resize count wrong exception"); + } catch (...) { + assert(false && "resize count wrong exception type"); + } + + // Check strong guarantee + copy_construction_count -= 2; + const vec<2, -1, 2> expected{3, 4}; + copy_construction_count += 2; + assert(end == resize_strong_guarantee.end()); + assert(resize_strong_guarantee == expected); + } + assert(nothing_leaked() && "resize count leaked"); + + { + const Evil<-1, -1, 4> to_be_inserted{3}; + vec<-1, -1, 4> resize_value_strong_guarantee{3, 4}; + const auto end = resize_value_strong_guarantee.end(); + try { + resize_value_strong_guarantee.resize(5, to_be_inserted); + assert(false && "resize count value no exception"); + } catch (ThrowType e) { + assert(e == ThrowType::CopyConstructed && "resize count value wrong exception"); + } catch (...) { + assert(false && "resize count value wrong exception type"); + } + + // Check strong guarantee + copy_construction_count -= 2; + const vec<-1, -1, 4> expected{3, 4}; + copy_construction_count += 2; + assert(end == resize_value_strong_guarantee.end()); + assert(resize_value_strong_guarantee == expected); + } + assert(nothing_leaked() && "resize count value leaked"); +} + +int main() { + test_construction(); + test_assignment(); + test_emplace(); + test_insert(); + test_resize(); +}