[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