On Tue, 7 Jul 2026 at 12:43, Tomasz Kamiński <[email protected]> wrote:
>
> Implementing features from later revision of the standard, may require
> extending the set of types stored directly in _Arg_value (without
> changing in size and aliment), and thus expanding the values in _Arg_t.
> However, any such value would be unrecognized by the TUs compiled with
> older release, and would lead to UB (call to __builtin_unreachable).
>
> This patch addresses above by introducing _M_handle_unrecognized method,
> that is called instead. As specializations of this method for all
> context supported at runtime (format_context and wformat_context) are
> exported from libstdc++.so, the newest version (supporting all _Arg_t
> values introduced later) will be picked (this is ensured by not providing
> template definition in header in such case).
> In consequence, the implementation may return handle object referencing
> new alternatives (such wrapping is already required to provide standard
> compliant behavior of visit_format_arg).
> As no new _Arg_t were introduced since GCC16, this method simply throws
> format_error now, and contains appropriate comment.
>
> Note that the above is not required for formatting when __do_vformat_to
> is exported (Unicode literal encoding and C++20). It remains necessary
> in remaining cases or when visit on basic_format_arg is called by user.
> In consequence addition of new argument types stored directly does not
> have negative impact on performance in most common case.
>
> This patch also removes the __type parameter from the _M_visit and
> _M_visit_user, and uses _M_type member instead. This prevents misuse,
> as the provided type was always required to match _M_type.
>
> libstdc++-v3/ChangeLog:
>
>         * config/abi/pre/gnu.ver (GLIBCXX_3.4.36): Export basic_format_arg
>         _M_handle_unrecognized method of specializations of
>         format_context and wformat_context.
>         * include/std/format (basic_format_arg::_M_handle_unrecognized):
>         Declare with explicit specializations for (w)format_context
>         if _GLIBCXX_EXTERN_TEMPLATE, and define otherwise.
>         (basic_format_arg::_M_visit): Remove __type parameter, and use
>         _M_type instead. Call _M_handle_unrecognized for unrecognized
>         _Arg_t values.
>         (basic_format_arg::_M_visit_user): Remove __type parameter,
>         and use _M_type instead. Adjust calls to _M_visit.
>         (basic_format_arg::visit, std::visit_format_arg)
>         (__format::__visit_format_arg): Remove _M_type argument from
>         _M_visit(_user) calls.
>         * src/c++20/format-inst.cc
>         (basic_format_arg::_M_handle_unrecognized): Define and export
>         explicit specializations for (w)format_context.
>
> Reviewed-by: Jonathan Wakely <[email protected]>
> Signed-off-by: Tomasz Kamiński <[email protected]>
> ---
> v2 removes the defintion of _M_handle_unrecognized template
> from the header, and moves it to source file if _GLIBCXX_EXTERN_TEMPLATE,
> is defined. Add revelant comment to the source file and commit description.
> Also addresses the other review comments.
>
> Tested on x86_64-linux locally. OK for trunk?

OK thanks


