Skip to content

Commit 8eb675b

Browse files
Auto merge of #141926 - compiler-errors:perf-unsizing, r=<try>
[PERF] Rework unsizing coercions in new solver r? `@ghost`
2 parents 2398bd6 + f4a5840 commit 8eb675b

File tree

7 files changed

+168
-110
lines changed

7 files changed

+168
-110
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 162 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@
3535
//! // and are then unable to coerce `&7i32` to `&mut i32`.
3636
//! ```
3737
38-
use std::ops::Deref;
38+
use std::ops::{ControlFlow, Deref};
3939

4040
use rustc_attr_data_structures::InlineAttr;
4141
use rustc_errors::codes::*;
4242
use rustc_errors::{Applicability, Diag, struct_span_code_err};
43-
use rustc_hir as hir;
4443
use rustc_hir::def_id::{DefId, LocalDefId};
44+
use rustc_hir::{self as hir, LangItem};
4545
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
4646
use rustc_infer::infer::relate::RelateResult;
4747
use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
@@ -57,6 +57,8 @@ use rustc_middle::ty::error::TypeError;
5757
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
5858
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span};
5959
use rustc_trait_selection::infer::InferCtxtExt as _;
60+
use rustc_trait_selection::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor};
61+
use rustc_trait_selection::solve::{Certainty, Goal, NoSolution};
6062
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
6163
use rustc_trait_selection::traits::{
6264
self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt,
@@ -593,118 +595,128 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
593595
Adjust::Pointer(PointerCoercion::Unsize),
594596
)?;
595597

596-
let mut selcx = traits::SelectionContext::new(self);
597-
598598
// Create an obligation for `Source: CoerceUnsized<Target>`.
599599
let cause = self.cause(self.cause.span, ObligationCauseCode::Coercion { source, target });
600+
let pred = ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]);
601+
let obligation = Obligation::new(self.tcx, cause, self.fcx.param_env, pred);
600602

601-
// Use a FIFO queue for this custom fulfillment procedure.
602-
//
603-
// A Vec (or SmallVec) is not a natural choice for a queue. However,
604-
// this code path is hot, and this queue usually has a max length of 1
605-
// and almost never more than 3. By using a SmallVec we avoid an
606-
// allocation, at the (very small) cost of (occasionally) having to
607-
// shift subsequent elements down when removing the front element.
608-
let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![Obligation::new(
609-
self.tcx,
610-
cause,
611-
self.fcx.param_env,
612-
ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target])
613-
)];
614-
615-
// Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
616-
// emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
617-
// inference might unify those two inner type variables later.
618-
let traits = [coerce_unsized_did, unsize_did];
619-
while !queue.is_empty() {
620-
let obligation = queue.remove(0);
621-
let trait_pred = match obligation.predicate.kind().no_bound_vars() {
622-
Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)))
623-
if traits.contains(&trait_pred.def_id()) =>
624-
{
625-
self.resolve_vars_if_possible(trait_pred)
626-
}
627-
// Eagerly process alias-relate obligations in new trait solver,
628-
// since these can be emitted in the process of solving trait goals,
629-
// but we need to constrain vars before processing goals mentioning
630-
// them.
631-
Some(ty::PredicateKind::AliasRelate(..)) => {
632-
let ocx = ObligationCtxt::new(self);
633-
ocx.register_obligation(obligation);
634-
if !ocx.select_where_possible().is_empty() {
635-
return Err(TypeError::Mismatch);
603+
if self.next_trait_solver() {
604+
coercion.obligations.push(obligation);
605+
606+
if self
607+
.infcx
608+
.visit_proof_tree(
609+
Goal::new(self.tcx, self.param_env, pred),
610+
&mut CoerceVisitor { fcx: self.fcx, span: self.cause.span },
611+
)
612+
.is_break()
613+
{
614+
return Err(TypeError::Mismatch);
615+
}
616+
} else {
617+
let mut selcx = traits::SelectionContext::new(self);
618+
// Use a FIFO queue for this custom fulfillment procedure.
619+
//
620+
// A Vec (or SmallVec) is not a natural choice for a queue. However,
621+
// this code path is hot, and this queue usually has a max length of 1
622+
// and almost never more than 3. By using a SmallVec we avoid an
623+
// allocation, at the (very small) cost of (occasionally) having to
624+
// shift subsequent elements down when removing the front element.
625+
let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![obligation];
626+
627+
// Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
628+
// emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
629+
// inference might unify those two inner type variables later.
630+
let traits = [coerce_unsized_did, unsize_did];
631+
while !queue.is_empty() {
632+
let obligation = queue.remove(0);
633+
let trait_pred = match obligation.predicate.kind().no_bound_vars() {
634+
Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)))
635+
if traits.contains(&trait_pred.def_id()) =>
636+
{
637+
self.resolve_vars_if_possible(trait_pred)
636638
}
637-
coercion.obligations.extend(ocx.into_pending_obligations());
638-
continue;
639-
}
640-
_ => {
641-
coercion.obligations.push(obligation);
642-
continue;
643-
}
644-
};
645-
debug!("coerce_unsized resolve step: {:?}", trait_pred);
646-
match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) {
647-
// Uncertain or unimplemented.
648-
Ok(None) => {
649-
if trait_pred.def_id() == unsize_did {
650-
let self_ty = trait_pred.self_ty();
651-
let unsize_ty = trait_pred.trait_ref.args[1].expect_ty();
652-
debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred);
653-
match (self_ty.kind(), unsize_ty.kind()) {
654-
(&ty::Infer(ty::TyVar(v)), ty::Dynamic(..))
655-
if self.type_var_is_sized(v) =>
656-
{
657-
debug!("coerce_unsized: have sized infer {:?}", v);
658-
coercion.obligations.push(obligation);
659-
// `$0: Unsize<dyn Trait>` where we know that `$0: Sized`, try going
660-
// for unsizing.
661-
}
662-
_ => {
663-
// Some other case for `$0: Unsize<Something>`. Note that we
664-
// hit this case even if `Something` is a sized type, so just
665-
// don't do the coercion.
666-
debug!("coerce_unsized: ambiguous unsize");
667-
return Err(TypeError::Mismatch);
639+
// Eagerly process alias-relate obligations in new trait solver,
640+
// since these can be emitted in the process of solving trait goals,
641+
// but we need to constrain vars before processing goals mentioning
642+
// them.
643+
Some(ty::PredicateKind::AliasRelate(..)) => {
644+
let ocx = ObligationCtxt::new(self);
645+
ocx.register_obligation(obligation);
646+
if !ocx.select_where_possible().is_empty() {
647+
return Err(TypeError::Mismatch);
648+
}
649+
coercion.obligations.extend(ocx.into_pending_obligations());
650+
continue;
651+
}
652+
_ => {
653+
coercion.obligations.push(obligation);
654+
continue;
655+
}
656+
};
657+
debug!("coerce_unsized resolve step: {:?}", trait_pred);
658+
match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) {
659+
// Uncertain or unimplemented.
660+
Ok(None) => {
661+
if trait_pred.def_id() == unsize_did {
662+
let self_ty = trait_pred.self_ty();
663+
let unsize_ty = trait_pred.trait_ref.args[1].expect_ty();
664+
debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred);
665+
match (self_ty.kind(), unsize_ty.kind()) {
666+
(&ty::Infer(ty::TyVar(v)), ty::Dynamic(..))
667+
if self.type_var_is_sized(v) =>
668+
{
669+
debug!("coerce_unsized: have sized infer {:?}", v);
670+
coercion.obligations.push(obligation);
671+
// `$0: Unsize<dyn Trait>` where we know that `$0: Sized`, try going
672+
// for unsizing.
673+
}
674+
_ => {
675+
// Some other case for `$0: Unsize<Something>`. Note that we
676+
// hit this case even if `Something` is a sized type, so just
677+
// don't do the coercion.
678+
debug!("coerce_unsized: ambiguous unsize");
679+
return Err(TypeError::Mismatch);
680+
}
668681
}
682+
} else {
683+
debug!("coerce_unsized: early return - ambiguous");
684+
return Err(TypeError::Mismatch);
669685
}
670-
} else {
671-
debug!("coerce_unsized: early return - ambiguous");
686+
}
687+
Err(traits::Unimplemented) => {
688+
debug!("coerce_unsized: early return - can't prove obligation");
672689
return Err(TypeError::Mismatch);
673690
}
674-
}
675-
Err(traits::Unimplemented) => {
676-
debug!("coerce_unsized: early return - can't prove obligation");
677-
return Err(TypeError::Mismatch);
678-
}
679691

680-
Err(SelectionError::TraitDynIncompatible(_)) => {
681-
// Dyn compatibility errors in coercion will *always* be due to the
682-
// fact that the RHS of the coercion is a non-dyn compatible `dyn Trait`
683-
// writen in source somewhere (otherwise we will never have lowered
684-
// the dyn trait from HIR to middle).
685-
//
686-
// There's no reason to emit yet another dyn compatibility error,
687-
// especially since the span will differ slightly and thus not be
688-
// deduplicated at all!
689-
self.fcx.set_tainted_by_errors(
690-
self.fcx
691-
.dcx()
692-
.span_delayed_bug(self.cause.span, "dyn compatibility during coercion"),
693-
);
694-
}
695-
Err(err) => {
696-
let guar = self.err_ctxt().report_selection_error(
697-
obligation.clone(),
698-
&obligation,
699-
&err,
700-
);
701-
self.fcx.set_tainted_by_errors(guar);
702-
// Treat this like an obligation and follow through
703-
// with the unsizing - the lack of a coercion should
704-
// be silent, as it causes a type mismatch later.
705-
}
692+
Err(SelectionError::TraitDynIncompatible(_)) => {
693+
// Dyn compatibility errors in coercion will *always* be due to the
694+
// fact that the RHS of the coercion is a non-dyn compatible `dyn Trait`
695+
// writen in source somewhere (otherwise we will never have lowered
696+
// the dyn trait from HIR to middle).
697+
//
698+
// There's no reason to emit yet another dyn compatibility error,
699+
// especially since the span will differ slightly and thus not be
700+
// deduplicated at all!
701+
self.fcx.set_tainted_by_errors(self.fcx.dcx().span_delayed_bug(
702+
self.cause.span,
703+
"dyn compatibility during coercion",
704+
));
705+
}
706+
Err(err) => {
707+
let guar = self.err_ctxt().report_selection_error(
708+
obligation.clone(),
709+
&obligation,
710+
&err,
711+
);
712+
self.fcx.set_tainted_by_errors(guar);
713+
// Treat this like an obligation and follow through
714+
// with the unsizing - the lack of a coercion should
715+
// be silent, as it causes a type mismatch later.
716+
}
706717

707-
Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),
718+
Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),
719+
}
708720
}
709721
}
710722

