diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -46,6 +46,7 @@ - P0174R2 (Deprecating Vestigial Library Parts in C++17) - N4190 (Removing auto_ptr, random_shuffle(), And Old Stuff) - P0154R1 (Hardware inference size) +- P2418R2 (Add support for ``std::generator``-like types to ``std::format``) - Marked the following papers as "Complete" (note that some of those might have been implemented in a previous release but not marked as such): @@ -83,6 +84,9 @@ of throwing an exception at run-time. (This does not affect the ``v`` functions.) +- All format functions in ```` allow the usage of non-copyable types as + argument for the formatting functions. This change causes bit fields to become + invalid arguments for the formatting functions. API Changes ----------- diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -203,5 +203,5 @@ "","","","","","" "`P2372R3 `__","LWG","Fixing locale handling in chrono formatters","October 2021","","" "`P2415R2 `__","LWG","What is a ``view``","October 2021","|Complete|","14.0" -"`P2418R2 `__","LWG","Add support for ``std::generator``-like types to ``std::format``","October 2021","|Partial|","" +"`P2418R2 `__","LWG","Add support for ``std::generator``-like types to ``std::format``","October 2021","|Complete|","15.0" "`P2432R1 `__","LWG","Fix ``istream_view``","October 2021","","" diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv --- a/libcxx/docs/Status/Cxx2bIssues.csv +++ b/libcxx/docs/Status/Cxx2bIssues.csv @@ -161,4 +161,5 @@ "","","","","" "`3645 `__","``resize_and_overwrite`` is overspecified to call its callback with lvalues","Not voted in","|Complete|","14.0","" "`3656 `__","Inconsistent bit operations returning a count","Not voted in","|Complete|","15.0","" +"`3631 `__","``basic_format_arg(T&&)`` should use ``remove_cvref_t`` throughout","Not voted in","|Complete|","15.0","" "","","","","" diff --git a/libcxx/include/__format/format_arg.h b/libcxx/include/__format/format_arg.h --- a/libcxx/include/__format/format_arg.h +++ b/libcxx/include/__format/format_arg.h @@ -147,15 +147,20 @@ /// Contains the implementation for basic_format_arg::handle. struct __handle { template - _LIBCPP_HIDE_FROM_ABI explicit __handle(const _Tp& __v) noexcept + _LIBCPP_HIDE_FROM_ABI explicit __handle(_Tp&& __v) noexcept : __ptr_(_VSTD::addressof(__v)), __format_([](basic_format_parse_context<_CharT>& __parse_ctx, _Context& __ctx, const void* __ptr) { - using _Formatter = typename _Context::template formatter_type<_Tp>; - using _Qp = conditional_t(), declval<_Context&>()); }, - const _Tp, _Tp>; + using _Dp = remove_cvref_t<_Tp>; + using _Formatter = typename _Context::template formatter_type<_Dp>; + constexpr bool __const_formattable = + requires { _Formatter().format(declval(), declval<_Context&>()); }; + using _Qp = conditional_t<__const_formattable, const _Dp, _Dp>; + + static_assert(__const_formattable || !is_const_v>, "Mandated by [format.arg]/18"); + _Formatter __f; __parse_ctx.advance_to(__f.parse(__parse_ctx)); - __ctx.advance_to(__f.format(*const_cast<_Qp*>(static_cast(__ptr)), __ctx)); + __ctx.advance_to(__f.format(*const_cast<_Qp*>(static_cast(__ptr)), __ctx)); }) {} const void* __ptr_; @@ -205,7 +210,9 @@ _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(basic_string_view<_CharT> __value) noexcept : __string_view_(__value) {} _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(const void* __value) noexcept : __ptr_(__value) {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__handle __value) noexcept : __handle_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__handle __value) noexcept + // TODO FMT Investigate why it doesn't work without the forward. + : __handle_(std::forward<__handle>(__value)) {} }; template @@ -251,11 +258,11 @@ __handle_.__format_(__parse_ctx, __ctx, __handle_.__ptr_); } - _LIBCPP_HIDE_FROM_ABI explicit handle(typename __basic_format_arg_value<_Context>::__handle __handle) noexcept + _LIBCPP_HIDE_FROM_ABI explicit handle(typename __basic_format_arg_value<_Context>::__handle& __handle) noexcept : __handle_(__handle) {} private: - typename __basic_format_arg_value<_Context>::__handle __handle_; + typename __basic_format_arg_value<_Context>::__handle& __handle_; }; #endif //_LIBCPP_STD_VER > 17 diff --git a/libcxx/include/__format/format_arg_store.h b/libcxx/include/__format/format_arg_store.h --- a/libcxx/include/__format/format_arg_store.h +++ b/libcxx/include/__format/format_arg_store.h @@ -197,7 +197,7 @@ int __shift = 0; ( [&] { - basic_format_arg<_Context> __arg = __create_format_arg<_Context>(_VSTD::forward<_Args>(__args)); + basic_format_arg<_Context> __arg = __create_format_arg<_Context>(__args); if (__shift != 0) __types |= static_cast(__arg.__type_) << __shift; else @@ -211,7 +211,7 @@ template _LIBCPP_HIDE_FROM_ABI void __store_basic_format_arg(basic_format_arg<_Context>* __data, _Args&&... __args) noexcept { - ([&] { *__data++ = __create_format_arg<_Context>(_VSTD::forward<_Args>(__args)); }(), ...); + ([&] { *__data++ = __create_format_arg<_Context>(__args); }(), ...); } template @@ -230,12 +230,12 @@ template struct _LIBCPP_TEMPLATE_VIS __format_arg_store { _LIBCPP_HIDE_FROM_ABI - __format_arg_store(_Args&&... __args) noexcept { + __format_arg_store(_Args&... __args) noexcept { if constexpr (sizeof...(_Args) != 0) { if constexpr (__format::__use_packed_format_arg_store(sizeof...(_Args))) - __format::__create_packed_storage(__storage.__types_, __storage.__values_, _VSTD::forward<_Args>(__args)...); + __format::__create_packed_storage(__storage.__types_, __storage.__values_, __args...); else - __format::__store_basic_format_arg<_Context>(__storage.__args_, _VSTD::forward<_Args>(__args)...); + __format::__store_basic_format_arg<_Context>(__storage.__args_, __args...); } } diff --git a/libcxx/include/format b/libcxx/include/format --- a/libcxx/include/format +++ b/libcxx/include/format @@ -36,13 +36,13 @@ // [format.functions], formatting functions template - string format(format-string fmt, const Args&... args); + string format(format-string fmt, Args&&... args); template - wstring format(wformat-string fmt, const Args&... args); + wstring format(wformat-string fmt, Args&&... args); template - string format(const locale& loc, format-string fmt, const Args&... args); + string format(const locale& loc, format-string fmt, Args&&... args); template - wstring format(const locale& loc, wformat-string fmt, const Args&... args); + wstring format(const locale& loc, wformat-string fmt, Args&&... args); string vformat(string_view fmt, format_args args); wstring vformat(wstring_view fmt, wformat_args args); @@ -50,13 +50,13 @@ wstring vformat(const locale& loc, wstring_view fmt, wformat_args args); template - Out format_to(Out out, format-string fmt, const Args&... args); + Out format_to(Out out, format-string fmt, Args&&... args); template - Out format_to(Out out, wformat-string fmt, const Args&... args); + Out format_to(Out out, wformat-string fmt, Args&&... args); template - Out format_to(Out out, const locale& loc, format-string fmt, const Args&... args); + Out format_to(Out out, const locale& loc, format-string fmt, Args&&... args); template - Out format_to(Out out, const locale& loc, wformat-string fmt, const Args&... args); + Out format_to(Out out, const locale& loc, wformat-string fmt, Args&&... args); template Out vformat_to(Out out, string_view fmt, format_args args); @@ -75,27 +75,27 @@ }; template format_to_n_result format_to_n(Out out, iter_difference_t n, - format-string fmt, const Args&... args); + format-string fmt, Args&&... args); template format_to_n_result format_to_n(Out out, iter_difference_t n, - wformat-string fmt, const Args&... args); + wformat-string fmt, Args&&... args); template format_to_n_result format_to_n(Out out, iter_difference_t n, const locale& loc, format-string fmt, - const Args&... args); + Args&&... args); template format_to_n_result format_to_n(Out out, iter_difference_t n, const locale& loc, wformat-string fmt, - const Args&... args); + Args&&... args); template - size_t formatted_size(format-string fmt, const Args&... args); + size_t formatted_size(format-string fmt, Args&&... args); template - size_t formatted_size(wformat-string fmt, const Args&... args); + size_t formatted_size(wformat-string fmt, Args&&... args); template - size_t formatted_size(const locale& loc, format-string fmt, const Args&... args); + size_t formatted_size(const locale& loc, format-string fmt, Args&&... args); template - size_t formatted_size(const locale& loc, wformat-string fmt, const Args&... args); + size_t formatted_size(const locale& loc, wformat-string fmt, Args&&... args); // [format.formatter], formatter template struct formatter; @@ -117,10 +117,10 @@ template format-arg-store - make_format_args(const Args&... args); + make_format_args(Args&&... args); template format-arg-store - make_wformat_args(const Args&... args); + make_wformat_args(Args&&... args); // [format.error], class format_error class format_error; @@ -190,26 +190,15 @@ using wformat_args = basic_format_args; #endif -// TODO FMT This helper wrapper can probably be removed after P2418 has been -// implemented. -template -_LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...> -__make_format_args(_Args&&... __args) { - return _VSTD::__format_arg_store<_Context, _Args...>( - _VSTD::forward<_Args>(__args)...); -} - -// TODO FMT After P2418 specify the return type instead of using auto. template -_LIBCPP_HIDE_FROM_ABI auto make_format_args(const _Args&... __args) { - return _VSTD::__make_format_args<_Context>(__args...); +_LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...> make_format_args(_Args&&... __args) { + return _VSTD::__format_arg_store<_Context, _Args...>(__args...); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS -// TODO FMT After P2418 specify the return type instead of using auto. template -_LIBCPP_HIDE_FROM_ABI auto make_wformat_args(const _Args&... __args) { - return _VSTD::make_format_args(__args...); +_LIBCPP_HIDE_FROM_ABI __format_arg_store make_wformat_args(_Args&&... __args) { + return _VSTD::__format_arg_store(__args...); } #endif @@ -563,7 +552,7 @@ template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt -format_to(_OutIt __out_it, __format_string_t<_Args...> __fmt, const _Args&... __args) { +format_to(_OutIt __out_it, __format_string_t<_Args...> __fmt, _Args&&... __args) { return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt.__str_, _VSTD::make_format_args(__args...)); } @@ -571,7 +560,7 @@ #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt -format_to(_OutIt __out_it, __wformat_string_t<_Args...> __fmt, const _Args&... __args) { +format_to(_OutIt __out_it, __wformat_string_t<_Args...> __fmt, _Args&&... __args) { return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt.__str_, _VSTD::make_wformat_args(__args...)); } @@ -595,14 +584,14 @@ template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string format(__format_string_t<_Args...> __fmt, - const _Args&... __args) { + _Args&&... __args) { return _VSTD::vformat(__fmt.__str_, _VSTD::make_format_args(__args...)); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring -format(__wformat_string_t<_Args...> __fmt, const _Args&... __args) { +format(__wformat_string_t<_Args...> __fmt, _Args&&... __args) { return _VSTD::vformat(__fmt.__str_, _VSTD::make_wformat_args(__args...)); } #endif @@ -619,7 +608,7 @@ template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> -format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, __format_string_t<_Args...> __fmt, const _Args&... __args) { +format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, __format_string_t<_Args...> __fmt, _Args&&... __args) { return _VSTD::__vformat_to_n(_VSTD::move(__out_it), __n, __fmt.__str_, _VSTD::make_format_args(__args...)); } @@ -627,7 +616,7 @@ template _OutIt, class... _Args> _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, __wformat_string_t<_Args...> __fmt, - const _Args&... __args) { + _Args&&... __args) { return _VSTD::__vformat_to_n(_VSTD::move(__out_it), __n, __fmt.__str_, _VSTD::make_wformat_args(__args...)); } #endif @@ -642,14 +631,14 @@ template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t -formatted_size(__format_string_t<_Args...> __fmt, const _Args&... __args) { +formatted_size(__format_string_t<_Args...> __fmt, _Args&&... __args) { return _VSTD::__vformatted_size(__fmt.__str_, basic_format_args{_VSTD::make_format_args(__args...)}); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t -formatted_size(__wformat_string_t<_Args...> __fmt, const _Args&... __args) { +formatted_size(__wformat_string_t<_Args...> __fmt, _Args&&... __args) { return _VSTD::__vformatted_size(__fmt.__str_, basic_format_args{_VSTD::make_wformat_args(__args...)}); } #endif @@ -694,7 +683,7 @@ template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt -format_to(_OutIt __out_it, locale __loc, __format_string_t<_Args...> __fmt, const _Args&... __args) { +format_to(_OutIt __out_it, locale __loc, __format_string_t<_Args...> __fmt, _Args&&... __args) { return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt.__str_, _VSTD::make_format_args(__args...)); } @@ -702,7 +691,7 @@ #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt -format_to(_OutIt __out_it, locale __loc, __wformat_string_t<_Args...> __fmt, const _Args&... __args) { +format_to(_OutIt __out_it, locale __loc, __wformat_string_t<_Args...> __fmt, _Args&&... __args) { return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt.__str_, _VSTD::make_wformat_args(__args...)); } @@ -729,7 +718,7 @@ template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string format(locale __loc, __format_string_t<_Args...> __fmt, - const _Args&... __args) { + _Args&&... __args) { return _VSTD::vformat(_VSTD::move(__loc), __fmt.__str_, _VSTD::make_format_args(__args...)); } @@ -737,7 +726,7 @@ #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring -format(locale __loc, __wformat_string_t<_Args...> __fmt, const _Args&... __args) { +format(locale __loc, __wformat_string_t<_Args...> __fmt, _Args&&... __args) { return _VSTD::vformat(_VSTD::move(__loc), __fmt.__str_, _VSTD::make_wformat_args(__args...)); } @@ -757,7 +746,7 @@ template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, __format_string_t<_Args...> __fmt, - const _Args&... __args) { + _Args&&... __args) { return _VSTD::__vformat_to_n(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.__str_, _VSTD::make_format_args(__args...)); } @@ -766,7 +755,7 @@ template _OutIt, class... _Args> _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, __wformat_string_t<_Args...> __fmt, - const _Args&... __args) { + _Args&&... __args) { return _VSTD::__vformat_to_n(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.__str_, _VSTD::make_wformat_args(__args...)); } @@ -783,14 +772,14 @@ template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t -formatted_size(locale __loc, __format_string_t<_Args...> __fmt, const _Args&... __args) { +formatted_size(locale __loc, __format_string_t<_Args...> __fmt, _Args&&... __args) { return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt.__str_, basic_format_args{_VSTD::make_format_args(__args...)}); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t -formatted_size(locale __loc, __wformat_string_t<_Args...> __fmt, const _Args&... __args) { +formatted_size(locale __loc, __wformat_string_t<_Args...> __fmt, _Args&&... __args) { return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt.__str_, basic_format_args{_VSTD::make_wformat_args(__args...)}); } #endif diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp --- a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp @@ -22,9 +22,10 @@ #include "test_macros.h" int main(int, char**) { - using Context [[maybe_unused]] = std::basic_format_context< std::back_insert_iterator>, char>; + [[maybe_unused]] auto store = std::make_format_args(42, nullptr, false, 1.0); - std::make_format_args(42, nullptr, false, 1.0); + LIBCPP_STATIC_ASSERT( + std::same_as>); return 0; } diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp --- a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp @@ -22,10 +22,10 @@ #include "test_macros.h" int main(int, char**) { - using Context [[maybe_unused]] = - std::basic_format_context>, wchar_t>; + [[maybe_unused]] auto store = std::make_wformat_args(42, nullptr, false, 1.0); - std::make_wformat_args(42, nullptr, false, 1.0); + LIBCPP_STATIC_ASSERT( + std::same_as>); return 0; } diff --git a/libcxx/test/std/utilities/format/format.functions/P2418.pass.cpp b/libcxx/test/std/utilities/format/format.functions/P2418.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/P2418.pass.cpp @@ -0,0 +1,126 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// Tests whether a move only type can be formatted. This is required by +// P2418R2 "Add support for std::generator-like types to std::format" + +// + +#include +#include + +#include "MoveOnly.h" +#include "make_string.h" +#include "test_macros.h" + +#ifndef TEST_HAS_NO_LOCALIZATION +# include +#endif + +#define SV(S) MAKE_STRING_VIEW(CharT, S) + +template +struct std::formatter : std::formatter { + // TODO FMT Make this a const member function after the base class has been adapted. + auto format(const MoveOnly& m, auto& ctx) -> decltype(ctx.out()) { + return std::formatter::format(m.get(), ctx); + } +}; + +template +static void test() { + MoveOnly m{10}; + CharT buffer[10]; +#ifndef TEST_HAS_NO_LOCALIZATION + std::locale loc; +#endif + + assert(std::format(SV("{}"), MoveOnly{}) == SV("1")); + + assert(std::format(SV("{}"), m) == SV("10")); + assert(m.get() == 10); + + assert(std::format(SV("{}"), std::move(m)) == SV("10")); + assert(m.get() == 10); + +#ifndef TEST_HAS_NO_LOCALIZATION + assert(std::format(loc, SV("{}"), MoveOnly{}) == SV("1")); + + assert(std::format(loc, SV("{}"), m) == SV("10")); + assert(m.get() == 10); + + assert(std::format(loc, SV("{}"), std::move(m)) == SV("10")); + assert(m.get() == 10); +#endif + + assert(std::format_to(buffer, SV("{}"), MoveOnly{}) == &buffer[1]); + + assert(std::format_to(buffer, SV("{}"), m) == &buffer[2]); + assert(m.get() == 10); + + assert(std::format_to(buffer, SV("{}"), std::move(m)) == &buffer[2]); + assert(m.get() == 10); + +#ifndef TEST_HAS_NO_LOCALIZATION + assert(std::format_to(buffer, loc, SV("{}"), MoveOnly{}) == &buffer[1]); + + assert(std::format_to(buffer, loc, SV("{}"), m) == &buffer[2]); + assert(m.get() == 10); + + assert(std::format_to(buffer, loc, SV("{}"), std::move(m)) == &buffer[2]); + assert(m.get() == 10); +#endif + + assert(std::format_to_n(buffer, 5, SV("{}"), MoveOnly{}).out == &buffer[1]); + + assert(std::format_to_n(buffer, 5, SV("{}"), m).out == &buffer[2]); + assert(m.get() == 10); + + assert(std::format_to_n(buffer, 5, SV("{}"), std::move(m)).out == &buffer[2]); + assert(m.get() == 10); + +#ifndef TEST_HAS_NO_LOCALIZATION + assert(std::format_to_n(buffer, 5, loc, SV("{}"), MoveOnly{}).out == &buffer[1]); + + assert(std::format_to_n(buffer, 5, loc, SV("{}"), m).out == &buffer[2]); + assert(m.get() == 10); + + assert(std::format_to_n(buffer, 5, loc, SV("{}"), std::move(m)).out == &buffer[2]); + assert(m.get() == 10); +#endif + + assert(std::formatted_size(SV("{}"), MoveOnly{}) == 1); + + assert(std::formatted_size(SV("{}"), m) == 2); + assert(m.get() == 10); + + assert(std::formatted_size(SV("{}"), std::move(m)) == 2); + assert(m.get() == 10); + +#ifndef TEST_HAS_NO_LOCALIZATION + assert(std::formatted_size(loc, SV("{}"), MoveOnly{}) == 1); + + assert(std::formatted_size(loc, SV("{}"), m) == 2); + assert(m.get() == 10); + + assert(std::formatted_size(loc, SV("{}"), std::move(m)) == 2); + assert(m.get() == 10); +#endif +} + +int main(int, char**) { + test(); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.functions/format.verify.cpp b/libcxx/test/std/utilities/format/format.functions/format.verify.cpp --- a/libcxx/test/std/utilities/format/format.functions/format.verify.cpp +++ b/libcxx/test/std/utilities/format/format.functions/format.verify.cpp @@ -88,3 +88,17 @@ // expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}} #endif } + +struct tiny { + int bit : 1; +}; + +void P2418() +{ + auto t = tiny{}; + std::format("{}", t.bit); // expected-error{{non-const reference cannot bind to bit-field 'bit'}} + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + std::format(L"{}", t.bit); // expected-error{{non-const reference cannot bind to bit-field 'bit'}} +#endif +} diff --git a/libcxx/test/std/utilities/format/format.functions/format_tests.h b/libcxx/test/std/utilities/format/format.functions/format_tests.h --- a/libcxx/test/std/utilities/format/format.functions/format_tests.h +++ b/libcxx/test/std/utilities/format/format.functions/format_tests.h @@ -2489,6 +2489,11 @@ check.template operator()<"answer is '{:X}'">(SV("answer is '0XAA55'"), status::foobar); check.template operator()<"answer is '{:s}'">(SV("answer is 'foobar'"), status::foobar); + // P2418 Changed the argument from a const reference to a forwarding reference. + // This mainly affects handle classes, however since we use an abstraction + // layer here it's "tricky" to verify whether this test would do the "right" + // thing. So these tests are done separately. + // *** type *** for (const auto& fmt : invalid_types("xXs")) check_exception("The format-spec type has a type not supported for a status argument", fmt, status::foo);