[libcxx-commits] [libcxx] 88622aa - [libc++][format] Implements formatter thread::id.

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Sat Apr 8 07:39:44 PDT 2023


Author: Mark de Wever
Date: 2023-04-08T16:39:34+02:00
New Revision: 88622aabf10764cc32d546f448076d25b13e94cd

URL: https://github.com/llvm/llvm-project/commit/88622aabf10764cc32d546f448076d25b13e94cd
DIFF: https://github.com/llvm/llvm-project/commit/88622aabf10764cc32d546f448076d25b13e94cd.diff

LOG: [libc++][format] Implements formatter thread::id.

Since stacktrace header is WIP and it's not sure that will be done
before LLVM17 update the documentation. When the header is implemented
implementing the formatter is trivial, so that can be done quickly
afterwards.

Implements parts of:
 - P2693R1 Formatting thread::id and stacktrace

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D144331

Added: 
    libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
    libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h
    libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
    libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
    libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/parse.pass.cpp

Modified: 
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/docs/ReleaseNotes.rst
    libcxx/docs/Status/Cxx2b.rst
    libcxx/docs/Status/Cxx2bPapers.csv
    libcxx/docs/Status/FormatPaper.csv
    libcxx/include/__format/parser_std_format_spec.h
    libcxx/include/__threading_support
    libcxx/include/thread
    libcxx/include/version
    libcxx/test/libcxx/transitive_includes/cxx03.csv
    libcxx/test/libcxx/transitive_includes/cxx11.csv
    libcxx/test/libcxx/transitive_includes/cxx14.csv
    libcxx/test/libcxx/transitive_includes/cxx17.csv
    libcxx/test/libcxx/transitive_includes/cxx20.csv
    libcxx/test/libcxx/transitive_includes/cxx2b.csv
    libcxx/test/std/language.support/support.limits/support.limits.general/thread.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
    libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
    libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
    libcxx/utils/generate_feature_test_macro_components.py

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index f3d26b9f7ed82..c3be76bd7697a 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -322,6 +322,8 @@ Status
     ------------------------------------------------- -----------------
     ``__cpp_lib_expected``                            ``202202L``
     ------------------------------------------------- -----------------
+    ``__cpp_lib_formatters``                          *unimplemented*
+    ------------------------------------------------- -----------------
     ``__cpp_lib_forward_like``                        ``202207L``
     ------------------------------------------------- -----------------
     ``__cpp_lib_invoke_r``                            ``202106L``

diff  --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index 8083ba337fc16..1e2d52ca6c140 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 - P2520R0 - ``move_iterator<T*>`` should be a random access iterator
 - P1328R1 - ``constexpr type_info::operator==()``
+- P1413R3 - Formatting ``thread::id`` (the ``stacktrace`` is not done yet)
 
 Improvements and New Features
 -----------------------------

