3535//! // and are then unable to coerce `&7i32` to `&mut i32`.
3636//! ```
3737
38+ use std:: ops:: ControlFlow ;
39+
3840use hir_def:: {
39- CallableDefId ,
41+ CallableDefId , TraitId ,
4042 attrs:: AttrFlags ,
4143 hir:: { ExprId , ExprOrPatId } ,
4244 signatures:: FunctionSignature ,
4345} ;
4446use rustc_ast_ir:: Mutability ;
4547use rustc_type_ir:: {
46- BoundVar , DebruijnIndex , TyVid , TypeAndMut , TypeFoldable , TypeFolder , TypeSuperFoldable ,
47- TypeVisitableExt ,
48+ BoundVar , DebruijnIndex , InferTy , TyVid , TypeAndMut , TypeFoldable , TypeFolder ,
49+ TypeSuperFoldable , TypeVisitableExt ,
4850 error:: TypeError ,
49- inherent:: {
50- Const as _, GenericArg as _, GenericArgs as _, IntoKind , Safety as _, SliceLike , Ty as _,
51- } ,
51+ inherent:: { Const as _, GenericArg as _, GenericArgs as _, IntoKind , Safety as _, Ty as _} ,
52+ solve:: { Certainty , NoSolution } ,
5253} ;
53- use smallvec:: { SmallVec , smallvec } ;
54+ use smallvec:: SmallVec ;
5455use tracing:: { debug, instrument} ;
5556
5657use crate :: {
@@ -62,16 +63,16 @@ use crate::{
6263 } ,
6364 next_solver:: {
6465 Binder , BoundConst , BoundRegion , BoundRegionKind , BoundTy , BoundTyKind , CallableIdWrapper ,
65- Canonical , ClauseKind , CoercePredicate , Const , ConstKind , DbInterner , ErrorGuaranteed ,
66- GenericArgs , ParamEnv , PolyFnSig , PredicateKind , Region , RegionKind , TraitRef , Ty , TyKind ,
66+ Canonical , CoercePredicate , Const , ConstKind , DbInterner , ErrorGuaranteed , GenericArgs ,
67+ Goal , ParamEnv , PolyFnSig , PredicateKind , Region , RegionKind , TraitRef , Ty , TyKind ,
6768 TypingMode ,
6869 abi:: Safety ,
6970 infer:: {
7071 DbInternerInferExt , InferCtxt , InferOk , InferResult ,
7172 relate:: RelateResult ,
72- select:: { ImplSource , SelectionError } ,
73- traits:: { Obligation , ObligationCause , PredicateObligation , PredicateObligations } ,
73+ traits:: { Obligation , ObligationCause , PredicateObligations } ,
7474 } ,
75+ inspect:: { InspectGoal , ProofTreeVisitor } ,
7576 obligation_ctxt:: ObligationCtxt ,
7677 } ,
7778 upvars:: upvars_mentioned,
@@ -85,9 +86,7 @@ trait CoerceDelegate<'db> {
8586
8687 fn set_diverging ( & mut self , diverging_ty : Ty < ' db > ) ;
8788
88- fn set_tainted_by_errors ( & mut self ) ;
89-
90- fn type_var_is_sized ( & mut self , var : TyVid ) -> bool ;
89+ fn type_var_is_sized ( & self , var : TyVid ) -> bool ;
9190}
9291
9392struct Coerce < D > {
@@ -129,11 +128,6 @@ impl<'db, D> Coerce<D>
129128where
130129 D : CoerceDelegate < ' db > ,
131130{
132- #[ inline]
133- fn set_tainted_by_errors ( & mut self ) {
134- self . delegate . set_tainted_by_errors ( ) ;
135- }
136-
137131 #[ inline]
138132 fn infcx ( & self ) -> & InferCtxt < ' db > {
139133 self . delegate . infcx ( )
@@ -680,125 +674,30 @@ where
680674
681675 // Create an obligation for `Source: CoerceUnsized<Target>`.
682676 let cause = self . cause . clone ( ) ;
683-
684- // Use a FIFO queue for this custom fulfillment procedure.
685- //
686- // A Vec (or SmallVec) is not a natural choice for a queue. However,
687- // this code path is hot, and this queue usually has a max length of 1
688- // and almost never more than 3. By using a SmallVec we avoid an
689- // allocation, at the (very small) cost of (occasionally) having to
690- // shift subsequent elements down when removing the front element.
691- let mut queue: SmallVec < [ PredicateObligation < ' db > ; 4 ] > = smallvec ! [ Obligation :: new(
677+ let pred = TraitRef :: new (
692678 self . interner ( ) ,
693- cause,
694- self . param_env( ) ,
695- TraitRef :: new(
696- self . interner( ) ,
697- coerce_unsized_did. into( ) ,
698- [ coerce_source, coerce_target]
679+ coerce_unsized_did. into ( ) ,
680+ [ coerce_source, coerce_target] ,
681+ ) ;
682+ let obligation = Obligation :: new ( self . interner ( ) , cause, self . param_env ( ) , pred) ;
683+
684+ coercion. obligations . push ( obligation) ;
685+
686+ if self
687+ . delegate
688+ . infcx ( )
689+ . visit_proof_tree (
690+ Goal :: new ( self . infcx ( ) . interner , self . param_env ( ) , pred) ,
691+ & mut CoerceVisitor {
692+ delegate : & self . delegate ,
693+ errored : false ,
694+ unsize_did,
695+ coerce_unsized_did,
696+ } ,
699697 )
700- ) ] ;
701- // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
702- // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
703- // inference might unify those two inner type variables later.
704- let traits = [ coerce_unsized_did, unsize_did] ;
705- while !queue. is_empty ( ) {
706- let obligation = queue. remove ( 0 ) ;
707- let trait_pred = match obligation. predicate . kind ( ) . no_bound_vars ( ) {
708- Some ( PredicateKind :: Clause ( ClauseKind :: Trait ( trait_pred) ) )
709- if traits. contains ( & trait_pred. def_id ( ) . 0 ) =>
710- {
711- self . infcx ( ) . resolve_vars_if_possible ( trait_pred)
712- }
713- // Eagerly process alias-relate obligations in new trait solver,
714- // since these can be emitted in the process of solving trait goals,
715- // but we need to constrain vars before processing goals mentioning
716- // them.
717- Some ( PredicateKind :: AliasRelate ( ..) ) => {
718- let mut ocx = ObligationCtxt :: new ( self . infcx ( ) ) ;
719- ocx. register_obligation ( obligation) ;
720- if !ocx. try_evaluate_obligations ( ) . is_empty ( ) {
721- return Err ( TypeError :: Mismatch ) ;
722- }
723- coercion. obligations . extend ( ocx. into_pending_obligations ( ) ) ;
724- continue ;
725- }
726- _ => {
727- coercion. obligations . push ( obligation) ;
728- continue ;
729- }
730- } ;
731- debug ! ( "coerce_unsized resolve step: {:?}" , trait_pred) ;
732- match self . infcx ( ) . select ( & obligation. with ( self . interner ( ) , trait_pred) ) {
733- // Uncertain or unimplemented.
734- Ok ( None ) => {
735- if trait_pred. def_id ( ) . 0 == unsize_did {
736- let self_ty = trait_pred. self_ty ( ) ;
737- let unsize_ty = trait_pred. trait_ref . args [ 1 ] . expect_ty ( ) ;
738- debug ! ( "coerce_unsized: ambiguous unsize case for {:?}" , trait_pred) ;
739- match ( self_ty. kind ( ) , unsize_ty. kind ( ) ) {
740- ( TyKind :: Infer ( rustc_type_ir:: TyVar ( v) ) , TyKind :: Dynamic ( ..) )
741- if self . delegate . type_var_is_sized ( v) =>
742- {
743- debug ! ( "coerce_unsized: have sized infer {:?}" , v) ;
744- coercion. obligations . push ( obligation) ;
745- // `$0: Unsize<dyn Trait>` where we know that `$0: Sized`, try going
746- // for unsizing.
747- }
748- _ => {
749- // Some other case for `$0: Unsize<Something>`. Note that we
750- // hit this case even if `Something` is a sized type, so just
751- // don't do the coercion.
752- debug ! ( "coerce_unsized: ambiguous unsize" ) ;
753- return Err ( TypeError :: Mismatch ) ;
754- }
755- }
756- } else {
757- debug ! ( "coerce_unsized: early return - ambiguous" ) ;
758- if !coerce_source. references_non_lt_error ( )
759- && !coerce_target. references_non_lt_error ( )
760- {
761- // rustc always early-returns here, even when the types contains errors. However not bailing
762- // improves error recovery, and while we don't implement generic consts properly, it also helps
763- // correct code.
764- return Err ( TypeError :: Mismatch ) ;
765- }
766- }
767- }
768- Err ( SelectionError :: Unimplemented ) => {
769- debug ! ( "coerce_unsized: early return - can't prove obligation" ) ;
770- return Err ( TypeError :: Mismatch ) ;
771- }
772-
773- Err ( SelectionError :: TraitDynIncompatible ( _) ) => {
774- // Dyn compatibility errors in coercion will *always* be due to the
775- // fact that the RHS of the coercion is a non-dyn compatible `dyn Trait`
776- // written in source somewhere (otherwise we will never have lowered
777- // the dyn trait from HIR to middle).
778- //
779- // There's no reason to emit yet another dyn compatibility error,
780- // especially since the span will differ slightly and thus not be
781- // deduplicated at all!
782- self . set_tainted_by_errors ( ) ;
783- }
784- Err ( _err) => {
785- // FIXME: Report an error:
786- // let guar = self.err_ctxt().report_selection_error(
787- // obligation.clone(),
788- // &obligation,
789- // &err,
790- // );
791- self . set_tainted_by_errors ( ) ;
792- // Treat this like an obligation and follow through
793- // with the unsizing - the lack of a coercion should
794- // be silent, as it causes a type mismatch later.
795- }
796-
797- Ok ( Some ( ImplSource :: UserDefined ( impl_source) ) ) => {
798- queue. extend ( impl_source. nested ) ;
799- }
800- Ok ( Some ( impl_source) ) => queue. extend ( impl_source. nested_obligations ( ) ) ,
801- }
698+ . is_break ( )
699+ {
700+ return Err ( TypeError :: Mismatch ) ;
802701 }
803702
804703 Ok ( coercion)
@@ -983,12 +882,7 @@ impl<'db> CoerceDelegate<'db> for InferenceCoercionDelegate<'_, '_, 'db> {
983882 }
984883
985884 #[ inline]
986- fn set_tainted_by_errors ( & mut self ) {
987- self . 0 . set_tainted_by_errors ( ) ;
988- }
989-
990- #[ inline]
991- fn type_var_is_sized ( & mut self , var : TyVid ) -> bool {
885+ fn type_var_is_sized ( & self , var : TyVid ) -> bool {
992886 self . 0 . table . type_var_is_sized ( var)
993887 }
994888}
@@ -1560,8 +1454,7 @@ impl<'db> CoerceDelegate<'db> for HirCoercionDelegate<'_, 'db> {
15601454 ( self . target_features , TargetFeatureIsSafeInTarget :: No )
15611455 }
15621456 fn set_diverging ( & mut self , _diverging_ty : Ty < ' db > ) { }
1563- fn set_tainted_by_errors ( & mut self ) { }
1564- fn type_var_is_sized ( & mut self , _var : TyVid ) -> bool {
1457+ fn type_var_is_sized ( & self , _var : TyVid ) -> bool {
15651458 false
15661459 }
15671460}
@@ -1721,3 +1614,81 @@ fn is_capturing_closure(db: &dyn HirDatabase, closure: InternedClosureId) -> boo
17211614 upvars_mentioned ( db, owner)
17221615 . is_some_and ( |upvars| upvars. get ( & expr) . is_some_and ( |upvars| !upvars. is_empty ( ) ) )
17231616}
1617+
1618+ /// Recursively visit goals to decide whether an unsizing is possible.
1619+ /// `Break`s when it isn't, and an error should be raised.
1620+ /// `Continue`s when an unsizing ok based on an implementation of the `Unsize` trait / lang item.
1621+ struct CoerceVisitor < ' a , D > {
1622+ delegate : & ' a D ,
1623+ /// Whether the coercion is impossible. If so we sometimes still try to
1624+ /// coerce in these cases to emit better errors. This changes the behavior
1625+ /// when hitting the recursion limit.
1626+ errored : bool ,
1627+ unsize_did : TraitId ,
1628+ coerce_unsized_did : TraitId ,
1629+ }
1630+
1631+ impl < ' a , ' db , D : CoerceDelegate < ' db > > ProofTreeVisitor < ' db > for CoerceVisitor < ' a , D > {
1632+ type Result = ControlFlow < ( ) > ;
1633+
1634+ fn visit_goal ( & mut self , goal : & InspectGoal < ' _ , ' db > ) -> Self :: Result {
1635+ let Some ( pred) = goal. goal ( ) . predicate . as_trait_clause ( ) else {
1636+ return ControlFlow :: Continue ( ( ) ) ;
1637+ } ;
1638+
1639+ // Make sure this predicate is referring to either an `Unsize` or `CoerceUnsized` trait,
1640+ // Otherwise there's nothing to do.
1641+ let def_id = pred. def_id ( ) . 0 ;
1642+ if def_id != self . unsize_did && def_id != self . coerce_unsized_did {
1643+ return ControlFlow :: Continue ( ( ) ) ;
1644+ }
1645+
1646+ match goal. result ( ) {
1647+ // If we prove the `Unsize` or `CoerceUnsized` goal, continue recursing.
1648+ Ok ( Certainty :: Yes ) => ControlFlow :: Continue ( ( ) ) ,
1649+ Err ( NoSolution ) => {
1650+ self . errored = true ;
1651+ // Even if we find no solution, continue recursing if we find a single candidate
1652+ // for which we're shallowly certain it holds to get the right error source.
1653+ if let [ only_candidate] = & goal. candidates ( ) [ ..]
1654+ && only_candidate. shallow_certainty ( ) == Certainty :: Yes
1655+ {
1656+ only_candidate. visit_nested_no_probe ( self )
1657+ } else {
1658+ ControlFlow :: Break ( ( ) )
1659+ }
1660+ }
1661+ Ok ( Certainty :: Maybe { .. } ) => {
1662+ // FIXME: structurally normalize?
1663+ if def_id == self . unsize_did
1664+ && let TyKind :: Dynamic ( ..) = pred. skip_binder ( ) . trait_ref . args . type_at ( 1 ) . kind ( )
1665+ && let TyKind :: Infer ( InferTy :: TyVar ( vid) ) = pred. self_ty ( ) . skip_binder ( ) . kind ( )
1666+ && self . delegate . type_var_is_sized ( vid)
1667+ {
1668+ // We get here when trying to unsize a type variable to a `dyn Trait`,
1669+ // knowing that that variable is sized. Unsizing definitely has to happen in that case.
1670+ // If the variable weren't sized, we may not need an unsizing coercion.
1671+ // In general, we don't want to add coercions too eagerly since it makes error messages much worse.
1672+ ControlFlow :: Continue ( ( ) )
1673+ } else if let Some ( cand) = goal. unique_applicable_candidate ( )
1674+ && cand. shallow_certainty ( ) == Certainty :: Yes
1675+ {
1676+ cand. visit_nested_no_probe ( self )
1677+ } else {
1678+ ControlFlow :: Break ( ( ) )
1679+ }
1680+ }
1681+ }
1682+ }
1683+
1684+ fn on_recursion_limit ( & mut self ) -> Self :: Result {
1685+ if self . errored {
1686+ // This prevents accidentally committing unfulfilled unsized coercions while trying to
1687+ // find the error source for diagnostics.
1688+ // See https://github.com/rust-lang/trait-system-refactor-initiative/issues/266.
1689+ ControlFlow :: Break ( ( ) )
1690+ } else {
1691+ ControlFlow :: Continue ( ( ) )
1692+ }
1693+ }
1694+ }
0 commit comments