>
>  libstdc++-v3/config/abi/pre/gnu.ver   |  4 +++
>  libstdc++-v3/include/std/format       | 52 +++++++++++++++++++--------
>  libstdc++-v3/src/c++20/format-inst.cc | 19 ++++++++++
>  3 files changed, 61 insertions(+), 14 deletions(-)
>
> diff --git a/libstdc++-v3/config/abi/pre/gnu.ver 
> b/libstdc++-v3/config/abi/pre/gnu.ver
> index 3a6afac8308..9e1bebb4f50 100644
> --- a/libstdc++-v3/config/abi/pre/gnu.ver
> +++ b/libstdc++-v3/config/abi/pre/gnu.ver
> @@ -2628,6 +2628,10 @@ GLIBCXX_3.4.36 {
>
>      
> _ZNSt6chrono8__detail25__recent_leap_second_infoERNS_16leap_second_infoEj;
>
> +    # basic_format_arg<format_context>::handle 
> basic_format_arg<format_context>::_M_handle_unrecognized() const;
> +    # basic_format_arg<wformat_context>::handle 
> basic_format_arg<wformat_context>::_M_handle_unrecognized() const;
> +    
> _ZNKSt16basic_format_argISt20basic_format_contextINSt8__format10_Sink_iterI[cw]EE[cw]EE22_M_handle_unrecognizedEv;
> +
>      # basic_string::allocate_at_least
>      
> _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE*_S_allocate_*;
>      
> _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE*_M_create_*;
> diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
> index dd275b6b817..516af6f14c8 100644
> --- a/libstdc++-v3/include/std/format
> +++ b/libstdc++-v3/include/std/format
> @@ -4404,12 +4404,12 @@ namespace __format
>        template<typename _Visitor>
>         _GLIBCXX_CONSTEXPR_FORMAT decltype(auto)
>         visit(this basic_format_arg __arg, _Visitor&& __vis)
> -       { return __arg._M_visit_user(std::forward<_Visitor>(__vis), 
> __arg._M_type); }
> +       { return __arg._M_visit_user(std::forward<_Visitor>(__vis)); }
>
>        template<typename _Res, typename _Visitor>
>         _GLIBCXX_CONSTEXPR_FORMAT _Res
>         visit(this basic_format_arg __arg, _Visitor&& __vis)
> -       { return __arg._M_visit_user(std::forward<_Visitor>(__vis), 
> __arg._M_type); }
> +       { return __arg._M_visit_user(std::forward<_Visitor>(__vis)); }
>  #endif
>
>      private:
> @@ -4622,13 +4622,17 @@ namespace __format
>         friend consteval __format::_Arg_t
>         __format::__to_arg_t_enum() noexcept;
>
> +      [[__gnu__::__noinline__]]
> +      handle
> +      _M_handle_unrecognized() const;
> +
>        template<typename _Visitor>
>         _GLIBCXX_CONSTEXPR_FORMAT decltype(auto)
> -       _M_visit(_Visitor&& __vis, __format::_Arg_t __type)
> +       _M_visit(_Visitor&& __vis)
>         {
> -         using namespace __format;
> -         switch (__type)
> +         switch (_M_type)
>           {
> +           using enum __format::_Arg_t;
>             case _Arg_none:
>               return std::forward<_Visitor>(__vis)(_M_val._M_none);
>             case _Arg_bool:
> @@ -4693,13 +4697,17 @@ namespace __format
>               return std::forward<_Visitor>(__vis)(_M_val._M_u128);
>  #endif
>             default:
> -             __builtin_unreachable();
> +             // Call exported definition of _M_handle_unrecognized from
> +             // libstdc++.so, that should recognize new _Arg_t values and
> +             // return basic_format_arg, containing a handle to that value.
> +             handle __h = _M_handle_unrecognized();
> +             return std::forward<_Visitor>(__vis)(__h);
>           }
>         }
>
>        template<typename _Visitor>
>         _GLIBCXX_CONSTEXPR_FORMAT decltype(auto)
> -       _M_visit_user(_Visitor&& __vis, __format::_Arg_t __type)
> +       _M_visit_user(_Visitor&& __vis)
>         {
>           return _M_visit([&__vis]<typename _Tp>(_Tp& __val) -> decltype(auto)
>             {
> @@ -4716,7 +4724,7 @@ namespace __format
>                  handle __h(__val);
>                  return std::forward<_Visitor>(__vis)(__h);
>                }
> -          }, __type);
> +         });
>         }
>      };
>
> @@ -4724,9 +4732,7 @@ namespace __format
>      _GLIBCXX26_DEPRECATED_SUGGEST("std::basic_format_arg::visit")
>      inline _GLIBCXX_CONSTEXPR_FORMAT decltype(auto)
>      visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg)
> -    {
> -      return __arg._M_visit_user(std::forward<_Visitor>(__vis), 
> __arg._M_type);
> -    }
> +    { return __arg._M_visit_user(std::forward<_Visitor>(__vis)); }
>
>  /// @cond undocumented
>  namespace __format
> @@ -4734,9 +4740,7 @@ namespace __format
>    template<typename _Visitor, typename _Ctx>
>      inline _GLIBCXX_CONSTEXPR_FORMAT decltype(auto)
>      __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx> __arg)
> -    {
> -      return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type);
> -    }
> +    { return __arg._M_visit(std::forward<_Visitor>(__vis)); }
>
>    struct _WidthPrecVisitor
>    {
> @@ -5069,6 +5073,26 @@ namespace __format
>        advance_to(iterator __it) { _M_out = std::move(__it); }
>      };
>
> +#if _GLIBCXX_EXTERN_TEMPLATE
> +   // The defintion _M_handle_unrecognized is placed in format-inst.cc
> +   // source file, to ensure that it will not be inlined by compiler.
> +   extern template basic_format_arg<format_context>::handle
> +     basic_format_arg<format_context>::_M_handle_unrecognized() const;
> +# ifdef _GLIBCXX_USE_WCHAR_T
> +   extern template basic_format_arg<wformat_context>::handle
> +     basic_format_arg<wformat_context>::_M_handle_unrecognized() const;
> +# endif
> +#else
> +  template<typename _Context>
> +    typename basic_format_arg<_Context>::handle
> +    basic_format_arg<_Context>::_M_handle_unrecognized() const
> +    {
> +      // If _M_type corresponds to a new value of _Arg_t introduced after
> +      // GCC 16, this function should return a handle that refers to the
> +      // union member of _M_val corresponding to that _Arg_t value.
> +      __throw_format_error("format error: unrecognized argument type");
> +    }
> +#endif
>
>  /// @cond undocumented
>  namespace __format
> diff --git a/libstdc++-v3/src/c++20/format-inst.cc 
> b/libstdc++-v3/src/c++20/format-inst.cc
> index 65a3f282732..fe15992485e 100644
> --- a/libstdc++-v3/src/c++20/format-inst.cc
> +++ b/libstdc++-v3/src/c++20/format-inst.cc
> @@ -27,6 +27,25 @@
>  namespace std
>  {
>  _GLIBCXX_BEGIN_NAMESPACE_VERSION
> +
> +  template<typename _Context>
> +    typename basic_format_arg<_Context>::handle
> +    basic_format_arg<_Context>::_M_handle_unrecognized() const
> +    {
> +      // If _M_type corresponds to a new value of _Arg_t introduced after 
> GCC 16,
> +      // this function should return a handle that refers to the union 
> member of
> +      // _M_val corresponding to that _Arg_t value.
> +      __throw_format_error("format error: unrecognized argument type");
> +    }
> +
> + template basic_format_arg<format_context>::handle
> +   basic_format_arg<format_context>::_M_handle_unrecognized() const;
> +
> +# ifdef _GLIBCXX_USE_WCHAR_T
> + template basic_format_arg<wformat_context>::handle
> +   basic_format_arg<wformat_context>::_M_handle_unrecognized() const;
> +# endif
> +
>  namespace __format
>  {
>
> --
> 2.54.0
>

Reply via email to