https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/79032
>From e03452fda84a5284420bba1913299b68caabb6cd Mon Sep 17 00:00:00 2001 From: Zingam <zin...@outlook.com> Date: Mon, 22 Jan 2024 20:35:00 +0200 Subject: [PATCH 1/2] Revert "Revert "[libc++][format] P2637R3: Member `visit` (`std::basic_format_arg`) (#76449)"" This reverts commit 02f95b77515fe18ed1076b94cbb850ea0cf3c77e. --- libcxx/docs/ReleaseNotes/18.rst | 1 + libcxx/docs/Status/Cxx2cPapers.csv | 2 +- libcxx/docs/Status/FormatIssues.csv | 2 +- libcxx/include/__config | 6 + libcxx/include/__format/format_arg.h | 109 +++++- libcxx/include/__format/format_context.h | 33 +- libcxx/include/format | 2 +- .../format.arg/visit.pass.cpp | 333 ++++++++++++++++ .../format.arg/visit.return_type.pass.cpp | 369 ++++++++++++++++++ .../visit_format_arg.deprecated.verify.cpp | 38 ++ .../format.arg/visit_format_arg.pass.cpp | 6 +- .../format.arguments/format.args/get.pass.cpp | 48 ++- libcxx/test/support/test_basic_format_arg.h | 20 +- libcxx/test/support/test_macros.h | 5 + .../generate_feature_test_macro_components.py | 1 + 15 files changed, 927 insertions(+), 48 deletions(-) create mode 100644 libcxx/test/std/utilities/format/format.arguments/format.arg/visit.pass.cpp create mode 100644 libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp create mode 100644 libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.deprecated.verify.cpp diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst index fd882bafe19a517..237a63022d55ff5 100644 --- a/libcxx/docs/ReleaseNotes/18.rst +++ b/libcxx/docs/ReleaseNotes/18.rst @@ -79,6 +79,7 @@ Implemented Papers - P1759R6 - Native handles and file streams - P2868R3 - Remove Deprecated ``std::allocator`` Typedef From C++26 - P2517R1 - Add a conditional ``noexcept`` specification to ``std::apply`` +- P2637R3 - Member ``visit`` - P2447R6 - ``span`` over initializer list diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index f80b1f6b663f045..c45aa3c510072e4 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -17,7 +17,7 @@ "`P0792R14 <https://wg21.link/P0792R14>`__","LWG","``function_ref``: a type-erased callable reference","Varna June 2023","","","" "`P2874R2 <https://wg21.link/P2874R2>`__","LWG","Mandating Annex D Require No More","Varna June 2023","","","" "`P2757R3 <https://wg21.link/P2757R3>`__","LWG","Type-checking format args","Varna June 2023","","","|format|" -"`P2637R3 <https://wg21.link/P2637R3>`__","LWG","Member ``visit``","Varna June 2023","|Partial|","18.0","" +"`P2637R3 <https://wg21.link/P2637R3>`__","LWG","Member ``visit``","Varna June 2023","|Complete|","18.0","" "`P2641R4 <https://wg21.link/P2641R4>`__","CWG, LWG","Checking if a ``union`` alternative is active","Varna June 2023","","","" "`P1759R6 <https://wg21.link/P1759R6>`__","LWG","Native handles and file streams","Varna June 2023","|Complete|","18.0","" "`P2697R1 <https://wg21.link/P2697R1>`__","LWG","Interfacing ``bitset`` with ``string_view``","Varna June 2023","|Complete|","18.0","" diff --git a/libcxx/docs/Status/FormatIssues.csv b/libcxx/docs/Status/FormatIssues.csv index 513988d08036ca6..6e58e752191ea5d 100644 --- a/libcxx/docs/Status/FormatIssues.csv +++ b/libcxx/docs/Status/FormatIssues.csv @@ -16,7 +16,7 @@ Number,Name,Standard,Assignee,Status,First released version "`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``","C++23","Mark de Wever","|In Progress|" "`P2510R3 <https://wg21.link/P2510R3>`__","Formatting pointers","C++26","Mark de Wever","|Complete|",17.0 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","C++26","","", -"`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","C++26","","", +"`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","C++26","Hristo Hristov","|Complete|",18.0 "`P2905R2 <https://wg21.link/P2905R2>`__","Runtime format strings","C++26 DR","Mark de Wever","|Complete|",18.0 "`P2918R2 <https://wg21.link/P2918R2>`__","Runtime format strings II","C++26","Mark de Wever","|Complete|",18.0 "`P2909R4 <https://wg21.link/P2909R4>`__","Fix formatting of code units as integers (Dude, where’s my ``char``?)","C++26 DR","Mark de Wever","|Complete|",18.0 diff --git a/libcxx/include/__config b/libcxx/include/__config index 9a64cdb489119d9..00489d971c296c2 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -995,6 +995,12 @@ typedef __char32_t char32_t; # define _LIBCPP_DEPRECATED_IN_CXX23 # endif +# if _LIBCPP_STD_VER >= 26 +# define _LIBCPP_DEPRECATED_IN_CXX26 _LIBCPP_DEPRECATED +# else +# define _LIBCPP_DEPRECATED_IN_CXX26 +# endif + # if !defined(_LIBCPP_HAS_NO_CHAR8_T) # define _LIBCPP_DEPRECATED_WITH_CHAR8_T _LIBCPP_DEPRECATED # else diff --git a/libcxx/include/__format/format_arg.h b/libcxx/include/__format/format_arg.h index 10fca15d5a7a94e..02ee3cef7d402b7 100644 --- a/libcxx/include/__format/format_arg.h +++ b/libcxx/include/__format/format_arg.h @@ -93,7 +93,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr __arg_t __get_packed_type(uint64_t __types, size } // namespace __format -// This function is not user obervable, so it can directly use the non-standard +// This function is not user observable, so it can directly use the non-standard // types of the "variant". See __arg_t for more details. template <class _Visitor, class _Context> _LIBCPP_HIDE_FROM_ABI decltype(auto) __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { @@ -144,6 +144,59 @@ _LIBCPP_HIDE_FROM_ABI decltype(auto) __visit_format_arg(_Visitor&& __vis, basic_ __libcpp_unreachable(); } +# if _LIBCPP_STD_VER >= 26 && defined(_LIBCPP_HAS_EXPLICIT_THIS_PARAMETER) + +template <class _Rp, class _Visitor, class _Context> +_LIBCPP_HIDE_FROM_ABI _Rp __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { + switch (__arg.__type_) { + case __format::__arg_t::__none: + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__monostate_); + case __format::__arg_t::__boolean: + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__boolean_); + case __format::__arg_t::__char_type: + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__char_type_); + case __format::__arg_t::__int: + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__int_); + case __format::__arg_t::__long_long: + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__long_long_); + case __format::__arg_t::__i128: +# ifndef _LIBCPP_HAS_NO_INT128 + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__i128_); +# else + __libcpp_unreachable(); +# endif + case __format::__arg_t::__unsigned: + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__unsigned_); + case __format::__arg_t::__unsigned_long_long: + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__unsigned_long_long_); + case __format::__arg_t::__u128: +# ifndef _LIBCPP_HAS_NO_INT128 + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__u128_); +# else + __libcpp_unreachable(); +# endif + case __format::__arg_t::__float: + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__float_); + case __format::__arg_t::__double: + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__double_); + case __format::__arg_t::__long_double: + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__long_double_); + case __format::__arg_t::__const_char_type_ptr: + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__const_char_type_ptr_); + case __format::__arg_t::__string_view: + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__string_view_); + case __format::__arg_t::__ptr: + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__ptr_); + case __format::__arg_t::__handle: + return std::invoke_r<_Rp>( + std::forward<_Visitor>(__vis), typename basic_format_arg<_Context>::handle{__arg.__value_.__handle_}); + } + + __libcpp_unreachable(); +} + +# endif // _LIBCPP_STD_VER >= 26 && defined(_LIBCPP_HAS_EXPLICIT_THIS_PARAMETER) + /// Contains the values used in basic_format_arg. /// /// This is a separate type so it's possible to store the values and types in @@ -227,6 +280,52 @@ class _LIBCPP_TEMPLATE_VIS basic_format_arg { _LIBCPP_HIDE_FROM_ABI explicit operator bool() const noexcept { return __type_ != __format::__arg_t::__none; } +# if _LIBCPP_STD_VER >= 26 && defined(_LIBCPP_HAS_EXPLICIT_THIS_PARAMETER) + + // This function is user facing, so it must wrap the non-standard types of + // the "variant" in a handle to stay conforming. See __arg_t for more details. + template <class _Visitor> + _LIBCPP_HIDE_FROM_ABI decltype(auto) visit(this basic_format_arg __arg, _Visitor&& __vis) { + switch (__arg.__type_) { +# ifndef _LIBCPP_HAS_NO_INT128 + case __format::__arg_t::__i128: { + typename __basic_format_arg_value<_Context>::__handle __h{__arg.__value_.__i128_}; + return std::invoke(std::forward<_Visitor>(__vis), typename basic_format_arg<_Context>::handle{__h}); + } + + case __format::__arg_t::__u128: { + typename __basic_format_arg_value<_Context>::__handle __h{__arg.__value_.__u128_}; + return std::invoke(std::forward<_Visitor>(__vis), typename basic_format_arg<_Context>::handle{__h}); + } +# endif + default: + return std::__visit_format_arg(std::forward<_Visitor>(__vis), __arg); + } + } + + // This function is user facing, so it must wrap the non-standard types of + // the "variant" in a handle to stay conforming. See __arg_t for more details. + template <class _Rp, class _Visitor> + _LIBCPP_HIDE_FROM_ABI _Rp visit(this basic_format_arg __arg, _Visitor&& __vis) { + switch (__arg.__type_) { +# ifndef _LIBCPP_HAS_NO_INT128 + case __format::__arg_t::__i128: { + typename __basic_format_arg_value<_Context>::__handle __h{__arg.__value_.__i128_}; + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), typename basic_format_arg<_Context>::handle{__h}); + } + + case __format::__arg_t::__u128: { + typename __basic_format_arg_value<_Context>::__handle __h{__arg.__value_.__u128_}; + return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), typename basic_format_arg<_Context>::handle{__h}); + } +# endif + default: + return std::__visit_format_arg<_Rp>(std::forward<_Visitor>(__vis), __arg); + } + } + +# endif // _LIBCPP_STD_VER >= 26 && defined(_LIBCPP_HAS_EXPLICIT_THIS_PARAMETER) + private: using char_type = typename _Context::char_type; @@ -267,7 +366,11 @@ class _LIBCPP_TEMPLATE_VIS basic_format_arg<_Context>::handle { // This function is user facing, so it must wrap the non-standard types of // the "variant" in a handle to stay conforming. See __arg_t for more details. template <class _Visitor, class _Context> -_LIBCPP_HIDE_FROM_ABI decltype(auto) visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { +# if _LIBCPP_STD_VER >= 26 && defined(_LIBCPP_HAS_EXPLICIT_THIS_PARAMETER) +_LIBCPP_DEPRECATED_IN_CXX26 +# endif + _LIBCPP_HIDE_FROM_ABI decltype(auto) + visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { switch (__arg.__type_) { # ifndef _LIBCPP_HAS_NO_INT128 case __format::__arg_t::__i128: { @@ -279,7 +382,7 @@ _LIBCPP_HIDE_FROM_ABI decltype(auto) visit_format_arg(_Visitor&& __vis, basic_fo typename __basic_format_arg_value<_Context>::__handle __h{__arg.__value_.__u128_}; return std::invoke(std::forward<_Visitor>(__vis), typename basic_format_arg<_Context>::handle{__h}); } -# endif +# endif // _LIBCPP_STD_VER >= 26 && defined(_LIBCPP_HAS_EXPLICIT_THIS_PARAMETER) default: return std::__visit_format_arg(std::forward<_Visitor>(__vis), __arg); } diff --git a/libcxx/include/__format/format_context.h b/libcxx/include/__format/format_context.h index 5b252b81f691bc1..0beaa84b028beb7 100644 --- a/libcxx/include/__format/format_context.h +++ b/libcxx/include/__format/format_context.h @@ -163,20 +163,25 @@ class _LIBCPP_TEMPLATE_VIS basic_format_context<typename __format::__retarget_bu # endif __ctx_(std::addressof(__ctx)), __arg_([](void* __c, size_t __id) { - return std::visit_format_arg( - [&](auto __arg) -> basic_format_arg<basic_format_context> { - if constexpr (same_as<decltype(__arg), monostate>) - return {}; - else if constexpr (same_as<decltype(__arg), typename basic_format_arg<_Context>::handle>) - // At the moment it's not possible for formatting to use a re-targeted handle. - // TODO FMT add this when support is needed. - std::__throw_format_error("Re-targeting handle not supported"); - else - return basic_format_arg<basic_format_context>{ - __format::__determine_arg_t<basic_format_context, decltype(__arg)>(), - __basic_format_arg_value<basic_format_context>(__arg)}; - }, - static_cast<_Context*>(__c)->arg(__id)); + auto __visitor = [&](auto __arg) -> basic_format_arg<basic_format_context> { + if constexpr (same_as<decltype(__arg), monostate>) + return {}; + else if constexpr (same_as<decltype(__arg), typename basic_format_arg<_Context>::handle>) + // At the moment it's not possible for formatting to use a re-targeted handle. + // TODO FMT add this when support is needed. + std::__throw_format_error("Re-targeting handle not supported"); + else + return basic_format_arg<basic_format_context>{ + __format::__determine_arg_t<basic_format_context, decltype(__arg)>(), + __basic_format_arg_value<basic_format_context>(__arg)}; + }; +# if _LIBCPP_STD_VER >= 26 && defined(_LIBCPP_HAS_EXPLICIT_THIS_PARAMETER) + return static_cast<_Context*>(__c)->arg(__id).visit(std::move(__visitor)); +# else + _LIBCPP_SUPPRESS_DEPRECATED_PUSH + return std::visit_format_arg(std::move(__visitor), static_cast<_Context*>(__c)->arg(__id)); + _LIBCPP_SUPPRESS_DEPRECATED_POP +# endif // _LIBCPP_STD_VER >= 26 && defined(_LIBCPP_HAS_EXPLICIT_THIS_PARAMETER) }) { } diff --git a/libcxx/include/format b/libcxx/include/format index ab9b336d0cdabee..64f6ba1d25284aa 100644 --- a/libcxx/include/format +++ b/libcxx/include/format @@ -170,7 +170,7 @@ namespace std { template<class Context> class basic_format_arg; template<class Visitor, class Context> - see below visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg); + see below visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg); // Deprecated in C++26 // [format.arg.store], class template format-arg-store template<class Context, class... Args> struct format-arg-store; // exposition only diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.pass.cpp new file mode 100644 index 000000000000000..994ccc70a38daae --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.pass.cpp @@ -0,0 +1,333 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23 +// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME +// The tested functionality needs deducing this. +// UNSUPPORTED: clang-16 || clang-17 +// XFAIL: apple-clang + +// <format> + +// class basic_format_arg; + +// template<class Visitor> +// decltype(auto) visit(this basic_format_arg arg, Visitor&& vis); // since C++26 + +#include <algorithm> +#include <cassert> +#include <format> +#include <type_traits> + +#include "constexpr_char_traits.h" +#include "make_string.h" +#include "min_allocator.h" +#include "test_macros.h" + +template <class Context, class To, class From> +void test(From value) { + auto store = std::make_format_args<Context>(value); + std::basic_format_args<Context> format_args{store}; + + LIBCPP_ASSERT(format_args.__size() == 1); + assert(format_args.get(0)); + + auto result = format_args.get(0).visit([v = To(value)](auto a) -> To { + if constexpr (std::is_same_v<To, decltype(a)>) { + assert(v == a); + return a; + } else { + assert(false); + return {}; + } + }); + + using ct = std::common_type_t<From, To>; + assert(static_cast<ct>(result) == static_cast<ct>(value)); +} + +// Some types, as an extension, are stored in the variant. The Standard +// requires them to be observed as a handle. +template <class Context, class T> +void test_handle(T value) { + auto store = std::make_format_args<Context>(value); + std::basic_format_args<Context> format_args{store}; + + LIBCPP_ASSERT(format_args.__size() == 1); + assert(format_args.get(0)); + + format_args.get(0).visit([](auto a) { + assert((std::is_same_v<decltype(a), typename std::basic_format_arg<Context>::handle>)); + }); +} + +// Test specific for string and string_view. +// +// Since both result in a string_view there's no need to pass this as a +// template argument. +template <class Context, class From> +void test_string_view(From value) { + auto store = std::make_format_args<Context>(value); + std::basic_format_args<Context> format_args{store}; + + LIBCPP_ASSERT(format_args.__size() == 1); + assert(format_args.get(0)); + + using CharT = typename Context::char_type; + using To = std::basic_string_view<CharT>; + using V = std::basic_string<CharT>; + + auto result = format_args.get(0).visit([v = V(value.begin(), value.end())](auto a) -> To { + if constexpr (std::is_same_v<To, decltype(a)>) { + assert(v == a); + return a; + } else { + assert(false); + return {}; + } + }); + + assert(std::equal(value.begin(), value.end(), result.begin(), result.end())); +} + +template <class CharT> +void test() { + using Context = std::basic_format_context<CharT*, CharT>; + std::basic_string<CharT> empty; + std::basic_string<CharT> str = MAKE_STRING(CharT, "abc"); + + // Test boolean types. + + test<Context, bool>(true); + test<Context, bool>(false); + + // Test CharT types. + + test<Context, CharT, CharT>('a'); + test<Context, CharT, CharT>('z'); + test<Context, CharT, CharT>('0'); + test<Context, CharT, CharT>('9'); + + // Test char types. + + if (std::is_same_v<CharT, char>) { + // char to char -> char + test<Context, CharT, char>('a'); + test<Context, CharT, char>('z'); + test<Context, CharT, char>('0'); + test<Context, CharT, char>('9'); + } else { + if (std::is_same_v<CharT, wchar_t>) { + // char to wchar_t -> wchar_t + test<Context, wchar_t, char>('a'); + test<Context, wchar_t, char>('z'); + test<Context, wchar_t, char>('0'); + test<Context, wchar_t, char>('9'); + } else if (std::is_signed_v<char>) { + // char to CharT -> int + // This happens when CharT is a char8_t, char16_t, or char32_t and char + // is a signed type. + // Note if sizeof(CharT) > sizeof(int) this test fails. If there are + // platforms where that occurs extra tests need to be added for char32_t + // testing it against a long long. + test<Context, int, char>('a'); + test<Context, int, char>('z'); + test<Context, int, char>('0'); + test<Context, int, char>('9'); + } else { + // char to CharT -> unsigned + // This happens when CharT is a char8_t, char16_t, or char32_t and char + // is an unsigned type. + // Note if sizeof(CharT) > sizeof(unsigned) this test fails. If there are + // platforms where that occurs extra tests need to be added for char32_t + // testing it against an unsigned long long. + test<Context, unsigned, char>('a'); + test<Context, unsigned, char>('z'); + test<Context, unsigned, char>('0'); + test<Context, unsigned, char>('9'); + } + } + + // Test signed integer types. + + test<Context, int, signed char>(std::numeric_limits<signed char>::min()); + test<Context, int, signed char>(0); + test<Context, int, signed char>(std::numeric_limits<signed char>::max()); + + test<Context, int, short>(std::numeric_limits<short>::min()); + test<Context, int, short>(std::numeric_limits<signed char>::min()); + test<Context, int, short>(0); + test<Context, int, short>(std::numeric_limits<signed char>::max()); + test<Context, int, short>(std::numeric_limits<short>::max()); + + test<Context, int, int>(std::numeric_limits<int>::min()); + test<Context, int, int>(std::numeric_limits<short>::min()); + test<Context, int, int>(std::numeric_limits<signed char>::min()); + test<Context, int, int>(0); + test<Context, int, int>(std::numeric_limits<signed char>::max()); + test<Context, int, int>(std::numeric_limits<short>::max()); + test<Context, int, int>(std::numeric_limits<int>::max()); + + using LongToType = std::conditional_t<sizeof(long) == sizeof(int), int, long long>; + + test<Context, LongToType, long>(std::numeric_limits<long>::min()); + test<Context, LongToType, long>(std::numeric_limits<int>::min()); + test<Context, LongToType, long>(std::numeric_limits<short>::min()); + test<Context, LongToType, long>(std::numeric_limits<signed char>::min()); + test<Context, LongToType, long>(0); + test<Context, LongToType, long>(std::numeric_limits<signed char>::max()); + test<Context, LongToType, long>(std::numeric_limits<short>::max()); + test<Context, LongToType, long>(std::numeric_limits<int>::max()); + test<Context, LongToType, long>(std::numeric_limits<long>::max()); + + test<Context, long long, long long>(std::numeric_limits<long long>::min()); + test<Context, long long, long long>(std::numeric_limits<long>::min()); + test<Context, long long, long long>(std::numeric_limits<int>::min()); + test<Context, long long, long long>(std::numeric_limits<short>::min()); + test<Context, long long, long long>(std::numeric_limits<signed char>::min()); + test<Context, long long, long long>(0); + test<Context, long long, long long>(std::numeric_limits<signed char>::max()); + test<Context, long long, long long>(std::numeric_limits<short>::max()); + test<Context, long long, long long>(std::numeric_limits<int>::max()); + test<Context, long long, long long>(std::numeric_limits<long>::max()); + test<Context, long long, long long>(std::numeric_limits<long long>::max()); + +#ifndef TEST_HAS_NO_INT128 + test_handle<Context, __int128_t>(0); +#endif // TEST_HAS_NO_INT128 + + // Test unsigned integer types. + + test<Context, unsigned, unsigned char>(0); + test<Context, unsigned, unsigned char>(std::numeric_limits<unsigned char>::max()); + + test<Context, unsigned, unsigned short>(0); + test<Context, unsigned, unsigned short>(std::numeric_limits<unsigned char>::max()); + test<Context, unsigned, unsigned short>(std::numeric_limits<unsigned short>::max()); + + test<Context, unsigned, unsigned>(0); + test<Context, unsigned, unsigned>(std::numeric_limits<unsigned char>::max()); + test<Context, unsigned, unsigned>(std::numeric_limits<unsigned short>::max()); + test<Context, unsigned, unsigned>(std::numeric_limits<unsigned>::max()); + + using UnsignedLongToType = + std::conditional_t<sizeof(unsigned long) == sizeof(unsigned), unsigned, unsigned long long>; + + test<Context, UnsignedLongToType, unsigned long>(0); + test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned char>::max()); + test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned short>::max()); + test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned>::max()); + test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned long>::max()); + + test<Context, unsigned long long, unsigned long long>(0); + test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned char>::max()); + test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned short>::max()); + test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned>::max()); + test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned long>::max()); + test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned long long>::max()); + +#ifndef TEST_HAS_NO_INT128 + test_handle<Context, __uint128_t>(0); +#endif // TEST_HAS_NO_INT128 + + // Test floating point types. + + test<Context, float, float>(-std::numeric_limits<float>::max()); + test<Context, float, float>(-std::numeric_limits<float>::min()); + test<Context, float, float>(-0.0); + test<Context, float, float>(0.0); + test<Context, float, float>(std::numeric_limits<float>::min()); + test<Context, float, float>(std::numeric_limits<float>::max()); + + test<Context, double, double>(-std::numeric_limits<double>::max()); + test<Context, double, double>(-std::numeric_limits<double>::min()); + test<Context, double, double>(-0.0); + test<Context, double, double>(0.0); + test<Context, double, double>(std::numeric_limits<double>::min()); + test<Context, double, double>(std::numeric_limits<double>::max()); + + test<Context, long double, long double>(-std::numeric_limits<long double>::max()); + test<Context, long double, long double>(-std::numeric_limits<long double>::min()); + test<Context, long double, long double>(-0.0); + test<Context, long double, long double>(0.0); + test<Context, long double, long double>(std::numeric_limits<long double>::min()); + test<Context, long double, long double>(std::numeric_limits<long double>::max()); + + // Test const CharT pointer types. + + test<Context, const CharT*, const CharT*>(empty.c_str()); + test<Context, const CharT*, const CharT*>(str.c_str()); + + // Test string_view types. + + { + using From = std::basic_string_view<CharT>; + + test_string_view<Context>(From()); + test_string_view<Context>(From(empty.c_str())); + test_string_view<Context>(From(str.c_str())); + } + + { + using From = std::basic_string_view<CharT, constexpr_char_traits<CharT>>; + + test_string_view<Context>(From()); + test_string_view<Context>(From(empty.c_str())); + test_string_view<Context>(From(str.c_str())); + } + + // Test string types. + + { + using From = std::basic_string<CharT>; + + test_string_view<Context>(From()); + test_string_view<Context>(From(empty.c_str())); + test_string_view<Context>(From(str.c_str())); + } + + { + using From = std::basic_string<CharT, constexpr_char_traits<CharT>, std::allocator<CharT>>; + + test_string_view<Context>(From()); + test_string_view<Context>(From(empty.c_str())); + test_string_view<Context>(From(str.c_str())); + } + + { + using From = std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>>; + + test_string_view<Context>(From()); + test_string_view<Context>(From(empty.c_str())); + test_string_view<Context>(From(str.c_str())); + } + + { + using From = std::basic_string<CharT, constexpr_char_traits<CharT>, min_allocator<CharT>>; + + test_string_view<Context>(From()); + test_string_view<Context>(From(empty.c_str())); + test_string_view<Context>(From(str.c_str())); + } + + // Test pointer types. + + test<Context, const void*>(nullptr); + int i = 0; + test<Context, const void*>(static_cast<void*>(&i)); + const int ci = 0; + test<Context, const void*>(static_cast<const void*>(&ci)); +} + +int main(int, char**) { + test<char>(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test<wchar_t>(); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp new file mode 100644 index 000000000000000..473278b7b443490 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp @@ -0,0 +1,369 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23 +// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME +// The tested functionality needs deducing this. +// UNSUPPORTED: clang-16 || clang-17 +// XFAIL: apple-clang + +// <format> + +// class basic_format_arg; + +// template<class R, class Visitor> +// R visit(this basic_format_arg arg, Visitor&& vis); + +#include <algorithm> +#include <cassert> +#include <format> +#include <type_traits> + +#include "constexpr_char_traits.h" +#include "make_string.h" +#include "min_allocator.h" +#include "test_macros.h" + +// The expected result type shouldn't matter,therefore use a hardcoded value for simplicity. +using ExpectedResultType = bool; +constexpr ExpectedResultType visited{true}; + +template <class ExpectedR> +ExpectedR make_expected_result() { + if constexpr (std::is_same_v<ExpectedR, bool>) { + return true; + } else if constexpr (std::is_same_v<ExpectedR, long>) { + return 192812079084L; + } else { + return "visited"; + } +} + +template <class Context, class To, class ExpectedR, class From> +void test(From value, const ExpectedR& expectedValue) { + auto store = std::make_format_args<Context>(value); + std::basic_format_args<Context> format_args{store}; + + LIBCPP_ASSERT(format_args.__size() == 1); + assert(format_args.get(0)); + + // member + { + std::same_as<ExpectedR> decltype(auto) result = + format_args.get(0).template visit<ExpectedR>([v = To(value)](auto a) -> ExpectedR { + if constexpr (std::is_same_v<To, decltype(a)>) { + assert(v == a); + return make_expected_result<ExpectedR>(); + } else { + assert(false); + return {}; + } + }); + + assert(result == expectedValue); + } +} + +// Some types, as an extension, are stored in the variant. The Standard +// requires them to be observed as a handle. +template <class Context, class T, class ExpectedR> +void test_handle(T value, ExpectedR expectedValue) { + auto store = std::make_format_args<Context>(value); + std::basic_format_args<Context> format_args{store}; + + LIBCPP_ASSERT(format_args.__size() == 1); + assert(format_args.get(0)); + + std::same_as<ExpectedR> decltype(auto) result = format_args.get(0).template visit<ExpectedR>([](auto a) -> ExpectedR { + assert((std::is_same_v<decltype(a), typename std::basic_format_arg<Context>::handle>)); + + return make_expected_result<ExpectedR>(); + }); + + assert(result == expectedValue); +} + +// Test specific for string and string_view. +// +// Since both result in a string_view there's no need to pass this as a +// template argument. +template <class Context, class ExpectedR, class From> +void test_string_view(From value, ExpectedR expectedValue) { + auto store = std::make_format_args<Context>(value); + std::basic_format_args<Context> format_args{store}; + + LIBCPP_ASSERT(format_args.__size() == 1); + assert(format_args.get(0)); + + using CharT = typename Context::char_type; + using To = std::basic_string_view<CharT>; + using V = std::basic_string<CharT>; + + std::same_as<ExpectedR> decltype(auto) result = + format_args.get(0).template visit<ExpectedR>([v = V(value.begin(), value.end())](auto a) -> ExpectedR { + if constexpr (std::is_same_v<To, decltype(a)>) { + assert(v == a); + return make_expected_result<ExpectedR>(); + } else { + assert(false); + return {}; + } + }); + + assert(result == expectedValue); +} + +template <class CharT> +void test() { + using Context = std::basic_format_context<CharT*, CharT>; + std::basic_string<CharT> empty; + std::basic_string<CharT> str = MAKE_STRING(CharT, "abc"); + + // Test boolean types. + + test<Context, bool, ExpectedResultType>(true, visited); + test<Context, bool, ExpectedResultType>(false, visited); + + test<Context, bool, std::string>(true, "visited"); + test<Context, bool, std::string>(false, "visited"); + + test<Context, bool, long>(true, 192812079084L); + test<Context, bool, long>(false, 192812079084L); + + // Test CharT types. + + test<Context, CharT, ExpectedResultType, CharT>('a', visited); + test<Context, CharT, ExpectedResultType, CharT>('z', visited); + test<Context, CharT, ExpectedResultType, CharT>('0', visited); + test<Context, CharT, ExpectedResultType, CharT>('9', visited); + + // Test char types. + + if (std::is_same_v<CharT, char>) { + // char to char -> char + test<Context, CharT, ExpectedResultType, char>('a', visited); + test<Context, CharT, ExpectedResultType, char>('z', visited); + test<Context, CharT, ExpectedResultType, char>('0', visited); + test<Context, CharT, ExpectedResultType, char>('9', visited); + } else { + if (std::is_same_v<CharT, wchar_t>) { + // char to wchar_t -> wchar_t + test<Context, wchar_t, ExpectedResultType, char>('a', visited); + test<Context, wchar_t, ExpectedResultType, char>('z', visited); + test<Context, wchar_t, ExpectedResultType, char>('0', visited); + test<Context, wchar_t, ExpectedResultType, char>('9', visited); + } else if (std::is_signed_v<char>) { + // char to CharT -> int + // This happens when CharT is a char8_t, char16_t, or char32_t and char + // is a signed type. + // Note if sizeof(CharT) > sizeof(int) this test fails. If there are + // platforms where that occurs extra tests need to be added for char32_t + // testing it against a long long. + test<Context, int, ExpectedResultType, char>('a', visited); + test<Context, int, ExpectedResultType, char>('z', visited); + test<Context, int, ExpectedResultType, char>('0', visited); + test<Context, int, ExpectedResultType, char>('9', visited); + } else { + // char to CharT -> unsigned + // This happens when CharT is a char8_t, char16_t, or char32_t and char + // is an unsigned type. + // Note if sizeof(CharT) > sizeof(unsigned) this test fails. If there are + // platforms where that occurs extra tests need to be added for char32_t + // testing it against an unsigned long long. + test<Context, unsigned, ExpectedResultType, char>('a', visited); + test<Context, unsigned, ExpectedResultType, char>('z', visited); + test<Context, unsigned, ExpectedResultType, char>('0', visited); + test<Context, unsigned, ExpectedResultType, char>('9', visited); + } + } + + // Test signed integer types. + + test<Context, int, ExpectedResultType, signed char>(std::numeric_limits<signed char>::min(), visited); + test<Context, int, ExpectedResultType, signed char>(0, visited); + test<Context, int, ExpectedResultType, signed char>(std::numeric_limits<signed char>::max(), visited); + + test<Context, int, ExpectedResultType, short>(std::numeric_limits<short>::min(), visited); + test<Context, int, ExpectedResultType, short>(std::numeric_limits<signed char>::min(), visited); + test<Context, int, ExpectedResultType, short>(0, visited); + test<Context, int, ExpectedResultType, short>(std::numeric_limits<signed char>::max(), visited); + test<Context, int, ExpectedResultType, short>(std::numeric_limits<short>::max(), visited); + + test<Context, int, ExpectedResultType, int>(std::numeric_limits<int>::min(), visited); + test<Context, int, ExpectedResultType, int>(std::numeric_limits<short>::min(), visited); + test<Context, int, ExpectedResultType, int>(std::numeric_limits<signed char>::min(), visited); + test<Context, int, ExpectedResultType, int>(0, visited); + test<Context, int, ExpectedResultType, int>(std::numeric_limits<signed char>::max(), visited); + test<Context, int, ExpectedResultType, int>(std::numeric_limits<short>::max(), visited); + test<Context, int, ExpectedResultType, int>(std::numeric_limits<int>::max(), visited); + + using LongToType = std::conditional_t<sizeof(long) == sizeof(int), int, long long>; + + test<Context, LongToType, ExpectedResultType, long>(std::numeric_limits<long>::min(), visited); + test<Context, LongToType, ExpectedResultType, long>(std::numeric_limits<int>::min(), visited); + test<Context, LongToType, ExpectedResultType, long>(std::numeric_limits<short>::min(), visited); + test<Context, LongToType, ExpectedResultType, long>(std::numeric_limits<signed char>::min(), visited); + test<Context, LongToType, ExpectedResultType, long>(0, visited); + test<Context, LongToType, ExpectedResultType, long>(std::numeric_limits<signed char>::max(), visited); + test<Context, LongToType, ExpectedResultType, long>(std::numeric_limits<short>::max(), visited); + test<Context, LongToType, ExpectedResultType, long>(std::numeric_limits<int>::max(), visited); + test<Context, LongToType, ExpectedResultType, long>(std::numeric_limits<long>::max(), visited); + + test<Context, long long, ExpectedResultType, long long>(std::numeric_limits<long long>::min(), visited); + test<Context, long long, ExpectedResultType, long long>(std::numeric_limits<long>::min(), visited); + test<Context, long long, ExpectedResultType, long long>(std::numeric_limits<int>::min(), visited); + test<Context, long long, ExpectedResultType, long long>(std::numeric_limits<short>::min(), visited); + test<Context, long long, ExpectedResultType, long long>(std::numeric_limits<signed char>::min(), visited); + test<Context, long long, ExpectedResultType, long long>(0, visited); + test<Context, long long, ExpectedResultType, long long>(std::numeric_limits<signed char>::max(), visited); + test<Context, long long, ExpectedResultType, long long>(std::numeric_limits<short>::max(), visited); + test<Context, long long, ExpectedResultType, long long>(std::numeric_limits<int>::max(), visited); + test<Context, long long, ExpectedResultType, long long>(std::numeric_limits<long>::max(), visited); + test<Context, long long, ExpectedResultType, long long>(std::numeric_limits<long long>::max(), visited); + +#ifndef TEST_HAS_NO_INT128 + test_handle<Context, __int128_t, ExpectedResultType>(0, visited); +#endif // TEST_HAS_NO_INT128 + + // Test unsigned integer types. + + test<Context, unsigned, ExpectedResultType, unsigned char>(0, visited); + test<Context, unsigned, ExpectedResultType, unsigned char>(std::numeric_limits<unsigned char>::max(), visited); + + test<Context, unsigned, ExpectedResultType, unsigned short>(0, visited); + test<Context, unsigned, ExpectedResultType, unsigned short>(std::numeric_limits<unsigned char>::max(), visited); + test<Context, unsigned, ExpectedResultType, unsigned short>(std::numeric_limits<unsigned short>::max(), visited); + + test<Context, unsigned, ExpectedResultType, unsigned>(0, visited); + test<Context, unsigned, ExpectedResultType, unsigned>(std::numeric_limits<unsigned char>::max(), visited); + test<Context, unsigned, ExpectedResultType, unsigned>(std::numeric_limits<unsigned short>::max(), visited); + test<Context, unsigned, ExpectedResultType, unsigned>(std::numeric_limits<unsigned>::max(), visited); + + using UnsignedLongToType = + std::conditional_t<sizeof(unsigned long) == sizeof(unsigned), unsigned, unsigned long long>; + + test<Context, UnsignedLongToType, ExpectedResultType, unsigned long>(0, visited); + test<Context, UnsignedLongToType, ExpectedResultType, unsigned long>( + std::numeric_limits<unsigned char>::max(), visited); + test<Context, UnsignedLongToType, ExpectedResultType, unsigned long>( + std::numeric_limits<unsigned short>::max(), visited); + test<Context, UnsignedLongToType, ExpectedResultType, unsigned long>(std::numeric_limits<unsigned>::max(), visited); + test<Context, UnsignedLongToType, ExpectedResultType, unsigned long>( + std::numeric_limits<unsigned long>::max(), visited); + + test<Context, unsigned long long, ExpectedResultType, unsigned long long>(0, visited); + test<Context, unsigned long long, ExpectedResultType, unsigned long long>( + std::numeric_limits<unsigned char>::max(), visited); + test<Context, unsigned long long, ExpectedResultType, unsigned long long>( + std::numeric_limits<unsigned short>::max(), visited); + test<Context, unsigned long long, ExpectedResultType, unsigned long long>( + std::numeric_limits<unsigned>::max(), visited); + test<Context, unsigned long long, ExpectedResultType, unsigned long long>( + std::numeric_limits<unsigned long>::max(), visited); + test<Context, unsigned long long, ExpectedResultType, unsigned long long>( + std::numeric_limits<unsigned long long>::max(), visited); + +#ifndef TEST_HAS_NO_INT128 + test_handle<Context, __uint128_t, ExpectedResultType>(0, visited); +#endif // TEST_HAS_NO_INT128 + + // Test floating point types. + + test<Context, float, ExpectedResultType, float>(-std::numeric_limits<float>::max(), visited); + test<Context, float, ExpectedResultType, float>(-std::numeric_limits<float>::min(), visited); + test<Context, float, ExpectedResultType, float>(-0.0, visited); + test<Context, float, ExpectedResultType, float>(0.0, visited); + test<Context, float, ExpectedResultType, float>(std::numeric_limits<float>::min(), visited); + test<Context, float, ExpectedResultType, float>(std::numeric_limits<float>::max(), visited); + + test<Context, double, ExpectedResultType, double>(-std::numeric_limits<double>::max(), visited); + test<Context, double, ExpectedResultType, double>(-std::numeric_limits<double>::min(), visited); + test<Context, double, ExpectedResultType, double>(-0.0, visited); + test<Context, double, ExpectedResultType, double>(0.0, visited); + test<Context, double, ExpectedResultType, double>(std::numeric_limits<double>::min(), visited); + test<Context, double, ExpectedResultType, double>(std::numeric_limits<double>::max(), visited); + + test<Context, long double, ExpectedResultType, long double>(-std::numeric_limits<long double>::max(), visited); + test<Context, long double, ExpectedResultType, long double>(-std::numeric_limits<long double>::min(), visited); + test<Context, long double, ExpectedResultType, long double>(-0.0, visited); + test<Context, long double, ExpectedResultType, long double>(0.0, visited); + test<Context, long double, ExpectedResultType, long double>(std::numeric_limits<long double>::min(), visited); + test<Context, long double, ExpectedResultType, long double>(std::numeric_limits<long double>::max(), visited); + + // Test const CharT pointer types. + + test<Context, const CharT*, ExpectedResultType, const CharT*>(empty.c_str(), visited); + test<Context, const CharT*, ExpectedResultType, const CharT*>(str.c_str(), visited); + + // Test string_view types. + + { + using From = std::basic_string_view<CharT>; + + test_string_view<Context, ExpectedResultType>(From(), visited); + test_string_view<Context, ExpectedResultType>(From(empty.c_str()), visited); + test_string_view<Context, ExpectedResultType>(From(str.c_str()), visited); + } + { + using From = std::basic_string_view<CharT, constexpr_char_traits<CharT>>; + + test_string_view<Context, ExpectedResultType>(From(), visited); + test_string_view<Context, ExpectedResultType>(From(empty.c_str()), visited); + test_string_view<Context, ExpectedResultType>(From(str.c_str()), visited); + } + + // Test string types. + + { + using From = std::basic_string<CharT>; + + test_string_view<Context, ExpectedResultType>(From(), visited); + test_string_view<Context, ExpectedResultType>(From(empty.c_str()), visited); + test_string_view<Context, ExpectedResultType>(From(str.c_str()), visited); + } + + { + using From = std::basic_string<CharT, constexpr_char_traits<CharT>, std::allocator<CharT>>; + + test_string_view<Context, ExpectedResultType>(From(), visited); + test_string_view<Context, ExpectedResultType>(From(empty.c_str()), visited); + test_string_view<Context, ExpectedResultType>(From(str.c_str()), visited); + } + + { + using From = std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>>; + + test_string_view<Context, ExpectedResultType>(From(), visited); + test_string_view<Context, ExpectedResultType>(From(empty.c_str()), visited); + test_string_view<Context, ExpectedResultType>(From(str.c_str()), visited); + } + + { + using From = std::basic_string<CharT, constexpr_char_traits<CharT>, min_allocator<CharT>>; + + test_string_view<Context, ExpectedResultType>(From(), visited); + test_string_view<Context, ExpectedResultType>(From(empty.c_str()), visited); + test_string_view<Context, ExpectedResultType>(From(str.c_str()), visited); + } + + // Test pointer types. + + test<Context, const void*, ExpectedResultType>(nullptr, visited); + int i = 0; + test<Context, const void*, ExpectedResultType>(static_cast<void*>(&i), visited); + const int ci = 0; + test<Context, const void*, ExpectedResultType>(static_cast<const void*>(&ci), visited); +} + +int main(int, char**) { + test<char>(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test<wchar_t>(); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.deprecated.verify.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.deprecated.verify.cpp new file mode 100644 index 000000000000000..acd9228369e6091 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.deprecated.verify.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23 +// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME +// UNSUPPORTED: clang-16 || clang-17 +// XFAIL: apple-clang + +// <format> + +// template<class Visitor, class Context> +// see below visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg); + +#include <format> +#include <tuple> + +#include "test_macros.h" + +template <typename CharT, class To, class From> +void test(From value) { + using Context = std::basic_format_context<CharT*, CharT>; + auto store = std::make_format_args<Context>(value); + std::basic_format_args<Context> format_args{store}; + + // expected-warning-re@+1 1-2 {{std::basic_format_context{{.*}}' is deprecated}} + std::ignore = std::visit_format_arg([]([[maybe_unused]] auto a) -> To { return {}; }, format_args.get(0)); +} + +void test() { + test<char, bool>('a'); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test<wchar_t, bool>('a'); +#endif +} diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp index 3ddf2d0ff732a76..3497d8935c8d623 100644 --- a/libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp @@ -11,7 +11,7 @@ // <format> // template<class Visitor, class Context> -// see below visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg); +// see below visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg); // Deprecated in C++26 #include <algorithm> #include <format> @@ -23,6 +23,10 @@ #include "make_string.h" #include "min_allocator.h" +#if TEST_STD_VER >= 26 && defined(TEST_HAS_EXPLICIT_THIS_PARAMETER) +TEST_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-declarations") +#endif + template <class Context, class To, class From> void test(From value) { auto store = std::make_format_args<Context>(value); diff --git a/libcxx/test/std/utilities/format/format.arguments/format.args/get.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.args/get.pass.cpp index 0f3931adf54dfbe..a5d3703397f4e4a 100644 --- a/libcxx/test/std/utilities/format/format.arguments/format.args/get.pass.cpp +++ b/libcxx/test/std/utilities/format/format.arguments/format.args/get.pass.cpp @@ -24,14 +24,17 @@ void test(From value) { auto store = std::make_format_args<Context>(value); const std::basic_format_args<Context> format_args{store}; - std::visit_format_arg( - [v = To(value)](auto a) { - if constexpr (std::is_same_v<To, decltype(a)>) - assert(v == a); - else - assert(false); - }, - format_args.get(0)); + auto visitor = [v = To(value)](auto a) { + if constexpr (std::is_same_v<To, decltype(a)>) + assert(v == a); + else + assert(false); + }; +#if TEST_STD_VER >= 26 && defined(TEST_HAS_EXPLICIT_THIS_PARAMETER) + format_args.get(0).visit(visitor); +#else + std::visit_format_arg(visitor, format_args.get(0)); +#endif } // Some types, as an extension, are stored in the variant. The Standard @@ -41,9 +44,12 @@ void test_handle(T value) { auto store = std::make_format_args<Context>(value); std::basic_format_args<Context> format_args{store}; - std::visit_format_arg( - [](auto a) { assert((std::is_same_v<decltype(a), typename std::basic_format_arg<Context>::handle>)); }, - format_args.get(0)); + auto visitor = [](auto a) { assert((std::is_same_v<decltype(a), typename std::basic_format_arg<Context>::handle>)); }; +#if TEST_STD_VER >= 26 && defined(TEST_HAS_EXPLICIT_THIS_PARAMETER) + format_args.get(0).visit(visitor); +#else + std::visit_format_arg(visitor, format_args.get(0)); +#endif } // Test specific for string and string_view. @@ -58,14 +64,18 @@ void test_string_view(From value) { using CharT = typename Context::char_type; using To = std::basic_string_view<CharT>; using V = std::basic_string<CharT>; - std::visit_format_arg( - [v = V(value.begin(), value.end())](auto a) { - if constexpr (std::is_same_v<To, decltype(a)>) - assert(v == a); - else - assert(false); - }, - format_args.get(0)); + + auto visitor = [v = V(value.begin(), value.end())](auto a) { + if constexpr (std::is_same_v<To, decltype(a)>) + assert(v == a); + else + assert(false); + }; +#if TEST_STD_VER >= 26 && defined(TEST_HAS_EXPLICIT_THIS_PARAMETER) + format_args.get(0).visit(visitor); +#else + std::visit_format_arg(visitor, format_args.get(0)); +#endif } template <class CharT> diff --git a/libcxx/test/support/test_basic_format_arg.h b/libcxx/test/support/test_basic_format_arg.h index d8547d34b6db436..1ec719a48b6c8ed 100644 --- a/libcxx/test/support/test_basic_format_arg.h +++ b/libcxx/test/support/test_basic_format_arg.h @@ -7,18 +7,22 @@ #include <concepts> #include <format> +#include <utility> #include "test_macros.h" /// Returns whether the basic_format_arg contains a type T with the expected value. template <class Context, class T> bool test_basic_format_arg(std::basic_format_arg<Context> arg, T expected) { - return std::visit_format_arg( - [expected](auto a) { - if constexpr (std::same_as<decltype(a), T>) - return a == expected; - else - return false; - }, - arg); + auto visitor = [expected](auto a) { + if constexpr (std::same_as<decltype(a), T>) + return a == expected; + else + return false; + }; +#if TEST_STD_VER >= 26 && defined(TEST_HAS_EXPLICIT_THIS_PARAMETER) + return arg.visit(std::move(visitor)); +#else + return std::visit_format_arg(std::move(visitor), arg); +#endif } diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h index fe68e13de6bcbaa..24f69c758f365cc 100644 --- a/libcxx/test/support/test_macros.h +++ b/libcxx/test/support/test_macros.h @@ -470,4 +470,9 @@ inline Tp const& DoNotOptimize(Tp const& value) { # define TEST_IF_AIX(arg_true, arg_false) arg_false #endif +// Clang-18 has support for deducing this, but it does not set the FTM. +#ifdef _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER +# define TEST_HAS_EXPLICIT_THIS_PARAMETER +#endif + #endif // SUPPORT_TEST_MACROS_HPP diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index b7d95d451f21376..065b70620cd170e 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -467,6 +467,7 @@ def add_version_header(tc): # "c++20": 202110 Not implemented P2372R3 Fixing locale handling in chrono formatters "c++20": 202106, # "c++23": 202207, Not implemented P2419R2 Clarify handling of encodings in localized formatting of chrono types + # "c++26": 202306, P2637R3 Member Visit (implemented) # "c++26": 202311, P2918R2 Runtime format strings II (implemented) }, # Note these three papers are adopted at the June 2023 meeting and have sequential numbering >From bd816bb5f141caf7f527ed2ba9c6e0531e5f57de Mon Sep 17 00:00:00 2001 From: Zingam <zin...@outlook.com> Date: Mon, 22 Jan 2024 20:42:27 +0200 Subject: [PATCH 2/2] Quick fix --- .../format.arguments/format.arg/visit.return_type.pass.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp index 473278b7b443490..b2c40d160454759 100644 --- a/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp @@ -131,9 +131,6 @@ void test() { test<Context, bool, std::string>(true, "visited"); test<Context, bool, std::string>(false, "visited"); - test<Context, bool, long>(true, 192812079084L); - test<Context, bool, long>(false, 192812079084L); - // Test CharT types. test<Context, CharT, ExpectedResultType, CharT>('a', visited); _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits