[libcxx-commits] [libcxx] 633d1d0 - [libc++] Use bounded iterators in std::span when the debug mode is enabled

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jun 27 05:35:04 PDT 2022


Author: Louis Dionne
Date: 2022-06-27T08:34:45-04:00
New Revision: 633d1d0df766d5838c4c0b675fc7ac077159cec6

URL: https://github.com/llvm/llvm-project/commit/633d1d0df766d5838c4c0b675fc7ac077159cec6
DIFF: https://github.com/llvm/llvm-project/commit/633d1d0df766d5838c4c0b675fc7ac077159cec6.diff

LOG: [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

Added: 
    libcxx/include/__iterator/bounded_iter.h
    libcxx/test/libcxx/containers/views/views.span/debug.iterator-indexing.pass.cpp
    libcxx/test/libcxx/containers/views/views.span/span.elem/assert.back.pass.cpp
    libcxx/test/libcxx/containers/views/views.span/span.elem/assert.front.pass.cpp
    libcxx/test/libcxx/containers/views/views.span/span.elem/assert.op_idx.pass.cpp
    libcxx/test/libcxx/containers/views/views.span/span.sub/assert.first.pass.cpp
    libcxx/test/libcxx/containers/views/views.span/span.sub/assert.last.pass.cpp
    libcxx/test/libcxx/containers/views/views.span/span.sub/assert.subspan.pass.cpp
    libcxx/test/libcxx/iterators/bounded_iter/arithmetic.pass.cpp
    libcxx/test/libcxx/iterators/bounded_iter/comparison.pass.cpp
    libcxx/test/libcxx/iterators/bounded_iter/dereference.pass.cpp
    libcxx/test/libcxx/iterators/bounded_iter/pointer_traits.pass.cpp
    libcxx/test/libcxx/iterators/bounded_iter/types.compile.pass.cpp
    libcxx/test/std/containers/views/views.span/span.sub/first.verify.cpp
    libcxx/test/std/containers/views/views.span/span.sub/last.verify.cpp
    libcxx/test/std/containers/views/views.span/span.sub/subspan.verify.cpp

Modified: 
    libcxx/include/CMakeLists.txt
    libcxx/include/__config
    libcxx/include/iterator
    libcxx/include/module.modulemap.in
    libcxx/include/span
    libcxx/test/libcxx/private_headers.verify.cpp

Removed: 
    libcxx/test/std/containers/views/views.span/span.sub/first.fail.cpp
    libcxx/test/std/containers/views/views.span/span.sub/last.fail.cpp
    libcxx/test/std/containers/views/views.span/span.sub/subspan.fail.cpp


################################################################################
diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 0c0ede4ce01fe..4c87e9032d063 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 90ae511b4269b..e4b7d25edf343 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 0000000000000..a395e2b638ebe
--- /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 
diff erence_type   = typename iterator_traits<_Iterator>::
diff erence_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[](
diff erence_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+=(
diff erence_type __n) _NOEXCEPT {
+    __current_ += __n;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 friend __bounded_iter
+  operator+(__bounded_iter const& __self, 
diff erence_type __n) _NOEXCEPT {
+    __bounded_iter __tmp(__self);
+    __tmp += __n;
+    return __tmp;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 friend __bounded_iter
+  operator+(
diff erence_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-=(
diff erence_type __n) _NOEXCEPT {
+    __current_ -= __n;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 friend __bounded_iter
+  operator-(__bounded_iter const& __self, 
diff erence_type __n) _NOEXCEPT {
+    __bounded_iter __tmp(__self);
+    __tmp -= __n;
+    return __tmp;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 friend 
diff erence_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 
diff erent 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 
diff erence_type = typename pointer_traits<_Iterator>::
diff erence_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 f0bcff96ad01a..dfa1d35e0d909 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 cd71a3c55d975..173f789666953 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 4d95d3640db71..84b23ce60287a 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 0000000000000..8ed68741493de
--- /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 0000000000000..e6fb20dbeb536
--- /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 0000000000000..8234ef8e68fb3
--- /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 0000000000000..2e643b3372084
--- /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 0000000000000..d17948f390fcf
--- /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 0000000000000..e49f37f340906
--- /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 0000000000000..487140f9fbb04
--- /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 0000000000000..feaef53ae09d5
--- /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::ptr
diff _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 0000000000000..f4b0da9511eaf
--- /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 0000000000000..d4261487044d6
--- /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 0000000000000..6ae0928b7e528
--- /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::
diff erence_type, typename BasePointerTraits::
diff erence_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 0000000000000..db95513055f81
--- /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 
diff erence_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::
diff erence_type, Iterator::
diff erence_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::
diff erence_type, std::ptr
diff _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 d7279a98658f1..5761f85714178 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
similarity index 51%
rename from libcxx/test/std/containers/views/views.span/span.sub/first.fail.cpp
rename to libcxx/test/std/containers/views/views.span/span.sub/first.verify.cpp
index 176311f36dd7d..8e011b55bc497 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 at span:* {{static_assert failed{{( due to requirement '.*')?}} "Count out of range in span::first()"}}
-  }
+  [[maybe_unused]] auto s1 = sp.first<5>(); // expected-error at 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 at 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 at 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
similarity index 51%
rename from libcxx/test/std/containers/views/views.span/span.sub/last.fail.cpp
rename to libcxx/test/std/containers/views/views.span/span.sub/last.verify.cpp
index 9cff076dd77e1..1594e68d0258b 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 at span:* {{static_assert failed{{( due to requirement '.*')?}} "Count out of range in span::last()"}}
-  }
+  [[maybe_unused]] auto s1 = sp.last<5>(); // expected-error at 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 at 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 at 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 33eeda0a08eaf..0000000000000
--- 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 at 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 at 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 at 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 at span:* {{static_assert failed{{( due to requirement '.*')?}} "Offset + count out of range in span::subspan()"}}, expected-error-re at 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 0000000000000..8ba6c8a214a9c
--- /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 at 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 at 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 at 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 at span:* {{span<T, N>::subspan<Offset, Count>(): Offset + Count out of range"}}, expected-error-re at span:* {{array is too large{{(.* elements)}}}}
+}


        


More information about the libcxx-commits mailing list