Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove support for specializing ToString outside the standard library #134258

Merged
merged 3 commits into from
Dec 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion compiler/rustc_span/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2471,13 +2471,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) {
Expand Down
69 changes: 32 additions & 37 deletions library/alloc/src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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 {
Expand All @@ -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('-');
Expand Down Expand Up @@ -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)*))
}
}
Expand All @@ -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)
}
}
Expand Down
6 changes: 0 additions & 6 deletions library/proc_macro/src/bridge/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
77 changes: 11 additions & 66 deletions library/proc_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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)]
Expand Down Expand Up @@ -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.
Expand All @@ -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(()),
}
}
}

Expand Down Expand Up @@ -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.
Expand All @@ -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}"),
}
}
}

Expand Down Expand Up @@ -912,24 +891,14 @@ 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.
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
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())))
}
}

Expand Down Expand Up @@ -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")]
Expand Down Expand Up @@ -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")]
Expand Down Expand Up @@ -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")]
Expand Down
3 changes: 0 additions & 3 deletions src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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,
Expand Down
43 changes: 0 additions & 43 deletions src/tools/clippy/tests/ui/to_string_trait_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!()
}
}
}
Loading
Loading