[libc++][format] Use forwarding references.

This implements a not accepted LWG issue. Not doing so would require
integral types to use the handle class instead of being directly stored
in the basic_format_arg.

The previous code used `std::forward` in places where it wasn't required
by the Standard. These are now removed.

Implements:
- P2418R2 Add support for std::generator-like types to std::format
- LWG 3631 basic_format_arg(T&&) should use remove_cvref_t<T> throughout

Reviewed By: ldionne, #libc

Differential Revision: https://reviews.llvm.org/D127570
This commit is contained in:
Mark de Wever 2021-10-05 19:25:37 +02:00
parent c4ccf608c2
commit 606e280811
11 changed files with 215 additions and 68 deletions

View File

@ -47,6 +47,7 @@ Implemented Papers
- N4190 (Removing auto_ptr, random_shuffle(), And Old <functional> Stuff)
- P0154R1 (Hardware inference size)
- P0618R0 (Deprecating <codecvt>)
- 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):
@ -84,6 +85,9 @@ New Features
of throwing an exception at run-time. (This does not affect the ``v``
functions.)
- All format functions in ``<format>`` 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
-----------

View File

@ -203,5 +203,5 @@
"","","","","",""
"`P2372R3 <https://wg21.link/P2372R3>`__","LWG","Fixing locale handling in chrono formatters","October 2021","",""
"`P2415R2 <https://wg21.link/P2415R2>`__","LWG","What is a ``view``","October 2021","|Complete|","14.0"
"`P2418R2 <https://wg21.link/P2418R2>`__","LWG","Add support for ``std::generator``-like types to ``std::format``","October 2021","|Partial|",""
"`P2418R2 <https://wg21.link/P2418R2>`__","LWG","Add support for ``std::generator``-like types to ``std::format``","October 2021","|Complete|","15.0"
"`P2432R1 <https://wg21.link/P2432R1>`__","LWG","Fix ``istream_view``","October 2021","",""

1 Paper # Group Paper Name Meeting Status First released version
203
204 `P2372R3 <https://wg21.link/P2372R3>`__ LWG Fixing locale handling in chrono formatters October 2021
205 `P2415R2 <https://wg21.link/P2415R2>`__ LWG What is a ``view`` October 2021 |Complete| 14.0
206 `P2418R2 <https://wg21.link/P2418R2>`__ LWG Add support for ``std::generator``-like types to ``std::format`` October 2021 |Partial| |Complete| 15.0
207 `P2432R1 <https://wg21.link/P2432R1>`__ LWG Fix ``istream_view`` October 2021

View File

@ -161,4 +161,5 @@
"","","","",""
"`3645 <https://wg21.link/LWG3645>`__","``resize_and_overwrite`` is overspecified to call its callback with lvalues","Not voted in","|Complete|","14.0",""
"`3656 <https://wg21.link/LWG3656>`__","Inconsistent bit operations returning a count","Not voted in","|Complete|","15.0",""
"`3631 <https://wg21.link/LWG3631>`__","``basic_format_arg(T&&)`` should use ``remove_cvref_t<T>`` throughout","Not voted in","|Complete|","15.0",""
"","","","",""

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -147,15 +147,20 @@ public:
/// Contains the implementation for basic_format_arg::handle.
struct __handle {
template <class _Tp>
_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<requires { _Formatter().format(declval<const _Tp&>(), 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<const _Dp&>(), declval<_Context&>()); };
using _Qp = conditional_t<__const_formattable, const _Dp, _Dp>;
static_assert(__const_formattable || !is_const_v<remove_reference_t<_Tp>>, "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<const _Tp*>(__ptr)), __ctx));
__ctx.advance_to(__f.format(*const_cast<_Qp*>(static_cast<const _Dp*>(__ptr)), __ctx));
}) {}
const void* __ptr_;
@ -205,7 +210,9 @@ public:
_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 <class _Context>
@ -251,11 +258,11 @@ public:
__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

View File

@ -197,7 +197,7 @@ _LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_fo
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<uint64_t>(__arg.__type_) << __shift;
else
@ -211,7 +211,7 @@ _LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_fo
template <class _Context, class... _Args>
_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 <class _Context, size_t N>
@ -230,12 +230,12 @@ struct __unpacked_format_arg_store {
template <class _Context, class... _Args>
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...);
}
}

