-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[libc++][format] Handle range-underlying-spec #81914
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
[libc++][format] Handle range-underlying-spec #81914
Conversation
An immediate colon signifeis that the range-format-spec contains only range-underlying-spec. This patch allows this code to compile and run: ```c++ std::println("{::<<9?}", std::span<const char>{"Hello", sizeof "Hello"}); ```
@llvm/pr-subscribers-libcxx Author: Po-yao Chang (poyaoc97) ChangesAn immediate colon signifeis that the range-format-spec contains only range-underlying-spec. This patch allows this code to compile and run: std::println("{::<<9?}", std::span<const char>{"Hello", sizeof "Hello"}); Patch is 36.85 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/81914.diff 8 Files Affected:
diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h
index 370b28a22bba77..ff415e5155f64e 100644
--- a/libcxx/include/__format/parser_std_format_spec.h
+++ b/libcxx/include/__format/parser_std_format_spec.h
@@ -143,9 +143,10 @@ struct __fields {
// formatters use the colon to mark the beginning of the
// underlying-format-spec. To avoid parsing ambiguities these formatter
// specializations prohibit the use of the colon as a fill character.
- uint16_t __use_range_fill_ : 1 {false};
- uint16_t __clear_brackets_ : 1 {false};
- uint16_t __consume_all_ : 1 {false};
+ uint16_t __use_range_fill_ : 1 {false};
+ uint16_t __clear_brackets_ : 1 {false};
+ uint16_t __consume_all_ : 1 {false};
+ uint16_t __has_range_underlying_spec_ : 1 {false};
};
// By not placing this constant in the formatter class it's not duplicated for
@@ -171,7 +172,8 @@ inline constexpr __fields __fields_pointer{.__zero_padding_ = true, .__type_ = t
# if _LIBCPP_STD_VER >= 23
inline constexpr __fields __fields_tuple{.__use_range_fill_ = true, .__clear_brackets_ = true};
-inline constexpr __fields __fields_range{.__use_range_fill_ = true, .__clear_brackets_ = true};
+inline constexpr __fields __fields_range{
+ .__use_range_fill_ = true, .__clear_brackets_ = true, .__has_range_underlying_spec_ = true};
inline constexpr __fields __fields_fill_align_width{};
# endif
@@ -355,7 +357,8 @@ class _LIBCPP_TEMPLATE_VIS __parser {
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator __parse(_ParseContext& __ctx, __fields __fields) {
auto __begin = __ctx.begin();
auto __end = __ctx.end();
- if (__begin == __end || *__begin == _CharT('}'))
+ if (__begin == __end || *__begin == _CharT('}') ||
+ (__fields.__has_range_underlying_spec_ && *__begin == _CharT(':')))
return __begin;
if (__parse_fill_align(__begin, __end, __fields.__use_range_fill_) && __begin == __end)
diff --git a/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h
index 05de1bb12dfa5a..9dab5ac98a7d5c 100644
--- a/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h
+++ b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h
@@ -53,7 +53,6 @@ void test_char_default(TestFunction check, ExceptionTest check_exception, auto&&
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -153,7 +152,7 @@ void test_char_string(TestFunction check, ExceptionTest check_exception, auto&&
check_exception("The format string contains an invalid escape sequence", SV("{:}<s}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<s}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<s}"), input);
+ check_exception("The type option contains an invalid value for a character formatting argument", SV("{::<s}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-s}"), input);
@@ -206,7 +205,7 @@ void test_char_escaped_string(TestFunction check, ExceptionTest check_exception,
check_exception("The format string contains an invalid escape sequence", SV("{:}<?s}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<?s}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<?s}"), input);
+ check_exception("The format specifier should consume the input or end with a '}'", SV("{::<?s}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-?s}"), input);
@@ -324,7 +323,6 @@ void test_bool(TestFunction check, ExceptionTest check_exception, auto&& input)
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -440,7 +438,6 @@ void test_int(TestFunction check, ExceptionTest check_exception, auto&& input) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -550,7 +547,6 @@ void test_floating_point(TestFunction check, ExceptionTest check_exception, auto
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -688,7 +684,6 @@ void test_pointer(TestFunction check, ExceptionTest check_exception, auto&& inpu
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -798,7 +793,6 @@ void test_string(TestFunction check, ExceptionTest check_exception, auto&& input
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -910,7 +904,6 @@ void test_status(TestFunction check, ExceptionTest check_exception, auto&& input
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
diff --git a/libcxx/test/std/containers/sequences/vector.bool/vector.bool.fmt/format.functions.tests.h b/libcxx/test/std/containers/sequences/vector.bool/vector.bool.fmt/format.functions.tests.h
index e6f8164c536421..8be3d9ab274a61 100644
--- a/libcxx/test/std/containers/sequences/vector.bool/vector.bool.fmt/format.functions.tests.h
+++ b/libcxx/test/std/containers/sequences/vector.bool/vector.bool.fmt/format.functions.tests.h
@@ -34,7 +34,6 @@ void format_test_vector_bool(TestFunction check, ExceptionTest check_exception,
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
diff --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtmap/format.functions.tests.h b/libcxx/test/std/utilities/format/format.range/format.range.fmtmap/format.functions.tests.h
index 2716f461895c8d..3ebaa054f0cabc 100644
--- a/libcxx/test/std/utilities/format/format.range/format.range.fmtmap/format.functions.tests.h
+++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtmap/format.functions.tests.h
@@ -45,7 +45,6 @@ void test_char(TestFunction check, ExceptionTest check_exception) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -87,7 +86,6 @@ void test_char(TestFunction check, ExceptionTest check_exception) {
check(SV("{__'a': 'A'___, __'b': 'B'___, __'c': 'C'___}"), SV("{::_^{}}"), input, 13);
check(SV("{#####'a': 'A', #####'b': 'B', #####'c': 'C'}"), SV("{::#>{}}"), input, 13);
- check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
@@ -157,7 +155,6 @@ void test_char_to_wchar(TestFunction check, ExceptionTest check_exception) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -198,7 +195,6 @@ void test_char_to_wchar(TestFunction check, ExceptionTest check_exception) {
check(SV("{__'a': 'A'___, __'b': 'B'___, __'c': 'C'___}"), SV("{::_^{}}"), input, 13);
check(SV("{#####'a': 'A', #####'b': 'B', #####'c': 'C'}"), SV("{::#>{}}"), input, 13);
- check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
@@ -267,7 +263,6 @@ void test_bool(TestFunction check, ExceptionTest check_exception) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -308,7 +303,6 @@ void test_bool(TestFunction check, ExceptionTest check_exception) {
check(SV("{_false: 0_, _true: 42_, _true: 1__}"), SV("{::_^{}}"), input, 10);
check(SV("{##false: 0, ##true: 42, ###true: 1}"), SV("{::#>{}}"), input, 10);
- check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
@@ -369,7 +363,6 @@ void test_int(TestFunction check, ExceptionTest check_exception, auto&& input) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -410,7 +403,6 @@ void test_int(TestFunction check, ExceptionTest check_exception, auto&& input) {
check(SV("{_-42: 42__, __1: -1___, _42: -42__}"), SV("{::_^{}}"), input, 10);
check(SV("{###-42: 42, #####1: -1, ###42: -42}"), SV("{::#>{}}"), input, 10);
- check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
@@ -478,7 +470,6 @@ void test_floating_point(TestFunction check, ExceptionTest check_exception) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -519,7 +510,6 @@ void test_floating_point(TestFunction check, ExceptionTest check_exception) {
check(SV("{_-42: 42__, __1: -1___}"), SV("{::_^{}}"), input, 10);
check(SV("{###-42: 42, #####1: -1}"), SV("{::#>{}}"), input, 10);
- check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
@@ -582,7 +572,6 @@ void test_pointer(TestFunction check, ExceptionTest check_exception) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:#}"), input);
@@ -621,7 +610,6 @@ void test_pointer(TestFunction check, ExceptionTest check_exception) {
check(SV("{__0x0: 0x0___}"), SV("{::_^{}}"), input, 13);
check(SV("{#####0x0: 0x0}"), SV("{::#>{}}"), input, 13);
- check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
@@ -685,7 +673,6 @@ void test_string(TestFunction check, ExceptionTest check_exception) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:#}"), input);
@@ -724,7 +711,6 @@ void test_string(TestFunction check, ExceptionTest check_exception) {
check(SV(R"({__"hello": "HELLO"___, __"world": "WORLD"___})"), SV("{::_^{}}"), input, 21);
check(SV(R"({#####"hello": "HELLO", #####"world": "WORLD"})"), SV("{::#>{}}"), input, 21);
- check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
@@ -788,7 +774,6 @@ void test_status(TestFunction check, ExceptionTest check_exception) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
diff --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.tests.h b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.tests.h
index a4d8c2d34c333f..e300b7713e48ce 100644
--- a/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.tests.h
+++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.tests.h
@@ -52,7 +52,6 @@ void test_char_default(TestFunction check, ExceptionTest check_exception) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -156,7 +155,7 @@ void test_char_string(TestFunction check, [[maybe_unused]] ExceptionTest check_e
check_exception("The format string contains an invalid escape sequence", SV("{:}<s}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<s}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<s}"), input);
+ check_exception("The type option contains an invalid value for a character formatting argument", SV("{::<s}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-s}"), input);
@@ -215,7 +214,7 @@ void test_char_escaped_string(TestFunction check, [[maybe_unused]] ExceptionTest
check_exception("The format string contains an invalid escape sequence", SV("{:}<?s}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<?s}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<?s}"), input);
+ check_exception("The format specifier should consume the input or end with a '}'", SV("{::<?s}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-?s}"), input);
@@ -289,7 +288,6 @@ void test_char_to_wchar(TestFunction check, ExceptionTest check_exception) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -405,7 +403,6 @@ void test_bool(TestFunction check, ExceptionTest check_exception) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -512,7 +509,6 @@ void test_bool_multiset(TestFunction check, ExceptionTest check_exception) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -621,7 +617,6 @@ void test_int(TestFunction check, ExceptionTest check_exception, auto&& input) {
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
- check_exception("The fill option contains an invalid value", SV("{::<}"), input);
// *** sign ***
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -730,7 +725,6 @@ void test_floating_point(TestFunction check, ExceptionTest check_exception, auto
check_exception("The format string contains an invalid escape sequence", SV(...
[truncated]
|
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.
Thanks for working on this. Since the patch is a draft I mainly glanced over it.
I'm curious, why did you add __has_range_underlying_spec_
instead of using __use_range_fill_
?
Because the tuple formatter like the range formatter prohibits ':' as a For example, |
I expected that motivation. I think it would be more future proof to handle tuple the same and get a slightly worse error message. If tuple gets an underlying format-spec we're less likely to reintroduce this bug. (I am investigating this feature for a future C++ proposal.) |
OK, will update the patch tmr. Looks like it would bump into
Do you have a timeline you would like to share? :) |
My time-line heavily depends on the available time I have ;-) Still I hope to get a paper done before the end of next month. |
Ping~ |
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.
Ping~
Sorry I didn't have time to look at this earlier.
Mainly looks good one small nit.
libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.tests.h
Outdated
Show resolved
Hide resolved
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.
One more change and then I'm happy with the patch.
An immediate colon signifeis that the range-format-spec contains only range-underlying-spec.
This patch allows this code to compile and run: