Skip to content

Commit 2f81e0d

Browse files
committed
Auto merge of rust-lang#139172 - lcnr:coerce-perf-2, r=<try>
more interesting fix for coercion hack perf regression cc rust-lang#136127 rust-lang#138542 rust-lang#139171 r? `@ghost`
2 parents 10a76d6 + 363d339 commit 2f81e0d

File tree

1 file changed

+58
-46
lines changed

1 file changed

+58
-46
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

+58-46
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ use rustc_middle::ty::error::TypeError;
5858
use rustc_middle::ty::{self, AliasTy, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
5959
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span};
6060
use rustc_trait_selection::infer::InferCtxtExt as _;
61+
use rustc_trait_selection::traits::EvaluationResult::*;
6162
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
6263
use rustc_trait_selection::traits::{
6364
self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt,
@@ -599,54 +600,65 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
599600
ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]),
600601
);
601602

602-
// If the root `Source: CoerceUnsized<Target>` obligation can't possibly hold,
603-
// we don't have to assume that this is unsizing coercion (it will always lead to an error)
604-
//
605-
// However, we don't want to bail early all the time, since the unholdable obligations
606-
// may be interesting for diagnostics (such as trying to coerce `&T` to `&dyn Id<This = U>`),
607-
// so we only bail if there (likely) is another way to convert the types.
608-
if !self.infcx.predicate_may_hold(&root_obligation) {
609-
if let Some(dyn_metadata_adt_def_id) = self.tcx.lang_items().get(LangItem::DynMetadata)
610-
&& let Some(metadata_type_def_id) = self.tcx.lang_items().get(LangItem::Metadata)
611-
{
612-
self.probe(|_| {
613-
let ocx = ObligationCtxt::new(&self.infcx);
614-
615-
// returns `true` if `<ty as Pointee>::Metadata` is `DynMetadata<_>`
616-
let has_dyn_trait_metadata = |ty| {
617-
let metadata_ty: Result<_, _> = ocx.structurally_normalize_ty(
618-
&ObligationCause::dummy(),
619-
self.fcx.param_env,
620-
Ty::new_alias(
621-
self.tcx,
622-
ty::AliasTyKind::Projection,
623-
AliasTy::new(self.tcx, metadata_type_def_id, [ty]),
624-
),
625-
);
603+
match self.infcx.evaluate_obligation_no_overflow(&root_obligation) {
604+
// Fast path if we're definitely able to coerce. This allows us to use the
605+
// cache of the `FulfillmentContext` and avoids manual calls to select.
606+
EvaluatedToOk | EvaluatedToOkModuloRegions | EvaluatedToOkModuloOpaqueTypes => {
607+
coercion.obligations.push(root_obligation);
608+
return Ok(coercion);
609+
}
610+
EvaluatedToAmbig | EvaluatedToAmbigStackDependent => {}
611+
EvaluatedToErr => {
612+
// If the root `Source: CoerceUnsized<Target>` obligation can't possibly hold,
613+
// we don't have to assume that this is unsizing coercion (it will always lead to an error)
614+
//
615+
// However, we don't want to bail early all the time, since the unholdable obligations
616+
// may be interesting for diagnostics (such as trying to coerce `&T` to `&dyn Id<This = U>`),
617+
// so we only bail if there (likely) is another way to convert the types.
618+
if let &ty::RawPtr(source_pointee, _) = coerce_source.kind()
619+
&& let &ty::RawPtr(target_pointee, _) = target.kind()
620+
&& let Some(dyn_metadata_adt_def_id) =
621+
self.tcx.lang_items().get(LangItem::DynMetadata)
622+
&& let Some(metadata_type_def_id) =
623+
self.tcx.lang_items().get(LangItem::Metadata)
624+
{
625+
self.probe(|_| {
626+
let ocx = ObligationCtxt::new(&self.infcx);
627+
628+
// returns `true` if `<ty as Pointee>::Metadata` is `DynMetadata<_>`
629+
let has_dyn_trait_metadata = |ty| {
630+
let metadata_ty: Result<_, _> = ocx.structurally_normalize_ty(
631+
&ObligationCause::dummy(),
632+
self.fcx.param_env,
633+
Ty::new_alias(
634+
self.tcx,
635+
ty::AliasTyKind::Projection,
636+
AliasTy::new(self.tcx, metadata_type_def_id, [ty]),
637+
),
638+
);
626639

627-
metadata_ty.is_ok_and(|metadata_ty| {
628-
metadata_ty
629-
.ty_adt_def()
630-
.is_some_and(|d| d.did() == dyn_metadata_adt_def_id)
631-
})
632-
};
633-
634-
// If both types are raw pointers to a (wrapper over a) trait object,
635-
// this might be a cast like `*const W<dyn Trait> -> *const dyn Trait`.
636-
// So it's better to bail and try that. (even if the cast is not possible, for
637-
// example due to vtables not matching, cast diagnostic will likely still be better)
638-
//
639-
// N.B. use `target`, not `coerce_target` (the latter is a var)
640-
if let &ty::RawPtr(source_pointee, _) = coerce_source.kind()
641-
&& let &ty::RawPtr(target_pointee, _) = target.kind()
642-
&& has_dyn_trait_metadata(source_pointee)
643-
&& has_dyn_trait_metadata(target_pointee)
644-
{
645-
return Err(TypeError::Mismatch);
646-
}
640+
metadata_ty.is_ok_and(|metadata_ty| {
641+
metadata_ty
642+
.ty_adt_def()
643+
.is_some_and(|d| d.did() == dyn_metadata_adt_def_id)
644+
})
645+
};
647646

648-
Ok(())
649-
})?;
647+
// If both types are raw pointers to a (wrapper over a) trait object,
648+
// this might be a cast like `*const W<dyn Trait> -> *const dyn Trait`.
649+
// So it's better to bail and try that. (even if the cast is not possible, for
650+
// example due to vtables not matching, cast diagnostic will likely still be better)
651+
//
652+
// N.B. use `target`, not `coerce_target` (the latter is a var)
653+
if has_dyn_trait_metadata(source_pointee)
654+
&& has_dyn_trait_metadata(target_pointee)
655+
{
656+
return Err(TypeError::Mismatch);
657+
}
658+
659+
Ok(())
660+
})?;
661+
}
650662
}
651663
}
652664

0 commit comments

Comments
 (0)