@@ -5,6 +5,7 @@ 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
10
self as ty, Interner , Movability , Mutability , TypeFoldable , TypeFolder , TypeSuperFoldable ,
10
11
Upcast as _, elaborate,
@@ -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,125 @@ 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
+ is_ambiguous : false ,
894
+ } ;
895
+
896
+ if folder. is_ambiguous {
897
+ return Err ( Ambiguous ) ;
898
+ }
893
899
894
- folder
900
+ let requirements = requirements. fold_with ( & mut folder) ;
901
+ Ok ( folder
895
902
. nested
896
903
. into_iter ( )
897
- . chain ( folded_requirements . into_iter ( ) . map ( |clause| Goal :: new ( cx, param_env, clause) ) )
898
- . collect ( )
904
+ . chain ( requirements . into_iter ( ) . map ( |clause| Goal :: new ( cx, param_env, clause) ) )
905
+ . collect ( ) )
899
906
}
900
907
901
- struct ReplaceProjectionWith < ' a , D : SolverDelegate < Interner = I > , I : Interner > {
902
- ecx : & ' a EvalCtxt < ' a , D > ,
908
+ struct ReplaceProjectionWith < ' a , ' b , I : Interner , D : SolverDelegate < Interner = I > > {
909
+ ecx : & ' a mut EvalCtxt < ' b , D > ,
903
910
param_env : I :: ParamEnv ,
904
- mapping : HashMap < I :: DefId , ty:: Binder < I , ty:: ProjectionPredicate < I > > > ,
911
+ self_ty : I :: Ty ,
912
+ mapping : HashMap < I :: DefId , Vec < ty:: Binder < I , ty:: ProjectionPredicate < I > > > > ,
905
913
nested : Vec < Goal < I , I :: Predicate > > ,
914
+ is_ambiguous : bool ,
915
+ }
916
+
917
+ impl < D : SolverDelegate < Interner = I > , I : Interner > ReplaceProjectionWith < ' _ , ' _ , I , D > {
918
+ /// Try to replace an alias with
919
+ fn try_eagerly_replace_alias ( & mut self , alias_term : ty:: AliasTerm < I > ) -> Option < I :: Term > {
920
+ if alias_term. self_ty ( ) != self . self_ty {
921
+ return None ;
922
+ }
923
+
924
+ let Some ( replacements) = self . mapping . get ( & alias_term. def_id ) else {
925
+ return None ;
926
+ } ;
927
+
928
+ // This is quite similar to the `projection_may_match` we use in unsizing,
929
+ // but here we want to unify a projection predicate against an alias term
930
+ // so we can replace it with the the projection predicate's term.
931
+ let projection_may_match =
932
+ |ecx : & mut EvalCtxt < ' _ , D > ,
933
+ source_projection : ty:: Binder < I , ty:: ProjectionPredicate < I > > ,
934
+ target_projection : ty:: AliasTerm < I > | {
935
+ source_projection. item_def_id ( ) == target_projection. def_id
936
+ && ecx
937
+ . probe ( |_| ProbeKind :: ProjetionCompatibility )
938
+ . enter ( |ecx| -> Result < _ , NoSolution > {
939
+ let source_projection =
940
+ ecx. instantiate_binder_with_infer ( source_projection) ;
941
+ ecx. eq (
942
+ self . param_env ,
943
+ source_projection. projection_term ,
944
+ target_projection,
945
+ ) ?;
946
+ ecx. try_evaluate_added_goals ( )
947
+ } )
948
+ . is_ok ( )
949
+ } ;
950
+
951
+ let mut matching_projections = replacements. iter ( ) . filter ( |source_projection| {
952
+ projection_may_match ( self . ecx , * * source_projection, alias_term)
953
+ } ) ;
954
+ let Some ( replacement) = matching_projections. next ( ) else {
955
+ // FIXME: This *shouldn't* happen.
956
+ return None ;
957
+ } ;
958
+ if matching_projections. next ( ) . is_some ( ) {
959
+ // If there's more than one projection that we can unify here, then we
960
+ // need to stall until inference constrains things so that there's only
961
+ // one choice.
962
+ self . is_ambiguous = true ;
963
+ return None ;
964
+ }
965
+
966
+ let replacement = self . ecx . instantiate_binder_with_infer ( * replacement) ;
967
+ self . nested . extend (
968
+ self . ecx
969
+ . eq_and_get_goals ( self . param_env , alias_term, replacement. projection_term )
970
+ . expect ( "expected to be able to unify goal projection with dyn's projection" ) ,
971
+ ) ;
972
+
973
+ Some ( replacement. term )
974
+ }
906
975
}
907
976
977
+ /// Marker for bailing with ambiguity.
978
+ pub ( crate ) struct Ambiguous ;
979
+
908
980
impl < D : SolverDelegate < Interner = I > , I : Interner > TypeFolder < I >
909
- for ReplaceProjectionWith < ' _ , D , I >
981
+ for ReplaceProjectionWith < ' _ , ' _ , I , D >
910
982
{
911
983
fn cx ( & self ) -> I {
912
984
self . ecx . cx ( )
913
985
}
914
986
915
987
fn fold_ty ( & mut self , ty : I :: Ty ) -> I :: Ty {
916
988
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 )
989
+ if let Some ( term) = self . try_eagerly_replace_alias ( alias_ty. into ( ) ) {
990
+ return term. expect_ty ( ) ;
937
991
}
938
- } else {
939
- ty. super_fold_with ( self )
940
992
}
993
+
994
+ ty. super_fold_with ( self )
941
995
}
942
996
}
0 commit comments