From patchwork Tue Dec 6 21:34:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 61611 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A3C2A382E44D for ; Tue, 6 Dec 2022 21:34:53 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A3C2A382E44D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1670362493; bh=9TLy2EmlQ34v+2rpzVJ7uXeNmJsnK5MlJHIqHuXmfZ4=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=RDtZN9Ym1Elc9dFjindZvb1DTw8HPqotOOEBhuGHZLQxXXgK17IEvm2Vt2CpQXdBl HPCHQfxLDRAdWsUFRt9aXRijZloF5izaoAOjKcl4JRLXqGVZBlFIrl0NvYeMs2P+s5 mflkVpBUgg2c5iWGia+8yomOdEPN7ni5kjkV0L9g= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 228033839D09 for ; Tue, 6 Dec 2022 21:34:26 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 228033839D09 Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-269-O02W6D6PP_SSJ17o3_r2wg-1; Tue, 06 Dec 2022 16:34:24 -0500 X-MC-Unique: O02W6D6PP_SSJ17o3_r2wg-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.rdu2.redhat.com [10.11.54.2]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id D349E299E76F; Tue, 6 Dec 2022 21:34:23 +0000 (UTC) Received: from localhost (unknown [10.33.36.203]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9895F40C6EC2; Tue, 6 Dec 2022 21:34:23 +0000 (UTC) To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: The Trouble with Tribbles Date: Tue, 6 Dec 2022 21:34:20 +0000 Message-Id: <20221206213420.232451-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.2 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jonathan Wakely via Gcc-patches From: Jonathan Wakely Reply-To: Jonathan Wakely Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Tested x86_64-linux. Pushed to trunk. -- >8 -- 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. --- libstdc++-v3/include/std/format | 29 +++++++----- .../testsuite/std/format/functions/format.cc | 4 ++ .../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") );