diff --git a/Cargo.lock b/Cargo.lock
index 3c278a6a491a9..fc31b8506cd87 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4546,6 +4546,7 @@ dependencies = [
  "rustc_index",
  "rustc_macros",
  "rustc_serialize",
+ "smallvec 1.6.1",
 ]
 
 [[package]]
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index 721cfdd4459e5..cc894ecf8c737 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -733,10 +733,10 @@ macro_rules! declare_arena {
                 self.dropless.alloc_slice(value)
             }
 
-            pub fn alloc_from_iter<'a, T: ArenaAllocatable<'tcx, U>, U>(
-                &'a self,
-                iter: impl ::std::iter::IntoIterator<Item = T>,
-            ) -> &'a mut [T] {
+            pub fn alloc_from_iter<T: ArenaAllocatable<'tcx, U>, U, V: IntoIterator<Item = T>>(
+                &self,
+                iter: V,
+            ) -> &mut [T] {
                 T::allocate_from_iter(self, iter)
             }
         }
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 1e224dbf83390..fa1662fd1ce43 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -495,12 +495,10 @@ impl MetaItemKind {
     fn token_trees_and_spacings(&self, span: Span) -> Vec<TreeAndSpacing> {
         match *self {
             MetaItemKind::Word => vec![],
-            MetaItemKind::NameValue(ref lit) => {
-                vec![
-                    TokenTree::token(token::Eq, span).into(),
-                    TokenTree::Token(lit.to_token()).into(),
-                ]
-            }
+            MetaItemKind::NameValue(ref lit) => vec![
+                TokenTree::token(token::Eq, span).into(),
+                TokenTree::Token(lit.to_token()).into(),
+            ],
             MetaItemKind::List(ref list) => {
                 let mut tokens = Vec::new();
                 for (i, item) in list.iter().enumerate() {
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index e9b8388c1c915..d287ba9824675 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -27,8 +27,7 @@ use rustc_middle::middle::cstore::{ForeignModule, LinkagePreference, NativeLib};
 use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
 use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState};
 use rustc_middle::mir::{self, Body, Promoted};
-use rustc_middle::ty::codec::TyDecoder;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, codec::TyDecoder, Interner, Ty, TyCtxt, TyInterner};
 use rustc_serialize::{opaque, Decodable, Decoder};
 use rustc_session::Session;
 use rustc_span::hygiene::ExpnDataDecodeMode;
@@ -245,10 +244,6 @@ impl<'a: 'x, 'tcx: 'x, 'x, T: Decodable<DecodeContext<'a, 'tcx>>> Lazy<[T]> {
 }
 
 impl<'a, 'tcx> DecodeContext<'a, 'tcx> {
-    fn tcx(&self) -> TyCtxt<'tcx> {
-        self.tcx.expect("missing TyCtxt in DecodeContext")
-    }
-
     fn cdata(&self) -> CrateMetadataRef<'a> {
         self.cdata.expect("missing CrateMetadata in DecodeContext")
     }
@@ -277,8 +272,8 @@ impl<'a, 'tcx> TyDecoder<'tcx> for DecodeContext<'a, 'tcx> {
     const CLEAR_CROSS_CRATE: bool = true;
 
     #[inline]
-    fn tcx(&self) -> TyCtxt<'tcx> {
-        self.tcx.expect("missing TyCtxt in DecodeContext")
+    fn interner(&self) -> TyInterner<'tcx> {
+        self.tcx.expect("missing TyCtxt in DecodeContext").interner()
     }
 
     #[inline]
@@ -299,16 +294,16 @@ impl<'a, 'tcx> TyDecoder<'tcx> for DecodeContext<'a, 'tcx> {
     where
         F: FnOnce(&mut Self) -> Result<Ty<'tcx>, Self::Error>,
     {
-        let tcx = self.tcx();
+        let mut interner = self.interner();
 
         let key = ty::CReaderCacheKey { cnum: self.cdata().cnum, pos: shorthand };
 
-        if let Some(&ty) = tcx.ty_rcache.borrow().get(&key) {
+        if let Some(ty) = interner.get_cached_ty(key) {
             return Ok(ty);
         }
 
         let ty = or_insert_with(self)?;
-        tcx.ty_rcache.borrow_mut().insert(key, ty);
+        interner.insert_cached_ty(key, ty);
         Ok(ty)
     }
 
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index 9a42bbe7bacdd..4b39912284b52 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -15,7 +15,7 @@ macro_rules! arena_types {
             // AdtDef are interned and compared by address
             [] adt_def: rustc_middle::ty::AdtDef,
             [] steal_mir: rustc_data_structures::steal::Steal<rustc_middle::mir::Body<$tcx>>,
-            [decode] mir: rustc_middle::mir::Body<$tcx>,
+            [] mir: rustc_middle::mir::Body<$tcx>,
             [] steal_promoted:
                 rustc_data_structures::steal::Steal<
                     rustc_index::vec::IndexVec<
@@ -23,16 +23,16 @@ macro_rules! arena_types {
                         rustc_middle::mir::Body<$tcx>
                     >
                 >,
-            [decode] promoted:
+            [] promoted:
                 rustc_index::vec::IndexVec<
                     rustc_middle::mir::Promoted,
                     rustc_middle::mir::Body<$tcx>
                 >,
-            [decode] typeck_results: rustc_middle::ty::TypeckResults<$tcx>,
-            [decode] borrowck_result:
+            [] typeck_results: rustc_middle::ty::TypeckResults<$tcx>,
+            [] borrowck_result:
                 rustc_middle::mir::BorrowCheckResult<$tcx>,
-            [decode] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult,
-            [decode] code_region: rustc_middle::mir::coverage::CodeRegion,
+            [] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult,
+            [] code_region: rustc_middle::mir::coverage::CodeRegion,
             [] const_allocs: rustc_middle::mir::interpret::Allocation,
             // Required for the incremental on-disk cache
             [few] mir_keys: rustc_hir::def_id::DefIdSet,
@@ -99,11 +99,11 @@ macro_rules! arena_types {
             // Note that this deliberately duplicates items in the `rustc_hir::arena`,
             // since we need to allocate this type on both the `rustc_hir` arena
             // (during lowering) and the `librustc_middle` arena (for decoding MIR)
-            [decode] asm_template: rustc_ast::InlineAsmTemplatePiece,
+            [] asm_template: rustc_ast::InlineAsmTemplatePiece,
 
             // This is used to decode the &'tcx [Span] for InlineAsm's line_spans.
-            [decode] span: rustc_span::Span,
-            [decode] used_trait_imports: rustc_data_structures::fx::FxHashSet<rustc_hir::def_id::LocalDefId>,
+            [] span: rustc_span::Span,
+            [] used_trait_imports: rustc_data_structures::fx::FxHashSet<rustc_hir::def_id::LocalDefId>,
         ], $tcx);
     )
 }
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index 55fe5f971e718..5bcaece8881f6 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -111,6 +111,7 @@ use rustc_macros::HashStable;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_serialize::{Decodable, Encodable};
 use rustc_target::abi::Endian;
