Skip to content

Commit 1b587a6

Browse files
committed
alloc: add ToString specialization for &&str
Fixes #128690
1 parent 83e9b93 commit 1b587a6

File tree

2 files changed

+71
-8
lines changed

2 files changed

+71
-8
lines changed

library/alloc/src/string.rs

+35-8
Original file line numberDiff line numberDiff line change
@@ -2643,14 +2643,41 @@ impl ToString for i8 {
26432643
}
26442644
}
26452645

2646-
#[doc(hidden)]
2647-
#[cfg(not(no_global_oom_handling))]
2648-
#[stable(feature = "str_to_string_specialization", since = "1.9.0")]
2649-
impl ToString for str {
2650-
#[inline]
2651-
fn to_string(&self) -> String {
2652-
String::from(self)
2653-
}
2646+
// Generic/generated code can sometimes have multiple, nested references
2647+
// for strings, including `&&&str`s that would never be written
2648+
// by hand. This macro generates twelve layers of nested `&`-impl
2649+
// for primitive strings.
2650+
macro_rules! to_string_str {
2651+
{type ; x $($x:ident)*} => {
2652+
&to_string_str! { type ; $($x)* }
2653+
};
2654+
{type ;} => { str };
2655+
{impl ; x $($x:ident)*} => {
2656+
to_string_str! { $($x)* }
2657+
};
2658+
{impl ;} => { };
2659+
{$self:expr ; x $($x:ident)*} => {
2660+
*(to_string_str! { $self ; $($x)* })
2661+
};
2662+
{$self:expr ;} => { $self };
2663+
{$($x:ident)*} => {
2664+
#[doc(hidden)]
2665+
#[cfg(not(no_global_oom_handling))]
2666+
#[stable(feature = "str_to_string_specialization", since = "1.9.0")]
2667+
impl ToString for to_string_str!(type ; $($x)*) {
2668+
#[inline]
2669+
fn to_string(&self) -> String {
2670+
String::from(to_string_str!(self ; $($x)*))
2671+
}
2672+
}
2673+
to_string_str! { impl ; $($x)* }
2674+
};
2675+
}
2676+
2677+
to_string_str! {
2678+
x x x x
2679+
x x x x
2680+
x x x x
26542681
}
26552682

26562683
#[doc(hidden)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//@ compile-flags: -C opt-level=3 -Z merge-functions=disabled
2+
#![crate_type = "lib"]
3+
4+
//! Make sure str::to_string is specialized not to use fmt machinery.
5+
6+
// CHECK-LABEL: define {{(dso_local )?}}void @one_ref
7+
#[no_mangle]
8+
pub fn one_ref(input: &str) -> String {
9+
// CHECK-NOT: {{(call|invoke).*}}fmt
10+
input.to_string()
11+
}
12+
13+
// CHECK-LABEL: define {{(dso_local )?}}void @two_ref
14+
#[no_mangle]
15+
pub fn two_ref(input: &&str) -> String {
16+
// CHECK-NOT: {{(call|invoke).*}}fmt
17+
input.to_string()
18+
}
19+
20+
// CHECK-LABEL: define {{(dso_local )?}}void @thirteen_ref
21+
#[no_mangle]
22+
pub fn thirteen_ref(input: &&&&&&&&&&&&&str) -> String {
23+
// CHECK-NOT: {{(call|invoke).*}}fmt
24+
input.to_string()
25+
}
26+
27+
// This is a known performance cliff because of the macro-generated
28+
// specialized impl. If this test suddenly starts failing,
29+
// consider removing the `to_string_str!` macro in `alloc/str/string.rs`.
30+
//
31+
// CHECK-LABEL: define {{(dso_local )?}}void @fourteen_ref
32+
#[no_mangle]
33+
pub fn fourteen_ref(input: &&&&&&&&&&&&&&str) -> String {
34+
// CHECK: {{(call|invoke).*}}fmt
35+
input.to_string()
36+
}

0 commit comments

Comments
 (0)