public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [committed 1/2] libstdc++: Implement P2905R2 "Runtime format strings" for C++20
@ 2024-01-08  1:19 Jonathan Wakely
  2024-01-08  1:19 ` [committed 2/2] libstdc++: Implement P2918R0 "Runtime format strings II" for C++26 Jonathan Wakely
  0 siblings, 1 reply; 7+ messages in thread
From: Jonathan Wakely @ 2024-01-08  1:19 UTC (permalink / raw)
  To: libstdc++, gcc-patches

Tested x86_64-linux and aarch64-linux. Pushed to trunk.

-- >8 --

This change makes std::make_format_args refuse to create dangling
references to temporaries. This makes the std::vformat API safer. This
was approved in Kona 2023 as a DR for C++20 so the change is implemented
unconditionally.

libstdc++-v3/ChangeLog:

	* include/bits/chrono_io.h (__formatter_chrono): Always use
	lvalue arguments to make_format_args.
	* include/std/format (make_format_args): Change parameter pack
	from forwarding references to lvalue references. Remove use of
	remove_reference_t which is now unnecessary.
	(format_to, formatted_size): Remove incorrect forwarding of
	arguments.
	* include/std/ostream (print): Remove forwarding of arguments.
	* include/std/print (print): Likewise.
	* testsuite/20_util/duration/io.cc: Use lvalues as arguments to
	make_format_args.
	* testsuite/std/format/arguments/args.cc: Likewise.
	* testsuite/std/format/arguments/lwg3810.cc: Likewise.
	* testsuite/std/format/functions/format.cc: Likewise.
	* testsuite/std/format/functions/vformat_to.cc: Likewise.
	* testsuite/std/format/string.cc: Likewise.
	* testsuite/std/time/day/io.cc: Likewise.
	* testsuite/std/time/month/io.cc: Likewise.
	* testsuite/std/time/weekday/io.cc: Likewise.
	* testsuite/std/time/year/io.cc: Likewise.
	* testsuite/std/time/year_month_day/io.cc: Likewise.
	* testsuite/std/format/arguments/args_neg.cc: New test.
---
 libstdc++-v3/include/bits/chrono_io.h         | 15 ++++++----
 libstdc++-v3/include/std/format               | 30 +++++++++----------
 libstdc++-v3/include/std/ostream              |  2 +-
 libstdc++-v3/include/std/print                |  2 +-
 libstdc++-v3/testsuite/20_util/duration/io.cc |  3 +-
 .../testsuite/std/format/arguments/args.cc    | 26 ++++++++++++----
 .../std/format/arguments/args_neg.cc          | 12 ++++++++
 .../testsuite/std/format/arguments/lwg3810.cc |  8 +++--
 .../testsuite/std/format/functions/format.cc  |  6 ++--
 .../std/format/functions/vformat_to.cc        |  9 ++++--
 libstdc++-v3/testsuite/std/format/string.cc   |  7 +++--
 libstdc++-v3/testsuite/std/time/day/io.cc     |  4 +--
 libstdc++-v3/testsuite/std/time/month/io.cc   |  4 +--
 libstdc++-v3/testsuite/std/time/weekday/io.cc |  4 +--
 libstdc++-v3/testsuite/std/time/year/io.cc    |  4 +--
 .../testsuite/std/time/year_month_day/io.cc   |  4 +--
 16 files changed, 93 insertions(+), 47 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/std/format/arguments/args_neg.cc

diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
index c30451651ea..ec2ae9d53cc 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -2273,7 +2273,8 @@ namespace __detail
       _Str __s = _GLIBCXX_WIDEN("{:02d} is not a valid day");
       if (__d.ok())
 	__s = __s.substr(0, 6);
-      __os << std::vformat(__s, make_format_args<_Ctx>((unsigned)__d));
+      auto __u = (unsigned)__d;
+      __os << std::vformat(__s, make_format_args<_Ctx>(__u));
       return __os;
     }
 
@@ -2302,8 +2303,10 @@ namespace __detail
 	__os << std::vformat(__os.getloc(), __s.substr(0, 6),
 			     make_format_args<_Ctx>(__m));
       else
-	__os << std::vformat(__s.substr(6),
-			     make_format_args<_Ctx>((unsigned)__m));
+	{
+	  auto __u = (unsigned)__m;
+	  __os << std::vformat(__s.substr(6), make_format_args<_Ctx>(__u));
+	}
       return __os;
     }
 
@@ -2364,8 +2367,10 @@ namespace __detail
 	__os << std::vformat(__os.getloc(), __s.substr(0, 6),
 			     make_format_args<_Ctx>(__wd));
       else
-	__os << std::vformat(__s.substr(6),
-			     make_format_args<_Ctx>(__wd.c_encoding()));
+	{
+	  auto __c = __wd.c_encoding();
+	  __os << std::vformat(__s.substr(6), make_format_args<_Ctx>(__c));
+	}
       return __os;
     }
 
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index 0d9f70ee555..160efa5155c 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -3413,7 +3413,7 @@ namespace __format
 
       template<typename _Ctx, typename... _Argz>
 	friend auto
-	make_format_args(_Argz&&...) noexcept;
+	make_format_args(_Argz&...) noexcept;
 
       template<typename _Visitor, typename _Ctx>
 	friend decltype(auto)
@@ -3583,7 +3583,7 @@ namespace __format
 
       template<typename _Ctx, typename... _Args>
 	friend auto
-	make_format_args(_Args&&...) noexcept;
+	make_format_args(_Args&...) noexcept;
 
       // An array of _Arg_t enums corresponding to _Args...
       template<typename... _Args>
@@ -3621,7 +3621,7 @@ namespace __format
 
   template<typename _Context, typename... _Args>
     auto
-    make_format_args(_Args&&... __fmt_args) noexcept;
+    make_format_args(_Args&... __fmt_args) noexcept;
 
   // An array of type-erased formatting arguments.
   template<typename _Context, typename... _Args>
@@ -3637,7 +3637,7 @@ namespace __format
 #else
 	std::
 #endif
-	make_format_args(_Argz&&...) noexcept;
+	make_format_args(_Argz&...) noexcept;
 
       // For a sufficiently small number of arguments we only store values.
       // basic_format_args can get the types from the _Args pack.
@@ -3711,11 +3711,11 @@ namespace __format
   template<typename _Context = format_context, typename... _Args>
     [[nodiscard,__gnu__::__always_inline__]]
     inline auto
-    make_format_args(_Args&&... __fmt_args) noexcept
+    make_format_args(_Args&... __fmt_args) noexcept
     {
       using _Fmt_arg = basic_format_arg<_Context>;
       using _Store = __format::_Arg_store<_Context, typename _Fmt_arg::template
-		     _Normalize<remove_reference_t<_Args>>...>;
+		     _Normalize<_Args>...>;
       return _Store(__fmt_args...);
     }
 
@@ -3724,7 +3724,7 @@ namespace __format
   template<typename... _Args>
     [[nodiscard,__gnu__::__always_inline__]]
     inline auto
-    make_wformat_args(_Args&&... __args) noexcept
+    make_wformat_args(_Args&... __args) noexcept
     { return std::make_format_args<wformat_context>(__args...); }
 #endif
 
@@ -4240,7 +4240,7 @@ namespace __format
     format_to(_Out __out, format_string<_Args...> __fmt, _Args&&... __args)
     {
       return std::vformat_to(std::move(__out), __fmt.get(),
-			     std::make_format_args(std::forward<_Args>(__args)...));
+			     std::make_format_args(__args...));
     }
 
 #ifdef _GLIBCXX_USE_WCHAR_T
@@ -4250,7 +4250,7 @@ namespace __format
     format_to(_Out __out, wformat_string<_Args...> __fmt, _Args&&... __args)
     {
       return std::vformat_to(std::move(__out), __fmt.get(),
-			     std::make_wformat_args(std::forward<_Args>(__args)...));
+			     std::make_wformat_args(__args...));
     }
 #endif
 
@@ -4261,7 +4261,7 @@ namespace __format
 	      _Args&&... __args)
     {
       return std::vformat_to(std::move(__out), __loc, __fmt.get(),
-			     std::make_format_args(std::forward<_Args>(__args)...));
+			     std::make_format_args(__args...));
     }
 
 #ifdef _GLIBCXX_USE_WCHAR_T
@@ -4272,7 +4272,7 @@ namespace __format
 	      _Args&&... __args)
     {
       return std::vformat_to(std::move(__out), __loc, __fmt.get(),
-			     std::make_wformat_args(std::forward<_Args>(__args)...));
+			     std::make_wformat_args(__args...));
     }
 #endif
 
@@ -4379,7 +4379,7 @@ namespace __format
     {
       __format::_Counting_sink<char> __buf;
       std::vformat_to(__buf.out(), __fmt.get(),
-		      std::make_format_args(std::forward<_Args>(__args)...));
+		      std::make_format_args(__args...));
       return __buf.count();
     }
 
@@ -4391,7 +4391,7 @@ namespace __format
     {
       __format::_Counting_sink<wchar_t> __buf;
       std::vformat_to(__buf.out(), __fmt.get(),
-		      std::make_wformat_args(std::forward<_Args>(__args)...));
+		      std::make_wformat_args(__args...));
       return __buf.count();
     }
 #endif
@@ -4404,7 +4404,7 @@ namespace __format
     {
       __format::_Counting_sink<char> __buf;
       std::vformat_to(__buf.out(), __loc, __fmt.get(),
-		      std::make_format_args(std::forward<_Args>(__args)...));
+		      std::make_format_args(__args...));
       return __buf.count();
     }
 
@@ -4417,7 +4417,7 @@ namespace __format
     {
       __format::_Counting_sink<wchar_t> __buf;
       std::vformat_to(__buf.out(), __loc, __fmt.get(),
-		      std::make_wformat_args(std::forward<_Args>(__args)...));
+		      std::make_wformat_args(__args...));
       return __buf.count();
     }
 #endif
diff --git a/libstdc++-v3/include/std/ostream b/libstdc++-v3/include/std/ostream
index 4dff0cf645d..7d501d67489 100644
--- a/libstdc++-v3/include/std/ostream
+++ b/libstdc++-v3/include/std/ostream
@@ -981,7 +981,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline void
     print(ostream& __os, format_string<_Args...> __fmt, _Args&&... __args)
     {
-      auto __fmtargs = std::make_format_args(std::forward<_Args>(__args)...);
+      auto __fmtargs = std::make_format_args(__args...);
       if constexpr (__unicode::__literal_encoding_is_utf8())
 	std::vprint_unicode(__os, __fmt.get(), __fmtargs);
       else
diff --git a/libstdc++-v3/include/std/print b/libstdc++-v3/include/std/print
index f44256c5dca..492f333dfa6 100644
--- a/libstdc++-v3/include/std/print
+++ b/libstdc++-v3/include/std/print
@@ -103,7 +103,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline void
     print(FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args)
     {
-      auto __fmtargs = std::make_format_args(std::forward<_Args>(__args)...);
+      auto __fmtargs = std::make_format_args(__args...);
       if constexpr (__unicode::__literal_encoding_is_utf8())
 	std::vprint_unicode(__stream, __fmt.get(), __fmtargs);
       else
diff --git a/libstdc++-v3/testsuite/20_util/duration/io.cc b/libstdc++-v3/testsuite/20_util/duration/io.cc
index 0582c0075a5..e141baf42dc 100644
--- a/libstdc++-v3/testsuite/20_util/duration/io.cc
+++ b/libstdc++-v3/testsuite/20_util/duration/io.cc
@@ -82,7 +82,8 @@ test_format()
     char fmt[] = { '{', ':', '%', c, '}' };
     try
     {
-      (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(1s));
+      auto s = 1s;
+      (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(s));
       // The call above should throw for any conversion-spec not in my_specs:
       VERIFY(my_specs.find(c) != my_specs.npos);
     }
diff --git a/libstdc++-v3/testsuite/std/format/arguments/args.cc b/libstdc++-v3/testsuite/std/format/arguments/args.cc
index a45f3fb24df..eba129ff894 100644
--- a/libstdc++-v3/testsuite/std/format/arguments/args.cc
+++ b/libstdc++-v3/testsuite/std/format/arguments/args.cc
@@ -46,7 +46,12 @@ struct std::formatter<E> : std::formatter<int>
 void
 test_args()
 {
-  auto store = std::make_format_args(false, 1, '2', 3.4);
+  bool b = false;
+  int i = 1;
+  char c = '2';
+  double d = 3.4;
+
+  auto store = std::make_format_args(b, i, c, d);
   std::format_args args = store;
   VERIFY(equals(args.get(0), false));
   VERIFY(equals(args.get(1), 1));
@@ -54,7 +59,11 @@ test_args()
   VERIFY(equals(args.get(3), 3.4));
   VERIFY(!args.get(4));
 
-  auto cstore = std::make_format_args<std::format_context>(5L, 6ULL, 7.8f);
+  long l = 5L;
+  unsigned long long ull = 6ULL;
+  float f = 7.8f;
+
+  auto cstore = std::make_format_args<std::format_context>(l, ull, f);
   std::format_args cargs = cstore;
   if constexpr (sizeof(long) == sizeof(int))
     VERIFY(equals(cargs.get(0), 5));
@@ -64,14 +73,17 @@ test_args()
   VERIFY(equals(cargs.get(2), 7.8f));
   VERIFY(!cargs.get(3));
 
-  VERIFY(equals(std::format_args(std::make_format_args(std::string("tenfour"))).get(0), std::string_view("tenfour")));
+  std::string s = "tenfour";
+  VERIFY(equals(std::format_args(std::make_format_args(s)).get(0), std::string_view("tenfour")));
 
+  char nine = '9';
+  wchar_t ten = L'X';
   // This needs to be on the stack so that testing pointer equality works.
   wchar_t eleven[] = L"eleven";
-  // This needs to be on the stack so that the wstring_view doesn't dangle.
+  long double twelve13 = 12.13L;
   std::wstring tenfour = L"tenfour";
 
-  auto wstore = std::make_wformat_args('9', L'X', eleven, 12.13L, tenfour);
+  auto wstore = std::make_wformat_args(nine, ten, eleven, twelve13, tenfour);
   std::wformat_args wargs = wstore;
   VERIFY(equals(wargs.get(0), static_cast<wchar_t>('9')));
   VERIFY(equals(wargs.get(1), L'X'));
@@ -80,7 +92,9 @@ test_args()
   VERIFY(equals(wargs.get(4), std::wstring_view(tenfour)));
   VERIFY(!wargs.get(5));
 
-  auto another_store = std::make_format_args(nullptr, E::ByGum);
+  std::nullptr_t null;
+  E eebygum = E::ByGum;
+  auto another_store = std::make_format_args(null, eebygum);
   args = another_store;
   VERIFY(equals(args.get(0), static_cast<const void*>(nullptr)));
   using handle = std::basic_format_arg<std::format_context>::handle;
diff --git a/libstdc++-v3/testsuite/std/format/arguments/args_neg.cc b/libstdc++-v3/testsuite/std/format/arguments/args_neg.cc
new file mode 100644
index 00000000000..16ac3040146
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/format/arguments/args_neg.cc
@@ -0,0 +1,12 @@
+// { dg-do compile { target c++20 } }
+
+// P2905R2 Runtime format strings
+
+#include <format>
+
+std::string rval() { return "path/etic/experience"; }
+
+void f()
+{
+  (void)std::make_format_args(rval()); // { dg-error "cannot bind non-const lvalue reference" }
+}
diff --git a/libstdc++-v3/testsuite/std/format/arguments/lwg3810.cc b/libstdc++-v3/testsuite/std/format/arguments/lwg3810.cc
index f89f40203cb..8a9f9edd578 100644
--- a/libstdc++-v3/testsuite/std/format/arguments/lwg3810.cc
+++ b/libstdc++-v3/testsuite/std/format/arguments/lwg3810.cc
@@ -4,7 +4,11 @@
 
 #include <format>
 
-auto args_store = std::make_format_args(1,2,3);
+int x = 1;
+long y = 2;
+short z = 3;
+
+auto args_store = std::make_format_args(x, y, z);
 std::basic_format_args args = args_store;
 static_assert(std::is_same_v<decltype(args), std::format_args>);
 
@@ -20,5 +24,5 @@ test_ctad()
   using SomeContext = std::wformat_context;
 
   // foo(make_format_args<SomeContext>(...)); // won't work
-  foo(basic_format_args(make_format_args<SomeContext>(1, 2, 3))); // should work
+  foo(basic_format_args(make_format_args<SomeContext>(x, y, z))); // should work
 }
diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc
index 6320e20170c..63702edbd42 100644
--- a/libstdc++-v3/testsuite/std/format/functions/format.cc
+++ b/libstdc++-v3/testsuite/std/format/functions/format.cc
@@ -267,14 +267,16 @@ test_width()
   }
 
   try {
-    auto args = std::make_format_args(false, true);
+    bool no = false, yes = true;
+    auto args = std::make_format_args(no, yes);
     s = std::vformat("DR 3720: restrict type of width arg-id {0:{1}}", args);
     VERIFY(false);
   } catch (const std::format_error&) {
   }
 
   try {
-    auto args = std::make_format_args('?', '!');
+    char wat = '?', bang = '!';
+    auto args = std::make_format_args(wat, bang);
     s = std::vformat("DR 3720: restrict type of width arg-id {0:{1}}", args);
     VERIFY(false);
   } catch (const std::format_error&) {
diff --git a/libstdc++-v3/testsuite/std/format/functions/vformat_to.cc b/libstdc++-v3/testsuite/std/format/functions/vformat_to.cc
index fe0367f7496..2be3d6f6761 100644
--- a/libstdc++-v3/testsuite/std/format/functions/vformat_to.cc
+++ b/libstdc++-v3/testsuite/std/format/functions/vformat_to.cc
@@ -29,17 +29,22 @@ private:
 void
 test_move_only()
 {
+  const char arg1[] = "matte";
+  int arg2 = '!';
+
   std::string str;
   move_only_iterator<char> mo(std::back_inserter(str));
   auto res = std::vformat_to(std::move(mo), "for{:.3} that{:c}",
-			     std::make_format_args("matte", (int)'!'));
+			     std::make_format_args(arg1, arg2));
   static_assert(std::is_same_v<decltype(res), decltype(mo)>);
   VERIFY( str == "format that!" );
 
+  const wchar_t warg1[] = L"matte";
+  long warg2 = L'!';
   std::wstring wstr;
   move_only_iterator<wchar_t> wmo(std::back_inserter(wstr));
   auto wres = std::vformat_to(std::move(wmo), L"for{:.3} that{:c}",
-			      std::make_wformat_args(L"matte", (long)L'!'));
+			      std::make_wformat_args(warg1, warg2));
   static_assert(std::is_same_v<decltype(wres), decltype(wmo)>);
   VERIFY( wstr == L"format that!" );
 }
diff --git a/libstdc++-v3/testsuite/std/format/string.cc b/libstdc++-v3/testsuite/std/format/string.cc
index 40aaebae04e..ddb3c5625cd 100644
--- a/libstdc++-v3/testsuite/std/format/string.cc
+++ b/libstdc++-v3/testsuite/std/format/string.cc
@@ -149,8 +149,9 @@ void
 test_pr110862()
 {
   try {
+    int i = 1;
     // PR libstdc++/110862 out-of-bounds read on invalid format string
-    (void) std::vformat("{0:{0}", std::make_format_args(1));
+    (void) std::vformat("{0:{0}", std::make_format_args(i));
     VERIFY( false );
   } catch (const std::format_error& e) {
     std::string_view what = e.what();
@@ -162,9 +163,11 @@ void
 test_pr110974()
 {
   try {
+    double d = 1.0;
+    int i = 1;
     // PR libstdc++/110974 out of bounds read on invalid format string "{:{}."
     std::string_view fmt{"{:{}.0", 5}; // "0" is not part of the format string.
-    (void) std::vformat(fmt, std::make_format_args(1.0, 1));
+    (void) std::vformat(fmt, std::make_format_args(d, i));
     VERIFY( false );
   } catch (const std::format_error& e) {
     std::string_view what = e.what();
diff --git a/libstdc++-v3/testsuite/std/time/day/io.cc b/libstdc++-v3/testsuite/std/time/day/io.cc
index 3454657f69f..36ce7ec7d17 100644
--- a/libstdc++-v3/testsuite/std/time/day/io.cc
+++ b/libstdc++-v3/testsuite/std/time/day/io.cc
@@ -51,8 +51,8 @@ test_format()
     char fmt[] = { '{', ':', '%', c, '}' };
     try
     {
-      (void) std::vformat(std::string_view(fmt, 5),
-			  std::make_format_args(day(1)));
+      day d(1);
+      (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(d));
       // The call above should throw for any conversion-spec not in my_specs:
       VERIFY(my_specs.find(c) != my_specs.npos);
     }
diff --git a/libstdc++-v3/testsuite/std/time/month/io.cc b/libstdc++-v3/testsuite/std/time/month/io.cc
index 7e80a53bbb6..99ec0737305 100644
--- a/libstdc++-v3/testsuite/std/time/month/io.cc
+++ b/libstdc++-v3/testsuite/std/time/month/io.cc
@@ -74,8 +74,8 @@ test_format()
     char fmt[] = { '{', ':', '%', c, '}' };
     try
     {
-      (void) std::vformat(std::string_view(fmt, 5),
-			  std::make_format_args(month(1)));
+      month m(1);
+      (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(m));
       // The call above should throw for any conversion-spec not in my_specs:
       VERIFY(my_specs.find(c) != my_specs.npos);
     }
diff --git a/libstdc++-v3/testsuite/std/time/weekday/io.cc b/libstdc++-v3/testsuite/std/time/weekday/io.cc
index 5ed90282f95..a56cdaef88c 100644
--- a/libstdc++-v3/testsuite/std/time/weekday/io.cc
+++ b/libstdc++-v3/testsuite/std/time/weekday/io.cc
@@ -77,8 +77,8 @@ test_format()
     char fmt[] = { '{', ':', '%', c, '}' };
     try
     {
-      (void) std::vformat(std::string_view(fmt, 5),
-			  std::make_format_args(weekday(1)));
+      weekday wd(1);
+      (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(wd));
       // The call above should throw for any conversion-spec not in my_specs:
       VERIFY(my_specs.find(c) != my_specs.npos);
     }
diff --git a/libstdc++-v3/testsuite/std/time/year/io.cc b/libstdc++-v3/testsuite/std/time/year/io.cc
index a6683ae20df..bcaa57faeb7 100644
--- a/libstdc++-v3/testsuite/std/time/year/io.cc
+++ b/libstdc++-v3/testsuite/std/time/year/io.cc
@@ -68,8 +68,8 @@ test_format()
     char fmt[] = { '{', ':', '%', c, '}' };
     try
     {
-      (void) std::vformat(std::string_view(fmt, 5),
-			  std::make_format_args(year(2022)));
+      year y = 2022y;
+      (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(y));
       // The call above should throw for any conversion-spec not in my_specs:
       VERIFY(my_specs.find(c) != my_specs.npos);
     }
diff --git a/libstdc++-v3/testsuite/std/time/year_month_day/io.cc b/libstdc++-v3/testsuite/std/time/year_month_day/io.cc
index 15c5a9f77be..cb82ef3b612 100644
--- a/libstdc++-v3/testsuite/std/time/year_month_day/io.cc
+++ b/libstdc++-v3/testsuite/std/time/year_month_day/io.cc
@@ -97,8 +97,8 @@ test_format()
     char fmt[] = { '{', ':', '%', c, '}' };
     try
     {
-      (void) std::vformat(std::string_view(fmt, 5),
-			  std::make_format_args(2022y/December/19));
+      year_month_day ymd = 2022y/December/19;
+      (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(ymd));
       // The call above should throw for any conversion-spec not in my_specs:
       VERIFY(my_specs.find(c) != my_specs.npos);
     }
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [committed 2/2] libstdc++: Implement P2918R0 "Runtime format strings II" for C++26
  2024-01-08  1:19 [committed 1/2] libstdc++: Implement P2905R2 "Runtime format strings" for C++20 Jonathan Wakely
@ 2024-01-08  1:19 ` Jonathan Wakely
  2024-01-10 18:32   ` Daniel Krügler
  0 siblings, 1 reply; 7+ messages in thread
From: Jonathan Wakely @ 2024-01-08  1:19 UTC (permalink / raw)
  To: libstdc++, gcc-patches

Tested x86_64-linux and aarch64-linux. Pushed to trunk.

-- >8 --

This adds std::runtime_format for C++26. These new overloaded functions
enhance the std::format API so that it isn't necessary to use the less
ergonomic std::vformat and std::make_format_args (which are meant to be
implementation details). This was approved in Kona 2023 for C++26.

libstdc++-v3/ChangeLog:

	* include/std/format (__format::_Runtime_format_string): Define
	new class template.
	(basic_format_string): Add non-consteval constructor for runtime
	format strings.
	(runtime_format): Define new function for C++26.
	* testsuite/std/format/runtime_format.cc: New test.
---
 libstdc++-v3/include/std/format               | 22 +++++++++++
 .../testsuite/std/format/runtime_format.cc    | 37 +++++++++++++++++++
 2 files changed, 59 insertions(+)
 create mode 100644 libstdc++-v3/testsuite/std/format/runtime_format.cc

diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index 160efa5155c..b3b5a0bbdbc 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -81,6 +81,9 @@ namespace __format
 
   template<typename _CharT>
     using __format_context = basic_format_context<_Sink_iter<_CharT>, _CharT>;
+
+  template<typename _CharT>
+    struct _Runtime_format_string { basic_string_view<_CharT> _M_str; };
 } // namespace __format
 /// @endcond
 
@@ -115,6 +118,11 @@ namespace __format
 	consteval
 	basic_format_string(const _Tp& __s);
 
+      [[__gnu__::__always_inline__]]
+      basic_format_string(__format::_Runtime_format_string<_CharT>&& __s)
+      : _M_str(__s._M_str)
+      { }
+
       [[__gnu__::__always_inline__]]
       constexpr basic_string_view<_CharT>
       get() const noexcept
@@ -133,6 +141,20 @@ namespace __format
       = basic_format_string<wchar_t, type_identity_t<_Args>...>;
 #endif
 
+#if __cplusplus > 202302L
+  [[__gnu__::__always_inline__]]
+  inline __format::_Runtime_format_string<char>
+  runtime_format(string_view __fmt)
+  { return {__fmt}; }
+
+#ifdef _GLIBCXX_USE_WCHAR_T
+  [[__gnu__::__always_inline__]]
+  inline __format::_Runtime_format_string<wchar_t>
+  runtime_format(wstring_view __fmt)
+  { return {__fmt}; }
+#endif
+#endif // C++26
+
   // [format.formatter], formatter
 
   /// The primary template of std::formatter is disabled.
diff --git a/libstdc++-v3/testsuite/std/format/runtime_format.cc b/libstdc++-v3/testsuite/std/format/runtime_format.cc
new file mode 100644
index 00000000000..174334c7676
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/format/runtime_format.cc
@@ -0,0 +1,37 @@
+// { dg-do run { target c++26 } }
+
+#include <format>
+#include <testsuite_hooks.h>
+
+void
+test_char()
+{
+  std::string fmt = "{}";
+  auto s = std::format(std::runtime_format(fmt), 123);
+  VERIFY( s == "123" );
+}
+
+void
+test_wchar()
+{
+  std::wstring fmt = L"{:#o}";
+  auto s = std::format(std::runtime_format(fmt), 456);
+  VERIFY( s == L"0710" );
+}
+
+void
+test_internal_api()
+{
+  // Using _Runtime_format_string directly works even in C++20 mode.
+  // This can be used internally by libstdc++.
+  std::string fmt = "{:#x}";
+  auto s = std::format(std::__format::_Runtime_format_string<char>(fmt), 789);
+  VERIFY( s == "0x315" );
+}
+
+int main()
+{
+  test_char();
+  test_wchar();
+  test_internal_api();
+}
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [committed 2/2] libstdc++: Implement P2918R0 "Runtime format strings II" for C++26
  2024-01-08  1:19 ` [committed 2/2] libstdc++: Implement P2918R0 "Runtime format strings II" for C++26 Jonathan Wakely
