Skip to content

Commit b29301c

Browse files
authored
[libc++][format] Handle range-underlying-spec (#81914)
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"}); ```
1 parent 3dd6750 commit b29301c

File tree

8 files changed

+24
-76
lines changed

8 files changed

+24
-76
lines changed

libcxx/include/__format/parser_std_format_spec.h

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -355,10 +355,10 @@ class _LIBCPP_TEMPLATE_VIS __parser {
355355
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator __parse(_ParseContext& __ctx, __fields __fields) {
356356
auto __begin = __ctx.begin();
357357
auto __end = __ctx.end();
358-
if (__begin == __end || *__begin == _CharT('}'))
358+
if (__begin == __end || *__begin == _CharT('}') || (__fields.__use_range_fill_ && *__begin == _CharT(':')))
359359
return __begin;
360360

361-
if (__parse_fill_align(__begin, __end, __fields.__use_range_fill_) && __begin == __end)
361+
if (__parse_fill_align(__begin, __end) && __begin == __end)
362362
return __begin;
363363

364364
if (__fields.__sign_) {
@@ -574,12 +574,10 @@ class _LIBCPP_TEMPLATE_VIS __parser {
574574
return false;
575575
}
576576

577-
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_fill_character(_CharT __fill, bool __use_range_fill) {
577+
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_fill_character(_CharT __fill) {
578578
// The forbidden fill characters all code points formed from a single code unit, thus the
579579
// check can be omitted when more code units are used.
580-
if (__use_range_fill && (__fill == _CharT('{') || __fill == _CharT(':')))
581-
std::__throw_format_error("The fill option contains an invalid value");
582-
else if (__fill == _CharT('{'))
580+
if (__fill == _CharT('{'))
583581
std::__throw_format_error("The fill option contains an invalid value");
584582
}
585583

@@ -590,7 +588,7 @@ class _LIBCPP_TEMPLATE_VIS __parser {
590588
# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
591589
|| (same_as<_CharT, wchar_t> && sizeof(wchar_t) == 2)
592590
# endif
593-
_LIBCPP_HIDE_FROM_ABI constexpr bool __parse_fill_align(_Iterator& __begin, _Iterator __end, bool __use_range_fill) {
591+
_LIBCPP_HIDE_FROM_ABI constexpr bool __parse_fill_align(_Iterator& __begin, _Iterator __end) {
594592
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
595593
__begin != __end,
596594
"when called with an empty input the function will cause "
@@ -606,7 +604,7 @@ class _LIBCPP_TEMPLATE_VIS __parser {
606604
// The forbidden fill characters all are code points encoded
607605
// in one code unit, thus the check can be omitted when more
608606
// code units are used.
609-
__validate_fill_character(*__begin, __use_range_fill);
607+
__validate_fill_character(*__begin);
610608

611609
std::copy_n(__begin, __code_units, std::addressof(__fill_.__data[0]));
612610
__begin += __code_units + 1;
@@ -623,7 +621,7 @@ class _LIBCPP_TEMPLATE_VIS __parser {
623621
# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
624622
template <contiguous_iterator _Iterator>
625623
requires(same_as<_CharT, wchar_t> && sizeof(wchar_t) == 4)
626-
_LIBCPP_HIDE_FROM_ABI constexpr bool __parse_fill_align(_Iterator& __begin, _Iterator __end, bool __use_range_fill) {
624+
_LIBCPP_HIDE_FROM_ABI constexpr bool __parse_fill_align(_Iterator& __begin, _Iterator __end) {
627625
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
628626
__begin != __end,
629627
"when called with an empty input the function will cause "
@@ -632,7 +630,7 @@ class _LIBCPP_TEMPLATE_VIS __parser {
632630
if (!__unicode::__is_scalar_value(*__begin))
633631
std::__throw_format_error("The fill option contains an invalid value");
634632

635-
__validate_fill_character(*__begin, __use_range_fill);
633+
__validate_fill_character(*__begin);
636634

637635
__fill_.__data[0] = *__begin;
638636
__begin += 2;
@@ -651,14 +649,14 @@ class _LIBCPP_TEMPLATE_VIS __parser {
651649
# else // _LIBCPP_HAS_NO_UNICODE
652650
// range-fill and tuple-fill are identical
653651
template <contiguous_iterator _Iterator>
654-
_LIBCPP_HIDE_FROM_ABI constexpr bool __parse_fill_align(_Iterator& __begin, _Iterator __end, bool __use_range_fill) {
652+
_LIBCPP_HIDE_FROM_ABI constexpr bool __parse_fill_align(_Iterator& __begin, _Iterator __end) {
655653
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
656654
__begin != __end,
657655
"when called with an empty input the function will cause "
658656
"undefined behavior by evaluating data not in the input");
659657
if (__begin + 1 != __end) {
660658
if (__parse_alignment(*(__begin + 1))) {
661-
__validate_fill_character(*__begin, __use_range_fill);
659+
__validate_fill_character(*__begin);
662660

663661
__fill_.__data[0] = *__begin;
664662
__begin += 2;

libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ void test_char_default(TestFunction check, ExceptionTest check_exception, auto&&
3535

3636
// when one is present there is no escaping,
3737
check(SV("[H, e, l, l, o]"), SV("{::}"), input);
38+
check(SV("[H, e, l, l, o]"), SV("{::<}"), input);
3839
// unless forced by the type specifier.
3940
check(SV("['H', 'e', 'l', 'l', 'o']"), SV("{::?}"), input);
41+
check(SV("['H', 'e', 'l', 'l', 'o']"), SV("{::<?}"), input);
4042

4143
// ***** underlying has no format-spec
4244

@@ -53,7 +55,6 @@ void test_char_default(TestFunction check, ExceptionTest check_exception, auto&&
5355

5456
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
5557
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
56-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
5758

5859
// *** sign ***
5960
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -153,7 +154,6 @@ void test_char_string(TestFunction check, ExceptionTest check_exception, auto&&
153154

154155
check_exception("The format string contains an invalid escape sequence", SV("{:}<s}"), input);
155156
check_exception("The fill option contains an invalid value", SV("{:{<s}"), input);
156-
check_exception("The fill option contains an invalid value", SV("{::<s}"), input);
157157

158158
// *** sign ***
159159
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-s}"), input);
@@ -177,6 +177,7 @@ void test_char_string(TestFunction check, ExceptionTest check_exception, auto&&
177177

178178
// *** type ***
179179
check_exception("Type m requires a pair or a tuple with two elements", SV("{:m}"), input);
180+
check_exception("The type option contains an invalid value for a character formatting argument", SV("{::<s}"), input);
180181

181182
// ***** Only underlying has a format-spec
182183
check_exception("Type s and an underlying format specification can't be used together", SV("{:s:}"), input);
@@ -206,7 +207,7 @@ void test_char_escaped_string(TestFunction check, ExceptionTest check_exception,
206207

207208
check_exception("The format string contains an invalid escape sequence", SV("{:}<?s}"), input);
208209
check_exception("The fill option contains an invalid value", SV("{:{<?s}"), input);
209-
check_exception("The fill option contains an invalid value", SV("{::<?s}"), input);
210+
check_exception("The format specifier should consume the input or end with a '}'", SV("{::<?s}"), input);
210211

211212
// *** sign ***
212213
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-?s}"), input);
@@ -324,7 +325,6 @@ void test_bool(TestFunction check, ExceptionTest check_exception, auto&& input)
324325

325326
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
326327
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
327-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
328328

329329
// *** sign ***
330330
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -440,7 +440,6 @@ void test_int(TestFunction check, ExceptionTest check_exception, auto&& input) {
440440

441441
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
442442
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
443-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
444443

445444
// *** sign ***
446445
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -550,7 +549,6 @@ void test_floating_point(TestFunction check, ExceptionTest check_exception, auto
550549

551550
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
552551
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
553-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
554552

555553
// *** sign ***
556554
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -688,7 +686,6 @@ void test_pointer(TestFunction check, ExceptionTest check_exception, auto&& inpu
688686

689687
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
690688
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
691-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
692689

693690
// *** sign ***
694691
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -798,7 +795,6 @@ void test_string(TestFunction check, ExceptionTest check_exception, auto&& input
798795

799796
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
800797
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
801-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
802798

803799
// *** sign ***
804800
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
@@ -910,7 +906,6 @@ void test_status(TestFunction check, ExceptionTest check_exception, auto&& input
910906

911907
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
912908
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
913-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
914909

915910
// *** sign ***
916911
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);

libcxx/test/std/containers/sequences/vector.bool/vector.bool.fmt/format.functions.tests.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ void format_test_vector_bool(TestFunction check, ExceptionTest check_exception,
3434

3535
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
3636
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
37-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
3837

3938
// *** sign ***
4039
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);

libcxx/test/std/utilities/format/format.range/format.range.fmtmap/format.functions.tests.h

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ void test_char(TestFunction check, ExceptionTest check_exception) {
4545

4646
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
4747
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
48-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
4948

5049
// *** sign ***
5150
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) {
8786
check(SV("{__'a': 'A'___, __'b': 'B'___, __'c': 'C'___}"), SV("{::_^{}}"), input, 13);
8887
check(SV("{#####'a': 'A', #####'b': 'B', #####'c': 'C'}"), SV("{::#>{}}"), input, 13);
8988

90-
check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
9189
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
9290
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
9391

@@ -157,7 +155,6 @@ void test_char_to_wchar(TestFunction check, ExceptionTest check_exception) {
157155

158156
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
159157
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
160-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
161158

162159
// *** sign ***
163160
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) {
198195
check(SV("{__'a': 'A'___, __'b': 'B'___, __'c': 'C'___}"), SV("{::_^{}}"), input, 13);
199196
check(SV("{#####'a': 'A', #####'b': 'B', #####'c': 'C'}"), SV("{::#>{}}"), input, 13);
200197

201-
check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
202198
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
203199
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
204200

@@ -267,7 +263,6 @@ void test_bool(TestFunction check, ExceptionTest check_exception) {
267263

268264
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
269265
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
270-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
271266

272267
// *** sign ***
273268
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) {
308303
check(SV("{_false: 0_, _true: 42_, _true: 1__}"), SV("{::_^{}}"), input, 10);
309304
check(SV("{##false: 0, ##true: 42, ###true: 1}"), SV("{::#>{}}"), input, 10);
310305

311-
check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
312306
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
313307
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
314308

@@ -369,7 +363,6 @@ void test_int(TestFunction check, ExceptionTest check_exception, auto&& input) {
369363

370364
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
371365
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
372-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
373366

374367
// *** sign ***
375368
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) {
410403
check(SV("{_-42: 42__, __1: -1___, _42: -42__}"), SV("{::_^{}}"), input, 10);
411404
check(SV("{###-42: 42, #####1: -1, ###42: -42}"), SV("{::#>{}}"), input, 10);
412405

413-
check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
414406
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
415407
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
416408

@@ -478,7 +470,6 @@ void test_floating_point(TestFunction check, ExceptionTest check_exception) {
478470

479471
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
480472
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
481-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
482473

483474
// *** sign ***
484475
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) {
519510
check(SV("{_-42: 42__, __1: -1___}"), SV("{::_^{}}"), input, 10);
520511
check(SV("{###-42: 42, #####1: -1}"), SV("{::#>{}}"), input, 10);
521512

522-
check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
523513
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
524514
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
525515

@@ -582,7 +572,6 @@ void test_pointer(TestFunction check, ExceptionTest check_exception) {
582572

583573
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
584574
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
585-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
586575

587576
// *** sign ***
588577
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) {
621610
check(SV("{__0x0: 0x0___}"), SV("{::_^{}}"), input, 13);
622611
check(SV("{#####0x0: 0x0}"), SV("{::#>{}}"), input, 13);
623612

624-
check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
625613
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
626614
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
627615

@@ -685,7 +673,6 @@ void test_string(TestFunction check, ExceptionTest check_exception) {
685673

686674
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
687675
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
688-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
689676

690677
// *** sign ***
691678
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) {
724711
check(SV(R"({__"hello": "HELLO"___, __"world": "WORLD"___})"), SV("{::_^{}}"), input, 21);
725712
check(SV(R"({#####"hello": "HELLO", #####"world": "WORLD"})"), SV("{::#>{}}"), input, 21);
726713

727-
check_exception("The fill option contains an invalid value", SV("{:::<}"), input);
728714
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
729715
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
730716

@@ -788,7 +774,6 @@ void test_status(TestFunction check, ExceptionTest check_exception) {
788774

789775
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
790776
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
791-
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
792777

793778
// *** sign ***
794779
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);

0 commit comments

Comments
 (0)