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 writesFoo
but 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::Item
where we can't normalize, because we don't know whatT
is. Even in that case, though, we still know thatT::Item: Sized
, because that bound is declared in theIterator
trait (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 normalizeFoo
to 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.