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
+ }
0 commit comments