Type variables can only be used on functions (and in number literals as
a carve-out for now). In all other cases, a type variable takes on a
single, concrete type based on later usages. This check emits errors
when this is violated.
The implementation is to check the rank of a variable after it could be
generalized. If the variable is not generalized but annotated as a type
variable, emit an error.
Sometimes, we might need to fixpoint-fix a unification like
[ Bar [ Bar <a>, Foo ], Foo ] as <a> 🛠️ [ Bar <b>, Foo ] as <b>
where we hit a comparison between <a> and <b>. In this case, follow each
recursion point independently and see if we can find the chain to the needle
we were searching for.
Closes#5476
At times we can have alias arguments that are recursive in their
position, but whose recursiveness is not immediately visible in the real
var.
Closes#5330
While unifying two unions, we may turn one of them into a recursive
union. If we don't recognize the recursiveness before merging, we'll
have lost the recursive value of the union.
There are times that multiple concrete types may appear in
unspecialized lambda sets that are being unified. The primary case is
during monomorphization, when unspecialized lambda sets join at the same
time that concrete types get instantiated. Since lambda set
specialization and compaction happens only after unifications are
complete, unifications that monomorphize can induce the above-described
situation.
In these cases,
- unspecialized lambda sets that are due to equivalent type variables
can be compacted, since they are in fact the same specialization.
- unspecialized lambda sets that are due to different type variables
cannot be compacted, even if their types unify, since they may point
to different specializations. For example, consider the unspecialized
lambda set `[[] + [A]:toEncoder:1 + [B]:toEncoder:1]` - this set wants
two encoders, one for `[A]` and one for `[B]`, which is materially
different from the set `[[] + [A, B]:toEncoder:1]`.