+use rustc_type_ir::Interner;
 
 use crate::mir;
 use crate::ty::codec::{TyDecoder, TyEncoder};
@@ -301,7 +302,7 @@ impl<'s> AllocDecodingSession<'s> {
                         AllocDiscriminant::Alloc => {
                             // If this is an allocation, we need to reserve an
                             // `AllocId` so we can decode cyclic graphs.
-                            let alloc_id = decoder.tcx().reserve_alloc_id();
+                            let alloc_id = decoder.interner().reserve_alloc_id();
                             *entry =
                                 State::InProgress(TinyList::new_single(self.session_id), alloc_id);
                             Some(alloc_id)
@@ -345,7 +346,7 @@ impl<'s> AllocDecodingSession<'s> {
                     // We already have a reserved `AllocId`.
                     let alloc_id = alloc_id.unwrap();
                     trace!("decoded alloc {:?}: {:#?}", alloc_id, alloc);
-                    decoder.tcx().set_alloc_id_same_memory(alloc_id, alloc);
+                    decoder.interner().set_alloc_id_same_memory(alloc_id, alloc);
                     Ok(alloc_id)
                 }
                 AllocDiscriminant::Fn => {
@@ -353,7 +354,7 @@ impl<'s> AllocDecodingSession<'s> {
                     trace!("creating fn alloc ID");
                     let instance = ty::Instance::decode(decoder)?;
                     trace!("decoded fn alloc instance: {:?}", instance);
-                    let alloc_id = decoder.tcx().create_fn_alloc(instance);
+                    let alloc_id = decoder.interner().create_fn_alloc(instance);
                     Ok(alloc_id)
                 }
                 AllocDiscriminant::Static => {
@@ -361,7 +362,7 @@ impl<'s> AllocDecodingSession<'s> {
                     trace!("creating extern static alloc ID");
                     let did = <DefId as Decodable<D>>::decode(decoder)?;
                     trace!("decoded static def-ID: {:?}", did);
-                    let alloc_id = decoder.tcx().create_static_alloc(did);
+                    let alloc_id = decoder.interner().create_static_alloc(did);
                     Ok(alloc_id)
                 }
             }
@@ -545,7 +546,7 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called
     /// twice for the same `(AllocId, Allocation)` pair.
-    fn set_alloc_id_same_memory(self, id: AllocId, mem: &'tcx Allocation) {
+    pub fn set_alloc_id_same_memory(self, id: AllocId, mem: &'tcx Allocation) {
         self.alloc_map.lock().alloc_map.insert_same(id, GlobalAlloc::Memory(mem));
     }
 }
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index 73ad87a9ef219..461e4462b2e4e 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -6,18 +6,18 @@
 // The functionality in here is shared between persisting to crate metadata and
 // persisting to incr. comp. caches.
 
-use crate::arena::ArenaAllocatable;
 use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos};
 use crate::mir::{
     self,
     interpret::{AllocId, Allocation},
 };
 use crate::ty::subst::SubstsRef;
-use crate::ty::{self, List, Ty, TyCtxt};
+use crate::ty::{self, List, Ty, TyInterner};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use rustc_span::Span;
+use rustc_type_ir::Interner;
 use std::hash::Hash;
 use std::intrinsics;
 use std::marker::DiscriminantKind;
@@ -160,7 +160,7 @@ encodable_via_deref! {
 pub trait TyDecoder<'tcx>: Decoder {
     const CLEAR_CROSS_CRATE: bool;
 
-    fn tcx(&self) -> TyCtxt<'tcx>;
+    fn interner(&self) -> TyInterner<'tcx>;
 
     fn peek_byte(&self) -> u8;
 
@@ -187,26 +187,6 @@ pub trait TyDecoder<'tcx>: Decoder {
     fn decode_alloc_id(&mut self) -> Result<AllocId, Self::Error>;
 }
 
-#[inline]
-pub fn decode_arena_allocable<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable<D>>(
-    decoder: &mut D,
-) -> Result<&'tcx T, D::Error>
-where
-    D: TyDecoder<'tcx>,
-{
-    Ok(decoder.tcx().arena.alloc(Decodable::decode(decoder)?))
-}
-
-#[inline]
-pub fn decode_arena_allocable_slice<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable<D>>(
-    decoder: &mut D,
-) -> Result<&'tcx [T], D::Error>
-where
-    D: TyDecoder<'tcx>,
-{
-    Ok(decoder.tcx().arena.alloc_from_iter(<Vec<T> as Decodable<D>>::decode(decoder)?))
-}
-
 impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for Ty<'tcx> {
     #[allow(rustc::usage_of_ty_tykind)]
     fn decode(decoder: &mut D) -> Result<Ty<'tcx>, D::Error> {
@@ -220,8 +200,7 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for Ty<'tcx> {
                 decoder.with_position(shorthand, Ty::decode)
             })
         } else {
-            let tcx = decoder.tcx();
-            Ok(tcx.mk_ty(ty::TyKind::decode(decoder)?))
+            Ok(decoder.interner().mk_ty(ty::TyKind::decode(decoder)?))
         }
     }
 }
@@ -244,7 +223,7 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for ty::Binder<ty::PredicateKind<'tc
 impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for ty::Predicate<'tcx> {
     fn decode(decoder: &mut D) -> Result<ty::Predicate<'tcx>, D::Error> {
         let predicate_kind = Decodable::decode(decoder)?;
-        let predicate = decoder.tcx().mk_predicate(predicate_kind);
+        let predicate = decoder.interner().mk_predicate(predicate_kind);
         Ok(predicate)
     }
 }
@@ -252,8 +231,7 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for ty::Predicate<'tcx> {
 impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for SubstsRef<'tcx> {
     fn decode(decoder: &mut D) -> Result<Self, D::Error> {
         let len = decoder.read_usize()?;
-        let tcx = decoder.tcx();
-        tcx.mk_substs((0..len).map(|_| Decodable::decode(decoder)))
+        Ok(decoder.interner().mk_substs((0..len).map(|_| Decodable::decode(decoder)))?)
     }
 }
 
@@ -262,14 +240,14 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for mir::Place<'tcx> {
         let local: mir::Local = Decodable::decode(decoder)?;
         let len = decoder.read_usize()?;
         let projection: &'tcx List<mir::PlaceElem<'tcx>> =
