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
4040use rustc_errors:: codes:: * ;
4141use rustc_errors:: { Applicability , Diag , struct_span_code_err} ;
42- use rustc_hir as hir;
4342use rustc_hir:: attrs:: InlineAttr ;
4443use rustc_hir:: def_id:: { DefId , LocalDefId } ;
44+ use rustc_hir:: { self as hir, LangItem } ;
4545use rustc_hir_analysis:: hir_ty_lowering:: HirTyLowerer ;
4646use rustc_infer:: infer:: relate:: RelateResult ;
4747use rustc_infer:: infer:: { DefineOpaqueTypes , InferOk , InferResult , RegionVariableOrigin } ;
@@ -56,6 +56,8 @@ use rustc_middle::ty::error::TypeError;
5656use rustc_middle:: ty:: { self , GenericArgsRef , Ty , TyCtxt , TypeVisitableExt } ;
5757use rustc_span:: { BytePos , DUMMY_SP , DesugaringKind , Span } ;
5858use rustc_trait_selection:: infer:: InferCtxtExt as _;
59+ use rustc_trait_selection:: solve:: inspect:: { self , InferCtxtProofTreeExt , ProofTreeVisitor } ;
60+ use rustc_trait_selection:: solve:: { Certainty , Goal , NoSolution } ;
5961use rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt ;
6062use rustc_trait_selection:: traits:: {
6163 self , ImplSource , NormalizeExt , ObligationCause , ObligationCauseCode , ObligationCtxt ,
@@ -639,24 +641,52 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
639641 Adjust :: Pointer ( PointerCoercion :: Unsize ) ,
640642 ) ?;
641643
642- let mut selcx = traits:: SelectionContext :: new ( self ) ;
643-
644644 // Create an obligation for `Source: CoerceUnsized<Target>`.
645645 let cause = self . cause ( self . cause . span , ObligationCauseCode :: Coercion { source, target } ) ;
646+ let pred = ty:: TraitRef :: new ( self . tcx , coerce_unsized_did, [ coerce_source, coerce_target] ) ;
647+ let obligation = Obligation :: new ( self . tcx , cause, self . fcx . param_env , pred) ;
648+
649+ if self . next_trait_solver ( ) {
650+ coercion. obligations . push ( obligation) ;
651+
652+ if self
653+ . infcx
654+ . visit_proof_tree (
655+ Goal :: new ( self . tcx , self . param_env , pred) ,
656+ & mut CoerceVisitor { fcx : self . fcx , span : self . cause . span } ,
657+ )
658+ . is_break ( )
659+ {
660+ return Err ( TypeError :: Mismatch ) ;
661+ }
662+ } else {
663+ self . coerce_unsized_old_solver (
664+ obligation,
665+ & mut coercion,
666+ coerce_unsized_did,
667+ unsize_did,
668+ ) ?;
669+ }
646670
671+ Ok ( coercion)
672+ }
673+
674+ fn coerce_unsized_old_solver (
675+ & self ,
676+ obligation : Obligation < ' tcx , ty:: Predicate < ' tcx > > ,
677+ coercion : & mut InferOk < ' tcx , ( Vec < Adjustment < ' tcx > > , Ty < ' tcx > ) > ,
678+ coerce_unsized_did : DefId ,
679+ unsize_did : DefId ,
680+ ) -> Result < ( ) , TypeError < ' tcx > > {
681+ let mut selcx = traits:: SelectionContext :: new ( self ) ;
647682 // Use a FIFO queue for this custom fulfillment procedure.
648683 //
649684 // A Vec (or SmallVec) is not a natural choice for a queue. However,
650685 // this code path is hot, and this queue usually has a max length of 1
651686 // and almost never more than 3. By using a SmallVec we avoid an
652687 // allocation, at the (very small) cost of (occasionally) having to
653688 // shift subsequent elements down when removing the front element.
654- let mut queue: SmallVec < [ PredicateObligation < ' tcx > ; 4 ] > = smallvec ! [ Obligation :: new(
655- self . tcx,
656- cause,
657- self . fcx. param_env,
658- ty:: TraitRef :: new( self . tcx, coerce_unsized_did, [ coerce_source, coerce_target] )
659- ) ] ;
689+ let mut queue: SmallVec < [ PredicateObligation < ' tcx > ; 4 ] > = smallvec ! [ obligation] ;
660690
661691 // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
662692 // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
@@ -749,7 +779,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
749779 // with the unsizing - the lack of a coercion should
750780 // be silent, as it causes a type mismatch later.
751781 }
752-
753782 Ok ( Some ( ImplSource :: UserDefined ( impl_source) ) ) => {
754783 queue. extend ( impl_source. nested ) ;
755784 // Certain incoherent `CoerceUnsized` implementations may cause ICEs,
@@ -767,7 +796,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
767796 }
768797 }
769798
770- Ok ( coercion )
799+ Ok ( ( ) )
771800 }
772801
773802 /// Applies reborrowing for `Pin`
@@ -2005,3 +2034,69 @@ impl AsCoercionSite for hir::Arm<'_> {
20052034 self . body
20062035 }
20072036}
2037+
2038+ /// Recursively visit goals to decide whether an unsizing is possible.
2039+ /// `Break`s when it isn't, and an error should be raised.
2040+ /// `Continue`s when an unsizing ok based on an implementation of the `Unsize` trait / lang item.
2041+ struct CoerceVisitor < ' a , ' tcx > {
2042+ fcx : & ' a FnCtxt < ' a , ' tcx > ,
2043+ span : Span ,
2044+ }
2045+
2046+ impl < ' tcx > ProofTreeVisitor < ' tcx > for CoerceVisitor < ' _ , ' tcx > {
2047+ type Result = ControlFlow < ( ) > ;
2048+
2049+ fn span ( & self ) -> Span {
2050+ self . span
2051+ }
2052+
2053+ fn visit_goal ( & mut self , goal : & inspect:: InspectGoal < ' _ , ' tcx > ) -> Self :: Result {
2054+ let Some ( pred) = goal. goal ( ) . predicate . as_trait_clause ( ) else {
2055+ return ControlFlow :: Continue ( ( ) ) ;
2056+ } ;
2057+
2058+ // Make sure this predicate is referring to either an `Unsize` or `CoerceUnsized` trait,
2059+ // Otherwise there's nothing to do.
2060+ if !self . fcx . tcx . is_lang_item ( pred. def_id ( ) , LangItem :: Unsize )
2061+ && !self . fcx . tcx . is_lang_item ( pred. def_id ( ) , LangItem :: CoerceUnsized )
2062+ {
2063+ return ControlFlow :: Continue ( ( ) ) ;
2064+ }
2065+
2066+ match goal. result ( ) {
2067+ // If we prove the `Unsize` or `CoerceUnsized` goal, continue recursing.
2068+ Ok ( Certainty :: Yes ) => ControlFlow :: Continue ( ( ) ) ,
2069+ Err ( NoSolution ) => {
2070+ // Even if we find no solution, continue recursing if we find a single candidate
2071+ // for which we're shallowly certain it holds to get the right error source.
2072+ if let [ only_candidate] = & goal. candidates ( ) [ ..]
2073+ && only_candidate. shallow_certainty ( ) == Certainty :: Yes
2074+ {
2075+ only_candidate. visit_nested_no_probe ( self )
2076+ } else {
2077+ ControlFlow :: Break ( ( ) )
2078+ }
2079+ }
2080+ Ok ( Certainty :: Maybe { .. } ) => {
2081+ // FIXME: structurally normalize?
2082+ if self . fcx . tcx . is_lang_item ( pred. def_id ( ) , LangItem :: Unsize )
2083+ && let ty:: Dynamic ( ..) = pred. skip_binder ( ) . trait_ref . args . type_at ( 1 ) . kind ( )
2084+ && let ty:: Infer ( ty:: TyVar ( vid) ) = * pred. self_ty ( ) . skip_binder ( ) . kind ( )
2085+ && self . fcx . type_var_is_sized ( vid)
2086+ {
2087+ // We get here when trying to unsize a type variable to a `dyn Trait`,
2088+ // knowing that that variable is sized. Unsizing definitely has to happen in that case.
2089+ // If the variable weren't sized, we may not need an unsizing coercion.
2090+ // In general, we don't want to add coercions too eagerly since it makes error messages much worse.
2091+ ControlFlow :: Continue ( ( ) )
2092+ } else if let Some ( cand) = goal. unique_applicable_candidate ( )
2093+ && cand. shallow_certainty ( ) == Certainty :: Yes
2094+ {
2095+ cand. visit_nested_no_probe ( self )
2096+ } else {
2097+ ControlFlow :: Break ( ( ) )
2098+ }
2099+ }
2100+ }
2101+ }
2102+ }
0 commit comments