[libcxx-commits] [libcxx] 4d8268f - [libc++][format] Improve format-arg-store.

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Wed May 18 11:11:41 PDT 2022


Author: Mark de Wever
Date: 2022-05-18T20:11:36+02:00
New Revision: 4d8268fbf4cd30d39530622b7f1dc487122b4f3c

URL: https://github.com/llvm/llvm-project/commit/4d8268fbf4cd30d39530622b7f1dc487122b4f3c
DIFF: https://github.com/llvm/llvm-project/commit/4d8268fbf4cd30d39530622b7f1dc487122b4f3c.diff

LOG: [libc++][format] Improve format-arg-store.

This optimizes the __format_arg_store type to allow a more efficient
storage of the basic_format_args.

It stores the data in two arrays:
- A struct with the tag of the exposition only variant's type and the
  offset of the element in the data array. Since this array only depends
  on the type information it's calculated at compile time and can be
  shared by different instances of this class.
- The arguments converted to the types used in the exposition only
  variant of basic_format_arg. This means the packed data can be
  directly copied to an element of this variant.

The new code uses rvalue reference arguments in preparation for P2418.
The handle class also has some changes to prepare for P2418. The real
changed for P2418 will be done separately, but these parts make it
easier to implement that paper.

Some parts of existing test code are removed since they were no longer
valid after the changes, but new tests have been added.

Implements parts of:
- P2418 Add support for std::generator-like types to std::format

Completes:
- LWG3473 Normative encouragement in non-normative note

Depends on D121138

Reviewed By: #libc, vitaut, Mordante

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

Added: 
    libcxx/include/__format/format_arg_store.h

Modified: 
    libcxx/docs/Status/Cxx20Papers.csv
    libcxx/docs/Status/Cxx2bIssues.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/__format/format_arg.h
    libcxx/include/__format/format_args.h
    libcxx/include/__format/format_fwd.h
    libcxx/include/format
    libcxx/include/module.modulemap
    libcxx/test/libcxx/private_headers.verify.cpp
    libcxx/test/libcxx/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp
    libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp
    libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp
    libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.bool.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.c_string.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_point.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.handle.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.pointer.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp
    libcxx/test/std/utilities/format/format.functions/format_tests.h

Removed: 
    libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp


################################################################################
diff  --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index a548445c43c4..b887d3d9f180 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -203,5 +203,5 @@
 "","","","","",""
 "`P2372R3 <https://wg21.link/P2372R3>`__","LWG","Fixing locale handling in chrono formatters","October 2021","",""
 "`P2415R2 <https://wg21.link/P2415R2>`__","LWG","What is a ``view``","October 2021","|Complete|","14.0"
-"`P2418R2 <https://wg21.link/P2418R2>`__","LWG","Add support for ``std::generator``-like types to ``std::format``","October 2021","",""
+"`P2418R2 <https://wg21.link/P2418R2>`__","LWG","Add support for ``std::generator``-like types to ``std::format``","October 2021","|Partial|",""
 "`P2432R1 <https://wg21.link/P2432R1>`__","LWG","Fix ``istream_view``","October 2021","",""

diff  --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv
index 0391f64f05df..93fa19d6cdf7 100644
--- a/libcxx/docs/Status/Cxx2bIssues.csv
+++ b/libcxx/docs/Status/Cxx2bIssues.csv
@@ -43,7 +43,7 @@
 "`3466 <https://wg21.link/LWG3466>`__","Specify the requirements for ``promise``/``future``/``shared_future`` consistently","November 2020","|Nothing To Do|",""
 "`3467 <https://wg21.link/LWG3467>`__","``bool`` can't be an integer-like type","November 2020","|Complete|","14.0"
 "`3472 <https://wg21.link/LWG3472>`__","``counted_iterator`` is missing preconditions","November 2020","|Complete|","14.0","|ranges|"
-"`3473 <https://wg21.link/LWG3473>`__","Normative encouragement in non-normative note","November 2020","|Nothing To Do|","","|format|"
+"`3473 <https://wg21.link/LWG3473>`__","Normative encouragement in non-normative note","November 2020","|Complete|","15.0","|format|"
 "`3474 <https://wg21.link/LWG3474>`__","Nesting ``join_views`` is broken because of CTAD","November 2020","|Complete|","15.0","|ranges|"
 "`3476 <https://wg21.link/LWG3476>`__","``thread`` and ``jthread`` constructors require that the parameters be move-constructible but never move construct the parameters","November 2020","",""
 "`3477 <https://wg21.link/LWG3477>`__","Simplify constraints for ``semiregular-box``","November 2020","","","|ranges|"

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 2da8b9875810..d7aa09e2c858 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -212,6 +212,7 @@ set(files
   __format/concepts.h
   __format/enable_insertable.h
   __format/format_arg.h
+  __format/format_arg_store.h
   __format/format_args.h
   __format/format_context.h
   __format/format_error.h

diff  --git a/libcxx/include/__format/format_arg.h b/libcxx/include/__format/format_arg.h
index 45356b22cd46..3c61446378b5 100644
--- a/libcxx/include/__format/format_arg.h
+++ b/libcxx/include/__format/format_arg.h
@@ -35,6 +35,14 @@ namespace __format {
 ///
 /// @note The 128-bit types are unconditionally in the list to avoid the values
 /// of the enums to depend on the availability of 128-bit integers.
+///
+/// @note The value is stored as a 5-bit value in the __packed_arg_t_bits. This
+/// limits the maximum number of elements to 32.
+/// When modifying update the test
+/// test/libcxx/utilities/format/format.arguments/format.arg/arg_t.compile.pass.cpp
+/// It could be packed in 4-bits but that means a new type directly becomes an
+/// ABI break. The packed type is 64-bit so this reduces the maximum number of
+/// packed elements from 16 to 12.
 enum class _LIBCPP_ENUM_VIS __arg_t : uint8_t {
   __none,
   __boolean,
@@ -53,58 +61,151 @@ enum class _LIBCPP_ENUM_VIS __arg_t : uint8_t {
   __ptr,
   __handle
 };
+
+inline constexpr unsigned __packed_arg_t_bits = 5;
+inline constexpr uint8_t __packed_arg_t_mask = 0x1f;
+
+inline constexpr unsigned __packed_types_storage_bits = 64;
+inline constexpr unsigned __packed_types_max = __packed_types_storage_bits / __packed_arg_t_bits;
+
+_LIBCPP_HIDE_FROM_ABI
+constexpr bool __use_packed_format_arg_store(size_t __size) { return __size <= __packed_types_max; }
+
+_LIBCPP_HIDE_FROM_ABI
+constexpr __arg_t __get_packed_type(uint64_t __types, size_t __id) {
+  _LIBCPP_ASSERT(__id <= __packed_types_max, "");
+
+  if (__id > 0)
+    __types >>= __id * __packed_arg_t_bits;
+
+  return static_cast<__format::__arg_t>(__types & __packed_arg_t_mask);
+}
+
 } // namespace __format
 
 template <class _Visitor, class _Context>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT decltype(auto)
-visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT decltype(auto) visit_format_arg(_Visitor&& __vis,
+                                                                                  basic_format_arg<_Context> __arg) {
   switch (__arg.__type_) {
   case __format::__arg_t::__none:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), monostate{});
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__monostate_);
   case __format::__arg_t::__boolean:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__boolean);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__boolean_);
   case __format::__arg_t::__char_type:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__char_type);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__char_type_);
   case __format::__arg_t::__int:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__int);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__int_);
   case __format::__arg_t::__long_long:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__long_long);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__long_long_);
   case __format::__arg_t::__i128:
-#ifndef _LIBCPP_HAS_NO_INT128
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__i128);
-#else
+#  ifndef _LIBCPP_HAS_NO_INT128
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__i128_);
+#  else
     __libcpp_unreachable();
-#endif
+#  endif
   case __format::__arg_t::__unsigned:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__unsigned);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__unsigned_);
   case __format::__arg_t::__unsigned_long_long:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis),
-                         __arg.__unsigned_long_long);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__unsigned_long_long_);
   case __format::__arg_t::__u128:
-#ifndef _LIBCPP_HAS_NO_INT128
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__u128);
-#else
-   __libcpp_unreachable();
-#endif
+#  ifndef _LIBCPP_HAS_NO_INT128
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__u128_);
+#  else
+    __libcpp_unreachable();
+#  endif
   case __format::__arg_t::__float:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__float);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__float_);
   case __format::__arg_t::__double:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__double);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__double_);
   case __format::__arg_t::__long_double:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__long_double);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__long_double_);
   case __format::__arg_t::__const_char_type_ptr:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis),
-                         __arg.__const_char_type_ptr);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__const_char_type_ptr_);
   case __format::__arg_t::__string_view:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__string_view);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__string_view_);
   case __format::__arg_t::__ptr:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__ptr);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__ptr_);
   case __format::__arg_t::__handle:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__handle);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis),
+                         typename basic_format_arg<_Context>::handle{__arg.__value_.__handle_});
   }
+
   __libcpp_unreachable();
 }
 
+/// Contains the values used in basic_format_arg.
+///
+/// This is a separate type so it's possible to store the values and types in
+/// separate arrays.
+template <class _Context>
+class __basic_format_arg_value {
+  using _CharT = typename _Context::char_type;
+
+public:
+  /// Contains the implementation for basic_format_arg::handle.
+  struct __handle {
+    template <class _Tp>
+    _LIBCPP_HIDE_FROM_ABI explicit __handle(const _Tp& __v) noexcept
+        : __ptr_(_VSTD::addressof(__v)),
+          __format_([](basic_format_parse_context<_CharT>& __parse_ctx, _Context& __ctx, const void* __ptr) {
+            using _Formatter = typename _Context::template formatter_type<_Tp>;
+            using _Qp = conditional_t<requires { _Formatter().format(declval<const _Tp&>(), declval<_Context&>()); },
+                                      const _Tp, _Tp>;
+            _Formatter __f;
+            __parse_ctx.advance_to(__f.parse(__parse_ctx));
+            __ctx.advance_to(__f.format(*const_cast<_Qp*>(static_cast<const _Tp*>(__ptr)), __ctx));
+          }) {}
+
+    const void* __ptr_;
+    void (*__format_)(basic_format_parse_context<_CharT>&, _Context&, const void*);
+  };
+
+  union {
+    monostate __monostate_;
+    bool __boolean_;
+    _CharT __char_type_;
+    int __int_;
+    unsigned __unsigned_;
+    long long __long_long_;
+    unsigned long long __unsigned_long_long_;
+#  ifndef _LIBCPP_HAS_NO_INT128
+    __int128_t __i128_;
+    __uint128_t __u128_;
+#  endif
+    float __float_;
+    double __double_;
+    long double __long_double_;
+    const _CharT* __const_char_type_ptr_;
+    basic_string_view<_CharT> __string_view_;
+    const void* __ptr_;
+    __handle __handle_;
+  };
+
+  // These constructors contain the exact storage type used. If adjustments are
+  // required, these will be done in __create_format_arg.
+
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value() noexcept : __monostate_() {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(bool __value) noexcept : __boolean_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(_CharT __value) noexcept : __char_type_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(int __value) noexcept : __int_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(unsigned __value) noexcept : __unsigned_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(long long __value) noexcept : __long_long_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(unsigned long long __value) noexcept
+      : __unsigned_long_long_(__value) {}
+#  ifndef _LIBCPP_HAS_NO_INT128
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__int128_t __value) noexcept : __i128_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__uint128_t __value) noexcept : __u128_(__value) {}
+#  endif
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(float __value) noexcept : __float_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(double __value) noexcept : __double_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(long double __value) noexcept : __long_double_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(const _CharT* __value) noexcept : __const_char_type_ptr_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(basic_string_view<_CharT> __value) noexcept
+      : __string_view_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(const void* __value) noexcept : __ptr_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__handle __value) noexcept : __handle_(__value) {}
+};
+
 template <class _Context>
 class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg {
 public:
@@ -131,146 +232,28 @@ class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg {
   //    .format(declval<const T&>(), declval<Context&>())
   // shall be well-formed when treated as an unevaluated operand.
 
-  template <class _Ctx, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT friend __format_arg_store<_Ctx, _Args...>
-  make_format_args(const _Args&...);
-
-  template <class _Visitor, class _Ctx>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT friend decltype(auto)
-  visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx> __arg);
-
-  union {
-    bool __boolean;
-    char_type __char_type;
-    int __int;
-    unsigned __unsigned;
-    long long __long_long;
-    unsigned long long __unsigned_long_long;
-#ifndef _LIBCPP_HAS_NO_INT128
-    __int128_t __i128;
-    __uint128_t __u128;
-#endif
-    float __float;
-    double __double;
-    long double __long_double;
-    const char_type* __const_char_type_ptr;
-    basic_string_view<char_type> __string_view;
-    const void* __ptr;
-    handle __handle;
-  };
+public:
+  __basic_format_arg_value<_Context> __value_;
   __format::__arg_t __type_;
 
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const bool& __v) noexcept
-      : __boolean(__v), __type_(__format::__arg_t::__boolean) {}
-
-  template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const _Tp& __v) noexcept
-      requires(same_as<_Tp, char_type> ||
-               (same_as<_Tp, char> && same_as<char_type, wchar_t>))
-      : __char_type(__v), __type_(__format::__arg_t::__char_type) {}
-
-  template <__libcpp_signed_integer _Tp>
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const _Tp& __v) noexcept {
-    if constexpr (sizeof(_Tp) <= sizeof(int)) {
-      __int = static_cast<int>(__v);
-      __type_ = __format::__arg_t::__int;
-    } else if constexpr (sizeof(_Tp) <= sizeof(long long)) {
-      __long_long = static_cast<long long>(__v);
-      __type_ = __format::__arg_t::__long_long;
-    }
-#ifndef _LIBCPP_HAS_NO_INT128
-    else if constexpr (sizeof(_Tp) == sizeof(__int128_t)) {
-      __i128 = __v;
-      __type_ = __format::__arg_t::__i128;
-    }
-#endif
-    else
-      static_assert(sizeof(_Tp) == 0, "An unsupported signed integer was used");
-  }
-
-  template <__libcpp_unsigned_integer _Tp>
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const _Tp& __v) noexcept {
-    if constexpr (sizeof(_Tp) <= sizeof(unsigned)) {
-      __unsigned = static_cast<unsigned>(__v);
-      __type_ = __format::__arg_t::__unsigned;
-    } else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long)) {
-      __unsigned_long_long = static_cast<unsigned long long>(__v);
-      __type_ = __format::__arg_t::__unsigned_long_long;
-    }
-#ifndef _LIBCPP_HAS_NO_INT128
-    else if constexpr (sizeof(_Tp) == sizeof(__int128_t)) {
-      __u128 = __v;
-      __type_ = __format::__arg_t::__u128;
-    }
-#endif
-    else
-      static_assert(sizeof(_Tp) == 0,
-                    "An unsupported unsigned integer was used");
-  }
-
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(float __v) noexcept
-      : __float(__v), __type_(__format::__arg_t::__float) {}
-
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(double __v) noexcept
-      : __double(__v), __type_(__format::__arg_t::__double) {}
-
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(long double __v) noexcept
-      : __long_double(__v), __type_(__format::__arg_t::__long_double) {}
-
-  // Note not a 'noexcept' function.
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const char_type* __s)
-      : __const_char_type_ptr(__s),
-        __type_(__format::__arg_t::__const_char_type_ptr) {
-    _LIBCPP_ASSERT(__s, "Used a nullptr argument to initialize a C-string");
-  }
-
-  template <class _Traits>
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(
-      basic_string_view<char_type, _Traits> __s) noexcept
-      : __string_view{__s.data(), __s.size()},
-        __type_(__format::__arg_t::__string_view) {}
-
-  template <class _Traits, class _Allocator>
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(
-      const basic_string<char_type, _Traits, _Allocator>& __s) noexcept
-      : __string_view{__s.data(), __s.size()},
-        __type_(__format::__arg_t::__string_view) {}
-
-  _LIBCPP_HIDE_FROM_ABI
-  explicit basic_format_arg(nullptr_t) noexcept
-      : __ptr(nullptr), __type_(__format::__arg_t::__ptr) {}
-
-  template <class _Tp>
-  requires is_void_v<_Tp> _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(_Tp* __p) noexcept
-      : __ptr(__p), __type_(__format::__arg_t::__ptr) {}
-
-  template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const _Tp& __v) noexcept
-      : __handle(__v), __type_(__format::__arg_t::__handle) {}
+  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(__format::__arg_t __type,
+                                                  __basic_format_arg_value<_Context> __value) noexcept
+      : __value_(__value), __type_(__type) {}
 };
 
 template <class _Context>
 class _LIBCPP_TEMPLATE_VIS basic_format_arg<_Context>::handle {
-  friend class basic_format_arg<_Context>;
-
 public:
   _LIBCPP_HIDE_FROM_ABI
   void format(basic_format_parse_context<char_type>& __parse_ctx, _Context& __ctx) const {
-    __format_(__parse_ctx, __ctx, __ptr_);
+    __handle_.__format_(__parse_ctx, __ctx, __handle_.__ptr_);
   }
 
+  _LIBCPP_HIDE_FROM_ABI explicit handle(typename __basic_format_arg_value<_Context>::__handle __handle) noexcept
+      : __handle_(__handle) {}
+
 private:
-  const void* __ptr_;
-  void (*__format_)(basic_format_parse_context<char_type>&, _Context&, const void*);
-
-  template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI explicit handle(const _Tp& __v) noexcept
-      : __ptr_(_VSTD::addressof(__v)),
-        __format_([](basic_format_parse_context<char_type>& __parse_ctx, _Context& __ctx, const void* __ptr) {
-          typename _Context::template formatter_type<_Tp> __f;
-          __parse_ctx.advance_to(__f.parse(__parse_ctx));
-          __ctx.advance_to(__f.format(*static_cast<const _Tp*>(__ptr), __ctx));
-        }) {}
+  typename __basic_format_arg_value<_Context>::__handle __handle_;
 };
 
 #endif //_LIBCPP_STD_VER > 17

