From patchwork Tue Jul 16 11:55:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1961060 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Keni8nL7; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WNd8P1DLFz1xrQ for ; Tue, 16 Jul 2024 22:06:29 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6A5263840C14 for ; Tue, 16 Jul 2024 12:06:27 +0000 (GMT) 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.133.124]) by sourceware.org (Postfix) with ESMTPS id A766B384A435 for ; Tue, 16 Jul 2024 12:04:45 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A766B384A435 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 A766B384A435 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721131495; cv=none; b=D4Nfmb7JjmT29FhClJYQW0Pg8ZgivZ90QRt88Mu6CbzHhkUh40Oo5SVmw6/Lge37z4wHMChrDtOrgyCgHpx8LzgxcqR2NvS4u7VYZvEiJx5JmIDX4GYDeMhf5hMOWlTPdasshW69KyWM3KYZ/TodO9g3To9O+8rQc2+xVx4fVGY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721131495; c=relaxed/simple; bh=jYVYVRUIE6fZOsesq2EQZApskhod5ez1xzapP91dtKg=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=Q96SZ6rZARY0SG5GaeJYGsDryxObhVIuoZI1EUCDfjrBGS1JkUErrJByzZ/m4yisSimlHO7PekdSYrbykolsQrZ5Fe4pkan5iQ1qaGif2EifbXCqxZtpDm0F/O03uQc3cOnGWFePlT7rbqa1NMbX3Gmd7F+CrxDMfcoGBjArp8Q= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1721131485; 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=ghgtJx8mFLtFs81JuaVAXbzSuc3A2u8QqxM437RHhYw=; b=Keni8nL7t6tE6pdpXoY6C3J/xG+M4AyRivncWLvGdnzbeS4dDaq2X8WMIBXVBcqByFwdnI eby108uH/4yw7W4lcs8gBw9GTXnAyBUxRmdDAGcFCdrTkEm4s4BcN62FV87sunKCJ0A62w lA0QVmBYUcmQYbBhTVUxlTU82PmtH3k= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-586-rVAEo3_JNIaQ07s0nIlcDA-1; Tue, 16 Jul 2024 08:04:41 -0400 X-MC-Unique: rVAEo3_JNIaQ07s0nIlcDA-1 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (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-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id C3AB01955D52; Tue, 16 Jul 2024 12:04:39 +0000 (UTC) Received: from localhost (unknown [10.42.28.14]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 0E4E019560B2; Tue, 16 Jul 2024 12:04:38 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [PATCH v3 1/6] libstdc++: Handle encodings in localized chrono formatting [PR109162] Date: Tue, 16 Jul 2024 12:55:57 +0100 Message-ID: <20240716120436.2135312-1-jwakely@redhat.com> In-Reply-To: <20240711232322.1264807-1-jwakely@redhat.com> References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-9.0 required=5.0 tests=BAYES_00, BODY_8BITS, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, RCVD_IN_SBL_CSS, 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~incoming=patchwork.ozlabs.org@gcc.gnu.org On Fri, 12 Jul 2024 at 00:23, I wrote: > > I sent v1 of this patch in February, and it added the new symbols to > libstdc++exp.a which meant users needed to use -lstdc++exp to format > chrono types in C++23 mode. That was less than ideal. > > This v2 patch adds the new symbols to the main library, which means no > extra step to get the new features, and we can enable them as a DR for > C++20 mode. But that means we need new exports in the shared library, > and so need to be more confident that the feature is stable and ready to > go into the lib. > > I'm not 100% confident that we want to add a new, private facet to the > std::locale, but it seems reasonable. And that's not exposed to users at > all, as the two new symbols added to the library hide the creation and > use of that facet. Here's v3, which fixes a missing export of the __sso_string constructors and destructors, needed so that the old ABI can use the new function to transcode a locale-specific string to UTF-8, with a std::string buffer. I haven't done so here, but we could keep a least recently used cache of __encoding facets, so that repeatedly calling std::format with the same locale doesn't need to keep re-checking the locale's encoding and then re-opening the same iconv_t descriptor. This v3 patch also tweaks the commented out parts of include/bits/version.def in preparation for enabling the C++26 features in the following patches in this series. Tested x86_64-linux. I think this is ready to push now, but I'll wait a bit for any comments on it. -- >8 -- This implements the C++23 paper P2419R2 (Clarify handling of encodings in localized formatting of chrono types). The requirement is that when the literal encoding is "a Unicode encoding form" and the formatting locale uses a different encoding, any locale-specific strings such as "août" for std::chrono::August should be converted to the literal encoding. Using the recently-added std::locale::encoding() function we can check the locale's encoding and then use iconv if a conversion is needed. Because nl_langinfo_l and iconv_open both allocate memory, a naive implementation would perform multiple allocations and deallocations for every snippet of locale-specific text that needs to be converted to UTF-8. To avoid that, a new internal locale::facet is defined to store the text_encoding and an iconv_t descriptor, which are then cached in the formatting locale. This requires access to the internals of a std::locale object in src/c++20/format.cc, so that new file needs to be compiled with -fno-access-control, as well as -std=gnu++26 in order to use std::text_encoding. Because the new std::text_encoding and std::locale::encoding() symbols are only in the libstdc++exp.a archive, we need to include src/c++26/text_encoding.cc in the main library, but not export its symbols yet. This means they can be used by the two new functions which are exported from the main library. The encoding conversions are done for C++20, treating it as a DR that resolves LWG 3656. With this change we can increase the value of the __cpp_lib_format macro for C++23. The value should be 202207 for P2419R2, but we already implement P2510R3 (Formatting pointers) so can use the value 202304. libstdc++-v3/ChangeLog: PR libstdc++/109162 * acinclude.m4 (libtool_VERSION): Update to 6:34:0. * config/abi/pre/gnu.ver: Disambiguate old patters. Add new GLIBCXX_3.4.34 symbol version and new exports. * configure: Regenerate. * include/bits/chrono_io.h (_ChronoSpec::_M_locale_specific): Add new accessor functions to use a reserved bit in _Spec. (__formatter_chrono::_M_parse): Use _M_locale_specific(true) when chrono-specs contains locale-dependent conversion specifiers. (__formatter_chrono::_M_format): Open iconv descriptor if conversion to UTF-8 will be needed. (__formatter_chrono::_M_write): New function to write a localized string with possible character conversion. (__formatter_chrono::_M_a_A, __formatter_chrono::_M_b_B) (__formatter_chrono::_M_p, __formatter_chrono::_M_r) (__formatter_chrono::_M_x, __formatter_chrono::_M_X) (__formatter_chrono::_M_locale_fmt): Use _M_write. * include/bits/version.def (format): Update value. * include/bits/version.h: Regenerate. * include/std/format (_GLIBCXX_P2518R3): Check feature test macro instead of __cplusplus. (basic_format_context): Declare __formatter_chrono as friend. * src/c++20/Makefile.am: Add new file. * src/c++20/Makefile.in: Regenerate. * src/c++20/format.cc: New file. * testsuite/std/time/format_localized.cc: New test. * testsuite/util/testsuite_abi.cc: Add new symbol version. --- libstdc++-v3/acinclude.m4 | 2 +- libstdc++-v3/config/abi/pre/gnu.ver | 18 +- libstdc++-v3/configure | 2 +- libstdc++-v3/include/bits/chrono_io.h | 96 ++++++++-- libstdc++-v3/include/bits/version.def | 29 ++- libstdc++-v3/include/bits/version.h | 4 +- libstdc++-v3/include/std/format | 16 +- libstdc++-v3/src/c++20/Makefile.am | 8 +- libstdc++-v3/src/c++20/Makefile.in | 10 +- libstdc++-v3/src/c++20/format.cc | 174 ++++++++++++++++++ .../testsuite/std/time/format_localized.cc | 47 +++++ libstdc++-v3/testsuite/util/testsuite_abi.cc | 1 + 12 files changed, 378 insertions(+), 29 deletions(-) create mode 100644 libstdc++-v3/src/c++20/format.cc create mode 100644 libstdc++-v3/testsuite/std/time/format_localized.cc diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 index e04aae25360..e4ed583b3ae 100644 --- a/libstdc++-v3/acinclude.m4 +++ b/libstdc++-v3/acinclude.m4 @@ -4230,7 +4230,7 @@ changequote([,])dnl fi # For libtool versioning info, format is CURRENT:REVISION:AGE -libtool_VERSION=6:33:0 +libtool_VERSION=6:34:0 # Everything parsed; figure out what files and settings to use. case $enable_symvers in diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 31449b5b87b..ae79b371d80 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -109,7 +109,11 @@ GLIBCXX_3.4 { std::[j-k]*; # std::length_error::l*; # std::length_error::~l*; - std::locale::[A-Za-e]*; + # std::locale::[A-Za-d]*; + std::locale::all; + std::locale::classic*; + std::locale::collate; + std::locale::ctype; std::locale::facet::[A-Za-z]*; std::locale::facet::_S_get_c_locale*; std::locale::facet::_S_clone_c_locale*; @@ -168,7 +172,7 @@ GLIBCXX_3.4 { std::strstream*; std::strstreambuf*; # std::t[a-q]*; - std::t[a-g]*; + std::terminate*; std::th[a-h]*; std::th[j-q]*; std::th[s-z]*; @@ -2528,6 +2532,16 @@ GLIBCXX_3.4.33 { _ZNKSt12__basic_fileIcE13native_handleEv; } GLIBCXX_3.4.32; +# GCC 15.1.0 +GLIBCXX_3.4.34 { + # std::__format::__with_encoding_conversion + _ZNSt8__format26__with_encoding_conversionERKSt6locale; + # std::__format::__locale_encoding_to_utf8 + _ZNSt8__format25__locale_encoding_to_utf8ERKSt6localeSt17basic_string_viewIcSt11char_traitsIcEEPv; + # __sso_string constructor and destructor + _ZNSt12__sso_string[CD][12]Ev; +} GLIBCXX_3.4.33; + # Symbols in the support library (libsupc++) have their own tag. CXXABI_1.3 { diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure index 5645e991af7..fe525308ae2 100755 --- a/libstdc++-v3/configure +++ b/libstdc++-v3/configure @@ -51040,7 +51040,7 @@ $as_echo "$as_me: WARNING: === Symbol versioning will be disabled." >&2;} fi # For libtool versioning info, format is CURRENT:REVISION:AGE -libtool_VERSION=6:33:0 +libtool_VERSION=6:34:0 # Everything parsed; figure out what files and settings to use. case $enable_symvers in diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index 72c66a0fef0..2f3ba89de61 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -38,8 +38,10 @@ #include // setw, setfill #include #include // from_chars +#include // __sso_string #include +#include namespace std _GLIBCXX_VISIBILITY(default) { @@ -211,6 +213,20 @@ namespace __format struct _ChronoSpec : _Spec<_CharT> { basic_string_view<_CharT> _M_chrono_specs; + + // Use one of the reserved bits in __format::_Spec. + // This indicates that a locale-dependent conversion specifier such as + // %a is used in the chrono-specs. This is not the same as the + // _Spec::_M_localized member which indicates that "L" was present + // in the format-spec, e.g. "{:L%a}" is localized and locale-specific, + // but "{:L}" is only localized and "{:%a}" is only locale-specific. + constexpr bool + _M_locale_specific() const noexcept + { return this->_M_reserved; } + + constexpr void + _M_locale_specific(bool __b) noexcept + { this->_M_reserved = __b; } }; // Represents the information provided by a chrono type. @@ -305,11 +321,12 @@ namespace __format const auto __chrono_specs = __first++; // Skip leading '%' if (*__chrono_specs != '%') __throw_format_error("chrono format error: no '%' at start of " - "chrono-specs"); + "chrono-specs"); _CharT __mod{}; bool __conv = true; int __needed = 0; + bool __locale_specific = false; while (__first != __last) { @@ -322,15 +339,18 @@ namespace __format case 'a': case 'A': __needed = _Weekday; + __locale_specific = true; break; case 'b': case 'h': case 'B': __needed = _Month; + __locale_specific = true; break; case 'c': __needed = _DateTime; __allowed_mods = _Mod_E; + __locale_specific = true; break; case 'C': __needed = _Year; @@ -368,6 +388,8 @@ namespace __format break; case 'p': case 'r': + __locale_specific = true; + [[fallthrough]]; case 'R': case 'T': __needed = _TimeOfDay; @@ -393,10 +415,12 @@ namespace __format break; case 'x': __needed = _Date; + __locale_specific = true; __allowed_mods = _Mod_E; break; case 'X': __needed = _TimeOfDay; + __locale_specific = true; __allowed_mods = _Mod_E; break; case 'y': @@ -436,6 +460,8 @@ namespace __format || (__mod == 'O' && !(__allowed_mods & _Mod_O))) __throw_format_error("chrono format error: invalid " " modifier in chrono-specs"); + if (__mod && __c != 'z') + __locale_specific = true; __mod = _CharT(); if ((__parts & __needed) != __needed) @@ -467,6 +493,7 @@ namespace __format _M_spec = __spec; _M_spec._M_chrono_specs = __string_view(__chrono_specs, __first - __chrono_specs); + _M_spec._M_locale_specific(__locale_specific); return __first; } @@ -486,6 +513,24 @@ namespace __format if (__first == __last) return _M_format_to_ostream(__t, __fc, __is_neg); +#if __glibcxx_format >= 202207L // C++ >= 23 + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3565. Handling of encodings in localized formatting + // of chrono types is underspecified + if constexpr (is_same_v<_CharT, char>) + if constexpr (__unicode::__literal_encoding_is_utf8()) + if (_M_spec._M_localized && _M_spec._M_locale_specific()) + { + extern locale __with_encoding_conversion(const locale&); + + // Allocate and cache the necessary state to convert strings + // in the locale's encoding to UTF-8. + locale __loc = __fc.locale(); + if (__loc != locale::classic()) + __fc._M_loc = __with_encoding_conversion(__loc); + } +#endif + _Sink_iter<_CharT> __out; __format::_Str_sink<_CharT> __sink; bool __write_direct = false; @@ -742,6 +787,30 @@ namespace __format static constexpr _CharT _S_space = _S_chars[14]; static constexpr const _CharT* _S_empty_spec = _S_chars + 15; + template + _OutIter + _M_write(_OutIter __out, const locale& __loc, __string_view __s) const + { +#if __glibcxx_format >= 202207L // C++ >= 20 + __sso_string __buf; + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3565. Handling of encodings in localized formatting + // of chrono types is underspecified + if constexpr (is_same_v<_CharT, char>) + if constexpr (__unicode::__literal_encoding_is_utf8()) + if (_M_spec._M_localized && _M_spec._M_locale_specific() + && __loc != locale::classic()) + { + extern string_view + __locale_encoding_to_utf8(const std::locale&, string_view, + void*); + + __s = __locale_encoding_to_utf8(__loc, __s, &__buf); + } +#endif + return __format::__write(std::move(__out), __s); + } + template typename _FormatContext::iterator _M_a_A(const _Tp& __t, typename _FormatContext::iterator __out, @@ -761,7 +830,7 @@ namespace __format else __tp._M_days_abbreviated(__days); __string_view __str(__days[__wd.c_encoding()]); - return __format::__write(std::move(__out), __str); + return _M_write(std::move(__out), __loc, __str); } template @@ -782,7 +851,7 @@ namespace __format else __tp._M_months_abbreviated(__months); __string_view __str(__months[(unsigned)__m - 1]); - return __format::__write(std::move(__out), __str); + return _M_write(std::move(__out), __loc, __str); } template @@ -1059,8 +1128,8 @@ namespace __format const auto& __tp = use_facet<__timepunct<_CharT>>(__loc); const _CharT* __ampm[2]; __tp._M_am_pm(__ampm); - return std::format_to(std::move(__out), _S_empty_spec, - __ampm[__hms.hours().count() >= 12]); + return _M_write(std::move(__out), __loc, + __ampm[__hms.hours().count() >= 12]); } template @@ -1095,8 +1164,9 @@ namespace __format basic_string<_CharT> __fmt(_S_empty_spec); __fmt.insert(1u, 1u, _S_colon); __fmt.insert(2u, __ampm_fmt); - return std::vformat_to(std::move(__out), __fmt, - std::make_format_args<_FormatContext>(__t)); + using _FmtStr = _Runtime_format_string<_CharT>; + return _M_write(std::move(__out), __loc, + std::format(__loc, _FmtStr(__fmt), __t)); } template @@ -1279,8 +1349,9 @@ namespace __format basic_string<_CharT> __fmt(_S_empty_spec); __fmt.insert(1u, 1u, _S_colon); __fmt.insert(2u, __rep); - return std::vformat_to(std::move(__out), __fmt, - std::make_format_args<_FormatContext>(__t)); + using _FmtStr = _Runtime_format_string<_CharT>; + return _M_write(std::move(__out), __loc, + std::format(__loc, _FmtStr(__fmt), __t)); } template @@ -1302,8 +1373,9 @@ namespace __format basic_string<_CharT> __fmt(_S_empty_spec); __fmt.insert(1u, 1u, _S_colon); __fmt.insert(2u, __rep); - return std::vformat_to(std::move(__out), __fmt, - std::make_format_args<_FormatContext>(__t)); + using _FmtStr = _Runtime_format_string<_CharT>; + return _M_write(std::move(__out), __loc, + std::format(__loc, _FmtStr(__fmt), __t)); } template @@ -1580,7 +1652,7 @@ namespace __format const auto& __tp = use_facet>(__loc); __tp.put(__os, __os, _S_space, &__tm, __fmt, __mod); if (__os) - __out = __format::__write(std::move(__out), __os.view()); + __out = _M_write(std::move(__out), __loc, __os.view()); return __out; } }; diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 42cdef2f526..74947301760 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1161,16 +1161,22 @@ ftms = { }; ftms = { + name = format; + // 202304 P2510R3 Formatting pointers + // 202305 P2757R3 Type checking format args + // 202306 P2637R3 Member visit + // 202311 P2918R2 Runtime format strings II + // values = { + // v = 202304; + // cxxmin = 26; + // hosted = yes; + // }; // 201907 Text Formatting, Integration of chrono, printf corner cases. // 202106 std::format improvements. // 202110 Fixing locale handling in chrono formatters, generator-like types. // 202207 Encodings in localized formatting of chrono, basic-format-string. - // 202207 P2286R8 Formatting Ranges - // 202207 P2585R1 Improving default container formatting - // TODO: #define __cpp_lib_format_ranges 202207L - name = format; values = { - v = 202110; + v = 202207; cxxmin = 20; hosted = yes; }; @@ -1374,6 +1380,19 @@ ftms = { }; }; +// ftms = { + // name = format_ranges; + // 202207 P2286R8 Formatting Ranges + // 202207 P2585R1 Improving default container formatting + // LWG3750 Too many papers bump __cpp_lib_format + // TODO: #define __cpp_lib_format_ranges 202207L + // values = { + // v = 202207; + // cxxmin = 23; + // hosted = yes; + // }; +// }; + ftms = { name = freestanding_algorithm; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 1eaf3733bc2..9f8673395da 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1305,9 +1305,9 @@ #if !defined(__cpp_lib_format) # if (__cplusplus >= 202002L) && _GLIBCXX_HOSTED -# define __glibcxx_format 202110L +# define __glibcxx_format 202207L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_format) -# define __cpp_lib_format 202110L +# define __cpp_lib_format 202207L # endif # endif #endif /* !defined(__cpp_lib_format) && defined(__glibcxx_want_format) */ diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index 16cee0d3c74..a4921ce391b 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -2342,10 +2342,10 @@ namespace __format // _GLIBCXX_RESOLVE_LIB_DEFECTS // P2510R3 Formatting pointers -#if __cplusplus > 202302L || ! defined __STRICT_ANSI__ -#define _GLIBCXX_P2518R3 1 +#if __glibcxx_format >= 202304L || ! defined __STRICT_ANSI__ +# define _GLIBCXX_P2518R3 1 #else -#define _GLIBCXX_P2518R3 0 +# define _GLIBCXX_P2518R3 0 #endif #if _GLIBCXX_P2518R3 @@ -3821,6 +3821,9 @@ namespace __format __do_vformat_to(_Out, basic_string_view<_CharT>, const basic_format_args<_Context>&, const locale* = nullptr); + + template struct __formatter_chrono; + } // namespace __format /// @endcond @@ -3831,6 +3834,11 @@ namespace __format * this class template explicitly. For typical uses of `std::format` the * library will use the specializations `std::format_context` (for `char`) * and `std::wformat_context` (for `wchar_t`). + * + * You are not allowed to define partial or explicit specializations of + * this class template. + * + * @since C++20 */ template class basic_format_context @@ -3863,6 +3871,8 @@ namespace __format const basic_format_args<_Context2>&, const locale*); + friend __format::__formatter_chrono<_CharT>; + public: ~basic_format_context() = default; diff --git a/libstdc++-v3/src/c++20/Makefile.am b/libstdc++-v3/src/c++20/Makefile.am index a24505e5141..d0f7859290c 100644 --- a/libstdc++-v3/src/c++20/Makefile.am +++ b/libstdc++-v3/src/c++20/Makefile.am @@ -36,7 +36,7 @@ else inst_sources = endif -sources = tzdb.cc +sources = tzdb.cc format.cc vpath % $(top_srcdir)/src/c++20 @@ -53,6 +53,12 @@ tzdb.o: tzdb.cc tzdata.zi.h $(CXXCOMPILE) -I. -c $< endif +# This needs access to std::text_encoding and to the internals of std::locale. +format.lo: format.cc + $(LTCXXCOMPILE) -std=gnu++26 -fno-access-control -c $< +format.o: format.cc + $(CXXCOMPILE) -std=gnu++26 -fno-access-control -c $< + if GLIBCXX_HOSTED libc__20convenience_la_SOURCES = $(sources) $(inst_sources) else diff --git a/libstdc++-v3/src/c++20/Makefile.in b/libstdc++-v3/src/c++20/Makefile.in index 3ec8c5ce804..d759b8dcc7c 100644 --- a/libstdc++-v3/src/c++20/Makefile.in +++ b/libstdc++-v3/src/c++20/Makefile.in @@ -121,7 +121,7 @@ CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libc__20convenience_la_LIBADD = -am__objects_1 = tzdb.lo +am__objects_1 = tzdb.lo format.lo @ENABLE_EXTERN_TEMPLATE_TRUE@am__objects_2 = sstream-inst.lo @GLIBCXX_HOSTED_TRUE@am_libc__20convenience_la_OBJECTS = \ @GLIBCXX_HOSTED_TRUE@ $(am__objects_1) $(am__objects_2) @@ -432,7 +432,7 @@ headers = @ENABLE_EXTERN_TEMPLATE_TRUE@inst_sources = \ @ENABLE_EXTERN_TEMPLATE_TRUE@ sstream-inst.cc -sources = tzdb.cc +sources = tzdb.cc format.cc @GLIBCXX_HOSTED_FALSE@libc__20convenience_la_SOURCES = @GLIBCXX_HOSTED_TRUE@libc__20convenience_la_SOURCES = $(sources) $(inst_sources) @@ -755,6 +755,12 @@ vpath % $(top_srcdir)/src/c++20 @USE_STATIC_TZDATA_TRUE@tzdb.o: tzdb.cc tzdata.zi.h @USE_STATIC_TZDATA_TRUE@ $(CXXCOMPILE) -I. -c $< +# This needs access to std::text_encoding and to the internals of std::locale. +format.lo: format.cc + $(LTCXXCOMPILE) -std=gnu++26 -fno-access-control -c $< +format.o: format.cc + $(CXXCOMPILE) -std=gnu++26 -fno-access-control -c $< + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/libstdc++-v3/src/c++20/format.cc b/libstdc++-v3/src/c++20/format.cc new file mode 100644 index 00000000000..507bac79e95 --- /dev/null +++ b/libstdc++-v3/src/c++20/format.cc @@ -0,0 +1,174 @@ +// Definitions for formatting -*- C++ -*- + +// Copyright The GNU Toolchain Authors. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +#define _GLIBCXX_USE_CXX11_ABI 1 +#include "../c++26/text_encoding.cc" + +#if defined _GLIBCXX_USE_NL_LANGINFO_L && defined _GLIBCXX_HAVE_ICONV +# include +# include +# include // make_unique +# include // strlen, strcpy +# include +# include +#endif + +namespace std +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION +namespace __format +{ +// Helpers for P2419R2 +// (Clarify handling of encodings in localized formatting of chrono types) +// Convert a string from the locale's charset to UTF-8. + +namespace +{ +// A non-standard locale::facet that caches the locale's std::text_encoding +// and an iconv descriptor for converting from that encoding to UTF-8. +struct __encoding : locale::facet +{ + static locale::id id; + + explicit + __encoding(const text_encoding& enc, size_t refs = 0) + : facet(refs), _M_enc(enc) + { +#if defined _GLIBCXX_HAVE_ICONV + if (enc != text_encoding::UTF8 && enc != text_encoding::ASCII) + _M_cd = ::iconv_open("UTF-8", enc.name()); +#endif + } + + ~__encoding() + { +#if defined _GLIBCXX_HAVE_ICONV + if (_M_has_desc()) + ::iconv_close(_M_cd); +#endif + } + + bool _M_has_desc() const + { +#if defined _GLIBCXX_HAVE_ICONV + return _M_cd != (::iconv_t)-1; +#else + return false; +#endif + } + + text_encoding _M_enc; +#if defined _GLIBCXX_HAVE_ICONV + ::iconv_t _M_cd = (::iconv_t)-1; +#endif +}; + +locale::id __encoding::id; +} // namespace + +std::locale +__with_encoding_conversion(const std::locale& loc) +{ +#if defined _GLIBCXX_USE_NL_LANGINFO_L && __CHAR_BIT__ == 8 + if (std::__try_use_facet<__encoding>(loc)) + return loc; + + string name = loc.name(); + if (name == "C" || name == "*") + return loc; + + text_encoding locenc = __locale_encoding(name.c_str()); + + if (locenc == text_encoding::UTF8 || locenc == text_encoding::ASCII + || locenc == text_encoding::unknown) + return loc; + + auto impl = std::make_unique(*loc._M_impl, 1); + auto facetp = std::make_unique<__encoding>(locenc); + locale loc2(loc, facetp.get()); // FIXME: PR libstdc++/113704 + facetp.release(); + // FIXME: Ideally we wouldn't need to reallocate this string again, + // just don't delete[] it in the locale(locale, Facet*) constructor. + if (const char* name = loc._M_impl->_M_names[0]) + { + loc2._M_impl->_M_names[0] = new char[strlen(name) + 1]; + strcpy(loc2._M_impl->_M_names[0], name); + } + return loc2; +#else + return loc; +#endif +} + +string_view +__locale_encoding_to_utf8(const std::locale& loc, string_view str, + void* poutbuf) +{ +#if defined _GLIBCXX_USE_NL_LANGINFO_L && __CHAR_BIT__ == 8 \ + && _GLIBCXX_HAVE_ICONV + string& outbuf = *static_cast(poutbuf); + // Don't need to use __try_use_facet with its dynamic_cast<__encoding*>, + // since we know there are no types derived from __encoding. If the array + // element is non-null, we have the facet. + auto id = __encoding::id._M_id(); + auto enc_facet = static_cast(loc._M_impl->_M_facets[id]); + if (!enc_facet || !enc_facet->_M_has_desc()) + return str; + + size_t inbytesleft = str.size(); + size_t written = 0; + bool done = false; + + auto overwrite = [&](char* p, size_t n) { + auto inbytes = const_cast(str.data()) + str.size() - inbytesleft; + char* outbytes = p + written; + size_t outbytesleft = n - written; + size_t res = ::iconv(enc_facet->_M_cd, &inbytes, &inbytesleft, + &outbytes, &outbytesleft); + if (res == (size_t)-1) + { + if (errno != E2BIG) + { + done = true; + return 0zu; + } + } + else + done = true; + written = outbytes - p; + return written; + }; + do + outbuf.resize_and_overwrite(outbuf.capacity() + (inbytesleft * 3 / 2), + overwrite); + while (!done); + if (outbuf.size()) + str = outbuf; +#endif // USE_NL_LANGINFO_L && CHAR_BIT == 8 && HAVE_ICONV + + return str; +} +} // namespace __format +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std diff --git a/libstdc++-v3/testsuite/std/time/format_localized.cc b/libstdc++-v3/testsuite/std/time/format_localized.cc new file mode 100644 index 00000000000..2e553110f03 --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/format_localized.cc @@ -0,0 +1,47 @@ +// { dg-do run { target c++20 } } +// { dg-require-namedlocale "ru_UA.koi8u" } +// { dg-require-namedlocale "es_ES.ISO8859-1" } +// { dg-require-namedlocale "fr_FR.ISO8859-1" } +// { dg-require-effective-target cxx11_abi } + +// P2419R2 +// Clarify handling of encodings in localized formatting of chrono types + +// Localized date-time strings such as "février" should be converted to UTF-8 +// if the locale uses a different encoding. + +#include +#include +#include + +void +test_ru() +{ + std::locale loc("ru_UA.koi8u"); + auto s = std::format(loc, "День недели: {:L}", std::chrono::Monday); + VERIFY( s == "День недели: Пн" ); +} + +void +test_es() +{ + std::locale loc(ISO_8859(1,es_ES)); + auto s = std::format(loc, "Día de la semana: {:L%A %a}", std::chrono::Wednesday); + VERIFY( s == "Día de la semana: miércoles mié" ); +} + +void +test_fr() +{ + std::locale loc(ISO_8859(1,fr_FR)); + auto s = std::format(loc, "Six mois après {0:L%b}, c'est {1:L%B}.", + std::chrono::February, std::chrono::August); + VERIFY( s == "Six mois après févr., c'est août." ); +} + +int main() +{ + test_ru(); + test_es(); + test_fr(); +} diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc b/libstdc++-v3/testsuite/util/testsuite_abi.cc index ec7c3df9ecc..ce9cda660fa 100644 --- a/libstdc++-v3/testsuite/util/testsuite_abi.cc +++ b/libstdc++-v3/testsuite/util/testsuite_abi.cc @@ -215,6 +215,7 @@ check_version(symbol& test, bool added) known_versions.push_back("GLIBCXX_3.4.31"); known_versions.push_back("GLIBCXX_3.4.32"); known_versions.push_back("GLIBCXX_3.4.33"); + known_versions.push_back("GLIBCXX_3.4.34"); known_versions.push_back("GLIBCXX_LDBL_3.4.31"); known_versions.push_back("GLIBCXX_IEEE128_3.4.29"); known_versions.push_back("GLIBCXX_IEEE128_3.4.30"); From patchwork Tue Jul 16 11:55:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1961058 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=JqaL+W5o; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WNd7P458Yz1xrQ for ; Tue, 16 Jul 2024 22:05:37 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E4E863839143 for ; Tue, 16 Jul 2024 12:05:34 +0000 (GMT) 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.133.124]) by sourceware.org (Postfix) with ESMTPS id F28A3384A4B6 for ; Tue, 16 Jul 2024 12:04:44 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org F28A3384A4B6 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 F28A3384A4B6 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721131495; cv=none; b=wHPxX5TJHOE5lqWvwdLh+yASopPTI9RUHdsKcEYFvO0rKROSXLL40vrHmJR4wRKA+2IV7KENn+OAdNqkuDze7djXiwLpJxXRm7FQM69l+52ezxnoIdku669YmVIERCmCiHX1b1N9OQpNhKZo1whmevfrO2AxPl9uC8dzgyM/Uhk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721131495; c=relaxed/simple; bh=Bn/jgeY8g2MNOO+Yy+55CBlzhhMNoPGbQ9tuLLzr42c=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=WyP3su/DmkynyjqvJ66sFDqDO8C6WmJSrv+tbFoCN1W7DUMlgnliL1nZfOSrlTJZ4vrjxb5LC/lfCMcDZeAcUQ25PP10+axlJ03KAYSQUhH+dXQyx3NjGyRfV08m43oq/+1F1eXDrxV2iSWvjqBFMobSl55FkEq7pQuqFZjZldo= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1721131484; 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=M+KcO7glLsAOU2Ycr7c4L+lMF0NPGQxeuW16L7tcIIQ=; b=JqaL+W5oNFug5+SL0N/PesNH6Q4ru7CNbhfv9XsUJNC+LAjBKCJO6KlRCbW5H2jDgE5i5p KBjbRTrZ6kTPQp931U7BQ21le3YG8dnEcSeHc1OppvI/lmz1SJzJj6Zirq3w3VZy/34M/u rhr10uKHQrO0Hf67qxuNdM9r/wgu+Bk= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-602-3VRLIIqiNO-1dZ0JbXndcw-1; Tue, 16 Jul 2024 08:04:43 -0400 X-MC-Unique: 3VRLIIqiNO-1dZ0JbXndcw-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id B3D0619560A2; Tue, 16 Jul 2024 12:04:41 +0000 (UTC) Received: from localhost (unknown [10.42.28.14]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1B78A1955F40; Tue, 16 Jul 2024 12:04:40 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [PATCH 2/6] libstdc++: Support P2510R3 "Formatting pointers" as a DR for C++20 Date: Tue, 16 Jul 2024 12:55:58 +0100 Message-ID: <20240716120436.2135312-2-jwakely@redhat.com> In-Reply-To: <20240716120436.2135312-1-jwakely@redhat.com> References: <20240716120436.2135312-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_NUMSUBJECT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, RCVD_IN_SBL_CSS, 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~incoming=patchwork.ozlabs.org@gcc.gnu.org Tested x86_64-linux. -- >8 -- We already enable this for -std=gnu++20 but we can do it for -std=c++20 too. Both libc++ and MSVC also treat this change as a DR for C++20. Now that the previous change to the value of __cpp_lib_format is supported, we can finally update it to 202304 to indicate support for this feature too. libstdc++-v3/ChangeLog: * include/bits/version.def (format): Update value for P2510R3. * include/bits/version.h: Regenerate. * include/std/format (_GLIBCXX_P2518R3): Remove misspelled macro and check __glibcxx_format instead. * testsuite/std/format/functions/format.cc: Check value of the __cpp_lib_format macro for formatting pointers support. * testsuite/std/format/parse_ctx.cc: Likewise. --- libstdc++-v3/include/bits/version.def | 6 +++--- libstdc++-v3/include/bits/version.h | 4 ++-- libstdc++-v3/include/std/format | 16 ++++------------ .../testsuite/std/format/functions/format.cc | 2 +- libstdc++-v3/testsuite/std/format/parse_ctx.cc | 4 ++-- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 74947301760..1acc9cd5cb9 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1162,12 +1162,11 @@ ftms = { ftms = { name = format; - // 202304 P2510R3 Formatting pointers // 202305 P2757R3 Type checking format args // 202306 P2637R3 Member visit // 202311 P2918R2 Runtime format strings II // values = { - // v = 202304; + // v = 202305; // cxxmin = 26; // hosted = yes; // }; @@ -1175,8 +1174,9 @@ ftms = { // 202106 std::format improvements. // 202110 Fixing locale handling in chrono formatters, generator-like types. // 202207 Encodings in localized formatting of chrono, basic-format-string. + // 202304 P2510R3 Formatting pointers values = { - v = 202207; + v = 202304; cxxmin = 20; hosted = yes; }; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 9f8673395da..5cd77770e21 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1305,9 +1305,9 @@ #if !defined(__cpp_lib_format) # if (__cplusplus >= 202002L) && _GLIBCXX_HOSTED -# define __glibcxx_format 202207L +# define __glibcxx_format 202304L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_format) -# define __cpp_lib_format 202207L +# define __cpp_lib_format 202304L # endif # endif #endif /* !defined(__cpp_lib_format) && defined(__glibcxx_want_format) */ diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index a4921ce391b..487177b5294 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -2342,13 +2342,7 @@ namespace __format // _GLIBCXX_RESOLVE_LIB_DEFECTS // P2510R3 Formatting pointers -#if __glibcxx_format >= 202304L || ! defined __STRICT_ANSI__ -# define _GLIBCXX_P2518R3 1 -#else -# define _GLIBCXX_P2518R3 0 -#endif - -#if _GLIBCXX_P2518R3 +#if __glibcxx_format >= 202304L __first = __spec._M_parse_zero_fill(__first, __last); if (__finished()) return __first; @@ -2360,11 +2354,9 @@ namespace __format { if (*__first == 'p') ++__first; -#if _GLIBCXX_P2518R3 +#if __glibcxx_format >= 202304L else if (*__first == 'P') { - // _GLIBCXX_RESOLVE_LIB_DEFECTS - // P2510R3 Formatting pointers __spec._M_type = __format::_Pres_P; ++__first; } @@ -2388,7 +2380,7 @@ namespace __format int __n = __ptr - __buf; __buf[0] = '0'; __buf[1] = 'x'; -#if _GLIBCXX_P2518R3 +#if __glibcxx_format >= 202304L if (_M_spec._M_type == __format::_Pres_P) { __buf[1] = 'X'; @@ -2413,7 +2405,7 @@ namespace __format } #endif -#if _GLIBCXX_P2518R3 +#if __glibcxx_format >= 202304L if (_M_spec._M_zero_fill) { size_t __width = _M_spec._M_get_width(__fc); diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc index 78cc1ab482a..5152bb0b0d0 100644 --- a/libstdc++-v3/testsuite/std/format/functions/format.cc +++ b/libstdc++-v3/testsuite/std/format/functions/format.cc @@ -458,7 +458,7 @@ test_pointer() s = std::format("{:20} {:20p}", p, pc); VERIFY( s == (str_int + ' ' + str_int) ); -#if __cplusplus > 202302L || ! defined __STRICT_ANSI__ +#if __cpp_lib_format >= 202304L // P2510R3 Formatting pointers s = std::format("{:06} {:07P} {:08p}", (void*)0, (const void*)0, nullptr); VERIFY( s == "0x0000 0X00000 0x000000" ); diff --git a/libstdc++-v3/testsuite/std/format/parse_ctx.cc b/libstdc++-v3/testsuite/std/format/parse_ctx.cc index 497427b54e5..3b3201c2a47 100644 --- a/libstdc++-v3/testsuite/std/format/parse_ctx.cc +++ b/libstdc++-v3/testsuite/std/format/parse_ctx.cc @@ -266,8 +266,8 @@ test_pointer() VERIFY( ! is_std_format_spec_for("G") ); VERIFY( ! is_std_format_spec_for("+p") ); -#if __cplusplus > 202302L || ! defined __STRICT_ANSI__ - // As an extension, we support P2510R3 Formatting pointers +#if __cpp_lib_format >= 202304L + // P2510R3 Formatting pointers VERIFY( is_std_format_spec_for("P") ); VERIFY( is_std_format_spec_for("0p") ); VERIFY( is_std_format_spec_for("0P") ); From patchwork Tue Jul 16 11:55:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1961061 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=cS3+1hju; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WNd8d6T4Pz1xrQ for ; Tue, 16 Jul 2024 22:06:41 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 3130A384A439 for ; Tue, 16 Jul 2024 12:06:40 +0000 (GMT) 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 8FC20384A04F for ; Tue, 16 Jul 2024 12:04:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 8FC20384A04F 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 8FC20384A04F 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=1721131500; cv=none; b=swF+DkEWiMiFGrpydYGTbgVFHzURY1FWfeYFk4Tvay+a1irJyNmf1QnFT9UZVqeZCmTevZjXOI7cMAWiTIV36aubYPULgbHuDAumry243bHVERvg8b0L7i+yhomzfj96DzOrzkwSRKVeVyDyGUny8rix10yFBWkHKdI0Rp4RD8g= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721131500; c=relaxed/simple; bh=axiJ8qlm5igQbegqNbG7whP7UBeun+0P9JAbE9NBvos=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=jxnF1ItwmPaO2ItH0LT1np7SYeisg6AM/lWT+BNVz7oYelqBZrUx6A7ft1NAh7VflHRLDbZyscCrBvGkFxqhEbGXm76pl6T5MrQiDZIWSZub7yGQX37oPKWZFhPoy/Tf6/2jb+uyg4cBIFMcdpSd5f0iWGYhDlF6GQsE52ylI4E= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1721131489; 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=yJwPgf11mcgr6cs94FIpKxrIwI8RcnyMxTOzQnzht/Y=; b=cS3+1hjuz7wImLL1S/dfkIGQIUOpkALLmKme8yQ1B2QPrmPwXVZdfI2WO6jRm5ZtOSOX+d 0Qj2y5vH+Rohh1++L613HJf4mJpXi9Ok4eh00/xh6FpCXe2b3qti1M4RVojedfktpFqu/I PGrmIno9paViUn26aw7rHy3IXvue/qU= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-15-sBpz57DoPVi82-3Ov__FwA-1; Tue, 16 Jul 2024 08:04:45 -0400 X-MC-Unique: sBpz57DoPVi82-3Ov__FwA-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 16B5A1955D4A; Tue, 16 Jul 2024 12:04:44 +0000 (UTC) Received: from localhost (unknown [10.42.28.14]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 643911955F40; Tue, 16 Jul 2024 12:04:43 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [PATCH 3/6] libstdc++: Implement C++26 type checking for std::format args [PR115776] Date: Tue, 16 Jul 2024 12:55:59 +0100 Message-ID: <20240716120436.2135312-3-jwakely@redhat.com> In-Reply-To: <20240716120436.2135312-1-jwakely@redhat.com> References: <20240716120436.2135312-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.5 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_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_SBL_CSS, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=unavailable 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~incoming=patchwork.ozlabs.org@gcc.gnu.org Tested x86_64-linux. -- >8 -- Implement the changes from P2757R3, which enhance the parse context to be able to do type checking on format arguments, and to use that to ensure that args used for width and precisions are integral types. libstdc++-v3/ChangeLog: PR libstdc++/115776 * include/bits/version.def (format): Update for C++26. * include/bits/version.h: Regenerate. * include/std/format (basic_format_parse_context): Remove default argument from constructor and split into two constructors. Make the constructor taking size_t private for C++26 and later. (basic_format_parse_context::check_dynamic_spec): New member function template. (basic_format_parse_context::check_dynamic_spec_integral): New member function. (basic_format_parse_context::check_dynamic_spec_string): Likewise. (__format::_Spec::_S_parse_width_or_precision): Use check_dynamic_spec_integral. (__format::__to_arg_t_enum): New helper function. (basic_format_arg): Declare __to_arg_t_enum as friend. (__format::_Scanner): Define and use a derived parse context type. (__format::_Checking_scanner): Make arg types available to parse context. * testsuite/std/format/functions/format.cc: Check for new values of __cpp_lib_format macro. * testsuite/std/format/parse_ctx.cc: Check all members of basic_format_parse_context. * testsuite/std/format/parse_ctx_neg.cc: New test. * testsuite/std/format/string.cc: Add more checks for dynamic width and precision args. --- libstdc++-v3/include/bits/version.def | 10 +- libstdc++-v3/include/bits/version.h | 7 +- libstdc++-v3/include/std/format | 142 ++++++++++++++++- .../testsuite/std/format/functions/format.cc | 4 + .../testsuite/std/format/parse_ctx.cc | 145 ++++++++++++++++++ .../testsuite/std/format/parse_ctx_neg.cc | 39 +++++ libstdc++-v3/testsuite/std/format/string.cc | 13 ++ 7 files changed, 347 insertions(+), 13 deletions(-) create mode 100644 libstdc++-v3/testsuite/std/format/parse_ctx_neg.cc diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 1acc9cd5cb9..bcb33c18aa4 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1165,11 +1165,11 @@ ftms = { // 202305 P2757R3 Type checking format args // 202306 P2637R3 Member visit // 202311 P2918R2 Runtime format strings II - // values = { - // v = 202305; - // cxxmin = 26; - // hosted = yes; - // }; + values = { + v = 202305; + cxxmin = 26; + hosted = yes; + }; // 201907 Text Formatting, Integration of chrono, printf corner cases. // 202106 std::format improvements. // 202110 Fixing locale handling in chrono formatters, generator-like types. diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 5cd77770e21..4d1af34bf8d 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1304,7 +1304,12 @@ #undef __glibcxx_want_barrier #if !defined(__cpp_lib_format) -# if (__cplusplus >= 202002L) && _GLIBCXX_HOSTED +# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED +# define __glibcxx_format 202305L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_format) +# define __cpp_lib_format 202305L +# endif +# elif (__cplusplus >= 202002L) && _GLIBCXX_HOSTED # define __glibcxx_format 202304L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_format) # define __cpp_lib_format 202304L diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index 487177b5294..0e61bc5e6bc 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -222,6 +222,9 @@ namespace __format inline void __failed_to_parse_format_spec() { __throw_format_error("format error: failed to parse format-spec"); } + + template class _Scanner; + } // namespace __format /// @endcond @@ -241,9 +244,8 @@ namespace __format using iterator = const_iterator; constexpr explicit - basic_format_parse_context(basic_string_view<_CharT> __fmt, - size_t __num_args = 0) noexcept - : _M_begin(__fmt.begin()), _M_end(__fmt.end()), _M_num_args(__num_args) + basic_format_parse_context(basic_string_view<_CharT> __fmt) noexcept + : _M_begin(__fmt.begin()), _M_end(__fmt.end()) { } basic_format_parse_context(const basic_format_parse_context&) = delete; @@ -283,13 +285,78 @@ namespace __format __format::__invalid_arg_id_in_format_string(); } +#if __cpp_lib_format >= 202305L + template + constexpr void + check_dynamic_spec(size_t __id) noexcept; + + constexpr void + check_dynamic_spec_integral(size_t __id) noexcept + { + check_dynamic_spec(__id); + } + + constexpr void + check_dynamic_spec_string(size_t __id) noexcept + { + check_dynamic_spec>(__id); + } + + private: + // Check the Mandates: condition for check_dynamic_spec(n) + template + static consteval bool + __check_dynamic_spec_types() + { + if constexpr (sizeof...(_Ts)) + { + int __counts[] = { + (is_same_v + ...), + (is_same_v<_CharT, _Ts> + ...), + (is_same_v + ...), + (is_same_v + ...), + (is_same_v + ...), + (is_same_v + ...), + (is_same_v + ...), + (is_same_v + ...), + (is_same_v + ...), + (is_same_v + ...), + (is_same_v, _Ts> + ...), + (is_same_v + ...) + }; + int __sum = 0; + for (int __c : __counts) + { + __sum += __c; + if (__c > 1) + __invalid_dynamic_spec("non-unique template argument type"); + } + if (__sum != sizeof...(_Ts)) + __invalid_dynamic_spec("disallowed template argument type"); + } + return true; + } + + // This must not be constexpr. + static void __invalid_dynamic_spec(const char*); + + friend __format::_Scanner<_CharT>; +#endif + + // This constructor should only be used by the implementation. + constexpr explicit + basic_format_parse_context(basic_string_view<_CharT> __fmt, + size_t __num_args) noexcept + : _M_begin(__fmt.begin()), _M_end(__fmt.end()), _M_num_args(__num_args) + { } + private: iterator _M_begin; iterator _M_end; enum _Indexing { _Unknown, _Manual, _Auto }; _Indexing _M_indexing = _Unknown; size_t _M_next_arg_id = 0; - size_t _M_num_args; + size_t _M_num_args = 0; }; /// @cond undocumented @@ -549,6 +616,9 @@ namespace __format __pc.check_arg_id(__v); __val = __v; } +#if __cpp_lib_format >= 202305L + __pc.check_dynamic_spec_integral(__val); +#endif ++__first; // past the '}' } return __first; @@ -3205,6 +3275,10 @@ namespace __format template class _Arg_store; + template + consteval _Arg_t + __to_arg_t_enum() noexcept; + } // namespace __format /// @endcond @@ -3486,6 +3560,10 @@ namespace __format friend decltype(auto) visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx>); + template + friend consteval __format::_Arg_t + __format::__to_arg_t_enum() noexcept; + template decltype(auto) _M_visit(_Visitor&& __vis, __format::_Arg_t __type) @@ -3901,7 +3979,11 @@ namespace __format { using iterator = typename basic_format_parse_context<_CharT>::iterator; - basic_format_parse_context<_CharT> _M_pc; + struct _Parse_context : basic_format_parse_context<_CharT> + { + using basic_format_parse_context<_CharT>::basic_format_parse_context; + const _Arg_t* _M_types = nullptr; + } _M_pc; constexpr explicit _Scanner(basic_string_view<_CharT> __str, size_t __nargs = -1) @@ -4061,6 +4143,16 @@ namespace __format } }; + template + consteval _Arg_t + __to_arg_t_enum() noexcept + { + using _Context = __format::__format_context<_CharT>; + using _Fmt_arg = basic_format_arg<_Context>; + using _NormalizedTp = typename _Fmt_arg::template _Normalize<_Tp>; + return _Fmt_arg::template _S_to_enum<_NormalizedTp>(); + } + // Validate a format string for Args. template class _Checking_scanner : public _Scanner<_CharT> @@ -4070,10 +4162,14 @@ namespace __format "std::formatter must be specialized for each type being formatted"); public: - constexpr + consteval _Checking_scanner(basic_string_view<_CharT> __str) : _Scanner<_CharT>(__str, sizeof...(_Args)) - { } + { +#if __cpp_lib_format >= 202305L + this->_M_pc._M_types = _M_types.data(); +#endif + } private: constexpr void @@ -4104,6 +4200,11 @@ namespace __format else __builtin_unreachable(); } + +#if __cpp_lib_format >= 202305L + array<_Arg_t, sizeof...(_Args)> + _M_types{ { __format::__to_arg_t_enum<_CharT, _Args>()... } }; +#endif }; template @@ -4202,6 +4303,33 @@ namespace __format } // namespace __format /// @endcond +#if __cpp_lib_format >= 202305L + template + template + constexpr void + basic_format_parse_context<_CharT>::check_dynamic_spec(size_t __id) noexcept + { + constexpr bool __ok = __check_dynamic_spec_types<_Ts...>(); + + if consteval { + if (__id >= _M_num_args) + __format::__invalid_arg_id_in_format_string(); + if constexpr (sizeof...(_Ts) != 0) + { + using _Parse_context = __format::_Scanner<_CharT>::_Parse_context; + auto __arg = static_cast<_Parse_context*>(this)->_M_types[__id]; + __format::_Arg_t __types[] = { + __format::__to_arg_t_enum<_CharT, _Ts>()... + }; + for (auto __t : __types) + if (__arg == __t) + return; + } + __invalid_dynamic_spec("arg(id) type does not match"); + } + } +#endif + template template requires convertible_to> diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc index 5152bb0b0d0..3c441d711b3 100644 --- a/libstdc++-v3/testsuite/std/format/functions/format.cc +++ b/libstdc++-v3/testsuite/std/format/functions/format.cc @@ -8,6 +8,8 @@ # error "Feature test macro for std::format is missing in " #elif __cpp_lib_format < 202110L # error "Feature test macro for std::format has wrong value in " +#elif __cplusplus > 202302L && __cpp_lib_format < 202305L +# error "Feature test macro for std::format has wrong value in " #endif #ifndef __cpp_lib_format_uchar @@ -22,6 +24,8 @@ # error "Feature test macro for std::format is missing in " #elif __cpp_lib_format < 202110L # error "Feature test macro for std::format has wrong value in " +#elif __cplusplus > 202302L && __cpp_lib_format < 202305L +# error "Feature test macro for std::format has wrong value in " #endif #ifndef __cpp_lib_format_uchar diff --git a/libstdc++-v3/testsuite/std/format/parse_ctx.cc b/libstdc++-v3/testsuite/std/format/parse_ctx.cc index 3b3201c2a47..b0cef2da41b 100644 --- a/libstdc++-v3/testsuite/std/format/parse_ctx.cc +++ b/libstdc++-v3/testsuite/std/format/parse_ctx.cc @@ -3,6 +3,91 @@ #include #include +static_assert(std::is_constructible_v); +static_assert(std::is_constructible_v); + +#if __cpp_lib_format < 202305 +constexpr bool construct_with_num_args = true; +#else +constexpr bool construct_with_num_args = false; +#endif + +static_assert(std::is_constructible_v + == construct_with_num_args); +static_assert(std::is_constructible_v + == construct_with_num_args); + +static_assert( ! std::is_constructible_v); +static_assert( ! std::is_constructible_v); + +static_assert( ! std::is_convertible_v ); +static_assert( ! std::is_convertible_v ); + +static_assert( ! std::is_default_constructible_v ); +static_assert( ! std::is_copy_constructible_v ); +static_assert( ! std::is_move_constructible_v ); +static_assert( ! std::is_copy_assignable_v ); +static_assert( ! std::is_move_assignable_v ); + +// This concept is satisfied if the next_arg_id() call is a constant expression +template> +concept arg_id_available = requires { + typename std::integral_constant::type; +}; + +void +test_members() +{ + std::string_view s = "spec string"; + + std::format_parse_context pc(s); + + VERIFY( pc.begin() == s.begin() ); + VERIFY( pc.end() == s.end() ); + pc.advance_to(s.begin() + 5); + VERIFY( pc.begin() == s.begin() + 5 ); + + // Runtime calls to these do not check for the correct number of args. + VERIFY( pc.next_arg_id() == 0 ); + VERIFY( pc.next_arg_id() == 1 ); + VERIFY( pc.next_arg_id() == 2 ); + try + { + // Cannot mix manual and automatic indexing. + pc.check_arg_id(0); + VERIFY( false ); + } + catch (const std::format_error&) + { + } + // But they do check during constant evaluation: + VERIFY( ! arg_id_available ); + VERIFY( ! arg_id_available ); + + std::format_parse_context pc2(""); + pc2.check_arg_id(2); + pc2.check_arg_id(1); + pc2.check_arg_id(3); + try + { + // Cannot mix manual and automatic indexing. + (void) pc2.next_arg_id(); + VERIFY( false ); + } + catch (const std::format_error&) + { + } +} + template bool is_std_format_spec_for(std::string_view spec) @@ -357,6 +442,65 @@ test_custom() VERIFY( ! is_std_format_spec_for("") ); } +#if __cpp_lib_format >= 202305 +struct X { }; + +template<> +struct std::formatter +{ + constexpr std::format_parse_context::iterator + parse(std::format_parse_context& pc) + { + std::string_view spec(pc.begin(), pc.end()); + auto p = spec.find('}'); + if (p != std::string_view::npos) + spec = spec.substr(0, p); // truncate to closing brace + if (spec == "int") + { + pc.check_dynamic_spec_integral(pc.next_arg_id()); + integer = true; + } + else if (spec == "str") + { + pc.check_dynamic_spec_string(pc.next_arg_id()); + integer = false; + } + else + throw std::format_error("invalid format-spec"); + return pc.begin() + spec.size(); + } + + std::format_context::iterator + format(X, std::format_context& c) const + { + std::visit_format_arg([this](T) { + if (is_integral_v != this->integer) + throw std::format_error("invalid argument type"); + }, c.arg(1)); + return c.out(); + } +private: + bool integer = false; +}; +#endif + +void +test_dynamic_type_check() +{ +#if __cpp_lib_format >= 202305 + std::format_parse_context pc("{1}.{2}"); + + // None of these calls should do anything at runtime, only during consteval: + pc.check_dynamic_spec<>(0); + pc.check_dynamic_spec(0); + pc.check_dynamic_spec_integral(0); + pc.check_dynamic_spec_string(0); + + (void) std::format("{:int}", X{}, 42L); + (void) std::format("{:str}", X{}, "H2G2"); +#endif +} + int main() { test_char(); @@ -366,4 +510,5 @@ int main() test_string(); test_pointer(); test_custom(); + test_dynamic_type_check(); } diff --git a/libstdc++-v3/testsuite/std/format/parse_ctx_neg.cc b/libstdc++-v3/testsuite/std/format/parse_ctx_neg.cc new file mode 100644 index 00000000000..d6a4366d7d0 --- /dev/null +++ b/libstdc++-v3/testsuite/std/format/parse_ctx_neg.cc @@ -0,0 +1,39 @@ +// { dg-do compile { target c++26 } } + +#include + +void +test_invalid() +{ + std::format_parse_context pc(""); + + // These types are all valid: + pc.check_dynamic_spec(0); + // For some reason, an empty pack of types is valid: + pc.check_dynamic_spec<>(0); + + pc.check_dynamic_spec(0); // { dg-error "here" } + // const void* is allowed, but void* is not + pc.check_dynamic_spec(0); // { dg-error "here" } + // int and long long are allowed, but long is not + pc.check_dynamic_spec(0); // { dg-error "here" } + // char_type is allowed, but other character types are not + pc.check_dynamic_spec(0); // { dg-error "here" } + pc.check_dynamic_spec(0); // { dg-error "here" } + // std::string_view is allowed, but std::string is not + pc.check_dynamic_spec(0); // { dg-error "here" } + pc.check_dynamic_spec(0); // { dg-error "here" } + + std::wformat_parse_context wpc(L""); + wpc.check_dynamic_spec(0); + wpc.check_dynamic_spec(0); // { dg-error "here" } + wpc.check_dynamic_spec(0); // { dg-error "here" } + wpc.check_dynamic_spec(0); // { dg-error "here" } +} + +// Each failure above will point to a call to this non-constexpr function: +// { dg-error "__invalid_dynamic_spec" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/std/format/string.cc b/libstdc++-v3/testsuite/std/format/string.cc index ddb3c5625cd..d5f30500139 100644 --- a/libstdc++-v3/testsuite/std/format/string.cc +++ b/libstdc++-v3/testsuite/std/format/string.cc @@ -109,9 +109,16 @@ test_format_spec() VERIFY( ! is_format_string_for("{:#?}", "str") ); VERIFY( ! is_format_string_for("{:#?}", 'c') ); + // The 0 option is not valid for charT and bool. VERIFY( ! is_format_string_for("{:0c}", 'c') ); VERIFY( ! is_format_string_for("{:0s}", true) ); + // Dynamic width arg must be an 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) ); + // Precision only valid for string and floating-point types. VERIFY( ! is_format_string_for("{:.3d}", 1) ); VERIFY( ! is_format_string_for("{:3.3d}", 1) ); @@ -119,6 +126,12 @@ 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. + 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) ); + // Invalid presentation types for integers. VERIFY( ! is_format_string_for("{:f}", 1) ); VERIFY( ! is_format_string_for("{:s}", 1) ); From patchwork Tue Jul 16 11:56:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1961059 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=QZonBXhQ; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WNd7k6NHZz1xrQ for ; Tue, 16 Jul 2024 22:05:54 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2D0EB3839163 for ; Tue, 16 Jul 2024 12:05:53 +0000 (GMT) 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.133.124]) by sourceware.org (Postfix) with ESMTPS id 26D39384A43F for ; Tue, 16 Jul 2024 12:04:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 26D39384A43F 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 26D39384A43F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721131497; cv=none; b=l/1eZAiFSwJQ6quy9mTeaz62P3yFTAqNOn0/gUqzgI8OlWmBOKkZrbc5Bmr6qaoUc4FRqTlPxRCdXuY7AALLyr4QsvgeEpsh9X5LFHcIGpwNXe1oHLgSLnYLLr8jTxRYsT+MsPfZLYaTiSOJKnE2G67hPlDAlNKilV9N8uHs04w= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721131497; c=relaxed/simple; bh=jnxru0QWtQ6L80pS61RIda8IjunwVfBtaDj/uJ5Xh+8=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=fUyqqscaP3y5XqgHHf12ODt2g970vEUiTCJZc/ND4JqtuplRMeVLvS6mkDOqAYUkH7/X4e5Xc+PA74Z7rKTiZhB3oHtOGe3GKEKIe76Ci27GPm0P9KrO4fU+NGp418Gck/AU6siQoitf3Q60F5cYincD1CHCZ7lIxg9VhPLK12Q= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1721131488; 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=EZIUGjidN8i2i/fHJRHUnPxu/s5eFG7E5cP2oEbWCcM=; b=QZonBXhQ3reYSJXfAAThbFeDx1QM/e/6lDlzfdgLmdlVAB+NuSC0keWUHO+h/BwoVeHw5k s9/Dzb8wfMwcUigJZbg4YI5vYSeD/sbZYbCPN9AL3+BuzBzB1WTxzA2k8+qkO0CQQnT5oq DM6iK53r8n/C6AF/db5BklM8grpuSEY= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-678-RdAoPgp7NLS6C4SbpafaqQ-1; Tue, 16 Jul 2024 08:04:47 -0400 X-MC-Unique: RdAoPgp7NLS6C4SbpafaqQ-1 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (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-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4104A1955D52; Tue, 16 Jul 2024 12:04:46 +0000 (UTC) Received: from localhost (unknown [10.42.28.14]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 6EF5A1955D42; Tue, 16 Jul 2024 12:04:45 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [PATCH 4/6] libstdc++: Define C++26 member visit for std::variant [PR110356] Date: Tue, 16 Jul 2024 12:56:00 +0100 Message-ID: <20240716120436.2135312-4-jwakely@redhat.com> In-Reply-To: <20240716120436.2135312-1-jwakely@redhat.com> References: <20240716120436.2135312-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.5 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, RCVD_IN_SBL_CSS, 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~incoming=patchwork.ozlabs.org@gcc.gnu.org Tested x86_64-linux. -- >8 -- Implement the std::variant changes from P2637R3. libstdc++-v3/ChangeLog: PR libstdc++/110356 * include/bits/version.def (variant): Update for C++26. * include/bits/version.h: Regenerate. * include/std/variant (variant::visit): New member functions. * testsuite/20_util/variant/visit.cc: Check second alternative. * testsuite/20_util/variant/visit_member.cc: New test. --- libstdc++-v3/include/bits/version.def | 5 + libstdc++-v3/include/bits/version.h | 7 +- libstdc++-v3/include/std/variant | 48 +++++++ .../testsuite/20_util/variant/visit.cc | 5 +- .../testsuite/20_util/variant/visit_member.cc | 117 ++++++++++++++++++ 5 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/variant/visit_member.cc diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index bcb33c18aa4..806f1e9549b 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -478,6 +478,11 @@ ftms = { ftms = { name = variant; + values = { + v = 202306; + cxxmin = 26; + extra_cond = "__cpp_concepts >= 202002L && __cpp_constexpr >= 201811L && __cpp_explicit_this_parameter"; + }; values = { v = 202106; cxxmin = 20; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 4d1af34bf8d..e8ca0faf5dc 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -529,7 +529,12 @@ #undef __glibcxx_want_type_trait_variable_templates #if !defined(__cpp_lib_variant) -# if (__cplusplus >= 202002L) && (__cpp_concepts >= 202002L && __cpp_constexpr >= 201811L) +# if (__cplusplus > 202302L) && (__cpp_concepts >= 202002L && __cpp_constexpr >= 201811L && __cpp_explicit_this_parameter) +# define __glibcxx_variant 202306L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_variant) +# define __cpp_lib_variant 202306L +# endif +# elif (__cplusplus >= 202002L) && (__cpp_concepts >= 202002L && __cpp_constexpr >= 201811L) # define __glibcxx_variant 202106L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_variant) # define __cpp_lib_variant 202106L diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index 3a23d9bc66d..154aab98812 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -1390,6 +1390,12 @@ namespace __detail::__variant constexpr __detail::__variant::__visit_result_t<_Visitor, _Variants...> visit(_Visitor&&, _Variants&&...); +#if __cplusplus > 201703L + template + constexpr _Res + visit(_Visitor&&, _Variants&&...); +#endif + template _GLIBCXX20_CONSTEXPR inline enable_if_t<(is_move_constructible_v<_Types> && ...) @@ -1758,6 +1764,48 @@ namespace __detail::__variant }, __rhs); } +#if __cpp_lib_variant >= 202306L // >= C++26 + // [variant.visit], visitation + + /** Simple visitation for a single variant + * + * To visit a single variant you can use `var.visit(visitor)` + * instead of `std::visit(visitor, var)`. + * + * @since C++26 + */ + template + constexpr decltype(auto) + visit(this _Self&& __self, _Visitor&& __vis) + { + using _CVar = __conditional_t>, + const variant, variant>; + using _Var = __conditional_t, + _CVar&&, _CVar&>; + static_assert(is_same_v<__like_t<_Self, variant>, _Var>); + return std::visit(std::forward<_Visitor>(__vis), (_Var)__self); + } + + /** Simple visitation for a single variant, with explicit return type + * + * To visit a single variant you can use `var.visit(visitor)` + * instead of `std::visit(visitor, var)`. + * + * @since C++26 + */ + template + constexpr _Res + visit(this _Self&& __self, _Visitor&& __vis) + { + using _CVar = __conditional_t>, + const variant, variant>; + using _Var = __conditional_t, + _CVar&&, _CVar&>; + static_assert(is_same_v<__like_t<_Self, variant>, _Var>); + return std::visit<_Res>(std::forward<_Visitor>(__vis), (_Var)__self); + } +#endif + private: template friend constexpr decltype(auto) diff --git a/libstdc++-v3/testsuite/20_util/variant/visit.cc b/libstdc++-v3/testsuite/20_util/variant/visit.cc index 7f79e6107ab..6edc7d7c028 100644 --- a/libstdc++-v3/testsuite/20_util/variant/visit.cc +++ b/libstdc++-v3/testsuite/20_util/variant/visit.cc @@ -18,7 +18,7 @@ // { dg-do run { target c++17 } } #include -#include +#include // reference_wrapper #include // N.B. there are more std::visit tests in ./compile.cc and ./run.cc @@ -84,6 +84,9 @@ test02() // Visit should not need arguments to be copyable: int res = std::visit(f, v); VERIFY( res == 1 ); + v.emplace(); + res = std::visit(f, v); + VERIFY( res == 0 ); } int diff --git a/libstdc++-v3/testsuite/20_util/variant/visit_member.cc b/libstdc++-v3/testsuite/20_util/variant/visit_member.cc new file mode 100644 index 00000000000..90332b94c72 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/variant/visit_member.cc @@ -0,0 +1,117 @@ +// Copyright (C) 2019-2024 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-do run { target c++26 } } + +#include + +#if __cpp_lib_variant < 202306L +# error __cpp_lib_variant has the wrong value in +#endif + +#include // reference_wrapper +#include + +void +test01() +{ + // Verify that visitation uses INVOKE and supports a pointer-to-member. + struct X { int n; }; + using V = std::variant>; + struct Derv : private V { + using V::V; + using V::visit; + }; + + Derv v{X{1}}; + static_assert( std::is_same_v ); + + // Verify that constness and value category are correctly forwarded. + std::variant v2{1}; + auto id = [](T&& t) -> T&& { return std::forward(t); }; + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + const auto& vc = v2; + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + + static_assert( std::is_same_v(id)), void> ); +} + +void +test02() +{ + struct NoCopy + { + NoCopy() { } + NoCopy(const NoCopy&) = delete; + NoCopy(NoCopy&&) = delete; + ~NoCopy() { } + + int operator()(int i) { return i; } + int operator()(const NoCopy&) { return 100; } + }; + + std::variant v{10}; + NoCopy f; + // Visit should not need arguments to be copyable: + int res = v.visit(f); + VERIFY( res == 10 ); + v.emplace(); + res = v.visit(f); + VERIFY( res == 100 ); + res = v.visit(f); + VERIFY( res == 1 ); +} + +void +test03() +{ + // Verify that member visit can access the variant as a private base class. + struct Derived : private std::variant + { + using variant::visit; + }; + Derived d; + int i = d.visit([](int& x) { return --x; }); + VERIFY( i == -1 ); + unsigned u = d.visit([](int x) { return x; }); + VERIFY( u == -1u ); +} + +void +test04() +{ + struct A { char a = 'a'; }; + struct B { char b = 'b'; }; + struct C { char c = 'c'; }; + auto f = [](auto x) { return B{}; }; + using State = std::variant; + State state = A{}; + auto res = std::move(state).visit(f); + // Verify that visit only matches the explicit return type overload. + static_assert( std::is_same_v ); +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); +} From patchwork Tue Jul 16 11:56:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1961062 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=RqUG7hJJ; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WNd9p2MVHz1xrQ for ; Tue, 16 Jul 2024 22:07:42 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 946B2384A058 for ; Tue, 16 Jul 2024 12:07:40 +0000 (GMT) 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 7D517384A05A for ; Tue, 16 Jul 2024 12:04:53 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7D517384A05A 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 7D517384A05A 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=1721131499; cv=none; b=u2KpJJ5EbkqqiuhpiBBN5PFUz0Shd9/j3mQCoeH+inm5Is3KVuOePrFnIp5EYZuo6gOnQyX0mKsuoS905EOQwrUXKzIlF1LML7Dbc80iCmuyzjnAN/1QW5o6PCp/GGGVB/TAsqnD09va2ClFqEuTZyX9dgzGaKPGV9pD2g776XE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721131499; c=relaxed/simple; bh=R99lnCMpFJu4LSNhBPaXystF7bmw0gH1vTQsbroj4ks=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=LDiybmZNrmWeSC1mJRDz7GNea/pZrUsyJAlA3cVDGVtG3z1QRfim57b7wgIPYbnb0WQMTCWcYwm7W7WccoGW1M6Uc5VzyC2JYhgfygKYDdyvPxcE71qA29xxTTvmhSAY99dUu93JZdIuhrCgKsjDv3qjpNuq7cJdDV73Zo0Ewdw= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1721131493; 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=69UR+FpFmtepx4MFg61pD+j57zMdG7/Nc6xSL0yDpac=; b=RqUG7hJJO1+CEPz8p2kxQwqGLWuvgiwfg1lMdGFwZR826I8hp50tFbwIANLpEg1nc1+LW3 FT6LgqBRaxApc+A24g1BgIX6Uo88RBboyz5BqFEFiye1exck3Nz57C5eDEaYlP+who1mgz oJFm37q5LJkj8bIHG3gvMuflvT+tEHg= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-267-OTjxh20VPPy4XbNlkBfAcA-1; Tue, 16 Jul 2024 08:04:49 -0400 X-MC-Unique: OTjxh20VPPy4XbNlkBfAcA-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5CE861955F3D; Tue, 16 Jul 2024 12:04:48 +0000 (UTC) Received: from localhost (unknown [10.42.28.14]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id CFDAA1955F40; Tue, 16 Jul 2024 12:04:47 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [PATCH 5/6] libstdc++: Define C++26 member visit for std::basic_format_arg [PR110356] Date: Tue, 16 Jul 2024 12:56:01 +0100 Message-ID: <20240716120436.2135312-5-jwakely@redhat.com> In-Reply-To: <20240716120436.2135312-1-jwakely@redhat.com> References: <20240716120436.2135312-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.5 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_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_SBL_CSS, 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~incoming=patchwork.ozlabs.org@gcc.gnu.org Tested x86_64-linux. -- >8 -- Implement the std::format changes from P2637R3. This adds visit member functions to std::basic_format_arg and deprecates the non-member function std::visit_format_arg. libstdc++-v3/ChangeLog: PR libstdc++/110356 * include/bits/c++config (_GLIBCXX26_DEPRECATED): Define. (_GLIBCXX26_DEPRECATED_SUGGEST): Define. * include/bits/version.def (format): Update for C++26. * include/bits/version.h: Regenerate. * include/std/format (basic_format_arg::visit): New member functions. (visit_format_arg): Add deprecated attribute. * testsuite/std/format/arguments/args.cc: Expect deprecated warnings. Check member visit. * testsuite/std/format/functions/format.cc: Update expected value for __cpp_lib_format macro. * testsuite/std/format/parse_ctx.cc: Add dg-warning for deprecation. --- libstdc++-v3/include/bits/c++config | 10 +++++ libstdc++-v3/include/bits/version.def | 2 +- libstdc++-v3/include/bits/version.h | 4 +- libstdc++-v3/include/std/format | 16 +++++++ .../testsuite/std/format/arguments/args.cc | 43 +++++++++++++++++++ .../testsuite/std/format/functions/format.cc | 4 +- .../testsuite/std/format/parse_ctx.cc | 2 +- 7 files changed, 75 insertions(+), 6 deletions(-) diff --git a/libstdc++-v3/include/bits/c++config b/libstdc++-v3/include/bits/c++config index 6dca2d9467a..0f0cc7cd659 100644 --- a/libstdc++-v3/include/bits/c++config +++ b/libstdc++-v3/include/bits/c++config @@ -90,6 +90,8 @@ // _GLIBCXX20_DEPRECATED_SUGGEST( string-literal ) // _GLIBCXX23_DEPRECATED // _GLIBCXX23_DEPRECATED_SUGGEST( string-literal ) +// _GLIBCXX26_DEPRECATED +// _GLIBCXX26_DEPRECATED_SUGGEST( string-literal ) #ifndef _GLIBCXX_USE_DEPRECATED # define _GLIBCXX_USE_DEPRECATED 1 #endif @@ -143,6 +145,14 @@ # define _GLIBCXX23_DEPRECATED_SUGGEST(ALT) #endif +#if defined(__DEPRECATED) && (__cplusplus >= 202400L) +# define _GLIBCXX26_DEPRECATED [[__deprecated__]] +# define _GLIBCXX26_DEPRECATED_SUGGEST(ALT) _GLIBCXX_DEPRECATED_SUGGEST(ALT) +#else +# define _GLIBCXX26_DEPRECATED +# define _GLIBCXX26_DEPRECATED_SUGGEST(ALT) +#endif + // Macros for ABI tag attributes. #ifndef _GLIBCXX_ABI_TAG_CXX11 # define _GLIBCXX_ABI_TAG_CXX11 __attribute ((__abi_tag__ ("cxx11"))) diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 806f1e9549b..ec330911d66 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1171,7 +1171,7 @@ ftms = { // 202306 P2637R3 Member visit // 202311 P2918R2 Runtime format strings II values = { - v = 202305; + v = 202306; cxxmin = 26; hosted = yes; }; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index e8ca0faf5dc..148ee87e087 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1310,9 +1310,9 @@ #if !defined(__cpp_lib_format) # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED -# define __glibcxx_format 202305L +# define __glibcxx_format 202306L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_format) -# define __cpp_lib_format 202305L +# define __cpp_lib_format 202306L # endif # elif (__cplusplus >= 202002L) && _GLIBCXX_HOSTED # define __glibcxx_format 202304L diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index 0e61bc5e6bc..f1a1f736a80 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -3346,6 +3346,18 @@ namespace __format explicit operator bool() const noexcept { return _M_type != __format::_Arg_none; } +#if __cpp_lib_format >= 202306L // >= C++26 + template + decltype(auto) + visit(this basic_format_arg __arg, _Visitor&& __vis) + { return __arg._M_visit(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); } +#endif + private: template friend class basic_format_args; @@ -3631,6 +3643,7 @@ namespace __format }; template + _GLIBCXX26_DEPRECATED_SUGGEST("std::basic_format_arg::visit") inline decltype(auto) visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { @@ -3666,6 +3679,8 @@ namespace __format } }; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" template inline size_t __int_from_arg(const basic_format_arg<_Context>& __arg) @@ -4299,6 +4314,7 @@ namespace __format else return std::move(__sink)._M_finish().out; } +#pragma GCC diagnostic pop } // namespace __format /// @endcond diff --git a/libstdc++-v3/testsuite/std/format/arguments/args.cc b/libstdc++-v3/testsuite/std/format/arguments/args.cc index eba129ff894..4e0eb69262b 100644 --- a/libstdc++-v3/testsuite/std/format/arguments/args.cc +++ b/libstdc++-v3/testsuite/std/format/arguments/args.cc @@ -3,6 +3,8 @@ #include #include +// { dg-warning "deprecated" "" { target c++26 } 0 } + template bool equals(std::basic_format_arg fmt_arg, T expected) { return std::visit_format_arg([=](auto arg_val) { @@ -51,6 +53,9 @@ test_args() char c = '2'; double d = 3.4; + // We need an lvalue of type format-arg-store for the + // lvalue args to point to, otherwise it will have a dangling pointer. + // This is not how you're supposed to use format args, just for testing. auto store = std::make_format_args(b, i, c, d); std::format_args args = store; VERIFY(equals(args.get(0), false)); @@ -102,8 +107,46 @@ test_args() VERIFY(std::visit_format_arg(is_handle, args.get(1))); } +void +test_member_visit() +{ +#if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit + int one = 1; + float two = 2.0; + std::string_view three = "three"; + auto store = std::make_format_args(one, two, three); // See comment above. + std::format_args args = store; + auto a1 = args.get(0).visit([=](auto a) { + if constexpr (std::is_same_v) + return a == one; + return false; + }); + VERIFY( a1 ); + auto a2 = args.get(1).visit([=](auto a) { + if constexpr (std::is_same_v) + return a == two; + return false; + }); + VERIFY( a2 ); + auto a3 = args.get(2).visit([=](auto a) { + if constexpr (std::is_same_v) + return a == three; + return false; + }); + VERIFY( a3 ); + + auto a4 = args.get(0).visit([](auto) { return "str"; }); + static_assert( std::is_same_v ); + VERIFY( a4 == "str" ); + args.get(0).visit([](auto){}); + using V = decltype(args.get(0).visit([](auto){})); + static_assert( std::is_same_v ); +#endif +} + int main() { test_empty(); test_args(); + test_member_visit(); } diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc index 3c441d711b3..0549d171e5a 100644 --- a/libstdc++-v3/testsuite/std/format/functions/format.cc +++ b/libstdc++-v3/testsuite/std/format/functions/format.cc @@ -8,7 +8,7 @@ # error "Feature test macro for std::format is missing in " #elif __cpp_lib_format < 202110L # error "Feature test macro for std::format has wrong value in " -#elif __cplusplus > 202302L && __cpp_lib_format < 202305L +#elif __cplusplus > 202302L && __cpp_lib_format < 202306L # error "Feature test macro for std::format has wrong value in " #endif @@ -24,7 +24,7 @@ # error "Feature test macro for std::format is missing in " #elif __cpp_lib_format < 202110L # error "Feature test macro for std::format has wrong value in " -#elif __cplusplus > 202302L && __cpp_lib_format < 202305L +#elif __cplusplus > 202302L && __cpp_lib_format < 202306L # error "Feature test macro for std::format has wrong value in " #endif diff --git a/libstdc++-v3/testsuite/std/format/parse_ctx.cc b/libstdc++-v3/testsuite/std/format/parse_ctx.cc index b0cef2da41b..88ffd77debe 100644 --- a/libstdc++-v3/testsuite/std/format/parse_ctx.cc +++ b/libstdc++-v3/testsuite/std/format/parse_ctx.cc @@ -473,7 +473,7 @@ struct std::formatter std::format_context::iterator format(X, std::format_context& c) const { - std::visit_format_arg([this](T) { + std::visit_format_arg([this](T) { // { dg-warning "deprecated" "" { target c++26 } } if (is_integral_v != this->integer) throw std::format_error("invalid argument type"); }, c.arg(1)); From patchwork Tue Jul 16 11:56:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1961063 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=FYM3yfss; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WNd9q4kG0z1xrQ for ; Tue, 16 Jul 2024 22:07:43 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8065E384A05E for ; Tue, 16 Jul 2024 12:07:41 +0000 (GMT) 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 68FB8384A05E for ; Tue, 16 Jul 2024 12:04:54 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 68FB8384A05E 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 68FB8384A05E 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=1721131501; cv=none; b=j1XYX8sXriZPyyC+BCCkXxYZU9SoZTi0PBrl0dhobLlp/WqcFn8iUnXBRBDr0m90/2P0mRUqacAIZk8pv1t6oHrNSfjaUnR23LfuwrFRgICP3TE5Iz80wlRp5AVUdqeR3+7b3EHTCy5PyeoCzLxoIB0M8R+OVIj/RrNJtr1DWbs= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721131501; c=relaxed/simple; bh=HTTca8TPpIdVSd5MReqLmTcceGCIrxRGx0EmVClq0JI=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=A7nU7y0DRevT6PlFsZU8VARK3AAm3qz9x2Zg6NyKfIWHe5W15gWD8ubtK1xfyg0z94SRHVjnZsKjRWcSbKVMJ3bP4Y9yWbM9fheu73uNmxaR0aY0ZDSP8LeaFJ05o8svBfStp++nFVRTNQiGRtWdsVyleSGvhiyZEWSq9k890V4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1721131494; 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=mzsiIvda8K8aSK7MUbLz+pG4F4VulAsX0WNmJpnEiXU=; b=FYM3yfssQKLRwCX1TTsrV3SJJRpTCuI1hrLVYWBn4baVUq0rghpFT/OBtepGTXwleQv/CL PYRHWnzEVLiF9NnIo3tLGMJi3ALiZ7UymYOeUtEIGH+MmiWmBAkb5mXNxn9XpBFn//P4j1 ly/wv87puTrg0852+29SghkXp94zIBs= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-443-RBpbWYLhM8en0yXO4C23gg-1; Tue, 16 Jul 2024 08:04:51 -0400 X-MC-Unique: RBpbWYLhM8en0yXO4C23gg-1 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (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-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 9B92D1955D4A; Tue, 16 Jul 2024 12:04:50 +0000 (UTC) Received: from localhost (unknown [10.42.28.14]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 07EB819560B2; Tue, 16 Jul 2024 12:04:49 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [PATCH 6/6] libstdc++: Bump __cpp_lib_format value for std::runtime_format Date: Tue, 16 Jul 2024 12:56:02 +0100 Message-ID: <20240716120436.2135312-6-jwakely@redhat.com> In-Reply-To: <20240716120436.2135312-1-jwakely@redhat.com> References: <20240716120436.2135312-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.5 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_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_SBL_CSS, 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~incoming=patchwork.ozlabs.org@gcc.gnu.org Tested x86_64-linux. -- >8 -- We already supported this feature, but couldn't set the feature test macro accordingly because we were missing support for older features. Now that we support all the older changes, we can set this to the correct value. libstdc++-v3/ChangeLog: * include/bits/version.def (format): Update value for C++26. * include/bits/version.h: Regenerate. * include/std/format (runtime_format, wruntime_format): Check __cpp_lib_format instead of __cplusplus. * testsuite/std/format/functions/format.cc: Update expected value of macro for C++26 mode. --- libstdc++-v3/include/bits/version.def | 2 +- libstdc++-v3/include/bits/version.h | 4 ++-- libstdc++-v3/include/std/format | 2 +- libstdc++-v3/testsuite/std/format/functions/format.cc | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index ec330911d66..ee0a9e45c44 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1171,7 +1171,7 @@ ftms = { // 202306 P2637R3 Member visit // 202311 P2918R2 Runtime format strings II values = { - v = 202306; + v = 202311; cxxmin = 26; hosted = yes; }; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 148ee87e087..cee497d7443 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1310,9 +1310,9 @@ #if !defined(__cpp_lib_format) # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED -# define __glibcxx_format 202306L +# define __glibcxx_format 202311L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_format) -# define __cpp_lib_format 202306L +# define __cpp_lib_format 202311L # endif # elif (__cplusplus >= 202002L) && _GLIBCXX_HOSTED # define __glibcxx_format 202304L diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index f1a1f736a80..838cc18a78a 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -155,7 +155,7 @@ namespace __format = basic_format_string...>; #endif -#if __cplusplus > 202302L +#if __cpp_lib_format >= 202311L // >= C++26 [[__gnu__::__always_inline__]] inline __format::_Runtime_format_string runtime_format(string_view __fmt) noexcept diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc index 0549d171e5a..7fc42017045 100644 --- a/libstdc++-v3/testsuite/std/format/functions/format.cc +++ b/libstdc++-v3/testsuite/std/format/functions/format.cc @@ -8,7 +8,7 @@ # error "Feature test macro for std::format is missing in " #elif __cpp_lib_format < 202110L # error "Feature test macro for std::format has wrong value in " -#elif __cplusplus > 202302L && __cpp_lib_format < 202306L +#elif __cplusplus > 202302L && __cpp_lib_format < 202311L # error "Feature test macro for std::format has wrong value in " #endif @@ -24,7 +24,7 @@ # error "Feature test macro for std::format is missing in " #elif __cpp_lib_format < 202110L # error "Feature test macro for std::format has wrong value in " -#elif __cplusplus > 202302L && __cpp_lib_format < 202306L +#elif __cplusplus > 202302L && __cpp_lib_format < 202311L # error "Feature test macro for std::format has wrong value in " #endif