summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark de Wever <koraq@xs4all.nl>2024-03-25 20:26:33 +0100
committerMark de Wever <koraq@xs4all.nl>2024-03-30 19:52:56 +0100
commitac458a68865883b6f96bb68201ee6e627a8c0a54 (patch)
treeb6e0cb0fb3a380b5cf35f3631204eb8bd44a8033
parent6aa53888a8e8a6e3f0bd279539703f4d4701b4e7 (diff)
[libc++][format] Implements P3107R5 in <format>.upstream/users/mordante/P3107R5_improves_print__format
This adds the new std::enable_nonlocking_formatter_optimization trait in <format>. This trait will be used in std::print to implement the performance benefits. Implements parts of - P3107R5 - Permit an efficient implementation of ``std::print``
-rw-r--r--libcxx/include/__format/formatter.h3
-rw-r--r--libcxx/include/__format/formatter_bool.h6
-rw-r--r--libcxx/include/__format/formatter_char.h10
-rw-r--r--libcxx/include/__format/formatter_floating_point.h8
-rw-r--r--libcxx/include/__format/formatter_integer.h33
-rw-r--r--libcxx/include/__format/formatter_pointer.h8
-rw-r--r--libcxx/include/__format/formatter_string.h27
-rw-r--r--libcxx/include/format15
-rw-r--r--libcxx/modules/std/format.inc2
-rw-r--r--libcxx/test/std/utilities/format/format.formatter/format.formatter.locking/enable_nonlocking_formatter_optimization.compile.pass.cpp235
10 files changed, 337 insertions, 10 deletions
diff --git a/libcxx/include/__format/formatter.h b/libcxx/include/__format/formatter.h
index 47e35789b817..0316354ba74a 100644
--- a/libcxx/include/__format/formatter.h
+++ b/libcxx/include/__format/formatter.h
@@ -41,6 +41,9 @@ struct _LIBCPP_TEMPLATE_VIS formatter {
# if _LIBCPP_STD_VER >= 23
template <class _Tp>
+constexpr bool enable_nonlocking_formatter_optimization = false;
+
+template <class _Tp>
_LIBCPP_HIDE_FROM_ABI constexpr void __set_debug_format(_Tp& __formatter) {
if constexpr (requires { __formatter.set_debug_format(); })
__formatter.set_debug_format();
diff --git a/libcxx/include/__format/formatter_bool.h b/libcxx/include/__format/formatter_bool.h
index 5e3daff7b3db..9d9fcd2469ce 100644
--- a/libcxx/include/__format/formatter_bool.h
+++ b/libcxx/include/__format/formatter_bool.h
@@ -70,7 +70,11 @@ public:
__format_spec::__parser<_CharT> __parser_;
};
-#endif //_LIBCPP_STD_VER >= 20
+# if _LIBCPP_STD_VER >= 23
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<bool> = true;
+# endif //_LIBCPP_STD_VER >= 23
+#endif //_LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__format/formatter_char.h b/libcxx/include/__format/formatter_char.h
index 3358d422252f..aa9c56538efd 100644
--- a/libcxx/include/__format/formatter_char.h
+++ b/libcxx/include/__format/formatter_char.h
@@ -84,9 +84,17 @@ struct _LIBCPP_TEMPLATE_VIS formatter<char, wchar_t> : public __formatter_char<w
template <>
struct _LIBCPP_TEMPLATE_VIS formatter<wchar_t, wchar_t> : public __formatter_char<wchar_t> {};
-
# endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
+# if _LIBCPP_STD_VER >= 23
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<char> = true;
+# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<wchar_t> = true;
+# endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
+# endif //_LIBCPP_STD_VER >= 23
+
#endif //_LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__format/formatter_floating_point.h b/libcxx/include/__format/formatter_floating_point.h
index 1d94cc349c0d..11aafab0db1f 100644
--- a/libcxx/include/__format/formatter_floating_point.h
+++ b/libcxx/include/__format/formatter_floating_point.h
@@ -774,6 +774,14 @@ struct _LIBCPP_TEMPLATE_VIS formatter<double, _CharT> : public __formatter_float
template <__fmt_char_type _CharT>
struct _LIBCPP_TEMPLATE_VIS formatter<long double, _CharT> : public __formatter_floating_point<_CharT> {};
+# if _LIBCPP_STD_VER >= 23
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<float> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<double> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<long double> = true;
+# endif //_LIBCPP_STD_VER >= 23
#endif //_LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__format/formatter_integer.h b/libcxx/include/__format/formatter_integer.h
index d57082b3881b..d63617233d76 100644
--- a/libcxx/include/__format/formatter_integer.h
+++ b/libcxx/include/__format/formatter_integer.h
@@ -89,7 +89,38 @@ template <__fmt_char_type _CharT>
struct _LIBCPP_TEMPLATE_VIS formatter<__uint128_t, _CharT> : public __formatter_integer<_CharT> {};
# endif
-#endif //_LIBCPP_STD_VER >= 20
+# if _LIBCPP_STD_VER >= 23
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<signed char> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<short> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<int> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<long> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<long long> = true;
+# ifndef _LIBCPP_HAS_NO_INT128
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<__int128_t> = true;
+# endif
+
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<unsigned char> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<unsigned short> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<unsigned> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<unsigned long> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<unsigned long long> = true;
+# ifndef _LIBCPP_HAS_NO_INT128
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<__uint128_t> = true;
+# endif
+# endif //_LIBCPP_STD_VER >= 23
+#endif //_LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__format/formatter_pointer.h b/libcxx/include/__format/formatter_pointer.h
index 3373996ec3d5..26788b944db1 100644
--- a/libcxx/include/__format/formatter_pointer.h
+++ b/libcxx/include/__format/formatter_pointer.h
@@ -66,6 +66,14 @@ struct _LIBCPP_TEMPLATE_VIS formatter<void*, _CharT> : public __formatter_pointe
template <__fmt_char_type _CharT>
struct _LIBCPP_TEMPLATE_VIS formatter<const void*, _CharT> : public __formatter_pointer<_CharT> {};
+# if _LIBCPP_STD_VER >= 23
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<nullptr_t> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<void*> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<const void*> = true;
+# endif //_LIBCPP_STD_VER >= 23
#endif //_LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__format/formatter_string.h b/libcxx/include/__format/formatter_string.h
index d1ccfb9b5f7d..7da5d63d4487 100644
--- a/libcxx/include/__format/formatter_string.h
+++ b/libcxx/include/__format/formatter_string.h
@@ -144,7 +144,32 @@ struct _LIBCPP_TEMPLATE_VIS formatter<basic_string_view<_CharT, _Traits>, _CharT
}
};
-#endif //_LIBCPP_STD_VER >= 20
+# if _LIBCPP_STD_VER >= 23
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<char*> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<const char*> = true;
+template <size_t _Size>
+inline constexpr bool enable_nonlocking_formatter_optimization<char[_Size]> = true;
+template <class _Traits, class _Allocator>
+inline constexpr bool enable_nonlocking_formatter_optimization<basic_string<char, _Traits, _Allocator>> = true;
+template <class _Traits>
+inline constexpr bool enable_nonlocking_formatter_optimization<basic_string_view<char, _Traits>> = true;
+
+# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<wchar_t*> = true;
+template <>
+inline constexpr bool enable_nonlocking_formatter_optimization<const wchar_t*> = true;
+template <size_t _Size>
+inline constexpr bool enable_nonlocking_formatter_optimization<wchar_t[_Size]> = true;
+template <class _Traits, class _Allocator>
+inline constexpr bool enable_nonlocking_formatter_optimization<basic_string<wchar_t, _Traits, _Allocator>> = true;
+template <class _Traits>
+inline constexpr bool enable_nonlocking_formatter_optimization<basic_string_view<wchar_t, _Traits>> = true;
+# endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
+# endif //_LIBCPP_STD_VER >= 23
+#endif //_LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/format b/libcxx/include/format
index f1e87de0f830..e037880a3053 100644
--- a/libcxx/include/format
+++ b/libcxx/include/format
@@ -126,6 +126,9 @@ namespace std {
// [format.formatter], formatter
template<class T, class charT = char> struct formatter;
+ template<class T>
+ constexpr bool enable_nonlocking_formatter_optimization = false; // since C++23
+
// [format.parse.ctx], class template basic_format_parse_context
template<class charT> class basic_format_parse_context;
using format_parse_context = basic_format_parse_context<char>;
@@ -133,7 +136,7 @@ namespace std {
// [format.range], formatting of ranges
// [format.range.fmtkind], variable template format_kind
- enum class range_format { // since C++23
+ enum class range_format { // since C++23
disabled,
map,
set,
@@ -143,20 +146,20 @@ namespace std {
};
template<class R>
- constexpr unspecified format_kind = unspecified; // since C++23
+ constexpr unspecified format_kind = unspecified; // since C++23
template<ranges::input_range R>
requires same_as<R, remove_cvref_t<R>>
- constexpr range_format format_kind<R> = see below; // since C++23
+ constexpr range_format format_kind<R> = see below; // since C++23
// [format.range.formatter], class template range_formatter
template<class T, class charT = char>
requires same_as<remove_cvref_t<T>, T> && formattable<T, charT>
- class range_formatter; // since C++23
+ class range_formatter; // since C++23
// [format.range.fmtdef], class template range-default-formatter
template<range_format K, ranges::input_range R, class charT>
- struct range-default-formatter; // exposition only, since C++23
+ struct range-default-formatter; // exposition only, since C++23
// [format.range.fmtmap], [format.range.fmtset], [format.range.fmtstr],
// specializations for maps, sets, and strings
@@ -173,7 +176,7 @@ namespace std {
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
+ template<class Context, class... Args> struct format-arg-store; // exposition only
template<class Context = format_context, class... Args>
format-arg-store<Context, Args...>
diff --git a/libcxx/modules/std/format.inc b/libcxx/modules/std/format.inc
index 743a43811005..09aa03ad73e3 100644
--- a/libcxx/modules/std/format.inc
+++ b/libcxx/modules/std/format.inc
@@ -46,6 +46,8 @@ export namespace std {
using std::formatter;
#if _LIBCPP_STD_VER >= 23
+ using std::enable_nonlocking_formatter_optimization;
+
// [format.formattable], concept formattable
using std::formattable;
#endif
diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.locking/enable_nonlocking_formatter_optimization.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.locking/enable_nonlocking_formatter_optimization.compile.pass.cpp
new file mode 100644
index 000000000000..fb078fdc7559
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.locking/enable_nonlocking_formatter_optimization.compile.pass.cpp
@@ -0,0 +1,235 @@
+//===----------------------------------------------------------------------===//
+// 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
+
+// <format>
+
+// template<class T>
+// constexpr bool enable_nonlocking_formatter_optimization = false;
+
+// Remarks: Pursuant to [namespace.std], users may specialize
+// enable_nonlocking_formatter_optimization for cv-unqualified program-defined
+// types. Such specializations shall be usable in constant expressions
+// ([expr.const]) and have type const bool.
+
+// [format.formatter.spec]
+// In addition, for each type T for which a formatter specialization is provided
+// above, each of the headers provides the following specialization:
+//
+// template<>
+// inline constexpr bool enable_nonlocking_formatter_optimization<T> = true;
+
+#include <array>
+#include <bitset>
+#include <bitset>
+#include <chrono>
+#include <complex>
+#include <concepts>
+#include <deque>
+#include <filesystem>
+#include <format>
+#include <forward_list>
+#include <list>
+#include <map>
+#include <memory>
+#include <optional>
+#include <queue>
+#include <set>
+#include <span>
+#include <stack>
+#include <system_error>
+#include <tuple>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <valarray>
+#include <variant>
+#include <vector>
+
+#include "test_macros.h"
+#include "min_allocator.h"
+
+#ifndef TEST_HAS_NO_LOCALIZATION
+# include <regex>
+#endif
+#ifndef TEST_HAS_NO_THREADS
+# include <thread>
+#endif
+
+// Tests for P0645 Text Formatting
+template <class CharT>
+void test_P0645() {
+ static_assert(std::enable_nonlocking_formatter_optimization<CharT>);
+
+ static_assert(std::enable_nonlocking_formatter_optimization<CharT*>);
+ static_assert(std::enable_nonlocking_formatter_optimization<const CharT*>);
+ static_assert(std::enable_nonlocking_formatter_optimization<CharT[42]>);
+
+ static_assert(std::enable_nonlocking_formatter_optimization<std::basic_string<CharT>>);
+ static_assert(std::enable_nonlocking_formatter_optimization<std::basic_string_view<CharT>>);
+
+ static_assert(std::enable_nonlocking_formatter_optimization<bool>);
+
+ static_assert(std::enable_nonlocking_formatter_optimization<signed char>);
+ static_assert(std::enable_nonlocking_formatter_optimization<signed short>);
+ static_assert(std::enable_nonlocking_formatter_optimization<signed int>);
+ static_assert(std::enable_nonlocking_formatter_optimization<signed long>);
+ static_assert(std::enable_nonlocking_formatter_optimization<signed long long>);
+#ifndef TEST_HAS_NO_INT128
+ static_assert(std::enable_nonlocking_formatter_optimization<__int128_t>);
+#endif
+
+ static_assert(std::enable_nonlocking_formatter_optimization<unsigned char>);
+ static_assert(std::enable_nonlocking_formatter_optimization<unsigned short>);
+ static_assert(std::enable_nonlocking_formatter_optimization<unsigned int>);
+ static_assert(std::enable_nonlocking_formatter_optimization<unsigned long>);
+ static_assert(std::enable_nonlocking_formatter_optimization<unsigned long long>);
+#ifndef TEST_HAS_NO_INT128
+ static_assert(std::enable_nonlocking_formatter_optimization<__uint128_t>);
+#endif
+
+ static_assert(std::enable_nonlocking_formatter_optimization<float>);
+ static_assert(std::enable_nonlocking_formatter_optimization<double>);
+ static_assert(std::enable_nonlocking_formatter_optimization<long double>);
+
+ static_assert(std::enable_nonlocking_formatter_optimization<std::nullptr_t>);
+ static_assert(std::enable_nonlocking_formatter_optimization<void*>);
+ static_assert(std::enable_nonlocking_formatter_optimization<const void*>);
+}
+
+// Tests for P1361 Integration of chrono with text formatting
+//
+// Some tests are commented out since these types haven't been implemented in
+// chrono yet. After P1361 has been implemented these formatters should be all
+// enabled.
+void test_P1361() {
+// The chrono formatters require localization support.
+// [time.format]/7
+// If the chrono-specs is omitted, the chrono object is formatted as if by
+// streaming it to std::ostringstream os with the formatting
+// locale imbued and copying os.str() through the output iterator of the
+// context with additional padding and adjustments as specified by the format
+// specifiers.
+// In libc++ std:::ostringstream requires localization support.
+#ifndef TEST_HAS_NO_LOCALIZATION
+
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::microseconds>);
+
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::sys_time<std::chrono::microseconds>>);
+ //static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::utc_time<std::chrono::microseconds>>);
+ //static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::tai_time<std::chrono::microseconds>>);
+ //static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::gps_time<std::chrono::microseconds>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::file_time<std::chrono::microseconds>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::local_time<std::chrono::microseconds>>);
+
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::day>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::month>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::year>);
+
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::weekday>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::weekday_indexed>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::weekday_last>);
+
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::month_day>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::month_day_last>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::month_weekday>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::month_weekday_last>);
+
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::year_month>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::year_month_day>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::year_month_day_last>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::year_month_weekday>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::year_month_weekday_last>);
+
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::hh_mm_ss<std::chrono::microseconds>>);
+
+ //static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::sys_info>);
+ //static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::local_info>);
+
+ //static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::zoned_time>);
+
+#endif // TEST_HAS_NO_LOCALIZATION
+}
+
+// Tests for P1636 Formatters for library types
+//
+// The paper hasn't been voted in so currently all formatters are disabled.
+// Note the paper has been abandoned, the types are kept since other papers may
+// introduce these formatters.
+void test_P1636() {
+#ifndef TEST_HAS_NO_THREADS
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::thread::id>);
+#endif
+}
+
+template <class Vector>
+void test_P2286_vector_bool() {
+ static_assert(!std::enable_nonlocking_formatter_optimization<Vector>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<typename Vector::reference>);
+
+ // The const_reference shall be a bool.
+ // However libc++ uses a __bit_const_reference<vector> when
+ // _LIBCPP_ABI_BITSET_VECTOR_BOOL_CONST_SUBSCRIPT_RETURN_BOOL is defined.
+ static_assert(!std::enable_nonlocking_formatter_optimization<const Vector&>);
+ static_assert(std::enable_nonlocking_formatter_optimization<typename Vector::const_reference> ==
+ std::same_as<typename Vector::const_reference, bool>);
+}
+
+// Tests for P2286 Formatting ranges
+void test_P2286() {
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::array<int, 42>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::vector<int>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::deque<int>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::forward_list<int>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::list<int>>);
+
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::set<int>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::map<int, int>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::multiset<int>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::multimap<int, int>>);
+
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::unordered_set<int>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::unordered_map<int, int>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::unordered_multiset<int>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::unordered_multimap<int, int>>);
+
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::stack<int>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::queue<int>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::priority_queue<int>>);
+
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::span<int>>);
+
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::valarray<int>>);
+
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::pair<int, int>>);
+ static_assert(!std::enable_nonlocking_formatter_optimization<std::tuple<int>>);
+
+ test_P2286_vector_bool<std::vector<bool>>();
+ test_P2286_vector_bool<std::vector<bool, std::allocator<bool>>>();
+ test_P2286_vector_bool<std::vector<bool, min_allocator<bool>>>();
+}
+
+// The trait does not care about whether the type is formattable, obviously the
+// trait for non formattable types are not used.
+struct not_formattable_nonlocking_disabled {};
+static_assert(!std::enable_nonlocking_formatter_optimization<not_formattable_nonlocking_disabled>);
+
+struct not_formattable_nonlocking_enabled {};
+template <>
+inline constexpr bool std::enable_nonlocking_formatter_optimization<not_formattable_nonlocking_enabled> = true;
+static_assert(std::enable_nonlocking_formatter_optimization<not_formattable_nonlocking_enabled>);
+
+void test() {
+ test_P0645<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test_P0645<wchar_t>();
+#endif
+ test_P1361();
+ test_P1636();
+ test_P2286();
+}