[libcxx-commits] [libcxx] 04d4f4b - [libc++][format] Adds container adaptor formatters.

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jan 19 08:50:23 PST 2023


Author: Mark de Wever
Date: 2023-01-19T17:50:16+01:00
New Revision: 04d4f4b3d4e4fd608a3bc3fe387006435f04b61d

URL: https://github.com/llvm/llvm-project/commit/04d4f4b3d4e4fd608a3bc3fe387006435f04b61d
DIFF: https://github.com/llvm/llvm-project/commit/04d4f4b3d4e4fd608a3bc3fe387006435f04b61d.diff

LOG: [libc++][format] Adds container adaptor formatters.

Implements parts of
- P2286R8 Formatting Ranges

Depends on D140653

Reviewed By: ldionne, #libc

Differential Revision: https://reviews.llvm.org/D141290

Added: 
    libcxx/include/__format/container_adaptor.h
    libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.format.pass.cpp
    libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h
    libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.vformat.pass.cpp
    libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.pass.cpp
    libcxx/test/std/containers/container.adaptors/container.adaptors.format/parse.pass.cpp

Modified: 
    libcxx/docs/Status/FormatPaper.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/__format/range_default_formatter.h
    libcxx/include/format
    libcxx/include/module.modulemap.in
    libcxx/include/queue
    libcxx/include/stack
    libcxx/test/libcxx/private_headers.verify.cpp
    libcxx/test/libcxx/transitive_includes/cxx03.csv
    libcxx/test/libcxx/transitive_includes/cxx11.csv
    libcxx/test/libcxx/transitive_includes/cxx14.csv
    libcxx/test/libcxx/transitive_includes/cxx17.csv
    libcxx/test/libcxx/transitive_includes/cxx20.csv
    libcxx/test/libcxx/transitive_includes/cxx2b.csv
    libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv
