From efd50e8961485b31b1d64b8706af61a9ffe24cce Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Sat, 4 Mar 2023 06:40:42 +0700 Subject: [PATCH] Implement LWG-3720 Restrict the valid types of arg-id for width and precision in std-format-spec (#3511) Co-authored-by: A. Jiang --- stl/inc/format | 8 ++++++-- .../P0645R10_text_formatting_formatting/test.cpp | 12 ++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 7c78aada0..aa4066bc5 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 +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 // 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 (_Is_signed_or_unsigned_large_integer_t<_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 (_Is_signed_or_unsigned_large_integer_t<_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 fabf6cde4..6c02ad97a 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -1356,10 +1356,13 @@ 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); + throw_helper(STR("{:*^{}}"), 'a', '0'); + 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 assert(format(STR("{0:{1}}"), -42, 4) == STR(" -42")); @@ -1407,6 +1410,11 @@ 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); + throw_helper(STR("{:.{}f}"), 3.14f, '0'); + assert(format(STR("{:.{}f}"), 3.14f, static_cast(2)) == STR("3.14")); } template