diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index c87309e22224f..424a0d742d12b 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -129,13 +129,13 @@ pub(crate) fn codegen_constant<'tcx>(
     };
     let const_val = match const_.val {
         ConstKind::Value(const_val) => const_val,
-        ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted })
-            if fx.tcx.is_static(def.did) =>
+        ConstKind::Unevaluated(uv)
+            if fx.tcx.is_static(uv.def.did) =>
         {
-            assert!(substs.is_empty());
-            assert!(promoted.is_none());
+            assert!(uv.substs(fx.tcx).is_empty());
+            assert!(uv.promoted.is_none());
 
-            return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty)).to_cvalue(fx);
+            return codegen_static_ref(fx, uv.def.did, fx.layout_of(const_.ty)).to_cvalue(fx);
         }
         ConstKind::Unevaluated(unevaluated) => {
             match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) {
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index 8375d4c7ca561..914376d58ddee 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -499,7 +499,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                         ty::Adt(def, ..) if !def.is_box() => {
                             // Again, only create type information if full debuginfo is enabled
                             if cx.sess().opts.debuginfo == DebugInfo::Full
-                                && !impl_self_ty.needs_subst()
+                                && !impl_self_ty.definitely_needs_subst(cx.tcx)
                             {
                                 Some(type_metadata(cx, impl_self_ty, rustc_span::DUMMY_SP))
                             } else {
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 58ce31e0c4c9d..673d348a2a89c 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1398,7 +1398,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 LocalRef::UnsizedPlace(_) => bug!("transmute must not involve unsized locals"),
                 LocalRef::Operand(None) => {
                     let dst_layout = bx.layout_of(self.monomorphized_place_ty(dst.as_ref()));
-                    assert!(!dst_layout.ty.has_erasable_regions());
+                    assert!(!dst_layout.ty.has_erasable_regions(self.cx.tcx()));
                     let place = PlaceRef::alloca(bx, dst_layout);
                     place.storage_live(bx);
                     self.codegen_transmute_into(bx, src, place);
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index 3bbc481b61093..e2edd44826717 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -216,7 +216,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
         let mut allocate_local = |local| {
             let decl = &mir.local_decls[local];
             let layout = bx.layout_of(fx.monomorphize(decl.ty));
-            assert!(!layout.ty.has_erasable_regions());
+            assert!(!layout.ty.has_erasable_regions(cx.tcx()));
 
             if local == mir::RETURN_PLACE && fx.fn_abi.ret.is_indirect() {
                 debug!("alloc: {:?} (return place) -> place", local);
diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
index 448dd6623482c..934ada9932e71 100644
--- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
+++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
@@ -470,7 +470,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
     {
         let needs_canonical_flags = if canonicalize_region_mode.any() {
             TypeFlags::NEEDS_INFER |
-            TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS`
+            TypeFlags::HAS_POTENTIAL_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_POTENTIAL_FREE_REGIONS`
             TypeFlags::HAS_TY_PLACEHOLDER |
             TypeFlags::HAS_CT_PLACEHOLDER
         } else {
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index 01d84e287bc9f..32308910aa7d9 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -129,6 +129,8 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
     where
         R: ConstEquateRelation<'tcx>,
     {
+        let a = self.tcx.expose_default_const_substs(a);
+        let b = self.tcx.expose_default_const_substs(b);
         debug!("{}.consts({:?}, {:?})", relation.tag(), a, b);
         if a == b {
             return Ok(a);
@@ -742,10 +744,9 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                     }
                 }
             }
-            ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted })
-                if self.tcx().lazy_normalization() =>
-            {
-                assert_eq!(promoted, None);
+            ty::ConstKind::Unevaluated(uv) if self.tcx().lazy_normalization() => {
+                assert_eq!(uv.promoted, None);
+                let substs = uv.substs(self.tcx());
                 let substs = self.relate_with_variance(
                     ty::Variance::Invariant,
                     ty::VarianceDiagInfo::default(),
@@ -754,7 +755,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                 )?;
                 Ok(self.tcx().mk_const(ty::Const {
                     ty: c.ty,
-                    val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }),
+                    val: ty::ConstKind::Unevaluated(ty::Unevaluated::new(uv.def, substs)),
                 }))
             }
             _ => relate::super_relate_consts(self, c, c),
@@ -976,10 +977,9 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
                     }
                 }
             }
-            ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted })
-                if self.tcx().lazy_normalization() =>
-            {
-                assert_eq!(promoted, None);
+            ty::ConstKind::Unevaluated(uv) if self.tcx().lazy_normalization() => {
+                assert_eq!(uv.promoted, None);
+                let substs = uv.substs(self.tcx());
                 let substs = self.relate_with_variance(
                     ty::Variance::Invariant,
                     ty::VarianceDiagInfo::default(),
@@ -988,7 +988,7 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
                 )?;
                 Ok(self.tcx().mk_const(ty::Const {
                     ty: c.ty,
-                    val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }),
+                    val: ty::ConstKind::Unevaluated(ty::Unevaluated::new(uv.def, substs)),
                 }))
             }
             _ => relate::super_relate_consts(self, c, c),
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 4830158c15afc..299dcf5f17a87 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1537,6 +1537,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         }
 
         impl<'tcx> ty::fold::TypeVisitor<'tcx> for OpaqueTypesVisitor<'tcx> {
