From 1c321b584036abb01875051ec8c3c1238605c96f Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 28 Feb 2023 13:37:01 +0700 Subject: [PATCH 1/5] Implement LWG-3720 Restrict the valid types of arg-id for width and precision in std-format-spec --- stl/inc/format | 8 +++-- .../test.cpp | 36 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 7c78aada0b..bcdd259b1a 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -1410,13 +1410,17 @@ _NODISCARD constexpr basic_format_arg<_Context> _Get_arg(const _Context& _Ctx, c return _Arg; } +template +constexpr bool _Standard_signed_or_unsigned_integer = _Is_any_of_v, signed char, unsigned char, short, + unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long>; + // Checks that the type and value of an argument associated with a dynamic // width specifier are valid. class _Width_checker { public: template _NODISCARD constexpr unsigned long long operator()(const _Ty _Value) const { - if constexpr (is_integral_v<_Ty>) { + if constexpr (_Standard_signed_or_unsigned_integer<_Ty>) { if constexpr (is_signed_v<_Ty>) { if (_Value < 0) { _Throw_format_error("Negative width."); @@ -1435,7 +1439,7 @@ class _Precision_checker { public: template _NODISCARD constexpr unsigned long long operator()(const _Ty _Value) const { - if constexpr (is_integral_v<_Ty>) { + if constexpr (_Standard_signed_or_unsigned_integer<_Ty>) { if constexpr (is_signed_v<_Ty>) { if (_Value < 0) { _Throw_format_error("Negative precision."); 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 d0ec2134ce..38897991ae 100644 --- a/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp @@ -96,6 +96,40 @@ constexpr bool test_basic_format_parse_context() { return true; } +// LWG-3720: Restrict the valid types of arg-id for width and precision in std-format-spec +void test_lwg3720() { + // Width + { + try { + (void) format("{:*^{}}\n", 'a', true); + assert(false); + } catch (const format_error&) { + } + } + { + try { + (void) format("{:*^{}}\n", 'a', '0'); + assert(false); + } catch (const format_error&) { + } + } + // Precision + { + try { + (void) format("{:.{}f}", 3.14f, true); + assert(false); + } catch (const format_error&) { + } + } + { + try { + (void) format("{:.{}f}", 3.14f, '0'); + assert(false); + } catch (const format_error&) { + } + } +} + int main() { test_basic_format_parse_context(); test_basic_format_parse_context(); @@ -106,4 +140,6 @@ int main() { assert(check_arg_id_not_constexpr_char); assert(check_arg_id_is_constexpr_wchar); assert(check_arg_id_not_constexpr_wchar); + + test_lwg3720(); } From ccc3a3320de273cba779b49b7a7b0b3abb0fb197 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 28 Feb 2023 13:51:52 +0700 Subject: [PATCH 2/5] add inline --- stl/inc/format | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index bcdd259b1a..31aa76cb7b 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -1411,8 +1411,8 @@ _NODISCARD constexpr basic_format_arg<_Context> _Get_arg(const _Context& _Ctx, c } template -constexpr bool _Standard_signed_or_unsigned_integer = _Is_any_of_v, signed char, unsigned char, short, - unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long>; +inline constexpr bool _Standard_signed_or_unsigned_integer = _Is_any_of_v, signed char, unsigned char, + short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long>; // Checks that the type and value of an argument associated with a dynamic // width specifier are valid. From 755fcb9c598cae1e99c62e6eddab50fbf2945d88 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 28 Feb 2023 16:15:10 +0700 Subject: [PATCH 3/5] fix tests --- .../test.cpp | 14 ++++++-- .../test.cpp | 36 ------------------- 2 files changed, 12 insertions(+), 38 deletions(-) diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp index fabf6cde48..86a43d04e0 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -1356,10 +1356,14 @@ void libfmt_formatter_test_runtime_width() { throw_helper(STR("{0:{1}}"), 0, (int_max + 1u)); throw_helper(STR("{0:{1}}"), 0, -1l); throw_helper(STR("{0:{1}}"), 0, (int_max + 1ul)); - assert(format(STR("{0:{1}}"), 0, '0') - == STR(" 0")); // behavior differs from libfmt, but conforms throw_helper(STR("{0:{1}}"), 0, 0.0); + // LWG-3720: Restrict the valid types of arg-id for width and precision in std-format-spec + throw_helper(STR("{:*^{}}"), 'a', true); +#ifdef _NATIVE_WCHAR_T_DEFINED + throw_helper(STR("{:*^{}}"), 'a', '0'); +#endif + assert(format(STR("{0:{1}}"), 42, 0) == STR("42")); // LWG-3721: zero dynamic width is OK assert(format(STR("{0:{1}}"), -42, 4) == STR(" -42")); @@ -1407,6 +1411,12 @@ void libfmt_formatter_test_runtime_precision() { throw_helper(STR("{0:.{1}}"), reinterpret_cast(0xcafe), 2); throw_helper(STR("{0:.{1}f}"), reinterpret_cast(0xcafe), 2); assert(format(STR("{0:.{1}}"), STR("str"), 2) == STR("st")); + + // LWG-3720: Restrict the valid types of arg-id for width and precision in std-format-spec + throw_helper(STR("{:.{}f}"), 3.14f, true); +#ifdef _NATIVE_WCHAR_T_DEFINED + throw_helper(STR("{:.{}f}"), 3.14f, '0'); +#endif } template 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 38897991ae..d0ec2134ce 100644 --- a/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp @@ -96,40 +96,6 @@ constexpr bool test_basic_format_parse_context() { return true; } -// LWG-3720: Restrict the valid types of arg-id for width and precision in std-format-spec -void test_lwg3720() { - // Width - { - try { - (void) format("{:*^{}}\n", 'a', true); - assert(false); - } catch (const format_error&) { - } - } - { - try { - (void) format("{:*^{}}\n", 'a', '0'); - assert(false); - } catch (const format_error&) { - } - } - // Precision - { - try { - (void) format("{:.{}f}", 3.14f, true); - assert(false); - } catch (const format_error&) { - } - } - { - try { - (void) format("{:.{}f}", 3.14f, '0'); - assert(false); - } catch (const format_error&) { - } - } -} - int main() { test_basic_format_parse_context(); test_basic_format_parse_context(); @@ -140,6 +106,4 @@ int main() { assert(check_arg_id_not_constexpr_char); assert(check_arg_id_is_constexpr_wchar); assert(check_arg_id_not_constexpr_wchar); - - test_lwg3720(); } From dbb0a3bf1734ab8a23a3e33f476ddfb9c230c7e3 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 28 Feb 2023 20:02:44 +0700 Subject: [PATCH 4/5] signed/unsigned integer types are never type-erased as `unsigned short` Co-authored-by: A. Jiang --- stl/inc/format | 8 ++++---- .../tests/P0645R10_text_formatting_formatting/test.cpp | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 31aa76cb7b..00872f4220 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -1411,8 +1411,8 @@ _NODISCARD constexpr basic_format_arg<_Context> _Get_arg(const _Context& _Ctx, c } template -inline constexpr bool _Standard_signed_or_unsigned_integer = _Is_any_of_v, signed char, unsigned char, - short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long>; +inline constexpr bool _Is_standard_signed_or_unsigned_integer = + _Is_any_of_v, int, unsigned int, long, unsigned long, long long, unsigned long long>; // Checks that the type and value of an argument associated with a dynamic // width specifier are valid. @@ -1420,7 +1420,7 @@ class _Width_checker { public: template _NODISCARD constexpr unsigned long long operator()(const _Ty _Value) const { - if constexpr (_Standard_signed_or_unsigned_integer<_Ty>) { + if constexpr (_Is_standard_signed_or_unsigned_integer<_Ty>) { if constexpr (is_signed_v<_Ty>) { if (_Value < 0) { _Throw_format_error("Negative width."); @@ -1439,7 +1439,7 @@ class _Precision_checker { public: template _NODISCARD constexpr unsigned long long operator()(const _Ty _Value) const { - if constexpr (_Standard_signed_or_unsigned_integer<_Ty>) { + if constexpr (_Is_standard_signed_or_unsigned_integer<_Ty>) { if constexpr (is_signed_v<_Ty>) { if (_Value < 0) { _Throw_format_error("Negative precision."); diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp index 86a43d04e0..6c02ad97a8 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -1360,9 +1360,8 @@ void libfmt_formatter_test_runtime_width() { // LWG-3720: Restrict the valid types of arg-id for width and precision in std-format-spec throw_helper(STR("{:*^{}}"), 'a', true); -#ifdef _NATIVE_WCHAR_T_DEFINED throw_helper(STR("{:*^{}}"), 'a', '0'); -#endif + assert(format(STR("{:*^{}}"), 'a', static_cast(2)) == STR("a*")); assert(format(STR("{0:{1}}"), 42, 0) == STR("42")); // LWG-3721: zero dynamic width is OK @@ -1414,9 +1413,8 @@ void libfmt_formatter_test_runtime_precision() { // LWG-3720: Restrict the valid types of arg-id for width and precision in std-format-spec throw_helper(STR("{:.{}f}"), 3.14f, true); -#ifdef _NATIVE_WCHAR_T_DEFINED throw_helper(STR("{:.{}f}"), 3.14f, '0'); -#endif + assert(format(STR("{:.{}f}"), 3.14f, static_cast(2)) == STR("3.14")); } template From 30a8a51b0e1e7de385c6bc37973d40be70b5f9dc Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 28 Feb 2023 21:50:33 +0700 Subject: [PATCH 5/5] rename _Is_standard_signed_or_unsigned_integer --- stl/inc/format | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 00872f4220..aa4066bc5e 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -1411,7 +1411,7 @@ _NODISCARD constexpr basic_format_arg<_Context> _Get_arg(const _Context& _Ctx, c } template -inline constexpr bool _Is_standard_signed_or_unsigned_integer = +inline constexpr bool _Is_signed_or_unsigned_large_integer_t = _Is_any_of_v, int, unsigned int, long, unsigned long, long long, unsigned long long>; // Checks that the type and value of an argument associated with a dynamic @@ -1420,7 +1420,7 @@ class _Width_checker { public: template _NODISCARD constexpr unsigned long long operator()(const _Ty _Value) const { - if constexpr (_Is_standard_signed_or_unsigned_integer<_Ty>) { + if constexpr (_Is_signed_or_unsigned_large_integer_t<_Ty>) { if constexpr (is_signed_v<_Ty>) { if (_Value < 0) { _Throw_format_error("Negative width."); @@ -1439,7 +1439,7 @@ class _Precision_checker { public: template _NODISCARD constexpr unsigned long long operator()(const _Ty _Value) const { - if constexpr (_Is_standard_signed_or_unsigned_integer<_Ty>) { + if constexpr (_Is_signed_or_unsigned_large_integer_t<_Ty>) { if constexpr (is_signed_v<_Ty>) { if (_Value < 0) { _Throw_format_error("Negative precision.");