-            decoder.tcx().mk_place_elems((0..len).map(|_| Decodable::decode(decoder)))?;
+            decoder.interner().mk_place_elems((0..len).map(|_| Decodable::decode(decoder)))?;
         Ok(mir::Place { local, projection })
     }
 }
 
 impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for ty::Region<'tcx> {
     fn decode(decoder: &mut D) -> Result<Self, D::Error> {
-        Ok(decoder.tcx().mk_region(Decodable::decode(decoder)?))
+        Ok(decoder.interner().mk_region(Decodable::decode(decoder)?))
     }
 }
 
@@ -278,7 +256,7 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for CanonicalVarInfos<'tcx> {
         let len = decoder.read_usize()?;
         let interned: Result<Vec<CanonicalVarInfo<'tcx>>, _> =
             (0..len).map(|_| Decodable::decode(decoder)).collect();
-        Ok(decoder.tcx().intern_canonical_var_infos(interned?.as_slice()))
+        Ok(decoder.interner().intern_canonical_var_infos(interned?.as_slice()))
     }
 }
 
@@ -290,7 +268,7 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for AllocId {
 
 impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for ty::SymbolName<'tcx> {
     fn decode(decoder: &mut D) -> Result<Self, D::Error> {
-        Ok(ty::SymbolName::new(decoder.tcx(), &decoder.read_str()?))
+        Ok(decoder.interner().mk_symbol_name(&decoder.read_str()?))
     }
 }
 
@@ -307,14 +285,14 @@ macro_rules! impl_decodable_via_ref {
 impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::AdtDef {
     fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
         let def_id = <DefId as Decodable<D>>::decode(decoder)?;
-        Ok(decoder.tcx().adt_def(def_id))
+        Ok(decoder.interner().adt_def(def_id))
     }
 }
 
 impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List<Ty<'tcx>> {
     fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
         let len = decoder.read_usize()?;
-        decoder.tcx().mk_type_list((0..len).map(|_| Decodable::decode(decoder)))
+        Ok(decoder.interner().mk_type_list((0..len).map(|_| Decodable::decode(decoder)))?)
     }
 }
 
@@ -323,25 +301,27 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D>
 {
     fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
         let len = decoder.read_usize()?;
-        decoder.tcx().mk_poly_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))
+        Ok(decoder
+            .interner()
+            .mk_poly_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))?)
     }
 }
 
 impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::Const<'tcx> {
     fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
