diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs
index 33bddf1dedc1b..74a3804bf03bb 100644
--- a/compiler/rustc_infer/src/traits/project.rs
+++ b/compiler/rustc_infer/src/traits/project.rs
@@ -92,7 +92,7 @@ pub enum ProjectionCacheEntry<'tcx> {
     Ambiguous,
     Recur,
     Error,
-    NormalizedTy(NormalizedTy<'tcx>),
+    NormalizedTy { value: NormalizedTy<'tcx>, full_obligations: Vec<PredicateObligation<'tcx>> },
 }
 
 impl<'tcx> ProjectionCacheStorage<'tcx> {
@@ -139,7 +139,12 @@ impl<'tcx> ProjectionCache<'_, 'tcx> {
     }
 
     /// Indicates that `key` was normalized to `value`.
-    pub fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTy<'tcx>) {
+    pub fn insert_ty(
+        &mut self,
+        key: ProjectionCacheKey<'tcx>,
+        value: NormalizedTy<'tcx>,
+        full_obligations: Vec<PredicateObligation<'tcx>>,
+    ) {
         debug!(
             "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}",
             key, value
@@ -149,7 +154,8 @@ impl<'tcx> ProjectionCache<'_, 'tcx> {
             debug!("Not overwriting Recur");
             return;
         }
-        let fresh_key = map.insert(key, ProjectionCacheEntry::NormalizedTy(value));
+        let fresh_key =
+            map.insert(key, ProjectionCacheEntry::NormalizedTy { value, full_obligations });
         assert!(!fresh_key, "never started projecting `{:?}`", key);
     }
 
@@ -160,7 +166,7 @@ impl<'tcx> ProjectionCache<'_, 'tcx> {
     pub fn complete(&mut self, key: ProjectionCacheKey<'tcx>) {
         let mut map = self.map();
         let ty = match map.get(&key) {
-            Some(&ProjectionCacheEntry::NormalizedTy(ref ty)) => {
+            Some(&ProjectionCacheEntry::NormalizedTy { value: ref ty, .. }) => {
                 debug!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty);
                 ty.value
             }
@@ -174,7 +180,10 @@ impl<'tcx> ProjectionCache<'_, 'tcx> {
 
         map.insert(
             key,
-            ProjectionCacheEntry::NormalizedTy(Normalized { value: ty, obligations: vec![] }),
+            ProjectionCacheEntry::NormalizedTy {
+                value: Normalized { value: ty, obligations: vec![] },
+                full_obligations: vec![],
+            },
         );
     }
 
@@ -186,10 +195,10 @@ impl<'tcx> ProjectionCache<'_, 'tcx> {
         if !ty.obligations.is_empty() {
             self.map().insert(
                 key,
-                ProjectionCacheEntry::NormalizedTy(Normalized {
-                    value: ty.value,
-                    obligations: vec![],
-                }),
+                ProjectionCacheEntry::NormalizedTy {
+                    value: Normalized { value: ty.value, obligations: vec![] },
+                    full_obligations: vec![],
+                },
             );
         }
     }
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 0e685205069cb..ec66aa6296fa9 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -516,7 +516,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
         Err(ProjectionCacheEntry::Recur) => {
             return Err(InProgress);
         }
-        Err(ProjectionCacheEntry::NormalizedTy(ty)) => {
+        Err(ProjectionCacheEntry::NormalizedTy { value: ty, full_obligations }) => {
             // This is the hottest path in this function.
             //
             // If we find the value in the cache, then return it along
@@ -529,7 +529,11 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
             // evaluation this is not the case, and dropping the trait
             // evaluations can causes ICEs (e.g., #43132).
             debug!(?ty, "found normalized ty");
-            obligations.extend(ty.obligations);
+            if selcx.skip_projection_cache() {
+                obligations.extend(full_obligations);
+            } else {
+                obligations.extend(ty.obligations);
+            }
             return Ok(Some(ty.value));
         }
         Err(ProjectionCacheEntry::Error) => {
@@ -571,14 +575,22 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
             };
 
             let cache_value = prune_cache_value_obligations(infcx, &result);
-            infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, cache_value);
+            infcx.inner.borrow_mut().projection_cache().insert_ty(
+                cache_key,
+                cache_value,
+                result.obligations.clone(),
+            );
             obligations.extend(result.obligations);
             Ok(Some(result.value))
         }
         Ok(ProjectedTy::NoProgress(projected_ty)) => {
             debug!(?projected_ty, "opt_normalize_projection_type: no progress");
             let result = Normalized { value: projected_ty, obligations: vec![] };
-            infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone());
+            infcx.inner.borrow_mut().projection_cache().insert_ty(
+                cache_key,
+                result.clone(),
+                vec![],
+            );
             // No need to extend `obligations`.
             Ok(Some(result.value))
         }
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 708688fa8a69d..49c26ded84b56 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -133,6 +133,7 @@ pub struct SelectionContext<'cx, 'tcx> {
     /// policy. In essence, canonicalized queries need their errors propagated
     /// rather than immediately reported because we do not have accurate spans.
     query_mode: TraitQueryMode,
+    skip_projection_cache: bool,
 }
 
 // A stack that walks back up the stack frame.
@@ -221,6 +222,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             intercrate_ambiguity_causes: None,
             allow_negative_impls: false,
             query_mode: TraitQueryMode::Standard,
+            skip_projection_cache: false,
         }
     }
 
@@ -232,6 +234,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             intercrate_ambiguity_causes: None,
             allow_negative_impls: false,
             query_mode: TraitQueryMode::Standard,
+            skip_projection_cache: false,
         }
     }
 
@@ -247,6 +250,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             intercrate_ambiguity_causes: None,
             allow_negative_impls,
             query_mode: TraitQueryMode::Standard,
+            skip_projection_cache: false,
         }
     }
 
@@ -262,9 +266,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             intercrate_ambiguity_causes: None,
             allow_negative_impls: false,
             query_mode,
+            skip_projection_cache: false,
         }
     }
 
+    pub fn skip_projection_cache(&self) -> bool {
+        self.skip_projection_cache
+    }
+
     /// Enables tracking of intercrate ambiguity causes. These are
     /// used in coherence to give improved diagnostics. We don't do
     /// this until we detect a coherence error because it can lead to
@@ -379,12 +388,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PredicateObligation<'tcx>,
     ) -> Result<EvaluationResult, OverflowError> {
-        self.evaluation_probe(|this| {
+        let old = std::mem::replace(&mut self.skip_projection_cache, true);
+        let res = self.evaluation_probe(|this| {
             this.evaluate_predicate_recursively(
                 TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
                 obligation.clone(),
             )
-        })
+        });
+        self.skip_projection_cache = old;
+        res
     }
 
     fn evaluation_probe(