diff  --git a/libcxx/include/__format/format_arg_store.h b/libcxx/include/__format/format_arg_store.h
new file mode 100644
index 000000000000..4a3fcd4ff339
--- /dev/null
+++ b/libcxx/include/__format/format_arg_store.h
@@ -0,0 +1,251 @@
+// -*- 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_FORMAT_ARG_STORE_H
+#define _LIBCPP___FORMAT_FORMAT_ARG_STORE_H
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#include <__config>
+#include <__format/concepts.h>
+#include <__format/format_arg.h>
+#include <__iterator/data.h>
+#include <__iterator/size.h>
+#include <cstring>
+#include <string>
+#include <string_view>
+#include <type_traits>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 17
+
+namespace __format {
+
+/// \returns The @c __arg_t based on the type of the formatting argument.
+///
+/// \pre \c __formattable<_Tp, typename _Context::char_type>
+template <class _Context, class _Tp>
+consteval __arg_t __determine_arg_t();
+
+// Boolean
+template <class, same_as<bool> _Tp>
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__boolean;
+}
+
+// Char
+template <class _Context, same_as<typename _Context::char_type> _Tp>
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__char_type;
+}
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+template <class _Context, class _CharT>
+  requires(same_as<typename _Context::char_type, wchar_t> && same_as<_CharT, char>)
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__char_type;
+}
+#  endif
+
+// Signed integers
+template <class, __libcpp_signed_integer _Tp>
+consteval __arg_t __determine_arg_t() {
+  if constexpr (sizeof(_Tp) <= sizeof(int))
+    return __arg_t::__int;
+  else if constexpr (sizeof(_Tp) <= sizeof(long long))
+    return __arg_t::__long_long;
+#  ifndef _LIBCPP_HAS_NO_INT128
+  else if constexpr (sizeof(_Tp) == sizeof(__int128_t))
+    return __arg_t::__i128;
+#  endif
+  else
+    static_assert(sizeof(_Tp) == 0, "an unsupported signed integer was used");
+}
+
+// Unsigned integers
+template <class, __libcpp_unsigned_integer _Tp>
+consteval __arg_t __determine_arg_t() {
+  if constexpr (sizeof(_Tp) <= sizeof(unsigned))
+    return __arg_t::__unsigned;
+  else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long))
+    return __arg_t::__unsigned_long_long;
+#  ifndef _LIBCPP_HAS_NO_INT128
+  else if constexpr (sizeof(_Tp) == sizeof(__uint128_t))
+    return __arg_t::__u128;
+#  endif
+  else
+    static_assert(sizeof(_Tp) == 0, "an unsupported unsigned integer was used");
+}
+
+// Floating-point
+template <class, same_as<float> _Tp>
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__float;
+}
+template <class, same_as<double> _Tp>
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__double;
+}
+template <class, same_as<long double> _Tp>
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__long_double;
+}
+
+// Char pointer
+template <class _Context, class _Tp>
+  requires(same_as<typename _Context::char_type*, _Tp> || same_as<const typename _Context::char_type*, _Tp>)
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__const_char_type_ptr;
+}
+
+// Char array
+template <class _Context, class _Tp>
+  requires(is_array_v<_Tp> && same_as<_Tp, typename _Context::char_type[extent_v<_Tp>]>)
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__string_view;
+}
+
+// String view
+template <class _Context, class _Tp>
+  requires(same_as<typename _Context::char_type, typename _Tp::value_type> &&
+           same_as<_Tp, basic_string_view<typename _Tp::value_type, typename _Tp::traits_type>>)
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__string_view;
+}
+
+// String
+template <class _Context, class _Tp>
+  requires(
+      same_as<typename _Context::char_type, typename _Tp::value_type> &&
+      same_as<_Tp, basic_string<typename _Tp::value_type, typename _Tp::traits_type, typename _Tp::allocator_type>>)
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__string_view;
+}
+
+// Pointers
+template <class, class _Ptr>
+  requires(same_as<_Ptr, void*> || same_as<_Ptr, const void*> || same_as<_Ptr, nullptr_t>)
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__ptr;
+}
+
+// Handle
+//
+// Note this version can't be constrained avoiding ambiguous overloads.
+// That means it can be instantiated by disabled formatters. To solve this, a
+// constrained version for not formattable formatters is added. That overload
+// is marked as deleted to fail creating a storage type for disabled formatters.
+template <class _Context, class _Tp>
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__handle;
+}
+
+template <class _Context, class _Tp>
+  requires(!__formattable<_Tp, typename _Context::char_type>)
+consteval __arg_t __determine_arg_t() = delete;
+
+template <class _Context, class _Tp>
+_LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp&& __value) noexcept {
+  constexpr __arg_t __arg = __determine_arg_t<_Context, remove_cvref_t<_Tp>>();
+  static_assert(__arg != __arg_t::__none);
+
+  // Not all types can be used to directly initialize the
+  // __basic_format_arg_value.  First handle all types needing adjustment, the
+  // final else requires no adjustment.
+  if constexpr (__arg == __arg_t::__char_type)
+    // On some platforms initializing a wchar_t from a char is a narrowing
+    // conversion.
+    return basic_format_arg<_Context>{__arg, static_cast<typename _Context::char_type>(__value)};
+  else if constexpr (__arg == __arg_t::__int)
+    return basic_format_arg<_Context>{__arg, static_cast<int>(__value)};
+  else if constexpr (__arg == __arg_t::__long_long)
+    return basic_format_arg<_Context>{__arg, static_cast<long long>(__value)};
+  else if constexpr (__arg == __arg_t::__unsigned)
+    return basic_format_arg<_Context>{__arg, static_cast<unsigned>(__value)};
+  else if constexpr (__arg == __arg_t::__unsigned_long_long)
+    return basic_format_arg<_Context>{__arg, static_cast<unsigned long long>(__value)};
+  else if constexpr (__arg == __arg_t::__string_view)
+    // When the _Traits or _Allocator are 
diff erent an implicit conversion will
+    // fail.
+    //
+    // Note since the input can be an array use the non-member functions to
+    // extract the constructor arguments.
+    return basic_format_arg<_Context>{
+        __arg, basic_string_view<typename _Context::char_type>{_VSTD::data(__value), _VSTD::size(__value)}};
+  else if constexpr (__arg == __arg_t::__ptr)
+    return basic_format_arg<_Context>{__arg, static_cast<const void*>(__value)};
+  else if constexpr (__arg == __arg_t::__handle)
+    return basic_format_arg<_Context>{
+        __arg, typename __basic_format_arg_value<_Context>::__handle{_VSTD::forward<_Tp>(__value)}};
+  else
+    return basic_format_arg<_Context>{__arg, __value};
+}
+
+template <class _Context, class... _Args>
+_LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values,
+                                                   _Args&&... __args) noexcept {
+  int __shift = 0;
+  (
+      [&] {
+        basic_format_arg<_Context> __arg = __create_format_arg<_Context>(_VSTD::forward<_Args>(__args));
+        if (__shift != 0)
+          __types |= static_cast<uint64_t>(__arg.__type_) << __shift;
+        else
+          // Assigns the initial value.
+          __types = static_cast<uint64_t>(__arg.__type_);
+        __shift += __packed_arg_t_bits;
+        *__values++ = __arg.__value_;
+      }(),
+      ...);
+}
+
+template <class _Context, class... _Args>
+_LIBCPP_HIDE_FROM_ABI void __store_basic_format_arg(basic_format_arg<_Context>* __data, _Args&&... __args) noexcept {
+  ([&] { *__data++ = __create_format_arg<_Context>(_VSTD::forward<_Args>(__args)); }(), ...);
+}
+
+template <class _Context, size_t N>
+struct __packed_format_arg_store {
+  __basic_format_arg_value<_Context> __values_[N];
+  uint64_t __types_;
+};
+
+template <class _Context, size_t N>
+struct __unpacked_format_arg_store {
+  basic_format_arg<_Context> __args_[N];
+};
+
+} // namespace __format
+
+template <class _Context, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS __format_arg_store {
+  _LIBCPP_HIDE_FROM_ABI
+  __format_arg_store(_Args&&... __args) noexcept {
+    if constexpr (sizeof...(_Args) != 0) {
+      if constexpr (__format::__use_packed_format_arg_store(sizeof...(_Args)))
+        __format::__create_packed_storage(__storage.__types_, __storage.__values_, _VSTD::forward<_Args>(__args)...);
+      else
+        __format::__store_basic_format_arg<_Context>(__storage.__args_, _VSTD::forward<_Args>(__args)...);
+    }
+  }
+
+  using _Storage = conditional_t<__format::__use_packed_format_arg_store(sizeof...(_Args)),
+                                 __format::__packed_format_arg_store<_Context, sizeof...(_Args)>,
+                                 __format::__unpacked_format_arg_store<_Context, sizeof...(_Args)>>;
+
+  _Storage __storage;
+};
+
+#endif //_LIBCPP_STD_VER > 17
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FORMAT_FORMAT_ARG_STORE_H

