diff options
author | Louis Dionne <ldionne.2@gmail.com> | 2022-06-06 14:01:38 -0400 |
---|---|---|
committer | Louis Dionne <ldionne.2@gmail.com> | 2022-06-27 08:34:45 -0400 |
commit | 633d1d0df766d5838c4c0b675fc7ac077159cec6 (patch) | |
tree | 9e3418953d2f4a858451fc3a090a9d2b09190918 /libcxx | |
parent | 92df8c273669590db7cf480a89f9a0181a8c1c47 (diff) | |
download | llvm-633d1d0df766d5838c4c0b675fc7ac077159cec6.zip llvm-633d1d0df766d5838c4c0b675fc7ac077159cec6.tar.gz llvm-633d1d0df766d5838c4c0b675fc7ac077159cec6.tar.bz2 |
[libc++] Use bounded iterators in std::span when the debug mode is enabled
Previously, we'd use raw pointers when the debug mode was enabled,
which means we wouldn't get out-of-range checking with std::span's
iterators.
This patch introduces a new class called __bounded_iter which can
be used to wrap iterators and make them carry around bounds-related
information. This allows iterators to assert when they are dereferenced
outside of their bounds.
As a fly-by change, this commit removes the _LIBCPP_ABI_SPAN_POINTER_ITERATORS
knob. Indeed, not using a raw pointer as the iterator type is useful to
avoid users depending on properties of raw pointers in their code.
This is an alternative to D127401.
Differential Revision: https://reviews.llvm.org/D127418
Diffstat (limited to 'libcxx')
23 files changed, 1052 insertions, 127 deletions
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 0c0ede4..4c87e90 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -289,6 +289,7 @@ set(files __iterator/access.h __iterator/advance.h __iterator/back_insert_iterator.h + __iterator/bounded_iter.h __iterator/common_iterator.h __iterator/concepts.h __iterator/counted_iterator.h diff --git a/libcxx/include/__config b/libcxx/include/__config index 90ae511..e4b7d25 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -93,8 +93,6 @@ # define _LIBCPP_ABI_OPTIMIZED_FUNCTION // All the regex constants must be distinct and nonzero. # define _LIBCPP_ABI_REGEX_CONSTANTS_NONZERO -// Use raw pointers, not wrapped ones, for std::span's iterator type. -# define _LIBCPP_ABI_SPAN_POINTER_ITERATORS // Re-worked external template instantiations for std::string with a focus on // performance and fast-path inlining. # define _LIBCPP_ABI_STRING_OPTIMIZED_EXTERNAL_INSTANTIATION diff --git a/libcxx/include/__iterator/bounded_iter.h b/libcxx/include/__iterator/bounded_iter.h new file mode 100644 index 0000000..a395e2b --- /dev/null +++ b/libcxx/include/__iterator/bounded_iter.h @@ -0,0 +1,229 @@ +// -*- 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___ITERATOR_BOUNDED_ITER_H +#define _LIBCPP___ITERATOR_BOUNDED_ITER_H + +#include <__assert> +#include <__config> +#include <__iterator/iterator_traits.h> +#include <__memory/pointer_traits.h> +#include <__utility/move.h> +#include <type_traits> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +// Iterator wrapper that carries the valid range it is allowed to access. +// +// This is a simple iterator wrapper for contiguous iterators that points +// within a [begin, end) range and carries these bounds with it. The iterator +// ensures that it is pointing within that [begin, end) range when it is +// dereferenced. +// +// Arithmetic operations are allowed and the bounds of the resulting iterator +// are not checked. Hence, it is possible to create an iterator pointing outside +// its range, but it is not possible to dereference it. +template <class _Iterator, class = __enable_if_t< __is_cpp17_contiguous_iterator<_Iterator>::value > > +struct __bounded_iter { + using value_type = typename iterator_traits<_Iterator>::value_type; + using difference_type = typename iterator_traits<_Iterator>::difference_type; + using pointer = typename iterator_traits<_Iterator>::pointer; + using reference = typename iterator_traits<_Iterator>::reference; + using iterator_category = typename iterator_traits<_Iterator>::iterator_category; +#if _LIBCPP_STD_VER > 17 + using iterator_concept = contiguous_iterator_tag; +#endif + + // Create a singular iterator. + // + // Such an iterator does not point to any object and is conceptually out of bounds, so it is + // not dereferenceable. Observing operations like comparison and assignment are valid. + _LIBCPP_HIDE_FROM_ABI __bounded_iter() = default; + + _LIBCPP_HIDE_FROM_ABI __bounded_iter(__bounded_iter const&) = default; + _LIBCPP_HIDE_FROM_ABI __bounded_iter(__bounded_iter&&) = default; + + template <class _OtherIterator, class = __enable_if_t< is_convertible<_OtherIterator, _Iterator>::value > > + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __bounded_iter(__bounded_iter<_OtherIterator> const& __other) _NOEXCEPT + : __current_(__other.__current_), + __begin_(__other.__begin_), + __end_(__other.__end_) {} + + // Assign a bounded iterator to another one, rebinding the bounds of the iterator as well. + _LIBCPP_HIDE_FROM_ABI __bounded_iter& operator=(__bounded_iter const&) = default; + _LIBCPP_HIDE_FROM_ABI __bounded_iter& operator=(__bounded_iter&&) = default; + +private: + // Create an iterator wrapping the given iterator, and whose bounds are described + // by the provided [begin, end) range. + // + // This constructor does not check whether the resulting iterator is within its bounds. + // However, it does check that the provided [begin, end) range is a valid range (that + // is, begin <= end). + // + // Since it is non-standard for iterators to have this constructor, __bounded_iter must + // be created via `std::__make_bounded_iter`. + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 explicit __bounded_iter( + _Iterator __current, _Iterator __begin, _Iterator __end) + : __current_(__current), __begin_(__begin), __end_(__end) { + _LIBCPP_ASSERT(__begin <= __end, "__bounded_iter(current, begin, end): [begin, end) is not a valid range"); + } + + template <class _It> + friend _LIBCPP_CONSTEXPR __bounded_iter<_It> __make_bounded_iter(_It, _It, _It); + +public: + // Dereference and indexing operations. + // + // These operations check that the iterator is dereferenceable, that is within [begin, end). + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 reference operator*() const _NOEXCEPT { + _LIBCPP_ASSERT( + __in_bounds(__current_), "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator"); + return *__current_; + } + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 pointer operator->() const _NOEXCEPT { + _LIBCPP_ASSERT( + __in_bounds(__current_), "__bounded_iter::operator->: Attempt to dereference an out-of-range iterator"); + return std::__to_address(__current_); + } + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 reference operator[](difference_type __n) const _NOEXCEPT { + _LIBCPP_ASSERT( + __in_bounds(__current_ + __n), "__bounded_iter::operator[]: Attempt to index an iterator out-of-range"); + return __current_[__n]; + } + + // Arithmetic operations. + // + // These operations do not check that the resulting iterator is within the bounds, since that + // would make it impossible to create a past-the-end iterator. + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 __bounded_iter& operator++() _NOEXCEPT { + ++__current_; + return *this; + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 __bounded_iter operator++(int) _NOEXCEPT { + __bounded_iter __tmp(*this); + ++*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 __bounded_iter& operator--() _NOEXCEPT { + --__current_; + return *this; + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 __bounded_iter operator--(int) _NOEXCEPT { + __bounded_iter __tmp(*this); + --*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 __bounded_iter& operator+=(difference_type __n) _NOEXCEPT { + __current_ += __n; + return *this; + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 friend __bounded_iter + operator+(__bounded_iter const& __self, difference_type __n) _NOEXCEPT { + __bounded_iter __tmp(__self); + __tmp += __n; + return __tmp; + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 friend __bounded_iter + operator+(difference_type __n, __bounded_iter const& __self) _NOEXCEPT { + __bounded_iter __tmp(__self); + __tmp += __n; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 __bounded_iter& operator-=(difference_type __n) _NOEXCEPT { + __current_ -= __n; + return *this; + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 friend __bounded_iter + operator-(__bounded_iter const& __self, difference_type __n) _NOEXCEPT { + __bounded_iter __tmp(__self); + __tmp -= __n; + return __tmp; + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 friend difference_type + operator-(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT { + return __x.__current_ - __y.__current_; + } + + // Comparison operations. + // + // These operations do not check whether the iterators are within their bounds. + // The valid range for each iterator is also not considered as part of the comparison, + // i.e. two iterators pointing to the same location will be considered equal even + // if they have different validity ranges. + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool + operator==(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT { + return __x.__current_ == __y.__current_; + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool + operator!=(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT { + return __x.__current_ != __y.__current_; + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool + operator<(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT { + return __x.__current_ < __y.__current_; + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool + operator>(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT { + return __x.__current_ > __y.__current_; + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool + operator<=(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT { + return __x.__current_ <= __y.__current_; + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool + operator>=(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT { + return __x.__current_ >= __y.__current_; + } + +private: + // Return whether the given iterator is in the bounds of this __bounded_iter. + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool __in_bounds(_Iterator const& __iter) const { + return __iter >= __begin_ && __iter < __end_; + } + + template <class> + friend struct pointer_traits; + _Iterator __current_; // current iterator + _Iterator __begin_, __end_; // valid range represented as [begin, end) +}; + +template <class _It> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __bounded_iter<_It> __make_bounded_iter(_It __it, _It __begin, _It __end) { + return __bounded_iter<_It>(std::move(__it), std::move(__begin), std::move(__end)); +} + +#if _LIBCPP_STD_VER <= 17 +template <class _Iterator> +struct __is_cpp17_contiguous_iterator<__bounded_iter<_Iterator> > : true_type {}; +#endif + +template <class _Iterator> +struct pointer_traits<__bounded_iter<_Iterator> > { + using pointer = __bounded_iter<_Iterator>; + using element_type = typename pointer_traits<_Iterator>::element_type; + using difference_type = typename pointer_traits<_Iterator>::difference_type; + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR static element_type* to_address(pointer __it) _NOEXCEPT { + return std::__to_address(__it.__current_); + } +}; + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___ITERATOR_BOUNDED_ITER_H diff --git a/libcxx/include/iterator b/libcxx/include/iterator index f0bcff9..dfa1d35 100644 --- a/libcxx/include/iterator +++ b/libcxx/include/iterator @@ -680,6 +680,7 @@ template <class E> constexpr const E* data(initializer_list<E> il) noexcept; #include <__iterator/access.h> #include <__iterator/advance.h> #include <__iterator/back_insert_iterator.h> +#include <__iterator/bounded_iter.h> #include <__iterator/common_iterator.h> #include <__iterator/concepts.h> #include <__iterator/counted_iterator.h> diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index cd71a3c..173f789 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -701,6 +701,7 @@ module std [system] { module access { private header "__iterator/access.h" } module advance { private header "__iterator/advance.h" } module back_insert_iterator { private header "__iterator/back_insert_iterator.h" } + module bounded_iter { private header "__iterator/bounded_iter.h" } module common_iterator { private header "__iterator/common_iterator.h" } module concepts { private header "__iterator/concepts.h" } module counted_iterator { private header "__iterator/counted_iterator.h" } diff --git a/libcxx/include/span b/libcxx/include/span index 4d95d36..84b23ce 100644 --- a/libcxx/include/span +++ b/libcxx/include/span @@ -131,6 +131,7 @@ template<class R> #include <__config> #include <__debug> #include <__fwd/span.h> +#include <__iterator/bounded_iter.h> #include <__iterator/concepts.h> #include <__iterator/iterator_traits.h> #include <__iterator/wrap_iter.h> @@ -216,10 +217,6 @@ concept __span_compatible_iterator = contiguous_iterator<_It> && __span_array_co template <class _Sentinel, class _It> concept __span_compatible_sentinel_for = sized_sentinel_for<_Sentinel, _It> && !is_convertible_v<_Sentinel, size_t>; -#if defined(_LIBCPP_ENABLE_DEBUG_MODE) || defined(_LIBCPP_ABI_SPAN_POINTER_ITERATORS) -# define _LIBCPP_SPAN_USE_POINTER_ITERATOR -#endif - template <typename _Tp, size_t _Extent> class _LIBCPP_TEMPLATE_VIS span { public: @@ -232,8 +229,8 @@ public: using const_pointer = const _Tp *; using reference = _Tp &; using const_reference = const _Tp &; -#ifdef _LIBCPP_SPAN_USE_POINTER_ITERATOR - using iterator = pointer; +#ifdef _LIBCPP_ENABLE_DEBUG_MODE + using iterator = __bounded_iter<pointer>; #else using iterator = __wrap_iter<pointer>; #endif @@ -314,7 +311,7 @@ public: _LIBCPP_INLINE_VISIBILITY constexpr span<element_type, _Count> first() const noexcept { - static_assert(_Count <= _Extent, "Count out of range in span::first()"); + static_assert(_Count <= _Extent, "span<T, N>::first<Count>(): Count out of range"); return span<element_type, _Count>{data(), _Count}; } @@ -322,21 +319,21 @@ public: _LIBCPP_INLINE_VISIBILITY constexpr span<element_type, _Count> last() const noexcept { - static_assert(_Count <= _Extent, "Count out of range in span::last()"); + static_assert(_Count <= _Extent, "span<T, N>::last<Count>(): Count out of range"); return span<element_type, _Count>{data() + size() - _Count, _Count}; } _LIBCPP_INLINE_VISIBILITY constexpr span<element_type, dynamic_extent> first(size_type __count) const noexcept { - _LIBCPP_ASSERT(__count <= size(), "Count out of range in span::first(count)"); + _LIBCPP_ASSERT(__count <= size(), "span<T, N>::first(count): count out of range"); return {data(), __count}; } _LIBCPP_INLINE_VISIBILITY constexpr span<element_type, dynamic_extent> last(size_type __count) const noexcept { - _LIBCPP_ASSERT(__count <= size(), "Count out of range in span::last(count)"); + _LIBCPP_ASSERT(__count <= size(), "span<T, N>::last(count): count out of range"); return {data() + size() - __count, __count}; } @@ -345,8 +342,8 @@ public: constexpr auto subspan() const noexcept -> span<element_type, _Count != dynamic_extent ? _Count : _Extent - _Offset> { - static_assert(_Offset <= _Extent, "Offset out of range in span::subspan()"); - static_assert(_Count == dynamic_extent || _Count <= _Extent - _Offset, "Offset + count out of range in span::subspan()"); + static_assert(_Offset <= _Extent, "span<T, N>::subspan<Offset, Count>(): Offset out of range"); + static_assert(_Count == dynamic_extent || _Count <= _Extent - _Offset, "span<T, N>::subspan<Offset, Count>(): Offset + Count out of range"); using _ReturnType = span<element_type, _Count != dynamic_extent ? _Count : _Extent - _Offset>; return _ReturnType{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count}; @@ -357,11 +354,11 @@ public: constexpr span<element_type, dynamic_extent> subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept { - _LIBCPP_ASSERT(__offset <= size(), "Offset out of range in span::subspan(offset, count)"); - _LIBCPP_ASSERT(__count <= size() || __count == dynamic_extent, "Count out of range in span::subspan(offset, count)"); + _LIBCPP_ASSERT(__offset <= size(), "span<T, N>::subspan(offset, count): offset out of range"); + _LIBCPP_ASSERT(__count <= size() || __count == dynamic_extent, "span<T, N>::subspan(offset, count): count out of range"); if (__count == dynamic_extent) return {data() + __offset, size() - __offset}; - _LIBCPP_ASSERT(__count <= size() - __offset, "Offset + count out of range in span::subspan(offset, count)"); + _LIBCPP_ASSERT(__count <= size() - __offset, "span<T, N>::subspan(offset, count): offset + count out of range"); return {data() + __offset, __count}; } @@ -371,7 +368,7 @@ public: _LIBCPP_INLINE_VISIBILITY constexpr reference operator[](size_type __idx) const noexcept { - _LIBCPP_ASSERT(__idx < size(), "span<T,N>[] index out of bounds"); + _LIBCPP_ASSERT(__idx < size(), "span<T, N>::operator[](index): index out of range"); return __data[__idx]; } @@ -391,15 +388,15 @@ public: // [span.iter], span iterator support _LIBCPP_INLINE_VISIBILITY constexpr iterator begin() const noexcept { -#ifdef _LIBCPP_SPAN_USE_POINTER_ITERATOR - return iterator(data()); +#ifdef _LIBCPP_ENABLE_DEBUG_MODE + 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_SPAN_USE_POINTER_ITERATOR - return iterator(data() + size()); +#ifdef _LIBCPP_ENABLE_DEBUG_MODE + return std::__make_bounded_iter(data() + size(), data(), data() + size()); #else return iterator(this, data() + size()); #endif @@ -430,8 +427,8 @@ public: using const_pointer = const _Tp *; using reference = _Tp &; using const_reference = const _Tp &; -#ifdef _LIBCPP_SPAN_USE_POINTER_ITERATOR - using iterator = pointer; +#ifdef _LIBCPP_ENABLE_DEBUG_MODE + using iterator = __bounded_iter<pointer>; #else using iterator = __wrap_iter<pointer>; #endif @@ -494,7 +491,7 @@ public: _LIBCPP_INLINE_VISIBILITY constexpr span<element_type, _Count> first() const noexcept { - _LIBCPP_ASSERT(_Count <= size(), "Count out of range in span::first()"); + _LIBCPP_ASSERT(_Count <= size(), "span<T>::first<Count>(): Count out of range"); return span<element_type, _Count>{data(), _Count}; } @@ -502,21 +499,21 @@ public: _LIBCPP_INLINE_VISIBILITY constexpr span<element_type, _Count> last() const noexcept { - _LIBCPP_ASSERT(_Count <= size(), "Count out of range in span::last()"); + _LIBCPP_ASSERT(_Count <= size(), "span<T>::last<Count>(): Count out of range"); return span<element_type, _Count>{data() + size() - _Count, _Count}; } _LIBCPP_INLINE_VISIBILITY constexpr span<element_type, dynamic_extent> first(size_type __count) const noexcept { - _LIBCPP_ASSERT(__count <= size(), "Count out of range in span::first(count)"); + _LIBCPP_ASSERT(__count <= size(), "span<T>::first(count): count out of range"); return {data(), __count}; } _LIBCPP_INLINE_VISIBILITY constexpr span<element_type, dynamic_extent> last (size_type __count) const noexcept { - _LIBCPP_ASSERT(__count <= size(), "Count out of range in span::last(count)"); + _LIBCPP_ASSERT(__count <= size(), "span<T>::last(count): count out of range"); return {data() + size() - __count, __count}; } @@ -524,8 +521,8 @@ public: _LIBCPP_INLINE_VISIBILITY constexpr span<element_type, _Count> subspan() const noexcept { - _LIBCPP_ASSERT(_Offset <= size(), "Offset out of range in span::subspan()"); - _LIBCPP_ASSERT(_Count == dynamic_extent || _Count <= size() - _Offset, "Offset + count out of range in span::subspan()"); + _LIBCPP_ASSERT(_Offset <= size(), "span<T>::subspan<Offset, Count>(): Offset out of range"); + _LIBCPP_ASSERT(_Count == dynamic_extent || _Count <= size() - _Offset, "span<T>::subspan<Offset, Count>(): Offset + Count out of range"); return span<element_type, _Count>{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count}; } @@ -533,11 +530,11 @@ public: _LIBCPP_INLINE_VISIBILITY subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept { - _LIBCPP_ASSERT(__offset <= size(), "Offset out of range in span::subspan(offset, count)"); - _LIBCPP_ASSERT(__count <= size() || __count == dynamic_extent, "count out of range in span::subspan(offset, count)"); + _LIBCPP_ASSERT(__offset <= size(), "span<T>::subspan(offset, count): offset out of range"); + _LIBCPP_ASSERT(__count <= size() || __count == dynamic_extent, "span<T>::subspan(offset, count): count out of range"); if (__count == dynamic_extent) return {data() + __offset, size() - __offset}; - _LIBCPP_ASSERT(__count <= size() - __offset, "Offset + count out of range in span::subspan(offset, count)"); + _LIBCPP_ASSERT(__count <= size() - __offset, "span<T>::subspan(offset, count): offset + count out of range"); return {data() + __offset, __count}; } @@ -547,19 +544,19 @@ public: _LIBCPP_INLINE_VISIBILITY constexpr reference operator[](size_type __idx) const noexcept { - _LIBCPP_ASSERT(__idx < size(), "span<T>[] index out of bounds"); + _LIBCPP_ASSERT(__idx < size(), "span<T>::operator[](index): index out of range"); return __data[__idx]; } _LIBCPP_INLINE_VISIBILITY constexpr reference front() const noexcept { - _LIBCPP_ASSERT(!empty(), "span<T>[].front() on empty span"); + _LIBCPP_ASSERT(!empty(), "span<T>::front() on empty span"); return __data[0]; } _LIBCPP_INLINE_VISIBILITY constexpr reference back() const noexcept { - _LIBCPP_ASSERT(!empty(), "span<T>[].back() on empty span"); + _LIBCPP_ASSERT(!empty(), "span<T>::back() on empty span"); return __data[size()-1]; } @@ -568,15 +565,15 @@ public: // [span.iter], span iterator support _LIBCPP_INLINE_VISIBILITY constexpr iterator begin() const noexcept { -#ifdef _LIBCPP_SPAN_USE_POINTER_ITERATOR - return iterator(data()); +#ifdef _LIBCPP_ENABLE_DEBUG_MODE + 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_SPAN_USE_POINTER_ITERATOR - return iterator(data() + size()); +#ifdef _LIBCPP_ENABLE_DEBUG_MODE + return std::__make_bounded_iter(data() + size(), data(), data() + size()); #else return iterator(this, data() + size()); #endif diff --git a/libcxx/test/libcxx/containers/views/views.span/debug.iterator-indexing.pass.cpp b/libcxx/test/libcxx/containers/views/views.span/debug.iterator-indexing.pass.cpp new file mode 100644 index 0000000..8ed6874 --- /dev/null +++ b/libcxx/test/libcxx/containers/views/views.span/debug.iterator-indexing.pass.cpp @@ -0,0 +1,97 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// Make sure that std::span's iterators check for OOB accesses when the debug mode is enabled. + +// REQUIRES: has-unix-headers +// UNSUPPORTED: !libcpp-has-debug-mode + +#include <span> + +#include "check_assertion.h" + +struct Foo { + int x; +}; + +int main(int, char**) { + // span<T>::iterator + { + Foo array[] = {{0}, {1}, {2}}; + std::span<Foo> const span(array, 3); + { + auto it = span.end(); + TEST_LIBCPP_ASSERT_FAILURE(*it, "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator"); + } + { + auto it = span.end(); + TEST_LIBCPP_ASSERT_FAILURE(it->x, "__bounded_iter::operator->: Attempt to dereference an out-of-range iterator"); + } + { + auto it = span.begin(); + TEST_LIBCPP_ASSERT_FAILURE(it[3], "__bounded_iter::operator[]: Attempt to index an iterator out-of-range"); + } + } + + // span<T, N>::iterator + { + Foo array[] = {{0}, {1}, {2}}; + std::span<Foo, 3> const span(array, 3); + { + auto it = span.end(); + TEST_LIBCPP_ASSERT_FAILURE(*it, "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator"); + } + { + auto it = span.end(); + TEST_LIBCPP_ASSERT_FAILURE(it->x, "__bounded_iter::operator->: Attempt to dereference an out-of-range iterator"); + } + { + auto it = span.begin(); + TEST_LIBCPP_ASSERT_FAILURE(it[3], "__bounded_iter::operator[]: Attempt to index an iterator out-of-range"); + } + } + + // span<T>::reverse_iterator + { + Foo array[] = {{0}, {1}, {2}}; + std::span<Foo> const span(array, 3); + { + auto it = span.rend(); + TEST_LIBCPP_ASSERT_FAILURE(*it, "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator"); + } + { + auto it = span.rend(); + TEST_LIBCPP_ASSERT_FAILURE(it->x, "__bounded_iter::operator->: Attempt to dereference an out-of-range iterator"); + } + { + auto it = span.rbegin(); + TEST_LIBCPP_ASSERT_FAILURE(it[3], "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator"); + } + } + + // span<T, N>::reverse_iterator + { + Foo array[] = {{0}, {1}, {2}}; + std::span<Foo, 3> const span(array, 3); + { + auto it = span.rend(); + TEST_LIBCPP_ASSERT_FAILURE(*it, "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator"); + } + { + auto it = span.rend(); + TEST_LIBCPP_ASSERT_FAILURE(it->x, "__bounded_iter::operator->: Attempt to dereference an out-of-range iterator"); + } + { + auto it = span.rbegin(); + TEST_LIBCPP_ASSERT_FAILURE(it[3], "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator"); + } + } + + return 0; +} diff --git a/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.back.pass.cpp b/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.back.pass.cpp new file mode 100644 index 0000000..e6fb20d --- /dev/null +++ b/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.back.pass.cpp @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// <span> +// +// constexpr reference back() const noexcept; + +// Make sure that accessing a span out-of-bounds triggers an assertion. + +// REQUIRES: has-unix-headers +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}} +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 + +#include <array> +#include <span> + +#include "check_assertion.h" + +int main(int, char**) { + { + std::array<int, 3> array{0, 1, 2}; + std::span<int> const s(array.data(), 0); + TEST_LIBCPP_ASSERT_FAILURE(s.back(), "span<T>::back() on empty span"); + } + + { + std::array<int, 3> array{0, 1, 2}; + std::span<int, 0> const s(array.data(), 0); + TEST_LIBCPP_ASSERT_FAILURE(s.back(), "span<T, N>::back() on empty span"); + } + + return 0; +} diff --git a/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.front.pass.cpp b/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.front.pass.cpp new file mode 100644 index 0000000..8234ef8 --- /dev/null +++ b/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.front.pass.cpp @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// <span> +// +// constexpr reference front() const noexcept; + +// Make sure that accessing a span out-of-bounds triggers an assertion. + +// REQUIRES: has-unix-headers +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}} +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 + +#include <array> +#include <span> + +#include "check_assertion.h" + +int main(int, char**) { + { + std::array<int, 3> array{0, 1, 2}; + std::span<int> const s(array.data(), 0); + TEST_LIBCPP_ASSERT_FAILURE(s.front(), "span<T>::front() on empty span"); + } + + { + std::array<int, 3> array{0, 1, 2}; + std::span<int, 0> const s(array.data(), 0); + TEST_LIBCPP_ASSERT_FAILURE(s.front(), "span<T, N>::front() on empty span"); + } + + return 0; +} diff --git a/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.op_idx.pass.cpp b/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.op_idx.pass.cpp new file mode 100644 index 0000000..2e643b3 --- /dev/null +++ b/libcxx/test/libcxx/containers/views/views.span/span.elem/assert.op_idx.pass.cpp @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// <span> +// +// constexpr reference operator[](size_type idx) const; + +// Make sure that accessing a span out-of-bounds triggers an assertion. + +// REQUIRES: has-unix-headers +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}} +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 + +#include <array> +#include <span> + +#include "check_assertion.h" + +int main(int, char**) { + { + std::array<int, 3> array{0, 1, 2}; + std::span<int> const s(array.data(), array.size()); + TEST_LIBCPP_ASSERT_FAILURE(s[3], "span<T>::operator[](index): index out of range"); + } + + { + std::array<int, 3> array{0, 1, 2}; + std::span<int, 3> const s(array.data(), array.size()); + TEST_LIBCPP_ASSERT_FAILURE(s[3], "span<T, N>::operator[](index): index out of range"); + } + + return 0; +} diff --git a/libcxx/test/libcxx/containers/views/views.span/span.sub/assert.first.pass.cpp b/libcxx/test/libcxx/containers/views/views.span/span.sub/assert.first.pass.cpp new file mode 100644 index 0000000..d17948f3 --- /dev/null +++ b/libcxx/test/libcxx/containers/views/views.span/span.sub/assert.first.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// <span> +// +// constexpr span<element_type, dynamic_extent> first(size_type count) const; + +// Make sure that creating a sub-span with an incorrect number of elements triggers an assertion. + +// REQUIRES: has-unix-headers +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}} +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 + +#include <array> +#include <span> + +#include "check_assertion.h" + +int main(int, char**) { + { + std::array<int, 3> array{0, 1, 2}; + std::span<int> const s(array.data(), array.size()); + TEST_LIBCPP_ASSERT_FAILURE(s.first(4), "span<T>::first(count): count out of range"); + TEST_LIBCPP_ASSERT_FAILURE(s.first<4>(), "span<T>::first<Count>(): Count out of range"); + } + { + std::array<int, 3> array{0, 1, 2}; + std::span<int, 3> const s(array.data(), array.size()); + TEST_LIBCPP_ASSERT_FAILURE(s.first(4), "span<T, N>::first(count): count out of range"); + // s.first<4>() caught at compile-time (tested elsewhere) + } + + return 0; +} diff --git a/libcxx/test/libcxx/containers/views/views.span/span.sub/assert.last.pass.cpp b/libcxx/test/libcxx/containers/views/views.span/span.sub/assert.last.pass.cpp new file mode 100644 index 0000000..e49f37f --- /dev/null +++ b/libcxx/test/libcxx/containers/views/views.span/span.sub/assert.last.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// <span> +// +// constexpr span<element_type, dynamic_extent> last(size_type count) const; + +// Make sure that creating a sub-span with an incorrect number of elements triggers an assertion. + +// REQUIRES: has-unix-headers +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}} +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 + +#include <array> +#include <span> + +#include "check_assertion.h" + +int main(int, char**) { + { + std::array<int, 3> array{0, 1, 2}; + std::span<int> const s(array.data(), array.size()); + TEST_LIBCPP_ASSERT_FAILURE(s.last(4), "span<T>::last(count): count out of range"); + TEST_LIBCPP_ASSERT_FAILURE(s.last<4>(), "span<T>::last<Count>(): Count out of range"); + } + { + std::array<int, 3> array{0, 1, 2}; + std::span<int, 3> const s(array.data(), array.size()); + TEST_LIBCPP_ASSERT_FAILURE(s.last(4), "span<T, N>::last(count): count out of range"); + // s.last<4>() caught at compile-time (tested elsewhere) + } + + return 0; +} diff --git a/libcxx/test/libcxx/containers/views/views.span/span.sub/assert.subspan.pass.cpp b/libcxx/test/libcxx/containers/views/views.span/span.sub/assert.subspan.pass.cpp new file mode 100644 index 0000000..487140f --- /dev/null +++ b/libcxx/test/libcxx/containers/views/views.span/span.sub/assert.subspan.pass.cpp @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// <span> +// +// constexpr span<element_type, dynamic_extent> subspan( +// size_type offset, size_type count = dynamic_extent) const; +// +// Requires: (0 <= Offset && Offset <= size()) +// && (Count == dynamic_extent || Count >= 0 && Offset + Count <= size()) + +// Make sure that creating a sub-span with an incorrect number of elements triggers an assertion. + +// REQUIRES: has-unix-headers +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}} +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 + +#include <array> +#include <span> +#include <cstddef> + +#include "check_assertion.h" + +int main(int, char**) { + { + std::array<int, 3> array{0, 1, 2}; + std::span<int> const s(array.data(), array.size()); + TEST_LIBCPP_ASSERT_FAILURE(s.subspan(4), "span<T>::subspan(offset, count): offset out of range"); + TEST_LIBCPP_ASSERT_FAILURE(s.subspan<4>(), "span<T>::subspan<Offset, Count>(): Offset out of range"); + + TEST_LIBCPP_ASSERT_FAILURE(s.subspan(0, 4), "span<T>::subspan(offset, count): count out of range"); + TEST_LIBCPP_ASSERT_FAILURE((s.subspan<0, 4>()), "span<T>::subspan<Offset, Count>(): Offset + Count out of range"); + + TEST_LIBCPP_ASSERT_FAILURE(s.subspan(1, 3), "span<T>::subspan(offset, count): offset + count out of range"); + TEST_LIBCPP_ASSERT_FAILURE((s.subspan<1, 3>()), "span<T>::subspan<Offset, Count>(): Offset + Count out of range"); + } + { + std::array<int, 3> array{0, 1, 2}; + std::span<int, 3> const s(array.data(), array.size()); + TEST_LIBCPP_ASSERT_FAILURE(s.subspan(4), "span<T, N>::subspan(offset, count): offset out of range"); + // s.subspan<4>() caught at compile-time (tested elsewhere) + + TEST_LIBCPP_ASSERT_FAILURE(s.subspan(0, 4), "span<T, N>::subspan(offset, count): count out of range"); + // s.subspan<0, 4>() caught at compile-time (tested elsewhere) + + TEST_LIBCPP_ASSERT_FAILURE(s.subspan(1, 3), "span<T, N>::subspan(offset, count): offset + count out of range"); + // s.subspan<1, 3>() caught at compile-time (tested elsewhere) + } + + return 0; +} diff --git a/libcxx/test/libcxx/iterators/bounded_iter/arithmetic.pass.cpp b/libcxx/test/libcxx/iterators/bounded_iter/arithmetic.pass.cpp new file mode 100644 index 0000000..feaef53 --- /dev/null +++ b/libcxx/test/libcxx/iterators/bounded_iter/arithmetic.pass.cpp @@ -0,0 +1,112 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// template <class _Iterator> +// struct __bounded_iter; +// +// Arithmetic operators + +#include <cstddef> +#include <iterator> + +#include "test_iterators.h" +#include "test_macros.h" + +template <class Iter> +TEST_CONSTEXPR_CXX14 bool tests() { + int array[] = {40, 41, 42, 43, 44}; + int* b = array + 0; + int* e = array + 5; + + // ++it + { + std::__bounded_iter<Iter> iter = std::__make_bounded_iter(Iter(b), Iter(b), Iter(e)); + std::__bounded_iter<Iter>& result = ++iter; + assert(&result == &iter); + assert(*iter == 41); + } + // it++ + { + std::__bounded_iter<Iter> iter = std::__make_bounded_iter(Iter(b), Iter(b), Iter(e)); + std::__bounded_iter<Iter> result = iter++; + assert(*result == 40); + assert(*iter == 41); + } + // --it + { + std::__bounded_iter<Iter> iter = std::__make_bounded_iter(Iter(b + 3), Iter(b), Iter(e)); + std::__bounded_iter<Iter>& result = --iter; + assert(&result == &iter); + assert(*iter == 42); + } + // it-- + { + std::__bounded_iter<Iter> iter = std::__make_bounded_iter(Iter(b + 3), Iter(b), Iter(e)); + std::__bounded_iter<Iter> result = iter--; + assert(*result == 43); + assert(*iter == 42); + } + // it += n + { + std::__bounded_iter<Iter> iter = std::__make_bounded_iter(Iter(b), Iter(b), Iter(e)); + std::__bounded_iter<Iter>& result = (iter += 3); + assert(&result == &iter); + assert(*iter == 43); + } + // it + n + { + std::__bounded_iter<Iter> iter = std::__make_bounded_iter(Iter(b), Iter(b), Iter(e)); + std::__bounded_iter<Iter> result = iter + 3; + assert(*iter == 40); + assert(*result == 43); + } + // n + it + { + std::__bounded_iter<Iter> iter = std::__make_bounded_iter(Iter(b), Iter(b), Iter(e)); + std::__bounded_iter<Iter> result = 3 + iter; + assert(*iter == 40); + assert(*result == 43); + } + // it -= n + { + std::__bounded_iter<Iter> iter = std::__make_bounded_iter(Iter(b + 3), Iter(b), Iter(e)); + std::__bounded_iter<Iter>& result = (iter -= 3); + assert(&result == &iter); + assert(*iter == 40); + } + // it - n + { + std::__bounded_iter<Iter> iter = std::__make_bounded_iter(Iter(b + 3), Iter(b), Iter(e)); + std::__bounded_iter<Iter> result = iter - 3; + assert(*iter == 43); + assert(*result == 40); + } + // it - it + { + std::__bounded_iter<Iter> iter1 = std::__make_bounded_iter(Iter(b), Iter(b), Iter(e)); + std::__bounded_iter<Iter> iter2 = std::__make_bounded_iter(Iter(e), Iter(b), Iter(e)); + std::ptrdiff_t result = iter2 - iter1; + assert(result == 5); + } + + return true; +} + +int main(int, char**) { + tests<int*>(); +#if TEST_STD_VER > 11 + static_assert(tests<int*>(), ""); +#endif + +#if TEST_STD_VER > 17 + tests<contiguous_iterator<int*> >(); + static_assert(tests<contiguous_iterator<int*> >(), ""); +#endif + + return 0; +} diff --git a/libcxx/test/libcxx/iterators/bounded_iter/comparison.pass.cpp b/libcxx/test/libcxx/iterators/bounded_iter/comparison.pass.cpp new file mode 100644 index 0000000..f4b0da9 --- /dev/null +++ b/libcxx/test/libcxx/iterators/bounded_iter/comparison.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// template <class _Iterator> +// struct __bounded_iter; +// +// Comparison operators + +#include <iterator> + +#include "test_iterators.h" +#include "test_macros.h" + +template <class Iter> +TEST_CONSTEXPR_CXX14 bool tests() { + int array[] = {0, 1, 2, 3, 4}; + int* b = array + 0; + int* e = array + 5; + std::__bounded_iter<Iter> const iter1 = std::__make_bounded_iter(Iter(b), Iter(b), Iter(e)); + std::__bounded_iter<Iter> const iter2 = std::__make_bounded_iter(Iter(e), Iter(b), Iter(e)); + + // operator== + { + assert(iter1 == iter1); + assert(!(iter1 == iter2)); + } + // operator!= + { + assert(iter1 != iter2); + assert(!(iter1 != iter1)); + } + // operator< + { + assert(iter1 < iter2); + assert(!(iter2 < iter1)); + assert(!(iter1 < iter1)); + } + // operator> + { + assert(iter2 > iter1); + assert(!(iter1 > iter2)); + assert(!(iter1 > iter1)); + } + // operator<= + { + assert(iter1 <= iter2); + assert(!(iter2 <= iter1)); + assert(iter1 <= iter1); + } + // operator>= + { + assert(iter2 >= iter1); + assert(!(iter1 >= iter2)); + assert(iter1 >= iter1); + } + + return true; +} + +int main(int, char**) { + tests<int*>(); +#if TEST_STD_VER > 11 + static_assert(tests<int*>(), ""); +#endif + +#if TEST_STD_VER > 17 + tests<contiguous_iterator<int*> >(); + static_assert(tests<contiguous_iterator<int*> >(), ""); +#endif + + return 0; +} diff --git a/libcxx/test/libcxx/iterators/bounded_iter/dereference.pass.cpp b/libcxx/test/libcxx/iterators/bounded_iter/dereference.pass.cpp new file mode 100644 index 0000000..d426148 --- /dev/null +++ b/libcxx/test/libcxx/iterators/bounded_iter/dereference.pass.cpp @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// template <class _Iterator> +// struct __bounded_iter; +// +// Dereference and indexing operators + +// REQUIRES: has-unix-headers +// UNSUPPORTED: c++03 +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}} +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 + +#include <iterator> + +#include "check_assertion.h" +#include "test_iterators.h" +#include "test_macros.h" + +struct Foo { + int x; + TEST_CONSTEXPR bool operator==(Foo const& other) const { return x == other.x; } +}; + +template <class Iter> +TEST_CONSTEXPR_CXX14 bool tests() { + Foo array[] = {Foo{40}, Foo{41}, Foo{42}, Foo{43}, Foo{44}}; + Foo* b = array + 0; + Foo* e = array + 5; + std::__bounded_iter<Iter> const iter1 = std::__make_bounded_iter(Iter(b), Iter(b), Iter(e)); + std::__bounded_iter<Iter> const iter2 = std::__make_bounded_iter(Iter(e), Iter(b), Iter(e)); + + // operator* + assert(*iter1 == Foo{40}); + // operator-> + assert(iter1->x == 40); + // operator[] + assert(iter1[0] == Foo{40}); + assert(iter1[1] == Foo{41}); + assert(iter1[2] == Foo{42}); + assert(iter2[-1] == Foo{44}); + assert(iter2[-2] == Foo{43}); + + return true; +} + +template <class Iter> +void test_death() { + Foo array[] = {Foo{0}, Foo{1}, Foo{2}, Foo{3}, Foo{4}}; + Foo* b = array + 0; + Foo* e = array + 5; + std::__bounded_iter<Iter> const iter = std::__make_bounded_iter(Iter(b), Iter(b), Iter(e)); + std::__bounded_iter<Iter> const oob = std::__make_bounded_iter(Iter(e), Iter(b), Iter(e)); + + // operator* + TEST_LIBCPP_ASSERT_FAILURE(*oob, "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator"); + // operator-> + TEST_LIBCPP_ASSERT_FAILURE(oob->x, "__bounded_iter::operator->: Attempt to dereference an out-of-range iterator"); + // operator[] + TEST_LIBCPP_ASSERT_FAILURE(iter[-1], "__bounded_iter::operator[]: Attempt to index an iterator out-of-range"); + TEST_LIBCPP_ASSERT_FAILURE(iter[5], "__bounded_iter::operator[]: Attempt to index an iterator out-of-range"); + TEST_LIBCPP_ASSERT_FAILURE(oob[0], "__bounded_iter::operator[]: Attempt to index an iterator out-of-range"); + TEST_LIBCPP_ASSERT_FAILURE(oob[1], "__bounded_iter::operator[]: Attempt to index an iterator out-of-range"); + TEST_LIBCPP_ASSERT_FAILURE(oob[-6], "__bounded_iter::operator[]: Attempt to index an iterator out-of-range"); +} + +int main(int, char**) { + tests<Foo*>(); + test_death<Foo*>(); +#if TEST_STD_VER > 11 + static_assert(tests<Foo*>(), ""); +#endif + +#if TEST_STD_VER > 17 + tests<contiguous_iterator<Foo*> >(); + test_death<contiguous_iterator<Foo*> >(); + static_assert(tests<contiguous_iterator<Foo*> >(), ""); +#endif + + return 0; +} diff --git a/libcxx/test/libcxx/iterators/bounded_iter/pointer_traits.pass.cpp b/libcxx/test/libcxx/iterators/bounded_iter/pointer_traits.pass.cpp new file mode 100644 index 0000000..6ae0928 --- /dev/null +++ b/libcxx/test/libcxx/iterators/bounded_iter/pointer_traits.pass.cpp @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// template <class _Iterator> +// struct __bounded_iter; +// +// std::pointer_traits specialization + +#include <cassert> +#include <cstddef> +#include <iterator> +#include <type_traits> + +#include "test_iterators.h" +#include "test_macros.h" + +template <class Iter> +TEST_CONSTEXPR_CXX14 bool tests() { + using BoundedIter = std::__bounded_iter<Iter>; + using PointerTraits = std::pointer_traits<BoundedIter>; + using BasePointerTraits = std::pointer_traits<Iter>; + static_assert(std::is_same<typename PointerTraits::pointer, BoundedIter>::value, ""); + static_assert(std::is_same<typename PointerTraits::element_type, typename BasePointerTraits::element_type>::value, ""); + static_assert(std::is_same<typename PointerTraits::difference_type, typename BasePointerTraits::difference_type>::value, ""); + + { + int array[] = {0, 1, 2, 3, 4}; + int* b = array + 0; + int* e = array + 5; + std::__bounded_iter<Iter> const iter1 = std::__make_bounded_iter(Iter(b), Iter(b), Iter(e)); + std::__bounded_iter<Iter> const iter2 = std::__make_bounded_iter(Iter(e), Iter(b), Iter(e)); + assert(std::__to_address(iter1) == b); // in-bounds iterator + assert(std::__to_address(iter2) == e); // out-of-bounds iterator +#if TEST_STD_VER > 17 + assert(std::to_address(iter1) == b); // in-bounds iterator + assert(std::to_address(iter2) == e); // out-of-bounds iterator +#endif + } + + return true; +} + +int main(int, char**) { + tests<int*>(); +#if TEST_STD_VER > 11 + static_assert(tests<int*>(), ""); +#endif + +#if TEST_STD_VER > 17 + tests<contiguous_iterator<int*> >(); + static_assert(tests<contiguous_iterator<int*> >(), ""); +#endif + + return 0; +} diff --git a/libcxx/test/libcxx/iterators/bounded_iter/types.compile.pass.cpp b/libcxx/test/libcxx/iterators/bounded_iter/types.compile.pass.cpp new file mode 100644 index 0000000..db95513 --- /dev/null +++ b/libcxx/test/libcxx/iterators/bounded_iter/types.compile.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// template <class _Iterator> +// struct __bounded_iter; +// +// Nested types + +#include <cstddef> +#include <iterator> +#include <type_traits> + +#include "test_macros.h" + +#if TEST_STD_VER > 17 +struct Iterator { + struct value_type {}; + using difference_type = int; + struct pointer {}; + using reference = value_type&; + struct iterator_category : std::random_access_iterator_tag {}; + using iterator_concept = std::contiguous_iterator_tag; +}; + +using BoundedIter1 = std::__bounded_iter<Iterator>; +static_assert(std::is_same<BoundedIter1::value_type, Iterator::value_type>::value, ""); +static_assert(std::is_same<BoundedIter1::difference_type, Iterator::difference_type>::value, ""); +static_assert(std::is_same<BoundedIter1::pointer, Iterator::pointer>::value, ""); +static_assert(std::is_same<BoundedIter1::reference, Iterator::reference>::value, ""); +static_assert(std::is_same<BoundedIter1::iterator_category, Iterator::iterator_category>::value, ""); +static_assert(std::is_same<BoundedIter1::iterator_concept, Iterator::iterator_concept>::value, ""); +#endif + + +using BoundedIter2 = std::__bounded_iter<int*>; +static_assert(std::is_same<BoundedIter2::value_type, int>::value, ""); +static_assert(std::is_same<BoundedIter2::difference_type, std::ptrdiff_t>::value, ""); +static_assert(std::is_same<BoundedIter2::pointer, int*>::value, ""); +static_assert(std::is_same<BoundedIter2::reference, int&>::value, ""); +static_assert(std::is_same<BoundedIter2::iterator_category, std::random_access_iterator_tag>::value, ""); +#if TEST_STD_VER > 17 +static_assert(std::is_same<BoundedIter2::iterator_concept, std::contiguous_iterator_tag>::value, ""); +#endif diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp index d7279a9..5761f85 100644 --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -320,6 +320,7 @@ END-SCRIPT #include <__iterator/access.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/access.h'}} #include <__iterator/advance.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/advance.h'}} #include <__iterator/back_insert_iterator.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/back_insert_iterator.h'}} +#include <__iterator/bounded_iter.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/bounded_iter.h'}} #include <__iterator/common_iterator.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/common_iterator.h'}} #include <__iterator/concepts.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/concepts.h'}} #include <__iterator/counted_iterator.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/counted_iterator.h'}} diff --git a/libcxx/test/std/containers/views/views.span/span.sub/first.fail.cpp b/libcxx/test/std/containers/views/views.span/span.sub/first.verify.cpp index 176311f..8e011b5 100644 --- a/libcxx/test/std/containers/views/views.span/span.sub/first.fail.cpp +++ b/libcxx/test/std/containers/views/views.span/span.sub/first.verify.cpp @@ -12,30 +12,18 @@ // template<size_t Count> // constexpr span<element_type, Count> first() const; // -// constexpr span<element_type, dynamic_extent> first(size_type count) const; -// // Requires: Count <= size(). #include <span> - #include <cstddef> -#include "test_macros.h" - -constexpr int carr[] = {1, 2, 3, 4}; - -int main(int, char**) { - std::span<const int, 4> sp(carr); +void f() { + int array[] = {1, 2, 3, 4}; + std::span<const int, 4> sp(array); // Count too large - { - [[maybe_unused]] auto s1 = sp.first<5>(); // expected-error-re@span:* {{static_assert failed{{( due to requirement '.*')?}} "Count out of range in span::first()"}} - } + [[maybe_unused]] auto s1 = sp.first<5>(); // expected-error@span:* {{span<T, N>::first<Count>(): Count out of range}} // Count numeric_limits - { - [[maybe_unused]] auto s1 = sp.first<std::size_t(-1)>(); // expected-error-re@span:* {{static_assert failed{{( due to requirement '.*')?}} "Count out of range in span::first()"}} - } - - return 0; + [[maybe_unused]] auto s2 = sp.first<std::size_t(-1)>(); // expected-error@span:* {{span<T, N>::first<Count>(): Count out of range}} } diff --git a/libcxx/test/std/containers/views/views.span/span.sub/last.fail.cpp b/libcxx/test/std/containers/views/views.span/span.sub/last.verify.cpp index 9cff076..1594e68 100644 --- a/libcxx/test/std/containers/views/views.span/span.sub/last.fail.cpp +++ b/libcxx/test/std/containers/views/views.span/span.sub/last.verify.cpp @@ -12,30 +12,18 @@ // template<size_t Count> // constexpr span<element_type, Count> last() const; // -// constexpr span<element_type, dynamic_extent> last(size_type count) const; -// // Requires: Count <= size(). #include <span> - #include <cstddef> -#include "test_macros.h" - -constexpr int carr[] = {1, 2, 3, 4}; - -int main(int, char**) { - std::span<const int, 4> sp(carr); +void f() { + int array[] = {1, 2, 3, 4}; + std::span<const int, 4> sp(array); // Count too large - { - [[maybe_unused]] auto s1 = sp.last<5>(); // expected-error-re@span:* {{static_assert failed{{( due to requirement '.*')?}} "Count out of range in span::last()"}} - } + [[maybe_unused]] auto s1 = sp.last<5>(); // expected-error@span:* {{span<T, N>::last<Count>(): Count out of range}} // Count numeric_limits - { - [[maybe_unused]] auto s1 = sp.last<std::size_t(-1)>(); // expected-error-re@span:* {{static_assert failed{{( due to requirement '.*')?}} "Count out of range in span::last()"}} - } - - return 0; + [[maybe_unused]] auto s2 = sp.last<std::size_t(-1)>(); // expected-error@span:* {{span<T, N>::last<Count>(): Count out of range}} } diff --git a/libcxx/test/std/containers/views/views.span/span.sub/subspan.fail.cpp b/libcxx/test/std/containers/views/views.span/span.sub/subspan.fail.cpp deleted file mode 100644 index 33eeda0..0000000 --- a/libcxx/test/std/containers/views/views.span/span.sub/subspan.fail.cpp +++ /dev/null @@ -1,53 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14, c++17 - -// <span> - -// template<size_t Offset, size_t Count = dynamic_extent> -// constexpr span<element_type, see below> subspan() const; -// -// constexpr span<element_type, dynamic_extent> subspan( -// size_type offset, size_type count = dynamic_extent) const; -// -// Requires: offset <= size() && -// (count == dynamic_extent || count <= size() - offset) - -#include <span> - -#include <cstddef> - -#include "test_macros.h" - -constexpr int carr[] = {1, 2, 3, 4}; - -int main(int, char**) { - std::span<const int, 4> sp(carr); - - // Offset too large templatized - { - [[maybe_unused]] auto s1 = sp.subspan<5>(); // expected-error-re@span:* {{static_assert failed{{( due to requirement '.*')?}} "Offset out of range in span::subspan()"}} - } - - // Count too large templatized - { - [[maybe_unused]] auto s1 = sp.subspan<0, 5>(); // expected-error-re@span:* {{static_assert failed{{( due to requirement '.*')?}} "Offset + count out of range in span::subspan()"}} - } - - // Offset + Count too large templatized - { - [[maybe_unused]] auto s1 = sp.subspan<2, 3>(); // expected-error-re@span:* {{static_assert failed{{( due to requirement '.*')?}} "Offset + count out of range in span::subspan()"}} - } - - // Offset + Count overflow templatized - { - [[maybe_unused]] auto s1 = sp.subspan<3, std::size_t(-2)>(); // expected-error-re@span:* {{static_assert failed{{( due to requirement '.*')?}} "Offset + count out of range in span::subspan()"}}, expected-error-re@span:* {{array is too large{{(.* elements)}}}} - } - - return 0; -} diff --git a/libcxx/test/std/containers/views/views.span/span.sub/subspan.verify.cpp b/libcxx/test/std/containers/views/views.span/span.sub/subspan.verify.cpp new file mode 100644 index 0000000..8ba6c8a --- /dev/null +++ b/libcxx/test/std/containers/views/views.span/span.sub/subspan.verify.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// This test also generates spurious warnings when instantiating std::span +// with a very large extent (like size_t(-2)) -- silence those. +// ADDITIONAL_COMPILE_FLAGS: -Xclang -verify-ignore-unexpected=warning + +// <span> + +// template<size_t Offset, size_t Count = dynamic_extent> +// constexpr span<element_type, see below> subspan() const; +// +// Requires: offset <= size() && +// (count == dynamic_extent || count <= size() - offset) + +#include <span> +#include <cstddef> + +void f() { + int array[] = {1, 2, 3, 4}; + std::span<const int, 4> sp(array); + + // Offset too large templatized + [[maybe_unused]] auto s1 = sp.subspan<5>(); // expected-error@span:* {{span<T, N>::subspan<Offset, Count>(): Offset out of range"}} + + // Count too large templatized + [[maybe_unused]] auto s2 = sp.subspan<0, 5>(); // expected-error@span:* {{span<T, N>::subspan<Offset, Count>(): Offset + Count out of range"}} + + // Offset + Count too large templatized + [[maybe_unused]] auto s3 = sp.subspan<2, 3>(); // expected-error@span:* {{span<T, N>::subspan<Offset, Count>(): Offset + Count out of range"}} + + // Offset + Count overflow templatized + [[maybe_unused]] auto s4 = sp.subspan<3, std::size_t(-2)>(); // expected-error@span:* {{span<T, N>::subspan<Offset, Count>(): Offset + Count out of range"}}, expected-error-re@span:* {{array is too large{{(.* elements)}}}} +} |