@@ -5,9 +5,10 @@ use derive_where::derive_where;
55use rustc_type_ir:: data_structures:: HashMap ;
66use rustc_type_ir:: inherent:: * ;
77use rustc_type_ir:: lang_items:: TraitSolverLangItem ;
8+ use rustc_type_ir:: solve:: inspect:: ProbeKind ;
89use rustc_type_ir:: {
9- self as ty, Interner , Movability , Mutability , TypeFoldable , TypeFolder , TypeSuperFoldable ,
10- Upcast as _, elaborate,
10+ self as ty, FallibleTypeFolder , Interner , Movability , Mutability , TypeFoldable ,
11+ TypeSuperFoldable , Upcast as _, elaborate,
1112} ;
1213use rustc_type_ir_macros:: { TypeFoldable_Generic , TypeVisitable_Generic } ;
1314use tracing:: instrument;
@@ -822,22 +823,16 @@ pub(in crate::solve) fn const_conditions_for_destruct<I: Interner>(
822823/// impl Baz for dyn Foo<Item = Ty> {}
823824/// ```
824825///
825- /// However, in order to make such impls well-formed , we need to do an
826+ /// However, in order to make such impls non-cyclical , we need to do an
826827/// additional step of eagerly folding the associated types in the where
827828/// clauses of the impl. In this example, that means replacing
828829/// `<Self as Foo>::Bar` with `Ty` in the first impl.
829- ///
830- // FIXME: This is only necessary as `<Self as Trait>::Assoc: ItemBound`
831- // bounds in impls are trivially proven using the item bound candidates.
832- // This is unsound in general and once that is fixed, we don't need to
833- // normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9
834- // for more details.
835830pub ( in crate :: solve) fn predicates_for_object_candidate < D , I > (
836- ecx : & EvalCtxt < ' _ , D > ,
831+ ecx : & mut EvalCtxt < ' _ , D > ,
837832 param_env : I :: ParamEnv ,
838833 trait_ref : ty:: TraitRef < I > ,
839834 object_bounds : I :: BoundExistentialPredicates ,
840- ) -> Vec < Goal < I , I :: Predicate > >
835+ ) -> Result < Vec < Goal < I , I :: Predicate > > , Ambiguous >
841836where
842837 D : SolverDelegate < Interner = I > ,
843838 I : Interner ,
@@ -871,72 +866,130 @@ where
871866 . extend ( cx. item_bounds ( associated_type_def_id) . iter_instantiated ( cx, trait_ref. args ) ) ;
872867 }
873868
874- let mut replace_projection_with = HashMap :: default ( ) ;
869+ let mut replace_projection_with: HashMap < _ , Vec < _ > > = HashMap :: default ( ) ;
875870 for bound in object_bounds. iter ( ) {
876871 if let ty:: ExistentialPredicate :: Projection ( proj) = bound. skip_binder ( ) {
872+ // FIXME: We *probably* should replace this with a dummy placeholder,
873+ // b/c don't want to replace literal instances of this dyn type that
874+ // show up in the bounds, but just ones that come from substituting
875+ // `Self` with the dyn type.
877876 let proj = proj. with_self_ty ( cx, trait_ref. self_ty ( ) ) ;
878- let old_ty = replace_projection_with. insert ( proj. def_id ( ) , bound. rebind ( proj) ) ;
879- assert_eq ! (
880- old_ty,
881- None ,
882- "{:?} has two generic parameters: {:?} and {:?}" ,
883- proj. projection_term,
884- proj. term,
885- old_ty. unwrap( )
886- ) ;
877+ replace_projection_with. entry ( proj. def_id ( ) ) . or_default ( ) . push ( bound. rebind ( proj) ) ;
887878 }
888879 }
889880
890- let mut folder =
891- ReplaceProjectionWith { ecx, param_env, mapping : replace_projection_with, nested : vec ! [ ] } ;
892- let folded_requirements = requirements. fold_with ( & mut folder) ;
881+ let mut folder = ReplaceProjectionWith {
882+ ecx,
883+ param_env,
884+ self_ty : trait_ref. self_ty ( ) ,
885+ mapping : & replace_projection_with,
886+ nested : vec ! [ ] ,
887+ } ;
893888
894- folder
889+ let requirements = requirements. try_fold_with ( & mut folder) ?;
890+ Ok ( folder
895891 . nested
896892 . into_iter ( )
897- . chain ( folded_requirements . into_iter ( ) . map ( |clause| Goal :: new ( cx, param_env, clause) ) )
898- . collect ( )
893+ . chain ( requirements . into_iter ( ) . map ( |clause| Goal :: new ( cx, param_env, clause) ) )
894+ . collect ( ) )
899895}
900896
901- struct ReplaceProjectionWith < ' a , D : SolverDelegate < Interner = I > , I : Interner > {
902- ecx : & ' a EvalCtxt < ' a , D > ,
897+ struct ReplaceProjectionWith < ' a , ' b , I : Interner , D : SolverDelegate < Interner = I > > {
898+ ecx : & ' a mut EvalCtxt < ' b , D > ,
903899 param_env : I :: ParamEnv ,
904- mapping : HashMap < I :: DefId , ty:: Binder < I , ty:: ProjectionPredicate < I > > > ,
900+ self_ty : I :: Ty ,
901+ mapping : & ' a HashMap < I :: DefId , Vec < ty:: Binder < I , ty:: ProjectionPredicate < I > > > > ,
905902 nested : Vec < Goal < I , I :: Predicate > > ,
906903}
907904
908- impl < D : SolverDelegate < Interner = I > , I : Interner > TypeFolder < I >
909- for ReplaceProjectionWith < ' _ , D , I >
905+ impl < D , I > ReplaceProjectionWith < ' _ , ' _ , I , D >
906+ where
907+ D : SolverDelegate < Interner = I > ,
908+ I : Interner ,
910909{
910+ fn projection_may_match (
911+ & mut self ,
912+ source_projection : ty:: Binder < I , ty:: ProjectionPredicate < I > > ,
913+ target_projection : ty:: AliasTerm < I > ,
914+ ) -> bool {
915+ source_projection. item_def_id ( ) == target_projection. def_id
916+ && self
917+ . ecx
918+ . probe ( |_| ProbeKind :: ProjectionCompatibility )
919+ . enter ( |ecx| -> Result < _ , NoSolution > {
920+ let source_projection = ecx. instantiate_binder_with_infer ( source_projection) ;
921+ ecx. eq ( self . param_env , source_projection. projection_term , target_projection) ?;
922+ ecx. try_evaluate_added_goals ( )
923+ } )
924+ . is_ok ( )
925+ }
926+
927+ /// Try to replace an alias with the term present in the projection bounds of the self type.
928+ /// Returns `Ok<None>` if this alias is not eligible to be replaced, or bail with
929+ /// `Err(Ambiguous)` if it's uncertain which projection bound to replace the term with due
930+ /// to multiple bounds applying.
931+ fn try_eagerly_replace_alias (
932+ & mut self ,
933+ alias_term : ty:: AliasTerm < I > ,
934+ ) -> Result < Option < I :: Term > , Ambiguous > {
935+ if alias_term. self_ty ( ) != self . self_ty {
936+ return Ok ( None ) ;
937+ }
938+
939+ let Some ( replacements) = self . mapping . get ( & alias_term. def_id ) else {
940+ return Ok ( None ) ;
941+ } ;
942+
943+ // This is quite similar to the `projection_may_match` we use in unsizing,
944+ // but here we want to unify a projection predicate against an alias term
945+ // so we can replace it with the the projection predicate's term.
946+ let mut matching_projections = replacements
947+ . iter ( )
948+ . filter ( |source_projection| self . projection_may_match ( * * source_projection, alias_term) ) ;
949+ let Some ( replacement) = matching_projections. next ( ) else {
950+ // This shouldn't happen.
951+ panic ! ( "could not replace {alias_term:?} with term from from {:?}" , self . self_ty) ;
952+ } ;
953+ // FIXME: This *may* have issues with duplicated projections.
954+ if matching_projections. next ( ) . is_some ( ) {
955+ // If there's more than one projection that we can unify here, then we
956+ // need to stall until inference constrains things so that there's only
957+ // one choice.
958+ return Err ( Ambiguous ) ;
959+ }
960+
961+ let replacement = self . ecx . instantiate_binder_with_infer ( * replacement) ;
962+ self . nested . extend (
963+ self . ecx
964+ . eq_and_get_goals ( self . param_env , alias_term, replacement. projection_term )
965+ . expect ( "expected to be able to unify goal projection with dyn's projection" ) ,
966+ ) ;
967+
968+ Ok ( Some ( replacement. term ) )
969+ }
970+ }
971+
972+ /// Marker for bailing with ambiguity.
973+ pub ( crate ) struct Ambiguous ;
974+
975+ impl < D , I > FallibleTypeFolder < I > for ReplaceProjectionWith < ' _ , ' _ , I , D >
976+ where
977+ D : SolverDelegate < Interner = I > ,
978+ I : Interner ,
979+ {
980+ type Error = Ambiguous ;
981+
911982 fn cx ( & self ) -> I {
912983 self . ecx . cx ( )
913984 }
914985
915- fn fold_ty ( & mut self , ty : I :: Ty ) -> I :: Ty {
986+ fn try_fold_ty ( & mut self , ty : I :: Ty ) -> Result < I :: Ty , Ambiguous > {
916987 if let ty:: Alias ( ty:: Projection , alias_ty) = ty. kind ( ) {
917- if let Some ( replacement) = self . mapping . get ( & alias_ty. def_id ) {
918- // We may have a case where our object type's projection bound is higher-ranked,
919- // but the where clauses we instantiated are not. We can solve this by instantiating
920- // the binder at the usage site.
921- let proj = self . ecx . instantiate_binder_with_infer ( * replacement) ;
922- // FIXME: Technically this equate could be fallible...
923- self . nested . extend (
924- self . ecx
925- . eq_and_get_goals (
926- self . param_env ,
927- alias_ty,
928- proj. projection_term . expect_ty ( self . ecx . cx ( ) ) ,
929- )
930- . expect (
931- "expected to be able to unify goal projection with dyn's projection" ,
932- ) ,
933- ) ;
934- proj. term . expect_ty ( )
935- } else {
936- ty. super_fold_with ( self )
988+ if let Some ( term) = self . try_eagerly_replace_alias ( alias_ty. into ( ) ) ? {
989+ return Ok ( term. expect_ty ( ) ) ;
937990 }
938- } else {
939- ty. super_fold_with ( self )
940991 }
992+
993+ ty. try_super_fold_with ( self )
941994 }
942995}
0 commit comments