diff  --git a/libcxx/include/__format/format_args.h b/libcxx/include/__format/format_args.h
index 40dde9840067..d90dc50acbd1 100644
--- a/libcxx/include/__format/format_args.h
+++ b/libcxx/include/__format/format_args.h
@@ -12,8 +12,11 @@
 
 #include <__availability>
 #include <__config>
+#include <__format/format_arg.h>
+#include <__format/format_arg_store.h>
 #include <__format/format_fwd.h>
 #include <cstddef>
+#include <cstdint>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -26,29 +29,47 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Context>
 class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_args {
 public:
-  // TODO FMT Implement [format.args]/5
-  // [Note 1: Implementations are encouraged to optimize the representation of
-  // basic_format_args for small number of formatting arguments by storing
-  // indices of type alternatives separately from values and packing the
-  // former. - end note]
-  // Note: Change  __format_arg_store to use a built-in array.
   _LIBCPP_HIDE_FROM_ABI basic_format_args() noexcept = default;
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI basic_format_args(
-      const __format_arg_store<_Context, _Args...>& __store) noexcept
-      : __size_(sizeof...(_Args)), __data_(__store.__args.data()) {}
+  _LIBCPP_HIDE_FROM_ABI basic_format_args(const __format_arg_store<_Context, _Args...>& __store) noexcept
+      : __size_(sizeof...(_Args)) {
+    if constexpr (sizeof...(_Args) != 0) {
+      if constexpr (__format::__use_packed_format_arg_store(sizeof...(_Args))) {
+        __values_ = __store.__storage.__values_;
+        __types_ = __store.__storage.__types_;
+      } else
+        __args_ = __store.__storage.__args_;
+    }
+  }
 
   _LIBCPP_HIDE_FROM_ABI
   basic_format_arg<_Context> get(size_t __id) const noexcept {
-    return __id < __size_ ? __data_[__id] : basic_format_arg<_Context>{};
+    if (__id >= __size_)
+      return basic_format_arg<_Context>{};
+
+    if (__format::__use_packed_format_arg_store(__size_))
+      return basic_format_arg<_Context>{__format::__get_packed_type(__types_, __id), __values_[__id]};
+
+    return __args_[__id];
   }
 
   _LIBCPP_HIDE_FROM_ABI size_t __size() const noexcept { return __size_; }
 
 private:
   size_t __size_{0};
-  const basic_format_arg<_Context>* __data_{nullptr};
+  // [format.args]/5
+  // [Note 1: Implementations are encouraged to optimize the representation of
+  // basic_format_args for small number of formatting arguments by storing
+  // indices of type alternatives separately from values and packing the
+  // former. - end note]
+  union {
+    struct {
+      const __basic_format_arg_value<_Context>* __values_;
+      uint64_t __types_;
+    };
+    const basic_format_arg<_Context>* __args_;
+  };
 };
 
 #endif //_LIBCPP_STD_VER > 17

