diff --git a/libcxx/include/__format/format_arg.h b/libcxx/include/__format/format_arg.h index 10f0ba9928ce7..9f399f709f570 100644 --- a/libcxx/include/__format/format_arg.h +++ b/libcxx/include/__format/format_arg.h @@ -219,7 +219,7 @@ class __basic_format_arg_value { __format_([](basic_format_parse_context<_CharT>& __parse_ctx, _Context& __ctx, const void* __ptr) { using _Dp = remove_const_t<_Tp>; using _Qp = conditional_t<__formattable_with, const _Dp, _Dp>; - static_assert(__formattable_with<_Qp, _Context>, "Mandated by [format.arg]/10"); + static_assert(__formattable_with<_Qp, _Context>, "Mandated by [format.arg]/12"); typename _Context::template formatter_type<_Dp> __f; __parse_ctx.advance_to(__f.parse(__parse_ctx)); @@ -334,21 +334,16 @@ class _LIBCPP_TEMPLATE_VIS _LIBCPP_NO_SPECIALIZATIONS basic_format_arg { private: using char_type = typename _Context::char_type; - // TODO FMT Implement constrain [format.arg]/4 - // Constraints: The template specialization - // typename Context::template formatter_type - // meets the Formatter requirements ([formatter.requirements]). The extent - // to which an implementation determines that the specialization meets the - // Formatter requirements is unspecified, except that as a minimum the - // expression - // typename Context::template formatter_type() - // .format(declval(), declval()) - // shall be well-formed when treated as an unevaluated operand. - public: __basic_format_arg_value<_Context> __value_; __format::__arg_t __type_; + // This constructor is used for the exposition only constructor. + // Per [format.arg]/4 + // template explicit basic_format_arg(T& v) noexcept; + // Constraints: T satisfies formattable-with. + // + // This constraint is implemented in __create_format_arg _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(__format::__arg_t __type, __basic_format_arg_value<_Context> __value) noexcept : __value_(__value), __type_(__type) {} diff --git a/libcxx/include/__format/format_arg_store.h b/libcxx/include/__format/format_arg_store.h index c6c7fdeedcfe6..711736ff19c4f 100644 --- a/libcxx/include/__format/format_arg_store.h +++ b/libcxx/include/__format/format_arg_store.h @@ -16,11 +16,15 @@ #include <__concepts/arithmetic.h> #include <__concepts/same_as.h> +#include <__concepts/semiregular.h> #include <__config> #include <__format/concepts.h> #include <__format/format_arg.h> +#include <__format/format_parse_context.h> #include <__type_traits/conditional.h> #include <__type_traits/extent.h> +#include <__type_traits/is_assignable.h> +#include <__type_traits/is_constructible.h> #include <__type_traits/remove_const.h> #include #include @@ -161,12 +165,11 @@ consteval __arg_t __determine_arg_t() { // // Modeled after template explicit basic_format_arg(T& v) noexcept; // [format.arg]/4-6 -template +template _Tp> _LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp& __value) noexcept { using _Dp = remove_const_t<_Tp>; constexpr __arg_t __arg = __format::__determine_arg_t<_Context, _Dp>(); static_assert(__arg != __arg_t::__none, "the supplied type is not formattable"); - static_assert(__formattable_with<_Tp, _Context>); // Not all types can be used to directly initialize the // __basic_format_arg_value. First handle all types needing adjustment, the @@ -205,6 +208,149 @@ _LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp& __valu return basic_format_arg<_Context>{__arg, __value}; } +// Helper function that issues a diagnostic when __formattable_with<_Tp, _Context> is false. +// +// Since it's quite easy to make a mistake writing a formatter specialization +// this function tries to give a better explanation. This should improve the +// diagnostics when trying to format type that has no properly specialized +// formatter. +template +[[noreturn]] _LIBCPP_HIDE_FROM_ABI constexpr void __diagnose_invalid_formatter() { + using _Formatter = typename _Context::template formatter_type>; + constexpr bool __is_disabled = + !is_default_constructible_v<_Formatter> && !is_copy_constructible_v<_Formatter> && + !is_move_constructible_v<_Formatter> && !is_copy_assignable_v<_Formatter> && !is_move_assignable_v<_Formatter>; + constexpr bool __is_semiregular = semiregular<_Formatter>; + + constexpr bool __has_parse_function = + requires(_Formatter& __f, basic_format_parse_context __pc) { + { __f.parse(__pc) }; + }; + constexpr bool __correct_parse_function_return_type = + requires(_Formatter& __f, basic_format_parse_context __pc) { + { __f.parse(__pc) } -> same_as; + }; + + // The reason these static_asserts are placed in an if-constexpr-chain is to + // only show one error. For example, when the formatter is not specialized it + // would show all static_assert messages. With this chain the compiler only + // evaluates one static_assert. + + if constexpr (__is_disabled) + + static_assert( +# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600 + sizeof(_Tp) == 0 +# else + false +# endif + , + "The required formatter specialization has not been provided."); + else if constexpr (!__is_semiregular) + static_assert( +# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600 + sizeof(_Tp) == 0 +# else + false +# endif + , + "The required formatter specialization is not semiregular."); + + else if constexpr (!__has_parse_function) + static_assert( +# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600 + sizeof(_Tp) == 0 +# else + false +# endif + , + "The required formatter specialization does not have a parse function taking the proper arguments."); + else if constexpr (!__correct_parse_function_return_type) + static_assert( +# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600 + sizeof(_Tp) == 0 +# else + false +# endif + , + "The required formatter specialization's parse function does not return the required type."); + + else { + // During constant evaluation this function is called, but the format + // member function has not been evaluated. This means these functions + // can't be evaluated at that time. + // + // Note this else branch should never been taken during constant + // eveluation, the static_asserts in one of the branches above should + // trigger. + constexpr bool __has_format_function = requires(_Formatter& __f, _Tp&& __t, _Context __fc) { + { __f.format(__t, __fc) }; + }; + constexpr bool __is_format_function_const_qualified = requires(const _Formatter& __cf, _Tp&& __t, _Context __fc) { + { __cf.format(__t, __fc) }; + }; + constexpr bool __correct_format_function_return_type = requires(_Formatter& __f, _Tp&& __t, _Context __fc) { + { __f.format(__t, __fc)->template same_as }; + }; + + if constexpr (!__has_format_function) + static_assert( +# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600 + sizeof(_Tp) == 0 +# else + false +# endif + , + "The required formatter specialization does not have a format function taking the proper arguments."); + else if constexpr (!__is_format_function_const_qualified) + static_assert( +# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600 + sizeof(_Tp) == 0 +# else + false +# endif + , + "The required formatter specialization's format function is not const qualified."); + else if constexpr (!__correct_format_function_return_type) + static_assert( +# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600 + sizeof(_Tp) == 0 +# else + false +# endif + , + "The required formatter specialization's format function does not return the required type."); + + else + // This should not happen; it makes sure the code is ill-formed. + static_assert( +# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600 + sizeof(_Tp) == 0 +# else + false +# endif + , + "The required formatter specialization is not formattable with its context."); + } +} + +// The Psuedo constructor is constrained per [format.arg]/4. +// The only way for a user to call __create_format_arg is from std::make_format_args. +// Per [format.arg.store]/3 +// template +// format-arg-store make_format_args(Args&... fmt_args); +// +// Preconditions: The type typename Context::template formatter_type> +// meets the BasicFormatter requirements ([formatter.requirements]) for each Ti in Args. +// +// This function is only called when the precondition is violated. +// Without this function the compilation would fail since the overload is +// SFINAE'ed away. This function is used to provide better diagnostics. +template +_LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp&) noexcept { + __format::__diagnose_invalid_formatter<_Context, _Tp>(); +} + template _LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values, _Args&... __args) noexcept { diff --git a/libcxx/include/__format/format_context.h b/libcxx/include/__format/format_context.h index 4dbfdbc02a267..11a4c427bcbc2 100644 --- a/libcxx/include/__format/format_context.h +++ b/libcxx/include/__format/format_context.h @@ -65,9 +65,13 @@ __format_context_create(_OutIt __out_it, basic_format_args>, char>; +template +using __format_context _LIBCPP_NODEBUG = + basic_format_context>, _CharT>; + +using format_context = __format_context; # if _LIBCPP_HAS_WIDE_CHARACTERS -using wformat_context = basic_format_context< back_insert_iterator<__format::__output_buffer>, wchar_t>; +using wformat_context = __format_context; # endif template diff --git a/libcxx/include/__format/format_functions.h b/libcxx/include/__format/format_functions.h index 5feaf7e5a064a..bea2ff0387eed 100644 --- a/libcxx/include/__format/format_functions.h +++ b/libcxx/include/__format/format_functions.h @@ -13,6 +13,7 @@ #include <__algorithm/clamp.h> #include <__concepts/convertible_to.h> #include <__concepts/same_as.h> +#include <__concepts/semiregular.h> #include <__config> #include <__format/buffer.h> #include <__format/format_arg.h> @@ -78,6 +79,27 @@ template namespace __format { +/// A "watered-down" __formattable_with for usage during constant evaluation. +/// +/// The member function "format" can have a decuded return type. When +/// constraining the __compile_time_handle this leads to a nested concept +/// eveluation that is always false. Instead ignore the "format" member function. +/// +/// In theory the same could happen to the "parse" member function. However +/// thisfunction does not is less likely to have odd cased, and this function +/// is "required" to be called during constant evaluation. So if this +/// function's return type needs to be deduced things will probably not work. +/// +/// See https://github.com/llvm/llvm-project/issues/81590 and +/// test/std/utilities/format/format.functions/bug_81590.compile.pass.cpp for +/// an example of a problematic "format" member function. +template >> +concept __formattable_with_constexpr = + semiregular<_Formatter> && + requires(_Formatter& __f, _Tp&& __t, basic_format_parse_context __pc) { + { __f.parse(__pc) } -> same_as; + }; + /// Helper class parse and handle argument. /// /// When parsing a handle which is not enabled the code is ill-formed. @@ -91,6 +113,7 @@ class _LIBCPP_TEMPLATE_VIS __compile_time_handle { } template + requires __formattable_with_constexpr<_Tp, __format_context<_CharT>> _LIBCPP_HIDE_FROM_ABI constexpr void __enable() { __parse_ = [](basic_format_parse_context<_CharT>& __ctx) { formatter<_Tp, _CharT> __f; @@ -98,6 +121,14 @@ class _LIBCPP_TEMPLATE_VIS __compile_time_handle { }; } + template + _LIBCPP_HIDE_FROM_ABI constexpr void __enable() { + // Avoids the throw set in the constructor. + // Otherwise two diagnostics will be issued. + __parse_ = [](basic_format_parse_context<_CharT>&) {}; + __format::__diagnose_invalid_formatter<__format_context<_CharT>, _Tp>(); + } + // Before calling __parse the proper handler needs to be set with __enable. // The default handler isn't a core constant expression. _LIBCPP_HIDE_FROM_ABI constexpr __compile_time_handle() diff --git a/libcxx/test/libcxx/utilities/format/format.arguments/format.arg.store/make_format_args.verify.cpp b/libcxx/test/libcxx/utilities/format/format.arguments/format.arg.store/make_format_args.verify.cpp new file mode 100644 index 0000000000000..adcd9ac115abb --- /dev/null +++ b/libcxx/test/libcxx/utilities/format/format.arguments/format.arg.store/make_format_args.verify.cpp @@ -0,0 +1,206 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// format-arg-store make_format_args(Args&... args); +// +// Preconditions: The type typename Context::template formatter_type> +// meets the BasicFormatter requirements ([formatter.requirements]) for each Ti in Args. +// +// When the precondition is violated libc++ diagnoses the issue with the +// formatter specialization. + +// Apple-clang-15 does not support static_assert(false). Instead the code use +// static_assert(sizof(T) == 0, ...). This gives different diagnostics. +// Since the compiler is no longer the latest XCode version it makes little +// sense to add work-arounds. +// XFAIL: apple-clang-15 + +#include + +#include "test_macros.h" + +struct no_formatter_specialization {}; +void test_no_formatter_specialization() { + no_formatter_specialization t; + // expected-error@*:* {{static assertion failed: The required formatter specialization has not been provided.}} + (void)std::make_format_args(t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization has not been provided.}} + (void)std::make_wformat_args(t); +#endif +} + +struct correct_formatter_specialization {}; +template +struct std::formatter { + template + constexpr typename ParseContext::iterator parse(ParseContext&); + + template + typename FormatContext::iterator format(correct_formatter_specialization&, FormatContext&) const; +}; +void test_correct_formatter_specialization() { + correct_formatter_specialization t; + (void)std::make_format_args(t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + (void)std::make_wformat_args(t); +#endif +} + +struct formatter_not_semiregular {}; +template +struct std::formatter { + formatter(int); + + template + constexpr typename ParseContext::iterator parse(ParseContext&); + + template + typename FormatContext::iterator format(formatter_not_semiregular&, FormatContext&) const; +}; +void test_formatter_not_semiregular() { + formatter_not_semiregular t; + // expected-error@*:* {{static assertion failed: The required formatter specialization is not semiregular.}} + (void)std::make_format_args(t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization is not semiregular.}} + (void)std::make_wformat_args(t); +#endif +} + +struct formatter_no_parse_function {}; +template +struct std::formatter { + template + typename FormatContext::iterator format(formatter_no_parse_function&, FormatContext&) const; +}; +void test_formatter_no_parse_function() { + formatter_no_parse_function t; + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a parse function taking the proper arguments.}} + (void)std::make_format_args(t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a parse function taking the proper arguments.}} + (void)std::make_wformat_args(t); +#endif +} + +struct parse_function_invalid_arguments {}; +template +struct std::formatter { + template + constexpr typename ParseContext::iterator parse(ParseContext&, int); + + template + typename FormatContext::iterator format(parse_function_invalid_arguments&, FormatContext&) const; +}; +void test_parse_function_invalid_arguments() { + parse_function_invalid_arguments t; + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a parse function taking the proper arguments.}} + (void)std::make_format_args(t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a parse function taking the proper arguments.}} + (void)std::make_wformat_args(t); +#endif +} + +struct parse_function_invalid_return_type {}; +template +struct std::formatter { + template + constexpr int parse(ParseContext&); + + template + typename FormatContext::iterator format(parse_function_invalid_return_type&, FormatContext&) const; +}; +void test_parse_function_invalid_return_type() { + parse_function_invalid_return_type t; + // expected-error@*:* {{static assertion failed: The required formatter specialization's parse function does not return the required type.}} + (void)std::make_format_args(t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization's parse function does not return the required type.}} + (void)std::make_wformat_args(t); +#endif +} + +struct no_format_function {}; +template +struct std::formatter { + template + constexpr typename ParseContext::iterator parse(ParseContext&); +}; +void test_no_format_function() { + no_format_function t; + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a format function taking the proper arguments.}} + (void)std::make_format_args(t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a format function taking the proper arguments.}} + (void)std::make_wformat_args(t); +#endif +} + +struct format_function_invalid_arguments {}; +template +struct std::formatter { + template + constexpr typename ParseContext::iterator parse(ParseContext&); + + template + typename FormatContext::iterator format(format_function_invalid_arguments&) const; +}; +void test_format_function_invalid_arguments() { + format_function_invalid_arguments t; + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a format function taking the proper arguments.}} + (void)std::make_format_args(t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a format function taking the proper arguments.}} + (void)std::make_wformat_args(t); +#endif +} + +struct format_function_invalid_return_type {}; +template +struct std::formatter { + template + constexpr typename ParseContext::iterator parse(ParseContext&); + + template + int format(format_function_invalid_return_type&, FormatContext&) const; +}; +void test_format_function_invalid_return_type() { + format_function_invalid_return_type t; + // expected-error@*:* {{static assertion failed: The required formatter specialization's format function does not return the required type.}} + (void)std::make_format_args(t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization's format function does not return the required type.}} + (void)std::make_wformat_args(t); +#endif +} + +struct format_function_not_const_qualified {}; +template +struct std::formatter { + template + constexpr typename ParseContext::iterator parse(ParseContext&); + + template + typename FormatContext::iterator format(format_function_not_const_qualified&, FormatContext&); +}; +void test_format_function_not_const_qualified() { + format_function_not_const_qualified t; + // expected-error@*:* {{static assertion failed: The required formatter specialization's format function is not const qualified.}} + (void)std::make_format_args(t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization's format function is not const qualified.}} + (void)std::make_wformat_args(t); +#endif +} diff --git a/libcxx/test/libcxx/utilities/format/format.functions/format.verify.cpp b/libcxx/test/libcxx/utilities/format/format.functions/format.verify.cpp new file mode 100644 index 0000000000000..6d9347a9d828d --- /dev/null +++ b/libcxx/test/libcxx/utilities/format/format.functions/format.verify.cpp @@ -0,0 +1,411 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// When the fmt is not a format string for Args the expression is not a core +// constant exprssion. In this case the code is ill-formed. +// When this happens due to issues with the formatter specialization libc++ +// diagnoses the issue. + +// Apple-clang-15 does not support static_assert(false). Instead the code use +// static_assert(sizof(T) == 0, ...). This gives different diagnostics. +// Since the compiler is no longer the latest XCode version it makes little +// sense to add work-arounds. +// XFAIL: apple-clang-15 + +#include +#include + +#include "test_macros.h" + +struct no_formatter_specialization {}; +void test_no_formatter_specialization() { + no_formatter_specialization t; + // expected-error@*:* {{static assertion failed: The required formatter specialization has not been provided.}} + (void)std::format("{}", t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization has not been provided.}} + (void)std::format(L"{}", t); +#endif +} + +struct correct_formatter_specialization {}; +template +struct std::formatter { + template + constexpr typename ParseContext::iterator parse(ParseContext& ctx) { + return ctx.begin(); + } + + template + typename FormatContext::iterator format(correct_formatter_specialization&, FormatContext& ctx) const { + return ctx.out(); + } +}; +void test_correct_formatter_specialization() { + correct_formatter_specialization t; + (void)std::format("{}", t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + (void)std::format(L"{}", t); +#endif +} + +struct formatter_not_semiregular {}; +template +struct std::formatter { + formatter(int); + + template + constexpr typename ParseContext::iterator parse(ParseContext& ctx) { + return ctx.begin(); + } + + template + typename FormatContext::iterator format(formatter_not_semiregular&, FormatContext&) const; +}; +void test_formatter_not_semiregular() { + formatter_not_semiregular t; + // expected-error@*:* {{static assertion failed: The required formatter specialization is not semiregular.}} + (void)std::format("{}", t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization is not semiregular.}} + (void)std::format(L"{}", t); +#endif +} + +struct formatter_no_parse_function {}; +template +struct std::formatter { + template + typename FormatContext::iterator format(formatter_no_parse_function&, FormatContext&) const; +}; +void test_formatter_no_parse_function() { + formatter_no_parse_function t; + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a parse function taking the proper arguments.}} + (void)std::format("{}", t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a parse function taking the proper arguments.}} + (void)std::format(L"{}", t); +#endif +} + +struct parse_function_invalid_arguments {}; +template +struct std::formatter { + template + constexpr typename ParseContext::iterator parse(ParseContext& ctx, int) { + return ctx.begin(); + } + + template + typename FormatContext::iterator format(parse_function_invalid_arguments&, FormatContext&) const; +}; +void test_parse_function_invalid_arguments() { + parse_function_invalid_arguments t; + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a parse function taking the proper arguments.}} + (void)std::format("{}", t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a parse function taking the proper arguments.}} + (void)std::format(L"{}", t); +#endif +} + +struct parse_function_invalid_return_type {}; +template +struct std::formatter { + template + constexpr int parse(ParseContext&) { + return 42; + } + + template + typename FormatContext::iterator format(parse_function_invalid_return_type&, FormatContext&) const; +}; +void test_parse_function_invalid_return_type() { + parse_function_invalid_return_type t; + // expected-error@*:* {{static assertion failed: The required formatter specialization's parse function does not return the required type.}} + (void)std::format("{}", t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization's parse function does not return the required type.}} + (void)std::format(L"{}", t); +#endif +} + +struct no_format_function {}; +template +struct std::formatter { + template + constexpr typename ParseContext::iterator parse(ParseContext& ctx) { + return ctx.begin(); + } +}; +void test_no_format_function() { + no_format_function t; + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a format function taking the proper arguments.}} + (void)std::format("{}", t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a format function taking the proper arguments.}} + (void)std::format(L"{}", t); +#endif +} + +struct format_function_invalid_arguments {}; +template +struct std::formatter { + template + constexpr typename ParseContext::iterator parse(ParseContext& ctx) { + return ctx.begin(); + } + + template + typename FormatContext::iterator format(format_function_invalid_arguments&) const; +}; +void test_format_function_invalid_arguments() { + format_function_invalid_arguments t; + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a format function taking the proper arguments.}} + (void)std::format("{}", t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a format function taking the proper arguments.}} + (void)std::format(L"{}", t); +#endif +} + +struct format_function_invalid_return_type {}; +template +struct std::formatter { + template + constexpr typename ParseContext::iterator parse(ParseContext& ctx) { + return ctx.begin(); + } + + template + int format(format_function_invalid_return_type&, FormatContext&) const; +}; +void test_format_function_invalid_return_type() { + format_function_invalid_return_type t; + // expected-error@*:* {{static assertion failed: The required formatter specialization's format function does not return the required type.}} + (void)std::format("{}", t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization's format function does not return the required type.}} + (void)std::format(L"{}", t); +#endif +} + +struct format_function_not_const_qualified {}; +template +struct std::formatter { + template + constexpr typename ParseContext::iterator parse(ParseContext& ctx) { + return ctx.begin(); + } + + template + typename FormatContext::iterator format(format_function_not_const_qualified&, FormatContext&); +}; +void test_format_function_not_const_qualified() { + format_function_not_const_qualified t; + // expected-error@*:* {{static assertion failed: The required formatter specialization's format function is not const qualified.}} + (void)std::format("{}", t); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // expected-error@*:* {{static assertion failed: The required formatter specialization's format function is not const qualified.}} + (void)std::format(L"{}", t); +#endif +} + +struct auto_deduction_correct_formatter_specialization + : std::variant { + auto_deduction_correct_formatter_specialization* p = nullptr; + constexpr const std::variant& decay() const noexcept { + return *this; + } +}; + +template <> +struct std::formatter { + template + static constexpr auto parse(ParseContext& ctx) { + return ctx.begin(); + } + static constexpr auto format(const auto_deduction_correct_formatter_specialization& x, auto& ctx) { + if (!x.p) + return ctx.out(); + auto m = [&](const auto_deduction_correct_formatter_specialization* t) { + return std::format_to(ctx.out(), "{}", *t); + }; + return std::visit(m, x.decay()); + } +}; + +void test_auto_deduction_correct_formatter_specialization() { + auto_deduction_correct_formatter_specialization t; + (void)std::format("{}", t); +} + +struct auto_deduction_no_parse_function : std::variant { + auto_deduction_no_parse_function* p = nullptr; + constexpr const std::variant& decay() const noexcept { return *this; } +}; + +template <> +struct std::formatter { + static constexpr auto format(const auto_deduction_no_parse_function& x, auto& ctx) { + if (!x.p) + return ctx.out(); + auto m = [&](const auto_deduction_no_parse_function* t) { return std::format_to(ctx.out(), "{}", *t); }; + return std::visit(m, x.decay()); + } +}; + +void test_auto_deduction_no_parse_function() { + auto_deduction_no_parse_function t; + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a parse function taking the proper arguments.}} + (void)std::format("{}", t); +} + +struct auto_deduction_parse_function_invalid_arguments + : std::variant { + auto_deduction_parse_function_invalid_arguments* p = nullptr; + constexpr const std::variant& decay() const noexcept { + return *this; + } +}; + +template <> +struct std::formatter { + template + static constexpr auto parse(ParseContext& ctx, int) { + return ctx.begin(); + } + static constexpr auto format(const auto_deduction_parse_function_invalid_arguments& x, auto& ctx) { + if (!x.p) + return ctx.out(); + auto m = [&](const auto_deduction_parse_function_invalid_arguments* t) { + return std::format_to(ctx.out(), "{}", *t); + }; + return std::visit(m, x.decay()); + } +}; + +void test_auto_deduction_parse_function_invalid_arguments() { + auto_deduction_parse_function_invalid_arguments t; + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a parse function taking the proper arguments.}} + (void)std::format("{}", t); +} + +struct auto_deduction_parse_function_invalid_return_types + : std::variant { + auto_deduction_parse_function_invalid_return_types* p = nullptr; + constexpr const std::variant& decay() const noexcept { + return *this; + } +}; + +template <> +struct std::formatter { + template + static constexpr auto parse(ParseContext&) { + return 42; + } + static constexpr auto format(const auto_deduction_parse_function_invalid_return_types& x, auto& ctx) { + if (!x.p) + return ctx.out(); + auto m = [&](const auto_deduction_parse_function_invalid_return_types* t) { + return std::format_to(ctx.out(), "{}", *t); + }; + return std::visit(m, x.decay()); + } +}; + +void test_auto_deduction_parse_function_invalid_return_types() { + // expected-error@*:* {{static assertion failed: The required formatter specialization's parse function does not return the required type.}} + auto_deduction_parse_function_invalid_return_types t; + (void)std::format("{}", t); +} + +struct auto_deduction_no_format_function : std::variant { + auto_deduction_no_format_function* p = nullptr; + // expected-error@*:* 2 {{static assertion failed: The required formatter specialization's format function does not return the required type.}} + constexpr const std::variant& decay() const noexcept { return *this; } +}; + +template <> +struct std::formatter { + template + static constexpr auto parse(ParseContext& ctx) { + return ctx.begin(); + } +}; + +void test_auto_deduction_no_format_function() { + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a format function taking the proper arguments.}} + auto_deduction_no_format_function t; + (void)std::format("{}", t); +} + +struct auto_deduction_format_function_invalid_arguments + : std::variant { + auto_deduction_format_function_invalid_arguments* p = nullptr; + constexpr const std::variant& decay() const noexcept { + return *this; + } +}; + +template <> +struct std::formatter { + template + static constexpr auto parse(ParseContext& ctx) { + return ctx.begin(); + } + static constexpr auto format(const auto_deduction_format_function_invalid_arguments& x, auto& ctx, int) { + if (!x.p) + return ctx.out(); + auto m = [&](const auto_deduction_format_function_invalid_arguments* t) { + return std::format_to(ctx.out(), "{}", *t); + }; + return std::visit(m, x.decay()); + } +}; + +void test_auto_deduction_format_function_invalid_arguments() { + // expected-error@*:* {{static assertion failed: The required formatter specialization does not have a format function taking the proper arguments.}} + auto_deduction_format_function_invalid_arguments t; + (void)std::format("{}", t); +} + +struct auto_deduction_format_function_invalid_return_types + : std::variant { + auto_deduction_format_function_invalid_return_types* p = nullptr; + constexpr const std::variant& decay() const noexcept { + return *this; + } +}; + +template <> +struct std::formatter { + template + static constexpr auto parse(ParseContext& ctx) { + return ctx.begin(); + } + static constexpr auto format(const auto_deduction_format_function_invalid_return_types& x, auto& ctx) { + if (!x.p) + return 42; + auto m = [&](const auto_deduction_format_function_invalid_return_types* t) { + std::format_to(ctx.out(), "{}", *t); + return 42; + }; + return std::visit(m, x.decay()); + } +}; + +void test_auto_deduction_format_function_invalid_return_types() { + auto_deduction_format_function_invalid_return_types t; + (void)std::format("{}", t); +}