public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-8429] libstdc++: Implement P2905R2 "Runtime format strings" for C++20
@ 2024-03-12 14:17 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2024-03-12 14:17 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:3c8faeac3d03e032d55fae390618e577c292a83e

commit r13-8429-g3c8faeac3d03e032d55fae390618e577c292a83e
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Sun Jan 7 22:21:08 2024 +0000

    libstdc++: Implement P2905R2 "Runtime format strings" for C++20
    
    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.
            * 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.
    
    (cherry picked from commit 2a8ee2592e48735d88df786cbafa6b0da39fc4d6)

Diff:
---
 libstdc++-v3/include/bits/chrono_io.h              | 15 +++++++----
 libstdc++-v3/include/std/format                    | 30 +++++++++++-----------
 libstdc++-v3/testsuite/20_util/duration/io.cc      |  3 ++-
 .../testsuite/std/format/arguments/args.cc         | 26 ++++++++++++++-----
 .../testsuite/std/format/arguments/args_neg.cc     | 12 +++++++++
 .../testsuite/std/format/arguments/lwg3810.cc      |  8 ++++--
 .../testsuite/std/format/functions/format.cc       |  6 +++--
 .../testsuite/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 +--
 14 files changed, 91 insertions(+), 45 deletions(-)

diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
index c42797f64c4..1c08130bf65 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -2195,7 +2195,8 @@ namespace chrono
       _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;
     }
 
@@ -2213,8 +2214,10 @@ namespace chrono
 	__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;
     }
 
@@ -2253,8 +2256,10 @@ namespace chrono
 	__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 807c97680c6..7bcaddb3715 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -3117,7 +3117,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)
@@ -3287,7 +3287,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>
@@ -3325,7 +3325,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>
@@ -3338,7 +3338,7 @@ namespace __format
 #if _GLIBCXX_INLINE_VERSION
 	__8:: // Needed for PR c++/59256
 #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.
@@ -3412,11 +3412,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...);
     }
 
@@ -3424,7 +3424,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...); }
 
 /// @cond undocumented
@@ -3857,7 +3857,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...));
     }
 
   template<typename _Out, typename... _Args>
@@ -3866,7 +3866,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...));
     }
 
   template<typename _Out, typename... _Args>
@@ -3876,7 +3876,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...));
     }
 
   template<typename _Out, typename... _Args>
@@ -3886,7 +3886,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...));
     }
 
   template<typename _Out, typename... _Args>
@@ -3988,7 +3988,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();
     }
 
@@ -3999,7 +3999,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();
     }
 
@@ -4011,7 +4011,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();
     }
 
@@ -4023,7 +4023,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();
     }
 
diff --git a/libstdc++-v3/testsuite/20_util/duration/io.cc b/libstdc++-v3/testsuite/20_util/duration/io.cc
index 5f0f25eac69..2043f0c4e9d 100644
--- a/libstdc++-v3/testsuite/20_util/duration/io.cc
+++ b/libstdc++-v3/testsuite/20_util/duration/io.cc
@@ -83,7 +83,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 ae2eab6d560..e2e7a3e7ee5 100644
--- a/libstdc++-v3/testsuite/std/format/arguments/args.cc
+++ b/libstdc++-v3/testsuite/std/format/arguments/args.cc
@@ -47,7 +47,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));
@@ -55,7 +60,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));
@@ -65,14 +74,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'));
@@ -81,7 +93,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 9ccb654de1b..df3d98e33f7 100644
--- a/libstdc++-v3/testsuite/std/format/arguments/lwg3810.cc
+++ b/libstdc++-v3/testsuite/std/format/arguments/lwg3810.cc
@@ -5,7 +5,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>);
 
@@ -21,5 +25,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 531b6a312d7..1f1802e561c 100644
--- a/libstdc++-v3/testsuite/std/format/functions/format.cc
+++ b/libstdc++-v3/testsuite/std/format/functions/format.cc
@@ -261,14 +261,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 6fa33b9fbac..320914e272a 100644
--- a/libstdc++-v3/testsuite/std/format/functions/vformat_to.cc
+++ b/libstdc++-v3/testsuite/std/format/functions/vformat_to.cc
@@ -30,17 +30,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 d70a2331645..a0c07c49f68 100644
--- a/libstdc++-v3/testsuite/std/format/string.cc
+++ b/libstdc++-v3/testsuite/std/format/string.cc
@@ -150,8 +150,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();
@@ -163,9 +164,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 b6da0701f98..592ecbb7216 100644
--- a/libstdc++-v3/testsuite/std/time/day/io.cc
+++ b/libstdc++-v3/testsuite/std/time/day/io.cc
@@ -52,8 +52,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 59c303bb96c..1f2a4c0e3ce 100644
--- a/libstdc++-v3/testsuite/std/time/month/io.cc
+++ b/libstdc++-v3/testsuite/std/time/month/io.cc
@@ -75,8 +75,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 ab213fb559a..3e1661b5052 100644
--- a/libstdc++-v3/testsuite/std/time/weekday/io.cc
+++ b/libstdc++-v3/testsuite/std/time/weekday/io.cc
@@ -78,8 +78,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 b50d9d89c61..18331deee9c 100644
--- a/libstdc++-v3/testsuite/std/time/year/io.cc
+++ b/libstdc++-v3/testsuite/std/time/year/io.cc
@@ -69,8 +69,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 967b4a72406..04e6e59c8f7 100644
--- a/libstdc++-v3/testsuite/std/time/year_month_day/io.cc
+++ b/libstdc++-v3/testsuite/std/time/year_month_day/io.cc
@@ -98,8 +98,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);
     }

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2024-03-12 14:17 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-12 14:17 [gcc r13-8429] libstdc++: Implement P2905R2 "Runtime format strings" for C++20 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).