From e0cc6aca78e8aaf32b744abb767a97128f938ba8 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 8 Sep 2020 13:26:04 -0700 Subject: [PATCH 1/3] Specialize format! with pattern-matching If the first argument to format! is "{}" then the rest of the args must be something that implements fmt::Display and thus ToString. We can thus call ToString on it. In order to type-safely eat all those arguments, a struct wrapper inside a closure prevents misuse. --- library/alloc/src/macros.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 2f744618d6936..2fe009d282c99 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -102,6 +102,18 @@ macro_rules! vec { #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] macro_rules! format { + // A faster path for simple format! calls. + ("{}", $($arg:tt,?)+) => {{ + // It is possible to implement ToString manually, bypassing Display. + // However, "{}" formats in terms of Display. This wrapper enforces + // equally strict type boundaries even though we are calling ToString. + struct NeedsDisplay { + inner: T, + } + let fast = |t: NeedsDisplay<_> | $crate::string::ToString::to_string(t.inner); + // This TokenTree must resolve to a Displayable value to be valid. + fast(NeedsDisplay { inner: &{$($arg)*} }) + }}; ($($arg:tt)*) => {{ let res = $crate::fmt::format($crate::__export::format_args!($($arg)*)); res From 4e45df786aaa5c0618f02fd2f85bb0ff64931a90 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 9 Sep 2020 17:13:56 -0700 Subject: [PATCH 2/3] Minimize expansion on NeedsDisplay This uses #[unstable] and #[allow_internal_unstable] to reduce the need for expansion of the struct in every macro invocation. --- library/alloc/src/macros.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 2fe009d282c99..3bf5b4eb6467a 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -64,6 +64,15 @@ macro_rules! vec { ($($x:expr,)*) => (vec![$($x),*]) } +// HACK(jubilee): Shim for specializing format! It is possible to manually +// implement ToString, bypassing Display. "{}" normally formats in terms of +// Display. NeedsDisplay enforces equally strict type boundarie. +#[unstable(feature = "display_type_guard", issue = "none")] +#[allow(dead_code)] +struct NeedsDisplay { + inner: T, +} + /// Creates a `String` using interpolation of runtime expressions. /// /// The first argument `format!` receives is a format string. This must be a string @@ -99,17 +108,12 @@ macro_rules! vec { /// format!("hello {}", "world!"); /// format!("x = {}, y = {y}", 10, y = 30); /// ``` +#[allow_internal_unstable(display_type_guard)] #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] macro_rules! format { // A faster path for simple format! calls. ("{}", $($arg:tt,?)+) => {{ - // It is possible to implement ToString manually, bypassing Display. - // However, "{}" formats in terms of Display. This wrapper enforces - // equally strict type boundaries even though we are calling ToString. - struct NeedsDisplay { - inner: T, - } let fast = |t: NeedsDisplay<_> | $crate::string::ToString::to_string(t.inner); // This TokenTree must resolve to a Displayable value to be valid. fast(NeedsDisplay { inner: &{$($arg)*} }) From 1ee0392606bb53a70ed082acb551e30dd6ee21c7 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 21 Oct 2020 11:55:09 -0700 Subject: [PATCH 3/3] Simplify to invoking Trait on ident --- library/alloc/src/macros.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 3bf5b4eb6467a..e641a0e07730b 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -64,15 +64,6 @@ macro_rules! vec { ($($x:expr,)*) => (vec![$($x),*]) } -// HACK(jubilee): Shim for specializing format! It is possible to manually -// implement ToString, bypassing Display. "{}" normally formats in terms of -// Display. NeedsDisplay enforces equally strict type boundarie. -#[unstable(feature = "display_type_guard", issue = "none")] -#[allow(dead_code)] -struct NeedsDisplay { - inner: T, -} - /// Creates a `String` using interpolation of runtime expressions. /// /// The first argument `format!` receives is a format string. This must be a string @@ -108,15 +99,12 @@ struct NeedsDisplay { /// format!("hello {}", "world!"); /// format!("x = {}, y = {y}", 10, y = 30); /// ``` -#[allow_internal_unstable(display_type_guard)] #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] macro_rules! format { // A faster path for simple format! calls. - ("{}", $($arg:tt,?)+) => {{ - let fast = |t: NeedsDisplay<_> | $crate::string::ToString::to_string(t.inner); - // This TokenTree must resolve to a Displayable value to be valid. - fast(NeedsDisplay { inner: &{$($arg)*} }) + ("{}", $arg:ident) => {{ + $crate::fmt::Display::to_string(&$arg) }}; ($($arg:tt)*) => {{ let res = $crate::fmt::format($crate::__export::format_args!($($arg)*));