@@ -30,8 +30,10 @@ use rustc_middle::ty::subst::Subst;
3030use rustc_middle:: ty:: { self , ToPredicate , Ty , TyCtxt , WithConstness } ;
3131use rustc_span:: symbol:: sym;
3232
33+ use std:: borrow:: Cow ;
3334use std:: collections:: BTreeMap ;
3435
36+ use crate :: traits:: ObligationsDedup ;
3537pub use rustc_middle:: traits:: Reveal ;
3638
3739pub type PolyProjectionObligation < ' tcx > = Obligation < ' tcx , ty:: PolyProjectionPredicate < ' tcx > > ;
@@ -839,6 +841,8 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
839841 // mode, which could lead to using incorrect cache results.
840842 let use_cache = !selcx. is_intercrate ( ) ;
841843
844+ let mut obligations = ObligationsDedup :: from_vec ( obligations) ;
845+
842846 let projection_ty = infcx. resolve_vars_if_possible ( projection_ty) ;
843847 let cache_key = ProjectionCacheKey :: new ( projection_ty) ;
844848
@@ -850,65 +854,76 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
850854 // bounds. It might be the case that we want two distinct caches,
851855 // or else another kind of cache entry.
852856
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) => {
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+ } ) ;
881880
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+ } ;
884925 }
885- return Err ( InProgress ) ;
886- }
887- Err ( ProjectionCacheEntry :: Recur ) => {
888- debug ! ( "recur cache" ) ;
889- return Err ( InProgress ) ;
890- }
891- Err ( ProjectionCacheEntry :: NormalizedTy ( ty) ) => {
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+ _ => { }
912927 }
913928 }
914929
@@ -966,7 +981,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
966981 if use_cache {
967982 infcx. inner . borrow_mut ( ) . projection_cache ( ) . insert_ty ( cache_key, result. clone ( ) ) ;
968983 }
969- obligations. extend ( result. obligations ) ;
984+ obligations. extend ( result. obligations . into_iter ( ) . map ( Cow :: Owned ) ) ;
970985 Ok ( Some ( result. value ) )
971986 }
972987 Ok ( ProjectedTy :: NoProgress ( projected_ty) ) => {
@@ -996,7 +1011,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
9961011 infcx. inner . borrow_mut ( ) . projection_cache ( ) . error ( cache_key) ;
9971012 }
9981013 let result = normalize_to_error ( selcx, param_env, projection_ty, cause, depth) ;
999- obligations. extend ( result. obligations ) ;
1014+ obligations. extend ( result. obligations . into_iter ( ) . map ( Cow :: Owned ) ) ;
10001015 Ok ( Some ( result. value ) )
10011016 }
10021017 }
0 commit comments