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) );