From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id B3F033833A2C; Tue, 6 Dec 2022 21:33:42 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B3F033833A2C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1670362422; bh=jO/0oGWvFpT9uoYxaTpX5M7hz9vakSnk7gIcpEvfNN4=; h=From:To:Subject:Date:From; b=ah9xbg4Gabl181UesfmExvSAyoF/T3ruieISLQTRB/TAHsIXY4B/CcvePTF9JF9uv Xl9lXO0q9KSjtisZ8cQ7OyhXTM5/hxhliJTZaIf/n5i07bqbagjn9UpWyXzDQmw1ja AeIB/LhTGIjF8tRlj75pIwYrwXcXw2UNV5a7tLsQ= MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r13-4523] libstdc++: The Trouble with Tribbles X-Act-Checkin: gcc X-Git-Author: Jonathan Wakely X-Git-Refname: refs/heads/master X-Git-Oldrev: ba1536dac780f3f92c5eab999fda6931f6247fc1 X-Git-Newrev: 48e21e878b2c6cfc7797088a7393a735de75883c Message-Id: <20221206213342.B3F033833A2C@sourceware.org> Date: Tue, 6 Dec 2022 21:33:42 +0000 (GMT) List-Id: https://gcc.gnu.org/g:48e21e878b2c6cfc7797088a7393a735de75883c commit r13-4523-g48e21e878b2c6cfc7797088a7393a735de75883c Author: Jonathan Wakely Date: Mon Dec 5 12:23:57 2022 +0000 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. Diff: --- libstdc++-v3/include/std/format | 29 ++++++++------ .../testsuite/std/format/functions/format.cc | 4 ++ 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 cb5ce40dece..6d6a770eb8c 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 struct _Scanner { diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc index 8019fbdf712..7a155208a48 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 dd6ca68290b..069dfceced5 100644 --- a/libstdc++-v3/testsuite/std/format/parse_ctx.cc +++ b/libstdc++-v3/testsuite/std/format/parse_ctx.cc @@ -4,11 +4,11 @@ #include #include -template +template 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("0") ); VERIFY( ! is_std_format_spec_for("00d") ); VERIFY( is_std_format_spec_for("01d") ); - VERIFY( ! is_std_format_spec_for("0{}d") ); + VERIFY( is_std_format_spec_for("0{}d") ); VERIFY( ! is_std_format_spec_for("0{1}d") ); - VERIFY(( is_std_format_spec_for("0{}d") )); - VERIFY(( ! is_std_format_spec_for("0{1}d") )); - VERIFY(( is_std_format_spec_for("0{1}d") )); + VERIFY(( is_std_format_spec_for("0{1}d") )); VERIFY( is_std_format_spec_for("1") ); VERIFY( ! is_std_format_spec_for("-1") ); VERIFY( is_std_format_spec_for("-1d") ); // sign and width @@ -98,11 +96,10 @@ test_int() VERIFY( is_std_format_spec_for("0") ); VERIFY( ! is_std_format_spec_for("00d") ); VERIFY( is_std_format_spec_for("01d") ); - VERIFY( ! is_std_format_spec_for("0{}d") ); VERIFY( ! is_std_format_spec_for("0{1}d") ); - VERIFY(( is_std_format_spec_for("0{}d") )); - VERIFY(( ! is_std_format_spec_for("0{1}d") )); - VERIFY(( is_std_format_spec_for("0{1}d") )); + VERIFY(( is_std_format_spec_for("0{}d") )); + VERIFY(( ! is_std_format_spec_for("0{1}d") )); + VERIFY(( is_std_format_spec_for("0{1}d") )); VERIFY( is_std_format_spec_for("1") ); VERIFY( is_std_format_spec_for("-1") ); // sign and width VERIFY( ! is_std_format_spec_for(".") ); @@ -193,24 +190,20 @@ test_float() VERIFY( is_std_format_spec_for("0") ); VERIFY( ! is_std_format_spec_for("00f") ); VERIFY( is_std_format_spec_for("01f") ); - VERIFY( ! is_std_format_spec_for("0{}f") ); + VERIFY( is_std_format_spec_for("0{}f") ); VERIFY( ! is_std_format_spec_for("0{1}f") ); - VERIFY(( is_std_format_spec_for("0{}f") )); - VERIFY(( ! is_std_format_spec_for("0{1}f") )); - VERIFY(( is_std_format_spec_for("0{1}f") )); + VERIFY( ! is_std_format_spec_for("0{1}f") ); + VERIFY(( is_std_format_spec_for("0{1}f") )); VERIFY( is_std_format_spec_for("1") ); VERIFY( is_std_format_spec_for("-1") ); // sign and width VERIFY( ! is_std_format_spec_for(".") ); VERIFY( is_std_format_spec_for(".1") ); - VERIFY( ! is_std_format_spec_for(".{}") ); + VERIFY( is_std_format_spec_for(".{}") ); VERIFY( ! is_std_format_spec_for(".{1}") ); - VERIFY(( is_std_format_spec_for(".{}") )); - VERIFY(( ! is_std_format_spec_for(".{1}") )); - VERIFY(( is_std_format_spec_for(".{1}") )); - VERIFY(( ! is_std_format_spec_for("{}.{}") )); - VERIFY(( is_std_format_spec_for("{}.{}") )); - VERIFY(( is_std_format_spec_for("{1}.{1}") )); - VERIFY(( is_std_format_spec_for("{2}.{1}") )); + VERIFY(( is_std_format_spec_for(".{1}") )); + VERIFY( is_std_format_spec_for("{}.{}") ); + VERIFY(( is_std_format_spec_for("{1}.{1}") )); + VERIFY(( is_std_format_spec_for("{2}.{1}") )); VERIFY( ! is_std_format_spec_for("c") ); VERIFY( ! is_std_format_spec_for("b") ); VERIFY( ! is_std_format_spec_for("B") ); @@ -302,11 +295,9 @@ test_string() VERIFY( ! is_std_format_spec_for("-1s") ); VERIFY( ! is_std_format_spec_for(".") ); VERIFY( is_std_format_spec_for(".1") ); - VERIFY( ! is_std_format_spec_for(".{}") ); - VERIFY(( ! is_std_format_spec_for(".{1}") )); - VERIFY(( is_std_format_spec_for(".{0}") )); - VERIFY(( is_std_format_spec_for(".{}") )); - VERIFY(( is_std_format_spec_for(".{1}") )); + VERIFY( is_std_format_spec_for(".{}") ); + VERIFY(( is_std_format_spec_for(".{0}") )); + VERIFY(( is_std_format_spec_for(".{1}") )); VERIFY( ! is_std_format_spec_for("c") ); VERIFY( ! is_std_format_spec_for("b") ); VERIFY( ! is_std_format_spec_for("B") );