Mordante updated this revision to Diff 511705.
Mordante marked 2 inline comments as done.
Mordante added a comment.

Rebased, addresses review comment, and get a CI run.


Herald added a comment.

NOTE: Clang-Format Team Automated Review Comment

It looks like your clang-format review does not contain any unit tests, please 
try to ensure all code changes have a unit test (unless this is an `NFC` or 
refactoring, adding documentation etc..)

Add your unit tests in `clang/unittests/Format` and you can build with `ninja 
FormatTests`.  We recommend using the `verifyFormat(xxx)` format of unit tests 
rather than `EXPECT_EQ` as this will ensure you change is tolerant to random 
whitespace changes (see FormatTest.cpp as an example)

For situations where your change is altering the TokenAnnotator.cpp which can 
happen if you are trying to improve the annotation phase to ensure we are 
correctly identifying the type of a token, please add a token annotator test in 
`TokenAnnotatorTest.cpp`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D144331/new/

https://reviews.llvm.org/D144331

Files:
  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/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
  
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

Index: libcxx/utils/generate_feature_test_macro_components.py
===================================================================
--- libcxx/utils/generate_feature_test_macro_components.py
+++ libcxx/utils/generate_feature_test_macro_components.py
@@ -327,6 +327,13 @@
     "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 },
Index: libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
===================================================================
--- libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
+++ libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
@@ -175,7 +175,8 @@
 // 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 @@
     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>();
 }
Index: libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
===================================================================
--- libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
+++ libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
@@ -18,16 +18,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;
 }
Index: libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/parse.pass.cpp
===================================================================
--- /dev/null
+++ 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;
+}
Index: libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
===================================================================
--- /dev/null
+++ 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() {
+#ifndef __APPLE__
+  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;
+}
Index: libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
===================================================================
--- /dev/null
+++ libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+// 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>;
+
+// 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;
+}
Index: libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h
===================================================================
--- /dev/null
+++ 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 *****/
+#ifndef __APPLE__
+  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  // __APPLE__
+  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 // __APPLE__
+
+  /***** 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
Index: libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
===================================================================
--- /dev/null
+++ libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+// 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... 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;
+}
Index: libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
===================================================================
--- libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ 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
Index: 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/thread.version.compile.pass.cpp
+++ 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"
Index: libcxx/test/libcxx/transitive_includes/cxx2b.csv
===================================================================
--- libcxx/test/libcxx/transitive_includes/cxx2b.csv
+++ libcxx/test/libcxx/transitive_includes/cxx2b.csv
@@ -584,14 +584,32 @@
 system_error stdexcept
 system_error string
 system_error version
+thread array
+thread charconv
 thread compare
 thread cstddef
+<<<<<<< HEAD
 thread ctime
 thread iosfwd
 thread limits
 thread ratio
 thread system_error
 thread tuple
+=======
+thread cstdint
+thread cstdlib
+thread initializer_list
+thread iosfwd
+thread limits
+thread locale
+thread stdexcept
+thread string
+thread string_view
+thread system_error
+thread tuple
+thread type_traits
+thread vector
+>>>>>>> 824b22b7f148 ([libc++][format] Implements formatter thread::id.)
 thread version
 tuple compare
 tuple cstddef
Index: libcxx/test/libcxx/transitive_includes/cxx20.csv
===================================================================
--- libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -838,19 +838,26 @@
 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
Index: libcxx/test/libcxx/transitive_includes/cxx17.csv
===================================================================
--- libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -832,6 +832,7 @@
 system_error string
 system_error type_traits
 system_error version
+thread array
 thread chrono
 thread compare
 thread cstddef
@@ -841,11 +842,15 @@
 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
Index: libcxx/test/libcxx/transitive_includes/cxx14.csv
===================================================================
--- libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -832,6 +832,7 @@
 system_error string
 system_error type_traits
 system_error version
+thread array
 thread chrono
 thread compare
 thread cstddef
@@ -841,11 +842,15 @@
 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
Index: libcxx/test/libcxx/transitive_includes/cxx11.csv
===================================================================
--- libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -830,6 +830,7 @@
 system_error string
 system_error type_traits
 system_error version
+thread array
 thread chrono
 thread compare
 thread cstddef
@@ -839,11 +840,15 @@
 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
Index: libcxx/test/libcxx/transitive_includes/cxx03.csv
===================================================================
--- libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -829,6 +829,7 @@
 system_error string
 system_error type_traits
 system_error version
+thread array
 thread chrono
 thread compare
 thread cstddef
@@ -838,11 +839,15 @@
 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
Index: libcxx/include/version
===================================================================
--- libcxx/include/version
+++ libcxx/include/version
@@ -85,6 +85,7 @@
 __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 @@
 # 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
Index: libcxx/include/thread
===================================================================
--- libcxx/include/thread
+++ libcxx/include/thread
@@ -64,6 +64,9 @@
 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 @@
 */
 
 #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 @@
 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_;
Index: libcxx/include/__threading_support
===================================================================
--- libcxx/include/__threading_support
+++ libcxx/include/__threading_support
@@ -643,6 +643,8 @@
     _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>;
Index: libcxx/include/__format/parser_std_format_spec.h
===================================================================
--- libcxx/include/__format/parser_std_format_spec.h
+++ libcxx/include/__format/parser_std_format_spec.h
@@ -143,6 +143,7 @@
 #  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 {
Index: libcxx/docs/Status/FormatPaper.csv
===================================================================
--- libcxx/docs/Status/FormatPaper.csv
+++ libcxx/docs/Status/FormatPaper.csv
@@ -40,5 +40,5 @@
 `[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,,
Index: libcxx/docs/Status/Cxx2bPapers.csv
===================================================================
--- libcxx/docs/Status/Cxx2bPapers.csv
+++ 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","","",""
Index: libcxx/docs/Status/Cxx2b.rst
===================================================================
--- libcxx/docs/Status/Cxx2b.rst
+++ libcxx/docs/Status/Cxx2b.rst
@@ -44,6 +44,9 @@
       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:
 
Index: libcxx/docs/ReleaseNotes.rst
===================================================================
--- libcxx/docs/ReleaseNotes.rst
+++ libcxx/docs/ReleaseNotes.rst
@@ -39,6 +39,7 @@
 ------------------
 - 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
 -----------------------------
Index: libcxx/docs/FeatureTestMacroTable.rst
===================================================================
--- libcxx/docs/FeatureTestMacroTable.rst
+++ libcxx/docs/FeatureTestMacroTable.rst
@@ -322,6 +322,8 @@
     ------------------------------------------------- -----------------
     ``__cpp_lib_expected``                            ``202202L``
     ------------------------------------------------- -----------------
+    ``__cpp_lib_formatters``                          *unimplemented*
+    ------------------------------------------------- -----------------
     ``__cpp_lib_forward_like``                        ``202207L``
     ------------------------------------------------- -----------------
     ``__cpp_lib_invoke_r``                            ``202106L``
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to