@ 2024-01-10 18:32   ` Daniel Krügler
  2024-01-10 19:41     ` Jonathan Wakely
  0 siblings, 1 reply; 7+ messages in thread
From: Daniel Krügler @ 2024-01-10 18:32 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: libstdc++, gcc-patches

Am Mo., 8. Jan. 2024 um 03:25 Uhr schrieb Jonathan Wakely <jwakely@redhat.com>:
>
> Tested x86_64-linux and aarch64-linux. Pushed to trunk.
>
> -- >8 --
>
> This adds std::runtime_format for C++26. These new overloaded functions
> enhance the std::format API so that it isn't necessary to use the less
> ergonomic std::vformat and std::make_format_args (which are meant to be
> implementation details). This was approved in Kona 2023 for C++26.
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/format (__format::_Runtime_format_string): Define
>         new class template.
>         (basic_format_string): Add non-consteval constructor for runtime
>         format strings.
>         (runtime_format): Define new function for C++26.
>         * testsuite/std/format/runtime_format.cc: New test.
> ---
>  libstdc++-v3/include/std/format               | 22 +++++++++++
>  .../testsuite/std/format/runtime_format.cc    | 37 +++++++++++++++++++
>  2 files changed, 59 insertions(+)
>  create mode 100644 libstdc++-v3/testsuite/std/format/runtime_format.cc
>
> diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
> index 160efa5155c..b3b5a0bbdbc 100644
> --- a/libstdc++-v3/include/std/format
> +++ b/libstdc++-v3/include/std/format
> @@ -81,6 +81,9 @@ namespace __format
>
>    template<typename _CharT>
>      using __format_context = basic_format_context<_Sink_iter<_CharT>, _CharT>;
> +
> +  template<typename _CharT>
> +    struct _Runtime_format_string { basic_string_view<_CharT> _M_str; };
>  } // namespace __format
>  /// @endcond
>
> @@ -115,6 +118,11 @@ namespace __format
>         consteval
>         basic_format_string(const _Tp& __s);
>
> +      [[__gnu__::__always_inline__]]
> +      basic_format_string(__format::_Runtime_format_string<_CharT>&& __s)
> +      : _M_str(__s._M_str)
> +      { }
> +

