Skip to content

Commit

Permalink
Rollup merge of #128759 - notriddle:notriddle/spec-to-string, r=worki…
Browse files Browse the repository at this point in the history
…ngjubilee,compiler-errors

alloc: add ToString specialization for `&&str`

Fixes #128690
  • Loading branch information
matthiaskrgr committed Aug 14, 2024
2 parents 2cb5908 + c6fb0f3 commit 85180cd
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 16 deletions.
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,
}

#[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

0 comments on commit 85180cd

Please sign in to comment.