Skip to content

Commit 14f1fb4

Browse files
committed
[libc++][format] Don't treat a closing '}' as part of format-spec
This allows: ``` std::println("{}>42", std::thread::id{}); std::println("{}>42", std::span<int>{}); std::println("{}>42", std::pair{42, "Hello"sv}); ``` to compile and run.
1 parent 2e4d276 commit 14f1fb4

File tree

4 files changed

+9
-4
lines changed

4 files changed

+9
-4
lines changed

libcxx/include/__format/format_functions.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ __handle_replacement_field(_Iterator __begin, _Iterator __end, _ParseCtx& __pars
251251
if (__r.__last == __end)
252252
std::__throw_format_error("The argument index should end with a ':' or a '}'");
253253

254-
bool __parse = *__r.__last == _CharT(':');
254+
bool __parse = __parse_ctx.__should_parse() = *__r.__last == _CharT(':');
255255
switch (*__r.__last) {
256256
case _CharT(':'):
257257
// The arg-id has a format-specifier, advance the input to the format-spec.
@@ -269,7 +269,7 @@ __handle_replacement_field(_Iterator __begin, _Iterator __end, _ParseCtx& __pars
269269
__arg_t __type = __ctx.arg(__r.__value);
270270
if (__type == __arg_t::__none)
271271
std::__throw_format_error("The argument index value is too large for the number of arguments supplied");
272-
else if (__type == __arg_t::__handle)
272+
else if (__parse && __type == __arg_t::__handle)
273273
__ctx.__handle(__r.__value).__parse(__parse_ctx);
274274
else if (__parse)
275275
__format::__compile_time_visit_format_arg(__parse_ctx, __ctx, __type);

libcxx/include/__format/format_parse_context.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ class _LIBCPP_TEMPLATE_VIS basic_format_parse_context {
3636
__end_(__fmt.end()),
3737
__indexing_(__unknown),
3838
__next_arg_id_(0),
39-
__num_args_(__num_args) {}
39+
__num_args_(__num_args),
40+
__parse(true) {}
4041

4142
basic_format_parse_context(const basic_format_parse_context&) = delete;
4243
basic_format_parse_context& operator=(const basic_format_parse_context&) = delete;
@@ -83,13 +84,16 @@ class _LIBCPP_TEMPLATE_VIS basic_format_parse_context {
8384
std::__throw_format_error("Argument index outside the valid range");
8485
}
8586

87+
_LIBCPP_HIDE_FROM_ABI constexpr bool& __should_parse() { return __parse; }
88+
8689
private:
8790
iterator __begin_;
8891
iterator __end_;
8992
enum _Indexing { __unknown, __manual, __automatic };
9093
_Indexing __indexing_;
9194
size_t __next_arg_id_;
9295
size_t __num_args_;
96+
bool __parse;
9397
};
9498
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(basic_format_parse_context);
9599

libcxx/include/__format/parser_std_format_spec.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ 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)
358+
if (__begin == __end || !__ctx.__should_parse())
359359
return __begin;
360360

361361
if (__parse_fill_align(__begin, __end, __fields.__use_range_fill_) && __begin == __end)

libcxx/test/std/utilities/format/format.tuple/format.functions.tests.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ void test_tuple_or_pair_int_int(TestFunction check, ExceptionTest check_exceptio
7777
template <class CharT, class TestFunction, class ExceptionTest, class TupleOrPair>
7878
void test_tuple_or_pair_int_string(TestFunction check, ExceptionTest check_exception, TupleOrPair&& input) {
7979
check(SV("(42, \"hello\")"), SV("{}"), input);
80+
check(SV("(42, \"hello\")^42"), SV("{}^42"), input);
8081

8182
// *** align-fill & width ***
8283
check(SV("(42, \"hello\") "), SV("{:18}"), input);

0 commit comments

Comments
 (0)