Auto merge of rust-lang/rust#141926 - compiler-errors:perf-unsizing, … · rust-lang/rust@8eb675b (original) (raw)
`@@ -35,13 +35,13 @@
`
35
35
`` //! // and are then unable to coerce &7i32 to &mut i32.
``
36
36
```` //! ```
````
37
37
``
38
``
`-
use std::ops::Deref;
`
``
38
`+
use std::ops::{ControlFlow, Deref};
`
39
39
``
40
40
`use rustc_attr_data_structures::InlineAttr;
`
41
41
`use rustc_errors::codes::*;
`
42
42
`use rustc_errors::{Applicability, Diag, struct_span_code_err};
`
43
``
`-
use rustc_hir as hir;
`
44
43
`use rustc_hir::def_id::{DefId, LocalDefId};
`
``
44
`+
use rustc_hir::{self as hir, LangItem};
`
45
45
`use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
`
46
46
`use rustc_infer::infer::relate::RelateResult;
`
47
47
`use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
`
`@@ -57,6 +57,8 @@ use rustc_middle::ty::error::TypeError;
`
57
57
`use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
`
58
58
`use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span};
`
59
59
`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};
`
60
62
`use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
`
61
63
`use rustc_trait_selection::traits::{
`
62
64
`self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt,
`
`@@ -593,118 +595,128 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
`
593
595
`Adjust::Pointer(PointerCoercion::Unsize),
`
594
596
`)?;
`
595
597
``
596
``
`-
let mut selcx = traits::SelectionContext::new(self);
`
597
``
-
598
598
`` // Create an obligation for Source: CoerceUnsized<Target>.
``
599
599
`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);
`
600
602
``
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)
`
636
638
`}
`
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
`+
}
`
668
681
`}
`
``
682
`+
} else {
`
``
683
`+
debug!("coerce_unsized: early return - ambiguous");
`
``
684
`+
return Err(TypeError::Mismatch);
`
669
685
`}
`
670
``
`-
} else {
`
671
``
`-
debug!("coerce_unsized: early return - ambiguous");
`
``
686
`+
}
`
``
687
`+
Err(traits::Unimplemented) => {
`
``
688
`+
debug!("coerce_unsized: early return - can't prove obligation");
`
672
689
`return Err(TypeError::Mismatch);
`
673
690
`}
`
674
``
`-
}
`
675
``
`-
Err(traits::Unimplemented) => {
`
676
``
`-
debug!("coerce_unsized: early return - can't prove obligation");
`
677
``
`-
return Err(TypeError::Mismatch);
`
678
``
`-
}
`
679
691
``
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
`+
}
`
706
717
``
707
``
`-
Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),
`
``
718
`+
Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),
`
``
719
`+
}
`
708
720
`}
`
709
721
`}
`
710
722
``
`@@ -2022,3 +2034,49 @@ impl AsCoercionSite for hir::Arm<'_> {
`
2022
2034
`self.body
`
2023
2035
`}
`
2024
2036
`}
`
``
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
`+
}
`