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,11 @@ /// /// @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 format-arg-store. 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 enum class _LIBCPP_ENUM_VIS __arg_t : uint8_t { __none, __boolean, @@ -131,9 +136,8 @@ // .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 + friend class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_args; template _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT friend decltype(auto) @@ -160,93 +164,70 @@ }; __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; + /// The Standard specifies several exposition only constructors. + /// These aren't used, this constructor is used for all types instead. + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(__format::__arg_t __type, const char* __data) noexcept + : __type_(__type) { + switch (__type) { + case __format::__arg_t::__none: + __libcpp_unreachable(); + return; + case __format::__arg_t::__boolean: + memcpy(_VSTD::addressof(__boolean), __data, sizeof(bool)); + return; + case __format::__arg_t::__char_type: + memcpy(_VSTD::addressof(__char_type), __data, sizeof(char_type)); + return; + case __format::__arg_t::__int: + memcpy(_VSTD::addressof(__int), __data, sizeof(int)); + return; + case __format::__arg_t::__long_long: + memcpy(_VSTD::addressof(__long_long), __data, sizeof(long long)); + return; + case __format::__arg_t::__i128: +# ifndef _LIBCPP_HAS_NO_INT128 + memcpy(_VSTD::addressof(__i128), __data, sizeof(__int128_t)); +# else + __libcpp_unreachable(); +# endif + return; + case __format::__arg_t::__unsigned: + memcpy(_VSTD::addressof(__unsigned), __data, sizeof(unsigned)); + return; + case __format::__arg_t::__unsigned_long_long: + memcpy(_VSTD::addressof(__long_long), __data, sizeof(unsigned long long)); + return; + case __format::__arg_t::__u128: +# ifndef _LIBCPP_HAS_NO_INT128 + memcpy(_VSTD::addressof(__u128), __data, sizeof(__uint128_t)); +# else + __libcpp_unreachable(); +# endif + return; + case __format::__arg_t::__float: + memcpy(_VSTD::addressof(__float), __data, sizeof(float)); + return; + case __format::__arg_t::__double: + memcpy(_VSTD::addressof(__double), __data, sizeof(double)); + return; + case __format::__arg_t::__long_double: + memcpy(_VSTD::addressof(__long_double), __data, sizeof(long double)); + return; + case __format::__arg_t::__const_char_type_ptr: + memcpy(_VSTD::addressof(__const_char_type_ptr), __data, sizeof(const char_type*)); + return; + case __format::__arg_t::__string_view: + memcpy(_VSTD::addressof(__string_view), __data, sizeof(basic_string_view)); + return; + case __format::__arg_t::__ptr: + memcpy(_VSTD::addressof(__ptr), __data, sizeof(const void*)); + return; + case __format::__arg_t::__handle: + memcpy(_VSTD::addressof(__handle), __data, sizeof(handle)); + return; } -#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"); + __libcpp_unreachable(); } - - 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) {} }; template @@ -259,6 +240,11 @@ __format_(__parse_ctx, __ctx, __ptr_); } + template + _LIBCPP_HIDE_FROM_ABI static handle __create(const _Tp& __v) { + return handle{__v}; + } + private: const void* __ptr_; void (*__format_)(basic_format_parse_context&, _Context&, const void*); @@ -267,9 +253,12 @@ _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; + 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(*static_cast(__ptr), __ctx)); + __ctx.advance_to(__f.format(*const_cast<_Qp*>(static_cast(__ptr)), __ctx)); }) {} }; 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,335 @@ +// -*- 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 +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +namespace __format { + +/// Contains the meta data of a formatting argument. +/// +/// [format.args]/1 +/// ... Implementations should optimize the representation of basic_format_args +/// for a small number of formatting arguments. +/// +/// The format-arg-store contains this optimization by storing an array with the +/// packed meta data and an array with the packed data. The meta data array +/// contains one element per formatting argument. This meta data holds the +/// offset of the argument in the packed data array and the type of the +/// argument. (By knowing the type the size of the argument is known.) +/// +/// To reduce the size required the meta data is packed. This limits the size of +/// the formatting argument types and the number of elements. The rational: +/// - The number of \ref __type used is 16. This fits in 4 bits. Using 4 bits will +/// lead to an ABI break when a new type is added. Adding new types is +/// probably rare, but adding one shouldn't result in an ABI break. To avoid +/// this issue the number of possible types is doubled. +/// - The offset is stored in 27 bits. The largest element requires 128 bits of +/// storage. This means when using the largest type 2^20 elements can be used. +/// It's unlikely the compiler can handle this number of arguments. +struct _LIBCPP_TYPE_VIS __arg_meta_data { + uint32_t __offset : 27; + // The type is an __arg_t but on some platforms when the type of the two + // bitfields differ they aren't stored in the same uint32_t. + uint32_t __type : 5; +}; + +static_assert(sizeof(__arg_meta_data) == sizeof(uint32_t)); + +// TODO FMT consider a variable storage type. +// It would be possible to optimize the size based on the size of the +// storage of arguments: +// - offset < 8 => uint8_t with 3 bits for the offset (1 large element) +// - offset < 512 => uint16_t with 9 bits for the offset (32 large elements) +// Where a large element takes 128 bits of storage. +// (Note offset means we discard the size of the last argument.) +// +// We can then store the meta-data pointer as a tagged pointer +// ptr & 0x03: +// 0 => __arg_meta_data_8 +// 1 => __arg_meta_data_16 +// 2 => __arg_meta_data +// 3 could be used for future expansion to a 64-bit type. + +/// \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 +consteval array<__arg_meta_data, sizeof...(_Args)> __get_meta_data() { + if constexpr (sizeof...(_Args) != 0) { + array<__arg_meta_data, sizeof...(_Args)> __result; + uint32_t __offset = 0; + __arg_meta_data* __meta_data = __result.data(); + ( + [&] { + auto __s = __make_storage_type<_Context, _Args>(); + *__meta_data++ = {__offset, static_cast(__s.__arg)}; + __offset += sizeof(typename decltype(__s)::type); + }(), + ...); + return __result; + } else { + return {}; + } +} + +template +consteval size_t __get_storage_size() { + if constexpr (sizeof...(_Args) != 0) + return (sizeof(typename decltype(__make_storage_type<_Context, _Args>())::type) + ...); + else + // A C-array shouldn't be zero sized; return a dummy element. + return 1; +} + +template +struct _LIBCPP_TEMPLATE_VIS __storage { + _Sp __value; +}; + +template +_LIBCPP_HIDE_FROM_ABI void __store_data_element(char*& __data, _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. + basic_string_view __s{_VSTD::data(__value), _VSTD::size(__value)}; + _VSTD::memcpy(static_cast(__data), static_cast(_VSTD::addressof(__s)), sizeof(_Sp)); + } else if constexpr (same_as<_Sp, typename basic_format_arg<_Context>::handle>) { + auto __handle = basic_format_arg<_Context>::handle::__create(_VSTD::forward<_Tp>(__value)); + _VSTD::memcpy(static_cast(__data), static_cast(_VSTD::addressof(__handle)), sizeof(_Sp)); +# 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. + wchar_t __s = __value; + _VSTD::memcpy(static_cast(__data), static_cast(_VSTD::addressof(__s)), sizeof(__s)); +# endif + } else { + static_assert(is_trivially_copyable_v<_Sp>); + __storage<_Sp> __s{_VSTD::forward<_Tp>(__value)}; + _VSTD::memcpy(static_cast(__data), static_cast(_VSTD::addressof(__s.__value)), sizeof(_Sp)); + } + __data += sizeof(_Sp); +} + +template +_LIBCPP_HIDE_FROM_ABI void __store_data(char* __data, _Args&&... __args) noexcept { + (__store_data_element<_Context>(__data, _VSTD::forward<_Args>(__args)), ...); +} + +} // namespace __format + +template +struct _LIBCPP_TEMPLATE_VIS __format_arg_store { + // Most exposition only constructors in [format.arg] are noexcept. + // But all can be, so use noexcept for this function. + _LIBCPP_HIDE_FROM_ABI + __format_arg_store(_Args&&... __args) noexcept { + if constexpr (sizeof...(_Args) != 0) + __format::__store_data<_Context>(__data, _VSTD::forward<_Args>(__args)...); + } + + static constexpr array<__format::__arg_meta_data, sizeof...(_Args)> __meta_data{ + __format::__get_meta_data<_Context, remove_cvref_t<_Args>...>()}; + + /// Contains the packed arguments of the formatting function. + /// + /// When formatting some argument types are converted. For example \c short is + /// promoted to an \c int. A \c std::string is converted to a + /// \c std::string_view. This buffer contains all elements already converted + /// to the type used in the \c std::formatter. + char __data[__format::__get_storage_size<_Context, remove_cvref_t<_Args>...>()]; +}; + +#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,6 +12,8 @@ #include <__availability> #include <__config> +#include <__format/format_arg.h> +#include <__format/format_arg_store.h> #include <__format/format_fwd.h> #include @@ -35,20 +37,30 @@ _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) { + __meta_data_ = __store.__meta_data.data(); + __data_ = static_cast(__store.__data); + } + } _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>{}; + + __format::__arg_meta_data __meta_data = __meta_data_[__id]; + return basic_format_arg<_Context>{static_cast<__format::__arg_t>(__meta_data.__type), + __data_ + __meta_data.__offset}; } _LIBCPP_HIDE_FROM_ABI size_t __size() const noexcept { return __size_; } private: size_t __size_{0}; - const basic_format_arg<_Context>* __data_{nullptr}; + const __format::__arg_meta_data* __meta_data_{nullptr}; + const char* __data_{nullptr}; }; #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/format_arg_store.pass.cpp b/libcxx/test/libcxx/utilities/format/format.arguments/format.arg/format_arg_store.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/format/format.arguments/format.arg/format_arg_store.pass.cpp @@ -0,0 +1,437 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// Test the internals of basic_format_args specifically whether the interals +// work. The internals aren't specified but have some requirements. +// +// [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. +// [Note 1: For example, by storing indices of type alternatives separately +// from values and packing the former. -- end note] + +#include +#include +#include +#include +#include +#include + +#include "constexpr_char_traits.h" +#include "test_macros.h" +#include "make_string.h" +#include "min_allocator.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) +#define STR(S) MAKE_STRING(CharT, S) +#define SV(S) MAKE_STRING_VIEW(CharT, S) + +// A user-defined type used to test the handle formatter. +enum class status : uint16_t { foo = 0xAAAA, bar = 0x5555, foobar = 0xAA55 }; + +// The formatter for a user-defined type used to test the handle formatter. +// For this test the formatter only needs to exist not do anything useful. +template +struct std::formatter { + constexpr auto parse(auto& parse_ctx) { return parse_ctx.begin(); } + auto format(status, auto& ctx) const { return ctx.out(); } +}; + +template +void test_equal(const std::basic_format_arg& arg, const T& value) { + assert(arg); + std::visit_format_arg( + [&value](auto a) { + if constexpr (std::is_same_v, decltype(a)>) + assert(value == a); + else + assert(false); + }, + arg); +} + +// Tests the formatting arguments. +template +void test_store() { + using context = std::basic_format_context>, CharT>; + + // Short names used to cast rvalues in std::make_format_args. + using sc = signed char; + using uc = unsigned char; + using us = unsigned short; + using ll = long long; + using ul = unsigned long; + using ull = unsigned long long; + using ld = long double; + using cn = const nullptr_t; + using v = void*; + using cv = const void*; + + // Depending on the sizeof(long) it's stored as an int or a long long. + // Some helpers to make it generic in other parts of the code. + constexpr size_t sizeof_long = (sizeof(long) == sizeof(int)) ? sizeof(int) : sizeof(long long); + constexpr size_t sizeof_ulong = sizeof_long; + constexpr std::__format::__arg_t type_long = + (sizeof(long) == sizeof(int)) ? std::__format::__arg_t::__int : std::__format::__arg_t::__long_long; + constexpr std::__format::__arg_t type_ulong = + (sizeof(long) == sizeof(int)) ? std::__format::__arg_t::__unsigned : std::__format::__arg_t::__unsigned_long_long; + + CharT* cstring = const_cast(CSTR("hello C")); + const CharT* const_cstring = CSTR("hello const C"); + std::basic_string s = STR("Hello string"); + std::basic_string_view sv = SV("Hello string_view"); + + // Create formatting args with all supported types. + auto store = std::make_format_args(true, CharT('A'), sc(-1), short(-42), int(-99), long(-200), ll(-457), + uc(1), us(42), unsigned(99), ul(200), ull(457), float(-1), double(42), + ld(-99), cstring, const_cstring, s, sv, nullptr, cn(nullptr), v(cstring), + cv(const_cstring), status::foo +#ifndef TEST_HAS_NO_INT128 + + , + __int128_t(-9999), __uint128_t(9999) +#endif + ); + + // First test whether size of the internal arrays is the expected size. +#ifndef TEST_HAS_NO_INT128 + static_assert((std::same_as>)); +#else + static_assert((std::same_as>)); +#endif + static_assert(sizeof(store.__data) == sizeof(bool) + sizeof(CharT) + 6 * sizeof(int) + 2 * sizeof_long + + 2 * sizeof(long long) + sizeof(float) + sizeof(double) + + sizeof(long double) + 2 * sizeof(CharT*) + + 2 * sizeof(std::basic_string_view) + 6 * sizeof(void*) +#ifndef TEST_HAS_NO_INT128 + + + 2 * sizeof(__int128_t) +#endif + ); + + // Test the contents of the meta data. + // Since the meta data only depends on the types of the argument it's calculated compile-time. + static_assert(store.__meta_data[0].__offset == 0); + static_assert(static_cast(store.__meta_data[0].__type) == std::__format::__arg_t::__boolean); + { + constexpr size_t offset = store.__meta_data[0].__offset + sizeof(bool); + static_assert(store.__meta_data[1].__offset == offset); + static_assert(static_cast(store.__meta_data[1].__type) == + std::__format::__arg_t::__char_type); + } + { + constexpr size_t offset = store.__meta_data[1].__offset + sizeof(CharT); + static_assert(store.__meta_data[2].__offset == offset); + static_assert(static_cast(store.__meta_data[2].__type) == std::__format::__arg_t::__int); + static_assert(store.__meta_data[3].__offset == offset + sizeof(int)); + static_assert(static_cast(store.__meta_data[3].__type) == std::__format::__arg_t::__int); + static_assert(store.__meta_data[4].__offset == offset + 2 * sizeof(int)); + static_assert(static_cast(store.__meta_data[4].__type) == std::__format::__arg_t::__int); + static_assert(store.__meta_data[5].__offset == offset + 3 * sizeof(int)); + static_assert(static_cast(store.__meta_data[5].__type) == type_long); + static_assert(store.__meta_data[6].__offset == offset + 3 * sizeof(int) + sizeof_long); + static_assert(static_cast(store.__meta_data[6].__type) == + std::__format::__arg_t::__long_long); + } + { + constexpr size_t offset = store.__meta_data[6].__offset + sizeof(long long); + static_assert(store.__meta_data[7].__offset == offset); + static_assert(static_cast(store.__meta_data[7].__type) == + std::__format::__arg_t::__unsigned); + static_assert(store.__meta_data[8].__offset == offset + sizeof(unsigned)); + static_assert(static_cast(store.__meta_data[8].__type) == + std::__format::__arg_t::__unsigned); + static_assert(store.__meta_data[9].__offset == offset + 2 * sizeof(unsigned)); + static_assert(static_cast(store.__meta_data[9].__type) == + std::__format::__arg_t::__unsigned); + static_assert(store.__meta_data[10].__offset == offset + 3 * sizeof(unsigned)); + static_assert(static_cast(store.__meta_data[10].__type) == type_ulong); + static_assert(store.__meta_data[11].__offset == offset + 3 * sizeof(unsigned) + sizeof_ulong); + static_assert(static_cast(store.__meta_data[11].__type) == + std::__format::__arg_t::__unsigned_long_long); + } + + { + constexpr size_t offset = store.__meta_data[11].__offset + sizeof(unsigned long long); + static_assert(store.__meta_data[12].__offset == offset); + static_assert(static_cast(store.__meta_data[12].__type) == std::__format::__arg_t::__float); + static_assert(store.__meta_data[13].__offset == offset + sizeof(float)); + static_assert(static_cast(store.__meta_data[13].__type) == + std::__format::__arg_t::__double); + static_assert(store.__meta_data[14].__offset == offset + sizeof(float) + sizeof(double)); + static_assert(static_cast(store.__meta_data[14].__type) == + std::__format::__arg_t::__long_double); + } + { + constexpr size_t offset = store.__meta_data[14].__offset + sizeof(long double); + static_assert(store.__meta_data[15].__offset == offset); + static_assert(static_cast(store.__meta_data[15].__type) == + std::__format::__arg_t::__const_char_type_ptr); + static_assert(store.__meta_data[16].__offset == offset + sizeof(CharT*)); + static_assert(static_cast(store.__meta_data[16].__type) == + std::__format::__arg_t::__const_char_type_ptr); + } + { + constexpr size_t offset = store.__meta_data[16].__offset + sizeof(CharT*); + static_assert(store.__meta_data[17].__offset == offset); + static_assert(static_cast(store.__meta_data[17].__type) == + std::__format::__arg_t::__string_view); + static_assert(store.__meta_data[18].__offset == offset + sizeof(std::basic_string_view)); + static_assert(static_cast(store.__meta_data[18].__type) == + std::__format::__arg_t::__string_view); + } + { + constexpr size_t offset = store.__meta_data[18].__offset + sizeof(std::basic_string_view); + static_assert(store.__meta_data[19].__offset == offset); + static_assert(static_cast(store.__meta_data[19].__type) == std::__format::__arg_t::__ptr); + static_assert(store.__meta_data[20].__offset == offset + sizeof(void*)); + static_assert(static_cast(store.__meta_data[20].__type) == std::__format::__arg_t::__ptr); + static_assert(store.__meta_data[21].__offset == offset + 2 * sizeof(void*)); + static_assert(static_cast(store.__meta_data[21].__type) == std::__format::__arg_t::__ptr); + static_assert(store.__meta_data[22].__offset == offset + 3 * sizeof(void*)); + static_assert(static_cast(store.__meta_data[22].__type) == std::__format::__arg_t::__ptr); + } + { + constexpr size_t offset = store.__meta_data[22].__offset + sizeof(void*); + static_assert(store.__meta_data[23].__offset == offset); + static_assert(static_cast(store.__meta_data[23].__type) == + std::__format::__arg_t::__handle); + } +#ifndef TEST_HAS_NO_INT128 + { + constexpr size_t offset = store.__meta_data[23].__offset + 2 * sizeof(void*); + static_assert(store.__meta_data[24].__offset == offset); + static_assert(static_cast(store.__meta_data[24].__type) == std::__format::__arg_t::__i128); + static_assert(store.__meta_data[25].__offset == offset + sizeof(__int128_t)); + static_assert(static_cast(store.__meta_data[25].__type) == std::__format::__arg_t::__u128); + } +#endif + + // Test the contents of the data retrieving them from the basic_format_args. + auto args = std::basic_format_args(store); + test_equal(args.get(0), true); + test_equal(args.get(1), CharT('A')); + test_equal(args.get(2), int(-1)); + test_equal(args.get(3), int(-42)); + test_equal(args.get(4), int(-99)); + if (sizeof(long) == sizeof(int)) + test_equal(args.get(5), int(-200)); + else + test_equal(args.get(5), ll(-200)); + test_equal(args.get(6), ll(-457)); + test_equal(args.get(7), unsigned(1)); + test_equal(args.get(8), unsigned(42)); + test_equal(args.get(9), unsigned(99)); + if (sizeof(long) == sizeof(int)) + test_equal(args.get(10), unsigned(200)); + else + test_equal(args.get(10), ull(200)); + test_equal(args.get(11), ull(457)); + test_equal(args.get(12), float(-1)); + test_equal(args.get(13), double(42)); + test_equal(args.get(14), ld(-99)); + test_equal(args.get(15), (const CharT*)(cstring)); + test_equal(args.get(16), const_cstring); + test_equal(args.get(17), std::basic_string_view{s}); + test_equal(args.get(18), sv); + test_equal(args.get(19), cv(nullptr)); + test_equal(args.get(20), cv(nullptr)); + test_equal(args.get(21), cv(cstring)); + test_equal(args.get(22), cv(const_cstring)); + // 23 handle This type can't be inspected, tests for format functions validate this. +#ifndef TEST_HAS_NO_INT128 + test_equal(args.get(24), __int128_t(-9999)); + test_equal(args.get(25), __uint128_t(9999)); +#endif +} + +template +void test_store_empty() { + auto store = std::make_format_args(); + static_assert((std::same_as>)); + static_assert(sizeof(store.__data) == 1); +} + +// Like test_store(), but this tests the widening of a char to wchar_t +void test_store_char_to_wchar() { +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + auto store = std::make_format_args('a'); + + static_assert((std::same_as>)); + static_assert(store.__meta_data[0].__offset == 0); + static_assert(static_cast(store.__meta_data[0].__type) == + std::__format::__arg_t::__char_type); + + static_assert(sizeof(store.__data) == sizeof(wchar_t)); + + auto args = std::basic_format_args(store); + test_equal(args.get(0), L'a'); +#endif +} + +template +void test_store_char_string_types() { + using context = std::basic_format_context>, CharT>; + +#define VALIDATE \ + static_assert((std::same_as>)); \ + static_assert(sizeof(store.__data) == sizeof(std::basic_string_view)); \ + \ + auto args = std::basic_format_args(store); \ + test_equal(args.get(0), std::basic_string_view{cs}); + + const CharT* cs = CSTR("hello world"); + { + std::basic_string_view> sv{cs}; + auto store = std::make_format_args(sv); + VALIDATE + } + { + std::basic_string, std::allocator> s{cs}; + auto store = std::make_format_args(s); + VALIDATE + } + { + std::basic_string, min_allocator> s{cs}; + auto store = std::make_format_args(s); + VALIDATE + } + { + std::basic_string, min_allocator> s{cs}; + auto store = std::make_format_args(s); + VALIDATE + } + +#undef VALIDATE +} + +template +void test() { + test_store(); + test_store_empty(); + test_store_char_to_wchar(); + test_store_char_string_types(); +} + +// Tests whether the storage type uses the proper type deduction and conversion rules. +namespace test_storage_type { +using namespace std::__format; + +template +concept valid = requires { __make_storage_type(); }; + +template +concept invalid = (!valid); + +template +consteval bool type_independant() { + static_assert(std::same_as()), __storage_type>); + static_assert(std::same_as()), __storage_type>); + static_assert(std::same_as()), __storage_type>); + static_assert(std::same_as()), __storage_type>); + if constexpr (sizeof(long) == sizeof(int)) + static_assert(std::same_as()), __storage_type>); + else + static_assert(std::same_as()), __storage_type>); + static_assert(std::same_as()), __storage_type>); +#ifndef TEST_HAS_NO_INT128 + static_assert(std::same_as()), __storage_type<__int128_t>>); +#endif + + static_assert(std::same_as()), __storage_type>); + static_assert(std::same_as()), __storage_type>); + static_assert(std::same_as()), __storage_type>); + if constexpr (sizeof(long) == sizeof(int)) + static_assert(std::same_as()), __storage_type>); + else + static_assert( + std::same_as()), __storage_type>); + static_assert( + std::same_as()), __storage_type>); +#ifndef TEST_HAS_NO_INT128 + static_assert(std::same_as()), __storage_type<__uint128_t>>); +#endif + + static_assert(std::same_as()), __storage_type>); + static_assert(std::same_as()), __storage_type>); + static_assert(std::same_as()), __storage_type>); + + static_assert(std::same_as()), __storage_type>); + static_assert(std::same_as()), __storage_type>); + static_assert(std::same_as()), __storage_type>); + + static_assert(std::same_as()), + __storage_type::handle>>); + + return true; +} + +// char types +static_assert(type_independant()); +static_assert(std::same_as()), __storage_type>); +static_assert(std::same_as()), __storage_type>); +static_assert( + std::same_as()), __storage_type>); +static_assert( + std::same_as()), __storage_type>); +static_assert(std::same_as()), + __storage_type>); +static_assert( + std::same_as()), __storage_type>); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS +// wchar_t types +static_assert(type_independant()); +static_assert(std::same_as()), __storage_type>); +static_assert( + std::same_as()), __storage_type>); +static_assert(std::same_as()), + __storage_type>); +static_assert(std::same_as()), + __storage_type>); +static_assert(std::same_as()), + __storage_type>); +static_assert(std::same_as()), + __storage_type>); + +// wchar_t to char narrowing +static_assert(invalid); +static_assert(invalid); +static_assert(invalid); +static_assert(invalid); +static_assert(invalid); +static_assert(invalid); +static_assert(invalid); + +// char to wchar_t expansion +static_assert(std::same_as()), __storage_type>); +static_assert(invalid); +static_assert(invalid); +static_assert(invalid); +static_assert(invalid); +static_assert(invalid); +static_assert(invalid); + +#endif + +} // namespace test_storage_type + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} 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 //