@@ -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,22 +823,16 @@ 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.
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.
835
830
pub ( in crate :: solve) fn predicates_for_object_candidate < D , I > (
836
- ecx : & EvalCtxt < ' _ , D > ,
831
+ ecx : & mut EvalCtxt < ' _ , D > ,
837
832
param_env : I :: ParamEnv ,
838
833
trait_ref : ty:: TraitRef < I > ,
839
834
object_bounds : I :: BoundExistentialPredicates ,
840
- ) -> Vec < Goal < I , I :: Predicate > >
835
+ ) -> Result < Vec < Goal < I , I :: Predicate > > , Ambiguous >
841
836
where
842
837
D : SolverDelegate < Interner = I > ,
843
838
I : Interner ,
@@ -871,72 +866,130 @@ where
871
866
. extend ( cx. item_bounds ( associated_type_def_id) . iter_instantiated ( cx, trait_ref. args ) ) ;
872
867
}
873
868
874
- let mut replace_projection_with = HashMap :: default ( ) ;
869
+ let mut replace_projection_with: HashMap < _ , Vec < _ > > = HashMap :: default ( ) ;
875
870
for bound in object_bounds. iter ( ) {
876
871
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.
877
876
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) ) ;
887
878
}
888
879
}
889
880
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
+ } ;
893
888
894
- folder
889
+ let requirements = requirements. try_fold_with ( & mut folder) ?;
890
+ Ok ( folder
895
891
. nested
896
892
. 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 ( ) )
899
895
}
900
896
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 > ,
903
899
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 > > > > ,
905
902
nested : Vec < Goal < I , I :: Predicate > > ,
906
903
}
907
904
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 ,
910
909
{
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
+
911
982
fn cx ( & self ) -> I {
912
983
self . ecx . cx ( )
913
984
}
914
985
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 > {
916
987
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 ( ) ) ;
937
990
}
938
- } else {
939
- ty. super_fold_with ( self )
940
991
}
992
+
993
+ ty. try_super_fold_with ( self )
941
994
}
942
995
}
0 commit comments