diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2022-12-05 12:23:57 +0000 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2022-12-06 21:33:29 +0000 |
commit | 48e21e878b2c6cfc7797088a7393a735de75883c (patch) | |
tree | a9cf68c07d14eccf8aaa5f6a1a5d0ea724dc64a8 /libstdc++-v3 | |
parent | ba1536dac780f3f92c5eab999fda6931f6247fc1 (diff) | |
download | gcc-48e21e878b2c6cfc7797088a7393a735de75883c.zip gcc-48e21e878b2c6cfc7797088a7393a735de75883c.tar.gz gcc-48e21e878b2c6cfc7797088a7393a735de75883c.tar.bz2 |
libstdc++: The Trouble with Tribbles
Fix digit grouping for integers formatted with "{:#Lx}" which were
including the "0x" prefix in the grouped digits. This resulted in output
like "0,xff,fff" instead of "0xff,fff".
Also change std:::basic_format_parse_context to not throw for an arg-id
that is larger than the actual number of format arguments. I clarified
with Victor Zverovich that this is the intended behaviour for the
run-time format-string checks. An out-of-range arg-id should be
diagnosed at compile-time (as clarified by LWG 3825) but not run-time.
The formatting function will still throw at run-time when args.arg(id)
returns an empty basic_format_arg.
libstdc++-v3/ChangeLog:
* include/std/format (basic_format_parse_context::next_arg_id):
Only check arg-id is in range during constant evaluation.
* testsuite/std/format/functions/format.cc: Check "{:#Lx}".
* testsuite/std/format/parse_ctx.cc: Adjust expected results for
format-strings using an out-of-range arg-id.
Diffstat (limited to 'libstdc++-v3')
-rw-r--r-- | libstdc++-v3/include/std/format | 29 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/std/format/functions/format.cc | 4 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/std/format/parse_ctx.cc | 45 |
3 files changed, 40 insertions, 38 deletions
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index cb5ce40..6d6a770 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -221,7 +221,10 @@ namespace __format if (_M_indexing == _Manual) __format::__conflicting_indexing_in_format_string(); _M_indexing = _Auto; - // if (std::is_constant_evaluated()) // XXX skip runtime check? + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3825. Missing compile-time argument id check in next_arg_id + if (std::is_constant_evaluated()) if (_M_next_arg_id == _M_num_args) __format::__invalid_arg_id_in_format_string(); return _M_next_arg_id++; @@ -234,7 +237,7 @@ namespace __format __format::__conflicting_indexing_in_format_string(); _M_indexing = _Manual; - // if (std::is_constant_evaluated()) // XXX skip runtime check? + if (std::is_constant_evaluated()) if (__id >= _M_num_args) __format::__invalid_arg_id_in_format_string(); } @@ -1167,15 +1170,18 @@ namespace __format string __grp = __np.grouping(); if (!__grp.empty()) { - size_t __n = __str.size(); + size_t __n = __str.size() - __prefix_len; auto __p = (_CharT*)__builtin_alloca(2 * __n - * sizeof(_CharT)); - auto __end = std::__add_grouping(__p, + * sizeof(_CharT) + + __prefix_len); + auto __s = __str.data(); + char_traits<_CharT>::copy(__p, __s, __prefix_len); + __s += __prefix_len; + auto __end = std::__add_grouping(__p + __prefix_len, __np.thousands_sep(), __grp.data(), __grp.size(), - __str.data(), - __str.data() + __n); + __s, __s + __n); __str = {__p, size_t(__end - __p)}; } } @@ -3381,10 +3387,11 @@ namespace __format __ctx.advance_to(__format::__write(__ctx.out())); } - // TODO define __process_format_string which takes an object with callbacks - // can use that for initial constexpr parse of format string (with callbacks - // that have no side effects, just non-constant on error). - + // Abstract base class defining an interface for scanning format strings. + // Scan the characters in a format string, dividing it up into strings of + // ordinary characters, escape sequences, and replacement fields. + // Call virtual functions for derived classes to parse format-specifiers + // or write formatted output. template<typename _CharT> struct _Scanner { diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc index 8019fbd..7a15520 100644 --- a/libstdc++-v3/testsuite/std/format/functions/format.cc +++ b/libstdc++-v3/testsuite/std/format/functions/format.cc @@ -111,6 +111,10 @@ test_std_examples() string s3 = format("{:L}", 1234); VERIFY(s3 == "1,234"); + // Test locale's "byte-and-a-half" grouping (Imperial word? tribble?). + string s4 = format("{:#Lx}", 0xfffff); + VERIFY(s4 == "0xff,fff"); + // Restore std::locale::global(std::locale::classic()); } diff --git a/libstdc++-v3/testsuite/std/format/parse_ctx.cc b/libstdc++-v3/testsuite/std/format/parse_ctx.cc index dd6ca68..069dfce 100644 --- a/libstdc++-v3/testsuite/std/format/parse_ctx.cc +++ b/libstdc++-v3/testsuite/std/format/parse_ctx.cc @@ -4,11 +4,11 @@ #include <format> #include <testsuite_hooks.h> -template<typename T, size_t N = 1, bool auto_indexing = true> +template<typename T, bool auto_indexing = true> bool is_std_format_spec_for(std::string_view spec) { - std::format_parse_context pc(spec, N); + std::format_parse_context pc(spec); if (auto_indexing) (void) pc.next_arg_id(); else @@ -49,11 +49,9 @@ test_char() VERIFY( ! is_std_format_spec_for<char>("0") ); VERIFY( ! is_std_format_spec_for<char>("00d") ); VERIFY( is_std_format_spec_for<char>("01d") ); - VERIFY( ! is_std_format_spec_for<char>("0{}d") ); + VERIFY( is_std_format_spec_for<char>("0{}d") ); VERIFY( ! is_std_format_spec_for<char>("0{1}d") ); - VERIFY(( is_std_format_spec_for<char, 2>("0{}d") )); - VERIFY(( ! is_std_format_spec_for<char, 2>("0{1}d") )); - VERIFY(( is_std_format_spec_for<char, 2, false>("0{1}d") )); + VERIFY(( is_std_format_spec_for<char, false>("0{1}d") )); VERIFY( is_std_format_spec_for<char>("1") ); VERIFY( ! is_std_format_spec_for<char>("-1") ); VERIFY( is_std_format_spec_for<char>("-1d") ); // sign and width @@ -98,11 +96,10 @@ test_int() VERIFY( is_std_format_spec_for<int>("0") ); VERIFY( ! is_std_format_spec_for<int>("00d") ); VERIFY( is_std_format_spec_for<int>("01d") ); - VERIFY( ! is_std_format_spec_for<int>("0{}d") ); VERIFY( ! is_std_format_spec_for<int>("0{1}d") ); - VERIFY(( is_std_format_spec_for<int, 2>("0{}d") )); - VERIFY(( ! is_std_format_spec_for<int, 2>("0{1}d") )); - VERIFY(( is_std_format_spec_for<int, 2, false>("0{1}d") )); + VERIFY(( is_std_format_spec_for<int>("0{}d") )); + VERIFY(( ! is_std_format_spec_for<int>("0{1}d") )); + VERIFY(( is_std_format_spec_for<int, false>("0{1}d") )); VERIFY( is_std_format_spec_for<int>("1") ); VERIFY( is_std_format_spec_for<int>("-1") ); // sign and width VERIFY( ! is_std_format_spec_for<int>(".") ); @@ -193,24 +190,20 @@ test_float() VERIFY( is_std_format_spec_for<float>("0") ); VERIFY( ! is_std_format_spec_for<float>("00f") ); VERIFY( is_std_format_spec_for<float>("01f") ); - VERIFY( ! is_std_format_spec_for<float>("0{}f") ); + VERIFY( is_std_format_spec_for<float>("0{}f") ); VERIFY( ! is_std_format_spec_for<float>("0{1}f") ); - VERIFY(( is_std_format_spec_for<float, 2>("0{}f") )); - VERIFY(( ! is_std_format_spec_for<float, 2>("0{1}f") )); - VERIFY(( is_std_format_spec_for<float, 2, false>("0{1}f") )); + VERIFY( ! is_std_format_spec_for<float>("0{1}f") ); + VERIFY(( is_std_format_spec_for<float, false>("0{1}f") )); VERIFY( is_std_format_spec_for<float>("1") ); VERIFY( is_std_format_spec_for<float>("-1") ); // sign and width VERIFY( ! is_std_format_spec_for<float>(".") ); VERIFY( is_std_format_spec_for<float>(".1") ); - VERIFY( ! is_std_format_spec_for<float>(".{}") ); + VERIFY( is_std_format_spec_for<float>(".{}") ); VERIFY( ! is_std_format_spec_for<float>(".{1}") ); - VERIFY(( is_std_format_spec_for<float, 2>(".{}") )); - VERIFY(( ! is_std_format_spec_for<float, 2>(".{1}") )); - VERIFY(( is_std_format_spec_for<float, 2, false>(".{1}") )); - VERIFY(( ! is_std_format_spec_for<float, 2>("{}.{}") )); - VERIFY(( is_std_format_spec_for<float, 3>("{}.{}") )); - VERIFY(( is_std_format_spec_for<float, 2, false>("{1}.{1}") )); - VERIFY(( is_std_format_spec_for<float, 3, false>("{2}.{1}") )); + VERIFY(( is_std_format_spec_for<float, false>(".{1}") )); + VERIFY( is_std_format_spec_for<float>("{}.{}") ); + VERIFY(( is_std_format_spec_for<float, false>("{1}.{1}") )); + VERIFY(( is_std_format_spec_for<float, false>("{2}.{1}") )); VERIFY( ! is_std_format_spec_for<float>("c") ); VERIFY( ! is_std_format_spec_for<float>("b") ); VERIFY( ! is_std_format_spec_for<float>("B") ); @@ -302,11 +295,9 @@ test_string() VERIFY( ! is_std_format_spec_for<const char*>("-1s") ); VERIFY( ! is_std_format_spec_for<const char*>(".") ); VERIFY( is_std_format_spec_for<const char*>(".1") ); - VERIFY( ! is_std_format_spec_for<const char*>(".{}") ); - VERIFY(( ! is_std_format_spec_for<const char*, 1, false>(".{1}") )); - VERIFY(( is_std_format_spec_for<const char*, 1, false>(".{0}") )); - VERIFY(( is_std_format_spec_for<const char*, 2>(".{}") )); - VERIFY(( is_std_format_spec_for<const char*, 2, false>(".{1}") )); + VERIFY( is_std_format_spec_for<const char*>(".{}") ); + VERIFY(( is_std_format_spec_for<const char*, false>(".{0}") )); + VERIFY(( is_std_format_spec_for<const char*, false>(".{1}") )); VERIFY( ! is_std_format_spec_for<const char*>("c") ); VERIFY( ! is_std_format_spec_for<const char*>("b") ); VERIFY( ! is_std_format_spec_for<const char*>("B") ); |