From: Ivan Lazaric <[email protected]> This patch implements constexpr formatting from P3391R2, and introduces the constexpr_format feature-test macro. Since pre-cxx11 cow_string is not constexpr-enabled, only exposing the feature-test macro under cxx11 ABI.
Add `__format::__toupper_numeric` function that work in constexpr. It is not fully general, but `<format>` doesn't need all uppercasing. The `__write_escaped_ascii` is not marked constexpr, to enable futhure improvement for non-unicode literal encodings (for example using `iconv`). Update `__check_dynamic_spec` to check if `__args` is run, to accomodate for dynamic_format or vformat invoked at compile time. Mark `basic_format_string` consteval constructor noexcept, since exceptions are now constexpr, and the standard mandates failure to parse to be non-catchable ill-formed. https://eel.is/c++draft/format#fmt.string-3 > Remarks: A call to this function is not a core constant expression > ([expr.const]) unless there exist args of types Args such that > str is a format string for args. Update some formatting tests to test constexpr as well, and introduce a dedicated smoke test for constexpr formatting. libstdc++-v3/include/ChangeLog: * bits/version.def (constexpr_exceptions): Move before format. (constexpr_format): Define. * bits/version.h: Regenerate. * std/format (_GLIBCXX_CONSTEXPR_FORMAT): Define and apply to all of functions except __write_escaped_ascii and __formatter_fp members. (_GLIBCXX_CONSTEXPR_FORMAT_ERROR): Replace with _GLIBCXX_CONSTEXPR_FORMAT. (basic_format_string::basic_format_string(const _Tp&)): Mark as noexcept. (__format::__toupper_numeric): Define. (__formatter_int::format): Use __format::__toupper_numeric and replace __builtin_memcpy with ranges::copy. (__formatter_fp::format): Use __format::__toupper_numeric. (__formatter_ptr::format): Avoid reinterpret_cast for null-pointer value to support std::nullptr_t formatting. (__format::__do_vformat_to): Replace __builtin_memcpy with ranges::copy. (basic_format_parse_context::__check_dynamic_spec): Skip checks if __args pointer is null. libstdc++-v3/testsuite/ChangeLog: * std/format/arguments/args_neg.cc: Diagnostics change. * std/format/constexpr.cc: New test. * std/format/debug.cc: Test at compile-time in C++26 and update iteration limit for paddding tests. * std/format/tuple.cc: Likewise. * std/format/functions/format.cc: Test at compile-time in C++26. * std/format/functions/format_to.cc: Likewise. * std/format/functions/size.cc: Likewise. * std/format/ranges/format_kind.cc: Likewise. * std/format/ranges/formatter.cc: Likewise. * std/format/ranges/sequence.cc: Likewise. * std/format/runtime_format.cc: Likewise. * std/format/string.cc: Likewise. * std/time/format/data_not_present_neg.cc: Diagnostics change. Co-authored-by: Tomasz Kamiński <[email protected]> Signed-off-by: Ivan Lazaric <[email protected]> --- v2 changes: - updates changelog - remove constexpr support for __write_escaped_ascii, they will be follow up patch enabling it for common case - removes workaroung for _Ptr_sink in vformat_to_n, after Jakub's make it constexpr in PR124347, clang support it in C++|23 or later. - constexpr_format now depend on constexpr_exceptions, (they were moved beforein file), and make __cpp_lib_contexp_format defined by <format> - replaced _GLIBCXX_CONSTEXPR_FORMAT_ERROR with simply _GLIBCXX_CONSTEXPR_FORMAT - increase constexpr iterations limit for also for tuple.cc, so transcoding finishes - removed ctype include was we use to_upper_numeric. Tested on x86_64-linux. OK for trunk? libstdc++-v3/include/bits/version.def | 32 +- libstdc++-v3/include/bits/version.h | 30 +- libstdc++-v3/include/std/format | 394 +++++++++++------- .../std/format/arguments/args_neg.cc | 4 +- .../testsuite/std/format/constexpr.cc | 166 ++++++++ libstdc++-v3/testsuite/std/format/debug.cc | 83 ++-- .../testsuite/std/format/dynamic_format.cc | 25 +- .../testsuite/std/format/functions/format.cc | 281 +++++++------ .../std/format/functions/format_to.cc | 58 ++- .../testsuite/std/format/functions/size.cc | 23 +- .../std/format/ranges/format_kind.cc | 15 +- .../testsuite/std/format/ranges/formatter.cc | 37 +- .../testsuite/std/format/ranges/sequence.cc | 64 ++- libstdc++-v3/testsuite/std/format/string.cc | 59 ++- libstdc++-v3/testsuite/std/format/tuple.cc | 72 +++- .../std/time/format/data_not_present_neg.cc | 3 +- 16 files changed, 932 insertions(+), 414 deletions(-) create mode 100644 libstdc++-v3/testsuite/std/format/constexpr.cc diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 6d789987642..a6adaef42a0 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1328,6 +1328,16 @@ ftms = { }; }; +ftms = { + name = constexpr_exceptions; + values = { + v = 202502; + cxxmin = 26; + extra_cond = "__cpp_constexpr_exceptions >= 202411L"; + cxx11abi = yes; + }; +}; + ftms = { name = format; // 202305 P2757R3 Type checking format args @@ -1351,6 +1361,18 @@ ftms = { }; }; +ftms = { + name = constexpr_format; + // 202511 P3391R2 constexpr std::format + values = { + v = 202511; + cxxmin = 26; + hosted = yes; + extra_cond = "__cpp_lib_constexpr_exceptions >= 202502L"; + cxx11abi = yes; + }; +}; + ftms = { name = format_uchar; values = { @@ -2325,16 +2347,6 @@ ftms = { }; }; -ftms = { - name = constexpr_exceptions; - values = { - v = 202502; - cxxmin = 26; - extra_cond = "__cpp_constexpr_exceptions >= 202411L"; - cxx11abi = yes; - }; -}; - ftms = { name = philox_engine; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index c44fe07ec60..7c24bcc5f44 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1476,6 +1476,16 @@ #endif /* !defined(__cpp_lib_barrier) */ #undef __glibcxx_want_barrier +#if !defined(__cpp_lib_constexpr_exceptions) +# if (__cplusplus > 202302L) && _GLIBCXX_USE_CXX11_ABI && (__cpp_constexpr_exceptions >= 202411L) +# define __glibcxx_constexpr_exceptions 202502L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_constexpr_exceptions) +# define __cpp_lib_constexpr_exceptions 202502L +# endif +# endif +#endif /* !defined(__cpp_lib_constexpr_exceptions) */ +#undef __glibcxx_want_constexpr_exceptions + #if !defined(__cpp_lib_format) # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED # define __glibcxx_format 202603L @@ -1491,6 +1501,16 @@ #endif /* !defined(__cpp_lib_format) */ #undef __glibcxx_want_format +#if !defined(__cpp_lib_constexpr_format) +# if (__cplusplus > 202302L) && _GLIBCXX_USE_CXX11_ABI && _GLIBCXX_HOSTED && (__cpp_lib_constexpr_exceptions >= 202502L) +# define __glibcxx_constexpr_format 202511L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_constexpr_format) +# define __cpp_lib_constexpr_format 202511L +# endif +# endif +#endif /* !defined(__cpp_lib_constexpr_format) */ +#undef __glibcxx_want_constexpr_format + #if !defined(__cpp_lib_format_uchar) # if (__cplusplus >= 202002L) && _GLIBCXX_HOSTED # define __glibcxx_format_uchar 202311L @@ -2586,16 +2606,6 @@ #endif /* !defined(__cpp_lib_bitset) */ #undef __glibcxx_want_bitset -#if !defined(__cpp_lib_constexpr_exceptions) -# if (__cplusplus > 202302L) && _GLIBCXX_USE_CXX11_ABI && (__cpp_constexpr_exceptions >= 202411L) -# define __glibcxx_constexpr_exceptions 202502L -# if defined(__glibcxx_want_all) || defined(__glibcxx_want_constexpr_exceptions) -# define __cpp_lib_constexpr_exceptions 202502L -# endif -# endif -#endif /* !defined(__cpp_lib_constexpr_exceptions) */ -#undef __glibcxx_want_constexpr_exceptions - #if !defined(__cpp_lib_philox_engine) # if (__cplusplus > 202302L) # define __glibcxx_philox_engine 202406L diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index 0f20fcc8d88..fe0f611b833 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -39,6 +39,7 @@ #define __glibcxx_want_format_ranges #define __glibcxx_want_format_uchar #define __glibcxx_want_constexpr_exceptions +#define __glibcxx_want_constexpr_format #include <bits/version.h> #ifdef __cpp_lib_format // C++ >= 20 && HOSTED @@ -63,8 +64,10 @@ #include <bits/utility.h> // tuple_size_v #include <ext/numeric_traits.h> // __int_traits -#if !__has_builtin(__builtin_toupper) -# include <cctype> +#ifdef __glibcxx_constexpr_format // C++ >= 26 && HOSTED && CXX11 strings +# define _GLIBCXX_CONSTEXPR_FORMAT constexpr +#else +# define _GLIBCXX_CONSTEXPR_FORMAT #endif #pragma GCC diagnostic push @@ -128,6 +131,7 @@ namespace __format struct _Dynamic_format_string { [[__gnu__::__always_inline__]] + _GLIBCXX_CONSTEXPR_FORMAT _Dynamic_format_string(basic_string_view<_CharT> __s) noexcept : _M_str(__s) { } @@ -170,9 +174,10 @@ namespace __format template<typename _Tp> requires convertible_to<const _Tp&, basic_string_view<_CharT>> consteval - basic_format_string(const _Tp& __s); + basic_format_string(const _Tp& __s) noexcept; [[__gnu__::__always_inline__]] + _GLIBCXX_CONSTEXPR_FORMAT basic_format_string(__format::_Dynamic_format_string<_CharT> __s) noexcept : _M_str(__s._M_str) { } @@ -197,13 +202,13 @@ namespace __format #if __cpp_lib_format >= 202603L // >= C++26 [[__gnu__::__always_inline__]] - inline __format::_Dynamic_format_string<char> + inline _GLIBCXX_CONSTEXPR_FORMAT __format::_Dynamic_format_string<char> dynamic_format(string_view __fmt) noexcept { return __fmt; } #ifdef _GLIBCXX_USE_WCHAR_T [[__gnu__::__always_inline__]] - inline __format::_Dynamic_format_string<wchar_t> + inline _GLIBCXX_CONSTEXPR_FORMAT __format::_Dynamic_format_string<wchar_t> dynamic_format(wstring_view __fmt) noexcept { return __fmt; } #endif @@ -220,56 +225,51 @@ namespace __format formatter& operator=(const formatter&) = delete; }; -#if __cpp_lib_constexpr_exceptions >= 202502L -#define _GLIBCXX_CONSTEXPR_FORMAT_ERROR constexpr -#else -#define _GLIBCXX_CONSTEXPR_FORMAT_ERROR -#endif - // [format.error], class format_error class format_error : public runtime_error { public: - _GLIBCXX_CONSTEXPR_FORMAT_ERROR explicit format_error(const string& __what) + _GLIBCXX_CONSTEXPR_FORMAT + explicit format_error(const string& __what) : runtime_error(__what) { } - _GLIBCXX_CONSTEXPR_FORMAT_ERROR explicit format_error(const char* __what) + + _GLIBCXX_CONSTEXPR_FORMAT explicit + format_error(const char* __what) : runtime_error(__what) { } }; /// @cond undocumented [[noreturn]] - inline void + inline _GLIBCXX_CONSTEXPR_FORMAT void __throw_format_error(const char* __what) { _GLIBCXX_THROW_OR_ABORT(format_error(__what)); } -#undef _GLIBCXX_CONSTEXPR_FORMAT_ERROR - namespace __format { // XXX use named functions for each constexpr error? [[noreturn]] - inline void + inline _GLIBCXX_CONSTEXPR_FORMAT void __unmatched_left_brace_in_format_string() { __throw_format_error("format error: unmatched '{' in format string"); } [[noreturn]] - inline void + inline _GLIBCXX_CONSTEXPR_FORMAT void __unmatched_right_brace_in_format_string() { __throw_format_error("format error: unmatched '}' in format string"); } [[noreturn]] - inline void + inline _GLIBCXX_CONSTEXPR_FORMAT void __conflicting_indexing_in_format_string() { __throw_format_error("format error: conflicting indexing style in format string"); } [[noreturn]] - inline void + inline _GLIBCXX_CONSTEXPR_FORMAT void __invalid_arg_id_in_format_string() { __throw_format_error("format error: invalid arg-id in format string"); } [[noreturn]] - inline void + inline _GLIBCXX_CONSTEXPR_FORMAT void __failed_to_parse_format_spec() { __throw_format_error("format error: failed to parse format-spec"); } @@ -517,7 +517,7 @@ namespace __format using enum _WidthPrec; template<typename _Context> - size_t + _GLIBCXX_CONSTEXPR_FORMAT size_t __int_from_arg(const basic_format_arg<_Context>& __arg); constexpr bool __is_digit(char __c) @@ -750,7 +750,7 @@ namespace __format } template<typename _Context> - size_t + _GLIBCXX_CONSTEXPR_FORMAT size_t _M_get_width(_Context& __ctx) const { size_t __width = 0; @@ -762,7 +762,7 @@ namespace __format } template<typename _Context> - size_t + _GLIBCXX_CONSTEXPR_FORMAT size_t _M_get_precision(_Context& __ctx) const { size_t __prec = -1; @@ -775,7 +775,7 @@ namespace __format }; template<typename _Int> - inline char* + inline _GLIBCXX_CONSTEXPR_FORMAT char* __put_sign(_Int __i, _Sign __sign, char* __dest) noexcept { if (__i < 0) @@ -792,7 +792,7 @@ namespace __format // Write STR to OUT (and do so efficiently if OUT is a _Sink_iter). template<typename _Out, typename _CharT> requires output_iterator<_Out, const _CharT&> - inline _Out + inline _GLIBCXX_CONSTEXPR_FORMAT _Out __write(_Out __out, basic_string_view<_CharT> __str) { if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>) @@ -809,7 +809,7 @@ namespace __format // Write STR to OUT with NFILL copies of FILL_CHAR specified by ALIGN. // pre: __align != _Align_default template<typename _Out, typename _CharT> - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out __write_padded(_Out __out, basic_string_view<_CharT> __str, _Align __align, size_t __nfill, char32_t __fill_char) { @@ -883,7 +883,7 @@ namespace __format // Write STR to OUT, with alignment and padding as determined by SPEC. // pre: __spec._M_align != _Align_default || __align != _Align_default template<typename _CharT, typename _Out> - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out __write_padded_as_spec(basic_string_view<type_identity_t<_CharT>> __str, size_t __estimated_width, basic_format_context<_Out, _CharT>& __fc, @@ -905,7 +905,7 @@ namespace __format } template<typename _CharT> - size_t + _GLIBCXX_CONSTEXPR_FORMAT size_t __truncate(basic_string_view<_CharT>& __s, size_t __prec) { if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>()) @@ -1049,7 +1049,7 @@ namespace __format using uint_least32_t = __UINT_LEAST32_TYPE__; template<typename _Out, typename _CharT> - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out __write_escape_seq(_Out __out, uint_least32_t __val, basic_string_view<_CharT> __prefix) { @@ -1079,7 +1079,7 @@ namespace __format } template<typename _Out, typename _CharT> - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out __write_escape_seqs(_Out __out, basic_string_view<_CharT> __units) { using _UChar = make_unsigned_t<_CharT>; @@ -1090,7 +1090,7 @@ namespace __format } template<typename _Out, typename _CharT> - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out __write_escaped_char(_Out __out, _CharT __c) { using _UChar = make_unsigned_t<_CharT>; @@ -1146,7 +1146,7 @@ namespace __format } template<typename _CharT, typename _Out> - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out __write_escaped_unicode_part(_Out __out, basic_string_view<_CharT>& __str, bool& __prev_esc, _Term_char __term) { @@ -1224,7 +1224,7 @@ namespace __format } template<typename _CharT, typename _Out> - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out __write_escaped_unicode(_Out __out, basic_string_view<_CharT> __str, _Term_char __term) { @@ -1236,7 +1236,7 @@ namespace __format } template<typename _CharT, typename _Out> - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out __write_escaped(_Out __out, basic_string_view<_CharT> __str, _Term_char __term) { __out = __format::__write(__out, _Escapes<_CharT>::_S_term(__term)); @@ -1257,12 +1257,14 @@ namespace __format struct _Optional_locale { [[__gnu__::__always_inline__]] + _GLIBCXX_CONSTEXPR_FORMAT _Optional_locale() : _M_dummy(), _M_hasval(false) { } _Optional_locale(const locale& __loc) noexcept : _M_loc(__loc), _M_hasval(true) { } + _GLIBCXX_CONSTEXPR_FORMAT _Optional_locale(const _Optional_locale& __l) noexcept : _M_dummy(), _M_hasval(__l._M_hasval) { @@ -1270,6 +1272,7 @@ namespace __format std::construct_at(&_M_loc, __l._M_loc); } + _GLIBCXX_CONSTEXPR_FORMAT _Optional_locale& operator=(const _Optional_locale& __l) noexcept { @@ -1291,6 +1294,7 @@ namespace __format return *this; } + _GLIBCXX_CONSTEXPR_FORMAT ~_Optional_locale() { if (_M_hasval) _M_loc.~locale(); } _Optional_locale& @@ -1317,7 +1321,8 @@ namespace __format return _M_loc; } - bool has_value() const noexcept { return _M_hasval; } + _GLIBCXX_CONSTEXPR_FORMAT bool + has_value() const noexcept { return _M_hasval; } union { char _M_dummy = '\0'; @@ -1391,7 +1396,7 @@ namespace __format } template<typename _Out> - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out format(basic_string_view<_CharT> __s, basic_format_context<_Out, _CharT>& __fc) const { @@ -1408,7 +1413,7 @@ namespace __format } template<typename _Out> - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out _M_format_escaped(basic_string_view<_CharT> __s, basic_format_context<_Out, _CharT>& __fc) const { @@ -1433,7 +1438,7 @@ namespace __format #if __glibcxx_format_ranges // C++ >= 23 && HOSTED template<ranges::input_range _Rg, typename _Out> requires same_as<remove_cvref_t<ranges::range_reference_t<_Rg>>, _CharT> - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out _M_format_range(_Rg&& __rg, basic_format_context<_Out, _CharT>& __fc) const { using _Range = remove_reference_t<_Rg>; @@ -1482,6 +1487,28 @@ namespace __format _Spec<_CharT> _M_spec{}; }; + // A partial implementation of std::toupper that is constexpr-enabled, + // sufficient for formatting purposes. + [[__gnu__::__always_inline__]] + constexpr char + __toupper_numeric(char __c) + { + switch (__c) + { + case 'a': return 'A'; + case 'b': return 'B'; + case 'c': return 'C'; + case 'd': return 'D'; + case 'e': return 'E'; + case 'f': return 'F'; + case 'i': return 'I'; + case 'n': return 'N'; + case 'p': return 'P'; + case 'x': return 'X'; + default: return __c; + } + } + template<__char _CharT> struct __formatter_int { @@ -1641,6 +1668,7 @@ namespace __format } template<typename _Int, typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(_Int __i, basic_format_context<_Out, _CharT>& __fc) const { @@ -1690,11 +1718,7 @@ namespace __format __res = to_chars(__start, __end, __u, 16); if (_M_spec._M_type == _Pres_X) for (auto __p = __start; __p != __res.ptr; ++__p) -#if __has_builtin(__builtin_toupper) - *__p = __builtin_toupper(*__p); -#else - *__p = std::toupper(*__p); -#endif + *__p = __format::__toupper_numeric(*__p); break; default: __builtin_unreachable(); @@ -1703,8 +1727,7 @@ namespace __format if (_M_spec._M_alt && __base_prefix.size()) { __start -= __base_prefix.size(); - __builtin_memcpy(__start, __base_prefix.data(), - __base_prefix.size()); + ranges::copy(__base_prefix, __start); } __start = __format::__put_sign(__i, _M_spec._M_sign, __start - 1); @@ -1728,6 +1751,7 @@ namespace __format } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(bool __i, basic_format_context<_Out, _CharT>& __fc) const { @@ -1758,6 +1782,7 @@ namespace __format } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator _M_format_character(_CharT __c, basic_format_context<_Out, _CharT>& __fc) const @@ -1789,7 +1814,7 @@ namespace __format } template<typename _Int> - static _CharT + static _GLIBCXX_CONSTEXPR_FORMAT _CharT _S_to_character(_Int __i) { using _Traits = __gnu_cxx::__int_traits<_CharT>; @@ -1810,6 +1835,7 @@ namespace __format } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator _M_format_int(basic_string_view<_CharT> __str, size_t __prefix_len, basic_format_context<_Out, _CharT>& __fc) const @@ -2199,7 +2225,7 @@ namespace __format if (__upper) { for (char* __p = __start; __p != __res.ptr; ++__p) - *__p = std::toupper(*__p); + *__p = __format::__toupper_numeric(*__p); } bool __have_sign = true; @@ -2512,10 +2538,13 @@ namespace __format } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(const void* __v, basic_format_context<_Out, _CharT>& __fc) const { - auto __u = reinterpret_cast<__UINTPTR_TYPE__>(__v); + auto __u = __v + ? reinterpret_cast<__UINTPTR_TYPE__>(__v) + : static_cast<__UINTPTR_TYPE__>(0); return __formatter_int<_CharT>(_M_spec).format(__u, __fc); } @@ -2550,6 +2579,7 @@ namespace __format } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(_CharT __u, basic_format_context<_Out, _CharT>& __fc) const { @@ -2588,6 +2618,7 @@ namespace __format } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, wchar_t>::iterator format(char __u, basic_format_context<_Out, wchar_t>& __fc) const { @@ -2623,6 +2654,7 @@ namespace __format template<typename _Out> [[__gnu__::__nonnull__]] + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(_CharT* __u, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format(__u, __fc); } @@ -2652,6 +2684,7 @@ namespace __format template<typename _Out> [[__gnu__::__nonnull__]] + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(const _CharT* __u, basic_format_context<_Out, _CharT>& __fc) const @@ -2682,6 +2715,7 @@ namespace __format { return _M_f.parse(__pc); } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(const _CharT (&__u)[_Nm], basic_format_context<_Out, _CharT>& __fc) const @@ -2711,6 +2745,7 @@ namespace __format { return _M_f.parse(__pc); } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, char>::iterator format(const basic_string<char, _Traits, _Alloc>& __u, basic_format_context<_Out, char>& __fc) const @@ -2743,6 +2778,7 @@ namespace __format { return _M_f.parse(__pc); } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, wchar_t>::iterator format(const basic_string<wchar_t, _Traits, _Alloc>& __u, basic_format_context<_Out, wchar_t>& __fc) const @@ -2776,6 +2812,7 @@ namespace __format { return _M_f.parse(__pc); } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, char>::iterator format(basic_string_view<char, _Traits> __u, basic_format_context<_Out, char>& __fc) const @@ -2808,6 +2845,7 @@ namespace __format { return _M_f.parse(__pc); } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, wchar_t>::iterator format(basic_string_view<wchar_t, _Traits> __u, basic_format_context<_Out, wchar_t>& __fc) const @@ -2871,6 +2909,7 @@ namespace __format } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(_Tp __u, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format(__u, __fc); } @@ -3082,6 +3121,7 @@ namespace __format { return _M_f.parse(__pc); } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(const void* __v, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format(__v, __fc); } @@ -3107,6 +3147,7 @@ namespace __format { return _M_f.parse(__pc); } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(void* __v, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format(__v, __fc); } @@ -3132,6 +3173,7 @@ namespace __format { return _M_f.parse(__pc); } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(nullptr_t, basic_format_context<_Out, _CharT>& __fc) const { return _M_f.format(nullptr, __fc); } @@ -3276,11 +3318,11 @@ namespace __format constexpr _Sink_iter operator++(int) { return *this; } - auto + _GLIBCXX_CONSTEXPR_FORMAT auto _M_reserve(size_t __n) const { return _M_sink->_M_reserve(__n); } - bool + _GLIBCXX_CONSTEXPR_FORMAT bool _M_discarding() const { return _M_sink->_M_discarding(); } }; @@ -3312,7 +3354,7 @@ namespace __format // The portion of the span that has been written to. [[__gnu__::__always_inline__]] - span<_CharT> + _GLIBCXX_CONSTEXPR_FORMAT span<_CharT> _M_used() const noexcept { return _M_span.first(_M_next - _M_span.begin()); } @@ -3329,7 +3371,7 @@ namespace __format { _M_next = _M_span.begin(); } // Replace the current output range. - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_reset(span<_CharT> __s, size_t __pos = 0) noexcept { _M_span = __s; @@ -3369,11 +3411,14 @@ namespace __format struct _Reservation { // True if the reservation was successful, false otherwise. + _GLIBCXX_CONSTEXPR_FORMAT explicit operator bool() const noexcept { return _M_sink; } // A pointer to write directly to the sink. - _CharT* get() const noexcept { return _M_sink->_M_next.operator->(); } + _GLIBCXX_CONSTEXPR_FORMAT _CharT* + get() const noexcept { return _M_sink->_M_next.operator->(); } // Add n to the _M_next iterator for the sink. - void _M_bump(size_t __n) { _M_sink->_M_bump(__n); } + _GLIBCXX_CONSTEXPR_FORMAT void + _M_bump(size_t __n) { _M_sink->_M_bump(__n); } _Sink* _M_sink; }; @@ -3381,7 +3426,7 @@ namespace __format // If anything is written to the reservation then there must be a call // to _M_bump(N2) before any call to another member function of *this, // where N2 is the number of characters written. - virtual _Reservation + _GLIBCXX_CONSTEXPR_FORMAT virtual _Reservation _M_reserve(size_t __n) { if (__n <= _M_unused().size()) @@ -3398,12 +3443,12 @@ namespace __format // Update the next output position after writing directly to the sink. // pre: no calls to _M_write or _M_overflow since _M_reserve. - virtual void + _GLIBCXX_CONSTEXPR_FORMAT virtual void _M_bump(size_t __n) { _M_next += __n; } // Returns true if the _Sink is discarding incoming characters. - virtual bool + _GLIBCXX_CONSTEXPR_FORMAT virtual bool _M_discarding() const { return false; } @@ -3421,7 +3466,7 @@ namespace __format template<typename _CharT> class _Fixedbuf_sink final : public _Sink<_CharT> { - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_overflow() override { __glibcxx_assert(false); @@ -3469,7 +3514,7 @@ namespace __format _Seq _M_seq; protected: // Transfer buffer contents to the sequence, so buffer can be refilled. - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_overflow() override { auto __s = this->_M_used(); @@ -3488,7 +3533,7 @@ namespace __format this->_M_rewind(); } - typename _Sink<_CharT>::_Reservation + _GLIBCXX_CONSTEXPR_FORMAT typename _Sink<_CharT>::_Reservation _M_reserve(size_t __n) override { // We might already have n characters available in this->_M_unused(), @@ -3524,7 +3569,7 @@ namespace __format return _Sink<_CharT>::_M_reserve(__n); } - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_bump(size_t __n) override { if constexpr (__is_specialization_of<_Seq, basic_string> @@ -3539,7 +3584,8 @@ namespace __format } } - void _M_trim(span<const _CharT> __s) + _GLIBCXX_CONSTEXPR_FORMAT void + _M_trim(span<const _CharT> __s) requires __is_specialization_of<_Seq, basic_string> { _GLIBCXX_DEBUG_ASSERT(__s.data() == this->_M_buf @@ -3555,16 +3601,18 @@ namespace __format // to _M_buf if it overflows? Or even do that for all unused capacity? [[__gnu__::__always_inline__]] + _GLIBCXX_CONSTEXPR_FORMAT _Seq_sink() noexcept(is_nothrow_default_constructible_v<_Seq>) { } + _GLIBCXX_CONSTEXPR_FORMAT _Seq_sink(_Seq&& __s) noexcept(is_nothrow_move_constructible_v<_Seq>) : _M_seq(std::move(__s)) { } using _Sink<_CharT>::out; - _Seq + _GLIBCXX_CONSTEXPR_FORMAT _Seq get() && { if (this->_M_used().size() != 0) @@ -3574,7 +3622,7 @@ namespace __format // A writable span that views everything written to the sink. // Will be either a view over _M_seq or the used part of _M_buf. - span<_CharT> + _GLIBCXX_CONSTEXPR_FORMAT span<_CharT> _M_span() { auto __s = this->_M_used(); @@ -3587,7 +3635,7 @@ namespace __format return __s; } - basic_string_view<_CharT> + _GLIBCXX_CONSTEXPR_FORMAT basic_string_view<_CharT> view() { auto __span = _M_span(); @@ -3612,7 +3660,7 @@ namespace __format protected: size_t _M_count = 0; - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_overflow() override { auto __s = this->_M_used(); @@ -3632,7 +3680,7 @@ namespace __format _M_count += __s.size(); } - bool + _GLIBCXX_CONSTEXPR_FORMAT bool _M_discarding() const override { // format_to_n return total number of characters, that would be written, @@ -3642,14 +3690,14 @@ namespace __format public: [[__gnu__::__always_inline__]] - explicit + _GLIBCXX_CONSTEXPR_FORMAT explicit _Iter_sink(_OutIter __out, iter_difference_t<_OutIter> __max = -1) : _M_out(std::move(__out)), _M_max(__max) { } using _Sink<_CharT>::out; - format_to_n_result<_OutIter> + _GLIBCXX_CONSTEXPR_FORMAT format_to_n_result<_OutIter> _M_finish() && { if (this->_M_used().size() != 0) @@ -3678,7 +3726,7 @@ namespace __format _CharT _M_buf[64]; // Write here after outputting _M_max characters. protected: - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_overflow() override { if (this->_M_unused().size() != 0) @@ -3702,7 +3750,7 @@ namespace __format } } - bool + _GLIBCXX_CONSTEXPR_FORMAT bool _M_discarding() const override { // format_to_n return total number of characters, that would be written, @@ -3710,7 +3758,7 @@ namespace __format return false; } - typename _Sink<_CharT>::_Reservation + _GLIBCXX_CONSTEXPR_FORMAT typename _Sink<_CharT>::_Reservation _M_reserve(size_t __n) final { auto __avail = this->_M_unused(); @@ -3727,7 +3775,7 @@ namespace __format private: template<typename _IterDifference> - static size_t + static _GLIBCXX_CONSTEXPR_FORMAT size_t _S_trim_max(_IterDifference __max) { if (__max < 0) @@ -3740,7 +3788,7 @@ namespace __format } [[__gnu__::__always_inline__]] - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_rebuf(_CharT* __ptr, size_t __total, size_t __inuse = 0) { std::span<_CharT> __span(__ptr, __total); @@ -3748,7 +3796,7 @@ namespace __format } public: - explicit + _GLIBCXX_CONSTEXPR_FORMAT explicit _Ptr_sink(_CharT* __ptr, size_t __n = _S_no_limit) noexcept : _Sink<_CharT>(_M_buf), _M_max(__n) { @@ -3773,13 +3821,13 @@ namespace __format } template<contiguous_iterator _OutIter> - explicit + _GLIBCXX_CONSTEXPR_FORMAT explicit _Ptr_sink(_OutIter __out, iter_difference_t<_OutIter> __n = -1) : _Ptr_sink(std::to_address(__out), _S_trim_max(__n)) { } template<contiguous_iterator _OutIter> - format_to_n_result<_OutIter> + _GLIBCXX_CONSTEXPR_FORMAT format_to_n_result<_OutIter> _M_finish(_OutIter __first) const { auto __s = this->_M_used(); @@ -3821,12 +3869,12 @@ namespace __format size_t _M_printwidth; [[__gnu__::__always_inline__]] - bool + _GLIBCXX_CONSTEXPR_FORMAT bool _M_ignoring() const { return _M_printwidth >= _M_maxwidth; } [[__gnu__::__always_inline__]] - bool + _GLIBCXX_CONSTEXPR_FORMAT bool _M_buffering() const { if (_M_printwidth < _M_padwidth) @@ -3836,7 +3884,7 @@ namespace __format return false; } - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_sync_discarding() { if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>) @@ -3844,7 +3892,7 @@ namespace __format _M_maxwidth = _M_printwidth; } - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_flush() { span<_CharT> __new = this->_M_used(); @@ -3854,7 +3902,7 @@ namespace __format this->_M_rewind(); } - bool + _GLIBCXX_CONSTEXPR_FORMAT bool _M_force_update() { auto __str = this->view(); @@ -3882,7 +3930,7 @@ namespace __format return false; } - bool + _GLIBCXX_CONSTEXPR_FORMAT bool _M_update(size_t __new) { _M_printwidth += __new; @@ -3892,7 +3940,7 @@ namespace __format return true; } - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_overflow() override { // Ignore characters in buffer, and override it. @@ -3907,11 +3955,11 @@ namespace __format _Str_sink<_CharT>::_M_overflow(); } - bool + _GLIBCXX_CONSTEXPR_FORMAT bool _M_discarding() const override { return _M_ignoring(); } - typename _Sink<_CharT>::_Reservation + _GLIBCXX_CONSTEXPR_FORMAT typename _Sink<_CharT>::_Reservation _M_reserve(size_t __n) override { // Ignore characters in buffer, if any. @@ -3930,7 +3978,7 @@ namespace __format return _Sink<_CharT>::_M_reserve(__n); } - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_bump(size_t __n) override { // Ignore the written characters. @@ -3945,19 +3993,19 @@ namespace __format public: [[__gnu__::__always_inline__]] - explicit + _GLIBCXX_CONSTEXPR_FORMAT explicit _Padding_sink(_Out __out, size_t __padwidth, size_t __maxwidth) : _M_padwidth(__padwidth), _M_maxwidth(__maxwidth), _M_out(std::move(__out)), _M_printwidth(0) { _M_sync_discarding(); } [[__gnu__::__always_inline__]] - explicit + _GLIBCXX_CONSTEXPR_FORMAT explicit _Padding_sink(_Out __out, size_t __padwidth) : _Padding_sink(std::move(__out), __padwidth, (size_t)-1) { } - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out _M_finish(_Align __align, char32_t __fill_char) { // Handle any characters in the buffer. @@ -3996,14 +4044,14 @@ namespace __format unsigned _M_prev_escape : 1; unsigned _M_out_discards : 1; - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_sync_discarding() { if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>) _M_out_discards = _M_out._M_discarding(); } - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_write() { span<_CharT> __bytes = this->_M_used(); @@ -4029,7 +4077,7 @@ namespace __format _M_sync_discarding(); } - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_overflow() override { if (_M_out_discards) @@ -4038,13 +4086,13 @@ namespace __format _M_write(); } - bool + _GLIBCXX_CONSTEXPR_FORMAT bool _M_discarding() const override { return _M_out_discards; } public: [[__gnu__::__always_inline__]] - explicit + _GLIBCXX_CONSTEXPR_FORMAT explicit _Escaping_sink(_Out __out, _Term_char __term) : _M_out(std::move(__out)), _M_term(__term), _M_prev_escape(true), _M_out_discards(false) @@ -4053,7 +4101,7 @@ namespace __format _M_sync_discarding(); } - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out _M_finish() { if (_M_out_discards) @@ -4105,7 +4153,7 @@ namespace __format const _Tp, _Tp>; template<typename _Tq> - static void + static _GLIBCXX_CONSTEXPR_FORMAT void _S_format(basic_format_parse_context<_CharT>& __parse_ctx, _Context& __format_ctx, const void* __ptr) { @@ -4118,7 +4166,7 @@ namespace __format template<typename _Tp> requires (!is_same_v<remove_cv_t<_Tp>, handle>) - explicit + explicit _GLIBCXX_CONSTEXPR_FORMAT handle(_Tp& __val) noexcept : _M_ptr(__builtin_addressof(__val)) , _M_func(&_S_format<__maybe_const_t<_Tp>>) @@ -4131,7 +4179,7 @@ namespace __format handle& operator=(const handle&) = default; [[__gnu__::__always_inline__]] - void + void _GLIBCXX_CONSTEXPR_FORMAT format(basic_format_parse_context<_CharT>& __pc, _Context& __fc) const { _M_func(__pc, __fc, this->_M_ptr); } @@ -4183,10 +4231,12 @@ namespace __format }; [[__gnu__::__always_inline__]] + _GLIBCXX_CONSTEXPR_FORMAT _Arg_value() : _M_none() { } #if 0 template<typename _Tp> + _GLIBCXX_CONSTEXPR_FORMAT _Arg_value(in_place_type_t<_Tp>, _Tp __val) { _S_get<_Tp>() = __val; } #endif @@ -4195,7 +4245,7 @@ namespace __format // Value of second argument (if provided), is assigned to that member. template<typename _Tp, typename _Self, typename... _Value> [[__gnu__::__always_inline__]] - static auto& + static _GLIBCXX_CONSTEXPR_FORMAT auto& _S_access(_Self& __u, _Value... __value) noexcept { static_assert(sizeof...(_Value) <= 1); @@ -4259,23 +4309,24 @@ namespace __format else if constexpr (is_same_v<_Tp, handle>) return __u._M_handle; // Otherwise, ill-formed. + __builtin_unreachable(); } template<typename _Tp> [[__gnu__::__always_inline__]] - auto& + _GLIBCXX_CONSTEXPR_FORMAT auto& _M_get() noexcept { return _S_access<_Tp>(*this); } template<typename _Tp> [[__gnu__::__always_inline__]] - const auto& + _GLIBCXX_CONSTEXPR_FORMAT const auto& _M_get() const noexcept { return _S_access<_Tp>(*this); } template<typename _Tp> [[__gnu__::__always_inline__]] - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_set(_Tp __v) noexcept { // Explicitly construct types without trivial default constructor. @@ -4295,7 +4346,8 @@ namespace __format class _Arg_store; template<typename _Visitor, typename _Ctx> - decltype(auto) __visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>); + _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) + __visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>); template<typename _Ch, typename _Tp> consteval _Arg_t @@ -4312,20 +4364,21 @@ namespace __format using handle = __format::_Arg_value<_Context>::handle; [[__gnu__::__always_inline__]] + _GLIBCXX_CONSTEXPR_FORMAT basic_format_arg() noexcept : _M_type(__format::_Arg_none) { } [[nodiscard,__gnu__::__always_inline__]] - explicit operator bool() const noexcept + explicit _GLIBCXX_CONSTEXPR_FORMAT operator bool() const noexcept { return _M_type != __format::_Arg_none; } #if __cpp_lib_format >= 202306L // >= C++26 template<typename _Visitor> - decltype(auto) + _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) visit(this basic_format_arg __arg, _Visitor&& __vis) { return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); } template<typename _Res, typename _Visitor> - _Res + _GLIBCXX_CONSTEXPR_FORMAT _Res visit(this basic_format_arg __arg, _Visitor&& __vis) { return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); } #endif @@ -4502,7 +4555,7 @@ namespace __format } template<typename _Tp> - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_set(_Tp __v) noexcept { _M_type = _S_to_enum<_Tp>(); @@ -4511,7 +4564,7 @@ namespace __format template<typename _Tp> requires __format::__formattable_with<_Tp, _Context> - explicit + _GLIBCXX_CONSTEXPR_FORMAT explicit basic_format_arg(_Tp& __v) noexcept { using _Td = _Normalize<_Tp>; @@ -4525,15 +4578,15 @@ namespace __format } template<typename _Ctx, typename... _Argz> - friend auto + friend _GLIBCXX_CONSTEXPR_FORMAT auto make_format_args(_Argz&...) noexcept; template<typename _Visitor, typename _Ctx> - friend decltype(auto) + friend _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx>); template<typename _Visitor, typename _Ctx> - friend decltype(auto) + friend _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) __format::__visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>); template<typename _Ch, typename _Tp> @@ -4541,7 +4594,7 @@ namespace __format __format::__to_arg_t_enum() noexcept; template<typename _Visitor> - decltype(auto) + _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) _M_visit(_Visitor&& __vis, __format::_Arg_t __type) { using namespace __format; @@ -4616,7 +4669,7 @@ namespace __format } template<typename _Visitor> - decltype(auto) + _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) _M_visit_user(_Visitor&& __vis, __format::_Arg_t __type) { return _M_visit([&__vis]<typename _Tp>(_Tp& __val) -> decltype(auto) @@ -4640,7 +4693,7 @@ namespace __format template<typename _Visitor, typename _Context> _GLIBCXX26_DEPRECATED_SUGGEST("std::basic_format_arg::visit") - inline decltype(auto) + inline _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); @@ -4650,7 +4703,7 @@ namespace __format namespace __format { template<typename _Visitor, typename _Ctx> - inline decltype(auto) + inline _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx> __arg) { return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); @@ -4659,7 +4712,7 @@ namespace __format struct _WidthPrecVisitor { template<typename _Tp> - size_t + _GLIBCXX_CONSTEXPR_FORMAT size_t operator()(_Tp& __arg) const { if constexpr (is_same_v<_Tp, monostate>) @@ -4685,7 +4738,7 @@ namespace __format #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" template<typename _Context> - inline size_t + inline _GLIBCXX_CONSTEXPR_FORMAT size_t __int_from_arg(const basic_format_arg<_Context>& __arg) { return __format::__visit_format_arg(_WidthPrecVisitor(), __arg); } @@ -4733,11 +4786,11 @@ namespace __format const _Format_arg* _M_args; // Active when _M_packed_size == 0 }; - size_t + _GLIBCXX_CONSTEXPR_FORMAT size_t _M_size() const noexcept { return _M_packed_size ? _M_packed_size : _M_unpacked_size; } - typename __format::_Arg_t + _GLIBCXX_CONSTEXPR_FORMAT typename __format::_Arg_t _M_type(size_t __i) const noexcept { uint64_t __t = _M_unpacked_size >> (__i * _S_packed_type_bits); @@ -4745,7 +4798,7 @@ namespace __format } template<typename _Ctx, typename... _Args> - friend auto + friend _GLIBCXX_CONSTEXPR_FORMAT auto make_format_args(_Args&...) noexcept; // An array of _Arg_t enums corresponding to _Args... @@ -4756,10 +4809,11 @@ namespace __format public: template<typename... _Args> + _GLIBCXX_CONSTEXPR_FORMAT basic_format_args(const _Store<_Args...>& __store) noexcept; [[nodiscard,__gnu__::__always_inline__]] - basic_format_arg<_Context> + _GLIBCXX_CONSTEXPR_FORMAT basic_format_arg<_Context> get(size_t __i) const noexcept { basic_format_arg<_Context> __arg; @@ -4781,7 +4835,7 @@ namespace __format -> basic_format_args<_Context>; template<typename _Context, typename... _Args> - auto + _GLIBCXX_CONSTEXPR_FORMAT auto make_format_args(_Args&... __fmt_args) noexcept; // An array of type-erased formatting arguments. @@ -4791,7 +4845,7 @@ namespace __format friend std::basic_format_args<_Context>; template<typename _Ctx, typename... _Argz> - friend auto std:: + friend _GLIBCXX_CONSTEXPR_FORMAT auto std:: #if _GLIBCXX_INLINE_VERSION __8:: // Needed for PR c++/59256 #endif @@ -4810,7 +4864,7 @@ namespace __format _Element_t _M_args[sizeof...(_Args)]; template<typename _Tp> - static _Element_t + static _GLIBCXX_CONSTEXPR_FORMAT _Element_t _S_make_elt(_Tp& __v) { using _Tq = remove_const_t<_Tp>; @@ -4836,6 +4890,7 @@ namespace __format template<typename... _Tp> requires (sizeof...(_Tp) == sizeof...(_Args)) [[__gnu__::__always_inline__]] + _GLIBCXX_CONSTEXPR_FORMAT _Arg_store(_Tp&... __a) noexcept : _M_args{_S_make_elt(__a)...} { } @@ -4847,7 +4902,7 @@ namespace __format template<typename _Context> template<typename... _Args> - inline + inline _GLIBCXX_CONSTEXPR_FORMAT basic_format_args<_Context>:: basic_format_args(const _Store<_Args...>& __store) noexcept { @@ -4881,7 +4936,7 @@ namespace __format /// Capture formatting arguments for use by `std::vformat`. template<typename _Context = format_context, typename... _Args> [[nodiscard,__gnu__::__always_inline__]] - inline auto + inline _GLIBCXX_CONSTEXPR_FORMAT auto make_format_args(_Args&... __fmt_args) noexcept { using _Fmt_arg = basic_format_arg<_Context>; @@ -4894,7 +4949,7 @@ namespace __format /// Capture formatting arguments for use by `std::vformat` (for wide output). template<typename... _Args> [[nodiscard,__gnu__::__always_inline__]] - inline auto + inline _GLIBCXX_CONSTEXPR_FORMAT auto make_wformat_args(_Args&... __args) noexcept { return std::make_format_args<wformat_context>(__args...); } #endif @@ -4903,7 +4958,7 @@ namespace __format namespace __format { template<typename _Out, typename _CharT, typename _Context> - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out __do_vformat_to(_Out, basic_string_view<_CharT>, const basic_format_args<_Context>&, const locale* = nullptr); @@ -4935,11 +4990,13 @@ namespace __format _Out _M_out; __format::_Optional_locale _M_loc; + _GLIBCXX_CONSTEXPR_FORMAT basic_format_context(basic_format_args<basic_format_context> __args, _Out __out) : _M_args(__args), _M_out(std::move(__out)) { } + _GLIBCXX_CONSTEXPR_FORMAT basic_format_context(basic_format_args<basic_format_context> __args, _Out __out, const std::locale& __loc) : _M_args(__args), _M_out(std::move(__out)), _M_loc(__loc) @@ -4952,7 +5009,7 @@ namespace __format basic_format_context& operator=(const basic_format_context&) = delete; template<typename _Out2, typename _CharT2, typename _Context2> - friend _Out2 + friend _GLIBCXX_CONSTEXPR_FORMAT _Out2 __format::__do_vformat_to(_Out2, basic_string_view<_CharT2>, const basic_format_args<_Context2>&, const locale*); @@ -4968,7 +5025,7 @@ namespace __format using formatter_type = formatter<_Tp, _CharT>; [[nodiscard]] - basic_format_arg<basic_format_context> + _GLIBCXX_CONSTEXPR_FORMAT basic_format_arg<basic_format_context> arg(size_t __id) const noexcept { return _M_args.get(__id); } @@ -4976,9 +5033,11 @@ namespace __format std::locale locale() { return _M_loc.value(); } [[nodiscard]] - iterator out() { return std::move(_M_out); } + _GLIBCXX_CONSTEXPR_FORMAT iterator + out() { return std::move(_M_out); } - void advance_to(iterator __it) { _M_out = std::move(__it); } + _GLIBCXX_CONSTEXPR_FORMAT void + advance_to(iterator __it) { _M_out = std::move(__it); } }; @@ -5117,6 +5176,7 @@ namespace __format class _Formatting_scanner : public _Scanner<_CharT> { public: + _GLIBCXX_CONSTEXPR_FORMAT _Formatting_scanner(basic_format_context<_Out, _CharT>& __fc, basic_string_view<_CharT> __str) : _Scanner<_CharT>(__str), _M_fc(__fc) @@ -5224,7 +5284,7 @@ namespace __format }; template<typename _CharT, unsigned = __unicode::__literal_encoding_is_unicode<_CharT>()> - _Sink_iter<_CharT> + _GLIBCXX_CONSTEXPR_FORMAT _Sink_iter<_CharT> __do_vformat_to(_Sink_iter<_CharT> __out, basic_string_view<_CharT> __fmt, __format_context<_CharT>& __ctx) { @@ -5241,7 +5301,7 @@ namespace __format const char* __chars[] = { "false", "true" }; if (auto __res = __out._M_reserve(__len)) { - __builtin_memcpy(__res.get(), __chars[__arg], __len); + ranges::copy_n(__chars[__arg], __len, __res.get()); __res._M_bump(__len); __done = true; } @@ -5279,7 +5339,7 @@ namespace __format string_view __sv = __arg; if (auto __res = __out._M_reserve(__sv.size())) { - __builtin_memcpy(__res.get(), __sv.data(), __sv.size()); + ranges::copy(__sv, __res.get()); __res._M_bump(__sv.size()); __done = true; } @@ -5312,7 +5372,7 @@ namespace __format #endif template<typename _Out, typename _CharT, typename _Context> - inline _Out + inline _GLIBCXX_CONSTEXPR_FORMAT _Out __do_vformat_to(_Out __out, basic_string_view<_CharT> __fmt, const basic_format_args<_Context>& __args, const locale* __loc) @@ -5339,7 +5399,7 @@ namespace __format } template<typename _Out, typename _CharT> - inline format_to_n_result<_Out> + inline _GLIBCXX_CONSTEXPR_FORMAT format_to_n_result<_Out> __do_vformat_to_n(_Out __out, iter_difference_t<_Out> __n, basic_string_view<_CharT> __fmt, const type_identity_t< @@ -5379,7 +5439,12 @@ namespace __format if constexpr (sizeof...(_Ts) != 0) { using _Parse_ctx = __format::_Scanner<_CharT>::_Parse_context; - auto __arg = static_cast<_Parse_ctx*>(this)->_M_types[__id]; + auto* __args = static_cast<_Parse_ctx*>(this)->_M_types; + // Formatting scanner, no type check. + if (!__args) + return; + + auto __arg = __args[__id]; __format::_Arg_t __types[] = { __format::__to_arg_t_enum<_CharT, _Ts>()... }; @@ -5397,7 +5462,7 @@ namespace __format requires convertible_to<const _Tp&, basic_string_view<_CharT>> consteval basic_format_string<_CharT, _Args...>:: - basic_format_string(const _Tp& __s) + basic_format_string(const _Tp& __s) noexcept : _M_str(__s) { __format::_Checking_scanner<_CharT, remove_cvref_t<_Args>...> @@ -5409,14 +5474,14 @@ namespace __format template<typename _Out> requires output_iterator<_Out, const char&> [[__gnu__::__always_inline__]] - inline _Out + inline _GLIBCXX_CONSTEXPR_FORMAT _Out vformat_to(_Out __out, string_view __fmt, format_args __args) { return __format::__do_vformat_to(std::move(__out), __fmt, __args); } #ifdef _GLIBCXX_USE_WCHAR_T template<typename _Out> requires output_iterator<_Out, const wchar_t&> [[__gnu__::__always_inline__]] - inline _Out + inline _GLIBCXX_CONSTEXPR_FORMAT _Out vformat_to(_Out __out, wstring_view __fmt, wformat_args __args) { return __format::__do_vformat_to(std::move(__out), __fmt, __args); } #endif @@ -5442,7 +5507,7 @@ namespace __format #endif [[nodiscard]] - inline string + inline _GLIBCXX_CONSTEXPR_FORMAT string vformat(string_view __fmt, format_args __args) { __format::_Str_sink<char> __buf; @@ -5452,7 +5517,7 @@ namespace __format #ifdef _GLIBCXX_USE_WCHAR_T [[nodiscard]] - inline wstring + inline _GLIBCXX_CONSTEXPR_FORMAT wstring vformat(wstring_view __fmt, wformat_args __args) { __format::_Str_sink<wchar_t> __buf; @@ -5483,14 +5548,14 @@ namespace __format template<typename... _Args> [[nodiscard]] - inline string + inline _GLIBCXX_CONSTEXPR_FORMAT string format(format_string<_Args...> __fmt, _Args&&... __args) { return std::vformat(__fmt.get(), std::make_format_args(__args...)); } #ifdef _GLIBCXX_USE_WCHAR_T template<typename... _Args> [[nodiscard]] - inline wstring + inline _GLIBCXX_CONSTEXPR_FORMAT wstring format(wformat_string<_Args...> __fmt, _Args&&... __args) { return std::vformat(__fmt.get(), std::make_wformat_args(__args...)); } #endif @@ -5519,7 +5584,7 @@ namespace __format template<typename _Out, typename... _Args> requires output_iterator<_Out, const char&> - inline _Out + inline _GLIBCXX_CONSTEXPR_FORMAT _Out format_to(_Out __out, format_string<_Args...> __fmt, _Args&&... __args) { return std::vformat_to(std::move(__out), __fmt.get(), @@ -5529,7 +5594,7 @@ namespace __format #ifdef _GLIBCXX_USE_WCHAR_T template<typename _Out, typename... _Args> requires output_iterator<_Out, const wchar_t&> - inline _Out + inline _GLIBCXX_CONSTEXPR_FORMAT _Out format_to(_Out __out, wformat_string<_Args...> __fmt, _Args&&... __args) { return std::vformat_to(std::move(__out), __fmt.get(), @@ -5561,7 +5626,7 @@ namespace __format template<typename _Out, typename... _Args> requires output_iterator<_Out, const char&> - inline format_to_n_result<_Out> + inline _GLIBCXX_CONSTEXPR_FORMAT format_to_n_result<_Out> format_to_n(_Out __out, iter_difference_t<_Out> __n, format_string<_Args...> __fmt, _Args&&... __args) { @@ -5573,7 +5638,7 @@ namespace __format #ifdef _GLIBCXX_USE_WCHAR_T template<typename _Out, typename... _Args> requires output_iterator<_Out, const wchar_t&> - inline format_to_n_result<_Out> + inline _GLIBCXX_CONSTEXPR_FORMAT format_to_n_result<_Out> format_to_n(_Out __out, iter_difference_t<_Out> __n, wformat_string<_Args...> __fmt, _Args&&... __args) { @@ -5615,10 +5680,11 @@ namespace __format class _Counting_sink final : public _Ptr_sink<_CharT> { public: + _GLIBCXX_CONSTEXPR_FORMAT _Counting_sink() : _Ptr_sink<_CharT>(nullptr, 0) { } [[__gnu__::__always_inline__]] - size_t + _GLIBCXX_CONSTEXPR_FORMAT size_t count() const { return this->_M_count + this->_M_used().size(); } }; @@ -5653,7 +5719,7 @@ namespace __format template<typename... _Args> [[nodiscard]] - inline size_t + inline _GLIBCXX_CONSTEXPR_FORMAT size_t formatted_size(format_string<_Args...> __fmt, _Args&&... __args) { __format::_Counting_sink<char> __buf; @@ -5665,7 +5731,7 @@ namespace __format #ifdef _GLIBCXX_USE_WCHAR_T template<typename... _Args> [[nodiscard]] - inline size_t + inline _GLIBCXX_CONSTEXPR_FORMAT size_t formatted_size(wformat_string<_Args...> __fmt, _Args&&... __args) { __format::_Counting_sink<wchar_t> __buf; @@ -5736,6 +5802,7 @@ namespace __format namespace __format { template<typename _CharT, typename _Out, typename _Callback> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator __format_padded(basic_format_context<_Out, _CharT>& __fc, const _Spec<_CharT>& __spec, @@ -5755,14 +5822,16 @@ namespace __format struct _Restore_out { + _GLIBCXX_CONSTEXPR_FORMAT _Restore_out(basic_format_context<_Sink_iter<_CharT>, _CharT>& __fc) : _M_ctx(std::addressof(__fc)), _M_out(__fc.out()) { } - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_disarm() { _M_ctx = nullptr; } + _GLIBCXX_CONSTEXPR_FORMAT ~_Restore_out() { if (_M_ctx) @@ -5796,7 +5865,7 @@ namespace __format } template<typename _Out> - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_format(__maybe_const<_Tp, _CharT>& __elem, basic_format_context<_Out, _CharT>& __fc, basic_string_view<_CharT> __sep) const @@ -5894,12 +5963,14 @@ namespace __format protected: template<typename _Tuple, typename _Out, size_t... _Ids> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator _M_format(_Tuple& __tuple, index_sequence<_Ids...>, basic_format_context<_Out, _CharT>& __fc) const { return _M_format_elems(std::get<_Ids>(__tuple)..., __fc); } template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator _M_format_elems(__maybe_const<_Tps, _CharT>&... __elems, basic_format_context<_Out, _CharT>& __fc) const @@ -5929,7 +6000,7 @@ namespace __format } template<typename _Out> - void + _GLIBCXX_CONSTEXPR_FORMAT void _M_format(__maybe_const<_Tps, _CharT>&... __elems, basic_format_context<_Out, _CharT>& __fc, _String_view __sep) const @@ -5945,7 +6016,7 @@ namespace __format }; template<size_t... _Ids> - static auto + static _GLIBCXX_CONSTEXPR_FORMAT auto _S_create_storage(index_sequence<_Ids...>) -> __formatters_storage<_Ids...>; using _Formatters @@ -5981,6 +6052,7 @@ namespace __format // We deviate from standard, that declares this as template accepting // unconstrained FormatContext type, which seems unimplementable. template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(__maybe_const_pair& __p, basic_format_context<_Out, _CharT>& __fc) const @@ -6008,6 +6080,7 @@ namespace __format // We deviate from standard, that declares this as template accepting // unconstrained FormatContext type, which seems unimplementable. template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(__maybe_const_tuple& __t, basic_format_context<_Out, _CharT>& __fc) const @@ -6171,6 +6244,7 @@ namespace __format template<ranges::input_range _Rg, typename _Out> requires formattable<ranges::range_reference_t<_Rg>, _CharT> && same_as<remove_cvref_t<ranges::range_reference_t<_Rg>>, _Tp> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(_Rg&& __rg, basic_format_context<_Out, _CharT>& __fc) const { @@ -6189,6 +6263,7 @@ namespace __format private: template<ranges::input_range _Rg, typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator _M_format(_Rg& __rg, basic_format_context<_Out, _CharT>& __fc) const { @@ -6206,6 +6281,7 @@ namespace __format template<ranges::input_range _Rg, typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator _M_format_elems(_Rg& __rg, basic_format_context<_Out, _CharT>& __fc) const @@ -6305,6 +6381,7 @@ namespace __format // We deviate from standard, that declares this as template accepting // unconstrained FormatContext type, which seems unimplementable. template<typename _Out> + _GLIBCXX_CONSTEXPR_FORMAT typename basic_format_context<_Out, _CharT>::iterator format(__format::__maybe_const_range<_Rg, _CharT>& __rg, basic_format_context<_Out, _CharT>& __fc) const @@ -6331,6 +6408,7 @@ namespace __format #endif // C++23 formatting ranges #undef _GLIBCXX_WIDEN +#undef _GLIBCXX_CONSTEXPR_FORMAT _GLIBCXX_END_NAMESPACE_VERSION } // namespace std diff --git a/libstdc++-v3/testsuite/std/format/arguments/args_neg.cc b/libstdc++-v3/testsuite/std/format/arguments/args_neg.cc index 83c7b22d081..6894d6c4fc3 100644 --- a/libstdc++-v3/testsuite/std/format/arguments/args_neg.cc +++ b/libstdc++-v3/testsuite/std/format/arguments/args_neg.cc @@ -15,7 +15,7 @@ void test_missing_specialization() { struct X { }; X x; - (void)std::make_format_args(x); // { dg-error "here" } + (void)std::make_format_args(x); // { dg-error "(here|in 'constexpr' expansion of)" } // { dg-error "std::formatter must be specialized" "" { target *-*-* } 0 } } @@ -37,7 +37,7 @@ void test(std::formatter<Y>& f, std::format_parse_context& pc) { void test_const_arg() { const Y y; - (void)std::make_format_args(y); // { dg-error "here" } + (void)std::make_format_args(y); // { dg-error "(here|in 'constexpr' expansion of)" } // { dg-error "format arg must be non-const" "" { target *-*-* } 0 } } diff --git a/libstdc++-v3/testsuite/std/format/constexpr.cc b/libstdc++-v3/testsuite/std/format/constexpr.cc new file mode 100644 index 00000000000..7f806081f4e --- /dev/null +++ b/libstdc++-v3/testsuite/std/format/constexpr.cc @@ -0,0 +1,166 @@ +// { dg-do compile { target c++26 } } + +#include <format> +#include <string> +#include <string_view> +#include <tuple> +#include <vector> +#include <testsuite_hooks.h> + +#if _GLIBCXX_USE_CXX11_ABI + +#ifndef __glibcxx_constexpr_format +# error "Feature test macro for constexpr std::format is missing in <format>" +#elif __glibcxx_constexpr_format < 202511L +# error "Feature test macro for constexpr std::format has wrong value in <format>" +#endif + +// Slightly more general from __format::_Widen, works with character literals. +template<typename CharT> +consteval auto widen(auto narrow, auto wide) +{ + if constexpr (std::is_same_v<CharT, wchar_t>) + return wide; + else + return narrow; +} + +#define WIDEN_(C, S) widen<C>(S, L##S) +#define WIDEN(S) WIDEN_(CharT, S) + +template<typename CharT> +constexpr void +test_format() +{ + using namespace std; + + basic_string<CharT> res; + + res = format(WIDEN("{}"), WIDEN('c')); + VERIFY( res == WIDEN("c") ); + res = format(WIDEN("{1} {0} {0}"), WIDEN('a'), WIDEN('b')); + VERIFY( res == WIDEN("b a a") ); + res = format(WIDEN("{:?}"), WIDEN('\n')); + VERIFY( res == WIDEN("'\\n'") ); + res = format(WIDEN("{:.^10}"), WIDEN("hello")); + VERIFY( res == WIDEN("..hello...") ); + res = format(WIDEN("{:.>{}}"), WIDEN("world"), 8); + VERIFY( res == WIDEN("...world") ); + res = format(WIDEN("{:+#06X}"), 0xa); + VERIFY( res == WIDEN("+0X00A") ); + res = format(WIDEN("{:p}"), nullptr); + VERIFY( res == WIDEN("0x0") ); + res = format(WIDEN("{:07P}"), nullptr); + VERIFY( res == WIDEN("0X00000") ); + res = format(WIDEN("{} {}"), true, false); + VERIFY( res == WIDEN("true false") ); + res = format(WIDEN("{:+#06b}"), true); + VERIFY( res == WIDEN("+0b001") ); + res = format(WIDEN("{} {} {}"), WIDEN("abc"), basic_string_view<CharT>(WIDEN("def")), basic_string<CharT>(WIDEN("ghi"))); + VERIFY( res == WIDEN("abc def ghi") ); + res = format(WIDEN("{:?}"), WIDEN("hello\nworld")); + VERIFY( res == WIDEN("\"hello\\nworld\"") ); + res = format(WIDEN("{}"), tuple(1, true)); + VERIFY( res == WIDEN("(1, true)") ); + res = format(WIDEN("{:t<12m}"), tuple(WIDEN('a'), WIDEN("bc"))); + VERIFY( res == WIDEN("'a': \"bc\"ttt") ); + res = format(WIDEN("{:n}"), tuple(nullptr, -1, 1)); + VERIFY( res == WIDEN("0x0, -1, 1") ); + res = format(WIDEN("{}"), vector{1, 2, 3, 4}); + VERIFY( res == WIDEN("[1, 2, 3, 4]") ); + res = format(WIDEN("{:?s}"), vector{WIDEN('a'), WIDEN('\n'), WIDEN('b')}); + VERIFY( res == WIDEN("\"a\\nb\"") ); + res = format(WIDEN("{:n:+}"), vector{1, 2, 3}); + VERIFY( res == WIDEN("+1, +2, +3") ); +} + +template<typename CharT> +constexpr void +test_format_to() +{ + using namespace std; + + CharT buf[100]; + CharT* out; + + out = format_to(buf, WIDEN("{:.^5}"), WIDEN("foo")); + VERIFY( basic_string_view<CharT>(buf, out) == WIDEN(".foo.") ); + out = format_to(buf, WIDEN("{} {}"), nullptr, true); + VERIFY( basic_string_view<CharT>(buf, out) == WIDEN("0x0 true") ); +} + +template<typename CharT> +constexpr void +test_vformat() +{ + using namespace std; + using context = __format::__format_context<CharT>; + + basic_string<CharT> res; + + int arg1 = 1; + CharT arg2 = WIDEN('a'); + bool arg3 = true; + res = vformat(WIDEN("{} {:?} {}"), make_format_args<context>(arg1, arg2, arg3)); + VERIFY( res == WIDEN("1 'a' true") ); +} + +template<typename CharT> +constexpr void +test_vformat_to() +{ + using namespace std; + using context = __format::__format_context<CharT>; + + CharT buf[100]; + CharT* out; + + nullptr_t arg1 = nullptr; + basic_string<CharT> arg2 = WIDEN("foo"); + tuple<int, int> arg3{-3, 5}; + out = vformat_to(buf, WIDEN("{} {:?} {}"), make_format_args<context>(arg1, arg2, arg3)); + VERIFY( basic_string_view<CharT>(buf, out) == WIDEN("0x0 \"foo\" (-3, 5)") ); +} + +template<typename CharT> +constexpr void +test_format_to_n() +{ + using namespace std; + + CharT buf[100]; + format_to_n_result<CharT*> out; + int n; + + n = 100; + out = format_to_n(buf, n, WIDEN("{:+} {:?} {}"), 1, WIDEN("\n\n"), vector{1, 2, 3}); + VERIFY( out.size <= n ); + VERIFY( out.out - buf == out.size ); + VERIFY( basic_string_view<CharT>(buf, out.size) == WIDEN("+1 \"\\n\\n\" [1, 2, 3]") ); + n = 12; + out = format_to_n(buf, n, WIDEN("{} {} {}"), true, nullptr, WIDEN("long string")); + VERIFY( out.size > n ); + VERIFY( out.out - buf == n ); + VERIFY( basic_string_view<CharT>(buf, out.out) == WIDEN("true 0x0 lon") ); +} + +constexpr bool +all_tests() +{ + test_format<char>(); + test_format<wchar_t>(); + test_format_to<char>(); + test_format_to<wchar_t>(); + test_vformat<char>(); + test_vformat<wchar_t>(); + test_vformat_to<char>(); + test_vformat_to<wchar_t>(); + test_format_to_n<char>(); + test_format_to_n<wchar_t>(); + + return true; +} + +static_assert(all_tests()); + +#endif // _GLIBCXX_USE_CXX11_ABI diff --git a/libstdc++-v3/testsuite/std/format/debug.cc b/libstdc++-v3/testsuite/std/format/debug.cc index 43e930c579e..01bb9074ba7 100644 --- a/libstdc++-v3/testsuite/std/format/debug.cc +++ b/libstdc++-v3/testsuite/std/format/debug.cc @@ -1,5 +1,5 @@ -// { dg-options "-fexec-charset=UTF-8 -fwide-exec-charset=UTF-32LE -DUNICODE_ENC" { target le } } -// { dg-options "-fexec-charset=UTF-8 -fwide-exec-charset=UTF-32BE -DUNICODE_ENC" { target be } } +// { dg-options "-fexec-charset=UTF-8 -fwide-exec-charset=UTF-32LE -DUNICODE_ENC -fconstexpr-ops-limit=500000000" { target le } } +// { dg-options "-fexec-charset=UTF-8 -fwide-exec-charset=UTF-32BE -DUNICODE_ENC -fconstexpr-ops-limit=500000000" { target be } } // { dg-do run { target c++23 } } // { dg-require-effective-target 4byte_wchar_t } // { dg-add-options no_pch } @@ -8,19 +8,25 @@ #include <format> #include <testsuite_hooks.h> -std::string +#ifdef __glibcxx_constexpr_format +# define CONSTEXPR constexpr +#else +# define CONSTEXPR +#endif + +CONSTEXPR std::string fdebug(char t) { return std::format("{:?}", t); } -std::wstring +CONSTEXPR std::wstring fdebug(wchar_t t) { return std::format(L"{:?}", t); } -std::string +CONSTEXPR std::string fdebug(std::string_view t) { return std::format("{:?}", t); } -std::wstring +CONSTEXPR std::wstring fdebug(std::wstring_view t) { return std::format(L"{:?}", t); } @@ -29,7 +35,7 @@ fdebug(std::wstring_view t) #define WIDEN(S) WIDEN_(CharT, S) template<typename CharT> -void +CONSTEXPR void test_basic_escapes() { std::basic_string<CharT> res; @@ -72,7 +78,7 @@ test_basic_escapes() } template<typename CharT> -void +CONSTEXPR void test_ascii_escapes() { std::basic_string<CharT> res; @@ -89,7 +95,7 @@ test_ascii_escapes() } template<typename CharT> -void +CONSTEXPR void test_extended_ascii() { std::basic_string<CharT> res; @@ -117,7 +123,7 @@ test_extended_ascii() } template<typename CharT> -void +CONSTEXPR void test_unicode_escapes() { #if UNICODE_ENC @@ -166,7 +172,7 @@ test_unicode_escapes() } template<typename CharT> -void +CONSTEXPR void test_grapheme_extend() { #if UNICODE_ENC @@ -192,7 +198,7 @@ test_grapheme_extend() } template<typename CharT> -void +CONSTEXPR void test_replacement_char() { #if UNICODE_ENC @@ -206,7 +212,7 @@ test_replacement_char() #endif // UNICODE_ENC } -void +CONSTEXPR void test_ill_formed_utf8_seq() { #if UNICODE_ENC @@ -244,7 +250,7 @@ test_ill_formed_utf8_seq() #endif // UNICODE_ENC } -void +CONSTEXPR void test_ill_formed_utf32() { #if UNICODE_ENC @@ -269,7 +275,7 @@ test_ill_formed_utf32() } template<typename CharT> -void +CONSTEXPR void test_fill() { std::basic_string<CharT> res; @@ -315,7 +321,7 @@ test_fill() } template<typename CharT> -void +CONSTEXPR void test_prec() { std::basic_string<CharT> res; @@ -341,7 +347,8 @@ test_prec() #endif // UNICODE_ENC } -bool strip_quote(std::string_view& v) +CONSTEXPR bool +strip_quote(std::string_view& v) { if (!v.starts_with('"')) return false; @@ -349,7 +356,8 @@ bool strip_quote(std::string_view& v) return true; } -bool strip_quotes(std::string_view& v) +CONSTEXPR bool +strip_quotes(std::string_view& v) { if (!v.starts_with('"') || !v.ends_with('"')) return false; @@ -358,7 +366,8 @@ bool strip_quotes(std::string_view& v) return true; } -bool strip_prefix(std::string_view& v, size_t n, char c) +CONSTEXPR bool +strip_prefix(std::string_view& v, size_t n, char c) { size_t pos = v.find_first_not_of(c); if (pos == std::string_view::npos) @@ -369,7 +378,8 @@ bool strip_prefix(std::string_view& v, size_t n, char c) return true; } -void test_padding() +CONSTEXPR void +test_padding() { std::string res; std::string_view resv; @@ -719,7 +729,8 @@ void test_padding() #endif // UNICODE_ENC } -void test_char_as_wchar() +CONSTEXPR void +test_char_as_wchar() { std::wstring res; @@ -751,8 +762,9 @@ struct std::formatter<DebugWrapper<T>, CharT> } template<typename Out> - Out format(DebugWrapper<T> const& t, - std::basic_format_context<Out, CharT>& fc) const + CONSTEXPR Out + format(DebugWrapper<T> const& t, + std::basic_format_context<Out, CharT>& fc) const { return under.format(t.val, fc); } private: @@ -760,7 +772,7 @@ private: }; template<typename CharT, typename StrT> -void +CONSTEXPR void test_formatter_str() { CharT buf[]{ 'a', 'b', 'c', 0 }; @@ -770,7 +782,7 @@ test_formatter_str() } template<typename CharT> -void +CONSTEXPR void test_formatter_arr() { std::basic_string<CharT> res; @@ -786,7 +798,7 @@ test_formatter_arr() } template<typename CharT, typename SrcT> -void +CONSTEXPR void test_formatter_char() { DebugWrapper<SrcT> in{ 'a' }; @@ -795,7 +807,7 @@ test_formatter_char() } template<typename CharT> -void +CONSTEXPR void test_formatters() { test_formatter_char<CharT, CharT>(); @@ -806,7 +818,7 @@ test_formatters() test_formatter_arr<CharT>(); } -void +CONSTEXPR void test_formatters_c() { test_formatters<char>(); @@ -814,7 +826,8 @@ test_formatters_c() test_formatter_char<wchar_t, char>(); } -int main() +CONSTEXPR bool +test_all() { test_basic_escapes<char>(); test_basic_escapes<wchar_t>(); @@ -840,4 +853,16 @@ int main() test_padding(); test_formatters_c(); + + return true; +} + +#if defined(__glibcxx_constexpr_format) && defined(UNICODE_ENC) +// Deboug ouput is supported only for unicode literal encoding +static_assert(test_all()); +#endif + +int main() +{ + test_all(); } diff --git a/libstdc++-v3/testsuite/std/format/dynamic_format.cc b/libstdc++-v3/testsuite/std/format/dynamic_format.cc index fde2555ccb2..aa3109cca4e 100644 --- a/libstdc++-v3/testsuite/std/format/dynamic_format.cc +++ b/libstdc++-v3/testsuite/std/format/dynamic_format.cc @@ -3,7 +3,13 @@ #include <format> #include <testsuite_hooks.h> -void +#ifdef __glibcxx_constexpr_format +# define CONSTEXPR constexpr +#else +# define CONSTEXPR +#endif + +CONSTEXPR void test_char() { std::string fmt = "{}"; @@ -11,7 +17,7 @@ test_char() VERIFY( s == "123" ); } -void +CONSTEXPR void test_wchar() { std::wstring fmt = L"{:#o}"; @@ -19,7 +25,7 @@ test_wchar() VERIFY( s == L"0710" ); } -void +CONSTEXPR void test_internal_api() { // Using _Dynamic_format_string directly works even in C++20 mode. @@ -40,9 +46,20 @@ static_assert( !std::is_constructible_v<std::format_string<>, static_assert( !std::is_constructible_v<std::wformat_string<>, decltype(std::dynamic_format(L""))&&> ); -int main() +CONSTEXPR bool +test_all() { test_char(); test_wchar(); test_internal_api(); + return true; +} + +#ifdef __glibcxx_constexpr_format +static_assert(test_all()); +#endif + +int main() +{ + test_all(); } diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc index d342114083e..88e95a788b5 100644 --- a/libstdc++-v3/testsuite/std/format/functions/format.cc +++ b/libstdc++-v3/testsuite/std/format/functions/format.cc @@ -5,6 +5,12 @@ #include <format> +#ifdef __glibcxx_constexpr_format +# define CONSTEXPR constexpr +#else +# define CONSTEXPR +#endif + #ifndef __cpp_lib_format # error "Feature test macro for std::format is missing in <format>" #elif __cpp_lib_format < 202110L @@ -41,7 +47,7 @@ #include <cstdio> #include <testsuite_hooks.h> -void +CONSTEXPR void test_no_args() { std::string s; @@ -55,7 +61,7 @@ test_no_args() VERIFY( s == "128bpm }" ); } -void +CONSTEXPR void test_unescaped() { #ifdef __cpp_exceptions @@ -78,7 +84,7 @@ struct brit_punc : std::numpunct<char> std::string do_falsename() const override { return "nah bruv"; } }; -void +CONSTEXPR void test_std_examples() { using namespace std; @@ -125,10 +131,13 @@ test_std_examples() VERIFY(s0 == "1,+1,1, 1"); string s1 = format("{0:},{0:+},{0:-},{0: }", -1); VERIFY(s1 == "-1,-1,-1,-1"); - string s2 = format("{0:},{0:+},{0:-},{0: }", inf); - VERIFY(s2 == "inf,+inf,inf, inf"); - string s3 = format("{0:},{0:+},{0:-},{0: }", nan); - VERIFY(s3 == "nan,+nan,nan, nan"); + if (!std::is_constant_evaluated()) + { + string s2 = format("{0:},{0:+},{0:-},{0: }", inf); + VERIFY(s2 == "inf,+inf,inf, inf"); + string s3 = format("{0:},{0:+},{0:-},{0: }", nan); + VERIFY(s3 == "nan,+nan,nan, nan"); + } } // alternate form and zero fill @@ -143,34 +152,35 @@ test_std_examples() } // integer presentation types - { - // Change global locale so "{:L}" adds digit separators. - std::locale::global(std::locale({}, new brit_punc)); - - string s0 = format("{}", 42); - VERIFY(s0 == "42"); - string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); - VERIFY(s1 == "101010 42 52 2a"); - string s2 = format("{0:#x} {0:#X}", 42); - VERIFY(s2 == "0x2a 0X2A"); - string s3 = format("{:L}", 1234); - VERIFY(s3 == "1,234"); - - // Test locale's "byte-and-a-half" grouping (Imperial word? tribble?). - string s4 = format("{:#Lx}", 0xfffff); - VERIFY(s4 == "0xff,fff"); - - // Restore - std::locale::global(std::locale::classic()); - - string s5 = format("{}", -100); // PR libstdc++/114325 - VERIFY(s5 == "-100"); - string s6 = format("{:d} {:d}", -123, 999); - VERIFY(s6 == "-123 999"); - } + if (!std::is_constant_evaluated()) + { + // Change global locale so "{:L}" adds digit separators. + std::locale::global(std::locale({}, new brit_punc)); + + string s0 = format("{}", 42); + VERIFY(s0 == "42"); + string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); + VERIFY(s1 == "101010 42 52 2a"); + string s2 = format("{0:#x} {0:#X}", 42); + VERIFY(s2 == "0x2a 0X2A"); + string s3 = format("{:L}", 1234); + VERIFY(s3 == "1,234"); + + // Test locale's "byte-and-a-half" grouping (Imperial word? tribble?). + string s4 = format("{:#Lx}", 0xfffff); + VERIFY(s4 == "0xff,fff"); + + // Restore + std::locale::global(std::locale::classic()); + + string s5 = format("{}", -100); // PR libstdc++/114325 + VERIFY(s5 == "-100"); + string s6 = format("{:d} {:d}", -123, 999); + VERIFY(s6 == "-123 999"); + } } -void +CONSTEXPR void test_alternate_forms() { std::string s; @@ -180,23 +190,26 @@ test_alternate_forms() s = std::format("{0:#b} {0:+#B} {0:#o} {0:#x} {0:+#X} {0: #d}", 0); VERIFY( s == "0b0 +0B0 0 0x0 +0X0 0" ); - s = std::format("{0:+#012g} {0:+#014g} {0:+#014g}", 1234.0); - VERIFY( s == "+00001234.00 +0000001234.00 +0000001234.00" ); - s = std::format("{0:+#0{1}g} {0:+#0{2}g} {0:+#0{2}g}", 1234.5, 12, 14); - VERIFY( s == "+00001234.50 +0000001234.50 +0000001234.50" ); - - s = std::format("{:#.2g}", -0.0); - VERIFY( s == "-0.0" ); - - // PR libstdc++/108046 - s = std::format("{0:#.0} {0:#.1} {0:#.0g}", 10.0); - VERIFY( s == "1.e+01 1.e+01 1.e+01" ); - - // PR libstdc++/113512 - s = std::format("{:#.3g}", 0.025); - VERIFY( s == "0.0250" ); - s = std::format("{:#07.3g}", 0.02); - VERIFY( s == "00.0200" ); + if (!std::is_constant_evaluated()) + { + s = std::format("{0:+#012g} {0:+#014g} {0:+#014g}", 1234.0); + VERIFY( s == "+00001234.00 +0000001234.00 +0000001234.00" ); + s = std::format("{0:+#0{1}g} {0:+#0{2}g} {0:+#0{2}g}", 1234.5, 12, 14); + VERIFY( s == "+00001234.50 +0000001234.50 +0000001234.50" ); + + s = std::format("{:#.2g}", -0.0); + VERIFY( s == "-0.0" ); + + // PR libstdc++/108046 + s = std::format("{0:#.0} {0:#.1} {0:#.0g}", 10.0); + VERIFY( s == "1.e+01 1.e+01 1.e+01" ); + + // PR libstdc++/113512 + s = std::format("{:#.3g}", 0.025); + VERIFY( s == "0.0250" ); + s = std::format("{:#07.3g}", 0.02); + VERIFY( s == "00.0200" ); + } } void @@ -275,7 +288,7 @@ test_locale() std::locale::global(cloc); } -void +CONSTEXPR void test_width() { std::string s; @@ -317,7 +330,7 @@ test_width() } } -void +CONSTEXPR void test_char() { std::string s; @@ -347,7 +360,7 @@ test_char() VERIFY( s == "11110000 11110000 240 360 f0 F0" ); } -void +CONSTEXPR void test_wchar() { using namespace std::literals; @@ -356,24 +369,27 @@ test_wchar() s = std::format(L"{}", L'a'); VERIFY( s == L"a" ); - s = std::format(L"{} {} {} {} {} {}", L'0', 1, 2LL, 3.4, L"five", L"six"s); - VERIFY( s == L"0 1 2 3.4 five six" ); - - std::locale loc; - s = std::format(loc, L"{:L} {:.3s}{:Lc}", true, L"data"sv, '.'); - VERIFY( s == L"true dat." ); - - s = std::format(L"{}", 0.0625); - VERIFY( s == L"0.0625" ); - s = std::format(L"{}", 0.25); - VERIFY( s == L"0.25" ); - s = std::format(L"{:+a} {:A}", 0x1.23p45, -0x1.abcdefp-15); - VERIFY( s == L"+1.23p+45 -1.ABCDEFP-15" ); - - double inf = std::numeric_limits<double>::infinity(); - double nan = std::numeric_limits<double>::quiet_NaN(); - s = std::format(L"{0} {0:F} {1} {1:E}", -inf, -nan); - VERIFY( s == L"-inf -INF -nan -NAN" ); + if (!std::is_constant_evaluated()) + { + s = std::format(L"{} {} {} {} {} {}", L'0', 1, 2LL, 3.4, L"five", L"six"s); + VERIFY( s == L"0 1 2 3.4 five six" ); + + std::locale loc; + s = std::format(loc, L"{:L} {:.3s}{:Lc}", true, L"data"sv, '.'); + VERIFY( s == L"true dat." ); + + s = std::format(L"{}", 0.0625); + VERIFY( s == L"0.0625" ); + s = std::format(L"{}", 0.25); + VERIFY( s == L"0.25" ); + s = std::format(L"{:+a} {:A}", 0x1.23p45, -0x1.abcdefp-15); + VERIFY( s == L"+1.23p+45 -1.ABCDEFP-15" ); + + double inf = std::numeric_limits<double>::infinity(); + double nan = std::numeric_limits<double>::quiet_NaN(); + s = std::format(L"{0} {0:F} {1} {1:E}", -inf, -nan); + VERIFY( s == L"-inf -INF -nan -NAN" ); + } s = std::format(L"{0:#b} {0:#B} {0:#x} {0:#X}", 99); VERIFY( s == L"0b1100011 0B1100011 0x63 0X63" ); @@ -382,20 +398,23 @@ test_wchar() s = std::format(L"{:d} {:d}", wchar_t(-1), char(-1)); VERIFY( s.find('-') == std::wstring::npos ); - auto ws = std::format(L"{:L}", 0.5); - VERIFY( ws == L"0.5" ); - // The default C locale. - std::locale cloc = std::locale::classic(); - // PR libstdc++/119671 use-after-free formatting floating-point to wstring - ws = std::format(cloc, L"{:L}", 0.5); - VERIFY( ws == L"0.5" ); - // A locale with no name, but with the same facets as the C locale. - std::locale locx(cloc, &std::use_facet<std::ctype<char>>(cloc)); - ws = std::format(locx, L"{:L}", 0.5); - VERIFY( ws == L"0.5" ); + if (!std::is_constant_evaluated()) + { + auto ws = std::format(L"{:L}", 0.5); + VERIFY( ws == L"0.5" ); + // The default C locale. + std::locale cloc = std::locale::classic(); + // PR libstdc++/119671 use-after-free formatting floating-point to wstring + ws = std::format(cloc, L"{:L}", 0.5); + VERIFY( ws == L"0.5" ); + // A locale with no name, but with the same facets as the C locale. + std::locale locx(cloc, &std::use_facet<std::ctype<char>>(cloc)); + ws = std::format(locx, L"{:L}", 0.5); + VERIFY( ws == L"0.5" ); + } } -void +CONSTEXPR void test_minmax() { auto check = []<typename T, typename U = std::make_unsigned_t<T>>(T, U = 0) { @@ -422,7 +441,7 @@ test_minmax() #endif } -void +CONSTEXPR void test_p1652r1() // printf corner cases in std::format { std::string s; @@ -436,27 +455,33 @@ test_p1652r1() // printf corner cases in std::format s = std::format("{:c}", c); VERIFY( s == "A" ); - // Problem 3: "-000nan" is not a floating point value - double nan = std::numeric_limits<double>::quiet_NaN(); - try { - s = std::vformat("{:0=6}", std::make_format_args(nan)); - VERIFY( false ); - } catch (const std::format_error&) { - } - - s = std::format("{:06}", nan); - VERIFY( s == " nan" ); + if (!std::is_constant_evaluated()) + { + // Problem 3: "-000nan" is not a floating point value + double nan = std::numeric_limits<double>::quiet_NaN(); + try { + s = std::vformat("{:0=6}", std::make_format_args(nan)); + VERIFY( false ); + } catch (const std::format_error&) { + } + + s = std::format("{:06}", nan); + VERIFY( s == " nan" ); + } // Problem 4: bool needs a type format specifier s = std::format("{:s}", true); VERIFY( s == "true" ); - // Problem 5: double does not roundtrip float - s = std::format("{}", 3.31f); - VERIFY( s == "3.31" ); + if (!std::is_constant_evaluated()) + { + // Problem 5: double does not roundtrip float + s = std::format("{}", 3.31f); + VERIFY( s == "3.31" ); + } } -void +CONSTEXPR void test_pointer() { void* p = nullptr; @@ -477,28 +502,31 @@ test_pointer() s = std::format("{:o<4},{:o>5},{:o^7}", p, pc, nullptr); // fill+align+width VERIFY( s == "0x0o,oo0x0,oo0x0oo" ); - pc = p = &s; - str_int = std::format("{:#x}", reinterpret_cast<std::uintptr_t>(p)); - s = std::format("{} {} {}", p, pc, nullptr); - VERIFY( s == (str_int + ' ' + str_int + " 0x0") ); - str_int = std::format("{:#20x}", reinterpret_cast<std::uintptr_t>(p)); - s = std::format("{:20} {:20p}", p, pc); - VERIFY( s == (str_int + ' ' + str_int) ); + if (!std::is_constant_evaluated()) + { + pc = p = &s; + str_int = std::format("{:#x}", reinterpret_cast<std::uintptr_t>(p)); + s = std::format("{} {} {}", p, pc, nullptr); + VERIFY( s == (str_int + ' ' + str_int + " 0x0") ); + str_int = std::format("{:#20x}", reinterpret_cast<std::uintptr_t>(p)); + s = std::format("{:20} {:20p}", p, pc); + VERIFY( s == (str_int + ' ' + str_int) ); #if __cpp_lib_format >= 202304L - // P2510R3 Formatting pointers - s = std::format("{:06} {:07P} {:08p}", (void*)0, (const void*)0, nullptr); - VERIFY( s == "0x0000 0X00000 0x000000" ); - str_int = std::format("{:#016x}", reinterpret_cast<std::uintptr_t>(p)); - s = std::format("{:016} {:016}", p, pc); - VERIFY( s == (str_int + ' ' + str_int) ); - str_int = std::format("{:#016X}", reinterpret_cast<std::uintptr_t>(p)); - s = std::format("{:016P} {:016P}", p, pc); - VERIFY( s == (str_int + ' ' + str_int) ); + // P2510R3 Formatting pointers + s = std::format("{:06} {:07P} {:08p}", (void*)0, (const void*)0, nullptr); + VERIFY( s == "0x0000 0X00000 0x000000" ); + str_int = std::format("{:#016x}", reinterpret_cast<std::uintptr_t>(p)); + s = std::format("{:016} {:016}", p, pc); + VERIFY( s == (str_int + ' ' + str_int) ); + str_int = std::format("{:#016X}", reinterpret_cast<std::uintptr_t>(p)); + s = std::format("{:016P} {:016P}", p, pc); + VERIFY( s == (str_int + ' ' + str_int) ); #endif + } } -void +CONSTEXPR void test_bool() { std::string s; @@ -519,7 +547,7 @@ test_bool() VERIFY( s == "0 0x1 0X0" ); } -void +CONSTEXPR void test_unicode() { #ifdef UNICODE @@ -579,13 +607,13 @@ test_unicode() #endif } -int main() +CONSTEXPR bool +test_all() { test_no_args(); test_unescaped(); test_std_examples(); test_alternate_forms(); - test_locale(); test_width(); test_char(); test_wchar(); @@ -594,4 +622,21 @@ int main() test_pointer(); test_bool(); test_unicode(); + + if (!std::is_constant_evaluated()) + { + test_infnan(); + test_locale(); + } + + return true; +} + +#ifdef __glibcxx_constexpr_format +static_assert(test_all()); +#endif + +int main() +{ + test_all(); } diff --git a/libstdc++-v3/testsuite/std/format/functions/format_to.cc b/libstdc++-v3/testsuite/std/format/functions/format_to.cc index 94e6262bc66..ec6816ddba9 100644 --- a/libstdc++-v3/testsuite/std/format/functions/format_to.cc +++ b/libstdc++-v3/testsuite/std/format/functions/format_to.cc @@ -6,22 +6,31 @@ #include <cstring> #include <testsuite_hooks.h> +#ifdef __glibcxx_constexpr_format +# define CONSTEXPR constexpr +#else +# define CONSTEXPR +#endif + struct punct : std::numpunct<char> { std::string do_grouping() const override { return "\2"; } }; -void +CONSTEXPR void test() { char buf[32] = { }; auto out = std::format_to(buf, "test"); VERIFY( out == buf+4 ); - std::locale loc({}, new punct); - auto out2 = std::format_to(buf, loc, "{:Ld}", 12345); - VERIFY( out2 == buf+7 ); - VERIFY( std::string_view(buf, out2 - buf) == "1,23,45" ); + if (!std::is_constant_evaluated()) + { + std::locale loc({}, new punct); + auto out2 = std::format_to(buf, loc, "{:Ld}", 12345); + VERIFY( out2 == buf+7 ); + VERIFY( std::string_view(buf, out2 - buf) == "1,23,45" ); + } } struct wpunct : std::numpunct<wchar_t> @@ -29,17 +38,20 @@ struct wpunct : std::numpunct<wchar_t> std::string do_grouping() const override { return "\2"; } }; -void +CONSTEXPR void test_wchar() { wchar_t buf[32] = { }; auto out = std::format_to(buf, L"123 + 456 = {}", 579); VERIFY( out == buf+15 ); - std::locale loc({}, new wpunct); - auto out2 = std::format_to(buf, loc, L"{:Ld}", 12345); - VERIFY( out2 == buf+7 ); - VERIFY( std::wstring_view(buf, out2 - buf) == L"1,23,45" ); + if (!std::is_constant_evaluated()) + { + std::locale loc({}, new wpunct); + auto out2 = std::format_to(buf, loc, L"{:Ld}", 12345); + VERIFY( out2 == buf+7 ); + VERIFY( std::wstring_view(buf, out2 - buf) == L"1,23,45" ); + } } template<typename I> @@ -50,20 +62,20 @@ struct move_only_iterator using difference_type = iterator::difference_type; using iterator_category = std::output_iterator_tag; - move_only_iterator(iterator b) : base_(b) { } + constexpr move_only_iterator(iterator b) : base_(b) { } move_only_iterator(move_only_iterator&&) = default; move_only_iterator& operator=(move_only_iterator&&) = default; - move_only_iterator& operator++() { ++base_; return *this; } - move_only_iterator operator++(int) { auto tmp = *this; ++base_; return tmp; } + constexpr move_only_iterator& operator++() { ++base_; return *this; } + constexpr move_only_iterator operator++(int) { auto tmp = *this; ++base_; return tmp; } - decltype(auto) operator*() { return *base_; } + constexpr decltype(auto) operator*() { return *base_; } private: iterator base_; }; -void +CONSTEXPR void test_move_only() { std::string str; @@ -79,7 +91,7 @@ test_move_only() VERIFY( std::wstring_view(vec.data(), vec.size()) == L"format hat!" ); } -void +CONSTEXPR void test_pr110917() { // PR libstdc++/110917 @@ -90,10 +102,22 @@ test_pr110917() VERIFY( ! std::memcmp(buf, "abc 123", 7) ); } -int main() +CONSTEXPR bool +test_all() { test(); test_wchar(); test_move_only(); test_pr110917(); + + return true; +} + +#ifdef __glibcxx_constexpr_format +static_assert(test_all()); +#endif + +int main() +{ + test_all(); } diff --git a/libstdc++-v3/testsuite/std/format/functions/size.cc b/libstdc++-v3/testsuite/std/format/functions/size.cc index 1ece4108d85..16029bd5949 100644 --- a/libstdc++-v3/testsuite/std/format/functions/size.cc +++ b/libstdc++-v3/testsuite/std/format/functions/size.cc @@ -4,7 +4,13 @@ #include <string> #include <testsuite_hooks.h> -void +#ifdef __glibcxx_constexpr_format +# define CONSTEXPR constexpr +#else +# define CONSTEXPR +#endif + +CONSTEXPR void test() { auto n = std::formatted_size(""); @@ -24,7 +30,7 @@ test() VERIFY( n == 5 ); } -void +CONSTEXPR void test_wchar() { auto n = std::formatted_size(L""); @@ -44,8 +50,19 @@ test_wchar() VERIFY( n == 5 ); } -int main() +CONSTEXPR bool +test_all() { test(); test_wchar(); + return true; +} + +#ifdef __glibcxx_constexpr_format +static_assert(test_all()); +#endif + +int main() +{ + test_all(); } diff --git a/libstdc++-v3/testsuite/std/format/ranges/format_kind.cc b/libstdc++-v3/testsuite/std/format/ranges/format_kind.cc index 1450fbaebc5..49c8246fee2 100644 --- a/libstdc++-v3/testsuite/std/format/ranges/format_kind.cc +++ b/libstdc++-v3/testsuite/std/format/ranges/format_kind.cc @@ -12,6 +12,12 @@ #include <unordered_set> #include <vector> +#ifdef __glibcxx_constexpr_format +# define CONSTEXPR constexpr +#else +# define CONSTEXPR +#endif + static_assert( std::format_kind<std::vector<int>> == std::range_format::sequence ); static_assert( std::format_kind<std::deque<int>> == std::range_format::sequence ); static_assert( std::format_kind<std::list<int>> == std::range_format::sequence ); @@ -64,7 +70,8 @@ struct CustFormat : std::vector<T> template<typename T, std::range_format rf> constexpr auto std::format_kind<CustFormat<T, rf>> = rf; -void test_override() +CONSTEXPR bool +test_override() { CustFormat<int, std::range_format::disabled> disabledf; static_assert( !std::formattable<decltype(disabledf), char> ); @@ -88,8 +95,14 @@ void test_override() VERIFY( std::format("{}", debugf) == R"("abcd")" ); // Support precision as string do VERIFY( std::format("{:.3}", debugf) == R"("ab)" ); + + return true; } +#ifdef __glibcxx_constexpr_format +static_assert(test_override()); +#endif + int main() { test_override(); diff --git a/libstdc++-v3/testsuite/std/format/ranges/formatter.cc b/libstdc++-v3/testsuite/std/format/ranges/formatter.cc index a50c5b1033f..947e6c3bc60 100644 --- a/libstdc++-v3/testsuite/std/format/ranges/formatter.cc +++ b/libstdc++-v3/testsuite/std/format/ranges/formatter.cc @@ -6,6 +6,12 @@ #include <vector> #include <span> +#ifdef __glibcxx_constexpr_format +# define CONSTEXPR constexpr +#else +# define CONSTEXPR +#endif + #define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) #define WIDEN(S) WIDEN_(CharT, S) @@ -32,7 +38,7 @@ struct std::formatter<MyVector<T, Formatter>, CharT> { return _formatter.parse(pc); } template<typename Out> - typename std::basic_format_context<Out, CharT>::iterator + CONSTEXPR std::basic_format_context<Out, CharT>::iterator format(const MyVector<T, Formatter>& mv, std::basic_format_context<Out, CharT>& fc) const { return _formatter.format(mv, fc); } @@ -42,7 +48,7 @@ private: }; template<typename CharT, template<typename, typename> class Formatter> -void +CONSTEXPR void test_default() { MyVector<int, Formatter> vec{1, 2, 3}; @@ -94,7 +100,7 @@ test_default() } template<typename CharT, template<typename, typename> class Formatter> -void +CONSTEXPR void test_override() { MyVector<CharT, Formatter> vc{'a', 'b', 'c', 'd'}; @@ -115,7 +121,8 @@ test_override() } template<template<typename, typename> class Formatter> -void test_outputs() +CONSTEXPR void +test_outputs() { test_default<char, Formatter>(); test_default<wchar_t, Formatter>(); @@ -123,7 +130,7 @@ void test_outputs() test_override<wchar_t, Formatter>(); } -void +CONSTEXPR void test_nested() { MyVector<MyVector<int>> v @@ -152,7 +159,8 @@ struct std::formatter<MyFlatMap, CharT> : std::range_formatter<MyFlatMap::reference> {}; -void test_const_ref_type_mismatch() +CONSTEXPR void +test_const_ref_type_mismatch() { MyFlatMap m{{1, 11}, {2, 22}}; std::string res = std::format("{:m}", m); @@ -163,13 +171,15 @@ template<typename T, typename CharT> using VectorFormatter = std::formatter<std::vector<T>, CharT>; template<template<typename> typename Range> -void test_nonblocking() +CONSTEXPR void +test_nonblocking() { static_assert(!std::enable_nonlocking_formatter_optimization< Range<int>>); } -int main() +CONSTEXPR bool +test_all() { test_outputs<std::range_formatter>(); test_outputs<VectorFormatter>(); @@ -179,4 +189,15 @@ int main() test_nonblocking<std::span>(); test_nonblocking<std::vector>(); test_nonblocking<MyVector>(); + + return true; +} + +#ifdef __glibcxx_constexpr_format +static_assert(test_all()); +#endif + +int main() +{ + test_all(); } diff --git a/libstdc++-v3/testsuite/std/format/ranges/sequence.cc b/libstdc++-v3/testsuite/std/format/ranges/sequence.cc index 7fb65f9c551..cba53b46270 100644 --- a/libstdc++-v3/testsuite/std/format/ranges/sequence.cc +++ b/libstdc++-v3/testsuite/std/format/ranges/sequence.cc @@ -1,5 +1,5 @@ // { dg-do run { target c++23 } } -// { dg-options "-fexec-charset=UTF-8" } +// { dg-options "-fexec-charset=UTF-8 -fconstexpr-ops-limit=5000000000" } // { dg-timeout-factor 2 } #include <array> @@ -12,6 +12,12 @@ #include <testsuite_iterators.h> #include <vector> +#ifdef __glibcxx_constexpr_format +# define CONSTEXPR constexpr +#else +# define CONSTEXPR +#endif + struct NotFormattable {}; @@ -19,7 +25,7 @@ static_assert(!std::formattable<std::vector<NotFormattable>, char>); static_assert(!std::formattable<std::span<NotFormattable>, wchar_t>); template<typename... Args> -bool +CONSTEXPR bool is_format_string_for(const char* str, Args&&... args) { try { @@ -31,7 +37,7 @@ is_format_string_for(const char* str, Args&&... args) } template<typename... Args> -bool +CONSTEXPR bool is_format_string_for(const wchar_t* str, Args&&... args) { try { @@ -43,7 +49,8 @@ is_format_string_for(const wchar_t* str, Args&&... args) } template<typename Rg, typename CharT> -bool is_range_formatter_spec_for(CharT const* spec, Rg&& rg) +CONSTEXPR bool +is_range_formatter_spec_for(CharT const* spec, Rg&& rg) { using V = std::remove_cvref_t<std::ranges::range_reference_t<Rg>>; std::range_formatter<V, CharT> fmt; @@ -56,7 +63,7 @@ bool is_range_formatter_spec_for(CharT const* spec, Rg&& rg) } } -void +CONSTEXPR void test_format_string() { // invalid format spec 'p' @@ -79,7 +86,8 @@ test_format_string() #define WIDEN(S) WIDEN_(CharT, S) template<typename CharT, typename Range, typename Storage> -void test_output() +CONSTEXPR void +test_output() { using Sv = std::basic_string_view<CharT>; using T = std::ranges::range_value_t<Range>; @@ -153,25 +161,30 @@ void test_output() } template<typename Cont> -void test_output_cont() +CONSTEXPR void +test_output_cont() { test_output<char, Cont&, Cont>(); test_output<wchar_t, Cont const&, Cont>(); } template<typename View> -void test_output_view() +CONSTEXPR void +test_output_view() { test_output<char, View, int[3]>(); test_output<wchar_t, View, int[3]>(); } -void +CONSTEXPR void test_outputs() { using namespace __gnu_test; test_output_cont<std::vector<int>>(); - test_output_cont<std::list<int>>(); + + if (!std::is_constant_evaluated()) + test_output_cont<std::list<int>>(); + test_output_cont<std::array<int, 3>>(); test_output_view<std::span<int>>(); @@ -185,7 +198,7 @@ test_outputs() test_output_view<test_forward_range<const int>>(); } -void +CONSTEXPR void test_nested() { std::vector<std::vector<int>> v @@ -201,7 +214,8 @@ test_nested() VERIFY( res == "+[01, 02, 11, 12]+" ); } -bool strip_quote(std::string_view& v) +CONSTEXPR bool +strip_quote(std::string_view& v) { if (!v.starts_with('"')) return false; @@ -209,7 +223,8 @@ bool strip_quote(std::string_view& v) return true; } -bool strip_prefix(std::string_view& v, std::string_view expected, bool quoted = false) +CONSTEXPR bool +strip_prefix(std::string_view& v, std::string_view expected, bool quoted = false) { if (quoted && !strip_quote(v)) return false; @@ -221,7 +236,8 @@ bool strip_prefix(std::string_view& v, std::string_view expected, bool quoted = return true; } -bool strip_squares(std::string_view& v) +CONSTEXPR bool +strip_squares(std::string_view& v) { if (!v.starts_with('[') || !v.ends_with(']')) return false; @@ -230,7 +246,8 @@ bool strip_squares(std::string_view& v) return true; } -bool strip_prefix(std::string_view& v, size_t n, char c) +CONSTEXPR bool +strip_prefix(std::string_view& v, size_t n, char c) { size_t pos = v.find_first_not_of(c); if (pos == std::string_view::npos) @@ -241,7 +258,8 @@ bool strip_prefix(std::string_view& v, size_t n, char c) return true; } -void test_padding() +CONSTEXPR void +test_padding() { std::string res; std::string_view resv; @@ -323,10 +341,22 @@ void test_padding() VERIFY( check_elems(resv, false) ); } -int main() +CONSTEXPR bool +test_all() { test_format_string(); test_outputs(); test_nested(); test_padding(); + + return true; +} + +#ifdef __glibcxx_constexpr_format +static_assert(test_all()); +#endif + +int main() +{ + test_all(); } diff --git a/libstdc++-v3/testsuite/std/format/string.cc b/libstdc++-v3/testsuite/std/format/string.cc index ee987a15ec3..004690ad8da 100644 --- a/libstdc++-v3/testsuite/std/format/string.cc +++ b/libstdc++-v3/testsuite/std/format/string.cc @@ -1,10 +1,17 @@ // { dg-do run { target c++20 } } +// { dg-options "-fconstexpr-ops-limit=100000000" } #include <format> #include <testsuite_hooks.h> +#ifdef __glibcxx_constexpr_format +# define CONSTEXPR constexpr +#else +# define CONSTEXPR +#endif + template<typename... Args> -bool +CONSTEXPR bool is_format_string_for(const char* str, Args&&... args) { try { @@ -16,7 +23,7 @@ is_format_string_for(const char* str, Args&&... args) } template<typename... Args> -bool +CONSTEXPR bool is_format_string_for(const wchar_t* str, Args&&... args) { try { @@ -27,7 +34,7 @@ is_format_string_for(const wchar_t* str, Args&&... args) } } -void +CONSTEXPR void test_no_args() { VERIFY( is_format_string_for("") ); @@ -42,7 +49,7 @@ test_no_args() VERIFY( ! is_format_string_for("{{{{{") ); } -void +CONSTEXPR void test_indexing() { VERIFY( is_format_string_for("{} to {}", "a", "b") ); // automatic indexing @@ -68,7 +75,7 @@ constexpr bool escaped_strings_supported = true; constexpr bool escaped_strings_supported = false; #endif -void +CONSTEXPR void test_format_spec() { VERIFY( is_format_string_for("{:}", 1) ); @@ -78,9 +85,14 @@ test_format_spec() VERIFY( is_format_string_for("{0:} {0:c}", 'c') ); VERIFY( is_format_string_for("{0:p} {0:}", nullptr) ); VERIFY( is_format_string_for("{:d} {:+d}", true, true) ); - VERIFY( is_format_string_for("{:0<-#03Ld}", 1) ); - VERIFY( is_format_string_for("{1:0<-#03.4Lf}", 1, 2.3) ); - VERIFY( is_format_string_for("{1:3.3f}", 1, 2.3) ); + + if (!std::is_constant_evaluated()) + { + VERIFY( is_format_string_for("{:0<-#03Ld}", 1) ); + VERIFY( is_format_string_for("{1:0<-#03.4Lf}", 1, 2.3) ); + VERIFY( is_format_string_for("{1:3.3f}", 1, 2.3) ); + } + VERIFY( is_format_string_for("{:#d}", 'c') ); VERIFY( is_format_string_for("{:#d}", true) ); VERIFY( is_format_string_for("{0:s} {0:?}", "str") == escaped_strings_supported ); @@ -130,13 +142,16 @@ test_format_spec() VERIFY( ! is_format_string_for("{:3.3p}", nullptr) ); // Dynamic precision arg must be a standard integer type. - VERIFY( ! is_format_string_for("{:.{}f}", 1.0, 1.5) ); - VERIFY( ! is_format_string_for("{:.{}f}", 1.0, true) ); - VERIFY( ! is_format_string_for("{:.{}f}", 1.0, "str") ); - VERIFY( ! is_format_string_for("{:.{}f}", 1.0, nullptr) ); + if (!std::is_constant_evaluated()) + { + VERIFY( ! is_format_string_for("{:.{}f}", 1.0, 1.5) ); + VERIFY( ! is_format_string_for("{:.{}f}", 1.0, true) ); + VERIFY( ! is_format_string_for("{:.{}f}", 1.0, "str") ); + VERIFY( ! is_format_string_for("{:.{}f}", 1.0, nullptr) ); #ifdef __SIZEOF_INT128__ - VERIFY( ! is_format_string_for("{:{}f}", 1.0, static_cast<unsigned __int128>(1)) ); + VERIFY( ! is_format_string_for("{:{}f}", 1.0, static_cast<unsigned __int128>(1)) ); #endif + } // Invalid presentation types for integers. VERIFY( ! is_format_string_for("{:f}", 1) ); @@ -164,7 +179,7 @@ test_format_spec() VERIFY( ! is_format_string_for(L"{:9999999}", 1) ); } -void +CONSTEXPR void test_pr110862() { try { @@ -178,7 +193,7 @@ test_pr110862() } } -void +CONSTEXPR void test_pr110974() { try { @@ -197,11 +212,23 @@ test_pr110974() } } -int main() +CONSTEXPR bool +test_all() { test_no_args(); test_indexing(); test_format_spec(); test_pr110862(); test_pr110974(); + + return true; +} + +#ifdef __glibcxx_constexpr_format +static_assert(test_all()); +#endif + +int main() +{ + test_all(); } diff --git a/libstdc++-v3/testsuite/std/format/tuple.cc b/libstdc++-v3/testsuite/std/format/tuple.cc index eace82730f0..dc7b860a902 100644 --- a/libstdc++-v3/testsuite/std/format/tuple.cc +++ b/libstdc++-v3/testsuite/std/format/tuple.cc @@ -1,5 +1,5 @@ // { dg-do run { target c++23 } } -// { dg-options "-fexec-charset=UTF-8" } +// { dg-options "-fexec-charset=UTF-8 -fconstexpr-ops-limit=500000000" } // { dg-timeout-factor 2 } #include <format> @@ -8,6 +8,12 @@ #include <tuple> #include <utility> +#ifdef __glibcxx_constexpr_format +# define CONSTEXPR constexpr +#else +# define CONSTEXPR +#endif + struct NotFormattable {}; @@ -15,7 +21,7 @@ static_assert( !std::formattable<std::pair<int, NotFormattable>, char> ); static_assert( !std::formattable<std::tuple<int, NotFormattable, int>, wchar_t> ); template<typename... Args> -bool +CONSTEXPR bool is_format_string_for(const char* str, Args&&... args) { try { @@ -27,7 +33,7 @@ is_format_string_for(const char* str, Args&&... args) } template<typename... Args> -bool +CONSTEXPR bool is_format_string_for(const wchar_t* str, Args&&... args) { try { @@ -41,7 +47,7 @@ is_format_string_for(const wchar_t* str, Args&&... args) #define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) #define WIDEN(S) WIDEN_(CharT, S) -void +CONSTEXPR void test_format_string() { // invalid format stringss @@ -123,7 +129,8 @@ void test_multi() } template<typename CharT, typename Tuple> -void test_empty() +CONSTEXPR void +test_empty() { std::basic_string<CharT> res; @@ -142,7 +149,8 @@ void test_empty() } template<typename CharT, typename Pair> -void test_pair() +CONSTEXPR void +test_pair() { using Ft = std::remove_cvref_t<std::tuple_element_t<0, Pair>>; using St = std::remove_cvref_t<std::tuple_element_t<1, Pair>>; @@ -167,7 +175,8 @@ void test_pair() } template<typename CharT, template<typename, typename> class PairT> -void test_pair_e() +CONSTEXPR void +test_pair_e() { test_pair<CharT, PairT<int, std::basic_string<CharT>>>(); test_pair<CharT, PairT<int, const CharT*>>(); @@ -196,7 +205,7 @@ struct std::formatter<MyPair<Pair>, CharT> { return _formatter.parse(pc); } template<typename Out> - typename std::basic_format_context<Out, CharT>::iterator + CONSTEXPR typename std::basic_format_context<Out, CharT>::iterator format(const MyPair<Pair>& mp, std::basic_format_context<Out, CharT>& fc) const { return _formatter.format(mp, fc); } @@ -206,7 +215,8 @@ private: }; template<typename CharT, template<typename, typename> class PairT> -void test_custom() +CONSTEXPR void +test_custom() { std::basic_string<CharT> res; MyPair<PairT<int, const CharT*>> c1(1, WIDEN("abc")); @@ -228,9 +238,12 @@ void test_custom() } template<typename CharT> -void test_outputs() +CONSTEXPR void +test_outputs() { - test_multi<CharT>(); + if (!std::is_constant_evaluated()) + test_multi<CharT>(); + test_empty<CharT, std::tuple<>>(); test_pair_e<CharT, std::pair>(); test_pair_e<CharT, std::tuple>(); @@ -238,7 +251,8 @@ void test_outputs() test_custom<CharT, std::tuple>(); } -void test_nested() +CONSTEXPR void +test_nested() { std::string res; std::tuple<std::tuple<>, std::pair<int, std::string>> tt{{}, {1, "abc"}}; @@ -251,7 +265,8 @@ void test_nested() VERIFY( res == R"((): (1, "abc"))" ); } -bool strip_quote(std::string_view& v) +CONSTEXPR bool +strip_quote(std::string_view& v) { if (!v.starts_with('"')) return false; @@ -259,7 +274,8 @@ bool strip_quote(std::string_view& v) return true; } -bool strip_prefix(std::string_view& v, std::string_view expected, bool quoted = false) +CONSTEXPR bool +strip_prefix(std::string_view& v, std::string_view expected, bool quoted = false) { if (quoted && !strip_quote(v)) return false; @@ -271,7 +287,8 @@ bool strip_prefix(std::string_view& v, std::string_view expected, bool quoted = return true; } -bool strip_parens(std::string_view& v) +CONSTEXPR bool +strip_parens(std::string_view& v) { if (!v.starts_with('(') || !v.ends_with(')')) return false; @@ -280,7 +297,8 @@ bool strip_parens(std::string_view& v) return true; } -bool strip_prefix(std::string_view& v, size_t n, char c) +CONSTEXPR bool +strip_prefix(std::string_view& v, size_t n, char c) { size_t pos = v.find_first_not_of(c); if (pos == std::string_view::npos) @@ -291,7 +309,8 @@ bool strip_prefix(std::string_view& v, size_t n, char c) return true; } -void test_padding() +CONSTEXPR void +test_padding() { std::string res; std::string_view resv; @@ -351,13 +370,14 @@ struct std::formatter<Custom, CharT> { return pc.begin(); } template<typename Out> - typename std::basic_format_context<Out, CharT>::iterator + CONSTEXPR typename std::basic_format_context<Out, CharT>::iterator format(Custom, const std::basic_format_context<Out, CharT>& fc) const { return fc.out(); } }; template<template<typename...> typename Tuple> -void test_nonblocking() +CONSTEXPR void +test_nonblocking() { static_assert(std::enable_nonlocking_formatter_optimization< Tuple<int, float>>); @@ -374,7 +394,8 @@ void test_nonblocking() Tuple<Custom&, float&>>); } -int main() +CONSTEXPR bool +test_all() { test_format_string(); test_outputs<char>(); @@ -384,4 +405,15 @@ int main() test_nonblocking<std::pair>(); test_nonblocking<std::tuple>(); + + return true; +} + +#ifdef __glibcxx_constexpr_format +static_assert(test_all()); +#endif + +int main() +{ + test_all(); } diff --git a/libstdc++-v3/testsuite/std/time/format/data_not_present_neg.cc b/libstdc++-v3/testsuite/std/time/format/data_not_present_neg.cc index cb8f916f216..86f3b618599 100644 --- a/libstdc++-v3/testsuite/std/time/format/data_not_present_neg.cc +++ b/libstdc++-v3/testsuite/std/time/format/data_not_present_neg.cc @@ -161,4 +161,5 @@ auto si7 = std::format("{:%Q}", sys_info()); // { dg-error "call to consteval fu auto si8 = std::format("{:%Z}", sys_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } #endif -// { dg-error "call to non-'constexpr' function" "" { target *-*-* } 0 } +// { dg-error "call to non-'constexpr' function" "" { target c++23_down } 0 } +// { dg-error "'std::terminate' called after throwing an exception" "" { target { ! c++23_down } } 0 } -- 2.54.0
