diff --git a/libcxx/include/span b/libcxx/include/span --- a/libcxx/include/span +++ b/libcxx/include/span @@ -195,9 +195,40 @@ >> : public true_type {}; +template +class __extent_storage { + public: + constexpr __extent_storage([[maybe_unused]] size_t __size) noexcept {} + + static constexpr size_t _extent() noexcept { return _Extent; } +}; + +template<> +class __extent_storage +{ +public: + constexpr __extent_storage(size_t __size) noexcept : __extent{__size} {} + + constexpr size_t _extent() const noexcept { return __extent; } + +private: + size_t __extent{}; +}; template class _LIBCPP_TEMPLATE_VIS span { + template + static constexpr size_t __subspan_extent() + { + if constexpr(_Count != dynamic_extent) { + return _Count; + } else if constexpr(_Extent != dynamic_extent) { + return _Extent - _Offset; + } else { + return dynamic_extent; + } + } + public: // constants and types using element_type = _Tp; @@ -218,69 +249,83 @@ static constexpr size_type extent = _Extent; // [span.cons], span constructors, copy, assignment, and destructor - template = nullptr> - _LIBCPP_INLINE_VISIBILITY constexpr span() noexcept : __data{nullptr} {} + template = nullptr> + _LIBCPP_INLINE_VISIBILITY constexpr span() noexcept : __data{nullptr}, __extent{0} {} constexpr span (const span&) noexcept = default; constexpr span& operator=(const span&) noexcept = default; - _LIBCPP_INLINE_VISIBILITY constexpr explicit span(pointer __ptr, size_type __count) : __data{__ptr} - { (void)__count; _LIBCPP_ASSERT(_Extent == __count, "size mismatch in span's constructor (ptr, len)"); } - _LIBCPP_INLINE_VISIBILITY constexpr explicit span(pointer __f, pointer __l) : __data{__f} - { (void)__l; _LIBCPP_ASSERT(_Extent == distance(__f, __l), "size mismatch in span's constructor (ptr, ptr)"); } + _LIBCPP_INLINE_VISIBILITY constexpr explicit(extent != dynamic_extent) span(pointer __ptr, size_type __count) + : __data{__ptr}, __extent{__count} { + if constexpr(extent != dynamic_extent) { + _LIBCPP_ASSERT(_Extent == __count, "size mismatch in span's constructor (ptr, len)"); + } + } - _LIBCPP_INLINE_VISIBILITY constexpr span(element_type (&__arr)[_Extent]) noexcept : __data{__arr} {} + _LIBCPP_INLINE_VISIBILITY constexpr explicit(extent != dynamic_extent) span(pointer __f, pointer __l) + : __data{__f}, __extent{static_cast(distance(__f, __l))} { + if constexpr(extent != dynamic_extent) { + _LIBCPP_ASSERT(_Extent == distance(__f, __l), "size mismatch in span's constructor (ptr, ptr)"); + } + } - template = nullptr> + _LIBCPP_INLINE_VISIBILITY + constexpr span(element_type (&__arr)[_Sz]) noexcept : __data{__arr}, __extent{_Sz} {} + + template , nullptr_t> = nullptr> _LIBCPP_INLINE_VISIBILITY - constexpr span(array<_OtherElementType, _Extent>& __arr) noexcept : __data{__arr.data()} {} + constexpr span(array<_OtherElementType, _Sz>& __arr) noexcept : __data{__arr.data()}, __extent{_Sz} {} - template , nullptr_t> = nullptr> _LIBCPP_INLINE_VISIBILITY - constexpr span(const array<_OtherElementType, _Extent>& __arr) noexcept : __data{__arr.data()} {} + constexpr span(const array<_OtherElementType, _Sz>& __arr) noexcept : __data{__arr.data()}, __extent{_Sz} {} template _LIBCPP_INLINE_VISIBILITY constexpr explicit span( _Container& __c, enable_if_t<__is_span_compatible_container<_Container, _Tp>::value, nullptr_t> = nullptr) - : __data{_VSTD::data(__c)} { + : __data{_VSTD::data(__c)}, __extent{static_cast(_VSTD::size(__c))} { + if constexpr(extent != dynamic_extent) { _LIBCPP_ASSERT(_Extent == _VSTD::size(__c), "size mismatch in span's constructor (range)"); + } } template _LIBCPP_INLINE_VISIBILITY constexpr explicit span(const _Container& __c, enable_if_t<__is_span_compatible_container::value, nullptr_t> = nullptr) - : __data{_VSTD::data(__c)} { + : __data{_VSTD::data(__c)}, __extent{static_cast(_VSTD::size(__c))} { + if constexpr(extent != dynamic_extent) { _LIBCPP_ASSERT(_Extent == _VSTD::size(__c), "size mismatch in span's constructor (range)"); + } } - template + template _LIBCPP_INLINE_VISIBILITY - constexpr span(const span<_OtherElementType, _Extent>& __other, - enable_if_t< - is_convertible_v<_OtherElementType(*)[], element_type (*)[]>, - nullptr_t> = nullptr) - : __data{__other.data()} {} - - template - _LIBCPP_INLINE_VISIBILITY - constexpr explicit span(const span<_OtherElementType, dynamic_extent>& __other, + constexpr explicit span(const span<_OtherElementType, _OtherExtent>& __other, enable_if_t< is_convertible_v<_OtherElementType(*)[], element_type (*)[]>, nullptr_t> = nullptr) noexcept - : __data{__other.data()} { _LIBCPP_ASSERT(_Extent == __other.size(), "size mismatch in span's constructor (other span)"); } + : __data{__other.data()}, __extent{__other.size()} { + if constexpr(_Extent != dynamic_extent) { + _LIBCPP_ASSERT(_Extent == __other.size(), "size mismatch in span's constructor (other span)"); + } + } - -// ~span() noexcept = default; + ~span() noexcept = default; template _LIBCPP_INLINE_VISIBILITY constexpr span first() const noexcept { - static_assert(_Count <= _Extent, "Count out of range in span::first()"); + if constexpr(extent == dynamic_extent) { + _LIBCPP_ASSERT(_Count <= size(), "Count out of range in span::first()"); + } else { + static_assert(_Count <= _Extent, "Count out of range in span::first()"); + } return span{data(), _Count}; } @@ -288,7 +333,11 @@ _LIBCPP_INLINE_VISIBILITY constexpr span last() const noexcept { - static_assert(_Count <= _Extent, "Count out of range in span::last()"); + if constexpr(extent == dynamic_extent) { + _LIBCPP_ASSERT(_Count <= size(), "Count out of range in span::last()"); + } else { + static_assert(_Count <= _Extent, "Count out of range in span::last()"); + } return span{data() + size() - _Count, _Count}; } @@ -308,13 +357,17 @@ template _LIBCPP_INLINE_VISIBILITY - constexpr auto subspan() const noexcept - -> span + constexpr auto subspan() const noexcept -> span()> { 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()"); - using _ReturnType = span; + if constexpr(_Count == dynamic_extent) { + _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()"); + } + + using _ReturnType = span()>; return _ReturnType{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count}; } @@ -331,9 +384,9 @@ return {data() + __offset, __count}; } - _LIBCPP_INLINE_VISIBILITY constexpr size_type size() const noexcept { return _Extent; } - _LIBCPP_INLINE_VISIBILITY constexpr size_type size_bytes() const noexcept { return _Extent * sizeof(element_type); } - _LIBCPP_INLINE_VISIBILITY constexpr bool empty() const noexcept { return _Extent == 0; } + _LIBCPP_INLINE_VISIBILITY constexpr size_type size() const noexcept { return __extent._extent(); } + _LIBCPP_INLINE_VISIBILITY constexpr size_type size_bytes() const noexcept { return __extent._extent() * sizeof(element_type); } + _LIBCPP_INLINE_VISIBILITY constexpr bool empty() const noexcept { return __extent._extent()== 0; } _LIBCPP_INLINE_VISIBILITY constexpr reference operator[](size_type __idx) const noexcept { @@ -361,178 +414,29 @@ _LIBCPP_INLINE_VISIBILITY constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); } _LIBCPP_INLINE_VISIBILITY constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); } - _LIBCPP_INLINE_VISIBILITY span __as_bytes() const noexcept - { return span{reinterpret_cast(data()), size_bytes()}; } + _LIBCPP_INLINE_VISIBILITY span + __as_bytes() const noexcept + { + if constexpr(_Extent == dynamic_extent) { + return span{reinterpret_cast(data()), size_bytes()}; + } else { + return span{reinterpret_cast(data()), size_bytes()}; + } + } - _LIBCPP_INLINE_VISIBILITY span __as_writable_bytes() const noexcept - { return span{reinterpret_cast(data()), size_bytes()}; } + _LIBCPP_INLINE_VISIBILITY span + __as_writable_bytes() const noexcept + { + if constexpr(_Extent == dynamic_extent) { + return span{reinterpret_cast(data()), size_bytes()}; + } else { + return span{reinterpret_cast(data()), size_bytes()}; + } + } private: pointer __data; - -}; - - -template -class _LIBCPP_TEMPLATE_VIS span<_Tp, dynamic_extent> { -private: - -public: -// constants and types - using element_type = _Tp; - using value_type = remove_cv_t<_Tp>; - using size_type = size_t; - using difference_type = ptrdiff_t; - using pointer = _Tp *; - using const_pointer = const _Tp *; - using reference = _Tp &; - using const_reference = const _Tp &; -#if (_LIBCPP_DEBUG_LEVEL == 2) || defined(_LIBCPP_ABI_SPAN_POINTER_ITERATORS) - using iterator = pointer; -#else - using iterator = __wrap_iter; -#endif - using reverse_iterator = _VSTD::reverse_iterator; - - static constexpr size_type extent = dynamic_extent; - -// [span.cons], span constructors, copy, assignment, and destructor - _LIBCPP_INLINE_VISIBILITY constexpr span() noexcept : __data{nullptr}, __size{0} {} - - constexpr span (const span&) noexcept = default; - constexpr span& operator=(const span&) noexcept = default; - - _LIBCPP_INLINE_VISIBILITY constexpr span(pointer __ptr, size_type __count) : __data{__ptr}, __size{__count} {} - _LIBCPP_INLINE_VISIBILITY constexpr span(pointer __f, pointer __l) : __data{__f}, __size{static_cast(distance(__f, __l))} {} - - template - _LIBCPP_INLINE_VISIBILITY - constexpr span(element_type (&__arr)[_Sz]) noexcept : __data{__arr}, __size{_Sz} {} - - template , nullptr_t> = nullptr> - _LIBCPP_INLINE_VISIBILITY - constexpr span(array<_OtherElementType, _Sz>& __arr) noexcept : __data{__arr.data()}, __size{_Sz} {} - - template , nullptr_t> = nullptr> - _LIBCPP_INLINE_VISIBILITY - constexpr span(const array<_OtherElementType, _Sz>& __arr) noexcept : __data{__arr.data()}, __size{_Sz} {} - - template - _LIBCPP_INLINE_VISIBILITY - constexpr span( _Container& __c, - enable_if_t<__is_span_compatible_container<_Container, _Tp>::value, nullptr_t> = nullptr) - : __data{_VSTD::data(__c)}, __size{(size_type) _VSTD::size(__c)} {} - - template - _LIBCPP_INLINE_VISIBILITY - constexpr span(const _Container& __c, - enable_if_t<__is_span_compatible_container::value, nullptr_t> = nullptr) - : __data{_VSTD::data(__c)}, __size{(size_type) _VSTD::size(__c)} {} - - - template - _LIBCPP_INLINE_VISIBILITY - constexpr span(const span<_OtherElementType, _OtherExtent>& __other, - enable_if_t< - is_convertible_v<_OtherElementType(*)[], element_type (*)[]>, - nullptr_t> = nullptr) noexcept - : __data{__other.data()}, __size{__other.size()} {} - -// ~span() noexcept = default; - - template - _LIBCPP_INLINE_VISIBILITY - constexpr span first() const noexcept - { - _LIBCPP_ASSERT(_Count <= size(), "Count out of range in span::first()"); - return span{data(), _Count}; - } - - template - _LIBCPP_INLINE_VISIBILITY - constexpr span last() const noexcept - { - _LIBCPP_ASSERT(_Count <= size(), "Count out of range in span::last()"); - return span{data() + size() - _Count, _Count}; - } - - _LIBCPP_INLINE_VISIBILITY - constexpr span first(size_type __count) const noexcept - { - _LIBCPP_ASSERT(__count <= size(), "Count out of range in span::first(count)"); - return {data(), __count}; - } - - _LIBCPP_INLINE_VISIBILITY - constexpr span last (size_type __count) const noexcept - { - _LIBCPP_ASSERT(__count <= size(), "Count out of range in span::last(count)"); - return {data() + size() - __count, __count}; - } - - template - _LIBCPP_INLINE_VISIBILITY - constexpr span 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()"); - return span{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count}; - } - - constexpr span - _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)"); - if (__count == dynamic_extent) - return {data() + __offset, size() - __offset}; - _LIBCPP_ASSERT(__count <= size() - __offset, "Offset + count out of range in span::subspan(offset, count)"); - return {data() + __offset, __count}; - } - - _LIBCPP_INLINE_VISIBILITY constexpr size_type size() const noexcept { return __size; } - _LIBCPP_INLINE_VISIBILITY constexpr size_type size_bytes() const noexcept { return __size * sizeof(element_type); } - _LIBCPP_INLINE_VISIBILITY constexpr bool empty() const noexcept { return __size == 0; } - - _LIBCPP_INLINE_VISIBILITY constexpr reference operator[](size_type __idx) const noexcept - { - _LIBCPP_ASSERT(__idx < size(), "span[] index out of bounds"); - return __data[__idx]; - } - - _LIBCPP_INLINE_VISIBILITY constexpr reference front() const noexcept - { - _LIBCPP_ASSERT(!empty(), "span[].front() on empty span"); - return __data[0]; - } - - _LIBCPP_INLINE_VISIBILITY constexpr reference back() const noexcept - { - _LIBCPP_ASSERT(!empty(), "span[].back() on empty span"); - return __data[size()-1]; - } - - - _LIBCPP_INLINE_VISIBILITY constexpr pointer data() const noexcept { return __data; } - -// [span.iter], span iterator support - _LIBCPP_INLINE_VISIBILITY constexpr iterator begin() const noexcept { return iterator(data()); } - _LIBCPP_INLINE_VISIBILITY constexpr iterator end() const noexcept { return iterator(data() + size()); } - _LIBCPP_INLINE_VISIBILITY constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); } - _LIBCPP_INLINE_VISIBILITY constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); } - - _LIBCPP_INLINE_VISIBILITY span __as_bytes() const noexcept - { return {reinterpret_cast(data()), size_bytes()}; } - - _LIBCPP_INLINE_VISIBILITY span __as_writable_bytes() const noexcept - { return {reinterpret_cast(data()), size_bytes()}; } - -private: - pointer __data; - size_type __size; + [[no_unique_address]] __extent_storage<_Extent> __extent; }; #if !defined(_LIBCPP_HAS_NO_RANGES) diff --git a/libcxx/test/std/containers/views/span.sub/subspan.fail.cpp b/libcxx/test/std/containers/views/span.sub/subspan.fail.cpp --- a/libcxx/test/std/containers/views/span.sub/subspan.fail.cpp +++ b/libcxx/test/std/containers/views/span.sub/subspan.fail.cpp @@ -47,7 +47,7 @@ // 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)}}}} + [[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()"}} } return 0;