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,131 +641,140 @@ 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) ;
646648
647- // Use a FIFO queue for this custom fulfillment procedure.
648- //
649- // A Vec (or SmallVec) is not a natural choice for a queue. However,
650- // this code path is hot, and this queue usually has a max length of 1
651- // and almost never more than 3. By using a SmallVec we avoid an
652- // allocation, at the (very small) cost of (occasionally) having to
653- // 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- ) ] ;
660-
661- // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
662- // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
663- // inference might unify those two inner type variables later.
664- let traits = [ coerce_unsized_did , unsize_did ] ;
665- while ! queue. is_empty ( ) {
666- let obligation = queue . remove ( 0 ) ;
667- let trait_pred = match obligation . predicate . kind ( ) . no_bound_vars ( ) {
668- Some ( ty :: PredicateKind :: Clause ( ty :: ClauseKind :: Trait ( trait_pred ) ) )
669- if traits . contains ( & trait_pred . def_id ( ) ) =>
670- {
671- self . resolve_vars_if_possible ( trait_pred )
672- }
673- // Eagerly process alias-relate obligations in new trait solver,
674- // since these can be emitted in the process of solving trait goals,
675- // but we need to constrain vars before processing goals mentioning
676- // them.
677- Some ( ty :: PredicateKind :: AliasRelate ( .. ) ) => {
678- let ocx = ObligationCtxt :: new ( self ) ;
679- ocx . register_obligation ( obligation ) ;
680- if !ocx . try_evaluate_obligations ( ) . is_empty ( ) {
681- return Err ( TypeError :: Mismatch ) ;
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+ let mut selcx = traits :: SelectionContext :: new ( self ) ;
664+ // Use a FIFO queue for this custom fulfillment procedure.
665+ //
666+ // A Vec (or SmallVec) is not a natural choice for a queue. However,
667+ // this code path is hot, and this queue usually has a max length of 1
668+ // and almost never more than 3. By using a SmallVec we avoid an
669+ // allocation, at the (very small) cost of (occasionally) having to
670+ // shift subsequent elements down when removing the front element.
671+ let mut queue : SmallVec < [ PredicateObligation < ' tcx > ; 4 ] > = smallvec ! [ obligation ] ;
672+
673+ // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
674+ // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
675+ // inference might unify those two inner type variables later.
676+ let traits = [ coerce_unsized_did , unsize_did ] ;
677+ while !queue . is_empty ( ) {
678+ let obligation = queue . remove ( 0 ) ;
679+ let trait_pred = match obligation . predicate . kind ( ) . no_bound_vars ( ) {
680+ Some ( ty :: PredicateKind :: Clause ( ty :: ClauseKind :: Trait ( trait_pred ) ) )
681+ if traits . contains ( & trait_pred . def_id ( ) ) =>
682+ {
683+ self . resolve_vars_if_possible ( trait_pred )
682684 }
683- coercion. obligations . extend ( ocx. into_pending_obligations ( ) ) ;
684- continue ;
685- }
686- _ => {
687- coercion. obligations . push ( obligation) ;
688- continue ;
689- }
690- } ;
691- debug ! ( "coerce_unsized resolve step: {:?}" , trait_pred) ;
692- match selcx. select ( & obligation. with ( selcx. tcx ( ) , trait_pred) ) {
693- // Uncertain or unimplemented.
694- Ok ( None ) => {
695- if trait_pred. def_id ( ) == unsize_did {
696- let self_ty = trait_pred. self_ty ( ) ;
697- let unsize_ty = trait_pred. trait_ref . args [ 1 ] . expect_ty ( ) ;
698- debug ! ( "coerce_unsized: ambiguous unsize case for {:?}" , trait_pred) ;
699- match ( self_ty. kind ( ) , unsize_ty. kind ( ) ) {
700- ( & ty:: Infer ( ty:: TyVar ( v) ) , ty:: Dynamic ( ..) )
701- if self . type_var_is_sized ( v) =>
702- {
703- debug ! ( "coerce_unsized: have sized infer {:?}" , v) ;
704- coercion. obligations . push ( obligation) ;
705- // `$0: Unsize<dyn Trait>` where we know that `$0: Sized`, try going
706- // for unsizing.
707- }
708- _ => {
709- // Some other case for `$0: Unsize<Something>`. Note that we
710- // hit this case even if `Something` is a sized type, so just
711- // don't do the coercion.
712- debug ! ( "coerce_unsized: ambiguous unsize" ) ;
713- return Err ( TypeError :: Mismatch ) ;
685+ // Eagerly process alias-relate obligations in new trait solver,
686+ // since these can be emitted in the process of solving trait goals,
687+ // but we need to constrain vars before processing goals mentioning
688+ // them.
689+ Some ( ty:: PredicateKind :: AliasRelate ( ..) ) => {
690+ let ocx = ObligationCtxt :: new ( self ) ;
691+ ocx. register_obligation ( obligation) ;
692+ if !ocx. try_evaluate_obligations ( ) . is_empty ( ) {
693+ return Err ( TypeError :: Mismatch ) ;
694+ }
695+ coercion. obligations . extend ( ocx. into_pending_obligations ( ) ) ;
696+ continue ;
697+ }
698+ _ => {
699+ coercion. obligations . push ( obligation) ;
700+ continue ;
701+ }
702+ } ;
703+ debug ! ( "coerce_unsized resolve step: {:?}" , trait_pred) ;
704+ match selcx. select ( & obligation. with ( selcx. tcx ( ) , trait_pred) ) {
705+ // Uncertain or unimplemented.
706+ Ok ( None ) => {
707+ if trait_pred. def_id ( ) == unsize_did {
708+ let self_ty = trait_pred. self_ty ( ) ;
709+ let unsize_ty = trait_pred. trait_ref . args [ 1 ] . expect_ty ( ) ;
710+ debug ! ( "coerce_unsized: ambiguous unsize case for {:?}" , trait_pred) ;
711+ match ( self_ty. kind ( ) , unsize_ty. kind ( ) ) {
712+ ( & ty:: Infer ( ty:: TyVar ( v) ) , ty:: Dynamic ( ..) )
713+ if self . type_var_is_sized ( v) =>
714+ {
715+ debug ! ( "coerce_unsized: have sized infer {:?}" , v) ;
716+ coercion. obligations . push ( obligation) ;
717+ // `$0: Unsize<dyn Trait>` where we know that `$0: Sized`, try going
718+ // for unsizing.
719+ }
720+ _ => {
721+ // Some other case for `$0: Unsize<Something>`. Note that we
722+ // hit this case even if `Something` is a sized type, so just
723+ // don't do the coercion.
724+ debug ! ( "coerce_unsized: ambiguous unsize" ) ;
725+ return Err ( TypeError :: Mismatch ) ;
726+ }
714727 }
728+ } else {
729+ debug ! ( "coerce_unsized: early return - ambiguous" ) ;
730+ return Err ( TypeError :: Mismatch ) ;
715731 }
716- } else {
717- debug ! ( "coerce_unsized: early return - ambiguous" ) ;
732+ }
733+ Err ( SelectionError :: Unimplemented ) => {
734+ debug ! ( "coerce_unsized: early return - can't prove obligation" ) ;
718735 return Err ( TypeError :: Mismatch ) ;
719736 }
720- }
721- Err ( SelectionError :: Unimplemented ) => {
722- debug ! ( "coerce_unsized: early return - can't prove obligation" ) ;
723- return Err ( TypeError :: Mismatch ) ;
724- }
725737
726- Err ( SelectionError :: TraitDynIncompatible ( _) ) => {
727- // Dyn compatibility errors in coercion will *always* be due to the
728- // fact that the RHS of the coercion is a non-dyn compatible `dyn Trait`
729- // written in source somewhere (otherwise we will never have lowered
730- // the dyn trait from HIR to middle).
731- //
732- // There's no reason to emit yet another dyn compatibility error,
733- // especially since the span will differ slightly and thus not be
734- // deduplicated at all!
735- self . fcx . set_tainted_by_errors (
736- self . fcx
737- . dcx ( )
738- . span_delayed_bug ( self . cause . span , "dyn compatibility during coercion" ) ,
739- ) ;
740- }
741- Err ( err) => {
742- let guar = self . err_ctxt ( ) . report_selection_error (
743- obligation. clone ( ) ,
744- & obligation,
745- & err,
746- ) ;
747- self . fcx . set_tainted_by_errors ( guar) ;
748- // Treat this like an obligation and follow through
749- // with the unsizing - the lack of a coercion should
750- // be silent, as it causes a type mismatch later.
751- }
752-
753- Ok ( Some ( ImplSource :: UserDefined ( impl_source) ) ) => {
754- queue. extend ( impl_source. nested ) ;
755- // Certain incoherent `CoerceUnsized` implementations may cause ICEs,
756- // so check the impl's validity. Taint the body so that we don't try
757- // to evaluate these invalid coercions in CTFE. We only need to do this
758- // for local impls, since upstream impls should be valid.
759- if impl_source. impl_def_id . is_local ( )
760- && let Err ( guar) =
761- self . tcx . ensure_ok ( ) . coerce_unsized_info ( impl_source. impl_def_id )
762- {
738+ Err ( SelectionError :: TraitDynIncompatible ( _) ) => {
739+ // Dyn compatibility errors in coercion will *always* be due to the
740+ // fact that the RHS of the coercion is a non-dyn compatible `dyn Trait`
741+ // writen in source somewhere (otherwise we will never have lowered
742+ // the dyn trait from HIR to middle).
743+ //
744+ // There's no reason to emit yet another dyn compatibility error,
745+ // especially since the span will differ slightly and thus not be
746+ // deduplicated at all!
747+ self . fcx . set_tainted_by_errors ( self . fcx . dcx ( ) . span_delayed_bug (
748+ self . cause . span ,
749+ "dyn compatibility during coercion" ,
750+ ) ) ;
751+ }
752+ Err ( err) => {
753+ let guar = self . err_ctxt ( ) . report_selection_error (
754+ obligation. clone ( ) ,
755+ & obligation,
756+ & err,
757+ ) ;
763758 self . fcx . set_tainted_by_errors ( guar) ;
759+ // Treat this like an obligation and follow through
760+ // with the unsizing - the lack of a coercion should
761+ // be silent, as it causes a type mismatch later.
762+ }
763+ Ok ( Some ( ImplSource :: UserDefined ( impl_source) ) ) => {
764+ queue. extend ( impl_source. nested ) ;
765+ // Certain incoherent `CoerceUnsized` implementations may cause ICEs,
766+ // so check the impl's validity. Taint the body so that we don't try
767+ // to evaluate these invalid coercions in CTFE. We only need to do this
768+ // for local impls, since upstream impls should be valid.
769+ if impl_source. impl_def_id . is_local ( )
770+ && let Err ( guar) =
771+ self . tcx . ensure_ok ( ) . coerce_unsized_info ( impl_source. impl_def_id )
772+ {
773+ self . fcx . set_tainted_by_errors ( guar) ;
774+ }
764775 }
776+ Ok ( Some ( impl_source) ) => queue. extend ( impl_source. nested_obligations ( ) ) ,
765777 }
766- Ok ( Some ( impl_source) ) => queue. extend ( impl_source. nested_obligations ( ) ) ,
767778 }
768779 }
769780
@@ -2005,3 +2016,52 @@ impl AsCoercionSite for hir::Arm<'_> {
20052016 self . body
20062017 }
20072018}
2019+
2020+ /// Recursively visit goals to decide whether an unsizing is possible.
2021+ /// `Break`s when it isn't, and an error should be raised.
2022+ /// `Continue`s when an unsizing ok based on an implementation of the `Unsize` trait / lang item.
2023+ struct CoerceVisitor < ' a , ' tcx > {
2024+ fcx : & ' a FnCtxt < ' a , ' tcx > ,
2025+ span : Span ,
2026+ }
2027+
2028+ impl < ' tcx > ProofTreeVisitor < ' tcx > for CoerceVisitor < ' _ , ' tcx > {
2029+ type Result = ControlFlow < ( ) > ;
2030+
2031+ fn span ( & self ) -> Span {
2032+ self . span
2033+ }
2034+
2035+ fn visit_goal ( & mut self , goal : & inspect:: InspectGoal < ' _ , ' tcx > ) -> Self :: Result {
2036+ let Some ( pred) = goal. goal ( ) . predicate . as_trait_clause ( ) else {
2037+ return ControlFlow :: Continue ( ( ) ) ;
2038+ } ;
2039+
2040+ if !self . fcx . tcx . is_lang_item ( pred. def_id ( ) , LangItem :: Unsize )
2041+ && !self . fcx . tcx . is_lang_item ( pred. def_id ( ) , LangItem :: CoerceUnsized )
2042+ {
2043+ return ControlFlow :: Continue ( ( ) ) ;
2044+ }
2045+
2046+ match goal. result ( ) {
2047+ Ok ( Certainty :: Yes ) => ControlFlow :: Continue ( ( ) ) ,
2048+ Err ( NoSolution ) => ControlFlow :: Break ( ( ) ) ,
2049+ Ok ( Certainty :: Maybe { .. } ) => {
2050+ // FIXME: structurally normalize?
2051+ if self . fcx . tcx . is_lang_item ( pred. def_id ( ) , LangItem :: Unsize )
2052+ && let ty:: Dynamic ( ..) = pred. skip_binder ( ) . trait_ref . args . type_at ( 1 ) . kind ( )
2053+ && let ty:: Infer ( ty:: TyVar ( vid) ) = * pred. self_ty ( ) . skip_binder ( ) . kind ( )
2054+ && self . fcx . type_var_is_sized ( vid)
2055+ {
2056+ ControlFlow :: Continue ( ( ) )
2057+ } else if let Some ( cand) = goal. unique_applicable_candidate ( )
2058+ && cand. shallow_certainty ( ) == Certainty :: Yes
2059+ {
2060+ cand. visit_nested_no_probe ( self )
2061+ } else {
2062+ ControlFlow :: Break ( ( ) )
2063+ }
2064+ }
2065+ }
2066+ }
2067+ }
0 commit comments