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> or fn 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 writes Foo but it is always known to be equivalent to u32
  • In an associated type, the user might write <vec::IntoIter<u32> as Iterator>::Item, but the compiler knows that can be normalized (see below) to u32. In generic functions, though, you might have a type like T::Item where we can't normalize, because we don't know what T is. Even in that case, though, we still know that T::Item: Sized, because that bound is declared in the Iterator 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 write Foo (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 normalize Foo 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.