index 9fb7cd09dc499..10463c44ef8fe 100644
--- a/libcxx/docs/Status/FormatPaper.csv
+++ b/libcxx/docs/Status/FormatPaper.csv
@@ -32,6 +32,6 @@ Section,Description,Dependencies,Assignee,Status,First released version
 `[format.string.std] <https://wg21.link/format.string.std>`_,"std-format-spec ``type`` debug",,Mark de Wever,|Complete|,Clang 16
 `[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: sequences",,Mark de Wever,|Complete|,Clang 16
 `[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: associative",,Mark de Wever,,
-`[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: container adaptors",,Mark de Wever,,
+`[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: container adaptors",,Mark de Wever,|Complete|,Clang 16
 `[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: ``pair`` and ``tuple``",,Mark de Wever,|Complete|,Clang 16
 `[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: ``vector<bool>``",,Mark de Wever,,

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 81d488ffc4b20..2d202444894f3 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -302,6 +302,7 @@ set(files
   __filesystem/u8path.h
   __format/buffer.h
   __format/concepts.h
+  __format/container_adaptor.h
   __format/enable_insertable.h
   __format/escaped_output_table.h
   __format/extended_grapheme_cluster_table.h

diff  --git a/libcxx/include/__format/container_adaptor.h b/libcxx/include/__format/container_adaptor.h
new file mode 100644
index 0000000000000..62b698186ea13
--- /dev/null
+++ b/libcxx/include/__format/container_adaptor.h
@@ -0,0 +1,70 @@
+// -*- 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___FORMAT_CONTAINER_ADAPTOR_H
+#define _LIBCPP___FORMAT_CONTAINER_ADAPTOR_H
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#include <__availability>
+#include <__config>
+#include <__format/concepts.h>
+#include <__format/formatter.h>
+#include <__format/range_default_formatter.h>
+#include <queue>
+#include <stack>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 20
+
+// [container.adaptors.format] only specifies the library should provide the
+// formatter specializations, not which header should provide them.
+// Since <format> includes a lot of headers, add these headers here instead of
+// adding more dependencies like, locale, optinal, string, tuple, etc. to the
+// adaptor headers. To use the format functions users already include <format>.
+
+template <class _Adaptor, class _CharT>
+struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __formatter_container_adaptor {
+private:
+  using __maybe_const_adaptor = __fmt_maybe_const<_Adaptor, _CharT>;
+  formatter<typename _Adaptor::container_type, _CharT> __underlying_;
+
+public:
+  template <class _ParseContext>
+  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
+    return __underlying_.parse(__ctx);
+  }
+
+  template <class _FormatContext>
+  _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator
+  format(__maybe_const_adaptor& __adaptor, _FormatContext& __ctx) const {
+    return __underlying_.format(__adaptor.__get_container(), __ctx);
+  }
+};
+
+template <class _CharT, class _Tp, formattable<_CharT> _Container>
+struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<queue<_Tp, _Container>, _CharT>
+    : public __formatter_container_adaptor<queue<_Tp, _Container>, _CharT> {};
+
+template <class _CharT, class _Tp, class _Container, class _Compare>
+struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<priority_queue<_Tp, _Container, _Compare>, _CharT>
+    : public __formatter_container_adaptor<priority_queue<_Tp, _Container, _Compare>, _CharT> {};
+
+template <class _CharT, class _Tp, formattable<_CharT> _Container>
+struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<stack<_Tp, _Container>, _CharT>
+    : public __formatter_container_adaptor<stack<_Tp, _Container>, _CharT> {};
+
+#endif //_LIBCPP_STD_VER > 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FORMAT_CONTAINER_ADAPTOR_H

diff  --git a/libcxx/include/__format/range_default_formatter.h b/libcxx/include/__format/range_default_formatter.h
index ee1dc52e37526..652c021277664 100644
--- a/libcxx/include/__format/range_default_formatter.h
+++ b/libcxx/include/__format/range_default_formatter.h
@@ -146,8 +146,6 @@ struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatte
   __range_default_formatter() = delete; // TODO FMT Implement
 };
 
-// Dispatcher to select the specialization based on the type of the range.
-
 template <ranges::input_range _Rp, class _CharT>
   requires(format_kind<_Rp> != range_format::disabled && formattable<ranges::range_reference_t<_Rp>, _CharT>)
 struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<_Rp, _CharT>

diff  --git a/libcxx/include/format b/libcxx/include/format
index aaac95b39c4c9..2c583c1065ba6 100644
--- a/libcxx/include/format
+++ b/libcxx/include/format
@@ -178,6 +178,7 @@ namespace std {
 #include <__config>
 #include <__format/buffer.h>
 #include <__format/concepts.h>
+#include <__format/container_adaptor.h>
 #include <__format/enable_insertable.h>
 #include <__format/format_arg.h>
 #include <__format/format_arg_store.h>

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 81f503e29c6a0..aa4da4dadc42f 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -839,6 +839,7 @@ module std [system] {
     module __format {
       module buffer                          { private header "__format/buffer.h" }
       module concepts                        { private header "__format/concepts.h" }
+      module container_adaptor               { private header "__format/container_adaptor.h" }
       module enable_insertable               { private header "__format/enable_insertable.h" }
       module escaped_output_table            { private header "__format/escaped_output_table.h" }
       module extended_grapheme_cluster_table { private header "__format/extended_grapheme_cluster_table.h" }

diff  --git a/libcxx/include/queue b/libcxx/include/queue
index c58da5ec6ee3c..6c1b892efadc3 100644
--- a/libcxx/include/queue
+++ b/libcxx/include/queue
@@ -382,6 +382,8 @@ public:
         swap(c, __q.c);
     }
 
+    _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI const _Container& __get_container() const { return c; }
+
     template <class _T1, class _C1>
     friend
     _LIBCPP_INLINE_VISIBILITY
@@ -633,6 +635,8 @@ public:
     void swap(priority_queue& __q)
         _NOEXCEPT_(__is_nothrow_swappable<container_type>::value &&
                    __is_nothrow_swappable<value_compare>::value);
+
+    _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI const _Container& __get_container() const { return c; }
 };
 
 #if _LIBCPP_STD_VER >= 17

diff  --git a/libcxx/include/stack b/libcxx/include/stack
index 2abbcd025c4aa..d653d1bc7e49c 100644
--- a/libcxx/include/stack
+++ b/libcxx/include/stack
@@ -255,6 +255,8 @@ public:
         swap(c, __s.c);
     }
 
+    _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI const _Container& __get_container() const { return c; }
+
     template <class T1, class _C1>
     friend
     bool

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 27b1c90ff5bfa..e61eeddd2b466 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -334,6 +334,7 @@ END-SCRIPT
 #include <__filesystem/u8path.h> // expected-error@*:* {{use of private header from outside its module: '__filesystem/u8path.h'}}
 #include <__format/buffer.h> // expected-error@*:* {{use of private header from outside its module: '__format/buffer.h'}}
 #include <__format/concepts.h> // expected-error@*:* {{use of private header from outside its module: '__format/concepts.h'}}
+#include <__format/container_adaptor.h> // expected-error@*:* {{use of private header from outside its module: '__format/container_adaptor.h'}}
 #include <__format/enable_insertable.h> // expected-error@*:* {{use of private header from outside its module: '__format/enable_insertable.h'}}
 #include <__format/escaped_output_table.h> // expected-error@*:* {{use of private header from outside its module: '__format/escaped_output_table.h'}}
 #include <__format/extended_grapheme_cluster_table.h> // expected-error@*:* {{use of private header from outside its module: '__format/extended_grapheme_cluster_table.h'}}

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c245d11b5a4e0..58fe49cf2844e 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -354,6 +354,8 @@ format initializer_list
 format limits
 format locale
 format optional
+format queue
+format stack
 format stdexcept
 format string
 format string_view

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 74bf87284072b..2f9ef7da30ca6 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -354,6 +354,8 @@ format initializer_list
 format limits
 format locale
 format optional
+format queue
+format stack
 format stdexcept
 format string
 format string_view

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 7ee16527026eb..0d10e239d8f2d 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -356,6 +356,8 @@ format initializer_list
 format limits
 format locale
 format optional
+format queue
+format stack
 format stdexcept
 format string
 format string_view

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 7ee16527026eb..0d10e239d8f2d 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -356,6 +356,8 @@ format initializer_list
 format limits
 format locale
 format optional
+format queue
+format stack
 format stdexcept
 format string
 format string_view

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index f48e46896b7ea..8f47b6120f7a8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -365,6 +365,8 @@ format initializer_list
 format limits
 format locale
 format optional
+format queue
+format stack
 format stdexcept
 format string
 format string_view

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx2b.csv b/libcxx/test/libcxx/transitive_includes/cxx2b.csv
index 7401de433dbca..186d0b4f5d121 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx2b.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx2b.csv
@@ -273,6 +273,8 @@ format initializer_list
 format limits
 format locale
 format optional
+format queue
+format stack
 format stdexcept
 format string
 format string_view

diff  --git a/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.format.pass.cpp b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.format.pass.cpp
new file mode 100644
index 0000000000000..7806576baae5e
--- /dev/null
+++ b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.format.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
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+
+// TODO FMT Fix this test using GCC, it currently times out.
+// UNSUPPORTED: gcc-12
+
+// This test requires the dylib support introduced in D92214.
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
+
+// [container.adaptors.format]
+// For each of queue, priority_queue, and stack, the library provides the
+// following formatter specialization where adaptor-type is the name of the
+// template:
+//
+// template<class charT, class T, formattable<charT> Container, class... U>
+//   struct formatter<adaptor-type<T, Container, U...>, charT>
+
+// template<class... Args>
+//   string format(format_string<Args...> fmt, Args&&... args);
+// template<class... Args>
+//   wstring format(wformat_string<Args...> fmt, Args&&... args);
+
+#include <format>
+#include <cassert>
+
+#include "format.functions.tests.h"
+#include "test_format_string.h"
+#include "test_macros.h"
+#include "assert_macros.h"
+
+auto test = []<class CharT, class... Args>(
+                std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
+  std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
+  TEST_REQUIRE(
+      out == expected,
+      test_concat_message("\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+};
+
+auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, Args&&...) {
+  // After P2216 most exceptions thrown by std::format become ill-formed.
+  // Therefore this tests does nothing.
+};
+
+int main(int, char**) {
+  format_tests<char>(test, test_exception);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  format_tests<wchar_t>(test, test_exception);
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h
new file mode 100644
index 0000000000000..cae566d9aecf5
--- /dev/null
+++ b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h
@@ -0,0 +1,947 @@
+//===----------------------------------------------------------------------===//
+// 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_CONTAINERS_CONTAINER_ADAPTORS_CONTAINER_ADAPTORS_FORMAT_FORMAT_FUNCTIONS_TESTS_H
+#define TEST_STD_CONTAINERS_CONTAINER_ADAPTORS_CONTAINER_ADAPTORS_FORMAT_FORMAT_FUNCTIONS_TESTS_H
+
+#include <algorithm>
+#include <array>
+#include <charconv>
+#include <concepts>
+#include <format>
+#include <list>
+#include <queue>
+#include <stack>
+
+#include "format.functions.common.h"
+#include "make_string.h"
+#include "platform_support.h" // locale name macros
+#include "test_macros.h"
+
+//
+// Char
+//
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_char_default(TestFunction check, ExceptionTest check_exception, auto&& input) {
+  // Note when no range-underlying-spec is present the char is escaped,
+  check(SV("['H', 'e', 'l', 'l', 'o']"), SV("{}"), input);
+
+  // when one is present there is no escaping,
+  check(SV("[H, e, l, l, o]"), SV("{::}"), input);
+  // unless forced by the type specifier.
+  check(SV("['H', 'e', 'l', 'l', 'o']"), SV("{::?}"), input);
+
+  // ***** underlying has no format-spec
+
+  // *** align-fill & width ***
+  check(SV("['H', 'e', 'l', 'l', 'o']     "), SV("{:30}"), input);
+  check(SV("['H', 'e', 'l', 'l', 'o']*****"), SV("{:*<30}"), input);
+  check(SV("__['H', 'e', 'l', 'l', 'o']___"), SV("{:_^30}"), input);
+  check(SV("#####['H', 'e', 'l', 'l', 'o']"), SV("{:#>30}"), input);
+
+  check(SV("['H', 'e', 'l', 'l', 'o']     "), SV("{:{}}"), input, 30);
+  check(SV("['H', 'e', 'l', 'l', 'o']*****"), SV("{:*<{}}"), input, 30);
+  check(SV("__['H', 'e', 'l', 'l', 'o']___"), SV("{:_^{}}"), input, 30);
+  check(SV("#####['H', 'e', 'l', 'l', 'o']"), SV("{:#>{}}"), input, 30);
+
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input);
+
+  // *** sign ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input);
+
+  // *** alternate form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
+
+  // *** zero-padding ***
+  check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input);
+
+  // *** n
+  check(SV("__'H', 'e', 'l', 'l', 'o'___"), SV("{:_^28n}"), input);
+
+  // *** type ***
+  check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input);
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>("s"))
+    check_exception("The format-spec should consume the input or end with a '}'", fmt, input);
+
+  // ***** Only underlying has a format-spec
+  check(SV("[H   , e   , l   , l   , o   ]"), SV("{::4}"), input);
+  check(SV("[H***, e***, l***, l***, o***]"), SV("{::*<4}"), input);
+  check(SV("[_H__, _e__, _l__, _l__, _o__]"), SV("{::_^4}"), input);
+  check(SV("[:::H, :::e, :::l, :::l, :::o]"), SV("{:::>4}"), input);
+
+  check(SV("[H   , e   , l   , l   , o   ]"), SV("{::{}}"), input, 4);
+  check(SV("[H***, e***, l***, l***, o***]"), SV("{::*<{}}"), input, 4);
+  check(SV("[_H__, _e__, _l__, _l__, _o__]"), SV("{::_^{}}"), input, 4);
+  check(SV("[:::H, :::e, :::l, :::l, :::o]"), SV("{:::>{}}"), input, 4);
+
+  check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input);
+  check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input);
+
+  // *** sign ***
+  check_exception("A sign field isn't allowed in this format-spec", SV("{::-}"), input);
+  check_exception("A sign field isn't allowed in this format-spec", SV("{::+}"), input);
+  check_exception("A sign field isn't allowed in this format-spec", SV("{:: }"), input);
+
+  check(SV("[72, 101, 108, 108, 111]"), SV("{::-d}"), input);
+  check(SV("[+72, +101, +108, +108, +111]"), SV("{::+d}"), input);
+  check(SV("[ 72,  101,  108,  108,  111]"), SV("{:: d}"), input);
+
+  // *** alternate form ***
+  check_exception("An alternate form field isn't allowed in this format-spec", SV("{::#}"), input);
+
+  check(SV("[0x48, 0x65, 0x6c, 0x6c, 0x6f]"), SV("{::#x}"), input);
+
+  // *** zero-padding ***
+  check_exception("A zero-padding field isn't allowed in this format-spec", SV("{::05}"), input);
+
+  check(SV("[00110, 00145, 00154, 00154, 00157]"), SV("{::05o}"), input);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input);
+
+  // *** locale-specific form ***
+  check(SV("[H, e, l, l, o]"), SV("{::L}"), input);
+
+  // *** type ***
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_nested_types<CharT>("bBcdoxX?"))
+    check_exception("The format-spec type has a type not supported for a char argument", fmt, input);
+
+  // ***** Both have a format-spec
+  check(SV("^^[:H, :e, :l, :l, :o]^^^"), SV("{:^^25::>2}"), input);
+  check(SV("^^[:H, :e, :l, :l, :o]^^^"), SV("{:^^{}::>2}"), input, 25);
+  check(SV("^^[:H, :e, :l, :l, :o]^^^"), SV("{:^^{}::>{}}"), input, 25, 2);
+
+  check_exception("Argument index out of bounds", SV("{:^^{}::>2}"), input);
+  check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 25);
+}
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_char_string(TestFunction check, ExceptionTest check_exception, auto&& input) {
+  check(SV("Hello"), SV("{:s}"), input);
+
+  // ***** underlying has no format-spec
+
+  // *** align-fill & width ***
+  check(SV("Hello   "), SV("{:8s}"), input);
+  check(SV("Hello***"), SV("{:*<8s}"), input);
+  check(SV("_Hello__"), SV("{:_^8s}"), input);
+  check(SV("###Hello"), SV("{:#>8s}"), input);
+
+  check(SV("Hello   "), SV("{:{}s}"), input, 8);
+  check(SV("Hello***"), SV("{:*<{}s}"), input, 8);
+  check(SV("_Hello__"), SV("{:_^{}s}"), input, 8);
+  check(SV("###Hello"), SV("{:#>{}s}"), input, 8);
+
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<s}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<s}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{::<s}"), input);
+
+  // *** sign ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:-s}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:+s}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{: s}"), input);
+
+  // *** alternate form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:#s}"), input);
+
+  // *** zero-padding ***
+  check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0s}"), input);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:.s}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:Ls}"), input);
+
+  // *** n
+  check_exception("The n option and type s can't be used together", SV("{:ns}"), input);
+
+  // *** type ***
+  check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input);
+
+  // ***** Only underlying has a format-spec
+  check_exception("Type s and an underlying format specification can't be used together", SV("{:s:}"), input);
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_nested_types<CharT>("bBcdoxX?"))
+    check_exception("The format-spec type has a type not supported for a char argument", fmt, input);
+
+  // ***** Both have a format-spec
+  check_exception("Type s and an underlying format specification can't be used together", SV("{:5s:5}"), input);
+}
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_char_escaped_string(TestFunction check, ExceptionTest check_exception, auto&& input) {
+  check(SV(R"("Hello")"), SV("{:?s}"), input);
+
+  // ***** underlying has no format-spec
+
+  // *** align-fill & width ***
+  check(SV(R"("Hello"   )"), SV("{:10?s}"), input);
+  check(SV(R"("Hello"***)"), SV("{:*<10?s}"), input);
+  check(SV(R"(_"Hello"__)"), SV("{:_^10?s}"), input);
+  check(SV(R"(###"Hello")"), SV("{:#>10?s}"), input);
+
+  check(SV(R"("Hello"   )"), SV("{:{}?s}"), input, 10);
+  check(SV(R"("Hello"***)"), SV("{:*<{}?s}"), input, 10);
+  check(SV(R"(_"Hello"__)"), SV("{:_^{}?s}"), input, 10);
+  check(SV(R"(###"Hello")"), SV("{:#>{}?s}"), input, 10);
+
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<?s}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<?s}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{::<?s}"), input);
+
+  // *** sign ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:-?s}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:+?s}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{: ?s}"), input);
+
+  // *** alternate form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:#?s}"), input);
+
+  // *** zero-padding ***
+  check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0?s}"), input);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:.?s}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:L?s}"), input);
+
+  // *** n
+  check_exception("The n option and type ?s can't be used together", SV("{:n?s}"), input);
+
+  // *** type ***
+  check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input);
+
+  // ***** Only underlying has a format-spec
+  check_exception("Type ?s and an underlying format specification can't be used together", SV("{:?s:}"), input);
+
+  // ***** Both have a format-spec
+  check_exception("Type ?s and an underlying format specification can't be used together", SV("{:5?s:5}"), input);
+}
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_char(TestFunction check, ExceptionTest check_exception) {
+  // These values are in numeric order when using ASCII, which is used by the priority_queue.
+  std::array input{CharT('H'), CharT('e'), CharT('l'), CharT('l'), CharT('o')};
+  test_char_default<CharT>(check, check_exception, std::queue{input.begin(), input.end()});
+  test_char_default<CharT>(check, check_exception, std::priority_queue{input.begin(), input.end(), std::greater{}});
+  test_char_default<CharT>(check, check_exception, std::stack{input.begin(), input.end()});
+
+  test_char_string<CharT>(check, check_exception, std::queue{input.begin(), input.end()});
+  test_char_string<CharT>(check, check_exception, std::priority_queue{input.begin(), input.end(), std::greater{}});
+  test_char_string<CharT>(check, check_exception, std::stack{input.begin(), input.end()});
+
+  test_char_escaped_string<CharT>(check, check_exception, std::queue{input.begin(), input.end()});
+  test_char_escaped_string<CharT>(
+      check, check_exception, std::priority_queue{input.begin(), input.end(), std::greater{}});
+  test_char_escaped_string<CharT>(check, check_exception, std::stack{input.begin(), input.end()});
+}
+
+//
+// char -> wchar_t
+//
+
+#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+template <class TestFunction, class ExceptionTest>
+void test_char_to_wchar(TestFunction check, ExceptionTest check_exception) {
+  std::array input{'H', 'e', 'l', 'l', 'o'};
+  test_char_default<wchar_t>(check, check_exception, std::queue{input.begin(), input.end()});
+  test_char_default<wchar_t>(check, check_exception, std::priority_queue{input.begin(), input.end(), std::greater{}});
+  test_char_default<wchar_t>(check, check_exception, std::stack{input.begin(), input.end()});
+
+  // The types s and ?s may only be used when using range_formatter<T, charT>
+  // where the types T and charT are the same. This means this can't be used for
+  // range_formatter<wchar_t, char> even when formatter<wchar_t, char> has a
+  // debug-enabled specialization.
+
+  using CharT = wchar_t;
+  check_exception("The range-format-spec type s requires formatting a character type",
+                  SV("{:s}"),
+                  std::queue{input.begin(), input.end()});
+  check_exception("The range-format-spec type s requires formatting a character type",
+                  SV("{:s}"),
+                  std::priority_queue{input.begin(), input.end()});
+  check_exception("The range-format-spec type s requires formatting a character type",
+                  SV("{:s}"),
+                  std::stack{input.begin(), input.end()});
+  check_exception("The range-format-spec type ?s requires formatting a character type",
+                  SV("{:?s}"),
+                  std::queue{input.begin(), input.end()});
+  check_exception("The range-format-spec type ?s requires formatting a character type",
+                  SV("{:?s}"),
+                  std::priority_queue{input.begin(), input.end()});
+  check_exception("The range-format-spec type ?s requires formatting a character type",
+                  SV("{:?s}"),
+                  std::stack{input.begin(), input.end()});
+}
+#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
+
+//
+// Bool
+//
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_bool(TestFunction check, ExceptionTest check_exception, auto&& input) {
+  check(SV("[true, true, false]"), SV("{}"), input);
+
+  // ***** underlying has no format-spec
+
+  // *** align-fill & width ***
+  check(SV("[true, true, false]     "), SV("{:24}"), input);
+  check(SV("[true, true, false]*****"), SV("{:*<24}"), input);
+  check(SV("__[true, true, false]___"), SV("{:_^24}"), input);
+  check(SV("#####[true, true, false]"), SV("{:#>24}"), input);
+
+  check(SV("[true, true, false]     "), SV("{:{}}"), input, 24);
+  check(SV("[true, true, false]*****"), SV("{:*<{}}"), input, 24);
+  check(SV("__[true, true, false]___"), SV("{:_^{}}"), input, 24);
+  check(SV("#####[true, true, false]"), SV("{:#>{}}"), input, 24);
+
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input);
+
+  // *** sign ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input);
+
+  // *** alternate form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
+
+  // *** zero-padding ***
+  check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input);
+
+  // *** n
+  check(SV("__true, true, false___"), SV("{:_^22n}"), input);
+
+  // *** type ***
+  check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input);
+  check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input);
+  check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input);
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>("s"))
+    check_exception("The format-spec should consume the input or end with a '}'", fmt, input);
+
+  // ***** Only underlying has a format-spec
+  check(SV("[true   , true   , false  ]"), SV("{::7}"), input);
+  check(SV("[true***, true***, false**]"), SV("{::*<7}"), input);
+  check(SV("[_true__, _true__, _false_]"), SV("{::_^7}"), input);
+  check(SV("[:::true, :::true, ::false]"), SV("{:::>7}"), input);
+
+  check(SV("[true   , true   , false  ]"), SV("{::{}}"), input, 7);
+  check(SV("[true***, true***, false**]"), SV("{::*<{}}"), input, 7);
+  check(SV("[_true__, _true__, _false_]"), SV("{::_^{}}"), input, 7);
+  check(SV("[:::true, :::true, ::false]"), SV("{:::>{}}"), input, 7);
+
+  check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input);
+  check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input);
+
+  // *** sign ***
+  check_exception("A sign field isn't allowed in this format-spec", SV("{::-}"), input);
+  check_exception("A sign field isn't allowed in this format-spec", SV("{::+}"), input);
+  check_exception("A sign field isn't allowed in this format-spec", SV("{:: }"), input);
+
+  check(SV("[1, 1, 0]"), SV("{::-d}"), input);
+  check(SV("[+1, +1, +0]"), SV("{::+d}"), input);
+  check(SV("[ 1,  1,  0]"), SV("{:: d}"), input);
+
+  // *** alternate form ***
+  check_exception("An alternate form field isn't allowed in this format-spec", SV("{::#}"), input);
+
+  check(SV("[0x1, 0x1, 0x0]"), SV("{::#x}"), input);
+
+  // *** zero-padding ***
+  check_exception("A zero-padding field isn't allowed in this format-spec", SV("{::05}"), input);
+
+  check(SV("[00001, 00001, 00000]"), SV("{::05o}"), input);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input);
+
+  // *** locale-specific form ***
+  check(SV("[true, true, false]"), SV("{::L}"), input);
+
+  // *** type ***
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_nested_types<CharT>("bBdosxX"))
+    check_exception("The format-spec type has a type not supported for a bool argument", fmt, input);
+
+  // ***** Both have a format-spec
+  check(SV("^^[:::true, :::true, ::false]^^^"), SV("{:^^32::>7}"), input);
+  check(SV("^^[:::true, :::true, ::false]^^^"), SV("{:^^{}::>7}"), input, 32);
+  check(SV("^^[:::true, :::true, ::false]^^^"), SV("{:^^{}::>{}}"), input, 32, 7);
+
+  check_exception("Argument index out of bounds", SV("{:^^{}::>5}"), input);
+  check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 32);
+}
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_bool(TestFunction check, ExceptionTest check_exception) {
+  std::array input{true, true, false};
+  test_bool<CharT>(check, check_exception, std::queue{input.begin(), input.end()});
+  // TODO FMT Use std::vector<bool> after it has been implemented.
+  test_bool<CharT>(check, check_exception, std::priority_queue<bool, std::deque<bool>>{input.begin(), input.end()});
+  test_bool<CharT>(check, check_exception, std::stack{input.begin(), input.end()});
+}
+
+//
+// Integral
+//
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_int(TestFunction check, ExceptionTest check_exception, auto&& input) {
+  check(SV("[-42, 1, 2, 42]"), SV("{}"), input);
+
+  // ***** underlying has no format-spec
+
+  // *** align-fill & width ***
+  check(SV("[-42, 1, 2, 42]     "), SV("{:20}"), input);
+  check(SV("[-42, 1, 2, 42]*****"), SV("{:*<20}"), input);
+  check(SV("__[-42, 1, 2, 42]___"), SV("{:_^20}"), input);
+  check(SV("#####[-42, 1, 2, 42]"), SV("{:#>20}"), input);
+
+  check(SV("[-42, 1, 2, 42]     "), SV("{:{}}"), input, 20);
+  check(SV("[-42, 1, 2, 42]*****"), SV("{:*<{}}"), input, 20);
+  check(SV("__[-42, 1, 2, 42]___"), SV("{:_^{}}"), input, 20);
+  check(SV("#####[-42, 1, 2, 42]"), SV("{:#>{}}"), input, 20);
+
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input);
+
+  // *** sign ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input);
+
+  // *** alternate form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
+
+  // *** zero-padding ***
+  check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input);
+
+  // *** n
+  check(SV("__-42, 1, 2, 42___"), SV("{:_^18n}"), input);
+
+  // *** type ***
+  check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input);
+  check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input);
+  check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input);
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>("s"))
+    check_exception("The format-spec should consume the input or end with a '}'", fmt, input);
+
+  // ***** Only underlying has a format-spec
+  check(SV("[  -42,     1,     2,    42]"), SV("{::5}"), input);
+  check(SV("[-42**, 1****, 2****, 42***]"), SV("{::*<5}"), input);
+  check(SV("[_-42_, __1__, __2__, _42__]"), SV("{::_^5}"), input);
+  check(SV("[::-42, ::::1, ::::2, :::42]"), SV("{:::>5}"), input);
+
+  check(SV("[  -42,     1,     2,    42]"), SV("{::{}}"), input, 5);
+  check(SV("[-42**, 1****, 2****, 42***]"), SV("{::*<{}}"), input, 5);
+  check(SV("[_-42_, __1__, __2__, _42__]"), SV("{::_^{}}"), input, 5);
+  check(SV("[::-42, ::::1, ::::2, :::42]"), SV("{:::>{}}"), input, 5);
+
+  check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input);
+  check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input);
+
+  // *** sign ***
+  check(SV("[-42, 1, 2, 42]"), SV("{::-}"), input);
+  check(SV("[-42, +1, +2, +42]"), SV("{::+}"), input);
+  check(SV("[-42,  1,  2,  42]"), SV("{:: }"), input);
+
+  // *** alternate form ***
+  check(SV("[-0x2a, 0x1, 0x2, 0x2a]"), SV("{::#x}"), input);
+
+  // *** zero-padding ***
+  check(SV("[-0042, 00001, 00002, 00042]"), SV("{::05}"), input);
+  check(SV("[-002a, 00001, 00002, 0002a]"), SV("{::05x}"), input);
+  check(SV("[-0x2a, 0x001, 0x002, 0x02a]"), SV("{::#05x}"), input);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input);
+
+  // *** locale-specific form ***
+  check(SV("[-42, 1, 2, 42]"), SV("{::L}"), input); // does nothing in this test, but is accepted.
+
+  // *** type ***
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_nested_types<CharT>("bBcdoxX"))
+    check_exception("The format-spec type has a type not supported for an integer argument", fmt, input);
+
+  // ***** Both have a format-spec
+  check(SV("^^[::-42, ::::1, ::::2, :::42]^^^"), SV("{:^^33::>5}"), input);
+  check(SV("^^[::-42, ::::1, ::::2, :::42]^^^"), SV("{:^^{}::>5}"), input, 33);
+  check(SV("^^[::-42, ::::1, ::::2, :::42]^^^"), SV("{:^^{}::>{}}"), input, 33, 5);
+
+  check_exception("Argument index out of bounds", SV("{:^^{}::>5}"), input);
+  check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 33);
+}
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_int(TestFunction check, ExceptionTest check_exception) {
+  std::array input{-42, 1, 2, 42};
+  test_int<CharT>(check, check_exception, std::queue{input.begin(), input.end()});
+  test_int<CharT>(check, check_exception, std::priority_queue{input.begin(), input.end(), std::greater{}});
+  test_int<CharT>(check, check_exception, std::stack{input.begin(), input.end()});
+}
+
+//
+// Floating point
+//
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_floating_point(TestFunction check, ExceptionTest check_exception, auto&& input) {
+  check(SV("[-42.5, 0, 1.25, 42.5]"), SV("{}"), input);
+
+  // ***** underlying has no format-spec
+
+  // *** align-fill & width ***
+  check(SV("[-42.5, 0, 1.25, 42.5]     "), SV("{:27}"), input);
+  check(SV("[-42.5, 0, 1.25, 42.5]*****"), SV("{:*<27}"), input);
+  check(SV("__[-42.5, 0, 1.25, 42.5]___"), SV("{:_^27}"), input);
+  check(SV("#####[-42.5, 0, 1.25, 42.5]"), SV("{:#>27}"), input);
+
+  check(SV("[-42.5, 0, 1.25, 42.5]     "), SV("{:{}}"), input, 27);
+  check(SV("[-42.5, 0, 1.25, 42.5]*****"), SV("{:*<{}}"), input, 27);
+  check(SV("__[-42.5, 0, 1.25, 42.5]___"), SV("{:_^{}}"), input, 27);
+  check(SV("#####[-42.5, 0, 1.25, 42.5]"), SV("{:#>{}}"), input, 27);
+
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input);
+
+  // *** sign ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input);
+
+  // *** alternate form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
+
+  // *** zero-padding ***
+  check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input);
+
+  // *** n
+  check(SV("__-42.5, 0, 1.25, 42.5___"), SV("{:_^25n}"), input);
+
+  // *** type ***
+  check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input);
+  check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input);
+  check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input);
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>("s"))
+    check_exception("The format-spec should consume the input or end with a '}'", fmt, input);
+
+  // ***** Only underlying has a format-spec
+  check(SV("[-42.5,     0,  1.25,  42.5]"), SV("{::5}"), input);
+  check(SV("[-42.5, 0****, 1.25*, 42.5*]"), SV("{::*<5}"), input);
+  check(SV("[-42.5, __0__, 1.25_, 42.5_]"), SV("{::_^5}"), input);
+  check(SV("[-42.5, ::::0, :1.25, :42.5]"), SV("{:::>5}"), input);
+
+  check(SV("[-42.5,     0,  1.25,  42.5]"), SV("{::{}}"), input, 5);
+  check(SV("[-42.5, 0****, 1.25*, 42.5*]"), SV("{::*<{}}"), input, 5);
+  check(SV("[-42.5, __0__, 1.25_, 42.5_]"), SV("{::_^{}}"), input, 5);
+  check(SV("[-42.5, ::::0, :1.25, :42.5]"), SV("{:::>{}}"), input, 5);
+
+  check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input);
+  check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input);
+
+  // *** sign ***
+  check(SV("[-42.5, 0, 1.25, 42.5]"), SV("{::-}"), input);
+  check(SV("[-42.5, +0, +1.25, +42.5]"), SV("{::+}"), input);
+  check(SV("[-42.5,  0,  1.25,  42.5]"), SV("{:: }"), input);
+
+  // *** alternate form ***
+  check(SV("[-42.5, 0., 1.25, 42.5]"), SV("{::#}"), input);
+
+  // *** zero-padding ***
+  check(SV("[-42.5, 00000, 01.25, 042.5]"), SV("{::05}"), input);
+  check(SV("[-42.5, 0000., 01.25, 042.5]"), SV("{::#05}"), input);
+
+  // *** precision ***
+  check(SV("[-42, 0, 1.2, 42]"), SV("{::.2}"), input);
+  check(SV("[-42.500, 0.000, 1.250, 42.500]"), SV("{::.3f}"), input);
+
+  check(SV("[-42, 0, 1.2, 42]"), SV("{::.{}}"), input, 2);
+  check(SV("[-42.500, 0.000, 1.250, 42.500]"), SV("{::.{}f}"), input, 3);
+
+  check_exception("The format-spec precision field doesn't contain a value or arg-id", SV("{::.}"), input);
+
+  // *** locale-specific form ***
+  check(SV("[-42.5, 0, 1.25, 42.5]"), SV("{::L}"), input); // does not require locales present
+#ifndef TEST_HAS_NO_LOCALIZATION
+  std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+  check(SV("[-42,5, 0, 1,25, 42,5]"), SV("{::L}"), input);
+
+  std::locale::global(std::locale(LOCALE_en_US_UTF_8));
+  check(SV("[-42.5, 0, 1.25, 42.5]"), SV("{::L}"), input);
+
+  std::locale::global(std::locale::classic());
+#endif // TEST_HAS_NO_LOCALIZATION
+
+  // *** type ***
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_nested_types<CharT>("aAeEfFgG"))
+    check_exception("The format-spec type has a type not supported for a floating-point argument", fmt, input);
+
+  // ***** Both have a format-spec
+  check(SV("^^[-42.5, ::::0, :1.25, :42.5]^^^"), SV("{:^^33::>5}"), input);
+  check(SV("^^[-42.5, ::::0, :1.25, :42.5]^^^"), SV("{:^^{}::>5}"), input, 33);
+  check(SV("^^[-42.5, ::::0, :1.25, :42.5]^^^"), SV("{:^^{}::>{}}"), input, 33, 5);
+
+  check(SV("^^[::-42, ::::0, ::1.2, :::42]^^^"), SV("{:^^33::>5.2}"), input);
+  check(SV("^^[::-42, ::::0, ::1.2, :::42]^^^"), SV("{:^^{}::>5.2}"), input, 33);
+  check(SV("^^[::-42, ::::0, ::1.2, :::42]^^^"), SV("{:^^{}::>{}.2}"), input, 33, 5);
+  check(SV("^^[::-42, ::::0, ::1.2, :::42]^^^"), SV("{:^^{}::>{}.{}}"), input, 33, 5, 2);
+
+  check_exception("Argument index out of bounds", SV("{:^^{}::>5.2}"), input);
+  check_exception("Argument index out of bounds", SV("{:^^{}::>{}.2}"), input, 33);
+  check_exception("Argument index out of bounds", SV("{:^^{}::>{}.{}}"), input, 33, 5);
+}
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_floating_point(TestFunction check, ExceptionTest check_exception) {
+  std::array input{-42.5l, 0.0l, 1.25l, 42.5l};
+  test_floating_point<CharT>(check, check_exception, std::queue{input.begin(), input.end()});
+  test_floating_point<CharT>(check, check_exception, std::priority_queue{input.begin(), input.end(), std::greater{}});
+  test_floating_point<CharT>(check, check_exception, std::stack{input.begin(), input.end()});
+}
+
+//
+// Pointer
+//
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_pointer(TestFunction check, ExceptionTest check_exception, auto&& input) {
+  check(SV("[0x0]"), SV("{}"), input);
+
+  // ***** underlying has no format-spec
+
+  // *** align-fill & width ***
+  check(SV("[0x0]     "), SV("{:10}"), input);
+  check(SV("[0x0]*****"), SV("{:*<10}"), input);
+  check(SV("__[0x0]___"), SV("{:_^10}"), input);
+  check(SV("#####[0x0]"), SV("{:#>10}"), input);
+
+  check(SV("[0x0]     "), SV("{:{}}"), input, 10);
+  check(SV("[0x0]*****"), SV("{:*<{}}"), input, 10);
+  check(SV("__[0x0]___"), SV("{:_^{}}"), input, 10);
+  check(SV("#####[0x0]"), SV("{:#>{}}"), input, 10);
+
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input);
+
+  // *** sign ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
+
+  // *** alternate form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
+
+  // *** zero-padding ***
+  check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input);
+
+  // *** n
+  check(SV("_0x0_"), SV("{:_^5n}"), input);
+
+  // *** type ***
+  check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input);
+  check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input);
+  check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input);
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>("s"))
+    check_exception("The format-spec should consume the input or end with a '}'", fmt, input);
+
+  // ***** Only underlying has a format-spec
+  check(SV("[  0x0]"), SV("{::5}"), input);
+  check(SV("[0x0**]"), SV("{::*<5}"), input);
+  check(SV("[_0x0_]"), SV("{::_^5}"), input);
+  check(SV("[::0x0]"), SV("{:::>5}"), input);
+
+  check(SV("[  0x0]"), SV("{::{}}"), input, 5);
+  check(SV("[0x0**]"), SV("{::*<{}}"), input, 5);
+  check(SV("[_0x0_]"), SV("{::_^{}}"), input, 5);
+  check(SV("[::0x0]"), SV("{:::>{}}"), input, 5);
+
+  check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input);
+  check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input);
+
+  // *** sign ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{::-}"), input);
+
+  // *** alternate form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{::#}"), input);
+
+  // *** zero-padding ***
+  check_exception("A format-spec width field shouldn't have a leading zero", SV("{::05}"), input);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{::L}"), input);
+
+  // *** type ***
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_nested_types<CharT>("p"))
+    check_exception("The format-spec type has a type not supported for a pointer argument", fmt, input);
+
+  // ***** Both have a format-spec
+  check(SV("^^[::0x0]^^^"), SV("{:^^12::>5}"), input);
+  check(SV("^^[::0x0]^^^"), SV("{:^^{}::>5}"), input, 12);
+  check(SV("^^[::0x0]^^^"), SV("{:^^{}::>{}}"), input, 12, 5);
+
+  check(SV("^^[::0x0]^^^"), SV("{:^^12::>5}"), input);
+  check(SV("^^[::0x0]^^^"), SV("{:^^{}::>5}"), input, 12);
+  check(SV("^^[::0x0]^^^"), SV("{:^^{}::>{}}"), input, 12, 5);
+
+  check_exception("Argument index out of bounds", SV("{:^^{}::>5}"), input);
+  check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 12);
+}
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_pointer(TestFunction check, ExceptionTest check_exception) {
+  std::array input{static_cast<void*>(0)};
+  test_pointer<CharT>(check, check_exception, std::queue{input.begin(), input.end()});
+  test_pointer<CharT>(check, check_exception, std::priority_queue{input.begin(), input.end()});
+  test_pointer<CharT>(check, check_exception, std::stack{input.begin(), input.end()});
+}
+
+//
+// String
+//
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_string(TestFunction check, ExceptionTest check_exception, auto&& input) {
+  check(SV(R"(["Hello", "world"])"), SV("{}"), input);
+
+  // ***** underlying has no format-spec
+
+  // *** align-fill & width ***
+  check(SV(R"(["Hello", "world"]     )"), SV("{:23}"), input);
+  check(SV(R"(["Hello", "world"]*****)"), SV("{:*<23}"), input);
+  check(SV(R"(__["Hello", "world"]___)"), SV("{:_^23}"), input);
+  check(SV(R"(#####["Hello", "world"])"), SV("{:#>23}"), input);
+
+  check(SV(R"(["Hello", "world"]     )"), SV("{:{}}"), input, 23);
+  check(SV(R"(["Hello", "world"]*****)"), SV("{:*<{}}"), input, 23);
+  check(SV(R"(__["Hello", "world"]___)"), SV("{:_^{}}"), input, 23);
+  check(SV(R"(#####["Hello", "world"])"), SV("{:#>{}}"), input, 23);
+
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input);
+
+  // *** sign ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
+
+  // *** alternate form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
+
+  // *** zero-padding ***
+  check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input);
+
+  // *** n
+  check(SV(R"(_"Hello", "world"_)"), SV("{:_^18n}"), input);
+
+  // *** type ***
+  check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input);
+  check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input);
+  check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input);
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>("s"))
+    check_exception("The format-spec should consume the input or end with a '}'", fmt, input);
+
+  // ***** Only underlying has a format-spec
+  check(SV(R"([Hello   , world   ])"), SV("{::8}"), input);
+  check(SV(R"([Hello***, world***])"), SV("{::*<8}"), input);
+  check(SV(R"([_Hello__, _world__])"), SV("{::_^8}"), input);
+  check(SV(R"([:::Hello, :::world])"), SV("{:::>8}"), input);
+
+  check(SV(R"([Hello   , world   ])"), SV("{::{}}"), input, 8);
+  check(SV(R"([Hello***, world***])"), SV("{::*<{}}"), input, 8);
+  check(SV(R"([_Hello__, _world__])"), SV("{::_^{}}"), input, 8);
+  check(SV(R"([:::Hello, :::world])"), SV("{:::>{}}"), input, 8);
+
+  check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input);
+  check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input);
+
+  // *** sign ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{::-}"), input);
+
+  // *** alternate form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{::#}"), input);
+
+  // *** zero-padding ***
+  check_exception("A format-spec width field shouldn't have a leading zero", SV("{::05}"), input);
+
+  // *** precision ***
+  check(SV(R"([Hel, wor])"), SV("{::.3}"), input);
+
+  check(SV(R"([Hel, wor])"), SV("{::.{}}"), input, 3);
+
+  check_exception("The format-spec precision field doesn't contain a value or arg-id", SV("{::.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{::L}"), input);
+
+  // *** type ***
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_nested_types<CharT>("s?"))
+    check_exception("The format-spec type has a type not supported for a string argument", fmt, input);
+
+  // ***** Both have a format-spec
+  check(SV(R"(^^[:::Hello, :::world]^^^)"), SV("{:^^25::>8}"), input);
+  check(SV(R"(^^[:::Hello, :::world]^^^)"), SV("{:^^{}::>8}"), input, 25);
+  check(SV(R"(^^[:::Hello, :::world]^^^)"), SV("{:^^{}::>{}}"), input, 25, 8);
+
+  check(SV(R"(^^[:::Hello, :::world]^^^)"), SV("{:^^25::>8}"), input);
+  check(SV(R"(^^[:::Hello, :::world]^^^)"), SV("{:^^{}::>8}"), input, 25);
+  check(SV(R"(^^[:::Hello, :::world]^^^)"), SV("{:^^{}::>{}}"), input, 25, 8);
+
+  check_exception("Argument index out of bounds", SV("{:^^{}::>8}"), input);
+  check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 25);
+}
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_string(TestFunction check, ExceptionTest check_exception) {
+  std::array input{STR("Hello"), STR("world")};
+  test_string<CharT>(check, check_exception, std::queue{input.begin(), input.end()});
+  test_string<CharT>(check, check_exception, std::priority_queue{input.begin(), input.end(), std::greater{}});
+  test_string<CharT>(check, check_exception, std::stack{input.begin(), input.end()});
+}
+
+//
+// Handle
+//
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_status(TestFunction check, ExceptionTest check_exception, auto&& input) {
+  check(SV("[0xaaaa, 0x5555, 0xaa55]"), SV("{}"), input);
+
+  // ***** underlying has no format-spec
+
+  // *** align-fill & width ***
+  check(SV("[0xaaaa, 0x5555, 0xaa55]     "), SV("{:29}"), input);
+  check(SV("[0xaaaa, 0x5555, 0xaa55]*****"), SV("{:*<29}"), input);
+  check(SV("__[0xaaaa, 0x5555, 0xaa55]___"), SV("{:_^29}"), input);
+  check(SV("#####[0xaaaa, 0x5555, 0xaa55]"), SV("{:#>29}"), input);
+
+  check(SV("[0xaaaa, 0x5555, 0xaa55]     "), SV("{:{}}"), input, 29);
+  check(SV("[0xaaaa, 0x5555, 0xaa55]*****"), SV("{:*<{}}"), input, 29);
+  check(SV("__[0xaaaa, 0x5555, 0xaa55]___"), SV("{:_^{}}"), input, 29);
+  check(SV("#####[0xaaaa, 0x5555, 0xaa55]"), SV("{:#>{}}"), input, 29);
+
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
+  check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input);
+
+  // *** sign ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input);
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input);
+
+  // *** alternate form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
+
+  // *** zero-padding ***
+  check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input);
+
+  // *** n
+  check(SV("__0xaaaa, 0x5555, 0xaa55___"), SV("{:_^27n}"), input);
+
+  // *** type ***
+  check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input);
+  check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input);
+  check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input);
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>("s"))
+    check_exception("The format-spec should consume the input or end with a '}'", fmt, input);
+
+  // ***** Only underlying has a format-spec
+  check_exception("The format-spec type has a type not supported for a status argument", SV("{::*<7}"), input);
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_nested_types<CharT>("sxX"))
+    check_exception("The format-spec type has a type not supported for a status argument", fmt, input);
+
+  check(SV("[0xaaaa, 0x5555, 0xaa55]"), SV("{::x}"), input);
+  check(SV("[0XAAAA, 0X5555, 0XAA55]"), SV("{::X}"), input);
+  check(SV("[foo, bar, foobar]"), SV("{::s}"), input);
+
+  // ***** Both have a format-spec
+  check(SV("^^[0XAAAA, 0X5555, 0XAA55]^^^"), SV("{:^^29:X}"), input);
+  check(SV("^^[0XAAAA, 0X5555, 0XAA55]^^^"), SV("{:^^{}:X}"), input, 29);
+
+  check_exception("Argument index out of bounds", SV("{:^^{}:X}"), input);
+}
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void test_status(TestFunction check, ExceptionTest check_exception) {
+  std::array input{status::foo, status::bar, status::foobar};
+  test_status<CharT>(check, check_exception, std::queue{input.begin(), input.end()});
+  test_status<CharT>(check, check_exception, std::priority_queue{input.begin(), input.end(), std::less{}});
+  test_status<CharT>(check, check_exception, std::stack{input.begin(), input.end()});
+}
+
+//
+// Driver
+//
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void format_tests(TestFunction check, ExceptionTest check_exception) {
+  test_char<CharT>(check, check_exception);
+#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+  if (std::same_as<CharT, wchar_t>) // avoid testing twice
+    test_char_to_wchar(check, check_exception);
+#endif
+  test_bool<CharT>(check, check_exception);
+  test_int<CharT>(check, check_exception);
+  test_floating_point<CharT>(check, check_exception);
+  test_pointer<CharT>(check, check_exception);
+  test_string<CharT>(check, check_exception);
+
+  test_status<CharT>(check, check_exception); // Has its own handler with its own parser
+}
+
+#endif // TEST_STD_CONTAINERS_CONTAINER_ADAPTORS_CONTAINER_ADAPTORS_FORMAT_FORMAT_FUNCTIONS_TESTS_H