View File

@ -36,13 +36,13 @@ namespace std {
// [format.functions], formatting functions
template<class... Args>
string format(format-string<Args...> fmt, const Args&... args);
string format(format-string<Args...> fmt, Args&&... args);
template<class... Args>
wstring format(wformat-string<Args...> fmt, const Args&... args);
wstring format(wformat-string<Args...> fmt, Args&&... args);
template<class... Args>
string format(const locale& loc, format-string<Args...> fmt, const Args&... args);
string format(const locale& loc, format-string<Args...> fmt, Args&&... args);
template<class... Args>
wstring format(const locale& loc, wformat-string<Args...> fmt, const Args&... args);
wstring format(const locale& loc, wformat-string<Args...> fmt, Args&&... args);
string vformat(string_view fmt, format_args args);
wstring vformat(wstring_view fmt, wformat_args args);
@ -50,13 +50,13 @@ namespace std {
wstring vformat(const locale& loc, wstring_view fmt, wformat_args args);
template<class Out, class... Args>
Out format_to(Out out, format-string<Args...> fmt, const Args&... args);
Out format_to(Out out, format-string<Args...> fmt, Args&&... args);
template<class Out, class... Args>
Out format_to(Out out, wformat-string<Args...> fmt, const Args&... args);
Out format_to(Out out, wformat-string<Args...> fmt, Args&&... args);
template<class Out, class... Args>
Out format_to(Out out, const locale& loc, format-string<Args...> fmt, const Args&... args);
Out format_to(Out out, const locale& loc, format-string<Args...> fmt, Args&&... args);
template<class Out, class... Args>
Out format_to(Out out, const locale& loc, wformat-string<Args...> fmt, const Args&... args);
Out format_to(Out out, const locale& loc, wformat-string<Args...> fmt, Args&&... args);
template<class Out>
Out vformat_to(Out out, string_view fmt, format_args args);
@ -75,27 +75,27 @@ namespace std {
};
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
format-string<Args...> fmt, const Args&... args);
format-string<Args...> fmt, Args&&... args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
wformat-string<Args...> fmt, const Args&... args);
wformat-string<Args...> fmt, Args&&... args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
const locale& loc, format-string<Args...> fmt,
const Args&... args);
Args&&... args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
const locale& loc, wformat-string<Args...> fmt,
const Args&... args);
Args&&... args);
template<class... Args>
size_t formatted_size(format-string<Args...> fmt, const Args&... args);
size_t formatted_size(format-string<Args...> fmt, Args&&... args);
template<class... Args>
size_t formatted_size(wformat-string<Args...> fmt, const Args&... args);
size_t formatted_size(wformat-string<Args...> fmt, Args&&... args);
template<class... Args>
size_t formatted_size(const locale& loc, format-string<Args...> fmt, const Args&... args);
size_t formatted_size(const locale& loc, format-string<Args...> fmt, Args&&... args);
template<class... Args>
size_t formatted_size(const locale& loc, wformat-string<Args...> fmt, const Args&... args);
size_t formatted_size(const locale& loc, wformat-string<Args...> fmt, Args&&... args);
// [format.formatter], formatter
template<class T, class charT = char> struct formatter;
@ -117,10 +117,10 @@ namespace std {
template<class Context = format_context, class... Args>
format-arg-store<Context, Args...>
make_format_args(const Args&... args);
make_format_args(Args&&... args);
template<class... Args>
format-arg-store<wformat_context, Args...>
make_wformat_args(const Args&... args);
make_wformat_args(Args&&... args);
// [format.error], class format_error
class format_error;
@ -190,26 +190,15 @@ using format_args = basic_format_args<format_context>;
using wformat_args = basic_format_args<wformat_context>;
#endif
// TODO FMT This helper wrapper can probably be removed after P2418 has been
// implemented.
template <class _Context, class... _Args>
_LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...>
__make_format_args(_Args&&... __args) {
return _VSTD::__format_arg_store<_Context, _Args...>(
_VSTD::forward<_Args>(__args)...);
}
// TODO FMT After P2418 specify the return type instead of using auto.
template <class _Context = format_context, class... _Args>
_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 <class... _Args>
_LIBCPP_HIDE_FROM_ABI auto make_wformat_args(const _Args&... __args) {
return _VSTD::make_format_args<wformat_context>(__args...);
_LIBCPP_HIDE_FROM_ABI __format_arg_store<wformat_context, _Args...> make_wformat_args(_Args&&... __args) {
return _VSTD::__format_arg_store<wformat_context, _Args...>(__args...);
}
#endif
@ -563,7 +552,7 @@ vformat_to(_OutIt __out_it, wstring_view __fmt, wformat_args __args) {
template <output_iterator<const char&> _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 @@ format_to(_OutIt __out_it, __format_string_t<_Args...> __fmt, const _Args&... __
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <output_iterator<const wchar_t&> _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 @@ vformat(wstring_view __fmt, wformat_args __args) {
template <class... _Args>
_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 <class... _Args>
_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 @@ _LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> __vformat_to_n(_OutIt __out_it,
template <output_iterator<const char&> _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<format_context>(_VSTD::move(__out_it), __n, __fmt.__str_, _VSTD::make_format_args(__args...));
}
@ -627,7 +616,7 @@ format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, __format_string_t<_A
template <output_iterator<const wchar_t&> _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<wformat_context>(_VSTD::move(__out_it), __n, __fmt.__str_, _VSTD::make_wformat_args(__args...));
}
#endif
@ -642,14 +631,14 @@ _LIBCPP_HIDE_FROM_ABI size_t __vformatted_size(basic_string_view<_CharT> __fmt,
template <class... _Args>
_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 <class... _Args>
_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 @@ _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt v
template <output_iterator<const char&> _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 @@ format_to(_OutIt __out_it, locale __loc, __format_string_t<_Args...> __fmt, cons
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <output_iterator<const wchar_t&> _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 @@ vformat(locale __loc, wstring_view __fmt, wformat_args __args) {
template <class... _Args>
_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 @@ _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string f
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <class... _Args>
_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 @@ _LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> __vformat_to_n(_OutIt __out_it,
template <output_iterator<const char&> _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<format_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.__str_,
_VSTD::make_format_args(__args...));
}
@ -766,7 +755,7 @@ format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, __form
template <output_iterator<const wchar_t&> _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<wformat_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.__str_,
_VSTD::make_wformat_args(__args...));
}
@ -783,14 +772,14 @@ _LIBCPP_HIDE_FROM_ABI size_t __vformatted_size(locale __loc, basic_string_view<_
template <class... _Args>
_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 <class... _Args>
_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

View File

@ -22,9 +22,10 @@
#include "test_macros.h"
int main(int, char**) {
using Context [[maybe_unused]] = std::basic_format_context< std::back_insert_iterator<std::basic_string<char>>, 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<decltype(store), std::__format_arg_store<std::format_context, int, nullptr_t, bool, double>>);
return 0;
}

View File

@ -22,10 +22,10 @@
#include "test_macros.h"
int main(int, char**) {
using Context [[maybe_unused]] =
std::basic_format_context<std::back_insert_iterator<std::basic_string<wchar_t>>, 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<decltype(store), std::__format_arg_store<std::wformat_context, int, nullptr_t, bool, double>>);
return 0;
}

View File

@ -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"
// <format>
#include <format>
#include <cassert>
#include "MoveOnly.h"
#include "make_string.h"
#include "test_macros.h"
#ifndef TEST_HAS_NO_LOCALIZATION
# include <locale>
#endif
#define SV(S) MAKE_STRING_VIEW(CharT, S)
template <class CharT>
struct std::formatter<MoveOnly, CharT> : std::formatter<int, CharT> {
// 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<int, CharT>::format(m.get(), ctx);
}
};
template <class CharT>
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<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif
return 0;
}

View File

@ -88,3 +88,17 @@ void f() {
// 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
}

View File

@ -2518,6 +2518,11 @@ void format_test_handle(TestFunction check, ExceptionTest check_exception) {
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<CharT>("xXs"))
check_exception("The format-spec type has a type not supported for a status argument", fmt, status::foo);