[v2] libstdc++: Hide 128-bit int and float types behind handle for basic_format_arg visitation [PR108053]

Message ID 20250312150106.78350-1-tkaminsk@redhat.com
State New
Headers
Series [v2] libstdc++: Hide 128-bit int and float types behind handle for basic_format_arg visitation [PR108053] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-aarch64-bootstrap success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap success Build passed

Commit Message

Tomasz Kamiński March 12, 2025, 2:39 p.m. UTC
  Implement visit_format_arg and basic_format_arg::visit function,
in terms of  _M_visit_user member functions, that wraps any type
stored inside basic_format_arg, that is not specified as standard
into the handle. This affects __in128, unsigned __in128,
PowerPC specific __iee128 and __ibm128, and  _Float128 for architectures
where long double is not 128bits.

The bfloat16, _Float16, _Float32, _Float32, and _Float128 for
128bits long double are not are not addressed, as they
are transformed into standard floating point types.

For internal purposes __format::__visit_format_arg function is
used, that provides an umodifed access to stored object.

	PR libstdc++/108053

libstdc++-v3/ChangeLog:

	* include/std/format (basic_format_arg::_M_visit_user):
	Helper function for wrapping extension types into handle
	(visit_format_arg): Call `_M_visit_user` instead of `_M_visit`.
	(basic_format_arg::visit): As above.
	(__format::__visit_format_arg): Provides direct access to
	values stored in basic_format_arg.
	(__format::__int_from_arg): Use __format::__visit_format_arg
	instead of std::visit_format_arg.
	(_Formatting_scanner::_M_format_arg): As above.
	(_Checking_scanner::__do_vformat_to): As above.
	* testsuite/std/format/arguments/args.cc: New tests.
	* testsuite/std/format/string.cc: Test for using __int128
	as width/precision.
---
Revision added missing decltype(auto) return type for lambda in _M_user_visit.
Tested on x84_64-linux. Tested arguments/args.cc` with -mlong-double-128.
Tested `std/format` test on ppc64le-linux, with both -mabi=ieeelongdouble and -mabi=ibmlongdouble.
OK for trunk?

 libstdc++-v3/include/std/format               | 49 +++++++++++--
 .../testsuite/std/format/arguments/args.cc    | 73 +++++++++++++++++++
 libstdc++-v3/testsuite/std/format/string.cc   | 10 ++-
 3 files changed, 123 insertions(+), 9 deletions(-)
  

Comments

Jonathan Wakely March 12, 2025, 5:53 p.m. UTC | #1
On 12/03/25 15:39 +0100, Tomasz Kamiński wrote:
>Implement visit_format_arg and basic_format_arg::visit function,
>in terms of  _M_visit_user member functions, that wraps any type
>stored inside basic_format_arg, that is not specified as standard
>into the handle. This affects __in128, unsigned __in128,

Spelling: __int128 (both times)

>PowerPC specific __iee128 and __ibm128, and  _Float128 for architectures

__ieee128

>where long double is not 128bits.
>
>The bfloat16, _Float16, _Float32, _Float32, and _Float128 for
>128bits long double are not are not addressed, as they
>are transformed into standard floating point types.
>
>For internal purposes __format::__visit_format_arg function is
>used, that provides an umodifed access to stored object.

unmodified

>
>	PR libstdc++/108053
>
>libstdc++-v3/ChangeLog:
>
>	* include/std/format (basic_format_arg::_M_visit_user):
>	Helper function for wrapping extension types into handle

Missing '.' at the end of the sentence.

>	(visit_format_arg): Call `_M_visit_user` instead of `_M_visit`.
>	(basic_format_arg::visit): As above.
>	(__format::__visit_format_arg): Provides direct access to
>	values stored in basic_format_arg.
>	(__format::__int_from_arg): Use __format::__visit_format_arg
>	instead of std::visit_format_arg.
>	(_Formatting_scanner::_M_format_arg): As above.
>	(_Checking_scanner::__do_vformat_to): As above.
>	* testsuite/std/format/arguments/args.cc: New tests.
>	* testsuite/std/format/string.cc: Test for using __int128
>	as width/precision.
>---
>Revision added missing decltype(auto) return type for lambda in _M_user_visit.
>Tested on x84_64-linux. Tested arguments/args.cc` with -mlong-double-128.
>Tested `std/format` test on ppc64le-linux, with both -mabi=ieeelongdouble and -mabi=ibmlongdouble.
>OK for trunk?

