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:
Matthias Krüger 2024-08-14 05:05:51 +02:00 committed by GitHub
commit 85180cd365
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 112 additions and 16 deletions

View file

@ -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 \

View file

@ -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)]

View 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()
}

View file

@ -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

View file

@ -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