Rollup merge of #128759 - notriddle:notriddle/spec-to-string, r=workingjubilee,compiler-errors
alloc: add ToString specialization for `&&str` Fixes #128690
This commit is contained in:
commit
85180cd365
5 changed files with 112 additions and 16 deletions
|
@ -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;
|
||||
|
@ -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 \
|
||||
|
@ -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 \
|
||||
|
@ -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 \
|
||||
|
|
|
@ -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)]
|
||||
|
|
36
tests/codegen/issues/str-to-string-128690.rs
Normal file
36
tests/codegen/issues/str-to-string-128690.rs
Normal file
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue