[libcxx-commits] [libcxx] WIP - [libc++][spanstream] P0448R4: A `strstream` replacement using `span<charT>` as buffer (PR #83541)

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Fri Mar 1 00:28:59 PST 2024


https://github.com/H-G-Hristov created https://github.com/llvm/llvm-project/pull/83541

Implements: P0448R https://wg21.link/P0448R4

- https://eel.is/c++draft/span.streams
  - https://eel.is/c++draft/spanbuf
  - https://eel.is/c++draft/ispanstream
  - https://eel.is/c++draft/ospanstream
  - https://eel.is/c++draft/spanstream

>From f31c773d106709053e45ee8aff8ce40948b9a51f Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 27 Feb 2024 08:51:49 +0200
Subject: [PATCH 1/7] [libc++][spanstream] P0448R4: A `strstream` replacement
 using `span<charT>` as buffer

Implements: P0448R https://wg21.link/P0448R4
- https://eel.is/c++draft/span.streams
  - https://eel.is/c++draft/spanbuf
  - https://eel.is/c++draft/ispanstream
  - https://eel.is/c++draft/ospanstream
  - https://eel.is/c++draft/spanstream
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/19.rst               |   1 +
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__std_clang_module             |   3 +
 libcxx/include/module.modulemap.in            |   4 +
 libcxx/include/spanstream                     | 428 ++++++++++++++++++
 libcxx/include/version                        |   2 +-
 libcxx/modules/std.compat.cppm.in             |   3 -
 libcxx/modules/std.cppm.in                    |   6 +-
 .../ispanstream.general/lit.local.cfg         |   3 +
 .../nothing_to_do.pass.cpp                    |  15 +
 .../ispanstream/types.compile.pass.cpp        |  79 ++++
 .../ospanstream.general/lit.local.cfg         |   3 +
 .../nothing_to_do.pass.cpp                    |  15 +
 .../ospanstream/types.compile.pass.cpp        |  79 ++++
 .../spanbuf/spanbuf.cons/default.pass.cpp     |  45 ++
 .../spanbuf/spanbuf.general/lit.local.cfg     |   3 +
 .../spanbuf.general/nothing_to_do.pass.cpp    |  15 +
 .../spanbuf/types.compile.pass.cpp            |  79 ++++
 .../spanstream.general/lit.local.cfg          |   3 +
 .../spanstream.general/nothing_to_do.pass.cpp |  15 +
 .../spanstream/types.compile.pass.cpp         |  78 ++++
 .../spanstream.version.compile.pass.cpp       |  68 +++
 .../version.version.compile.pass.cpp          |  32 +-
 .../generate_feature_test_macro_components.py |   1 -
 libcxx/utils/libcxx/header_information.py     |   3 +-
 27 files changed, 955 insertions(+), 33 deletions(-)
 create mode 100644 libcxx/include/spanstream
 create mode 100644 libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.general/lit.local.cfg
 create mode 100644 libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.general/nothing_to_do.pass.cpp
 create mode 100644 libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/input.output/span.streams/ospanstream/ospanstream.general/lit.local.cfg
 create mode 100644 libcxx/test/std/input.output/span.streams/ospanstream/ospanstream.general/nothing_to_do.pass.cpp
 create mode 100644 libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/default.pass.cpp
 create mode 100644 libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.general/lit.local.cfg
 create mode 100644 libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.general/nothing_to_do.pass.cpp
 create mode 100644 libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/input.output/span.streams/spanstream/spanstream.general/lit.local.cfg
 create mode 100644 libcxx/test/std/input.output/span.streams/spanstream/spanstream.general/nothing_to_do.pass.cpp
 create mode 100644 libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/spanstream.version.compile.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 468226c0c2dddf..1c39df01b3f0ee 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -370,7 +370,7 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_reference_from_temporary``              *unimplemented*
     --------------------------------------------------- -----------------
-    ``__cpp_lib_spanstream``                            *unimplemented*
+    ``__cpp_lib_spanstream``                            ``202106L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_stacktrace``                            *unimplemented*
     --------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index 78c6bb87a5a402..536960b4681298 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -41,6 +41,7 @@ Implemented Papers
 - P2637R3 - Member ``visit``
 - P2652R2 - Disallow User Specialization of ``allocator_traits``
 - P2819R2 - Add ``tuple`` protocol to ``complex``
+- P0448R4 - A ``strstream`` replacement using ``span<charT>`` as buffer
 
 
 Improvements and New Features
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index eb415ed8c031fa..084c101ece9392 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -12,7 +12,7 @@
 "`P2259R1 <https://wg21.link/P2259R1>`__","LWG","Repairing input range adaptors and counted_iterator","February 2021","","","|ranges|"
 "","","","","","",""
 "`P0401R6 <https://wg21.link/P0401R6>`__","LWG","Providing size feedback in the Allocator interface","June 2021","|Complete|","15.0"
-"`P0448R4 <https://wg21.link/P0448R4>`__","LWG","A strstream replacement using span<charT> as buffer","June 2021","",""
+"`P0448R4 <https://wg21.link/P0448R4>`__","LWG","A ``strstream`` replacement using ``span<charT>`` as buffer","June 2021","|Partial|","19.0"
 "`P1132R8 <https://wg21.link/P1132R8>`__","LWG","out_ptr - a scalable output pointer abstraction","June 2021","",""
 "`P1328R1 <https://wg21.link/P1328R1>`__","LWG","Making std::type_info::operator== constexpr","June 2021","|Complete|","17.0"
 "`P1425R4 <https://wg21.link/P1425R4>`__","LWG","Iterators pair constructors for stack and queue","June 2021","|Complete|","14.0","|ranges|"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index cafd8c6e00d968..ef5d523011e099 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -986,6 +986,7 @@ set(files
   shared_mutex
   source_location
   span
+  spanstream
   sstream
   stack
   stdatomic.h
diff --git a/libcxx/include/__std_clang_module b/libcxx/include/__std_clang_module
index 18d6ce6b46c1f6..f7cefc697c316f 100644
--- a/libcxx/include/__std_clang_module
+++ b/libcxx/include/__std_clang_module
@@ -163,6 +163,9 @@
 #include <source_location>
 #include <span>
 #if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <spanstream>
+#endif
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
 #  include <sstream>
 #endif
 #include <stack>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 219906aa9a5668..b70c99f898fe75 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -233,6 +233,10 @@ module std_span [system] {
   export std_version
   export std_private_span_span_fwd
 }
+module std_spanstream [system] {
+  header "spanstream"
+  export *
+}
 module std_sstream [system] {
   header "sstream"
   export *
diff --git a/libcxx/include/spanstream b/libcxx/include/spanstream
new file mode 100644
index 00000000000000..f25a04c2cb5254
--- /dev/null
+++ b/libcxx/include/spanstream
@@ -0,0 +1,428 @@
+// -*- 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_SPANSTREAM
+#define _LIBCPP_SPANSTREAM
+
+// clang-format off
+
+/*
+  Span-based streams [span.streams]
+
+  template<class charT, class traits = char_traits<charT>>
+    class basic_spanbuf;
+
+  template<class charT, class traits>
+    void swap(basic_spanbuf<charT, traits>& x, basic_spanbuf<charT, traits>& y);
+
+  using spanbuf = basic_spanbuf<char>;
+  using wspanbuf = basic_spanbuf<wchar_t>;
+
+  template<class charT, class traits = char_traits<charT>>
+    class basic_ispanstream;
+
+  template<class charT, class traits>
+    void swap(basic_ispanstream<charT, traits>& x, basic_ispanstream<charT, traits>& y);
+
+  using ispanstream = basic_ispanstream<char>;
+  using wispanstream = basic_ispanstream<wchar_t>;
+
+  template<class charT, class traits = char_traits<charT>>
+    class basic_ospanstream;
+
+  template<class charT, class traits>
+    void swap(basic_ospanstream<charT, traits>& x, basic_ospanstream<charT, traits>& y);
+
+  using ospanstream = basic_ospanstream<char>;
+  using wospanstream = basic_ospanstream<wchar_t>;
+
+  template<class charT, class traits = char_traits<charT>>
+    class basic_spanstream;
+
+  template<class charT, class traits>
+    void swap(basic_spanstream<charT, traits>& x, basic_spanstream<charT, traits>& y);
+
+  using spanstream = basic_spanstream<char>;
+  using wspanstream = basic_spanstream<wchar_t>;
+*/
+
+// clang-format on
+
+#include <__assert> // all public C++ headers provide the assertion handler
+#include <__availability>
+#include <__config>
+#include <__memory/addressof.h>
+#include <__ranges/concepts.h>
+#include <__utility/cmp.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+#include <__utility/swap.h>
+#include <iostream>
+#include <span>
+#include <streambuf>
+#include <version>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+// Class template basic_spanbuf [spanbuf]
+
+template <class _CharT, class _Traits = char_traits<_CharT>>
+class _LIBCPP_TEMPLATE_VIS basic_spanbuf : public basic_streambuf<_CharT, _Traits> {
+public:
+  using char_type   = _CharT;
+  using int_type    = typename _Traits::int_type;
+  using pos_type    = typename _Traits::pos_type;
+  using off_type    = typename _Traits::off_type;
+  using traits_type = _Traits;
+
+  // [spanbuf.cons], constructors
+
+  _LIBCPP_HIDE_FROM_ABI basic_spanbuf() : basic_spanbuf(ios_base::in | ios_base::out) {}
+
+  _LIBCPP_HIDE_FROM_ABI explicit basic_spanbuf(ios_base::openmode __which)
+      : basic_spanbuf(std::span<_CharT>(), __which) {}
+
+  _LIBCPP_HIDE_FROM_ABI explicit basic_spanbuf(std::span<_CharT> __s,
+                                               ios_base::openmode __which = ios_base::in | ios_base::out)
+      : basic_streambuf<_CharT, _Traits>{}, __mode_{__which}, __buf_{__s} {
+    this->span(__s);
+  }
+
+  basic_spanbuf(const basic_spanbuf&) = delete;
+  _LIBCPP_HIDE_FROM_ABI basic_spanbuf(basic_spanbuf&& __rhs)
+      : basic_streambuf<_CharT, _Traits>{std::move(__rhs)},
+        __mode_{std::move(__rhs.__mode_)},
+        __buf_{std::move(__rhs.__buf_)} {}
+
+  // [spanbuf.assign], assignment and swap
+
+  basic_spanbuf& operator=(const basic_spanbuf&) = delete;
+  _LIBCPP_HIDE_FROM_ABI basic_spanbuf& operator=(basic_spanbuf&& __rhs) {
+    basic_spanbuf __tmp{std::move(__rhs)};
+    this->swap(__tmp);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void swap(basic_spanbuf& __rhs) {
+    basic_streambuf<_CharT, _Traits>::swap(__rhs);
+    std::swap(__mode_, __rhs.__mode_);
+    std::swap(__buf_, __rhs.__buf_);
+  }
+
+  // [spanbuf.members], member functions
+
+  _LIBCPP_HIDE_FROM_ABI std::span<_CharT> span() const noexcept {
+    if (__mode_ & ios_base::out) {
+      return std::span<_CharT>(this->pbase(), this->pptr());
+    }
+    return __buf_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void span(std::span<_CharT> __s) noexcept {
+    __buf_ = __s;
+
+    if (__mode_ & ios_base::out) {
+      this->setp(__s.data(), __s.data() + __s.size());
+      if (__mode_ & ios_base::ate) {
+        this->pbump(__s.size());
+      }
+    }
+
+    if (__mode_ & ios_base::in) {
+      this->setg(__s.data(), __s.data(), __s.data() + __s.size());
+    }
+  }
+
+protected:
+  // [spanbuf.virtuals], overridden virtual functions
+
+  _LIBCPP_HIDE_FROM_ABI basic_streambuf<_CharT, _Traits>* setbuf(_CharT* __s, streamsize __n) override {
+    this->span(std::span<_CharT>(__s, __n));
+    return this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI pos_type
+  seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode __which = ios_base::in | ios_base::out) override {
+    const pos_type __error(off_type(-1));
+
+    if ((__which & ios_base::in) && (__which & ios_base::out) && (ios_base::cur == __way))
+      return __error;
+
+    off_type __baseoff = [this, __way, __which] {
+      switch (__way) {
+      case ios_base::beg:
+        return off_type(0);
+
+      case ios_base::cur:
+        if (__which & ios_base::out)
+          return off_type(this->pptr() - this->pbase());
+        return off_type(this->gptr() - this->eback());
+
+      case ios_base::end:
+        if ((__which & ios_base::out) && !(__which & ios_base::in))
+          return off_type(this->pptr() - this->pbase());
+        return off_type(__buf_.size());
+      }
+    }();
+
+    off_type __newoff;
+    if (__builtin_add_overflow(__baseoff, __off, &__newoff) || (__newoff < off_type(0)) ||
+        (std::cmp_greater(__newoff, __buf_.size())))
+      return __error;
+
+    if (__which & ios_base::in) {
+      if ((this->gptr() == nullptr) && (__newoff != off_type(0)))
+        return __error;
+      this->setg(this->eback(), this->eback() + __newoff, this->egptr());
+    }
+
+    if (__which & ios_base::out) {
+      if ((this->pptr() == nullptr) && (__newoff != off_type(0)))
+        return __error;
+      this->setp(this->pbase(), this->epptr());
+      this->pbump(__newoff);
+    }
+
+    return pos_type(__newoff);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI pos_type seekpos(pos_type __sp,
+                                         ios_base::openmode __which = ios_base::in | ios_base::out) override {
+    return seekoff(off_type(__sp), ios_base::beg, __which);
+  }
+
+private:
+  ios_base::openmode __mode_; // exposition only
+  std::span<_CharT> __buf_;   // exposition only
+};
+
+template <class _CharT, class _Traits>
+void swap(basic_spanbuf<_CharT, _Traits>& __x, basic_spanbuf<_CharT, _Traits>& __y) {
+  __x.swap(__y);
+}
+
+using spanbuf = basic_spanbuf<char>;
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+using wspanbuf = basic_spanbuf<wchar_t>;
+#  endif
+
+// Class template basic_ispanstream [ispanstream]
+
+template <class _CharT, class _Traits = char_traits<_CharT>>
+class basic_ispanstream : public basic_istream<_CharT, _Traits> {
+public:
+  using char_type   = _CharT;
+  using int_type    = typename _Traits::int_type;
+  using pos_type    = typename _Traits::pos_type;
+  using off_type    = typename _Traits::off_type;
+  using traits_type = _Traits;
+
+  // [ispanstream.cons], constructors
+
+  explicit basic_ispanstream(std::span<_CharT> __s, ios_base::openmode __which = ios_base::in)
+      : basic_istream<_CharT, _Traits>(std::addressof(__sb_)),
+        __sb_(basic_spanbuf<_CharT, _Traits>(__s, __which | ios_base::in)) {}
+
+  basic_ispanstream(const basic_ispanstream&) = delete;
+
+  basic_ispanstream(basic_ispanstream&& __rhs)
+      : basic_istream<_CharT, _Traits>(std::move(__rhs)), __sb_(std::move(__rhs.__sb_)) {
+    basic_istream<_CharT, _Traits>::set_rdbuf(std::addressof(__sb_));
+  }
+
+  template <ranges::borrowed_range _ROSeq>
+    requires(!convertible_to<_ROSeq, std::span<_CharT>>) && convertible_to<_ROSeq, std::span<const _CharT>>
+  explicit basic_ispanstream(_ROSeq&& __s) : basic_istream<_CharT, _Traits>(std::addressof(__sb_)) {
+    std::span<const _CharT> __sp(std::forward<_ROSeq>(__s));
+    this->span(std::span<_CharT>(std::span<_CharT>(const_cast<_CharT*>(__sp.data()), __sp.size())));
+  }
+
+  basic_ispanstream& operator=(const basic_ispanstream&) = delete;
+
+  basic_ispanstream& operator=(basic_ispanstream&& __rhs) {
+    basic_ispanstream __tmp{std::move(__rhs)};
+    this->swap(__tmp);
+    return *this;
+  }
+
+  // [ispanstream.swap], swap
+
+  void swap(basic_ispanstream& __rhs) {
+    basic_istream<_CharT, _Traits>::swap(__rhs);
+    __sb_.swap(__rhs.__sb_);
+  }
+
+  // [ispanstream.members], member functions
+
+  basic_spanbuf<_CharT, _Traits>* rdbuf() const noexcept {
+    return const_cast<basic_spanbuf<_CharT, _Traits>*>(std::addressof(__sb_));
+  }
+
+  std::span<const _CharT> span() const noexcept { return rdbuf()->span(); }
+
+  void span(std::span<_CharT> __s) noexcept { rdbuf()->span(__s); }
+
+  template <ranges::borrowed_range _ROSeq>
+    requires(!convertible_to<_ROSeq, std::span<_CharT>>) && convertible_to<_ROSeq, std::span<const _CharT>>
+  void span(_ROSeq&& __s) noexcept {
+    std::span<const _CharT> __sp(std::forward<_ROSeq>(__s));
+    this->span(std::span<_CharT>(const_cast<_CharT*>(__sp.data()), __sp.size()));
+  }
+
+private:
+  basic_spanbuf<_CharT, _Traits> __sb_; // exposition only
+};
+
+template <class _CharT, class _Traits>
+void swap(basic_ispanstream<_CharT, _Traits>& __x, basic_ispanstream<_CharT, _Traits>& __y) {
+  __x.swap(__y);
+}
+
+using ispanstream = basic_ispanstream<char>;
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+using wispanstream = basic_ispanstream<wchar_t>;
+#  endif
+
+// Class template basic_ospanstream [ospanstream]
+
+template <class _CharT, class _Traits = char_traits<_CharT>>
+class basic_ospanstream : public basic_ostream<_CharT, _Traits> {
+public:
+  using char_type   = _CharT;
+  using int_type    = typename _Traits::int_type;
+  using pos_type    = typename _Traits::pos_type;
+  using off_type    = typename _Traits::off_type;
+  using traits_type = _Traits;
+
+  // [ospanstream.cons], constructors
+
+  explicit basic_ospanstream(std::span<_CharT> __s, ios_base::openmode __which = ios_base::out)
+      : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)),
+        __sb_(basic_spanbuf<_CharT, _Traits>(__s, __which | ios_base::out)) {}
+
+  basic_ospanstream(const basic_ospanstream&) = delete;
+
+  basic_ospanstream(basic_ospanstream&& __rhs) : basic_ospanstream(std::move(__rhs)), __sb_(std::move(__rhs.__sb_)) {
+    basic_ostream<_CharT, _Traits>::set_rdbuf(std::addressof(__sb_));
+  }
+
+  basic_ospanstream& operator=(const basic_ospanstream&) = delete;
+
+  basic_ospanstream& operator=(basic_ospanstream&& __rhs) {
+    basic_ospanstream __tmp{std::move(__rhs)};
+    this->swap(__tmp);
+    return *this;
+  }
+
+  // [ospanstream.swap], swap
+
+  void swap(basic_ospanstream& __rhs) {
+    basic_ostream<_CharT, _Traits>::swap(__rhs);
+    __sb_.swap(__rhs.__sb_);
+  }
+
+  // [ospanstream.members], member functions
+
+  basic_spanbuf<_CharT, _Traits>* rdbuf() const noexcept { return __sb_; }
+
+  std::span<_CharT> span() const noexcept { return __sb_.rdbuf()->span(); }
+
+  void span(std::span<_CharT> __s) noexcept { rdbuf()->span(__s); }
+
+private:
+  basic_spanbuf<_CharT, _Traits> __sb_; // exposition only
+};
+
+template <class _CharT, class _Traits>
+void swap(basic_ospanstream<_CharT, _Traits>& __x, basic_ospanstream<_CharT, _Traits>& __y) {
+  __x.swap(__y);
+}
+
+using ospanstream = basic_ospanstream<char>;
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+using wospanstream = basic_ospanstream<wchar_t>;
+#  endif
+
+template <class _CharT, class _Traits = char_traits<_CharT>>
+class basic_spanstream : public basic_iostream<_CharT, _Traits> {
+public:
+  using char_type   = _CharT;
+  using int_type    = typename _Traits::int_type;
+  using pos_type    = typename _Traits::pos_type;
+  using off_type    = typename _Traits::off_type;
+  using traits_type = _Traits;
+
+  // [spanstream.cons], constructors
+
+  explicit basic_spanstream(std::span<_CharT> __s, ios_base::openmode __which = ios_base::out | ios_base::in)
+      : basic_iostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(basic_spanbuf<_CharT, _Traits>(__s, __which)) {}
+
+  basic_spanstream(const basic_spanstream&) = delete;
+
+  basic_spanstream(basic_spanstream&& __rhs) : basic_spanstream(std::move(__rhs)), __sb_(__rhs.__sb_) {
+    basic_iostream<_CharT, _Traits>::set_rdbuf(std::addressof(__sb_));
+  }
+
+  basic_spanstream& operator=(const basic_spanstream&) = delete;
+
+  basic_spanstream& operator=(basic_spanstream&& __rhs) {
+    basic_spanstream __tmp{std::move(__rhs)};
+    this->swap(__tmp);
+    return *this;
+  }
+
+  // [spanstream.swap], swap
+
+  void swap(basic_spanstream& __rhs) {
+    basic_iostream<_CharT, _Traits>::swap(__rhs);
+    __sb_.swap(__rhs.__sb_);
+  }
+
+  // [spanstream.members], members
+
+  basic_spanbuf<_CharT, _Traits>* rdbuf() const noexcept { return __sb_; }
+
+  std::span<_CharT> span() const noexcept { return rdbuf()->span(); }
+
+  void span(std::span<_CharT> __s) noexcept { rdbuf()->span(__s); }
+
+private:
+  basic_spanbuf<_CharT, _Traits> __sb_; // exposition only
+};
+
+template <class _CharT, class _Traits>
+void swap(basic_spanstream<_CharT, _Traits>& __x, basic_spanstream<_CharT, _Traits>& __y) {
+  __x.swap(__y);
+}
+
+using spanstream = basic_spanstream<char>;
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+using wspanstream = basic_spanstream<wchar_t>;
+#  endif
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#if _LIBCPP_STD_VER <= 20 && !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES)
+#  include <type_traits>
+#endif
+
+#endif // _LIBCPP_SPANSTREAM
diff --git a/libcxx/include/version b/libcxx/include/version
index b18927a2bc38c2..08fe5e0e584edc 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -472,7 +472,7 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 # define __cpp_lib_ranges_to_container                  202202L
 // # define __cpp_lib_ranges_zip                           202110L
 // # define __cpp_lib_reference_from_temporary             202202L
-// # define __cpp_lib_spanstream                           202106L
+# define __cpp_lib_spanstream                           202106L
 // # define __cpp_lib_stacktrace                           202011L
 # define __cpp_lib_stdatomic_h                          202011L
 # define __cpp_lib_string_contains                      202011L
diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index b44dbab25c74b4..80e6b254905ec1 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -75,9 +75,6 @@ module;
 #  if __has_include(<rcu>)
 #    error "please update the header information for <rcu> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<rcu>)
-#  if __has_include(<spanstream>)
-#    error "please update the header information for <spanstream> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<spanstream>)
 #  if __has_include(<stacktrace>)
 #    error "please update the header information for <stacktrace> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<stacktrace>)
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index b8d89130aae989..d53d666c8de5c4 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -133,6 +133,9 @@ module;
 #include <source_location>
 #include <span>
 #if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#  include <spanstream>
+#endif
+#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
 #  include <sstream>
 #endif
 #include <stack>
@@ -197,9 +200,6 @@ module;
 #  if __has_include(<rcu>)
 #    error "please update the header information for <rcu> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<rcu>)
-#  if __has_include(<spanstream>)
-#    error "please update the header information for <spanstream> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<spanstream>)
 #  if __has_include(<stacktrace>)
 #    error "please update the header information for <stacktrace> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<stacktrace>)
diff --git a/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.general/lit.local.cfg b/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.general/lit.local.cfg
new file mode 100644
index 00000000000000..2cb10010c45072
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.general/lit.local.cfg
@@ -0,0 +1,3 @@
+# All non-trivial uses of iostreams require localization support
+if "no-localization" in config.available_features:
+    config.unsupported = True
diff --git a/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.general/nothing_to_do.pass.cpp b/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.general/nothing_to_do.pass.cpp
new file mode 100644
index 00000000000000..4a307014fa0f72
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.general/nothing_to_do.pass.cpp
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+// [ispanstream.general]
+
+int main(int, char**) { return 0; }
diff --git a/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp
new file mode 100644
index 00000000000000..773efffd61a39c
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+//   template<class charT, class traits = char_traits<charT>>
+//   class basic_ispanstream
+//     : public basic_istream<charT, traits> {
+//   public:
+//     using char_type   = charT;
+//     using int_type    = typename traits::int_type;
+//     using pos_type    = typename traits::pos_type;
+//     using off_type    = typename traits::off_type;
+//     using traits_type = traits;
+
+//   using ispanstream = basic_ispanstream<char>;
+//   using wispanstream = basic_ispanstream<wchar_t>;
+
+#include <spanstream>
+#include <string>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Types
+
+static_assert(std::is_base_of_v<std::basic_istream<char>, std::ispanstream>);
+static_assert(std::is_same_v<std::ispanstream::char_type, char>);
+static_assert(std::is_same_v<std::ispanstream::int_type, std::char_traits<char>::int_type>);
+static_assert(std::is_same_v<std::ispanstream::pos_type, std::char_traits<char>::pos_type>);
+static_assert(std::is_same_v<std::ispanstream::off_type, std::char_traits<char>::off_type>);
+static_assert(std::is_same_v<std::ispanstream::traits_type, std::char_traits<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(std::is_base_of_v<std::basic_istream<wchar_t>, std::wispanstream>);
+static_assert(std::is_same_v<std::wispanstream::char_type, wchar_t>);
+static_assert(std::is_same_v<std::wispanstream::int_type, std::char_traits<wchar_t>::int_type>);
+static_assert(std::is_same_v<std::wispanstream::pos_type, std::char_traits<wchar_t>::pos_type>);
+static_assert(std::is_same_v<std::wispanstream::off_type, std::char_traits<wchar_t>::off_type>);
+static_assert(std::is_same_v<std::wispanstream::traits_type, std::char_traits<wchar_t>>);
+#endif
+
+// Copy properties
+
+static_assert(!std::is_copy_constructible_v<std::ispanstream>);
+static_assert(!std::is_copy_assignable_v<std::ispanstream>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::is_copy_constructible_v<std::wispanstream>);
+static_assert(!std::is_copy_assignable_v<std::wispanstream>);
+#endif
+
+// Move properties
+
+static_assert(!std::is_copy_constructible_v<std::ispanstream>);
+static_assert(!std::is_copy_assignable_v<std::ispanstream>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::is_copy_constructible_v<std::wispanstream>);
+static_assert(!std::is_copy_assignable_v<std::wispanstream>);
+#endif
+
+// Move properties
+
+static_assert(std::is_move_constructible_v<std::ispanstream>);
+static_assert(std::is_move_assignable_v<std::ispanstream>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(std::is_move_constructible_v<std::wispanstream>);
+static_assert(std::is_move_assignable_v<std::wispanstream>);
+#endif
+
diff --git a/libcxx/test/std/input.output/span.streams/ospanstream/ospanstream.general/lit.local.cfg b/libcxx/test/std/input.output/span.streams/ospanstream/ospanstream.general/lit.local.cfg
new file mode 100644
index 00000000000000..2cb10010c45072
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/ospanstream/ospanstream.general/lit.local.cfg
@@ -0,0 +1,3 @@
+# All non-trivial uses of iostreams require localization support
+if "no-localization" in config.available_features:
+    config.unsupported = True
diff --git a/libcxx/test/std/input.output/span.streams/ospanstream/ospanstream.general/nothing_to_do.pass.cpp b/libcxx/test/std/input.output/span.streams/ospanstream/ospanstream.general/nothing_to_do.pass.cpp
new file mode 100644
index 00000000000000..6f9497040a8977
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/ospanstream/ospanstream.general/nothing_to_do.pass.cpp
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+// [ospanstream.general]
+
+int main(int, char**) { return 0; }
diff --git a/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp
new file mode 100644
index 00000000000000..ab0cac07168407
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+//   template<class charT, class traits = char_traits<charT>>
+//   class basic_ospanstream
+//     : public basic_ostream<charT, traits> {
+//   public:
+//     using char_type   = charT;
+//     using int_type    = typename traits::int_type;
+//     using pos_type    = typename traits::pos_type;
+//     using off_type    = typename traits::off_type;
+//     using traits_type = traits;
+
+//   using ospanstream = basic_ospanstream<char>;
+//   using wospanstream = basic_ospanstream<wchar_t>;
+
+#include <spanstream>
+#include <string>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Types
+
+static_assert(std::is_base_of_v<std::basic_ostream<char>, std::ospanstream>);
+static_assert(std::is_same_v<std::ospanstream::char_type, char>);
+static_assert(std::is_same_v<std::ospanstream::int_type, std::char_traits<char>::int_type>);
+static_assert(std::is_same_v<std::ospanstream::pos_type, std::char_traits<char>::pos_type>);
+static_assert(std::is_same_v<std::ospanstream::off_type, std::char_traits<char>::off_type>);
+static_assert(std::is_same_v<std::ospanstream::traits_type, std::char_traits<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(std::is_base_of_v<std::basic_ostream<wchar_t>, std::wospanstream>);
+static_assert(std::is_same_v<std::wospanstream::char_type, wchar_t>);
+static_assert(std::is_same_v<std::wospanstream::int_type, std::char_traits<wchar_t>::int_type>);
+static_assert(std::is_same_v<std::wospanstream::pos_type, std::char_traits<wchar_t>::pos_type>);
+static_assert(std::is_same_v<std::wospanstream::off_type, std::char_traits<wchar_t>::off_type>);
+static_assert(std::is_same_v<std::wospanstream::traits_type, std::char_traits<wchar_t>>);
+#endif
+
+// Copy properties
+
+static_assert(!std::is_copy_constructible_v<std::ospanstream>);
+static_assert(!std::is_copy_assignable_v<std::ospanstream>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::is_copy_constructible_v<std::wospanstream>);
+static_assert(!std::is_copy_assignable_v<std::wospanstream>);
+#endif
+
+// Move properties
+
+static_assert(!std::is_copy_constructible_v<std::ospanstream>);
+static_assert(!std::is_copy_assignable_v<std::ospanstream>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::is_copy_constructible_v<std::wospanstream>);
+static_assert(!std::is_copy_assignable_v<std::wospanstream>);
+#endif
+
+// Move properties
+
+static_assert(std::is_move_constructible_v<std::ospanstream>);
+static_assert(std::is_move_assignable_v<std::ospanstream>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(std::is_move_constructible_v<std::wospanstream>);
+static_assert(std::is_move_assignable_v<std::wospanstream>);
+#endif
+
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/default.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/default.pass.cpp
new file mode 100644
index 00000000000000..4675c0e5fc2990
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/default.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+//   template<class charT, class traits = char_traits<charT>>
+//   class basic_spanbuf
+//     : public basic_streambuf<charT, traits> {
+
+//     // [spanbuf.cons], constructors
+//     basic_spanbuf();
+
+#include <cassert>
+#include <spanstream>
+
+#include "constexpr_char_traits.h"
+#include "test_macros.h"
+
+template <typename CharT, typename Traits = std::char_traits<CharT>>
+void test() {
+  using SpanBuf = std::basic_spanbuf<CharT, Traits>;
+
+  static_assert(std::default_initializable<SpanBuf>);
+
+  SpanBuf spBuf;
+  assert(spBuf.span().empty());
+}
+
+int main(int, char**) {
+  test<char>();
+  test<char, constexpr_char_traits<char>>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+  test<wchar_t, constexpr_char_traits<wchar_t>>();
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.general/lit.local.cfg b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.general/lit.local.cfg
new file mode 100644
index 00000000000000..2cb10010c45072
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.general/lit.local.cfg
@@ -0,0 +1,3 @@
+# All non-trivial uses of iostreams require localization support
+if "no-localization" in config.available_features:
+    config.unsupported = True
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.general/nothing_to_do.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.general/nothing_to_do.pass.cpp
new file mode 100644
index 00000000000000..fc746f4af7c762
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.general/nothing_to_do.pass.cpp
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+// [spanbuf.general]
+
+int main(int, char**) { return 0; }
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp
new file mode 100644
index 00000000000000..4585147ea8d2e7
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+//   template<class charT, class traits = char_traits<charT>>
+//   class basic_spanbuf
+//     : public basic_streambuf<charT, traits> {
+//   public:
+//     using char_type   = charT;
+//     using int_type    = typename traits::int_type;
+//     using pos_type    = typename traits::pos_type;
+//     using off_type    = typename traits::off_type;
+//     using traits_type = traits;
+
+//   using spanbuf = basic_spanbufchar>;
+//   using wspanbuf = basic_spanbuf<wchar_t>;
+
+#include <spanstream>
+#include <string>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Types
+
+static_assert(std::is_base_of_v<std::basic_streambuf<char>, std::spanbuf>);
+static_assert(std::is_same_v<std::spanbuf::char_type, char>);
+static_assert(std::is_same_v<std::spanbuf::int_type, std::char_traits<char>::int_type>);
+static_assert(std::is_same_v<std::spanbuf::pos_type, std::char_traits<char>::pos_type>);
+static_assert(std::is_same_v<std::spanbuf::off_type, std::char_traits<char>::off_type>);
+static_assert(std::is_same_v<std::spanbuf::traits_type, std::char_traits<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(std::is_base_of_v<std::basic_streambuf<wchar_t>, std::wspanbuf>);
+static_assert(std::is_same_v<std::wspanbuf::char_type, wchar_t>);
+static_assert(std::is_same_v<std::wspanbuf::int_type, std::char_traits<wchar_t>::int_type>);
+static_assert(std::is_same_v<std::wspanbuf::pos_type, std::char_traits<wchar_t>::pos_type>);
+static_assert(std::is_same_v<std::wspanbuf::off_type, std::char_traits<wchar_t>::off_type>);
+static_assert(std::is_same_v<std::wspanbuf::traits_type, std::char_traits<wchar_t>>);
+#endif
+
+// Copy properties
+
+static_assert(!std::is_copy_constructible_v<std::spanbuf>);
+static_assert(!std::is_copy_assignable_v<std::spanbuf>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::is_copy_constructible_v<std::wspanbuf>);
+static_assert(!std::is_copy_assignable_v<std::wspanbuf>);
+#endif
+
+// Move properties
+
+static_assert(!std::is_copy_constructible_v<std::spanbuf>);
+static_assert(!std::is_copy_assignable_v<std::spanbuf>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::is_copy_constructible_v<std::wspanbuf>);
+static_assert(!std::is_copy_assignable_v<std::wspanbuf>);
+#endif
+
+// Move properties
+
+static_assert(std::is_move_constructible_v<std::spanbuf>);
+static_assert(std::is_move_assignable_v<std::spanbuf>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(std::is_move_constructible_v<std::wspanbuf>);
+static_assert(std::is_move_assignable_v<std::wspanbuf>);
+#endif
+
diff --git a/libcxx/test/std/input.output/span.streams/spanstream/spanstream.general/lit.local.cfg b/libcxx/test/std/input.output/span.streams/spanstream/spanstream.general/lit.local.cfg
new file mode 100644
index 00000000000000..2cb10010c45072
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/spanstream/spanstream.general/lit.local.cfg
@@ -0,0 +1,3 @@
+# All non-trivial uses of iostreams require localization support
+if "no-localization" in config.available_features:
+    config.unsupported = True
diff --git a/libcxx/test/std/input.output/span.streams/spanstream/spanstream.general/nothing_to_do.pass.cpp b/libcxx/test/std/input.output/span.streams/spanstream/spanstream.general/nothing_to_do.pass.cpp
new file mode 100644
index 00000000000000..8677bb6d0906d3
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/spanstream/spanstream.general/nothing_to_do.pass.cpp
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+// [spanstream.general]
+
+int main(int, char**) { return 0; }
diff --git a/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp
new file mode 100644
index 00000000000000..4e90f259ccf488
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+//   template<class charT, class traits = char_traits<charT>>
+//   class basic_spanstream
+//     : public basic_iostream<charT, traits> {
+//   public:
+//     using char_type   = charT;
+//     using int_type    = typename traits::int_type;
+//     using pos_type    = typename traits::pos_type;
+//     using off_type    = typename traits::off_type;
+//     using traits_type = traits;
+
+//   using spanstream = basic_spanstream<char>;
+//   using wspanstream = basic_spanstream<wchar_t>;
+
+#include <spanstream>
+#include <string>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Types
+
+static_assert(std::is_base_of_v<std::basic_iostream<char>, std::spanstream>);
+static_assert(std::is_same_v<std::spanstream::char_type, char>);
+static_assert(std::is_same_v<std::spanstream::int_type, std::char_traits<char>::int_type>);
+static_assert(std::is_same_v<std::spanstream::pos_type, std::char_traits<char>::pos_type>);
+static_assert(std::is_same_v<std::spanstream::off_type, std::char_traits<char>::off_type>);
+static_assert(std::is_same_v<std::spanstream::traits_type, std::char_traits<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(std::is_base_of_v<std::basic_iostream<wchar_t>, std::wspanstream>);
+static_assert(std::is_same_v<std::wspanstream::char_type, wchar_t>);
+static_assert(std::is_same_v<std::wspanstream::int_type, std::char_traits<wchar_t>::int_type>);
+static_assert(std::is_same_v<std::wspanstream::pos_type, std::char_traits<wchar_t>::pos_type>);
+static_assert(std::is_same_v<std::wspanstream::off_type, std::char_traits<wchar_t>::off_type>);
+static_assert(std::is_same_v<std::wspanstream::traits_type, std::char_traits<wchar_t>>);
+#endif
+
+// Copy properties
+
+static_assert(!std::is_copy_constructible_v<std::spanstream>);
+static_assert(!std::is_copy_assignable_v<std::spanstream>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::is_copy_constructible_v<std::wspanstream>);
+static_assert(!std::is_copy_assignable_v<std::wspanstream>);
+#endif
+
+// Move properties
+
+static_assert(!std::is_copy_constructible_v<std::spanstream>);
+static_assert(!std::is_copy_assignable_v<std::spanstream>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::is_copy_constructible_v<std::wspanstream>);
+static_assert(!std::is_copy_assignable_v<std::wspanstream>);
+#endif
+
+// Move properties
+
+static_assert(std::is_move_constructible_v<std::spanstream>);
+static_assert(std::is_move_assignable_v<std::spanstream>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(std::is_move_constructible_v<std::wspanstream>);
+static_assert(std::is_move_assignable_v<std::wspanstream>);
+#endif
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/spanstream.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/spanstream.version.compile.pass.cpp
new file mode 100644
index 00000000000000..8719352d4367e3
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/spanstream.version.compile.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+//
+// clang-format off
+
+// <spanstream>
+
+// Test the feature test macros defined by <spanstream>
+
+/*  Constant                Value
+    __cpp_lib_spanstream    202106L [C++23]
+*/
+
+#include <spanstream>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+# ifdef __cpp_lib_spanstream
+#   error "__cpp_lib_spanstream should not be defined before c++23"
+# endif
+
+#elif TEST_STD_VER == 14
+
+# ifdef __cpp_lib_spanstream
+#   error "__cpp_lib_spanstream should not be defined before c++23"
+# endif
+
+#elif TEST_STD_VER == 17
+
+# ifdef __cpp_lib_spanstream
+#   error "__cpp_lib_spanstream should not be defined before c++23"
+# endif
+
+#elif TEST_STD_VER == 20
+
+# ifdef __cpp_lib_spanstream
+#   error "__cpp_lib_spanstream should not be defined before c++23"
+# endif
+
+#elif TEST_STD_VER == 23
+
+# ifndef __cpp_lib_spanstream
+#   error "__cpp_lib_spanstream should be defined in c++23"
+# endif
+# if __cpp_lib_spanstream != 202106L
+#   error "__cpp_lib_spanstream should have the value 202106L in c++23"
+# endif
+
+#elif TEST_STD_VER > 23
+
+# ifndef __cpp_lib_spanstream
+#   error "__cpp_lib_spanstream should be defined in c++26"
+# endif
+# if __cpp_lib_spanstream != 202106L
+#   error "__cpp_lib_spanstream should have the value 202106L in c++26"
+# endif
+
+#endif // TEST_STD_VER > 23
+
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 14271308624e65..097939b4551f37 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -5608,17 +5608,11 @@
 #   error "__cpp_lib_span_initializer_list should not be defined before c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_spanstream
-#     error "__cpp_lib_spanstream should be defined in c++23"
-#   endif
-#   if __cpp_lib_spanstream != 202106L
-#     error "__cpp_lib_spanstream should have the value 202106L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_spanstream
-#     error "__cpp_lib_spanstream should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_spanstream
+#   error "__cpp_lib_spanstream should be defined in c++23"
+# endif
+# if __cpp_lib_spanstream != 202106L
+#   error "__cpp_lib_spanstream should have the value 202106L in c++23"
 # endif
 
 # ifndef __cpp_lib_ssize
@@ -7355,17 +7349,11 @@
 #   error "__cpp_lib_span_initializer_list should have the value 202311L in c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_spanstream
-#     error "__cpp_lib_spanstream should be defined in c++26"
-#   endif
-#   if __cpp_lib_spanstream != 202106L
-#     error "__cpp_lib_spanstream should have the value 202106L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_spanstream
-#     error "__cpp_lib_spanstream should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_spanstream
+#   error "__cpp_lib_spanstream should be defined in c++26"
+# endif
+# if __cpp_lib_spanstream != 202106L
+#   error "__cpp_lib_spanstream should have the value 202106L in c++26"
 # endif
 
 # ifndef __cpp_lib_ssize
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index b688a30cdb792d..d46949b96069b2 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1101,7 +1101,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_spanstream",
             "values": {"c++23": 202106},
             "headers": ["spanstream"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_ssize",
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index b2aa3739ea697d..37e6b41176c020 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -26,6 +26,7 @@
     "locale": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
     "ostream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
     "regex": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
+    "spanstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
     "sstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
     "streambuf": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
     "strstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
@@ -76,6 +77,7 @@
     "regex": "// UNSUPPORTED: no-localization",
     "semaphore": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
     "shared_mutex": "// UNSUPPORTED: no-threads, c++03, c++11",
+    "spanstream": "// UNSUPPORTED: no-localization",
     "sstream": "// UNSUPPORTED: no-localization",
     "stdatomic.h": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17, c++20",
     "stop_token": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
@@ -145,7 +147,6 @@
     "hazard_pointer",
     "linalg",
     "rcu",
-    "spanstream",
     "stacktrace",
     "stdfloat",
     "text_encoding",

>From 65e3e35a4cff6152c386da7c45127361c434bbd0 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 28 Feb 2024 18:30:36 +0200
Subject: [PATCH 2/7] Added forward declarations to `<iosfwd>`

---
 libcxx/include/CMakeLists.txt      |  1 +
 libcxx/include/__fwd/spanstream.h  | 48 ++++++++++++++++++++++++++++++
 libcxx/include/iosfwd              | 20 +++++++++++++
 libcxx/include/libcxx.imp          |  1 +
 libcxx/include/module.modulemap.in | 15 +++++-----
 libcxx/include/spanstream          | 15 +++++-----
 6 files changed, 86 insertions(+), 14 deletions(-)
 create mode 100644 libcxx/include/__fwd/spanstream.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index ef5d523011e099..024d4e0a6f523d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -439,6 +439,7 @@ set(files
   __fwd/ostream.h
   __fwd/pair.h
   __fwd/span.h
+  __fwd/spanstream.h
   __fwd/sstream.h
   __fwd/streambuf.h
   __fwd/string.h
diff --git a/libcxx/include/__fwd/spanstream.h b/libcxx/include/__fwd/spanstream.h
new file mode 100644
index 00000000000000..2afeaaecf7fb4d
--- /dev/null
+++ b/libcxx/include/__fwd/spanstream.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FWD_SPANSTREAM_H
+#define _LIBCPP___FWD_SPANSTREAM_H
+
+#include <__config>
+#include <__fwd/string.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+template <class _CharT, class traits = char_traits<_CharT>>
+class _LIBCPP_TEMPLATE_VIS basic_spanbuf;
+template <class _CharT, class traits = char_traits<_CharT>>
+class _LIBCPP_TEMPLATE_VIS basic_ispanstream;
+template <class _CharT, class traits = char_traits<_CharT>>
+class _LIBCPP_TEMPLATE_VIS basic_ospanstream;
+template <class _CharT, class traits = char_traits<_CharT>>
+class _LIBCPP_TEMPLATE_VIS basic_spanstream;
+
+using spanbuf     = basic_spanbuf<char>;
+using ispanstream = basic_ispanstream<char>;
+using ospanstream = basic_ospanstream<char>;
+using spanstream  = basic_spanstream<char>;
+
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+using wspanbuf     = basic_spanbuf<wchar_t>;
+using wispanstream = basic_ispanstream<wchar_t>;
+using wospanstream = basic_ospanstream<wchar_t>;
+using wspanstream  = basic_spanstream<wchar_t>;
+#  endif
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FWD_SPANSTREAM_H
diff --git a/libcxx/include/iosfwd b/libcxx/include/iosfwd
index e28998d004156d..c760284f0dee88 100644
--- a/libcxx/include/iosfwd
+++ b/libcxx/include/iosfwd
@@ -42,6 +42,15 @@ template <class charT, class traits = char_traits<charT>, class Allocator = allo
 template <class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
     class basic_stringstream;
 
+  template<class charT, class traits = char_traits<charT>>
+    class basic_spanbuf;                                                             // Since C++23
+  template<class charT, class traits = char_traits<charT>>
+    class basic_ispanstream;                                                         // Since C++23
+  template<class charT, class traits = char_traits<charT>>
+    class basic_ospanstream;                                                         // Since C++23
+  template<class charT, class traits = char_traits<charT>>
+    class basic_spanstream;                                                          // Since C++23
+
 template <class charT, class traits = char_traits<charT> > class basic_filebuf;
 template <class charT, class traits = char_traits<charT> > class basic_ifstream;
 template <class charT, class traits = char_traits<charT> > class basic_ofstream;
@@ -63,6 +72,11 @@ typedef basic_istringstream<char>    istringstream;
 typedef basic_ostringstream<char>    ostringstream;
 typedef basic_stringstream<char>     stringstream;
 
+using spanbuf     = basic_spanbuf<char>;                                             // Since C++23
+using ispanstream = basic_ispanstream<char>;                                         // Since C++23
+using ospanstream = basic_ospanstream<char>;                                         // Since C++23
+using spanstream  = basic_spanstream<char>;                                          // Since C++23
+
 typedef basic_filebuf<char>          filebuf;
 typedef basic_ifstream<char>         ifstream;
 typedef basic_ofstream<char>         ofstream;
@@ -78,6 +92,11 @@ typedef basic_istringstream<wchar_t> wistringstream;
 typedef basic_ostringstream<wchar_t> wostringstream;
 typedef basic_stringstream<wchar_t>  wstringstream;
 
+using wspanbuf     = basic_spanbuf<wchar_t>;                                         // Since C++23
+using wispanstream = basic_ispanstream<wchar_t>;                                     // Since C++23
+using wospanstream = basic_ospanstream<wchar_t>;                                     // Since C++23
+using wspanstream  = basic_spanstream<wchar_t>;                                      // Since C++23
+
 typedef basic_filebuf<wchar_t>       wfilebuf;
 typedef basic_ifstream<wchar_t>      wifstream;
 typedef basic_ofstream<wchar_t>      wofstream;
@@ -112,6 +131,7 @@ using wosyncstream = basic_osyncstream<wchar_t>;  // C++20
 #include <__fwd/ios.h>
 #include <__fwd/istream.h>
 #include <__fwd/ostream.h>
+#include <__fwd/spanstream.h>
 #include <__fwd/sstream.h>
 #include <__fwd/streambuf.h>
 #include <__fwd/string.h>
diff --git a/libcxx/include/libcxx.imp b/libcxx/include/libcxx.imp
index 22fbea99b848bb..c25f0bbd215031 100644
--- a/libcxx/include/libcxx.imp
+++ b/libcxx/include/libcxx.imp
@@ -434,6 +434,7 @@
   { include: [ "<__fwd/ostream.h>", "private", "<ostream>", "public" ] },
   { include: [ "<__fwd/pair.h>", "private", "<utility>", "public" ] },
   { include: [ "<__fwd/span.h>", "private", "<span>", "public" ] },
+  { include: [ "<__fwd/spanstream.h>", "private", "<spanstream>", "public" ] },
   { include: [ "<__fwd/sstream.h>", "private", "<sstream>", "public" ] },
   { include: [ "<__fwd/streambuf.h>", "private", "<streambuf>", "public" ] },
   { include: [ "<__fwd/string.h>", "private", "<string>", "public" ] },
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b70c99f898fe75..00d877960a4cc4 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1392,14 +1392,15 @@ module std_private_functional_unary_function             [system] { header "__fu
 module std_private_functional_unary_negate               [system] { header "__functional/unary_negate.h" }
 module std_private_functional_weak_result_type           [system] { header "__functional/weak_result_type.h" }
 
-module std_private_ios_fpos [system] { header "__ios/fpos.h" }
+module std_private_ios_fpos                         [system] { header "__ios/fpos.h" }
 
-module std_private_iosfwd_fstream_fwd   [system] { header "__fwd/fstream.h" }
-module std_private_iosfwd_ios_fwd       [system] { header "__fwd/ios.h" }
-module std_private_iosfwd_istream_fwd   [system] { header "__fwd/istream.h" }
-module std_private_iosfwd_ostream_fwd   [system] { header "__fwd/ostream.h" }
-module std_private_iosfwd_sstream_fwd   [system] { header "__fwd/sstream.h" }
-module std_private_iosfwd_streambuf_fwd [system] { header "__fwd/streambuf.h" }
+module std_private_iosfwd_fstream_fwd               [system] { header "__fwd/fstream.h" }
+module std_private_iosfwd_ios_fwd                   [system] { header "__fwd/ios.h" }
+module std_private_iosfwd_istream_fwd               [system] { header "__fwd/istream.h" }
+module std_private_iosfwd_ostream_fwd               [system] { header "__fwd/ostream.h" }
+module std_private_iosfwd_spanstream_fwd            [system] { header "__fwd/spanstream.h" }
+module std_private_iosfwd_sstream_fwd               [system] { header "__fwd/sstream.h" }
+module std_private_iosfwd_streambuf_fwd             [system] { header "__fwd/streambuf.h" }
 
 module std_private_iterator_access                  [system] { header "__iterator/access.h" }
 module std_private_iterator_advance                 [system] { header "__iterator/advance.h" }
diff --git a/libcxx/include/spanstream b/libcxx/include/spanstream
index f25a04c2cb5254..ffaba85ef252c0 100644
--- a/libcxx/include/spanstream
+++ b/libcxx/include/spanstream
@@ -57,6 +57,7 @@
 #include <__assert> // all public C++ headers provide the assertion handler
 #include <__availability>
 #include <__config>
+#include <__fwd/spanstream.h>
 #include <__memory/addressof.h>
 #include <__ranges/concepts.h>
 #include <__utility/cmp.h>
@@ -81,7 +82,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 // Class template basic_spanbuf [spanbuf]
 
-template <class _CharT, class _Traits = char_traits<_CharT>>
+template <class _CharT, class _Traits>
 class _LIBCPP_TEMPLATE_VIS basic_spanbuf : public basic_streambuf<_CharT, _Traits> {
 public:
   using char_type   = _CharT;
@@ -223,8 +224,8 @@ using wspanbuf = basic_spanbuf<wchar_t>;
 
 // Class template basic_ispanstream [ispanstream]
 
-template <class _CharT, class _Traits = char_traits<_CharT>>
-class basic_ispanstream : public basic_istream<_CharT, _Traits> {
+template <class _CharT, class _Traits>
+class _LIBCPP_TEMPLATE_VIS basic_ispanstream : public basic_istream<_CharT, _Traits> {
 public:
   using char_type   = _CharT;
   using int_type    = typename _Traits::int_type;
@@ -300,8 +301,8 @@ using wispanstream = basic_ispanstream<wchar_t>;
 
 // Class template basic_ospanstream [ospanstream]
 
-template <class _CharT, class _Traits = char_traits<_CharT>>
-class basic_ospanstream : public basic_ostream<_CharT, _Traits> {
+template <class _CharT, class _Traits>
+class _LIBCPP_TEMPLATE_VIS basic_ospanstream : public basic_ostream<_CharT, _Traits> {
 public:
   using char_type   = _CharT;
   using int_type    = typename _Traits::int_type;
@@ -358,8 +359,8 @@ using ospanstream = basic_ospanstream<char>;
 using wospanstream = basic_ospanstream<wchar_t>;
 #  endif
 
-template <class _CharT, class _Traits = char_traits<_CharT>>
-class basic_spanstream : public basic_iostream<_CharT, _Traits> {
+template <class _CharT, class _Traits>
+class _LIBCPP_TEMPLATE_VIS basic_spanstream : public basic_iostream<_CharT, _Traits> {
 public:
   using char_type   = _CharT;
   using int_type    = typename _Traits::int_type;

>From 8bce8c113c19c6aeac11485f1c4689831e07c20c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 28 Feb 2024 19:01:08 +0200
Subject: [PATCH 3/7] Tests: updated `types.compile.pass`

---
 .../ispanstream/types.compile.pass.cpp        | 67 +++++++++----------
 .../ospanstream/types.compile.pass.cpp        | 67 +++++++++----------
 .../spanbuf/types.compile.pass.cpp            | 67 +++++++++----------
 .../spanstream/types.compile.pass.cpp         | 66 +++++++++---------
 4 files changed, 132 insertions(+), 135 deletions(-)

diff --git a/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp
index 773efffd61a39c..9615d270116ee4 100644
--- a/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp
@@ -27,53 +27,52 @@
 #include <string>
 #include <type_traits>
 
+#include "constexpr_char_traits.h"
 #include "test_macros.h"
 
-// Types
+template <typename CharT, typename Traits = std::char_traits<CharT>>
+void test() {
+  using SpStream = std::basic_ispanstream<CharT, Traits>;
 
-static_assert(std::is_base_of_v<std::basic_istream<char>, std::ispanstream>);
-static_assert(std::is_same_v<std::ispanstream::char_type, char>);
-static_assert(std::is_same_v<std::ispanstream::int_type, std::char_traits<char>::int_type>);
-static_assert(std::is_same_v<std::ispanstream::pos_type, std::char_traits<char>::pos_type>);
-static_assert(std::is_same_v<std::ispanstream::off_type, std::char_traits<char>::off_type>);
-static_assert(std::is_same_v<std::ispanstream::traits_type, std::char_traits<char>>);
+  // Types
 
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_base_of_v<std::basic_istream<wchar_t>, std::wispanstream>);
-static_assert(std::is_same_v<std::wispanstream::char_type, wchar_t>);
-static_assert(std::is_same_v<std::wispanstream::int_type, std::char_traits<wchar_t>::int_type>);
-static_assert(std::is_same_v<std::wispanstream::pos_type, std::char_traits<wchar_t>::pos_type>);
-static_assert(std::is_same_v<std::wispanstream::off_type, std::char_traits<wchar_t>::off_type>);
-static_assert(std::is_same_v<std::wispanstream::traits_type, std::char_traits<wchar_t>>);
-#endif
+  static_assert(std::is_base_of_v<std::basic_istream<CharT, Traits>, SpStream>);
+  static_assert(std::is_same_v<typename SpStream::char_type, CharT>);
+  static_assert(std::is_same_v<typename SpStream::int_type, typename Traits::int_type>);
+  static_assert(std::is_same_v<typename SpStream::pos_type, typename Traits::pos_type>);
+  static_assert(std::is_same_v<typename SpStream::off_type, typename Traits::off_type>);
+  static_assert(std::is_same_v<typename SpStream::traits_type, Traits>);
 
-// Copy properties
+  // Copy properties
 
-static_assert(!std::is_copy_constructible_v<std::ispanstream>);
-static_assert(!std::is_copy_assignable_v<std::ispanstream>);
+  static_assert(!std::is_copy_constructible_v<SpStream>);
+  static_assert(!std::is_copy_assignable_v<SpStream>);
 
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(!std::is_copy_constructible_v<std::wispanstream>);
-static_assert(!std::is_copy_assignable_v<std::wispanstream>);
-#endif
+  // Move properties
 
-// Move properties
+  static_assert(!std::is_copy_constructible_v<SpStream>);
+  static_assert(!std::is_copy_assignable_v<SpStream>);
 
-static_assert(!std::is_copy_constructible_v<std::ispanstream>);
-static_assert(!std::is_copy_assignable_v<std::ispanstream>);
+  // Move properties
 
+  static_assert(std::is_move_constructible_v<SpStream>);
+  static_assert(std::is_move_assignable_v<SpStream>);
+}
+
+void test() {
+  test<char>();
+  test<char, constexpr_char_traits<char>>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(!std::is_copy_constructible_v<std::wispanstream>);
-static_assert(!std::is_copy_assignable_v<std::wispanstream>);
+  test<wchar_t>();
+  test<wchar_t, constexpr_char_traits<wchar_t>>();
 #endif
+}
 
-// Move properties
-
-static_assert(std::is_move_constructible_v<std::ispanstream>);
-static_assert(std::is_move_assignable_v<std::ispanstream>);
+// Aliases
 
+static_assert(std::is_base_of_v<std::basic_istream<char>, std::ispanstream>);
+static_assert(std::is_same_v<std::basic_ispanstream<char>, std::ispanstream>);
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_move_constructible_v<std::wispanstream>);
-static_assert(std::is_move_assignable_v<std::wispanstream>);
+static_assert(std::is_base_of_v<std::basic_istream<wchar_t>, std::wispanstream>);
+static_assert(std::is_same_v<std::basic_ispanstream<wchar_t>, std::wispanstream>);
 #endif
-
diff --git a/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp
index ab0cac07168407..e5a9e574eb39f7 100644
--- a/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp
@@ -27,53 +27,52 @@
 #include <string>
 #include <type_traits>
 
+#include "constexpr_char_traits.h"
 #include "test_macros.h"
 
-// Types
+template <typename CharT, typename Traits = std::char_traits<CharT>>
+void test() {
+  using SpStream = std::basic_ospanstream<CharT, Traits>;
 
-static_assert(std::is_base_of_v<std::basic_ostream<char>, std::ospanstream>);
-static_assert(std::is_same_v<std::ospanstream::char_type, char>);
-static_assert(std::is_same_v<std::ospanstream::int_type, std::char_traits<char>::int_type>);
-static_assert(std::is_same_v<std::ospanstream::pos_type, std::char_traits<char>::pos_type>);
-static_assert(std::is_same_v<std::ospanstream::off_type, std::char_traits<char>::off_type>);
-static_assert(std::is_same_v<std::ospanstream::traits_type, std::char_traits<char>>);
+  // Types
 
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_base_of_v<std::basic_ostream<wchar_t>, std::wospanstream>);
-static_assert(std::is_same_v<std::wospanstream::char_type, wchar_t>);
-static_assert(std::is_same_v<std::wospanstream::int_type, std::char_traits<wchar_t>::int_type>);
-static_assert(std::is_same_v<std::wospanstream::pos_type, std::char_traits<wchar_t>::pos_type>);
-static_assert(std::is_same_v<std::wospanstream::off_type, std::char_traits<wchar_t>::off_type>);
-static_assert(std::is_same_v<std::wospanstream::traits_type, std::char_traits<wchar_t>>);
-#endif
+  static_assert(std::is_base_of_v<std::basic_ostream<CharT, Traits>, SpStream>);
+  static_assert(std::is_same_v<typename SpStream::char_type, CharT>);
+  static_assert(std::is_same_v<typename SpStream::int_type, typename Traits::int_type>);
+  static_assert(std::is_same_v<typename SpStream::pos_type, typename Traits::pos_type>);
+  static_assert(std::is_same_v<typename SpStream::off_type, typename Traits::off_type>);
+  static_assert(std::is_same_v<typename SpStream::traits_type, Traits>);
 
-// Copy properties
+  // Copy properties
 
-static_assert(!std::is_copy_constructible_v<std::ospanstream>);
-static_assert(!std::is_copy_assignable_v<std::ospanstream>);
+  static_assert(!std::is_copy_constructible_v<SpStream>);
+  static_assert(!std::is_copy_assignable_v<SpStream>);
 
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(!std::is_copy_constructible_v<std::wospanstream>);
-static_assert(!std::is_copy_assignable_v<std::wospanstream>);
-#endif
+  // Move properties
 
-// Move properties
+  static_assert(!std::is_copy_constructible_v<SpStream>);
+  static_assert(!std::is_copy_assignable_v<SpStream>);
 
-static_assert(!std::is_copy_constructible_v<std::ospanstream>);
-static_assert(!std::is_copy_assignable_v<std::ospanstream>);
+  // Move properties
 
+  static_assert(std::is_move_constructible_v<SpStream>);
+  static_assert(std::is_move_assignable_v<SpStream>);
+}
+
+void test() {
+  test<char>();
+  test<char, constexpr_char_traits<char>>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(!std::is_copy_constructible_v<std::wospanstream>);
-static_assert(!std::is_copy_assignable_v<std::wospanstream>);
+  test<wchar_t>();
+  test<wchar_t, constexpr_char_traits<wchar_t>>();
 #endif
+}
 
-// Move properties
-
-static_assert(std::is_move_constructible_v<std::ospanstream>);
-static_assert(std::is_move_assignable_v<std::ospanstream>);
+// Aliases
 
+static_assert(std::is_base_of_v<std::basic_ostream<char>, std::ospanstream>);
+static_assert(std::is_same_v<std::basic_ospanstream<char>, std::ospanstream>);
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_move_constructible_v<std::wospanstream>);
-static_assert(std::is_move_assignable_v<std::wospanstream>);
+static_assert(std::is_base_of_v<std::basic_ostream<wchar_t>, std::wospanstream>);
+static_assert(std::is_same_v<std::basic_ospanstream<wchar_t>, std::wospanstream>);
 #endif
-
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp
index 4585147ea8d2e7..a8cc07ed524ea8 100644
--- a/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp
@@ -27,53 +27,52 @@
 #include <string>
 #include <type_traits>
 
+#include "constexpr_char_traits.h"
 #include "test_macros.h"
 
-// Types
+template <typename CharT, typename Traits = std::char_traits<CharT>>
+void test() {
+  using SpBuf = std::basic_spanbuf<CharT, Traits>;
 
-static_assert(std::is_base_of_v<std::basic_streambuf<char>, std::spanbuf>);
-static_assert(std::is_same_v<std::spanbuf::char_type, char>);
-static_assert(std::is_same_v<std::spanbuf::int_type, std::char_traits<char>::int_type>);
-static_assert(std::is_same_v<std::spanbuf::pos_type, std::char_traits<char>::pos_type>);
-static_assert(std::is_same_v<std::spanbuf::off_type, std::char_traits<char>::off_type>);
-static_assert(std::is_same_v<std::spanbuf::traits_type, std::char_traits<char>>);
+  // Types
 
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_base_of_v<std::basic_streambuf<wchar_t>, std::wspanbuf>);
-static_assert(std::is_same_v<std::wspanbuf::char_type, wchar_t>);
-static_assert(std::is_same_v<std::wspanbuf::int_type, std::char_traits<wchar_t>::int_type>);
-static_assert(std::is_same_v<std::wspanbuf::pos_type, std::char_traits<wchar_t>::pos_type>);
-static_assert(std::is_same_v<std::wspanbuf::off_type, std::char_traits<wchar_t>::off_type>);
-static_assert(std::is_same_v<std::wspanbuf::traits_type, std::char_traits<wchar_t>>);
-#endif
+  static_assert(std::is_base_of_v<std::basic_streambuf<CharT, Traits>, SpBuf>);
+  static_assert(std::is_same_v<typename SpBuf::char_type, CharT>);
+  static_assert(std::is_same_v<typename SpBuf::int_type, typename Traits::int_type>);
+  static_assert(std::is_same_v<typename SpBuf::pos_type, typename Traits::pos_type>);
+  static_assert(std::is_same_v<typename SpBuf::off_type, typename Traits::off_type>);
+  static_assert(std::is_same_v<typename SpBuf::traits_type, Traits>);
 
-// Copy properties
+  // Copy properties
 
-static_assert(!std::is_copy_constructible_v<std::spanbuf>);
-static_assert(!std::is_copy_assignable_v<std::spanbuf>);
+  static_assert(!std::is_copy_constructible_v<SpBuf>);
+  static_assert(!std::is_copy_assignable_v<SpBuf>);
 
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(!std::is_copy_constructible_v<std::wspanbuf>);
-static_assert(!std::is_copy_assignable_v<std::wspanbuf>);
-#endif
+  // Move properties
 
-// Move properties
+  static_assert(!std::is_copy_constructible_v<SpBuf>);
+  static_assert(!std::is_copy_assignable_v<SpBuf>);
 
-static_assert(!std::is_copy_constructible_v<std::spanbuf>);
-static_assert(!std::is_copy_assignable_v<std::spanbuf>);
+  // Move properties
 
+  static_assert(std::is_move_constructible_v<SpBuf>);
+  static_assert(std::is_move_assignable_v<SpBuf>);
+}
+
+void test() {
+  test<char>();
+  test<char, constexpr_char_traits<char>>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(!std::is_copy_constructible_v<std::wspanbuf>);
-static_assert(!std::is_copy_assignable_v<std::wspanbuf>);
+  test<wchar_t>();
+  test<wchar_t, constexpr_char_traits<wchar_t>>();
 #endif
+}
 
-// Move properties
-
-static_assert(std::is_move_constructible_v<std::spanbuf>);
-static_assert(std::is_move_assignable_v<std::spanbuf>);
+// Aliases
 
+static_assert(std::is_base_of_v<std::basic_streambuf<char>, std::spanbuf>);
+static_assert(std::is_same_v<std::basic_spanbuf<char>, std::spanbuf>);
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_move_constructible_v<std::wspanbuf>);
-static_assert(std::is_move_assignable_v<std::wspanbuf>);
+static_assert(std::is_base_of_v<std::basic_streambuf<wchar_t>, std::wspanbuf>);
+static_assert(std::is_same_v<std::basic_spanbuf<wchar_t>, std::wspanbuf>);
 #endif
-
diff --git a/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp
index 4e90f259ccf488..5714834be2ce11 100644
--- a/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp
@@ -27,52 +27,52 @@
 #include <string>
 #include <type_traits>
 
+#include "constexpr_char_traits.h"
 #include "test_macros.h"
 
-// Types
+template <typename CharT, typename Traits = std::char_traits<CharT>>
+void test() {
+  using SpStream = std::basic_spanstream<CharT, Traits>;
 
-static_assert(std::is_base_of_v<std::basic_iostream<char>, std::spanstream>);
-static_assert(std::is_same_v<std::spanstream::char_type, char>);
-static_assert(std::is_same_v<std::spanstream::int_type, std::char_traits<char>::int_type>);
-static_assert(std::is_same_v<std::spanstream::pos_type, std::char_traits<char>::pos_type>);
-static_assert(std::is_same_v<std::spanstream::off_type, std::char_traits<char>::off_type>);
-static_assert(std::is_same_v<std::spanstream::traits_type, std::char_traits<char>>);
+  // Types
 
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_base_of_v<std::basic_iostream<wchar_t>, std::wspanstream>);
-static_assert(std::is_same_v<std::wspanstream::char_type, wchar_t>);
-static_assert(std::is_same_v<std::wspanstream::int_type, std::char_traits<wchar_t>::int_type>);
-static_assert(std::is_same_v<std::wspanstream::pos_type, std::char_traits<wchar_t>::pos_type>);
-static_assert(std::is_same_v<std::wspanstream::off_type, std::char_traits<wchar_t>::off_type>);
-static_assert(std::is_same_v<std::wspanstream::traits_type, std::char_traits<wchar_t>>);
-#endif
+  static_assert(std::is_base_of_v<std::basic_iostream<CharT, Traits>, SpStream>);
+  static_assert(std::is_same_v<typename SpStream::char_type, CharT>);
+  static_assert(std::is_same_v<typename SpStream::int_type, typename Traits::int_type>);
+  static_assert(std::is_same_v<typename SpStream::pos_type, typename Traits::pos_type>);
+  static_assert(std::is_same_v<typename SpStream::off_type, typename Traits::off_type>);
+  static_assert(std::is_same_v<typename SpStream::traits_type, Traits>);
 
-// Copy properties
+  // Copy properties
 
-static_assert(!std::is_copy_constructible_v<std::spanstream>);
-static_assert(!std::is_copy_assignable_v<std::spanstream>);
+  static_assert(!std::is_copy_constructible_v<SpStream>);
+  static_assert(!std::is_copy_assignable_v<SpStream>);
 
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(!std::is_copy_constructible_v<std::wspanstream>);
-static_assert(!std::is_copy_assignable_v<std::wspanstream>);
-#endif
+  // Move properties
+
+  static_assert(!std::is_copy_constructible_v<SpStream>);
+  static_assert(!std::is_copy_assignable_v<SpStream>);
 
-// Move properties
+  // Move properties
 
-static_assert(!std::is_copy_constructible_v<std::spanstream>);
-static_assert(!std::is_copy_assignable_v<std::spanstream>);
+  static_assert(std::is_move_constructible_v<SpStream>);
+  static_assert(std::is_move_assignable_v<SpStream>);
+}
 
+void test() {
+  test<char>();
+  test<char, constexpr_char_traits<char>>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(!std::is_copy_constructible_v<std::wspanstream>);
-static_assert(!std::is_copy_assignable_v<std::wspanstream>);
+  test<wchar_t>();
+  test<wchar_t, constexpr_char_traits<wchar_t>>();
 #endif
+}
 
-// Move properties
-
-static_assert(std::is_move_constructible_v<std::spanstream>);
-static_assert(std::is_move_assignable_v<std::spanstream>);
+// Aliases
 
+static_assert(std::is_base_of_v<std::basic_iostream<char>, std::spanstream>);
+static_assert(std::is_same_v<std::basic_spanstream<char>, std::spanstream>);
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_move_constructible_v<std::wspanstream>);
-static_assert(std::is_move_assignable_v<std::wspanstream>);
+static_assert(std::is_base_of_v<std::basic_iostream<wchar_t>, std::wspanstream>);
+static_assert(std::is_same_v<std::basic_spanstream<wchar_t>, std::wspanstream>);
 #endif

>From fbf8fcdc8554d3d6c5af1b0ee658028fe5e27904 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 28 Feb 2024 19:38:58 +0200
Subject: [PATCH 4/7] Implementation: added `_LIBCPP_HIDE_FROM_ABI`

---
 libcxx/include/__fwd/spanstream.h |  8 ++--
 libcxx/include/spanstream         | 74 +++++++++++++++++--------------
 2 files changed, 44 insertions(+), 38 deletions(-)

diff --git a/libcxx/include/__fwd/spanstream.h b/libcxx/include/__fwd/spanstream.h
index 2afeaaecf7fb4d..f5ff2ad2c9cfc6 100644
--- a/libcxx/include/__fwd/spanstream.h
+++ b/libcxx/include/__fwd/spanstream.h
@@ -20,13 +20,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 23
 
-template <class _CharT, class traits = char_traits<_CharT>>
+template <class _CharT, class _Traits = char_traits<_CharT>>
 class _LIBCPP_TEMPLATE_VIS basic_spanbuf;
-template <class _CharT, class traits = char_traits<_CharT>>
+template <class _CharT, class _Traits = char_traits<_CharT>>
 class _LIBCPP_TEMPLATE_VIS basic_ispanstream;
-template <class _CharT, class traits = char_traits<_CharT>>
+template <class _CharT, class _Traits = char_traits<_CharT>>
 class _LIBCPP_TEMPLATE_VIS basic_ospanstream;
-template <class _CharT, class traits = char_traits<_CharT>>
+template <class _CharT, class _Traits = char_traits<_CharT>>
 class _LIBCPP_TEMPLATE_VIS basic_spanstream;
 
 using spanbuf     = basic_spanbuf<char>;
diff --git a/libcxx/include/spanstream b/libcxx/include/spanstream
index ffaba85ef252c0..16bbf04d81c559 100644
--- a/libcxx/include/spanstream
+++ b/libcxx/include/spanstream
@@ -105,6 +105,7 @@ public:
   }
 
   basic_spanbuf(const basic_spanbuf&) = delete;
+
   _LIBCPP_HIDE_FROM_ABI basic_spanbuf(basic_spanbuf&& __rhs)
       : basic_streambuf<_CharT, _Traits>{std::move(__rhs)},
         __mode_{std::move(__rhs.__mode_)},
@@ -113,6 +114,7 @@ public:
   // [spanbuf.assign], assignment and swap
 
   basic_spanbuf& operator=(const basic_spanbuf&) = delete;
+
   _LIBCPP_HIDE_FROM_ABI basic_spanbuf& operator=(basic_spanbuf&& __rhs) {
     basic_spanbuf __tmp{std::move(__rhs)};
     this->swap(__tmp);
@@ -213,13 +215,13 @@ private:
 };
 
 template <class _CharT, class _Traits>
-void swap(basic_spanbuf<_CharT, _Traits>& __x, basic_spanbuf<_CharT, _Traits>& __y) {
+_LIBCPP_HIDE_FROM_ABI void swap(basic_spanbuf<_CharT, _Traits>& __x, basic_spanbuf<_CharT, _Traits>& __y) {
   __x.swap(__y);
 }
 
-using spanbuf = basic_spanbuf<char>;
+using std::spanbuf;
 #  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
-using wspanbuf = basic_spanbuf<wchar_t>;
+using std::wspanbuf;
 #  endif
 
 // Class template basic_ispanstream [ispanstream]
@@ -235,27 +237,28 @@ public:
 
   // [ispanstream.cons], constructors
 
-  explicit basic_ispanstream(std::span<_CharT> __s, ios_base::openmode __which = ios_base::in)
+  _LIBCPP_HIDE_FROM_ABI explicit basic_ispanstream(std::span<_CharT> __s, ios_base::openmode __which = ios_base::in)
       : basic_istream<_CharT, _Traits>(std::addressof(__sb_)),
         __sb_(basic_spanbuf<_CharT, _Traits>(__s, __which | ios_base::in)) {}
 
   basic_ispanstream(const basic_ispanstream&) = delete;
 
-  basic_ispanstream(basic_ispanstream&& __rhs)
+  _LIBCPP_HIDE_FROM_ABI basic_ispanstream(basic_ispanstream&& __rhs)
       : basic_istream<_CharT, _Traits>(std::move(__rhs)), __sb_(std::move(__rhs.__sb_)) {
     basic_istream<_CharT, _Traits>::set_rdbuf(std::addressof(__sb_));
   }
 
   template <ranges::borrowed_range _ROSeq>
     requires(!convertible_to<_ROSeq, std::span<_CharT>>) && convertible_to<_ROSeq, std::span<const _CharT>>
-  explicit basic_ispanstream(_ROSeq&& __s) : basic_istream<_CharT, _Traits>(std::addressof(__sb_)) {
+  _LIBCPP_HIDE_FROM_ABI explicit basic_ispanstream(_ROSeq&& __s)
+      : basic_istream<_CharT, _Traits>(std::addressof(__sb_)) {
     std::span<const _CharT> __sp(std::forward<_ROSeq>(__s));
     this->span(std::span<_CharT>(std::span<_CharT>(const_cast<_CharT*>(__sp.data()), __sp.size())));
   }
 
   basic_ispanstream& operator=(const basic_ispanstream&) = delete;
 
-  basic_ispanstream& operator=(basic_ispanstream&& __rhs) {
+  _LIBCPP_HIDE_FROM_ABI basic_ispanstream& operator=(basic_ispanstream&& __rhs) {
     basic_ispanstream __tmp{std::move(__rhs)};
     this->swap(__tmp);
     return *this;
@@ -263,24 +266,24 @@ public:
 
   // [ispanstream.swap], swap
 
-  void swap(basic_ispanstream& __rhs) {
+  _LIBCPP_HIDE_FROM_ABI void swap(basic_ispanstream& __rhs) {
     basic_istream<_CharT, _Traits>::swap(__rhs);
     __sb_.swap(__rhs.__sb_);
   }
 
   // [ispanstream.members], member functions
 
-  basic_spanbuf<_CharT, _Traits>* rdbuf() const noexcept {
+  _LIBCPP_HIDE_FROM_ABI basic_spanbuf<_CharT, _Traits>* rdbuf() const noexcept {
     return const_cast<basic_spanbuf<_CharT, _Traits>*>(std::addressof(__sb_));
   }
 
-  std::span<const _CharT> span() const noexcept { return rdbuf()->span(); }
+  _LIBCPP_HIDE_FROM_ABI std::span<const _CharT> span() const noexcept { return rdbuf()->span(); }
 
-  void span(std::span<_CharT> __s) noexcept { rdbuf()->span(__s); }
+  _LIBCPP_HIDE_FROM_ABI void span(std::span<_CharT> __s) noexcept { rdbuf()->span(__s); }
 
   template <ranges::borrowed_range _ROSeq>
     requires(!convertible_to<_ROSeq, std::span<_CharT>>) && convertible_to<_ROSeq, std::span<const _CharT>>
-  void span(_ROSeq&& __s) noexcept {
+  _LIBCPP_HIDE_FROM_ABI void span(_ROSeq&& __s) noexcept {
     std::span<const _CharT> __sp(std::forward<_ROSeq>(__s));
     this->span(std::span<_CharT>(const_cast<_CharT*>(__sp.data()), __sp.size()));
   }
@@ -290,13 +293,13 @@ private:
 };
 
 template <class _CharT, class _Traits>
-void swap(basic_ispanstream<_CharT, _Traits>& __x, basic_ispanstream<_CharT, _Traits>& __y) {
+_LIBCPP_HIDE_FROM_ABI void swap(basic_ispanstream<_CharT, _Traits>& __x, basic_ispanstream<_CharT, _Traits>& __y) {
   __x.swap(__y);
 }
 
-using ispanstream = basic_ispanstream<char>;
+using std::ispanstream;
 #  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
-using wispanstream = basic_ispanstream<wchar_t>;
+using std::wispanstream;
 #  endif
 
 // Class template basic_ospanstream [ospanstream]
@@ -312,19 +315,20 @@ public:
 
   // [ospanstream.cons], constructors
 
-  explicit basic_ospanstream(std::span<_CharT> __s, ios_base::openmode __which = ios_base::out)
+  _LIBCPP_HIDE_FROM_ABI explicit basic_ospanstream(std::span<_CharT> __s, ios_base::openmode __which = ios_base::out)
       : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)),
         __sb_(basic_spanbuf<_CharT, _Traits>(__s, __which | ios_base::out)) {}
 
   basic_ospanstream(const basic_ospanstream&) = delete;
 
-  basic_ospanstream(basic_ospanstream&& __rhs) : basic_ospanstream(std::move(__rhs)), __sb_(std::move(__rhs.__sb_)) {
+  _LIBCPP_HIDE_FROM_ABI basic_ospanstream(basic_ospanstream&& __rhs)
+      : basic_ospanstream(std::move(__rhs)), __sb_(std::move(__rhs.__sb_)) {
     basic_ostream<_CharT, _Traits>::set_rdbuf(std::addressof(__sb_));
   }
 
   basic_ospanstream& operator=(const basic_ospanstream&) = delete;
 
-  basic_ospanstream& operator=(basic_ospanstream&& __rhs) {
+  _LIBCPP_HIDE_FROM_ABI basic_ospanstream& operator=(basic_ospanstream&& __rhs) {
     basic_ospanstream __tmp{std::move(__rhs)};
     this->swap(__tmp);
     return *this;
@@ -332,31 +336,31 @@ public:
 
   // [ospanstream.swap], swap
 
-  void swap(basic_ospanstream& __rhs) {
+  _LIBCPP_HIDE_FROM_ABI void swap(basic_ospanstream& __rhs) {
     basic_ostream<_CharT, _Traits>::swap(__rhs);
     __sb_.swap(__rhs.__sb_);
   }
 
   // [ospanstream.members], member functions
 
-  basic_spanbuf<_CharT, _Traits>* rdbuf() const noexcept { return __sb_; }
+  _LIBCPP_HIDE_FROM_ABI basic_spanbuf<_CharT, _Traits>* rdbuf() const noexcept { return __sb_; }
 
-  std::span<_CharT> span() const noexcept { return __sb_.rdbuf()->span(); }
+  _LIBCPP_HIDE_FROM_ABI std::span<_CharT> span() const noexcept { return __sb_.rdbuf()->span(); }
 
-  void span(std::span<_CharT> __s) noexcept { rdbuf()->span(__s); }
+  _LIBCPP_HIDE_FROM_ABI void span(std::span<_CharT> __s) noexcept { rdbuf()->span(__s); }
 
 private:
   basic_spanbuf<_CharT, _Traits> __sb_; // exposition only
 };
 
 template <class _CharT, class _Traits>
-void swap(basic_ospanstream<_CharT, _Traits>& __x, basic_ospanstream<_CharT, _Traits>& __y) {
+_LIBCPP_HIDE_FROM_ABI void swap(basic_ospanstream<_CharT, _Traits>& __x, basic_ospanstream<_CharT, _Traits>& __y) {
   __x.swap(__y);
 }
 
-using ospanstream = basic_ospanstream<char>;
+using std::ospanstream;
 #  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
-using wospanstream = basic_ospanstream<wchar_t>;
+using std::wospanstream;
 #  endif
 
 template <class _CharT, class _Traits>
@@ -370,18 +374,20 @@ public:
 
   // [spanstream.cons], constructors
 
-  explicit basic_spanstream(std::span<_CharT> __s, ios_base::openmode __which = ios_base::out | ios_base::in)
+  _LIBCPP_HIDE_FROM_ABI explicit basic_spanstream(std::span<_CharT> __s,
+                                                  ios_base::openmode __which = ios_base::out | ios_base::in)
       : basic_iostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(basic_spanbuf<_CharT, _Traits>(__s, __which)) {}
 
   basic_spanstream(const basic_spanstream&) = delete;
 
-  basic_spanstream(basic_spanstream&& __rhs) : basic_spanstream(std::move(__rhs)), __sb_(__rhs.__sb_) {
+  _LIBCPP_HIDE_FROM_ABI basic_spanstream(basic_spanstream&& __rhs)
+      : basic_spanstream(std::move(__rhs)), __sb_(__rhs.__sb_) {
     basic_iostream<_CharT, _Traits>::set_rdbuf(std::addressof(__sb_));
   }
 
   basic_spanstream& operator=(const basic_spanstream&) = delete;
 
-  basic_spanstream& operator=(basic_spanstream&& __rhs) {
+  _LIBCPP_HIDE_FROM_ABI basic_spanstream& operator=(basic_spanstream&& __rhs) {
     basic_spanstream __tmp{std::move(__rhs)};
     this->swap(__tmp);
     return *this;
@@ -396,24 +402,24 @@ public:
 
   // [spanstream.members], members
 
-  basic_spanbuf<_CharT, _Traits>* rdbuf() const noexcept { return __sb_; }
+  _LIBCPP_HIDE_FROM_ABI basic_spanbuf<_CharT, _Traits>* rdbuf() const noexcept { return __sb_; }
 
-  std::span<_CharT> span() const noexcept { return rdbuf()->span(); }
+  _LIBCPP_HIDE_FROM_ABI std::span<_CharT> span() const noexcept { return rdbuf()->span(); }
 
-  void span(std::span<_CharT> __s) noexcept { rdbuf()->span(__s); }
+  _LIBCPP_HIDE_FROM_ABI void span(std::span<_CharT> __s) noexcept { rdbuf()->span(__s); }
 
 private:
   basic_spanbuf<_CharT, _Traits> __sb_; // exposition only
 };
 
 template <class _CharT, class _Traits>
-void swap(basic_spanstream<_CharT, _Traits>& __x, basic_spanstream<_CharT, _Traits>& __y) {
+_LIBCPP_HIDE_FROM_ABI void swap(basic_spanstream<_CharT, _Traits>& __x, basic_spanstream<_CharT, _Traits>& __y) {
   __x.swap(__y);
 }
 
-using spanstream = basic_spanstream<char>;
+using std::spanstream;
 #  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
-using wspanstream = basic_spanstream<wchar_t>;
+using std::wspanstream;
 #  endif
 
 #endif // _LIBCPP_STD_VER >= 23

>From 1c64c2833e7b93aef2d9d2137e4efea10fe4b083 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 29 Feb 2024 11:05:55 +0200
Subject: [PATCH 5/7] Tests: `spanbuf` constructor tests

---
 .../ispanstream/types.compile.pass.cpp        |  14 +-
 .../std/input.output/span.streams/macros.h    |  18 ++
 .../ospanstream/types.compile.pass.cpp        |  14 +-
 .../spanbuf/spanbuf.cons/default.pass.cpp     |  25 +-
 .../spanbuf/spanbuf.cons/mode.pass.cpp        |  96 ++++++++
 .../spanbuf/spanbuf.cons/move.pass.cpp        | 224 ++++++++++++++++++
 .../spanbuf/spanbuf.cons/span.mode.pass.cpp   | 155 ++++++++++++
 .../spanbuf/types.compile.pass.cpp            |  14 +-
 .../spanstream/types.compile.pass.cpp         |  14 +-
 .../std/input.output/span.streams/types.h     |  73 ++++++
 10 files changed, 615 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/test/std/input.output/span.streams/macros.h
 create mode 100644 libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/mode.pass.cpp
 create mode 100644 libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/move.pass.cpp
 create mode 100644 libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/span.mode.pass.cpp
 create mode 100644 libcxx/test/std/input.output/span.streams/types.h

diff --git a/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp
index 9615d270116ee4..c566a3fce647dc 100644
--- a/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp
@@ -30,18 +30,18 @@
 #include "constexpr_char_traits.h"
 #include "test_macros.h"
 
-template <typename CharT, typename Traits = std::char_traits<CharT>>
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
 void test() {
-  using SpStream = std::basic_ispanstream<CharT, Traits>;
+  using SpStream = std::basic_ispanstream<CharT, TraitsT>;
 
   // Types
 
-  static_assert(std::is_base_of_v<std::basic_istream<CharT, Traits>, SpStream>);
+  static_assert(std::is_base_of_v<std::basic_istream<CharT, TraitsT>, SpStream>);
   static_assert(std::is_same_v<typename SpStream::char_type, CharT>);
-  static_assert(std::is_same_v<typename SpStream::int_type, typename Traits::int_type>);
-  static_assert(std::is_same_v<typename SpStream::pos_type, typename Traits::pos_type>);
-  static_assert(std::is_same_v<typename SpStream::off_type, typename Traits::off_type>);
-  static_assert(std::is_same_v<typename SpStream::traits_type, Traits>);
+  static_assert(std::is_same_v<typename SpStream::int_type, typename TraitsT::int_type>);
+  static_assert(std::is_same_v<typename SpStream::pos_type, typename TraitsT::pos_type>);
+  static_assert(std::is_same_v<typename SpStream::off_type, typename TraitsT::off_type>);
+  static_assert(std::is_same_v<typename SpStream::traits_type, TraitsT>);
 
   // Copy properties
 
diff --git a/libcxx/test/std/input.output/span.streams/macros.h b/libcxx/test/std/input.output/span.streams/macros.h
new file mode 100644
index 00000000000000..679686eaa41a82
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/macros.h
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_INPUTOUTPUT_SPANSTREAMS_MACROS_H
+#define TEST_STD_INPUTOUTPUT_SPANSTREAMS_MACROS_H
+
+#include "make_string.h"
+
+#define CS(S) MAKE_CSTRING(CharT, S)
+#define ST(S, a) std::basic_string<CharT, TraitsT, AllocT>(MAKE_CSTRING(CharT, S), MKSTR_LEN(CharT, S), a)
+#define SV(S) std::basic_string_view<CharT, TraitsT>(MAKE_CSTRING(CharT, S), MKSTR_LEN(CharT, S))
+
+#endif // TEST_STD_INPUTOUTPUT_SPANSTREAMS_MACROS_H
diff --git a/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp
index e5a9e574eb39f7..595770aa5bd6b0 100644
--- a/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp
@@ -30,18 +30,18 @@
 #include "constexpr_char_traits.h"
 #include "test_macros.h"
 
-template <typename CharT, typename Traits = std::char_traits<CharT>>
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
 void test() {
-  using SpStream = std::basic_ospanstream<CharT, Traits>;
+  using SpStream = std::basic_ospanstream<CharT, TraitsT>;
 
   // Types
 
-  static_assert(std::is_base_of_v<std::basic_ostream<CharT, Traits>, SpStream>);
+  static_assert(std::is_base_of_v<std::basic_ostream<CharT, TraitsT>, SpStream>);
   static_assert(std::is_same_v<typename SpStream::char_type, CharT>);
-  static_assert(std::is_same_v<typename SpStream::int_type, typename Traits::int_type>);
-  static_assert(std::is_same_v<typename SpStream::pos_type, typename Traits::pos_type>);
-  static_assert(std::is_same_v<typename SpStream::off_type, typename Traits::off_type>);
-  static_assert(std::is_same_v<typename SpStream::traits_type, Traits>);
+  static_assert(std::is_same_v<typename SpStream::int_type, typename TraitsT::int_type>);
+  static_assert(std::is_same_v<typename SpStream::pos_type, typename TraitsT::pos_type>);
+  static_assert(std::is_same_v<typename SpStream::off_type, typename TraitsT::off_type>);
+  static_assert(std::is_same_v<typename SpStream::traits_type, TraitsT>);
 
   // Copy properties
 
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/default.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/default.pass.cpp
index 4675c0e5fc2990..4c6b1ae3080d9e 100644
--- a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/default.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/default.pass.cpp
@@ -21,19 +21,36 @@
 #include <spanstream>
 
 #include "constexpr_char_traits.h"
+#include "nasty_string.h"
 #include "test_macros.h"
 
-template <typename CharT, typename Traits = std::char_traits<CharT>>
-void test() {
-  using SpanBuf = std::basic_spanbuf<CharT, Traits>;
+void test_sfinae_with_nasty_char() {
+  using SpBuf = std::basic_spanbuf<nasty_char, nasty_char_traits>;
+
+  static_assert(std::default_initializable<SpBuf>);
+}
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
+void test_sfinae() {
+  using SpBuf = std::basic_spanbuf<CharT, TraitsT>;
 
-  static_assert(std::default_initializable<SpanBuf>);
+  static_assert(std::default_initializable<SpBuf>);
+}
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
+void test() {
+  using SpanBuf = std::basic_spanbuf<CharT, TraitsT>;
 
   SpanBuf spBuf;
+  assert(spBuf.span().data() == nullptr);
   assert(spBuf.span().empty());
+  assert(spBuf.span().size() == 0);
 }
 
 int main(int, char**) {
+  test_sfinae_with_nasty_char();
+  test_sfinae<char>();
+  test_sfinae<char, constexpr_char_traits<char>>();
   test<char>();
   test<char, constexpr_char_traits<char>>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/mode.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/mode.pass.cpp
new file mode 100644
index 00000000000000..5a497e5b3238c0
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/mode.pass.cpp
@@ -0,0 +1,96 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+//   template<class charT, class traits = char_traits<charT>>
+//   class basic_spanbuf
+//     : public basic_streambuf<charT, traits> {
+
+//     // [spanbuf.cons], constructors
+//
+//     explicit basic_spanbuf(ios_base::openmode which);
+
+#include <cassert>
+#include <concepts>
+#include <span>
+#include <spanstream>
+
+#include "constexpr_char_traits.h"
+#include "nasty_string.h"
+#include "test_convertible.h"
+#include "test_macros.h"
+
+#include "../../types.h"
+
+void test_sfinae_with_nasty_char() {
+  using SpBuf = std::basic_spanbuf<nasty_char, nasty_char_traits>;
+
+  static_assert(std::constructible_from<SpBuf, std::ios_base::openmode>);
+  static_assert(!test_convertible<SpBuf, std::ios_base::openmode>());
+}
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
+void test_sfinae() {
+  using SpBuf = std::basic_spanbuf<CharT, TraitsT>;
+
+  // `Mode`
+  static_assert(std::constructible_from<SpBuf, std::ios_base::openmode>);
+  static_assert(!test_convertible<SpBuf, std::ios_base::openmode>());
+
+  // Non-mode
+  static_assert(!std::constructible_from<SpBuf, const NonMode>);
+  static_assert(!test_convertible<SpBuf, const NonMode>());
+}
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
+void test() {
+  using SpBuf = std::basic_spanbuf<CharT, TraitsT>;
+
+  static_assert(std::default_initializable<SpBuf>);
+
+  // Mode: `in`
+  {
+    SpBuf spBuf(std::ios_base::in);
+    assert(spBuf.span().data() == nullptr);
+    assert(spBuf.span().empty());
+    assert(spBuf.span().size() == 0);
+  }
+  // Mode: `out`
+  {
+    SpBuf spBuf(std::ios_base::out);
+    assert(spBuf.span().data() == nullptr);
+    assert(spBuf.span().empty());
+    assert(spBuf.span().size() == 0);
+  }
+  // Mode: multiple
+  {
+    SpBuf spBuf(std::ios_base::out | std::ios_base::in | std::ios_base::binary);
+    assert(spBuf.span().data() == nullptr);
+    assert(spBuf.span().empty());
+    assert(spBuf.span().size() == 0);
+  }
+}
+
+int main(int, char**) {
+  test_sfinae_with_nasty_char();
+  test_sfinae<char>();
+  test_sfinae<char, constexpr_char_traits<char>>();
+  test<char>();
+  test<char, constexpr_char_traits<char>>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_sfinae<wchar_t>();
+  test_sfinae<wchar_t, constexpr_char_traits<wchar_t>>();
+  test<wchar_t>();
+  test<wchar_t, constexpr_char_traits<wchar_t>>();
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/move.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/move.pass.cpp
new file mode 100644
index 00000000000000..cfc67b07ea3030
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/move.pass.cpp
@@ -0,0 +1,224 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+//   template<class charT, class traits = char_traits<charT>>
+//   class basic_spanbuf
+//     : public basic_streambuf<charT, traits> {
+
+//     // [spanbuf.cons], constructors
+//
+//     basic_spanbuf(basic_spanbuf&& rhs);
+
+#include <cassert>
+#include <concepts>
+#include <span>
+#include <spanstream>
+
+#include "constexpr_char_traits.h"
+#include "nasty_string.h"
+#include "test_convertible.h"
+#include "test_macros.h"
+
+template <typename CharT, typename TraitsT>
+struct TestSpanBuf : std::basic_spanbuf<CharT, TraitsT> {
+  using std::basic_spanbuf<CharT, TraitsT>::basic_spanbuf;
+
+  TestSpanBuf(std::basic_spanbuf<CharT, TraitsT>&& rhs) : std::basic_spanbuf<CharT, TraitsT>(std::move(rhs)) {}
+
+  void check_postconditions(TestSpanBuf<CharT, TraitsT> const& rhs_p) const {
+    assert(this->span().data() == rhs_p.span().data());
+    assert(this->span().size() == rhs_p.span().size());
+    assert(this->eback() == rhs_p.eback());
+    assert(this->gptr() == rhs_p.gptr());
+    assert(this->egptr() == rhs_p.egptr());
+    assert(this->pbase() == rhs_p.pbase());
+    assert(this->pptr() == rhs_p.pptr());
+    assert(this->epptr() == rhs_p.epptr());
+    assert(this->getloc() == rhs_p.getloc());
+  }
+};
+
+void test_sfinae_with_nasty_char() {
+  using SpBuf = std::basic_spanbuf<nasty_char, nasty_char_traits>;
+
+  static_assert(std::move_constructible<SpBuf>);
+}
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
+void test_sfinae() {
+  using SpBuf = std::basic_spanbuf<CharT, TraitsT>;
+
+  static_assert(std::move_constructible<SpBuf>);
+}
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
+void test() {
+  using SpBuf = std::basic_spanbuf<CharT, TraitsT>;
+
+  {
+    // Empty `span`
+    {
+      // Mode: `ios_base::in`
+      {
+        SpBuf rhsSpBuf{std::ios_base::in};
+        SpBuf spBuf(std::move(rhsSpBuf));
+        assert(spBuf.span().data() == nullptr);
+        assert(spBuf.span().empty());
+        assert(spBuf.span().size() == 0);
+      }
+      // Mode: `ios_base::out`
+      {
+        SpBuf rhsSpBuf{std::ios_base::out};
+        SpBuf spBuf(std::move(rhsSpBuf));
+        assert(spBuf.span().data() == nullptr);
+        assert(spBuf.span().empty());
+        assert(spBuf.span().size() == 0);
+      }
+      // Mode: multiple
+      {
+        SpBuf rhsSpBuf{std::ios_base::out | std::ios_base::in};
+        SpBuf spBuf(std::move(rhsSpBuf));
+        assert(spBuf.span().data() == nullptr);
+        assert(spBuf.span().empty());
+        assert(spBuf.span().size() == 0);
+      }
+    }
+
+    // Non-empty `span`
+    {
+      CharT arr[4];
+      std::span<CharT> sp{arr};
+
+      // Mode: `ios_base::in`
+      {
+        SpBuf rhsSpBuf{sp, std::ios_base::in};
+        SpBuf spBuf(std::move(rhsSpBuf));
+        assert(spBuf.span().data() == arr);
+        assert(!spBuf.span().empty());
+        assert(spBuf.span().size() == 4);
+      }
+      // Mode `ios_base::out`
+      {
+        SpBuf rhsSpBuf{sp, std::ios_base::out};
+        SpBuf spBuf(std::move(rhsSpBuf));
+        assert(spBuf.span().data() == arr);
+        assert(spBuf.span().empty());
+        assert(spBuf.span().size() == 0);
+      }
+      // Mode: multiple
+      {
+        SpBuf rhsSpBuf{sp, std::ios_base::out | std::ios_base::in | std::ios_base::binary};
+        SpBuf spBuf(std::move(rhsSpBuf));
+        assert(spBuf.span().data() == arr);
+        assert(spBuf.span().empty());
+        assert(spBuf.span().size() == 0);
+      }
+    }
+  }
+
+  // Check post-conditions
+  {
+    // Empty `span`
+    {
+      {
+        std::span<CharT> sp;
+        TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp);
+        TestSpanBuf<CharT, TraitsT> spBuf(std::move(static_cast<SpBuf&>(rhsSpBuf)));
+        assert(rhsSpBuf.span().empty());
+        assert(spBuf.span().data() == nullptr);
+        spBuf.check_postconditions(rhsSpBuf);
+      }
+      // Mode: `ios_base::in`
+      {
+        std::span<CharT> sp;
+        TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp, std::ios_base::in);
+        TestSpanBuf<CharT, TraitsT> spBuf(std::move(static_cast<SpBuf&>(rhsSpBuf)));
+        assert(rhsSpBuf.span().empty());
+        assert(spBuf.span().data() == nullptr);
+        spBuf.check_postconditions(rhsSpBuf);
+      }
+      // Mode: `ios_base::out`
+      {
+        std::span<CharT> sp;
+        TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp, std::ios_base::out);
+        TestSpanBuf<CharT, TraitsT> spBuf(std::move(static_cast<SpBuf&>(rhsSpBuf)));
+        assert(rhsSpBuf.span().empty());
+        assert(spBuf.span().data() == nullptr);
+        spBuf.check_postconditions(rhsSpBuf);
+      }
+      // Mode: multiple
+      {
+        std::span<CharT> sp;
+        TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp, std::ios_base::out | std::ios_base::in | std::ios_base::binary);
+        TestSpanBuf<CharT, TraitsT> spBuf(std::move(static_cast<SpBuf&>(rhsSpBuf)));
+        assert(rhsSpBuf.span().empty());
+        assert(spBuf.span().data() == nullptr);
+        spBuf.check_postconditions(rhsSpBuf);
+      }
+    }
+
+    // Non-empty `span`
+    {
+      CharT arr[4];
+
+      {
+        std::span<CharT> sp{arr};
+        TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp);
+        TestSpanBuf<CharT, TraitsT> spBuf(std::move(static_cast<SpBuf&>(rhsSpBuf)));
+        assert(rhsSpBuf.span().empty());
+        assert(spBuf.span().data() == arr);
+        spBuf.check_postconditions(rhsSpBuf);
+      }
+      // Mode: `ios_base::in`
+      {
+        std::span<CharT> sp{arr};
+        TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp, std::ios_base::in);
+        TestSpanBuf<CharT, TraitsT> spBuf(std::move(static_cast<SpBuf&>(rhsSpBuf)));
+        assert(!rhsSpBuf.span().empty());
+        assert(spBuf.span().data() == arr);
+        spBuf.check_postconditions(rhsSpBuf);
+      }
+      // Mode: `ios_base::out`
+      {
+        std::span<CharT> sp{arr};
+        TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp, std::ios_base::out);
+        TestSpanBuf<CharT, TraitsT> spBuf(std::move(static_cast<SpBuf&>(rhsSpBuf)));
+        assert(rhsSpBuf.span().empty());
+        assert(spBuf.span().data() == arr);
+        spBuf.check_postconditions(rhsSpBuf);
+      }
+      // Mode: multiple
+      {
+        std::span<CharT> sp{arr};
+        TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp, std::ios_base::out | std::ios_base::in | std::ios_base::binary);
+        TestSpanBuf<CharT, TraitsT> spBuf(std::move(static_cast<SpBuf&>(rhsSpBuf)));
+        assert(rhsSpBuf.span().empty());
+        assert(spBuf.span().data() == arr);
+        spBuf.check_postconditions(rhsSpBuf);
+      }
+    }
+  }
+}
+
+int main(int, char**) {
+  test_sfinae_with_nasty_char();
+  test_sfinae<char>();
+  test_sfinae<char, constexpr_char_traits<char>>();
+  test<char>();
+  test<char, constexpr_char_traits<char>>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+  test<wchar_t, constexpr_char_traits<wchar_t>>();
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/span.mode.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/span.mode.pass.cpp
new file mode 100644
index 00000000000000..fb58235d8c1fe0
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/span.mode.pass.cpp
@@ -0,0 +1,155 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+//   template<class charT, class traits = char_traits<charT>>
+//   class basic_spanbuf
+//     : public basic_streambuf<charT, traits> {
+
+//     // [spanbuf.cons], constructors
+//
+//     explicit basic_spanbuf(std::span<charT> s,
+//                            ios_base::openmode which = ios_base::in | ios_base::out);
+
+#include <cassert>
+#include <concepts>
+#include <span>
+#include <spanstream>
+#include <utility>
+
+#include "constexpr_char_traits.h"
+#include "nasty_string.h"
+#include "test_convertible.h"
+#include "test_macros.h"
+
+#include "../../macros.h"
+#include "../../types.h"
+
+void test_sfinae_with_nasty_char() {
+  using SpBuf = std::basic_spanbuf<nasty_char, nasty_char_traits>;
+
+  static_assert(std::constructible_from<SpBuf, const std::span<nasty_char>, std::ios_base::openmode>);
+  static_assert(!test_convertible<SpBuf, std::ios_base::openmode>());
+}
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
+void test_sfinae() {
+  using SpBuf = std::basic_spanbuf<CharT, TraitsT>;
+
+  // `Mode`
+  static_assert(std::constructible_from<SpBuf, const std::span<CharT>, std::ios_base::openmode>);
+  static_assert(!test_convertible<SpBuf, const std::span<CharT>, std::ios_base::openmode>());
+
+  // Non-mode
+  static_assert(!std::constructible_from<SpBuf, const std::span<CharT>, const NonMode>);
+  static_assert(!test_convertible<SpBuf, const std::span<CharT>, const NonMode>());
+}
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
+void test() {
+  using SpBuf = std::basic_spanbuf<CharT, TraitsT>;
+
+  static_assert(std::default_initializable<SpBuf>);
+
+  // Empty `span`
+  {
+    std::span<CharT> sp{};
+
+    // Mode: `ios_base::in`
+    {
+      SpBuf spBuf(sp, std::ios_base::in);
+      assert(spBuf.span().data() == nullptr);
+      assert(spBuf.span().empty());
+      assert(spBuf.span().size() == 0);
+    }
+    {
+      SpBuf spBuf(std::as_const(sp), std::ios_base::in);
+      assert(spBuf.span().data() == nullptr);
+      assert(spBuf.span().empty());
+      assert(spBuf.span().size() == 0);
+    }
+    // Mode: `ios_base::out`
+    {
+      SpBuf spBuf(sp, std::ios_base::out);
+      assert(spBuf.span().data() == nullptr);
+      assert(spBuf.span().empty());
+      assert(spBuf.span().size() == 0);
+    }
+    {
+      SpBuf spBuf(std::as_const(sp), std::ios_base::out);
+      assert(spBuf.span().data() == nullptr);
+      assert(spBuf.span().empty());
+      assert(spBuf.span().size() == 0);
+    }
+  }
+
+  // Non-empty `span`
+  {
+    CharT arr[4];
+    std::span<CharT> sp{arr};
+
+    // Mode: `ios_base::in`
+    {
+      SpBuf spBuf(sp, std::ios_base::in);
+      assert(spBuf.span().data() == arr);
+      assert(!spBuf.span().empty());
+      assert(spBuf.span().size() == 4);
+    }
+    {
+      SpBuf spBuf(std::as_const(sp), std::ios_base::in);
+      assert(spBuf.span().data() == arr);
+      assert(!spBuf.span().empty());
+      assert(spBuf.span().size() == 4);
+    }
+    // Mode `ios_base::out`
+    {
+      SpBuf spBuf(sp, std::ios_base::out);
+      assert(spBuf.span().data() == arr);
+      assert(spBuf.span().empty());
+      assert(spBuf.span().size() == 0);
+    }
+    {
+      SpBuf spBuf(std::as_const(sp), std::ios_base::out);
+      assert(spBuf.span().data() == arr);
+      assert(spBuf.span().empty());
+      assert(spBuf.span().size() == 0);
+    }
+    // Mode: multiple
+    {
+      SpBuf spBuf(sp, std::ios_base::out | std::ios_base::in);
+      assert(spBuf.span().data() == arr);
+      assert(spBuf.span().empty());
+      assert(spBuf.span().size() == 0);
+    }
+    {
+      SpBuf spBuf(std::as_const(sp), std::ios_base::out | std::ios_base::in | std::ios_base::binary);
+      assert(spBuf.span().data() == arr);
+      assert(spBuf.span().empty());
+      assert(spBuf.span().size() == 0);
+    }
+  }
+}
+
+int main(int, char**) {
+  test_sfinae_with_nasty_char();
+  test_sfinae<char>();
+  test_sfinae<char, constexpr_char_traits<char>>();
+  test<char>();
+  test<char, constexpr_char_traits<char>>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_sfinae<wchar_t>();
+  test_sfinae<wchar_t, constexpr_char_traits<wchar_t>>();
+  test<wchar_t>();
+  test<wchar_t, constexpr_char_traits<wchar_t>>();
+#endif
+
+  return 0;
+}
\ No newline at end of file
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp
index a8cc07ed524ea8..4c18422ebff28c 100644
--- a/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp
@@ -30,18 +30,18 @@
 #include "constexpr_char_traits.h"
 #include "test_macros.h"
 
-template <typename CharT, typename Traits = std::char_traits<CharT>>
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
 void test() {
-  using SpBuf = std::basic_spanbuf<CharT, Traits>;
+  using SpBuf = std::basic_spanbuf<CharT, TraitsT>;
 
   // Types
 
-  static_assert(std::is_base_of_v<std::basic_streambuf<CharT, Traits>, SpBuf>);
+  static_assert(std::is_base_of_v<std::basic_streambuf<CharT, TraitsT>, SpBuf>);
   static_assert(std::is_same_v<typename SpBuf::char_type, CharT>);
-  static_assert(std::is_same_v<typename SpBuf::int_type, typename Traits::int_type>);
-  static_assert(std::is_same_v<typename SpBuf::pos_type, typename Traits::pos_type>);
-  static_assert(std::is_same_v<typename SpBuf::off_type, typename Traits::off_type>);
-  static_assert(std::is_same_v<typename SpBuf::traits_type, Traits>);
+  static_assert(std::is_same_v<typename SpBuf::int_type, typename TraitsT::int_type>);
+  static_assert(std::is_same_v<typename SpBuf::pos_type, typename TraitsT::pos_type>);
+  static_assert(std::is_same_v<typename SpBuf::off_type, typename TraitsT::off_type>);
+  static_assert(std::is_same_v<typename SpBuf::traits_type, TraitsT>);
 
   // Copy properties
 
diff --git a/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp
index 5714834be2ce11..67d29cf041d9cc 100644
--- a/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp
@@ -30,18 +30,18 @@
 #include "constexpr_char_traits.h"
 #include "test_macros.h"
 
-template <typename CharT, typename Traits = std::char_traits<CharT>>
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
 void test() {
-  using SpStream = std::basic_spanstream<CharT, Traits>;
+  using SpStream = std::basic_spanstream<CharT, TraitsT>;
 
   // Types
 
-  static_assert(std::is_base_of_v<std::basic_iostream<CharT, Traits>, SpStream>);
+  static_assert(std::is_base_of_v<std::basic_iostream<CharT, TraitsT>, SpStream>);
   static_assert(std::is_same_v<typename SpStream::char_type, CharT>);
-  static_assert(std::is_same_v<typename SpStream::int_type, typename Traits::int_type>);
-  static_assert(std::is_same_v<typename SpStream::pos_type, typename Traits::pos_type>);
-  static_assert(std::is_same_v<typename SpStream::off_type, typename Traits::off_type>);
-  static_assert(std::is_same_v<typename SpStream::traits_type, Traits>);
+  static_assert(std::is_same_v<typename SpStream::int_type, typename TraitsT::int_type>);
+  static_assert(std::is_same_v<typename SpStream::pos_type, typename TraitsT::pos_type>);
+  static_assert(std::is_same_v<typename SpStream::off_type, typename TraitsT::off_type>);
+  static_assert(std::is_same_v<typename SpStream::traits_type, TraitsT>);
 
   // Copy properties
 
diff --git a/libcxx/test/std/input.output/span.streams/types.h b/libcxx/test/std/input.output/span.streams/types.h
new file mode 100644
index 00000000000000..18788a56579dcb
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/types.h
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_INPUTOUTPUT_SPANSTREAMS_TYPES_H
+#define TEST_STD_INPUTOUTPUT_SPANSTREAMS_TYPES_H
+
+#include <string_view>
+#include <concepts>
+
+#include "test_macros.h"
+
+// template <typename CharT, class Traits = std::char_traits<CharT>>
+// class ConstConvertibleStringView {
+// public:
+//   explicit ConstConvertibleStringView(const CharT* cs) : cs_{cs} {}
+
+//   operator std::basic_string_view<CharT, Traits>() = delete;
+//   operator std::basic_string_view<CharT, Traits>() const { return std::basic_string_view<CharT, Traits>(cs_); }
+
+// private:
+//   const CharT* cs_;
+// };
+
+// static_assert(!std::constructible_from<std::basic_string_view<char>, ConstConvertibleStringView<char>>);
+// static_assert(!std::convertible_to<ConstConvertibleStringView<char>, std::basic_string_view<char>>);
+
+// static_assert(std::constructible_from<std::basic_string_view<char>, const ConstConvertibleStringView<char>>);
+// static_assert(std::convertible_to<const ConstConvertibleStringView<char>, std::basic_string_view<char>>);
+
+// #ifndef TEST_HAS_NO_WIDE_CHARACTERS
+// static_assert(!std::constructible_from<std::basic_string_view<wchar_t>, ConstConvertibleStringView<wchar_t>>);
+// static_assert(!std::convertible_to<ConstConvertibleStringView<wchar_t>, std::basic_string_view<wchar_t>>);
+
+// static_assert(std::constructible_from<std::basic_string_view<wchar_t>, const ConstConvertibleStringView<wchar_t>>);
+// static_assert(std::convertible_to<const ConstConvertibleStringView<wchar_t>, std::basic_string_view<wchar_t>>);
+// #endif
+
+// template <typename CharT, class Traits = std::char_traits<CharT>>
+// class NonConstConvertibleStringView {
+// public:
+//   explicit NonConstConvertibleStringView(const CharT* cs) : cs_{cs} {}
+
+//   operator std::basic_string_view<CharT, Traits>() { return std::basic_string_view<CharT, Traits>(cs_); }
+//   operator std::basic_string_view<CharT, Traits>() const = delete;
+
+// private:
+//   const CharT* cs_;
+// };
+
+// static_assert(std::constructible_from<std::basic_string_view<char>, NonConstConvertibleStringView<char>>);
+// static_assert(std::convertible_to<NonConstConvertibleStringView<char>, std::basic_string_view<char>>);
+
+// static_assert(!std::constructible_from<std::basic_string_view<char>, const NonConstConvertibleStringView<char>>);
+// static_assert(!std::convertible_to<const NonConstConvertibleStringView<char>, std::basic_string_view<char>>);
+
+// #ifndef TEST_HAS_NO_WIDE_CHARACTERS
+// static_assert(std::constructible_from<std::basic_string_view<wchar_t>, NonConstConvertibleStringView<wchar_t>>);
+// static_assert(std::convertible_to<NonConstConvertibleStringView<wchar_t>, std::basic_string_view<wchar_t>>);
+
+// static_assert(!std::constructible_from<std::basic_string_view<wchar_t>, const NonConstConvertibleStringView<wchar_t>>);
+// static_assert(!std::convertible_to<const NonConstConvertibleStringView<wchar_t>, std::basic_string_view<wchar_t>>);
+// #endif
+
+struct SomeObject {};
+
+struct NonMode {};
+
+#endif // TEST_STD_INPUTOUTPUT_SPANSTREAMS_TYPES_H

>From ab66849d1e524f41705b5f29ee6c09534f510a2b Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 1 Mar 2024 09:49:14 +0200
Subject: [PATCH 6/7] Tests: tweaks

---
 .../ospanstream/types.compile.pass.cpp        | 11 +++---
 .../spanbuf/spanbuf.cons/default.pass.cpp     | 20 +---------
 .../spanbuf/spanbuf.cons/mode.pass.cpp        |  9 ++++-
 .../spanbuf/spanbuf.cons/move.pass.cpp        | 24 ++++++++++--
 .../spanbuf/spanbuf.cons/span.mode.pass.cpp   | 38 +++++++++++++++++--
 .../spanbuf/types.compile.pass.cpp            | 11 +++---
 .../spanstream/types.compile.pass.cpp         | 11 +++---
 7 files changed, 83 insertions(+), 41 deletions(-)

diff --git a/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp
index 595770aa5bd6b0..5374011f61cc27 100644
--- a/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/ospanstream/types.compile.pass.cpp
@@ -28,12 +28,17 @@
 #include <type_traits>
 
 #include "constexpr_char_traits.h"
+#include "nasty_string.h"
 #include "test_macros.h"
 
 template <typename CharT, typename TraitsT = std::char_traits<CharT>>
 void test() {
   using SpStream = std::basic_ospanstream<CharT, TraitsT>;
 
+  // Constructors
+
+  static_assert(!std::is_default_constructible_v<SpStream>);
+
   // Types
 
   static_assert(std::is_base_of_v<std::basic_ostream<CharT, TraitsT>, SpStream>);
@@ -50,11 +55,6 @@ void test() {
 
   // Move properties
 
-  static_assert(!std::is_copy_constructible_v<SpStream>);
-  static_assert(!std::is_copy_assignable_v<SpStream>);
-
-  // Move properties
-
   static_assert(std::is_move_constructible_v<SpStream>);
   static_assert(std::is_move_assignable_v<SpStream>);
 }
@@ -62,6 +62,7 @@ void test() {
 void test() {
   test<char>();
   test<char, constexpr_char_traits<char>>();
+  test<nasty_char, nasty_char_traits>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<wchar_t>();
   test<wchar_t, constexpr_char_traits<wchar_t>>();
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/default.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/default.pass.cpp
index 4c6b1ae3080d9e..a94551a96e0002 100644
--- a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/default.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/default.pass.cpp
@@ -24,33 +24,17 @@
 #include "nasty_string.h"
 #include "test_macros.h"
 
-void test_sfinae_with_nasty_char() {
-  using SpBuf = std::basic_spanbuf<nasty_char, nasty_char_traits>;
-
-  static_assert(std::default_initializable<SpBuf>);
-}
-
-template <typename CharT, typename TraitsT = std::char_traits<CharT>>
-void test_sfinae() {
-  using SpBuf = std::basic_spanbuf<CharT, TraitsT>;
-
-  static_assert(std::default_initializable<SpBuf>);
-}
-
 template <typename CharT, typename TraitsT = std::char_traits<CharT>>
 void test() {
-  using SpanBuf = std::basic_spanbuf<CharT, TraitsT>;
+  using SpBuf = std::basic_spanbuf<CharT, TraitsT>;
 
-  SpanBuf spBuf;
+  SpBuf spBuf;
   assert(spBuf.span().data() == nullptr);
   assert(spBuf.span().empty());
   assert(spBuf.span().size() == 0);
 }
 
 int main(int, char**) {
-  test_sfinae_with_nasty_char();
-  test_sfinae<char>();
-  test_sfinae<char, constexpr_char_traits<char>>();
   test<char>();
   test<char, constexpr_char_traits<char>>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/mode.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/mode.pass.cpp
index 5a497e5b3238c0..b452b6e80248e1 100644
--- a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/mode.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/mode.pass.cpp
@@ -33,15 +33,20 @@
 void test_sfinae_with_nasty_char() {
   using SpBuf = std::basic_spanbuf<nasty_char, nasty_char_traits>;
 
+  // Mode
   static_assert(std::constructible_from<SpBuf, std::ios_base::openmode>);
   static_assert(!test_convertible<SpBuf, std::ios_base::openmode>());
+
+  // Non-mode
+  static_assert(!std::constructible_from<SpBuf, const NonMode>);
+  static_assert(!test_convertible<SpBuf, const NonMode>());
 }
 
 template <typename CharT, typename TraitsT = std::char_traits<CharT>>
 void test_sfinae() {
   using SpBuf = std::basic_spanbuf<CharT, TraitsT>;
 
-  // `Mode`
+  // Mode
   static_assert(std::constructible_from<SpBuf, std::ios_base::openmode>);
   static_assert(!test_convertible<SpBuf, std::ios_base::openmode>());
 
@@ -72,7 +77,7 @@ void test() {
   }
   // Mode: multiple
   {
-    SpBuf spBuf(std::ios_base::out | std::ios_base::in | std::ios_base::binary);
+    SpBuf spBuf(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
     assert(spBuf.span().data() == nullptr);
     assert(spBuf.span().empty());
     assert(spBuf.span().size() == 0);
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/move.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/move.pass.cpp
index cfc67b07ea3030..b32f6703981af6 100644
--- a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/move.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/move.pass.cpp
@@ -32,7 +32,7 @@ template <typename CharT, typename TraitsT>
 struct TestSpanBuf : std::basic_spanbuf<CharT, TraitsT> {
   using std::basic_spanbuf<CharT, TraitsT>::basic_spanbuf;
 
-  TestSpanBuf(std::basic_spanbuf<CharT, TraitsT>&& rhs) : std::basic_spanbuf<CharT, TraitsT>(std::move(rhs)) {}
+  TestSpanBuf(std::basic_spanbuf<CharT, TraitsT>&& rhs_p) : std::basic_spanbuf<CharT, TraitsT>(std::move(rhs_p)) {}
 
   void check_postconditions(TestSpanBuf<CharT, TraitsT> const& rhs_p) const {
     assert(this->span().data() == rhs_p.span().data());
@@ -67,6 +67,14 @@ void test() {
   {
     // Empty `span`
     {
+      // Mode: default
+      {
+        SpBuf rhsSpBuf;
+        SpBuf spBuf(std::move(rhsSpBuf));
+        assert(spBuf.span().data() == nullptr);
+        assert(spBuf.span().empty());
+        assert(spBuf.span().size() == 0);
+      }
       // Mode: `ios_base::in`
       {
         SpBuf rhsSpBuf{std::ios_base::in};
@@ -98,6 +106,14 @@ void test() {
       CharT arr[4];
       std::span<CharT> sp{arr};
 
+      // Mode: default
+      {
+        SpBuf rhsSpBuf{sp};
+        SpBuf spBuf(std::move(rhsSpBuf));
+        assert(spBuf.span().data() == arr);
+        assert(spBuf.span().empty());
+        assert(spBuf.span().size() == 0);
+      }
       // Mode: `ios_base::in`
       {
         SpBuf rhsSpBuf{sp, std::ios_base::in};
@@ -129,6 +145,7 @@ void test() {
   {
     // Empty `span`
     {
+      // Mode: default
       {
         std::span<CharT> sp;
         TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp);
@@ -158,7 +175,7 @@ void test() {
       // Mode: multiple
       {
         std::span<CharT> sp;
-        TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp, std::ios_base::out | std::ios_base::in | std::ios_base::binary);
+        TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp, std::ios_base::in | std::ios_base::out | std::ios_base::binary);
         TestSpanBuf<CharT, TraitsT> spBuf(std::move(static_cast<SpBuf&>(rhsSpBuf)));
         assert(rhsSpBuf.span().empty());
         assert(spBuf.span().data() == nullptr);
@@ -170,6 +187,7 @@ void test() {
     {
       CharT arr[4];
 
+      // Mode: default
       {
         std::span<CharT> sp{arr};
         TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp);
@@ -199,7 +217,7 @@ void test() {
       // Mode: multiple
       {
         std::span<CharT> sp{arr};
-        TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp, std::ios_base::out | std::ios_base::in | std::ios_base::binary);
+        TestSpanBuf<CharT, TraitsT> rhsSpBuf(sp, std::ios_base::in | std::ios_base::out | std::ios_base::binary);
         TestSpanBuf<CharT, TraitsT> spBuf(std::move(static_cast<SpBuf&>(rhsSpBuf)));
         assert(rhsSpBuf.span().empty());
         assert(spBuf.span().data() == arr);
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/span.mode.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/span.mode.pass.cpp
index fb58235d8c1fe0..ff600b92e5c025 100644
--- a/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/span.mode.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/spanbuf.cons/span.mode.pass.cpp
@@ -36,15 +36,20 @@
 void test_sfinae_with_nasty_char() {
   using SpBuf = std::basic_spanbuf<nasty_char, nasty_char_traits>;
 
+  // Mode
   static_assert(std::constructible_from<SpBuf, const std::span<nasty_char>, std::ios_base::openmode>);
   static_assert(!test_convertible<SpBuf, std::ios_base::openmode>());
+
+  // Non-mode
+  static_assert(!std::constructible_from<SpBuf, const std::span<nasty_char>, const NonMode>);
+  static_assert(!test_convertible<SpBuf, const NonMode>());
 }
 
 template <typename CharT, typename TraitsT = std::char_traits<CharT>>
 void test_sfinae() {
   using SpBuf = std::basic_spanbuf<CharT, TraitsT>;
 
-  // `Mode`
+  // Mode
   static_assert(std::constructible_from<SpBuf, const std::span<CharT>, std::ios_base::openmode>);
   static_assert(!test_convertible<SpBuf, const std::span<CharT>, std::ios_base::openmode>());
 
@@ -63,6 +68,13 @@ void test() {
   {
     std::span<CharT> sp{};
 
+    // Mode: default
+    {
+      SpBuf spBuf(sp);
+      assert(spBuf.span().data() == nullptr);
+      assert(spBuf.span().empty());
+      assert(spBuf.span().size() == 0);
+    }
     // Mode: `ios_base::in`
     {
       SpBuf spBuf(sp, std::ios_base::in);
@@ -89,6 +101,19 @@ void test() {
       assert(spBuf.span().empty());
       assert(spBuf.span().size() == 0);
     }
+    // Mode: multiple
+    {
+      SpBuf spBuf(sp, std::ios_base::in | std::ios_base::out | std::ios_base::binary);
+      assert(spBuf.span().data() == nullptr);
+      assert(spBuf.span().empty());
+      assert(spBuf.span().size() == 0);
+    }
+    {
+      SpBuf spBuf(std::as_const(sp), std::ios_base::in | std::ios_base::out | std::ios_base::binary);
+      assert(spBuf.span().data() == nullptr);
+      assert(spBuf.span().empty());
+      assert(spBuf.span().size() == 0);
+    }
   }
 
   // Non-empty `span`
@@ -96,6 +121,13 @@ void test() {
     CharT arr[4];
     std::span<CharT> sp{arr};
 
+    // Mode: default
+    {
+      SpBuf spBuf(sp);
+      assert(spBuf.span().data() == arr);
+      assert(spBuf.span().empty());
+      assert(spBuf.span().size() == 0);
+    }
     // Mode: `ios_base::in`
     {
       SpBuf spBuf(sp, std::ios_base::in);
@@ -124,13 +156,13 @@ void test() {
     }
     // Mode: multiple
     {
-      SpBuf spBuf(sp, std::ios_base::out | std::ios_base::in);
+      SpBuf spBuf(sp, std::ios_base::in | std::ios_base::out | std::ios_base::binary);
       assert(spBuf.span().data() == arr);
       assert(spBuf.span().empty());
       assert(spBuf.span().size() == 0);
     }
     {
-      SpBuf spBuf(std::as_const(sp), std::ios_base::out | std::ios_base::in | std::ios_base::binary);
+      SpBuf spBuf(std::as_const(sp), std::ios_base::in | std::ios_base::out | std::ios_base::binary);
       assert(spBuf.span().data() == arr);
       assert(spBuf.span().empty());
       assert(spBuf.span().size() == 0);
diff --git a/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp
index 4c18422ebff28c..dec47dbf7b8ebc 100644
--- a/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/spanbuf/types.compile.pass.cpp
@@ -28,12 +28,17 @@
 #include <type_traits>
 
 #include "constexpr_char_traits.h"
+#include "nasty_string.h"
 #include "test_macros.h"
 
 template <typename CharT, typename TraitsT = std::char_traits<CharT>>
 void test() {
   using SpBuf = std::basic_spanbuf<CharT, TraitsT>;
 
+  // Constructors
+
+  static_assert(std::is_default_constructible_v<SpBuf>);
+
   // Types
 
   static_assert(std::is_base_of_v<std::basic_streambuf<CharT, TraitsT>, SpBuf>);
@@ -50,11 +55,6 @@ void test() {
 
   // Move properties
 
-  static_assert(!std::is_copy_constructible_v<SpBuf>);
-  static_assert(!std::is_copy_assignable_v<SpBuf>);
-
-  // Move properties
-
   static_assert(std::is_move_constructible_v<SpBuf>);
   static_assert(std::is_move_assignable_v<SpBuf>);
 }
@@ -62,6 +62,7 @@ void test() {
 void test() {
   test<char>();
   test<char, constexpr_char_traits<char>>();
+  test<nasty_char, nasty_char_traits>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<wchar_t>();
   test<wchar_t, constexpr_char_traits<wchar_t>>();
diff --git a/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp
index 67d29cf041d9cc..7c33c604237a3b 100644
--- a/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/spanstream/types.compile.pass.cpp
@@ -28,12 +28,17 @@
 #include <type_traits>
 
 #include "constexpr_char_traits.h"
+#include "nasty_string.h"
 #include "test_macros.h"
 
 template <typename CharT, typename TraitsT = std::char_traits<CharT>>
 void test() {
   using SpStream = std::basic_spanstream<CharT, TraitsT>;
 
+  // Constructors
+
+  static_assert(!std::is_default_constructible_v<SpStream>);
+
   // Types
 
   static_assert(std::is_base_of_v<std::basic_iostream<CharT, TraitsT>, SpStream>);
@@ -50,11 +55,6 @@ void test() {
 
   // Move properties
 
-  static_assert(!std::is_copy_constructible_v<SpStream>);
-  static_assert(!std::is_copy_assignable_v<SpStream>);
-
-  // Move properties
-
   static_assert(std::is_move_constructible_v<SpStream>);
   static_assert(std::is_move_assignable_v<SpStream>);
 }
@@ -62,6 +62,7 @@ void test() {
 void test() {
   test<char>();
   test<char, constexpr_char_traits<char>>();
+  test<nasty_char, nasty_char_traits>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<wchar_t>();
   test<wchar_t, constexpr_char_traits<wchar_t>>();

>From 8b953196d70c2cf204a284147b52e0c172f97696 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 1 Mar 2024 09:49:44 +0200
Subject: [PATCH 7/7] Tests: `ispanstream` constructors - WIP

---
 .../ispanstream.cons/move.pass.cpp            |  81 +++++++++++
 .../ispanstream/ispanstream.cons/ros.pass.cpp |  60 ++++++++
 .../ispanstream.cons/span.mode.pass.cpp       | 136 ++++++++++++++++++
 .../ispanstream/types.compile.pass.cpp        |  11 +-
 4 files changed, 283 insertions(+), 5 deletions(-)
 create mode 100644 libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.cons/move.pass.cpp
 create mode 100644 libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.cons/ros.pass.cpp
 create mode 100644 libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.cons/span.mode.pass.cpp

diff --git a/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.cons/move.pass.cpp b/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.cons/move.pass.cpp
new file mode 100644
index 00000000000000..ce711deedc140d
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.cons/move.pass.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+//   template<class charT, class traits = char_traits<charT>>
+//   class basic_ispanstream
+//     : public basic_streambuf<charT, traits> {
+
+//     // [spanbuf.cons], constructors
+//
+//     basic_ispanstream(basic_ispanstream&& rhs);
+
+#include <cassert>
+#include <concepts>
+#include <span>
+#include <spanstream>
+
+#include "constexpr_char_traits.h"
+#include "nasty_string.h"
+#include "test_convertible.h"
+#include "test_macros.h"
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
+void test() {
+  using SpStream = std::basic_ispanstream<CharT, TraitsT>;
+
+  CharT arr[4];
+  std::span<CharT> sp{arr};
+
+  // Mode: default
+  {
+    SpStream rhsSpStream{sp};
+    SpStream sps(std::move(rhsSpStream));
+    assert(sps.span().data() == arr);
+    assert(!sps.span().empty());
+    assert(sps.span().size() == 4);
+  }
+  // Mode: `ios_base::in`
+  {
+    SpStream rhsSpStream{sp, std::ios_base::in};
+    SpStream sps(std::move(rhsSpStream));
+    assert(sps.span().data() == arr);
+    assert(!sps.span().empty());
+    assert(sps.span().size() == 4);
+  }
+  // Mode `ios_base::out`
+  {
+    SpStream rhsSpStream{sp, std::ios_base::out};
+    SpStream sps(std::move(rhsSpStream));
+    assert(sps.span().data() == arr);
+    assert(sps.span().empty());
+    assert(sps.span().size() == 0);
+  }
+  // Mode: multiple
+  {
+    SpStream rhsSpStream{sp, std::ios_base::in | std::ios_base::out | std::ios_base::binary};
+    SpStream sps(std::move(rhsSpStream));
+    assert(sps.span().data() == arr);
+    assert(sps.span().empty());
+    assert(sps.span().size() == 0);
+  }
+}
+
+int main(int, char**) {
+  test<char>();
+  test<char, constexpr_char_traits<char>>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+  test<wchar_t, constexpr_char_traits<wchar_t>>();
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.cons/ros.pass.cpp b/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.cons/ros.pass.cpp
new file mode 100644
index 00000000000000..d4c9b2a765ac1b
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.cons/ros.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <spanstream>
+
+//   template<class charT, class traits = char_traits<charT>>
+//   class basic_ispanstream
+//     : public basic_streambuf<charT, traits> {
+
+//     // [spanbuf.cons], constructors
+//
+//     template<class ROS> explicit basic_ispanstream(ROS&& s);
+
+#include <cassert>
+#include <spanstream>
+
+#include "constexpr_char_traits.h"
+#include "nasty_string.h"
+#include "test_macros.h"
+
+void test_sfinae_with_nasty_char() {
+  using SpStream = std::basic_ispanstream<nasty_char, nasty_char_traits>;
+
+  // TODO:
+}
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
+void test_sfinae() {
+  using SpStream = std::basic_ispanstream<CharT, TraitsT>;
+
+  // TODO:
+}
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
+void test() {
+  using SpStream = std::basic_ispanstream<CharT, TraitsT>;
+
+  // TODO:
+}
+
+int main(int, char**) {
+  test_sfinae_with_nasty_char();
+  test_sfinae<char>();
+  test_sfinae<char, constexpr_char_traits<char>>();
+  test<char>();
+  test<char, constexpr_char_traits<char>>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+  test<wchar_t, constexpr_char_traits<wchar_t>>();
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.cons/span.mode.pass.cpp b/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.cons/span.mode.pass.cpp
new file mode 100644
index 00000000000000..0938e35d0fb60a
--- /dev/null
+++ b/libcxx/test/std/input.output/span.streams/ispanstream/ispanstream.cons/span.mode.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <spanstream>
+
+//   template<class charT, class traits = char_traits<charT>>
+//   class basic_ispanstream
+//     : public basic_streambuf<charT, traits> {
+
+//     // [spanbuf.cons], constructors
+//
+//     explicit basic_ispanstream(std::span<charT> s,
+//                                ios_base::openmode which = ios_base::in);
+
+#include <cassert>
+#include <concepts>
+#include <span>
+#include <spanstream>
+#include <utility>
+
+#include "constexpr_char_traits.h"
+#include "nasty_string.h"
+#include "test_convertible.h"
+#include "test_macros.h"
+
+#include "../../macros.h"
+#include "../../types.h"
+
+void test_sfinae_with_nasty_char() {
+  using SpStream = std::basic_ispanstream<nasty_char, nasty_char_traits>;
+
+  // Mode
+  static_assert(std::constructible_from<SpStream, const std::span<nasty_char>, std::ios_base::openmode>);
+  static_assert(!test_convertible<SpStream, std::ios_base::openmode>());
+
+  // Non-mode
+  static_assert(!std::constructible_from<SpStream, const std::span<nasty_char>, const NonMode>);
+  static_assert(!test_convertible<SpStream, const std::span<nasty_char>, const NonMode>());
+}
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
+void test_sfinae() {
+  using SpStream = std::basic_ispanstream<CharT, TraitsT>;
+
+  // Mode
+  static_assert(std::constructible_from<SpStream, const std::span<CharT>, std::ios_base::openmode>);
+  static_assert(!test_convertible<SpStream, const std::span<CharT>, std::ios_base::openmode>());
+
+  // Non-mode
+  static_assert(!std::constructible_from<SpStream, const std::span<CharT>, const NonMode>);
+  static_assert(!test_convertible<SpStream, const std::span<CharT>, const NonMode>());
+}
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>>
+void test() {
+  using SpStream = std::basic_ispanstream<CharT, TraitsT>;
+
+  CharT arr[4];
+  std::span<CharT> sp{arr};
+
+  // Mode: default
+  {
+    SpStream sps(sp);
+    assert(sps.span().data() == arr);
+    assert(!sps.span().empty());
+    assert(sps.span().size() == 4);
+  }
+  {
+    SpStream sps(std::as_const(sp));
+    assert(sps.span().data() == arr);
+    assert(!sps.span().empty());
+    assert(sps.span().size() == 4);
+  }
+  // Mode: `ios_base::in`
+  {
+    SpStream sps(sp, std::ios_base::in);
+    assert(sps.span().data() == arr);
+    assert(!sps.span().empty());
+    assert(sps.span().size() == 4);
+  }
+  {
+    SpStream sps(std::as_const(sp), std::ios_base::in);
+    assert(sps.span().data() == arr);
+    assert(!sps.span().empty());
+    assert(sps.span().size() == 4);
+  }
+  // Mode `ios_base::out`
+  {
+    SpStream sps(sp, std::ios_base::out);
+    assert(sps.span().data() == arr);
+    assert(sps.span().empty());
+    assert(sps.span().size() == 0);
+  }
+  {
+    SpStream sps(std::as_const(sp), std::ios_base::out);
+    assert(sps.span().data() == arr);
+    assert(sps.span().empty());
+    assert(sps.span().size() == 0);
+  }
+  // Mode: multiple
+  {
+    SpStream sps(sp, std::ios_base::in | std::ios_base::out | std::ios_base::binary);
+    assert(sps.span().data() == arr);
+    assert(sps.span().empty());
+    assert(sps.span().size() == 0);
+  }
+  {
+    SpStream sps(std::as_const(sp), std::ios_base::in | std::ios_base::out | std::ios_base::binary);
+    assert(sps.span().data() == arr);
+    assert(sps.span().empty());
+    assert(sps.span().size() == 0);
+  }
+}
+
+int main(int, char**) {
+  test_sfinae_with_nasty_char();
+  test_sfinae<char>();
+  test_sfinae<char, constexpr_char_traits<char>>();
+  test<char>();
+  test<char, constexpr_char_traits<char>>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_sfinae<wchar_t>();
+  test_sfinae<wchar_t, constexpr_char_traits<wchar_t>>();
+  test<wchar_t>();
+  test<wchar_t, constexpr_char_traits<wchar_t>>();
+#endif
+
+  return 0;
+}
\ No newline at end of file
diff --git a/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp b/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp
index c566a3fce647dc..ddd15186b7c189 100644
--- a/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp
+++ b/libcxx/test/std/input.output/span.streams/ispanstream/types.compile.pass.cpp
@@ -28,12 +28,17 @@
 #include <type_traits>
 
 #include "constexpr_char_traits.h"
+#include "nasty_string.h"
 #include "test_macros.h"
 
 template <typename CharT, typename TraitsT = std::char_traits<CharT>>
 void test() {
   using SpStream = std::basic_ispanstream<CharT, TraitsT>;
 
+  // Constructors
+
+  static_assert(!std::is_default_constructible_v<SpStream>);
+
   // Types
 
   static_assert(std::is_base_of_v<std::basic_istream<CharT, TraitsT>, SpStream>);
@@ -50,11 +55,6 @@ void test() {
 
   // Move properties
 
-  static_assert(!std::is_copy_constructible_v<SpStream>);
-  static_assert(!std::is_copy_assignable_v<SpStream>);
-
-  // Move properties
-
   static_assert(std::is_move_constructible_v<SpStream>);
   static_assert(std::is_move_assignable_v<SpStream>);
 }
@@ -62,6 +62,7 @@ void test() {
 void test() {
   test<char>();
   test<char, constexpr_char_traits<char>>();
+  test<nasty_char, nasty_char_traits>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<wchar_t>();
   test<wchar_t, constexpr_char_traits<wchar_t>>();



More information about the libcxx-commits mailing list