diff  --git a/libcxx/include/__format/format_fwd.h b/libcxx/include/__format/format_fwd.h
index 908d10dbc3b6..f7c72e21211a 100644
--- a/libcxx/include/__format/format_fwd.h
+++ b/libcxx/include/__format/format_fwd.h
@@ -25,9 +25,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Context>
 class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg;
 
-template <class _Context, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS __format_arg_store;
-
 template <class _OutIt, class _CharT>
   requires output_iterator<_OutIt, const _CharT&>
 class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_context;

diff  --git a/libcxx/include/format b/libcxx/include/format
index 070bfe286f0d..55ce2b18a935 100644
--- a/libcxx/include/format
+++ b/libcxx/include/format
@@ -130,6 +130,7 @@ namespace std {
 #include <__format/concepts.h>
 #include <__format/enable_insertable.h>
 #include <__format/format_arg.h>
+#include <__format/format_arg_store.h>
 #include <__format/format_args.h>
 #include <__format/format_context.h>
 #include <__format/format_error.h>
@@ -176,22 +177,25 @@ using format_args = basic_format_args<format_context>;
 using wformat_args = basic_format_args<wformat_context>;
 #endif
 
+// TODO FMT This helper wrapper can probably be removed after P2418 has been
+// implemented.
 template <class _Context, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS __format_arg_store {
-  // TODO FMT Use a built-in array.
-  array<basic_format_arg<_Context>, sizeof...(_Args)> __args;
-};
+_LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...>
+__make_format_args(_Args&&... __args) {
+  return _VSTD::__format_arg_store<_Context, _Args...>(
+      _VSTD::forward<_Args>(__args)...);
+}
 
+// TODO FMT After P2418 specify the return type instead of using auto.
 template <class _Context = format_context, class... _Args>
-_LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...>
-make_format_args(const _Args&... __args) {
-  return {basic_format_arg<_Context>(__args)...};
+_LIBCPP_HIDE_FROM_ABI auto make_format_args(const _Args&... __args) {
+  return _VSTD::__make_format_args<_Context>(__args...);
 }
 
 #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+// TODO FMT After P2418 specify the return type instead of using auto.
 template <class... _Args>
-_LIBCPP_HIDE_FROM_ABI __format_arg_store<wformat_context, _Args...>
-make_wformat_args(const _Args&... __args) {
+_LIBCPP_HIDE_FROM_ABI auto make_wformat_args(const _Args&... __args) {
   return _VSTD::make_format_args<wformat_context>(__args...);
 }
 #endif

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index ef97b81e0fce..87969556a0a7 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -545,6 +545,7 @@ module std [system] {
       module concepts                 { private header "__format/concepts.h" }
       module enable_insertable        { private header "__format/enable_insertable.h" }
       module format_arg               { private header "__format/format_arg.h" }
+      module format_arg_store         { private header "__format/format_arg_store.h" }
       module format_args              { private header "__format/format_args.h" }
       module format_context {
         private header "__format/format_context.h"

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index cfeddfa4411f..057aaa4e11ad 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -244,6 +244,7 @@ END-SCRIPT
 #include <__format/concepts.h> // expected-error@*:* {{use of private header from outside its module: '__format/concepts.h'}}
 #include <__format/enable_insertable.h> // expected-error@*:* {{use of private header from outside its module: '__format/enable_insertable.h'}}
 #include <__format/format_arg.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_arg.h'}}
+#include <__format/format_arg_store.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_arg_store.h'}}
 #include <__format/format_args.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_args.h'}}
 #include <__format/format_context.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_context.h'}}
 #include <__format/format_error.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_error.h'}}

diff  --git a/libcxx/test/libcxx/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp b/libcxx/test/libcxx/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp
index 4a671e952c02..0f460b69d167 100644
--- a/libcxx/test/libcxx/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp
@@ -27,9 +27,11 @@
 
 template <class Context, class To, class From>
 void test(From value) {
-  auto format_args = std::make_format_args<Context>(value);
-  assert(format_args.__args.size() == 1);
-  assert(format_args.__args[0]);
+  auto store = std::make_format_args<Context>(value);
+  std::basic_format_args<Context> format_args{store};
+
+  assert(format_args.__size() == 1);
+  assert(format_args.get(0));
 
   auto result = std::visit_format_arg(
       [v = To(value)](auto a) -> To {
@@ -41,7 +43,7 @@ void test(From value) {
           return {};
         }
       },
-      format_args.__args[0]);
+      format_args.get(0));
 
   using ct = std::common_type_t<From, To>;
   assert(static_cast<ct>(result) == static_cast<ct>(value));
@@ -53,9 +55,11 @@ void test(From value) {
 // template argument.
 template <class Context, class From>
 void test_string_view(From value) {
-  auto format_args = std::make_format_args<Context>(value);
-  assert(format_args.__args.size() == 1);
-  assert(format_args.__args[0]);
+  auto store = std::make_format_args<Context>(value);
+  std::basic_format_args<Context> format_args{store};
+
+  assert(format_args.__size() == 1);
+  assert(format_args.get(0));
 
   using CharT = typename Context::char_type;
   using To = std::basic_string_view<CharT>;
@@ -70,7 +74,7 @@ void test_string_view(From value) {
           return {};
         }
       },
-      format_args.__args[0]);
+      format_args.get(0));
 
   assert(std::equal(value.begin(), value.end(), result.begin(), result.end()));
 }
@@ -351,15 +355,11 @@ void test() {
   test<Context, const void*>(static_cast<const void*>(&ci));
 }
 
-void test() {
+int main(int, char**) {
   test<char>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<wchar_t>();
 #endif
-}
-
-int main(int, char**) {
-  test();
 
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp
deleted file mode 100644
index 3054625846ac..000000000000
--- a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-//===----------------------------------------------------------------------===//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: libcpp-has-no-incomplete-format
-
-// <format>
-
-// template<class Context, class... Args>
-// struct format-arg-store {      // exposition only
-//   array<basic_format_arg<Context>, sizeof...(Args)> args;
-// };
-//
-// Note more testing is done in the unit test for:
-// template<class Visitor, class Context>
-//   see below visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg);
-
-#include <format>
-#include <cassert>
-#include <cstddef>
-#include <type_traits>
-
-#include "test_macros.h"
-
-template <class CharT>
-void test() {
-  using Context = std::basic_format_context<CharT*, CharT>;
-  {
-    [[maybe_unused]] auto store = std::make_format_args<Context>();
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store), std::__format_arg_store<Context>>);
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store.__args),
-                       std::array<std::basic_format_arg<Context>, 0>>);
-    LIBCPP_ASSERT(store.__args.size() == 0);
-  }
-  {
-    [[maybe_unused]] auto store = std::make_format_args<Context>(1);
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store), std::__format_arg_store<Context, int>>);
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store.__args),
-                       std::array<std::basic_format_arg<Context>, 1>>);
-    LIBCPP_ASSERT(store.__args.size() == 1);
-  }
-  {
-    [[maybe_unused]] auto store = std::make_format_args<Context>(1, 'c');
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store),
-                       std::__format_arg_store<Context, int, char>>);
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store.__args),
-                       std::array<std::basic_format_arg<Context>, 2>>);
-    LIBCPP_ASSERT(store.__args.size() == 2);
-  }
-  {
-    [[maybe_unused]] auto store = std::make_format_args<Context>(1, 'c', nullptr);
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store),
-                       std::__format_arg_store<Context, int, char, std::nullptr_t>>);
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store.__args),
-                       std::array<std::basic_format_arg<Context>, 3>>);
-    LIBCPP_ASSERT(store.__args.size() == 3);
-  }
-}
-
-void test() {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-}
-
-int main(int, char**) {
-  test();
-
-  return 0;
-}