-        Ok(decoder.tcx().mk_const(Decodable::decode(decoder)?))
+        Ok(decoder.interner().mk_const(Decodable::decode(decoder)?))
     }
 }
 
 impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for Allocation {
     fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
-        Ok(decoder.tcx().intern_const_alloc(Decodable::decode(decoder)?))
+        Ok(decoder.interner().intern_const_alloc(Decodable::decode(decoder)?))
     }
 }
 
 impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [(ty::Predicate<'tcx>, Span)] {
     fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
-        Ok(decoder.tcx().arena.alloc_from_iter(
+        Ok(decoder.interner().alloc_predicate_span_from_iter(
             (0..decoder.read_usize()?)
                 .map(|_| Decodable::decode(decoder))
                 .collect::<Result<Vec<_>, _>>()?,
@@ -351,7 +331,7 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [(ty::Predicate<'tcx>,
 
 impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::Node<'tcx>] {
     fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
-        Ok(decoder.tcx().arena.alloc_from_iter(
+        Ok(decoder.interner().alloc_node_from_iter(
             (0..decoder.read_usize()?)
                 .map(|_| Decodable::decode(decoder))
                 .collect::<Result<Vec<_>, _>>()?,
@@ -361,7 +341,7 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::N
 
 impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::NodeId] {
     fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
-        Ok(decoder.tcx().arena.alloc_from_iter(
+        Ok(decoder.interner().alloc_node_id_from_iter(
             (0..decoder.read_usize()?)
                 .map(|_| Decodable::decode(decoder))
                 .collect::<Result<Vec<_>, _>>()?,
@@ -392,39 +372,59 @@ macro_rules! __impl_decoder_methods {
     }
 }
 
-macro_rules! impl_arena_allocatable_decoder {
-    ([]$args:tt) => {};
-    ([decode $(, $attrs:ident)*]
-     [[$name:ident: $ty:ty], $tcx:lifetime]) => {
-        impl<$tcx, D: TyDecoder<$tcx>> RefDecodable<$tcx, D> for $ty {
+macro_rules! impl_ty_decoder_arena_type {
+    ($ty:ty, $alloc_method:ident, $alloc_slice_method:ident) => {
+        impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for $ty {
             #[inline]
-            fn decode(decoder: &mut D) -> Result<&$tcx Self, D::Error> {
-                decode_arena_allocable(decoder)
+            fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
+                Ok(decoder.interner().$alloc_method(Decodable::decode(decoder)?))
             }
         }
 
-        impl<$tcx, D: TyDecoder<$tcx>> RefDecodable<$tcx, D> for [$ty] {
+        impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [$ty] {
             #[inline]
-            fn decode(decoder: &mut D) -> Result<&$tcx Self, D::Error> {
-                decode_arena_allocable_slice(decoder)
+            fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
+                Ok(decoder
+                    .interner()
+                    .$alloc_slice_method(<Vec<$ty> as Decodable<D>>::decode(decoder)?))
             }
         }
     };
-    ([$ignore:ident $(, $attrs:ident)*]$args:tt) => {
-        impl_arena_allocatable_decoder!([$($attrs),*]$args);
-    };
-}
-
-macro_rules! impl_arena_allocatable_decoders {
-    ([], [$($a:tt $name:ident: $ty:ty,)*], $tcx:lifetime) => {
-        $(
-            impl_arena_allocatable_decoder!($a [[$name: $ty], $tcx]);
-        )*
-    }
 }
 
-rustc_hir::arena_types!(impl_arena_allocatable_decoders, [], 'tcx);
-arena_types!(impl_arena_allocatable_decoders, [], 'tcx);
+impl_ty_decoder_arena_type!(rustc_middle::mir::Body<'tcx>, alloc_mir, alloc_mir_from_iter);
+impl_ty_decoder_arena_type!(
+    rustc_middle::ty::TypeckResults<'tcx>,
+    alloc_type_check_results,
+    alloc_type_check_results_from_iter
+);
+impl_ty_decoder_arena_type!(
+    rustc_middle::mir::BorrowCheckResult<'tcx>,
+    alloc_borrowck_result,
+    alloc_borrowck_result_from_iter
+);
+impl_ty_decoder_arena_type!(
+    rustc_middle::mir::UnsafetyCheckResult,
+    alloc_unsafety_check_result,
+    alloc_unsafety_check_result_from_iter
+);
+impl_ty_decoder_arena_type!(
+    rustc_middle::mir::coverage::CodeRegion,
+    alloc_code_region,
+    alloc_code_region_from_iter
+);
+impl_ty_decoder_arena_type!(
+    rustc_ast::InlineAsmTemplatePiece,
+    alloc_asm_template,
+    alloc_asm_template_from_iter
+);
+impl_ty_decoder_arena_type!(rustc_span::Span, alloc_span, alloc_span_from_iter);
+impl_ty_decoder_arena_type!(
+    rustc_data_structures::fx::FxHashSet<rustc_hir::def_id::LocalDefId>,
+    alloc_used_trait_imports,
+    alloc_used_trait_imports_from_iter
+);
+impl_ty_decoder_arena_type!(rustc_index::vec::IndexVec<rustc_middle::mir::Promoted,rustc_middle::mir::Body<'tcx>>, alloc_promoted, alloc_promoted_from_iter);
 
 #[macro_export]
 macro_rules! implement_ty_decoder {
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index e1d79248171a8..08c2af639b5f1 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -11,8 +11,13 @@ use crate::middle;
 use crate::middle::cstore::{CrateStoreDyn, EncodedMetadata};
 use crate::middle::resolve_lifetime::{self, ObjectLifetimeDefault};
 use crate::middle::stability;
-use crate::mir::interpret::{self, Allocation, ConstValue, Scalar};
-use crate::mir::{Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted};
+use crate::mir::abstract_const::{Node as ConstNode, NodeId};
+use crate::mir::coverage::CodeRegion;
+use crate::mir::interpret::{self, AllocId, Allocation, ConstValue, Scalar};
+use crate::mir::{
+    Body, BorrowCheckResult, Field, Local, Place, PlaceElem, ProjectionKind, Promoted,
+    UnsafetyCheckResult,
+};
 use crate::traits;
 use crate::ty::query::{self, OnDiskCache, TyCtxtAt};
 use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSubsts};
@@ -20,8 +25,8 @@ use crate::ty::TyKind::*;
 use crate::ty::{
     self, AdtDef, AdtKind, Binder, BindingMode, BoundVar, CanonicalPolyFnSig, Const, ConstVid,
     DefIdTree, ExistentialPredicate, FloatTy, FloatVar, FloatVid, GenericParamDefKind, InferConst,
-    InferTy, IntTy, IntVar, IntVid, List, ParamConst, ParamTy, PolyFnSig, Predicate,
-    PredicateInner, PredicateKind, ProjectionTy, Region, RegionKind, ReprOptions,
+    InferTy, Instance, IntTy, IntVar, IntVid, List, ParamConst, ParamTy, PolyFnSig, Predicate,
+    PredicateInner, PredicateKind, ProjectionTy, Region, RegionKind, ReprOptions, SymbolName,
     TraitObjectVisitor, Ty, TyKind, TyS, TyVar, TyVid, TypeAndMut, UintTy, Visibility,
 };
 use rustc_ast as ast;
@@ -34,11 +39,11 @@ use rustc_data_structures::stable_hasher::{
     hash_stable_hashmap, HashStable, StableHasher, StableVec,
 };
 use rustc_data_structures::steal::Steal;
-use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal};
+use rustc_data_structures::sync::{self, HashMapExt, Lock, Lrc, WorkerLocal};
 use rustc_errors::ErrorReported;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
+use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefPathHash, LocalDefId, LOCAL_CRATE};
 use rustc_hir::definitions::Definitions;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::lang_items::LangItem;
@@ -56,8 +61,8 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{Layout, TargetDataLayout, VariantIdx};
 use rustc_target::spec::abi;
+use rustc_type_ir::{InternAs, InternIteratorElement, Interner};
 
-use smallvec::SmallVec;
 use std::any::Any;
 use std::borrow::Borrow;
 use std::cmp::Ordering;
@@ -69,6 +74,321 @@ use std::mem;
 use std::ops::{Bound, Deref};
 use std::sync::Arc;
 
+pub struct TyInterner<'tcx> {
+    tcx: TyCtxt<'tcx>,
+}
+
+#[allow(rustc::usage_of_ty_tykind)]
+impl<'tcx> Interner for TyInterner<'tcx> {
+    type GenericArg = GenericArg<'tcx>;
+    type ListGenericArg = &'tcx List<GenericArg<'tcx>>;
+
+    type ExistentialPredicate = ty::Binder<ExistentialPredicate<'tcx>>;
+    type ListExistentialPredicate = &'tcx List<Binder<ExistentialPredicate<'tcx>>>;
+
+    type Predicate = Predicate<'tcx>;
+    type BinderPredicateKind = Binder<PredicateKind<'tcx>>;
+
+    type Ty = Ty<'tcx>;
+    type ListType = &'tcx List<Ty<'tcx>>;
+    type TyKind = TyKind<'tcx>;
+    type TypeKey = ty::CReaderCacheKey;
+
+    type AllocationId = AllocId;
+    type Allocation = Allocation;
+    type InternedAllocation = &'tcx Allocation;
+
+    type DefId = DefId;
+    type Instance = Instance<'tcx>;
+
+    type CanonicalVarInfo = CanonicalVarInfo<'tcx>;
+    type ListCanonicalVarInfo = CanonicalVarInfos<'tcx>;
+
+    type RegionKind = RegionKind;
+    type Region = Region<'tcx>;
+
+    type Const = Const<'tcx>;
+    type InternedConst = &'tcx Const<'tcx>;
+
+    type PlaceElem = PlaceElem<'tcx>;
+    type ListPlaceElem = &'tcx List<PlaceElem<'tcx>>;
+
+    type DefPathHash = DefPathHash;
+    type AdtDef = &'tcx ty::AdtDef;
+
+    type SymbolName = SymbolName<'tcx>;
+
+    type Mir = Body<'tcx>;
+    type AllocatedMir = &'tcx mut Body<'tcx>;
+    type AllocatedMirSlice = &'tcx mut [Body<'tcx>];
+
+    type Promoted = IndexVec<Promoted, Body<'tcx>>;
+    type AllocatedPromoted = &'tcx mut IndexVec<Promoted, Body<'tcx>>;
+    type AllocatedPromotedSlice = &'tcx mut [IndexVec<Promoted, Body<'tcx>>];
+
+    type TypeCheckResults = TypeckResults<'tcx>;
+    type AllocatedTypeCheckResults = &'tcx mut TypeckResults<'tcx>;
+    type AllocatedTypeCheckResultsSlice = &'tcx mut [TypeckResults<'tcx>];
+
+    type BorrowCheckResult = BorrowCheckResult<'tcx>;
+    type AllocatedBorrowCheckResult = &'tcx mut BorrowCheckResult<'tcx>;
+    type AllocatedBorrowCheckResultSlice = &'tcx mut [BorrowCheckResult<'tcx>];
+
+    type CodeRegion = CodeRegion;
+    type AllocatedCodeRegion = &'tcx mut CodeRegion;
+    type AllocatedCodeRegionSlice = &'tcx mut [CodeRegion];
+
+    type UnsafetyCheckResult = UnsafetyCheckResult;
+    type AllocatedUnsafetyCheckResult = &'tcx mut UnsafetyCheckResult;
+    type AllocatedUnsafetyCheckResultSlice = &'tcx mut [UnsafetyCheckResult];
+
+    type Span = Span;
+    type AllocatedSpan = &'tcx mut Span;
+    type AllocatedSpanSlice = &'tcx mut [Span];
+
+    type UsedTraitsImports = FxHashSet<LocalDefId>;
+    type AllocatedUsedTraitsImports = &'tcx mut FxHashSet<LocalDefId>;
+    type AllocatedUsedTraitsImportsSlice = &'tcx mut [FxHashSet<LocalDefId>];
+
+    type AsmTemplate = ast::InlineAsmTemplatePiece;
+    type AllocatedAsmTemplate = &'tcx mut ast::InlineAsmTemplatePiece;
+    type AllocatedAsmTemplateSlice = &'tcx mut [ast::InlineAsmTemplatePiece];
+
+    type PredicateSpan = (ty::Predicate<'tcx>, Span);
+    type AllocatedPredicateSpanSlice = &'tcx mut [(ty::Predicate<'tcx>, Span)];
+
+    type Node = ConstNode<'tcx>;
+    type AllocatedNodeSlice = &'tcx mut [ConstNode<'tcx>];
+
+    type NodeId = NodeId;
+    type AllocatedNodeIdSlice = &'tcx mut [NodeId];
+
+    fn alloc_predicate_span_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::PredicateSpan>,
+    ) -> Self::AllocatedPredicateSpanSlice {
+        self.tcx.arena.alloc_from_iter(iter)
+    }
+
+    fn alloc_node_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::Node>,
+    ) -> Self::AllocatedNodeSlice {
+        self.tcx.arena.alloc_from_iter(iter)
+    }
+
+    fn alloc_node_id_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::NodeId>,
+    ) -> Self::AllocatedNodeIdSlice {
+        self.tcx.arena.alloc_from_iter(iter)
+    }
+
+    fn alloc_span(self, value: Self::Span) -> Self::AllocatedSpan {
+        self.tcx.arena.alloc::<_, Self::Span>(value)
+    }
+
+    fn alloc_span_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::Span>,
+    ) -> Self::AllocatedSpanSlice {
+        self.tcx.arena.alloc_from_iter::<Self::Span, Self::Span, _>(iter)
+    }
+
+    fn alloc_promoted(self, value: Self::Promoted) -> Self::AllocatedPromoted {
+        self.tcx.arena.alloc(value)
+    }
+
+    fn alloc_promoted_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::Promoted>,
+    ) -> Self::AllocatedPromotedSlice {
+        self.tcx.arena.alloc_from_iter(iter)
+    }
+
+    fn alloc_borrowck_result(
+        self,
+        value: Self::BorrowCheckResult,
+    ) -> Self::AllocatedBorrowCheckResult {
+        self.tcx.arena.alloc(value)
+    }
+
+    fn alloc_borrowck_result_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::BorrowCheckResult>,
+    ) -> Self::AllocatedBorrowCheckResultSlice {
+        self.tcx.arena.alloc_from_iter(iter)
+    }
+
+    fn alloc_type_check_results(
+        self,
+        value: Self::TypeCheckResults,
+    ) -> Self::AllocatedTypeCheckResults {
+        self.tcx.arena.alloc(value)
+    }
+
+    fn alloc_type_check_results_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::TypeCheckResults>,
+    ) -> Self::AllocatedTypeCheckResultsSlice {
+        self.tcx.arena.alloc_from_iter(iter)
+    }
+
+    fn alloc_mir(self, value: Self::Mir) -> Self::AllocatedMir {
+        self.tcx.arena.alloc(value)
+    }
+
+    fn alloc_mir_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::Mir>,
+    ) -> Self::AllocatedMirSlice {
+        self.tcx.arena.alloc_from_iter(iter)
+    }
+
+    fn alloc_code_region(self, value: Self::CodeRegion) -> Self::AllocatedCodeRegion {
+        self.tcx.arena.alloc(value)
+    }
+
+    fn alloc_code_region_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::CodeRegion>,
+    ) -> Self::AllocatedCodeRegionSlice {
+        self.tcx.arena.alloc_from_iter(iter)
+    }
+
+    fn alloc_unsafety_check_result(
+        self,
+        value: Self::UnsafetyCheckResult,
+    ) -> Self::AllocatedUnsafetyCheckResult {
+        self.tcx.arena.alloc(value)
+    }
+
+    fn alloc_unsafety_check_result_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::UnsafetyCheckResult>,
+    ) -> Self::AllocatedUnsafetyCheckResultSlice {
+        self.tcx.arena.alloc_from_iter(iter)
+    }
+
+    fn alloc_used_trait_imports(
+        self,
+        value: Self::UsedTraitsImports,
+    ) -> Self::AllocatedUsedTraitsImports {
+        self.tcx.arena.alloc(value)
+    }
+
+    fn alloc_used_trait_imports_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::UsedTraitsImports>,
+    ) -> Self::AllocatedUsedTraitsImportsSlice {
+        self.tcx.arena.alloc_from_iter(iter)
+    }
+
+    fn alloc_asm_template(self, value: Self::AsmTemplate) -> Self::AllocatedAsmTemplate {
+        self.tcx.arena.alloc(value)
+    }
+
+    fn alloc_asm_template_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::AsmTemplate>,
+    ) -> Self::AllocatedAsmTemplateSlice {
+        self.tcx.arena.alloc_from_iter(iter)
+    }
+
+    fn mk_symbol_name(self, name: &str) -> Self::SymbolName {
+        SymbolName::new(self.tcx, name)
+    }
+
+    fn get_cached_ty(&self, k: Self::TypeKey) -> Option<Self::Ty> {
+        self.tcx.ty_rcache.borrow().get(&k).map(|x| x.to_owned())
+    }
+    fn insert_same_cached_ty(&mut self, key: Self::TypeKey, value: Self::Ty) {
+        self.tcx.ty_rcache.borrow_mut().insert_same(key, value);
+    }
+
+    fn insert_cached_ty(&mut self, key: Self::TypeKey, value: Self::Ty) -> Option<Self::Ty> {
+        self.tcx.ty_rcache.borrow_mut().insert(key, value)
+    }
+
+    fn adt_def(self, def_id: Self::DefId) -> Self::AdtDef {
+        self.tcx.adt_def(def_id)
+    }
+
+    fn mk_predicate(self, binder: Self::BinderPredicateKind) -> Self::Predicate {
+        self.tcx.mk_predicate(binder)
+    }
+
+    fn mk_ty(self, st: TyKind<'tcx>) -> Ty<'tcx> {
+        self.tcx.mk_ty(st)
+    }
+
+    fn mk_substs<I: InternAs<[Self::GenericArg], Self::ListGenericArg>>(
+        self,
+        iter: I,
+    ) -> I::Output {
+        self.tcx.mk_substs(iter)
+    }
+
+    fn mk_poly_existential_predicates<
+        I: InternAs<[Self::ExistentialPredicate], Self::ListExistentialPredicate>,
+    >(
+        self,
+        iter: I,
+    ) -> I::Output {
+        self.tcx.mk_poly_existential_predicates(iter)
+    }
+
+    fn mk_type_list<I: InternAs<[Self::Ty], Self::ListType>>(self, iter: I) -> I::Output {
+        iter.intern_with(|xs| self.tcx.intern_type_list(xs))
+    }
+
+    fn mk_place_elems<I: InternAs<[Self::PlaceElem], Self::ListPlaceElem>>(
+        self,
+        iter: I,
+    ) -> I::Output {
+        iter.intern_with(|xs| self.tcx.intern_place_elems(xs))
+    }
+
+    fn mk_region(self, kind: Self::RegionKind) -> Self::Region {
+        self.tcx.mk_region(kind)
+    }
+
+    fn mk_const(self, c: Self::Const) -> Self::InternedConst {
+        self.tcx.mk_const(c)
+    }
+
+    fn intern_const_alloc(self, alloc: Self::Allocation) -> Self::InternedAllocation {
+        self.tcx.allocation_interner.intern(alloc, |alloc| self.tcx.arena.alloc(alloc))
+    }
+
+    fn intern_canonical_var_infos(
+        self,
+        ts: &[Self::CanonicalVarInfo],
+    ) -> Self::ListCanonicalVarInfo {
+        self.tcx.intern_canonical_var_infos(ts)
+    }
+
+    fn reserve_alloc_id(self) -> Self::AllocationId {
+        self.tcx.reserve_alloc_id()
+    }
+
+    fn set_alloc_id_same_memory(self, id: Self::AllocationId, mem: Self::InternedAllocation) {
+        self.tcx.set_alloc_id_same_memory(id, mem)
+    }
+
+    fn create_fn_alloc(self, instance: Self::Instance) -> Self::AllocationId {
+        self.tcx.create_fn_alloc(instance)
+    }
+
+    fn create_static_alloc(self, static_id: Self::DefId) -> Self::AllocationId {
+        self.tcx.create_static_alloc(static_id)
+    }
+
+    fn def_path_hash_to_def_id(self, hash: Self::DefPathHash) -> Option<Self::DefId> {
+        self.tcx.on_disk_cache.as_ref().unwrap().def_path_hash_to_def_id(self.tcx, hash)
+    }
+}
+
 /// A type that is not publicly constructable. This prevents people from making [`TyKind::Error`]s
 /// except through the error-reporting functions on a [`tcx`][TyCtxt].
 #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
@@ -1025,6 +1345,10 @@ pub struct GlobalCtxt<'tcx> {
 }
 
 impl<'tcx> TyCtxt<'tcx> {
+    pub fn interner(self) -> TyInterner<'tcx> {
+        TyInterner { tcx: self }
+    }
+
     pub fn typeck_opt_const_arg(
         self,
         def: ty::WithOptConstParam<LocalDefId>,
@@ -2647,81 +2971,6 @@ impl TyCtxtAt<'tcx> {
     }
 }
 
-pub trait InternAs<T: ?Sized, R> {
-    type Output;
-    fn intern_with<F>(self, f: F) -> Self::Output
-    where
-        F: FnOnce(&T) -> R;
-}
-
-impl<I, T, R, E> InternAs<[T], R> for I
-where
-    E: InternIteratorElement<T, R>,
-    I: Iterator<Item = E>,
-{
-    type Output = E::Output;
-    fn intern_with<F>(self, f: F) -> Self::Output
-    where
-        F: FnOnce(&[T]) -> R,
-    {
-        E::intern_with(self, f)
-    }
-}
-
-pub trait InternIteratorElement<T, R>: Sized {
-    type Output;
-    fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output;
-}
-
-impl<T, R> InternIteratorElement<T, R> for T {
-    type Output = R;
-    fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output {
-        f(&iter.collect::<SmallVec<[_; 8]>>())
-    }
-}
-
-impl<'a, T, R> InternIteratorElement<T, R> for &'a T
-where
-    T: Clone + 'a,
-{
-    type Output = R;
-    fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output {
-        f(&iter.cloned().collect::<SmallVec<[_; 8]>>())
-    }
-}
-
-impl<T, R, E> InternIteratorElement<T, R> for Result<T, E> {
-    type Output = Result<R, E>;
-    fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>(
-        mut iter: I,
-        f: F,
-    ) -> Self::Output {
-        // This code is hot enough that it's worth specializing for the most
-        // common length lists, to avoid the overhead of `SmallVec` creation.
-        // The match arms are in order of frequency. The 1, 2, and 0 cases are
-        // typically hit in ~95% of cases. We assume that if the upper and
-        // lower bounds from `size_hint` agree they are correct.
-        Ok(match iter.size_hint() {
-            (1, Some(1)) => {
-                let t0 = iter.next().unwrap()?;
-                assert!(iter.next().is_none());
-                f(&[t0])
-            }
-            (2, Some(2)) => {
-                let t0 = iter.next().unwrap()?;
-                let t1 = iter.next().unwrap()?;
-                assert!(iter.next().is_none());
-                f(&[t0, t1])
-            }
-            (0, Some(0)) => {
-                assert!(iter.next().is_none());
-                f(&[])
-            }
-            _ => f(&iter.collect::<Result<SmallVec<[_; 8]>, _>>()?),
-        })
-    }
-}
-
 // We are comparing types with different invariant lifetimes, so `ptr::eq`
 // won't work for us.
 fn ptr_eq<T, U>(t: *const T, u: *const U) -> bool {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index f5aef108927db..7bec890706436 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -59,7 +59,7 @@ pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt};
 pub use self::context::{
     tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
     CtxtInterners, DelaySpanBugEmitted, FreeRegionInfo, GeneratorInteriorTypeCause, GlobalCtxt,
-    Lift, ResolvedOpaqueTy, TyCtxt, TypeckResults, UserType, UserTypeAnnotationIndex,
+    Lift, ResolvedOpaqueTy, TyCtxt, TyInterner, TypeckResults, UserType, UserTypeAnnotationIndex,
 };
 pub use self::instance::{Instance, InstanceDef};
 pub use self::list::List;
diff --git a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs
index d0cd8a48f99b3..38a1476f49115 100644
--- a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs
+++ b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs
@@ -3,10 +3,10 @@ use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState};
 use crate::mir::{self, interpret};
 use crate::ty::codec::{RefDecodable, TyDecoder, TyEncoder};
 use crate::ty::context::TyCtxt;