Some more minor comments below, mostly typos in comments, but one
small change to _M_visit_user.

OK for trunk with those changes, thanks.


> libstdc++-v3/include/std/format               | 49 +++++++++++--
> .../testsuite/std/format/arguments/args.cc    | 73 +++++++++++++++++++
> libstdc++-v3/testsuite/std/format/string.cc   | 10 ++-
> 3 files changed, 123 insertions(+), 9 deletions(-)
>
>diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
>index e7e0d2d142b..5085658cdb7 100644
>--- a/libstdc++-v3/include/std/format
>+++ b/libstdc++-v3/include/std/format
>@@ -3290,10 +3290,12 @@ namespace __format
>   template<typename _Context, typename... _Args>
>     class _Arg_store;
>
>+  template<typename _Visitor, typename _Ctx>
>+    decltype(auto) __visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>);
>+
>   template<typename _Ch, typename _Tp>
>     consteval _Arg_t
>     __to_arg_t_enum() noexcept;
>-
> } // namespace __format
> /// @endcond
>
>@@ -3365,12 +3367,12 @@ namespace __format
>       template<typename _Visitor>
> 	decltype(auto)
> 	visit(this basic_format_arg __arg, _Visitor&& __vis)
>-	{ return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); }
>+	{ return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); }
>
>       template<typename _Res, typename _Visitor>
> 	_Res
> 	visit(this basic_format_arg __arg, _Visitor&& __vis)
>-	{ return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); }
>+	{ return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); }
> #endif
>
>     private:
>@@ -3587,6 +3589,10 @@ namespace __format
> 	friend decltype(auto)
> 	visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx>);
>
>+      template<typename _Visitor, typename _Ctx>
>+	friend decltype(auto)
>+	__format::__visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>);
>+
>       template<typename _Ch, typename _Tp>
> 	friend consteval __format::_Arg_t
> 	__format::__to_arg_t_enum() noexcept;
>@@ -3655,6 +3661,28 @@ namespace __format
> 	      __builtin_unreachable();
> 	  }
> 	}
>+
>+      template<typename _Visitor>
>+	decltype(auto)
>+	_M_visit_user(_Visitor&& __vis, __format::_Arg_t __type)
>+	{
>+	  return _M_visit([&__vis]<typename _Tp>(_Tp& __val) -> decltype(auto)
>+	    {
>+	      constexpr bool __user_facing = __is_one_of<_Tp,
>+	        monostate, bool, _CharT,

I think the only way it's possible to get a monostate here is for a
default constructed format arg, and visiting that is probably a
mistake. So let's put monostate at the end of this list, otherwise every
time we evaluate this __is_one_of check the first instantiation is
is_same<_Tp, monostate> which will fail nearly every time.


>+	        int, unsigned int, long long int, unsigned long long int,
>+	        float, double, long double,
>+	        const _CharT*, basic_string_view<_CharT>,
>+	        const void*, handle>::value;
>+	     if constexpr (__user_facing)
>+	       return std::forward<_Visitor>(__vis)(__val);
>+	     else
>+	       {
>+	         handle __h(__val);
>+	         return std::forward<_Visitor>(__vis)(__h);
>+	       }
>+	   }, __type);
>+	}
>     };
>
>   template<typename _Visitor, typename _Context>
>@@ -3662,12 +3690,19 @@ namespace __format
>     inline decltype(auto)
>     visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg)
>     {
>-      return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type);
>+      return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type);
>     }
>
> /// @cond undocumented
> namespace __format
> {
>+  template<typename _Visitor, typename _Ctx>
>+    inline decltype(auto)
>+    __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx> __arg)
>+    {
>+      return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type);
>+    }
>+
>   struct _WidthPrecVisitor
>   {
>     template<typename _Tp>
>@@ -3699,7 +3734,7 @@ namespace __format
>   template<typename _Context>
>     inline size_t
>     __int_from_arg(const basic_format_arg<_Context>& __arg)
>-    { return std::visit_format_arg(_WidthPrecVisitor(), __arg); }
>+    { return __format::__visit_format_arg(_WidthPrecVisitor(), __arg); }
>
>   // Pack _Arg_t enum values into a single 60-bit integer.
>   template<int _Bits, size_t _Nm>
>@@ -4152,7 +4187,7 @@ namespace __format
> 	using _Context = basic_format_context<_Out, _CharT>;
> 	using handle = typename basic_format_arg<_Context>::handle;
>
>-	std::visit_format_arg([this](auto& __arg) {
>+	__format::__visit_format_arg([this](auto& __arg) {
> 	  using _Type = remove_reference_t<decltype(__arg)>;
> 	  using _Formatter = typename _Context::template formatter_type<_Type>;
> 	  if constexpr (is_same_v<_Type, monostate>)
>@@ -4254,7 +4289,7 @@ namespace __format
> 	if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}')
> 	  {
> 	    bool __done = false;
>-	    std::visit_format_arg([&](auto& __arg) {
>+	    __format::__visit_format_arg([&](auto& __arg) {
> 	      using _Tp = remove_cvref_t<decltype(__arg)>;
> 	      if constexpr (is_same_v<_Tp, bool>)
> 		{
>diff --git a/libstdc++-v3/testsuite/std/format/arguments/args.cc b/libstdc++-v3/testsuite/std/format/arguments/args.cc
>index 2cea0a1b63f..8379528a3dd 100644
>--- a/libstdc++-v3/testsuite/std/format/arguments/args.cc
>+++ b/libstdc++-v3/testsuite/std/format/arguments/args.cc
>@@ -148,9 +148,82 @@ test_member_visit()
> #endif
> }
>
>+template<typename T>
>+void test_visited_as_handle()
>+{
>+  T v{};
>+  auto store = std::make_format_args(v);
>+  std::format_args args = store;
>+
>+  constexpr auto is_handle = [](auto arg) {
>+    return std::is_same_v<decltype(arg), decltype(args.get(0))::handle>;
>+  };
>+  VERIFY( std::visit_format_arg(is_handle, args.get(0)) );
>+#if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit
>+  VERIFY( args.get(0).visit(is_handle) );
>+#endif
>+}
>+
>+template<typename E, typename S>
>+void test_visited_as()
>+{
>+  auto v = static_cast<S>(1.0);
>+  auto store = std::make_format_args(v);
>+  std::format_args args = store;
>+
>+  auto is_expected_val = [v](auto arg) {
>+    if constexpr (std::is_same_v<decltype(arg), E>)
>+      return arg == static_cast<E>(v);
>+    return false;
>+  };
>+  VERIFY( std::visit_format_arg(is_expected_val, args.get(0)) );
>+#if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit
>+  VERIFY( args.get(0).visit(is_expected_val) );
>+#endif
>+}
>+
>+template<typename T>
>+concept can_format = std::is_default_constructible_v<std::formatter<T, char>>;
>+
> int main()
> {
>   test_empty();
>   test_args();
>   test_member_visit();
>+
>+#ifdef __SIZEOF_INT128__
>+  test_visited_as_handle<__int128>();
>+  test_visited_as_handle<unsigned __int128>();
>+#endif
>+// TODO: This should be passed visited as handle.
>+#ifdef __STDCPP_FLOAT16_T__
>+  if constexpr (can_format<_Float16>)
>+    test_visited_as<float, _Float16>();
>+#endif
>+#ifdef __STDCPP_BFLOAT16_T__
>+  if constexpr (can_format<__gnu_cxx::__bfloat16_t>)
>+    test_visited_as<float, __gnu_cxx::__bfloat16_t>();
>+#endif
>+#ifdef __FLT32_DIG__
>+  if constexpr (can_format<_Float32>)
>+    test_visited_as<float, _Float32>();
>+#endif
>+#ifdef __FLT64_DIG__
>+  if constexpr (can_format<_Float64>)
>+    test_visited_as<double, _Float64>();
>+#endif
>+#ifdef __FLT128_DIG__
>+  if constexpr (can_format<_Float128>)
>+# ifdef _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128
>+    test_visited_as<long double, _Float128>();
>+# else
>+    test_visited_as_handle<_Float128>();
>+# endif
>+#endif
>+#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
>+  if constexpr (!std::is_same_v<__ieee128, long double>)
>+    test_visited_as_handle<__ieee128>();
>+  if constexpr (!std::is_same_v<__ibm128, long double>)
>+    test_visited_as_handle<__ibm128>();
>+#endif
> }
>diff --git a/libstdc++-v3/testsuite/std/format/string.cc b/libstdc++-v3/testsuite/std/format/string.cc
>index d5f30500139..0933639faf0 100644
>--- a/libstdc++-v3/testsuite/std/format/string.cc
>+++ b/libstdc++-v3/testsuite/std/format/string.cc
>@@ -113,11 +113,14 @@ test_format_spec()
>   VERIFY( ! is_format_string_for("{:0c}", 'c') );
>   VERIFY( ! is_format_string_for("{:0s}", true) );
>
>-  // Dynamic width arg must be an integer type.
>+  // Dynamic width arg must be an standar integer type.

"an standar" -> "a standard"

>   VERIFY( ! is_format_string_for("{:{}d}", 1, 1.5) );
>   VERIFY( ! is_format_string_for("{:{}d}", 1, true) );
>   VERIFY( ! is_format_string_for("{:{}d}", 1, "str") );
>   VERIFY( ! is_format_string_for("{:{}d}", 1, nullptr) );
>+#ifdef __SIZEOF_INT128__
>+  VERIFY( ! is_format_string_for("{:{}d}", 1, static_cast<__int128>(1)) );
>+#endif
>
>   // Precision only valid for string and floating-point types.
>   VERIFY( ! is_format_string_for("{:.3d}", 1) );
>@@ -126,11 +129,14 @@ test_format_spec()
>   VERIFY( ! is_format_string_for("{:3.3s}", 'c') );
>   VERIFY( ! is_format_string_for("{:3.3p}", nullptr) );
>
>-  // Dynamic precision arg must be an integer type.
>+  // Dynamic precision arg must be an standard integer type.

"an" -> "a"

>   VERIFY( ! is_format_string_for("{:.{}f}", 1.0, 1.5) );
>   VERIFY( ! is_format_string_for("{:.{}f}", 1.0, true) );
>   VERIFY( ! is_format_string_for("{:.{}f}", 1.0, "str") );
>   VERIFY( ! is_format_string_for("{:.{}f}", 1.0, nullptr) );
>+#ifdef __SIZEOF_INT128__
>+  VERIFY( ! is_format_string_for("{:{}f}", 1.0, static_cast<unsigned __int128>(1)) );
>+#endif
>
>   // Invalid presentation types for integers.
>   VERIFY( ! is_format_string_for("{:f}", 1) );
>-- 
>2.48.1
>
  

Patch

diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index e7e0d2d142b..5085658cdb7 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -3290,10 +3290,12 @@  namespace __format
   template<typename _Context, typename... _Args>
     class _Arg_store;
 
+  template<typename _Visitor, typename _Ctx>
+    decltype(auto) __visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>);
+
   template<typename _Ch, typename _Tp>
     consteval _Arg_t
     __to_arg_t_enum() noexcept;
-
 } // namespace __format
 /// @endcond
 
@@ -3365,12 +3367,12 @@  namespace __format
       template<typename _Visitor>
 	decltype(auto)
 	visit(this basic_format_arg __arg, _Visitor&& __vis)
-	{ return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); }
+	{ return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); }
 
       template<typename _Res, typename _Visitor>
 	_Res
 	visit(this basic_format_arg __arg, _Visitor&& __vis)
-	{ return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); }
+	{ return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); }
 #endif
 
     private:
@@ -3587,6 +3589,10 @@  namespace __format
 	friend decltype(auto)
 	visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx>);
 
+      template<typename _Visitor, typename _Ctx>
+	friend decltype(auto)
+	__format::__visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>);
+
       template<typename _Ch, typename _Tp>
 	friend consteval __format::_Arg_t
 	__format::__to_arg_t_enum() noexcept;
@@ -3655,6 +3661,28 @@  namespace __format
 	      __builtin_unreachable();
 	  }
 	}
