about summary refs log tree commit diff
path: root/library/core
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-03-16 13:46:52 +0000
committerbors <bors@rust-lang.org>2023-03-16 13:46:52 +0000
commit1203e0866e6c3659775efcb8aecad21dc13ef38b (patch)
tree649280a25f8efd4058120de4a4e104959b83e3f4 /library/core
parent7ac4b82ddd596a218cac8cd6b88a91b54fcdcf13 (diff)
parenta08016514847d39ae3096ff41f98759abf59bee8 (diff)
downloadrust-1203e0866e6c3659775efcb8aecad21dc13ef38b.tar.gz
rust-1203e0866e6c3659775efcb8aecad21dc13ef38b.zip
Auto merge of #106824 - m-ou-se:format-args-flatten, r=oli-obk
Flatten/inline format_args!() and (string and int) literal arguments into format_args!()

Implements https://github.com/rust-lang/rust/issues/78356

Gated behind `-Zflatten-format-args=yes`.

Part of #99012

This change inlines string literals, integer literals and nested format_args!() into format_args!() during ast lowering, making all of the following pairs result in equivalent hir:

```rust
println!("Hello, {}!", "World");
println!("Hello, World!");
```

```rust
println!("[info] {}", format_args!("error"));
println!("[info] error");
```

```rust
println!("[{}] {}", status, format_args!("error: {}", msg));
println!("[{}] error: {}", status, msg);
```

```rust
println!("{} + {} = {}", 1, 2, 1 + 2);
println!("1 + 2 = {}", 1 + 2);
```

And so on.

This is useful for macros. E.g. a `log::info!()` macro could just pass the tokens from the user directly into a `format_args!()` that gets efficiently flattened/inlined into a `format_args!("info: {}")`.

It also means that `dbg!(x)` will have its file, line, and expression name inlined:

```rust
eprintln!("[{}:{}] {} = {:#?}", file!(), line!(), stringify!(x), x); // before
eprintln!("[example.rs:1] x = {:#?}", x); // after
```

Which can be nice in some cases, but also means a lot more unique static strings than before if dbg!() is used a lot.
Diffstat (limited to 'library/core')
-rw-r--r--library/core/src/fmt/mod.rs26
-rw-r--r--library/core/src/panicking.rs4
-rw-r--r--library/core/tests/fmt/mod.rs8
3 files changed, 30 insertions, 8 deletions
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index c9821bf8109..6d764237dc8 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -392,8 +392,31 @@ enum FlagV1 {
 }
 
 impl<'a> Arguments<'a> {
+    #[doc(hidden)]
+    #[inline]
+    #[unstable(feature = "fmt_internals", issue = "none")]
+    #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")]
+    pub const fn new_const(pieces: &'a [&'static str]) -> Self {
+        if pieces.len() > 1 {
+            panic!("invalid args");
+        }
+        Arguments { pieces, fmt: None, args: &[] }
+    }
+
     /// When using the format_args!() macro, this function is used to generate the
     /// Arguments structure.
+    #[cfg(not(bootstrap))]
+    #[doc(hidden)]
+    #[inline]
+    #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
+    pub fn new_v1(pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> {
+        if pieces.len() < args.len() || pieces.len() > args.len() + 1 {
+            panic!("invalid args");
+        }
+        Arguments { pieces, fmt: None, args }
+    }
+
+    #[cfg(bootstrap)]
     #[doc(hidden)]
     #[inline]
     #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
@@ -417,8 +440,7 @@ impl<'a> Arguments<'a> {
     #[doc(hidden)]
     #[inline]
     #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
-    #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")]
-    pub const fn new_v1_formatted(
+    pub fn new_v1_formatted(
         pieces: &'a [&'static str],
         args: &'a [ArgumentV1<'a>],
         fmt: &'a [rt::v1::Argument],
diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs
index 805a1e51ae9..dd0105c0eb4 100644
--- a/library/core/src/panicking.rs
+++ b/library/core/src/panicking.rs
@@ -111,7 +111,7 @@ pub const fn panic(expr: &'static str) -> ! {
     // truncation and padding (even though none is used here). Using
     // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the
     // output binary, saving up to a few kilobytes.
-    panic_fmt(fmt::Arguments::new_v1(&[expr], &[]));
+    panic_fmt(fmt::Arguments::new_const(&[expr]));
 }
 
 /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize.
@@ -120,7 +120,7 @@ pub const fn panic(expr: &'static str) -> ! {
 #[lang = "panic_nounwind"] // needed by codegen for non-unwinding panics
 #[rustc_nounwind]
 pub fn panic_nounwind(expr: &'static str) -> ! {
-    panic_nounwind_fmt(fmt::Arguments::new_v1(&[expr], &[]));
+    panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]));
 }
 
 #[inline]
diff --git a/library/core/tests/fmt/mod.rs b/library/core/tests/fmt/mod.rs
index 61807635813..c1c80c46c78 100644
--- a/library/core/tests/fmt/mod.rs
+++ b/library/core/tests/fmt/mod.rs
@@ -22,11 +22,11 @@ fn test_pointer_formats_data_pointer() {
 #[test]
 fn test_estimated_capacity() {
     assert_eq!(format_args!("").estimated_capacity(), 0);
-    assert_eq!(format_args!("{}", "").estimated_capacity(), 0);
+    assert_eq!(format_args!("{}", {""}).estimated_capacity(), 0);
     assert_eq!(format_args!("Hello").estimated_capacity(), 5);
-    assert_eq!(format_args!("Hello, {}!", "").estimated_capacity(), 16);
-    assert_eq!(format_args!("{}, hello!", "World").estimated_capacity(), 0);
-    assert_eq!(format_args!("{}. 16-bytes piece", "World").estimated_capacity(), 32);
+    assert_eq!(format_args!("Hello, {}!", {""}).estimated_capacity(), 16);
+    assert_eq!(format_args!("{}, hello!", {"World"}).estimated_capacity(), 0);
+    assert_eq!(format_args!("{}. 16-bytes piece", {"World"}).estimated_capacity(), 32);
 }
 
 #[test]