about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-12-15 04:45:24 +0000
committerbors <bors@rust-lang.org>2024-12-15 04:45:24 +0000
commit4790a435cbcb55c94ccdef51bf7a9b2e55824528 (patch)
tree742141865ec8c1b2fe10325b1983c0af7d83ea6b
parent7caf35b2e5401d7740fdc567fdc388059208150b (diff)
parent3f3f27bb84b46be8294e27c6a2d64f0c918f3679 (diff)
downloadrust-4790a435cbcb55c94ccdef51bf7a9b2e55824528.tar.gz
rust-4790a435cbcb55c94ccdef51bf7a9b2e55824528.zip
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.
-rw-r--r--compiler/rustc_span/src/lib.rs1
-rw-r--r--compiler/rustc_span/src/symbol.rs7
-rw-r--r--library/alloc/src/string.rs69
-rw-r--r--library/proc_macro/src/bridge/symbol.rs6
-rw-r--r--library/proc_macro/src/lib.rs77
-rw-r--r--src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs3
-rw-r--r--src/tools/clippy/tests/ui/to_string_trait_impl.rs43
-rw-r--r--src/tools/clippy/tests/ui/to_string_trait_impl.stderr14
-rw-r--r--tests/ui/specialization/min_specialization/issue-79224.rs10
-rw-r--r--tests/ui/specialization/min_specialization/issue-79224.stderr8
10 files changed, 58 insertions, 180 deletions
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index bd8b93bb4d1..6e25de847fc 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -25,7 +25,6 @@
 #![feature(hash_set_entry)]
 #![feature(if_let_guard)]
 #![feature(let_chains)]
-#![feature(min_specialization)]
 #![feature(negative_impls)]
 #![feature(read_buf)]
 #![feature(round_char_boundary)]
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index e3708896ba9..e51a05b313e 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -2476,13 +2476,6 @@ impl fmt::Display for Symbol {
     }
 }
 