diff  --git a/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.vformat.pass.cpp b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.vformat.pass.cpp
new file mode 100644
index 0000000000000..27c52fa2b6368
--- /dev/null
+++ b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.vformat.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+
+// TODO FMT Fix this test using GCC, it currently times out.
+// UNSUPPORTED: gcc-12
+
+// This test requires the dylib support introduced in D92214.
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
+
+// [container.adaptors.format]
+// For each of queue, priority_queue, and stack, the library provides the
+// following formatter specialization where adaptor-type is the name of the
+// template:
+//
+// template<class charT, class T, formattable<charT> Container, class... U>
+//   struct formatter<adaptor-type<T, Container, U...>, charT>
+
+// string vformat(string_view fmt, format_args args);
+// wstring vformat(wstring_view fmt, wformat_args args);
+
+#include <format>
+#include <cassert>
+
+#include "format.functions.tests.h"
+#include "test_macros.h"
+#include "assert_macros.h"
+
+auto test = []<class CharT, class... Args>(
+                std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) {
+  std::basic_string<CharT> out = std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...));
+  TEST_REQUIRE(
+      out == expected,
+      test_concat_message("\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+};
+
+auto test_exception =
+    []<class CharT, class... Args>(
+        [[maybe_unused]] std::string_view what,
+        [[maybe_unused]] std::basic_string_view<CharT> fmt,
+        [[maybe_unused]] Args&&... args) {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+      try {
+        TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...));
+        TEST_FAIL(test_concat_message("\nFormat string   ", fmt, "\nDidn't throw an exception.\n"));
+      } catch (const std::format_error& e) {
+        TEST_LIBCPP_REQUIRE(
+            e.what() == what,
+            test_concat_message(
+                "\nFormat string   ", fmt, "\nExpected exception ", what, "\nActual exception   ", e.what(), '\n'));
+
+        return;
+      }
+      assert(false);
+#endif
+    };
+
+int main(int, char**) {
+  format_tests<char>(test, test_exception);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  format_tests<wchar_t>(test, test_exception);
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.pass.cpp b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.pass.cpp
new file mode 100644
index 0000000000000..901c63a008486
--- /dev/null
+++ b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.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
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+
+// This test requires the dylib support introduced in D92214.
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
+
+// [container.adaptors.format]
+// For each of queue, priority_queue, and stack, the library provides the
+// following formatter specialization where adaptor-type is the name of the
+// template:
+//
+// template<class charT, class T, formattable<charT> Container, class... U>
+//   struct formatter<adaptor-type<T, Container, U...>, charT>
+
+// template<class FormatContext>
+//   typename FormatContext::iterator
+//     format(maybe-const-adaptor& r, FormatContext& ctx) const;
+
+// Note this tests the basics of this function. It's tested in more detail in
+// the format functions test.
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <format>
+#include <queue>
+#include <stack>
+
+#include "test_format_context.h"
+#include "test_macros.h"
+#include "make_string.h"
+
+#define SV(S) MAKE_STRING_VIEW(CharT, S)
+
+template <class StringViewT, class Arg>
+void test_format(StringViewT expected, Arg arg) {
+  using CharT      = typename StringViewT::value_type;
+  using String     = std::basic_string<CharT>;
+  using OutIt      = std::back_insert_iterator<String>;
+  using FormatCtxT = std::basic_format_context<OutIt, CharT>;
+
+  const std::formatter<Arg, CharT> formatter;
+
+  String result;
+  OutIt out             = std::back_inserter(result);
+  FormatCtxT format_ctx = test_format_context_create<OutIt, CharT>(out, std::make_format_args<FormatCtxT>(arg));
+  formatter.format(arg, format_ctx);
+  assert(result == expected);
+}
+
+template <class CharT>
+void test_fmt() {
+  std::array input{1, 42, 99, 0};
+  test_format(SV("[1, 42, 99, 0]"), std::queue<int>{input.begin(), input.end()});
+  test_format(SV("[99, 42, 1, 0]"), std::priority_queue<int>{input.begin(), input.end()});
+  test_format(SV("[1, 42, 99, 0]"), std::stack<int>{input.begin(), input.end()});
+}
+
+void test() {
+  test_fmt<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_fmt<wchar_t>();
+#endif
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/container.adaptors/container.adaptors.format/parse.pass.cpp b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/parse.pass.cpp
new file mode 100644
index 0000000000000..11f08371b5ead
--- /dev/null
+++ b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/parse.pass.cpp
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+// 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
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+
+// This test requires the dylib support introduced in D92214.
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
+
+// [container.adaptors.format]
+// For each of queue, priority_queue, and stack, the library provides the
+// following formatter specialization where adaptor-type is the name of the
+// template:
+//
+// template<class charT, class T, formattable<charT> Container, class... U>
+//   struct formatter<adaptor-type<T, Container, U...>, charT>
+
+// template<class ParseContext>
+//   constexpr typename ParseContext::iterator
+//     parse(ParseContext& ctx);
+
+// Note this tests the basics of this function. It's tested in more detail in
+// the format functions test.
+
+#include <cassert>
+#include <concepts>
+#include <format>
+#include <queue>
+#include <stack>
+
+#include "test_format_context.h"
+#include "test_macros.h"
+#include "make_string.h"
+
+#define SV(S) MAKE_STRING_VIEW(CharT, S)
+
+template <class Arg, class StringViewT>
+constexpr void test_parse(StringViewT fmt) {
+  using CharT    = typename StringViewT::value_type;
+  auto parse_ctx = std::basic_format_parse_context<CharT>(fmt);
+  std::formatter<Arg, CharT> formatter;
+  static_assert(std::semiregular<decltype(formatter)>);
+
+  std::same_as<typename StringViewT::iterator> auto it = formatter.parse(parse_ctx);
+  assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}'));
+}
+
+template <class StringViewT>
+constexpr void test_parse(StringViewT fmt) {
+  test_parse<std::queue<int>>(fmt);
+  test_parse<std::priority_queue<int>>(fmt);
+  test_parse<std::stack<int>>(fmt);
+}
+
+template <class CharT>
+constexpr void test_fmt() {
+  test_parse(SV(""));
+  test_parse(SV(":d"));
+
+  test_parse(SV("}"));
+  test_parse(SV(":d}"));
+}
+
+constexpr bool test() {
+  test_fmt<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_fmt<wchar_t>();
+#endif
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
index e17a6d0da2d47..06a29502a9724 100644
--- a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
@@ -195,9 +195,6 @@ void test_P1636() {
 }
 
 // Tests for P2286 Formatting ranges
-//
-// The paper hasn't been voted in so currently all formatters are disabled.
-// TODO validate whether the test is correct after the paper has been accepted.
 template <class CharT>
 void test_P2286() {
   assert_is_formattable<std::array<int, 42>, CharT>();
@@ -216,9 +213,9 @@ void test_P2286() {
   assert_is_not_formattable<std::unordered_multiset<int>, CharT>();
   assert_is_not_formattable<std::unordered_multimap<int, int>, CharT>();
 
-  assert_is_not_formattable<std::stack<int>, CharT>();
-  assert_is_not_formattable<std::queue<int>, CharT>();
-  assert_is_not_formattable<std::priority_queue<int>, CharT>();
+  assert_is_formattable<std::stack<int>, CharT>();
+  assert_is_formattable<std::queue<int>, CharT>();
+  assert_is_formattable<std::priority_queue<int>, CharT>();
 
   assert_is_formattable<std::span<int>, CharT>();
 


        


More information about the libcxx-commits mailing list