@@ -5,9 +5,10 @@ use derive_where::derive_where;
5
5
use rustc_type_ir:: data_structures:: HashMap ;
6
6
use rustc_type_ir:: inherent:: * ;
7
7
use rustc_type_ir:: lang_items:: TraitSolverLangItem ;
8
+ use rustc_type_ir:: solve:: inspect:: ProbeKind ;
8
9
use 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,
11
12
} ;
12
13
use rustc_type_ir_macros:: { TypeFoldable_Generic , TypeVisitable_Generic } ;
13
14
use tracing:: instrument;
@@ -822,7 +823,7 @@ pub(in crate::solve) fn const_conditions_for_destruct<I: Interner>(
822
823
/// impl Baz for dyn Foo<Item = Ty> {}
823
824
/// ```
824
825
///
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
826
827
/// additional step of eagerly folding the associated types in the where
827
828
/// clauses of the impl. In this example, that means replacing
828
829
/// `<Self as Foo>::Bar` with `Ty` in the first impl.
@@ -833,11 +834,11 @@ pub(in crate::solve) fn const_conditions_for_destruct<I: Interner>(
833
834
// normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9
834
835
// for more details.
835
836
pub ( in crate :: solve) fn predicates_for_object_candidate < D , I > (
836
- ecx : & EvalCtxt < ' _ , D > ,
837
+ ecx : & mut EvalCtxt < ' _ , D > ,
837
838
param_env : I :: ParamEnv ,
838
839
trait_ref : ty:: TraitRef < I > ,
839
840
object_bounds : I :: BoundExistentialPredicates ,
840
- ) -> Vec < Goal < I , I :: Predicate > >
841
+ ) -> Result < Vec < Goal < I , I :: Predicate > > , Ambiguous >
841
842
where
842
843
D : SolverDelegate < Interner = I > ,
843
844
I : Interner ,
@@ -871,72 +872,130 @@ where
871
872
. extend ( cx. item_bounds ( associated_type_def_id) . iter_instantiated ( cx, trait_ref. args ) ) ;
872
873
}
873
874
874
- let mut replace_projection_with = HashMap :: default ( ) ;
875
+ let mut replace_projection_with: HashMap < _ , Vec < _ > > = HashMap :: default ( ) ;
875
876
for bound in object_bounds. iter ( ) {
876
877
if let ty:: ExistentialPredicate :: Projection ( proj) = bound. skip_binder ( ) {
878
+ // FIXME: We *probably* should replace this with a dummy placeholder,
879
+ // b/c don't want to replace literal instances of this dyn type that
880
+ // show up in the bounds, but just ones that come from substituting
881
+ // `Self` with the dyn type.
877
882
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
- ) ;
883
+ replace_projection_with. entry ( proj. def_id ( ) ) . or_default ( ) . push ( bound. rebind ( proj) ) ;
887
884
}
888
885
}
889
886
890
- let mut folder =
891
- ReplaceProjectionWith { ecx, param_env, mapping : replace_projection_with, nested : vec ! [ ] } ;
892
- let folded_requirements = requirements. fold_with ( & mut folder) ;
887
+ let mut folder = ReplaceProjectionWith {
888
+ ecx,
889
+ param_env,
890
+ self_ty : trait_ref. self_ty ( ) ,
891
+ mapping : & replace_projection_with,
892
+ nested : vec ! [ ] ,
893
+ } ;
893
894
894
- folder
895
+ let requirements = requirements. try_fold_with ( & mut folder) ?;
896
+ Ok ( folder
895
897
. nested
896
898
. into_iter ( )
897
- . chain ( folded_requirements . into_iter ( ) . map ( |clause| Goal :: new ( cx, param_env, clause) ) )
898
- . collect ( )
899
+ . chain ( requirements . into_iter ( ) . map ( |clause| Goal :: new ( cx, param_env, clause) ) )
900
+ . collect ( ) )
899
901
}
900
902
901
- struct ReplaceProjectionWith < ' a , D : SolverDelegate < Interner = I > , I : Interner > {
902
- ecx : & ' a EvalCtxt < ' a , D > ,
903
+ struct ReplaceProjectionWith < ' a , ' b , I : Interner , D : SolverDelegate < Interner = I > > {
904
+ ecx : & ' a mut EvalCtxt < ' b , D > ,
903
905
param_env : I :: ParamEnv ,
904
- mapping : HashMap < I :: DefId , ty:: Binder < I , ty:: ProjectionPredicate < I > > > ,
906
+ self_ty : I :: Ty ,
907
+ mapping : & ' a HashMap < I :: DefId , Vec < ty:: Binder < I , ty:: ProjectionPredicate < I > > > > ,
905
908
nested : Vec < Goal < I , I :: Predicate > > ,
906
909
}
907
910
908
- impl < D : SolverDelegate < Interner = I > , I : Interner > TypeFolder < I >
909
- for ReplaceProjectionWith < ' _ , D , I >
911
+ impl < D , I > ReplaceProjectionWith < ' _ , ' _ , I , D >
912
+ where
913
+ D : SolverDelegate < Interner = I > ,
914
+ I : Interner ,
915
+ {
916
+ fn projection_may_match (
917
+ & mut self ,
918
+ source_projection : ty:: Binder < I , ty:: ProjectionPredicate < I > > ,
919
+ target_projection : ty:: AliasTerm < I > ,
920
+ ) -> bool {
921
+ source_projection. item_def_id ( ) == target_projection. def_id
922
+ && self
923
+ . ecx
924
+ . probe ( |_| ProbeKind :: ProjectionCompatibility )
925
+ . enter ( |ecx| -> Result < _ , NoSolution > {
926
+ let source_projection = ecx. instantiate_binder_with_infer ( source_projection) ;
927
+ ecx. eq ( self . param_env , source_projection. projection_term , target_projection) ?;
928
+ ecx. try_evaluate_added_goals ( )
929
+ } )
930
+ . is_ok ( )
931
+ }
932
+
933
+ /// Try to replace an alias with the term present in the projection bounds of the self type.
934
+ /// Returns `Ok<None>` if this alias is not eligible to be replaced, or bail with
935
+ /// `Err(Ambiguous)` if it's uncertain which projection bound to replace the term with due
936
+ /// to multiple bounds applying.
937
+ fn try_eagerly_replace_alias (
938
+ & mut self ,
939
+ alias_term : ty:: AliasTerm < I > ,
940
+ ) -> Result < Option < I :: Term > , Ambiguous > {
941
+ if alias_term. self_ty ( ) != self . self_ty {
942
+ return Ok ( None ) ;
943
+ }
944
+
945
+ let Some ( replacements) = self . mapping . get ( & alias_term. def_id ) else {
946
+ return Ok ( None ) ;
947
+ } ;
948
+
949
+ // This is quite similar to the `projection_may_match` we use in unsizing,
950
+ // but here we want to unify a projection predicate against an alias term
951
+ // so we can replace it with the the projection predicate's term.
952
+ let mut matching_projections = replacements
953
+ . iter ( )
954
+ . filter ( |source_projection| self . projection_may_match ( * * source_projection, alias_term) ) ;
955
+ let Some ( replacement) = matching_projections. next ( ) else {
956
+ // This shouldn't happen.
957
+ panic ! ( "could not replace {alias_term:?} with term from from {:?}" , self . self_ty) ;
958
+ } ;
959
+ // FIXME: This *may* have issues with duplicated projections.
960
+ if matching_projections. next ( ) . is_some ( ) {
961
+ // If there's more than one projection that we can unify here, then we
962
+ // need to stall until inference constrains things so that there's only
963
+ // one choice.
964
+ return Err ( Ambiguous ) ;
965
+ }
966
+
967
+ let replacement = self . ecx . instantiate_binder_with_infer ( * replacement) ;
968
+ self . nested . extend (
969
+ self . ecx
970
+ . eq_and_get_goals ( self . param_env , alias_term, replacement. projection_term )
971
+ . expect ( "expected to be able to unify goal projection with dyn's projection" ) ,
972
+ ) ;
973
+
974
+ Ok ( Some ( replacement. term ) )
975
+ }
976
+ }
977
+
978
+ /// Marker for bailing with ambiguity.
979
+ pub ( crate ) struct Ambiguous ;
980
+
981
+ impl < D , I > FallibleTypeFolder < I > for ReplaceProjectionWith < ' _ , ' _ , I , D >
982
+ where
983
+ D : SolverDelegate < Interner = I > ,
984
+ I : Interner ,
910
985
{
986
+ type Error = Ambiguous ;
987
+
911
988
fn cx ( & self ) -> I {
912
989
self . ecx . cx ( )
913
990
}
914
991
915
- fn fold_ty ( & mut self , ty : I :: Ty ) -> I :: Ty {
992
+ fn try_fold_ty ( & mut self , ty : I :: Ty ) -> Result < I :: Ty , Ambiguous > {
916
993
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 )
994
+ if let Some ( term) = self . try_eagerly_replace_alias ( alias_ty. into ( ) ) ? {
995
+ return Ok ( term. expect_ty ( ) ) ;
937
996
}
938
- } else {
939
- ty. super_fold_with ( self )
940
997
}
998
+
999
+ ty. try_super_fold_with ( self )
941
1000
}
942
1001
}
0 commit comments