-// takes advantage of `str::to_string` specialization
-impl ToString for Symbol {
-    fn to_string(&self) -> String {
-        self.as_str().to_string()
-    }
-}
-
 impl<CTX> HashStable<CTX> for Symbol {
     #[inline]
     fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index c5378d78d59..23d060d2158 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -2675,12 +2675,25 @@ pub trait ToString {
 #[cfg(not(no_global_oom_handling))]
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: fmt::Display + ?Sized> ToString for T {
+    #[inline]
+    fn to_string(&self) -> String {
+        <Self as SpecToString>::spec_to_string(self)
+    }
+}
+
+#[cfg(not(no_global_oom_handling))]
+trait SpecToString {
+    fn spec_to_string(&self) -> String;
+}
+
+#[cfg(not(no_global_oom_handling))]
+impl<T: fmt::Display + ?Sized> SpecToString for T {
     // A common guideline is to not inline generic functions. However,
     // removing `#[inline]` from this method causes non-negligible regressions.
     // See <https://github.com/rust-lang/rust/pull/74852>, the last attempt
     // to try to remove it.
     #[inline]
-    default fn to_string(&self) -> String {
+    default fn spec_to_string(&self) -> String {
         let mut buf = String::new();
         let mut formatter =
             core::fmt::Formatter::new(&mut buf, core::fmt::FormattingOptions::new());
@@ -2691,42 +2704,34 @@ impl<T: fmt::Display + ?Sized> ToString for T {
     }
 }
 
-#[doc(hidden)]
 #[cfg(not(no_global_oom_handling))]
-#[unstable(feature = "ascii_char", issue = "110998")]
-impl ToString for core::ascii::Char {
+impl SpecToString for core::ascii::Char {
     #[inline]
-    fn to_string(&self) -> String {
+    fn spec_to_string(&self) -> String {
         self.as_str().to_owned()
     }
 }
 
-#[doc(hidden)]
 #[cfg(not(no_global_oom_handling))]
-#[stable(feature = "char_to_string_specialization", since = "1.46.0")]
-impl ToString for char {
+impl SpecToString for char {
     #[inline]
-    fn to_string(&self) -> String {
+    fn spec_to_string(&self) -> String {
         String::from(self.encode_utf8(&mut [0; 4]))
     }
 }
 
-#[doc(hidden)]
 #[cfg(not(no_global_oom_handling))]
-#[stable(feature = "bool_to_string_specialization", since = "1.68.0")]
-impl ToString for bool {
+impl SpecToString for bool {
     #[inline]
-    fn to_string(&self) -> String {
+    fn spec_to_string(&self) -> String {
         String::from(if *self { "true" } else { "false" })
     }
 }
 
-#[doc(hidden)]
 #[cfg(not(no_global_oom_handling))]
-#[stable(feature = "u8_to_string_specialization", since = "1.54.0")]
-impl ToString for u8 {
+impl SpecToString for u8 {
     #[inline]
-    fn to_string(&self) -> String {
+    fn spec_to_string(&self) -> String {
         let mut buf = String::with_capacity(3);
         let mut n = *self;
         if n >= 10 {
@@ -2742,12 +2747,10 @@ impl ToString for u8 {
     }
 }
 
-#[doc(hidden)]
 #[cfg(not(no_global_oom_handling))]
-#[stable(feature = "i8_to_string_specialization", since = "1.54.0")]
-impl ToString for i8 {
+impl SpecToString for i8 {
     #[inline]
-    fn to_string(&self) -> String {
+    fn spec_to_string(&self) -> String {
         let mut buf = String::with_capacity(4);
         if self.is_negative() {
             buf.push('-');
@@ -2788,11 +2791,9 @@ macro_rules! to_string_expr_wrap_in_deref {
 macro_rules! to_string_str {
     {$($($x:ident)*),+} => {
         $(
-            #[doc(hidden)]
-            #[stable(feature = "str_to_string_specialization", since = "1.9.0")]
-            impl ToString for to_string_str_wrap_in_ref!($($x)*) {
+            impl SpecToString for to_string_str_wrap_in_ref!($($x)*) {
                 #[inline]
-                fn to_string(&self) -> String {
+                fn spec_to_string(&self) -> String {
                     String::from(to_string_expr_wrap_in_deref!(self ; $($x)*))
                 }
             }
@@ -2816,32 +2817,26 @@ to_string_str! {
     x,
 }
 
-#[doc(hidden)]
 #[cfg(not(no_global_oom_handling))]
-#[stable(feature = "cow_str_to_string_specialization", since = "1.17.0")]
-impl ToString for Cow<'_, str> {
+impl SpecToString for Cow<'_, str> {
     #[inline]
-    fn to_string(&self) -> String {
+    fn spec_to_string(&self) -> String {
         self[..].to_owned()
     }
 }
 
-#[doc(hidden)]
 #[cfg(not(no_global_oom_handling))]
-#[stable(feature = "string_to_string_specialization", since = "1.17.0")]
-impl ToString for String {
+impl SpecToString for String {
     #[inline]
-    fn to_string(&self) -> String {
+    fn spec_to_string(&self) -> String {
         self.to_owned()
     }
 }
 
-#[doc(hidden)]
 #[cfg(not(no_global_oom_handling))]
-#[stable(feature = "fmt_arguments_to_string_specialization", since = "1.71.0")]
-impl ToString for fmt::Arguments<'_> {
+impl SpecToString for fmt::Arguments<'_> {
     #[inline]
-    fn to_string(&self) -> String {
+    fn spec_to_string(&self) -> String {
         crate::fmt::format(*self)
     }
 }
diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs
index edad6e7ac39..6a1cecd69fb 100644
--- a/library/proc_macro/src/bridge/symbol.rs
+++ b/library/proc_macro/src/bridge/symbol.rs
@@ -91,12 +91,6 @@ impl fmt::Debug for Symbol {
     }
 }
 
-impl ToString for Symbol {
-    fn to_string(&self) -> String {
-        self.with(|s| s.to_owned())
-    }
-}
-
 impl fmt::Display for Symbol {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.with(|s| fmt::Display::fmt(s, f))
diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index 4aa47ce4e4f..26a09f0daca 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -19,9 +19,6 @@
 )]
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
-// This library is copied into rust-analyzer to allow loading rustc compiled proc macros.
-// Please avoid unstable features where possible to minimize the amount of changes necessary
-// to make it compile with rust-analyzer on stable.
 #![feature(staged_api)]
 #![feature(allow_internal_unstable)]
 #![feature(decl_macro)]
@@ -30,7 +27,6 @@
 #![feature(panic_can_unwind)]
 #![feature(restricted_std)]
 #![feature(rustc_attrs)]
-#![feature(min_specialization)]
 #![feature(extend_one)]
 #![recursion_limit = "256"]
 #![allow(internal_features)]
@@ -185,16 +181,6 @@ impl FromStr for TokenStream {
     }
 }
 
-// N.B., the bridge only provides `to_string`, implement `fmt::Display`
-// based on it (the reverse of the usual relationship between the two).
-#[doc(hidden)]
-#[stable(feature = "proc_macro_lib", since = "1.15.0")]
-impl ToString for TokenStream {
-    fn to_string(&self) -> String {
-        self.0.as_ref().map(|t| t.to_string()).unwrap_or_default()
-    }
-}
-
 /// Prints the token stream as a string that is supposed to be losslessly convertible back
 /// into the same token stream (modulo spans), except for possibly `TokenTree::Group`s
 /// with `Delimiter::None` delimiters and negative numeric literals.
@@ -210,7 +196,10 @@ impl ToString for TokenStream {
 impl fmt::Display for TokenStream {
     #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(&self.to_string())
+        match &self.0 {
+            Some(ts) => write!(f, "{}", ts.to_string()),
+            None => Ok(()),
+        }
     }
 }
 
@@ -756,21 +745,6 @@ impl From<Literal> for TokenTree {
     }
 }
 
-// N.B., the bridge only provides `to_string`, implement `fmt::Display`
-// based on it (the reverse of the usual relationship between the two).
-#[doc(hidden)]
-#[stable(feature = "proc_macro_lib", since = "1.15.0")]
-impl ToString for TokenTree {
-    fn to_string(&self) -> String {
-        match *self {
-            TokenTree::Group(ref t) => t.to_string(),
-            TokenTree::Ident(ref t) => t.to_string(),
-            TokenTree::Punct(ref t) => t.to_string(),
-            TokenTree::Literal(ref t) => t.to_string(),
-        }
-    }
-}
-
 /// Prints the token tree as a string that is supposed to be losslessly convertible back
 /// into the same token tree (modulo spans), except for possibly `TokenTree::Group`s
 /// with `Delimiter::None` delimiters and negative numeric literals.
@@ -786,7 +760,12 @@ impl ToString for TokenTree {
 impl fmt::Display for TokenTree {
     #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(&self.to_string())
+        match self {
+            TokenTree::Group(t) => write!(f, "{t}"),
+            TokenTree::Ident(t) => write!(f, "{t}"),
+            TokenTree::Punct(t) => write!(f, "{t}"),
+            TokenTree::Literal(t) => write!(f, "{t}"),
+        }
     }
 }
 
@@ -912,16 +891,6 @@ impl Group {
     }
 }
 
-// N.B., the bridge only provides `to_string`, implement `fmt::Display`
-// based on it (the reverse of the usual relationship between the two).
-#[doc(hidden)]
-#[stable(feature = "proc_macro_lib", since = "1.15.0")]
-impl ToString for Group {
-    fn to_string(&self) -> String {
-        TokenStream::from(TokenTree::from(self.clone())).to_string()
-    }
-}
-
 /// Prints the group as a string that should be losslessly convertible back
 /// into the same group (modulo spans), except for possibly `TokenTree::Group`s
 /// with `Delimiter::None` delimiters.
@@ -929,7 +898,7 @@ impl ToString for Group {
 impl fmt::Display for Group {
     #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(&self.to_string())
+        write!(f, "{}", TokenStream::from(TokenTree::from(self.clone())))
     }
 }
 
@@ -1035,14 +1004,6 @@ impl Punct {
     }
 }
 
-#[doc(hidden)]
-#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
-impl ToString for Punct {
-    fn to_string(&self) -> String {
-        self.as_char().to_string()
-    }
-}
-
 /// Prints the punctuation character as a string that should be losslessly convertible
 /// back into the same character.
 #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
@@ -1138,14 +1099,6 @@ impl Ident {
     }
 }
 
-#[doc(hidden)]
-#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
-impl ToString for Ident {
-    fn to_string(&self) -> String {
-        self.0.sym.with(|sym| if self.0.is_raw { ["r#", sym].concat() } else { sym.to_owned() })
-    }
-}
-
 /// Prints the identifier as a string that should be losslessly convertible back
 /// into the same identifier.
 #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
@@ -1520,14 +1473,6 @@ impl FromStr for Literal {
     }
 }
 
-#[doc(hidden)]
-#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
-impl ToString for Literal {
-    fn to_string(&self) -> String {
-        self.with_stringify_parts(|parts| parts.concat())
-    }
-}
-
 /// Prints the literal as a string that should be losslessly convertible
 /// back into the same literal (except for possible rounding for floating point literals).
 #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
diff --git a/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs b/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs
index 0361836cdec..9596b85664b 100644
--- a/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::ty::implements_trait;
 use rustc_hir::{Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -54,8 +53,6 @@ impl<'tcx> LateLintPass<'tcx> for ToStringTraitImpl {
         }) = it.kind
             && let Some(trait_did) = trait_ref.trait_def_id()
             && cx.tcx.is_diagnostic_item(sym::ToString, trait_did)
-            && let Some(display_did) = cx.tcx.get_diagnostic_item(sym::Display)
-            && !implements_trait(cx, cx.tcx.type_of(it.owner_id).instantiate_identity(), display_did, &[])
         {
             span_lint_and_help(
                 cx,
diff --git a/src/tools/clippy/tests/ui/to_string_trait_impl.rs b/src/tools/clippy/tests/ui/to_string_trait_impl.rs
index 4c1202d4203..7be9f7994f0 100644
--- a/src/tools/clippy/tests/ui/to_string_trait_impl.rs
+++ b/src/tools/clippy/tests/ui/to_string_trait_impl.rs
@@ -30,46 +30,3 @@ impl Bar {
         String::from("Bar")
     }
 }
-
-mod issue12263 {
-    pub struct MyStringWrapper<'a>(&'a str);
-
-    impl std::fmt::Display for MyStringWrapper<'_> {
-        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-            self.0.fmt(f)
-        }
-    }
-
-    impl ToString for MyStringWrapper<'_> {
-        fn to_string(&self) -> String {
-            self.0.to_string()
-        }
-    }
-
-    pub struct S<T>(T);
-    impl std::fmt::Display for S<String> {
-        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-            todo!()
-        }
-    }
-    // no specialization if the generics differ, so lint
-    impl ToString for S<i32> {
-        fn to_string(&self) -> String {
-            todo!()
-        }
-    }
-
-    pub struct S2<T>(T);
-    impl std::fmt::Display for S2<String> {
-        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-            todo!()
-        }
-    }
-
-    // also specialization if the generics don't differ
-    impl ToString for S2<String> {
-        fn to_string(&self) -> String {
-            todo!()
-        }
-    }
-}
diff --git a/src/tools/clippy/tests/ui/to_string_trait_impl.stderr b/src/tools/clippy/tests/ui/to_string_trait_impl.stderr
index 304c9a5e1fb..fe8afc215f0 100644
--- a/src/tools/clippy/tests/ui/to_string_trait_impl.stderr
+++ b/src/tools/clippy/tests/ui/to_string_trait_impl.stderr
@@ -12,17 +12,5 @@ LL | | }
    = note: `-D clippy::to-string-trait-impl` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::to_string_trait_impl)]`
 
-error: direct implementation of `ToString`
-  --> tests/ui/to_string_trait_impl.rs:56:5
-   |
-LL | /     impl ToString for S<i32> {
-LL | |         fn to_string(&self) -> String {
-LL | |             todo!()
-LL | |         }
-LL | |     }
-   | |_____^
-   |
-   = help: prefer implementing `Display` instead
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
diff --git a/tests/ui/specialization/min_specialization/issue-79224.rs b/tests/ui/specialization/min_specialization/issue-79224.rs
index 6ddd3d79ccf..15429bf60e5 100644
--- a/tests/ui/specialization/min_specialization/issue-79224.rs
+++ b/tests/ui/specialization/min_specialization/issue-79224.rs
@@ -1,6 +1,16 @@
 #![feature(min_specialization)]
 use std::fmt::{self, Display};
 
+pub trait ToString {
+    fn to_string(&self) -> String;
+}
+
+impl<T: Display + ?Sized> ToString for T {
+    default fn to_string(&self) -> String {
+        todo!()
+    }
+}
+
 pub enum Cow<'a, B: ?Sized + 'a, O = <B as ToOwned>::Owned>
 where
     B: ToOwned,
diff --git a/tests/ui/specialization/min_specialization/issue-79224.stderr b/tests/ui/specialization/min_specialization/issue-79224.stderr
index 2b8169af6a2..b2118ccd81b 100644
--- a/tests/ui/specialization/min_specialization/issue-79224.stderr
+++ b/tests/ui/specialization/min_specialization/issue-79224.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `B: Clone` is not satisfied
-  --> $DIR/issue-79224.rs:18:29
+  --> $DIR/issue-79224.rs:28:29
    |
 LL | impl<B: ?Sized> Display for Cow<'_, B> {
    |                             ^^^^^^^^^^ the trait `Clone` is not implemented for `B`
@@ -11,7 +11,7 @@ LL | impl<B: ?Sized + std::clone::Clone> Display for Cow<'_, B> {
    |                +++++++++++++++++++
 
 error[E0277]: the trait bound `B: Clone` is not satisfied
-  --> $DIR/issue-79224.rs:20:5
+  --> $DIR/issue-79224.rs:30:5
    |
 LL |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `B`
@@ -23,7 +23,7 @@ LL | impl<B: ?Sized + std::clone::Clone> Display for Cow<'_, B> {
    |                +++++++++++++++++++
 
 error[E0277]: the trait bound `B: Clone` is not satisfied
-  --> $DIR/issue-79224.rs:20:13
+  --> $DIR/issue-79224.rs:30:13
    |
 LL |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    |             ^^^^ the trait `Clone` is not implemented for `B`
@@ -35,7 +35,7 @@ LL | impl<B: ?Sized + std::clone::Clone> Display for Cow<'_, B> {
    |                +++++++++++++++++++
 
 error[E0277]: the trait bound `B: Clone` is not satisfied
-  --> $DIR/issue-79224.rs:20:62
+  --> $DIR/issue-79224.rs:30:62
    |
 LL |       fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    |  ______________________________________________________________^