@@ -30,8 +30,10 @@ use rustc_middle::ty::subst::Subst;
30
30
use rustc_middle:: ty:: { self , ToPredicate , Ty , TyCtxt } ;
31
31
use rustc_span:: symbol:: sym;
32
32
33
+ use std:: borrow:: Cow ;
33
34
use std:: collections:: BTreeMap ;
34
35
36
+ use crate :: traits:: ObligationsDedup ;
35
37
pub use rustc_middle:: traits:: Reveal ;
36
38
37
39
pub type PolyProjectionObligation < ' tcx > = Obligation < ' tcx , ty:: PolyProjectionPredicate < ' tcx > > ;
@@ -839,6 +841,8 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
839
841
// mode, which could lead to using incorrect cache results.
840
842
let use_cache = !selcx. is_intercrate ( ) ;
841
843
844
+ let mut obligations = ObligationsDedup :: from_vec ( obligations) ;
845
+
842
846
let projection_ty = infcx. resolve_vars_if_possible ( projection_ty) ;
843
847
let cache_key = ProjectionCacheKey :: new ( projection_ty) ;
844
848
@@ -850,65 +854,76 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
850
854
// bounds. It might be the case that we want two distinct caches,
851
855
// or else another kind of cache entry.
852
856
853
- let cache_result = if use_cache {
854
- infcx. inner . borrow_mut ( ) . projection_cache ( ) . try_start ( cache_key)
855
- } else {
856
- Ok ( ( ) )
857
- } ;
858
- match cache_result {
859
- Ok ( ( ) ) => debug ! ( "no cache" ) ,
860
- Err ( ProjectionCacheEntry :: Ambiguous ) => {
861
- // If we found ambiguity the last time, that means we will continue
862
- // to do so until some type in the key changes (and we know it
863
- // hasn't, because we just fully resolved it).
864
- debug ! ( "found cache entry: ambiguous" ) ;
865
- return Ok ( None ) ;
866
- }
867
- Err ( ProjectionCacheEntry :: InProgress ) => {
868
- // Under lazy normalization, this can arise when
869
- // bootstrapping. That is, imagine an environment with a
870
- // where-clause like `A::B == u32`. Now, if we are asked
871
- // to normalize `A::B`, we will want to check the
872
- // where-clauses in scope. So we will try to unify `A::B`
873
- // with `A::B`, which can trigger a recursive
874
- // normalization.
875
-
876
- debug ! ( "found cache entry: in-progress" ) ;
877
-
878
- // Cache that normalizing this projection resulted in a cycle. This
879
- // should ensure that, unless this happens within a snapshot that's
880
- // rolled back, fulfillment or evaluation will notice the cycle.
857
+ if use_cache {
858
+ let result =
859
+ infcx. inner . borrow_mut ( ) . projection_cache ( ) . try_start_borrowed ( cache_key, |cached| {
860
+ match cached {
861
+ ProjectionCacheEntry :: NormalizedTy { ty, complete : _ } => {
862
+ // This is the hottest path in this function.
863
+ //
864
+ // If we find the value in the cache, then return it along
865
+ // with the obligations that went along with it. Note
866
+ // that, when using a fulfillment context, these
867
+ // obligations could in principle be ignored: they have
868
+ // already been registered when the cache entry was
869
+ // created (and hence the new ones will quickly be
870
+ // discarded as duplicated). But when doing trait
871
+ // evaluation this is not the case, and dropping the trait
872
+ // evaluations can causes ICEs (e.g., #43132).
873
+ debug ! ( ?ty, "found normalized ty" ) ;
874
+ obligations. extend ( ty. obligations . iter ( ) . map ( Cow :: Borrowed ) ) ;
875
+ Ok ( Some ( ty. value ) )
876
+ }
877
+ cached @ _ => Err ( cached. clone ( ) ) ,
878
+ }
879
+ } ) ;
881
880
882
- if use_cache {
883
- infcx. inner . borrow_mut ( ) . projection_cache ( ) . recur ( cache_key) ;
881
+ match result {
882
+ Some ( Ok ( ret) ) => return Ok ( ret) ,
883
+ Some ( Err ( cached) ) => {
884
+ return match cached {
885
+ ProjectionCacheEntry :: Ambiguous => {
886
+ // If we found ambiguity the last time, that means we will continue
887
+ // to do so until some type in the key changes (and we know it
888
+ // hasn't, because we just fully resolved it).
889
+ debug ! ( "found cache entry: ambiguous" ) ;
890
+ Ok ( None )
891
+ }
892
+ ProjectionCacheEntry :: InProgress => {
893
+ // Under lazy normalization, this can arise when
894
+ // bootstrapping. That is, imagine an environment with a
895
+ // where-clause like `A::B == u32`. Now, if we are asked
896
+ // to normalize `A::B`, we will want to check the
897
+ // where-clauses in scope. So we will try to unify `A::B`
898
+ // with `A::B`, which can trigger a recursive
899
+ // normalization.
900
+
901
+ debug ! ( "found cache entry: in-progress" ) ;
902
+
903
+ // Cache that normalizing this projection resulted in a cycle. This
904
+ // should ensure that, unless this happens within a snapshot that's
905
+ // rolled back, fulfillment or evaluation will notice the cycle.
906
+
907
+ if use_cache {
908
+ infcx. inner . borrow_mut ( ) . projection_cache ( ) . recur ( cache_key) ;
909
+ }
910
+ Err ( InProgress )
911
+ }
912
+ ProjectionCacheEntry :: Recur => {
913
+ debug ! ( "recur cache" ) ;
914
+ Err ( InProgress )
915
+ }
916
+ ProjectionCacheEntry :: Error => {
917
+ debug ! ( "opt_normalize_projection_type: found error" ) ;
918
+ let result =
919
+ normalize_to_error ( selcx, param_env, projection_ty, cause, depth) ;
920
+ obligations. extend ( result. obligations . into_iter ( ) . map ( Cow :: Owned ) ) ;
921
+ Ok ( Some ( result. value ) )
922
+ }
923
+ _ => unreachable ! ( "unexpected variant" ) ,
924
+ } ;
884
925
}
885
- return Err ( InProgress ) ;
886
- }
887
- Err ( ProjectionCacheEntry :: Recur ) => {
888
- debug ! ( "recur cache" ) ;
889
- return Err ( InProgress ) ;
890
- }
891
- Err ( ProjectionCacheEntry :: NormalizedTy { ty, complete : _ } ) => {
892
- // This is the hottest path in this function.
893
- //
894
- // If we find the value in the cache, then return it along
895
- // with the obligations that went along with it. Note
896
- // that, when using a fulfillment context, these
897
- // obligations could in principle be ignored: they have
898
- // already been registered when the cache entry was
899
- // created (and hence the new ones will quickly be
900
- // discarded as duplicated). But when doing trait
901
- // evaluation this is not the case, and dropping the trait
902
- // evaluations can causes ICEs (e.g., #43132).
903
- debug ! ( ?ty, "found normalized ty" ) ;
904
- obligations. extend ( ty. obligations ) ;
905
- return Ok ( Some ( ty. value ) ) ;
906
- }
907
- Err ( ProjectionCacheEntry :: Error ) => {
908
- debug ! ( "opt_normalize_projection_type: found error" ) ;
909
- let result = normalize_to_error ( selcx, param_env, projection_ty, cause, depth) ;
910
- obligations. extend ( result. obligations ) ;
911
- return Ok ( Some ( result. value ) ) ;
926
+ _ => { }
912
927
}
913
928
}
914
929
@@ -955,7 +970,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
955
970
if use_cache {
956
971
infcx. inner . borrow_mut ( ) . projection_cache ( ) . insert_ty ( cache_key, result. clone ( ) ) ;
957
972
}
958
- obligations. extend ( result. obligations ) ;
973
+ obligations. extend ( result. obligations . into_iter ( ) . map ( Cow :: Owned ) ) ;
959
974
Ok ( Some ( result. value ) )
960
975
}
961
976
Ok ( ProjectedTy :: NoProgress ( projected_ty) ) => {
@@ -985,7 +1000,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
985
1000
infcx. inner . borrow_mut ( ) . projection_cache ( ) . error ( cache_key) ;
986
1001
}
987
1002
let result = normalize_to_error ( selcx, param_env, projection_ty, cause, depth) ;
988
- obligations. extend ( result. obligations ) ;
1003
+ obligations. extend ( result. obligations . into_iter ( ) . map ( Cow :: Owned ) ) ;
989
1004
Ok ( Some ( result. value ) )
990
1005
}
991
1006
}
0 commit comments