-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[libc++] Implements Runtime format strings II. #72543
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-libcxx Author: Mark de Wever (mordante) ChangesImplements
Full diff: https://github.com/llvm/llvm-project/pull/72543.diff 10 Files Affected:
diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index d5df7fde5be91af..7cc8481317bf62f 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -48,6 +48,7 @@ Implemented Papers
- P2538R1 - ADL-proof ``std::projected``
- P2614R2 - Deprecate ``numeric_limits::has_denorm``
- P0053R7 - C++ Synchronized Buffered Ostream (in the experimental library)
+- P2918R2 - Runtime format strings II
Improvements and New Features
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index eb5398f66d0e0c1..e7ddc906b622bd9 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -31,7 +31,7 @@
"`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|"
-"`P2918R2 <https://wg21.link/P2918R2>`__","LWG","Runtime format strings II","Kona November 2023","","","|format|"
+"`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","","","|format| |DR|"
"`P0952R2 <https://wg21.link/P0952R2>`__","LWG","A new specification for ``std::generate_canonical``","Kona November 2023","","",""
"`P2447R6 <https://wg21.link/P2447R6>`__","LWG","``std::span`` over an initializer list","Kona November 2023","","",""
diff --git a/libcxx/docs/Status/FormatIssues.csv b/libcxx/docs/Status/FormatIssues.csv
index 5c6463fa97ed2ba..14ea7c4360a373f 100644
--- a/libcxx/docs/Status/FormatIssues.csv
+++ b/libcxx/docs/Status/FormatIssues.csv
@@ -18,7 +18,7 @@ Number,Name,Standard,Assignee,Status,First released version
"`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|"
-"`P2918R2 <https://wg21.link/P2918R2>`__","Runtime format strings II","C++26","Mark de Wever","|In Progress|"
+"`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","|In Progress|"
`P1361 <https://wg21.link/P1361>`_,"Integration of chrono with text formatting","C++20",Mark de Wever,|In Progress|,
`P2372 <https://wg21.link/P2372>`__,"Fixing locale handling in chrono formatters","C++20",Mark de Wever,|In Progress|,
diff --git a/libcxx/include/__format/format_functions.h b/libcxx/include/__format/format_functions.h
index bb62c1ce10c15cb..a0ece7b2bc5db70 100644
--- a/libcxx/include/__format/format_functions.h
+++ b/libcxx/include/__format/format_functions.h
@@ -338,6 +338,28 @@ __vformat_to(_ParseCtx&& __parse_ctx, _Ctx&& __ctx) {
} // namespace __format
+# if _LIBCPP_STD_VER >= 26
+template <class _CharT>
+struct _LIBCPP_TEMPLATE_VIS __runtime_format_string {
+private:
+ basic_string_view<_CharT> __str_;
+
+ template <class _Cp, class... _Args>
+ friend struct _LIBCPP_TEMPLATE_VIS basic_format_string;
+
+public:
+ _LIBCPP_HIDE_FROM_ABI __runtime_format_string(basic_string_view<_CharT> __s) noexcept : __str_(__s) {}
+
+ __runtime_format_string(const __runtime_format_string&) = delete;
+ __runtime_format_string& operator=(const __runtime_format_string&) = delete;
+};
+
+_LIBCPP_HIDE_FROM_ABI inline __runtime_format_string<char> runtime_format(string_view __fmt) noexcept { return __fmt; }
+_LIBCPP_HIDE_FROM_ABI inline __runtime_format_string<wchar_t> runtime_format(wstring_view __fmt) noexcept {
+ return __fmt;
+}
+# endif //_LIBCPP_STD_VER >= 26
+
template <class _CharT, class... _Args>
struct _LIBCPP_TEMPLATE_VIS basic_format_string {
template <class _Tp>
@@ -350,6 +372,9 @@ struct _LIBCPP_TEMPLATE_VIS basic_format_string {
_LIBCPP_HIDE_FROM_ABI constexpr basic_string_view<_CharT> get() const noexcept {
return __str_;
}
+# if _LIBCPP_STD_VER >= 26
+ _LIBCPP_HIDE_FROM_ABI basic_format_string(__runtime_format_string<_CharT> __s) noexcept : __str_(__s.__str_) {}
+# endif //_LIBCPP_STD_VER >= 26
private:
basic_string_view<_CharT> __str_;
diff --git a/libcxx/include/format b/libcxx/include/format
index c48bcf6e8403930..8a4837a868a9845 100644
--- a/libcxx/include/format
+++ b/libcxx/include/format
@@ -31,6 +31,7 @@ namespace std {
public:
template<class T> consteval basic_format_string(const T& s);
+ basic_format_string(runtime-format-string<charT> s) noexcept : str(s.str) {} // since C++26
constexpr basic_string_view<charT> get() const noexcept { return str; }
};
@@ -41,6 +42,24 @@ namespace std {
using wformat_string = // since C++23, exposition only before C++23
basic_format_string<wchar_t, type_identity_t<Args>...>;
+ template<class charT> struct runtime-format-string { // since C++26, exposition-only
+ private:
+ basic_string_view<charT> str; // exposition-only
+
+ public:
+ runtime-format-string(basic_string_view<charT> s) noexcept : str(s) {}
+
+ runtime-format-string(const runtime-format-string&) = delete;
+ runtime-format-string& operator=(const runtime-format-string&) = delete;
+ };
+
+ runtime-format-string<char> runtime_format(string_view fmt) noexcept {
+ return fmt;
+ }
+ runtime-format-string<wchar_t> runtime_format(wstring_view fmt) noexcept {
+ return fmt;
+ }
+
// [format.functions], formatting functions
template<class... Args>
string format(format-string<Args...> fmt, Args&&... args);
diff --git a/libcxx/modules/std/format.inc b/libcxx/modules/std/format.inc
index c1bc91f8317dd0a..743a43811005a4b 100644
--- a/libcxx/modules/std/format.inc
+++ b/libcxx/modules/std/format.inc
@@ -28,6 +28,9 @@ export namespace std {
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
using std::wformat_string;
#endif
+#if _LIBCPP_STD_VER >= 26
+ using std::runtime_format;
+#endif //_LIBCPP_STD_VER >= 26
// [format.functions], formatting functions
using std::format;
diff --git a/libcxx/test/std/utilities/format/format.functions/format.locale.runtime_format.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format.locale.runtime_format.pass.cpp
new file mode 100644
index 000000000000000..e2e10a3dd97d29e
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.functions/format.locale.runtime_format.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+// 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, c++23
+// UNSUPPORTED: no-localization
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// XFAIL: availability-fp_to_chars-missing
+
+// <format>
+
+// template<class charT, class... Args>
+// struct basic_format_string {
+// ...
+// basic_format_string(runtime-format-string<charT> s) noexcept : str(s.str) {}
+// ...
+// }
+
+// template<class... Args>
+// string format(const locale& loc, format_string<Args...> fmt, const Args&... args);
+// template<class... Args>
+// wstring format(const locale& loc, wformat_string<Args...> fmt, const Args&... args);
+
+#include <format>
+#include <cassert>
+#include <locale>
+#include <vector>
+
+#include "test_macros.h"
+#include "format_tests.h"
+#include "string_literal.h"
+#include "assert_macros.h"
+#include "concat_macros.h"
+
+auto test = []<class CharT, class... Args>(
+ std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) constexpr {
+ std::basic_string<CharT> out = std::format(std::locale(), std::runtime_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>(
+ [[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::format(std::locale(), std::runtime_format(fmt), std::forward<Args>(args)...));
+ };
+
+int main(int, char**) {
+ format_tests<char, execution_modus::partial>(test, test_exception);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ format_tests_char_to_wchar_t(test);
+ format_tests<wchar_t, execution_modus::partial>(test, test_exception);
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/format/format.functions/format.runtime_format.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format.runtime_format.pass.cpp
new file mode 100644
index 000000000000000..8a8e8527e0cba49
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.functions/format.runtime_format.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+// 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, c++23
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// XFAIL: availability-fp_to_chars-missing
+
+// <format>
+
+// template<class charT, class... Args>
+// struct basic_format_string {
+// ...
+// basic_format_string(runtime-format-string<charT> s) noexcept : str(s.str) {}
+// ...
+// }
+
+// template<class... Args>
+// string format(format_string<Args...> fmt, const Args&... args);
+// template<class... Args>
+// wstring format(wformat_string<Args...> fmt, const Args&... args);
+
+#include <format>
+#include <cassert>
+#include <vector>
+
+#include "test_macros.h"
+#include "format_tests.h"
+#include "string_literal.h"
+#include "assert_macros.h"
+#include "concat_macros.h"
+
+auto test = []<class CharT, class... Args>(
+ std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) constexpr {
+ std::basic_string<CharT> out = std::format(std::runtime_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>(
+ [[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::format(std::runtime_format(fmt), std::forward<Args>(args)...));
+ };
+
+int main(int, char**) {
+ format_tests<char, execution_modus::partial>(test, test_exception);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ format_tests_char_to_wchar_t(test);
+ format_tests<wchar_t, execution_modus::partial>(test, test_exception);
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/format/format.syn/runtime_format_string.pass.cpp b/libcxx/test/std/utilities/format/format.syn/runtime_format_string.pass.cpp
new file mode 100644
index 000000000000000..660fe79835ac090
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.syn/runtime_format_string.pass.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++23
+
+// <format>
+
+// template<class charT> struct runtime-format-string { // exposition-only
+// private:
+// basic_string_view<charT> str; // exposition-only
+//
+// public:
+// runtime-format-string(basic_string_view<charT> s) noexcept : str(s) {}
+//
+// runtime-format-string(const runtime-format-string&) = delete;
+// runtime-format-string& operator=(const runtime-format-string&) = delete;
+// };
+// runtime-format-string<char> runtime_format(string_view fmt) noexcept;
+// runtime-format-string<wchar_t> runtime_format(wstring_view fmt) noexcept;
+
+#include <format>
+
+#include <cassert>
+#include <concepts>
+#include <string_view>
+#include <type_traits>
+
+#include "test_macros.h"
+
+template <class T, class CharT>
+static void test_properties() {
+ static_assert(std::is_nothrow_convertible_v<std::basic_string_view<CharT>, T>);
+ static_assert(std::is_nothrow_constructible_v<T, std::basic_string_view<CharT>>);
+
+ static_assert(!std::copy_constructible<T>);
+ static_assert(!std::is_copy_assignable_v<T>);
+
+ static_assert(!std::move_constructible<T>);
+ static_assert(!std::is_move_assignable_v<T>);
+}
+
+int main(int, char**) {
+ static_assert(noexcept(std::runtime_format(std::string_view{})));
+ auto format_string = std::runtime_format(std::string_view{});
+ LIBCPP_ASSERT((std::same_as<decltype(format_string), std::__runtime_format_string<char>>));
+ test_properties<decltype(format_string), char>();
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ static_assert(noexcept(std::runtime_format(std::wstring_view{})));
+ auto wformat_string = std::runtime_format(std::wstring_view{});
+ LIBCPP_ASSERT((std::same_as<decltype(wformat_string), std::__runtime_format_string<wchar_t>>));
+ test_properties<decltype(wformat_string), wchar_t>();
+#endif // TEST_HAS_NO_WIDE_CHARACTERS
+
+ return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 0c07ac432d7c7e4..0d4f1708e85a333 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -467,7 +467,7 @@ def add_version_header(tc):
# "c++20": 202110 Not implemented P2372R3 Fixing locale handling in chrono formatters
"c++20": 202106,
# "c++23": 202207, Not implemented P2419R2 Clarify handling of encodings in localized formatting of chrono types
- # "c++26": 202311, Not implemented P2918R2 Runtime format strings II
+ # "c++26": 202311, P2918R2 Runtime format strings II (implemented)
},
# Note these three papers are adopted at the June 2023 meeting and have sequential numbering
# 202304 P2510R3 Formatting pointers (Implemented)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically LGTM with comments.
libcxx/test/std/utilities/format/format.functions/format.locale.runtime_format.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/utilities/format/format.functions/format.runtime_format.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/utilities/format/format.functions/format.locale.runtime_format.pass.cpp
Show resolved
Hide resolved
libcxx/test/std/utilities/format/format.functions/format.runtime_format.pass.cpp
Show resolved
Hide resolved
libcxx/test/std/utilities/format/format.functions/format.runtime_format.pass.cpp
Show resolved
Hide resolved
libcxx/test/std/utilities/format/format.syn/runtime_format_string.pass.cpp
Show resolved
Hide resolved
libcxx/test/std/utilities/format/format.syn/runtime_format_string.pass.cpp
Show resolved
Hide resolved
1f397fd
to
cf2c112
Compare
cf2c112
to
f73eeac
Compare
Implements - P2918R2 Runtime format strings II
f73eeac
to
bd45154
Compare
Thanks for the heads up, it seems I overlooked the buildbot e-mail. |
Is this one of these disabled tests? |
All these fail msan bot
|
|
Not sure what that stack means, so I just disabled these tests on our bots as a few similar were already disabled. |
I see you disabled them in zorg and not in the test itself, the same for the previous errors. That means we don't see them exclusion in the libc++ tests themselves; hence my conclusion that the other tests worked. I wanted to see which format-string causes the issue, there are hundreds of them tested. Unfortunately it seems the stack is not complete so it's hard to tell. |
On e1f911e
|
It looks like libunwind in this setup fails, but I am not sure how to debug that. |
Thanks for the complete stack-trace. This seems to fail in a test that validates the expected exception is thrown. The test line contains So I also start to suspect an issue in the unwinder. I'm also not sure how to debug that. Since the tests passes on other platforms including platforms using sanitizer I'm happy to keep the suppression on Zorg. |
Implements