My understanding is that this constructor should be noexcept according to N4971.

>        [[__gnu__::__always_inline__]]
>        constexpr basic_string_view<_CharT>
>        get() const noexcept
> @@ -133,6 +141,20 @@ namespace __format
>        = basic_format_string<wchar_t, type_identity_t<_Args>...>;
>  #endif
>
> +#if __cplusplus > 202302L
> +  [[__gnu__::__always_inline__]]
> +  inline __format::_Runtime_format_string<char>
> +  runtime_format(string_view __fmt)
> +  { return {__fmt}; }
> +
> +#ifdef _GLIBCXX_USE_WCHAR_T
> +  [[__gnu__::__always_inline__]]
> +  inline __format::_Runtime_format_string<wchar_t>
> +  runtime_format(wstring_view __fmt)
> +  { return {__fmt}; }
> +#endif
> +#endif // C++26
> +

These runtime_format overloads should also be noexcept.

- Daniel

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [committed 2/2] libstdc++: Implement P2918R0 "Runtime format strings II" for C++26
  2024-01-10 18:32   ` Daniel Krügler
@ 2024-01-10 19:41     ` Jonathan Wakely
  2024-01-11 20:23       ` [PATCH] libstdc++: Fix std::runtime_format deviations from the spec [PR113320] Jonathan Wakely
  0 siblings, 1 reply; 7+ messages in thread
From: Jonathan Wakely @ 2024-01-10 19:41 UTC (permalink / raw)
  To: Daniel Krügler; +Cc: libstdc++, gcc-patches

On Wed, 10 Jan 2024 at 18:33, Daniel Krügler <daniel.kruegler@gmail.com> wrote:
>
> Am Mo., 8. Jan. 2024 um 03:25 Uhr schrieb Jonathan Wakely <jwakely@redhat.com>:
> >
> > Tested x86_64-linux and aarch64-linux. Pushed to trunk.
> >
> > -- >8 --
> >
> > This adds std::runtime_format for C++26. These new overloaded functions
> > enhance the std::format API so that it isn't necessary to use the less
> > ergonomic std::vformat and std::make_format_args (which are meant to be
> > implementation details). This was approved in Kona 2023 for C++26.
> >
> > libstdc++-v3/ChangeLog:
> >
> >         * include/std/format (__format::_Runtime_format_string): Define
> >         new class template.
> >         (basic_format_string): Add non-consteval constructor for runtime
> >         format strings.
> >         (runtime_format): Define new function for C++26.
> >         * testsuite/std/format/runtime_format.cc: New test.
> > ---
> >  libstdc++-v3/include/std/format               | 22 +++++++++++
> >  .../testsuite/std/format/runtime_format.cc    | 37 +++++++++++++++++++
> >  2 files changed, 59 insertions(+)
> >  create mode 100644 libstdc++-v3/testsuite/std/format/runtime_format.cc
> >
> > diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
> > index 160efa5155c..b3b5a0bbdbc 100644
> > --- a/libstdc++-v3/include/std/format
> > +++ b/libstdc++-v3/include/std/format
> > @@ -81,6 +81,9 @@ namespace __format
> >
> >    template<typename _CharT>
> >      using __format_context = basic_format_context<_Sink_iter<_CharT>, _CharT>;
> > +
> > +  template<typename _CharT>
> > +    struct _Runtime_format_string { basic_string_view<_CharT> _M_str; };
> >  } // namespace __format
> >  /// @endcond
> >
> > @@ -115,6 +118,11 @@ namespace __format
> >         consteval
> >         basic_format_string(const _Tp& __s);
> >
> > +      [[__gnu__::__always_inline__]]
> > +      basic_format_string(__format::_Runtime_format_string<_CharT>&& __s)
> > +      : _M_str(__s._M_str)
> > +      { }
> > +
>
> My understanding is that this constructor should be noexcept according to N4971.
>
> >        [[__gnu__::__always_inline__]]
> >        constexpr basic_string_view<_CharT>
> >        get() const noexcept
> > @@ -133,6 +141,20 @@ namespace __format
> >        = basic_format_string<wchar_t, type_identity_t<_Args>...>;
> >  #endif
> >
> > +#if __cplusplus > 202302L
> > +  [[__gnu__::__always_inline__]]
> > +  inline __format::_Runtime_format_string<char>
> > +  runtime_format(string_view __fmt)
> > +  { return {__fmt}; }
> > +
> > +#ifdef _GLIBCXX_USE_WCHAR_T
> > +  [[__gnu__::__always_inline__]]
> > +  inline __format::_Runtime_format_string<wchar_t>
> > +  runtime_format(wstring_view __fmt)
> > +  { return {__fmt}; }
> > +#endif
> > +#endif // C++26
> > +
>
> These runtime_format overloads should also be noexcept.

Good catch. Looks like I implemented it from the P2918R0 proposal, not
the final R2. Oops.

I'll apply the changes, thanks!


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH] libstdc++: Fix std::runtime_format deviations from the spec [PR113320]
  2024-01-10 19:41     ` Jonathan Wakely
