[libcxx-commits] [libcxx] 7fb9f99 - [libc++][format] Adds bool formatter.

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Thu Oct 7 08:17:35 PDT 2021


Author: Mark de Wever
Date: 2021-10-07T17:17:27+02:00
New Revision: 7fb9f99f3bb645337b4f4e6a2a3515219be82011

URL: https://github.com/llvm/llvm-project/commit/7fb9f99f3bb645337b4f4e6a2a3515219be82011
DIFF: https://github.com/llvm/llvm-project/commit/7fb9f99f3bb645337b4f4e6a2a3515219be82011.diff

LOG: [libc++][format] Adds bool formatter.

Implements the formatter for Boolean types.
[format.formatter.spec]/2.3
For each charT, for each cv-unqualified arithmetic type ArithmeticT other
than char, wchar_t, char8_t, char16_t, or char32_t, a specialization
```
  template<> struct formatter<ArithmeticT, charT>;
```
This removes the stub implemented in D96664.

Implements parts of:
- P0645 Text Formatting
- P1652 Printf corner cases in std::format

Completes:
- P1868 width: clarifying units of width and precision in std::format

Reviewed By: #libc, ldionne

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

Added: 
    libcxx/include/__format/formatter_bool.h
    libcxx/test/libcxx/diagnostics/detail.headers/format/formatter_bool.module.verify.cpp
    libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_bool.pass.cpp

Modified: 
    libcxx/docs/Status/Cxx20Papers.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/format
    libcxx/include/module.modulemap
    libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.bool.pass.cpp
    libcxx/test/std/utilities/format/format.functions/format_tests.h
    libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp
    libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp
    libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
    libcxx/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp
    libcxx/test/std/utilities/format/format.functions/vformat_to.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 18acdf922437f..a4dd75c5ca039 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -171,7 +171,7 @@
 "`P1460 <https://wg21.link/P1460>`__","LWG","Mandating the Standard Library: Clause 20 - Utilities library","Prague","* *",""
 "`P1739 <https://wg21.link/P1739>`__","LWG","Avoid template bloat for safe_ranges in combination with ""subrange-y"" view adaptors","Prague","* *",""
 "`P1831 <https://wg21.link/P1831>`__","LWG","Deprecating volatile: library","Prague","* *",""