@@ -2022,3 +2034,49 @@ impl AsCoercionSite for hir::Arm<'_> {
20222034
self.body
20232035
}
20242036
}
2037+
2038+
struct CoerceVisitor<'a, 'tcx> {
2039+
fcx: &'a FnCtxt<'a, 'tcx>,
2040+
span: Span,
2041+
}
2042+
2043+
impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> {
2044+
type Result = ControlFlow<()>;
2045+
2046+
fn span(&self) -> Span {
2047+
self.span
2048+
}
2049+
2050+
fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
2051+
let Some(pred) = goal.goal().predicate.as_trait_clause() else {
2052+
return ControlFlow::Continue(());
2053+
};
2054+
2055+
if !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize)
2056+
&& !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::CoerceUnsized)
2057+
{
2058+
return ControlFlow::Continue(());
2059+
}
2060+
2061+
match goal.result() {
2062+
Ok(Certainty::Yes) => ControlFlow::Continue(()),
2063+
Err(NoSolution) => ControlFlow::Break(()),
2064+
Ok(Certainty::Maybe(_)) => {
2065+
// FIXME: structurally normalize?
2066+
if self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize)
2067+
&& let ty::Dynamic(..) = pred.skip_binder().trait_ref.args.type_at(1).kind()
2068+
&& let ty::Infer(ty::TyVar(vid)) = *pred.self_ty().skip_binder().kind()
2069+
&& self.fcx.type_var_is_sized(vid)
2070+
{
2071+
ControlFlow::Continue(())
2072+
} else if let Some(cand) = goal.unique_applicable_candidate()
2073+
&& cand.shallow_certainty() == Certainty::Yes
2074+
{
2075+
cand.visit_nested_no_probe(self)
2076+
} else {
2077+
ControlFlow::Break(())
2078+
}
2079+
}
2080+
}
2081+
}
2082+
}

compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_middle::traits::solve::GoalSource;
55
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
66
use rustc_span::Span;
77
use rustc_trait_selection::solve::inspect::{
8-
InspectConfig, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor,
8+
InferCtxtProofTreeExt, InspectConfig, InspectGoal, ProofTreeVisitor,
99
};
1010
use tracing::{debug, instrument, trace};
1111

compiler/rustc_trait_selection/src/solve/fulfill.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use tracing::instrument;
2323
use self::derive_errors::*;
2424
use super::Certainty;
2525
use super::delegate::SolverDelegate;
26-
use super::inspect::{self, ProofTreeInferCtxtExt};
26+
use super::inspect::{self, InferCtxtProofTreeExt};
2727
use crate::traits::{FulfillmentError, ScrubbedTraitError};
2828

2929
mod derive_errors;

compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_next_trait_solver::solve::{
1717
use tracing::{instrument, trace};
1818

1919
use crate::solve::delegate::SolverDelegate;
20-
use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
20+
use crate::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor};
2121
use crate::solve::{Certainty, deeply_normalize_for_diagnostics};
2222
use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf};
2323

compiler/rustc_trait_selection/src/solve/inspect/analyse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ pub trait ProofTreeVisitor<'tcx> {
421421
fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> Self::Result;
422422
}
423423

424-
#[extension(pub trait ProofTreeInferCtxtExt<'tcx>)]
424+
#[extension(pub trait InferCtxtProofTreeExt<'tcx>)]
425425
impl<'tcx> InferCtxt<'tcx> {
426426
fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>(
427427
&self,

compiler/rustc_trait_selection/src/solve/select.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_macros::extension;
1111
use rustc_middle::{bug, span_bug};
1212
use rustc_span::Span;
1313

14-
use crate::solve::inspect::{self, ProofTreeInferCtxtExt};
14+
use crate::solve::inspect::{self, InferCtxtProofTreeExt};
1515

1616
#[extension(pub trait InferCtxtSelectExt<'tcx>)]
1717
impl<'tcx> InferCtxt<'tcx> {

compiler/rustc_trait_selection/src/traits/coherence.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use tracing::{debug, instrument, warn};
2828
use super::ObligationCtxt;
2929
use crate::error_reporting::traits::suggest_new_overflow_limit;
3030
use crate::infer::InferOk;
31-
use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
31+
use crate::solve::inspect::{InferCtxtProofTreeExt, InspectGoal, ProofTreeVisitor};
3232
use crate::solve::{SolverDelegate, deeply_normalize_for_diagnostics, inspect};
3333
use crate::traits::query::evaluate_obligation::InferCtxtExt;
3434
use crate::traits::select::IntercrateAmbiguityCause;

0 commit comments

Comments
 (0)