[libcxx-commits] [libcxx] [libc++] Implements Runtime format strings. (PR #73353)

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Fri Dec 8 08:59:11 PST 2023


https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/73353

>From fda02a31aaf8cbb7f64cc8f52ce4eba9a41e44cc Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Fri, 24 Nov 2023 18:21:44 +0100
Subject: [PATCH] [libc++] Implements Runtime format strings.

This change requires quite a number of changes in the tests; this is not
code I expect people to use in the wild. So I don't expect breakage for
users.

Implements:
- P2905R2 Runtime format strings, as a Defect Report
---
 libcxx/docs/ReleaseNotes/18.rst                   |  1 +
 libcxx/docs/Status/Cxx2cPapers.csv                |  2 +-
 libcxx/docs/Status/FormatIssues.csv               |  2 +-
 libcxx/include/__format/format_arg_store.h        |  6 +++---
 libcxx/include/__format/format_functions.h        |  8 ++++----
 libcxx/include/format                             |  4 ++--
 .../print.fun/no_file_description.pass.cpp        |  6 ++++--
 .../print.fun/vprint_nonunicode.sh.cpp            |  8 ++++++--
 .../print.fun/vprint_unicode.sh.cpp               |  8 ++++++--
 .../format.arg.store/make_format_args.pass.cpp    | 15 +++++++++++++--
 .../format.arg.store/make_format_args.sh.cpp      |  3 ++-
 .../format.arg.store/make_wformat_args.pass.cpp   | 15 +++++++++++++--
 .../format.args/ctad.compile.pass.cpp             |  5 +++--
 .../format.arguments/format.args/ctor.pass.cpp    |  9 ++++++---
 .../format.context/format.context/arg.pass.cpp    |  6 ++++--
 .../format.context/format.context/ctor.pass.cpp   |  5 ++++-
 .../format.context/format.context/locale.pass.cpp |  5 ++++-
 .../formatter.string.pass.cpp                     |  3 +--
 18 files changed, 78 insertions(+), 33 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index abefe4c28ca95..9e509db6359c4 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -50,6 +50,7 @@ Implemented Papers
 - P0053R7 - C++ Synchronized Buffered Ostream (in the experimental library)
 - P2467R1 - Support exclusive mode for fstreams
 - P0020R6 - Floating Point Atomic
+- P2905R2 - Runtime format strings
 - P2918R2 - Runtime format strings II
 - P2871R3 - Remove Deprecated Unicode Conversion Facets from C++26
 - P2870R3 - Remove basic_string::reserve()
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 1d071b7ebcb4a..ff83648aa7683 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -30,7 +30,7 @@
 "`P0543R3 <https://wg21.link/P0543R3>`__","LWG","Saturation arithmetic","Kona November 2023","","",""
 "`P2407R5 <https://wg21.link/P2407R5>`__","LWG","Freestanding Library: Partial Classes","Kona November 2023","","",""
 "`P2546R5 <https://wg21.link/P2546R5>`__","LWG","Debugging Support","Kona November 2023","","",""
-"`P2905R2 <https://wg21.link/P2905R2>`__","LWG","Runtime format strings","Kona November 2023","","","|format| |DR|"
+"`P2905R2 <https://wg21.link/P2905R2>`__","LWG","Runtime format strings","Kona November 2023","|Complete|","18.0","|format| |DR|"
 "`P2918R2 <https://wg21.link/P2918R2>`__","LWG","Runtime format strings II","Kona November 2023","|Complete|","18.0","|format|"
 "`P2909R4 <https://wg21.link/P2909R4>`__","LWG","Fix formatting of code units as integers (Dude, where’s my ``char``?)","Kona November 2023","|Complete|","18.0","|format| |DR|"
 "`P0952R2 <https://wg21.link/P0952R2>`__","LWG","A new specification for ``std::generate_canonical``","Kona November 2023","","",""
diff --git a/libcxx/docs/Status/FormatIssues.csv b/libcxx/docs/Status/FormatIssues.csv
index 005de97405f7c..efb3e484f357e 100644
--- a/libcxx/docs/Status/FormatIssues.csv
+++ b/libcxx/docs/Status/FormatIssues.csv
@@ -17,7 +17,7 @@ Number,Name,Standard,Assignee,Status,First released version
 "`P2510R3 <https://wg21.link/P2510R3>`__","Formatting pointers","C++26","Mark de Wever","|Complete|",17.0
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","C++26","","",
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","C++26","","",
-"`P2905R2 <https://wg21.link/P2905R2>`__","Runtime format strings","C++26 DR","Mark de Wever","|In Progress|"
+"`P2905R2 <https://wg21.link/P2905R2>`__","Runtime format strings","C++26 DR","Mark de Wever","|Complete|",18.0
 "`P2918R2 <https://wg21.link/P2918R2>`__","Runtime format strings II","C++26","Mark de Wever","|Complete|",18.0
 "`P2909R4 <https://wg21.link/P2909R4>`__","Fix formatting of code units as integers (Dude, where’s my ``char``?)","C++26 DR","Mark de Wever","|Complete|",18.0
 `P1361 <https://wg21.link/P1361>`_,"Integration of chrono with text formatting","C++20",Mark de Wever,|In Progress|,
diff --git a/libcxx/include/__format/format_arg_store.h b/libcxx/include/__format/format_arg_store.h
index 2962962ab5d1c..64ee12440b62f 100644
--- a/libcxx/include/__format/format_arg_store.h
+++ b/libcxx/include/__format/format_arg_store.h
@@ -206,8 +206,8 @@ _LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp& __valu
 }
 
 template <class _Context, class... _Args>
