Polymorphism, revisited
Earlier, I proudly detailed how I was lazily compiling functions when they were first called, thus having their polymorphic type available when compiling them.
Turns out this wasn't such a great idea. This kind of clever dynamism doesn't work at all with static analysis. The closure analyser, for example, could see that function A closed over B, but if B was polymorphic, it had no idea which specialization of B it should put in the closure data.
The new approach is a renaming pass, happening right after type-checking. This pass replaces every reference to variable names in the syntax tree with an object that uniquely determines the variable (so different variables with the same name get different objects). This allows the code to 'annotate' variables by storing information in this object, and makes it possible for further passes to search the environment using a pointer compare, rather than a string compare.
But the main task of this renaming pass is to 'split' polymorphic functions, so that each specialization becomes a separate binding, with its own name. Uses of these functions are then renamed to use the name that corresponds to their type. This makes the existence of polymorphism much less problematic for further passes—they know exactly which variable refers to which binding, and don't have to deal with a single variable referring to more than one instance of a function.
The hairy thing in this new approach was keeping track of types. Scoped types can cause funny things like having a piece of abstract syntax tree be typed by a type that's defined inside of it. The poor code that tries to interpret this type will have no idea what the type name indicates, and in fact can't know—there might be multiple type definition by that name inside of the tree. To get around this, I extended the type checker to 'resolve' types, inserting the actual type objects where their names used to appear, so that further passes don't have to deal with type names anymore.