The role of the Interner
Most everything in the IR is parameterized by the Interner trait:
trait Interner: Copy + Clone + Debug + Eq + Ord {
..
}
We'll go over the details later, but for now it suffices to say that
the interner is defined by the embedder and can be used to control
(to a certain extent) the actual representation of types, goals, and
other things in memory. For example, the Interner trait could be
used to intern all the types, as rustc does, or it could be used to
Box them instead, as the chalk testing harness currently does.
Controlling representation with Interner
The purpose of the Interner trait is to give control over how
types and other bits of chalk-ir are represented in memory. This is
done via an "indirection" strategy. We'll explain that strategy here
in terms of Ty and TyKind, the two types used to represent
Rust types, but the same pattern is repeated for many other things.
Types are represented by a Ty<I> type and the TyKind<I> enum.
There is no direct connection between them. The link is rather made
by the Interner trait, via the InternedTy associated type:
struct Ty<I: Interner>(I::InternedTy);
enum TyKind<I: Interner> { .. }
The way this works is that the Interner trait has an associated
type InternedTy and two related methods, intern_ty and ty_data:
trait Interner {
type InternedTy;
fn intern_ty(&self, data: &TyKind<Self>) -> Self::InternedTy;
fn ty_data(data: &Self::InternedTy) -> &TyData<Self>;
}
However, as a user you are not meant to use these directly. Rather,
they are encapsulated in methods on the Ty and TyKind types:
impl<I: Interner> Ty<I> {
fn data(&self) -> &TyKind<I> {
I::lookup_ty(self)
}
}
and
impl<I: Interner> TyKind<I> {
fn intern(&self, i: &I) -> Ty<I> {
Ty(i.intern_ty(self))
}
}
Note that there is an assumption here that ty_data needs no
context. This effectively constrains the InternedTy representation
to be a Box or & type. To be more general, at the cost of some
convenience, we could make that a method as well, so that one would
invoke ty.data(i) instead of just ty.data(). This would permit us
to use (for example) integers to represent interned types, which might
be nice (e.g., to permit using generational indices).