@ 2024-01-11 20:23       ` Jonathan Wakely
  2024-01-12  6:53         ` Daniel Krügler
  0 siblings, 1 reply; 7+ messages in thread
From: Jonathan Wakely @ 2024-01-11 20:23 UTC (permalink / raw)
  To: libstdc++, gcc-patches; +Cc: Daniel Krügler

Tested x86_64-linux. Does this look better now?

It also fixes PR113320 that got reported.

-- >8 --

I seem to have implemented this feature based on the P2918R0 revision,
not the final P2918R2 one that was approved for C++26. This commit fixes
it.

The runtime-format-string type should not have a publicly accessible
data member, so add a constructor and make it a friend of
basic_format_string. It should also be non-copyable, so that it can only
be constructed from a prvalue via temporary materialization. Change the
basic_format_string constructor parameter to pass by value. Also add
noexcept to the constructors and runtime_format generator functions.

libstdc++-v3/ChangeLog:

	PR libstdc++/113320
	* include/std/format (__format::_Runtime_format_string): Add
	constructor and disable copy operations.
	(basic_format_string(_Runtime_format_string)): Add noexcept and
	take parameter by value not rvalue reference.
	(runtime_format): Add noexcept.
	* testsuite/std/format/runtime_format.cc: Check noexcept. Check
	that construction is only possible from prvalues, not xvalues.