+
+      template<typename _Visitor>
+	decltype(auto)
+	_M_visit_user(_Visitor&& __vis, __format::_Arg_t __type)
+	{
+	  return _M_visit([&__vis]<typename _Tp>(_Tp& __val) -> decltype(auto)
+	    {
+	      constexpr bool __user_facing = __is_one_of<_Tp,
+	        monostate, bool, _CharT,
+	        int, unsigned int, long long int, unsigned long long int,
+	        float, double, long double,
+	        const _CharT*, basic_string_view<_CharT>,
+	        const void*, handle>::value;
+	     if constexpr (__user_facing)
+	       return std::forward<_Visitor>(__vis)(__val);
+	     else
+	       {
+	         handle __h(__val);
+	         return std::forward<_Visitor>(__vis)(__h);
+	       }
+	   }, __type);
+	}
     };
 
   template<typename _Visitor, typename _Context>
@@ -3662,12 +3690,19 @@  namespace __format
     inline decltype(auto)
     visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg)
     {
-      return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type);
+      return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type);
     }
 
 /// @cond undocumented
 namespace __format
 {
+  template<typename _Visitor, typename _Ctx>
+    inline decltype(auto)
+    __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx> __arg)
+    {
+      return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type);
+    }
+
   struct _WidthPrecVisitor
   {
     template<typename _Tp>
@@ -3699,7 +3734,7 @@  namespace __format
   template<typename _Context>
     inline size_t
     __int_from_arg(const basic_format_arg<_Context>& __arg)
-    { return std::visit_format_arg(_WidthPrecVisitor(), __arg); }
+    { return __format::__visit_format_arg(_WidthPrecVisitor(), __arg); }
 
   // Pack _Arg_t enum values into a single 60-bit integer.
   template<int _Bits, size_t _Nm>
@@ -4152,7 +4187,7 @@  namespace __format
 	using _Context = basic_format_context<_Out, _CharT>;
 	using handle = typename basic_format_arg<_Context>::handle;
 
-	std::visit_format_arg([this](auto& __arg) {
+	__format::__visit_format_arg([this](auto& __arg) {
 	  using _Type = remove_reference_t<decltype(__arg)>;
 	  using _Formatter = typename _Context::template formatter_type<_Type>;
 	  if constexpr (is_same_v<_Type, monostate>)
@@ -4254,7 +4289,7 @@  namespace __format
 	if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}')
 	  {
 	    bool __done = false;
-	    std::visit_format_arg([&](auto& __arg) {
+	    __format::__visit_format_arg([&](auto& __arg) {
 	      using _Tp = remove_cvref_t<decltype(__arg)>;
 	      if constexpr (is_same_v<_Tp, bool>)
 		{
diff --git a/libstdc++-v3/testsuite/std/format/arguments/args.cc b/libstdc++-v3/testsuite/std/format/arguments/args.cc
index 2cea0a1b63f..8379528a3dd 100644
--- a/libstdc++-v3/testsuite/std/format/arguments/args.cc
+++ b/libstdc++-v3/testsuite/std/format/arguments/args.cc
@@ -148,9 +148,82 @@  test_member_visit()
 #endif
 }
 
+template<typename T>
+void test_visited_as_handle()
+{
+  T v{};
+  auto store = std::make_format_args(v);
+  std::format_args args = store;
+
+  constexpr auto is_handle = [](auto arg) {
+    return std::is_same_v<decltype(arg), decltype(args.get(0))::handle>;
+  };
+  VERIFY( std::visit_format_arg(is_handle, args.get(0)) );
+#if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit
+  VERIFY( args.get(0).visit(is_handle) );
+#endif
+}
+
+template<typename E, typename S>
+void test_visited_as()
+{
+  auto v = static_cast<S>(1.0);
+  auto store = std::make_format_args(v);
+  std::format_args args = store;
+
+  auto is_expected_val = [v](auto arg) {
+    if constexpr (std::is_same_v<decltype(arg), E>)
+      return arg == static_cast<E>(v);
+    return false;
+  };
+  VERIFY( std::visit_format_arg(is_expected_val, args.get(0)) );
+#if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit
+  VERIFY( args.get(0).visit(is_expected_val) );
+#endif
+}
+
+template<typename T>
+concept can_format = std::is_default_constructible_v<std::formatter<T, char>>;
+
 int main()
 {
   test_empty();
   test_args();
   test_member_visit();
+
+#ifdef __SIZEOF_INT128__
+  test_visited_as_handle<__int128>();
+  test_visited_as_handle<unsigned __int128>();
+#endif
+// TODO: This should be passed visited as handle.
+#ifdef __STDCPP_FLOAT16_T__
+  if constexpr (can_format<_Float16>)
+    test_visited_as<float, _Float16>();
+#endif
+#ifdef __STDCPP_BFLOAT16_T__
+  if constexpr (can_format<__gnu_cxx::__bfloat16_t>)
+    test_visited_as<float, __gnu_cxx::__bfloat16_t>();
+#endif
+#ifdef __FLT32_DIG__
+  if constexpr (can_format<_Float32>)
+    test_visited_as<float, _Float32>();
+#endif
+#ifdef __FLT64_DIG__
+  if constexpr (can_format<_Float64>)
+    test_visited_as<double, _Float64>();
+#endif
+#ifdef __FLT128_DIG__
+  if constexpr (can_format<_Float128>)
+# ifdef _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128
+    test_visited_as<long double, _Float128>();
+# else
+    test_visited_as_handle<_Float128>();
+# endif
+#endif
+#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
+  if constexpr (!std::is_same_v<__ieee128, long double>)
+    test_visited_as_handle<__ieee128>();
+  if constexpr (!std::is_same_v<__ibm128, long double>)
+    test_visited_as_handle<__ibm128>();
+#endif
 }
diff --git a/libstdc++-v3/testsuite/std/format/string.cc b/libstdc++-v3/testsuite/std/format/string.cc
index d5f30500139..0933639faf0 100644
--- a/libstdc++-v3/testsuite/std/format/string.cc
+++ b/libstdc++-v3/testsuite/std/format/string.cc
@@ -113,11 +113,14 @@  test_format_spec()
   VERIFY( ! is_format_string_for("{:0c}", 'c') );
   VERIFY( ! is_format_string_for("{:0s}", true) );
 
-  // Dynamic width arg must be an integer type.
+  // Dynamic width arg must be an standar integer type.
   VERIFY( ! is_format_string_for("{:{}d}", 1, 1.5) );
   VERIFY( ! is_format_string_for("{:{}d}", 1, true) );
   VERIFY( ! is_format_string_for("{:{}d}", 1, "str") );
   VERIFY( ! is_format_string_for("{:{}d}", 1, nullptr) );
+#ifdef __SIZEOF_INT128__
+  VERIFY( ! is_format_string_for("{:{}d}", 1, static_cast<__int128>(1)) );
+#endif
 
   // Precision only valid for string and floating-point types.
   VERIFY( ! is_format_string_for("{:.3d}", 1) );
@@ -126,11 +129,14 @@  test_format_spec()
   VERIFY( ! is_format_string_for("{:3.3s}", 'c') );
   VERIFY( ! is_format_string_for("{:3.3p}", nullptr) );
 
-  // Dynamic precision arg must be an integer type.
+  // Dynamic precision arg must be an standard integer type.
   VERIFY( ! is_format_string_for("{:.{}f}", 1.0, 1.5) );
   VERIFY( ! is_format_string_for("{:.{}f}", 1.0, true) );
   VERIFY( ! is_format_string_for("{:.{}f}", 1.0, "str") );
   VERIFY( ! is_format_string_for("{:.{}f}", 1.0, nullptr) );
+#ifdef __SIZEOF_INT128__
+  VERIFY( ! is_format_string_for("{:{}f}", 1.0, static_cast<unsigned __int128>(1)) );
+#endif
 
   // Invalid presentation types for integers.
   VERIFY( ! is_format_string_for("{:f}", 1) );