diff --git a/stl/inc/chrono b/stl/inc/chrono index 458b9fbae4..578dff72e4 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5829,11 +5829,11 @@ namespace chrono { struct _Chrono_formatter { _Chrono_formatter() = default; - explicit _Chrono_formatter(const basic_string_view<_CharT> _Time_zone_abbreviation_) + constexpr explicit _Chrono_formatter(const basic_string_view<_CharT> _Time_zone_abbreviation_) : _Time_zone_abbreviation{_Time_zone_abbreviation_} {} template - _NODISCARD auto _Parse(basic_format_parse_context<_CharT>& _Parse_ctx) { + _NODISCARD constexpr auto _Parse(basic_format_parse_context<_CharT>& _Parse_ctx) { _Chrono_specs_setter<_CharT, basic_format_parse_context<_CharT>> _Callback{_Specs, _Parse_ctx}; const auto _It = _Parse_chrono_format_specs(_Parse_ctx._Unchecked_begin(), _Parse_ctx._Unchecked_end(), _Callback); @@ -5867,39 +5867,39 @@ namespace chrono { return _Res_iter; } - static void _Check_modifier(const char _Type, const char _Modifier) { - if (_Modifier == '\0') { - return; - } + enum _Allowed_bit : uint8_t { _E_mod = 1, _O_mod = 2, _EO_mod = _E_mod | _O_mod }; - enum _Allowed_bit : uint8_t { _E_mod = 1, _O_mod = 2, _EO_mod = _E_mod | _O_mod }; + struct _Table_entry { + char _Type; + _Allowed_bit _Allowed; + }; - struct _Table_entry { - char _Type; - _Allowed_bit _Allowed; - }; + static constexpr _Table_entry _Table[] = { + {'c', _E_mod}, + {'C', _E_mod}, + {'d', _O_mod}, + {'e', _O_mod}, + {'H', _O_mod}, + {'I', _O_mod}, + {'m', _O_mod}, + {'M', _O_mod}, + {'S', _O_mod}, + {'u', _O_mod}, + {'U', _O_mod}, + {'V', _O_mod}, + {'w', _O_mod}, + {'W', _O_mod}, + {'x', _E_mod}, + {'X', _E_mod}, + {'y', _EO_mod}, + {'Y', _E_mod}, + {'z', _EO_mod}, + }; - static constexpr _Table_entry _Table[] = { - {'c', _E_mod}, - {'C', _E_mod}, - {'d', _O_mod}, - {'e', _O_mod}, - {'H', _O_mod}, - {'I', _O_mod}, - {'m', _O_mod}, - {'M', _O_mod}, - {'S', _O_mod}, - {'u', _O_mod}, - {'U', _O_mod}, - {'V', _O_mod}, - {'w', _O_mod}, - {'W', _O_mod}, - {'x', _E_mod}, - {'X', _E_mod}, - {'y', _EO_mod}, - {'Y', _E_mod}, - {'z', _EO_mod}, - }; + static constexpr void _Check_modifier(const char _Type, const char _Modifier) { + if (_Modifier == '\0') { + return; + } const _Allowed_bit _Mod = _Modifier == 'E' ? _E_mod : _O_mod; @@ -6329,7 +6329,7 @@ namespace chrono { template struct _Fill_tm_formatter { - auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { + constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { return _Impl.template _Parse<_Ty>(_Parse_ctx); } @@ -6420,7 +6420,7 @@ struct formatter<_CHRONO local_info, _CharT> // template struct formatter<_CHRONO sys_time<_Duration>, _CharT> { - auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { + constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { return _Impl.template _Parse<_CHRONO sys_time<_Duration>>(_Parse_ctx); } @@ -6435,7 +6435,7 @@ private: template struct formatter<_CHRONO utc_time<_Duration>, _CharT> { - auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { + constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { return _Impl.template _Parse<_CHRONO utc_time<_Duration>>(_Parse_ctx); } @@ -6451,7 +6451,7 @@ private: template struct formatter<_CHRONO tai_time<_Duration>, _CharT> { - auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { + constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { return _Impl.template _Parse<_CHRONO tai_time<_Duration>>(_Parse_ctx); } @@ -6470,7 +6470,7 @@ private: template struct formatter<_CHRONO gps_time<_Duration>, _CharT> { - auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { + constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { return _Impl.template _Parse<_CHRONO gps_time<_Duration>>(_Parse_ctx); } @@ -6489,7 +6489,7 @@ private: template struct formatter<_CHRONO file_time<_Duration>, _CharT> { - auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { + constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { return _Impl.template _Parse<_CHRONO file_time<_Duration>>(_Parse_ctx); } diff --git a/stl/inc/format b/stl/inc/format index c0d803354a..fa018e3d4f 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -97,7 +97,7 @@ enum class _Basic_format_arg_type : uint8_t { static_assert(static_cast(_Basic_format_arg_type::_Custom_type) < 16, "must fit in 4-bit bitfield"); _NODISCARD constexpr bool _Is_integral_fmt_type(_Basic_format_arg_type _Ty) { - return _Ty > _Basic_format_arg_type::_None && _Ty <= _Basic_format_arg_type::_ULong_long_type; + return _Ty > _Basic_format_arg_type::_None && _Ty <= _Basic_format_arg_type::_Char_type; } _NODISCARD constexpr bool _Is_arithmetic_fmt_type(_Basic_format_arg_type _Ty) { return _Ty > _Basic_format_arg_type::_None && _Ty <= _Basic_format_arg_type::_Long_double_type; @@ -218,6 +218,12 @@ public: _THROW(format_error("Can not switch from manual to automatic indexing")); } + if (_STD is_constant_evaluated()) { + if (static_cast(_Next_arg_id) >= _Num_args) { + _You_see_this_error_because_arg_id_is_out_of_range(); + } + } + return static_cast(_Next_arg_id++); } @@ -538,7 +544,7 @@ private: return (_Len >= 4) ? 4 : -1; } - _NODISCARD static int _Estimate_utf8_character_width(const char* const _Ptr, const int _Units) noexcept { + _NODISCARD static constexpr int _Estimate_utf8_character_width(const char* const _Ptr, const int _Units) noexcept { // Return an estimate for the width of the character composed of _Units code units, // whose first code unit is denoted by _Ptr. auto _Ch = static_cast(*_Ptr); @@ -567,7 +573,8 @@ private: } public: - _NODISCARD int _Units_in_next_character(const char* const _First, const char* const _Last) const noexcept { + _NODISCARD constexpr int _Units_in_next_character( + const char* const _First, const char* const _Last) const noexcept { // Returns a count of the number of code units that compose the first encoded character in // [_First, _Last), or -1 if [_First, _Last) doesn't contain an entire encoded character or // *_First is not a valid lead byte. @@ -592,7 +599,7 @@ public: } } - _NODISCARD const char* _Find_encoded(const char* _First, const char* const _Last, const char _Val) const { + _NODISCARD constexpr const char* _Find_encoded(const char* _First, const char* const _Last, const char _Val) const { // Returns the first occurrence of _Val as an encoded character (and not, for example, as a // continuation byte) in [_First, _Last). if constexpr (_Statically_Utf8) { @@ -615,7 +622,7 @@ public: } } - _NODISCARD int _Estimate_width(const char* const _Ptr, const int _Units) const { + _NODISCARD constexpr int _Estimate_width(const char* const _Ptr, const int _Units) const { // Return an estimate for the width of the character composed of _Units code units, // whose first code unit is denoted by _Ptr. if constexpr (_Statically_Utf8) { @@ -635,7 +642,8 @@ public: template class _Fmt_codec { public: - _NODISCARD int _Units_in_next_character(const wchar_t* _First, const wchar_t* const _Last) const noexcept { + _NODISCARD constexpr int _Units_in_next_character( + const wchar_t* _First, const wchar_t* const _Last) const noexcept { // Returns a count of the number of code units that compose the first encoded character in // [_First, _Last), or -1 if [_First, _Last) doesn't contain an entire encoded character or // *_First is an unpaired surrogate. @@ -656,12 +664,12 @@ public: return 2; // surrogate pair } - _NODISCARD const wchar_t* _Find_encoded( + _NODISCARD constexpr const wchar_t* _Find_encoded( const wchar_t* const _First, const wchar_t* const _Last, const wchar_t _Val) const { return _Find_unchecked(_First, _Last, _Val); } - _NODISCARD int _Estimate_width(const wchar_t* const _Ptr, const int _Units) const { + _NODISCARD constexpr int _Estimate_width(const wchar_t* const _Ptr, const int _Units) const { // Return an estimate for the width of the character composed of _Units code units, // whose first code unit is denoted by _Ptr. auto _Ch = static_cast(*_Ptr); @@ -678,7 +686,8 @@ public: }; template _Callbacks_type> -_NODISCARD const _CharT* _Parse_align(const _CharT* _First, const _CharT* _Last, _Callbacks_type&& _Callbacks) { +_NODISCARD constexpr const _CharT* _Parse_align( + const _CharT* _First, const _CharT* _Last, _Callbacks_type&& _Callbacks) { // align and fill _STL_INTERNAL_CHECK(_First != _Last && *_First != '}'); auto _Parsed_align = _Fmt_align::_None; @@ -901,6 +910,11 @@ _NODISCARD constexpr const _CharT* _Parse_format_specs( if (*_First != '}') { _Callbacks._On_type(*_First); ++_First; + } else { + // call the type callback so it gets a default type, this is required + // since _Specs_checker needs to be able to tell that it got a default type + // to raise an error for default formatted bools with a sign modifier + _Callbacks._On_type(static_cast<_CharT>('\0')); } return _First; @@ -944,7 +958,7 @@ _NODISCARD constexpr const _CharT* _Parse_replacement_field( } template _HandlerT> -void _Parse_format_string(basic_string_view<_CharT> _Format_str, _HandlerT&& _Handler) { +constexpr void _Parse_format_string(basic_string_view<_CharT> _Format_str, _HandlerT&& _Handler) { auto _First = _Format_str.data(); auto _Last = _First + _Format_str.size(); const _Fmt_codec<_CharT> _Codec; @@ -1055,9 +1069,6 @@ public: } constexpr void _On_type(const _CharT _Type) { - if (_Type < 0 || _Type > (numeric_limits::max) ()) { - _THROW(format_error("Invalid type specification.")); - } _Specs._Type = static_cast(_Type); } @@ -1205,12 +1216,21 @@ private: } }; -class _Numeric_specs_checker { +// Checks that the type of the argument printed by a replacement +// field with format specs actually satisfies the requirements for +// that format spec. If the requirements are met then calls the base class +// handler method. +template +class _Specs_checker : public _Handler { private: - _Basic_format_arg_type _Arg_type = _Basic_format_arg_type::_None; + _Basic_format_arg_type _Arg_type; + // we'll see this if we get a modifier that requires an integer presentation type + // for types that can have either integer or non-integer presentation types (charT or bool) + bool _Need_arithmetic_presentation_type = false; public: - constexpr explicit _Numeric_specs_checker(_Basic_format_arg_type _Arg_type_) : _Arg_type(_Arg_type_) {} + constexpr explicit _Specs_checker(const _Handler& _Handler_inst, const _Basic_format_arg_type _Arg_type_) + : _Handler(_Handler_inst), _Arg_type(_Arg_type_) {} constexpr void _Require_numeric_argument() const { if (!_Is_arithmetic_fmt_type(_Arg_type)) { @@ -1218,56 +1238,168 @@ public: } } - constexpr void _Check_sign() const { - _Require_numeric_argument(); - if (_Is_integral_fmt_type(_Arg_type) && _Arg_type != _Basic_format_arg_type::_Int_type - && _Arg_type != _Basic_format_arg_type::_Long_long_type - && _Arg_type != _Basic_format_arg_type::_Char_type) { - _THROW(format_error("Format specifier requires signed argument.")); - } - } - constexpr void _Check_precision() const { if (_Is_integral_fmt_type(_Arg_type) || _Arg_type == _Basic_format_arg_type::_Pointer_type) { _THROW(format_error("Precision not allowed for this argument type.")); } } -}; - -// Uses _Numeric_specs_checker to check that the type of the argument printed by -// a replacement field with format specs actually satisfies the requirements for -// that format spec. If the requirements are met then calls the base class -// handler method. -template -class _Specs_checker : public _Handler { -private: - _Numeric_specs_checker _Numeric_checker; - -public: - constexpr explicit _Specs_checker(const _Handler& _Handler_inst, const _Basic_format_arg_type _Arg_type_) - : _Handler(_Handler_inst), _Numeric_checker(_Arg_type_) {} // _On_align has no checking, since we don't implement numeric alignments. + constexpr void _On_localized() { + _Require_numeric_argument(); + _Handler::_On_localized(); + } + constexpr void _On_hash() { - // Note that '#' is not valid for CharT or bool unless you - // pass a numeric presentation type, but we encounter '#' before - // the presentation type so we can not check that requirement here - _Numeric_checker._Require_numeric_argument(); + _Need_arithmetic_presentation_type = true; + _Require_numeric_argument(); _Handler::_On_hash(); } + constexpr void _On_sign(_Fmt_sign _Sgn) { + _Need_arithmetic_presentation_type = true; + _Require_numeric_argument(); + _Handler::_On_sign(_Sgn); + } + constexpr void _On_zero() { - // Note 0 is again not valid for CharT or bool unless a numeric - // presentation type is used. - _Numeric_checker._Require_numeric_argument(); + _Need_arithmetic_presentation_type = true; + _Require_numeric_argument(); _Handler::_On_zero(); } constexpr void _On_precision(int _Precision) { - _Numeric_checker._Check_precision(); + _Check_precision(); _Handler::_On_precision(_Precision); } + + template + constexpr void _On_dynamic_precision(const _Ty _Val) { + _Check_precision(); + _Handler::_On_dynamic_precision(_Val); + } + + template + constexpr void _On_type(_CharT _Type) { + if (_Type < 0 || _Type > (numeric_limits::max) ()) { + _THROW(format_error("Invalid type specification.")); + } + const char _Narrow_type = static_cast(_Type); + enum class _Presentation_type_category { _Default, _Integer, _Floating, _String, _Pointer, _Char }; + auto _Cat = _Presentation_type_category::_Default; + switch (_Narrow_type) { + case '\0': + break; + case 's': + _Cat = _Presentation_type_category::_String; + break; + case 'c': + _Cat = _Presentation_type_category::_Char; + break; + case 'd': + case 'B': + case 'b': + case 'X': + case 'x': + case 'o': + _Cat = _Presentation_type_category::_Integer; + break; + case 'A': + case 'a': + case 'E': + case 'e': + case 'F': + case 'f': + case 'G': + case 'g': + _Cat = _Presentation_type_category::_Floating; + break; + case 'p': + _Cat = _Presentation_type_category::_Pointer; + break; + default: + _THROW(format_error("Invalid presentation type specifier")); + } + + switch (_Arg_type) { + case _Basic_format_arg_type::_None: + _STL_INTERNAL_CHECK(!"Invalid argument type."); + break; + case _Basic_format_arg_type::_Bool_type: + if (_Cat == _Presentation_type_category::_Default) { + _Cat = _Presentation_type_category::_String; + } + // note, we don't get a call if there isn't a type, but none is valid for everything. + if (_Cat != _Presentation_type_category::_String && _Cat != _Presentation_type_category::_Integer + && _Cat != _Presentation_type_category::_Char) { + _THROW(format_error("Invalid presentation type for bool")); + } + break; + case _Basic_format_arg_type::_Char_type: + if (_Cat == _Presentation_type_category::_Default) { + _Cat = _Presentation_type_category::_Char; + } + + if (_Cat != _Presentation_type_category::_Char && _Cat != _Presentation_type_category::_Integer) { + _THROW(format_error("Invalid presentation type for char/wchar_t")); + } + break; + case _Basic_format_arg_type::_Int_type: + case _Basic_format_arg_type::_UInt_type: + case _Basic_format_arg_type::_Long_long_type: + case _Basic_format_arg_type::_ULong_long_type: + if (_Cat == _Presentation_type_category::_Default) { + _Cat = _Presentation_type_category::_Integer; + } + + if (_Cat != _Presentation_type_category::_Integer && _Cat != _Presentation_type_category::_Char) { + _THROW(format_error("Invalid presentation type for integer")); + } + break; + case _Basic_format_arg_type::_Float_type: + case _Basic_format_arg_type::_Double_type: + case _Basic_format_arg_type::_Long_double_type: + if (_Cat == _Presentation_type_category::_Default) { + _Cat = _Presentation_type_category::_Floating; + } + + if (_Cat != _Presentation_type_category::_Floating) { + _THROW(format_error("Invalid presentation type for floating-point")); + } + break; + case _Basic_format_arg_type::_CString_type: + case _Basic_format_arg_type::_String_type: + if (_Cat == _Presentation_type_category::_Default) { + _Cat = _Presentation_type_category::_String; + } + + if (_Cat != _Presentation_type_category::_String) { + _THROW(format_error("Invalid presentation type for string")); + } + break; + case _Basic_format_arg_type::_Pointer_type: + if (_Cat == _Presentation_type_category::_Default) { + _Cat = _Presentation_type_category::_Pointer; + } + + if (_Cat != _Presentation_type_category::_Pointer) { + _THROW(format_error("Invalid presentation type for pointer")); + } + break; + case _Basic_format_arg_type::_Custom_type: + // there's no checking we can do here for custom types + // (however if a custom type uses a standard formatter + // to do its spec parsing it should get the above checks) + break; + } + + if (_Need_arithmetic_presentation_type && _Cat != _Presentation_type_category::_Integer + && _Cat != _Presentation_type_category::_Floating) { + _THROW(format_error("Modifier requires an integer presentation type for bool")); + } + _Handler::_On_type(_Type); + } }; template @@ -1367,10 +1499,10 @@ private: static constexpr size_t _Num_args = sizeof...(_Args); static constexpr size_t _Storage_length = (_Traits::template _Storage_size<_Args> + ...); - // The actual storage representation: _Num_args offsets into _Storage, followed immediately by the untyped _Storage - // which holds copies of the object representations of arguments (with no regard for alignment). These must be - // allocated consecutively, since basic_format_args thinks it can store a pointer to _Index_array and use arithmetic - // to access the bytes of _Storage. + // The actual storage representation: _Num_args offsets into _Storage, followed immediately by the untyped + // _Storage which holds copies of the object representations of arguments (with no regard for alignment). + // These must be allocated consecutively, since basic_format_args thinks it can store a pointer to + // _Index_array and use arithmetic to access the bytes of _Storage. _Format_arg_index _Index_array[_Num_args]; unsigned char _Storage[_Storage_length]; @@ -2101,9 +2233,7 @@ _NODISCARD _OutputIt _Write_integral( return _Fmt_write(_STD move(_Out), static_cast<_CharT>(_Value), _Specs, _Locale); } - if (_Specs._Precision != -1) { - _THROW(format_error("integral cannot have a precision")); - } + _STL_INTERNAL_CHECK(_Specs._Precision == -1); if (_Specs._Sgn == _Fmt_sign::_None) { _Specs._Sgn = _Fmt_sign::_Minus; @@ -2113,9 +2243,6 @@ _NODISCARD _OutputIt _Write_integral( bool _To_upper = false; switch (_Specs._Type) { - case '\0': - case 'd': - break; case 'B': _To_upper = true; [[fallthrough]]; @@ -2131,8 +2258,6 @@ _NODISCARD _OutputIt _Write_integral( case 'o': _Base = 8; break; - default: - _THROW(format_error("invalid integral type")); } // long long -1 representation in binary is 64 bits + sign @@ -2213,9 +2338,7 @@ _NODISCARD _OutputIt _Fmt_write( return _Write_integral(_STD move(_Out), static_cast(_Value), _Specs, _Locale); } - if (_Specs._Precision != -1) { - _THROW(format_error("bool cannot have a precision")); - } + _STL_INTERNAL_CHECK(_Specs._Precision == -1); if (_Specs._Localized) { _Specs._Localized = false; @@ -2240,9 +2363,7 @@ _NODISCARD _OutputIt _Fmt_write( return _Write_integral(_STD move(_Out), _Value, _Specs, _Locale); } - if (_Specs._Precision != -1) { - _THROW(format_error("charT cannot have a precision")); - } + _STL_INTERNAL_CHECK(_Specs._Precision == -1); // Clear the type so that the string_view writer doesn't fail on 'c'. _Specs._Type = '\0'; @@ -2265,8 +2386,6 @@ _NODISCARD _OutputIt _Fmt_write( auto _Precision = _Specs._Precision; switch (_Specs._Type) { - case '\0': - break; case 'A': _To_upper = true; [[fallthrough]]; @@ -2303,8 +2422,6 @@ _NODISCARD _OutputIt _Fmt_write( _Format = chars_format::general; _Exponent = 'e'; break; - default: - _THROW(format_error("invalid floating point type")); } // Consider the powers of 2 in decimal: @@ -2319,9 +2436,9 @@ _NODISCARD _OutputIt _Fmt_write( // Therefore, the min subnormal 2^-1074 consumes 1074 digits of precision (digits after the decimal point). // We need 3 more characters for a potential negative sign, the zero integer part, and the decimal point. // Therefore, the precision can be clamped to 1074. - // The largest number consumes 309 digits before the decimal point. With a precision of 1074, and it being negative, - // it would use a buffer of size 1074+309+2. - // We need to add an additional number to the max exponent to accommodate the ones place. + // The largest number consumes 309 digits before the decimal point. With a precision of 1074, and it being + // negative, it would use a buffer of size 1074+309+2. We need to add an additional number to the max + // exponent to accommodate the ones place. constexpr auto _Max_precision = 1074; constexpr auto _Buffer_size = _Max_precision + DBL_MAX_10_EXP + 3; char _Buffer[_Buffer_size]; @@ -2480,29 +2597,12 @@ _NODISCARD _OutputIt _Fmt_write( template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, const void* const _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale) { - if (_Specs._Type != '\0' && _Specs._Type != 'p') { - _THROW(format_error("invalid const void* type")); - } - - if (_Specs._Sgn != _Fmt_sign::_None) { - _THROW(format_error("const void* cannot have a sign")); - } - - if (_Specs._Alt) { - _THROW(format_error("const void* cannot have an alternative representation")); - } - - if (_Specs._Precision != -1) { - _THROW(format_error("const void* cannot have a precision")); - } - - if (_Specs._Leading_zero) { - _THROW(format_error("const void* cannot have a leading zero")); - } - - if (_Specs._Localized) { - _THROW(format_error("const void* cannot be localized")); - } + _STL_INTERNAL_CHECK(_Specs._Type == '\0' || _Specs._Type == 'p'); + _STL_INTERNAL_CHECK(_Specs._Sgn == _Fmt_sign::_None); + _STL_INTERNAL_CHECK(!_Specs._Alt); + _STL_INTERNAL_CHECK(_Specs._Precision == -1); + _STL_INTERNAL_CHECK(!_Specs._Leading_zero); + _STL_INTERNAL_CHECK(!_Specs._Localized); // Since the bit width of 0 is 0x0, special-case it instead of complicating the math even more. int _Width = 3; @@ -2571,25 +2671,11 @@ _NODISCARD const _CharT* _Measure_string_prefix(const basic_string_view<_CharT> template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, const basic_string_view<_CharT> _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale) { - if (_Specs._Type != '\0' && _Specs._Type != 's') { - _THROW(format_error("invalid string type")); - } - - if (_Specs._Sgn != _Fmt_sign::_None) { - _THROW(format_error("string cannot have a sign")); - } - - if (_Specs._Alt) { - _THROW(format_error("string cannot have an alternative representation")); - } - - if (_Specs._Leading_zero) { - _THROW(format_error("string cannot have a leading zero")); - } - - if (_Specs._Localized) { - _THROW(format_error("string cannot be localized")); - } + _STL_INTERNAL_CHECK(_Specs._Type == '\0' || _Specs._Type == 's'); + _STL_INTERNAL_CHECK(_Specs._Sgn == _Fmt_sign::_None); + _STL_INTERNAL_CHECK(!_Specs._Alt); + _STL_INTERNAL_CHECK(!_Specs._Leading_zero); + _STL_INTERNAL_CHECK(!_Specs._Localized); if (_Specs._Precision < 0 && _Specs._Width <= 0) { return _Fmt_write(_STD move(_Out), _Value); @@ -2650,6 +2736,50 @@ struct _Arg_formatter { } }; +// Special compile time version of _Parse_format_specs. This version is parameterized on +// the type of the argument associated with the format specifier, since we don't really +// care about avoiding code bloat for code that never runs at runtime, and we can't form +// the erased basic_format_args structure at compile time. +template +consteval typename _ParseContext::iterator _Compile_time_parse_format_specs(_ParseContext& _Pc) { + using _CharT = typename _ParseContext::char_type; + using _Context = basic_format_context>, _CharT>; + using _ArgTraits = _Format_arg_traits<_Context>; + using _FormattedTypeMapping = typename _ArgTraits::template _Storage_type<_Ty>; + // If the type is going to use a custom formatter we should just use that, + // instead of trying to instantiate a custom formatter for its erased handle + // type + using _FormattedType = conditional_t::handle>, + _Ty, _FormattedTypeMapping>; + formatter<_FormattedType, _CharT> _Formatter{}; + return _Formatter.parse(_Pc); +} + +// set of format parsing actions that only checks for validity +template +struct _Format_checker { + using _ParseContext = basic_format_parse_context<_CharT>; + using _ParseFunc = typename _ParseContext::iterator (*)(_ParseContext&); + + static constexpr size_t _Num_args = sizeof...(_Args); + _ParseContext _Parse_context; + _ParseFunc _Parse_funcs[_Num_args > 0 ? _Num_args : 1]; + + consteval explicit _Format_checker(basic_string_view<_CharT> _Fmt) noexcept + : _Parse_context(_Fmt, _Num_args), _Parse_funcs{&_Compile_time_parse_format_specs<_Args, _ParseContext>...} {} + constexpr void _On_text(const _CharT*, const _CharT*) const noexcept {} + constexpr void _On_replacement_field(size_t, const _CharT*) const noexcept {} + constexpr const _CharT* _On_format_specs(const size_t _Id, const _CharT* _First, const _CharT*) { + _Parse_context.advance_to(_Parse_context.begin() + (_First - _Parse_context.begin()._Unwrapped())); + if (_Id < _Num_args) { + auto _Iter = _Parse_funcs[_Id](_Parse_context); // TRANSITION, VSO-1451773 (workaround: named variable) + return _Iter._Unwrapped(); + } else { + return _First; + } + } +}; + // The top level set of parsing "actions". template struct _Format_handler { @@ -2712,7 +2842,7 @@ template struct _Formatter_base { using _Pc = basic_format_parse_context<_CharT>; - typename _Pc::iterator parse(_Pc& _ParseCtx) { + constexpr typename _Pc::iterator parse(_Pc& _ParseCtx) { _Specs_checker<_Dynamic_specs_handler<_Pc>> _Handler(_Dynamic_specs_handler<_Pc>{_Specs, _ParseCtx}, _ArgType); const auto _It = _Parse_format_specs(_ParseCtx._Unchecked_begin(), _ParseCtx._Unchecked_end(), _Handler); if (_It != _ParseCtx._Unchecked_end() && *_It != '}') { @@ -2772,8 +2902,8 @@ _FORMAT_SPECIALIZE_FOR(unsigned char, _Basic_format_arg_type::_UInt_type); template <> struct formatter : _Formatter_base {}; -// We could use the macro for these specializations, but it's confusing to refer to symbols that are defined inside the -// macro in the macro's "call". +// We could use the macro for these specializations, but it's confusing to refer to symbols that are defined +// inside the macro in the macro's "call". template <_Format_supported_charT _CharT> struct formatter<_CharT*, _CharT> : _Formatter_base<_CharT*, _CharT, _Basic_format_arg_type::_CString_type> {}; @@ -2793,6 +2923,25 @@ template <_Format_supported_charT _CharT, class _Traits> struct formatter, _CharT> : _Formatter_base, _CharT, _Basic_format_arg_type::_String_type> {}; +template +struct _Basic_format_string { + basic_string_view<_CharT> _Str; + + template + requires convertible_to> + consteval _Basic_format_string(const _Ty& _Str_val) : _Str(_Str_val) { + if (_Is_execution_charset_utf8()) { + _Parse_format_string(_Str, _Format_checker<_CharT, remove_cvref_t<_Args>...>{_Str}); + } + } +}; + +template +using _Fmt_string = _Basic_format_string...>; + +template +using _Fmt_wstring = _Basic_format_string...>; + using format_args = basic_format_args; using wformat_args = basic_format_args; @@ -2863,23 +3012,23 @@ _OutputIt vformat_to(_OutputIt _Out, const locale& _Loc, const wstring_view _Fmt } template _OutputIt, class... _Types> -_OutputIt format_to(_OutputIt _Out, const string_view _Fmt, const _Types&... _Args) { - return _STD vformat_to(_STD move(_Out), _Fmt, _STD make_format_args(_Args...)); +_OutputIt format_to(_OutputIt _Out, const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { + return _STD vformat_to(_STD move(_Out), _Fmt._Str, _STD make_format_args(_Args...)); } template _OutputIt, class... _Types> -_OutputIt format_to(_OutputIt _Out, const wstring_view _Fmt, const _Types&... _Args) { - return _STD vformat_to(_STD move(_Out), _Fmt, _STD make_wformat_args(_Args...)); +_OutputIt format_to(_OutputIt _Out, const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { + return _STD vformat_to(_STD move(_Out), _Fmt._Str, _STD make_wformat_args(_Args...)); } template _OutputIt, class... _Types> -_OutputIt format_to(_OutputIt _Out, const locale& _Loc, const string_view _Fmt, const _Types&... _Args) { - return _STD vformat_to(_STD move(_Out), _Loc, _Fmt, _STD make_format_args(_Args...)); +_OutputIt format_to(_OutputIt _Out, const locale& _Loc, const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { + return _STD vformat_to(_STD move(_Out), _Loc, _Fmt._Str, _STD make_format_args(_Args...)); } template _OutputIt, class... _Types> -_OutputIt format_to(_OutputIt _Out, const locale& _Loc, const wstring_view _Fmt, const _Types&... _Args) { - return _STD vformat_to(_STD move(_Out), _Loc, _Fmt, _STD make_wformat_args(_Args...)); +_OutputIt format_to(_OutputIt _Out, const locale& _Loc, const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { + return _STD vformat_to(_STD move(_Out), _Loc, _Fmt._Str, _STD make_wformat_args(_Args...)); } #if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1433873 @@ -2925,23 +3074,23 @@ wstring vformat(const locale& _Loc, const wstring_view _Fmt, const wformat_args #undef _TEMPLATE_INT_0_NODISCARD // TRANSITION, VSO-1433873 template -_NODISCARD string format(const string_view _Fmt, const _Types&... _Args) { - return _STD vformat(_Fmt, _STD make_format_args(_Args...)); +_NODISCARD string format(const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { + return _STD vformat(_Fmt._Str, _STD make_format_args(_Args...)); } template -_NODISCARD wstring format(const wstring_view _Fmt, const _Types&... _Args) { - return _STD vformat(_Fmt, _STD make_wformat_args(_Args...)); +_NODISCARD wstring format(const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { + return _STD vformat(_Fmt._Str, _STD make_wformat_args(_Args...)); } template -_NODISCARD string format(const locale& _Loc, const string_view _Fmt, const _Types&... _Args) { - return _STD vformat(_Loc, _Fmt, _STD make_format_args(_Args...)); +_NODISCARD string format(const locale& _Loc, const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { + return _STD vformat(_Loc, _Fmt._Str, _STD make_format_args(_Args...)); } template -_NODISCARD wstring format(const locale& _Loc, const wstring_view _Fmt, const _Types&... _Args) { - return _STD vformat(_Loc, _Fmt, _STD make_wformat_args(_Args...)); +_NODISCARD wstring format(const locale& _Loc, const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { + return _STD vformat(_Loc, _Fmt._Str, _STD make_wformat_args(_Args...)); } template @@ -2951,62 +3100,62 @@ struct format_to_n_result { }; template _OutputIt, class... _Types> -format_to_n_result<_OutputIt> format_to_n( - _OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const string_view _Fmt, const _Types&... _Args) { +format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, + const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { _Fmt_iterator_buffer<_OutputIt, char, _Fmt_fixed_buffer_traits> _Buf(_STD move(_Out), _Max); - _STD vformat_to(_Fmt_it{_Buf}, _Fmt, _STD make_format_args(_Args...)); + _STD vformat_to(_Fmt_it{_Buf}, _Fmt._Str, _STD make_format_args(_Args...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; } template _OutputIt, class... _Types> -format_to_n_result<_OutputIt> format_to_n( - _OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const wstring_view _Fmt, const _Types&... _Args) { +format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, + const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { _Fmt_iterator_buffer<_OutputIt, wchar_t, _Fmt_fixed_buffer_traits> _Buf(_STD move(_Out), _Max); - _STD vformat_to(_Fmt_wit{_Buf}, _Fmt, _STD make_wformat_args(_Args...)); + _STD vformat_to(_Fmt_wit{_Buf}, _Fmt._Str, _STD make_wformat_args(_Args...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; } template _OutputIt, class... _Types> format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const locale& _Loc, - const string_view _Fmt, const _Types&... _Args) { + const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { _Fmt_iterator_buffer<_OutputIt, char, _Fmt_fixed_buffer_traits> _Buf(_STD move(_Out), _Max); - _STD vformat_to(_Fmt_it{_Buf}, _Loc, _Fmt, _STD make_format_args(_Args...)); + _STD vformat_to(_Fmt_it{_Buf}, _Loc, _Fmt._Str, _STD make_format_args(_Args...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; } template _OutputIt, class... _Types> format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const locale& _Loc, - const wstring_view _Fmt, const _Types&... _Args) { + const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { _Fmt_iterator_buffer<_OutputIt, wchar_t, _Fmt_fixed_buffer_traits> _Buf(_STD move(_Out), _Max); - _STD vformat_to(_Fmt_wit{_Buf}, _Loc, _Fmt, _STD make_wformat_args(_Args...)); + _STD vformat_to(_Fmt_wit{_Buf}, _Loc, _Fmt._Str, _STD make_wformat_args(_Args...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; } template -_NODISCARD size_t formatted_size(const string_view _Fmt, const _Types&... _Args) { +_NODISCARD size_t formatted_size(const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { _Fmt_counting_buffer _Buf; - _STD vformat_to(_Fmt_it{_Buf}, _Fmt, _STD make_format_args(_Args...)); + _STD vformat_to(_Fmt_it{_Buf}, _Fmt._Str, _STD make_format_args(_Args...)); return _Buf._Count(); } template -_NODISCARD size_t formatted_size(const wstring_view _Fmt, const _Types&... _Args) { +_NODISCARD size_t formatted_size(const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { _Fmt_counting_buffer _Buf; - _STD vformat_to(_Fmt_wit{_Buf}, _Fmt, _STD make_wformat_args(_Args...)); + _STD vformat_to(_Fmt_wit{_Buf}, _Fmt._Str, _STD make_wformat_args(_Args...)); return _Buf._Count(); } template -_NODISCARD size_t formatted_size(const locale& _Loc, const string_view _Fmt, const _Types&... _Args) { +_NODISCARD size_t formatted_size(const locale& _Loc, const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { _Fmt_counting_buffer _Buf; - _STD vformat_to(_Fmt_it{_Buf}, _Loc, _Fmt, _STD make_format_args(_Args...)); + _STD vformat_to(_Fmt_it{_Buf}, _Loc, _Fmt._Str, _STD make_format_args(_Args...)); return _Buf._Count(); } template -_NODISCARD size_t formatted_size(const locale& _Loc, const wstring_view _Fmt, const _Types&... _Args) { +_NODISCARD size_t formatted_size(const locale& _Loc, const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { _Fmt_counting_buffer _Buf; - _STD vformat_to(_Fmt_wit{_Buf}, _Loc, _Fmt, _STD make_wformat_args(_Args...)); + _STD vformat_to(_Fmt_wit{_Buf}, _Loc, _Fmt._Str, _STD make_wformat_args(_Args...)); return _Buf._Count(); } diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 037be9c5a9..96953bdf69 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -256,6 +256,7 @@ // P2106R0 Range Algorithm Result Types // P2116R0 Removing tuple-Like Protocol Support From Fixed-Extent span // P2210R2 Superior String Splitting +// P2216R3 std::format Improvements // P2231R1 Completing constexpr In optional And variant // P2251R1 Require span And basic_string_view To Be Trivially Copyable // (span always provides this behavior) @@ -1269,7 +1270,7 @@ #define __cpp_lib_erase_if 202002L #if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 and GH-1814 -#define __cpp_lib_format 201907L +#define __cpp_lib_format 202106L // P2216R3 std::format Improvements #endif // _HAS_CXX23 && defined(__cpp_lib_concepts) #define __cpp_lib_generic_unordered_lookup 201811L diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index b95db5c0ee..14bcc1ad36 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -637,6 +637,9 @@ std/language.support/support.limits/support.limits.general/iterator.version.pass # Test expects __cpp_lib_chrono to have the old value 201611L for P0505R0; we define the C++20 value 201907L for P1466R3. std/language.support/support.limits/support.limits.general/chrono.version.pass.cpp FAIL +# Test expects __cpp_lib_format to have the old value 201907L for P0645R10; we define the C++20 value 202106L for P2216R3. +std/language.support/support.limits/support.limits.general/format.version.pass.cpp FAIL + # We unconditionally define __cpp_lib_addressof_constexpr; test error says it # "should not be defined when TEST_HAS_BUILTIN(__builtin_addressof) || TEST_GCC_VER >= 700 is not defined!" std/language.support/support.limits/support.limits.general/memory.version.pass.cpp FAIL diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index def794acb7..f41eed917a 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -637,6 +637,9 @@ language.support\support.limits\support.limits.general\iterator.version.pass.cpp # Test expects __cpp_lib_chrono to have the old value 201611L for P0505R0; we define the C++20 value 201907L for P1466R3. language.support\support.limits\support.limits.general\chrono.version.pass.cpp +# Test expects __cpp_lib_format to have the old value 201907L for P0645R10; we define the C++20 value 202106L for P2216R3. +language.support\support.limits\support.limits.general\format.version.pass.cpp + # We unconditionally define __cpp_lib_addressof_constexpr; test error says it # "should not be defined when TEST_HAS_BUILTIN(__builtin_addressof) || TEST_GCC_VER >= 700 is not defined!" language.support\support.limits\support.limits.general\memory.version.pass.cpp diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp index 0aa24f69ee..500b2b0b71 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp @@ -207,10 +207,19 @@ bool test_parse_chrono_format_specs() { return true; } +template +auto make_testing_format_args(Args&&... vals) { + if constexpr (is_same_v) { + return make_wformat_args(forward(vals)...); + } else { + return make_format_args(forward(vals)...); + } +} + template void throw_helper(const basic_string_view fmt, const Args&... vals) { try { - (void) format(fmt, vals...); + (void) vformat(fmt, make_testing_format_args(vals...)); assert(false); } catch (const format_error&) { } @@ -334,15 +343,15 @@ void test_day_formatter() { using view_typ = basic_string_view; using str_typ = basic_string; - view_typ s0(STR("{:%d}")); - view_typ s1(STR("{:%e}")); - view_typ s2(STR("{:%Od}")); - view_typ s3(STR("{:%Oe}")); - view_typ s4(STR("{}")); - view_typ s5(STR("{:=>8}")); - view_typ s6(STR("{:lit}")); - view_typ s7(STR("{:%d days}")); - view_typ s8(STR("{:*^6%dmm}")); + constexpr view_typ s0(STR("{:%d}")); + constexpr view_typ s1(STR("{:%e}")); + constexpr view_typ s2(STR("{:%Od}")); + constexpr view_typ s3(STR("{:%Oe}")); + constexpr view_typ s4(STR("{}")); + constexpr view_typ s5(STR("{:=>8}")); + constexpr view_typ s6(STR("{:lit}")); + constexpr view_typ s7(STR("{:%d days}")); + constexpr view_typ s8(STR("{:*^6%dmm}")); str_typ a0(STR("27")); str_typ a1(STR("05")); diff --git a/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp index a087e83d50..02dd3d9354 100644 --- a/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp @@ -10,6 +10,16 @@ #include using namespace std; +// copied from the text_formatting_formatting test case +template +auto make_testing_format_args(Args&&... vals) { + if constexpr (is_same_v) { + return make_wformat_args(forward(vals)...); + } else { + return make_format_args(forward(vals)...); + } +} + // copied from the string_view tests template struct choose_literal; // not defined @@ -74,12 +84,14 @@ constexpr void test_disabled_formatter_is_disabled() { template void test_custom_equiv_with_format(const charT* fmt_string, const T& val) { - assert(format(fmt_string, custom_formattable_type{val}) == format(fmt_string, val)); + assert(vformat(fmt_string, make_testing_format_args(custom_formattable_type{val})) + == vformat(fmt_string, make_testing_format_args(val))); } template void test_custom_equiv_with_format_mixed(const charT* fmt_string, const T& val) { - assert(format(fmt_string, custom_formattable_type{val}, val) == format(fmt_string, val, val)); + assert(vformat(fmt_string, make_testing_format_args(custom_formattable_type{val}, val)) + == vformat(fmt_string, make_testing_format_args(val, val))); } template diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp index e6c4939de4..a2d8a121fa 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -58,7 +58,7 @@ auto make_testing_format_args(Args&&... vals) { template void throw_helper(const charT* fmt, const Args&... vals) { try { - (void) format(fmt, vals...); + (void) vformat(fmt, make_testing_format_args(vals...)); assert(false); } catch (const format_error&) { } @@ -500,7 +500,7 @@ void test_integral_specs() { // Alternate form assert(format(STR("{:#}"), Integral{0}) == STR("0")); assert(format(STR("{:#d}"), Integral{0}) == STR("0")); - assert(format(STR("{:#c}"), Integral{'a'}) == STR("a")); + throw_helper(STR("{:#c}"), Integral{'a'}); assert(format(STR("{:#b}"), Integral{0}) == STR("0b0")); assert(format(STR("{:#B}"), Integral{0}) == STR("0B0")); @@ -574,7 +574,8 @@ void test_integral_specs() { template void test_type(const charT* fmt, T val) { - assert(format(fmt, val) == format(fmt, static_cast(val))); + assert(vformat(fmt, make_testing_format_args(val)) + == vformat(fmt, make_testing_format_args(static_cast(val)))); } template @@ -589,6 +590,7 @@ void test_bool_specs() { // Alternate form throw_helper(STR("{:#}"), true); + throw_helper(STR("{:#c}"), true); // Leading zero throw_helper(STR("{:0}"), true); @@ -665,6 +667,7 @@ void test_char_specs() { // Precision throw_helper(STR("{:.5}"), charT{'X'}); + // Types assert(format(STR("{:c}"), charT{'X'}) == STR("X")); throw_helper(STR("{:a}"), charT{'X'}); @@ -685,6 +688,15 @@ void test_float_specs() { const Float inf = numeric_limits::infinity(); const Float nan = numeric_limits::quiet_NaN(); + // invalid specs + throw_helper(STR("{:b}"), 3.14f); + throw_helper(STR("{:B}"), 3.14f); + throw_helper(STR("{:c}"), 3.14f); + throw_helper(STR("{:d}"), 3.14f); + throw_helper(STR("{:o}"), 3.14f); + throw_helper(STR("{:x}"), 3.14f); + throw_helper(STR("{:X}"), 3.14f); + assert(format(STR("{:}"), Float{0}) == STR("0")); assert(format(STR("{:}"), inf) == STR("inf")); assert(format(STR("{:}"), nan) == STR("nan")); @@ -979,9 +991,9 @@ void test_spec_replacement_field() { test_pointer_specs(); test_string_specs(); } - template -void test_size_helper(const size_t expected_size, const basic_string_view fmt, const Args&... args) { +void test_size_helper_impl( + const size_t expected_size, const _Basic_format_string fmt, const Args&... args) { assert(formatted_size(fmt, args...) == expected_size); assert(formatted_size(locale::classic(), fmt, args...) == expected_size); @@ -993,7 +1005,7 @@ void test_size_helper(const size_t expected_size, const basic_string_view assert(res.size == signed_size); assert(res.out - str.begin() == signed_size); assert(res.out == str.end()); - assert(format(fmt, args...) == str); + assert(vformat(fmt._Str, make_testing_format_args(args...)) == str); basic_string locale_str; locale_str.resize(expected_size); @@ -1013,11 +1025,21 @@ void test_size_helper(const size_t expected_size, const basic_string_view assert(str.starts_with(half_str)); } +template +void test_size_helper(const size_t expected_size, const _Fmt_string fmt, Args&&... args) { + test_size_helper_impl(expected_size, fmt, forward(args)...); +} +template +void test_size_helper(const size_t expected_size, const _Fmt_wstring fmt, Args&&... args) { + test_size_helper_impl(expected_size, fmt, forward(args)...); +} + + template void test_size() { - test_size_helper(3, STR("{}"), 123); - test_size_helper(6, STR("{}"), 3.1415); - test_size_helper(8, STR("{:8}"), STR("scully")); + test_size_helper(3, STR("{}"), 123); + test_size_helper(6, STR("{}"), 3.1415); + test_size_helper(8, STR("{:8}"), STR("scully")); } // The libfmt_ tests are derived from tests in @@ -1344,6 +1366,29 @@ void test_slow_append_path() { assert(str == hello_world); } +template +void test_sane_c_specifier() { + throw_helper(STR("{:#}"), true); + throw_helper(STR("{:#c}"), true); + throw_helper(STR("{:+}"), true); + throw_helper(STR("{:+c}"), true); + assert(format(STR("{:^}"), true) == STR("true")); + assert(format(STR("{:^c}"), true) == STR("\x1")); + throw_helper(STR("{:0}"), true); + throw_helper(STR("{:0c}"), true); + assert(format(STR("{:c}"), true) == STR("\x1")); + + throw_helper(STR("{:#}"), 'c'); + throw_helper(STR("{:#c}"), 'c'); + throw_helper(STR("{:+}"), 'c'); + throw_helper(STR("{:+c}"), 'c'); + assert(format(STR("{:^}"), 'c') == STR("c")); + assert(format(STR("{:^c}"), 'c') == STR("c")); + throw_helper(STR("{:0}"), 'c'); + throw_helper(STR("{:0c}"), 'c'); + assert(format(STR("{:c}"), 'c') == STR("c")); +} + void test() { test_simple_formatting(); test_simple_formatting(); @@ -1413,6 +1458,9 @@ void test() { test_slow_append_path(); test_slow_append_path(); + + test_sane_c_specifier(); + test_sane_c_specifier(); } int main() { diff --git a/tests/std/tests/P0645R10_text_formatting_legacy_text_encoding/test.cpp b/tests/std/tests/P0645R10_text_formatting_legacy_text_encoding/test.cpp index 081eecd7ce..d09c0a4aaf 100644 --- a/tests/std/tests/P0645R10_text_formatting_legacy_text_encoding/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_legacy_text_encoding/test.cpp @@ -14,7 +14,7 @@ using namespace std; void test_multibyte_format_strings() { const auto s = "\x93\xfa\x96{\x92\x6e\x90}"sv; // Note the use of `{` and `}` as continuation bytes (from GH-1576) - assert(format(s) == s); + assert(format("\x93\xfa\x96{\x92\x6e\x90}"sv) == s); assert(format("{:.2}", s) == "\x93\xfa"sv); assert(format("{:4.2}", s) == "\x93\xfa "sv); diff --git a/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp b/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp index 1f62d3529d..c587832f85 100644 --- a/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp @@ -60,7 +60,7 @@ constexpr bool test_basic_format_parse_context() { } { // arg_id - basic_format_parse_context context{format_string}; + basic_format_parse_context context{format_string, 10}; const same_as auto first_arg_id = context.next_arg_id(); assert(first_arg_id == 0); diff --git a/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp b/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp index 08a70afaac..1c1d43370f 100644 --- a/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp @@ -22,7 +22,8 @@ void test_multibyte_format_strings() { { try { - (void) format("{:\x9f\x8f\x88<10}"sv, 42); // Bad fill character encoding: missing lead byte before \x9f + // Bad fill character encoding: missing lead byte before \x9f + (void) vformat("{:\x9f\x8f\x88<10}"sv, make_format_args(42)); assert(false); } catch (const format_error&) { } diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 00ae965fee..b7af4383c5 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -725,10 +725,10 @@ STATIC_ASSERT(__cpp_lib_filesystem == 201703L); #if _HAS_CXX23 && !defined(__EDG__) // TRANSITION, EDG concepts support and GH-1814 #ifndef __cpp_lib_format #error __cpp_lib_format is not defined -#elif __cpp_lib_format != 201907L -#error __cpp_lib_format is not 201907L +#elif __cpp_lib_format != 202106L +#error __cpp_lib_format is not 202106L #else -STATIC_ASSERT(__cpp_lib_format == 201907L); +STATIC_ASSERT(__cpp_lib_format == 202106L); #endif #else #ifdef __cpp_lib_format