-"`P1868 <https://wg21.link/P1868>`__","LWG","width: clarifying units of width and precision in std::format","Prague","|In Progress|",""
+"`P1868 <https://wg21.link/P1868>`__","LWG","width: clarifying units of width and precision in std::format","Prague","|Complete|","14.0"
 "`P1908 <https://wg21.link/P1908>`__","CWG","Reserving Attribute Namespaces for Future Use","Prague","* *",""
 "`P1937 <https://wg21.link/P1937>`__","CWG","Fixing inconsistencies between constexpr and consteval functions","Prague","* *",""
 "`P1956 <https://wg21.link/P1956>`__","LWG","On the names of low-level bit manipulation functions","Prague","|Complete|","12.0"

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 568f06bda4812..3460f72f80000 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -140,6 +140,7 @@ set(files
   __format/format_parse_context.h
   __format/format_string.h
   __format/formatter.h
+  __format/formatter_bool.h
   __format/formatter_char.h
   __format/formatter_integer.h
   __format/formatter_integral.h

diff  --git a/libcxx/include/__format/formatter_bool.h b/libcxx/include/__format/formatter_bool.h
new file mode 100644
index 0000000000000..8d9a1d26a4e22
--- /dev/null
+++ b/libcxx/include/__format/formatter_bool.h
@@ -0,0 +1,145 @@
+// -*- 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_FORMATTER_BOOL_H
+#define _LIBCPP___FORMAT_FORMATTER_BOOL_H
+
+#include <__availability>
+#include <__config>
+#include <__format/format_error.h>
+#include <__format/format_fwd.h>
+#include <__format/formatter.h>
+#include <__format/formatter_integral.h>
+#include <__format/parser_std_format_spec.h>
+#include <string_view>
+
+#ifndef _LIBCPP_HAS_NO_LOCALIZATION
+#include <locale>
+#endif
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 17
+
+// TODO FMT Remove this once we require compilers with proper C++20 support.
+// If the compiler has no concepts support, the format header will be disabled.
+// Without concepts support enable_if needs to be used and that too much effort
+// to support compilers with partial C++20 support.
+#if !defined(_LIBCPP_HAS_NO_CONCEPTS)
+
+namespace __format_spec {
+
+template <class _CharT>
+class _LIBCPP_TEMPLATE_VIS __parser_bool : public __parser_integral<_CharT> {
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr auto parse(auto& __parse_ctx)
+      -> decltype(__parse_ctx.begin()) {
+    auto __it = __parser_integral<_CharT>::__parse(__parse_ctx);
+
+    switch (this->__type) {
+    case _Flags::_Type::__default:
+      this->__type = _Flags::_Type::__string;
+      [[fallthrough]];
+    case _Flags::_Type::__string:
+      this->__handle_bool();
+      break;
+
+    case _Flags::_Type::__char:
+      this->__handle_char();
+      break;
+
+    case _Flags::_Type::__binary_lower_case:
+    case _Flags::_Type::__binary_upper_case:
+    case _Flags::_Type::__octal:
+    case _Flags::_Type::__decimal:
+    case _Flags::_Type::__hexadecimal_lower_case:
+    case _Flags::_Type::__hexadecimal_upper_case:
+      this->__handle_integer();
+      break;
+
+    default:
+      __throw_format_error(
+          "The format-spec type has a type not supported for a bool argument");
+    }
+
+    return __it;
+  }
+};
+
+template <class _CharT>
+struct _LIBCPP_TEMPLATE_VIS __bool_strings;
+
+template <>
+struct _LIBCPP_TEMPLATE_VIS __bool_strings<char> {
+  static constexpr string_view __true{"true"};
+  static constexpr string_view __false{"false"};
+};
+
+template <>
+struct _LIBCPP_TEMPLATE_VIS __bool_strings<wchar_t> {
+  static constexpr wstring_view __true{L"true"};
+  static constexpr wstring_view __false{L"false"};
+};
+
+template <class _CharT>
+using __formatter_bool = __formatter_integral<__parser_bool<_CharT>>;
+
+} //namespace __format_spec
+
+// [format.formatter.spec]/2.3
+// For each charT, for each cv-unqualified arithmetic type ArithmeticT other
+// than char, wchar_t, char8_t, char16_t, or char32_t, a specialization
+
+template <class _CharT>
+struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<bool, _CharT>
+    : public __format_spec::__formatter_bool<_CharT> {
+  using _Base = __format_spec::__formatter_bool<_CharT>;
+
+  _LIBCPP_HIDE_FROM_ABI auto format(bool __value, auto& __ctx)
+      -> decltype(__ctx.out()) {
+    if (this->__type != __format_spec::_Flags::_Type::__string)
+      return _Base::format(static_cast<unsigned char>(__value), __ctx);
+
+    if (this->__width_needs_substitution())
+      this->__substitute_width_arg_id(__ctx.arg(this->__width));
+
+#ifndef _LIBCPP_HAS_NO_LOCALIZATION
+    if (this->__locale_specific_form) {
+      const auto& __np = use_facet<numpunct<_CharT>>(__ctx.locale());
+      basic_string<_CharT> __str = __value ? __np.truename() : __np.falsename();
+      return __formatter::__write_unicode(
+          __ctx.out(), basic_string_view<_CharT>{__str}, this->__width, -1,
+          this->__fill, this->__alignment);
+    }
+#endif
+    basic_string_view<_CharT> __str =
+        __value ? __format_spec::__bool_strings<_CharT>::__true
+                : __format_spec::__bool_strings<_CharT>::__false;
+
+    // The output only uses ASCII so every character is one column.
+    unsigned __size = __str.size();
+    if (__size >= this->__width)
+      return _VSTD::copy(__str.begin(), __str.end(), __ctx.out());
+
+    return __formatter::__write(__ctx.out(), __str.begin(), __str.end(), __size,
+                                this->__width, this->__fill, this->__alignment);
+  }
+};
+
+#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS)
+
+#endif //_LIBCPP_STD_VER > 17
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FORMAT_FORMATTER_BOOL_H

diff  --git a/libcxx/include/format b/libcxx/include/format
index d4e24b6c30fcf..91160151489aa 100644
--- a/libcxx/include/format
+++ b/libcxx/include/format
@@ -279,6 +279,7 @@ namespace std {
 #include <__format/format_parse_context.h>
 #include <__format/format_string.h>
 #include <__format/formatter.h>
+#include <__format/formatter_bool.h>
 #include <__format/formatter_char.h>
 #include <__format/formatter_integer.h>
 #include <__format/formatter_string.h>
@@ -389,28 +390,6 @@ private:
 // These specializations are helper stubs and not proper formatters.
 // TODO FMT Implement the proper formatter specializations.
 
-// [format.formatter.spec]/2.3
-// For each charT, for each cv-unqualified arithmetic type ArithmeticT other
-// than char, wchar_t, char8_t, char16_t, or char32_t, a specialization
-
-// Boolean.
-template <class _CharT>
-struct _LIBCPP_TEMPLATE_VIS formatter<bool, _CharT> {
-  _LIBCPP_HIDE_FROM_ABI
-  auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) {
-    // TODO FMT Implement
-    return __parse_ctx.begin();
-  }
-
-  _LIBCPP_HIDE_FROM_ABI
-  auto format(bool __b, auto& __ctx) -> decltype(__ctx.out()) {
-    // TODO FMT Implement using formatting arguments
-    auto __out_it = __ctx.out();
-    *__out_it++ = _CharT('0') + __b;
-    return __out_it;
-  }
-};
-
 // Floating point types.
 // TODO FMT There are no replacements for the floating point stubs due to not
 // having floating point support in std::to_chars yet. These stubs aren't

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 4f107738313fe..5abe2431300a7 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -450,6 +450,7 @@ module std [system] {
       module format_parse_context   { private header "__format/format_parse_context.h"   }
       module format_string          { private header "__format/format_string.h"          }
       module formatter              { private header "__format/formatter.h"              }
+      module formatter_bool         { private header "__format/formatter_bool.h"         }
       module formatter_char         { private header "__format/formatter_char.h"         }
       module formatter_integer      { private header "__format/formatter_integer.h"      }
       module formatter_integral     { private header "__format/formatter_integral.h"     }

diff  --git a/libcxx/test/libcxx/diagnostics/detail.headers/format/formatter_bool.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/format/formatter_bool.module.verify.cpp
new file mode 100644
index 0000000000000..9cd9dfa37a16f
--- /dev/null
+++ b/libcxx/test/libcxx/diagnostics/detail.headers/format/formatter_bool.module.verify.cpp
@@ -0,0 +1,16 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: modules-build
+
+// WARNING: This test was generated by 'generate_private_header_tests.py'
+// and should not be edited manually.
+
+// expected-error@*:* {{use of private header from outside its module: '__format/formatter_bool.h'}}
+#include <__format/formatter_bool.h>

diff  --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_bool.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_bool.pass.cpp
new file mode 100644
index 0000000000000..c6fdbee325320
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_bool.pass.cpp
@@ -0,0 +1,452 @@
+//===----------------------------------------------------------------------===//
+// 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
+// UNSUPPORTED: libcpp-no-concepts
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+
+// <format>
+
+// Tests the parsing of the format string as specified in [format.string.std].
+// It validates whether the std-format-spec is valid for a boolean type.
+
+#include <format>
+#include <cassert>
+#ifndef _LIBCPP_HAS_NO_LOCALIZATION
+# include <iostream>
+#endif
+
+#include "concepts_precision.h"
+#include "test_macros.h"
+#include "make_string.h"
+#include "test_exception.h"
+
+#define CSTR(S) MAKE_CSTRING(CharT, S)
+
+using namespace std::__format_spec;
+
+template <class CharT>
+using Parser = __parser_bool<CharT>;
+
+template <class CharT>
+struct Expected {
+  CharT fill = CharT(' ');
+  _Flags::_Alignment alignment = _Flags::_Alignment::__left;
+  _Flags::_Sign sign = _Flags::_Sign::__default;
+  bool alternate_form = false;
+  bool zero_padding = false;
+  uint32_t width = 0;
+  bool width_as_arg = false;
+  bool locale_specific_form = false;
+  _Flags::_Type type = _Flags::_Type::__string;
+};
+
+template <class CharT>
+constexpr void test(Expected<CharT> expected, size_t size,
+                    std::basic_string_view<CharT> fmt) {
+  // Initialize parser with sufficient arguments to avoid the parsing to fail
+  // due to insufficient arguments.
+  std::basic_format_parse_context<CharT> parse_ctx(fmt,
+                                                   std::__format::__number_max);
+  auto begin = parse_ctx.begin();
+  auto end = parse_ctx.end();
+  Parser<CharT> parser;
+  auto it = parser.parse(parse_ctx);
+
+  assert(begin == parse_ctx.begin());
+  assert(end == parse_ctx.end());
+
+  assert(begin + size == it);
+  assert(parser.__fill == expected.fill);
+  assert(parser.__alignment == expected.alignment);
+  assert(parser.__sign == expected.sign);
+  assert(parser.__alternate_form == expected.alternate_form);
+  assert(parser.__zero_padding == expected.zero_padding);
+  assert(parser.__width == expected.width);
+  assert(parser.__width_as_arg == expected.width_as_arg);
+  assert(parser.__locale_specific_form == expected.locale_specific_form);
+  assert(parser.__type == expected.type);
+}
+
+template <class CharT>
+constexpr void test(Expected<CharT> expected, size_t size, const CharT* f) {
+  // The format-spec is valid if completely consumed or terminates at a '}'.
+  // The valid inputs all end with a '}'. The test is executed twice:
+  // - first with the terminating '}',
+  // - second consuming the entire input.
+  std::basic_string_view<CharT> fmt{f};
+  assert(fmt.back() == CharT('}') && "Pre-condition failure");
+
+  test(expected, size, fmt);
+  fmt.remove_suffix(1);
+  test(expected, size, fmt);
+}
+
+template <class CharT>
+constexpr void test_as_string() {
+
+  test({}, 1, CSTR("s}"));
+
+  // *** Align-fill ***
+  test({.alignment = _Flags::_Alignment::__left}, 1, CSTR("<}"));
+  test({.alignment = _Flags::_Alignment::__center}, 1, "^}");
+  test({.alignment = _Flags::_Alignment::__right}, 1, ">}");
+
+  test({.alignment = _Flags::_Alignment::__left}, 2, CSTR("<s}"));
+  test({.alignment = _Flags::_Alignment::__center}, 2, "^s}");
+  test({.alignment = _Flags::_Alignment::__right}, 2, ">s}");
+
+  test({.fill = CharT('L'), .alignment = _Flags::_Alignment::__left}, 2,
+       CSTR("L<}"));
+  test({.fill = CharT('#'), .alignment = _Flags::_Alignment::__center}, 2,
+       CSTR("#^}"));
+  test({.fill = CharT('0'), .alignment = _Flags::_Alignment::__right}, 2,
+       CSTR("0>}"));
+
+  test({.fill = CharT('L'), .alignment = _Flags::_Alignment::__left}, 3,
+       CSTR("L<s}"));
+  test({.fill = CharT('#'), .alignment = _Flags::_Alignment::__center}, 3,
+       CSTR("#^s}"));
+  test({.fill = CharT('0'), .alignment = _Flags::_Alignment::__right}, 3,
+       CSTR("0>s}"));
+
+  // *** Sign ***
+  test_exception<Parser<CharT>>(
+      "A sign field isn't allowed in this format-spec", CSTR("-}"));
+  test_exception<Parser<CharT>>(
+      "A sign field isn't allowed in this format-spec", CSTR("-s}"));
+
+  // *** Alternate form ***
+  test_exception<Parser<CharT>>(
+      "An alternate form field isn't allowed in this format-spec", CSTR("#}"));
+  test_exception<Parser<CharT>>(
+      "An alternate form field isn't allowed in this format-spec", CSTR("#s}"));
+
+  // *** Zero padding ***
+  test_exception<Parser<CharT>>(
+      "A zero-padding field isn't allowed in this format-spec", CSTR("0}"));
+  test_exception<Parser<CharT>>(
+      "A zero-padding field isn't allowed in this format-spec", CSTR("0s}"));
+
+  // *** Width ***
+  test({.width = 0, .width_as_arg = false}, 0, CSTR("}"));
+  test({.width = 1, .width_as_arg = false}, 1, CSTR("1}"));
+  test({.width = 10, .width_as_arg = false}, 2, CSTR("10}"));
+  test({.width = 1000, .width_as_arg = false}, 4, CSTR("1000}"));
+  test({.width = 1000000, .width_as_arg = false}, 7, CSTR("1000000}"));
+
+  test({.width = 0, .width_as_arg = true}, 2, CSTR("{}}"));
+  test({.width = 0, .width_as_arg = true}, 3, CSTR("{0}}"));
+  test({.width = 1, .width_as_arg = true}, 3, CSTR("{1}}"));
+
+  test_exception<Parser<CharT>>(
+      "A format-spec width field shouldn't have a leading zero", CSTR("00"));
+
+  static_assert(std::__format::__number_max == 2'147'483'647,
+                "Update the assert and the test.");
+  test({.width = 2'147'483'647, .width_as_arg = false}, 10,
+       CSTR("2147483647}"));
+  test_exception<Parser<CharT>>(
+      "The numeric value of the format-spec is too large", CSTR("2147483648"));
+  test_exception<Parser<CharT>>(
+      "The numeric value of the format-spec is too large", CSTR("5000000000"));
+  test_exception<Parser<CharT>>(
+      "The numeric value of the format-spec is too large", CSTR("10000000000"));
+
+  test_exception<Parser<CharT>>("End of input while parsing format-spec arg-id",
+                                CSTR("{"));
+  test_exception<Parser<CharT>>(
+      "A format-spec arg-id should terminate at a '}'", CSTR("{0"));
+  test_exception<Parser<CharT>>(
+      "The arg-id of the format-spec starts with an invalid character",
+      CSTR("{a"));
+  test_exception<Parser<CharT>>(
+      "A format-spec arg-id should terminate at a '}'", CSTR("{1"));
+  test_exception<Parser<CharT>>(
+      "A format-spec arg-id should terminate at a '}'", CSTR("{9"));
+  test_exception<Parser<CharT>>(
+      "A format-spec arg-id should terminate at a '}'", CSTR("{9:"));
+  test_exception<Parser<CharT>>(
+      "A format-spec arg-id should terminate at a '}'", CSTR("{9a"));
+
+  static_assert(std::__format::__number_max == 2'147'483'647,
+                "Update the assert and the test.");
+  // Note the static_assert tests whether the arg-id is valid.
+  // Therefore the following should be true arg-id < __format::__number_max.
+  test({.width = 2'147'483'646, .width_as_arg = true}, 12,
+       CSTR("{2147483646}}"));
+  test_exception<Parser<CharT>>(
+      "The numeric value of the format-spec is too large",
+      CSTR("{2147483648}"));
+  test_exception<Parser<CharT>>(
+      "The numeric value of the format-spec is too large",
+      CSTR("{5000000000}"));
+  test_exception<Parser<CharT>>(
+      "The numeric value of the format-spec is too large",
+      CSTR("{10000000000}"));
+
+  // *** Precision ***
+  test_exception<Parser<CharT>>(
+      "The format-spec should consume the input or end with a '}'", CSTR("."));
+  test_exception<Parser<CharT>>(
+      "The format-spec should consume the input or end with a '}'", CSTR(".1"));
+
+  // *** Locale-specific form ***
+  test({.locale_specific_form = true}, 1, CSTR("L}"));
+  test({.locale_specific_form = true}, 2, CSTR("Ls}"));
+}
+
+template <class CharT>
+constexpr void test_as_char() {
+
+  test({.type = _Flags::_Type::__char}, 1, CSTR("c}"));
+
+  // *** Align-fill ***
+  test({.alignment = _Flags::_Alignment::__left, .type = _Flags::_Type::__char},
+       2, CSTR("<c}"));
+  test({.alignment = _Flags::_Alignment::__center,
+        .type = _Flags::_Type::__char},
+       2, "^c}");
+  test(
+      {.alignment = _Flags::_Alignment::__right, .type = _Flags::_Type::__char},
+      2, ">c}");
+
+  test({.fill = CharT('L'),
+        .alignment = _Flags::_Alignment::__left,
+        .type = _Flags::_Type::__char},
+       3, CSTR("L<c}"));
+  test({.fill = CharT('#'),
+        .alignment = _Flags::_Alignment::__center,
+        .type = _Flags::_Type::__char},
+       3, CSTR("#^c}"));
+  test({.fill = CharT('0'),
+        .alignment = _Flags::_Alignment::__right,
+        .type = _Flags::_Type::__char},
+       3, CSTR("0>c}"));
+
+  // *** Sign ***
+  test_exception<Parser<CharT>>(
+      "A sign field isn't allowed in this format-spec", CSTR("-c}"));
+
+  // *** Alternate form ***
+  test_exception<Parser<CharT>>(
+      "An alternate form field isn't allowed in this format-spec", CSTR("#c}"));
+
+  // *** Zero padding ***
+  test_exception<Parser<CharT>>(
+      "A zero-padding field isn't allowed in this format-spec", CSTR("0c}"));
+
+  // *** Width ***
+  test({.width = 0, .width_as_arg = false, .type = _Flags::_Type::__char}, 1,
+       CSTR("c}"));
+  test({.width = 1, .width_as_arg = false, .type = _Flags::_Type::__char}, 2,
+       CSTR("1c}"));
+  test({.width = 10, .width_as_arg = false, .type = _Flags::_Type::__char}, 3,
+       CSTR("10c}"));
+  test({.width = 1000, .width_as_arg = false, .type = _Flags::_Type::__char}, 5,
+       CSTR("1000c}"));
+  test({.width = 1000000, .width_as_arg = false, .type = _Flags::_Type::__char},
+       8, CSTR("1000000c}"));
+
+  test({.width = 0, .width_as_arg = true, .type = _Flags::_Type::__char}, 3,
+       CSTR("{}c}"));
+  test({.width = 0, .width_as_arg = true, .type = _Flags::_Type::__char}, 4,
+       CSTR("{0}c}"));
+  test({.width = 1, .width_as_arg = true, .type = _Flags::_Type::__char}, 4,
+       CSTR("{1}c}"));
+
+  // *** Precision ***
+  test_exception<Parser<CharT>>(
+      "The format-spec should consume the input or end with a '}'", CSTR("."));
+  test_exception<Parser<CharT>>(
+      "The format-spec should consume the input or end with a '}'", CSTR(".1"));
+
+  // *** Locale-specific form ***
+  test({.locale_specific_form = true, .type = _Flags::_Type::__char}, 2,
+       CSTR("Lc}"));
+}
+
+template <class CharT>
+constexpr void test_as_integer() {
+
+  test({.alignment = _Flags::_Alignment::__right,
+        .type = _Flags::_Type::__decimal},
+       1, CSTR("d}"));
+
+  // *** Align-fill ***
+  test({.alignment = _Flags::_Alignment::__left,
+        .type = _Flags::_Type::__decimal},
+       2, CSTR("<d}"));
+  test({.alignment = _Flags::_Alignment::__center,
+        .type = _Flags::_Type::__decimal},
+       2, "^d}");
+  test({.alignment = _Flags::_Alignment::__right,
+        .type = _Flags::_Type::__decimal},
+       2, ">d}");
+
+  test({.fill = CharT('L'),
+        .alignment = _Flags::_Alignment::__left,
+        .type = _Flags::_Type::__decimal},
+       3, CSTR("L<d}"));
+  test({.fill = CharT('#'),
+        .alignment = _Flags::_Alignment::__center,
+        .type = _Flags::_Type::__decimal},
+       3, CSTR("#^d}"));
+  test({.fill = CharT('0'),
+        .alignment = _Flags::_Alignment::__right,
+        .type = _Flags::_Type::__decimal},
+       3, CSTR("0>d}"));
+
+  // *** Sign ***
+  test({.alignment = _Flags::_Alignment::__right,
+        .sign = _Flags::_Sign::__minus,
+        .type = _Flags::_Type::__decimal},
+       2, CSTR("-d}"));
+  test({.alignment = _Flags::_Alignment::__right,
+        .sign = _Flags::_Sign::__plus,
+        .type = _Flags::_Type::__decimal},
+       2, CSTR("+d}"));
+  test({.alignment = _Flags::_Alignment::__right,
+        .sign = _Flags::_Sign::__space,
+        .type = _Flags::_Type::__decimal},
+       2, CSTR(" d}"));
+
+  // *** Alternate form ***
+  test({.alignment = _Flags::_Alignment::__right,
+        .alternate_form = true,
+        .type = _Flags::_Type::__decimal},
+       2, CSTR("#d}"));
+
+  // *** Zero padding ***
+  test({.alignment = _Flags::_Alignment::__default,
+        .zero_padding = true,
+        .type = _Flags::_Type::__decimal},
+       2, CSTR("0d}"));
+  test({.alignment = _Flags::_Alignment::__center,
+        .type = _Flags::_Type::__decimal},
+       3, CSTR("^0d}"));
+
+  // *** Width ***
+  test({.alignment = _Flags::_Alignment::__right,
+        .width = 0,
+        .width_as_arg = false,
+        .type = _Flags::_Type::__decimal},
+       1, CSTR("d}"));
+  test({.alignment = _Flags::_Alignment::__right,
+        .width = 1,
+        .width_as_arg = false,
+        .type = _Flags::_Type::__decimal},
+       2, CSTR("1d}"));
+  test({.alignment = _Flags::_Alignment::__right,
+        .width = 10,
+        .width_as_arg = false,
+        .type = _Flags::_Type::__decimal},
+       3, CSTR("10d}"));
+  test({.alignment = _Flags::_Alignment::__right,
+        .width = 1000,
+        .width_as_arg = false,
+        .type = _Flags::_Type::__decimal},
+       5, CSTR("1000d}"));
+  test({.alignment = _Flags::_Alignment::__right,
+        .width = 1000000,
+        .width_as_arg = false,
+        .type = _Flags::_Type::__decimal},
+       8, CSTR("1000000d}"));
+
+  test({.alignment = _Flags::_Alignment::__right,
+        .width = 0,
+        .width_as_arg = true,
+        .type = _Flags::_Type::__decimal},
+       3, CSTR("{}d}"));
+  test({.alignment = _Flags::_Alignment::__right,
+        .width = 0,
+        .width_as_arg = true,
+        .type = _Flags::_Type::__decimal},
+       4, CSTR("{0}d}"));
+  test({.alignment = _Flags::_Alignment::__right,
+        .width = 1,
+        .width_as_arg = true,
+        .type = _Flags::_Type::__decimal},
+       4, CSTR("{1}d}"));
+
+  // *** Precision ***
+  test_exception<Parser<CharT>>(
+      "The format-spec should consume the input or end with a '}'", CSTR("."));
+  test_exception<Parser<CharT>>(
+      "The format-spec should consume the input or end with a '}'", CSTR(".1"));
+
+  // *** Locale-specific form ***
+  test({.alignment = _Flags::_Alignment::__right,
+        .locale_specific_form = true,
+        .type = _Flags::_Type::__decimal},
+       2, CSTR("Ld}"));
+}
+
+template <class CharT>
+constexpr void test() {
+  Parser<CharT> parser;
+
+  assert(parser.__fill == CharT(' '));
+  assert(parser.__alignment == _Flags::_Alignment::__default);
+  assert(parser.__sign == _Flags::_Sign::__default);
+  assert(parser.__alternate_form == false);
+  assert(parser.__zero_padding == false);
+  assert(parser.__width == 0);
+  assert(parser.__width_as_arg == false);
+  static_assert(!has_precision<decltype(parser)>);
+  static_assert(!has_precision_as_arg<decltype(parser)>);
+  assert(parser.__locale_specific_form == false);
+  assert(parser.__type == _Flags::_Type::__default);
+
+  test({}, 0, CSTR("}"));
+
+  test_as_string<CharT>();
+  test_as_char<CharT>();
+  test_as_integer<CharT>();
+
+  // *** Type ***
+  {
+    const char* expected =
+        "The format-spec type has a type not supported for a bool argument";
+    test_exception<Parser<CharT>>(expected, CSTR("A}"));
+    test_exception<Parser<CharT>>(expected, CSTR("E}"));
+    test_exception<Parser<CharT>>(expected, CSTR("F}"));
+    test_exception<Parser<CharT>>(expected, CSTR("G}"));
+    test_exception<Parser<CharT>>(expected, CSTR("a}"));
+    test_exception<Parser<CharT>>(expected, CSTR("e}"));
+    test_exception<Parser<CharT>>(expected, CSTR("f}"));
+    test_exception<Parser<CharT>>(expected, CSTR("g}"));
+    test_exception<Parser<CharT>>(expected, CSTR("p}"));
+  }
+
+  // **** General ***
+  test_exception<Parser<CharT>>(
+      "The format-spec should consume the input or end with a '}'", CSTR("ss"));
+}
+
+constexpr bool test() {
+  test<char>();
+  test<wchar_t>();
+
+  return true;
+}
+
+int main(int, char**) {
+#ifndef _WIN32
+  // Make sure the parsers match the expectations. The layout of the
+  // subobjects is chosen to minimize the size required.
+  static_assert(sizeof(Parser<char>) == 2 * sizeof(uint32_t));
+  static_assert(
+      sizeof(Parser<wchar_t>) ==
+      (sizeof(wchar_t) <= 2 ? 2 * sizeof(uint32_t) : 3 * sizeof(uint32_t)));
+#endif
+
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.bool.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.bool.pass.cpp
index 9a62d04be37f4..41b6f1246945f 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.bool.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.bool.pass.cpp
@@ -67,8 +67,8 @@ void test_termination_condition(StringT expected, StringT f, bool arg) {
 
 template <class CharT>
 void test_boolean() {
-  test_termination_condition(STR("1"), STR("}"), true);
-  test_termination_condition(STR("0"), STR("}"), false);
+  test_termination_condition(STR("true"), STR("}"), true);
+  test_termination_condition(STR("false"), STR("}"), false);
 }
 
 int main(int, char**) {

diff  --git a/libcxx/test/std/utilities/format/format.functions/format_tests.h b/libcxx/test/std/utilities/format/format.functions/format_tests.h
index 1478288b8c976..3da2edfbf066b 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_tests.h
+++ b/libcxx/test/std/utilities/format/format.functions/format_tests.h
@@ -15,6 +15,7 @@
 // ExceptionTest must be callable as check_exception(expected-exception, string-to-format, args-to-format...)
 
 #define STR(S) MAKE_STRING(CharT, S)
+#define CSTR(S) MAKE_CSTRING(CharT, S)
 
 template <class CharT>
 std::vector<std::basic_string<CharT>> invalid_types(std::string valid) {
@@ -260,6 +261,238 @@ void format_string_tests(TestFunction check, ExceptionTest check_exception) {
   format_test_string_unicode<CharT>(check);
 }
 
+template <class CharT, class TestFunction, class ExceptionTest>
+void format_test_bool(TestFunction check, ExceptionTest check_exception) {
+
+  // *** align-fill & width ***
+  check(STR("answer is 'true   '"), STR("answer is '{:7}'"), true);
+  check(STR("answer is '   true'"), STR("answer is '{:>7}'"), true);
+  check(STR("answer is 'true   '"), STR("answer is '{:<7}'"), true);
+  check(STR("answer is ' true  '"), STR("answer is '{:^7}'"), true);
+
+  check(STR("answer is 'false   '"), STR("answer is '{:8s}'"), false);
+  check(STR("answer is '   false'"), STR("answer is '{:>8s}'"), false);
+  check(STR("answer is 'false   '"), STR("answer is '{:<8s}'"), false);
+  check(STR("answer is ' false  '"), STR("answer is '{:^8s}'"), false);
+
+  check(STR("answer is '---true'"), STR("answer is '{:->7}'"), true);
+  check(STR("answer is 'true---'"), STR("answer is '{:-<7}'"), true);
+  check(STR("answer is '-true--'"), STR("answer is '{:-^7}'"), true);
+
+  check(STR("answer is '---false'"), STR("answer is '{:->8s}'"), false);
+  check(STR("answer is 'false---'"), STR("answer is '{:-<8s}'"), false);
+  check(STR("answer is '-false--'"), STR("answer is '{:-^8s}'"), false);
+
+  // *** Sign ***
+  check_exception("A sign field isn't allowed in this format-spec", STR("{:-}"),
+                  true);
+  check_exception("A sign field isn't allowed in this format-spec", STR("{:+}"),
+                  true);
+  check_exception("A sign field isn't allowed in this format-spec", STR("{: }"),
+                  true);
+
+  check_exception("A sign field isn't allowed in this format-spec",
+                  STR("{:-s}"), true);
+  check_exception("A sign field isn't allowed in this format-spec",
+                  STR("{:+s}"), true);
+  check_exception("A sign field isn't allowed in this format-spec",
+                  STR("{: s}"), true);
+
+  // *** alternate form ***
+  check_exception("An alternate form field isn't allowed in this format-spec",
+                  STR("{:#}"), true);
+  check_exception("An alternate form field isn't allowed in this format-spec",
+                  STR("{:#s}"), true);
+
+  // *** zero-padding ***
+  check_exception("A zero-padding field isn't allowed in this format-spec",
+                  STR("{:0}"), true);
+  check_exception("A zero-padding field isn't allowed in this format-spec",
+                  STR("{:0s}"), true);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'",
+                  STR("{:.}"), true);
+  check_exception("The format-spec should consume the input or end with a '}'",
+                  STR("{:.0}"), true);
+  check_exception("The format-spec should consume the input or end with a '}'",
+                  STR("{:.42}"), true);
+
+  check_exception("The format-spec should consume the input or end with a '}'",
+                  STR("{:.s}"), true);
+  check_exception("The format-spec should consume the input or end with a '}'",
+                  STR("{:.0s}"), true);
+  check_exception("The format-spec should consume the input or end with a '}'",
+                  STR("{:.42s}"), true);
+
+  // *** locale-specific form ***
+  // See locale-specific_form.pass.cpp
+
+  // *** type ***
+  for (const auto& fmt : invalid_types<CharT>("bBcdosxX"))
+    check_exception(
+        "The format-spec type has a type not supported for a bool argument",
+        fmt, true);
+}
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void format_test_bool_as_char(TestFunction check,
+                              ExceptionTest check_exception) {
+  // *** align-fill & width ***
+  check(STR("answer is '\1     '"), STR("answer is '{:6c}'"), true);
+  check(STR("answer is '     \1'"), STR("answer is '{:>6c}'"), true);
+  check(STR("answer is '\1     '"), STR("answer is '{:<6c}'"), true);
+  check(STR("answer is '  \1   '"), STR("answer is '{:^6c}'"), true);
+
+  check(STR("answer is '-----\1'"), STR("answer is '{:->6c}'"), true);
+  check(STR("answer is '\1-----'"), STR("answer is '{:-<6c}'"), true);
+  check(STR("answer is '--\1---'"), STR("answer is '{:-^6c}'"), true);
+
+  check(std::basic_string<CharT>(CSTR("answer is '\0     '"), 18),
+        STR("answer is '{:6c}'"), false);
+  check(std::basic_string<CharT>(CSTR("answer is '\0     '"), 18),
+        STR("answer is '{:6c}'"), false);
+  check(std::basic_string<CharT>(CSTR("answer is '     \0'"), 18),
+        STR("answer is '{:>6c}'"), false);
+  check(std::basic_string<CharT>(CSTR("answer is '\0     '"), 18),
+        STR("answer is '{:<6c}'"), false);
+  check(std::basic_string<CharT>(CSTR("answer is '  \0   '"), 18),
+        STR("answer is '{:^6c}'"), false);
+
+  check(std::basic_string<CharT>(CSTR("answer is '-----\0'"), 18),
+        STR("answer is '{:->6c}'"), false);
+  check(std::basic_string<CharT>(CSTR("answer is '\0-----'"), 18),
+        STR("answer is '{:-<6c}'"), false);
+  check(std::basic_string<CharT>(CSTR("answer is '--\0---'"), 18),
+        STR("answer is '{:-^6c}'"), false);
+
+  // *** Sign ***
+  check_exception("A sign field isn't allowed in this format-spec",
+                  STR("{:-c}"), true);
+  check_exception("A sign field isn't allowed in this format-spec",
+                  STR("{:+c}"), true);
+  check_exception("A sign field isn't allowed in this format-spec",
+                  STR("{: c}"), true);
+
+  // *** alternate form ***
+  check_exception("An alternate form field isn't allowed in this format-spec",
+                  STR("{:#c}"), true);
+
+  // *** zero-padding ***
+  check_exception("A zero-padding field isn't allowed in this format-spec",
+                  STR("{:0c}"), true);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'",
+                  STR("{:.c}"), true);
+  check_exception("The format-spec should consume the input or end with a '}'",
+                  STR("{:.0c}"), true);
+  check_exception("The format-spec should consume the input or end with a '}'",
+                  STR("{:.42c}"), true);
+
+  // *** locale-specific form ***
+  // Note it has no effect but it's allowed.
+  check(STR("answer is '*'"), STR("answer is '{:Lc}'"), '*');
+
+  // *** type ***
+  for (const auto& fmt : invalid_types<CharT>("bBcdosxX"))
+    check_exception(
+        "The format-spec type has a type not supported for a bool argument",
+        fmt, true);
+}
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void format_test_bool_as_integer(TestFunction check,
+                                 ExceptionTest check_exception) {
+  // *** align-fill & width ***
+  check(STR("answer is '1'"), STR("answer is '{:<1d}'"), true);
+  check(STR("answer is '1 '"), STR("answer is '{:<2d}'"), true);
+  check(STR("answer is '0 '"), STR("answer is '{:<2d}'"), false);
+
+  check(STR("answer is '     1'"), STR("answer is '{:6d}'"), true);
+  check(STR("answer is '     1'"), STR("answer is '{:>6d}'"), true);
+  check(STR("answer is '1     '"), STR("answer is '{:<6d}'"), true);
+  check(STR("answer is '  1   '"), STR("answer is '{:^6d}'"), true);
+
+  check(STR("answer is '*****0'"), STR("answer is '{:*>6d}'"), false);
+  check(STR("answer is '0*****'"), STR("answer is '{:*<6d}'"), false);
+  check(STR("answer is '**0***'"), STR("answer is '{:*^6d}'"), false);
+
+  // Test whether zero padding is ignored
+  check(STR("answer is '     1'"), STR("answer is '{:>06d}'"), true);
+  check(STR("answer is '1     '"), STR("answer is '{:<06d}'"), true);
+  check(STR("answer is '  1   '"), STR("answer is '{:^06d}'"), true);
+
+  // *** Sign ***
+  check(STR("answer is 1"), STR("answer is {:d}"), true);
+  check(STR("answer is 0"), STR("answer is {:-d}"), false);
+  check(STR("answer is +1"), STR("answer is {:+d}"), true);
+  check(STR("answer is  0"), STR("answer is {: d}"), false);
+
+  // *** alternate form ***
+  check(STR("answer is +1"), STR("answer is {:+#d}"), true);
+  check(STR("answer is +1"), STR("answer is {:+b}"), true);
+  check(STR("answer is +0b1"), STR("answer is {:+#b}"), true);
+  check(STR("answer is +0B1"), STR("answer is {:+#B}"), true);
+  check(STR("answer is +1"), STR("answer is {:+o}"), true);
+  check(STR("answer is +01"), STR("answer is {:+#o}"), true);
+  check(STR("answer is +1"), STR("answer is {:+x}"), true);
+  check(STR("answer is +0x1"), STR("answer is {:+#x}"), true);
+  check(STR("answer is +1"), STR("answer is {:+X}"), true);
+  check(STR("answer is +0X1"), STR("answer is {:+#X}"), true);
+
+  check(STR("answer is 0"), STR("answer is {:#d}"), false);
+  check(STR("answer is 0"), STR("answer is {:b}"), false);
+  check(STR("answer is 0b0"), STR("answer is {:#b}"), false);
+  check(STR("answer is 0B0"), STR("answer is {:#B}"), false);
+  check(STR("answer is 0"), STR("answer is {:o}"), false);
+  check(STR("answer is 0"), STR("answer is {:#o}"), false);
+  check(STR("answer is 0"), STR("answer is {:x}"), false);
+  check(STR("answer is 0x0"), STR("answer is {:#x}"), false);
+  check(STR("answer is 0"), STR("answer is {:X}"), false);
+  check(STR("answer is 0X0"), STR("answer is {:#X}"), false);
+
+  // *** zero-padding & width ***
+  check(STR("answer is +00000000001"), STR("answer is {:+#012d}"), true);
+  check(STR("answer is +00000000001"), STR("answer is {:+012b}"), true);
+  check(STR("answer is +0b000000001"), STR("answer is {:+#012b}"), true);
+  check(STR("answer is +0B000000001"), STR("answer is {:+#012B}"), true);
+  check(STR("answer is +00000000001"), STR("answer is {:+012o}"), true);
+  check(STR("answer is +00000000001"), STR("answer is {:+#012o}"), true);
+  check(STR("answer is +00000000001"), STR("answer is {:+012x}"), true);
+  check(STR("answer is +0x000000001"), STR("answer is {:+#012x}"), true);
+  check(STR("answer is +00000000001"), STR("answer is {:+012X}"), true);
+  check(STR("answer is +0X000000001"), STR("answer is {:+#012X}"), true);
+
+  check(STR("answer is 000000000000"), STR("answer is {:#012d}"), false);
+  check(STR("answer is 000000000000"), STR("answer is {:012b}"), false);
+  check(STR("answer is 0b0000000000"), STR("answer is {:#012b}"), false);
+  check(STR("answer is 0B0000000000"), STR("answer is {:#012B}"), false);
+  check(STR("answer is 000000000000"), STR("answer is {:012o}"), false);
+  check(STR("answer is 000000000000"), STR("answer is {:#012o}"), false);
+  check(STR("answer is 000000000000"), STR("answer is {:012x}"), false);
+  check(STR("answer is 0x0000000000"), STR("answer is {:#012x}"), false);
+  check(STR("answer is 000000000000"), STR("answer is {:012X}"), false);
+  check(STR("answer is 0X0000000000"), STR("answer is {:#012X}"), false);
+
+  // *** precision ***
+  check_exception("The format-spec should consume the input or end with a '}'",
+                  STR("{:.}"), true);
+  check_exception("The format-spec should consume the input or end with a '}'",
+                  STR("{:.0}"), true);
+  check_exception("The format-spec should consume the input or end with a '}'",
+                  STR("{:.42}"), true);
+
+  // *** locale-specific form ***
+  // See locale-specific_form.pass.cpp
+
+  // *** type ***
+  for (const auto& fmt : invalid_types<CharT>("bBcdosxX"))
+    check_exception(
+        "The format-spec type has a type not supported for a bool argument",
+        fmt, true);
+}
+
 template <class I, class CharT, class TestFunction, class ExceptionTest>
 void format_test_integer_as_integer(TestFunction check,
                                     ExceptionTest check_exception) {
@@ -743,8 +976,8 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
   check(STR("}"), STR("}}"));
 
   // *** Test argument ID ***
-  check(STR("hello 01"), STR("hello {0:}{1:}"), false, true);
-  check(STR("hello 10"), STR("hello {1:}{0:}"), false, true);
+  check(STR("hello false true"), STR("hello {0:} {1:}"), false, true);
+  check(STR("hello true false"), STR("hello {1:} {0:}"), false, true);
 
   // ** Test invalid format strings ***
   check_exception("The format string terminates at a '{'", STR("{"));
@@ -799,7 +1032,11 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
   format_string_tests<CharT>(check, check_exception);
 
   // *** Test Boolean format argument ***
-  check(STR("hello 01"), STR("hello {}{}"), false, true);
+  check(STR("hello false true"), STR("hello {} {}"), false, true);
+
+  format_test_bool<CharT>(check, check_exception);
+  format_test_bool_as_char<CharT>(check, check_exception);
+  format_test_bool_as_integer<CharT>(check, check_exception);
 
   // *** Test signed integral format argument ***
   check(STR("hello 42"), STR("hello {}"), static_cast<signed char>(42));

diff  --git a/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp
index 9598bea7076da..461f541c73750 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp
@@ -56,8 +56,8 @@ auto test = []<class CharT, class... Args>(std::basic_string<CharT> expected,
     CharT out[4096];
     CharT* it = std::format_to(out, std::locale(), fmt, args...);
     assert(std::distance(out, it) == int(expected.size()));
-    *it = '\0';
-    assert(out == expected);
+    // Convert to std::string since output contains '\0' for boolean tests.
+    assert(std::basic_string<CharT>(out, it) == expected);
   }
 };
 

diff  --git a/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp
index 795587cb4f993..fc988beaca97e 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp
@@ -57,8 +57,8 @@ auto test = []<class CharT, class... Args>(std::basic_string<CharT> expected,
     CharT out[4096];
     CharT* it = std::format_to(out, fmt, args...);
     assert(std::distance(out, it) == int(expected.size()));
-    *it = '\0';
-    assert(out == expected);
+    // Convert to std::string since output contains '\0' for boolean tests.
+    assert(std::basic_string<CharT>(out, it) == expected);
   }
 };
 

diff  --git a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
index b32f22a5020b2..eda372310ac8d 100644
--- a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
@@ -227,6 +227,56 @@ void test(std::basic_string<CharT> expected, std::locale loc,
   }
 }
 
+#ifndef _LIBCPP_HAS_NO_UNICODE
+template <class CharT>
+struct numpunct_unicode;
+
+template <>
+struct numpunct_unicode<char> : std::numpunct<char> {
+  string_type do_truename() const override { return "gültig"; }
+  string_type do_falsename() const override { return "ungültig"; }
+};
+
+template <>
+struct numpunct_unicode<wchar_t> : std::numpunct<wchar_t> {
+  string_type do_truename() const override { return L"gültig"; }
+  string_type do_falsename() const override { return L"ungültig"; }
+};
+#endif
+
+template <class CharT>
+void test_bool() {
+  std::locale loc = std::locale(std::locale(), new numpunct<CharT>());
+
+  std::locale::global(std::locale(LOCALE_en_US_UTF_8));
+  assert(std::locale().name() == LOCALE_en_US_UTF_8);
+  test(STR("true"), STR("{:L}"), true);
+  test(STR("false"), STR("{:L}"), false);
+
+  test(STR("yes"), loc, STR("{:L}"), true);
+  test(STR("no"), loc, STR("{:L}"), false);
+
+  std::locale::global(loc);
+  test(STR("yes"), STR("{:L}"), true);
+  test(STR("no"), STR("{:L}"), false);
+
+  test(STR("true"), std::locale(LOCALE_en_US_UTF_8), STR("{:L}"), true);
+  test(STR("false"), std::locale(LOCALE_en_US_UTF_8), STR("{:L}"), false);
+
+#ifndef _LIBCPP_HAS_NO_UNICODE
+  std::locale loc_unicode =
+      std::locale(std::locale(), new numpunct_unicode<CharT>());
+
+  test(STR("gültig"), loc_unicode, STR("{:L}"), true);
+  test(STR("ungültig"), loc_unicode, STR("{:L}"), false);
+
+  test(STR("gültig   "), loc_unicode, STR("{:9L}"), true);
+  test(STR("gültig!!!"), loc_unicode, STR("{:!<9L}"), true);
+  test(STR("_gültig__"), loc_unicode, STR("{:_^9L}"), true);
+  test(STR("   gültig"), loc_unicode, STR("{:>9L}"), true);
+#endif
+}
+
 template <class CharT>
 void test_integer() {
   std::locale loc = std::locale(std::locale(), new numpunct<CharT>());
@@ -557,6 +607,7 @@ void test_integer() {
 
 template <class CharT>
 void test() {
+  test_bool<CharT>();
   test_integer<CharT>();
 }
 

diff  --git a/libcxx/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp
index 6569934bec767..a006a2b686c08 100644
--- a/libcxx/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp
@@ -68,8 +68,8 @@ auto test = []<class CharT, class... Args>(std::basic_string<CharT> expected,
         std::make_format_args<std::basic_format_context<CharT*, CharT>>(
             args...));
     assert(std::distance(out, it) == int(expected.size()));
-    *it = '\0';
-    assert(out == expected);
+    // Convert to std::string since output contains '\0' for boolean tests.
+    assert(std::basic_string<CharT>(out, it) == expected);
   }
 };
 

diff  --git a/libcxx/test/std/utilities/format/format.functions/vformat_to.pass.cpp b/libcxx/test/std/utilities/format/format.functions/vformat_to.pass.cpp
index 179776a7f0760..1c243e68d493e 100644
--- a/libcxx/test/std/utilities/format/format.functions/vformat_to.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/vformat_to.pass.cpp
@@ -71,8 +71,8 @@ auto test = []<class CharT, class... Args>(std::basic_string<CharT> expected,
         std::make_format_args<std::basic_format_context<CharT*, CharT>>(
             args...));
     assert(std::distance(out, it) == int(expected.size()));
-    *it = '\0';
-    assert(out == expected);
+    // Convert to std::string since output contains '\0' for boolean tests.
+    assert(std::basic_string<CharT>(out, it) == expected);
   }
 };
 


        


More information about the libcxx-commits mailing list