Alias types
Alias types are used in chalk to handle a number of distinct Rust concepts:
- Explicit type aliases like
type Foo = u32(in theory) - Associated types like
impl Iterator for Foo { type Item = Bar } - Opaque types generated by impl Traits, like
type Foo = impl Iterator<Item = u32>orfn foo() -> impl Iterator<Item = u32>.
What all these aliases have in common is that they let the user write the name of one type that turns out to be equivalent to another, although the equivalent type is not always known:
- In an explicit type alias like
type Foo = u32, the user writesFoobut it is always known to be equivalent tou32 - In an associated type, the user might write
<vec::IntoIter<u32> as Iterator>::Item, but the compiler knows that can be normalized (see below) tou32. In generic functions, though, you might have a type likeT::Itemwhere we can't normalize, because we don't know whatTis. Even in that case, though, we still know thatT::Item: Sized, because that bound is declared in theIteratortrait (by default, as it happens). We describe how both cases are handled in more detail in the section on associated types. - In an opaque type like
type Foo = impl Iterator<Item = u32>, the user might writeFoo(which indirectly references the opaque type) but they never get to rely on the precise underlying type. However, when generating code, the compiler does need to be able to normalizeFooto the precise underlying type, so normalization still does occur. We describe this in more detail in the opaque types section.
How aliases work
All aliases have a few parts:
- The Alias type, which represents what the user wrote directly, but where there is some underlying type.
- Normalization rules, which indicate when the alias type can be converted into its underlying type.
- A corresponding Placeholder type, which is used in cases where the alias cannot be converted into its underlying type.
Equating an alias
Alias types are integrated specially into unification. Whenever there is an
attempt to unify an Alias type A with some other type T, we generate an
AliasEq that must be solved:
AliasEq(A = T)
The rules for how to solve an AliasEq goal will be generated by lowering the alias
definition, and depend a bit on the kind of alias. We describe that lowering in the
clauses section.
Alias placeholders
For each kind of alias (except for explicit type aliases), there is also a
corresponding placeholder variant in the TyKind enum. In those cases
where we cannot normalize the alias to something specific, it can be equated to
the placeholder type (see e.g. AssociatedType, which is the placeholder
variant for associated type projections). Note that placeholders are
application types -- unlike an alias, a placeholder is only known to be equal
with itself, just like an application type.