diff  --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp
index 8ce362292127..753b27f4efc5 100644
--- a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp
@@ -22,18 +22,9 @@
 #include "test_macros.h"
 
 int main(int, char**) {
-  using Context [[maybe_unused]] = std::basic_format_context<
-      std::back_insert_iterator<std::basic_string<char>>, char>;
-
-  [[maybe_unused]] auto value = std::make_format_args(42, nullptr, false, 1.0);
-
-  LIBCPP_ASSERT(value.__args.size() == 4);
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[0], 42));
-  // Note [format.arg]/11 specifies a nullptr is stored as a const void*.
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[1],
-                                      static_cast<const void*>(nullptr)));
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[2], false));
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[3], 1.0));
+  using Context [[maybe_unused]] = std::basic_format_context< std::back_insert_iterator<std::basic_string<char>>, char>;
+
+  std::make_format_args(42, nullptr, false, 1.0);
 
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp
index 51869be9842e..f3740e240add 100644
--- a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp
@@ -22,18 +22,10 @@
 #include "test_macros.h"
 
 int main(int, char**) {
-  using Context [[maybe_unused]] = std::basic_format_context<
-      std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>;
-
-  [[maybe_unused]] auto value = std::make_wformat_args(42, nullptr, false, 1.0);
-
-  LIBCPP_ASSERT(value.__args.size() == 4);
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[0], 42));
-  // Note [format.arg]/11 specifies a nullptr is stored as a const void*.
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[1],
-                                      static_cast<const void*>(nullptr)));
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[2], false));
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[3], 1.0));
+  using Context [[maybe_unused]] =
+      std::basic_format_context<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>;
+
+  std::make_wformat_args(42, nullptr, false, 1.0);
 
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp
index 4731c66ff093..ecda3d1bb0b0 100644
--- a/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp
@@ -22,17 +22,6 @@
 
 #include "test_macros.h"
 