---
 libstdc++-v3/include/std/format               | 37 ++++++++++++-------
 .../testsuite/std/format/runtime_format.cc    | 11 ++++++
 2 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index b3b5a0bbdbc..540f8b805f8 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -70,6 +70,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // [format.context], class template basic_format_context
   template<typename _Out, typename _CharT> class basic_format_context;
 
+  // [format.fmt.string], class template basic_format_string
+  template<typename _CharT, typename... _Args> struct basic_format_string;
+
 /// @cond undocumented
 namespace __format
 {
@@ -83,7 +86,20 @@ namespace __format
     using __format_context = basic_format_context<_Sink_iter<_CharT>, _CharT>;
 
   template<typename _CharT>
-    struct _Runtime_format_string { basic_string_view<_CharT> _M_str; };
+    struct _Runtime_format_string
+    {
+      [[__gnu__::__always_inline__]]
+      _Runtime_format_string(basic_string_view<_CharT> __s) noexcept
+      : _M_str(__s) { }
+
+      _Runtime_format_string(const _Runtime_format_string&) = delete;
+      void operator=(const _Runtime_format_string&) = delete;
+
+    private:
+      basic_string_view<_CharT> _M_str;
+
+      template<typename, typename...> friend struct std::basic_format_string;
+    };
 } // namespace __format
 /// @endcond
 
