Skip to content

Commit 4790a43

Browse files
committed
Auto merge of #134258 - bjorn3:no_public_specialization, r=petrochenkov
Remove support for specializing ToString outside the standard library This is the only trait specializable outside of the standard library. Before stabilizing specialization we will probably want to remove support for this. It was originally made specializable to allow a more efficient ToString in libproc_macro back when this way the only way to get any data out of a TokenStream. We now support getting individual tokens, so proc macros no longer need to call it as often.
2 parents 7caf35b + 3f3f27b commit 4790a43

File tree

10 files changed

+58
-180
lines changed

10 files changed

+58
-180
lines changed

compiler/rustc_span/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
#![feature(hash_set_entry)]
2626
#![feature(if_let_guard)]
2727
#![feature(let_chains)]
28-
#![feature(min_specialization)]
2928
#![feature(negative_impls)]
3029
#![feature(read_buf)]
3130
#![feature(round_char_boundary)]

compiler/rustc_span/src/symbol.rs

-7
Original file line numberDiff line numberDiff line change
@@ -2476,13 +2476,6 @@ impl fmt::Display for Symbol {
24762476
}
24772477
}
24782478

2479-
// takes advantage of `str::to_string` specialization
2480-
impl ToString for Symbol {
2481-
fn to_string(&self) -> String {
2482-
self.as_str().to_string()
2483-
}
2484-
}
2485-
24862479
impl<CTX> HashStable<CTX> for Symbol {
24872480
#[inline]
24882481
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {

library/alloc/src/string.rs

+32-37
Original file line numberDiff line numberDiff line change
@@ -2675,12 +2675,25 @@ pub trait ToString {
26752675
#[cfg(not(no_global_oom_handling))]
26762676
#[stable(feature = "rust1", since = "1.0.0")]
26772677
impl<T: fmt::Display + ?Sized> ToString for T {
2678+
#[inline]
2679+
fn to_string(&self) -> String {
2680+
<Self as SpecToString>::spec_to_string(self)
2681+
}
2682+
}
2683+
2684+
#[cfg(not(no_global_oom_handling))]
2685+
trait SpecToString {
2686+
fn spec_to_string(&self) -> String;
2687+
}
2688+
2689+
#[cfg(not(no_global_oom_handling))]
2690+
impl<T: fmt::Display + ?Sized> SpecToString for T {
26782691
// A common guideline is to not inline generic functions. However,
26792692
// removing `#[inline]` from this method causes non-negligible regressions.
26802693
// See <https://github.com/rust-lang/rust/pull/74852>, the last attempt
26812694
// to try to remove it.
26822695
#[inline]
2683-
default fn to_string(&self) -> String {
2696+
default fn spec_to_string(&self) -> String {
26842697
let mut buf = String::new();
26852698
let mut formatter =
26862699
core::fmt::Formatter::new(&mut buf, core::fmt::FormattingOptions::new());
@@ -2691,42 +2704,34 @@ impl<T: fmt::Display + ?Sized> ToString for T {
26912704
}
26922705
}
26932706

2694-
#[doc(hidden)]
26952707
#[cfg(not(no_global_oom_handling))]
2696-
#[unstable(feature = "ascii_char", issue = "110998")]
2697-
impl ToString for core::ascii::Char {
2708+
impl SpecToString for core::ascii::Char {
26982709
#[inline]
2699-
fn to_string(&self) -> String {
2710+
fn spec_to_string(&self) -> String {
27002711
self.as_str().to_owned()
27012712
}
27022713
}
27032714

2704-
#[doc(hidden)]
27052715
#[cfg(not(no_global_oom_handling))]
2706-
#[stable(feature = "char_to_string_specialization", since = "1.46.0")]
2707-
impl ToString for char {
2716+
impl SpecToString for char {
27082717
#[inline]
2709-
fn to_string(&self) -> String {
2718+
fn spec_to_string(&self) -> String {
27102719
String::from(self.encode_utf8(&mut [0; 4]))
27112720
}
27122721
}
27132722

2714-
#[doc(hidden)]
27152723
#[cfg(not(no_global_oom_handling))]
2716-
#[stable(feature = "bool_to_string_specialization", since = "1.68.0")]
2717-
impl ToString for bool {
2724+
impl SpecToString for bool {
27182725
#[inline]
2719-
fn to_string(&self) -> String {
2726+
fn spec_to_string(&self) -> String {
27202727
String::from(if *self { "true" } else { "false" })
27212728
}
27222729
}
27232730

2724-
#[doc(hidden)]
27252731
#[cfg(not(no_global_oom_handling))]
2726-
#[stable(feature = "u8_to_string_specialization", since = "1.54.0")]
2727-
impl ToString for u8 {
2732+
impl SpecToString for u8 {
27282733
#[inline]
2729-
fn to_string(&self) -> String {
2734+
fn spec_to_string(&self) -> String {
27302735
let mut buf = String::with_capacity(3);
27312736
let mut n = *self;
27322737
if n >= 10 {
@@ -2742,12 +2747,10 @@ impl ToString for u8 {
27422747
}
27432748
}
27442749

2745-
#[doc(hidden)]
27462750
#[cfg(not(no_global_oom_handling))]
2747-
#[stable(feature = "i8_to_string_specialization", since = "1.54.0")]
2748-
impl ToString for i8 {
2751+
impl SpecToString for i8 {
27492752
#[inline]
2750-
fn to_string(&self) -> String {
2753+
fn spec_to_string(&self) -> String {
27512754
let mut buf = String::with_capacity(4);
27522755
if self.is_negative() {
27532756
buf.push('-');
@@ -2788,11 +2791,9 @@ macro_rules! to_string_expr_wrap_in_deref {
27882791
macro_rules! to_string_str {
27892792
{$($($x:ident)*),+} => {
27902793
$(
2791-
#[doc(hidden)]
2792-
#[stable(feature = "str_to_string_specialization", since = "1.9.0")]
2793-
impl ToString for to_string_str_wrap_in_ref!($($x)*) {
2794+
impl SpecToString for to_string_str_wrap_in_ref!($($x)*) {
27942795
#[inline]
2795-
fn to_string(&self) -> String {
2796+
fn spec_to_string(&self) -> String {
27962797
String::from(to_string_expr_wrap_in_deref!(self ; $($x)*))
27972798
}
27982799
}
@@ -2816,32 +2817,26 @@ to_string_str! {
28162817
x,
28172818
}
28182819

2819-
#[doc(hidden)]
28202820
#[cfg(not(no_global_oom_handling))]
2821-
#[stable(feature = "cow_str_to_string_specialization", since = "1.17.0")]
2822-
impl ToString for Cow<'_, str> {
2821+
impl SpecToString for Cow<'_, str> {
28232822
#[inline]
2824-
fn to_string(&self) -> String {
2823+
fn spec_to_string(&self) -> String {
28252824
self[..].to_owned()
28262825
}
28272826
}
28282827

2829-
#[doc(hidden)]
28302828
#[cfg(not(no_global_oom_handling))]
2831-
#[stable(feature = "string_to_string_specialization", since = "1.17.0")]
2832-
impl ToString for String {
2829+
impl SpecToString for String {
28332830
#[inline]
2834-
fn to_string(&self) -> String {
2831+
fn spec_to_string(&self) -> String {
28352832
self.to_owned()
28362833
}
28372834
}
28382835

2839-
#[doc(hidden)]
28402836
#[cfg(not(no_global_oom_handling))]
2841-
#[stable(feature = "fmt_arguments_to_string_specialization", since = "1.71.0")]
2842-
impl ToString for fmt::Arguments<'_> {
2837+
impl SpecToString for fmt::Arguments<'_> {
28432838
#[inline]
2844-
fn to_string(&self) -> String {
2839+
fn spec_to_string(&self) -> String {
28452840
crate::fmt::format(*self)
28462841
}
28472842
}

library/proc_macro/src/bridge/symbol.rs

-6
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,6 @@ impl fmt::Debug for Symbol {
9191
}
9292
}
9393

94-
impl ToString for Symbol {
95-
fn to_string(&self) -> String {
96-
self.with(|s| s.to_owned())
97-
}
98-
}
99-
10094
impl fmt::Display for Symbol {
10195
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
10296
self.with(|s| fmt::Display::fmt(s, f))

library/proc_macro/src/lib.rs

+11-66
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@
1919
)]
2020
#![doc(rust_logo)]
2121
#![feature(rustdoc_internals)]
22-
// This library is copied into rust-analyzer to allow loading rustc compiled proc macros.
23-
// Please avoid unstable features where possible to minimize the amount of changes necessary
24-
// to make it compile with rust-analyzer on stable.
2522
#![feature(staged_api)]
2623
#![feature(allow_internal_unstable)]
2724
#![feature(decl_macro)]
@@ -30,7 +27,6 @@
3027
#![feature(panic_can_unwind)]
3128
#![feature(restricted_std)]
3229
#![feature(rustc_attrs)]
33-
#![feature(min_specialization)]
3430
#![feature(extend_one)]
3531
#![recursion_limit = "256"]
3632
#![allow(internal_features)]
@@ -185,16 +181,6 @@ impl FromStr for TokenStream {
185181
}
186182
}
187183

188-
// N.B., the bridge only provides `to_string`, implement `fmt::Display`
189-
// based on it (the reverse of the usual relationship between the two).
190-
#[doc(hidden)]
191-
#[stable(feature = "proc_macro_lib", since = "1.15.0")]
192-
impl ToString for TokenStream {
193-
fn to_string(&self) -> String {
194-
self.0.as_ref().map(|t| t.to_string()).unwrap_or_default()
195-
}
196-
}
197-
198184
/// Prints the token stream as a string that is supposed to be losslessly convertible back
199185
/// into the same token stream (modulo spans), except for possibly `TokenTree::Group`s
200186
/// with `Delimiter::None` delimiters and negative numeric literals.
@@ -210,7 +196,10 @@ impl ToString for TokenStream {
210196
impl fmt::Display for TokenStream {
211197
#[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization
212198
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213-
f.write_str(&self.to_string())
199+
match &self.0 {
200+
Some(ts) => write!(f, "{}", ts.to_string()),
201+
None => Ok(()),
202+
}
214203
}
215204
}
216205

@@ -756,21 +745,6 @@ impl From<Literal> for TokenTree {
756745
}
757746
}
758747

759-
// N.B., the bridge only provides `to_string`, implement `fmt::Display`
760-
// based on it (the reverse of the usual relationship between the two).
761-
#[doc(hidden)]
762-
#[stable(feature = "proc_macro_lib", since = "1.15.0")]
763-
impl ToString for TokenTree {
764-
fn to_string(&self) -> String {
765-
match *self {
766-
TokenTree::Group(ref t) => t.to_string(),
767-
TokenTree::Ident(ref t) => t.to_string(),
768-
TokenTree::Punct(ref t) => t.to_string(),
769-
TokenTree::Literal(ref t) => t.to_string(),
770-
}
771-
}
772-
}
773-
774748
/// Prints the token tree as a string that is supposed to be losslessly convertible back
775749
/// into the same token tree (modulo spans), except for possibly `TokenTree::Group`s
776750
/// with `Delimiter::None` delimiters and negative numeric literals.
@@ -786,7 +760,12 @@ impl ToString for TokenTree {
786760
impl fmt::Display for TokenTree {
787761
#[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization
788762
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
789-
f.write_str(&self.to_string())
763+
match self {
764+
TokenTree::Group(t) => write!(f, "{t}"),
765+
TokenTree::Ident(t) => write!(f, "{t}"),
766+
TokenTree::Punct(t) => write!(f, "{t}"),
767+
TokenTree::Literal(t) => write!(f, "{t}"),
768+
}
790769
}
791770
}
792771

@@ -912,24 +891,14 @@ impl Group {
912891
}
913892
}
914893

915-
// N.B., the bridge only provides `to_string`, implement `fmt::Display`
916-
// based on it (the reverse of the usual relationship between the two).
917-
#[doc(hidden)]
918-
#[stable(feature = "proc_macro_lib", since = "1.15.0")]
919-
impl ToString for Group {
920-
fn to_string(&self) -> String {
921-
TokenStream::from(TokenTree::from(self.clone())).to_string()
922-
}
923-
}
924-
925894
/// Prints the group as a string that should be losslessly convertible back
926895
/// into the same group (modulo spans), except for possibly `TokenTree::Group`s
927896
/// with `Delimiter::None` delimiters.
928897
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
929898
impl fmt::Display for Group {
930899
#[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization
931900
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
932-
f.write_str(&self.to_string())
901+
write!(f, "{}", TokenStream::from(TokenTree::from(self.clone())))
933902
}
934903
}
935904

@@ -1035,14 +1004,6 @@ impl Punct {
10351004
}
10361005
}
10371006

1038-
#[doc(hidden)]
1039-
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
1040-
impl ToString for Punct {
1041-
fn to_string(&self) -> String {
1042-
self.as_char().to_string()
1043-
}
1044-
}
1045-
10461007
/// Prints the punctuation character as a string that should be losslessly convertible
10471008
/// back into the same character.
10481009
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
@@ -1138,14 +1099,6 @@ impl Ident {
11381099
}
11391100
}
11401101

1141-
#[doc(hidden)]
1142-
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
1143-
impl ToString for Ident {
1144-
fn to_string(&self) -> String {
1145-
self.0.sym.with(|sym| if self.0.is_raw { ["r#", sym].concat() } else { sym.to_owned() })
1146-
}
1147-
}
1148-
11491102
/// Prints the identifier as a string that should be losslessly convertible back
11501103
/// into the same identifier.
11511104
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
@@ -1520,14 +1473,6 @@ impl FromStr for Literal {
15201473
}
15211474
}
15221475

1523-
#[doc(hidden)]
1524-
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
1525-
impl ToString for Literal {
1526-
fn to_string(&self) -> String {
1527-
self.with_stringify_parts(|parts| parts.concat())
1528-
}
1529-
}
1530-
15311476
/// Prints the literal as a string that should be losslessly convertible
15321477
/// back into the same literal (except for possible rounding for floating point literals).
15331478
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]

src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs

-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use clippy_utils::diagnostics::span_lint_and_help;
2-
use clippy_utils::ty::implements_trait;
32
use rustc_hir::{Impl, Item, ItemKind};
43
use rustc_lint::{LateContext, LateLintPass};
54
use rustc_session::declare_lint_pass;
@@ -54,8 +53,6 @@ impl<'tcx> LateLintPass<'tcx> for ToStringTraitImpl {
5453
}) = it.kind
5554
&& let Some(trait_did) = trait_ref.trait_def_id()
5655
&& cx.tcx.is_diagnostic_item(sym::ToString, trait_did)
57-
&& let Some(display_did) = cx.tcx.get_diagnostic_item(sym::Display)
58-
&& !implements_trait(cx, cx.tcx.type_of(it.owner_id).instantiate_identity(), display_did, &[])
5956
{
6057
span_lint_and_help(
6158
cx,

src/tools/clippy/tests/ui/to_string_trait_impl.rs

-43
Original file line numberDiff line numberDiff line change
@@ -30,46 +30,3 @@ impl Bar {
3030
String::from("Bar")
3131
}
3232
}
33-
34-
mod issue12263 {
35-
pub struct MyStringWrapper<'a>(&'a str);
36-
37-
impl std::fmt::Display for MyStringWrapper<'_> {
38-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39-
self.0.fmt(f)
40-
}
41-
}
42-
43-
impl ToString for MyStringWrapper<'_> {
44-
fn to_string(&self) -> String {
45-
self.0.to_string()
46-
}
47-
}
48-
49-
pub struct S<T>(T);
50-
impl std::fmt::Display for S<String> {
51-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52-
todo!()
53-
}
54-
}
55-
// no specialization if the generics differ, so lint
56-
impl ToString for S<i32> {
57-
fn to_string(&self) -> String {
58-
todo!()
59-
}
60-
}
61-
62-
pub struct S2<T>(T);
63-
impl std::fmt::Display for S2<String> {
64-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65-
todo!()
66-
}
67-
}
68-
69-
// also specialization if the generics don't differ
70-
impl ToString for S2<String> {
71-
fn to_string(&self) -> String {
72-
todo!()
73-
}
74-
}
75-
}

0 commit comments

Comments
 (0)