+            fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+                Some(self.tcx)
+            }
+
             fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
                 if let Some((kind, def_id)) = TyCategory::from_ty(self.tcx, t) {
                     let span = self.tcx.def_span(def_id);
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index c2025f3fe4da2..e00003face9ce 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -51,7 +51,7 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> {
 
     fn node_ty_contains_target(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
         self.node_type_opt(hir_id).map(|ty| self.infcx.resolve_vars_if_possible(ty)).filter(|ty| {
-            ty.walk().any(|inner| {
+            ty.walk(self.infcx.tcx).any(|inner| {
                 inner == self.target
                     || match (inner.unpack(), self.target.unpack()) {
                         (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
index fde4ec05ffc86..81059fbcb1051 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -9,7 +9,9 @@ use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorRepor
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{walk_ty, ErasedMap, NestedVisitorMap, Visitor};
 use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind};
-use rustc_middle::ty::{self, AssocItemContainer, RegionKind, Ty, TypeFoldable, TypeVisitor};
+use rustc_middle::ty::{
+    self, AssocItemContainer, RegionKind, Ty, TyCtxt, TypeFoldable, TypeVisitor,
+};
 use rustc_span::symbol::Ident;
 use rustc_span::{MultiSpan, Span};
 
@@ -476,8 +478,14 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
 /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
 pub(super) struct TraitObjectVisitor(pub(super) FxHashSet<DefId>);
 
-impl TypeVisitor<'_> for TraitObjectVisitor {
-    fn visit_ty(&mut self, t: Ty<'_>) -> ControlFlow<Self::BreakTy> {
+impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor {
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        // The default anon const substs cannot include
+        // trait objects, so we don't have to bother looking.
+        None
+    }
+
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         match t.kind() {
             ty::Dynamic(preds, RegionKind::ReStatic) => {
                 if let Some(def_id) = preds.principal_def_id() {
diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs
index 4af1bdf97a773..c40e409891bc2 100644
--- a/compiler/rustc_infer/src/infer/freshen.rs
+++ b/compiler/rustc_infer/src/infer/freshen.rs
@@ -146,7 +146,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
     }
 
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        if !t.needs_infer() && !t.has_erasable_regions() {
+        if !t.needs_infer() && !t.has_erasable_regions(self.tcx()) {
             return t;
         }
 
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 9013bea749a29..bc98a3aa3f95e 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -675,13 +675,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     /// canonicalizing the consts.
     pub fn try_unify_abstract_consts(
         &self,
-        a: ty::Unevaluated<'tcx>,
-        b: ty::Unevaluated<'tcx>,
+        a: ty::Unevaluated<'tcx, ()>,
+        b: ty::Unevaluated<'tcx, ()>,
     ) -> bool {
-        let canonical = self.canonicalize_query(
-            ((a.def, a.substs), (b.def, b.substs)),
-            &mut OriginalQueryValues::default(),
-        );
+        let canonical = self.canonicalize_query((a, b), &mut OriginalQueryValues::default());
         debug!("canonical consts: {:?}", &canonical.value);
 
         self.tcx.try_unify_abstract_consts(canonical.value)
@@ -1592,16 +1589,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     pub fn const_eval_resolve(
         &self,
         param_env: ty::ParamEnv<'tcx>,
-        ty::Unevaluated { def, substs, promoted }: ty::Unevaluated<'tcx>,
+        unevaluated: ty::Unevaluated<'tcx>,
         span: Option<Span>,
     ) -> EvalToConstValueResult<'tcx> {
         let mut original_values = OriginalQueryValues::default();
-        let canonical = self.canonicalize_query((param_env, substs), &mut original_values);
+        let canonical = self.canonicalize_query((param_env, unevaluated), &mut original_values);
 
-        let (param_env, substs) = canonical.value;
+        let (param_env, unevaluated) = canonical.value;
         // The return value is the evaluated value which doesn't contain any reference to inference
         // variables, thus we don't need to substitute back the original values.
-        self.tcx.const_eval_resolve(param_env, ty::Unevaluated { def, substs, promoted }, span)
+        self.tcx.const_eval_resolve(param_env, unevaluated, span)
     }
 
     /// If `typ` is a type variable of some kind, resolve it one level
diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
index 042e6159aff8b..c211d8e94a68b 100644
--- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
@@ -202,6 +202,7 @@ where
         };
 
         value.skip_binder().visit_with(&mut ScopeInstantiator {
+            tcx: self.infcx.tcx,
             next_region: &mut next_region,
             target_index: ty::INNERMOST,
             bound_region_scope: &mut scope,
@@ -757,6 +758,7 @@ where
 /// `for<..`>.  For each of those, it creates an entry in
 /// `bound_region_scope`.
 struct ScopeInstantiator<'me, 'tcx> {
+    tcx: TyCtxt<'tcx>,
     next_region: &'me mut dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
     // The debruijn index of the scope we are instantiating.
     target_index: ty::DebruijnIndex,
@@ -764,6 +766,10 @@ struct ScopeInstantiator<'me, 'tcx> {
 }
 
 impl<'me, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx> {
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.tcx)
+    }
+
     fn visit_binder<T: TypeFoldable<'tcx>>(
         &mut self,
         t: &ty::Binder<'tcx, T>,
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index f69212c599b62..dba73251b4f0d 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -189,7 +189,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
         visited: &mut SsoHashSet<GenericArg<'tcx>>,
     ) -> VerifyBound<'tcx> {
         let mut bounds = parent
-            .walk_shallow(visited)
+            .walk_shallow(self.tcx, visited)
             .filter_map(|child| match child.unpack() {
                 GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)),
                 GenericArgKind::Lifetime(lt) => {
diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs
index 48b8ee17594e3..4b08c2eb9c19e 100644
--- a/compiler/rustc_infer/src/infer/resolve.rs
+++ b/compiler/rustc_infer/src/infer/resolve.rs
@@ -126,6 +126,11 @@ impl<'a, 'tcx> UnresolvedTypeFinder<'a, 'tcx> {
 
 impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> {
     type BreakTy = (Ty<'tcx>, Option<Span>);
+
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.infcx.tcx)
+    }
+
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         let t = self.infcx.shallow_resolve(t);
         if t.has_infer_types() {
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index add0c0ff332f2..8f848f54aad63 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -152,8 +152,8 @@ declare_lint! {
 declare_lint_pass!(BoxPointers => [BOX_POINTERS]);
 
 impl BoxPointers {
-    fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) {
-        for leaf in ty.walk() {
+    fn check_heap_type<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
+        for leaf in ty.walk(cx.tcx) {
             if let GenericArgKind::Type(leaf_ty) = leaf.unpack() {
                 if leaf_ty.is_box() {
                     cx.struct_span_lint(BOX_POINTERS, span, |lint| {
@@ -1656,7 +1656,7 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
                     ConstEquate(..) |
                     TypeWellFormedFromEnv(..) => continue,
                 };
-                if predicate.is_global() {
+                if predicate.is_global(cx.tcx) {
                     cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| {
                         lint.build(&format!(
                             "{} bound {} does not depend on any type \
diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs
index 479cc00199f6a..c14f16b6d11ba 100644
--- a/compiler/rustc_lint/src/noop_method_call.rs
+++ b/compiler/rustc_lint/src/noop_method_call.rs
@@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
             _ => return,
         };
         let substs = cx.typeck_results().node_substs(expr.hir_id);
-        if substs.needs_subst() {
+        if substs.definitely_needs_subst(cx.tcx) {
             // We can't resolve on types that require monomorphization, so we don't handle them if
             // we need to perfom substitution.
             return;
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 829a2abef0db2..5d25578163976 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -1160,6 +1160,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
         impl<'a, 'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> {
             type BreakTy = Ty<'tcx>;
+            fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+                Some(self.cx.tcx)
+            }
 
             fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
                 match ty.kind() {
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index fa7c0670e8ce6..c63613ae3af29 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -38,7 +38,7 @@ impl<'tcx> TyCtxt<'tcx> {
         ct: ty::Unevaluated<'tcx>,
         span: Option<Span>,
     ) -> EvalToConstValueResult<'tcx> {
-        match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs) {
+        match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs(self)) {
             Ok(Some(instance)) => {
                 let cid = GlobalId { instance, promoted: ct.promoted };
                 self.const_eval_global_id(param_env, cid, span)
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 00c2f0a0299a1..8e2917ee5b47c 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -242,6 +242,7 @@ pub struct Body<'tcx> {
 
 impl<'tcx> Body<'tcx> {
     pub fn new(
+        tcx: TyCtxt<'tcx>,
         source: MirSource<'tcx>,
         basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
         source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>,
@@ -284,7 +285,7 @@ impl<'tcx> Body<'tcx> {
             predecessor_cache: PredecessorCache::new(),
             is_cyclic: GraphIsCyclicCache::new(),
         };
-        body.is_polymorphic = body.has_param_types_or_consts();
+        body.is_polymorphic = body.definitely_has_param_types_or_consts(tcx);
         body
     }
 
@@ -294,7 +295,7 @@ impl<'tcx> Body<'tcx> {
     /// is only useful for testing but cannot be `#[cfg(test)]` because it is used in a different
     /// crate.
     pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
-        let mut body = Body {
+        Body {
             phase: MirPhase::Build,
             source: MirSource::item(DefId::local(CRATE_DEF_INDEX)),
             basic_blocks,
@@ -310,9 +311,7 @@ impl<'tcx> Body<'tcx> {
             is_polymorphic: false,
             predecessor_cache: PredecessorCache::new(),
             is_cyclic: GraphIsCyclicCache::new(),
-        };
-        body.is_polymorphic = body.has_param_types_or_consts();
-        body
+        }
     }
 
     #[inline]
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 68de7f29193d8..f8e0d910a5463 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -114,6 +114,10 @@ rustc_queries! {
         desc { |tcx| "compute const default for a given parameter `{}`", tcx.def_path_str(param)  }
     }
 
+    query default_anon_const_substs(key: DefId) -> SubstsRef<'tcx> {
+        desc { |tcx| "computing the default generic arguments for `{}`", tcx.def_path_str(key) }
+    }
+
     /// Records the type of every item.
     query type_of(key: DefId) -> Ty<'tcx> {
         desc { |tcx| "computing type of `{}`", tcx.def_path_str(key) }
@@ -299,12 +303,11 @@ rustc_queries! {
     }
 
     query try_unify_abstract_consts(key: (
-        (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),
-        (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>)
+        ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>
     )) -> bool {
         desc {
             |tcx| "trying to unify the generic constants {} and {}",
-            tcx.def_path_str(key.0.0.did), tcx.def_path_str(key.1.0.did)
+            tcx.def_path_str(key.0.def.did), tcx.def_path_str(key.1.def.did)
         }
     }
 
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index c78151271c171..869b2ab9dbcbc 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -1,6 +1,5 @@
 use crate::mir::interpret::ConstValue;
 use crate::mir::interpret::{LitToConstInput, Scalar};
-use crate::ty::subst::InternalSubsts;
 use crate::ty::{self, Ty, TyCtxt};
 use crate::ty::{ParamEnv, ParamEnvAnd};
 use rustc_errors::ErrorReported;
@@ -100,7 +99,7 @@ impl<'tcx> Const<'tcx> {
             }
             _ => ty::ConstKind::Unevaluated(ty::Unevaluated {
                 def: def.to_global(),
-                substs: InternalSubsts::identity_for_item(tcx, def.did.to_def_id()),
+                substs_: None,
                 promoted: None,
             }),
         };
diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs
index f2db95d162b88..7188eed544551 100644
--- a/compiler/rustc_middle/src/ty/consts/kind.rs
+++ b/compiler/rustc_middle/src/ty/consts/kind.rs
@@ -1,4 +1,5 @@
 use std::convert::TryInto;
+use std::fmt;
 
 use crate::mir::interpret::{AllocId, ConstValue, Scalar};
 use crate::mir::Promoted;
@@ -12,12 +13,53 @@ use rustc_target::abi::Size;
 
 use super::ScalarInt;
 /// An unevaluated, potentially generic, constant.
-#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
+///
+/// If `substs_` is `None` it means that this anon const
+/// still has its default substs.
+///
+/// We check for all possible substs in `fn default_anon_const_substs`,
+/// so refer to that check for more info.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
 #[derive(Hash, HashStable)]
-pub struct Unevaluated<'tcx> {
+pub struct Unevaluated<'tcx, P = Option<Promoted>> {
     pub def: ty::WithOptConstParam<DefId>,
-    pub substs: SubstsRef<'tcx>,
-    pub promoted: Option<Promoted>,
+    pub substs_: Option<SubstsRef<'tcx>>,
+    pub promoted: P,
+}
+
+impl<'tcx> Unevaluated<'tcx> {
+    #[inline]
+    pub fn shrink(self) -> Unevaluated<'tcx, ()> {
+        debug_assert_eq!(self.promoted, None);
+        Unevaluated { def: self.def, substs_: self.substs_, promoted: () }
+    }
+}
+
+impl<'tcx> Unevaluated<'tcx, ()> {
+    #[inline]
+    pub fn expand(self) -> Unevaluated<'tcx> {
+        Unevaluated { def: self.def, substs_: self.substs_, promoted: None }
+    }
+}
+
+impl<'tcx, P: Default> Unevaluated<'tcx, P> {
+    #[inline]
+    pub fn new(def: ty::WithOptConstParam<DefId>, substs: SubstsRef<'tcx>) -> Unevaluated<'tcx, P> {
+        Unevaluated { def, substs_: Some(substs), promoted: Default::default() }
+    }
+}
+
+impl<'tcx, P: Default + PartialEq + fmt::Debug> Unevaluated<'tcx, P> {
+    #[inline]
+    pub fn substs(self, tcx: TyCtxt<'tcx>) -> SubstsRef<'tcx> {
+        self.substs_.unwrap_or_else(|| {
+            // We must not use the parents default substs for promoted constants
+            // as that can result in incorrect substs and calls the `default_anon_const_substs`
+            // for something that might not actually be a constant.
+            debug_assert_eq!(self.promoted, Default::default());
+            tcx.default_anon_const_substs(self.def.did)
+        })
+    }
 }
 
 /// Represents a constant in Rust.
@@ -109,7 +151,7 @@ impl<'tcx> ConstKind<'tcx> {
         tcx: TyCtxt<'tcx>,
         param_env: ParamEnv<'tcx>,
     ) -> Option<Result<ConstValue<'tcx>, ErrorReported>> {
-        if let ConstKind::Unevaluated(Unevaluated { def, substs, promoted }) = self {
+        if let ConstKind::Unevaluated(unevaluated) = self {
             use crate::mir::interpret::ErrorHandled;
 
             // HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
@@ -118,29 +160,32 @@ impl<'tcx> ConstKind<'tcx> {
             // Note that we erase regions *before* calling `with_reveal_all_normalized`,
             // so that we don't try to invoke this query with
             // any region variables.
-            let param_env_and_substs = tcx
+            let param_env_and = tcx
                 .erase_regions(param_env)
                 .with_reveal_all_normalized(tcx)
-                .and(tcx.erase_regions(substs));
+                .and(tcx.erase_regions(unevaluated));
 
             // HACK(eddyb) when the query key would contain inference variables,
             // attempt using identity substs and `ParamEnv` instead, that will succeed
             // when the expression doesn't depend on any parameters.
             // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
             // we can call `infcx.const_eval_resolve` which handles inference variables.
-            let param_env_and_substs = if param_env_and_substs.needs_infer() {
-                tcx.param_env(def.did).and(InternalSubsts::identity_for_item(tcx, def.did))
+            let param_env_and = if param_env_and.needs_infer() {
+                tcx.param_env(unevaluated.def.did).and(ty::Unevaluated {
+                    def: unevaluated.def,
+                    substs_: Some(InternalSubsts::identity_for_item(tcx, unevaluated.def.did)),
+                    promoted: unevaluated.promoted,
+                })
             } else {
-                param_env_and_substs
+                param_env_and
             };
 
             // FIXME(eddyb) maybe the `const_eval_*` methods should take
-            // `ty::ParamEnvAnd<SubstsRef>` instead of having them separate.
-            let (param_env, substs) = param_env_and_substs.into_parts();
+            // `ty::ParamEnvAnd` instead of having them separate.
+            let (param_env, unevaluated) = param_env_and.into_parts();
             // try to resolve e.g. associated constants to their definition on an impl, and then
             // evaluate the const.
-            match tcx.const_eval_resolve(param_env, ty::Unevaluated { def, substs, promoted }, None)
-            {
+            match tcx.const_eval_resolve(param_env, unevaluated, None) {
                 // NOTE(eddyb) `val` contains no lifetimes/types/consts,
                 // and we use the original type, so nothing from `substs`
                 // (which may be identity substs, see above),
diff --git a/compiler/rustc_middle/src/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs
index 759d1a017aa2a..63eb55ed1a620 100644
--- a/compiler/rustc_middle/src/ty/erase_regions.rs
+++ b/compiler/rustc_middle/src/ty/erase_regions.rs
@@ -21,7 +21,9 @@ impl<'tcx> TyCtxt<'tcx> {
         T: TypeFoldable<'tcx>,
     {
         // If there's nothing to erase avoid performing the query at all
-        if !value.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND | TypeFlags::HAS_FREE_REGIONS) {
+        if !value
+            .has_type_flags(TypeFlags::HAS_RE_LATE_BOUND | TypeFlags::HAS_POTENTIAL_FREE_REGIONS)
+        {
             return value;
         }
         debug!("erase_regions({:?})", value);
diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs
index 04df706d90842..a078b6fb742a7 100644
--- a/compiler/rustc_middle/src/ty/flags.rs
+++ b/compiler/rustc_middle/src/ty/flags.rs
@@ -34,6 +34,12 @@ impl FlagComputation {
         result.flags
     }
 
+    pub fn for_unevaluated_const(uv: ty::Unevaluated<'_>) -> TypeFlags {
+        let mut result = FlagComputation::new();
+        result.add_unevaluated_const(uv);
+        result.flags
+    }
+
     fn add_flags(&mut self, flags: TypeFlags) {
         self.flags = self.flags | flags;
     }
@@ -91,7 +97,7 @@ impl FlagComputation {
             &ty::Error(_) => self.add_flags(TypeFlags::HAS_ERROR),
 
             &ty::Param(_) => {
-                self.add_flags(TypeFlags::HAS_TY_PARAM);
+                self.add_flags(TypeFlags::HAS_KNOWN_TY_PARAM);
                 self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
             }
 
@@ -246,8 +252,8 @@ impl FlagComputation {
             ty::PredicateKind::ClosureKind(_def_id, substs, _kind) => {
                 self.add_substs(substs);
             }
-            ty::PredicateKind::ConstEvaluatable(_def_id, substs) => {
-                self.add_substs(substs);
+            ty::PredicateKind::ConstEvaluatable(uv) => {
+                self.add_unevaluated_const(uv);
             }
             ty::PredicateKind::ConstEquate(expected, found) => {
                 self.add_const(expected);
@@ -292,7 +298,7 @@ impl FlagComputation {
                 self.add_bound_var(debruijn);
             }
             ty::ConstKind::Param(_) => {
-                self.add_flags(TypeFlags::HAS_CT_PARAM);
+                self.add_flags(TypeFlags::HAS_KNOWN_CT_PARAM);
                 self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
             }
             ty::ConstKind::Placeholder(_) => {
@@ -304,8 +310,24 @@ impl FlagComputation {
         }
     }
 
-    fn add_unevaluated_const(&mut self, ct: ty::Unevaluated<'tcx>) {
-        self.add_substs(ct.substs);
+    fn add_unevaluated_const<P>(&mut self, ct: ty::Unevaluated<'tcx, P>) {
+        // The generic arguments of unevaluated consts are a bit special,
+        // see the `rustc-dev-guide` for more information.
+        //
+        // FIXME(@lcnr): Actually add a link here.
+        if let Some(substs) = ct.substs_ {
+            // If they are available, we treat them as ordinary generic arguments.
+            self.add_substs(substs);
+        } else {
+            // Otherwise, we add `HAS_UNKNOWN_DEFAULT_CONST_SUBSTS` to signify
+            // that our const may potentially refer to generic parameters.
+            //
+            // Note that depending on which generic parameters are actually
+            // used in this constant, we may not actually refer to any generic
+            // parameters at all.
+            self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
+            self.add_flags(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS);
+        }
         self.add_flags(TypeFlags::HAS_CT_PROJECTION);
     }
 
diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs
index a40210d5a3622..a04b0a7ef6136 100644
--- a/compiler/rustc_middle/src/ty/fold.rs
+++ b/compiler/rustc_middle/src/ty/fold.rs
@@ -74,8 +74,14 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
         self.has_vars_bound_at_or_above(ty::INNERMOST)
     }
 
+    fn definitely_has_type_flags(&self, tcx: TyCtxt<'tcx>, flags: TypeFlags) -> bool {
+        self.visit_with(&mut HasTypeFlagsVisitor { tcx: Some(tcx), flags }).break_value()
+            == Some(FoundFlags)
+    }
+
     fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags)
+        self.visit_with(&mut HasTypeFlagsVisitor { tcx: None, flags }).break_value()
+            == Some(FoundFlags)
     }
     fn has_projections(&self) -> bool {
         self.has_type_flags(TypeFlags::HAS_PROJECTION)
@@ -86,8 +92,18 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
     fn references_error(&self) -> bool {
         self.has_type_flags(TypeFlags::HAS_ERROR)
     }
-    fn has_param_types_or_consts(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_CT_PARAM)
+    fn potentially_has_param_types_or_consts(&self) -> bool {
+        self.has_type_flags(
+            TypeFlags::HAS_KNOWN_TY_PARAM
+                | TypeFlags::HAS_KNOWN_CT_PARAM
+                | TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS,
+        )
+    }
+    fn definitely_has_param_types_or_consts(&self, tcx: TyCtxt<'tcx>) -> bool {
+        self.definitely_has_type_flags(
+            tcx,
+            TypeFlags::HAS_KNOWN_TY_PARAM | TypeFlags::HAS_KNOWN_CT_PARAM,
+        )
     }
     fn has_infer_regions(&self) -> bool {
         self.has_type_flags(TypeFlags::HAS_RE_INFER)
@@ -108,13 +124,18 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
                 | TypeFlags::HAS_CT_PLACEHOLDER,
         )
     }
-    fn needs_subst(&self) -> bool {
-        self.has_type_flags(TypeFlags::NEEDS_SUBST)
+    fn potentially_needs_subst(&self) -> bool {
+        self.has_type_flags(
+            TypeFlags::KNOWN_NEEDS_SUBST | TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS,
+        )
+    }
+    fn definitely_needs_subst(&self, tcx: TyCtxt<'tcx>) -> bool {
+        self.definitely_has_type_flags(tcx, TypeFlags::KNOWN_NEEDS_SUBST)
     }
     /// "Free" regions in this context means that it has any region
     /// that is not (a) erased or (b) late-bound.
-    fn has_free_regions(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
+    fn has_free_regions(&self, tcx: TyCtxt<'tcx>) -> bool {
+        self.definitely_has_type_flags(tcx, TypeFlags::HAS_KNOWN_FREE_REGIONS)
     }
 
     fn has_erased_regions(&self) -> bool {
@@ -122,15 +143,25 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
     }
 
     /// True if there are any un-erased free regions.
-    fn has_erasable_regions(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
+    fn has_erasable_regions(&self, tcx: TyCtxt<'tcx>) -> bool {
+        self.definitely_has_type_flags(tcx, TypeFlags::HAS_KNOWN_FREE_REGIONS)
+    }
+
+    /// Indicates whether this value definitely references only 'global'
+    /// generic parameters that are the same regardless of what fn we are
+    /// in. This is used for caching.
+    ///
+    /// Note that this function is pessimistic and may incorrectly return
+    /// `false`.
+    fn is_known_global(&self) -> bool {
+        !self.has_type_flags(TypeFlags::HAS_POTENTIAL_FREE_LOCAL_NAMES)
     }
 
     /// Indicates whether this value references only 'global'
     /// generic parameters that are the same regardless of what fn we are
     /// in. This is used for caching.
-    fn is_global(&self) -> bool {
-        !self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES)
+    fn is_global(&self, tcx: TyCtxt<'tcx>) -> bool {
+        !self.definitely_has_type_flags(tcx, TypeFlags::HAS_KNOWN_FREE_LOCAL_NAMES)
     }
 
     /// True if there are any late-bound regions
@@ -182,6 +213,10 @@ pub trait TypeFolder<'tcx>: Sized {
         c.super_fold_with(self)
     }
 
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        p.super_fold_with(self)
+    }
+
     fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> {
         bug!("most type folders should not be folding MIR datastructures: {:?}", c)
     }
@@ -189,6 +224,17 @@ pub trait TypeFolder<'tcx>: Sized {
 
 pub trait TypeVisitor<'tcx>: Sized {
     type BreakTy = !;
+    /// Supplies the `tcx` for an unevaluated anonymous constant in case its default substs
+    /// are not yet supplied.
+    ///
+    /// Returning `None` for this method is only recommended if the `TypeVisitor`
+    /// does not care about default anon const substs, as it ignores generic parameters,
+    /// and fetching the default substs would cause a query cycle.
+    ///
+    /// For visitors which return `None` we completely skip the default substs in `ty::Unevaluated::super_visit_with`.
+    /// This means that incorrectly returning `None` can very quickly lead to ICE or other critical bugs, so be careful and
+    /// try to return an actual `tcx` if possible.
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>>;
 
     fn visit_binder<T: TypeFoldable<'tcx>>(
         &mut self,
@@ -209,6 +255,10 @@ pub trait TypeVisitor<'tcx>: Sized {
         c.super_visit_with(self)
     }
 
+    fn visit_unevaluated_const(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> {
+        uv.super_visit_with(self)
+    }
+
     fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
         p.super_visit_with(self)
     }
@@ -301,7 +351,8 @@ impl<'tcx> TyCtxt<'tcx> {
         value: &impl TypeFoldable<'tcx>,
         callback: impl FnMut(ty::Region<'tcx>) -> bool,
     ) -> bool {
-        struct RegionVisitor<F> {
+        struct RegionVisitor<'tcx, F> {
+            tcx: TyCtxt<'tcx>,
             /// The index of a binder *just outside* the things we have
             /// traversed. If we encounter a bound region bound by this
             /// binder or one outer to it, it appears free. Example:
@@ -323,12 +374,16 @@ impl<'tcx> TyCtxt<'tcx> {
             callback: F,
         }
 
-        impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor<F>
+        impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor<'tcx, F>
         where
             F: FnMut(ty::Region<'tcx>) -> bool,
         {
             type BreakTy = ();
 
+            fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+                Some(self.tcx)
+            }
+
             fn visit_binder<T: TypeFoldable<'tcx>>(
                 &mut self,
                 t: &Binder<'tcx, T>,
@@ -356,7 +411,7 @@ impl<'tcx> TyCtxt<'tcx> {
 
             fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
                 // We're only interested in types involving regions
-                if ty.flags().intersects(TypeFlags::HAS_FREE_REGIONS) {
+                if ty.flags().intersects(TypeFlags::HAS_POTENTIAL_FREE_REGIONS) {
                     ty.super_visit_with(self)
                 } else {
                     ControlFlow::CONTINUE
@@ -364,7 +419,9 @@ impl<'tcx> TyCtxt<'tcx> {
             }
         }
 
-        value.visit_with(&mut RegionVisitor { outer_index: ty::INNERMOST, callback }).is_break()
+        value
+            .visit_with(&mut RegionVisitor { tcx: self, outer_index: ty::INNERMOST, callback })
+            .is_break()
     }
 }
 
@@ -708,7 +765,7 @@ impl<'tcx> TyCtxt<'tcx> {
     where
         T: TypeFoldable<'tcx>,
     {
-        let mut collector = LateBoundRegionsCollector::new(just_constraint);
+        let mut collector = LateBoundRegionsCollector::new(self, just_constraint);
         let result = value.as_ref().skip_binder().visit_with(&mut collector);
         assert!(result.is_continue()); // should never have stopped early
         collector.regions
@@ -775,6 +832,11 @@ impl<'tcx> ValidateBoundVars<'tcx> {
 impl<'tcx> TypeVisitor<'tcx> for ValidateBoundVars<'tcx> {
     type BreakTy = ();
 
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        // Anonymous constants do not contain bound vars in their substs by default.
+        None
+    }
+
     fn visit_binder<T: TypeFoldable<'tcx>>(
         &mut self,
         t: &Binder<'tcx, T>,
@@ -989,6 +1051,11 @@ struct HasEscapingVarsVisitor {
 impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor {
     type BreakTy = FoundEscapingVars;
 
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        // Anonymous constants do not contain bound vars in their substs by default.
+        None
+    }
+
     fn visit_binder<T: TypeFoldable<'tcx>>(
         &mut self,
         t: &Binder<'tcx, T>,
@@ -1053,25 +1120,28 @@ impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor {
 struct FoundFlags;
 
 // FIXME: Optimize for checking for infer flags
-struct HasTypeFlagsVisitor {
+struct HasTypeFlagsVisitor<'tcx> {
+    tcx: Option<TyCtxt<'tcx>>,
     flags: ty::TypeFlags,
 }
 
-impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor {
+impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor<'tcx> {
     type BreakTy = FoundFlags;
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        bug!("we shouldn't call this method as we manually look at ct substs");
+    }
 
     #[inline]
-    fn visit_ty(&mut self, t: Ty<'_>) -> ControlFlow<Self::BreakTy> {
-        debug!(
-            "HasTypeFlagsVisitor: t={:?} t.flags={:?} self.flags={:?}",
-            t,
-            t.flags(),
-            self.flags
-        );
-        if t.flags().intersects(self.flags) {
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        let flags = t.flags();
+        debug!("HasTypeFlagsVisitor: t={:?} flags={:?} self.flags={:?}", t, flags, self.flags);
+        if flags.intersects(self.flags) {
             ControlFlow::Break(FoundFlags)
         } else {
-            ControlFlow::CONTINUE
+            match flags.intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) {
+                true if self.tcx.is_some() => UnknownConstSubstsVisitor::search(&self, t),
+                _ => ControlFlow::CONTINUE,
+            }
         }
     }
 
@@ -1093,27 +1163,144 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor {
         if flags.intersects(self.flags) {
             ControlFlow::Break(FoundFlags)
         } else {
-            ControlFlow::CONTINUE
+            match flags.intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) {
+                true if self.tcx.is_some() => UnknownConstSubstsVisitor::search(&self, c),
+                _ => ControlFlow::CONTINUE,
+            }
+        }
+    }
+
+    #[inline]
+    fn visit_unevaluated_const(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> {
+        let flags = FlagComputation::for_unevaluated_const(uv);
+        debug!("HasTypeFlagsVisitor: uv={:?} uv.flags={:?} self.flags={:?}", uv, flags, self.flags);
+        if flags.intersects(self.flags) {
+            ControlFlow::Break(FoundFlags)
+        } else {
+            match flags.intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) {
+                true if self.tcx.is_some() => UnknownConstSubstsVisitor::search(&self, uv),
+                _ => ControlFlow::CONTINUE,
+            }
         }
     }
 
     #[inline]
     fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
+        let flags = predicate.inner.flags;
         debug!(
-            "HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}",
-            predicate, predicate.inner.flags, self.flags
+            "HasTypeFlagsVisitor: predicate={:?} flags={:?} self.flags={:?}",
+            predicate, flags, self.flags
         );
-        if predicate.inner.flags.intersects(self.flags) {
+        if flags.intersects(self.flags) {
             ControlFlow::Break(FoundFlags)
+        } else {
+            match flags.intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) {
+                true if self.tcx.is_some() => UnknownConstSubstsVisitor::search(&self, predicate),
+                _ => ControlFlow::CONTINUE,
+            }
+        }
+    }
+}
+
+struct UnknownConstSubstsVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    flags: ty::TypeFlags,
+}
+
+impl<'tcx> UnknownConstSubstsVisitor<'tcx> {
+    /// This is fairly cold and we don't want to
+    /// bloat the size of the `HasTypeFlagsVisitor`.
+    #[inline(never)]
+    pub fn search<T: TypeFoldable<'tcx>>(
+        visitor: &HasTypeFlagsVisitor<'tcx>,
+        v: T,
+    ) -> ControlFlow<FoundFlags> {
+        if visitor.flags.intersects(TypeFlags::MAY_NEED_DEFAULT_CONST_SUBSTS) {
+            v.super_visit_with(&mut UnknownConstSubstsVisitor {
+                tcx: visitor.tcx.unwrap(),
+                flags: visitor.flags,
+            })
         } else {
             ControlFlow::CONTINUE
         }
     }
 }
 
+impl<'tcx> TypeVisitor<'tcx> for UnknownConstSubstsVisitor<'tcx> {
+    type BreakTy = FoundFlags;
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        bug!("we shouldn't call this method as we manually look at ct substs");
+    }
+
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        if t.flags().intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) {
+            t.super_visit_with(self)
+        } else {
+            ControlFlow::CONTINUE
+        }
+    }
+
+    #[inline]
+    fn visit_unevaluated_const(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> {
+        if uv.substs_.is_none() {
+            self.tcx
+                .default_anon_const_substs(uv.def.did)
+                .visit_with(&mut HasTypeFlagsVisitor { tcx: Some(self.tcx), flags: self.flags })
+        } else {
+            ControlFlow::CONTINUE
+        }
+    }
+
+    #[inline]
+    fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
+        if predicate.inner.flags.intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) {
+            predicate.super_visit_with(self)
+        } else {
+            ControlFlow::CONTINUE
+        }
+    }
+}
+
+impl<'tcx> TyCtxt<'tcx> {
+    /// This is a HACK(const_generics) and should probably not be needed.
+    /// Might however be perf relevant, so who knows.
+    ///
+    /// FIXME(@lcnr): explain this function a bit more
+    pub fn expose_default_const_substs<T: TypeFoldable<'tcx>>(self, v: T) -> T {
+        v.fold_with(&mut ExposeDefaultConstSubstsFolder { tcx: self })
+    }
+}
+
+struct ExposeDefaultConstSubstsFolder<'tcx> {
+    tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> TypeFolder<'tcx> for ExposeDefaultConstSubstsFolder<'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if ty.flags().intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) {
+            ty.super_fold_with(self)
+        } else {
+            ty
+        }
+    }
+
+    fn fold_predicate(&mut self, pred: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if pred.inner.flags.intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) {
+            pred.super_fold_with(self)
+        } else {
+            pred
+        }
+    }
+}
+
 /// Collects all the late-bound regions at the innermost binding level
 /// into a hash set.
-struct LateBoundRegionsCollector {
+struct LateBoundRegionsCollector<'tcx> {
+    tcx: TyCtxt<'tcx>,
     current_index: ty::DebruijnIndex,
     regions: FxHashSet<ty::BoundRegionKind>,
 
@@ -1127,9 +1314,10 @@ struct LateBoundRegionsCollector {
     just_constrained: bool,
 }
 
-impl LateBoundRegionsCollector {
-    fn new(just_constrained: bool) -> Self {
+impl LateBoundRegionsCollector<'tcx> {
+    fn new(tcx: TyCtxt<'tcx>, just_constrained: bool) -> Self {
         LateBoundRegionsCollector {
+            tcx,
             current_index: ty::INNERMOST,
             regions: Default::default(),
             just_constrained,
@@ -1137,7 +1325,11 @@ impl LateBoundRegionsCollector {
     }
 }
 
-impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector {
+impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector<'tcx> {
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.tcx)
+    }
+
     fn visit_binder<T: TypeFoldable<'tcx>>(
         &mut self,
         t: &Binder<'tcx, T>,
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 8f90c7a8845ee..6f234a3958afd 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1727,7 +1727,9 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
         // Ignore layouts that are done with non-empty environments or
         // non-monomorphic layouts, as the user only wants to see the stuff
         // resulting from the final codegen session.
-        if layout.ty.has_param_types_or_consts() || !self.param_env.caller_bounds().is_empty() {
+        if layout.ty.definitely_has_param_types_or_consts(self.tcx)
+            || !self.param_env.caller_bounds().is_empty()
+        {
             return;
         }
 
@@ -1894,7 +1896,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
                 let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
                 match tail.kind() {
                     ty::Param(_) | ty::Projection(_) => {
-                        debug_assert!(tail.has_param_types_or_consts());
+                        debug_assert!(tail.definitely_has_param_types_or_consts(tcx));
                         Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) })
                     }
                     _ => bug!(
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 62e093581b862..8f814a84b4959 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -502,7 +502,7 @@ pub enum PredicateKind<'tcx> {
     Coerce(CoercePredicate<'tcx>),
 
     /// Constant initializer must evaluate successfully.
-    ConstEvaluatable(ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),
+    ConstEvaluatable(ty::Unevaluated<'tcx, ()>),
 
     /// Constants must be equal. The first component is the const that is expected.
     ConstEquate(&'tcx Const<'tcx>, &'tcx Const<'tcx>),
@@ -1292,7 +1292,7 @@ impl<'tcx> ParamEnv<'tcx> {
             Reveal::UserFacing => ParamEnvAnd { param_env: self, value },
 
             Reveal::All => {
-                if value.is_global() {
+                if value.is_known_global() {
                     ParamEnvAnd { param_env: self.without_caller_bounds(), value }
                 } else {
                     ParamEnvAnd { param_env: self, value }
diff --git a/compiler/rustc_middle/src/ty/outlives.rs b/compiler/rustc_middle/src/ty/outlives.rs
index 86750d5c08111..ef4ad998f10c8 100644
--- a/compiler/rustc_middle/src/ty/outlives.rs
+++ b/compiler/rustc_middle/src/ty/outlives.rs
@@ -194,7 +194,7 @@ fn compute_components_recursive(
     out: &mut SmallVec<[Component<'tcx>; 4]>,
     visited: &mut SsoHashSet<GenericArg<'tcx>>,
 ) {
-    for child in parent.walk_shallow(visited) {
+    for child in parent.walk_shallow(tcx, visited) {
         match child.unpack() {
             GenericArgKind::Type(ty) => {
                 compute_components(tcx, ty, out, visited);
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 4befeb1d827cf..0e1197b265bc4 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -927,29 +927,28 @@ pub trait PrettyPrinter<'tcx>:
         }
 
         match ct.val {
-            ty::ConstKind::Unevaluated(ty::Unevaluated {
-                def,
-                substs,
-                promoted: Some(promoted),
-            }) => {
-                p!(print_value_path(def.did, substs));
-                p!(write("::{:?}", promoted));
-            }
-            ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted: None }) => {
-                match self.tcx().def_kind(def.did) {
-                    DefKind::Static | DefKind::Const | DefKind::AssocConst => {
-                        p!(print_value_path(def.did, substs))
-                    }
-                    _ => {
-                        if def.is_local() {
-                            let span = self.tcx().def_span(def.did);
-                            if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) {
-                                p!(write("{}", snip))
+            ty::ConstKind::Unevaluated(uv) => {
+                if let Some(promoted) = uv.promoted {
+                    let substs = uv.substs_.unwrap();
+                    p!(print_value_path(uv.def.did, substs));
+                    p!(write("::{:?}", promoted));
+                } else {
+                    let tcx = self.tcx();
+                    match tcx.def_kind(uv.def.did) {
+                        DefKind::Static | DefKind::Const | DefKind::AssocConst => {
+                            p!(print_value_path(uv.def.did, uv.substs(tcx)))
+                        }
+                        _ => {
+                            if uv.def.is_local() {
+                                let span = tcx.def_span(uv.def.did);
+                                if let Ok(snip) = tcx.sess.source_map().span_to_snippet(span) {
+                                    p!(write("{}", snip))
+                                } else {
+                                    print_underscore!()
+                                }
                             } else {
                                 print_underscore!()
                             }
-                        } else {
-                            print_underscore!()
                         }
                     }
                 }
@@ -1194,7 +1193,7 @@ pub trait PrettyPrinter<'tcx>:
 
             // Aggregates, printed as array/tuple/struct/variant construction syntax.
             //
-            // NB: the `has_param_types_or_consts` check ensures that we can use
+            // NB: the `potentially_has_param_types_or_consts` check ensures that we can use
             // the `destructure_const` query with an empty `ty::ParamEnv` without
             // introducing ICEs (e.g. via `layout_of`) from missing bounds.
             // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`
@@ -1202,7 +1201,9 @@ pub trait PrettyPrinter<'tcx>:
             //
             // FIXME(eddyb) for `--emit=mir`/`-Z dump-mir`, we should provide the
             // correct `ty::ParamEnv` to allow printing *all* constant values.
-            (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_param_types_or_consts() => {
+            (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..))
+                if !ty.potentially_has_param_types_or_consts() =>
+            {
                 let contents = self.tcx().destructure_const(
                     ty::ParamEnv::reveal_all()
                         .and(self.tcx().mk_const(ty::Const { val: ty::ConstKind::Value(ct), ty })),
@@ -2017,6 +2018,7 @@ impl<F: fmt::Write> FmtPrinter<'_, 'tcx, F> {
         debug!("prepare_late_bound_region_info(value: {:?})", value);
 
         struct LateBoundRegionNameCollector<'a, 'tcx> {
+            tcx: TyCtxt<'tcx>,
             used_region_names: &'a mut FxHashSet<Symbol>,
             type_collector: SsoHashSet<Ty<'tcx>>,
         }
@@ -2024,6 +2026,10 @@ impl<F: fmt::Write> FmtPrinter<'_, 'tcx, F> {
         impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_, 'tcx> {
             type BreakTy = ();
 
+            fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+                Some(self.tcx)
+            }
+
             fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
                 debug!("LateBoundRegionNameCollector::visit_region(r: {:?}, address: {:p})", r, &r);
                 if let ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) = *r {
@@ -2053,6 +2059,7 @@ impl<F: fmt::Write> FmtPrinter<'_, 'tcx, F> {
 
         self.used_region_names.clear();
         let mut collector = LateBoundRegionNameCollector {
+            tcx: self.tcx,
             used_region_names: &mut self.used_region_names,
             type_collector: SsoHashSet::new(),
         };
@@ -2287,8 +2294,8 @@ define_print_and_forward_display! {
                 print_value_path(closure_def_id, &[]),
                 write("` implements the trait `{}`", kind))
             }
-            ty::PredicateKind::ConstEvaluatable(def, substs) => {
-                p!("the constant `", print_value_path(def.did, substs), "` can be evaluated")
+            ty::PredicateKind::ConstEvaluatable(uv) => {
+                p!("the constant `", print_value_path(uv.def.did, uv.substs_.map_or(&[], |x| x)), "` can be evaluated")
             }
             ty::PredicateKind::ConstEquate(c1, c2) => {
                 p!("the constant `", print(c1), "` equals `", print(c2), "`")
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index 9c48f05617e09..4a2c8349fdc68 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -579,7 +579,7 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
         (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu))
             if tcx.features().const_evaluatable_checked =>
         {
-            tcx.try_unify_abstract_consts(((au.def, au.substs), (bu.def, bu.substs)))
+            tcx.try_unify_abstract_consts((au.shrink(), bu.shrink()))
         }
 
         // While this is slightly incorrect, it shouldn't matter for `min_const_generics`
@@ -591,13 +591,13 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
             let substs = relation.relate_with_variance(
                 ty::Variance::Invariant,
                 ty::VarianceDiagInfo::default(),
-                au.substs,
-                bu.substs,
+                au.substs(tcx),
+                bu.substs(tcx),
             )?;
             return Ok(tcx.mk_const(ty::Const {
                 val: ty::ConstKind::Unevaluated(ty::Unevaluated {
                     def: au.def,
-                    substs,
+                    substs_: Some(substs),
                     promoted: au.promoted,
                 }),
                 ty: a.ty,
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index a46cac1e7f7f5..2ab25c839704d 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -190,8 +190,8 @@ impl fmt::Debug for ty::PredicateKind<'tcx> {
             ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => {
                 write!(f, "ClosureKind({:?}, {:?}, {:?})", closure_def_id, closure_substs, kind)
             }
-            ty::PredicateKind::ConstEvaluatable(def_id, substs) => {
-                write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs)
+            ty::PredicateKind::ConstEvaluatable(uv) => {
+                write!(f, "ConstEvaluatable({:?}, {:?})", uv.def, uv.substs_)
             }
             ty::PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2),
             ty::PredicateKind::TypeWellFormedFromEnv(ty) => {
@@ -447,8 +447,8 @@ impl<'a, 'tcx> Lift<'tcx> for ty::PredicateKind<'a> {
             ty::PredicateKind::ObjectSafe(trait_def_id) => {
                 Some(ty::PredicateKind::ObjectSafe(trait_def_id))
             }
-            ty::PredicateKind::ConstEvaluatable(def_id, substs) => {
-                tcx.lift(substs).map(|substs| ty::PredicateKind::ConstEvaluatable(def_id, substs))
+            ty::PredicateKind::ConstEvaluatable(uv) => {
+                tcx.lift(uv).map(|uv| ty::PredicateKind::ConstEvaluatable(uv))
             }
             ty::PredicateKind::ConstEquate(c1, c2) => {
                 tcx.lift((c1, c2)).map(|(c1, c2)| ty::PredicateKind::ConstEquate(c1, c2))
@@ -974,6 +974,10 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Region<'tcx> {
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
+    fn fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self {
+        folder.fold_predicate(self)
+    }
+
     fn super_fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self {
         let new = self.inner.kind.fold_with(folder);
         folder.tcx().reuse_or_mk_predicate(self, new)
@@ -1046,13 +1050,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> {
         match self {
             ty::ConstKind::Infer(ic) => ty::ConstKind::Infer(ic.fold_with(folder)),
             ty::ConstKind::Param(p) => ty::ConstKind::Param(p.fold_with(folder)),
-            ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) => {
-                ty::ConstKind::Unevaluated(ty::Unevaluated {
-                    def,
-                    substs: substs.fold_with(folder),
-                    promoted,
-                })
-            }
+            ty::ConstKind::Unevaluated(uv) => ty::ConstKind::Unevaluated(uv.fold_with(folder)),
             ty::ConstKind::Value(_)
             | ty::ConstKind::Bound(..)
             | ty::ConstKind::Placeholder(..)
@@ -1064,7 +1062,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> {
         match *self {
             ty::ConstKind::Infer(ic) => ic.visit_with(visitor),
             ty::ConstKind::Param(p) => p.visit_with(visitor),
-            ty::ConstKind::Unevaluated(ct) => ct.substs.visit_with(visitor),
+            ty::ConstKind::Unevaluated(uv) => uv.visit_with(visitor),
             ty::ConstKind::Value(_)
             | ty::ConstKind::Bound(..)
             | ty::ConstKind::Placeholder(_)
@@ -1082,3 +1080,53 @@ impl<'tcx> TypeFoldable<'tcx> for InferConst<'tcx> {
         ControlFlow::CONTINUE
     }
 }
+
+impl<'tcx> TypeFoldable<'tcx> for ty::Unevaluated<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self {
+        ty::Unevaluated {
+            def: self.def,
+            substs_: Some(self.substs(folder.tcx()).fold_with(folder)),
+            promoted: self.promoted,
+        }
+    }
+
+    fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+        visitor.visit_unevaluated_const(*self)
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+        if let Some(tcx) = visitor.tcx_for_anon_const_substs() {
+            self.substs(tcx).visit_with(visitor)
+        } else if let Some(substs) = self.substs_ {
+            substs.visit_with(visitor)
+        } else {
+            debug!("ignoring default substs of `{:?}`", self.def);
+            ControlFlow::CONTINUE
+        }
+    }
+}
+
+impl<'tcx> TypeFoldable<'tcx> for ty::Unevaluated<'tcx, ()> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self {
+        ty::Unevaluated {
+            def: self.def,
+            substs_: Some(self.substs(folder.tcx()).fold_with(folder)),
+            promoted: self.promoted,
+        }
+    }
+
+    fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+        visitor.visit_unevaluated_const(self.expand())
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+        if let Some(tcx) = visitor.tcx_for_anon_const_substs() {
+            self.substs(tcx).visit_with(visitor)
+        } else if let Some(substs) = self.substs_ {
+            substs.visit_with(visitor)
+        } else {
+            debug!("ignoring default substs of `{:?}`", self.def);
+            ControlFlow::CONTINUE
+        }
+    }
+}
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 8e0a2d4a28df0..8b893b779c0a2 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -1565,26 +1565,26 @@ impl RegionKind {
 
         match *self {
             ty::ReVar(..) => {
-                flags = flags | TypeFlags::HAS_FREE_REGIONS;
-                flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS;
+                flags = flags | TypeFlags::HAS_KNOWN_FREE_REGIONS;
+                flags = flags | TypeFlags::HAS_KNOWN_FREE_LOCAL_REGIONS;
                 flags = flags | TypeFlags::HAS_RE_INFER;
             }
             ty::RePlaceholder(..) => {
-                flags = flags | TypeFlags::HAS_FREE_REGIONS;
-                flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS;
+                flags = flags | TypeFlags::HAS_KNOWN_FREE_REGIONS;
+                flags = flags | TypeFlags::HAS_KNOWN_FREE_LOCAL_REGIONS;
                 flags = flags | TypeFlags::HAS_RE_PLACEHOLDER;
             }
             ty::ReEarlyBound(..) => {
-                flags = flags | TypeFlags::HAS_FREE_REGIONS;
-                flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS;
-                flags = flags | TypeFlags::HAS_RE_PARAM;
+                flags = flags | TypeFlags::HAS_KNOWN_FREE_REGIONS;
+                flags = flags | TypeFlags::HAS_KNOWN_FREE_LOCAL_REGIONS;
+                flags = flags | TypeFlags::HAS_KNOWN_RE_PARAM;
             }
             ty::ReFree { .. } => {
-                flags = flags | TypeFlags::HAS_FREE_REGIONS;
-                flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS;
+                flags = flags | TypeFlags::HAS_KNOWN_FREE_REGIONS;
+                flags = flags | TypeFlags::HAS_KNOWN_FREE_LOCAL_REGIONS;
             }
             ty::ReEmpty(_) | ty::ReStatic => {
-                flags = flags | TypeFlags::HAS_FREE_REGIONS;
+                flags = flags | TypeFlags::HAS_KNOWN_FREE_REGIONS;
             }
             ty::ReLateBound(..) => {
                 flags = flags | TypeFlags::HAS_RE_LATE_BOUND;
diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs
index 84ec2e76c0b07..2438d1a16021b 100644
--- a/compiler/rustc_middle/src/ty/subst.rs
+++ b/compiler/rustc_middle/src/ty/subst.rs
@@ -486,7 +486,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
     }
 
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        if !t.needs_subst() {
+        if !t.potentially_needs_subst() {
             return t;
         }
 
@@ -497,10 +497,6 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
     }
 
     fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
-        if !c.needs_subst() {
-            return c;
-        }
-
         if let ty::ConstKind::Param(p) = c.val {
             self.const_for_param(p, c)
         } else {
diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs
index c2fe5f1ef3f62..73985cf31e0f9 100644
--- a/compiler/rustc_middle/src/ty/walk.rs
+++ b/compiler/rustc_middle/src/ty/walk.rs
@@ -1,8 +1,8 @@
 //! An iterator over the type substructure.
 //! WARNING: this does not keep track of the region depth.
 
-use crate::ty;
 use crate::ty::subst::{GenericArg, GenericArgKind};
+use crate::ty::{self, TyCtxt};
 use rustc_data_structures::sso::SsoHashSet;
 use smallvec::{self, SmallVec};
 
@@ -11,6 +11,7 @@ use smallvec::{self, SmallVec};
 type TypeWalkerStack<'tcx> = SmallVec<[GenericArg<'tcx>; 8]>;
 
 pub struct TypeWalker<'tcx> {
+    expose_default_const_substs: Option<TyCtxt<'tcx>>,
     stack: TypeWalkerStack<'tcx>,
     last_subtree: usize,
     pub visited: SsoHashSet<GenericArg<'tcx>>,
@@ -25,8 +26,13 @@ pub struct TypeWalker<'tcx> {
 /// It maintains a set of visited types and
 /// skips any types that are already there.
 impl<'tcx> TypeWalker<'tcx> {
-    pub fn new(root: GenericArg<'tcx>) -> Self {
-        Self { stack: smallvec![root], last_subtree: 1, visited: SsoHashSet::new() }
+    fn new(expose_default_const_substs: Option<TyCtxt<'tcx>>, root: GenericArg<'tcx>) -> Self {
+        Self {
+            expose_default_const_substs,
+            stack: smallvec![root],
+            last_subtree: 1,
+            visited: SsoHashSet::new(),
+        }
     }
 
     /// Skips the subtree corresponding to the last type
@@ -55,7 +61,7 @@ impl<'tcx> Iterator for TypeWalker<'tcx> {
             let next = self.stack.pop()?;
             self.last_subtree = self.stack.len();
             if self.visited.insert(next) {
-                push_inner(&mut self.stack, next);
+                push_inner(self.expose_default_const_substs, &mut self.stack, next);
                 debug!("next: stack={:?}", self.stack);
                 return Some(next);
             }
@@ -74,8 +80,8 @@ impl GenericArg<'tcx> {
     /// Foo<Bar<isize>> => { Foo<Bar<isize>>, Bar<isize>, isize }
     /// [isize] => { [isize], isize }
     /// ```
-    pub fn walk(self) -> TypeWalker<'tcx> {
-        TypeWalker::new(self)
+    pub fn walk(self, tcx: TyCtxt<'tcx>) -> TypeWalker<'tcx> {
+        TypeWalker::new(Some(tcx), self)
     }
 
     /// Iterator that walks the immediate children of `self`. Hence
@@ -87,16 +93,21 @@ impl GenericArg<'tcx> {
     /// and skips any types that are already there.
     pub fn walk_shallow(
         self,
+        tcx: TyCtxt<'tcx>,
         visited: &mut SsoHashSet<GenericArg<'tcx>>,
     ) -> impl Iterator<Item = GenericArg<'tcx>> {
         let mut stack = SmallVec::new();
-        push_inner(&mut stack, self);
+        push_inner(Some(tcx), &mut stack, self);
         stack.retain(|a| visited.insert(*a));
         stack.into_iter()
     }
 }
 
 impl<'tcx> super::TyS<'tcx> {
+    pub fn walk_ignoring_default_const_substs(&'tcx self) -> TypeWalker<'tcx> {
+        TypeWalker::new(None, self.into())
+    }
+
     /// Iterator that walks `self` and any types reachable from
     /// `self`, in depth-first order. Note that just walks the types
     /// that appear in `self`, it does not descend into the fields of
@@ -107,18 +118,22 @@ impl<'tcx> super::TyS<'tcx> {
     /// Foo<Bar<isize>> => { Foo<Bar<isize>>, Bar<isize>, isize }
     /// [isize] => { [isize], isize }
     /// ```
-    pub fn walk(&'tcx self) -> TypeWalker<'tcx> {
-        TypeWalker::new(self.into())
+    pub fn walk(&'tcx self, tcx: TyCtxt<'tcx>) -> TypeWalker<'tcx> {
+        TypeWalker::new(Some(tcx), self.into())
     }
 }
 
-// We push `GenericArg`s on the stack in reverse order so as to
-// maintain a pre-order traversal. As of the time of this
-// writing, the fact that the traversal is pre-order is not
-// known to be significant to any code, but it seems like the
-// natural order one would expect (basically, the order of the
-// types as they are written).
-fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) {
+/// We push `GenericArg`s on the stack in reverse order so as to
+/// maintain a pre-order traversal. As of the time of this
+/// writing, the fact that the traversal is pre-order is not
+/// known to be significant to any code, but it seems like the
+/// natural order one would expect (basically, the order of the
+/// types as they are written).
+fn push_inner<'tcx>(
+    expose_default_const_substs: Option<TyCtxt<'tcx>>,
+    stack: &mut TypeWalkerStack<'tcx>,
+    parent: GenericArg<'tcx>,
+) {
     match parent.unpack() {
         GenericArgKind::Type(parent_ty) => match *parent_ty.kind() {
             ty::Bool
@@ -196,7 +211,11 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
                 | ty::ConstKind::Error(_) => {}
 
                 ty::ConstKind::Unevaluated(ct) => {
-                    stack.extend(ct.substs.iter().rev());
+                    if let Some(tcx) = expose_default_const_substs {
+                        stack.extend(ct.substs(tcx).iter().rev());
+                    } else if let Some(substs) = ct.substs_ {
+                        stack.extend(substs.iter().rev());
+                    }
                 }
             }
         }
diff --git a/compiler/rustc_mir/src/borrow_check/type_check/liveness/trace.rs b/compiler/rustc_mir/src/borrow_check/type_check/liveness/trace.rs
index e411f1dc10889..566c11811e6e1 100644
--- a/compiler/rustc_mir/src/borrow_check/type_check/liveness/trace.rs
+++ b/compiler/rustc_mir/src/borrow_check/type_check/liveness/trace.rs
@@ -171,7 +171,7 @@ impl LivenessResults<'me, 'typeck, 'flow, 'tcx> {
         for (local, location) in drop_used {
             if !live_locals.contains(&local) {
                 let local_ty = self.cx.body.local_decls[local].ty;
-                if local_ty.has_free_regions() {
+                if local_ty.has_free_regions(self.cx.typeck.tcx()) {
                     self.cx.add_drop_live_facts_for(local, local_ty, &[location], &locations);
                 }
             }
diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
index 999b25319bf8d..639bcb8fa94ef 100644
--- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
@@ -377,8 +377,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
                 },
                 _ => None,
             };
-            if let Some(ty::Unevaluated { def, substs, promoted }) = maybe_uneval {
-                if let Some(promoted) = promoted {
+            if let Some(uv) = maybe_uneval {
+                if let Some(promoted) = uv.promoted {
                     let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>,
                                      promoted: &Body<'tcx>,
                                      ty,
@@ -413,8 +413,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
                         ConstraintCategory::Boring,
                         self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
                             constant.literal.ty(),
-                            def.did,
-                            UserSubsts { substs, user_self_ty: None },
+                            uv.def.did,
+                            UserSubsts { substs: uv.substs(self.tcx()), user_self_ty: None },
                         )),
                     ) {
                         span_mirbug!(
diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs
index 81f78621d1458..403dc1b4793d0 100644
--- a/compiler/rustc_mir/src/interpret/operand.rs
+++ b/compiler/rustc_mir/src/interpret/operand.rs
@@ -555,9 +555,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         match val.val {
             ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric),
             ty::ConstKind::Error(_) => throw_inval!(AlreadyReported(ErrorReported)),
-            ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) => {
-                let instance = self.resolve(def, substs)?;
-                Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into())
+            ty::ConstKind::Unevaluated(uv) => {
+                let instance = self.resolve(uv.def, uv.substs(*self.tcx))?;
+                Ok(self.eval_to_allocation(GlobalId { instance, promoted: uv.promoted })?.into())
             }
             ty::ConstKind::Infer(..) | ty::ConstKind::Placeholder(..) => {
                 span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", val)
diff --git a/compiler/rustc_mir/src/interpret/util.rs b/compiler/rustc_mir/src/interpret/util.rs
index 89f34cd07aa4b..eb0fdebb665fa 100644
--- a/compiler/rustc_mir/src/interpret/util.rs
+++ b/compiler/rustc_mir/src/interpret/util.rs
@@ -9,7 +9,7 @@ where
     T: TypeFoldable<'tcx>,
 {
     debug!("ensure_monomorphic_enough: ty={:?}", ty);
-    if !ty.needs_subst() {
+    if !ty.potentially_needs_subst() {
         return Ok(());
     }
 
@@ -21,19 +21,12 @@ where
     impl<'tcx> TypeVisitor<'tcx> for UsedParamsNeedSubstVisitor<'tcx> {
         type BreakTy = FoundParam;
 
-        fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
-            if !c.needs_subst() {
-                return ControlFlow::CONTINUE;
-            }
-
-            match c.val {
-                ty::ConstKind::Param(..) => ControlFlow::Break(FoundParam),
-                _ => c.super_visit_with(self),
-            }
+        fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+            Some(self.tcx)
         }
 
         fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-            if !ty.needs_subst() {
+            if !ty.potentially_needs_subst() {
                 return ControlFlow::CONTINUE;
             }
 
@@ -50,7 +43,7 @@ where
                         let is_used = unused_params.contains(index).map_or(true, |unused| !unused);
                         // Only recurse when generic parameters in fns, closures and generators
                         // are used and require substitution.
-                        match (is_used, subst.needs_subst()) {
+                        match (is_used, subst.definitely_needs_subst(self.tcx)) {
                             // Just in case there are closures or generators within this subst,
                             // recurse.
                             (true, true) => return subst.super_visit_with(self),
@@ -73,6 +66,13 @@ where
                 _ => ty.super_visit_with(self),
             }
         }
+
+        fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+            match c.val {
+                ty::ConstKind::Param(..) => ControlFlow::Break(FoundParam),
+                _ => c.super_visit_with(self),
+            }
+        }
     }
 
     let mut vis = UsedParamsNeedSubstVisitor { tcx };
diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs
index 95c4237f38396..f03036267acfa 100644
--- a/compiler/rustc_mir/src/monomorphize/collector.rs
+++ b/compiler/rustc_mir/src/monomorphize/collector.rs
@@ -573,7 +573,7 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
     let type_length = instance
         .substs
         .iter()
-        .flat_map(|arg| arg.walk())
+        .flat_map(|arg| arg.walk(tcx))
         .filter(|arg| match arg.unpack() {
             GenericArgKind::Type(_) | GenericArgKind::Const(_) => true,
             GenericArgKind::Lifetime(_) => false,
diff --git a/compiler/rustc_mir/src/monomorphize/polymorphize.rs b/compiler/rustc_mir/src/monomorphize/polymorphize.rs
index fea1adfa3d58b..3c55a4b0a8fb1 100644
--- a/compiler/rustc_mir/src/monomorphize/polymorphize.rs
+++ b/compiler/rustc_mir/src/monomorphize/polymorphize.rs
@@ -178,7 +178,7 @@ fn mark_used_by_predicates<'tcx>(
             // Consider all generic params in a predicate as used if any other parameter in the
             // predicate is used.
             let any_param_used = {
-                let mut vis = HasUsedGenericParams { unused_parameters };
+                let mut vis = HasUsedGenericParams { tcx, unused_parameters };
                 predicate.visit_with(&mut vis).is_break()
             };
 
@@ -283,9 +283,12 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.tcx)
+    }
     #[instrument(skip(self))]
     fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> ControlFlow<Self::BreakTy> {
-        if !c.has_param_types_or_consts() {
+        if !c.potentially_has_param_types_or_consts() {
             return ControlFlow::CONTINUE;
         }
 
@@ -295,7 +298,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
                 self.unused_parameters.clear(param.index);
                 ControlFlow::CONTINUE
             }
-            ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted: Some(p)})
+            ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs_: _, promoted: Some(p)})
                 // Avoid considering `T` unused when constants are of the form:
                 //   `<Self as Foo<T>>::foo::promoted[p]`
                 if self.def_id == def.did && !self.tcx.generics_of(def.did).has_self =>
@@ -306,10 +309,10 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
                 self.visit_body(&promoted[p]);
                 ControlFlow::CONTINUE
             }
-            ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted: None })
-                if self.tcx.def_kind(def.did) == DefKind::AnonConst =>
+            ty::ConstKind::Unevaluated(uv)
+                if self.tcx.def_kind(uv.def.did) == DefKind::AnonConst =>
             {
-                self.visit_child_body(def.did, substs);
+                self.visit_child_body(uv.def.did, uv.substs(self.tcx));
                 ControlFlow::CONTINUE
             }
             _ => c.super_visit_with(self),
@@ -318,7 +321,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
 
     #[instrument(skip(self))]
     fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-        if !ty.has_param_types_or_consts() {
+        if !ty.potentially_has_param_types_or_consts() {
             return ControlFlow::CONTINUE;
         }
 
@@ -346,16 +349,21 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
 }
 
 /// Visitor used to check if a generic parameter is used.
-struct HasUsedGenericParams<'a> {
+struct HasUsedGenericParams<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
     unused_parameters: &'a FiniteBitSet<u32>,
 }
 
-impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a> {
+impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a, 'tcx> {
     type BreakTy = ();
 
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.tcx)
+    }
+
     #[instrument(skip(self))]
     fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> ControlFlow<Self::BreakTy> {
-        if !c.has_param_types_or_consts() {
+        if !c.potentially_has_param_types_or_consts() {
             return ControlFlow::CONTINUE;
         }
 
@@ -373,7 +381,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a> {
 
     #[instrument(skip(self))]
     fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-        if !ty.has_param_types_or_consts() {
+        if !ty.potentially_has_param_types_or_consts() {
             return ControlFlow::CONTINUE;
         }
 
diff --git a/compiler/rustc_mir/src/shim.rs b/compiler/rustc_mir/src/shim.rs
index 8c3d828894c8e..8083ec954478a 100644
--- a/compiler/rustc_mir/src/shim.rs
+++ b/compiler/rustc_mir/src/shim.rs
@@ -163,7 +163,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>)
 
     let source = MirSource::from_instance(ty::InstanceDef::DropGlue(def_id, ty));
     let mut body =
-        new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span);
+        new_body(tcx, source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span);
 
     if ty.is_some() {
         // The first argument (index 0), but add 1 for the return value.
@@ -202,6 +202,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>)
 }
 
 fn new_body<'tcx>(
+    tcx: TyCtxt<'tcx>,
     source: MirSource<'tcx>,
     basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
     local_decls: IndexVec<Local, LocalDecl<'tcx>>,
@@ -209,6 +210,7 @@ fn new_body<'tcx>(
     span: Span,
 ) -> Body<'tcx> {
     Body::new(
+        tcx,
         source,
         basic_blocks,
         IndexVec::from_elem_n(
@@ -353,7 +355,14 @@ impl CloneShimBuilder<'tcx> {
             self.def_id,
             self.sig.inputs_and_output[0],
         ));
-        new_body(source, self.blocks, self.local_decls, self.sig.inputs().len(), self.span)
+        new_body(
+            self.tcx,
+            source,
+            self.blocks,
+            self.local_decls,
+            self.sig.inputs().len(),
+            self.span,
+        )
     }
 
     fn source_info(&self) -> SourceInfo {
@@ -851,8 +860,14 @@ fn build_call_shim<'tcx>(
         block(&mut blocks, vec![], TerminatorKind::Resume, true);
     }
 
-    let mut body =
-        new_body(MirSource::from_instance(instance), blocks, local_decls, sig.inputs().len(), span);
+    let mut body = new_body(
+        tcx,
+        MirSource::from_instance(instance),
+        blocks,
+        local_decls,
+        sig.inputs().len(),
+        span,
+    );
 
     if let Abi::RustCall = sig.abi {
         body.spread_arg = Some(Local::new(sig.inputs().len()));
@@ -917,6 +932,7 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> {
 
     let source = MirSource::item(ctor_id);
     let body = new_body(
+        tcx,
         source,
         IndexVec::from_elem_n(start_block, 1),
         local_decls,
diff --git a/compiler/rustc_mir/src/transform/check_consts/check.rs b/compiler/rustc_mir/src/transform/check_consts/check.rs
index 09e908e675731..0361ddc915d4c 100644
--- a/compiler/rustc_mir/src/transform/check_consts/check.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/check.rs
@@ -365,7 +365,7 @@ impl Checker<'mir, 'tcx> {
     fn check_local_or_return_ty(&mut self, ty: Ty<'tcx>, local: Local) {
         let kind = self.body.local_kind(local);
 
-        for ty in ty.walk() {
+        for ty in ty.walk(self.tcx) {
             let ty = match ty.unpack() {
                 GenericArgKind::Type(ty) => ty,
 
diff --git a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
index ac8c748ea8571..413a9638eb37b 100644
--- a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
@@ -247,7 +247,7 @@ where
 
     // Check the qualifs of the value of `const` items.
     if let Some(ct) = constant.literal.const_for_ty() {
-        if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted }) = ct.val {
+        if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs_: _, promoted }) = ct.val {
             assert!(promoted.is_none());
             // Don't peek inside trait associated constants.
             if cx.tcx.trait_of_item(def.did).is_none() {
diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs
index 4578f04c03452..1932cd5de29b5 100644
--- a/compiler/rustc_mir/src/transform/const_prop.rs
+++ b/compiler/rustc_mir/src/transform/const_prop.rs
@@ -120,7 +120,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
             .predicates_of(def_id.to_def_id())
             .predicates
             .iter()
-            .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
+            .filter_map(|(p, _)| if p.is_global(tcx) { Some(*p) } else { None });
         if traits::impossible_predicates(
             tcx,
             traits::elaborate_predicates(tcx, predicates).map(|o| o.predicate).collect(),
@@ -132,6 +132,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
         trace!("ConstProp starting for {:?}", def_id);
 
         let dummy_body = &Body::new(
+            tcx,
             body.source,
             body.basic_blocks().clone(),
             body.source_scopes.clone(),
@@ -468,7 +469,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
     /// Returns the value, if any, of evaluating `c`.
     fn eval_constant(&mut self, c: &Constant<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
         // FIXME we need to revisit this for #67176
-        if c.needs_subst() {
+        if c.definitely_needs_subst(self.tcx) {
             return None;
         }
 
@@ -483,14 +484,14 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
                             // Promoteds must lint and not error as the user didn't ask for them
                             ConstKind::Unevaluated(ty::Unevaluated {
                                 def: _,
-                                substs: _,
+                                substs_: _,
                                 promoted: Some(_),
                             }) => true,
                             // Out of backwards compatibility we cannot report hard errors in unused
                             // generic functions using associated constants of the generic parameters.
-                            _ => c.literal.needs_subst(),
+                            _ => c.literal.definitely_needs_subst(*tcx),
                         },
-                        ConstantKind::Val(_, ty) => ty.needs_subst(),
+                        ConstantKind::Val(_, ty) => ty.definitely_needs_subst(*tcx),
                     };
                     if lint_only {
                         // Out of backwards compatibility we cannot report hard errors in unused
@@ -720,7 +721,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         }
 
         // FIXME we need to revisit this for #67176
-        if rvalue.needs_subst() {
+        if rvalue.definitely_needs_subst(self.tcx) {
             return None;
         }
 
diff --git a/compiler/rustc_mir/src/transform/function_item_references.rs b/compiler/rustc_mir/src/transform/function_item_references.rs
index 26fe796cb916f..ba2c91a9347ec 100644
--- a/compiler/rustc_mir/src/transform/function_item_references.rs
+++ b/compiler/rustc_mir/src/transform/function_item_references.rs
@@ -49,7 +49,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
                     // Handle calls to `transmute`
                     if self.tcx.is_diagnostic_item(sym::transmute, def_id) {
                         let arg_ty = args[0].ty(self.body, self.tcx);
-                        for generic_inner_ty in arg_ty.walk() {
+                        for generic_inner_ty in arg_ty.walk(self.tcx) {
                             if let GenericArgKind::Type(inner_ty) = generic_inner_ty.unpack() {
                                 if let Some((fn_id, fn_substs)) =
                                     FunctionItemRefChecker::is_fn_ref(inner_ty)
@@ -110,7 +110,7 @@ impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> {
                 let arg_defs = self.tcx.fn_sig(def_id).skip_binder().inputs();
                 for (arg_num, arg_def) in arg_defs.iter().enumerate() {
                     // For all types reachable from the argument type in the fn sig
-                    for generic_inner_ty in arg_def.walk() {
+                    for generic_inner_ty in arg_def.walk(self.tcx) {
                         if let GenericArgKind::Type(inner_ty) = generic_inner_ty.unpack() {
                             // If the inner type matches the type bound by `Pointer`
                             if TyS::same_type(inner_ty, bound_ty) {
diff --git a/compiler/rustc_mir/src/transform/inline/cycle.rs b/compiler/rustc_mir/src/transform/inline/cycle.rs
index c9eafafff57cd..385394ba67d4d 100644
--- a/compiler/rustc_mir/src/transform/inline/cycle.rs
+++ b/compiler/rustc_mir/src/transform/inline/cycle.rs
@@ -89,7 +89,7 @@ crate fn mir_callgraph_reachable(
                     // FIXME: A not fully substituted drop shim can cause ICEs if one attempts to
                     // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this
                     // needs some more analysis.
-                    if callee.needs_subst() {
+                    if callee.definitely_needs_subst(tcx) {
                         continue;
                     }
                 }
diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs
index fa648a6dd49ff..d4c2456e9a436 100644
--- a/compiler/rustc_mir/src/transform/mod.rs
+++ b/compiler/rustc_mir/src/transform/mod.rs
@@ -400,7 +400,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -
         }
     }
 
-    debug_assert!(!body.has_free_regions(), "Free regions in MIR for CTFE");
+    debug_assert!(!body.has_free_regions(tcx), "Free regions in MIR for CTFE");
 
     body
 }
@@ -594,7 +594,7 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
         tcx.mir_drops_elaborated_and_const_checked(ty::WithOptConstParam::unknown(did)).steal();
     run_optimization_passes(tcx, &mut body);
 
-    debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR");
+    debug_assert!(!body.has_free_regions(tcx), "Free regions in optimized MIR");
 
     body
 }
@@ -621,7 +621,7 @@ fn promoted_mir<'tcx>(
         run_post_borrowck_cleanup_passes(tcx, body);
     }
 
-    debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR");
+    debug_assert!(!promoted.has_free_regions(tcx), "Free regions in promoted MIR");
 
     tcx.arena.alloc(promoted)
 }
diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs
index 822b422985cb3..1b43670ba3ac1 100644
--- a/compiler/rustc_mir/src/transform/promote_consts.rs
+++ b/compiler/rustc_mir/src/transform/promote_consts.rs
@@ -859,13 +859,17 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                             ty,
                             val: ty::ConstKind::Unevaluated(ty::Unevaluated {
                                 def,
-                                substs: InternalSubsts::for_item(tcx, def.did, |param, _| {
-                                    if let ty::GenericParamDefKind::Lifetime = param.kind {
-                                        tcx.lifetimes.re_erased.into()
-                                    } else {
-                                        tcx.mk_param_from_def(param)
-                                    }
-                                }),
+                                substs_: Some(InternalSubsts::for_item(
+                                    tcx,
+                                    def.did,
+                                    |param, _| {
+                                        if let ty::GenericParamDefKind::Lifetime = param.kind {
+                                            tcx.lifetimes.re_erased.into()
+                                        } else {
+                                            tcx.mk_param_from_def(param)
+                                        }
+                                    },
+                                )),
                                 promoted: Some(promoted_id),
                             }),
                         })
@@ -988,6 +992,7 @@ pub fn promote_candidates<'tcx>(
         scope.parent_scope = None;
 
         let promoted = Body::new(
+            tcx,
             body.source, // `promoted` gets filled in below
             IndexVec::new(),
             IndexVec::from_elem_n(scope, 1),
diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs
index 7598a011bb677..92591db668ce9 100644
--- a/compiler/rustc_mir/src/util/pretty.rs
+++ b/compiler/rustc_mir/src/util/pretty.rs
@@ -475,7 +475,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> {
                 ty::ConstKind::Unevaluated(uv) => format!(
                     "Unevaluated({}, {:?}, {:?})",
                     self.tcx.def_path_str(uv.def.did),
-                    uv.substs,
+                    uv.substs(self.tcx),
                     uv.promoted
                 ),
                 ty::ConstKind::Value(val) => format!("Value({:?})", val),
@@ -682,6 +682,12 @@ pub fn write_allocations<'tcx>(
     }
     struct CollectAllocIds(BTreeSet<AllocId>);
     impl<'tcx> TypeVisitor<'tcx> for CollectAllocIds {
+        fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+            // `AllocId`s are only inside of `ConstKind::Value` which
+            // can't be part of the anon const default substs.
+            None
+        }
+
         fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
             if let ty::ConstKind::Value(val) = c.val {
                 self.0.extend(alloc_ids_from_const(val));
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index bfebe860971e9..0a760a740dcae 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -239,10 +239,10 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_
         // The exception is `body.user_type_annotations`, which is used unmodified
         // by borrow checking.
         debug_assert!(
-            !(body.local_decls.has_free_regions()
-                || body.basic_blocks().has_free_regions()
-                || body.var_debug_info.has_free_regions()
-                || body.yield_ty().has_free_regions()),
+            !(body.local_decls.has_free_regions(tcx)
+                || body.basic_blocks().has_free_regions(tcx)
+                || body.var_debug_info.has_free_regions(tcx)
+                || body.yield_ty().has_free_regions(tcx)),
             "Unexpected free regions in MIR: {:?}",
             body,
         );
@@ -755,6 +755,7 @@ fn construct_error<'a, 'tcx>(
     cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
 
     let mut body = Body::new(
+        tcx,
         MirSource::item(def.did.to_def_id()),
         cfg.basic_blocks,
         source_scopes,
@@ -843,6 +844,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
 
         Body::new(
+            self.tcx,
             MirSource::item(self.def_id),
             self.cfg.basic_blocks,
             self.source_scopes,
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index f1d882e1ddd70..9106f1d2342a4 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -693,11 +693,10 @@ impl<'tcx> Cx<'tcx> {
                                 // and not the beginning of discriminants (which is always `0`)
                                 let substs = InternalSubsts::identity_for_item(self.tcx(), did);
                                 let lhs = ty::Const {
-                                    val: ty::ConstKind::Unevaluated(ty::Unevaluated {
-                                        def: ty::WithOptConstParam::unknown(did),
+                                    val: ty::ConstKind::Unevaluated(ty::Unevaluated::new(
+                                        ty::WithOptConstParam::unknown(did),
                                         substs,
-                                        promoted: None,
-                                    }),
+                                    )),
                                     ty: var_ty,
                                 };
                                 let lhs = self.thir.exprs.push(mk_const(self.tcx().mk_const(lhs)));
@@ -889,11 +888,10 @@ impl<'tcx> Cx<'tcx> {
                 debug!("convert_path_expr: (const) user_ty={:?}", user_ty);
                 ExprKind::Literal {
                     literal: self.tcx.mk_const(ty::Const {
-                        val: ty::ConstKind::Unevaluated(ty::Unevaluated {
-                            def: ty::WithOptConstParam::unknown(def_id),
+                        val: ty::ConstKind::Unevaluated(ty::Unevaluated::new(
+                            ty::WithOptConstParam::unknown(def_id),
                             substs,
-                            promoted: None,
-                        }),
+                        )),
                         ty: self.typeck_results().node_type(expr.hir_id),
                     }),
                     user_ty,
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index 926bd830da0ac..bbb5de34d1860 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -237,7 +237,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
         // code at the moment, because types like `for <'a> fn(&'a ())` do
         // not *yet* implement `PartialEq`. So for now we leave this here.
         has_impl
-            || ty.walk().any(|t| match t.unpack() {
+            || ty.walk(self.tcx()).any(|t| match t.unpack() {
                 ty::subst::GenericArgKind::Lifetime(_) => false,
                 ty::subst::GenericArgKind::Type(t) => t.is_fn_ptr(),
                 ty::subst::GenericArgKind::Const(_) => false,
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index d84175724cc32..6fe68a0c17a5d 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -134,11 +134,11 @@ where
                 ty.visit_with(self)
             }
             ty::PredicateKind::RegionOutlives(..) => ControlFlow::CONTINUE,
-            ty::PredicateKind::ConstEvaluatable(defs, substs)
+            ty::PredicateKind::ConstEvaluatable(uv)
                 if self.def_id_visitor.tcx().features().const_evaluatable_checked =>
             {
                 let tcx = self.def_id_visitor.tcx();
-                if let Ok(Some(ct)) = AbstractConst::new(tcx, defs, substs) {
+                if let Ok(Some(ct)) = AbstractConst::new(tcx, uv) {
                     self.visit_abstract_const_expr(tcx, ct)?;
                 }
                 ControlFlow::CONTINUE
@@ -179,6 +179,10 @@ where
 {
     type BreakTy = V::BreakTy;
 
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.def_id_visitor.tcx())
+    }
+
     fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<V::BreakTy> {
         let tcx = self.def_id_visitor.tcx();
         // InternalSubsts are not visited here because they are visited below in `super_visit_with`.
diff --git a/compiler/rustc_query_impl/src/keys.rs b/compiler/rustc_query_impl/src/keys.rs
index 38ab26d66ac6a..d85f1c04524dd 100644
--- a/compiler/rustc_query_impl/src/keys.rs
+++ b/compiler/rustc_query_impl/src/keys.rs
@@ -217,18 +217,13 @@ impl<'tcx> Key for (DefId, SubstsRef<'tcx>) {
     }
 }
 
-impl<'tcx> Key
-    for (
-        (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),
-        (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),
-    )
-{
+impl<'tcx> Key for (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>) {
     #[inline(always)]
     fn query_crate_is_local(&self) -> bool {
-        (self.0).0.did.krate == LOCAL_CRATE
+        (self.0).def.did.krate == LOCAL_CRATE
     }
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        (self.0).0.did.default_span(tcx)
+        (self.0).def.did.default_span(tcx)
     }
 }
 
diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs
index 1b4e6b4e38177..e236ef996bcc6 100644
--- a/compiler/rustc_symbol_mangling/src/legacy.rs
+++ b/compiler/rustc_symbol_mangling/src/legacy.rs
@@ -107,9 +107,9 @@ fn get_symbol_hash<'tcx>(
         tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher);
 
         // Include the main item-type. Note that, in this case, the
-        // assertions about `needs_subst` may not hold, but this item-type
+        // assertions about `definitely_needs_subst` may not hold, but this item-type
         // ought to be the same for every reference anyway.
-        assert!(!item_type.has_erasable_regions());
+        assert!(!item_type.has_erasable_regions(tcx));
         hcx.while_hashing_spans(false, |hcx| {
             hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
                 item_type.hash_stable(hcx, &mut hasher);
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index c4c1ec8ce4e0a..e9ebd6d64aa9d 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -277,7 +277,9 @@ impl Printer<'tcx> for &mut SymbolMangler<'tcx> {
 
         // Encode impl generic params if the substitutions contain parameters (implying
         // polymorphization is enabled) and this isn't an inherent impl.
-        if impl_trait_ref.is_some() && substs.iter().any(|a| a.has_param_types_or_consts()) {
+        if impl_trait_ref.is_some()
+            && substs.iter().any(|a| a.definitely_has_param_types_or_consts(self.tcx))
+        {
             self = self.path_generic_args(
                 |this| {
                     this.path_append_ns(
diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs
index c5a6e301deba9..b743c809ca243 100644
--- a/compiler/rustc_trait_selection/src/opaque_types.rs
+++ b/compiler/rustc_trait_selection/src/opaque_types.rs
@@ -352,6 +352,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         if !required_region_bounds.is_empty() {
             for required_region in required_region_bounds {
                 concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
+                    tcx,
                     op: |r| self.sub_regions(infer::CallReturn(span), required_region, r),
                 });
             }
@@ -427,6 +428,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             }
         }
         concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
+            tcx,
             op: |r| self.sub_regions(infer::CallReturn(span), least_region, r),
         });
     }
@@ -461,6 +463,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         );
 
         concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
+            tcx: self.tcx,
             op: |r| {
                 self.member_constraint(
                     opaque_type_key.def_id,
@@ -546,14 +549,19 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
 //
 // We ignore any type parameters because impl trait values are assumed to
 // capture all the in-scope type parameters.
-struct ConstrainOpaqueTypeRegionVisitor<OP> {
+struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP> {
+    tcx: TyCtxt<'tcx>,
     op: OP,
 }
 
-impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<OP>
+impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP>
 where
     OP: FnMut(ty::Region<'tcx>),
 {
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.tcx)
+    }
+
     fn visit_binder<T: TypeFoldable<'tcx>>(
         &mut self,
         t: &ty::Binder<'tcx, T>,
@@ -575,7 +583,7 @@ where
 
     fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         // We're only interested in types involving regions
-        if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
+        if !ty.flags().intersects(ty::TypeFlags::HAS_POTENTIAL_FREE_REGIONS) {
             return ControlFlow::CONTINUE;
         }
 
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 9bb4af16a8f53..668a74bd69715 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -391,7 +391,7 @@ fn orphan_check_trait_ref<'tcx>(
 ) -> Result<(), OrphanCheckErr<'tcx>> {
     debug!("orphan_check_trait_ref(trait_ref={:?}, in_crate={:?})", trait_ref, in_crate);
 
-    if trait_ref.needs_infer() && trait_ref.needs_subst() {
+    if trait_ref.needs_infer() && trait_ref.definitely_needs_subst(tcx) {
         bug!(
             "can't orphan check a trait ref with both params and inference variables {:?}",
             trait_ref
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index b1a938836b70e..5c0bd1d8962f0 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -19,7 +19,7 @@ use rustc_middle::mir::{self, Rvalue, StatementKind, TerminatorKind};
 use rustc_middle::ty::subst::{Subst, SubstsRef};
 use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
 use rustc_session::lint;
-use rustc_span::def_id::{DefId, LocalDefId};
+use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
 
 use std::cmp;
@@ -29,26 +29,20 @@ use std::ops::ControlFlow;
 /// Check if a given constant can be evaluated.
 pub fn is_const_evaluatable<'cx, 'tcx>(
     infcx: &InferCtxt<'cx, 'tcx>,
-    def: ty::WithOptConstParam<DefId>,
-    substs: SubstsRef<'tcx>,
+    uv: ty::Unevaluated<'tcx, ()>,
     param_env: ty::ParamEnv<'tcx>,
     span: Span,
 ) -> Result<(), NotConstEvaluatable> {
-    debug!("is_const_evaluatable({:?}, {:?})", def, substs);
+    debug!("is_const_evaluatable({:?})", uv);
     if infcx.tcx.features().const_evaluatable_checked {
         let tcx = infcx.tcx;
-        match AbstractConst::new(tcx, def, substs)? {
+        match AbstractConst::new(tcx, uv)? {
             // We are looking at a generic abstract constant.
             Some(ct) => {
                 for pred in param_env.caller_bounds() {
                     match pred.kind().skip_binder() {
-                        ty::PredicateKind::ConstEvaluatable(b_def, b_substs) => {
-                            if b_def == def && b_substs == substs {
-                                debug!("is_const_evaluatable: caller_bound ~~> ok");
-                                return Ok(());
-                            }
-
-                            if let Some(b_ct) = AbstractConst::new(tcx, b_def, b_substs)? {
+                        ty::PredicateKind::ConstEvaluatable(uv) => {
+                            if let Some(b_ct) = AbstractConst::new(tcx, uv)? {
                                 // Try to unify with each subtree in the AbstractConst to allow for
                                 // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
                                 // predicate for `(N + 1) * 2`
@@ -91,7 +85,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
                         let leaf = leaf.subst(tcx, ct.substs);
                         if leaf.has_infer_types_or_consts() {
                             failure_kind = FailureKind::MentionsInfer;
-                        } else if leaf.has_param_types_or_consts() {
+                        } else if leaf.definitely_has_param_types_or_consts(tcx) {
                             failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
                         }
 
@@ -101,7 +95,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
                         let ty = ty.subst(tcx, ct.substs);
                         if ty.has_infer_types_or_consts() {
                             failure_kind = FailureKind::MentionsInfer;
-                        } else if ty.has_param_types_or_consts() {
+                        } else if ty.definitely_has_param_types_or_consts(tcx) {
                             failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
                         }
 
@@ -134,7 +128,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
     }
 
     let future_compat_lint = || {
-        if let Some(local_def_id) = def.did.as_local() {
+        if let Some(local_def_id) = uv.def.did.as_local() {
             infcx.tcx.struct_span_lint_hir(
                 lint::builtin::CONST_EVALUATABLE_UNCHECKED,
                 infcx.tcx.hir().local_def_id_to_hir_id(local_def_id),
@@ -155,16 +149,12 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
     // and hopefully soon change this to an error.
     //
     // See #74595 for more details about this.
-    let concrete = infcx.const_eval_resolve(
-        param_env,
-        ty::Unevaluated { def, substs, promoted: None },
-        Some(span),
-    );
-
-    if concrete.is_ok() && substs.has_param_types_or_consts() {
-        match infcx.tcx.def_kind(def.did) {
+    let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span));
+
+    if concrete.is_ok() && uv.substs(infcx.tcx).definitely_has_param_types_or_consts(infcx.tcx) {
+        match infcx.tcx.def_kind(uv.def.did) {
             DefKind::AnonConst => {
-                let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(def);
+                let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def);
 
                 if mir_body.is_polymorphic {
                     future_compat_lint();
@@ -176,7 +166,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
 
     debug!(?concrete, "is_const_evaluatable");
     match concrete {
-        Err(ErrorHandled::TooGeneric) => Err(match substs.has_infer_types_or_consts() {
+        Err(ErrorHandled::TooGeneric) => Err(match uv.has_infer_types_or_consts() {
             true => NotConstEvaluatable::MentionsInfer,
             false => NotConstEvaluatable::MentionsParam,
         }),
@@ -201,15 +191,14 @@ pub struct AbstractConst<'tcx> {
     pub substs: SubstsRef<'tcx>,
 }
 
-impl AbstractConst<'tcx> {
+impl<'tcx> AbstractConst<'tcx> {
     pub fn new(
         tcx: TyCtxt<'tcx>,
-        def: ty::WithOptConstParam<DefId>,
-        substs: SubstsRef<'tcx>,
+        uv: ty::Unevaluated<'tcx, ()>,
     ) -> Result<Option<AbstractConst<'tcx>>, ErrorReported> {
-        let inner = tcx.mir_abstract_const_opt_const_arg(def)?;
-        debug!("AbstractConst::new({:?}) = {:?}", def, inner);
-        Ok(inner.map(|inner| AbstractConst { inner, substs }))
+        let inner = tcx.mir_abstract_const_opt_const_arg(uv.def)?;
+        debug!("AbstractConst::new({:?}) = {:?}", uv, inner);
+        Ok(inner.map(|inner| AbstractConst { inner, substs: uv.substs(tcx) }))
     }
 
     pub fn from_const(
@@ -217,9 +206,7 @@ impl AbstractConst<'tcx> {
         ct: &ty::Const<'tcx>,
     ) -> Result<Option<AbstractConst<'tcx>>, ErrorReported> {
         match ct.val {
-            ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted: _ }) => {
-                AbstractConst::new(tcx, def, substs)
-            }
+            ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv.shrink()),
             ty::ConstKind::Error(_) => Err(ErrorReported),
             _ => Ok(None),
         }
@@ -569,14 +556,11 @@ pub(super) fn mir_abstract_const<'tcx>(
 
 pub(super) fn try_unify_abstract_consts<'tcx>(
     tcx: TyCtxt<'tcx>,
-    ((a, a_substs), (b, b_substs)): (
-        (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),
-        (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),
-    ),
+    (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>),
 ) -> bool {
     (|| {
-        if let Some(a) = AbstractConst::new(tcx, a, a_substs)? {
-            if let Some(b) = AbstractConst::new(tcx, b, b_substs)? {
+        if let Some(a) = AbstractConst::new(tcx, a)? {
+            if let Some(b) = AbstractConst::new(tcx, b)? {
                 return Ok(try_unify(tcx, a, b));
             }
         }
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 90b6ee5f5a6a6..6a7aafc334ba2 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -811,10 +811,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 }
 
                 match obligation.predicate.kind().skip_binder() {
-                    ty::PredicateKind::ConstEvaluatable(def, _) => {
+                    ty::PredicateKind::ConstEvaluatable(uv) => {
                         let mut err =
                             self.tcx.sess.struct_span_err(span, "unconstrained generic constant");
-                        let const_span = self.tcx.def_span(def.did);
+                        let const_span = self.tcx.def_span(uv.def.did);
                         match self.tcx.sess.source_map().span_to_snippet(const_span) {
                             Ok(snippet) => err.help(&format!(
                                 "try adding a `where` bound using this expression: `where [(); {}]:`",
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 4b94deff825bd..f214930c57fe4 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -543,11 +543,10 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
                     }
                 }
 
-                ty::PredicateKind::ConstEvaluatable(def_id, substs) => {
+                ty::PredicateKind::ConstEvaluatable(uv) => {
                     match const_evaluatable::is_const_evaluatable(
                         self.selcx.infcx(),
-                        def_id,
-                        substs,
+                        uv,
                         obligation.param_env,
                         obligation.cause.span,
                     ) {
@@ -555,7 +554,9 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
                         Err(NotConstEvaluatable::MentionsInfer) => {
                             pending_obligation.stalled_on.clear();
                             pending_obligation.stalled_on.extend(
-                                substs.iter().filter_map(TyOrConstInferVar::maybe_from_generic_arg),
+                                uv.substs(infcx.tcx)
+                                    .iter()
+                                    .filter_map(TyOrConstInferVar::maybe_from_generic_arg),
                             );
                             ProcessResult::Unchanged
                         }
@@ -570,7 +571,8 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
 
                 ty::PredicateKind::ConstEquate(c1, c2) => {
                     debug!(?c1, ?c2, "equating consts");
-                    if self.selcx.tcx().features().const_evaluatable_checked {
+                    let tcx = self.selcx.tcx();
+                    if tcx.features().const_evaluatable_checked {
                         // FIXME: we probably should only try to unify abstract constants
                         // if the constants depend on generic parameters.
                         //
@@ -578,7 +580,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
                         if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
                             (c1.val, c2.val)
                         {
-                            if infcx.try_unify_abstract_consts(a, b) {
+                            if infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) {
                                 return ProcessResult::Changed(vec![]);
                             }
                         }
@@ -597,7 +599,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
                                 Err(ErrorHandled::TooGeneric) => {
                                     stalled_on.extend(
                                         unevaluated
-                                            .substs
+                                            .substs(tcx)
                                             .iter()
                                             .filter_map(TyOrConstInferVar::maybe_from_generic_arg),
                                     );
@@ -668,7 +670,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
         stalled_on: &mut Vec<TyOrConstInferVar<'tcx>>,
     ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
         let infcx = self.selcx.infcx();
-        if obligation.predicate.is_global() {
+        if obligation.predicate.is_known_global() {
             // no type variables present, can use evaluation for better caching.
             // FIXME: consider caching errors too.
             //
@@ -727,7 +729,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
     ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
         let tcx = self.selcx.tcx();
 
-        if obligation.predicate.is_global() {
+        if obligation.predicate.is_global(tcx) {
             // no type variables present, can use evaluation for better caching.
             // FIXME: consider caching errors too.
             //
@@ -766,14 +768,15 @@ fn substs_infer_vars<'a, 'tcx>(
     selcx: &mut SelectionContext<'a, 'tcx>,
     substs: ty::Binder<'tcx, SubstsRef<'tcx>>,
 ) -> impl Iterator<Item = TyOrConstInferVar<'tcx>> {
+    let tcx = selcx.tcx();
     selcx
         .infcx()
         .resolve_vars_if_possible(substs)
         .skip_binder() // ok because this check doesn't care about regions
         .iter()
         .filter(|arg| arg.has_infer_types_or_consts())
-        .flat_map(|arg| {
-            let mut walker = arg.walk();
+        .flat_map(move |arg| {
+            let mut walker = arg.walk(tcx);
             while let Some(c) = walker.next() {
                 if !c.has_infer_types_or_consts() {
                     walker.visited.remove(&c);
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 477d29f1a4fb9..554b2950263ec 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -450,7 +450,7 @@ fn subst_and_check_impossible_predicates<'tcx>(
     debug!("subst_and_check_impossible_predicates(key={:?})", key);
 
     let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates;
-    predicates.retain(|predicate| !predicate.needs_subst());
+    predicates.retain(|predicate| !predicate.definitely_needs_subst(tcx));
     let result = impossible_predicates(tcx, predicates);
 
     debug!("subst_and_check_impossible_predicates(key={:?}) = {:?}", key, result);
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 02b43de0d1606..dd974e6fd4f1c 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -278,7 +278,7 @@ fn predicate_references_self(
     (predicate, sp): (ty::Predicate<'tcx>, Span),
 ) -> Option<Span> {
     let self_ty = tcx.types.self_param;
-    let has_self_ty = |arg: &GenericArg<'_>| arg.walk().any(|arg| arg == self_ty.into());
+    let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk(tcx).any(|arg| arg == self_ty.into());
     match predicate.kind().skip_binder() {
         ty::PredicateKind::Trait(ref data) => {
             // In the case of a trait predicate, we can skip the "self" type.
@@ -771,6 +771,9 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>(
 
     impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> {
         type BreakTy = ();
+        fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+            Some(self.tcx)
+        }
 
         fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
             match t.kind() {
@@ -851,12 +854,12 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>(
         }
 
         fn visit_predicate(&mut self, pred: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
-            if let ty::PredicateKind::ConstEvaluatable(def, substs) = pred.kind().skip_binder() {
+            if let ty::PredicateKind::ConstEvaluatable(ct) = pred.kind().skip_binder() {
                 // FIXME(const_evaluatable_checked): We should probably deduplicate the logic for
                 // `AbstractConst`s here, it might make sense to change `ConstEvaluatable` to
                 // take a `ty::Const` instead.
                 use rustc_middle::mir::abstract_const::Node;
-                if let Ok(Some(ct)) = AbstractConst::new(self.tcx, def, substs) {
+                if let Ok(Some(ct)) = AbstractConst::new(self.tcx, ct) {
                     const_evaluatable::walk_abstract_const(self.tcx, ct, |node| match node.root() {
                         Node::Leaf(leaf) => {
                             let leaf = leaf.subst(self.tcx, ct.substs);
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 21e1bd8f46467..ed5fe466c69f7 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -78,8 +78,11 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
         // The rest of the code is already set up to be lazy about replacing bound vars,
         // and only when we actually have to normalize.
         if value.has_escaping_bound_vars() {
-            let mut max_visitor =
-                MaxEscapingBoundVarVisitor { outer_index: ty::INNERMOST, escaping: 0 };
+            let mut max_visitor = MaxEscapingBoundVarVisitor {
+                tcx: self.infcx.tcx,
+                outer_index: ty::INNERMOST,
+                escaping: 0,
+            };
             value.visit_with(&mut max_visitor);
             if max_visitor.escaping > 0 {
                 normalizer.universes.extend((0..max_visitor.escaping).map(|_| None));
@@ -106,13 +109,18 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
 }
 
 /// Visitor to find the maximum escaping bound var
-struct MaxEscapingBoundVarVisitor {
+struct MaxEscapingBoundVarVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
     // The index which would count as escaping
     outer_index: ty::DebruijnIndex,
     escaping: usize,
 }
 
-impl<'tcx> TypeVisitor<'tcx> for MaxEscapingBoundVarVisitor {
+impl<'tcx> TypeVisitor<'tcx> for MaxEscapingBoundVarVisitor<'tcx> {
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.tcx)
+    }
+
     fn visit_binder<T: TypeFoldable<'tcx>>(
         &mut self,
         t: &ty::Binder<'tcx, T>,
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index ba62ce1d5312e..c571a1d628427 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -947,7 +947,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
                 let mut unsizing_params = GrowableBitSet::new_empty();
                 if tcx.features().relaxed_struct_unsize {
-                    for arg in tail_field_ty.walk() {
+                    for arg in tail_field_ty.walk(tcx) {
                         if let Some(i) = maybe_unsizing_param_idx(arg) {
                             unsizing_params.insert(i);
                         }
@@ -956,7 +956,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     // Ensure none of the other fields mention the parameters used
                     // in unsizing.
                     for field in prefix_fields {
-                        for arg in tcx.type_of(field.did).walk() {
+                        for arg in tcx.type_of(field.did).walk(tcx) {
                             if let Some(i) = maybe_unsizing_param_idx(arg) {
                                 unsizing_params.remove(i);
                             }
@@ -968,7 +968,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     }
                 } else {
                     let mut found = false;
-                    for arg in tail_field_ty.walk() {
+                    for arg in tail_field_ty.walk(tcx) {
                         if let Some(i) = maybe_unsizing_param_idx(arg) {
                             unsizing_params.insert(i);
                             found = true;
@@ -984,7 +984,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     // by putting it in a query; it would only need the `DefId` as it
                     // looks at declared field types, not anything substituted.
                     for field in prefix_fields {
-                        for arg in tcx.type_of(field.did).walk() {
+                        for arg in tcx.type_of(field.did).walk(tcx) {
                             if let Some(i) = maybe_unsizing_param_idx(arg) {
                                 if unsizing_params.contains(i) {
                                     return Err(Unimplemented);
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index c150b22226627..1580562a92f38 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -544,7 +544,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 },
 
                 ty::PredicateKind::TypeOutlives(pred) => {
-                    if pred.0.is_global() {
+                    if pred.0.is_known_global() {
                         Ok(EvaluatedToOk)
                     } else {
                         Ok(EvaluatedToOkModuloRegions)
@@ -598,11 +598,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     }
                 }
 
-                ty::PredicateKind::ConstEvaluatable(def_id, substs) => {
+                ty::PredicateKind::ConstEvaluatable(uv) => {
                     match const_evaluatable::is_const_evaluatable(
                         self.infcx,
-                        def_id,
-                        substs,
+                        uv,
                         obligation.param_env,
                         obligation.cause.span,
                     ) {
@@ -624,7 +623,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
                             (c1.val, c2.val)
                         {
-                            if self.infcx.try_unify_abstract_consts(a, b) {
+                            if self.infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) {
                                 return Ok(EvaluatedToOk);
                             }
                         }
@@ -692,8 +691,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         debug!(?obligation, "evaluate_trait_predicate_recursively");
 
         if !self.intercrate
-            && obligation.is_global()
-            && obligation.param_env.caller_bounds().iter().all(|bound| bound.needs_subst())
+            && obligation.is_global(self.tcx())
+            && obligation
+                .param_env
+                .caller_bounds()
+                .iter()
+                .all(|bound| bound.definitely_needs_subst(self.tcx()))
         {
             // If a param env has no global bounds, global obligations do not
             // depend on its particular value in order to work, so we can clear
@@ -1452,7 +1455,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // the param_env so that it can be given the lowest priority. See
         // #50825 for the motivation for this.
         let is_global =
-            |cand: &ty::PolyTraitRef<'_>| cand.is_global() && !cand.has_late_bound_regions();
+            |cand: &ty::PolyTraitRef<'_>| cand.is_known_global() && !cand.has_late_bound_regions();
 
         // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`,
         // and `DiscriminantKindCandidate` to anything else.
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
index a6323a65aadbc..ac8bab0cf36a7 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_match.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs
@@ -130,6 +130,9 @@ impl Search<'a, 'tcx> {
 
 impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
     type BreakTy = NonStructuralMatchTy<'tcx>;
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.tcx())
+    }
 
     fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         debug!("Search visiting ty: {:?}", ty);
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index afef784b4c6cc..75307f135636b 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -132,8 +132,9 @@ pub fn predicate_obligations<'a, 'tcx>(
             wf.compute(a.into());
             wf.compute(b.into());
         }
-        ty::PredicateKind::ConstEvaluatable(def, substs) => {
-            let obligations = wf.nominal_obligations(def.did, substs);
+        ty::PredicateKind::ConstEvaluatable(uv) => {
+            let substs = uv.substs(wf.tcx());
+            let obligations = wf.nominal_obligations(uv.def.did, substs);
             wf.out.extend(obligations);
 
             for arg in substs.iter() {
@@ -422,7 +423,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
 
     /// Pushes all the predicates needed to validate that `ty` is WF into `out`.
     fn compute(&mut self, arg: GenericArg<'tcx>) {
-        let mut walker = arg.walk();
+        let mut walker = arg.walk(self.tcx());
         let param_env = self.param_env;
         let depth = self.recursion_depth;
         while let Some(arg) = walker.next() {
@@ -435,14 +436,17 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
 
                 GenericArgKind::Const(constant) => {
                     match constant.val {
-                        ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) => {
-                            assert!(promoted.is_none());
+                        ty::ConstKind::Unevaluated(uv) => {
+                            assert!(uv.promoted.is_none());
+                            let substs = uv.substs(self.tcx());
 
-                            let obligations = self.nominal_obligations(def.did, substs);
+                            let obligations = self.nominal_obligations(uv.def.did, substs);
                             self.out.extend(obligations);
 
-                            let predicate = ty::PredicateKind::ConstEvaluatable(def, substs)
-                                .to_predicate(self.tcx());
+                            let predicate = ty::PredicateKind::ConstEvaluatable(
+                                ty::Unevaluated::new(uv.def, substs),
+                            )
+                            .to_predicate(self.tcx());
                             let cause = self.cause(traits::MiscObligation);
                             self.out.push(traits::Obligation::with_depth(
                                 cause,
diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs
index 330fd497fa1f8..1d4196e574782 100644
--- a/compiler/rustc_traits/src/chalk/lowering.rs
+++ b/compiler/rustc_traits/src/chalk/lowering.rs
@@ -806,7 +806,7 @@ crate fn collect_bound_vars<'tcx, T: TypeFoldable<'tcx>>(
     tcx: TyCtxt<'tcx>,
     ty: Binder<'tcx, T>,
 ) -> (T, chalk_ir::VariableKinds<RustInterner<'tcx>>, BTreeMap<DefId, u32>) {
-    let mut bound_vars_collector = BoundVarsCollector::new();
+    let mut bound_vars_collector = BoundVarsCollector::new(tcx);
     ty.as_ref().skip_binder().visit_with(&mut bound_vars_collector);
     let mut parameters = bound_vars_collector.parameters;
     let named_parameters: BTreeMap<DefId, u32> = bound_vars_collector
@@ -836,14 +836,16 @@ crate fn collect_bound_vars<'tcx, T: TypeFoldable<'tcx>>(
 }
 
 crate struct BoundVarsCollector<'tcx> {
+    tcx: TyCtxt<'tcx>,
     binder_index: ty::DebruijnIndex,
     crate parameters: BTreeMap<u32, chalk_ir::VariableKind<RustInterner<'tcx>>>,
     crate named_parameters: Vec<DefId>,
 }
 
 impl<'tcx> BoundVarsCollector<'tcx> {
-    crate fn new() -> Self {
+    crate fn new(tcx: TyCtxt<'tcx>) -> Self {
         BoundVarsCollector {
+            tcx,
             binder_index: ty::INNERMOST,
             parameters: BTreeMap::new(),
             named_parameters: vec![],
@@ -852,6 +854,10 @@ impl<'tcx> BoundVarsCollector<'tcx> {
 }
 
 impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> {
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.tcx)
+    }
+
     fn visit_binder<T: TypeFoldable<'tcx>>(
         &mut self,
         t: &Binder<'tcx, T>,
@@ -1070,6 +1076,11 @@ impl PlaceholdersCollector {
 }
 
 impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector {
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        // Anon const substs do not contain placeholders by default.
+        None
+    }
+
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         match t.kind() {
             ty::Placeholder(p) if p.universe == self.universe_index => {
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 4c03abb38ca55..b00d2ab35617f 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -54,6 +54,10 @@ impl<'tcx> BoundVarsCollector<'tcx> {
 impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> {
     type BreakTy = ();
 
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        // Anon const substs do not contain bound vars by default.
+        None
+    }
     fn visit_binder<T: TypeFoldable<'tcx>>(
         &mut self,
         t: &Binder<'tcx, T>,
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 8332e7384110e..1b0fff93a99fb 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -361,7 +361,7 @@ fn well_formed_types_in_env<'tcx>(
         // constituents are well-formed.
         NodeKind::InherentImpl => {
             let self_ty = tcx.type_of(def_id);
-            inputs.extend(self_ty.walk());
+            inputs.extend(self_ty.walk(tcx));
         }
 
         // In an fn, we assume that the arguments and all their constituents are
@@ -370,7 +370,7 @@ fn well_formed_types_in_env<'tcx>(
             let fn_sig = tcx.fn_sig(def_id);
             let fn_sig = tcx.liberate_late_bound_regions(def_id, fn_sig);
 
-            inputs.extend(fn_sig.inputs().iter().flat_map(|ty| ty.walk()));
+            inputs.extend(fn_sig.inputs().iter().flat_map(|ty| ty.walk(tcx)));
         }
 
         NodeKind::Other => (),
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index 2d102127dd9d6..c405bbe2d1f53 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -19,94 +19,116 @@ bitflags! {
         // Does this have parameters? Used to determine whether substitution is
         // required.
         /// Does this have `Param`?
-        const HAS_TY_PARAM                = 1 << 0;
+        const HAS_KNOWN_TY_PARAM                = 1 << 0;
         /// Does this have `ReEarlyBound`?
-        const HAS_RE_PARAM                = 1 << 1;
+        const HAS_KNOWN_RE_PARAM                = 1 << 1;
         /// Does this have `ConstKind::Param`?
-        const HAS_CT_PARAM                = 1 << 2;
+        const HAS_KNOWN_CT_PARAM                = 1 << 2;
 
-        const NEEDS_SUBST                 = TypeFlags::HAS_TY_PARAM.bits
-                                          | TypeFlags::HAS_RE_PARAM.bits
-                                          | TypeFlags::HAS_CT_PARAM.bits;
+        const KNOWN_NEEDS_SUBST                 = TypeFlags::HAS_KNOWN_TY_PARAM.bits
+                                                | TypeFlags::HAS_KNOWN_RE_PARAM.bits
+                                                | TypeFlags::HAS_KNOWN_CT_PARAM.bits;
 
         /// Does this have `Infer`?
-        const HAS_TY_INFER                = 1 << 3;
+        const HAS_TY_INFER                      = 1 << 3;
         /// Does this have `ReVar`?
-        const HAS_RE_INFER                = 1 << 4;
+        const HAS_RE_INFER                      = 1 << 4;
         /// Does this have `ConstKind::Infer`?
-        const HAS_CT_INFER                = 1 << 5;
+        const HAS_CT_INFER                      = 1 << 5;
 
         /// Does this have inference variables? Used to determine whether
         /// inference is required.
-        const NEEDS_INFER                 = TypeFlags::HAS_TY_INFER.bits
-                                          | TypeFlags::HAS_RE_INFER.bits
-                                          | TypeFlags::HAS_CT_INFER.bits;
+        const NEEDS_INFER                       = TypeFlags::HAS_TY_INFER.bits
+                                                | TypeFlags::HAS_RE_INFER.bits
+                                                | TypeFlags::HAS_CT_INFER.bits;
 
         /// Does this have `Placeholder`?
-        const HAS_TY_PLACEHOLDER          = 1 << 6;
+        const HAS_TY_PLACEHOLDER                = 1 << 6;
         /// Does this have `RePlaceholder`?
-        const HAS_RE_PLACEHOLDER          = 1 << 7;
+        const HAS_RE_PLACEHOLDER                = 1 << 7;
         /// Does this have `ConstKind::Placeholder`?
-        const HAS_CT_PLACEHOLDER          = 1 << 8;
+        const HAS_CT_PLACEHOLDER                = 1 << 8;
 
         /// `true` if there are "names" of regions and so forth
         /// that are local to a particular fn/inferctxt
-        const HAS_FREE_LOCAL_REGIONS      = 1 << 9;
+        const HAS_KNOWN_FREE_LOCAL_REGIONS      = 1 << 9;
 
         /// `true` if there are "names" of types and regions and so forth
         /// that are local to a particular fn
-        const HAS_FREE_LOCAL_NAMES        = TypeFlags::HAS_TY_PARAM.bits
-                                          | TypeFlags::HAS_CT_PARAM.bits
-                                          | TypeFlags::HAS_TY_INFER.bits
-                                          | TypeFlags::HAS_CT_INFER.bits
-                                          | TypeFlags::HAS_TY_PLACEHOLDER.bits
-                                          | TypeFlags::HAS_CT_PLACEHOLDER.bits
-                                          // We consider 'freshened' types and constants
-                                          // to depend on a particular fn.
-                                          // The freshening process throws away information,
-                                          // which can make things unsuitable for use in a global
-                                          // cache. Note that there is no 'fresh lifetime' flag -
-                                          // freshening replaces all lifetimes with `ReErased`,
-                                          // which is different from how types/const are freshened.
-                                          | TypeFlags::HAS_TY_FRESH.bits
-                                          | TypeFlags::HAS_CT_FRESH.bits
-                                          | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits;
+        const HAS_KNOWN_FREE_LOCAL_NAMES        = TypeFlags::HAS_KNOWN_TY_PARAM.bits
+                                                | TypeFlags::HAS_KNOWN_CT_PARAM.bits
+                                                | TypeFlags::HAS_TY_INFER.bits
+                                                | TypeFlags::HAS_CT_INFER.bits
+                                                | TypeFlags::HAS_TY_PLACEHOLDER.bits
+                                                | TypeFlags::HAS_CT_PLACEHOLDER.bits
+                                                // We consider 'freshened' types and constants
+                                                // to depend on a particular fn.
+                                                // The freshening process throws away information,
+                                                // which can make things unsuitable for use in a global
+                                                // cache. Note that there is no 'fresh lifetime' flag -
+                                                // freshening replaces all lifetimes with `ReErased`,
+                                                // which is different from how types/const are freshened.
+                                                | TypeFlags::HAS_TY_FRESH.bits
+                                                | TypeFlags::HAS_CT_FRESH.bits
+                                                | TypeFlags::HAS_KNOWN_FREE_LOCAL_REGIONS.bits;
+
+        const HAS_POTENTIAL_FREE_LOCAL_NAMES    = TypeFlags::HAS_KNOWN_FREE_LOCAL_NAMES.bits
+                                                | TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS.bits;
 
         /// Does this have `Projection`?
-        const HAS_TY_PROJECTION           = 1 << 10;
+        const HAS_TY_PROJECTION                 = 1 << 10;
         /// Does this have `Opaque`?
-        const HAS_TY_OPAQUE               = 1 << 11;
+        const HAS_TY_OPAQUE                     = 1 << 11;
         /// Does this have `ConstKind::Unevaluated`?
-        const HAS_CT_PROJECTION           = 1 << 12;
+        const HAS_CT_PROJECTION                 = 1 << 12;
 
         /// Could this type be normalized further?
-        const HAS_PROJECTION              = TypeFlags::HAS_TY_PROJECTION.bits
-                                          | TypeFlags::HAS_TY_OPAQUE.bits
-                                          | TypeFlags::HAS_CT_PROJECTION.bits;
+        const HAS_PROJECTION                    = TypeFlags::HAS_TY_PROJECTION.bits
+                                                | TypeFlags::HAS_TY_OPAQUE.bits
+                                                | TypeFlags::HAS_CT_PROJECTION.bits;
 
         /// Is an error type/const reachable?
-        const HAS_ERROR                   = 1 << 13;
+        const HAS_ERROR                         = 1 << 13;
 
         /// Does this have any region that "appears free" in the type?
         /// Basically anything but `ReLateBound` and `ReErased`.
-        const HAS_FREE_REGIONS            = 1 << 14;
+        const HAS_KNOWN_FREE_REGIONS            = 1 << 14;
+
+        const HAS_POTENTIAL_FREE_REGIONS        = TypeFlags::HAS_KNOWN_FREE_REGIONS.bits
+                                                | TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS.bits;
 
         /// Does this have any `ReLateBound` regions? Used to check
         /// if a global bound is safe to evaluate.
-        const HAS_RE_LATE_BOUND           = 1 << 15;
+        const HAS_RE_LATE_BOUND                 = 1 << 15;
 
         /// Does this have any `ReErased` regions?
-        const HAS_RE_ERASED               = 1 << 16;
+        const HAS_RE_ERASED                     = 1 << 16;
 
         /// Does this value have parameters/placeholders/inference variables which could be
         /// replaced later, in a way that would change the results of `impl` specialization?
-        const STILL_FURTHER_SPECIALIZABLE = 1 << 17;
+        ///
+        /// Note that this flag being set is not a guarantee, as it is also
+        /// set if there are any anon consts with unknown default substs.
+        const STILL_FURTHER_SPECIALIZABLE       = 1 << 17;
 
         /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`?
-        const HAS_TY_FRESH                = 1 << 18;
+        const HAS_TY_FRESH                      = 1 << 18;
 
         /// Does this value have `InferConst::Fresh`?
-        const HAS_CT_FRESH                = 1 << 19;
+        const HAS_CT_FRESH                      = 1 << 19;
+
+        /// Does this value have unknown default anon const substs.
+        ///
+        /// For more details refer to...
+        /// FIXME(@lcnr): ask me for now, still have to write all of this.
+        const HAS_UNKNOWN_DEFAULT_CONST_SUBSTS  = 1 << 20;
+        /// Flags which can be influenced by default anon const substs.
+        const MAY_NEED_DEFAULT_CONST_SUBSTS     = TypeFlags::HAS_KNOWN_RE_PARAM.bits
+                                                | TypeFlags::HAS_KNOWN_TY_PARAM.bits
+                                                | TypeFlags::HAS_KNOWN_CT_PARAM.bits
+                                                | TypeFlags::HAS_KNOWN_FREE_LOCAL_REGIONS.bits
+                                                | TypeFlags::HAS_KNOWN_FREE_REGIONS.bits;
+
     }
 }
 
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index 606fd2812ecc7..d916ff7db3d51 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -394,7 +394,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     if self.is_object && has_default {
                         let default_ty = tcx.at(self.span).type_of(param.def_id);
                         let self_param = tcx.types.self_param;
-                        if default_ty.walk().any(|arg| arg == self_param.into()) {
+                        if default_ty.walk(tcx).any(|arg| arg == self_param.into()) {
                             // There is no suitable inference default for a type parameter
                             // that references self, in an object type.
                             return true;
@@ -1354,7 +1354,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                         // A `Self` within the original bound will be substituted with a
                         // `trait_object_dummy_self`, so check for that.
                         let references_self =
-                            pred.skip_binder().ty.walk().any(|arg| arg == dummy_self.into());
+                            pred.skip_binder().ty.walk(tcx).any(|arg| arg == dummy_self.into());
 
                         // If the projection output contains `Self`, force the user to
                         // elaborate it explicitly to avoid a lot of complexity.
@@ -2204,7 +2204,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 self.prohibit_generics(path.segments);
                 // Try to evaluate any array length constants.
                 let normalized_ty = self.normalize_ty(span, tcx.at(span).type_of(def_id));
-                if forbid_generic && normalized_ty.needs_subst() {
+                if forbid_generic && normalized_ty.definitely_needs_subst(tcx) {
                     let mut err = tcx.sess.struct_span_err(
                         path.span,
                         "generic `Self` types are currently not permitted in anonymous constants",
diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs
index a7fd20888428f..3c9d5b4def4e7 100644
--- a/compiler/rustc_typeck/src/check/check.rs
+++ b/compiler/rustc_typeck/src/check/check.rs
@@ -470,14 +470,17 @@ pub(super) fn check_opaque_for_inheriting_lifetimes(
     debug!(?item, ?span);
 
     struct FoundParentLifetime;
-    struct FindParentLifetimeVisitor<'tcx>(&'tcx ty::Generics);
+    struct FindParentLifetimeVisitor<'tcx>(TyCtxt<'tcx>, &'tcx ty::Generics);
     impl<'tcx> ty::fold::TypeVisitor<'tcx> for FindParentLifetimeVisitor<'tcx> {
         type BreakTy = FoundParentLifetime;
+        fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+            Some(self.0)
+        }
 
         fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
             debug!("FindParentLifetimeVisitor: r={:?}", r);
             if let RegionKind::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = r {
-                if *index < self.0.parent_count as u32 {
+                if *index < self.1.parent_count as u32 {
                     return ControlFlow::Break(FoundParentLifetime);
                 } else {
                     return ControlFlow::CONTINUE;
@@ -499,21 +502,24 @@ pub(super) fn check_opaque_for_inheriting_lifetimes(
     }
 
     struct ProhibitOpaqueVisitor<'tcx> {
+        tcx: TyCtxt<'tcx>,
         opaque_identity_ty: Ty<'tcx>,
         generics: &'tcx ty::Generics,
-        tcx: TyCtxt<'tcx>,
         selftys: Vec<(Span, Option<String>)>,
     }
 
     impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
         type BreakTy = Ty<'tcx>;
+        fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+            Some(self.tcx)
+        }
 
         fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
             debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t);
             if t == self.opaque_identity_ty {
                 ControlFlow::CONTINUE
             } else {
-                t.super_visit_with(&mut FindParentLifetimeVisitor(self.generics))
+                t.super_visit_with(&mut FindParentLifetimeVisitor(self.tcx, self.generics))
                     .map_break(|FoundParentLifetime| t)
             }
         }
@@ -1476,7 +1482,7 @@ pub(super) fn check_type_params_are_used<'tcx>(
         return;
     }
 
-    for leaf in ty.walk() {
+    for leaf in ty.walk(tcx) {
         if let GenericArgKind::Type(leaf_ty) = leaf.unpack() {
             if let ty::Param(param) = leaf_ty.kind() {
                 debug!("found use of ty param {:?}", param);
@@ -1578,8 +1584,12 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) {
                 .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t)))
                 .filter(|(_, ty)| !matches!(ty.kind(), ty::Never))
             {
-                struct VisitTypes(Vec<DefId>);
-                impl<'tcx> ty::fold::TypeVisitor<'tcx> for VisitTypes {
+                struct OpaqueTypeCollector(Vec<DefId>);
+                impl<'tcx> ty::fold::TypeVisitor<'tcx> for OpaqueTypeCollector {
+                    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+                        // Default anon const substs cannot contain opaque types.
+                        None
+                    }
                     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
                         match *t.kind() {
                             ty::Opaque(def, _) => {
@@ -1590,7 +1600,7 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) {
                         }
                     }
                 }
-                let mut visitor = VisitTypes(vec![]);
+                let mut visitor = OpaqueTypeCollector(vec![]);
                 ty.visit_with(&mut visitor);
                 for def_id in visitor.0 {
                     let ty_span = tcx.def_span(def_id);
diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs
index c34dd705bef7b..fd150978f0074 100644
--- a/compiler/rustc_typeck/src/check/dropck.rs
+++ b/compiler/rustc_typeck/src/check/dropck.rs
@@ -236,9 +236,9 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
                     relator.relate(predicate.rebind(a), p.rebind(b)).is_ok()
                 }
                 (
-                    ty::PredicateKind::ConstEvaluatable(def_a, substs_a),
-                    ty::PredicateKind::ConstEvaluatable(def_b, substs_b),
-                ) => tcx.try_unify_abstract_consts(((def_a, substs_a), (def_b, substs_b))),
+                    ty::PredicateKind::ConstEvaluatable(a),
+                    ty::PredicateKind::ConstEvaluatable(b),
+                ) => tcx.try_unify_abstract_consts((a, b)),
                 (ty::PredicateKind::TypeOutlives(a), ty::PredicateKind::TypeOutlives(b)) => {
                     relator.relate(predicate.rebind(a.0), p.rebind(b.0)).is_ok()
                 }
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
index 15fa696b6c446..25d1c8706e874 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -239,7 +239,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.tag(),
         );
 
-        if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) {
+        if self.can_contain_user_lifetime_bounds((substs, user_self_ty)) {
             let canonicalized = self.infcx.canonicalize_user_type_annotation(UserType::TypeOf(
                 def_id,
                 UserSubsts { substs, user_self_ty },
@@ -481,7 +481,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let ty = self.to_ty(ast_ty);
         debug!("to_ty_saving_user_provided_ty: ty={:?}", ty);
 
-        if Self::can_contain_user_lifetime_bounds(ty) {
+        if self.can_contain_user_lifetime_bounds(ty) {
             let c_ty = self.infcx.canonicalize_response(UserType::Ty(ty));
             debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty);
             self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty);
@@ -526,11 +526,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     // reader, although I have my doubts). Also pass in types with inference
     // types, because they may be repeated. Other sorts of things are already
     // sufficiently enforced with erased regions. =)
-    fn can_contain_user_lifetime_bounds<T>(t: T) -> bool
+    fn can_contain_user_lifetime_bounds<T>(&self, t: T) -> bool
     where
         T: TypeFoldable<'tcx>,
     {
-        t.has_free_regions() || t.has_projections() || t.has_infer_types()
+        t.has_free_regions(self.tcx) || t.has_projections() || t.has_infer_types()
     }
 
     pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> {
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index b624e07374ec7..9efb52a08b7e1 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -937,7 +937,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         let ty = self.resolve_vars_if_possible(ty);
                         // We walk the argument type because the argument's type could have
                         // been `Option<T>`, but the `FulfillmentError` references `T`.
-                        if ty.walk().any(|arg| arg == predicate.self_ty().into()) {
+                        if ty.walk(self.tcx).any(|arg| arg == predicate.self_ty().into()) {
                             Some(i)
                         } else {
                             None
diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs
index 963436d05d8ef..9b495fba1975d 100644
--- a/compiler/rustc_typeck/src/check/op.rs
+++ b/compiler/rustc_typeck/src/check/op.rs
@@ -428,7 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                 }
                 if let Some(missing_trait) = missing_trait {
-                    let mut visitor = TypeParamVisitor(vec![]);
+                    let mut visitor = TypeParamVisitor(self.tcx, vec![]);
                     visitor.visit_ty(lhs_ty);
 
                     if op.node == hir::BinOpKind::Add
@@ -439,7 +439,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // This has nothing here because it means we did string
                         // concatenation (e.g., "Hello " + "World!"). This means
                         // we don't want the note in the else clause to be emitted
-                    } else if let [ty] = &visitor.0[..] {
+                    } else if let [ty] = &visitor.1[..] {
                         if let ty::Param(p) = *ty.kind() {
                             // Check if the method would be found if the type param wasn't
                             // involved. If so, it means that adding a trait bound to the param is
@@ -1003,12 +1003,15 @@ fn suggest_constraining_param(
     }
 }
 
-struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>);
+struct TypeParamVisitor<'tcx>(TyCtxt<'tcx>, Vec<Ty<'tcx>>);
 
 impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> {
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.0)
+    }
     fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         if let ty::Param(_) = ty.kind() {
-            self.0.push(ty);
+            self.1.push(ty);
         }
         ty.super_visit_with(self)
     }
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs
index 632b9492ee43a..2e01e99d36ec6 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -541,10 +541,10 @@ fn check_type_defn<'tcx, F>(
                 fcx.register_predicate(traits::Obligation::new(
                     cause,
                     fcx.param_env,
-                    ty::PredicateKind::ConstEvaluatable(
+                    ty::PredicateKind::ConstEvaluatable(ty::Unevaluated::new(
                         ty::WithOptConstParam::unknown(discr_def_id.to_def_id()),
                         discr_substs,
-                    )
+                    ))
                     .to_predicate(tcx),
                 ));
             }
@@ -746,7 +746,7 @@ fn check_where_clauses<'tcx, 'fcx>(
                     // Ignore dependent defaults -- that is, where the default of one type
                     // parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
                     // be sure if it will error or not as user might always specify the other.
-                    if !ty.needs_subst() {
+                    if !ty.definitely_needs_subst(tcx) {
                         fcx.register_wf_obligation(
                             ty.into(),
                             tcx.def_span(param.def_id),
@@ -762,7 +762,7 @@ fn check_where_clauses<'tcx, 'fcx>(
                     // for `struct Foo<const N: usize, const M: usize = { 1 - 2 }>`
                     // we should eagerly error.
                     let default_ct = tcx.const_param_default(param.def_id);
-                    if !default_ct.needs_subst() {
+                    if !default_ct.definitely_needs_subst(tcx) {
                         fcx.register_wf_obligation(
                             default_ct.into(),
                             tcx.def_span(param.def_id),
@@ -796,7 +796,7 @@ fn check_where_clauses<'tcx, 'fcx>(
                 if is_our_default(param) {
                     let default_ty = tcx.type_of(param.def_id);
                     // ... and it's not a dependent default, ...
-                    if !default_ty.needs_subst() {
+                    if !default_ty.definitely_needs_subst(tcx) {
                         // ... then substitute it with the default.
                         return default_ty.into();
                     }
@@ -809,7 +809,7 @@ fn check_where_clauses<'tcx, 'fcx>(
                 if is_our_default(param) {
                     let default_ct = tcx.const_param_default(param.def_id);
                     // ... and it's not a dependent default, ...
-                    if !default_ct.needs_subst() {
+                    if !default_ct.definitely_needs_subst(tcx) {
                         // ... then substitute it with the default.
                         return default_ct.into();
                     }
@@ -825,12 +825,15 @@ fn check_where_clauses<'tcx, 'fcx>(
         .predicates
         .iter()
         .flat_map(|&(pred, sp)| {
-            #[derive(Default)]
-            struct CountParams {
+            struct CountParams<'tcx> {
+                tcx: TyCtxt<'tcx>,
                 params: FxHashSet<u32>,
             }
-            impl<'tcx> ty::fold::TypeVisitor<'tcx> for CountParams {
+            impl<'tcx> ty::fold::TypeVisitor<'tcx> for CountParams<'tcx> {
                 type BreakTy = ();
+                fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+                    Some(self.tcx)
+                }
 
                 fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
                     if let ty::Param(param) = t.kind() {
@@ -850,12 +853,12 @@ fn check_where_clauses<'tcx, 'fcx>(
                     c.super_visit_with(self)
                 }
             }
-            let mut param_count = CountParams::default();
+            let mut param_count = CountParams { tcx: fcx.tcx, params: FxHashSet::default() };
             let has_region = pred.visit_with(&mut param_count).is_break();
             let substituted_pred = pred.subst(tcx, substs);
             // Don't check non-defaulted params, dependent defaults (including lifetimes)
             // or preds with multiple params.
-            if substituted_pred.has_param_types_or_consts()
+            if substituted_pred.definitely_has_param_types_or_consts(tcx)
                 || param_count.params.len() > 1
                 || has_region
             {
@@ -1377,7 +1380,7 @@ fn check_false_global_bounds(fcx: &FnCtxt<'_, '_>, span: Span, id: hir::HirId) {
     for obligation in implied_obligations {
         let pred = obligation.predicate;
         // Match the existing behavior.
-        if pred.is_global() && !pred.has_late_bound_regions() {
+        if pred.is_global(fcx.tcx) && !pred.has_late_bound_regions() {
             let pred = fcx.normalize_associated_types_in(span, pred);
             let obligation = traits::Obligation::new(
                 traits::ObligationCause::new(span, id, traits::TrivialBound),
diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs
index 2e59defdb7ba0..c57ec9ef78f68 100644
--- a/compiler/rustc_typeck/src/check/writeback.rs
+++ b/compiler/rustc_typeck/src/check/writeback.rs
@@ -130,7 +130,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
 
     fn write_ty_to_typeck_results(&mut self, hir_id: hir::HirId, ty: Ty<'tcx>) {
         debug!("write_ty_to_typeck_results({:?}, {:?})", hir_id, ty);
-        assert!(!ty.needs_infer() && !ty.has_placeholders() && !ty.has_free_regions());
+        assert!(!ty.needs_infer() && !ty.has_placeholders() && !ty.has_free_regions(self.tcx()));
         self.typeck_results.node_types_mut().insert(hir_id, ty);
     }
 
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 1c6fd5983cb59..75f9405e082ca 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -68,6 +68,7 @@ fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
 pub fn provide(providers: &mut Providers) {
     *providers = Providers {
         opt_const_param_of: type_of::opt_const_param_of,
+        default_anon_const_substs: type_of::default_anon_const_substs,
         type_of: type_of::type_of,
         item_bounds: item_bounds::item_bounds,
         explicit_item_bounds: item_bounds::explicit_item_bounds,
@@ -2320,7 +2321,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
             tcx,
             &mut predicates,
             trait_ref,
-            &mut cgp::parameters_for_impl(self_ty, trait_ref),
+            &mut cgp::parameters_for_impl(tcx, self_ty, trait_ref),
         );
     }
 
@@ -2355,7 +2356,7 @@ fn const_evaluatable_predicates_of<'tcx>(
                 assert_eq!(uv.promoted, None);
                 let span = self.tcx.hir().span(c.hir_id);
                 self.preds.insert((
-                    ty::PredicateKind::ConstEvaluatable(uv.def, uv.substs).to_predicate(self.tcx),
+                    ty::PredicateKind::ConstEvaluatable(uv.shrink()).to_predicate(self.tcx),
                     span,
                 ));
             }
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs
index 4048a54b58c98..9bce5ee0da263 100644
--- a/compiler/rustc_typeck/src/collect/type_of.rs
+++ b/compiler/rustc_typeck/src/collect/type_of.rs
@@ -7,7 +7,7 @@ use rustc_hir::intravisit;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::{HirId, Node};
 use rustc_middle::hir::map::Map;
-use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
+use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, SubstsRef};
 use rustc_middle::ty::util::IntTypeExt;
 use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable, TypeFolder};
 use rustc_span::symbol::Ident;
@@ -274,6 +274,31 @@ fn get_path_containing_arg_in_pat<'hir>(
     arg_path
 }
 
+pub(super) fn default_anon_const_substs(tcx: TyCtxt<'_>, def_id: DefId) -> SubstsRef<'_> {
+    let generics = tcx.generics_of(def_id);
+    if let Some(parent) = generics.parent {
+        // This is the reason we bother with having optional anon const substs.
+        //
+        // In the future the substs of an anon const will depend on its parents predicates
+        // at which point eagerly looking at them will cause a query cycle.
+        //
+        // So for now this is only an assurance that this approach won't cause cycle errors in
+        // the future.
+        let _cycle_check = tcx.predicates_of(parent);
+    }
+
+    let substs = InternalSubsts::identity_for_item(tcx, def_id);
+    // We only expect substs with the following type flags as default substs.
+    //
+    // Getting this wrong can lead to ICE and unsoundness, so we assert it here.
+    for arg in substs.iter() {
+        let allowed_flags = ty::TypeFlags::MAY_NEED_DEFAULT_CONST_SUBSTS
+            | ty::TypeFlags::STILL_FURTHER_SPECIALIZABLE;
+        assert!(!arg.has_type_flags(!allowed_flags));
+    }
+    substs
+}
+
 pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
     let def_id = def_id.expect_local();
     use rustc_hir::*;
diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_typeck/src/constrained_generic_params.rs
index 529de1a287484..9b6f0be47caf5 100644
--- a/compiler/rustc_typeck/src/constrained_generic_params.rs
+++ b/compiler/rustc_typeck/src/constrained_generic_params.rs
@@ -27,12 +27,13 @@ impl From<ty::ParamConst> for Parameter {
 
 /// Returns the set of parameters constrained by the impl header.
 pub fn parameters_for_impl<'tcx>(
+    tcx: TyCtxt<'tcx>,
     impl_self_ty: Ty<'tcx>,
     impl_trait_ref: Option<ty::TraitRef<'tcx>>,
 ) -> FxHashSet<Parameter> {
     let vec = match impl_trait_ref {
-        Some(tr) => parameters_for(&tr, false),
-        None => parameters_for(&impl_self_ty, false),
+        Some(tr) => parameters_for(tcx, &tr, false),
+        None => parameters_for(tcx, &impl_self_ty, false),
     };
     vec.into_iter().collect()
 }
@@ -43,20 +44,26 @@ pub fn parameters_for_impl<'tcx>(
 /// of parameters whose values are needed in order to constrain `ty` - these
 /// differ, with the latter being a superset, in the presence of projections.
 pub fn parameters_for<'tcx>(
+    tcx: TyCtxt<'tcx>,
     t: &impl TypeFoldable<'tcx>,
     include_nonconstraining: bool,
 ) -> Vec<Parameter> {
-    let mut collector = ParameterCollector { parameters: vec![], include_nonconstraining };
+    let mut collector = ParameterCollector { tcx, parameters: vec![], include_nonconstraining };
     t.visit_with(&mut collector);
     collector.parameters
 }
 
-struct ParameterCollector {
+struct ParameterCollector<'tcx> {
+    tcx: TyCtxt<'tcx>,
     parameters: Vec<Parameter>,
     include_nonconstraining: bool,
 }
 
-impl<'tcx> TypeVisitor<'tcx> for ParameterCollector {
+impl<'tcx> TypeVisitor<'tcx> for ParameterCollector<'tcx> {
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.tcx)
+    }
+
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         match *t.kind() {
             ty::Projection(..) | ty::Opaque(..) if !self.include_nonconstraining => {
@@ -198,12 +205,12 @@ pub fn setup_constraining_predicates<'tcx>(
                 //     `<<T as Bar>::Baz as Iterator>::Output = <U as Iterator>::Output`
                 // Then the projection only applies if `T` is known, but it still
                 // does not determine `U`.
-                let inputs = parameters_for(&projection.projection_ty, true);
+                let inputs = parameters_for(tcx, &projection.projection_ty, true);
                 let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(&p));
                 if !relies_only_on_inputs {
                     continue;
                 }
-                input_parameters.extend(parameters_for(&projection.ty, false));
+                input_parameters.extend(parameters_for(tcx, &projection.ty, false));
             } else {
                 continue;
             }
diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs
index 1240946860573..194c4efdbb058 100644
--- a/compiler/rustc_typeck/src/impl_wf_check.rs
+++ b/compiler/rustc_typeck/src/impl_wf_check.rs
@@ -119,7 +119,7 @@ fn enforce_impl_params_are_constrained(
     let impl_predicates = tcx.predicates_of(impl_def_id);
     let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
 
-    let mut input_parameters = cgp::parameters_for_impl(impl_self_ty, impl_trait_ref);
+    let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref);
     cgp::identify_constrained_generic_params(
         tcx,
         impl_predicates,
@@ -136,7 +136,7 @@ fn enforce_impl_params_are_constrained(
             match item.kind {
                 ty::AssocKind::Type => {
                     if item.defaultness.has_value() {
-                        cgp::parameters_for(&tcx.type_of(def_id), true)
+                        cgp::parameters_for(tcx, &tcx.type_of(def_id), true)
                     } else {
                         Vec::new()
                     }
diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
index e148370a03610..d3e4be128a8df 100644
--- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
@@ -207,15 +207,15 @@ fn unconstrained_parent_impl_substs<'tcx>(
                 continue;
             }
 
-            unconstrained_parameters.extend(cgp::parameters_for(&projection_ty, true));
+            unconstrained_parameters.extend(cgp::parameters_for(tcx, &projection_ty, true));
 
-            for param in cgp::parameters_for(&projected_ty, false) {
+            for param in cgp::parameters_for(tcx, &projected_ty, false) {
                 if !unconstrained_parameters.contains(&param) {
                     constrained_params.insert(param.0);
                 }
             }
 
-            unconstrained_parameters.extend(cgp::parameters_for(&projected_ty, true));
+            unconstrained_parameters.extend(cgp::parameters_for(tcx, &projected_ty, true));
         }
     }
 
@@ -249,7 +249,7 @@ fn check_duplicate_params<'tcx>(
     parent_substs: &Vec<GenericArg<'tcx>>,
     span: Span,
 ) {
-    let mut base_params = cgp::parameters_for(parent_substs, true);
+    let mut base_params = cgp::parameters_for(tcx, parent_substs, true);
     base_params.sort_by_key(|param| param.0);
     if let (_, [duplicate, ..]) = base_params.partition_dedup() {
         let param = impl1_substs[duplicate.0 as usize];
@@ -363,7 +363,7 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc
     match predicate.kind().skip_binder() {
         // Global predicates are either always true or always false, so we
         // are fine to specialize on.
-        _ if predicate.is_global() => (),
+        _ if predicate.is_global(tcx) => (),
         // We allow specializing on explicitly marked traits with no associated
         // items.
         ty::PredicateKind::Trait(ty::TraitPredicate {
diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_typeck/src/outlives/implicit_infer.rs
index ccf0fb7273837..9fe26711f2102 100644
--- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs
+++ b/compiler/rustc_typeck/src/outlives/implicit_infer.rs
@@ -114,7 +114,18 @@ fn insert_required_predicates_to_be_wf<'tcx>(
     required_predicates: &mut RequiredPredicates<'tcx>,
     explicit_map: &mut ExplicitPredicatesMap<'tcx>,
 ) {
-    for arg in field_ty.walk() {
+    // We must not look into the default substs of consts
+    // as computing those depends on the results of `predicates_of`.
+    //
+    // Luckily the only types contained in default substs are type
+    // parameters which don't matter here.
+    //
+    // FIXME(const_generics): Once more complex const parameter types
+    // are allowed, this might be incorrect. I think that we will still be
+    // fine, as all outlives relations of the const param types should also
+    // be part of the adt containing it, but we should still both update the
+    // documentation and add some tests for this.
+    for arg in field_ty.walk_ignoring_default_const_substs() {
         let ty = match arg.unpack() {
             GenericArgKind::Type(ty) => ty,
 
@@ -306,7 +317,7 @@ pub fn check_explicit_predicates<'tcx>(
         // 'b`.
         if let Some(self_ty) = ignored_self_ty {
             if let GenericArgKind::Type(ty) = outlives_predicate.0.unpack() {
-                if ty.walk().any(|arg| arg == self_ty.into()) {
+                if ty.walk(tcx).any(|arg| arg == self_ty.into()) {
                     debug!("skipping self ty = {:?}", &ty);
                     continue;
                 }
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index bdd5350aab2cb..bdfe3ffc13f13 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -287,7 +287,7 @@ crate fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
 
 crate fn print_const(cx: &DocContext<'_>, n: &'tcx ty::Const<'_>) -> String {
     match n.val {
-        ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted }) => {
+        ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs_: _, promoted }) => {
             let mut s = if let Some(def) = def.as_local() {
                 let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def.did);
                 print_const_expr(cx.tcx, cx.tcx.hir().body_owned_by(hir_id))
diff --git a/src/test/mir-opt/const_promotion_extern_static.BAR.PromoteTemps.diff b/src/test/mir-opt/const_promotion_extern_static.BAR.PromoteTemps.diff
index 6f4bd65b85d4d..49d0a7ff45503 100644
--- a/src/test/mir-opt/const_promotion_extern_static.BAR.PromoteTemps.diff
+++ b/src/test/mir-opt/const_promotion_extern_static.BAR.PromoteTemps.diff
@@ -30,7 +30,7 @@
 -         _3 = [move _4];                  // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
 -         _2 = &_3;                        // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
 +                                          // + span: $DIR/const-promotion-extern-static.rs:9:31: 9:35
-+                                          // + literal: Const { ty: &[&i32; 1], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:6 ~ const_promotion_extern_static[55e6]::BAR), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
++                                          // + literal: Const { ty: &[&i32; 1], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:6 ~ const_promotion_extern_static[55e6]::BAR), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
 +         _2 = &(*_6);                     // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
           _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
 -         StorageDead(_4);                 // scope 0 at $DIR/const-promotion-extern-static.rs:9:34: 9:35
diff --git a/src/test/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff b/src/test/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff
index 8c9a37bce854b..f2504ae880eab 100644
--- a/src/test/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff
+++ b/src/test/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff
@@ -32,7 +32,7 @@
 -         _3 = [move _4];                  // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
 -         _2 = &_3;                        // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
 +                                          // + span: $DIR/const-promotion-extern-static.rs:13:31: 13:46
-+                                          // + literal: Const { ty: &[&i32; 1], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:7 ~ const_promotion_extern_static[55e6]::FOO), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
++                                          // + literal: Const { ty: &[&i32; 1], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:7 ~ const_promotion_extern_static[55e6]::FOO), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
 +         _2 = &(*_6);                     // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
           _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
 -         StorageDead(_4);                 // scope 0 at $DIR/const-promotion-extern-static.rs:13:45: 13:46
diff --git a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.diff b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.diff
index db6e1369161fb..f8fd2cc9b0d08 100644
--- a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.diff
+++ b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.diff
@@ -31,7 +31,7 @@
                                            // + val: Unevaluated(main, [], Some(promoted[0]))
                                            // mir::Constant
                                            // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35
-                                           // + literal: Const { ty: &[i32; 3], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ bad_op_unsafe_oob_for_slices[8240]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                           // + literal: Const { ty: &[i32; 3], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ bad_op_unsafe_oob_for_slices[8240]::main), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
           _3 = _9;                         // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35
           _2 = &raw const (*_3);           // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35
           _1 = move _2 as *const [i32] (Pointer(Unsize)); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35
diff --git a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.diff b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.diff
index db6e1369161fb..f8fd2cc9b0d08 100644
--- a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.diff
+++ b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.diff
@@ -31,7 +31,7 @@
                                            // + val: Unevaluated(main, [], Some(promoted[0]))
                                            // mir::Constant
                                            // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35
-                                           // + literal: Const { ty: &[i32; 3], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ bad_op_unsafe_oob_for_slices[8240]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                           // + literal: Const { ty: &[i32; 3], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ bad_op_unsafe_oob_for_slices[8240]::main), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
           _3 = _9;                         // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35
           _2 = &raw const (*_3);           // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35
           _1 = move _2 as *const [i32] (Pointer(Unsize)); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35
diff --git a/src/test/mir-opt/const_prop/const_prop_fails_gracefully.main.ConstProp.diff b/src/test/mir-opt/const_prop/const_prop_fails_gracefully.main.ConstProp.diff
index d7bc035e667af..49237934223e7 100644
--- a/src/test/mir-opt/const_prop/const_prop_fails_gracefully.main.ConstProp.diff
+++ b/src/test/mir-opt/const_prop/const_prop_fails_gracefully.main.ConstProp.diff
@@ -22,7 +22,7 @@
                                            // + val: Unevaluated(FOO, [], None)
                                            // mir::Constant
                                            // + span: $DIR/const_prop_fails_gracefully.rs:7:13: 7:16
-                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:5 ~ const_prop_fails_gracefully[2706]::main::FOO), const_param_did: None }, substs: [], promoted: None }) }
+                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:5 ~ const_prop_fails_gracefully[2706]::main::FOO), const_param_did: None }, substs_: Some([]), promoted: None }) }
           _2 = &raw const (*_3);           // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16
           _1 = move _2 as usize (Misc);    // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:39
           StorageDead(_2);                 // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:38: 7:39
diff --git a/src/test/mir-opt/const_prop/ref_deref.main.ConstProp.diff b/src/test/mir-opt/const_prop/ref_deref.main.ConstProp.diff
index 4e19465189c93..54acae43abc58 100644
--- a/src/test/mir-opt/const_prop/ref_deref.main.ConstProp.diff
+++ b/src/test/mir-opt/const_prop/ref_deref.main.ConstProp.diff
@@ -17,7 +17,7 @@
                                            // + val: Unevaluated(main, [], Some(promoted[0]))
                                            // mir::Constant
                                            // + span: $DIR/ref_deref.rs:5:6: 5:10
-                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ ref_deref[cb9b]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ ref_deref[cb9b]::main), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
           _2 = _4;                         // scope 0 at $DIR/ref_deref.rs:5:6: 5:10
 -         _1 = (*_2);                      // scope 0 at $DIR/ref_deref.rs:5:5: 5:10
 +         _1 = const 4_i32;                // scope 0 at $DIR/ref_deref.rs:5:5: 5:10
diff --git a/src/test/mir-opt/const_prop/ref_deref.main.PromoteTemps.diff b/src/test/mir-opt/const_prop/ref_deref.main.PromoteTemps.diff
index a58dabdaa4159..3b5c86942a751 100644
--- a/src/test/mir-opt/const_prop/ref_deref.main.PromoteTemps.diff
+++ b/src/test/mir-opt/const_prop/ref_deref.main.PromoteTemps.diff
@@ -20,7 +20,7 @@
 +                                          // + val: Unevaluated(main, [], Some(promoted[0]))
 +                                          // mir::Constant
 +                                          // + span: $DIR/ref_deref.rs:5:6: 5:10
-+                                          // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ ref_deref[cb9b]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
++                                          // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ ref_deref[cb9b]::main), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
 +         _2 = &(*_4);                     // scope 0 at $DIR/ref_deref.rs:5:6: 5:10
           _1 = (*_2);                      // scope 0 at $DIR/ref_deref.rs:5:5: 5:10
 -         StorageDead(_3);                 // scope 0 at $DIR/ref_deref.rs:5:10: 5:11
diff --git a/src/test/mir-opt/const_prop/ref_deref_project.main.ConstProp.diff b/src/test/mir-opt/const_prop/ref_deref_project.main.ConstProp.diff
index bb265b2af7d30..8590a98491b7b 100644
--- a/src/test/mir-opt/const_prop/ref_deref_project.main.ConstProp.diff
+++ b/src/test/mir-opt/const_prop/ref_deref_project.main.ConstProp.diff
@@ -17,7 +17,7 @@
                                            // + val: Unevaluated(main, [], Some(promoted[0]))
                                            // mir::Constant
                                            // + span: $DIR/ref_deref_project.rs:5:6: 5:17
-                                           // + literal: Const { ty: &(i32, i32), val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ ref_deref_project[e8c3]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                           // + literal: Const { ty: &(i32, i32), val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ ref_deref_project[e8c3]::main), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
           _2 = &((*_4).1: i32);            // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17
           _1 = (*_2);                      // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17
           StorageDead(_2);                 // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18
diff --git a/src/test/mir-opt/const_prop/ref_deref_project.main.PromoteTemps.diff b/src/test/mir-opt/const_prop/ref_deref_project.main.PromoteTemps.diff
index bb7570fabbba8..4e10ae1793488 100644
--- a/src/test/mir-opt/const_prop/ref_deref_project.main.PromoteTemps.diff
+++ b/src/test/mir-opt/const_prop/ref_deref_project.main.PromoteTemps.diff
@@ -20,7 +20,7 @@
 +                                          // + val: Unevaluated(main, [], Some(promoted[0]))
 +                                          // mir::Constant
 +                                          // + span: $DIR/ref_deref_project.rs:5:6: 5:17
-+                                          // + literal: Const { ty: &(i32, i32), val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ ref_deref_project[e8c3]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
++                                          // + literal: Const { ty: &(i32, i32), val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ ref_deref_project[e8c3]::main), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
 +         _2 = &((*_4).1: i32);            // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17
           _1 = (*_2);                      // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17
 -         StorageDead(_3);                 // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18
diff --git a/src/test/mir-opt/const_prop/slice_len.main.ConstProp.32bit.diff b/src/test/mir-opt/const_prop/slice_len.main.ConstProp.32bit.diff
index d5a36182e3501..f305d602bbde1 100644
--- a/src/test/mir-opt/const_prop/slice_len.main.ConstProp.32bit.diff
+++ b/src/test/mir-opt/const_prop/slice_len.main.ConstProp.32bit.diff
@@ -24,7 +24,7 @@
                                            // + val: Unevaluated(main, [], Some(promoted[0]))
                                            // mir::Constant
                                            // + span: $DIR/slice_len.rs:5:6: 5:19
-                                           // + literal: Const { ty: &[u32; 3], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ slice_len[6547]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                           // + literal: Const { ty: &[u32; 3], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ slice_len[6547]::main), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
           _4 = _9;                         // scope 0 at $DIR/slice_len.rs:5:6: 5:19
           _3 = _4;                         // scope 0 at $DIR/slice_len.rs:5:6: 5:19
           _2 = move _3 as &[u32] (Pointer(Unsize)); // scope 0 at $DIR/slice_len.rs:5:6: 5:19
diff --git a/src/test/mir-opt/const_prop/slice_len.main.ConstProp.64bit.diff b/src/test/mir-opt/const_prop/slice_len.main.ConstProp.64bit.diff
index d5a36182e3501..f305d602bbde1 100644
--- a/src/test/mir-opt/const_prop/slice_len.main.ConstProp.64bit.diff
+++ b/src/test/mir-opt/const_prop/slice_len.main.ConstProp.64bit.diff
@@ -24,7 +24,7 @@
                                            // + val: Unevaluated(main, [], Some(promoted[0]))
                                            // mir::Constant
                                            // + span: $DIR/slice_len.rs:5:6: 5:19
-                                           // + literal: Const { ty: &[u32; 3], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ slice_len[6547]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                           // + literal: Const { ty: &[u32; 3], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ slice_len[6547]::main), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
           _4 = _9;                         // scope 0 at $DIR/slice_len.rs:5:6: 5:19
           _3 = _4;                         // scope 0 at $DIR/slice_len.rs:5:6: 5:19
           _2 = move _3 as &[u32] (Pointer(Unsize)); // scope 0 at $DIR/slice_len.rs:5:6: 5:19
diff --git a/src/test/mir-opt/inline/inline_retag.bar.Inline.after.mir b/src/test/mir-opt/inline/inline_retag.bar.Inline.after.mir
index 5ceefce6114f0..0ff0e8ea7b1fa 100644
--- a/src/test/mir-opt/inline/inline_retag.bar.Inline.after.mir
+++ b/src/test/mir-opt/inline/inline_retag.bar.Inline.after.mir
@@ -38,7 +38,7 @@ fn bar() -> bool {
                                          // + val: Unevaluated(bar, [], Some(promoted[1]))
                                          // mir::Constant
                                          // + span: $DIR/inline-retag.rs:12:7: 12:9
-                                         // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:4 ~ inline_retag[a78c]::bar), const_param_did: None }, substs: [], promoted: Some(promoted[1]) }) }
+                                         // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:4 ~ inline_retag[a78c]::bar), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[1]) }) }
         Retag(_10);                      // scope 1 at $DIR/inline-retag.rs:12:7: 12:9
         _4 = &(*_10);                    // scope 1 at $DIR/inline-retag.rs:12:7: 12:9
         Retag(_4);                       // scope 1 at $DIR/inline-retag.rs:12:7: 12:9
@@ -52,7 +52,7 @@ fn bar() -> bool {
                                          // + val: Unevaluated(bar, [], Some(promoted[0]))
                                          // mir::Constant
                                          // + span: $DIR/inline-retag.rs:12:11: 12:14
-                                         // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:4 ~ inline_retag[a78c]::bar), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                         // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:4 ~ inline_retag[a78c]::bar), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
         Retag(_9);                       // scope 1 at $DIR/inline-retag.rs:12:11: 12:14
         _7 = &(*_9);                     // scope 1 at $DIR/inline-retag.rs:12:11: 12:14
         Retag(_7);                       // scope 1 at $DIR/inline-retag.rs:12:11: 12:14
diff --git a/src/test/mir-opt/issue_73223.main.PreCodegen.32bit.diff b/src/test/mir-opt/issue_73223.main.PreCodegen.32bit.diff
index e955feb7d1f41..95632293d9991 100644
--- a/src/test/mir-opt/issue_73223.main.PreCodegen.32bit.diff
+++ b/src/test/mir-opt/issue_73223.main.PreCodegen.32bit.diff
@@ -66,7 +66,7 @@
                                            // + val: Unevaluated(main, [], Some(promoted[0]))
                                            // mir::Constant
                                            // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
-                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ issue_73223[2d0f]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ issue_73223[2d0f]::main), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
           _8 = _20;                        // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           (_6.0: &i32) = move _7;          // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           (_6.1: &i32) = move _8;          // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
diff --git a/src/test/mir-opt/issue_73223.main.PreCodegen.64bit.diff b/src/test/mir-opt/issue_73223.main.PreCodegen.64bit.diff
index e955feb7d1f41..95632293d9991 100644
--- a/src/test/mir-opt/issue_73223.main.PreCodegen.64bit.diff
+++ b/src/test/mir-opt/issue_73223.main.PreCodegen.64bit.diff
@@ -66,7 +66,7 @@
                                            // + val: Unevaluated(main, [], Some(promoted[0]))
                                            // mir::Constant
                                            // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
-                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ issue_73223[2d0f]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ issue_73223[2d0f]::main), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
           _8 = _20;                        // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           (_6.0: &i32) = move _7;          // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           (_6.1: &i32) = move _8;          // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
diff --git a/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.32bit.diff b/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.32bit.diff
index ff8c410a7268d..1581e945e70e6 100644
--- a/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.32bit.diff
+++ b/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.32bit.diff
@@ -87,7 +87,7 @@
                                            // + val: Unevaluated(main, [], Some(promoted[0]))
                                            // mir::Constant
                                            // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
-                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ issue_73223[2d0f]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ issue_73223[2d0f]::main), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
           _11 = _28;                       // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           (_9.0: &i32) = move _10;         // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           (_9.1: &i32) = move _11;         // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
diff --git a/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.64bit.diff b/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.64bit.diff
index ff8c410a7268d..1581e945e70e6 100644
--- a/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.64bit.diff
+++ b/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.64bit.diff
@@ -87,7 +87,7 @@
                                            // + val: Unevaluated(main, [], Some(promoted[0]))
                                            // mir::Constant
                                            // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
-                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ issue_73223[2d0f]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ issue_73223[2d0f]::main), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
           _11 = _28;                       // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           (_9.0: &i32) = move _10;         // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           (_9.1: &i32) = move _11;         // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
diff --git a/src/test/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff b/src/test/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff
index acb8aca75904b..f427375e5a624 100644
--- a/src/test/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff
+++ b/src/test/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff
@@ -50,7 +50,7 @@
                                            // + val: Unevaluated(discriminant, [T], Some(promoted[2]))
                                            // mir::Constant
                                            // + span: $DIR/lower_intrinsics.rs:70:42: 70:44
-                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:27 ~ lower_intrinsics[2872]::discriminant), const_param_did: None }, substs: [T], promoted: Some(promoted[2]) }) }
+                                           // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:27 ~ lower_intrinsics[2872]::discriminant), const_param_did: None }, substs_: Some([T]), promoted: Some(promoted[2]) }) }
           _7 = &(*_19);                    // scope 0 at $DIR/lower_intrinsics.rs:70:42: 70:44
           _6 = &(*_7);                     // scope 0 at $DIR/lower_intrinsics.rs:70:42: 70:44
 -         _5 = discriminant_value::<i32>(move _6) -> bb2; // scope 0 at $DIR/lower_intrinsics.rs:70:5: 70:45
@@ -74,7 +74,7 @@
                                            // + val: Unevaluated(discriminant, [T], Some(promoted[1]))
                                            // mir::Constant
                                            // + span: $DIR/lower_intrinsics.rs:71:42: 71:45
-                                           // + literal: Const { ty: &(), val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:27 ~ lower_intrinsics[2872]::discriminant), const_param_did: None }, substs: [T], promoted: Some(promoted[1]) }) }
+                                           // + literal: Const { ty: &(), val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:27 ~ lower_intrinsics[2872]::discriminant), const_param_did: None }, substs_: Some([T]), promoted: Some(promoted[1]) }) }
           _11 = &(*_18);                   // scope 0 at $DIR/lower_intrinsics.rs:71:42: 71:45
           _10 = &(*_11);                   // scope 0 at $DIR/lower_intrinsics.rs:71:42: 71:45
 -         _9 = discriminant_value::<()>(move _10) -> bb3; // scope 0 at $DIR/lower_intrinsics.rs:71:5: 71:46
@@ -98,7 +98,7 @@
                                            // + val: Unevaluated(discriminant, [T], Some(promoted[0]))
                                            // mir::Constant
                                            // + span: $DIR/lower_intrinsics.rs:72:42: 72:47
-                                           // + literal: Const { ty: &E, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:27 ~ lower_intrinsics[2872]::discriminant), const_param_did: None }, substs: [T], promoted: Some(promoted[0]) }) }
+                                           // + literal: Const { ty: &E, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:27 ~ lower_intrinsics[2872]::discriminant), const_param_did: None }, substs_: Some([T]), promoted: Some(promoted[0]) }) }
           _15 = &(*_17);                   // scope 0 at $DIR/lower_intrinsics.rs:72:42: 72:47
           _14 = &(*_15);                   // scope 0 at $DIR/lower_intrinsics.rs:72:42: 72:47
 -         _13 = discriminant_value::<E>(move _14) -> bb4; // scope 0 at $DIR/lower_intrinsics.rs:72:5: 72:48
diff --git a/src/test/mir-opt/match_false_edges.full_tested_match.PromoteTemps.after.mir b/src/test/mir-opt/match_false_edges.full_tested_match.PromoteTemps.after.mir
index c27ee528b80b7..7f80d81b7edc2 100644
--- a/src/test/mir-opt/match_false_edges.full_tested_match.PromoteTemps.after.mir
+++ b/src/test/mir-opt/match_false_edges.full_tested_match.PromoteTemps.after.mir
@@ -57,7 +57,7 @@ fn full_tested_match() -> () {
                                          // + val: Unevaluated(full_tested_match, [], Some(promoted[0]))
                                          // mir::Constant
                                          // + span: $DIR/match_false_edges.rs:16:14: 16:15
-                                         // + literal: Const { ty: &std::option::Option<i32>, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:5 ~ match_false_edges[4011]::full_tested_match), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                         // + literal: Const { ty: &std::option::Option<i32>, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:5 ~ match_false_edges[4011]::full_tested_match), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
         _6 = &(((*_11) as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15
         _4 = &shallow _2;                // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27
         StorageLive(_7);                 // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27
diff --git a/src/test/mir-opt/retag.array_casts.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/retag.array_casts.SimplifyCfg-elaborate-drops.after.mir
index 787310d072a9b..ee99d033af320 100644
--- a/src/test/mir-opt/retag.array_casts.SimplifyCfg-elaborate-drops.after.mir
+++ b/src/test/mir-opt/retag.array_casts.SimplifyCfg-elaborate-drops.after.mir
@@ -127,7 +127,7 @@ fn array_casts() -> () {
                                          // + val: Unevaluated(array_casts, [], Some(promoted[0]))
                                          // mir::Constant
                                          // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
-                                         // + literal: Const { ty: &usize, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:15 ~ retag[13e7]::array_casts), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                         // + literal: Const { ty: &usize, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:15 ~ retag[13e7]::array_casts), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
         Retag(_35);                      // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
         _18 = &(*_35);                   // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
         Retag(_18);                      // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
diff --git a/src/test/mir-opt/retag.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/retag.main.SimplifyCfg-elaborate-drops.after.mir
index 8abc6a3e4b515..cdf7282c8c3c4 100644
--- a/src/test/mir-opt/retag.main.SimplifyCfg-elaborate-drops.after.mir
+++ b/src/test/mir-opt/retag.main.SimplifyCfg-elaborate-drops.after.mir
@@ -153,7 +153,7 @@ fn main() -> () {
                                          // + val: Unevaluated(main, [], Some(promoted[0]))
                                          // mir::Constant
                                          // + span: $DIR/retag.rs:47:21: 47:23
-                                         // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:13 ~ retag[13e7]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
+                                         // + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:13 ~ retag[13e7]::main), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
         Retag(_28);                      // scope 7 at $DIR/retag.rs:47:21: 47:23
         _23 = &(*_28);                   // scope 7 at $DIR/retag.rs:47:21: 47:23
         Retag(_23);                      // scope 7 at $DIR/retag.rs:47:21: 47:23
diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs
index 8b0e9e6bc9b93..685dbf26250ce 100644
--- a/src/tools/clippy/clippy_lints/src/escape.rs
+++ b/src/tools/clippy/clippy_lints/src/escape.rs
@@ -53,7 +53,7 @@ fn is_non_trait_box(ty: Ty<'_>) -> bool {
 struct EscapeDelegate<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     set: HirIdSet,
-    trait_self_ty: Option<Ty<'a>>,
+    trait_self_ty: Option<Ty<'tcx>>,
     too_large_for_stack: u64,
 }
 
@@ -171,7 +171,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
                 // skip if there is a `self` parameter binding to a type
                 // that contains `Self` (i.e.: `self: Box<Self>`), see #4804
                 if let Some(trait_self_ty) = self.trait_self_ty {
-                    if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) {
+                    if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(self.cx.tcx, cmt.place.ty(), trait_self_ty) {
                         return;
                     }
                 }
diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs
index 8992d25932cad..89146b4dd2c9b 100644
--- a/src/tools/clippy/clippy_lints/src/let_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs
@@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
             if let Some(init) = local.init;
             then {
                 let init_ty = cx.typeck_results().expr_ty(init);
-                let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
+                let contains_sync_guard = init_ty.walk(cx.tcx).any(|inner| match inner.unpack() {
                     GenericArgKind::Type(inner_ty) => {
                         SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
                     },
diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
index 0f6cd5de761f9..545498a10478d 100644
--- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
@@ -49,7 +49,7 @@ pub(super) fn check<'tcx>(
         if same_item_push_visitor.should_lint();
         if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push;
         let vec_ty = cx.typeck_results().expr_ty(vec);
-        let ty = vec_ty.walk().nth(1).unwrap().expect_ty();
+        let ty = vec_ty.walk(cx.tcx).nth(1).unwrap().expect_ty();
         if cx
             .tcx
             .lang_items()
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 91606ed3b2bb0..9626cf79dc129 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -1987,10 +1987,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
 
             // walk the return type and check for Self (this does not check associated types)
             if let Some(self_adt) = self_ty.ty_adt_def() {
-                if contains_adt_constructor(ret_ty, self_adt) {
+                if contains_adt_constructor(cx.tcx, ret_ty, self_adt) {
                     return;
                 }
-            } else if contains_ty(ret_ty, self_ty) {
+            } else if contains_ty(cx.tcx, ret_ty, self_ty) {
                 return;
             }
 
@@ -2001,10 +2001,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                     if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
                         // walk the associated type and check for Self
                         if let Some(self_adt) = self_ty.ty_adt_def() {
-                            if contains_adt_constructor(projection_predicate.ty, self_adt) {
+                            if contains_adt_constructor(cx.tcx, projection_predicate.ty, self_adt) {
                                 return;
                             }
-                        } else if contains_ty(projection_predicate.ty, self_ty) {
+                        } else if contains_ty(cx.tcx, projection_predicate.ty, self_ty) {
                             return;
                         }
                     }
@@ -2053,7 +2053,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             if let TraitItemKind::Fn(_, _) = item.kind;
             let ret_ty = return_ty(cx, item.hir_id());
             let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty();
-            if !contains_ty(ret_ty, self_ty);
+            if !contains_ty(cx.tcx, ret_ty, self_ty);
 
             then {
                 span_lint(
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index 5e559991c1697..90b2aa1689626 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -117,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
         let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
 
         let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds().iter())
-            .filter(|p| !p.is_global())
+            .filter(|p| !p.is_global(cx.tcx))
             .filter_map(|obligation| {
                 // Note that we do not want to deal with qualified predicates here.
                 match obligation.predicate.kind().no_bound_vars() {
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 3f9110295fc6b..2a85a67fa099c 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -187,11 +187,7 @@ fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: D
 
     let result = cx.tcx.const_eval_resolve(
         cx.param_env,
-        ty::Unevaluated {
-            def: ty::WithOptConstParam::unknown(def_id),
-            substs,
-            promoted: None,
-        },
+        ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs),
         None,
     );
     is_value_unfrozen_raw(cx, result, ty)
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index 530b3396abef6..f5e43264a5c69 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -14,7 +14,7 @@ use rustc_middle::mir::{
     visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
     Mutability,
 };
-use rustc_middle::ty::{self, fold::TypeVisitor, Ty};
+use rustc_middle::ty::{self, fold::TypeVisitor, Ty, TyCtxt};
 use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::{BytePos, Span};
@@ -576,7 +576,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
                 self.possible_borrower.add(borrowed.local, lhs);
             },
             other => {
-                if ContainsRegion
+                if ContainsRegion(self.cx.tcx)
                     .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty)
                     .is_continue()
                 {
@@ -625,7 +625,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
                 .flat_map(HybridBitSet::iter)
                 .collect();
 
-            if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() {
+            if ContainsRegion(self.cx.tcx).visit_ty(self.body.local_decls[*dest].ty).is_break() {
                 mutable_variables.push(*dest);
             }
 
@@ -701,12 +701,15 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> {
     }
 }
 
-struct ContainsRegion;
+struct ContainsRegion<'tcx>(TyCtxt<'tcx>);
 
-impl TypeVisitor<'_> for ContainsRegion {
+impl<'tcx> TypeVisitor<'tcx> for ContainsRegion<'tcx> {
     type BreakTy = ();
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.0)
+    }
 
-    fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow<Self::BreakTy> {
+    fn visit_region(&mut self, _: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
         ControlFlow::BREAK
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index e153288aa58df..681baed8c3696 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -288,7 +288,7 @@ impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
                 .fn_sig(def_id)
                 .output()
                 .skip_binder()
-                .walk()
+                .walk(self.cx.tcx)
                 .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs
index 4472edecbed86..4ba5e1a0f5357 100644
--- a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs
+++ b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs
@@ -62,10 +62,10 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors {
 
         // Ensure method is constructor-like
         if let Some(self_adt) = self_ty.ty_adt_def() {
-            if !contains_adt_constructor(ret_ty, self_adt) {
+            if !contains_adt_constructor(cx.tcx, ret_ty, self_adt) {
                 return;
             }
-        } else if !contains_ty(ret_ty, self_ty) {
+        } else if !contains_ty(cx.tcx, ret_ty, self_ty) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
index 6fc5707a4eeff..97b1b2dae3c1a 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
@@ -218,7 +218,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
 
 fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let ty = cx.typeck_results().expr_ty(expr);
-    matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
+    matches!(ty.kind(), ty::Ref(..)) || ty.walk(cx.tcx).any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
 }
 
 impl LateLintPass<'_> for UnnecessarySortBy {
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index a3601cca2eff1..9ae50e47ca4c5 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -170,7 +170,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
                 //
                 // See also https://github.com/rust-lang/rust-clippy/issues/2894.
                 for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) {
-                    if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) {
+                    if trait_sem_ty.walk(cx.tcx).any(|inner| inner == self_ty.into()) {
                         let mut visitor = SkipTyCollector::default();
                         visitor.visit_ty(impl_hir_ty);
                         types_to_skip.extend(visitor.types_to_skip);
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index 9ba1381da6593..8bf31807d55d1 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -346,11 +346,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
                     .tcx
                     .const_eval_resolve(
                         self.param_env,
-                        ty::Unevaluated {
-                            def: ty::WithOptConstParam::unknown(def_id),
-                            substs,
-                            promoted: None,
-                        },
+                        ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs),
                         None,
                     )
                     .ok()
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 32a73984674ad..ddff1686ba2ce 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -1581,7 +1581,7 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
         .predicates_of(did)
         .predicates
         .iter()
-        .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
+        .filter_map(|(p, _)| if p.is_global(cx.tcx) { Some(*p) } else { None });
     traits::impossible_predicates(
         cx.tcx,
         traits::elaborate_predicates(cx.tcx, predicates)
@@ -1627,7 +1627,7 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<S
     if is_primitive {
         // if we have wrappers like Array, Slice or Tuple, print these
         // and get the type enclosed in the slice ref
-        match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
+        match expr_type.peel_refs().walk(cx.tcx).nth(1).unwrap().expect_ty().kind() {
             rustc_ty::Slice(..) => return Some("slice".into()),
             rustc_ty::Array(..) => return Some("array".into()),
             rustc_ty::Tuple(..) => return Some("tuple".into()),
@@ -1635,7 +1635,7 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<S
                 // is_recursively_primitive_type() should have taken care
                 // of the rest and we can rely on the type that is found
                 let refs_peeled = expr_type.peel_refs();
-                return Some(refs_peeled.walk().last().unwrap().to_string());
+                return Some(refs_peeled.walk(cx.tcx).last().unwrap().to_string());
             },
         }
     }
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 8e544f580665e..e5bbf75c3b0a1 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -88,7 +88,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&Ru
 }
 
 fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
-    for arg in ty.walk() {
+    for arg in ty.walk(tcx) {
         let ty = match arg.unpack() {
             GenericArgKind::Type(ty) => ty,
 
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index a2221a0b283b0..3cd8ed5aa2c8c 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -10,7 +10,7 @@ use rustc_hir::{TyKind, Unsafety};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy};
+use rustc_middle::ty::{self, TyCtxt, AdtDef, IntTy, Ty, TypeFoldable, UintTy};
 use rustc_span::sym;
 use rustc_span::symbol::{Ident, Symbol};
 use rustc_span::DUMMY_SP;
@@ -36,8 +36,8 @@ pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 }
 
 /// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
-pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool {
-    ty.walk().any(|inner| match inner.unpack() {
+pub fn contains_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool {
+    ty.walk(tcx).any(|inner| match inner.unpack() {
         GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty),
         GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
     })
@@ -45,8 +45,8 @@ pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool {
 
 /// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
 /// constructor.
-pub fn contains_adt_constructor(ty: Ty<'_>, adt: &AdtDef) -> bool {
-    ty.walk().any(|inner| match inner.unpack() {
+pub fn contains_adt_constructor<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, adt: &'tcx AdtDef) -> bool {
+    ty.walk(tcx).any(|inner| match inner.unpack() {
         GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt),
         GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
     })
@@ -209,7 +209,7 @@ fn is_normalizable_helper<'tcx>(
                         .iter()
                         .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
                 }),
-                _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
+                _ => ty.walk(cx.tcx).all(|generic_arg| match generic_arg.unpack() {
                     GenericArgKind::Type(inner_ty) if inner_ty != ty => {
                         is_normalizable_helper(cx, param_env, inner_ty, cache)
                     },