-use crate::ty::{self, Ty};
+use crate::ty::{self, Ty, TyInterner};
 use rustc_data_structures::fingerprint::{Fingerprint, FingerprintDecoder, FingerprintEncoder};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
-use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, OnceCell};
+use rustc_data_structures::sync::{Lock, Lrc, OnceCell};
 use rustc_data_structures::thin_vec::ThinVec;
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_errors::Diagnostic;
@@ -28,6 +28,7 @@ use rustc_span::hygiene::{
 use rustc_span::source_map::{SourceMap, StableSourceFileId};
 use rustc_span::CachingSourceMapView;
 use rustc_span::{BytePos, ExpnData, SourceFile, Span, DUMMY_SP};
+use rustc_type_ir::Interner;
 use std::collections::hash_map::Entry;
 use std::iter::FromIterator;
 use std::mem;
@@ -741,8 +742,8 @@ impl<'a, 'tcx> TyDecoder<'tcx> for CacheDecoder<'a, 'tcx> {
     const CLEAR_CROSS_CRATE: bool = false;
 
     #[inline]
-    fn tcx(&self) -> TyCtxt<'tcx> {
-        self.tcx
+    fn interner(&self) -> TyInterner<'tcx> {
+        self.tcx.interner()
     }
 
     #[inline]