@@ -104,8 +120,6 @@ namespace __format
   template<typename _Context>
     class basic_format_arg;
 
-  // [format.fmt.string], class template basic_format_string
-
   /** A compile-time checked format string for the specified argument types.
    *
    * @since C++23 but available as an extension in C++20.
@@ -119,7 +133,7 @@ namespace __format
 	basic_format_string(const _Tp& __s);
 
       [[__gnu__::__always_inline__]]
-      basic_format_string(__format::_Runtime_format_string<_CharT>&& __s)
+      basic_format_string(__format::_Runtime_format_string<_CharT> __s) noexcept
       : _M_str(__s._M_str)
       { }
 
@@ -144,14 +158,14 @@ namespace __format
 #if __cplusplus > 202302L
   [[__gnu__::__always_inline__]]
   inline __format::_Runtime_format_string<char>
-  runtime_format(string_view __fmt)
-  { return {__fmt}; }
+  runtime_format(string_view __fmt) noexcept
+  { return __fmt; }
 
 #ifdef _GLIBCXX_USE_WCHAR_T
   [[__gnu__::__always_inline__]]
   inline __format::_Runtime_format_string<wchar_t>
-  runtime_format(wstring_view __fmt)
-  { return {__fmt}; }
+  runtime_format(wstring_view __fmt) noexcept
+  { return __fmt; }
 #endif
 #endif // C++26
 
@@ -3652,12 +3666,9 @@ namespace __format
       friend std::basic_format_args<_Context>;
 
       template<typename _Ctx, typename... _Argz>
-	friend auto
+	friend auto std::
 #if _GLIBCXX_INLINE_VERSION
-	// Needed for PR c++/59526
-	std::__8::
-#else
-	std::
+	__8:: // Needed for PR c++/59256
 #endif
 	make_format_args(_Argz&...) noexcept;
 
diff --git a/libstdc++-v3/testsuite/std/format/runtime_format.cc b/libstdc++-v3/testsuite/std/format/runtime_format.cc
index 174334c7676..f2bfa5b434d 100644
--- a/libstdc++-v3/testsuite/std/format/runtime_format.cc
+++ b/libstdc++-v3/testsuite/std/format/runtime_format.cc
@@ -29,6 +29,17 @@ test_internal_api()
   VERIFY( s == "0x315" );
 }
 
+static_assert( noexcept(std::format_string<>(std::runtime_format(""))) );
+static_assert( noexcept(std::wformat_string<>(std::runtime_format(L""))) );
+static_assert( noexcept(std::format_string<int>(std::runtime_format(""))) );
+static_assert( noexcept(std::wformat_string<char>(std::runtime_format(L""))) );
+// A format string can be constructed from the result of std::runtime_format
+// using copy elision, but cannot be constructed from an xvalue.
+static_assert( !std::is_constructible_v<std::format_string<>,
+					decltype(std::runtime_format(""))&&> );
+static_assert( !std::is_constructible_v<std::wformat_string<>,
+					decltype(std::runtime_format(L""))&&> );
+
 int main()
 {
   test_char();
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] libstdc++: Fix std::runtime_format deviations from the spec [PR113320]
  2024-01-11 20:23       ` [PATCH] libstdc++: Fix std::runtime_format deviations from the spec [PR113320] Jonathan Wakely
@ 2024-01-12  6:53         ` Daniel Krügler
  2024-01-12  9:49           ` Jonathan Wakely
  0 siblings, 1 reply; 7+ messages in thread
From: Daniel Krügler @ 2024-01-12  6:53 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: libstdc++, gcc-patches

Am Do., 11. Jan. 2024 um 21:23 Uhr schrieb Jonathan Wakely <jwakely@redhat.com>:
>
> Tested x86_64-linux. Does this look better now?

Yes, thank you.

- Daniel

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] libstdc++: Fix std::runtime_format deviations from the spec [PR113320]
  2024-01-12  6:53         ` Daniel Krügler
@ 2024-01-12  9:49           ` Jonathan Wakely
  0 siblings, 0 replies; 7+ messages in thread
From: Jonathan Wakely @ 2024-01-12  9:49 UTC (permalink / raw)
  To: Daniel Krügler; +Cc: Jonathan Wakely, libstdc++, gcc-patches

On Fri, 12 Jan 2024 at 06:53, Daniel Krügler <daniel.kruegler@gmail.com> wrote:
>
> Am Do., 11. Jan. 2024 um 21:23 Uhr schrieb Jonathan Wakely <jwakely@redhat.com>:
> >
> > Tested x86_64-linux. Does this look better now?
>
> Yes, thank you.

Thanks, pushed.

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2024-01-12  9:49 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-08  1:19 [committed 1/2] libstdc++: Implement P2905R2 "Runtime format strings" for C++20 Jonathan Wakely
2024-01-08  1:19 ` [committed 2/2] libstdc++: Implement P2918R0 "Runtime format strings II" for C++26 Jonathan Wakely
2024-01-10 18:32   ` Daniel Krügler
2024-01-10 19:41     ` Jonathan Wakely
2024-01-11 20:23       ` [PATCH] libstdc++: Fix std::runtime_format deviations from the spec [PR113320] Jonathan Wakely
2024-01-12  6:53         ` Daniel Krügler
2024-01-12  9:49           ` Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).