-void test(const auto& store) {
-#ifdef _LIBCPP_VERSION
-  for (const auto& arg : store.__args) {
-    assert(arg);
-    assert(static_cast<bool>(arg));
-  }
-#else
-  (void)store;
-#endif
-}
-
 template <class CharT>
 void test() {
   using Context = std::basic_format_context<CharT*, CharT>;
@@ -43,19 +32,13 @@ void test() {
     ASSERT_NOEXCEPT(static_cast<bool>(format_arg));
     assert(!static_cast<bool>(format_arg));
   }
-  test(std::make_format_args<Context>());
-  test(std::make_format_args<Context>(1));
-  test(std::make_format_args<Context>(1, 'c'));
-  test(std::make_format_args<Context>(1, 'c', nullptr));
 }
 
-void test() {
+int main(int, char**) {
   test<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<wchar_t>();
-}
-
-int main(int, char**) {
-  test();
+#endif
 
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp
index f1117bd79223..17d8908b2a23 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp
@@ -49,13 +49,6 @@ int main(int, char**) {
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>();
 #endif
-#ifndef TEST_HAS_NO_CHAR8_T
-  test<std::back_insert_iterator<std::basic_string<char8_t>>, char8_t>();
-#endif
-#ifndef TEST_HAS_NO_UNICODE_CHARS
-  test<std::back_insert_iterator<std::basic_string<char16_t>>, char16_t>();
-  test<std::back_insert_iterator<std::basic_string<char32_t>>, char32_t>();
-#endif
 
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp
index 07a1be5b274b..65938ea6d9c7 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp
@@ -121,22 +121,11 @@ void test() {
 #endif
 }
 
-void test() {
+int main(int, char**) {
   test<std::back_insert_iterator<std::basic_string<char>>, char>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>();
 #endif
-#ifndef TEST_HAS_NO_CHAR8_T
-  test<std::back_insert_iterator<std::basic_string<char8_t>>, char8_t>();
-#endif
-#ifndef TEST_HAS_NO_UNICODE_CHARS
-  test<std::back_insert_iterator<std::basic_string<char16_t>>, char16_t>();
-  test<std::back_insert_iterator<std::basic_string<char32_t>>, char32_t>();
-#endif
-}
-
-int main(int, char**) {
-  test();
 
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp
index c802a48e275c..a8c353578a59 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp
@@ -77,21 +77,11 @@ void test() {
   }
 }
 
-void test() {
+int main(int, char**) {
   test<std::back_insert_iterator<std::basic_string<char>>, char>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>();
 #endif
-#ifndef TEST_HAS_NO_CHAR8_T
-  test<std::back_insert_iterator<std::basic_string<char8_t>>, char8_t>();
-#endif
-#ifndef TEST_HAS_NO_UNICODE_CHARS
-  test<std::back_insert_iterator<std::basic_string<char16_t>>, char16_t>();
-  test<std::back_insert_iterator<std::basic_string<char32_t>>, char32_t>();
-#endif
-}
-int main(int, char**) {
-  test();
 
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.bool.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.bool.pass.cpp
index bd48281a11d3..75a83a043bb5 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.bool.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.bool.pass.cpp
@@ -7,6 +7,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // <format>
 

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.c_string.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.c_string.pass.cpp
index 21b906eba065..293671338419 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.c_string.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.c_string.pass.cpp
@@ -7,6 +7,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // <format>
 

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_point.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_point.pass.cpp
index 25e26f9af26d..38e891a1f147 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_point.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_point.pass.cpp
@@ -7,6 +7,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // <format>
 

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.handle.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.handle.pass.cpp
index 4f3c94eaabfc..75b68dc4800d 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.handle.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.handle.pass.cpp
@@ -7,6 +7,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // <format>
 

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.pointer.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.pointer.pass.cpp
index ef3a1fd50027..470f2c5af391 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.pointer.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.pointer.pass.cpp
@@ -7,6 +7,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // <format>
 

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp
index 1c43224e7f5d..41f99ea7014f 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp
@@ -7,6 +7,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // <format>
 

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 102bc4cfea90..0c6993595b5e 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_tests.h
+++ b/libcxx/test/std/utilities/format/format.functions/format_tests.h
@@ -2464,6 +2464,24 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
   check.template operator()<"hello {0:} {1:}">(SV("hello false true"), false, true);
   check.template operator()<"hello {1:} {0:}">(SV("hello true false"), false, true);
 
+  // *** Test many arguments ***
+
+  // [format.args]/1
+  // An instance of basic_format_args provides access to formatting arguments.
+  // Implementations should optimize the representation of basic_format_args
+  // for a small number of formatting arguments.
+  //
+  // These's no guidances what "a small number of formatting arguments" is.
+  // - fmtlib uses a 15 elements
+  // - libc++ uses 12 elements
+  // - MSVC STL uses a 
diff erent approach regardless of the number of arguments
+  // - libstdc++ has no implementation yet
+  // fmtlib and libc++ use a similar approach, this approach can support 16
+  // elements (based on design choices both support less elements). This test
+  // makes sure "the large number of formatting arguments" code path is tested.
+  check.template operator()<"{}{}{}{}{}{}{}{}{}{}\t{}{}{}{}{}{}{}{}{}{}">(SV("1234567890\t1234567890"), 1, 2, 3, 4, 5,
+                                                                          6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+
   // ** Test invalid format strings ***
   check_exception("The format string terminates at a '{'", SV("{"));
   check_exception("The replacement field misses a terminating '}'", SV("{:"), 42);


        


More information about the libcxx-commits mailing list