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_attr_data_structures:: InlineAttr ;
4141use rustc_errors:: codes:: * ;
4242use rustc_errors:: { Applicability , Diag , struct_span_code_err} ;
43- use rustc_hir as hir;
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:: { Coercion , DefineOpaqueTypes , InferOk , InferResult } ;
@@ -57,6 +57,8 @@ use rustc_middle::ty::error::TypeError;
5757use rustc_middle:: ty:: { self , GenericArgsRef , Ty , TyCtxt , TypeVisitableExt } ;
5858use rustc_span:: { BytePos , DUMMY_SP , DesugaringKind , Span } ;
5959use 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 } ;
6062use rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt ;
6163use 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+ }
0 commit comments