On Wed, 8 Oct 2025 at 08:50, Tomasz Kamiński <[email protected]> wrote:
>
> This patch addresses several issues related to the additional specializations
> for enable_nonlocking_formatter_optimization fomr P3235R3 proposal:
>
> * LWG4399 [1]: Apply remove_cvref_t to tuple and pair elements when checking
> if
> the direct printing optimization is enabled.
> * LWG4398 [2]: Disable the direct printing optimization for the standard
> library
> container adaptors: queue, priority_queue, and stack.
> * LWG4400 [3]: Enable the direct printing optimization only for durations that
> use standard arithmetic types. Conditionally enable it for hh_mm_ss
> and time_points based on their underlying Duration template argument.
>
> [1] https://cplusplus.github.io/LWG/issue4399
> [2] https://cplusplus.github.io/LWG/issue4398
> [3] https://cplusplus.github.io/LWG/issue4400
>
> PR libstdc++/121790
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/chrono_io.h (enable_nonlocking_formatter_optimization):
> Adjust specializations for duration, hh_mm_ss and time_points.
> * include/std/format (enable_nonlocking_formatter_optimization):
> Apply remove_cvref_t on pair and tuple elements.
> * include/std/queue (enable_nonlocking_formatter_optimization):
> Change specialization value to false.
> * include/std/stack (enable_nonlocking_formatter_optimization):
> Change specialization value to false.
> * testsuite/std/format/ranges/adaptors.cc: Adjusted tests.
> * testsuite/std/format/tuple.cc: Adjusted tests.
> * testsuite/std/time/format/nonlocking.cc: Adjusted tests.
> ---
> v2 fills the placeholders with issues correct issue numbers.
OK, I don't think we need to wait for LWG to approve these. If they
want different fixes, we can revert or adjust our code.
All three patches in the series are OK for trunk, thanks.
>
> libstdc++-v3/include/bits/chrono_io.h | 98 ++++++++++++-------
> libstdc++-v3/include/std/format | 12 ++-
> libstdc++-v3/include/std/queue | 10 +-
> libstdc++-v3/include/std/stack | 5 +-
> .../testsuite/std/format/ranges/adaptors.cc | 9 +-
> libstdc++-v3/testsuite/std/format/tuple.cc | 5 +-
> .../testsuite/std/time/format/nonlocking.cc | 40 ++++----
> 7 files changed, 102 insertions(+), 77 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/chrono_io.h
> b/libstdc++-v3/include/bits/chrono_io.h
> index 1540f53c21a..480a97dbcb1 100644
> --- a/libstdc++-v3/include/bits/chrono_io.h
> +++ b/libstdc++-v3/include/bits/chrono_io.h
> @@ -559,7 +559,7 @@ namespace __format
> __formatter_chrono(_ChronoSpec<_CharT> __spec) noexcept
> : _M_spec(__spec)
> { }
> -
> +
> constexpr typename basic_format_parse_context<_CharT>::iterator
> _M_parse(basic_format_parse_context<_CharT>& __pc, _ChronoParts
> __parts,
> const _ChronoSpec<_CharT>& __def)
> @@ -2233,10 +2233,12 @@ namespace __format
> };
>
> #if __glibcxx_print >= 202406L
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4400. enable_nonlocking_formatter_optimization for durations with
> custom rep
> template<typename _Rep, typename _Period>
> constexpr bool
> enable_nonlocking_formatter_optimization<chrono::duration<_Rep, _Period>>
> - = enable_nonlocking_formatter_optimization<_Rep>;
> + = is_arithmetic_v<_Rep>;
> #endif
>
> template<__format::__char _CharT>
> @@ -2985,10 +2987,12 @@ namespace __format
> };
>
> #if __glibcxx_print >= 202406L
> - template<typename _Duration>
> - constexpr bool
> - enable_nonlocking_formatter_optimization<chrono::hh_mm_ss<_Duration>>
> - = true;
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4400. enable_nonlocking_formatter_optimization for durations with
> custom rep
> + template<typename _Duration>
> + constexpr bool
> + enable_nonlocking_formatter_optimization<chrono::hh_mm_ss<_Duration>>
> + = enable_nonlocking_formatter_optimization<_Duration>;
> #endif
>
> #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
> @@ -3076,10 +3080,12 @@ namespace __format
> };
>
> #if __glibcxx_print >= 202406L
> - template<typename _Duration>
> - constexpr bool
> - enable_nonlocking_formatter_optimization<chrono::sys_time<_Duration>>
> - = true;
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4400. enable_nonlocking_formatter_optimization for durations with
> custom rep
> + template<typename _Duration>
> + constexpr bool
> + enable_nonlocking_formatter_optimization<chrono::sys_time<_Duration>>
> + = enable_nonlocking_formatter_optimization<_Duration>;
> #endif
>
> template<typename _Duration, __format::__char _CharT>
> @@ -3127,10 +3133,12 @@ namespace __format
> };
>
> #if __glibcxx_print >= 202406L
> - template<typename _Duration>
> - constexpr bool
> - enable_nonlocking_formatter_optimization<chrono::utc_time<_Duration>>
> - = true;
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4400. enable_nonlocking_formatter_optimization for durations with
> custom rep
> + template<typename _Duration>
> + constexpr bool
> + enable_nonlocking_formatter_optimization<chrono::utc_time<_Duration>>
> + = enable_nonlocking_formatter_optimization<_Duration>;
> #endif
>
> template<typename _Duration, __format::__char _CharT>
> @@ -3169,10 +3177,12 @@ namespace __format
> };
>
> #if __glibcxx_print >= 202406L
> - template<typename _Duration>
> - constexpr bool
> - enable_nonlocking_formatter_optimization<chrono::tai_time<_Duration>>
> - = true;
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4400. enable_nonlocking_formatter_optimization for durations with
> custom rep
> + template<typename _Duration>
> + constexpr bool
> + enable_nonlocking_formatter_optimization<chrono::tai_time<_Duration>>
> + = enable_nonlocking_formatter_optimization<_Duration>;
> #endif
>
> template<typename _Duration, __format::__char _CharT>
> @@ -3211,10 +3221,12 @@ namespace __format
> };
>
> #if __glibcxx_print >= 202406L
> - template<typename _Duration>
> - constexpr bool
> - enable_nonlocking_formatter_optimization<chrono::gps_time<_Duration>>
> - = true;
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4400. enable_nonlocking_formatter_optimization for durations with
> custom rep
> + template<typename _Duration>
> + constexpr bool
> + enable_nonlocking_formatter_optimization<chrono::gps_time<_Duration>>
> + = enable_nonlocking_formatter_optimization<_Duration>;
> #endif
>
> template<typename _Duration, __format::__char _CharT>
> @@ -3253,10 +3265,12 @@ namespace __format
> };
>
> #if __glibcxx_print >= 202406L
> - template<typename _Duration>
> - constexpr bool
> - enable_nonlocking_formatter_optimization<chrono::file_time<_Duration>>
> - = true;
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4400. enable_nonlocking_formatter_optimization for durations with
> custom rep
> + template<typename _Duration>
> + constexpr bool
> + enable_nonlocking_formatter_optimization<chrono::file_time<_Duration>>
> + = enable_nonlocking_formatter_optimization<_Duration>;
> #endif
>
> template<typename _Duration, __format::__char _CharT>
> @@ -3294,10 +3308,12 @@ namespace __format
> };
>
> #if __glibcxx_print >= 202406L
> - template<typename _Duration>
> - constexpr bool
> - enable_nonlocking_formatter_optimization<chrono::local_time<_Duration>>
> - = true;
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4400. enable_nonlocking_formatter_optimization for durations with
> custom rep
> + template<typename _Duration>
> + constexpr bool
> + enable_nonlocking_formatter_optimization<chrono::local_time<_Duration>>
> + = enable_nonlocking_formatter_optimization<_Duration>;
> #endif
>
> template<typename _Duration, __format::__char _CharT>
> @@ -3361,10 +3377,13 @@ namespace __format
> };
>
> #if __glibcxx_print >= 202406L
> - template<typename _Duration>
> - constexpr bool
> - enable_nonlocking_formatter_optimization<
> - chrono::__detail::__local_time_fmt<_Duration>> = true;
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4400. enable_nonlocking_formatter_optimization for durations with
> custom rep
> + template<typename _Duration>
> + constexpr bool
> + enable_nonlocking_formatter_optimization<
> + chrono::__detail::__local_time_fmt<_Duration>>
> + = enable_nonlocking_formatter_optimization<_Duration>;
> #endif
>
> #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
> @@ -3388,10 +3407,13 @@ namespace __format
> };
>
> #if __glibcxx_print >= 202406L
> - template<typename _Duration>
> - constexpr bool
> - enable_nonlocking_formatter_optimization<
> - chrono::zoned_time<_Duration, const chrono::time_zone*>> = true;
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4400. enable_nonlocking_formatter_optimization for durations with
> custom rep
> + template<typename _Duration>
> + constexpr bool
> + enable_nonlocking_formatter_optimization<
> + chrono::zoned_time<_Duration, const chrono::time_zone*>>
> + = enable_nonlocking_formatter_optimization<_Duration>;
> #endif
> #endif
>
> diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
> index ad29f9336e8..281c038559e 100644
> --- a/libstdc++-v3/include/std/format
> +++ b/libstdc++-v3/include/std/format
> @@ -5873,11 +5873,12 @@ namespace __format
> };
>
> #if __glibcxx_print >= 202406L
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4399. enable_nonlocking_formatter_optimization for pair and tuple needs
> remove_cvref_t
> template<typename _Fp, typename _Sp>
> constexpr bool enable_nonlocking_formatter_optimization<pair<_Fp, _Sp>>
> - // TODO this should have remove_cvref_t.
> - = enable_nonlocking_formatter_optimization<_Fp>
> - && enable_nonlocking_formatter_optimization<_Sp>;
> + = enable_nonlocking_formatter_optimization<remove_cvref_t<_Fp>>
> + && enable_nonlocking_formatter_optimization<remove_cvref_t<_Sp>>;
> #endif
>
> template<__format::__char _CharT, formattable<_CharT>... _Tps>
> @@ -5899,10 +5900,11 @@ namespace __format
> };
>
> #if __glibcxx_print >= 202406L
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4399. enable_nonlocking_formatter_optimization for pair and tuple needs
> remove_cvref_t
> template<typename... _Tps>
> - // TODO this should have remove_cvref_t.
> constexpr bool enable_nonlocking_formatter_optimization<tuple<_Tps...>>
> - = (enable_nonlocking_formatter_optimization<_Tps> && ...);
> + = (enable_nonlocking_formatter_optimization<remove_cvref_t<_Tps>> &&
> ...);
> #endif
>
> // [format.range.formatter], class template range_formatter
> diff --git a/libstdc++-v3/include/std/queue b/libstdc++-v3/include/std/queue
> index ade09f42ea8..bf2b344c81c 100644
> --- a/libstdc++-v3/include/std/queue
> +++ b/libstdc++-v3/include/std/queue
> @@ -113,10 +113,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> };
>
> #if __glibcxx_print >= 202406L
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4398. enable_nonlocking_formatter_optimization should be disabled for
> container adaptors
> template<typename _Tp, typename _Container>
> constexpr bool
> - // TODO should be false
> - enable_nonlocking_formatter_optimization<queue<_Tp, _Container>> = true;
> + enable_nonlocking_formatter_optimization<queue<_Tp, _Container>> = false;
> #endif
>
> template<__format::__char _CharT, typename _Tp,
> @@ -154,11 +155,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> };
>
> #if __glibcxx_print >= 202406L
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4398. enable_nonlocking_formatter_optimization should be disabled for
> container adaptors
> template<typename _Tp, typename _Container, typename _Comparator>
> constexpr bool
> - // TODO should be false
> enable_nonlocking_formatter_optimization<
> - priority_queue<_Tp, _Container, _Comparator>> = true;
> + priority_queue<_Tp, _Container, _Comparator>> = false;
> #endif
>
> _GLIBCXX_END_NAMESPACE_VERSION
> diff --git a/libstdc++-v3/include/std/stack b/libstdc++-v3/include/std/stack
> index 88bc0d2cb6b..507b9746c8a 100644
> --- a/libstdc++-v3/include/std/stack
> +++ b/libstdc++-v3/include/std/stack
> @@ -107,10 +107,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> };
>
> #if __glibcxx_print >= 202406L
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 4398. enable_nonlocking_formatter_optimization should be disabled for
> container adaptors
> template<typename _Tp, typename _Container>
> constexpr bool
> - // TODO should be false
> - enable_nonlocking_formatter_optimization<stack<_Tp, _Container>> = true;
> + enable_nonlocking_formatter_optimization<stack<_Tp, _Container>> = false;
> #endif
> _GLIBCXX_END_NAMESPACE_VERSION
> } // namespace std
> diff --git a/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc
> b/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc
> index d9fc01ab893..649eea47f16 100644
> --- a/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc
> +++ b/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc
> @@ -122,14 +122,13 @@ test_output()
> // Formatter check if container is formattable, not container elements.
> static_assert(!std::formattable<Adaptor<int, NotFormattableCont<int>>,
> CharT>);
>
> - // TODO should be false
> - static_assert(std::enable_nonlocking_formatter_optimization<
> + static_assert(!std::enable_nonlocking_formatter_optimization<
> Adaptor<int>>);
> - static_assert(std::enable_nonlocking_formatter_optimization<
> + static_assert(!std::enable_nonlocking_formatter_optimization<
> Adaptor<MutFormat>>);
> - static_assert(std::enable_nonlocking_formatter_optimization<
> + static_assert(!std::enable_nonlocking_formatter_optimization<
> Adaptor<int, std::deque<int>>>);
> - static_assert(std::enable_nonlocking_formatter_optimization<
> + static_assert(!std::enable_nonlocking_formatter_optimization<
> Adaptor<int, NotFormattableCont<int>>>);
> }
>
> diff --git a/libstdc++-v3/testsuite/std/format/tuple.cc
> b/libstdc++-v3/testsuite/std/format/tuple.cc
> index 001235ba643..eace82730f0 100644
> --- a/libstdc++-v3/testsuite/std/format/tuple.cc
> +++ b/libstdc++-v3/testsuite/std/format/tuple.cc
> @@ -361,10 +361,9 @@ void test_nonblocking()
> {
> static_assert(std::enable_nonlocking_formatter_optimization<
> Tuple<int, float>>);
> - // TODO missing remove_cv_ref
> - static_assert(!std::enable_nonlocking_formatter_optimization<
> + static_assert(std::enable_nonlocking_formatter_optimization<
> Tuple<const int, const float>>);
> - static_assert(!std::enable_nonlocking_formatter_optimization<
> + static_assert(std::enable_nonlocking_formatter_optimization<
> Tuple<int&, float&>>);
>
> static_assert(!std::enable_nonlocking_formatter_optimization<
> diff --git a/libstdc++-v3/testsuite/std/time/format/nonlocking.cc
> b/libstdc++-v3/testsuite/std/time/format/nonlocking.cc
> index c7aac75cb83..f1b57b5a348 100644
> --- a/libstdc++-v3/testsuite/std/time/format/nonlocking.cc
> +++ b/libstdc++-v3/testsuite/std/time/format/nonlocking.cc
> @@ -43,9 +43,9 @@ static_assert(std::enable_nonlocking_formatter_optimization<
> #endif
>
> template<typename Duration>
> -using local_time_fmt
> +using local_time_fmt
> =
> decltype(std::chrono::local_time_format(std::chrono::local_time<Duration>{}));
> -
> +
> static_assert(std::enable_nonlocking_formatter_optimization<
> std::chrono::seconds>);
> static_assert(std::enable_nonlocking_formatter_optimization<
> @@ -71,19 +71,19 @@ using BufferedDuration = std::chrono::duration<Rep<void,
> int>>;
>
> static_assert(!std::enable_nonlocking_formatter_optimization<
> BufferedDuration>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::local_time<BufferedDuration>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::sys_time<BufferedDuration>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::utc_time<BufferedDuration>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::gps_time<BufferedDuration>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::tai_time<BufferedDuration>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::file_time<BufferedDuration>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> local_time_fmt<BufferedDuration>>);
>
> template<>
> @@ -92,21 +92,21 @@ inline constexpr bool
>
> using NonBufferedRep = std::chrono::duration<Rep<void, long>>;
>
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> NonBufferedRep>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::local_time<NonBufferedRep>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::sys_time<NonBufferedRep>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::utc_time<NonBufferedRep>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::gps_time<NonBufferedRep>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::tai_time<NonBufferedRep>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::file_time<NonBufferedRep>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> local_time_fmt<NonBufferedRep>>);
>
> using NonBufferedDuration = std::chrono::duration<Rep<void, short>>;
> @@ -135,9 +135,9 @@
> static_assert(std::enable_nonlocking_formatter_optimization<
> #if _GLIBCXX_USE_CXX11_ABI || !_GLIBCXX_USE_DUAL_ABI
> static_assert(std::enable_nonlocking_formatter_optimization<
> std::chrono::zoned_time<std::chrono::seconds>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::zoned_time<BufferedDuration>>);
> -static_assert(std::enable_nonlocking_formatter_optimization<
> +static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::zoned_time<NonBufferedRep>>);
> static_assert(std::enable_nonlocking_formatter_optimization<
> std::chrono::zoned_time<NonBufferedDuration>>);
> @@ -150,7 +150,7 @@ struct std::chrono::zoned_traits<MyTimeZone>
> {
> static const MyTimeZone* default_zone();
> static const MyTimeZone* locate_zone(std::string_view name);
> -};
> +};
>
> static_assert(!std::enable_nonlocking_formatter_optimization<
> std::chrono::zoned_time<std::chrono::seconds, MyTimeZone>>);
> --
> 2.51.0
>