From patchwork Wed Mar 12 14:39:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Tomasz_Kami=C5=84ski?= X-Patchwork-Id: 107782 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 58FC6385842A for ; Wed, 12 Mar 2025 15:03:43 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 58FC6385842A Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=duUf8wSn 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 ESMTP id 4E3943858401 for ; Wed, 12 Mar 2025 15:01:19 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 4E3943858401 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 4E3943858401 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741791679; cv=none; b=UhQ9X6JysS87v5qIe2cPMTztzL5UnN4/kZcuJQQkVBUX4HRSqa16b/CNbl4xHdGSqrS9xmc67fjYJPzDQvVoAFuy5MnWXJTxo2X4UXlcUcHRwh4jrYrEgjGz9tGtdMdec2w25a6IopURsWD5MMrk+9uBB0roAr6w50mDqldwwko= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741791679; c=relaxed/simple; bh=5gyTiBrH+ut0LewHdx6KaLxMC+J/4iIKbdQy+y2dH2s=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=E3RS7axRnvuxPYSugo+6o/pa+Th3vlm75+X9F/OpkzfOJGfzXW+KfIEMkudqnW4jzbzDmyvsPHyrfFKvGZe+fuEsVoCZJs2khBND7Du5sVoztkLm5FBMh1bZfTB9XDrhbF+9EcQbLLVfqBCy3sZqOsSAadaipvlUCQ8A3rzbCJk= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 4E3943858401 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1741791679; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ox82Yr3UWQeeMGpLrJva93Vu4kXTimu/56nMIGBZ4kA=; b=duUf8wSnmNM7bwu3KyNFo/RfnOkye3x4juo23dafh6F+SAImJ3Qlf3Gpg91HX5wWvUKXkj nrnqWwQU05mgquI/Yry9y+24KRiSYzKE7mlWWdZ56d/hv3ggPFA/f7EkNPFWyDIGAyYw9S kO7Jl8Ejjq0JEL8VF9GTxqn4DNf4PfM= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-237-7ehDpxEWPaSbuaasjQ-wOg-1; Wed, 12 Mar 2025 11:01:16 -0400 X-MC-Unique: 7ehDpxEWPaSbuaasjQ-wOg-1 X-Mimecast-MFC-AGG-ID: 7ehDpxEWPaSbuaasjQ-wOg_1741791675 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 42C5C180AF66; Wed, 12 Mar 2025 15:01:15 +0000 (UTC) Received: from localhost (unknown [10.44.32.170]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 66A991801751; Wed, 12 Mar 2025 15:01:14 +0000 (UTC) From: =?utf-8?q?Tomasz_Kami=C5=84ski?= To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [PATCH v2] libstdc++: Hide 128-bit int and float types behind handle for basic_format_arg visitation [PR108053] Date: Wed, 12 Mar 2025 15:39:13 +0100 Message-ID: <20250312150106.78350-1-tkaminsk@redhat.com> In-Reply-To: <20250311165014.594965-1-tkaminsk@redhat.com> References: <20250311165014.594965-1-tkaminsk@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: GX7lCwYHGA8PZJp5SUGpPfIsVURN1G4PtzdCKJc76ro_1741791675 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-14.0 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_HOSTKARMA_W, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org Implement visit_format_arg and basic_format_arg::visit function, in terms of _M_visit_user member functions, that wraps any type stored inside basic_format_arg, that is not specified as standard into the handle. This affects __in128, unsigned __in128, PowerPC specific __iee128 and __ibm128, and _Float128 for architectures where long double is not 128bits. The bfloat16, _Float16, _Float32, _Float32, and _Float128 for 128bits long double are not are not addressed, as they are transformed into standard floating point types. For internal purposes __format::__visit_format_arg function is used, that provides an umodifed access to stored object. PR libstdc++/108053 libstdc++-v3/ChangeLog: * include/std/format (basic_format_arg::_M_visit_user): Helper function for wrapping extension types into handle (visit_format_arg): Call `_M_visit_user` instead of `_M_visit`. (basic_format_arg::visit): As above. (__format::__visit_format_arg): Provides direct access to values stored in basic_format_arg. (__format::__int_from_arg): Use __format::__visit_format_arg instead of std::visit_format_arg. (_Formatting_scanner::_M_format_arg): As above. (_Checking_scanner::__do_vformat_to): As above. * testsuite/std/format/arguments/args.cc: New tests. * testsuite/std/format/string.cc: Test for using __int128 as width/precision. --- Revision added missing decltype(auto) return type for lambda in _M_user_visit. Tested on x84_64-linux. Tested arguments/args.cc` with -mlong-double-128. Tested `std/format` test on ppc64le-linux, with both -mabi=ieeelongdouble and -mabi=ibmlongdouble. OK for trunk? libstdc++-v3/include/std/format | 49 +++++++++++-- .../testsuite/std/format/arguments/args.cc | 73 +++++++++++++++++++ libstdc++-v3/testsuite/std/format/string.cc | 10 ++- 3 files changed, 123 insertions(+), 9 deletions(-) diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index e7e0d2d142b..5085658cdb7 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -3290,10 +3290,12 @@ namespace __format template class _Arg_store; + template + decltype(auto) __visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>); + template consteval _Arg_t __to_arg_t_enum() noexcept; - } // namespace __format /// @endcond @@ -3365,12 +3367,12 @@ namespace __format template decltype(auto) visit(this basic_format_arg __arg, _Visitor&& __vis) - { return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); } + { return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); } template _Res visit(this basic_format_arg __arg, _Visitor&& __vis) - { return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); } + { return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); } #endif private: @@ -3587,6 +3589,10 @@ namespace __format friend decltype(auto) visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx>); + template + friend decltype(auto) + __format::__visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>); + template friend consteval __format::_Arg_t __format::__to_arg_t_enum() noexcept; @@ -3655,6 +3661,28 @@ namespace __format __builtin_unreachable(); } } + + template + decltype(auto) + _M_visit_user(_Visitor&& __vis, __format::_Arg_t __type) + { + return _M_visit([&__vis](_Tp& __val) -> decltype(auto) + { + constexpr bool __user_facing = __is_one_of<_Tp, + monostate, bool, _CharT, + int, unsigned int, long long int, unsigned long long int, + float, double, long double, + const _CharT*, basic_string_view<_CharT>, + const void*, handle>::value; + if constexpr (__user_facing) + return std::forward<_Visitor>(__vis)(__val); + else + { + handle __h(__val); + return std::forward<_Visitor>(__vis)(__h); + } + }, __type); + } }; template @@ -3662,12 +3690,19 @@ namespace __format inline decltype(auto) visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { - return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); + return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); } /// @cond undocumented namespace __format { + template + inline decltype(auto) + __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx> __arg) + { + return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); + } + struct _WidthPrecVisitor { template @@ -3699,7 +3734,7 @@ namespace __format template inline size_t __int_from_arg(const basic_format_arg<_Context>& __arg) - { return std::visit_format_arg(_WidthPrecVisitor(), __arg); } + { return __format::__visit_format_arg(_WidthPrecVisitor(), __arg); } // Pack _Arg_t enum values into a single 60-bit integer. template @@ -4152,7 +4187,7 @@ namespace __format using _Context = basic_format_context<_Out, _CharT>; using handle = typename basic_format_arg<_Context>::handle; - std::visit_format_arg([this](auto& __arg) { + __format::__visit_format_arg([this](auto& __arg) { using _Type = remove_reference_t; using _Formatter = typename _Context::template formatter_type<_Type>; if constexpr (is_same_v<_Type, monostate>) @@ -4254,7 +4289,7 @@ namespace __format if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}') { bool __done = false; - std::visit_format_arg([&](auto& __arg) { + __format::__visit_format_arg([&](auto& __arg) { using _Tp = remove_cvref_t; if constexpr (is_same_v<_Tp, bool>) { diff --git a/libstdc++-v3/testsuite/std/format/arguments/args.cc b/libstdc++-v3/testsuite/std/format/arguments/args.cc index 2cea0a1b63f..8379528a3dd 100644 --- a/libstdc++-v3/testsuite/std/format/arguments/args.cc +++ b/libstdc++-v3/testsuite/std/format/arguments/args.cc @@ -148,9 +148,82 @@ test_member_visit() #endif } +template +void test_visited_as_handle() +{ + T v{}; + auto store = std::make_format_args(v); + std::format_args args = store; + + constexpr auto is_handle = [](auto arg) { + return std::is_same_v; + }; + VERIFY( std::visit_format_arg(is_handle, args.get(0)) ); +#if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit + VERIFY( args.get(0).visit(is_handle) ); +#endif +} + +template +void test_visited_as() +{ + auto v = static_cast(1.0); + auto store = std::make_format_args(v); + std::format_args args = store; + + auto is_expected_val = [v](auto arg) { + if constexpr (std::is_same_v) + return arg == static_cast(v); + return false; + }; + VERIFY( std::visit_format_arg(is_expected_val, args.get(0)) ); +#if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit + VERIFY( args.get(0).visit(is_expected_val) ); +#endif +} + +template +concept can_format = std::is_default_constructible_v>; + int main() { test_empty(); test_args(); test_member_visit(); + +#ifdef __SIZEOF_INT128__ + test_visited_as_handle<__int128>(); + test_visited_as_handle(); +#endif +// TODO: This should be passed visited as handle. +#ifdef __STDCPP_FLOAT16_T__ + if constexpr (can_format<_Float16>) + test_visited_as(); +#endif +#ifdef __STDCPP_BFLOAT16_T__ + if constexpr (can_format<__gnu_cxx::__bfloat16_t>) + test_visited_as(); +#endif +#ifdef __FLT32_DIG__ + if constexpr (can_format<_Float32>) + test_visited_as(); +#endif +#ifdef __FLT64_DIG__ + if constexpr (can_format<_Float64>) + test_visited_as(); +#endif +#ifdef __FLT128_DIG__ + if constexpr (can_format<_Float128>) +# ifdef _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128 + test_visited_as(); +# else + test_visited_as_handle<_Float128>(); +# endif +#endif +#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT + if constexpr (!std::is_same_v<__ieee128, long double>) + test_visited_as_handle<__ieee128>(); + if constexpr (!std::is_same_v<__ibm128, long double>) + test_visited_as_handle<__ibm128>(); +#endif } diff --git a/libstdc++-v3/testsuite/std/format/string.cc b/libstdc++-v3/testsuite/std/format/string.cc index d5f30500139..0933639faf0 100644 --- a/libstdc++-v3/testsuite/std/format/string.cc +++ b/libstdc++-v3/testsuite/std/format/string.cc @@ -113,11 +113,14 @@ test_format_spec() VERIFY( ! is_format_string_for("{:0c}", 'c') ); VERIFY( ! is_format_string_for("{:0s}", true) ); - // Dynamic width arg must be an integer type. + // Dynamic width arg must be an standar integer type. VERIFY( ! is_format_string_for("{:{}d}", 1, 1.5) ); VERIFY( ! is_format_string_for("{:{}d}", 1, true) ); VERIFY( ! is_format_string_for("{:{}d}", 1, "str") ); VERIFY( ! is_format_string_for("{:{}d}", 1, nullptr) ); +#ifdef __SIZEOF_INT128__ + VERIFY( ! is_format_string_for("{:{}d}", 1, static_cast<__int128>(1)) ); +#endif // Precision only valid for string and floating-point types. VERIFY( ! is_format_string_for("{:.3d}", 1) ); @@ -126,11 +129,14 @@ test_format_spec() VERIFY( ! is_format_string_for("{:3.3s}", 'c') ); VERIFY( ! is_format_string_for("{:3.3p}", nullptr) ); - // Dynamic precision arg must be an integer type. + // Dynamic precision arg must be an standard integer type. VERIFY( ! is_format_string_for("{:.{}f}", 1.0, 1.5) ); VERIFY( ! is_format_string_for("{:.{}f}", 1.0, true) ); VERIFY( ! is_format_string_for("{:.{}f}", 1.0, "str") ); VERIFY( ! is_format_string_for("{:.{}f}", 1.0, nullptr) ); +#ifdef __SIZEOF_INT128__ + VERIFY( ! is_format_string_for("{:{}f}", 1.0, static_cast(1)) ); +#endif // Invalid presentation types for integers. VERIFY( ! is_format_string_for("{:f}", 1) );