From ed3b1e273f954a0916ec57b10087a95a3e35499e Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Thu, 19 Aug 2021 18:52:37 -0700 Subject: [PATCH 01/24] modify build files to use interna compiler --- CMakeLists.txt | 5 +++-- stl/inc/format | 46 ++++++++++++++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8999f1b925..d8a0b9fa23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,9 +75,10 @@ set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "") get_filename_component(TOOLSET_BINARIES_DIR "${CMAKE_CXX_COMPILER}" DIRECTORY) # Example: $\VC\Tools\MSVC\14.23.27931\bin\Hostx86\x86 get_filename_component(TOOLSET_ROOT_DIR "${TOOLSET_BINARIES_DIR}" DIRECTORY) # $\VC\Tools\MSVC\14.23.27931\bin\Hostx86 get_filename_component(TOOLSET_ROOT_DIR "${TOOLSET_ROOT_DIR}" DIRECTORY) # $\VC\Tools\MSVC\14.23.27931\bin -get_filename_component(TOOLSET_ROOT_DIR "${TOOLSET_ROOT_DIR}" DIRECTORY) # $\VC\Tools\MSVC\14.23.27931 +#get_filename_component(TOOLSET_ROOT_DIR "${TOOLSET_ROOT_DIR}" DIRECTORY) # $\VC\Tools\MSVC\14.23.27931 -set(TOOLSET_LIB "${TOOLSET_ROOT_DIR}/lib/${VCLIBS_X86_OR_X64}") +#set(TOOLSET_LIB "${TOOLSET_ROOT_DIR}/lib/${VCLIBS_X86_OR_X64}") +set(TOOLSET_LIB "${TOOLSET_ROOT_DIR}/lib/${VCLIBS_I386_OR_AMD64}") add_subdirectory(boost-math) add_subdirectory(stl) diff --git a/stl/inc/format b/stl/inc/format index c0d803354a..ba95e84ada 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2793,6 +2793,20 @@ 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 > _Ty> + consteval _Basic_format_string(const _Ty& _Str_val) : _Str(_Str_val) {} +}; + +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 +2877,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 +2939,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 From 7d1b09699f0153fa17f771efc3c516c567feeece Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Tue, 24 Aug 2021 14:58:08 -0700 Subject: [PATCH 02/24] tests now pass. --- CMakeLists.txt | 5 ++--- .../test.cpp | 16 ++++++++++++++-- .../P0645R10_text_formatting_formatting/test.cpp | 7 ++++--- .../test.cpp | 2 +- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d8a0b9fa23..8999f1b925 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,10 +75,9 @@ set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "") get_filename_component(TOOLSET_BINARIES_DIR "${CMAKE_CXX_COMPILER}" DIRECTORY) # Example: $\VC\Tools\MSVC\14.23.27931\bin\Hostx86\x86 get_filename_component(TOOLSET_ROOT_DIR "${TOOLSET_BINARIES_DIR}" DIRECTORY) # $\VC\Tools\MSVC\14.23.27931\bin\Hostx86 get_filename_component(TOOLSET_ROOT_DIR "${TOOLSET_ROOT_DIR}" DIRECTORY) # $\VC\Tools\MSVC\14.23.27931\bin -#get_filename_component(TOOLSET_ROOT_DIR "${TOOLSET_ROOT_DIR}" DIRECTORY) # $\VC\Tools\MSVC\14.23.27931 +get_filename_component(TOOLSET_ROOT_DIR "${TOOLSET_ROOT_DIR}" DIRECTORY) # $\VC\Tools\MSVC\14.23.27931 -#set(TOOLSET_LIB "${TOOLSET_ROOT_DIR}/lib/${VCLIBS_X86_OR_X64}") -set(TOOLSET_LIB "${TOOLSET_ROOT_DIR}/lib/${VCLIBS_I386_OR_AMD64}") +set(TOOLSET_LIB "${TOOLSET_ROOT_DIR}/lib/${VCLIBS_X86_OR_X64}") add_subdirectory(boost-math) add_subdirectory(stl) 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..078a25dfe0 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&) { } @@ -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 @@ -993,7 +994,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, make_testing_format_args(args...)) == str); basic_string locale_str; locale_str.resize(expected_size); 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); From 3fb90914274f07eb8a3a964e027b65c8e49519c3 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Fri, 10 Sep 2021 18:15:05 -0700 Subject: [PATCH 03/24] _Format_checker --- stl/inc/format | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index ba95e84ada..5b851ea490 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -944,7 +944,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; @@ -2650,6 +2650,21 @@ struct _Arg_formatter { } }; +// set of format parsing actions that +// only checks for validity +template +struct _Format_checker { + basic_format_parse_context<_CharT> _Parse_context; + constexpr _Format_checker(basic_string_view<_CharT> _Fmt, const size_t _Num_args) noexcept + : _Parse_context(_Fmt, _Num_args) {} + constexpr void _On_text(const _CharT*, const _CharT*) {} + constexpr void _On_replacement_field(const size_t, const _CharT*) {} + constexpr const _CharT* _On_format_specs(const size_t, const _CharT* _First, const _CharT* _Last) { + _Parse_context.advance_to(_Parse_context.begin() + (_First - &*_Parse_context.begin())); + return _Last; + } +}; + // The top level set of parsing "actions". template struct _Format_handler { @@ -2798,7 +2813,9 @@ struct _Basic_format_string { basic_string_view<_CharT> _Str; template > _Ty> - consteval _Basic_format_string(const _Ty& _Str_val) : _Str(_Str_val) {} + consteval _Basic_format_string(const _Ty& _Str_val) : _Str(_Str_val) { + _Parse_format_string(_Str, _Format_checker<_CharT>{_Str, sizeof...(_Args)}); + } }; template From f3963d644e128d1a8f48390bb543a3ed3090db3d Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Mon, 13 Sep 2021 17:48:09 -0700 Subject: [PATCH 04/24] _Format_checker needs arguments --- stl/inc/format | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 5b851ea490..a97100f942 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2650,11 +2650,29 @@ struct _Arg_formatter { } }; + +// special compile time version of _Parse_format_specs, this one 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 also because +// we'd like to avoid actually erasing the argument types and forming the basic_format_args +// structure at compile time. +template +constexpr typename _ParseContext::iterator _Compile_time_parse_format_specs(_ParseContext& _Pc) { + auto _Fn = +} + // set of format parsing actions that // only checks for validity -template +template struct _Format_checker { - basic_format_parse_context<_CharT> _Parse_context; + using _Pc = basic_format_parse_context<_CharT>; + using _ParseFunc = typename _Pc::iterator (*)(_Pc&); + + constexpr size_t _Num_args = sizeof...(Args); + _Pc _Parse_context; + _ParseFunc _Parse_funcs[_Num_args > 0 ? _Num_args : 1]; + + constexpr _Format_checker(basic_string_view<_CharT> _Fmt, const size_t _Num_args) noexcept : _Parse_context(_Fmt, _Num_args) {} constexpr void _On_text(const _CharT*, const _CharT*) {} @@ -2727,7 +2745,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 != '}') { From 5a3f046c7dbdffc75425cb407ef97e9e33a98dde Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Wed, 15 Sep 2021 17:01:57 -0700 Subject: [PATCH 05/24] implement format_checker using format traits --- stl/inc/format | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index a97100f942..fa3f8d5a4d 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2658,7 +2658,10 @@ struct _Arg_formatter { // structure at compile time. template constexpr typename _ParseContext::iterator _Compile_time_parse_format_specs(_ParseContext& _Pc) { - auto _Fn = + using _CharT = typename _ParseContext::char_type; + using _Context = basic_format_context<_Fmt_it, _CharT>; + using _ArgTraits = _Format_arg_traits<_Context>; + formatter{}.parse(_Pc); } // set of format parsing actions that @@ -2668,18 +2671,18 @@ struct _Format_checker { using _Pc = basic_format_parse_context<_CharT>; using _ParseFunc = typename _Pc::iterator (*)(_Pc&); - constexpr size_t _Num_args = sizeof...(Args); + static constexpr size_t _Num_args = sizeof...(_Args); _Pc _Parse_context; _ParseFunc _Parse_funcs[_Num_args > 0 ? _Num_args : 1]; constexpr _Format_checker(basic_string_view<_CharT> _Fmt, const size_t _Num_args) noexcept - : _Parse_context(_Fmt, _Num_args) {} + : _Parse_context(_Fmt, _Num_args), _Parse_funcs{&_Compile_time_parse_format_specs<_Args, _Pc>...} {} constexpr void _On_text(const _CharT*, const _CharT*) {} constexpr void _On_replacement_field(const size_t, const _CharT*) {} - constexpr const _CharT* _On_format_specs(const size_t, const _CharT* _First, const _CharT* _Last) { - _Parse_context.advance_to(_Parse_context.begin() + (_First - &*_Parse_context.begin())); - return _Last; + 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())); + return _Id < _Num_args ? _Parse_funcs[_Id](_Parse_context)._Unwrapped() : _First; } }; From fce24f82ece14e2b0719e10b707acd176629316d Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Tue, 21 Sep 2021 16:17:20 -0700 Subject: [PATCH 06/24] make more required functions constexpr --- stl/inc/format | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index fa3f8d5a4d..295b65fae3 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -567,7 +567,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. @@ -678,7 +679,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; @@ -2658,10 +2660,12 @@ struct _Arg_formatter { // structure at compile time. template constexpr typename _ParseContext::iterator _Compile_time_parse_format_specs(_ParseContext& _Pc) { - using _CharT = typename _ParseContext::char_type; - using _Context = basic_format_context<_Fmt_it, _CharT>; - using _ArgTraits = _Format_arg_traits<_Context>; - formatter{}.parse(_Pc); + // using _CharT = typename _ParseContext::char_type; + using _Context = basic_format_context<_Fmt_it, char>; + using _ArgTraits = _Format_arg_traits<_Context>; + using _FormattedType = _ArgTraits::template _Storage_type<_Ty>; + auto _Formatter = formatter<_FormattedType, char>(); + return _Formatter.parse(_Pc); } // set of format parsing actions that @@ -2676,7 +2680,7 @@ struct _Format_checker { _ParseFunc _Parse_funcs[_Num_args > 0 ? _Num_args : 1]; - constexpr _Format_checker(basic_string_view<_CharT> _Fmt, const size_t _Num_args) noexcept + explicit constexpr _Format_checker(basic_string_view<_CharT> _Fmt) noexcept : _Parse_context(_Fmt, _Num_args), _Parse_funcs{&_Compile_time_parse_format_specs<_Args, _Pc>...} {} constexpr void _On_text(const _CharT*, const _CharT*) {} constexpr void _On_replacement_field(const size_t, const _CharT*) {} @@ -2835,7 +2839,7 @@ struct _Basic_format_string { template > _Ty> consteval _Basic_format_string(const _Ty& _Str_val) : _Str(_Str_val) { - _Parse_format_string(_Str, _Format_checker<_CharT>{_Str, sizeof...(_Args)}); + _Parse_format_string(_Str, _Format_checker<_CharT>{_Str}); } }; From ecc6c69e3834a96c04a1a74ae75280af57b0dde7 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Mon, 27 Sep 2021 15:42:17 -0700 Subject: [PATCH 07/24] compile time format checking works --- stl/inc/format | 37 +++++++++++++------ .../P0645R10_text_formatting_utf8/test.cpp | 2 +- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 295b65fae3..dce644b5dc 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -217,6 +217,11 @@ public: if (_Next_arg_id < 0) { _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 +543,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); @@ -593,7 +598,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) { @@ -616,7 +621,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) { @@ -636,7 +641,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. @@ -657,12 +663,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); @@ -2660,11 +2666,16 @@ struct _Arg_formatter { // structure at compile time. template constexpr typename _ParseContext::iterator _Compile_time_parse_format_specs(_ParseContext& _Pc) { - // using _CharT = typename _ParseContext::char_type; - using _Context = basic_format_context<_Fmt_it, char>; - using _ArgTraits = _Format_arg_traits<_Context>; - using _FormattedType = _ArgTraits::template _Storage_type<_Ty>; - auto _Formatter = formatter<_FormattedType, char>(); + using _CharT = typename _ParseContext::char_type; + using _Context = basic_format_context<_Fmt_it, _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 it's erased handle + // type + using _FormattedType = conditional_t::handle>, + _Ty, _FormattedTypeMapping>; + auto _Formatter = formatter<_FormattedType, _CharT>(); return _Formatter.parse(_Pc); } @@ -2839,7 +2850,9 @@ struct _Basic_format_string { template > _Ty> consteval _Basic_format_string(const _Ty& _Str_val) : _Str(_Str_val) { - _Parse_format_string(_Str, _Format_checker<_CharT>{_Str}); + if (_Is_execution_charset_utf8()) { + _Parse_format_string(_Str, _Format_checker<_CharT, remove_cvref_t<_Args>...>{_Str}); + } } }; diff --git a/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp b/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp index 08a70afaac..1e825bc7d5 100644 --- a/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp @@ -22,7 +22,7 @@ void test_multibyte_format_strings() { { try { - (void) format("{:\x9f\x8f\x88<10}"sv, 42); // Bad fill character encoding: missing lead byte before \x9f + (void) vformat("{:\x9f\x8f\x88<10}"sv, make_format_args(42)); // Bad fill character encoding: missing lead byte before \x9f assert(false); } catch (const format_error&) { } From 8a692e68992da9d17415ed92ac68155e078587b8 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Mon, 27 Sep 2021 15:44:31 -0700 Subject: [PATCH 08/24] Comment wording change --- stl/inc/format | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index dce644b5dc..92f857d9bc 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2659,11 +2659,10 @@ struct _Arg_formatter { }; -// special compile time version of _Parse_format_specs, this one is parameterized on +// 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 also because -// we'd like to avoid actually erasing the argument types and forming the basic_format_args -// structure at compile time. +// 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 constexpr typename _ParseContext::iterator _Compile_time_parse_format_specs(_ParseContext& _Pc) { using _CharT = typename _ParseContext::char_type; From 028557bdc60f80abb8a6a245000c6754bad683b4 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Mon, 27 Sep 2021 16:13:55 -0700 Subject: [PATCH 09/24] fix formatting error, and fix spurious test failure test failure was due to added compile time checking in basic_format_parse_context --- .../std/tests/P0645R10_text_formatting_parse_contexts/test.cpp | 2 +- tests/std/tests/P0645R10_text_formatting_utf8/test.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) 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 1e825bc7d5..97abb5e424 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) vformat("{:\x9f\x8f\x88<10}"sv, make_format_args(42)); // Bad fill character encoding: missing lead byte before \x9f + (void) vformat("{:\x9f\x8f\x88<10}"sv, + make_format_args(42)); // Bad fill character encoding: missing lead byte before \x9f assert(false); } catch (const format_error&) { } From 7b365565e7050da66d36a80b78984f1d984a8077 Mon Sep 17 00:00:00 2001 From: Charlie Barto Date: Tue, 28 Sep 2021 10:47:13 -0700 Subject: [PATCH 10/24] Apply some of miscco's suggestions from code review Co-authored-by: Michael Schellenberger Costa --- stl/inc/format | 10 ++++------ tests/std/tests/P0645R10_text_formatting_utf8/test.cpp | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 92f857d9bc..49feee3fe0 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -217,6 +217,7 @@ public: if (_Next_arg_id < 0) { _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(); @@ -2658,7 +2659,6 @@ 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 @@ -2678,8 +2678,7 @@ constexpr typename _ParseContext::iterator _Compile_time_parse_format_specs(_Par return _Formatter.parse(_Pc); } -// set of format parsing actions that -// only checks for validity +// set of format parsing actions that only checks for validity template struct _Format_checker { using _Pc = basic_format_parse_context<_CharT>; @@ -2689,11 +2688,10 @@ struct _Format_checker { _Pc _Parse_context; _ParseFunc _Parse_funcs[_Num_args > 0 ? _Num_args : 1]; - explicit constexpr _Format_checker(basic_string_view<_CharT> _Fmt) noexcept : _Parse_context(_Fmt, _Num_args), _Parse_funcs{&_Compile_time_parse_format_specs<_Args, _Pc>...} {} - constexpr void _On_text(const _CharT*, const _CharT*) {} - constexpr void _On_replacement_field(const size_t, const _CharT*) {} + constexpr void _On_text(const _CharT*, const _CharT*) const noexcept {} + constexpr void _On_replacement_field(const 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())); return _Id < _Num_args ? _Parse_funcs[_Id](_Parse_context)._Unwrapped() : _First; diff --git a/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp b/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp index 97abb5e424..de7177ecbb 100644 --- a/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp @@ -22,8 +22,8 @@ void test_multibyte_format_strings() { { try { - (void) vformat("{:\x9f\x8f\x88<10}"sv, - make_format_args(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&) { } From fec2238ef6885c528d036a9a4491ff06039003e2 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Tue, 28 Sep 2021 11:19:36 -0700 Subject: [PATCH 11/24] fix formatting. --- tests/std/tests/P0645R10_text_formatting_utf8/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp b/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp index de7177ecbb..1c1d43370f 100644 --- a/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp @@ -23,7 +23,7 @@ void test_multibyte_format_strings() { { try { // Bad fill character encoding: missing lead byte before \x9f - (void) vformat("{:\x9f\x8f\x88<10}"sv, make_format_args(42)); + (void) vformat("{:\x9f\x8f\x88<10}"sv, make_format_args(42)); assert(false); } catch (const format_error&) { } From cde2a8ef75a0e4342fd9ab2c635b9f4bc5ea011f Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Thu, 30 Sep 2021 17:00:52 -0700 Subject: [PATCH 12/24] correct chrono tests and make chrono parse functions constexpr correct usage of wrong format output iterator in compile time parsing. --- stl/inc/chrono | 20 ++++++------- stl/inc/format | 2 +- .../test.cpp | 29 ++++++++++++------- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 458b9fbae4..08e811bfc5 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_) + explicit constexpr _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,7 +5867,7 @@ namespace chrono { return _Res_iter; } - static void _Check_modifier(const char _Type, const char _Modifier) { + static constexpr void _Check_modifier(const char _Type, const char _Modifier) { if (_Modifier == '\0') { return; } @@ -5879,7 +5879,7 @@ namespace chrono { _Allowed_bit _Allowed; }; - static constexpr _Table_entry _Table[] = { + constexpr _Table_entry _Table[] = { {'c', _E_mod}, {'C', _E_mod}, {'d', _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 49feee3fe0..e43a551a59 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2666,7 +2666,7 @@ struct _Arg_formatter { template constexpr typename _ParseContext::iterator _Compile_time_parse_format_specs(_ParseContext& _Pc) { using _CharT = typename _ParseContext::char_type; - using _Context = basic_format_context<_Fmt_it, _CharT>; + 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, 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")); From b03e4d07cae771ad3c95c8c845147e9fb0603893 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Tue, 16 Nov 2021 18:05:58 -0800 Subject: [PATCH 13/24] apply "easy" review comments. --- stl/inc/chrono | 58 +++++++++++++++++++++++++------------------------- stl/inc/format | 19 +++++++++-------- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 08e811bfc5..b1b9c80f85 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5867,40 +5867,40 @@ namespace chrono { return _Res_iter; } + 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; + }; + + 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; } - 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; - }; - - 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}, - }; - const _Allowed_bit _Mod = _Modifier == 'E' ? _E_mod : _O_mod; if (auto _It = _RANGES find(_Table, _Type, &_Table_entry::_Type); _It != _STD end(_Table)) { diff --git a/stl/inc/format b/stl/inc/format index e43a551a59..d36771d06f 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2663,14 +2663,14 @@ struct _Arg_formatter { // 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 +template constexpr 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 it's erased handle + // instead of trying to instantiate a custom formatter for its erased handle // type using _FormattedType = conditional_t::handle>, _Ty, _FormattedTypeMapping>; @@ -2679,17 +2679,17 @@ constexpr typename _ParseContext::iterator _Compile_time_parse_format_specs(_Par } // set of format parsing actions that only checks for validity -template +template struct _Format_checker { - using _Pc = basic_format_parse_context<_CharT>; - using _ParseFunc = typename _Pc::iterator (*)(_Pc&); + using _ParseContext = basic_format_parse_context<_CharT>; + using _ParseFunc = typename _ParseContext::iterator (*)(_ParseContext&); static constexpr size_t _Num_args = sizeof...(_Args); - _Pc _Parse_context; + _ParseContext _Parse_context; _ParseFunc _Parse_funcs[_Num_args > 0 ? _Num_args : 1]; - explicit constexpr _Format_checker(basic_string_view<_CharT> _Fmt) noexcept - : _Parse_context(_Fmt, _Num_args), _Parse_funcs{&_Compile_time_parse_format_specs<_Args, _Pc>...} {} + constexpr 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(const size_t, const _CharT*) const noexcept {} constexpr const _CharT* _On_format_specs(const size_t _Id, const _CharT* _First, const _CharT*) { @@ -2845,7 +2845,8 @@ template struct _Basic_format_string { basic_string_view<_CharT> _Str; - template > _Ty> + 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}); From 4b638310875e0a9473083e93d6605cd911c3962a Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Wed, 17 Nov 2021 16:20:32 -0800 Subject: [PATCH 14/24] Add type checking to _Specs_checker --- stl/inc/format | 131 +++++++++++++----- .../test.cpp | 9 ++ 2 files changed, 108 insertions(+), 32 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index d36771d06f..d37d0bbb8f 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -1064,9 +1064,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); } @@ -1214,12 +1211,18 @@ private: } }; -class _Numeric_specs_checker { +// 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: - _Basic_format_arg_type _Arg_type = _Basic_format_arg_type::_None; + _Basic_format_arg_type _Arg_type; 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)) { @@ -1241,20 +1244,6 @@ public: _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. @@ -1262,21 +1251,99 @@ public: // 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(); + _Require_numeric_argument(); _Handler::_On_hash(); } 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(); + _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_type(_CharT _Type) { + if (_Type < 0 || _Type > (numeric_limits::max) ()) { + _THROW(format_error("Invalid type specification.")); + } + char _Narrow_type = static_cast(_Type); + switch (_Arg_type) { + case _Basic_format_arg_type::_None: + _STL_INTERNAL_CHECK(!"Invalid argument type."); + break; + case _Basic_format_arg_type::_Bool_type: + // note, we don't get a call if there isn't a type, but none is valid for everything. + if (_Narrow_type == 's') { + break; + } + [[fallthrough]]; + case _Basic_format_arg_type::_Char_type: + 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: + switch (_Narrow_type) { + case 'c': + case 'd': + case 'B': + case 'b': + case 'X': + case 'x': + case 'o': + break; + default: + _THROW(format_error("invalid integral or character specifier")); + } + break; + case _Basic_format_arg_type::_Float_type: + case _Basic_format_arg_type::_Double_type: + case _Basic_format_arg_type::_Long_double_type: + switch (_Narrow_type) { + case 'A': + case 'a': + case 'E': + case 'e': + case 'F': + case 'f': + case 'G': + case 'g': + break; + default: + _THROW(format_error("invalid floating point type specifier")); + } + break; + case _Basic_format_arg_type::_CString_type: + case _Basic_format_arg_type::_String_type: + switch (_Narrow_type) { + case 's': + break; + default: + _THROW(format_error("invalid string type specifier")); + } + break; + case _Basic_format_arg_type::_Pointer_type: + switch (_Narrow_type) { + case 'p': + break; + default: + _THROW(format_error("invalid pointer type specifier")); + } + 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; + } + + _Handler::_On_type(_Type); + } }; template @@ -1376,10 +1443,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]; @@ -2328,9 +2395,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]; @@ -2820,8 +2887,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> {}; diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp index 078a25dfe0..fc926a7f70 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -686,6 +686,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")); From 1116bb60828095e213ddcca1b0c553450d93ec15 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Wed, 17 Nov 2021 20:04:29 -0800 Subject: [PATCH 15/24] remove internal checks for type validity in the writer functions. --- stl/inc/format | 229 +++++++++--------- .../test.cpp | 1 + 2 files changed, 116 insertions(+), 114 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index d37d0bbb8f..971bb3eaca 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; @@ -910,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; @@ -1211,14 +1216,17 @@ private: } }; -// 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 +// 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; + // we'll se 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 _Specs_checker(const _Handler& _Handler_inst, const _Basic_format_arg_type _Arg_type_) @@ -1230,15 +1238,6 @@ 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.")); @@ -1247,17 +1246,25 @@ public: // _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 + _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. + _Need_arithmetic_presentation_type = true; _Require_numeric_argument(); _Handler::_On_zero(); } @@ -1267,72 +1274,109 @@ public: _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.")); } char _Narrow_type = static_cast(_Type); + enum class _Presentation_type_category { _Default, _Integer, _Floating, _String, _Pointer, _Char_default }; + auto _Cat = _Presentation_type_category::_Default; + switch (_Narrow_type) { + case '\0': + break; + case 's': + _Cat = _Presentation_type_category::_String; + break; + case 'c': + 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 (_Narrow_type == 's') { - break; + if (_Cat != _Presentation_type_category::_String && _Cat != _Presentation_type_category::_Integer) { + _THROW(format_error("Invalid presentation type for bool")); } - [[fallthrough]]; + break; case _Basic_format_arg_type::_Char_type: + if (_Cat == _Presentation_type_category::_Default) { + _Cat = _Presentation_type_category::_Char_default; + } + if (_Cat != _Presentation_type_category::_Char_default && _Cat != _Presentation_type_category::_Integer) { + _THROW(format_error("Invalid presentation type for char")); + } + 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: - switch (_Narrow_type) { - case 'c': - case 'd': - case 'B': - case 'b': - case 'X': - case 'x': - case 'o': - break; - default: - _THROW(format_error("invalid integral or character specifier")); + if (_Cat == _Presentation_type_category::_Default) { + _Cat = _Presentation_type_category::_Integer; + } + if (_Cat != _Presentation_type_category::_Integer) { + _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: - switch (_Narrow_type) { - case 'A': - case 'a': - case 'E': - case 'e': - case 'F': - case 'f': - case 'G': - case 'g': - break; - default: - _THROW(format_error("invalid floating point type specifier")); + 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: - switch (_Narrow_type) { - case 's': - break; - default: - _THROW(format_error("invalid string type specifier")); + 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: - switch (_Narrow_type) { - case 'p': - break; - default: - _THROW(format_error("invalid pointer type specifier")); + 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: @@ -1341,7 +1385,10 @@ public: // 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); } }; @@ -2177,9 +2224,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; @@ -2189,9 +2234,6 @@ _NODISCARD _OutputIt _Write_integral( bool _To_upper = false; switch (_Specs._Type) { - case '\0': - case 'd': - break; case 'B': _To_upper = true; [[fallthrough]]; @@ -2207,8 +2249,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 @@ -2289,9 +2329,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; @@ -2316,9 +2354,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'; @@ -2341,8 +2377,6 @@ _NODISCARD _OutputIt _Fmt_write( auto _Precision = _Specs._Precision; switch (_Specs._Type) { - case '\0': - break; case 'A': _To_upper = true; [[fallthrough]]; @@ -2379,8 +2413,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: @@ -2556,29 +2588,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; @@ -2647,25 +2662,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); diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp index fc926a7f70..699e319c89 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -666,6 +666,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'}); From 036e70787222c4b843e39de5ab68fb776b990b1f Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Wed, 17 Nov 2021 20:05:41 -0800 Subject: [PATCH 16/24] constexpr before explicit --- stl/inc/chrono | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index b1b9c80f85..578dff72e4 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5829,7 +5829,7 @@ namespace chrono { struct _Chrono_formatter { _Chrono_formatter() = default; - explicit constexpr _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 From 0f4a95222e19738f821491bb520993a24458ee3a Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Thu, 18 Nov 2021 13:47:47 -0800 Subject: [PATCH 17/24] update the required stuff in yvals_core.h --- stl/inc/yvals_core.h | 3 ++- .../VSO_0157762_feature_test_macros/test.compile.pass.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 037be9c5a9..d710c323cf 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/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..a53baae431 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 +#elif __cpp_lib_format != 202106L #error __cpp_lib_format is not 201907L #else -STATIC_ASSERT(__cpp_lib_format == 201907L); +STATIC_ASSERT(__cpp_lib_format == 202106L); #endif #else #ifdef __cpp_lib_format From ea100be7ee3c89d0159460b2f0a2f96b693f1c32 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Thu, 18 Nov 2021 14:54:05 -0800 Subject: [PATCH 18/24] skip newly failing tests in libcxx for the version macro change --- tests/libcxx/expected_results.txt | 3 +++ tests/libcxx/skipped_tests.txt | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index b95db5c0ee..5c9aea5b4d 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 202106LL 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..9ef9753602 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 202106LL 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 From f8bc41b6aa42fa4b112f9ba8ab245819869f4672 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 19 Nov 2021 19:52:26 -0800 Subject: [PATCH 19/24] Simple code review feedback. --- stl/inc/format | 16 +++++++++++----- stl/inc/yvals_core.h | 4 ++-- tests/libcxx/expected_results.txt | 2 +- tests/libcxx/skipped_tests.txt | 2 +- .../test.compile.pass.cpp | 2 +- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 971bb3eaca..9b1666c249 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -1224,7 +1224,7 @@ template class _Specs_checker : public _Handler { private: _Basic_format_arg_type _Arg_type; - // we'll se this if we get a modifier that requires an integer presentation 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; @@ -1285,7 +1285,7 @@ public: if (_Type < 0 || _Type > (numeric_limits::max) ()) { _THROW(format_error("Invalid type specification.")); } - char _Narrow_type = static_cast(_Type); + const char _Narrow_type = static_cast(_Type); enum class _Presentation_type_category { _Default, _Integer, _Floating, _String, _Pointer, _Char_default }; auto _Cat = _Presentation_type_category::_Default; switch (_Narrow_type) { @@ -1337,6 +1337,7 @@ public: if (_Cat == _Presentation_type_category::_Default) { _Cat = _Presentation_type_category::_Char_default; } + if (_Cat != _Presentation_type_category::_Char_default && _Cat != _Presentation_type_category::_Integer) { _THROW(format_error("Invalid presentation type for char")); } @@ -1348,6 +1349,7 @@ public: if (_Cat == _Presentation_type_category::_Default) { _Cat = _Presentation_type_category::_Integer; } + if (_Cat != _Presentation_type_category::_Integer) { _THROW(format_error("Invalid presentation type for integer")); } @@ -1358,8 +1360,9 @@ public: 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")); + _THROW(format_error("Invalid presentation type for floating-point")); } break; case _Basic_format_arg_type::_CString_type: @@ -1367,6 +1370,7 @@ public: 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")); } @@ -1375,6 +1379,7 @@ public: 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")); } @@ -1385,6 +1390,7 @@ public: // 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")); @@ -2742,7 +2748,7 @@ constexpr typename _ParseContext::iterator _Compile_time_parse_format_specs(_Par // type using _FormattedType = conditional_t::handle>, _Ty, _FormattedTypeMapping>; - auto _Formatter = formatter<_FormattedType, _CharT>(); + formatter<_FormattedType, _CharT> _Formatter{}; return _Formatter.parse(_Pc); } @@ -2759,7 +2765,7 @@ struct _Format_checker { constexpr 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(const size_t, 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())); return _Id < _Num_args ? _Parse_funcs[_Id](_Parse_context)._Unwrapped() : _First; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index d710c323cf..96953bdf69 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -256,7 +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 +// 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) @@ -1270,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 202106L // P2216R3 std::format improvements +#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 5c9aea5b4d..14bcc1ad36 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -637,7 +637,7 @@ 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 202106LL for P2216R3. +# 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 diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 9ef9753602..f41eed917a 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -637,7 +637,7 @@ 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 202106LL for P2216R3. +# 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 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 a53baae431..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 @@ -726,7 +726,7 @@ STATIC_ASSERT(__cpp_lib_filesystem == 201703L); #ifndef __cpp_lib_format #error __cpp_lib_format is not defined #elif __cpp_lib_format != 202106L -#error __cpp_lib_format is not 201907L +#error __cpp_lib_format is not 202106L #else STATIC_ASSERT(__cpp_lib_format == 202106L); #endif From f6d17b76322cef1560ff96e9cee0910afefe58de Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Mon, 22 Nov 2021 18:24:01 -0800 Subject: [PATCH 20/24] address code review comments (tests broken, need to figure what to do for the sized escape hatches) --- stl/inc/format | 26 +++++++++++-------- .../test.cpp | 1 + 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 971bb3eaca..b8924e492f 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -1338,7 +1338,7 @@ public: _Cat = _Presentation_type_category::_Char_default; } if (_Cat != _Presentation_type_category::_Char_default && _Cat != _Presentation_type_category::_Integer) { - _THROW(format_error("Invalid presentation type for char")); + _THROW(format_error("Invalid presentation type for char/wchar_t")); } break; case _Basic_format_arg_type::_Int_type: @@ -2733,6 +2733,10 @@ struct _Arg_formatter { // the erased basic_format_args structure at compile time. template constexpr typename _ParseContext::iterator _Compile_time_parse_format_specs(_ParseContext& _Pc) { + // only allow this function to run at compile-time + if (!_STD is_constant_evaluated()) { + _CSTD abort(); + } using _CharT = typename _ParseContext::char_type; using _Context = basic_format_context>, _CharT>; using _ArgTraits = _Format_arg_traits<_Context>; @@ -3086,16 +3090,16 @@ 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...)); 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...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; @@ -3103,7 +3107,7 @@ format_to_n_result<_OutputIt> format_to_n( 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...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; @@ -3111,35 +3115,35 @@ format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_ 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...)); 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...)); 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...)); 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...)); 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...)); return _Buf._Count(); diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp index 699e319c89..d10318a34e 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -590,6 +590,7 @@ void test_bool_specs() { // Alternate form throw_helper(STR("{:#}"), true); + assert(format(STR("{:#c}"), true) == STR("\x1")); // Leading zero throw_helper(STR("{:0}"), true); From 473dd60cca8f15c60822cab0de958542f659da07 Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 1 Dec 2021 20:40:11 -0800 Subject: [PATCH 21/24] fix the test failures caused by checking on formatted_size/format_to_n. --- stl/inc/format | 16 +++++++------- .../test.cpp | 22 ++++++++++++++----- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index b8924e492f..ffedc55112 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -3093,7 +3093,7 @@ template _OutputIt, class... _Types> 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()}; } @@ -3101,7 +3101,7 @@ template _OutputIt, class... _Types> 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()}; } @@ -3109,7 +3109,7 @@ template _OutputIt, class... _Types> format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const locale& _Loc, 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()}; } @@ -3117,35 +3117,35 @@ template _OutputIt, class... _Types> format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const locale& _Loc, 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 _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 _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 _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 _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/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp index d10318a34e..af5c00a368 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -991,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); @@ -1005,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(vformat(fmt, make_testing_format_args(args...)) == str); + assert(vformat(fmt._Str, make_testing_format_args(args...)) == str); basic_string locale_str; locale_str.resize(expected_size); @@ -1025,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 From bb3fc358b8f60b9ea4f41de642e69c9aa42f1881 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Thu, 2 Dec 2021 17:19:03 -0800 Subject: [PATCH 22/24] 'c' is no longer a semi-integer presentation type --- stl/inc/format | 13 ++++---- .../test.cpp | 30 +++++++++++++++++-- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 4e4223abd8..5c125a4ab6 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -1286,7 +1286,7 @@ public: _THROW(format_error("Invalid type specification.")); } const char _Narrow_type = static_cast(_Type); - enum class _Presentation_type_category { _Default, _Integer, _Floating, _String, _Pointer, _Char_default }; + enum class _Presentation_type_category { _Default, _Integer, _Floating, _String, _Pointer, _Char }; auto _Cat = _Presentation_type_category::_Default; switch (_Narrow_type) { case '\0': @@ -1295,6 +1295,8 @@ public: _Cat = _Presentation_type_category::_String; break; case 'c': + _Cat = _Presentation_type_category::_Char; + break; case 'd': case 'B': case 'b': @@ -1329,16 +1331,17 @@ public: _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) { + 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_default; + _Cat = _Presentation_type_category::_Char; } - if (_Cat != _Presentation_type_category::_Char_default && _Cat != _Presentation_type_category::_Integer) { + if (_Cat != _Presentation_type_category::_Char && _Cat != _Presentation_type_category::_Integer) { _THROW(format_error("Invalid presentation type for char/wchar_t")); } break; @@ -1350,7 +1353,7 @@ public: _Cat = _Presentation_type_category::_Integer; } - if (_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; diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp index af5c00a368..a2d8a121fa 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -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")); @@ -590,7 +590,7 @@ void test_bool_specs() { // Alternate form throw_helper(STR("{:#}"), true); - assert(format(STR("{:#c}"), true) == STR("\x1")); + throw_helper(STR("{:#c}"), true); // Leading zero throw_helper(STR("{:0}"), true); @@ -1366,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(); @@ -1435,6 +1458,9 @@ void test() { test_slow_append_path(); test_slow_append_path(); + + test_sane_c_specifier(); + test_sane_c_specifier(); } int main() { From c21e7110662814a6ed14908552a0b0cb757ceaf9 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Fri, 3 Dec 2021 16:34:14 -0800 Subject: [PATCH 23/24] ensure _Compile_time_parse_format_specs only runs at compile-time via consteval instead of via is_constant_evaluated(). --- stl/inc/format | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 5c125a4ab6..12a2059d5c 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2741,11 +2741,7 @@ struct _Arg_formatter { // 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 -constexpr typename _ParseContext::iterator _Compile_time_parse_format_specs(_ParseContext& _Pc) { - // only allow this function to run at compile-time - if (!_STD is_constant_evaluated()) { - _CSTD abort(); - } +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>; @@ -2769,7 +2765,7 @@ struct _Format_checker { _ParseContext _Parse_context; _ParseFunc _Parse_funcs[_Num_args > 0 ? _Num_args : 1]; - constexpr explicit _Format_checker(basic_string_view<_CharT> _Fmt) noexcept + 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 {} From cc15ff78b2f61626945faa484b07eb89bdf8bc1c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 16 Dec 2021 01:44:17 -0800 Subject: [PATCH 24/24] Work around VSO-1451773. --- stl/inc/format | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/stl/inc/format b/stl/inc/format index 12a2059d5c..fa018e3d4f 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2771,7 +2771,12 @@ struct _Format_checker { 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())); - return _Id < _Num_args ? _Parse_funcs[_Id](_Parse_context)._Unwrapped() : _First; + if (_Id < _Num_args) { + auto _Iter = _Parse_funcs[_Id](_Parse_context); // TRANSITION, VSO-1451773 (workaround: named variable) + return _Iter._Unwrapped(); + } else { + return _First; + } } };