diff  --git a/libcxx/docs/Status/Cxx2b.rst b/libcxx/docs/Status/Cxx2b.rst
index 3fbbb10f3e30b..cd425b1667a03 100644
--- a/libcxx/docs/Status/Cxx2b.rst
+++ b/libcxx/docs/Status/Cxx2b.rst
@@ -44,6 +44,9 @@ Paper Status
       clang doesn't issue a diagnostic for deprecated using template declarations.
    .. [#note-P2520R0] P2520R0: Libc++ implemented this paper as a DR in C++20 as well.
    .. [#note-P2711R1] P2711R1: ``join_with_view`` hasn't been done yet since this type isn't implemented yet.
+   .. [#note-P2693R1] P1413R3: The formatter for ``std::thread::id`` is implemented.
+      The formatter for ``stacktrace`` is not implemented, since ``stacktrace`` is
+      not implemented yet.
 
 .. _issues-status-cxx2b:
 

diff  --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv
index 900130cfdd506..9bf09b36dd574 100644
--- a/libcxx/docs/Status/Cxx2bPapers.csv
+++ b/libcxx/docs/Status/Cxx2bPapers.csv
@@ -113,7 +113,7 @@
 "`P2713R1 <https://wg21.link/P2713R1>`__","LWG", "Escaping improvements in ``std::format``","February 2023","","","|format|"
 "`P2675R1 <https://wg21.link/P2675R1>`__","LWG", "``format``'s width estimation is too approximate and not forward compatible","February 2023","","","|format|"
 "`P2572R1 <https://wg21.link/P2572R1>`__","LWG", "``std::format`` fill character allowances","February 2023","","","|format|"
-"`P2693R1 <https://wg21.link/P2693R1>`__","LWG", "Formatting ``thread::id`` and ``stacktrace``","February 2023","","","|format|"
+"`P2693R1 <https://wg21.link/P2693R1>`__","LWG", "Formatting ``thread::id`` and ``stacktrace``","February 2023","|Partial| [#note-P2693R1]_","","|format|"
 "`P2679R2 <https://wg21.link/P2679R2>`__","LWG", "Fixing ``std::start_lifetime_as`` for arrays","February 2023","","",""
 "`P2674R1 <https://wg21.link/P2674R1>`__","LWG", "A trait for implicit lifetime types","February 2023","","",""
 "`P2655R3 <https://wg21.link/P2655R3>`__","LWG", "``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","February 2023","","",""

diff  --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv
index 3c9e4f5dd6923..6661feeeb17af 100644
--- a/libcxx/docs/Status/FormatPaper.csv
+++ b/libcxx/docs/Status/FormatPaper.csv
@@ -40,5 +40,5 @@ Section,Description,Dependencies,Assignee,Status,First released version
 `[format.range.fmtstr] <https://wg21.link/format.range.fmtstr>`_,"Formatting for ranges: strings",,Mark de Wever,|In Progress|,
 `[format.range.fmtstr] <https://wg21.link/format.range.fmtstr>`_,"Formatting for ranges: strings",,Mark de Wever,|In Progress|,
 "`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``"
-`[thread.thread.id] <https://wg21.link/thread.thread.id>`_,"Formatting ``thread::id``",,Mark de Wever,|In Progress|,
+`[thread.thread.id] <https://wg21.link/thread.thread.id>`_,"Formatting ``thread::id``",,Mark de Wever,|Complete|,Clang 17
 `[stacktrace.format] <https://wg21.link/stacktrace.format>`_,"Formatting ``stacktrace``",A ``<stacktrace>`` implementation,Mark de Wever,,

diff  --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h
index c500525932025..0c5413249a1ef 100644
--- a/libcxx/include/__format/parser_std_format_spec.h
+++ b/libcxx/include/__format/parser_std_format_spec.h
@@ -143,6 +143,7 @@ inline constexpr __fields __fields_pointer{.__type_ = true};
 #  if _LIBCPP_STD_VER >= 23
 inline constexpr __fields __fields_tuple{.__use_range_fill_ = true};
 inline constexpr __fields __fields_range{.__use_range_fill_ = true};
+inline constexpr __fields __fields_fill_align_width{};
 #  endif
 
 enum class _LIBCPP_ENUM_VIS __alignment : uint8_t {

diff  --git a/libcxx/include/__threading_support b/libcxx/include/__threading_support
index f758aa84fe9b4..08dfd73c0a4d5 100644
--- a/libcxx/include/__threading_support
+++ b/libcxx/include/__threading_support
@@ -643,6 +643,8 @@ private:
     _LIBCPP_INLINE_VISIBILITY
     __thread_id(__libcpp_thread_id __id) : __id_(__id) {}
 
+    _LIBCPP_HIDE_FROM_ABI friend __libcpp_thread_id __get_underlying_id(const __thread_id __id) { return __id.__id_; }
+
     friend __thread_id this_thread::get_id() _NOEXCEPT;
     friend class _LIBCPP_TYPE_VIS thread;
     friend struct _LIBCPP_TEMPLATE_VIS hash<__thread_id>;

diff  --git a/libcxx/include/thread b/libcxx/include/thread
index 8959eb0de0d38..d2e67fa6c214c 100644
--- a/libcxx/include/thread
+++ b/libcxx/include/thread
@@ -64,6 +64,9 @@ template<class charT, class traits>
 basic_ostream<charT, traits>&
 operator<<(basic_ostream<charT, traits>& out, thread::id id);
 
+template<class charT>
+struct formatter<thread::id, charT>;
+
 namespace this_thread
 {
 
@@ -84,11 +87,18 @@ void sleep_for(const chrono::duration<Rep, Period>& rel_time);
 */
 
 #include <__assert> // all public C++ headers provide the assertion handler
+#include <__availability>
 #include <__chrono/steady_clock.h>
 #include <__chrono/time_point.h>
+#include <__concepts/arithmetic.h>
 #include <__condition_variable/condition_variable.h>
 #include <__config>
 #include <__exception/terminate.h>
+#include <__format/concepts.h>
+#include <__format/format_parse_context.h>
+#include <__format/formatter.h>
+#include <__format/formatter_integral.h>
+#include <__format/parser_std_format_spec.h>
 #include <__functional/hash.h>
 #include <__memory/addressof.h>
 #include <__memory/unique_ptr.h>
@@ -224,6 +234,45 @@ basic_ostream<_CharT, _Traits>&
 operator<<(basic_ostream<_CharT, _Traits>& __os, __thread_id __id)
 {return __os << __id.__id_;}
 
+#if _LIBCPP_STD_VER >= 23
+template <__fmt_char_type _CharT>
+struct _LIBCPP_TEMPLATE_VIS formatter<__thread_id, _CharT> {
+  public:
+    _LIBCPP_HIDE_FROM_ABI constexpr auto parse(basic_format_parse_context<_CharT>& __parse_ctx)
+        -> decltype(__parse_ctx.begin()) {
+        return __parser_.__parse(__parse_ctx, __format_spec::__fields_fill_align_width);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI auto format(__thread_id __id, auto& __ctx) const -> decltype(__ctx.out()) {
+        // In __threading_support __libcpp_thread_id is either a
+        // unsigned long long or a pthread_t.
+        //
+        // The type of pthread_t is left unspecified in POSIX so it can be any
+        // type. The most logical types are an integral or pointer.
+        // On Linux systems pthread_t is an unsigned long long.
+        // On Apple systems pthread_t is a pointer type.
+        //
+        // Note the output should match what the stream operator does. Since
+        // the ostream operator has been shipped years before this formatter
+        // was added to the Standard, this formatter does what the stream
+        // operator does. This may require platform specific changes.
+
+        using _Tp = decltype(__get_underlying_id(__id));
+        using _Cp = conditional_t<integral<_Tp>, _Tp, conditional_t<is_pointer_v<_Tp>, uintptr_t, void>>;
+        static_assert(!is_same_v<_Cp, void>, "unsupported thread::id type, please file a bug report");
+
+        __format_spec::__parsed_specifications<_CharT> __specs = __parser_.__get_parsed_std_specifications(__ctx);
+        if constexpr (is_pointer_v<_Tp>) {
+          __specs.__std_.__alternate_form_ = true;
+          __specs.__std_.__type_           = __format_spec::__type::__hexadecimal_lower_case;
+        }
+        return __formatter::__format_integer(reinterpret_cast<_Cp>(__get_underlying_id(__id)), __ctx, __specs);
+    }
+
+    __format_spec::__parser<_CharT> __parser_{.__alignment_ = __format_spec::__alignment::__right};
+};
+#endif // _LIBCPP_STD_VER >= 23
+
 class _LIBCPP_TYPE_VIS thread
 {
     __libcpp_thread_t __t_;

diff  --git a/libcxx/include/version b/libcxx/include/version
index add66cef7ae7c..f9527cf2bdd83 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -85,6 +85,7 @@ __cpp_lib_execution                                     201902L <execution>
 __cpp_lib_expected                                      202202L <expected>
 __cpp_lib_filesystem                                    201703L <filesystem>
 __cpp_lib_format                                        202106L <format>
+__cpp_lib_formatters                                    202302L <stacktrace> <thread>
 __cpp_lib_forward_like                                  202207L <utility>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
@@ -397,6 +398,9 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_constexpr_memory                     202202L
 # define __cpp_lib_constexpr_typeinfo                   202106L
 # define __cpp_lib_expected                             202202L
+# if !defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format) && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
+// #   define __cpp_lib_formatters                         202302L
+# endif
 # define __cpp_lib_forward_like                         202207L
 # define __cpp_lib_invoke_r                             202106L
 # define __cpp_lib_is_scoped_enum                       202011L

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c837cb14ff65a..f8995327e5146 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -829,6 +829,7 @@ system_error stdexcept
 system_error string
 system_error type_traits
 system_error version
+thread array
 thread chrono
 thread compare
 thread cstddef
@@ -838,11 +839,15 @@ thread ctime
 thread functional
 thread iosfwd
 thread limits
+thread locale
 thread new
 thread ratio
+thread string
+thread string_view
 thread system_error
 thread tuple
 thread type_traits
+thread vector
 thread version
 tuple compare
 tuple cstddef

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 21acc10a91053..33f39ae09b322 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -830,6 +830,7 @@ system_error stdexcept
 system_error string
 system_error type_traits
 system_error version
+thread array
 thread chrono
 thread compare
 thread cstddef
@@ -839,11 +840,15 @@ thread ctime
 thread functional
 thread iosfwd
 thread limits
+thread locale
 thread new
 thread ratio
+thread string
+thread string_view
 thread system_error
 thread tuple
 thread type_traits
+thread vector
 thread version
 tuple compare
 tuple cstddef

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c43917ef474b1..d51c0c38f5354 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -832,6 +832,7 @@ system_error stdexcept
 system_error string
 system_error type_traits
 system_error version
+thread array
 thread chrono
 thread compare
 thread cstddef
@@ -841,11 +842,15 @@ thread ctime
 thread functional
 thread iosfwd
 thread limits
+thread locale
 thread new
 thread ratio
+thread string
+thread string_view
 thread system_error
 thread tuple
 thread type_traits
+thread vector
 thread version
 tuple compare
 tuple cstddef

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index c43917ef474b1..d51c0c38f5354 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -832,6 +832,7 @@ system_error stdexcept
 system_error string
 system_error type_traits
 system_error version
+thread array
 thread chrono
 thread compare
 thread cstddef
@@ -841,11 +842,15 @@ thread ctime
 thread functional
 thread iosfwd
 thread limits
+thread locale
 thread new
 thread ratio
+thread string
+thread string_view
 thread system_error
 thread tuple
 thread type_traits
+thread vector
 thread version
 tuple compare
 tuple cstddef

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 6d57cfa6706f3..522b2eb79eef7 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -838,19 +838,26 @@ system_error stdexcept
 system_error string
 system_error type_traits
 system_error version
+thread array
 thread compare
 thread cstddef
 thread cstdint
+thread cstdlib
 thread cstring
 thread ctime
 thread functional
 thread iosfwd
 thread limits
+thread locale
 thread new
 thread ratio
+thread stdexcept
+thread string
+thread string_view
 thread system_error
 thread tuple
 thread type_traits
+thread vector
 thread version
 tuple compare
 tuple cstddef

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx2b.csv b/libcxx/test/libcxx/transitive_includes/cxx2b.csv
index b0ba2350f43f4..3e02d8d48f47c 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx2b.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx2b.csv
@@ -584,14 +584,22 @@ system_error limits
 system_error stdexcept
 system_error string
 system_error version
+thread array
 thread compare
 thread cstddef
+thread cstdint
+thread cstdlib
 thread ctime
 thread iosfwd
 thread limits
+thread locale
 thread ratio
+thread stdexcept
+thread string
+thread string_view
 thread system_error
 thread tuple
+thread vector
 thread version
 tuple compare
 tuple cstddef

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/thread.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/thread.version.compile.pass.cpp
index 0f5c066fc0ffb..47f6c61e85ac8 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/thread.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/thread.version.compile.pass.cpp
@@ -17,8 +17,9 @@
 
 // Test the feature test macros defined by <thread>
 
-/*  Constant             Value
-    __cpp_lib_jthread    201911L [C++20]
+/*  Constant                Value
+    __cpp_lib_formatters    202302L [C++2b]
+    __cpp_lib_jthread       201911L [C++20]
 */
 
 #include <thread>
@@ -26,24 +27,40 @@
 
 #if TEST_STD_VER < 14
 
+# ifdef __cpp_lib_formatters
+#   error "__cpp_lib_formatters should not be defined before c++2b"
+# endif
+
 # ifdef __cpp_lib_jthread
 #   error "__cpp_lib_jthread should not be defined before c++20"
 # endif
 
 #elif TEST_STD_VER == 14
 
+# ifdef __cpp_lib_formatters
+#   error "__cpp_lib_formatters should not be defined before c++2b"
+# endif
+
 # ifdef __cpp_lib_jthread
 #   error "__cpp_lib_jthread should not be defined before c++20"
 # endif
 
 #elif TEST_STD_VER == 17
 
+# ifdef __cpp_lib_formatters
+#   error "__cpp_lib_formatters should not be defined before c++2b"
+# endif
+
 # ifdef __cpp_lib_jthread
 #   error "__cpp_lib_jthread should not be defined before c++20"
 # endif
 
 #elif TEST_STD_VER == 20
 
+# ifdef __cpp_lib_formatters
+#   error "__cpp_lib_formatters should not be defined before c++2b"
+# endif
+
 # if !defined(_LIBCPP_VERSION)
 #   ifndef __cpp_lib_jthread
 #     error "__cpp_lib_jthread should be defined in c++20"
@@ -59,6 +76,19 @@
 
 #elif TEST_STD_VER > 20
 
+# if !defined(_LIBCPP_VERSION)
+#   ifndef __cpp_lib_formatters
+#     error "__cpp_lib_formatters should be defined in c++2b"
+#   endif
+#   if __cpp_lib_formatters != 202302L
+#     error "__cpp_lib_formatters should have the value 202302L in c++2b"
+#   endif
+# else // _LIBCPP_VERSION
+#   ifdef __cpp_lib_formatters
+#     error "__cpp_lib_formatters should not be defined because it is unimplemented in libc++!"
+#   endif
+# endif
+
 # if !defined(_LIBCPP_VERSION)
 #   ifndef __cpp_lib_jthread
 #     error "__cpp_lib_jthread should be defined in c++2b"

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 284d1e5ebb041..59c9fa402579b 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -79,6 +79,7 @@
     __cpp_lib_expected                             202202L [C++2b]
     __cpp_lib_filesystem                           201703L [C++17]
     __cpp_lib_format                               202106L [C++20]
+    __cpp_lib_formatters                           202302L [C++2b]
     __cpp_lib_forward_like                         202207L [C++2b]
     __cpp_lib_gcd_lcm                              201606L [C++17]
     __cpp_lib_generic_associative_lookup           201304L [C++14]
@@ -431,6 +432,10 @@
 #   error "__cpp_lib_format should not be defined before c++20"
 # endif
 
+# ifdef __cpp_lib_formatters
+#   error "__cpp_lib_formatters should not be defined before c++2b"
+# endif
+
 # ifdef __cpp_lib_forward_like
 #   error "__cpp_lib_forward_like should not be defined before c++2b"
 # endif
@@ -1086,6 +1091,10 @@
 #   error "__cpp_lib_format should not be defined before c++20"
 # endif
 
+# ifdef __cpp_lib_formatters
+#   error "__cpp_lib_formatters should not be defined before c++2b"
+# endif
+
 # ifdef __cpp_lib_forward_like
 #   error "__cpp_lib_forward_like should not be defined before c++2b"
 # endif
@@ -1855,6 +1864,10 @@
 #   error "__cpp_lib_format should not be defined before c++20"
 # endif
 
+# ifdef __cpp_lib_formatters
+#   error "__cpp_lib_formatters should not be defined before c++2b"
+# endif
+
 # ifdef __cpp_lib_forward_like
 #   error "__cpp_lib_forward_like should not be defined before c++2b"
 # endif
@@ -2903,6 +2916,10 @@
 #   endif
 # endif
 
+# ifdef __cpp_lib_formatters
+#   error "__cpp_lib_formatters should not be defined before c++2b"
+# endif
+
 # ifdef __cpp_lib_forward_like
 #   error "__cpp_lib_forward_like should not be defined before c++2b"
 # endif
@@ -4140,6 +4157,19 @@
 #   endif
 # endif
 
+# if !defined(_LIBCPP_VERSION)
+#   ifndef __cpp_lib_formatters
+#     error "__cpp_lib_formatters should be defined in c++2b"
+#   endif
+#   if __cpp_lib_formatters != 202302L
+#     error "__cpp_lib_formatters should have the value 202302L in c++2b"
+#   endif
+# else // _LIBCPP_VERSION
+#   ifdef __cpp_lib_formatters
+#     error "__cpp_lib_formatters should not be defined because it is unimplemented in libc++!"
+#   endif
+# endif
+
 # ifndef __cpp_lib_forward_like
 #   error "__cpp_lib_forward_like should be defined in c++2b"
 # endif

diff  --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
new file mode 100644
index 0000000000000..5ac9ec7187d39
--- /dev/null
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+// 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
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+
+// TODO FMT Fix this test using GCC, it currently times out.
+// UNSUPPORTED: gcc-12
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// <thread>
+
+// template<class charT>
+// struct formatter<thread::id, charT>;
+
+// template<class... Args>
+//   string format(format_string<Args...> fmt, Args&&... args);
+// template<class... Args>
+//   wstring format(wformat_string<Args...> fmt, Args&&... args);
+
+#include <format>
+#include <cassert>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "format.functions.tests.h"
+#include "test_format_string.h"
+#include "test_macros.h"
+
+auto test = []<class CharT, class... Args>(
+                std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
+  std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
+  TEST_REQUIRE(out == expected,
+               TEST_WRITE_CONCATENATED(
+                   "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+};
+
+auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, Args&&...) {
+  // After P2216 most exceptions thrown by std::format become ill-formed.
+  // Therefore this tests does nothing.
+};
+
+int main(int, char**) {
+  format_tests<char>(test, test_exception);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  format_tests<wchar_t>(test, test_exception);
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h
new file mode 100644
index 0000000000000..82e7212c79da8
--- /dev/null
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
+#define TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
+
+#include <thread>
+
+#include "format.functions.common.h"
+#include "test_macros.h"
+
+template <class CharT, class TestFunction, class ExceptionTest>
+void format_tests(TestFunction check, ExceptionTest check_exception) {
+  // Note the output of std::thread::id is unspecified. The output text is the
+  // same as the stream operator. Since that format is already released this
+  // test follows the practice on existing systems.
+  std::thread::id input{};
+
+  /***** Test the type specific part *****/
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
+  check(SV("0"), SV("{}"), input);
+
+  // *** align-fill & width ***
+  check(SV("    0"), SV("{:5}"), input);
+  check(SV("0****"), SV("{:*<5}"), input);
+  check(SV("__0__"), SV("{:_^5}"), input);
+  check(SV("::::0"), SV("{::>5}"), input); // This is not a range, so : is allowed as fill character.
+
+  check(SV("    0"), SV("{:{}}"), input, 5);
+  check(SV("0****"), SV("{:*<{}}"), input, 5);
+  check(SV("__0__"), SV("{:_^{}}"), input, 5);
+  check(SV("####0"), SV("{:#>{}}"), input, 5);
+#else  // !defined(__APPLE__) && !defined(__FreeBSD__)
+  check(SV("0x0"), SV("{}"), input);
+
+  // *** align-fill & width ***
+  check(SV("    0x0"), SV("{:7}"), input);
+  check(SV("0x0****"), SV("{:*<7}"), input);
+  check(SV("__0x0__"), SV("{:_^7}"), input);
+  check(SV("::::0x0"), SV("{::>7}"), input); // This is not a range, so : is allowed as fill character.
+
+  check(SV("    0x0"), SV("{:{}}"), input, 7);
+  check(SV("0x0****"), SV("{:*<{}}"), input, 7);
+  check(SV("__0x0__"), SV("{:_^{}}"), input, 7);
+  check(SV("####0x0"), SV("{:#>{}}"), input, 7);
+#endif // !defined(__APPLE__) && !defined(__FreeBSD__)
+
+  /***** Test the type generic part *****/
+  check_exception("The format-spec fill field contains an invalid character", SV("{:}<}"), input);
+  check_exception("The format-spec fill field contains an invalid character", SV("{:{<}"), input);
+
+  // *** sign ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:-}"), input);
+  check_exception("The replacement field misses a terminating '}'", SV("{:+}"), input);
+  check_exception("The replacement field misses a terminating '}'", SV("{: }"), input);
+
+  // *** alternate form ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:#}"), input);
+
+  // *** zero-padding ***
+  check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input);
+
+  // *** precision ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:L}"), input);
+
+  // *** type ***
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>(""))
+    check_exception("The replacement field misses a terminating '}'", fmt, input);
+}
+
+#endif // TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H

diff  --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
new file mode 100644
index 0000000000000..555a10ce85cda
--- /dev/null
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+// 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
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+
+// TODO FMT Fix this test using GCC, it currently times out.
+// UNSUPPORTED: gcc-12
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// <thread>
+
+// template<class charT>
+// struct formatter<thread::id, charT>;
+
+// string vformat(string_view fmt, format_args args);
+// wstring vformat(wstring_view fmt, wformat_args args);
+
+#include <format>
+#include <cassert>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "format.functions.tests.h"
+#include "test_macros.h"
+
+auto test = []<class CharT, class... Args>(
+                std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) {
+  std::basic_string<CharT> out = std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...));
+  TEST_REQUIRE(out == expected,
+               TEST_WRITE_CONCATENATED(
+                   "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+};
+
+auto test_exception =
+    []<class CharT, class... Args>(
+        [[maybe_unused]] std::string_view what,
+        [[maybe_unused]] std::basic_string_view<CharT> fmt,
+        [[maybe_unused]] Args&&... args) {
+      TEST_VALIDATE_EXCEPTION(
+          std::format_error,
+          [&]([[maybe_unused]] const std::format_error& e) {
+            TEST_LIBCPP_REQUIRE(
+                e.what() == what,
+                TEST_WRITE_CONCATENATED(
+                    "\nFormat string   ", fmt, "\nExpected exception ", what, "\nActual exception   ", e.what(), '\n'));
+          },
+          TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...)));
+    };
+
+int main(int, char**) {
+  format_tests<char>(test, test_exception);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  format_tests<wchar_t>(test, test_exception);
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
new file mode 100644
index 0000000000000..548c9c1d88016
--- /dev/null
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+// 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
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+
+// TODO FMT Fix this test using GCC, it currently times out.
+// UNSUPPORTED: gcc-12
+
+// <thread>
+
+// template<class charT>
+// struct formatter<thread::id, charT>;
+
+// template<class FormatContext>
+//   typename FormatContext::iterator
+//     format(const T& r, FormatContext& ctx) const;
+
+// Note this tests the basics of this function. It's tested in more detail in
+// the format functions test.
+
+#include <cassert>
+#include <concepts>
+#include <thread>
+
+#include "test_format_context.h"
+#include "test_macros.h"
+#include "make_string.h"
+
+#define SV(S) MAKE_STRING_VIEW(CharT, S)
+
+template <class StringViewT>
+void test_format(StringViewT expected, std::thread::id arg) {
+  using CharT      = typename StringViewT::value_type;
+  using String     = std::basic_string<CharT>;
+  using OutIt      = std::back_insert_iterator<String>;
+  using FormatCtxT = std::basic_format_context<OutIt, CharT>;
+
+  const std::formatter<std::thread::id, CharT> formatter;
+
+  String result;
+  OutIt out             = std::back_inserter(result);
+  FormatCtxT format_ctx = test_format_context_create<OutIt, CharT>(out, std::make_format_args<FormatCtxT>(arg));
+  formatter.format(arg, format_ctx);
+  assert(result == expected);
+}
+
+template <class CharT>
+void test_fmt() {
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
+  test_format(SV("0"), std::thread::id());
+#else
+  test_format(SV("0x0"), std::thread::id());
+#endif
+}
+
+void test() {
+  test_fmt<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_fmt<wchar_t>();
+#endif
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/parse.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/parse.pass.cpp
new file mode 100644
index 0000000000000..cc5b6aec59476
--- /dev/null
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/parse.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+// 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
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+
+// TODO FMT Fix this test using GCC, it currently times out.
+// UNSUPPORTED: gcc-12
+
+// <thread>
+
+// template<class charT>
+// struct formatter<thread::id, charT>;
+
+// template<class ParseContext>
+//   constexpr typename ParseContext::iterator
+//     parse(ParseContext& ctx);
+
+// Note this tests the basics of this function. It's tested in more detail in
+// the format functions test.
+
+#include <cassert>
+#include <concepts>
+#include <thread>
+
+#include "test_format_context.h"
+#include "test_macros.h"
+#include "make_string.h"
+
+#define SV(S) MAKE_STRING_VIEW(CharT, S)
+
+template <class StringViewT>
+constexpr void test_parse(StringViewT fmt) {
+  using CharT    = typename StringViewT::value_type;
+  auto parse_ctx = std::basic_format_parse_context<CharT>(fmt);
+  std::formatter<std::thread::id, CharT> formatter;
+  static_assert(std::semiregular<decltype(formatter)>);
+
+  std::same_as<typename StringViewT::iterator> auto it = formatter.parse(parse_ctx);
+  assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}'));
+}
+
+template <class CharT>
+constexpr void test_fmt() {
+  test_parse(SV(""));
+  test_parse(SV("1"));
+
+  test_parse(SV("}"));
+  test_parse(SV("1}"));
+}
+
+constexpr bool test() {
+  test_fmt<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_fmt<wchar_t>();
+#endif
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
index 9050d9cce2921..d9d21cccd5bdf 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
@@ -9,6 +9,9 @@
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: no-localization
 
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
 // <thread>
 
 // class thread::id
@@ -18,16 +21,38 @@
 // operator<<(basic_ostream<charT, traits>& out, thread::id id);
 
 #include <thread>
+#include <format>
 #include <sstream>
 #include <cassert>
 
+#include "make_string.h"
 #include "test_macros.h"
 
-int main(int, char**)
-{
-    std::thread::id id0 = std::this_thread::get_id();
-    std::ostringstream os;
-    os << id0;
+template <class CharT>
+static void test() {
+  std::thread::id id0 = std::this_thread::get_id();
+  std::basic_ostringstream<CharT> os;
+  os << id0;
+
+#if TEST_STD_VER > 20 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
+  // C++23 added a formatter specialization for thread::id.
+  // This changed the requirement of ostream to have a
+  // [thread.thread.id]/2
+  //   The text representation for the character type charT of an object of
+  //   type thread::id is an unspecified sequence of charT ...
+  // This definition is used for both streaming and formatting.
+  //
+  // Test whether the output is identical.
+  std::basic_string<CharT> s = std::format(MAKE_STRING_VIEW(CharT, "{}"), id0);
+  assert(s == os.str());
+#endif
+}
+
+int main(int, char**) {
+  test<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+#endif
 
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
index c7adfea4237d5..6957c7b14e356 100644
--- a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
@@ -175,7 +175,8 @@ void test_P1361() {
 // Tests for P1636 Formatters for library types
 //
 // The paper hasn't been voted in so currently all formatters are disabled.
-// TODO validate whether the test is correct after the paper has been accepted.
+// Note the paper has been abandoned, the types are kept since other papers may
+// introduce these formatters.
 template <class CharT>
 void test_P1636() {
   assert_is_not_formattable<std::basic_streambuf<CharT>, CharT>();
@@ -191,7 +192,7 @@ void test_P1636() {
     assert_is_not_formattable<std::sub_match<CharT*>, CharT>();
 #endif
 #ifndef TEST_HAS_NO_THREADS
-  assert_is_not_formattable<std::thread::id, CharT>();
+  assert_is_formattable<std::thread::id, CharT>();
 #endif
   assert_is_not_formattable<std::unique_ptr<int>, CharT>();
 }

diff  --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 967894d4c3068..d617352b2cfcd 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -327,6 +327,13 @@ def add_version_header(tc):
     "test_suite_guard": "!defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)",
     "libcxx_guard": "!defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)",
     "unimplemented": True,
+  }, {
+    "name": "__cpp_lib_formatters",
+    "values": { "c++2b": 202302 },
+    "headers": ["stacktrace", "thread"],
+    "test_suite_guard": "!defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format) && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)",
+    "libcxx_guard": "!defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format) && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)",
+    "unimplemented": True,
   }, {
     "name": "__cpp_lib_forward_like",
     "values": { "c++2b": 202207 },


        


More information about the libcxx-commits mailing list