lang: Add modern type unification implementation
This adds a modern type unification algorithm, which drastically improves performance, particularly for bigger programs. This required a change to the AST to add TypeCheck methods (for Stmt) and Infer/Check methods (for Expr). This also changed how the functions express their invariants, and as a result this was changed as well. This greatly improves the way we express these invariants, and as a result it makes adding new polymorphic functions significantly easier. This also makes error output for the user a lot better in pretty much all scenarios. The one downside of this patch is that a good chunk of it is merged in this giant single commit since it was hard to do it step-wise. That's not the end of the world. This couldn't be done without the guidance of Sam who helped me in explaining, debugging, and writing all the sneaky algorithmic parts and much more. Thanks again Sam! Co-authored-by: Samuel Gélineau <gelisam@gmail.com>
This commit is contained in:
@@ -79,10 +79,11 @@ type Stmt interface {
|
||||
// SetScope sets the scope here and propagates it downwards.
|
||||
SetScope(*Scope) error
|
||||
|
||||
// Unify returns the list of invariants that this node produces. It does
|
||||
// so recursively on any children elements that exist in the AST, and
|
||||
// returns the collection to the caller.
|
||||
Unify() ([]Invariant, error)
|
||||
// TypeCheck returns the list of invariants that this node produces. It
|
||||
// does so recursively on any children elements that exist in the AST,
|
||||
// and returns the collection to the caller. It calls TypeCheck for
|
||||
// child statements, and Infer/Check for child expressions.
|
||||
TypeCheck() ([]*UnificationInvariant, error)
|
||||
|
||||
// Graph returns the reactive function graph expressed by this node.
|
||||
Graph() (*pgraph.Graph, error)
|
||||
@@ -128,10 +129,21 @@ type Expr interface {
|
||||
// determine it statically. This errors if it is not yet known.
|
||||
Type() (*types.Type, error)
|
||||
|
||||
// Unify returns the list of invariants that this node produces. It does
|
||||
// so recursively on any children elements that exist in the AST, and
|
||||
// returns the collection to the caller.
|
||||
Unify() ([]Invariant, error)
|
||||
// Infer returns the type of itself and a collection of invariants. The
|
||||
// returned type may contain unification variables. It collects the
|
||||
// invariants by calling Check on its children expressions. In making
|
||||
// those calls, it passes in the known type for that child to get it to
|
||||
// "Check" it. When the type is not known, it should create a new
|
||||
// unification variable to pass in to the child Check calls. Infer
|
||||
// usually only calls Check on things inside of it, and often does not
|
||||
// call another Infer.
|
||||
Infer() (*types.Type, []*UnificationInvariant, error)
|
||||
|
||||
// Check is checking that the input type is equal to the object that
|
||||
// Check is running on. In doing so, it adds any invariants that are
|
||||
// necessary. Check must always call Infer to produce the invariant. The
|
||||
// implementation can be generic for all expressions.
|
||||
Check(typ *types.Type) ([]*UnificationInvariant, error)
|
||||
|
||||
// Graph returns the reactive function graph expressed by this node. It
|
||||
// takes in the environment of any functions in scope. It also returns
|
||||
|
||||
Reference in New Issue
Block a user