mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-17 01:45:03 +01:00
fix type inference
This commit is contained in:
parent
162d71c8b8
commit
e88d0f8782
2 changed files with 37 additions and 8 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import { TY_INT, TY_STRING } from "./ast";
|
import { TY_INT, TY_STRING, TY_UNIT } from "./ast";
|
||||||
import { DUMMY_SPAN as SPAN } from "./error";
|
import { DUMMY_SPAN as SPAN } from "./error";
|
||||||
import { InferContext } from "./typeck";
|
import { InferContext } from "./typeck";
|
||||||
|
|
||||||
|
|
@ -34,3 +34,21 @@ it("should conflict assignments to resolvable type vars", () => {
|
||||||
|
|
||||||
expect(() => infcx.assign(a, TY_STRING, SPAN)).toThrow();
|
expect(() => infcx.assign(a, TY_STRING, SPAN)).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should not cycle", () => {
|
||||||
|
const infcx = new InferContext();
|
||||||
|
|
||||||
|
const a = infcx.newVar();
|
||||||
|
const b = infcx.newVar();
|
||||||
|
|
||||||
|
infcx.assign(a, b, SPAN);
|
||||||
|
infcx.assign(b, a, SPAN);
|
||||||
|
|
||||||
|
const aType = infcx.resolveIfPossible(a);
|
||||||
|
expect(aType.kind).toEqual("var");
|
||||||
|
|
||||||
|
infcx.assign(a, TY_UNIT, SPAN);
|
||||||
|
|
||||||
|
const bType = infcx.resolveIfPossible(b);
|
||||||
|
expect(bType.kind).toEqual("tuple");
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -283,21 +283,32 @@ export class InferContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private findRoot(variable: number): number {
|
||||||
|
let root = variable;
|
||||||
|
let nextVar;
|
||||||
|
while ((nextVar = this.tyVars[root]).kind === "unified") {
|
||||||
|
root = nextVar.index;
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to constrain a type variable to be of a specific type.
|
* Try to constrain a type variable to be of a specific type.
|
||||||
* INVARIANT: Both sides must not be of res "final", use resolveIfPossible
|
* INVARIANT: Both sides must not be of res "final", use resolveIfPossible
|
||||||
* before calling this.
|
* before calling this.
|
||||||
*/
|
*/
|
||||||
private constrainVar(variable: number, ty: Ty) {
|
private constrainVar(variable: number, ty: Ty) {
|
||||||
let root = variable;
|
let root = this.findRoot(variable);
|
||||||
let nextVar;
|
|
||||||
while ((nextVar = this.tyVars[root]).kind === "unified") {
|
|
||||||
root = nextVar.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ty.kind === "var") {
|
if (ty.kind === "var") {
|
||||||
// Point the lhs to the rhs.
|
// Now we point our root to the other root to unify the two graphs.
|
||||||
this.tyVars[root] = { kind: "unified", index: ty.index };
|
const otherRoot = this.findRoot(ty.index);
|
||||||
|
|
||||||
|
// If both types have the same root, we don't need to do anything
|
||||||
|
// as they're already part of the same graph.
|
||||||
|
if (root != otherRoot) {
|
||||||
|
this.tyVars[root] = { kind: "unified", index: otherRoot };
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.tyVars[root] = { kind: "final", ty };
|
this.tyVars[root] = { kind: "final", ty };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue