diff --git a/libcxx/include/__format/format_arg.h b/libcxx/include/__format/format_arg.h index ed5e76275ea87..2fe5571584dc9 100644 --- a/libcxx/include/__format/format_arg.h +++ b/libcxx/include/__format/format_arg.h @@ -56,7 +56,7 @@ namespace __format { /// @note Some members of this enum are an extension. These extensions need /// special behaviour in visit_format_arg. There they need to be wrapped in a /// handle to satisfy the user observable behaviour. The internal function -/// __visit_format_arg doesn't do this wrapping. So in the format functions +/// __directly_visit_format_arg doesn't do this wrapping. So in the format functions /// this function is used to avoid unneeded overhead. enum class __arg_t : uint8_t { __none, @@ -98,10 +98,86 @@ _LIBCPP_HIDE_FROM_ABI constexpr __arg_t __get_packed_type(uint64_t __types, size } // namespace __format -// This function is not user observable, so it can directly use the non-standard -// types of the "variant". See __arg_t for more details. +/// Contains the values used in basic_format_arg. +/// +/// This is a separate type so it's possible to store the values and types in +/// separate arrays. +template +class __basic_format_arg_value { + using _CharT _LIBCPP_NODEBUG = typename _Context::char_type; + +public: + /// Contains the implementation for basic_format_arg::handle. + struct __handle { + template + _LIBCPP_HIDE_FROM_ABI explicit __handle(_Tp& __v) noexcept + : __ptr_(std::addressof(__v)), + __format_([](basic_format_parse_context<_CharT>& __parse_ctx, _Context& __ctx, const void* __ptr) { + using _Dp = remove_const_t<_Tp>; + using _Qp = conditional_t<__formattable_with, const _Dp, _Dp>; + static_assert(__formattable_with<_Qp, _Context>, "Mandated by [format.arg]/10"); + + typename _Context::template formatter_type<_Dp> __f; + __parse_ctx.advance_to(__f.parse(__parse_ctx)); + __ctx.advance_to(__f.format(*const_cast<_Qp*>(static_cast(__ptr)), __ctx)); + }) {} + + const void* __ptr_; + void (*__format_)(basic_format_parse_context<_CharT>&, _Context&, const void*); + }; + + union { + monostate __monostate_; + bool __boolean_; + _CharT __char_type_; + int __int_; + unsigned __unsigned_; + long long __long_long_; + unsigned long long __unsigned_long_long_; +# if _LIBCPP_HAS_INT128 + __int128_t __i128_; + __uint128_t __u128_; +# endif + float __float_; + double __double_; + long double __long_double_; + const _CharT* __const_char_type_ptr_; + basic_string_view<_CharT> __string_view_; + const void* __ptr_; + __handle __handle_; + }; + + // These constructors contain the exact storage type used. If adjustments are + // required, these will be done in __create_format_arg. + + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value() noexcept : __monostate_() {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(bool __value) noexcept : __boolean_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(_CharT __value) noexcept : __char_type_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(int __value) noexcept : __int_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(unsigned __value) noexcept : __unsigned_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(long long __value) noexcept : __long_long_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(unsigned long long __value) noexcept + : __unsigned_long_long_(__value) {} +# if _LIBCPP_HAS_INT128 + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__int128_t __value) noexcept : __i128_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__uint128_t __value) noexcept : __u128_(__value) {} +# endif + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(float __value) noexcept : __float_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(double __value) noexcept : __double_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(long double __value) noexcept : __long_double_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(const _CharT* __value) noexcept : __const_char_type_ptr_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(basic_string_view<_CharT> __value) noexcept + : __string_view_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(const void* __value) noexcept : __ptr_(__value) {} + _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__handle&& __value) noexcept : __handle_(std::move(__value)) {} +}; + +// This function is not user observable, so it can directly use the non-standard types of the "variant". +// See __arg_t for more details. For direct visitation, see https://reviews.llvm.org/D138052. +// TODO: Investigate why GCC 15 hangs if something like std::__visit_format_arg<__direct::__yes>(...) is used, and fuse +// __visit_format_arg and __directly_visit_format_arg once GCC no longer hangs. template -_LIBCPP_HIDE_FROM_ABI decltype(auto) __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { +_LIBCPP_HIDE_FROM_ABI decltype(auto) __directly_visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { switch (__arg.__type_) { case __format::__arg_t::__none: return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__monostate_); @@ -149,6 +225,64 @@ _LIBCPP_HIDE_FROM_ABI decltype(auto) __visit_format_arg(_Visitor&& __vis, basic_ __libcpp_unreachable(); } +// __visit_format_arg is same as __directly_visit_format_arg except for indirectly visitation of 128-bit integers. +// Per [format.arg], the variant alternative types are fully specified, so we need to avoid direct visitation of 128-bit +// extended integer types when the visitor is user-provided. +template +_LIBCPP_HIDE_FROM_ABI decltype(auto) __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { + switch (__arg.__type_) { + case __format::__arg_t::__none: + return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__monostate_); + case __format::__arg_t::__boolean: + return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__boolean_); + case __format::__arg_t::__char_type: + return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__char_type_); + case __format::__arg_t::__int: + return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__int_); + case __format::__arg_t::__long_long: + return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__long_long_); + case __format::__arg_t::__i128: +# if _LIBCPP_HAS_INT128 + { + 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}); + } +# else + __libcpp_unreachable(); +# endif + case __format::__arg_t::__unsigned: + return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__unsigned_); + case __format::__arg_t::__unsigned_long_long: + return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__unsigned_long_long_); + case __format::__arg_t::__u128: +# if _LIBCPP_HAS_INT128 + { + 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}); + } +# else + __libcpp_unreachable(); +# endif + case __format::__arg_t::__float: + return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__float_); + case __format::__arg_t::__double: + return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__double_); + case __format::__arg_t::__long_double: + return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__long_double_); + case __format::__arg_t::__const_char_type_ptr: + return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__const_char_type_ptr_); + case __format::__arg_t::__string_view: + return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__string_view_); + case __format::__arg_t::__ptr: + return std::invoke(std::forward<_Visitor>(__vis), __arg.__value_.__ptr_); + case __format::__arg_t::__handle: + return std::invoke( + std::forward<_Visitor>(__vis), typename basic_format_arg<_Context>::handle{__arg.__value_.__handle_}); + } + + __libcpp_unreachable(); +} + # if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER template @@ -166,7 +300,10 @@ _LIBCPP_HIDE_FROM_ABI _Rp __visit_format_arg(_Visitor&& __vis, basic_format_arg< return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__long_long_); case __format::__arg_t::__i128: # if _LIBCPP_HAS_INT128 - return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__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}); + } # else __libcpp_unreachable(); # endif @@ -176,7 +313,10 @@ _LIBCPP_HIDE_FROM_ABI _Rp __visit_format_arg(_Visitor&& __vis, basic_format_arg< return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__unsigned_long_long_); case __format::__arg_t::__u128: # if _LIBCPP_HAS_INT128 - return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__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}); + } # else __libcpp_unreachable(); # endif @@ -202,80 +342,6 @@ _LIBCPP_HIDE_FROM_ABI _Rp __visit_format_arg(_Visitor&& __vis, basic_format_arg< # endif // _LIBCPP_STD_VER >= 26 && _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 -/// separate arrays. -template -class __basic_format_arg_value { - using _CharT _LIBCPP_NODEBUG = typename _Context::char_type; - -public: - /// Contains the implementation for basic_format_arg::handle. - struct __handle { - template - _LIBCPP_HIDE_FROM_ABI explicit __handle(_Tp& __v) noexcept - : __ptr_(std::addressof(__v)), - __format_([](basic_format_parse_context<_CharT>& __parse_ctx, _Context& __ctx, const void* __ptr) { - using _Dp = remove_const_t<_Tp>; - using _Qp = conditional_t<__formattable_with, const _Dp, _Dp>; - static_assert(__formattable_with<_Qp, _Context>, "Mandated by [format.arg]/10"); - - typename _Context::template formatter_type<_Dp> __f; - __parse_ctx.advance_to(__f.parse(__parse_ctx)); - __ctx.advance_to(__f.format(*const_cast<_Qp*>(static_cast(__ptr)), __ctx)); - }) {} - - const void* __ptr_; - void (*__format_)(basic_format_parse_context<_CharT>&, _Context&, const void*); - }; - - union { - monostate __monostate_; - bool __boolean_; - _CharT __char_type_; - int __int_; - unsigned __unsigned_; - long long __long_long_; - unsigned long long __unsigned_long_long_; -# if _LIBCPP_HAS_INT128 - __int128_t __i128_; - __uint128_t __u128_; -# endif - float __float_; - double __double_; - long double __long_double_; - const _CharT* __const_char_type_ptr_; - basic_string_view<_CharT> __string_view_; - const void* __ptr_; - __handle __handle_; - }; - - // These constructors contain the exact storage type used. If adjustments are - // required, these will be done in __create_format_arg. - - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value() noexcept : __monostate_() {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(bool __value) noexcept : __boolean_(__value) {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(_CharT __value) noexcept : __char_type_(__value) {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(int __value) noexcept : __int_(__value) {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(unsigned __value) noexcept : __unsigned_(__value) {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(long long __value) noexcept : __long_long_(__value) {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(unsigned long long __value) noexcept - : __unsigned_long_long_(__value) {} -# if _LIBCPP_HAS_INT128 - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__int128_t __value) noexcept : __i128_(__value) {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__uint128_t __value) noexcept : __u128_(__value) {} -# endif - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(float __value) noexcept : __float_(__value) {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(double __value) noexcept : __double_(__value) {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(long double __value) noexcept : __long_double_(__value) {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(const _CharT* __value) noexcept : __const_char_type_ptr_(__value) {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(basic_string_view<_CharT> __value) noexcept - : __string_view_(__value) {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(const void* __value) noexcept : __ptr_(__value) {} - _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__handle&& __value) noexcept : __handle_(std::move(__value)) {} -}; - template class _LIBCPP_NO_SPECIALIZATIONS basic_format_arg { public: @@ -291,42 +357,14 @@ class _LIBCPP_NO_SPECIALIZATIONS basic_format_arg { // the "variant" in a handle to stay conforming. See __arg_t for more details. template _LIBCPP_HIDE_FROM_ABI decltype(auto) visit(this basic_format_arg __arg, _Visitor&& __vis) { - switch (__arg.__type_) { -# if _LIBCPP_HAS_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); - } + 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 _LIBCPP_HIDE_FROM_ABI _Rp visit(this basic_format_arg __arg, _Visitor&& __vis) { - switch (__arg.__type_) { -# if _LIBCPP_HAS_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); - } + return std::__visit_format_arg<_Rp>(std::forward<_Visitor>(__vis), __arg); } # endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER @@ -376,21 +414,7 @@ _LIBCPP_DEPRECATED_IN_CXX26 # endif _LIBCPP_HIDE_FROM_ABI decltype(auto) visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { - switch (__arg.__type_) { -# if _LIBCPP_HAS_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 // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER - default: - return std::__visit_format_arg(std::forward<_Visitor>(__vis), __arg); - } + return std::__visit_format_arg(std::forward<_Visitor>(__vis), __arg); } #endif // _LIBCPP_STD_VER >= 20 diff --git a/libcxx/include/__format/format_functions.h b/libcxx/include/__format/format_functions.h index 873265bc17c24..c99113e6a8020 100644 --- a/libcxx/include/__format/format_functions.h +++ b/libcxx/include/__format/format_functions.h @@ -275,7 +275,7 @@ __handle_replacement_field(_Iterator __begin, _Iterator __end, _ParseCtx& __pars else if (__parse) __format::__compile_time_visit_format_arg(__parse_ctx, __ctx, __type); } else - std::__visit_format_arg( + std::__visit_format_arg( // TODO: Use __directly_visit_format_arg? [&](auto __arg) { if constexpr (same_as) std::__throw_format_error("The argument index value is too large for the number of arguments supplied"); @@ -468,7 +468,7 @@ template if (auto __only_first_arg = __fmt == _LIBCPP_STATICALLY_WIDEN(_CharT, "{}"); __builtin_constant_p(__only_first_arg) && __only_first_arg) { if (auto __arg = __args.get(0); __builtin_constant_p(__arg.__type_)) { - return std::__visit_format_arg( + return std::__visit_format_arg( // TODO: Use __directly_visit_format_arg? [](_Tp&& __argument) -> optional> { if constexpr (is_same_v, basic_string_view<_CharT>>) { return basic_string<_CharT>{__argument}; diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h index 99ab3dc23c295..818cf21a76d37 100644 --- a/libcxx/include/__format/parser_std_format_spec.h +++ b/libcxx/include/__format/parser_std_format_spec.h @@ -83,26 +83,21 @@ __parse_arg_id(_Iterator __begin, _Iterator __end, _ParseContext& __ctx) { template _LIBCPP_HIDE_FROM_ABI constexpr uint32_t __substitute_arg_id(basic_format_arg<_Context> __format_arg) { - // [format.string.std]/8 - // If the corresponding formatting argument is not of integral type... - // This wording allows char and bool too. LWG-3720 changes the wording to - // If the corresponding formatting argument is not of standard signed or - // unsigned integer type, - // This means the 128-bit will not be valid anymore. - // TODO FMT Verify this resolution is accepted and add a test to verify - // 128-bit integrals fail and switch to visit_format_arg. + // [format.string.std]/10 + // [...] The option is valid only if the corresponding formatting argument is of standard signed or unsigned integer + // type. [...] + // This means 128-bit extented integer types are invalid here. return std::__visit_format_arg( [](auto __arg) -> uint32_t { using _Type = decltype(__arg); if constexpr (same_as<_Type, monostate>) std::__throw_format_error("The argument index value is too large for the number of arguments supplied"); - // [format.string.std]/8 - // If { arg-idopt } is used in a width or precision, the value of the - // corresponding formatting argument is used in its place. If the - // corresponding formatting argument is not of standard signed or unsigned - // integer type, or its value is negative for precision or non-positive for - // width, an exception of type format_error is thrown. + // [format.string.std]/10 + // If { arg-idopt } is used in a width or precision option, the value of the corresponding formatting argument + // is used as the value of the option. The option is valid only if the corresponding formatting argument is of + // standard signed or unsigned integer type. If its value is negative, an exception of type format_error is + // thrown. // // When an integral is used in a format function, it is stored as one of // the types checked below. Other integral types are promoted. For example, 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 index 20e0a5ed66bd0..2013b41ceaaf6 100644 --- 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 @@ -27,6 +27,7 @@ #include "make_string.h" #include "min_allocator.h" #include "test_macros.h" +#include "visitors.h" template void test(From value) { @@ -36,6 +37,9 @@ void test(From value) { LIBCPP_ASSERT(format_args.__size() == 1); assert(format_args.get(0)); + // https://github.com/llvm/llvm-project/issues/139582 + format_args.get(0).visit(limited_visitor{}); + auto result = format_args.get(0).visit([v = To(value)](auto a) -> To { if constexpr (std::is_same_v) { assert(v == a); @@ -60,6 +64,9 @@ void test_handle(T value) { LIBCPP_ASSERT(format_args.__size() == 1); assert(format_args.get(0)); + // https://github.com/llvm/llvm-project/issues/139582 + format_args.get(0).visit(limited_visitor{}); + format_args.get(0).visit([](auto a) { assert((std::is_same_v::handle>)); }); 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 8a79dd4d50f20..39cd757f1c568 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 @@ -27,6 +27,7 @@ #include "make_string.h" #include "min_allocator.h" #include "test_macros.h" +#include "visitors.h" // The expected result type shouldn't matter,therefore use a hardcoded value for simplicity. using ExpectedResultType = bool; @@ -117,6 +118,26 @@ void test_string_view(From value, ExpectedR expectedValue) { assert(result == expectedValue); } +// https://github.com/llvm/llvm-project/issues/139582 +template +void test_limited_visitation(From value) { + auto store = std::make_format_args(value); + std::basic_format_args format_args{store}; + + LIBCPP_ASSERT(format_args.__size() == 1); + assert(format_args.get(0)); + + if constexpr (std::is_void_v) { + format_args.get(0).template visit(limited_int_visitor{}); + static_assert( + std::is_same_v(limited_int_visitor{}))>); + } else { + std::same_as decltype(auto) result = + format_args.get(0).template visit(limited_int_visitor{}); + assert(result); + } +} + template void test() { using Context = std::basic_format_context; @@ -354,6 +375,25 @@ void test() { test(static_cast(&i), visited); const int ci = 0; test(static_cast(&ci), visited); + + // https://github.com/llvm/llvm-project/issues/139582 + // Test limited visitors. + test_limited_visitation(true); + test_limited_visitation(42); + test_limited_visitation(0.5); + test_limited_visitation(str); +#ifndef TEST_HAS_NO_INT128 + test_limited_visitation(__int128_t{0}); + test_limited_visitation(__uint128_t{0}); +#endif // TEST_HAS_NO_INT128 + test_limited_visitation(true); + test_limited_visitation(42); + test_limited_visitation(0.5); + test_limited_visitation(str); +#ifndef TEST_HAS_NO_INT128 + test_limited_visitation(__int128_t{0}); + test_limited_visitation(__uint128_t{0}); +#endif // TEST_HAS_NO_INT128 } int main(int, char**) { 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 d99675a71f321..55a58cff9a861 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 @@ -24,6 +24,7 @@ #include "test_macros.h" #include "make_string.h" #include "min_allocator.h" +#include "visitors.h" #if TEST_STD_VER >= 26 && defined(TEST_HAS_EXPLICIT_THIS_PARAMETER) TEST_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-declarations") @@ -37,6 +38,9 @@ void test(From value) { LIBCPP_ASSERT(format_args.__size() == 1); assert(format_args.get(0)); + // https://github.com/llvm/llvm-project/issues/139582 + std::visit_format_arg(limited_visitor{}, format_args.get(0)); + auto result = std::visit_format_arg( [v = To(value)](auto a) -> To { if constexpr (std::is_same_v) { @@ -63,6 +67,9 @@ void test_handle(T value) { LIBCPP_ASSERT(format_args.__size() == 1); assert(format_args.get(0)); + // https://github.com/llvm/llvm-project/issues/139582 + std::visit_format_arg(limited_visitor{}, format_args.get(0)); + std::visit_format_arg( [](auto a) { assert((std::is_same_v::handle>)); }, format_args.get(0)); diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/visitors.h b/libcxx/test/std/utilities/format/format.arguments/format.arg/visitors.h new file mode 100644 index 0000000000000..a188a0089f99c --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg/visitors.h @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_STD_UTILITIES_FORMAT_FORMAT_FORMAT_ARGUMENTS_FORMAT_ARG_VISITORS_H +#define TEST_STD_UTILITIES_FORMAT_FORMAT_FORMAT_ARGUMENTS_FORMAT_ARG_VISITORS_H + +#include +#include +#include +#include + +// [format.arg] +// namespace std { +// template +// class basic_format_arg { +// public: +// class handle; +// private: +// [...] +// using char_type = typename Context::char_type; // exposition only +// +// variant, +// const void*, handle> value; // exposition only +// [...] +// }; +// } + +template +concept format_arg_visit_type_for = + std::same_as || std::same_as || std::same_as || + std::same_as || std::same_as || std::same_as || + std::same_as || std::same_as || std::same_as || + std::same_as || std::same_as || + std::same_as> || std::same_as || + std::same_as::handle>; + +// Verify that visitors don't see other types. + +template +struct limited_visitor { + void operator()(auto) const = delete; + + void operator()(format_arg_visit_type_for auto) const {} +}; + +template +struct limited_int_visitor { + void operator()(auto) const = delete; + + int operator()(format_arg_visit_type_for auto) const { return 42; } +}; + +#endif // TEST_STD_UTILITIES_FORMAT_FORMAT_FORMAT_ARGUMENTS_FORMAT_ARG_VISITORS_H