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

alloc: add ToString specialization for &&str #128759

Merged
merged 4 commits into from
Aug 14, 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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::print::{FmtPrinter, Printer};
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
use rustc_span::def_id::DefId;
Expand Down Expand Up @@ -313,11 +314,15 @@ impl<T> Trait<T> for X {
(ty::Dynamic(t, _, ty::DynKind::Dyn), _)
if let Some(def_id) = t.principal_def_id() =>
{
let mut impl_def_ids = vec![];
let mut has_matching_impl = false;
tcx.for_each_relevant_impl(def_id, values.found, |did| {
impl_def_ids.push(did)
if DeepRejectCtxt::new(tcx, TreatParams::ForLookup)
.types_may_unify(values.found, tcx.type_of(did).skip_binder())
{
has_matching_impl = true;
}
});
if let [_] = &impl_def_ids[..] {
if has_matching_impl {
let trait_name = tcx.item_name(def_id);
diag.help(format!(
"`{}` implements `{trait_name}` so you could box the found value \
Expand All @@ -330,11 +335,15 @@ impl<T> Trait<T> for X {
(_, ty::Dynamic(t, _, ty::DynKind::Dyn))
if let Some(def_id) = t.principal_def_id() =>
{
let mut impl_def_ids = vec![];
let mut has_matching_impl = false;
tcx.for_each_relevant_impl(def_id, values.expected, |did| {
impl_def_ids.push(did)
if DeepRejectCtxt::new(tcx, TreatParams::ForLookup)
.types_may_unify(values.expected, tcx.type_of(did).skip_binder())
{
has_matching_impl = true;
}
});
if let [_] = &impl_def_ids[..] {
if has_matching_impl {
let trait_name = tcx.item_name(def_id);
diag.help(format!(
"`{}` implements `{trait_name}` so you could change the expected \
Expand All @@ -346,11 +355,15 @@ impl<T> Trait<T> for X {
(ty::Dynamic(t, _, ty::DynKind::DynStar), _)
if let Some(def_id) = t.principal_def_id() =>
{
let mut impl_def_ids = vec![];
let mut has_matching_impl = false;
tcx.for_each_relevant_impl(def_id, values.found, |did| {
impl_def_ids.push(did)
if DeepRejectCtxt::new(tcx, TreatParams::ForLookup)
.types_may_unify(values.found, tcx.type_of(did).skip_binder())
{
has_matching_impl = true;
}
});
if let [_] = &impl_def_ids[..] {
if has_matching_impl {
let trait_name = tcx.item_name(def_id);
diag.help(format!(
"`{}` implements `{trait_name}`, `#[feature(dyn_star)]` is likely \
Expand Down
54 changes: 47 additions & 7 deletions library/alloc/src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2643,14 +2643,54 @@ impl ToString for i8 {
}
}

#[doc(hidden)]
// Generic/generated code can sometimes have multiple, nested references
// for strings, including `&&&str`s that would never be written
// by hand. This macro generates twelve layers of nested `&`-impl
// for primitive strings.
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "str_to_string_specialization", since = "1.9.0")]
impl ToString for str {
#[inline]
fn to_string(&self) -> String {
String::from(self)
}
macro_rules! to_string_str_wrap_in_ref {
{x $($x:ident)*} => {
&to_string_str_wrap_in_ref! { $($x)* }
};
{} => { str };
}
#[cfg(not(no_global_oom_handling))]
macro_rules! to_string_expr_wrap_in_deref {
{$self:expr ; x $($x:ident)*} => {
*(to_string_expr_wrap_in_deref! { $self ; $($x)* })
};
{$self:expr ;} => { $self };
}
#[cfg(not(no_global_oom_handling))]
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)*) {
#[inline]
fn to_string(&self) -> String {
String::from(to_string_expr_wrap_in_deref!(self ; $($x)*))
}
}
)+
};
}

#[cfg(not(no_global_oom_handling))]
to_string_str! {
x x x x x x x x x x x x,
x x x x x x x x x x x,
x x x x x x x x x x,
x x x x x x x x x,
x x x x x x x x,
x x x x x x x,
x x x x x x,
x x x x x,
x x x x,
x x x,
x x,
x,
notriddle marked this conversation as resolved.
Show resolved Hide resolved
}

#[doc(hidden)]
Expand Down
36 changes: 36 additions & 0 deletions tests/codegen/issues/str-to-string-128690.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//@ compile-flags: -C opt-level=3 -Z merge-functions=disabled
#![crate_type = "lib"]

//! Make sure str::to_string is specialized not to use fmt machinery.

// CHECK-LABEL: define {{(dso_local )?}}void @one_ref
#[no_mangle]
pub fn one_ref(input: &str) -> String {
// CHECK-NOT: {{(call|invoke).*}}fmt
input.to_string()
}

// CHECK-LABEL: define {{(dso_local )?}}void @two_ref
#[no_mangle]
pub fn two_ref(input: &&str) -> String {
// CHECK-NOT: {{(call|invoke).*}}fmt
input.to_string()
}

// CHECK-LABEL: define {{(dso_local )?}}void @thirteen_ref
#[no_mangle]
pub fn thirteen_ref(input: &&&&&&&&&&&&&str) -> String {
// CHECK-NOT: {{(call|invoke).*}}fmt
input.to_string()
}

// This is a known performance cliff because of the macro-generated
// specialized impl. If this test suddenly starts failing,
// consider removing the `to_string_str!` macro in `alloc/str/string.rs`.
//
// CHECK-LABEL: define {{(dso_local )?}}void @fourteen_ref
#[no_mangle]
pub fn fourteen_ref(input: &&&&&&&&&&&&&&str) -> String {
// CHECK: {{(call|invoke).*}}fmt
input.to_string()
}
3 changes: 3 additions & 0 deletions tests/ui/cast/ptr-to-trait-obj-different-args.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ LL | let y: *const dyn Trait<Y> = x as _;
|
= note: expected trait object `dyn Trait<X>`
found trait object `dyn Trait<Y>`
= help: `dyn Trait<Y>` implements `Trait` so you could box the found value and coerce it to the trait object `Box<dyn Trait>`, you will have to change the expected type as well

error[E0308]: mismatched types
--> $DIR/ptr-to-trait-obj-different-args.rs:27:34
Expand All @@ -25,6 +26,7 @@ LL | let _: *const dyn Trait<T> = x as _;
|
= note: expected trait object `dyn Trait<X>`
found trait object `dyn Trait<T>`
= help: `dyn Trait<T>` implements `Trait` so you could box the found value and coerce it to the trait object `Box<dyn Trait>`, you will have to change the expected type as well

error[E0308]: mismatched types
--> $DIR/ptr-to-trait-obj-different-args.rs:28:34
Expand All @@ -37,6 +39,7 @@ LL | let _: *const dyn Trait<X> = t as _;
|
= note: expected trait object `dyn Trait<T>`
found trait object `dyn Trait<X>`
= help: `dyn Trait<X>` implements `Trait` so you could box the found value and coerce it to the trait object `Box<dyn Trait>`, you will have to change the expected type as well

error[E0308]: mismatched types
--> $DIR/ptr-to-trait-obj-different-args.rs:36:5
Expand Down
4 changes: 4 additions & 0 deletions tests/ui/coercion/coerce-expect-unsized-ascribed.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ LL | let _ = type_ascribe!(Box::new( if true { false } else { true }), Box<d
|
= note: expected struct `Box<dyn Debug>`
found struct `Box<bool>`
= help: `bool` implements `Debug` so you could box the found value and coerce it to the trait object `Box<dyn Debug>`, you will have to change the expected type as well

error[E0308]: mismatched types
--> $DIR/coerce-expect-unsized-ascribed.rs:16:27
Expand All @@ -51,6 +52,7 @@ LL | let _ = type_ascribe!(Box::new( match true { true => 'a', false => 'b'
|
= note: expected struct `Box<dyn Debug>`
found struct `Box<char>`
= help: `char` implements `Debug` so you could box the found value and coerce it to the trait object `Box<dyn Debug>`, you will have to change the expected type as well

error[E0308]: mismatched types
--> $DIR/coerce-expect-unsized-ascribed.rs:18:27
Expand Down Expand Up @@ -96,6 +98,7 @@ LL | let _ = type_ascribe!(&if true { false } else { true }, &dyn Debug);
|
= note: expected reference `&dyn Debug`
found reference `&bool`
= help: `bool` implements `Debug` so you could box the found value and coerce it to the trait object `Box<dyn Debug>`, you will have to change the expected type as well

error[E0308]: mismatched types
--> $DIR/coerce-expect-unsized-ascribed.rs:24:27
Expand All @@ -105,6 +108,7 @@ LL | let _ = type_ascribe!(&match true { true => 'a', false => 'b' }, &dyn D
|
= note: expected reference `&dyn Debug`
found reference `&char`
= help: `char` implements `Debug` so you could box the found value and coerce it to the trait object `Box<dyn Debug>`, you will have to change the expected type as well

error[E0308]: mismatched types
--> $DIR/coerce-expect-unsized-ascribed.rs:26:27
Expand Down
Loading