diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -203,5 +203,5 @@ "","","","","","" "`P2372R3 `__","LWG","Fixing locale handling in chrono formatters","October 2021","","" "`P2415R2 `__","LWG","What is a ``view``","October 2021","|Complete|","14.0" -"`P2418R2 `__","LWG","Add support for ``std::generator``-like types to ``std::format``","October 2021","","" +"`P2418R2 `__","LWG","Add support for ``std::generator``-like types to ``std::format``","October 2021","|Partial|","" "`P2432R1 `__","LWG","Fix ``istream_view``","October 2021","","" diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv --- a/libcxx/docs/Status/Cxx2bIssues.csv +++ b/libcxx/docs/Status/Cxx2bIssues.csv @@ -43,7 +43,7 @@ "`3466 `__","Specify the requirements for ``promise``/``future``/``shared_future`` consistently","November 2020","|Nothing To Do|","" "`3467 `__","``bool`` can't be an integer-like type","November 2020","|Complete|","14.0" "`3472 `__","``counted_iterator`` is missing preconditions","November 2020","|Complete|","14.0","|ranges|" -"`3473 `__","Normative encouragement in non-normative note","November 2020","|Nothing To Do|","","|format|" +"`3473 `__","Normative encouragement in non-normative note","November 2020","|Complete|","15.0","|format|" "`3474 `__","Nesting ``join_views`` is broken because of CTAD","November 2020","","","|ranges|" "`3476 `__","``thread`` and ``jthread`` constructors require that the parameters be move-constructible but never move construct the parameters","November 2020","","" "`3477 `__","Simplify constraints for ``semiregular-box``","November 2020","","","|ranges|" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -186,6 +186,7 @@ __filesystem/u8path.h __format/concepts.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 --- a/libcxx/include/__format/format_arg.h +++ b/libcxx/include/__format/format_arg.h @@ -35,6 +35,14 @@ /// /// @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,149 @@ __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 < __packed_types_max) + __types >>= __packed_arg_t_bits * (__packed_types_max - 1 - __id); + + return static_cast<__format::__arg_t>(__types & __packed_arg_t_mask); +} + } // namespace __format template -_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 __basic_format_arg_value { + using _CharT = typename _Context::char_type; + +public: + /// Contains the implementation for basic_format_arg::handle. + struct __handle { + template + _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(), 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(__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() : __monostate_() {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(bool __value) : __boolean_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(_CharT __value) : __char_type_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(int __value) : __int_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(unsigned __value) : __unsigned_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(long long __value) : __long_long_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(unsigned long long __value) : __unsigned_long_long_(__value) {} +# ifndef _LIBCPP_HAS_NO_INT128 + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__int128_t __value) : __i128_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__uint128_t __value) : __u128_(__value) {} +# endif + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(float __value) : __float_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(double __value) : __double_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(long double __value) : __long_double_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(const _CharT* __value) : __const_char_type_ptr_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(basic_string_view<_CharT> __value) : __string_view_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(const void* __value) : __ptr_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__handle __value) : __handle_(__value) {} +}; + template class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg { public: @@ -131,146 +230,28 @@ // .format(declval(), declval()) // shall be well-formed when treated as an unevaluated operand. - template - _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT friend __format_arg_store<_Ctx, _Args...> - make_format_args(const _Args&...); - - template - _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 __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(bool __v) noexcept - : __boolean(__v), __type_(__format::__arg_t::__boolean) {} - - template - _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(_Tp __v) noexcept - requires(same_as<_Tp, char_type> || - (same_as<_Tp, char> && same_as)) - : __char_type(__v), __type_(__format::__arg_t::__char_type) {} - - template <__libcpp_signed_integer _Tp> - _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(_Tp __v) noexcept { - if constexpr (sizeof(_Tp) <= sizeof(int)) { - __int = static_cast(__v); - __type_ = __format::__arg_t::__int; - } else if constexpr (sizeof(_Tp) <= sizeof(long long)) { - __long_long = static_cast(__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(_Tp __v) noexcept { - if constexpr (sizeof(_Tp) <= sizeof(unsigned)) { - __unsigned = static_cast(__v); - __type_ = __format::__arg_t::__unsigned; - } else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long)) { - __unsigned_long_long = static_cast(__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 - _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg( - basic_string_view __s) noexcept - : __string_view{__s.data(), __s.size()}, - __type_(__format::__arg_t::__string_view) {} - - template - _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg( - const basic_string& __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 - requires is_void_v<_Tp> _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(_Tp* __p) noexcept - : __ptr(__p), __type_(__format::__arg_t::__ptr) {} - - template - _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 _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& __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&, _Context&, const void*); - - template - _LIBCPP_HIDE_FROM_ABI explicit handle(const _Tp& __v) noexcept - : __ptr_(_VSTD::addressof(__v)), - __format_([](basic_format_parse_context& __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(__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 --- /dev/null +++ b/libcxx/include/__format/format_arg_store.h @@ -0,0 +1,283 @@ +// -*- 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 +#include +#include +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +namespace __format { + +/// \returns the type based in the stored type +/// +/// \pre \c _Tp is a valid type for the "variant". +/// +/// \note Whether the \c char of \c wchar_t type is "active" depends on the +/// context used. But since this function doesn't need to do validation there's +/// no dependency on the context. +template +consteval __arg_t __get_arg_t() { + if constexpr (same_as<_Tp, bool>) + return __arg_t::__boolean; + else if constexpr (same_as<_Tp, char> _LIBCPP_IF_WIDE_CHARACTERS(|| same_as<_Tp, wchar_t>)) + return __arg_t::__char_type; + else if constexpr (same_as<_Tp, int>) + return __arg_t::__int; + else if constexpr (same_as<_Tp, long long>) + return __arg_t::__long_long; +# ifndef _LIBCPP_HAS_NO_INT128 + else if constexpr (same_as<_Tp, __int128_t>) + return __arg_t::__i128; +# endif + else if constexpr (same_as<_Tp, unsigned>) + return __arg_t::__unsigned; + else if constexpr (same_as<_Tp, unsigned long long>) + return __arg_t::__unsigned_long_long; +# ifndef _LIBCPP_HAS_NO_INT128 + else if constexpr (same_as<_Tp, __uint128_t>) + return __arg_t::__u128; +# endif + else if constexpr (same_as<_Tp, float>) + return __arg_t::__float; + else if constexpr (same_as<_Tp, double>) + return __arg_t::__double; + else if constexpr (same_as<_Tp, long double>) + return __arg_t::__long_double; + else if constexpr (same_as<_Tp, const char*> _LIBCPP_IF_WIDE_CHARACTERS(|| same_as<_Tp, const wchar_t*>)) + return __arg_t::__const_char_type_ptr; + else if constexpr (same_as<_Tp, basic_string_view> _LIBCPP_IF_WIDE_CHARACTERS( + || same_as<_Tp, basic_string_view>)) + return __arg_t::__string_view; + else if constexpr (same_as<_Tp, const void*>) + return __arg_t::__ptr; + else + return __arg_t::__handle; +} + +template +struct __storage_type { + using type = _Tp; + static constexpr __arg_t __arg = __get_arg_t<_Tp>(); +}; + +template +consteval auto __make_storage_type(); + +// The types that: +// - are valid regardless of the context +// - are stored without conversion +template + requires(same_as<_Tp, bool> || same_as<_Tp, float> || same_as<_Tp, double> || same_as<_Tp, long double>) +consteval auto __make_storage_type() { + return __storage_type<_Tp>{}; +} + +// The character class conversions. +template + requires(same_as) +consteval auto __make_storage_type() { + return __storage_type<_CharT>{}; +} + +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +template + requires(same_as && same_as<_CharT, char>) +consteval auto __make_storage_type() { + return __storage_type{}; +} +# endif + +// The signed integer types +template +consteval auto __make_storage_type() { + if constexpr (sizeof(_Tp) <= sizeof(int)) + return __storage_type{}; + else if constexpr (sizeof(_Tp) <= sizeof(long long)) + return __storage_type{}; +# ifndef _LIBCPP_HAS_NO_INT128 + else if constexpr (sizeof(_Tp) == sizeof(__int128_t)) + return __storage_type<__int128_t>{}; +# endif + else + static_assert(sizeof(_Tp) == 0, "an unsupported signed integer was used"); +} + +// The unsigned integer types +template +consteval auto __make_storage_type() { + if constexpr (sizeof(_Tp) <= sizeof(unsigned)) + return __storage_type{}; + else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long)) + return __storage_type{}; +# ifndef _LIBCPP_HAS_NO_INT128 + else if constexpr (sizeof(_Tp) == sizeof(__uint128_t)) + return __storage_type<__uint128_t>{}; +# endif + else + static_assert(sizeof(_Tp) == 0, "an unsupported unsigned integer was used"); +} + +// Char pointer +template + requires(same_as || same_as) +consteval auto __make_storage_type() { + return __storage_type{}; +} + +// Char array +template + requires(is_array_v<_Tp> && same_as<_Tp, typename _Context::char_type[extent_v<_Tp>]>) +consteval auto __make_storage_type() { + return __storage_type>{}; +} + +// String view +template + requires(same_as && + same_as<_Tp, basic_string_view>) +consteval auto __make_storage_type() { + return __storage_type>{}; +} + +// String +template + requires( + same_as && + same_as<_Tp, basic_string>) +consteval auto __make_storage_type() { + return __storage_type>{}; +} + +// Pointers +template + requires(same_as<_Ptr, void*> || same_as<_Ptr, const void*> || same_as<_Ptr, nullptr_t>) +consteval auto __make_storage_type() { + return __storage_type{}; +} + +// 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 +consteval auto __make_storage_type() { + return __storage_type::handle>{}; +} + +template + requires(!__formattable<_Tp, typename _Context::char_type>) +consteval auto __make_storage_type() = delete; + +template +struct _LIBCPP_TEMPLATE_VIS __storage { + _Sp __value; +}; + +template +_LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp&& __value) noexcept { + using _Sp = typename decltype(__make_storage_type<_Context, remove_cvref_t<_Tp>>())::type; + if constexpr (same_as<_Sp, basic_string_view>) + // When the _Traits or _Allocator are different an implicit conversion will + // fail. Instead of adding special cases to __storage<_Sp> handle the + // special case here. + // + // Note since the input can be an array use the non-member functions to + // extract the constructor arguments. + return basic_format_arg<_Context>{__get_arg_t<_Sp>(), basic_string_view{ + _VSTD::data(__value), _VSTD::size(__value)}}; + else if constexpr (same_as<_Sp, typename basic_format_arg<_Context>::handle>) + return basic_format_arg<_Context>{ + __get_arg_t<_Sp>(), typename __basic_format_arg_value<_Context>::__handle{_VSTD::forward<_Tp>(__value)}}; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + else if constexpr (same_as<_Sp, wchar_t> && same_as, char>) + // On some platforms initializing a wchar_t from a char is a narrowing conversion. + return basic_format_arg<_Context>{__get_arg_t<_Sp>(), static_cast(__value)}; +# endif + else { + static_assert(is_trivially_copyable_v<_Sp>); + __storage<_Sp> __s{_VSTD::forward<_Tp>(__value)}; + return basic_format_arg<_Context>{__get_arg_t<_Sp>(), __s.__value}; + } +} + +template +_LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values, + _Args&&... __args) noexcept { + ( + [&] { + basic_format_arg<_Context> __arg = __create_format_arg<_Context>(_VSTD::forward<_Args>(__args)); + __types <<= __packed_arg_t_bits; + __types |= static_cast(__arg.__type_); + *__values++ = __arg.__value_; + }(), + ...); + + if constexpr (sizeof...(_Args) < __packed_types_max) + __types <<= __packed_arg_t_bits * (__packed_types_max - sizeof...(_Args)); +} + +template +_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 +struct __packed_format_arg_store { + __basic_format_arg_value<_Context> __values_[N]; + uint64_t __types_; +}; + +template +struct __unpacked_format_arg_store { + basic_format_arg<_Context> __args_[N]; +}; + +} // namespace __format + +template +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 --- 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 +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -26,29 +29,47 @@ template 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 - _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 --- a/libcxx/include/__format/format_fwd.h +++ b/libcxx/include/__format/format_fwd.h @@ -25,9 +25,6 @@ template class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg; -template -struct _LIBCPP_TEMPLATE_VIS __format_arg_store; - template 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 --- a/libcxx/include/format +++ b/libcxx/include/format @@ -127,6 +127,7 @@ #include <__debug> #include <__format/concepts.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> @@ -173,22 +174,25 @@ using wformat_args = basic_format_args; #endif +// TODO FMT This helper wrapper can probably be removed after P2418 has been +// implemented. template -struct _LIBCPP_TEMPLATE_VIS __format_arg_store { - // TODO FMT Use a built-in array. - array, 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 -_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 -_LIBCPP_HIDE_FROM_ABI __format_arg_store -make_wformat_args(const _Args&... __args) { +_LIBCPP_HIDE_FROM_ABI auto make_wformat_args(const _Args&... __args) { return _VSTD::make_format_args(__args...); } #endif diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -514,6 +514,7 @@ module __format { module concepts { private header "__format/concepts.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/diagnostics/detail.headers/format/format_arg_store.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/format/format_arg_store.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/format/format_arg_store.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// 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/format_arg_store.h'}} +#include <__format/format_arg_store.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 --- 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 @@ -29,9 +29,11 @@ template void test(From value) { - auto format_args = std::make_format_args(value); - assert(format_args.__args.size() == 1); - assert(format_args.__args[0]); + auto store = std::make_format_args(value); + std::basic_format_args 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 { @@ -43,7 +45,7 @@ return {}; } }, - format_args.__args[0]); + format_args.get(0)); using ct = std::common_type_t; assert(static_cast(result) == static_cast(value)); @@ -55,9 +57,11 @@ // template argument. template void test_string_view(From value) { - auto format_args = std::make_format_args(value); - assert(format_args.__args.size() == 1); - assert(format_args.__args[0]); + auto store = std::make_format_args(value); + std::basic_format_args 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; @@ -72,7 +76,7 @@ return {}; } }, - format_args.__args[0]); + format_args.get(0)); assert(std::equal(value.begin(), value.end(), result.begin(), result.end())); } @@ -353,15 +357,11 @@ test(static_cast(&ci)); } -void test() { +int main(int, char**) { test(); #ifndef TEST_HAS_NO_WIDE_CHARACTERS test(); #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 --- a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp +++ /dev/null @@ -1,85 +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 -// TODO FMT Evaluate gcc-11 status -// UNSUPPORTED: gcc-11 - -// - -// template -// struct format-arg-store { // exposition only -// array, sizeof...(Args)> args; -// }; -// -// Note more testing is done in the unit test for: -// template -// see below visit_format_arg(Visitor&& vis, basic_format_arg arg); - -#include -#include -#include -#include - -#include "test_macros.h" - -template -void test() { - using Context = std::basic_format_context; - { - [[maybe_unused]] auto store = std::make_format_args(); - LIBCPP_STATIC_ASSERT( - std::is_same_v>); - LIBCPP_STATIC_ASSERT( - std::is_same_v, 0>>); - LIBCPP_ASSERT(store.__args.size() == 0); - } - { - [[maybe_unused]] auto store = std::make_format_args(1); - LIBCPP_STATIC_ASSERT( - std::is_same_v>); - LIBCPP_STATIC_ASSERT( - std::is_same_v, 1>>); - LIBCPP_ASSERT(store.__args.size() == 1); - } - { - [[maybe_unused]] auto store = std::make_format_args(1, 'c'); - LIBCPP_STATIC_ASSERT( - std::is_same_v>); - LIBCPP_STATIC_ASSERT( - std::is_same_v, 2>>); - LIBCPP_ASSERT(store.__args.size() == 2); - } - { - [[maybe_unused]] auto store = std::make_format_args(1, 'c', nullptr); - LIBCPP_STATIC_ASSERT( - std::is_same_v>); - LIBCPP_STATIC_ASSERT( - std::is_same_v, 3>>); - LIBCPP_ASSERT(store.__args.size() == 3); - } -} - -void test() { - test(); -#ifndef TEST_HAS_NO_WIDE_CHARACTERS - test(); -#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 --- 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 @@ -24,18 +24,9 @@ #include "test_macros.h" int main(int, char**) { - using Context [[maybe_unused]] = std::basic_format_context< - std::back_insert_iterator>, 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(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>, 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 --- 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 @@ -24,18 +24,10 @@ #include "test_macros.h" int main(int, char**) { - using Context [[maybe_unused]] = std::basic_format_context< - std::back_insert_iterator>, 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(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>, 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 --- 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 @@ -24,17 +24,6 @@ #include "test_macros.h" -void test(const auto& store) { -#ifdef _LIBCPP_VERSION - for (const auto& arg : store.__args) { - assert(arg); - assert(static_cast(arg)); - } -#else - (void)store; -#endif -} - template void test() { using Context = std::basic_format_context; @@ -45,19 +34,13 @@ ASSERT_NOEXCEPT(static_cast(format_arg)); assert(!static_cast(format_arg)); } - test(std::make_format_args()); - test(std::make_format_args(1)); - test(std::make_format_args(1, 'c')); - test(std::make_format_args(1, 'c', nullptr)); } -void test() { +int main(int, char**) { test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS test(); -} - -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 --- 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 @@ -51,13 +51,6 @@ #ifndef TEST_HAS_NO_WIDE_CHARACTERS test>, wchar_t>(); #endif -#ifndef TEST_HAS_NO_CHAR8_T - test>, char8_t>(); -#endif -#ifndef TEST_HAS_NO_UNICODE_CHARS - test>, char16_t>(); - test>, 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 --- 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 @@ -123,22 +123,11 @@ #endif } -void test() { +int main(int, char**) { test>, char>(); #ifndef TEST_HAS_NO_WIDE_CHARACTERS test>, wchar_t>(); #endif -#ifndef TEST_HAS_NO_CHAR8_T - test>, char8_t>(); -#endif -#ifndef TEST_HAS_NO_UNICODE_CHARS - test>, char16_t>(); - test>, 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 --- 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 @@ -79,21 +79,11 @@ } } -void test() { +int main(int, char**) { test>, char>(); #ifndef TEST_HAS_NO_WIDE_CHARACTERS test>, wchar_t>(); #endif -#ifndef TEST_HAS_NO_CHAR8_T - test>, char8_t>(); -#endif -#ifndef TEST_HAS_NO_UNICODE_CHARS - test>, char16_t>(); - test>, char32_t>(); -#endif -} -int main(int, char**) { - test(); return 0; } 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 --- 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 // 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 --- 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 // 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 --- 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 // 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 --- 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 // 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 --- 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 // 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 --- 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 // diff --git a/libcxx/test/std/utilities/format/format.functions/format_tests.h b/libcxx/test/std/utilities/format/format.functions/format_tests.h --- a/libcxx/test/std/utilities/format/format.functions/format_tests.h +++ b/libcxx/test/std/utilities/format/format.functions/format_tests.h @@ -2455,6 +2455,25 @@ check(SV("hello false true"), SV("hello {0:} {1:}"), false, true); check(SV("hello true false"), SV("hello {1:} {0:}"), 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 different 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(SV("1234567890\t1234567890"), + SV("{}{}{}{}{}{}{}{}{}{}\t{}{}{}{}{}{}{}{}{}{}"), 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);