@@ -763,18 +764,18 @@ impl<'a, 'tcx> TyDecoder<'tcx> for CacheDecoder<'a, 'tcx> {
     where
         F: FnOnce(&mut Self) -> Result<Ty<'tcx>, Self::Error>,
     {
-        let tcx = self.tcx();
+        let mut interner = self.interner();
 
         let cache_key =
             ty::CReaderCacheKey { cnum: CrateNum::ReservedForIncrCompCache, pos: shorthand };
 
-        if let Some(&ty) = tcx.ty_rcache.borrow().get(&cache_key) {
+        if let Some(ty) = interner.get_cached_ty(cache_key) {
             return Ok(ty);
         }
 
         let ty = or_insert_with(self)?;
         // This may overwrite the entry, but it should overwrite with the same value.
-        tcx.ty_rcache.borrow_mut().insert_same(cache_key, ty);
+        interner.insert_same_cached_ty(cache_key, ty);
         Ok(ty)
     }
 
@@ -904,12 +905,9 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for DefId {
         // If we get to this point, then all of the query inputs were green,
         // which means that the definition with this hash is guaranteed to
         // still exist in the current compilation session.
-        Ok(d.tcx()
-            .on_disk_cache
-            .as_ref()
-            .unwrap()
-            .def_path_hash_to_def_id(d.tcx(), def_path_hash)
-            .unwrap())
+
+        let def_if = d.interner().def_path_hash_to_def_id(def_path_hash);
+        Ok(def_if.unwrap())
     }
 }
 
diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml
index 3f64bd899979f..b35bac47fdd3e 100644
--- a/compiler/rustc_type_ir/Cargo.toml
+++ b/compiler/rustc_type_ir/Cargo.toml
@@ -13,3 +13,4 @@ rustc_index = { path = "../rustc_index" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_macros = { path = "../rustc_macros" }
+smallvec = { version = "1.0", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index fccd8b795ef56..3de555cef2303 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -9,9 +9,285 @@ extern crate rustc_macros;
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::unify::{EqUnifyValue, UnifyKey};
+use smallvec::SmallVec;
 use std::fmt;
+use std::iter::IntoIterator;
 use std::mem::discriminant;
 
+pub trait Interner {
+    type GenericArg;
+    type ListGenericArg;
+
+    type ExistentialPredicate;
+    type ListExistentialPredicate;
+
+    type Predicate;
+    type BinderPredicateKind;
+
+    type Ty;
+    type ListType;
+    type TyKind;
+    type TypeKey;
+
+    type AllocationId;
+    type Allocation;
+    type InternedAllocation;
+
+    type DefId;
+    type Instance;
+
+    type CanonicalVarInfo;
+    type ListCanonicalVarInfo;
+
+    type RegionKind;
+    type Region;
+
+    type Const;
+    type InternedConst;
+
+    type PlaceElem;
+    type ListPlaceElem;
+
+    type DefPathHash;
+    type AdtDef;
+
+    type SymbolName;
+
+    type Mir;
+    type AllocatedMir;
+    type AllocatedMirSlice;
+
+    type Promoted;
+    type AllocatedPromoted;
+    type AllocatedPromotedSlice;
+
+    type TypeCheckResults;
+    type AllocatedTypeCheckResults;
+    type AllocatedTypeCheckResultsSlice;
+
+    type BorrowCheckResult;
+    type AllocatedBorrowCheckResult;
+    type AllocatedBorrowCheckResultSlice;
+
+    type CodeRegion;
+    type AllocatedCodeRegion;
+    type AllocatedCodeRegionSlice;
+
+    type UnsafetyCheckResult;
+    type AllocatedUnsafetyCheckResult;
+    type AllocatedUnsafetyCheckResultSlice;
+
+    type Span;
+    type AllocatedSpan;
+    type AllocatedSpanSlice;
+
+    type UsedTraitsImports;
+    type AllocatedUsedTraitsImports;
+    type AllocatedUsedTraitsImportsSlice;
+
+    type AsmTemplate;
+    type AllocatedAsmTemplate;
+    type AllocatedAsmTemplateSlice;
+
+    type PredicateSpan;
+    type AllocatedPredicateSpanSlice;
+
+    type Node;
+    type AllocatedNodeSlice;
+
+    type NodeId;
+    type AllocatedNodeIdSlice;
+
+    fn alloc_mir(self, value: Self::Mir) -> Self::AllocatedMir;
+    fn alloc_mir_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::Mir>,
+    ) -> Self::AllocatedMirSlice;
+
+    fn alloc_promoted(self, value: Self::Promoted) -> Self::AllocatedPromoted;
+    fn alloc_promoted_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::Promoted>,
+    ) -> Self::AllocatedPromotedSlice;
+
+    fn alloc_type_check_results(
+        self,
+        value: Self::TypeCheckResults,
+    ) -> Self::AllocatedTypeCheckResults;
+    fn alloc_type_check_results_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::TypeCheckResults>,
+    ) -> Self::AllocatedTypeCheckResultsSlice;
+
+    fn alloc_borrowck_result(
+        self,
+        value: Self::BorrowCheckResult,
+    ) -> Self::AllocatedBorrowCheckResult;
+    fn alloc_borrowck_result_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::BorrowCheckResult>,
+    ) -> Self::AllocatedBorrowCheckResultSlice;
+
+    fn alloc_unsafety_check_result(
+        self,
+        value: Self::UnsafetyCheckResult,
+    ) -> Self::AllocatedUnsafetyCheckResult;
+    fn alloc_unsafety_check_result_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::UnsafetyCheckResult>,
+    ) -> Self::AllocatedUnsafetyCheckResultSlice;
+
+    fn alloc_code_region(self, value: Self::CodeRegion) -> Self::AllocatedCodeRegion;
+    fn alloc_code_region_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::CodeRegion>,
+    ) -> Self::AllocatedCodeRegionSlice;
+
+    fn alloc_span(self, value: Self::Span) -> Self::AllocatedSpan;
+    fn alloc_span_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::Span>,
+    ) -> Self::AllocatedSpanSlice;
+
+    fn alloc_used_trait_imports(
+        self,
+        value: Self::UsedTraitsImports,
+    ) -> Self::AllocatedUsedTraitsImports;
+    fn alloc_used_trait_imports_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::UsedTraitsImports>,
+    ) -> Self::AllocatedUsedTraitsImportsSlice;
+
+    fn alloc_asm_template(self, value: Self::AsmTemplate) -> Self::AllocatedAsmTemplate;
+    fn alloc_asm_template_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::AsmTemplate>,
+    ) -> Self::AllocatedAsmTemplateSlice;
+
+    fn alloc_predicate_span_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::PredicateSpan>,
+    ) -> Self::AllocatedPredicateSpanSlice;
+    fn alloc_node_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::Node>,
+    ) -> Self::AllocatedNodeSlice;
+    fn alloc_node_id_from_iter(
+        self,
+        iter: impl IntoIterator<Item = Self::NodeId>,
+    ) -> Self::AllocatedNodeIdSlice;
+
+    fn get_cached_ty(&self, k: Self::TypeKey) -> Option<Self::Ty>;
+    fn insert_same_cached_ty(&mut self, key: Self::TypeKey, value: Self::Ty);
+    fn insert_cached_ty(&mut self, key: Self::TypeKey, value: Self::Ty) -> Option<Self::Ty>;
+    fn adt_def(self, def_id: Self::DefId) -> Self::AdtDef;
+    fn mk_symbol_name(self, name: &str) -> Self::SymbolName;
+    fn mk_predicate(self, binder: Self::BinderPredicateKind) -> Self::Predicate;
+    fn mk_ty(self, st: Self::TyKind) -> Self::Ty;
+    fn mk_substs<I: InternAs<[Self::GenericArg], Self::ListGenericArg>>(self, iter: I)
+    -> I::Output;
+    fn mk_poly_existential_predicates<
+        I: InternAs<[Self::ExistentialPredicate], Self::ListExistentialPredicate>,
+    >(
+        self,
+        iter: I,
+    ) -> I::Output;
+    fn mk_type_list<I: InternAs<[Self::Ty], Self::ListType>>(self, iter: I) -> I::Output;
+    fn mk_place_elems<I: InternAs<[Self::PlaceElem], Self::ListPlaceElem>>(
+        self,
+        iter: I,
+    ) -> I::Output;
+    fn mk_region(self, kind: Self::RegionKind) -> Self::Region;
+    fn mk_const(self, c: Self::Const) -> Self::InternedConst;
+    fn intern_const_alloc(self, alloc: Self::Allocation) -> Self::InternedAllocation;
+    fn intern_canonical_var_infos(
+        self,
+        ts: &[Self::CanonicalVarInfo],
+    ) -> Self::ListCanonicalVarInfo;
+    fn reserve_alloc_id(self) -> Self::AllocationId;
+    fn set_alloc_id_same_memory(self, id: Self::AllocationId, mem: Self::InternedAllocation);
+    fn create_fn_alloc(self, instance: Self::Instance) -> Self::AllocationId;
+    fn create_static_alloc(self, static_id: Self::DefId) -> Self::AllocationId;
+    fn def_path_hash_to_def_id(self, hash: Self::DefPathHash) -> Option<Self::DefId>;
+
+    // arena methods
+}
+
+pub trait InternAs<T: ?Sized, R> {
+    type Output;
+    fn intern_with<F>(self, f: F) -> Self::Output
+    where
+        F: FnOnce(&T) -> R;
+}
+
+impl<I, T, R, E> InternAs<[T], R> for I
+where
+    E: InternIteratorElement<T, R>,
+    I: Iterator<Item = E>,
+{
+    type Output = E::Output;
+    fn intern_with<F>(self, f: F) -> Self::Output
+    where
+        F: FnOnce(&[T]) -> R,
+    {
+        E::intern_with(self, f)
+    }
+}
+
+pub trait InternIteratorElement<T, R>: Sized {
+    type Output;
+    fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output;
+}
+
+impl<T, R> InternIteratorElement<T, R> for T {
+    type Output = R;
+    fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output {
+        f(&iter.collect::<SmallVec<[_; 8]>>())
+    }
+}
+
+impl<'a, T, R> InternIteratorElement<T, R> for &'a T
+where
+    T: Clone + 'a,
+{
+    type Output = R;
+    fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output {
+        f(&iter.cloned().collect::<SmallVec<[_; 8]>>())
+    }
+}
+
+impl<T, R, E> InternIteratorElement<T, R> for Result<T, E> {
+    type Output = Result<R, E>;
+    fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>(
+        mut iter: I,
+        f: F,
+    ) -> Self::Output {
+        // This code is hot enough that it's worth specializing for the most
+        // common length lists, to avoid the overhead of `SmallVec` creation.
+        // The match arms are in order of frequency. The 1, 2, and 0 cases are
+        // typically hit in ~95% of cases. We assume that if the upper and
+        // lower bounds from `size_hint` agree they are correct.
+        Ok(match iter.size_hint() {
+            (1, Some(1)) => {
+                let t0 = iter.next().unwrap()?;
+                assert!(iter.next().is_none());
+                f(&[t0])
+            }
+            (2, Some(2)) => {
+                let t0 = iter.next().unwrap()?;
+                let t1 = iter.next().unwrap()?;
+                assert!(iter.next().is_none());
+                f(&[t0, t1])
+            }
+            (0, Some(0)) => {
+                assert!(iter.next().is_none());
+                f(&[])
+            }
+            _ => f(&iter.collect::<Result<SmallVec<[_; 8]>, _>>()?),
+        })
+    }
+}
+
 bitflags! {
     /// Flags that we track on types. These flags are propagated upwards
     /// through the type during type construction, so that we can quickly check