-_LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values,
-                                                   _Args&&... __args) noexcept {
+_LIBCPP_HIDE_FROM_ABI void
+__create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values, _Args&... __args) noexcept {
   int __shift = 0;
   (
       [&] {
@@ -224,7 +224,7 @@ _LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_fo
 }
 
 template <class _Context, class... _Args>
-_LIBCPP_HIDE_FROM_ABI void __store_basic_format_arg(basic_format_arg<_Context>* __data, _Args&&... __args) noexcept {
+_LIBCPP_HIDE_FROM_ABI void __store_basic_format_arg(basic_format_arg<_Context>* __data, _Args&... __args) noexcept {
   ([&] { *__data++ = __format::__create_format_arg<_Context>(__args); }(), ...);
 }
 
diff --git a/libcxx/include/__format/format_functions.h b/libcxx/include/__format/format_functions.h
index 164592d2ec152..8b2111f0e287c 100644
--- a/libcxx/include/__format/format_functions.h
+++ b/libcxx/include/__format/format_functions.h
@@ -63,15 +63,15 @@ using wformat_args = basic_format_args<wformat_context>;
 #endif
 
 template <class _Context = format_context, class... _Args>
-_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...> make_format_args(_Args&&... __args) {
-  return std::__format_arg_store<_Context, _Args...>(__args...);
+_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...> make_format_args(_Args&... __args) {
+  return _VSTD::__format_arg_store<_Context, _Args...>(__args...);
 }
 
 #  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
 template <class... _Args>
 _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI __format_arg_store<wformat_context, _Args...>
-make_wformat_args(_Args&&... __args) {
-  return std::__format_arg_store<wformat_context, _Args...>(__args...);
+make_wformat_args(_Args&... __args) {
+  return _VSTD::__format_arg_store<wformat_context, _Args...>(__args...);
 }
 #  endif
 
diff --git a/libcxx/include/format b/libcxx/include/format
index 7b8d5922cb497..ab9b336d0cdab 100644
--- a/libcxx/include/format
+++ b/libcxx/include/format
@@ -177,10 +177,10 @@ namespace std {
 
   template<class Context = format_context, class... Args>
     format-arg-store<Context, Args...>
-      make_format_args(Args&&... args);
+      make_format_args(Args&... args);
   template<class... Args>
     format-arg-store<wformat_context, Args...>
-      make_wformat_args(Args&&... args);
+      make_wformat_args(Args&... args);
 
   // [format.error], class format_error
   class format_error;
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
index c9297318cd5d6..f502616b677b7 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
@@ -69,7 +69,8 @@ static void test_vprint_unicode() {
   FILE* file = fmemopen(buffer.data(), buffer.size(), "wb");
   assert(file);
 
-  std::vprint_unicode(file, "hello world{}", std::make_format_args('!'));
+  char c = '!';
+  std::vprint_unicode(file, "hello world{}", std::make_format_args(c));
   long pos = std::ftell(file);
   std::fclose(file);
 
@@ -83,7 +84,8 @@ static void test_vprint_nonunicode() {
   FILE* file = fmemopen(buffer.data(), buffer.size(), "wb");
   assert(file);
 
-  std::vprint_nonunicode(file, "hello world{}", std::make_format_args('!'));
+  char c = '!';
+  std::vprint_nonunicode(file, "hello world{}", std::make_format_args(c));
   long pos = std::ftell(file);
   std::fclose(file);
 
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode.sh.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode.sh.cpp
index 63e1d2ad82b74..c1a690f559b11 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode.sh.cpp
@@ -35,8 +35,12 @@
 
 int main(int, char**) {
   // The data is passed as-is so it does not depend on the encoding of the input.
-  std::vprint_nonunicode("{} {} ", std::make_format_args(1234, "一二三四"));
-  std::vprint_nonunicode("{} {}", std::make_format_args(true, nullptr));
+  int i         = 1234;
+  const char* s = "一二三四";
+  bool b        = true;
+  nullptr_t p   = nullptr;
+  std::vprint_nonunicode("{} {} ", std::make_format_args(i, s));
+  std::vprint_nonunicode("{} {}", std::make_format_args(b, p));
 
   return 0;
 }
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode.sh.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode.sh.cpp
index a9bcc33d2e014..198e71b55d9be 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode.sh.cpp
@@ -35,8 +35,12 @@
 
 int main(int, char**) {
   // The data is passed as-is so it does not depend on the encoding of the input.
-  std::vprint_unicode("{} {} ", std::make_format_args(1234, "一二三四"));
-  std::vprint_unicode("{} {}", std::make_format_args(true, nullptr));
+  int i         = 1234;
+  const char* s = "一二三四";
+  bool b        = true;
+  nullptr_t p   = nullptr;
+  std::vprint_unicode("{} {} ", std::make_format_args(i, s));
+  std::vprint_unicode("{} {}", std::make_format_args(b, p));
 
   return 0;
 }
diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp
index 72184bbd3920a..62fd0f25ae3a9 100644
--- a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp
@@ -10,7 +10,7 @@
 // <format>
 
 // template<class Context = format_context, class... Args>
-// format-arg-store<Context, Args...> make_format_args(const Args&... args);
+// format-arg-store<Context, Args...> make_format_args(Args&... args);
 
 #include <cassert>
 #include <format>
@@ -20,8 +20,19 @@
 #include "test_basic_format_arg.h"
 #include "test_macros.h"
 
+template <class... Args>
+concept can_make_format_args = requires(Args&&... args) { std::make_format_args(std::forward<Args>(args)...); };
+
+static_assert(can_make_format_args<int&>);
+static_assert(!can_make_format_args<int>);
+static_assert(!can_make_format_args<int&&>);
+
 int main(int, char**) {
-  [[maybe_unused]] auto store = std::make_format_args(42, nullptr, false, 'x');
+  int i                       = 1;
+  char c                      = 'c';
+  nullptr_t p                 = nullptr;
+  bool b                      = false;
+  [[maybe_unused]] auto store = std::make_format_args(i, p, b, c);
 
   LIBCPP_STATIC_ASSERT(
       std::same_as<decltype(store), std::__format_arg_store<std::format_context, int, nullptr_t, bool, char>>);
diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.sh.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.sh.cpp
index 95a94b0bff5d0..2d5ee8349b749 100644
--- a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.sh.cpp
+++ b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.sh.cpp
@@ -24,6 +24,7 @@
 #include "test_macros.h"
 
 void test() {
+  char c = 'c';
   TEST_IGNORE_NODISCARD
-  std::make_format_args<std::basic_format_context<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>>('c');
+  std::make_format_args<std::basic_format_context<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>>(c);
 }
diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp
index 22c6f031efc6f..73c4395a4a630 100644
--- a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp
@@ -12,7 +12,7 @@
 
 // template<class... Args>
 //   format-arg-store<wformat_context, Args...>
-//   make_wformat_args(const Args&... args);
+//   make_wformat_args(Args&... args);
 
 #include <cassert>
 #include <format>
@@ -20,8 +20,19 @@
 #include "test_basic_format_arg.h"
 #include "test_macros.h"
 
+template <class... Args>
+concept can_make_wformat_args = requires(Args&&... args) { std::make_wformat_args(std::forward<Args>(args)...); };
+
+static_assert(can_make_wformat_args<int&>);
+static_assert(!can_make_wformat_args<int>);
+static_assert(!can_make_wformat_args<int&&>);
+
 int main(int, char**) {
-  [[maybe_unused]] auto store = std::make_wformat_args(42, nullptr, false, 'x');
+  int i                       = 1;
+  char c                      = 'c';
+  nullptr_t p                 = nullptr;
+  bool b                      = false;
+  [[maybe_unused]] auto store = std::make_wformat_args(i, p, b, c);
 
   LIBCPP_STATIC_ASSERT(
       std::same_as<decltype(store), std::__format_arg_store<std::wformat_context, int, nullptr_t, bool, char>>);
diff --git a/libcxx/test/std/utilities/format/format.arguments/format.args/ctad.compile.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.args/ctad.compile.pass.cpp
index 7cadd4e76c74e..b87b5c774ef7e 100644
--- a/libcxx/test/std/utilities/format/format.arguments/format.args/ctad.compile.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.arguments/format.args/ctad.compile.pass.cpp
@@ -18,12 +18,13 @@
 #include "test_macros.h"
 
 void test() {
+  int i = 1;
   // Note the Standard way to create a format-arg-store is by using make_format_args.
-  static_assert(std::same_as<decltype(std::basic_format_args(std::make_format_args(42))),
+  static_assert(std::same_as<decltype(std::basic_format_args(std::make_format_args(i))),
                              std::basic_format_args<std::format_context>>);
 
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  static_assert(std::same_as<decltype(std::basic_format_args(std::make_wformat_args(42))),
+  static_assert(std::same_as<decltype(std::basic_format_args(std::make_wformat_args(i))),
                              std::basic_format_args<std::wformat_context>>);
 
 #endif
diff --git a/libcxx/test/std/utilities/format/format.arguments/format.args/ctor.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.args/ctor.pass.cpp
index c2d2d19d978bb..c0575c545bde3 100644
--- a/libcxx/test/std/utilities/format/format.arguments/format.args/ctor.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.arguments/format.args/ctor.pass.cpp
@@ -20,6 +20,9 @@
 
 template <class CharT>
 void test() {
+  int i         = 1;
+  char c        = 'c';
+  nullptr_t p   = nullptr;
   using Context = std::basic_format_context<CharT*, CharT>;
   {
     ASSERT_NOEXCEPT(std::basic_format_args<Context>{});
@@ -28,14 +31,14 @@ void test() {
     assert(!format_args.get(0));
   }
   {
-    auto store = std::make_format_args<Context>(1);
+    auto store = std::make_format_args<Context>(i);
     ASSERT_NOEXCEPT(std::basic_format_args<Context>{store});
     std::basic_format_args<Context> format_args{store};
     assert(format_args.get(0));
     assert(!format_args.get(1));
   }
   {
-    auto store = std::make_format_args<Context>(1, 'c');
+    auto store = std::make_format_args<Context>(i, c);
     ASSERT_NOEXCEPT(std::basic_format_args<Context>{store});
     std::basic_format_args<Context> format_args{store};
     assert(format_args.get(0));
@@ -43,7 +46,7 @@ void test() {
     assert(!format_args.get(2));
   }
   {
-    auto store = std::make_format_args<Context>(1, 'c', nullptr);
+    auto store = std::make_format_args<Context>(i, c, p);
     ASSERT_NOEXCEPT(std::basic_format_args<Context>{store});
     std::basic_format_args<Context> format_args{store};
     assert(format_args.get(0));
diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp
index 153f8bbaa4b37..824813d33a519 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp
@@ -23,9 +23,11 @@
 
 template <class OutIt, class CharT>
 void test() {
+  bool b                          = true;
+  CharT c                         = CharT('a');
+  int a                           = 42;
   std::basic_string<CharT> string = MAKE_STRING(CharT, "string");
-  auto store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(
-      true, CharT('a'), 42, string);
+  auto store                      = std::make_format_args<std::basic_format_context<OutIt, CharT>>(b, c, a, string);
   std::basic_format_args args = store;
 
   std::basic_string<CharT> output;
diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp
index 4384f7b0fe68c..40720105060f0 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp
@@ -36,11 +36,14 @@
 
 template <class OutIt, class CharT>
 void test() {
+  int a                           = 42;
+  bool b                          = true;
+  CharT c                         = CharT('a');
   std::basic_string<CharT> string = MAKE_STRING(CharT, "string");
   // The type of the object is an exposition only type. The temporary is needed
   // to extend the lifetime of the object since args stores a pointer to the
   // data in this object.
-  auto format_arg_store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(true, CharT('a'), 42, string);
+  auto format_arg_store       = std::make_format_args<std::basic_format_context<OutIt, CharT>>(b, c, a, string);
   std::basic_format_args args = format_arg_store;
 
   {
diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp
index 5533fe1b9f280..14bdc1426098e 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp
@@ -34,7 +34,10 @@ void test() {
   // The type of the object is an exposition only type. The temporary is needed
   // to extend the lifetime of the object since args stores a pointer to the
   // data in this object.
-  auto format_arg_store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(true, CharT('a'), 42, string);
+  int a                       = 42;
+  bool b                      = true;
+  CharT c                     = CharT('a');
+  auto format_arg_store       = std::make_format_args<std::basic_format_context<OutIt, CharT>>(b, c, a, string);
   std::basic_format_args args = format_arg_store;
 
   {
diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp
index 292de0cdd8450..e99deb2db5530 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp
@@ -53,8 +53,7 @@ void test(StringT expected, StringViewT fmt, StringT a, std::size_t offset) {
   using FormatCtxT = std::basic_format_context<decltype(out), CharT>;
 
   ArgumentT arg = a;
-  FormatCtxT format_ctx = test_format_context_create<decltype(out), CharT>(
-      out, std::make_format_args<FormatCtxT>(std::forward<ArgumentT>(arg)));
+  FormatCtxT format_ctx = test_format_context_create<decltype(out), CharT>(out, std::make_format_args<FormatCtxT>(arg));
   formatter.format(arg, format_ctx);
   assert(result == expected);
 }



More information about the libcxx-commits mailing list