diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv --- a/libcxx/docs/Status/Cxx20Issues.csv +++ b/libcxx/docs/Status/Cxx20Issues.csv @@ -202,7 +202,7 @@ "`3233 `__","Broken requirements for ``shared_ptr``\ converting constructors","Prague","","" "`3237 `__","LWG 3038 and 3190 have inconsistent PRs","Prague","","" "`3238 `__","Insufficiently-defined behavior of ``std::function``\ deduction guides","Prague","","" -"`3242 `__","``std::format``\ : missing rules for ``arg-id``\ in ``width``\ and ``precision``\ ","Prague","","" +"`3242 `__","``std::format``\ : missing rules for ``arg-id``\ in ``width``\ and ``precision``\ ","Prague","|Complete|","Clang 13" "`3243 `__","``std::format``\ and negative zeroes","Prague","","" "`3247 `__","``ranges::iter_move``\ should perform ADL-only lookup of ``iter_move``\ ","Prague","","" "`3248 `__","``std::format``\ ``#b``\ , ``#B``\ , ``#o``\ , ``#x``\ , and ``#X``\ presentation types misformat negative numbers","Prague","","" 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 @@ -157,7 +157,7 @@ "`P1871 `__","LWG","Should concepts be enabled or disabled?","Belfast","* *","" "`P1872 `__","LWG","span should have size_type, not index_type","Belfast","|Complete|","10.0" "`P1878 `__","LWG","Constraining Readable Types","Belfast","* *","" -"`P1892 `__","LWG","Extended locale-specific presentation specifiers for std::format","Belfast","* *","" +"`P1892 `__","LWG","Extended locale-specific presentation specifiers for std::format","Belfast","|Complete|","13.0" "`P1902 `__","LWG","Missing feature-test macros 2018-2019","Belfast","* *","" "`P1959 `__","LWG","Remove std::weak_equality and std::strong_equality","Belfast","* *","" "`P1960 `__","LWG","NB Comment Changes Reviewed by SG1","Belfast","* *","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -108,6 +108,7 @@ __format/format_parse_context.h __format/format_string.h __format/formatter.h + __format/parser_std_format_spec.h __function_like.h __functional_base __functional/binary_function.h diff --git a/libcxx/include/__format/format_string.h b/libcxx/include/__format/format_string.h --- a/libcxx/include/__format/format_string.h +++ b/libcxx/include/__format/format_string.h @@ -127,8 +127,7 @@ if (__v > __number_max || (__begin != __end_input && *__begin >= _CharT('0') && *__begin <= _CharT('9'))) - __throw_format_error( - "The numeric values of the format-spec is too large"); + __throw_format_error("The numeric value of the format-spec is too large"); __value = __v; } diff --git a/libcxx/include/__format/formatter.h b/libcxx/include/__format/formatter.h --- a/libcxx/include/__format/formatter.h +++ b/libcxx/include/__format/formatter.h @@ -13,6 +13,7 @@ #include <__availability> #include <__config> #include <__format/format_error.h> +#include <__format/parser_std_format_spec.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) #pragma GCC system_header diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/parser_std_format_spec.h @@ -0,0 +1,659 @@ +// -*- 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_PARSER_STD_FORMAT_SPEC_H +#define _LIBCPP___FORMAT_PARSER_STD_FORMAT_SPEC_H + +#include <__config> +#include <__debug> +#include <__format/format_arg.h> +#include <__format/format_error.h> +#include <__format/format_string.h> +#include <__variant/monostate.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) && \ + !defined(_LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED) + +namespace __format_spec { + +/** + * Contains the flags for the std-format-spec. + * + * Some format-options can only be used for specific C++types and may depend on + * the selected format-type. + * * The C++type filtering can be done using the proper policies for + * @ref __parser_std. + * * The format-type filtering needs to be done post parsing in the parser + * derived from @ref __parser_std. + */ +class _LIBCPP_TYPE_VIS _Flags { +public: + enum class _LIBCPP_ENUM_VIS _Alignment : uint8_t { + /** + * No alignment is set in the format string. + * + * Zero-padding is ignored when an alignment is selected. + * The default alignment depends on the selected format-type. + */ + __default, + __left, + __center, + __right + }; + enum class _LIBCPP_ENUM_VIS _Sign : uint8_t { + /** + * No sign is set in the format string. + * + * The sign isn't allowed for certain format-types. By using this value + * it's possible to detect whether or not the user explicitly set the sign + * flag. For formatting purposes it behaves the same as @ref __minus. + */ + __default, + __minus, + __plus, + __space + }; + + _Alignment __alignment : 2 {_Alignment::__default}; + _Sign __sign : 2 {_Sign::__default}; + uint8_t __alternate_form : 1 {false}; + uint8_t __zero_padding : 1 {false}; + uint8_t __locale_specific_form : 1 {false}; + + enum class _LIBCPP_ENUM_VIS _Type : uint8_t { + __default, + __string, + __binary_lower_case, + __binary_upper_case, + __octal, + __decimal, + __hexadecimal_lower_case, + __hexadecimal_upper_case, + __pointer, + __char, + __float_hexadecimal_lower_case, + __float_hexadecimal_upper_case, + __scientific_lower_case, + __scientific_upper_case, + __fixed, + __general_lower_case, + __general_upper_case + }; + + _Type __type{_Type::__default}; +}; + +namespace __detail { +template +[[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr bool +__parse_alignment(_CharT __c, _Flags& __flags) noexcept { + switch (__c) { + case _CharT('<'): + __flags.__alignment = _Flags::_Alignment::__left; + return true; + + case _CharT('^'): + __flags.__alignment = _Flags::_Alignment::__center; + return true; + + case _CharT('>'): + __flags.__alignment = _Flags::_Alignment::__right; + return true; + } + return false; +} +} // namespace __detail + +template +class _LIBCPP_TEMPLATE_VIS __parser_fill_align { +public: + // TODO FMT The standard doesn't specify this character is a Unicode + // character. Validate what fmt and MSVC have implemented. + _CharT __fill{_CharT(' ')}; + +protected: + [[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT* __end, _Flags& __flags) { + _LIBCPP_ASSERT(__begin != __end, "Precondition failure"); + if (__begin + 1 != __end) { + if (__detail::__parse_alignment(*(__begin + 1), __flags)) { + if (*__begin == _CharT('{') || *__begin == _CharT('}')) + __throw_format_error( + "The format-spec fill field contains an invalid character"); + __fill = *__begin; + return __begin + 2; + } + } + + if (__detail::__parse_alignment(*__begin, __flags)) + return __begin + 1; + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_sign { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, _Flags& __flags) noexcept { + switch (*__begin) { + case _CharT('-'): + __flags.__sign = _Flags::_Sign::__minus; + break; + case _CharT('+'): + __flags.__sign = _Flags::_Sign::__plus; + break; + case _CharT(' '): + __flags.__sign = _Flags::_Sign::__space; + break; + default: + return __begin; + } + return __begin + 1; + } +}; + +class _LIBCPP_TYPE_VIS __parser_no_sign { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, _Flags&) { + switch (*__begin) { + case _CharT('+'): + case _CharT('-'): + case _CharT(' '): + __throw_format_error("A sign field isn't allowed in this format-spec"); + } + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_alternate_form { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, _Flags& __flags) noexcept { + if (*__begin == _CharT('#')) { + __flags.__alternate_form = true; + ++__begin; + } + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_no_alternate_form { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, _Flags&) { + if (*__begin == _CharT('#')) + __throw_format_error( + "An alternate form field isn't allowed in this format-spec"); + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_zero_padding { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, _Flags& __flags) noexcept { + if (*__begin == _CharT('0')) { + __flags.__zero_padding = true; + ++__begin; + } + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_no_zero_padding { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, _Flags&) { + if (*__begin == _CharT('0')) + __throw_format_error( + "A zero-padding field isn't allowed in this format-spec"); + + return __begin; + } +}; + +template +[[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr __format:: + __parse_number_result<_CharT> + __parse_arg_id(const _CharT* __begin, const _CharT* __end, + auto& __parse_ctx) { + // This function is a wrapper to call the real parser. But it does the + // validation for the pre-conditions and post-conditions. + if (__begin == __end) + __throw_format_error("End of input while parsing format-spec arg-id"); + + __format::__parse_number_result __r = + __format::__parse_arg_id(__begin, __end, __parse_ctx); + + if (__r.__ptr == __end || *__r.__ptr != _CharT('}')) + __throw_format_error("A format-spec arg-id should terminate at a '}'"); + + ++__r.__ptr; + return __r; +} + +template +[[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr uint32_t +__substitute_arg_id(basic_format_arg<_Context> __arg) { + return visit_format_arg( + [](auto __arg) -> uint32_t { + using _Type = decltype(__arg); + if constexpr (integral<_Type>) { + if constexpr (signed_integral<_Type>) { + if (__arg < 0) + __throw_format_error("A format-spec arg-id replacement shouldn't " + "have a negative value"); + } + + using _CT = common_type_t<_Type, decltype(__format::__number_max)>; + if (static_cast<_CT>(__arg) > + static_cast<_CT>(__format::__number_max)) + __throw_format_error("A format-spec arg-id replacement exceeds " + "the maximum supported value"); + return __arg; + } else if constexpr (same_as<_Type, monostate>) + __throw_format_error("Argument index out of bounds"); + else + __throw_format_error("A format-spec arg-id replacement argument " + "isn't an integral type"); + }, + __arg); +} + +class _LIBCPP_TYPE_VIS __parser_width { +public: + /** Contains a width or an arg-id. */ + uint32_t __width : 31 {0}; + /** Determines whether the value stored is a width or an arg-id. */ + uint32_t __width_as_arg : 1 {0}; + +protected: + /** + * Does the supplied std-format-spec contain a width field? + * + * When the field isn't present there's no padding required. This can be used + * to optimize the formatting. + */ + [[nodiscard]] constexpr bool __has_width_field() const noexcept { + return __width_as_arg || __width; + } + + /** + * Does the supplied width field contain an arg-id? + * + * If @c true the formatter needs to call @ref __substitute_width_arg_id. + */ + [[nodiscard]] constexpr bool __width_needs_substitution() const noexcept { + return __width_as_arg; + } + + template + [[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { + if (*__begin == _CharT('0')) + __throw_format_error( + "A format-spec width field shouldn't have a leading zero"); + + if (*__begin == _CharT('{')) { + __format::__parse_number_result __r = + __parse_arg_id(++__begin, __end, __parse_ctx); + __width = __r.__value; + __width_as_arg = 1; + return __r.__ptr; + } + + if (*__begin < _CharT('0') || *__begin > _CharT('9')) + return __begin; + + __format::__parse_number_result __r = + __format::__parse_number(__begin, __end); + __width = __r.__value; + _LIBCPP_ASSERT(__width != 0, + "A zero value isn't allowed and should be impossible, " + "due to validations in this function"); + return __r.__ptr; + } + + void _LIBCPP_INLINE_VISIBILITY constexpr __substitute_width_arg_id( + auto __arg) { + _LIBCPP_ASSERT(__width_as_arg == 1, + "Substitute width called when no substitution is required"); + + // The clearing of the flag isn't required but looks better when debugging + // the code. + __width_as_arg = 0; + __width = __substitute_arg_id(__arg); + if (__width == 0) + __throw_format_error( + "A format-spec width field replacement should have a positive value"); + } +}; + +class _LIBCPP_TYPE_VIS __parser_precision { +public: + /** Contains a precision or an arg-id. */ + uint32_t __precision : 31 {__format::__number_max}; + /** + * Determines whether the value stored is a precision or an arg-id. + * + * @note Since @ref __precision == @ref __format::__number_max is a valid + * value, the default value contains an arg-id of INT32_MAX. (This number of + * arguments isn't supported by compilers.) This is used to detect whether + * the std-format-spec contains a precision field. + */ + uint32_t __precision_as_arg : 1 {1}; + +protected: + /** + * Does the supplied std-format-spec contain a precision field? + * + * When the field isn't present there's no truncating required. This can be + * used to optimize the formatting. + */ + [[nodiscard]] constexpr bool __has_precision_field() const noexcept { + return __precision_as_arg == 0 || __precision != __format::__number_max; + } + + /** + * Does the supplied precision field contain an arg-id? + * + * If @c true the formatter needs to call @ref __substitute_precision_arg_id. + */ + [[nodiscard]] constexpr bool __precision_needs_substitution() const noexcept { + return __precision_as_arg && __precision != __format::__number_max; + } + + template + [[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { + if (*__begin != _CharT('.')) + return __begin; + + ++__begin; + if (__begin == __end) + __throw_format_error("End of input while parsing format-spec precision"); + + if (*__begin == _CharT('0')) { + ++__begin; + if (__begin != __end && *__begin >= '0' && *__begin <= '9') + __throw_format_error( + "A format-spec precision field shouldn't have a leading zero"); + + __precision = 0; + __precision_as_arg = 0; + return __begin; + } + + if (*__begin == _CharT('{')) { + __format::__parse_number_result __arg_id = + __parse_arg_id(++__begin, __end, __parse_ctx); + _LIBCPP_ASSERT(__arg_id.__value != __format::__number_max, + "Unsupported number of arguments, since this number of " + "arguments is used a special value"); + __precision = __arg_id.__value; + return __arg_id.__ptr; + } + + if (*__begin < _CharT('0') || *__begin > _CharT('9')) + __throw_format_error( + "The format-spec precision field doesn't contain a value or arg-id"); + + __format::__parse_number_result __r = + __format::__parse_number(__begin, __end); + __precision = __r.__value; + __precision_as_arg = 0; + return __r.__ptr; + } + + void _LIBCPP_INLINE_VISIBILITY constexpr __substitute_precision_arg_id( + auto __arg) { + _LIBCPP_ASSERT( + __precision_as_arg == 1 && __precision != __format::__number_max, + "Substitute precision called when no substitution is required"); + + // The clearing of the flag isn't required but looks better when debugging + // the code. + __precision_as_arg = 0; + __precision = __substitute_arg_id(__arg); + } +}; + +class _LIBCPP_TYPE_VIS __parser_no_precision { +protected: + template + [[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT*, auto&) { + if (*__begin == _CharT('.')) + __throw_format_error( + "A precision field isn't allowed in this format-spec"); + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_locale_specific_form { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, _Flags& __flags) noexcept { + if (*__begin == _CharT('L')) { + __flags.__locale_specific_form = true; + ++__begin; + } + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_no_locale_specific_form { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, _Flags&) { + if (*__begin == _CharT('L')) + __throw_format_error( + "A locale-specific form field isn't allowed in this format-spec"); + + return __begin; + } +}; + +/** Parses the type. */ +class _LIBCPP_TYPE_VIS __parser_type { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, _Flags& __flags) { + if (*__begin != _CharT('}')) { + // Determines the type. It does not validate whether the selected type + // is valid. Most formatters have optional fields that are only allowed + // for certain types. These parsers need to do validation after the + // type has been parsed. So its easier to implement the validation for + // all types in the specific parse function. + switch (*__begin++) { + case 'A': + __flags.__type = _Flags::_Type::__float_hexadecimal_upper_case; + break; + case 'B': + __flags.__type = _Flags::_Type::__binary_upper_case; + break; + case 'E': + __flags.__type = _Flags::_Type::__scientific_upper_case; + break; + case 'F': + __flags.__type = _Flags::_Type::__fixed; + break; + case 'G': + __flags.__type = _Flags::_Type::__general_upper_case; + break; + case 'X': + __flags.__type = _Flags::_Type::__hexadecimal_upper_case; + break; + case 'a': + __flags.__type = _Flags::_Type::__float_hexadecimal_lower_case; + break; + case 'b': + __flags.__type = _Flags::_Type::__binary_lower_case; + break; + case 'c': + __flags.__type = _Flags::_Type::__char; + break; + case 'd': + __flags.__type = _Flags::_Type::__decimal; + break; + case 'e': + __flags.__type = _Flags::_Type::__scientific_lower_case; + break; + case 'f': + __flags.__type = _Flags::_Type::__fixed; + break; + case 'g': + __flags.__type = _Flags::_Type::__general_lower_case; + break; + case 'o': + __flags.__type = _Flags::_Type::__octal; + break; + case 'p': + __flags.__type = _Flags::_Type::__pointer; + break; + case 's': + __flags.__type = _Flags::_Type::__string; + break; + case 'x': + __flags.__type = _Flags::_Type::__hexadecimal_lower_case; + break; + default: + __throw_format_error("The format-spec type field has an invalid value"); + } + } + return __begin; + } +}; + +/** + * The parser for the std-format-spec. + * + * The class is a policy based class. The order of the inheritance is picked to + * have the minimal padding for all subclasses. + */ +template +class _LIBCPP_TEMPLATE_VIS __parser_std + : public __parser_width, // provides __width(|as_arg) + public _ParserPrecision, // provides __precision(|as_arg) + public __parser_fill_align<_CharT>, // provides __fill and uses __flags + public _Flags, // provides __flags + private _ParserSign, // uses __flags + private _ParserAlternateForm, // uses __flags + private _ParserZeroPadding, // uses __flags + private _ParserLocaleSpecificForm, // uses __flags + private __parser_type // uses __flags +{ +public: + using char_type = _CharT; + + /** + * The low-level std-format-spec parse function. + * + * @pre __begin points at the beginning of the std-format-spec. This means + * directly after the ':'. + * @pre The std-format-spec parses the entire input, or the first unmatched + * character is a '}'. + * + * @returns The iterator pointing at the last parsed character. + */ + [[nodiscard]] auto + _LIBCPP_INLINE_VISIBILITY constexpr parse(auto& __parse_ctx) + -> decltype(__parse_ctx.begin()) { + auto __begin = __parse_ctx.begin(); + auto __end = __parse_ctx.end(); + if (__begin == __end) + return __begin; + + __begin = __parser_fill_align<_CharT>::__parse(__begin, __end, + static_cast<_Flags&>(*this)); + if (__begin == __end) + return __begin; + + __begin = _ParserSign::__parse(__begin, static_cast<_Flags&>(*this)); + if (__begin == __end) + return __begin; + + __begin = + _ParserAlternateForm::__parse(__begin, static_cast<_Flags&>(*this)); + if (__begin == __end) + return __begin; + + __begin = _ParserZeroPadding::__parse(__begin, static_cast<_Flags&>(*this)); + if (__begin == __end) + return __begin; + + __begin = __parser_width::__parse(__begin, __end, __parse_ctx); + if (__begin == __end) + return __begin; + + __begin = _ParserPrecision::__parse(__begin, __end, __parse_ctx); + if (__begin == __end) + return __begin; + + __begin = _ParserLocaleSpecificForm::__parse(__begin, + static_cast<_Flags&>(*this)); + if (__begin == __end) + return __begin; + + __begin = __parser_type::__parse(__begin, static_cast<_Flags&>(*this)); + + if (__begin != __end && *__begin != _CharT('}')) + __throw_format_error( + "The format-spec should consume the input or end with a '}'"); + + return __begin; + } +}; + +} // namespace __format_spec + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) && !defined(_LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_PARSER_STD_FORMAT_SPEC_H diff --git a/libcxx/include/format b/libcxx/include/format --- a/libcxx/include/format +++ b/libcxx/include/format @@ -281,6 +281,7 @@ #include <__format/format_error.h> #include <__format/format_parse_context.h> #include <__format/format_string.h> +#include <__format/parser_std_format_spec.h> #include <__format/formatter.h> #include <__variant/monostate.h> #include diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -391,18 +391,19 @@ export * module __format { - module format_arg { header "__format/format_arg.h" } - module format_args { header "__format/format_args.h" } + module format_arg { header "__format/format_arg.h" } + module format_args { header "__format/format_args.h" } module format_context { header "__format/format_context.h" export optional export locale } - module format_error { header "__format/format_error.h" } - module format_fwd { header "__format/format_fwd.h" } - module format_parse_context { header "__format/format_parse_context.h" } - module format_string { header "__format/format_string.h" } - module formatter { header "__format/formatter.h" } + module format_error { header "__format/format_error.h" } + module format_fwd { header "__format/format_fwd.h" } + module format_parse_context { header "__format/format_parse_context.h" } + module format_string { header "__format/format_string.h" } + module formatter { header "__format/formatter.h" } + module parser_std_format_spec { header "__format/parser_std_format_spec.h" } } } module forward_list { diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_alternate_form.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_alternate_form.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_alternate_form.pass.cpp @@ -0,0 +1,92 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the alternate form. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = __parser_std; + +template +constexpr void test(bool expected, size_t size, + std::basic_string_view fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == expected); + assert(parser.__zero_padding == false); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test(bool expected, size_t size, const CharT* f) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, size, fmt); + fmt.remove_suffix(1); + test(expected, size, fmt); +} + +template +constexpr void test() { + test(false, 0, CSTR("}")); + test(true, 1, CSTR("#}")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_fill_align.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_fill_align.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_fill_align.pass.cpp @@ -0,0 +1,112 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the fill and alignment. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +struct Expected { + CharT fill; + _Flags::_Alignment alignment; +}; + +template +constexpr void test(Expected expected, size_t size, + std::basic_string_view fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == expected.fill); + assert(parser.__alignment == expected.alignment); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == false); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test(Expected expected, size_t size, const CharT* f) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, size, fmt); + fmt.remove_suffix(1); + test(expected, size, fmt); +} + +template +constexpr void test() { + test({CharT(' '), _Flags::_Alignment::__default}, 0, CSTR("}")); + test({CharT(' '), _Flags::_Alignment::__left}, 1, CSTR("<}")); + test({CharT(' '), _Flags::_Alignment::__center}, 1, CSTR("^}")); + test({CharT(' '), _Flags::_Alignment::__right}, 1, CSTR(">}")); + + test({CharT('1'), _Flags::_Alignment::__left}, 2, CSTR("1<}")); + test({CharT('a'), _Flags::_Alignment::__center}, 2, CSTR("a^}")); + test({CharT('#'), _Flags::_Alignment::__right}, 2, CSTR("#>}")); + + test_exception("The format-spec fill field contains an invalid character", + CSTR("{<")); + test_exception("The format-spec fill field contains an invalid character", + CSTR("}<")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_locale_specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_locale_specific_form.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_locale_specific_form.pass.cpp @@ -0,0 +1,93 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the locale-specific form. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +template +constexpr void test(bool expected, size_t size, + std::basic_string_view fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == false); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == expected); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test(bool expected, size_t size, const CharT* f) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, size, fmt); + fmt.remove_suffix(1); + test(expected, size, fmt); +} + +template +constexpr void test() { + test(false, 0, CSTR("}")); + test(true, 1, CSTR("L}")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_alternate_form.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_alternate_form.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_alternate_form.pass.cpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the alternate form. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(std::basic_string_view fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == false); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test(const CharT* f) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(fmt); + fmt.remove_suffix(1); + test(fmt); +} + +template +constexpr void test() { + test(CSTR("}")); + test_exception("An alternate form field isn't allowed in this format-spec", + CSTR("#")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_locale_specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_locale_specific_form.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_locale_specific_form.pass.cpp @@ -0,0 +1,96 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the locale-specific form. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(std::basic_string_view fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == false); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test(const CharT* f) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(fmt); + fmt.remove_suffix(1); + test(fmt); +} + +template +constexpr void test() { + test(CSTR("}")); + test_exception( + "A locale-specific form field isn't allowed in this format-spec", + CSTR("L")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_precision.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_precision.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_precision.pass.cpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the precision. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(std::basic_string_view fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == false); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test(const CharT* f) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(fmt); + fmt.remove_suffix(1); + test(fmt); +} + +template +constexpr void test() { + test(CSTR("}")); + test_exception("A precision field isn't allowed in this format-spec", + CSTR(".")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_sign.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_sign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_sign.pass.cpp @@ -0,0 +1,96 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the sign. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(std::basic_string_view fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == false); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test(const CharT* f) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(fmt); + fmt.remove_suffix(1); + test(fmt); +} + +template +constexpr void test() { + test(CSTR("}")); + test_exception("A sign field isn't allowed in this format-spec", CSTR("-")); + test_exception("A sign field isn't allowed in this format-spec", CSTR("+")); + test_exception("A sign field isn't allowed in this format-spec", CSTR(" ")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_zero_padding.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_zero_padding.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_zero_padding.pass.cpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the zero-padding. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(std::basic_string_view fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == false); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test(const CharT* f) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(fmt); + fmt.remove_suffix(1); + test(fmt); +} + +template +constexpr void test() { + test(CSTR("}")); + test_exception("A zero-padding field isn't allowed in this format-spec", + CSTR("0")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_precision.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_precision.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_precision.pass.cpp @@ -0,0 +1,158 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the precision. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +struct Expected { + uint32_t precision{0}; + bool precision_as_arg{false}; +}; + +template +constexpr void test(Expected expected, size_t size, + std::basic_string_view fmt) { + // Initialize parser with sufficient arguments to avoid the parsing to fail + // due to insufficient arguments. + std::basic_format_parse_context parse_ctx(fmt, + std::__format::__number_max); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == false); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__precision == expected.precision); + assert(parser.__precision_as_arg == expected.precision_as_arg); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test(Expected expected, size_t size, const CharT* f) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, size, fmt); + fmt.remove_suffix(1); + test(expected, size, fmt); +} + +template +constexpr void test() { + test({std::__format::__number_max, 1}, 0, CSTR("}")); + test({0, 0}, 2, CSTR(".0}")); + test({1, 0}, 2, CSTR(".1}")); + test({10, 0}, 3, CSTR(".10}")); + test({1000, 0}, 5, CSTR(".1000}")); + test({1000000, 0}, 8, CSTR(".1000000}")); + + test({0, 1}, 3, CSTR(".{}}")); + test({0, 1}, 4, CSTR(".{0}}")); + test({1, 1}, 4, CSTR(".{1}}")); + + test_exception("A format-spec precision field shouldn't have a leading zero", + CSTR(".00")); + test_exception("A format-spec precision field shouldn't have a leading zero", + CSTR(".01")); + test_exception( + "The format-spec precision field doesn't contain a value or arg-id", + CSTR(".a")); + test_exception( + "The format-spec precision field doesn't contain a value or arg-id", + ".:"); + + static_assert(std::__format::__number_max == 2'147'483'647, + "Update the assert and the test."); + test({2'147'483'647, 0}, 11, CSTR(".2147483647}")); + test_exception("The numeric value of the format-spec is too large", + CSTR(".2147483648")); + test_exception("The numeric value of the format-spec is too large", + CSTR(".5000000000")); + test_exception("The numeric value of the format-spec is too large", + CSTR(".10000000000")); + + test_exception("End of input while parsing format-spec arg-id", CSTR(".{")); + test_exception("A format-spec arg-id should terminate at a '}'", CSTR(".{0")); + test_exception( + "The arg-id of the format-spec starts with an invalid character", + CSTR(".{a")); + test_exception("A format-spec arg-id should terminate at a '}'", CSTR(".{1")); + test_exception("A format-spec arg-id should terminate at a '}'", CSTR(".{9")); + test_exception("A format-spec arg-id should terminate at a '}'", + CSTR(".{9:")); + test_exception("A format-spec arg-id should terminate at a '}'", + CSTR(".{9a")); + + static_assert(std::__format::__number_max == 2'147'483'647, + "Update the assert and the test."); + // Note the static_assert tests whether the arg-id is valid. + // Therefore the following should be true arg-id < __format::__number_max. + test({2'147'483'646, 1}, 13, CSTR(".{2147483646}}")); + test_exception("The numeric value of the format-spec is too large", + CSTR(".{2147483648}")); + test_exception("The numeric value of the format-spec is too large", + CSTR(".{5000000000}")); + test_exception("The numeric value of the format-spec is too large", + CSTR(".{10000000000}")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_sign.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_sign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_sign.pass.cpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the sign. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +template +constexpr void test(_Flags::_Sign expected, size_t size, + std::basic_string_view fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == expected); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == false); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test(_Flags::_Sign expected, size_t size, const CharT* f) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, size, fmt); + fmt.remove_suffix(1); + test(expected, size, fmt); +} + +template +constexpr void test() { + test(_Flags::_Sign::__default, 0, CSTR("}")); + test(_Flags::_Sign::__minus, 1, CSTR("-}")); + test(_Flags::_Sign::__plus, 1, CSTR("+}")); + test(_Flags::_Sign::__space, 1, CSTR(" }")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_type.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_type.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_type.pass.cpp @@ -0,0 +1,157 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the type. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(_Flags::_Type expected, size_t size, + std::basic_string_view fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == false); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == expected); +} + +template +constexpr void test(_Flags::_Type expected, size_t size, const CharT* f) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, size, fmt); + fmt.remove_suffix(1); + test(expected, size, fmt); +} + +template +constexpr void test() { + test(_Flags::_Type::__default, 0, CSTR("}")); + + test(_Flags::_Type::__float_hexadecimal_upper_case, 1, CSTR("A}")); + test(_Flags::_Type::__binary_upper_case, 1, CSTR("B}")); + test_exception("The format-spec type field has an invalid value", CSTR("C")); + test_exception("The format-spec type field has an invalid value", CSTR("D")); + test(_Flags::_Type::__scientific_upper_case, 1, CSTR("E}")); + test(_Flags::_Type::__fixed, 1, CSTR("F}")); + test(_Flags::_Type::__general_upper_case, 1, CSTR("G}")); + test_exception("The format-spec type field has an invalid value", CSTR("H")); + test_exception("The format-spec type field has an invalid value", CSTR("I")); + test_exception("The format-spec type field has an invalid value", CSTR("J")); + test_exception("The format-spec type field has an invalid value", CSTR("K")); + test_exception( + "A locale-specific form field isn't allowed in this format-spec", + CSTR("L")); + test_exception("The format-spec type field has an invalid value", CSTR("M")); + test_exception("The format-spec type field has an invalid value", CSTR("N")); + test_exception("The format-spec type field has an invalid value", CSTR("O")); + test_exception("The format-spec type field has an invalid value", CSTR("P")); + test_exception("The format-spec type field has an invalid value", CSTR("Q")); + test_exception("The format-spec type field has an invalid value", CSTR("R")); + test_exception("The format-spec type field has an invalid value", CSTR("S")); + test_exception("The format-spec type field has an invalid value", CSTR("T")); + test_exception("The format-spec type field has an invalid value", CSTR("U")); + test_exception("The format-spec type field has an invalid value", CSTR("V")); + test_exception("The format-spec type field has an invalid value", CSTR("W")); + test(_Flags::_Type::__hexadecimal_upper_case, 1, CSTR("X}")); + test_exception("The format-spec type field has an invalid value", CSTR("Y")); + test_exception("The format-spec type field has an invalid value", CSTR("Z")); + + test(_Flags::_Type::__float_hexadecimal_lower_case, 1, CSTR("a}")); + test(_Flags::_Type::__binary_lower_case, 1, CSTR("b}")); + test(_Flags::_Type::__char, 1, CSTR("c}")); + test(_Flags::_Type::__decimal, 1, CSTR("d}")); + test(_Flags::_Type::__scientific_lower_case, 1, CSTR("e}")); + test(_Flags::_Type::__fixed, 1, CSTR("f}")); + test(_Flags::_Type::__general_lower_case, 1, CSTR("g}")); + test_exception("The format-spec type field has an invalid value", CSTR("h")); + test_exception("The format-spec type field has an invalid value", CSTR("i")); + test_exception("The format-spec type field has an invalid value", CSTR("j")); + test_exception("The format-spec type field has an invalid value", CSTR("k")); + test_exception("The format-spec type field has an invalid value", CSTR("l")); + test_exception("The format-spec type field has an invalid value", CSTR("m")); + test_exception("The format-spec type field has an invalid value", CSTR("n")); + test(_Flags::_Type::__octal, 1, CSTR("o}")); + test(_Flags::_Type::__pointer, 1, CSTR("p}")); + test_exception("The format-spec type field has an invalid value", CSTR("q")); + test_exception("The format-spec type field has an invalid value", CSTR("r")); + test(_Flags::_Type::__string, 1, CSTR("s}")); + test_exception("The format-spec type field has an invalid value", + CSTR("t")); + test_exception("The format-spec type field has an invalid value", + CSTR("u")); + test_exception("The format-spec type field has an invalid value", + CSTR("v")); + test_exception("The format-spec type field has an invalid value", + CSTR("w")); + test(_Flags::_Type::__hexadecimal_lower_case, 1, CSTR("x}")); + test_exception("The format-spec type field has an invalid value", CSTR("y")); + test_exception("The format-spec type field has an invalid value", CSTR("z")); + + test_exception("The format-spec should consume the input or end with a '}'", + CSTR("ab")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_width.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_width.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_width.pass.cpp @@ -0,0 +1,144 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the width. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = __parser_std; + +#include "test_exception.h" // requires parser + +struct Expected { + uint32_t width{0}; + bool width_as_arg{false}; +}; + +template +constexpr void test(Expected expected, size_t size, + std::basic_string_view fmt) { + // Initialize parser with sufficient arguments to avoid the parsing to fail + // due to insufficient arguments. + std::basic_format_parse_context parse_ctx(fmt, + std::__format::__number_max); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == false); + assert(parser.__width == expected.width); + assert(parser.__width_as_arg == expected.width_as_arg); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test(Expected expected, size_t size, const CharT* f) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, size, fmt); + fmt.remove_suffix(1); + test(expected, size, fmt); +} + +template +constexpr void test() { + test({0, 0}, 0, CSTR("}")); + test({1, 0}, 1, CSTR("1}")); + test({10, 0}, 2, CSTR("10}")); + test({1000, 0}, 4, CSTR("1000}")); + test({1000000, 0}, 7, CSTR("1000000}")); + + test({0, 1}, 2, CSTR("{}}")); + test({0, 1}, 3, CSTR("{0}}")); + test({1, 1}, 3, CSTR("{1}}")); + + test_exception("A format-spec width field shouldn't have a leading zero", + CSTR("00")); + + static_assert(std::__format::__number_max == 2'147'483'647, + "Update the assert and the test."); + test({2'147'483'647, 0}, 10, CSTR("2147483647}")); + test_exception("The numeric value of the format-spec is too large", + CSTR("2147483648")); + test_exception("The numeric value of the format-spec is too large", + CSTR("5000000000")); + test_exception("The numeric value of the format-spec is too large", + CSTR("10000000000")); + + test_exception("End of input while parsing format-spec arg-id", CSTR("{")); + test_exception("A format-spec arg-id should terminate at a '}'", CSTR("{0")); + test_exception( + "The arg-id of the format-spec starts with an invalid character", + CSTR("{a")); + test_exception("A format-spec arg-id should terminate at a '}'", CSTR("{1")); + test_exception("A format-spec arg-id should terminate at a '}'", CSTR("{9")); + test_exception("A format-spec arg-id should terminate at a '}'", CSTR("{9:")); + test_exception("A format-spec arg-id should terminate at a '}'", CSTR("{9a")); + + static_assert(std::__format::__number_max == 2'147'483'647, + "Update the assert and the test."); + // Note the static_assert tests whether the arg-id is valid. + // Therefore the following should be true arg-id < __format::__number_max. + test({2'147'483'646, 1}, 12, CSTR("{2147483646}}")); + test_exception("The numeric value of the format-spec is too large", + CSTR("{2147483648}")); + test_exception("The numeric value of the format-spec is too large", + CSTR("{5000000000}")); + test_exception("The numeric value of the format-spec is too large", + CSTR("{10000000000}")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_zero_padding.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_zero_padding.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_zero_padding.pass.cpp @@ -0,0 +1,92 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the zero-padding. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = __parser_std; + +template +constexpr void test(bool expected, size_t size, + std::basic_string_view fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == expected); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test(bool expected, size_t size, const CharT* f) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, size, fmt); + fmt.remove_suffix(1); + test(expected, size, fmt); +} + +template +constexpr void test() { + test(false, 0, CSTR("}")); + test(true, 1, CSTR("0}")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/test_exception.h b/libcxx/test/std/utilities/format/format.string/format.string.std/test_exception.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/test_exception.h @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// 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_TEST_STD_UTILITIES_FORMAT_FORMAT_STRING_FORMAT_STRING_STD_TEST_EXCEPTION_H +#define _LIBCPP_TEST_STD_UTILITIES_FORMAT_FORMAT_STRING_FORMAT_STRING_STD_TEST_EXCEPTION_H + +// Helper header for the tests in this directory. +// Note the header isn't freestanding. + +namespace detail { +#ifndef _LIBCPP_NO_EXCEPTIONS +template +void test_exception(std::string_view what, const CharT* fmt) { + try { + std::basic_format_parse_context parse_ctx(fmt); + (void)parser{}.parse(parse_ctx); +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + if constexpr (std::same_as) + std::cerr << "\nFormat string " << fmt + << "\nDidn't throw an exception.\n"; +#endif + assert(false); + } catch (std::format_error& e) { +#if defined(_LIBCPP_VERSION) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) + if constexpr (std::same_as) + if (e.what() != what) + std::cerr << "\nFormat string " << fmt << "\nExpected exception " + << what << "\nActual exception " << e.what() << '\n'; +#endif + LIBCPP_ASSERT(e.what() == what); + return; + } + + assert(false); +} +#endif +} // namespace detail + +/** + * Wrapper for the exception tests. + * + * When using the real function directly during in a constexpr test and add + * the `std::is_constant_evaluated()` test there the compilation fails. This + * happens since assert calls the non-constexpr function '__assert_fail'. + * Solve this issue with an layer of indirection. + */ +template +constexpr void test_exception(std::string_view what, const CharT* fmt) { +#ifndef _LIBCPP_NO_EXCEPTIONS + if (!std::is_constant_evaluated()) + detail::test_exception(what, fmt); +#else + (void)what; + (void)fmt; +#endif +} + +#endif // _LIBCPP_TEST_STD_UTILITIES_FORMAT_FORMAT_STRING_FORMAT_STRING_STD_TEST_EXCEPTION_H