diff --git a/chalk-engine/src/forest.rs b/chalk-engine/src/forest.rs
index 463aca513f9..ebe4bc5c884 100644
--- a/chalk-engine/src/forest.rs
+++ b/chalk-engine/src/forest.rs
@@ -113,6 +113,23 @@ pub enum SubstitutionResult<S> {
     Floundered,
 }
 
+impl<S> SubstitutionResult<S> {
+    pub fn as_ref(&self) -> SubstitutionResult<&S> {
+        match self {
+            SubstitutionResult::Definite(subst) => SubstitutionResult::Definite(subst),
+            SubstitutionResult::Ambiguous(subst) => SubstitutionResult::Ambiguous(subst),
+            SubstitutionResult::Floundered => SubstitutionResult::Floundered,
+        }
+    }
+    pub fn map<U, F: FnOnce(S) -> U>(self, f: F) -> SubstitutionResult<U> {
+        match self {
+            SubstitutionResult::Definite(subst) => SubstitutionResult::Definite(f(subst)),
+            SubstitutionResult::Ambiguous(subst) => SubstitutionResult::Ambiguous(f(subst)),
+            SubstitutionResult::Floundered => SubstitutionResult::Floundered,
+        }
+    }
+}
+
 impl<S: Display> Display for SubstitutionResult<S> {
     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
diff --git a/chalk-integration/src/lib.rs b/chalk-integration/src/lib.rs
index 2bb1c8d08a3..3b48a6910b8 100644
--- a/chalk-integration/src/lib.rs
+++ b/chalk-integration/src/lib.rs
@@ -11,6 +11,7 @@ pub mod program;
 pub mod program_environment;
 pub mod query;
 
+use chalk_ir::interner::{ChalkIr, HasInterner};
 pub use chalk_ir::interner::{Identifier, RawId};
 use chalk_ir::Binders;
 
@@ -21,9 +22,16 @@ pub enum TypeSort {
     Opaque,
 }
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Unit;
+
+impl HasInterner for Unit {
+    type Interner = ChalkIr;
+}
+
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub struct TypeKind {
     pub sort: TypeSort,
     pub name: Identifier,
-    pub binders: Binders<()>,
+    pub binders: Binders<Unit>,
 }
diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs
index 02e4d782a3b..14da2404cd5 100644
--- a/chalk-integration/src/lowering.rs
+++ b/chalk-integration/src/lowering.rs
@@ -1,7 +1,7 @@
 use chalk_ir::cast::{Cast, Caster};
-use chalk_ir::interner::ChalkIr;
+use chalk_ir::interner::{ChalkIr, HasInterner};
 use chalk_ir::{
-    self, AssocTypeId, BoundVar, ClausePriority, DebruijnIndex, ImplId, OpaqueTyId,
+    self, AssocTypeId, BoundVar, ClausePriority, DebruijnIndex, ImplId, OpaqueTyId, ParameterKinds,
     QuantifiedWhereClauses, StructId, Substitution, TraitId,
 };
 use chalk_parse::ast::*;
@@ -174,11 +174,16 @@ impl<'k> Env<'k> {
     where
         I: IntoIterator<Item = chalk_ir::ParameterKind<Ident>>,
         I::IntoIter: ExactSizeIterator,
+        T: HasInterner<Interner = ChalkIr>,
         OP: FnOnce(&Self) -> LowerResult<T>,
     {
+        let interner = &ChalkIr;
         let binders: Vec<_> = binders.into_iter().collect();
         let env = self.introduce(binders.iter().cloned())?;
-        Ok(chalk_ir::Binders::new(binders.anonymize(), op(&env)?))
+        Ok(chalk_ir::Binders::new(
+            ParameterKinds::from(interner, binders.anonymize()),
+            op(&env)?,
+        ))
     }
 }
 
@@ -594,10 +599,14 @@ trait LowerWhereClauses {
 
 impl LowerTypeKind for StructDefn {
     fn lower_type_kind(&self) -> LowerResult<TypeKind> {
+        let interner = &ChalkIr;
         Ok(TypeKind {
             sort: TypeSort::Struct,
             name: self.name.str,
-            binders: chalk_ir::Binders::new(self.all_parameters().anonymize(), ()),
+            binders: chalk_ir::Binders::new(
+                ParameterKinds::from(interner, self.all_parameters().anonymize()),
+                crate::Unit,
+            ),
         })
     }
 }
@@ -610,14 +619,15 @@ impl LowerWhereClauses for StructDefn {
 
 impl LowerTypeKind for TraitDefn {
     fn lower_type_kind(&self) -> LowerResult<TypeKind> {
+        let interner = &ChalkIr;
         let binders: Vec<_> = self.parameter_kinds.iter().map(|p| p.lower()).collect();
         Ok(TypeKind {
             sort: TypeSort::Trait,
             name: self.name.str,
             binders: chalk_ir::Binders::new(
                 // for the purposes of the *type*, ignore `Self`:
-                binders.anonymize(),
-                (),
+                ParameterKinds::from(interner, binders.anonymize()),
+                crate::Unit,
             ),
         })
     }
@@ -625,11 +635,15 @@ impl LowerTypeKind for TraitDefn {
 
 impl LowerTypeKind for OpaqueTyDefn {
     fn lower_type_kind(&self) -> LowerResult<TypeKind> {
+        let interner = &ChalkIr;
         let binders: Vec<_> = self.parameter_kinds.iter().map(|p| p.lower()).collect();
         Ok(TypeKind {
             sort: TypeSort::Opaque,
             name: self.identifier.str,
-            binders: chalk_ir::Binders::new(binders.anonymize(), ()),
+            binders: chalk_ir::Binders::new(
+                ParameterKinds::from(interner, binders.anonymize()),
+                crate::Unit,
+            ),
         })
     }
 }
@@ -835,6 +849,7 @@ trait LowerTraitBound {
 
 impl LowerTraitBound for TraitBound {
     fn lower(&self, env: &Env) -> LowerResult<rust_ir::TraitBound<ChalkIr>> {
+        let interner = &ChalkIr;
         let trait_id = env.lookup_trait(self.trait_name)?;
 
         let k = env.trait_kind(trait_id);
@@ -848,15 +863,15 @@ impl LowerTraitBound for TraitBound {
             .map(|a| Ok(a.lower(env)?))
             .collect::<LowerResult<Vec<_>>>()?;
 
-        if parameters.len() != k.binders.len() {
+        if parameters.len() != k.binders.len(interner) {
             Err(RustIrError::IncorrectNumberOfTypeParameters {
                 identifier: self.trait_name,
-                expected: k.binders.len(),
+                expected: k.binders.len(interner),
                 actual: parameters.len(),
             })?;
         }
 
-        for (binder, param) in k.binders.binders.iter().zip(parameters.iter()) {
+        for (binder, param) in k.binders.binders.iter(interner).zip(parameters.iter()) {
             if binder.kind() != param.kind() {
                 Err(RustIrError::IncorrectTraitParameterKind {
                     identifier: self.trait_name,
@@ -1062,10 +1077,10 @@ impl LowerTy for Ty {
             Ty::Id { name } => match env.lookup_type(name)? {
                 TypeLookup::Struct(id) => {
                     let k = env.struct_kind(id);
-                    if k.binders.len() > 0 {
+                    if k.binders.len(interner) > 0 {
                         Err(RustIrError::IncorrectNumberOfTypeParameters {
                             identifier: name,
-                            expected: k.binders.len(),
+                            expected: k.binders.len(interner),
                             actual: 0,
                         })
                     } else {
@@ -1118,10 +1133,10 @@ impl LowerTy for Ty {
                 };
 
                 let k = env.struct_kind(id);
-                if k.binders.len() != args.len() {
+                if k.binders.len(interner) != args.len() {
                     Err(RustIrError::IncorrectNumberOfTypeParameters {
                         identifier: name,
-                        expected: k.binders.len(),
+                        expected: k.binders.len(interner),
                         actual: args.len(),
                     })?;
                 }
@@ -1131,7 +1146,7 @@ impl LowerTy for Ty {
                     args.iter().map(|t| Ok(t.lower(env)?)),
                 )?;
 
-                for (param, arg) in k.binders.binders.iter().zip(args.iter()) {
+                for (param, arg) in k.binders.binders.iter(interner).zip(args.iter()) {
                     if param.kind() != arg.kind() {
                         Err(RustIrError::IncorrectParameterKind {
                             identifier: name,
@@ -1362,14 +1377,16 @@ pub trait LowerGoal<A> {
 
 impl LowerGoal<LoweredProgram> for Goal {
     fn lower(&self, program: &LoweredProgram) -> LowerResult<chalk_ir::Goal<ChalkIr>> {
+        let interner = &ChalkIr;
         let associated_ty_lookups: BTreeMap<_, _> = program
             .associated_ty_data
             .iter()
             .map(|(&associated_ty_id, datum)| {
                 let trait_datum = &program.trait_data[&datum.trait_id];
-                let num_trait_params = trait_datum.binders.len();
-                let num_addl_params = datum.binders.len() - num_trait_params;
-                let addl_parameter_kinds = datum.binders.binders[..num_addl_params].to_owned();
+                let num_trait_params = trait_datum.binders.len(interner);
+                let num_addl_params = datum.binders.len(interner) - num_trait_params;
+                let addl_parameter_kinds =
+                    datum.binders.binders.as_slice(interner)[..num_addl_params].to_owned();
                 let lookup = AssociatedTyLookup {
                     id: associated_ty_id,
                     addl_parameter_kinds,
diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs
index 55d62709acd..95c54a829b9 100644
--- a/chalk-integration/src/program.rs
+++ b/chalk-integration/src/program.rs
@@ -188,6 +188,33 @@ impl tls::DebugContext for Program {
         write!(fmt, "{:?}", parameter.data(interner).inner_debug())
     }
 
+    fn debug_parameter_kinds(
+        &self,
+        parameter_kinds: &chalk_ir::ParameterKinds<ChalkIr>,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Result<(), fmt::Error> {
+        let interner = self.interner();
+        write!(fmt, "{:?}", parameter_kinds.as_slice(interner))
+    }
+
+    fn debug_parameter_kinds_with_angles(
+        &self,
+        parameter_kinds: &chalk_ir::ParameterKinds<ChalkIr>,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Result<(), fmt::Error> {
+        let interner = self.interner();
+        write!(fmt, "{:?}", parameter_kinds.inner_debug(interner))
+    }
+
+    fn debug_canonical_var_kinds(
+        &self,
+        parameter_kinds: &chalk_ir::CanonicalVarKinds<ChalkIr>,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Result<(), fmt::Error> {
+        let interner = self.interner();
+        write!(fmt, "{:?}", parameter_kinds.as_slice(interner))
+    }
+
     fn debug_goal(
         &self,
         goal: &Goal<ChalkIr>,
diff --git a/chalk-ir/src/cast.rs b/chalk-ir/src/cast.rs
index 052ead6b6e2..7d984cf92fb 100644
--- a/chalk-ir/src/cast.rs
+++ b/chalk-ir/src/cast.rs
@@ -82,6 +82,8 @@ reflexive_impl!(for(I: Interner) Goal<I>);
 reflexive_impl!(for(I: Interner) WhereClause<I>);
 reflexive_impl!(for(I: Interner) ProgramClause<I>);
 reflexive_impl!(for(I: Interner) QuantifiedWhereClause<I>);
+reflexive_impl!(for(I: Interner) ParameterKinds<I>);
+reflexive_impl!(for(I: Interner) CanonicalVarKinds<I>);
 
 impl<I: Interner> CastTo<WhereClause<I>> for TraitRef<I> {
     fn cast_to(self, _interner: &I) -> WhereClause<I> {
@@ -138,7 +140,7 @@ impl<I: Interner> CastTo<Goal<I>> for EqGoal<I> {
     }
 }
 
-impl<T: CastTo<Goal<I>>, I: Interner> CastTo<Goal<I>> for Binders<T> {
+impl<I: Interner, T: HasInterner<Interner = I> + CastTo<Goal<I>>> CastTo<Goal<I>> for Binders<T> {
     fn cast_to(self, interner: &I) -> Goal<I> {
         GoalData::Quantified(
             QuantifierKind::ForAll,
@@ -193,10 +195,10 @@ where
     }
 }
 
-impl<T, I> CastTo<ProgramClause<I>> for Binders<T>
+impl<I, T> CastTo<ProgramClause<I>> for Binders<T>
 where
-    T: CastTo<DomainGoal<I>>,
     I: Interner,
+    T: HasInterner<Interner = I> + CastTo<DomainGoal<I>>,
 {
     fn cast_to(self, interner: &I) -> ProgramClause<I> {
         ProgramClauseData::ForAll(self.map(|bound| ProgramClauseImplication {
@@ -268,16 +270,16 @@ where
 impl<T, U> CastTo<Canonical<U>> for Canonical<T>
 where
     T: CastTo<U> + HasInterner,
-    U: HasInterner,
+    U: HasInterner<Interner = T::Interner>,
 {
-    fn cast_to(self, interner: &U::Interner) -> Canonical<U> {
+    fn cast_to(self, interner: &T::Interner) -> Canonical<U> {
         // Subtle point: It should be ok to re-use the binders here,
         // because `cast()` never introduces new inference variables,
         // nor changes the "substance" of the type we are working
         // with. It just introduces new wrapper types.
         Canonical {
             value: self.value.cast(interner),
-            binders: self.binders,
+            binders: self.binders.cast(interner),
         }
     }
 }
diff --git a/chalk-ir/src/could_match.rs b/chalk-ir/src/could_match.rs
index 32ca416f970..1febdbf1ba6 100644
--- a/chalk-ir/src/could_match.rs
+++ b/chalk-ir/src/could_match.rs
@@ -50,7 +50,7 @@ where
 
             fn zip_binders<T>(&mut self, a: &Binders<T>, b: &Binders<T>) -> Fallible<()>
             where
-                T: Zip<I>,
+                T: HasInterner + Zip<I>,
             {
                 Zip::zip_with(self, &a.value, &b.value)
             }
diff --git a/chalk-ir/src/debug.rs b/chalk-ir/src/debug.rs
index bcb6c7630b7..acccb5a7da0 100644
--- a/chalk-ir/src/debug.rs
+++ b/chalk-ir/src/debug.rs
@@ -210,22 +210,63 @@ impl<I: Interner> Debug for LifetimeData<I> {
     }
 }
 
+impl<I: Interner> ParameterKinds<I> {
+    fn debug(&self) -> ParameterKindsDebug<'_, I> {
+        ParameterKindsDebug(self)
+    }
+
+    pub fn inner_debug<'a>(&'a self, interner: &'a I) -> ParameterKindsInnerDebug<'a, I> {
+        ParameterKindsInnerDebug {
+            parameter_kinds: self,
+            interner,
+        }
+    }
+}
+
+struct ParameterKindsDebug<'a, I: Interner>(&'a ParameterKinds<I>);
+
+impl<'a, I: Interner> Debug for ParameterKindsDebug<'a, I> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> {
+        I::debug_parameter_kinds_with_angles(self.0, fmt)
+            .unwrap_or_else(|| write!(fmt, "{:?}", self.0.interned))
+    }
+}
+
+pub struct ParameterKindsInnerDebug<'a, I: Interner> {
+    parameter_kinds: &'a ParameterKinds<I>,
+    interner: &'a I,
+}
+
+impl<'a, I: Interner> Debug for ParameterKindsInnerDebug<'a, I> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> {
+        // NB: We print parameter kinds as a list delimited by `<>`,
+        // like `<K1, K2, ..>`. This is because parameter kind lists
+        // are always associated with binders like `forall<type> {
+        // ... }`.
+        write!(fmt, "<")?;
+        for (index, binder) in self.parameter_kinds.iter(self.interner).enumerate() {
+            if index > 0 {
+                write!(fmt, ", ")?;
+            }
+            match *binder {
+                ParameterKind::Ty(()) => write!(fmt, "type")?,
+                ParameterKind::Lifetime(()) => write!(fmt, "lifetime")?,
+            }
+        }
+        write!(fmt, ">")
+    }
+}
+
 impl<I: Interner> Debug for GoalData<I> {
     fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> {
         match self {
-            GoalData::Quantified(qkind, ref subgoal) => {
-                write!(fmt, "{:?}<", qkind)?;
-                for (index, binder) in subgoal.binders.iter().enumerate() {
-                    if index > 0 {
-                        write!(fmt, ", ")?;
-                    }
-                    match *binder {
-                        ParameterKind::Ty(()) => write!(fmt, "type")?,
-                        ParameterKind::Lifetime(()) => write!(fmt, "lifetime")?,
-                    }
-                }
-                write!(fmt, "> {{ {:?} }}", subgoal.value)
-            }
+            GoalData::Quantified(qkind, ref subgoal) => write!(
+                fmt,
+                "{:?}{:?} {{ {:?} }}",
+                qkind,
+                subgoal.binders.debug(),
+                subgoal.value
+            ),
             GoalData::Implies(ref wc, ref g) => write!(fmt, "if ({:?}) {{ {:?} }}", wc, g),
             GoalData::All(ref goals) => write!(fmt, "all{:?}", goals),
             GoalData::Not(ref g) => write!(fmt, "not {{ {:?} }}", g),
@@ -593,30 +634,13 @@ impl<I: Interner> Debug for EqGoal<I> {
     }
 }
 
-impl<T: Debug> Debug for Binders<T> {
+impl<T: HasInterner + Debug> Debug for Binders<T> {
     fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> {
         let Binders {
             ref binders,
             ref value,
         } = *self;
-
-        // NB: We always print the `for<>`, even if it is empty,
-        // because it may affect the debruijn indices of things
-        // contained within. For example, `for<> { ^1.0 }` is very
-        // different from `^1.0` in terms of what variable is being
-        // referenced.
-
-        write!(fmt, "for<")?;
-        for (index, binder) in binders.iter().enumerate() {
-            if index > 0 {
-                write!(fmt, ", ")?;
-            }
-            match *binder {
-                ParameterKind::Ty(()) => write!(fmt, "type")?,
-                ParameterKind::Lifetime(()) => write!(fmt, "lifetime")?,
-            }
-        }
-        write!(fmt, "> ")?;
+        write!(fmt, "for{:?} ", binders.debug())?;
         Debug::fmt(value, fmt)
     }
 }
@@ -636,10 +660,32 @@ impl<I: Interner> Debug for Environment<I> {
     }
 }
 
-impl<T: Display> Display for Canonical<T> {
-    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
-        let Canonical { binders, value } = self;
+impl<I: Interner> Debug for CanonicalVarKinds<I> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> {
+        I::debug_canonical_var_kinds(self, fmt)
+            .unwrap_or_else(|| write!(fmt, "{:?}", self.interned))
+    }
+}
 
+impl<T: HasInterner + Display> Canonical<T> {
+    pub fn display<'a>(&'a self, interner: &'a T::Interner) -> CanonicalDisplay<'a, T> {
+        CanonicalDisplay {
+            canonical: self,
+            interner,
+        }
+    }
+}
+
+pub struct CanonicalDisplay<'a, T: HasInterner> {
+    canonical: &'a Canonical<T>,
+    interner: &'a T::Interner,
+}
+
+impl<'a, T: HasInterner + Display> Display for CanonicalDisplay<'a, T> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
+        let Canonical { binders, value } = self.canonical;
+        let interner = self.interner;
+        let binders = binders.as_slice(interner);
         if binders.is_empty() {
             // Ordinarily, we try to print all binder levels, if they
             // are empty, but we can skip in this *particular* case
diff --git a/chalk-ir/src/fold/binder_impls.rs b/chalk-ir/src/fold/binder_impls.rs
index eee1f6045f3..306f95c2500 100644
--- a/chalk-ir/src/fold/binder_impls.rs
+++ b/chalk-ir/src/fold/binder_impls.rs
@@ -30,7 +30,8 @@ impl<I: Interner, TI: TargetInterner<I>> Fold<I, TI> for Fn<I> {
 
 impl<T, I: Interner, TI: TargetInterner<I>> Fold<I, TI> for Binders<T>
 where
-    T: Fold<I, TI>,
+    T: HasInterner<Interner = I> + Fold<I, TI>,
+    <T as Fold<I, TI>>::Result: HasInterner<Interner = TI>,
     I: Interner,
 {
     type Result = Binders<T::Result>;
@@ -48,17 +49,18 @@ where
             value: self_value,
         } = self;
         let value = self_value.fold_with(folder, outer_binder.shifted_in())?;
-        Ok(Binders {
-            binders: self_binders.clone(),
-            value: value,
-        })
+        let binders = ParameterKinds {
+            interned: TI::transfer_parameter_kinds(self_binders.interned().clone()),
+        };
+        Ok(Binders::new(binders, value))
     }
 }
 
-impl<T, I, TI> Fold<I, TI> for Canonical<T>
+impl<I, T, TI> Fold<I, TI> for Canonical<T>
 where
-    T: Fold<I, TI>,
     I: Interner,
+    T: HasInterner<Interner = I> + Fold<I, TI>,
+    <T as Fold<I, TI>>::Result: HasInterner<Interner = TI>,
     TI: TargetInterner<I>,
 {
     type Result = Canonical<T::Result>;
@@ -76,8 +78,11 @@ where
             value: self_value,
         } = self;
         let value = self_value.fold_with(folder, outer_binder.shifted_in())?;
+        let binders = CanonicalVarKinds {
+            interned: TI::transfer_canonical_var_kinds(self_binders.interned().clone()),
+        };
         Ok(Canonical {
-            binders: self_binders.clone(),
+            binders: binders,
             value: value,
         })
     }
diff --git a/chalk-ir/src/interner.rs b/chalk-ir/src/interner.rs
index 5bc210fb48b..e112cbb88ab 100644
--- a/chalk-ir/src/interner.rs
+++ b/chalk-ir/src/interner.rs
@@ -1,6 +1,7 @@
 use crate::AliasTy;
 use crate::ApplicationTy;
 use crate::AssocTypeId;
+use crate::CanonicalVarKinds;
 use crate::Goal;
 use crate::GoalData;
 use crate::Goals;
@@ -10,6 +11,8 @@ use crate::OpaqueTy;
 use crate::OpaqueTyId;
 use crate::Parameter;
 use crate::ParameterData;
+use crate::ParameterKind;
+use crate::ParameterKinds;
 use crate::ProgramClause;
 use crate::ProgramClauseData;
 use crate::ProgramClauseImplication;
@@ -23,6 +26,7 @@ use crate::Substitution;
 use crate::TraitId;
 use crate::Ty;
 use crate::TyData;
+use crate::UniverseIndex;
 use chalk_engine::context::Context;
 use chalk_engine::ExClause;
 use std::fmt::{self, Debug};
@@ -126,6 +130,23 @@ pub trait Interner: Debug + Copy + Eq + Ord + Hash {
     /// and can be converted back to its underlying data via `quantified_where_clauses_data`.
     type InternedQuantifiedWhereClauses: Debug + Clone + Eq + Hash;
 
+    /// "Interned" representation of a list of parameter kind.  
+    /// In normal user code, `Self::InternedParameterKinds` is not referenced.
+    /// Instead, we refer to `ParameterKinds<Self>`, which wraps this type.
+    ///
+    /// An `InternedParameterKinds` is created by `intern_parameter_kinds`
+    /// and can be converted back to its underlying data via `parameter_kinds_data`.
+    type InternedParameterKinds: Debug + Clone + Eq + Hash;
+
+    /// "Interned" representation of a list of parameter kind with universe index.  
+    /// In normal user code, `Self::InternedCanonicalVarKinds` is not referenced.
+    /// Instead, we refer to `CanonicalVarKinds<Self>`, which wraps this type.
+    ///
+    /// An `InternedCanonicalVarKinds` is created by
+    /// `intern_canonical_var_kinds` and can be converted back
+    /// to its underlying data via `canonical_var_kinds_data`.
+    type InternedCanonicalVarKinds: Debug + Clone + Eq + Hash;
+
     /// The core "id" type used for struct-ids and the like.
     type DefId: Debug + Copy + Eq + Ord + Hash;
 
@@ -263,6 +284,51 @@ pub trait Interner: Debug + Copy + Eq + Ord + Hash {
         None
     }
 
+    /// Prints the debug representation of a parameter kinds list. To get good
+    /// results, this requires inspecting TLS, and is difficult to
+    /// code without reference to a specific interner (and hence
+    /// fully known types).
+    ///
+    /// Returns `None` to fallback to the default debug output (e.g.,
+    /// if no info about current program is available from TLS).
+    #[allow(unused_variables)]
+    fn debug_parameter_kinds(
+        parameter_kinds: &ParameterKinds<Self>,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Option<fmt::Result> {
+        None
+    }
+
+    /// Prints the debug representation of a parameter kinds list, with angle brackets.
+    /// To get good results, this requires inspecting TLS, and is difficult to
+    /// code without reference to a specific interner (and hence
+    /// fully known types).
+    ///
+    /// Returns `None` to fallback to the default debug output (e.g.,
+    /// if no info about current program is available from TLS).
+    #[allow(unused_variables)]
+    fn debug_parameter_kinds_with_angles(
+        parameter_kinds: &ParameterKinds<Self>,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Option<fmt::Result> {
+        None
+    }
+
+    /// Prints the debug representation of an parameter kinds list with universe index.
+    /// To get good results, this requires inspecting TLS, and is difficult to
+    /// code without reference to a specific interner (and hence
+    /// fully known types).
+    ///
+    /// Returns `None` to fallback to the default debug output (e.g.,
+    /// if no info about current program is available from TLS).
+    #[allow(unused_variables)]
+    fn debug_canonical_var_kinds(
+        canonical_var_kinds: &CanonicalVarKinds<Self>,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Option<fmt::Result> {
+        None
+    }
+
     /// Prints the debug representation of an goal. To get good
     /// results, this requires inspecting TLS, and is difficult to
     /// code without reference to a specific interner (and hence
@@ -493,16 +559,68 @@ pub trait Interner: Debug + Copy + Eq + Ord + Hash {
         &self,
         clauses: &'a Self::InternedQuantifiedWhereClauses,
     ) -> &'a [QuantifiedWhereClause<Self>];
+
+    /// Create an "interned" parameter kinds from `data`. This is not
+    /// normally invoked directly; instead, you invoke
+    /// `ParameterKinds::from` (which will ultimately call this
+    /// method).
+    fn intern_parameter_kinds(
+        &self,
+        data: impl IntoIterator<Item = ParameterKind<()>>,
+    ) -> Self::InternedParameterKinds;
+
+    /// Lookup the slice of `ParameterKind` that was interned to
+    /// create a `ParameterKinds`.
+    fn parameter_kinds_data<'a>(
+        &self,
+        parameter_kinds: &'a Self::InternedParameterKinds,
+    ) -> &'a [ParameterKind<()>];
+
+    /// Create an "interned" parameter kinds with universe index from `data`. This is not
+    /// normally invoked directly; instead, you invoke
+    /// `CanonicalVarKinds::from` (which will ultimately call this
+    /// method).
+    fn intern_canonical_var_kinds(
+        &self,
+        data: impl IntoIterator<Item = ParameterKind<UniverseIndex>>,
+    ) -> Self::InternedCanonicalVarKinds;
+
+    /// Lookup the slice of `ParameterKind` that was interned to
+    /// create a `ParameterKinds`.
+    fn canonical_var_kinds_data<'a>(
+        &self,
+        canonical_var_kinds: &'a Self::InternedCanonicalVarKinds,
+    ) -> &'a [ParameterKind<UniverseIndex>];
 }
 
 pub trait TargetInterner<I: Interner>: Interner {
     fn transfer_def_id(def_id: I::DefId) -> Self::DefId;
+
+    fn transfer_parameter_kinds(
+        parameter_kinds: I::InternedParameterKinds,
+    ) -> Self::InternedParameterKinds;
+
+    fn transfer_canonical_var_kinds(
+        parameter_kinds: I::InternedCanonicalVarKinds,
+    ) -> Self::InternedCanonicalVarKinds;
 }
 
 impl<I: Interner> TargetInterner<I> for I {
     fn transfer_def_id(def_id: I::DefId) -> Self::DefId {
         def_id
     }
+
+    fn transfer_parameter_kinds(
+        parameter_kinds: I::InternedParameterKinds,
+    ) -> Self::InternedParameterKinds {
+        parameter_kinds
+    }
+
+    fn transfer_canonical_var_kinds(
+        parameter_kinds: I::InternedCanonicalVarKinds,
+    ) -> Self::InternedCanonicalVarKinds {
+        parameter_kinds
+    }
 }
 
 /// Implemented by types that have an associated interner (which
@@ -550,6 +668,8 @@ mod default {
         type InternedProgramClause = ProgramClauseData<ChalkIr>;
         type InternedProgramClauses = Vec<ProgramClause<ChalkIr>>;
         type InternedQuantifiedWhereClauses = Vec<QuantifiedWhereClause<ChalkIr>>;
+        type InternedParameterKinds = Vec<ParameterKind<()>>;
+        type InternedCanonicalVarKinds = Vec<ParameterKind<UniverseIndex>>;
         type DefId = RawId;
         type Identifier = Identifier;
 
@@ -621,6 +741,33 @@ mod default {
             tls::with_current_program(|prog| Some(prog?.debug_parameter(parameter, fmt)))
         }
 
+        fn debug_parameter_kinds(
+            parameter_kinds: &ParameterKinds<Self>,
+            fmt: &mut fmt::Formatter<'_>,
+        ) -> Option<fmt::Result> {
+            tls::with_current_program(|prog| {
+                Some(prog?.debug_parameter_kinds(parameter_kinds, fmt))
+            })
+        }
+
+        fn debug_parameter_kinds_with_angles(
+            parameter_kinds: &ParameterKinds<Self>,
+            fmt: &mut fmt::Formatter<'_>,
+        ) -> Option<fmt::Result> {
+            tls::with_current_program(|prog| {
+                Some(prog?.debug_parameter_kinds_with_angles(parameter_kinds, fmt))
+            })
+        }
+
+        fn debug_canonical_var_kinds(
+            canonical_var_kinds: &CanonicalVarKinds<Self>,
+            fmt: &mut fmt::Formatter<'_>,
+        ) -> Option<fmt::Result> {
+            tls::with_current_program(|prog| {
+                Some(prog?.debug_canonical_var_kinds(canonical_var_kinds, fmt))
+            })
+        }
+
         fn debug_goal(goal: &Goal<ChalkIr>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
             tls::with_current_program(|prog| Some(prog?.debug_goal(goal, fmt)))
         }
@@ -786,6 +933,30 @@ mod default {
         ) -> &'a [QuantifiedWhereClause<Self>] {
             clauses
         }
+        fn intern_parameter_kinds(
+            &self,
+            data: impl IntoIterator<Item = ParameterKind<()>>,
+        ) -> Self::InternedParameterKinds {
+            data.into_iter().collect()
+        }
+        fn parameter_kinds_data<'a>(
+            &self,
+            parameter_kinds: &'a Self::InternedParameterKinds,
+        ) -> &'a [ParameterKind<()>] {
+            parameter_kinds
+        }
+        fn intern_canonical_var_kinds(
+            &self,
+            data: impl IntoIterator<Item = ParameterKind<UniverseIndex>>,
+        ) -> Self::InternedCanonicalVarKinds {
+            data.into_iter().collect()
+        }
+        fn canonical_var_kinds_data<'a>(
+            &self,
+            canonical_var_kinds: &'a Self::InternedCanonicalVarKinds,
+        ) -> &'a [ParameterKind<UniverseIndex>] {
+            canonical_var_kinds
+        }
     }
 
     impl HasInterner for ChalkIr {
@@ -826,6 +997,10 @@ where
     type Interner = I;
 }
 
+impl<'a, T: HasInterner> HasInterner for std::slice::Iter<'a, T> {
+    type Interner = T::Interner;
+}
+
 impl<C: HasInterner + Context> HasInterner for ExClause<C> {
     type Interner = <C as HasInterner>::Interner;
 }
diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs
index 042ace29dc0..ddc3e2be11b 100644
--- a/chalk-ir/src/lib.rs
+++ b/chalk-ir/src/lib.rs
@@ -1193,9 +1193,9 @@ impl<I: Interner> HasInterner for AliasEq<I> {
 ///
 /// (IOW, we use deBruijn indices, where binders are introduced in reverse order
 /// of `self.binders`.)
-#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct Binders<T> {
-    pub binders: Vec<ParameterKind<()>>,
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct Binders<T: HasInterner> {
+    pub binders: ParameterKinds<T::Interner>,
     value: T,
 }
 
@@ -1203,8 +1203,8 @@ impl<T: HasInterner> HasInterner for Binders<T> {
     type Interner = T::Interner;
 }
 
-impl<T> Binders<T> {
-    pub fn new(binders: Vec<ParameterKind<()>>, value: T) -> Self {
+impl<T: HasInterner> Binders<T> {
+    pub fn new(binders: ParameterKinds<T::Interner>, value: T) -> Self {
         Self { binders, value }
     }
 
@@ -1236,6 +1236,7 @@ impl<T> Binders<T> {
     pub fn map<U, OP>(self, op: OP) -> Binders<U>
     where
         OP: FnOnce(T) -> U,
+        U: HasInterner<Interner = T::Interner>,
     {
         let value = op(self.value);
         Binders {
@@ -1247,6 +1248,7 @@ impl<T> Binders<T> {
     pub fn map_ref<'a, U, OP>(&'a self, op: OP) -> Binders<U>
     where
         OP: FnOnce(&'a T) -> U,
+        U: HasInterner<Interner = T::Interner>,
     {
         self.as_ref().map(op)
     }
@@ -1257,26 +1259,23 @@ impl<T> Binders<T> {
     /// from the closure to account for the binder that will be added.
     ///
     /// XXX FIXME -- this is potentially a pretty footgun-y function.
-    pub fn with_fresh_type_var<I>(interner: &I, op: impl FnOnce(Ty<I>) -> T) -> Binders<T>
-    where
-        I: Interner,
-    {
+    pub fn with_fresh_type_var(
+        interner: &T::Interner,
+        op: impl FnOnce(Ty<T::Interner>) -> T,
+    ) -> Binders<T> {
         // The new variable is at the front and everything afterwards is shifted up by 1
-        let new_var =
-            TyData::<I>::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(interner);
+        let new_var = TyData::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(interner);
         let value = op(new_var);
-        Binders {
-            binders: iter::once(ParameterKind::Ty(())).collect(),
-            value,
-        }
+        let binders = ParameterKinds::from(interner, iter::once(ParameterKind::Ty(())));
+        Binders { binders, value }
     }
 
-    pub fn len(&self) -> usize {
-        self.binders.len()
+    pub fn len(&self, interner: &T::Interner) -> usize {
+        self.binders.len(interner)
     }
 }
 
-impl<T> From<Binders<T>> for (Vec<ParameterKind<()>>, T) {
+impl<T: HasInterner> From<Binders<T>> for (ParameterKinds<T::Interner>, T) {
     fn from(binders: Binders<T>) -> Self {
         (binders.binders, binders.value)
     }
@@ -1297,7 +1296,7 @@ where
         parameters: &(impl AsParameters<I> + ?Sized),
     ) -> T::Result {
         let parameters = parameters.as_parameters(interner);
-        assert_eq!(self.binders.len(), parameters.len());
+        assert_eq!(self.binders.len(interner), parameters.len());
         Subst::apply(interner, parameters, &self.value)
     }
 }
@@ -1308,6 +1307,7 @@ impl<'a, V> IntoIterator for &'a Binders<V>
 where
     V: HasInterner,
     &'a V: IntoIterator,
+    <&'a V as IntoIterator>::Item: HasInterner<Interner = V::Interner>,
 {
     type Item = Binders<<&'a V as IntoIterator>::Item>;
     type IntoIter = BindersIntoIterator<&'a V>;
@@ -1319,8 +1319,12 @@ where
 
 /// Allows iterating over a Binders<Vec<T>>, for instance.
 /// Each element will include the same set of parameter bounds.
-impl<V: IntoIterator> IntoIterator for Binders<V> {
-    type Item = Binders<<V as IntoIterator>::Item>;
+impl<V, U> IntoIterator for Binders<V>
+where
+    V: HasInterner + IntoIterator<Item = U>,
+    U: HasInterner<Interner = V::Interner>,
+{
+    type Item = Binders<U>;
     type IntoIter = BindersIntoIterator<V>;
 
     fn into_iter(self) -> Self::IntoIter {
@@ -1331,18 +1335,21 @@ impl<V: IntoIterator> IntoIterator for Binders<V> {
     }
 }
 
-pub struct BindersIntoIterator<V: IntoIterator> {
+pub struct BindersIntoIterator<V: HasInterner + IntoIterator> {
     iter: <V as IntoIterator>::IntoIter,
-    binders: Vec<ParameterKind<()>>,
+    binders: ParameterKinds<V::Interner>,
 }
 
-impl<V: IntoIterator> Iterator for BindersIntoIterator<V> {
+impl<V> Iterator for BindersIntoIterator<V>
+where
+    V: HasInterner + IntoIterator,
+    <V as IntoIterator>::Item: HasInterner<Interner = V::Interner>,
+{
     type Item = Binders<<V as IntoIterator>::Item>;
     fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next().map(|v| Binders {
-            binders: self.binders.clone(),
-            value: v,
-        })
+        self.iter
+            .next()
+            .map(|v| Binders::new(self.binders.clone(), v))
     }
 }
 
@@ -1490,21 +1497,118 @@ impl<I: Interner> ProgramClauses<I> {
     }
 }
 
+#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, HasInterner)]
+pub struct ParameterKinds<I: Interner> {
+    interned: I::InternedParameterKinds,
+}
+
+impl<I: Interner> ParameterKinds<I> {
+    pub fn new(interner: &I) -> Self {
+        Self::from(interner, None::<ParameterKind<()>>)
+    }
+
+    pub fn interned(&self) -> &I::InternedParameterKinds {
+        &self.interned
+    }
+
+    pub fn from(
+        interner: &I,
+        parameter_kinds: impl IntoIterator<Item = ParameterKind<()>>,
+    ) -> Self {
+        ParameterKinds {
+            interned: I::intern_parameter_kinds(interner, parameter_kinds.into_iter()),
+        }
+    }
+
+    pub fn from_fallible<E>(
+        interner: &I,
+        parameter_kinds: impl IntoIterator<Item = Result<ParameterKind<()>, E>>,
+    ) -> Result<Self, E> {
+        let parameter_kinds = parameter_kinds
+            .into_iter()
+            .collect::<Result<Vec<ParameterKind<()>>, _>>()?;
+        Ok(Self::from(interner, parameter_kinds))
+    }
+
+    pub fn iter(&self, interner: &I) -> std::slice::Iter<'_, ParameterKind<()>> {
+        self.as_slice(interner).iter()
+    }
+
+    pub fn is_empty(&self, interner: &I) -> bool {
+        self.as_slice(interner).is_empty()
+    }
+
+    pub fn len(&self, interner: &I) -> usize {
+        self.as_slice(interner).len()
+    }
+
+    pub fn as_slice(&self, interner: &I) -> &[ParameterKind<()>] {
+        interner.parameter_kinds_data(&self.interned)
+    }
+}
+
+#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, HasInterner)]
+pub struct CanonicalVarKinds<I: Interner> {
+    interned: I::InternedCanonicalVarKinds,
+}
+
+impl<I: Interner> CanonicalVarKinds<I> {
+    pub fn new(interner: &I) -> Self {
+        Self::from(interner, None::<ParameterKind<UniverseIndex>>)
+    }
+
+    pub fn interned(&self) -> &I::InternedCanonicalVarKinds {
+        &self.interned
+    }
+
+    pub fn from(
+        interner: &I,
+        parameter_kinds: impl IntoIterator<Item = ParameterKind<UniverseIndex>>,
+    ) -> Self {
+        CanonicalVarKinds {
+            interned: I::intern_canonical_var_kinds(interner, parameter_kinds.into_iter()),
+        }
+    }
+
+    pub fn from_fallible<E>(
+        interner: &I,
+        parameter_kinds: impl IntoIterator<Item = Result<ParameterKind<UniverseIndex>, E>>,
+    ) -> Result<Self, E> {
+        let parameter_kinds = parameter_kinds
+            .into_iter()
+            .collect::<Result<Vec<ParameterKind<UniverseIndex>>, _>>()?;
+        Ok(Self::from(interner, parameter_kinds))
+    }
+
+    pub fn iter(&self, interner: &I) -> std::slice::Iter<'_, ParameterKind<UniverseIndex>> {
+        self.as_slice(interner).iter()
+    }
+
+    pub fn is_empty(&self, interner: &I) -> bool {
+        self.as_slice(interner).is_empty()
+    }
+
+    pub fn len(&self, interner: &I) -> usize {
+        self.as_slice(interner).len()
+    }
+
+    pub fn as_slice(&self, interner: &I) -> &[ParameterKind<UniverseIndex>] {
+        interner.canonical_var_kinds_data(&self.interned)
+    }
+}
+
 /// Wraps a "canonicalized item". Items are canonicalized as follows:
 ///
 /// All unresolved existential variables are "renumbered" according to their
 /// first appearance; the kind/universe of the variable is recorded in the
 /// `binders` field.
-#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct Canonical<T> {
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Canonical<T: HasInterner> {
     pub value: T,
-    pub binders: Vec<ParameterKind<UniverseIndex>>,
+    pub binders: CanonicalVarKinds<T::Interner>,
 }
 
-impl<T> HasInterner for Canonical<T>
-where
-    T: HasInterner,
-{
+impl<T: HasInterner> HasInterner for Canonical<T> {
     type Interner = T::Interner;
 }
 
@@ -1514,32 +1618,32 @@ where
 /// distinctions.
 ///
 /// To produce one of these values, use the `u_canonicalize` method.
-#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct UCanonical<T> {
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct UCanonical<T: HasInterner> {
     pub canonical: Canonical<T>,
     pub universes: usize,
 }
 
-impl<T> UCanonical<T> {
-    pub fn is_trivial_substitution<I: Interner>(
+impl<T: HasInterner> UCanonical<T> {
+    pub fn is_trivial_substitution(
         &self,
-        interner: &I,
-        canonical_subst: &Canonical<AnswerSubst<I>>,
+        interner: &T::Interner,
+        canonical_subst: &Canonical<AnswerSubst<T::Interner>>,
     ) -> bool {
         let subst = &canonical_subst.value.subst;
         assert_eq!(
-            self.canonical.binders.len(),
+            self.canonical.binders.len(interner),
             subst.parameters(interner).len()
         );
         subst.is_identity_subst(interner)
     }
 
-    pub fn trivial_substitution<I: Interner>(&self, interner: &I) -> Substitution<I> {
+    pub fn trivial_substitution(&self, interner: &T::Interner) -> Substitution<T::Interner> {
         let binders = &self.canonical.binders;
         Substitution::from(
             interner,
             binders
-                .iter()
+                .iter(interner)
                 .enumerate()
                 .map(|(index, pk)| {
                     let bound_var = BoundVar::new(DebruijnIndex::INNERMOST, index);
@@ -1634,16 +1738,9 @@ impl<I: Interner> Goal<I> {
         self,
         interner: &I,
         kind: QuantifierKind,
-        binders: Vec<ParameterKind<()>>,
+        binders: ParameterKinds<I>,
     ) -> Goal<I> {
-        GoalData::Quantified(
-            kind,
-            Binders {
-                value: self,
-                binders,
-            },
-        )
-        .intern(interner)
+        GoalData::Quantified(kind, Binders::new(binders, self)).intern(interner)
     }
 
     /// Takes a goal `G` and turns it into `not { G }`
diff --git a/chalk-ir/src/tls.rs b/chalk-ir/src/tls.rs
index 40cb40b6ee2..29b632ab6d0 100644
--- a/chalk-ir/src/tls.rs
+++ b/chalk-ir/src/tls.rs
@@ -1,8 +1,9 @@
 use crate::interner::ChalkIr;
 use crate::{
-    debug::SeparatorTraitRef, AliasTy, ApplicationTy, AssocTypeId, Goal, Goals, Lifetime, OpaqueTy,
-    OpaqueTyId, Parameter, ProgramClause, ProgramClauseImplication, ProgramClauses, ProjectionTy,
-    QuantifiedWhereClauses, StructId, Substitution, TraitId, Ty,
+    debug::SeparatorTraitRef, AliasTy, ApplicationTy, AssocTypeId, CanonicalVarKinds, Goal, Goals,
+    Lifetime, OpaqueTy, OpaqueTyId, Parameter, ParameterKinds, ProgramClause,
+    ProgramClauseImplication, ProgramClauses, ProjectionTy, QuantifiedWhereClauses, StructId,
+    Substitution, TraitId, Ty,
 };
 use std::cell::RefCell;
 use std::fmt;
@@ -69,6 +70,24 @@ pub trait DebugContext {
         fmt: &mut fmt::Formatter<'_>,
     ) -> Result<(), fmt::Error>;
 
+    fn debug_parameter_kinds(
+        &self,
+        parameter_kinds: &ParameterKinds<ChalkIr>,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Result<(), fmt::Error>;
+
+    fn debug_parameter_kinds_with_angles(
+        &self,
+        parameter_kinds: &ParameterKinds<ChalkIr>,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Result<(), fmt::Error>;
+
+    fn debug_canonical_var_kinds(
+        &self,
+        parameter_kinds: &CanonicalVarKinds<ChalkIr>,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Result<(), fmt::Error>;
+
     fn debug_goal(
         &self,
         goal: &Goal<ChalkIr>,
diff --git a/chalk-ir/src/visit/binder_impls.rs b/chalk-ir/src/visit/binder_impls.rs
index 712bf11b8b4..58ab0871db6 100644
--- a/chalk-ir/src/visit/binder_impls.rs
+++ b/chalk-ir/src/visit/binder_impls.rs
@@ -3,6 +3,7 @@
 //!
 //! The more interesting impls of `Visit` remain in the `visit` module.
 
+use crate::interner::HasInterner;
 use crate::{Binders, Canonical, DebruijnIndex, Fn, Interner, Visit, VisitResult, Visitor};
 
 impl<I: Interner> Visit<I> for Fn<I> {
@@ -23,7 +24,7 @@ impl<I: Interner> Visit<I> for Fn<I> {
 
 impl<T, I: Interner> Visit<I> for Binders<T>
 where
-    T: Visit<I>,
+    T: HasInterner + Visit<I>,
 {
     fn visit_with<'i, R: VisitResult>(
         &self,
@@ -37,10 +38,10 @@ where
     }
 }
 
-impl<T, I> Visit<I> for Canonical<T>
+impl<I, T> Visit<I> for Canonical<T>
 where
-    T: Visit<I>,
     I: Interner,
+    T: HasInterner<Interner = I> + Visit<I>,
 {
     fn visit_with<'i, R: VisitResult>(
         &self,
diff --git a/chalk-ir/src/zip.rs b/chalk-ir/src/zip.rs
index 81467eced21..60879d89c79 100644
--- a/chalk-ir/src/zip.rs
+++ b/chalk-ir/src/zip.rs
@@ -31,7 +31,7 @@ pub trait Zipper<'i, I: Interner> {
     /// Zips two values appearing beneath binders.
     fn zip_binders<T>(&mut self, a: &Binders<T>, b: &Binders<T>) -> Fallible<()>
     where
-        T: Zip<I> + Fold<I, I, Result = T>;
+        T: HasInterner<Interner = I> + Zip<I> + Fold<I, I, Result = T>;
 
     /// Retreives the interner from the underlying zipper object
     fn interner(&self) -> &'i I;
@@ -52,7 +52,7 @@ where
 
     fn zip_binders<T>(&mut self, a: &Binders<T>, b: &Binders<T>) -> Fallible<()>
     where
-        T: Zip<I> + Fold<I, I, Result = T>,
+        T: HasInterner<Interner = I> + Zip<I> + Fold<I, I, Result = T>,
     {
         (**self).zip_binders(a, b)
     }
@@ -166,7 +166,9 @@ impl<I: Interner> Zip<I> for Lifetime<I> {
     }
 }
 
-impl<I: Interner, T: Zip<I> + Fold<I, I, Result = T>> Zip<I> for Binders<T> {
+impl<I: Interner, T: HasInterner<Interner = I> + Zip<I> + Fold<I, I, Result = T>> Zip<I>
+    for Binders<T>
+{
     fn zip_with<'i, Z: Zipper<'i, I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
     where
         I: 'i,
diff --git a/chalk-rust-ir/src/lib.rs b/chalk-rust-ir/src/lib.rs
index 01096b6e374..c5c688d7810 100644
--- a/chalk-rust-ir/src/lib.rs
+++ b/chalk-rust-ir/src/lib.rs
@@ -72,7 +72,7 @@ pub struct DefaultImplDatum<I: Interner> {
     pub binders: Binders<DefaultImplDatumBound<I>>,
 }
 
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, HasInterner)]
 pub struct DefaultImplDatumBound<I: Interner> {
     pub trait_ref: TraitRef<I>,
     pub accessible_tys: Vec<Ty<I>>,
@@ -169,7 +169,7 @@ impl<I: Interner> TraitDatum<I> {
     }
 }
 
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, HasInterner)]
 pub struct TraitDatumBound<I: Interner> {
     /// Where clauses defined on the trait:
     ///
@@ -429,7 +429,10 @@ impl<I: Interner> AssociatedTyDatum<I> {
         // scope for this associated type:
         let substitution = Substitution::from(
             interner,
-            binders.iter().zip(0..).map(|p| p.to_parameter(interner)),
+            binders
+                .iter(interner)
+                .zip(0..)
+                .map(|p| p.to_parameter(interner)),
         );
 
         // The self type will be `<P0 as Foo<P1..Pn>>::Item<Pn..Pm>` etc
diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs
index 2aa21df0a66..760f759a989 100644
--- a/chalk-solve/src/clauses.rs
+++ b/chalk-solve/src/clauses.rs
@@ -58,12 +58,16 @@ pub fn push_auto_trait_impls<I: Interner>(
     );
 
     let struct_datum = &builder.db.struct_datum(struct_id);
+    let interner = builder.interner();
 
     // Must be an auto trait.
     assert!(builder.db.trait_datum(auto_trait_id).is_auto_trait());
 
     // Auto traits never have generic parameters of their own (apart from `Self`).
-    assert_eq!(builder.db.trait_datum(auto_trait_id).binders.len(), 1);
+    assert_eq!(
+        builder.db.trait_datum(auto_trait_id).binders.len(interner),
+        1
+    );
 
     // If there is a `impl AutoTrait for Foo<..>` or `impl !AutoTrait
     // for Foo<..>`, where `Foo` is the struct we're looking at, then
@@ -74,7 +78,6 @@ pub fn push_auto_trait_impls<I: Interner>(
     }
 
     let binders = struct_datum.binders.map_ref(|b| &b.fields);
-    let interner = builder.interner();
     builder.push_binders(&binders, |builder, fields| {
         let self_ty: Ty<_> = ApplicationTy {
             name: struct_id.cast(interner),
diff --git a/chalk-solve/src/clauses/builder.rs b/chalk-solve/src/clauses/builder.rs
index 4851af21ee4..755e635041c 100644
--- a/chalk-solve/src/clauses/builder.rs
+++ b/chalk-solve/src/clauses/builder.rs
@@ -81,8 +81,11 @@ impl<'me, I: Interner> ClauseBuilder<'me, I> {
                 .push(ProgramClauseData::Implies(clause).intern(interner));
         } else {
             self.clauses.push(
-                ProgramClauseData::ForAll(Binders::new(self.binders.clone(), clause))
-                    .intern(interner),
+                ProgramClauseData::ForAll(Binders::new(
+                    ParameterKinds::from(interner, self.binders.clone()),
+                    clause,
+                ))
+                .intern(interner),
             );
         }
 
@@ -119,11 +122,11 @@ impl<'me, I: Interner> ClauseBuilder<'me, I> {
 
         let old_len = self.binders.len();
         let interner = self.interner();
-        self.binders.extend(binders.binders.clone());
+        self.binders.extend(binders.binders.iter(interner).cloned());
         self.parameters.extend(
             binders
                 .binders
-                .iter()
+                .iter(interner)
                 .zip(old_len..)
                 .map(|p| p.to_parameter(interner)),
         );
@@ -144,7 +147,10 @@ impl<'me, I: Interner> ClauseBuilder<'me, I> {
     #[allow(dead_code)]
     pub fn push_bound_ty(&mut self, op: impl FnOnce(&mut Self, Ty<I>)) {
         let interner = self.interner();
-        let binders = Binders::new(vec![ParameterKind::Ty(())], PhantomData::<I>);
+        let binders = Binders::new(
+            ParameterKinds::from(interner, vec![ParameterKind::Ty(())]),
+            PhantomData::<I>,
+        );
         self.push_binders(&binders, |this, PhantomData| {
             let ty = this
                 .placeholders_in_scope()
diff --git a/chalk-solve/src/clauses/generalize.rs b/chalk-solve/src/clauses/generalize.rs
index 0bfb0250874..976a28c6d54 100644
--- a/chalk-solve/src/clauses/generalize.rs
+++ b/chalk-solve/src/clauses/generalize.rs
@@ -9,8 +9,9 @@
 use chalk_engine::fallible::Fallible;
 use chalk_ir::{
     fold::{Fold, Folder},
-    interner::Interner,
-    Binders, BoundVar, DebruijnIndex, Lifetime, LifetimeData, ParameterKind, Ty, TyData,
+    interner::{HasInterner, Interner},
+    Binders, BoundVar, DebruijnIndex, Lifetime, LifetimeData, ParameterKind, ParameterKinds, Ty,
+    TyData,
 };
 use std::collections::HashMap;
 
@@ -21,7 +22,11 @@ pub struct Generalize<'i, I: Interner> {
 }
 
 impl<I: Interner> Generalize<'_, I> {
-    pub fn apply<T: Fold<I, I>>(interner: &I, value: &T) -> Binders<T::Result> {
+    pub fn apply<T>(interner: &I, value: &T) -> Binders<T::Result>
+    where
+        T: HasInterner<Interner = I> + Fold<I, I>,
+        T::Result: HasInterner<Interner = I>,
+    {
         let mut generalize = Generalize {
             binders: Vec::new(),
             mapping: HashMap::new(),
@@ -30,7 +35,7 @@ impl<I: Interner> Generalize<'_, I> {
         let value = value
             .fold_with(&mut generalize, DebruijnIndex::INNERMOST)
             .unwrap();
-        Binders::new(generalize.binders, value)
+        Binders::new(ParameterKinds::from(interner, generalize.binders), value)
     }
 }
 
diff --git a/chalk-solve/src/goal_builder.rs b/chalk-solve/src/goal_builder.rs
index f4d17af73de..b915a0d84b7 100644
--- a/chalk-solve/src/goal_builder.rs
+++ b/chalk-solve/src/goal_builder.rs
@@ -137,7 +137,7 @@ impl<'i, I: Interner> GoalBuilder<'i, I> {
             interner,
             binders
                 .binders
-                .iter()
+                .iter(interner)
                 .zip(0..)
                 .map(|p| p.to_parameter(interner)),
         );
diff --git a/chalk-solve/src/infer.rs b/chalk-solve/src/infer.rs
index 6176c838a67..f891729fc53 100644
--- a/chalk-solve/src/infer.rs
+++ b/chalk-solve/src/infer.rs
@@ -1,4 +1,4 @@
-use chalk_ir::interner::Interner;
+use chalk_ir::interner::{HasInterner, Interner};
 use chalk_ir::*;
 use chalk_ir::{cast::Cast, fold::Fold};
 
@@ -52,7 +52,7 @@ impl<I: Interner> InferenceTable<I> {
         canonical: &Canonical<T>,
     ) -> (Self, Substitution<I>, T)
     where
-        T: Fold<I, Result = T> + Clone,
+        T: HasInterner<Interner = I> + Fold<I, Result = T> + Clone,
     {
         let mut table = InferenceTable::new();
 
@@ -61,7 +61,7 @@ impl<I: Interner> InferenceTable<I> {
             table.new_universe();
         }
 
-        let subst = table.fresh_subst(interner, &canonical.binders);
+        let subst = table.fresh_subst(interner, canonical.binders.as_slice(interner));
         let value = subst.apply(&canonical.value, interner);
         // let value = canonical.value.fold_with(&mut &subst, 0).unwrap();
 
diff --git a/chalk-solve/src/infer/canonicalize.rs b/chalk-solve/src/infer/canonicalize.rs
index 8fe71537ac7..60f04308517 100644
--- a/chalk-solve/src/infer/canonicalize.rs
+++ b/chalk-solve/src/infer/canonicalize.rs
@@ -74,14 +74,19 @@ struct Canonicalizer<'q, I: Interner> {
 }
 
 impl<'q, I: Interner> Canonicalizer<'q, I> {
-    fn into_binders(self) -> Vec<ParameterKind<UniverseIndex>> {
+    fn into_binders(self) -> CanonicalVarKinds<I> {
         let Canonicalizer {
-            table, free_vars, ..
+            table,
+            free_vars,
+            interner,
+            ..
         } = self;
-        free_vars
-            .into_iter()
-            .map(|p_v| p_v.map(|v| table.universe_of_unbound_var(v)))
-            .collect()
+        CanonicalVarKinds::from(
+            interner,
+            free_vars
+                .into_iter()
+                .map(|p_v| p_v.map(|v| table.universe_of_unbound_var(v))),
+        )
     }
 
     fn add(&mut self, free_var: ParameterEnaVariable<I>) -> usize {
diff --git a/chalk-solve/src/infer/instantiate.rs b/chalk-solve/src/infer/instantiate.rs
index b227ffef3da..5777822768d 100644
--- a/chalk-solve/src/infer/instantiate.rs
+++ b/chalk-solve/src/infer/instantiate.rs
@@ -1,4 +1,5 @@
 use chalk_ir::fold::*;
+use chalk_ir::interner::HasInterner;
 use std::fmt::Debug;
 
 use super::*;
@@ -30,9 +31,9 @@ impl<I: Interner> InferenceTable<I> {
         bound: &Canonical<T>,
     ) -> T::Result
     where
-        T: Fold<I> + Debug,
+        T: HasInterner<Interner = I> + Fold<I> + Debug,
     {
-        let subst = self.fresh_subst(interner, &bound.binders);
+        let subst = self.fresh_subst(interner, &bound.binders.as_slice(interner));
         subst.apply(&bound.value, interner)
     }
 
@@ -61,28 +62,28 @@ impl<I: Interner> InferenceTable<I> {
     }
 
     /// Variant on `instantiate_in` that takes a `Binders<T>`.
-    pub(crate) fn instantiate_binders_existentially<T>(
+    pub(crate) fn instantiate_binders_existentially<'a, T>(
         &mut self,
-        interner: &I,
-        arg: impl IntoBindersAndValue<Value = T>,
+        interner: &'a I,
+        arg: impl IntoBindersAndValue<'a, I, Value = T>,
     ) -> T::Result
     where
         T: Fold<I>,
     {
-        let (binders, value) = arg.into_binders_and_value();
+        let (binders, value) = arg.into_binders_and_value(interner);
         let max_universe = self.max_universe;
         self.instantiate_in(interner, max_universe, binders, &value)
     }
 
-    pub(crate) fn instantiate_binders_universally<T>(
+    pub(crate) fn instantiate_binders_universally<'a, T>(
         &mut self,
-        interner: &I,
-        arg: impl IntoBindersAndValue<Value = T>,
+        interner: &'a I,
+        arg: impl IntoBindersAndValue<'a, I, Value = T>,
     ) -> T::Result
     where
         T: Fold<I>,
     {
-        let (binders, value) = arg.into_binders_and_value();
+        let (binders, value) = arg.into_binders_and_value(interner);
         let ui = self.new_universe();
         let parameters: Vec<_> = binders
             .into_iter()
@@ -102,30 +103,34 @@ impl<I: Interner> InferenceTable<I> {
     }
 }
 
-pub(crate) trait IntoBindersAndValue {
+pub(crate) trait IntoBindersAndValue<'a, I: Interner> {
     type Binders: IntoIterator<Item = ParameterKind<()>>;
     type Value;
 
-    fn into_binders_and_value(self) -> (Self::Binders, Self::Value);
+    fn into_binders_and_value(self, interner: &'a I) -> (Self::Binders, Self::Value);
 }
 
-impl<'a, T> IntoBindersAndValue for &'a Binders<T> {
+impl<'a, I, T> IntoBindersAndValue<'a, I> for &'a Binders<T>
+where
+    I: Interner,
+    T: HasInterner<Interner = I>,
+{
     type Binders = std::iter::Cloned<std::slice::Iter<'a, ParameterKind<()>>>;
     type Value = &'a T;
 
-    fn into_binders_and_value(self) -> (Self::Binders, Self::Value) {
-        (self.binders.iter().cloned(), self.skip_binders())
+    fn into_binders_and_value(self, interner: &'a I) -> (Self::Binders, Self::Value) {
+        (self.binders.iter(interner).cloned(), self.skip_binders())
     }
 }
 
-impl<'a, I> IntoBindersAndValue for &'a Fn<I>
+impl<'a, I> IntoBindersAndValue<'a, I> for &'a Fn<I>
 where
     I: Interner,
 {
     type Binders = std::iter::Map<std::ops::Range<usize>, fn(usize) -> chalk_ir::ParameterKind<()>>;
     type Value = &'a Substitution<I>;
 
-    fn into_binders_and_value(self) -> (Self::Binders, Self::Value) {
+    fn into_binders_and_value(self, _interner: &'a I) -> (Self::Binders, Self::Value) {
         fn make_lifetime(_: usize) -> ParameterKind<()> {
             ParameterKind::Lifetime(())
         }
@@ -135,11 +140,11 @@ where
     }
 }
 
-impl<'a, T> IntoBindersAndValue for (&'a Vec<ParameterKind<()>>, &'a T) {
+impl<'a, T, I: Interner> IntoBindersAndValue<'a, I> for (&'a Vec<ParameterKind<()>>, &'a T) {
     type Binders = std::iter::Cloned<std::slice::Iter<'a, ParameterKind<()>>>;
     type Value = &'a T;
 
-    fn into_binders_and_value(self) -> (Self::Binders, Self::Value) {
+    fn into_binders_and_value(self, _interner: &'a I) -> (Self::Binders, Self::Value) {
         (self.0.iter().cloned(), &self.1)
     }
 }
diff --git a/chalk-solve/src/infer/invert.rs b/chalk-solve/src/infer/invert.rs
index 227aef71aec..5a4ffad82b4 100644
--- a/chalk-solve/src/infer/invert.rs
+++ b/chalk-solve/src/infer/invert.rs
@@ -88,7 +88,7 @@ impl<I: Interner> InferenceTable<I> {
         }
 
         // If this contains free universal variables, replace them with existentials.
-        assert!(quantified.binders.is_empty());
+        assert!(quantified.binders.is_empty(interner));
         let inverted = quantified
             .value
             .fold_with(&mut Inverter::new(interner, self), DebruijnIndex::INNERMOST)
diff --git a/chalk-solve/src/infer/test.rs b/chalk-solve/src/infer/test.rs
index 1cf5f8d3e5b..471c7c719eb 100644
--- a/chalk-solve/src/infer/test.rs
+++ b/chalk-solve/src/infer/test.rs
@@ -181,11 +181,14 @@ fn quantify_simple() {
             .quantified,
         Canonical {
             value: ty!(apply (item 0) (bound 0) (bound 1) (bound 2)),
-            binders: vec![
-                ParameterKind::Ty(U2),
-                ParameterKind::Ty(U1),
-                ParameterKind::Ty(U0),
-            ],
+            binders: CanonicalVarKinds::from(
+                interner,
+                vec![
+                    ParameterKind::Ty(U2),
+                    ParameterKind::Ty(U1),
+                    ParameterKind::Ty(U0),
+                ]
+            ),
         }
     );
 }
@@ -219,11 +222,14 @@ fn quantify_bound() {
             .quantified,
         Canonical {
             value: ty!(apply (item 0) (apply (item 1) (bound 0) (bound 1)) (bound 2) (bound 0) (bound 1)),
-            binders: vec![
-                ParameterKind::Ty(U1),
-                ParameterKind::Ty(U0),
-                ParameterKind::Ty(U2),
-            ],
+            binders: CanonicalVarKinds::from(
+                interner,
+                vec![
+                    ParameterKind::Ty(U1),
+                    ParameterKind::Ty(U0),
+                    ParameterKind::Ty(U2),
+                ]
+            ),
         }
     );
 }
@@ -260,7 +266,10 @@ fn quantify_ty_under_binder() {
             .quantified,
         Canonical {
             value: ty!(function 3 (apply (item 0) (bound 1) (bound 1 0) (bound 1 0) (lifetime (bound 1 1)))),
-            binders: vec![ParameterKind::Ty(U0), ParameterKind::Lifetime(U0)],
+            binders: CanonicalVarKinds::from(
+                interner,
+                vec![ParameterKind::Ty(U0), ParameterKind::Lifetime(U0)]
+            ),
         }
     );
 }
diff --git a/chalk-solve/src/infer/ucanonicalize.rs b/chalk-solve/src/infer/ucanonicalize.rs
index 4b4e4137607..84df92fb90c 100644
--- a/chalk-solve/src/infer/ucanonicalize.rs
+++ b/chalk-solve/src/infer/ucanonicalize.rs
@@ -1,17 +1,21 @@
 use chalk_engine::fallible::*;
 use chalk_ir::fold::{Fold, Folder};
-use chalk_ir::interner::Interner;
+use chalk_ir::interner::{HasInterner, Interner};
 use chalk_ir::visit::{Visit, Visitor};
 use chalk_ir::*;
 
 use super::InferenceTable;
 
 impl<I: Interner> InferenceTable<I> {
-    pub(crate) fn u_canonicalize<T: Fold<I> + Visit<I>>(
+    pub(crate) fn u_canonicalize<T>(
         &mut self,
         interner: &I,
         value0: &Canonical<T>,
-    ) -> UCanonicalized<T::Result> {
+    ) -> UCanonicalized<T::Result>
+    where
+        T: HasInterner<Interner = I> + Fold<I> + Visit<I>,
+        T::Result: HasInterner<Interner = I>,
+    {
         debug!("u_canonicalize({:#?})", value0);
 
         // First, find all the universes that appear in `value`.
@@ -37,11 +41,13 @@ impl<I: Interner> InferenceTable<I> {
                 DebruijnIndex::INNERMOST,
             )
             .unwrap();
-        let binders = value0
-            .binders
-            .iter()
-            .map(|pk| pk.map(|ui| universes.map_universe_to_canonical(ui)))
-            .collect();
+        let binders = CanonicalVarKinds::from(
+            interner,
+            value0
+                .binders
+                .iter(interner)
+                .map(|pk| pk.map(|ui| universes.map_universe_to_canonical(ui))),
+        );
 
         UCanonicalized {
             quantified: UCanonical {
@@ -57,7 +63,7 @@ impl<I: Interner> InferenceTable<I> {
 }
 
 #[derive(Debug)]
-pub(crate) struct UCanonicalized<T> {
+pub(crate) struct UCanonicalized<T: HasInterner> {
     /// The canonicalized result.
     pub(crate) quantified: UCanonical<T>,
 
diff --git a/chalk-solve/src/infer/unify.rs b/chalk-solve/src/infer/unify.rs
index d76ae6cc55f..a8c6521861e 100644
--- a/chalk-solve/src/infer/unify.rs
+++ b/chalk-solve/src/infer/unify.rs
@@ -4,7 +4,7 @@ use crate::infer::instantiate::IntoBindersAndValue;
 use chalk_engine::fallible::*;
 use chalk_ir::cast::Cast;
 use chalk_ir::fold::{Fold, Folder};
-use chalk_ir::interner::Interner;
+use chalk_ir::interner::{HasInterner, Interner};
 use chalk_ir::zip::{Zip, Zipper};
 use std::fmt::Debug;
 
@@ -185,14 +185,15 @@ impl<'t, I: Interner> Unifier<'t, I> {
         }
     }
 
-    fn unify_binders<T, R>(
+    fn unify_binders<'a, T, R>(
         &mut self,
-        a: impl IntoBindersAndValue<Value = T> + Copy + Debug,
-        b: impl IntoBindersAndValue<Value = T> + Copy + Debug,
+        a: impl IntoBindersAndValue<'a, I, Value = T> + Copy + Debug,
+        b: impl IntoBindersAndValue<'a, I, Value = T> + Copy + Debug,
     ) -> Fallible<()>
     where
         T: Fold<I, Result = R>,
         R: Zip<I> + Fold<I, Result = R>,
+        't: 'a,
     {
         // for<'a...> T == for<'b...> U
         //
@@ -352,7 +353,7 @@ impl<'i, I: Interner> Zipper<'i, I> for Unifier<'i, I> {
 
     fn zip_binders<T>(&mut self, a: &Binders<T>, b: &Binders<T>) -> Fallible<()>
     where
-        T: Zip<I> + Fold<I, Result = T>,
+        T: HasInterner<Interner = I> + Zip<I> + Fold<I, Result = T>,
     {
         // The binders that appear in types (apart from quantified types, which are
         // handled in `unify_ty`) appear as part of `dyn Trait` and `impl Trait` types.
diff --git a/chalk-solve/src/recursive.rs b/chalk-solve/src/recursive.rs
index fd6a8e57927..d4a2f9541fb 100644
--- a/chalk-solve/src/recursive.rs
+++ b/chalk-solve/src/recursive.rs
@@ -366,7 +366,10 @@ impl<'me, I: Interner> Solver<'me, I> {
                 ProgramClauseData::Implies(implication) => {
                     let res = self.solve_via_implication(
                         canonical_goal,
-                        &Binders::new(vec![], implication.clone()),
+                        &Binders::new(
+                            ParameterKinds::from(self.program.interner(), vec![]),
+                            implication.clone(),
+                        ),
                         minimums,
                     );
                     if let (Ok(solution), priority) = res {
@@ -521,10 +524,10 @@ fn combine_with_priorities<I: Interner>(
                 );
                 (higher, ClausePriority::High)
             } else {
-                (higher.combine(lower), ClausePriority::High)
+                (higher.combine(lower, interner), ClausePriority::High)
             }
         }
-        (_, _, a, b) => (a.combine(b), prio_a),
+        (_, _, a, b) => (a.combine(b, interner), prio_a),
     }
 }
 
diff --git a/chalk-solve/src/recursive/fulfill.rs b/chalk-solve/src/recursive/fulfill.rs
index e401f6955ca..6fbd3c52e5d 100644
--- a/chalk-solve/src/recursive/fulfill.rs
+++ b/chalk-solve/src/recursive/fulfill.rs
@@ -108,7 +108,7 @@ impl<'s, 'db, I: Interner> Fulfill<'s, 'db, I> {
     /// Wraps `InferenceTable::instantiate_in`
     pub(crate) fn instantiate_binders_existentially<T>(
         &mut self,
-        arg: impl IntoBindersAndValue<Value = T>,
+        arg: impl IntoBindersAndValue<'s, I, Value = T>,
     ) -> T::Result
     where
         T: Fold<I, I>,
diff --git a/chalk-solve/src/solve.rs b/chalk-solve/src/solve.rs
index a8e72282f95..7c2d7227a21 100644
--- a/chalk-solve/src/solve.rs
+++ b/chalk-solve/src/solve.rs
@@ -69,14 +69,18 @@ impl<I: Interner> Solution<I> {
     //    Clone`.
     //
     // But you get the idea.
-    pub(crate) fn combine(self, other: Solution<I>) -> Solution<I> {
+    pub(crate) fn combine(self, other: Solution<I>, interner: &I) -> Solution<I> {
         use self::Guidance::*;
 
         if self == other {
             return self;
         }
 
-        debug!("combine {} with {}", self, other);
+        debug!(
+            "combine {} with {}",
+            self.display(interner),
+            other.display(interner)
+        );
 
         // Otherwise, always downgrade to Ambig:
 
@@ -138,18 +142,35 @@ impl<I: Interner> Solution<I> {
             _ => false,
         }
     }
+
+    pub fn display<'a>(&'a self, interner: &'a I) -> SolutionDisplay<'a, I> {
+        SolutionDisplay {
+            solution: self,
+            interner,
+        }
+    }
+}
+
+pub struct SolutionDisplay<'a, I: Interner> {
+    solution: &'a Solution<I>,
+    interner: &'a I,
 }
 
-impl<I: Interner> fmt::Display for Solution<I> {
+impl<'a, I: Interner> fmt::Display for SolutionDisplay<'a, I> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
-        match self {
-            Solution::Unique(constrained) => write!(f, "Unique; {}", constrained,),
-            Solution::Ambig(Guidance::Definite(subst)) => {
-                write!(f, "Ambiguous; definite substitution {}", subst)
-            }
-            Solution::Ambig(Guidance::Suggested(subst)) => {
-                write!(f, "Ambiguous; suggested substitution {}", subst)
-            }
+        let SolutionDisplay { solution, interner } = self;
+        match solution {
+            Solution::Unique(constrained) => write!(f, "Unique; {}", constrained.display(interner)),
+            Solution::Ambig(Guidance::Definite(subst)) => write!(
+                f,
+                "Ambiguous; definite substitution {}",
+                subst.display(interner)
+            ),
+            Solution::Ambig(Guidance::Suggested(subst)) => write!(
+                f,
+                "Ambiguous; suggested substitution {}",
+                subst.display(interner)
+            ),
             Solution::Ambig(Guidance::Unknown) => write!(f, "Ambiguous; no inference guidance"),
         }
     }
diff --git a/chalk-solve/src/solve/slg/aggregate.rs b/chalk-solve/src/solve/slg/aggregate.rs
index dcf9ad9f891..4d5f34f27ff 100644
--- a/chalk-solve/src/solve/slg/aggregate.rs
+++ b/chalk-solve/src/solve/slg/aggregate.rs
@@ -146,7 +146,7 @@ fn merge_into_guidance<I: Interner>(
             // We have two values for some variable X that
             // appears in the root goal. Find out the universe
             // of X.
-            let universe = root_goal.binders[index].into_inner();
+            let universe = root_goal.binders.as_slice(interner)[index].into_inner();
 
             let ty = match value.data(interner) {
                 ParameterKind::Ty(ty) => ty,
diff --git a/chalk-solve/src/solve/slg/resolvent.rs b/chalk-solve/src/solve/slg/resolvent.rs
index 89d39388c96..d384aeff7dd 100644
--- a/chalk-solve/src/solve/slg/resolvent.rs
+++ b/chalk-solve/src/solve/slg/resolvent.rs
@@ -3,7 +3,7 @@ use crate::solve::slg::{self, SlgContext, TruncatingInferenceTable};
 use chalk_engine::fallible::Fallible;
 use chalk_ir::fold::shift::Shift;
 use chalk_ir::fold::Fold;
-use chalk_ir::interner::Interner;
+use chalk_ir::interner::{HasInterner, Interner};
 use chalk_ir::zip::{Zip, Zipper};
 use chalk_ir::*;
 
@@ -483,7 +483,7 @@ impl<'i, I: Interner> Zipper<'i, I> for AnswerSubstitutor<'i, I> {
 
     fn zip_binders<T>(&mut self, answer: &Binders<T>, pending: &Binders<T>) -> Fallible<()>
     where
-        T: Zip<I> + Fold<I, Result = T>,
+        T: HasInterner<Interner = I> + Zip<I> + Fold<I, Result = T>,
     {
         self.outer_binder.shift_in();
         Zip::zip_with(self, answer.skip_binders(), pending.skip_binders())?;
diff --git a/chalk-solve/src/split.rs b/chalk-solve/src/split.rs
index 1a93a0a2db2..eb2384e1bce 100644
--- a/chalk-solve/src/split.rs
+++ b/chalk-solve/src/split.rs
@@ -29,7 +29,7 @@ pub trait Split<I: Interner>: RustIrDatabase<I> {
         let parameters = substitution.parameters(interner);
         let associated_ty_data = &self.associated_ty_data(associated_ty_id);
         let trait_datum = &self.trait_datum(associated_ty_data.trait_id);
-        let trait_num_params = trait_datum.binders.len();
+        let trait_num_params = trait_datum.binders.len(interner);
         let split_point = parameters.len() - trait_num_params;
         let (other_params, trait_params) = parameters.split_at(split_point);
         (associated_ty_data.clone(), trait_params, other_params)
@@ -86,8 +86,9 @@ pub trait Split<I: Interner>: RustIrDatabase<I> {
         parameters: &'p [P],
         associated_ty_value: &AssociatedTyValue<I>,
     ) -> (&'p [P], &'p [P]) {
+        let interner = self.interner();
         let impl_datum = self.impl_datum(associated_ty_value.impl_id);
-        let impl_params_len = impl_datum.binders.len();
+        let impl_params_len = impl_datum.binders.len(interner);
         assert!(parameters.len() >= impl_params_len);
 
         // the impl parameters are a suffix
diff --git a/src/main.rs b/src/main.rs
index a4cf7ef2e28..5a13aeef86d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8,6 +8,7 @@ use std::process::exit;
 use chalk_integration::db::ChalkDatabase;
 use chalk_integration::lowering::*;
 use chalk_integration::query::LoweringDatabase;
+use chalk_ir::interner::ChalkIr;
 use chalk_solve::ext::*;
 use chalk_solve::{RustIrDatabase, SolverChoice};
 use docopt::Docopt;
@@ -67,7 +68,7 @@ impl LoadedProgram {
         let peeled_goal = goal.into_peeled_goal(self.db.interner());
         if multiple_answers {
             if self.db.solve_multiple(&peeled_goal, |v, has_next| {
-                println!("{}\n", v);
+                println!("{}\n", v.as_ref().map(|v| v.display(&ChalkIr)));
                 if has_next {
                     if let Some(ref mut rl) = rl {
                         loop {
@@ -94,7 +95,7 @@ impl LoadedProgram {
             }
         } else {
             match self.db.solve(&peeled_goal) {
-                Some(v) => println!("{}\n", v),
+                Some(v) => println!("{}\n", v.display(&ChalkIr)),
                 None => println!("No possible solution.\n"),
             }
         }
diff --git a/tests/test/mod.rs b/tests/test/mod.rs
index 2e1f26b1a6a..535915e90e1 100644
--- a/tests/test/mod.rs
+++ b/tests/test/mod.rs
@@ -26,7 +26,7 @@ fn assert_result(mut result: Option<Solution<ChalkIr>>, expected: &str) {
         _ => {}
     }
     let result = match result {
-        Some(v) => format!("{}", v),
+        Some(v) => format!("{}", v.display(&ChalkIr)),
         None => format!("No possible solution"),
     };
 
@@ -262,7 +262,13 @@ fn solve_goal(program_text: &str, goals: Vec<(&str, SolverChoice, TestGoal)>) {
                         db.solve_multiple(&peeled_goal, |result, next_result| {
                             match expected.next() {
                                 Some(expected) => {
-                                    assert_same(&format!("{}", &result), expected);
+                                    assert_same(
+                                        &format!(
+                                            "{}",
+                                            result.as_ref().map(|v| v.display(&ChalkIr))
+                                        ),
+                                        expected,
+                                    );
                                 }
                                 None => {
                                     assert!(!next_result, "Unexpected next solution");
@@ -280,7 +286,10 @@ fn solve_goal(program_text: &str, goals: Vec<(&str, SolverChoice, TestGoal)>) {
                     let mut expected = expected.into_iter();
                     db.solve_multiple(&peeled_goal, |result, next_result| match expected.next() {
                         Some(solution) => {
-                            assert_same(&format!("{}", &result), solution);
+                            assert_same(
+                                &format!("{}", result.as_ref().map(|v| v.display(&ChalkIr))),
+                                solution,
+                            );
                             if !next_result {
                                 assert!(expected.next().is_none(), "Not enough solutions found");
                             }