[libcxx-commits] [libcxx] [libc++][format] Don't treat a closing '}' as part of format-spec (PR #81305)

Po-yao Chang via libcxx-commits libcxx-commits at lists.llvm.org
Sat Feb 10 07:30:22 PST 2024


https://github.com/poyaoc97 updated https://github.com/llvm/llvm-project/pull/81305

>From 12dc222f05737d1950c50c4fa426011d39805acf Mon Sep 17 00:00:00 2001
From: Po-yao Chang <poyaoc97 at gmail.com>
Date: Sat, 10 Feb 2024 04:06:01 +0800
Subject: [PATCH] [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.
---
 libcxx/include/__format/format_functions.h                  | 4 ++--
 libcxx/include/__format/format_parse_context.h              | 6 +++++-
 libcxx/include/__format/parser_std_format_spec.h            | 2 +-
 .../utilities/format/format.tuple/format.functions.tests.h  | 1 +
 4 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__format/format_functions.h b/libcxx/include/__format/format_functions.h
index 3ee53539f4ee6c..ad0ec354686807 100644
--- a/libcxx/include/__format/format_functions.h
+++ b/libcxx/include/__format/format_functions.h
@@ -251,7 +251,7 @@ __handle_replacement_field(_Iterator __begin, _Iterator __end, _ParseCtx& __pars
   if (__r.__last == __end)
     std::__throw_format_error("The argument index should end with a ':' or a '}'");
 
-  bool __parse = *__r.__last == _CharT(':');
+  bool __parse = __parse_ctx.__should_parse() = *__r.__last == _CharT(':');
   switch (*__r.__last) {
   case _CharT(':'):
     // 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
     __arg_t __type = __ctx.arg(__r.__value);
     if (__type == __arg_t::__none)
       std::__throw_format_error("The argument index value is too large for the number of arguments supplied");
-    else if (__type == __arg_t::__handle)
+    else if (__parse && __type == __arg_t::__handle)
       __ctx.__handle(__r.__value).__parse(__parse_ctx);
     else if (__parse)
       __format::__compile_time_visit_format_arg(__parse_ctx, __ctx, __type);
diff --git a/libcxx/include/__format/format_parse_context.h b/libcxx/include/__format/format_parse_context.h
index aefcd5497f3b9b..74a0d0a6305c69 100644
--- a/libcxx/include/__format/format_parse_context.h
+++ b/libcxx/include/__format/format_parse_context.h
@@ -36,7 +36,8 @@ class _LIBCPP_TEMPLATE_VIS basic_format_parse_context {
         __end_(__fmt.end()),
         __indexing_(__unknown),
         __next_arg_id_(0),
-        __num_args_(__num_args) {}
+        __num_args_(__num_args),
+        __parse_(true) {}
 
   basic_format_parse_context(const basic_format_parse_context&)            = delete;
   basic_format_parse_context& operator=(const basic_format_parse_context&) = delete;
@@ -83,6 +84,8 @@ class _LIBCPP_TEMPLATE_VIS basic_format_parse_context {
       std::__throw_format_error("Argument index outside the valid range");
   }
 
+  _LIBCPP_HIDE_FROM_ABI constexpr bool& __should_parse() { return __parse_; }
+
 private:
   iterator __begin_;
   iterator __end_;
@@ -90,6 +93,7 @@ class _LIBCPP_TEMPLATE_VIS basic_format_parse_context {
   _Indexing __indexing_;
   size_t __next_arg_id_;
   size_t __num_args_;
+  bool __parse_;
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(basic_format_parse_context);
 
diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h
index cf8af87b212849..d42ce2dbb456aa 100644
--- a/libcxx/include/__format/parser_std_format_spec.h
+++ b/libcxx/include/__format/parser_std_format_spec.h
@@ -355,7 +355,7 @@ 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)
+    if (__begin == __end || !__ctx.__should_parse())
       return __begin;
 
     if (__parse_fill_align(__begin, __end, __fields.__use_range_fill_) && __begin == __end)
diff --git a/libcxx/test/std/utilities/format/format.tuple/format.functions.tests.h b/libcxx/test/std/utilities/format/format.tuple/format.functions.tests.h
index dd99d2070dbb8a..bcb2bc7ebe03b8 100644
--- a/libcxx/test/std/utilities/format/format.tuple/format.functions.tests.h
+++ b/libcxx/test/std/utilities/format/format.tuple/format.functions.tests.h
@@ -77,6 +77,7 @@ void test_tuple_or_pair_int_int(TestFunction check, ExceptionTest check_exceptio
 template <class CharT, class TestFunction, class ExceptionTest, class TupleOrPair>
 void test_tuple_or_pair_int_string(TestFunction check, ExceptionTest check_exception, TupleOrPair&& input) {
   check(SV("(42, \"hello\")"), SV("{}"), input);
+  check(SV("(42, \"hello\")^42"), SV("{}^42"), input);
 
   // *** align-fill & width ***
   check(SV("(42, \"hello\")     "), SV("{:18}"), input);



More information about the libcxx-commits mailing list