[libcxx-commits] [libcxx] [libc++][format] Implements P3107R5 in <format>. (PR #86713)

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Mar 26 11:54:43 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Mark de Wever (mordante)

<details>
<summary>Changes</summary>

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``

---

Patch is 21.24 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/86713.diff


9 Files Affected:

- (modified) libcxx/include/__format/formatter.h (+3) 
- (modified) libcxx/include/__format/formatter_bool.h (+5-1) 
- (modified) libcxx/include/__format/formatter_char.h (+9-1) 
- (modified) libcxx/include/__format/formatter_floating_point.h (+8) 
- (modified) libcxx/include/__format/formatter_integer.h (+32-1) 
- (modified) libcxx/include/__format/formatter_pointer.h (+8) 
- (modified) libcxx/include/__format/formatter_string.h (+26-1) 
- (modified) libcxx/include/format (+9-6) 
- (added) libcxx/test/std/utilities/format/format.formatter/format.formatter.locking/enable_nonlocking_formatter_optimization.compile.pass.cpp (+233) 


``````````diff
diff --git a/libcxx/include/__format/formatter.h b/libcxx/include/__format/formatter.h
index 47e35789b8175b..0316354ba74a16 100644
--- a/libcxx/include/__format/formatter.h
+++ b/libcxx/include/__format/formatter.h
@@ -40,6 +40,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(); })
diff --git a/libcxx/include/__format/formatter_bool.h b/libcxx/include/__format/formatter_bool.h
index 5e3daff7b3dba6..9d9fcd2469ce1f 100644
--- a/libcxx/include/__format/formatter_bool.h
+++ b/libcxx/include/__format/formatter_bool.h
@@ -70,7 +70,11 @@ struct _LIBCPP_TEMPLATE_VIS formatter<bool, _CharT> {
   __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 3358d422252f43..aa9c56538efdce 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 1d94cc349c0dd6..11aafab0db1fd9 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 d57082b3881baa..d63617233d7631 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 3373996ec3d5fa..26788b944db136 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 d1ccfb9b5f7dc9..7da5d63d4487c0 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 146613464534f7..e6d62fa9b5ad52 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/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 00000000000000..442f1e8528fc90
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.locking/enable_nonlocking_formatter_optimization.compile.pass.cpp
@@ -0,0 +1,233 @@
+//===----------------------------------------------------------------------===//
+// 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 "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>);
+}
+
+// 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_...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/86713


More information about the libcxx-commits mailing list