This commit is contained in:
parent
0655f398ce
commit
9012cfbd5f
@ -94,7 +94,7 @@ class CheckerCx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assertCompatible(left: Ty, right: Ty, loc: Loc): void {
|
assertCompatible(left: Ty, right: Ty, loc: Loc): void {
|
||||||
if (!left.compatibleWith(right)) {
|
if (!left.resolvableWith(right)) {
|
||||||
this.error(
|
this.error(
|
||||||
loc,
|
loc,
|
||||||
`type '${left.pretty()}' not compatible with type '${right.pretty()}'`,
|
`type '${left.pretty()}' not compatible with type '${right.pretty()}'`,
|
||||||
@ -122,7 +122,7 @@ class StmtChecker {
|
|||||||
const ty = node.kind.expr
|
const ty = node.kind.expr
|
||||||
? this.cx.expr(node.kind.expr)
|
? this.cx.expr(node.kind.expr)
|
||||||
: Ty.Void;
|
: Ty.Void;
|
||||||
if (!ty.compatibleWith(retTy)) {
|
if (!ty.resolvableWith(retTy)) {
|
||||||
this.cx.error(
|
this.cx.error(
|
||||||
node.loc,
|
node.loc,
|
||||||
`type '${ty.pretty()}' not compatible with return type '${retTy.pretty()}'`,
|
`type '${ty.pretty()}' not compatible with return type '${retTy.pretty()}'`,
|
||||||
@ -292,13 +292,13 @@ class ExprChecker {
|
|||||||
const argTy = this.cx.expr(node.kind.arg);
|
const argTy = this.cx.expr(node.kind.arg);
|
||||||
if (
|
if (
|
||||||
(exprTy.is("Array") || exprTy.is("Slice")) &&
|
(exprTy.is("Array") || exprTy.is("Slice")) &&
|
||||||
argTy.compatibleWith(Ty.I32)
|
argTy.resolvableWith(Ty.I32)
|
||||||
) {
|
) {
|
||||||
return exprTy.kind.ty;
|
return exprTy.kind.ty;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
(exprTy.is("Array") || exprTy.is("Slice")) &&
|
(exprTy.is("Array") || exprTy.is("Slice")) &&
|
||||||
argTy.compatibleWith(Ty.create("Range", {}))
|
argTy.resolvableWith(Ty.create("Range", {}))
|
||||||
) {
|
) {
|
||||||
return Ty.create("Slice", { ty: exprTy.kind.ty });
|
return Ty.create("Slice", { ty: exprTy.kind.ty });
|
||||||
}
|
}
|
||||||
@ -345,7 +345,7 @@ class ExprChecker {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const i of args.keys()) {
|
for (const i of args.keys()) {
|
||||||
if (!args[i].compatibleWith(params[i])) {
|
if (!args[i].resolvableWith(params[i])) {
|
||||||
this.reportArgTypeNotCompatible(
|
this.reportArgTypeNotCompatible(
|
||||||
node,
|
node,
|
||||||
args,
|
args,
|
||||||
@ -404,10 +404,10 @@ class ExprChecker {
|
|||||||
|
|
||||||
private checkUnaryExpr(node: ast.NodeWithKind<"UnaryExpr">): Ty {
|
private checkUnaryExpr(node: ast.NodeWithKind<"UnaryExpr">): Ty {
|
||||||
const exprTy = this.cx.expr(node.kind.expr);
|
const exprTy = this.cx.expr(node.kind.expr);
|
||||||
if (node.kind.op === "Neg" && exprTy.compatibleWith(Ty.I32)) {
|
if (node.kind.op === "Neg" && exprTy.resolvableWith(Ty.I32)) {
|
||||||
return Ty.I32;
|
return Ty.I32;
|
||||||
}
|
}
|
||||||
if (node.kind.op === "Not" && exprTy.compatibleWith(Ty.Bool)) {
|
if (node.kind.op === "Not" && exprTy.resolvableWith(Ty.Bool)) {
|
||||||
return Ty.Bool;
|
return Ty.Bool;
|
||||||
}
|
}
|
||||||
if (node.kind.op === "Ref") {
|
if (node.kind.op === "Ref") {
|
||||||
@ -455,7 +455,7 @@ class ExprChecker {
|
|||||||
private checkRangeExpr(node: ast.NodeWithKind<"RangeExpr">): Ty {
|
private checkRangeExpr(node: ast.NodeWithKind<"RangeExpr">): Ty {
|
||||||
for (const operandExpr of [node.kind.begin, node.kind.end]) {
|
for (const operandExpr of [node.kind.begin, node.kind.end]) {
|
||||||
const operandTy = operandExpr && this.cx.expr(operandExpr);
|
const operandTy = operandExpr && this.cx.expr(operandExpr);
|
||||||
if (operandTy && !operandTy.compatibleWith(Ty.I32)) {
|
if (operandTy && !operandTy.resolvableWith(Ty.I32)) {
|
||||||
this.cx.error(
|
this.cx.error(
|
||||||
operandExpr.loc,
|
operandExpr.loc,
|
||||||
`range operand must be '${Ty.I32.pretty()}', not '${operandTy.pretty()}'`,
|
`range operand must be '${Ty.I32.pretty()}', not '${operandTy.pretty()}'`,
|
||||||
@ -567,7 +567,7 @@ class TyChecker {
|
|||||||
if (node.is("ArrayTy")) {
|
if (node.is("ArrayTy")) {
|
||||||
const ty = this.cx.ty(node.kind.ty);
|
const ty = this.cx.ty(node.kind.ty);
|
||||||
const lengthTy = this.cx.expr(node.kind.length);
|
const lengthTy = this.cx.expr(node.kind.length);
|
||||||
if (!lengthTy.compatibleWith(Ty.I32)) {
|
if (!lengthTy.resolvableWith(Ty.I32)) {
|
||||||
this.cx.error(
|
this.cx.error(
|
||||||
node.kind.length.loc,
|
node.kind.length.loc,
|
||||||
`for array length, expected 'int', got '${lengthTy.pretty()}'`,
|
`for array length, expected 'int', got '${lengthTy.pretty()}'`,
|
||||||
@ -606,7 +606,7 @@ const binaryOpTests: BinaryOpTest[] = [
|
|||||||
"Rem",
|
"Rem",
|
||||||
];
|
];
|
||||||
if (
|
if (
|
||||||
ops.includes(op) && left.is("Int") && left.compatibleWith(right)
|
ops.includes(op) && left.is("Int") && left.resolvableWith(right)
|
||||||
) {
|
) {
|
||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
@ -615,7 +615,7 @@ const binaryOpTests: BinaryOpTest[] = [
|
|||||||
(op, left, right) => {
|
(op, left, right) => {
|
||||||
const ops: ast.BinaryOp[] = ["Eq", "Ne", "Lt", "Gt", "Lte", "Gte"];
|
const ops: ast.BinaryOp[] = ["Eq", "Ne", "Lt", "Gt", "Lte", "Gte"];
|
||||||
if (
|
if (
|
||||||
ops.includes(op) && left.is("Int") && left.compatibleWith(right)
|
ops.includes(op) && left.is("Int") && left.resolvableWith(right)
|
||||||
) {
|
) {
|
||||||
return Ty.Bool;
|
return Ty.Bool;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,32 +1,195 @@
|
|||||||
import { Syms } from "./resolve.ts";
|
import { Syms } from "./resolve.ts";
|
||||||
import * as ast from "../ast.ts";
|
import * as ast from "../ast.ts";
|
||||||
import { Ty } from "../ty.ts";
|
import { Ty } from "../ty.ts";
|
||||||
|
import { FileReporter, Loc } from "../diagnostics.ts";
|
||||||
|
|
||||||
export function checkFn(fn: ast.FnStmt, syms: Syms) {
|
export function checkFn(
|
||||||
|
fn: ast.FnStmt,
|
||||||
|
syms: Syms,
|
||||||
|
reporter: FileReporter,
|
||||||
|
): CheckedFn {
|
||||||
|
return new TypeChecker(fn, syms, reporter).check();
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FnTys {
|
export class CheckedFn {
|
||||||
constructor(
|
constructor(
|
||||||
|
private fnTy: Ty,
|
||||||
private nodeTys: Map<ast.Node, Ty>,
|
private nodeTys: Map<ast.Node, Ty>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get(node: ast.Node): Ty {
|
ty(): Ty {
|
||||||
const ty = this.nodeTys.get(node);
|
return this.fnTy;
|
||||||
|
}
|
||||||
|
|
||||||
|
exprTy(expr: ast.Node): Ty {
|
||||||
|
const ty = this.nodeTys.get(expr);
|
||||||
if (ty === undefined) {
|
if (ty === undefined) {
|
||||||
throw new Error(`no type for '${node.kind.tag}'`);
|
throw new Error(`no type for '${expr.kind.tag}'`);
|
||||||
}
|
}
|
||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FnChecker {
|
class TypeChecker {
|
||||||
private nodeTys = new Map<ast.Node, Ty>();
|
private nodeTys = new Map<ast.Node, Ty>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private fn: ast.FnStmt,
|
private fn: ast.FnStmt,
|
||||||
private syms: Syms,
|
private syms: Syms,
|
||||||
|
private reporter: FileReporter,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
check() {
|
check(): CheckedFn {
|
||||||
|
const params = this.fn.kind.params
|
||||||
|
.map((node) => {
|
||||||
|
const param = node.as("Param");
|
||||||
|
if (!param.kind.ty) {
|
||||||
|
this.reporter
|
||||||
|
.error(param.loc, `parameter must have a type`);
|
||||||
|
this.reporter.abort();
|
||||||
|
}
|
||||||
|
return this.ty(param.kind.ty);
|
||||||
|
});
|
||||||
|
|
||||||
|
const retTy = this.fn.kind.retTy
|
||||||
|
? this.ty(this.fn.kind.retTy)
|
||||||
|
: Ty.Void;
|
||||||
|
|
||||||
|
this.fn.kind.body.visit({
|
||||||
|
visit: (node) => {
|
||||||
|
if (!node.is("ReturnStmt")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ty = node.kind.expr
|
||||||
|
? this.expr(node.kind.expr, retTy)
|
||||||
|
: Ty.Void;
|
||||||
|
if (!ty.resolvableWith(retTy)) {
|
||||||
|
this.reporter.error(
|
||||||
|
node.loc,
|
||||||
|
`type '${ty.pretty()}' not compatible with return type '${retTy.pretty()}'`,
|
||||||
|
);
|
||||||
|
this.reporter.info(
|
||||||
|
this.fn.kind.retTy?.loc ?? this.fn.loc,
|
||||||
|
`return type '${retTy}' defined here`,
|
||||||
|
);
|
||||||
|
this.reporter.abort();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const ty = Ty.create("FnStmt", {
|
||||||
|
stmt: this.fn,
|
||||||
|
ty: Ty.create("Fn", { params, retTy }),
|
||||||
|
});
|
||||||
|
|
||||||
|
return new CheckedFn(ty, this.nodeTys);
|
||||||
|
}
|
||||||
|
|
||||||
|
private expr(expr: ast.Node, expected: Ty): Ty {
|
||||||
|
return this.cachedCheck(expr, () => this.checkExpr(expr, expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkExpr(expr: ast.Node, expected: Ty): Ty {
|
||||||
|
const k = expr.kind;
|
||||||
|
switch (k.tag) {
|
||||||
|
case "Error":
|
||||||
|
case "File":
|
||||||
|
case "Block":
|
||||||
|
case "ExprStmt":
|
||||||
|
case "AssignStmt":
|
||||||
|
case "FnStmt":
|
||||||
|
case "ReturnStmt":
|
||||||
|
case "LetStmt":
|
||||||
|
case "IfStmt":
|
||||||
|
case "WhileStmt":
|
||||||
|
case "BreakStmt":
|
||||||
|
case "Param":
|
||||||
|
case "IdentTy":
|
||||||
|
case "PtrTy":
|
||||||
|
case "PtrMutTy":
|
||||||
|
case "ArrayTy":
|
||||||
|
case "SliceTy":
|
||||||
|
throw new Error(`node '${k.tag}' not an expression`);
|
||||||
|
case "IdentExpr": {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
case "IntExpr": {
|
||||||
|
return this.resolve(Ty.AnyInt, expected, expr.loc).ty;
|
||||||
|
}
|
||||||
|
case "StrExpr": {
|
||||||
|
return this.resolve(
|
||||||
|
Ty.Ptr(Ty.Slice(Ty.U8)),
|
||||||
|
expected,
|
||||||
|
expr.loc,
|
||||||
|
).ty;
|
||||||
|
}
|
||||||
|
case "ArrayExpr": {
|
||||||
|
if (k.values.length === 0) {
|
||||||
|
if (expected.is("Any")) {
|
||||||
|
return Ty.Array(Ty.Any, 0);
|
||||||
|
} else {
|
||||||
|
return this.resolve(
|
||||||
|
Ty.Array(Ty.Any, 0),
|
||||||
|
expected,
|
||||||
|
expr.loc,
|
||||||
|
).ty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
case "IndexExpr": {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
case "CallExpr": {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
case "UnaryExpr": {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
case "BinaryExpr": {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
case "RangeExpr": {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
k satisfies never;
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ty(ty: ast.Node): Ty {
|
||||||
|
return this.cachedCheck(ty, () => this.checkTy(ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkTy(ty: ast.Node): Ty {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
private cachedCheck(node: ast.Node, func: () => Ty) {
|
||||||
|
const existing = this.nodeTys.get(node);
|
||||||
|
if (existing !== undefined) {
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
const ty = func();
|
||||||
|
this.nodeTys.set(node, ty);
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolve(ty: Ty, expected: Ty, loc: Loc): TyRes {
|
||||||
|
if (!ty.resolvableWith(expected)) {
|
||||||
|
this.reporter.error(
|
||||||
|
loc,
|
||||||
|
`expected type '${expected.pretty()}', got '${ty.pretty()}'`,
|
||||||
|
);
|
||||||
|
this.reporter.abort();
|
||||||
|
}
|
||||||
|
throw new Error("not implemented");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TyRes = {
|
||||||
|
ty: Ty;
|
||||||
|
rewriteSubtree: boolean;
|
||||||
|
};
|
||||||
|
|||||||
@ -346,16 +346,16 @@ class FnLowerer {
|
|||||||
const operandTy = this.tys.expr(expr.kind.expr);
|
const operandTy = this.tys.expr(expr.kind.expr);
|
||||||
if (
|
if (
|
||||||
expr.kind.op === "Neg" &&
|
expr.kind.op === "Neg" &&
|
||||||
operandTy.compatibleWith(Ty.I32) &&
|
operandTy.resolvableWith(Ty.I32) &&
|
||||||
resultTy.compatibleWith(Ty.I32)
|
resultTy.resolvableWith(Ty.I32)
|
||||||
) {
|
) {
|
||||||
const operand = this.lowerExpr(expr.kind.expr);
|
const operand = this.lowerExpr(expr.kind.expr);
|
||||||
return this.pushInst(Ty.I32, "Negate", { source: operand });
|
return this.pushInst(Ty.I32, "Negate", { source: operand });
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
expr.kind.op === "Not" &&
|
expr.kind.op === "Not" &&
|
||||||
operandTy.compatibleWith(Ty.Bool) &&
|
operandTy.resolvableWith(Ty.Bool) &&
|
||||||
resultTy.compatibleWith(Ty.Bool)
|
resultTy.resolvableWith(Ty.Bool)
|
||||||
) {
|
) {
|
||||||
const operand = this.lowerExpr(expr.kind.expr);
|
const operand = this.lowerExpr(expr.kind.expr);
|
||||||
return this.pushInst(Ty.Bool, "Not", { source: operand });
|
return this.pushInst(Ty.Bool, "Not", { source: operand });
|
||||||
@ -428,8 +428,8 @@ const binaryOpTests: BinaryOpTest[] = [
|
|||||||
if (
|
if (
|
||||||
ops.includes(op) &&
|
ops.includes(op) &&
|
||||||
left.is("Int") &&
|
left.is("Int") &&
|
||||||
left.compatibleWith(right) &&
|
left.resolvableWith(right) &&
|
||||||
result.compatibleWith(left)
|
result.resolvableWith(left)
|
||||||
) {
|
) {
|
||||||
return op as BinaryOp;
|
return op as BinaryOp;
|
||||||
}
|
}
|
||||||
@ -440,7 +440,7 @@ const binaryOpTests: BinaryOpTest[] = [
|
|||||||
if (
|
if (
|
||||||
ops.includes(op) &&
|
ops.includes(op) &&
|
||||||
left.is("Int") &&
|
left.is("Int") &&
|
||||||
left.compatibleWith(right) &&
|
left.resolvableWith(right) &&
|
||||||
result.is("Bool")
|
result.is("Bool")
|
||||||
) {
|
) {
|
||||||
return op as BinaryOp;
|
return op as BinaryOp;
|
||||||
|
|||||||
83
src/ty.ts
83
src/ty.ts
@ -24,7 +24,6 @@ export class Ty {
|
|||||||
|
|
||||||
static Error = Ty.create("Error", {});
|
static Error = Ty.create("Error", {});
|
||||||
static Void = Ty.create("Void", {});
|
static Void = Ty.create("Void", {});
|
||||||
static IntLiteral = Ty.create("IntLiteral", {});
|
|
||||||
static I8 = Ty.create("Int", { intTy: "i8" });
|
static I8 = Ty.create("Int", { intTy: "i8" });
|
||||||
static I16 = Ty.create("Int", { intTy: "i16" });
|
static I16 = Ty.create("Int", { intTy: "i16" });
|
||||||
static I32 = Ty.create("Int", { intTy: "i32" });
|
static I32 = Ty.create("Int", { intTy: "i32" });
|
||||||
@ -37,6 +36,24 @@ export class Ty {
|
|||||||
static USize = Ty.create("Int", { intTy: "usize" });
|
static USize = Ty.create("Int", { intTy: "usize" });
|
||||||
static Bool = Ty.create("Bool", {});
|
static Bool = Ty.create("Bool", {});
|
||||||
|
|
||||||
|
static Ptr(ty: Ty): Ty {
|
||||||
|
return this.create("Ptr", { ty });
|
||||||
|
}
|
||||||
|
static PtrMut(ty: Ty): Ty {
|
||||||
|
return this.create("PtrMut", { ty });
|
||||||
|
}
|
||||||
|
static Slice(ty: Ty): Ty {
|
||||||
|
return this.create("Slice", { ty });
|
||||||
|
}
|
||||||
|
static Array(ty: Ty, length: number): Ty {
|
||||||
|
return this.create("Array", { ty, length });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Only used in type checker. */
|
||||||
|
static Any = Ty.create("Any", {});
|
||||||
|
/** Only used in type checker. */
|
||||||
|
static AnyInt = Ty.create("AnyInt", {});
|
||||||
|
|
||||||
private internHash(): string {
|
private internHash(): string {
|
||||||
return JSON.stringify(this.kind);
|
return JSON.stringify(this.kind);
|
||||||
}
|
}
|
||||||
@ -52,7 +69,28 @@ export class Ty {
|
|||||||
return this.kind.tag === tag;
|
return this.kind.tag === tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
compatibleWith(other: Ty): boolean {
|
assertIs<
|
||||||
|
Tag extends TyKind["tag"],
|
||||||
|
>(tag: Tag): asserts this is Ty & { kind: { tag: Tag } } {
|
||||||
|
if (this.kind.tag !== tag) {
|
||||||
|
throw new Error(`ty is not '${tag}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
as<
|
||||||
|
Tag extends TyKind["tag"],
|
||||||
|
>(tag: Tag): Ty & { kind: { tag: Tag } } {
|
||||||
|
this.assertIs(tag);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for checking and inference in the type checker
|
||||||
|
* to say if two types are able to implicitly be converted
|
||||||
|
* into each other or to a third type. Past type inference
|
||||||
|
* this function is meaningless.
|
||||||
|
*/
|
||||||
|
resolvableWith(other: Ty): boolean {
|
||||||
// types are interned--we can just do this
|
// types are interned--we can just do this
|
||||||
if (this === other) {
|
if (this === other) {
|
||||||
return true;
|
return true;
|
||||||
@ -64,10 +102,6 @@ export class Ty {
|
|||||||
if (this.is("Void")) {
|
if (this.is("Void")) {
|
||||||
return other.is("Void");
|
return other.is("Void");
|
||||||
}
|
}
|
||||||
if (this.is("IntLiteral")) {
|
|
||||||
// return other.is("Int") || other.is("UInt");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (this.is("Int")) {
|
if (this.is("Int")) {
|
||||||
return other.is("Int") && this.kind.intTy == other.kind.intTy;
|
return other.is("Int") && this.kind.intTy == other.kind.intTy;
|
||||||
}
|
}
|
||||||
@ -79,7 +113,7 @@ export class Ty {
|
|||||||
if (!other.is("Ptr")) {
|
if (!other.is("Ptr")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!this.kind.ty.compatibleWith(other.kind.ty)) {
|
if (!this.kind.ty.resolvableWith(other.kind.ty)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -88,7 +122,7 @@ export class Ty {
|
|||||||
if (!other.is("Array")) {
|
if (!other.is("Array")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!this.kind.ty.compatibleWith(other.kind.ty)) {
|
if (!this.kind.ty.resolvableWith(other.kind.ty)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.kind.length !== other.kind.length) {
|
if (this.kind.length !== other.kind.length) {
|
||||||
@ -100,7 +134,7 @@ export class Ty {
|
|||||||
if (!other.is("Slice")) {
|
if (!other.is("Slice")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!this.kind.ty.compatibleWith(other.kind.ty)) {
|
if (!this.kind.ty.resolvableWith(other.kind.ty)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -113,28 +147,26 @@ export class Ty {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (const i of this.kind.params.keys()) {
|
for (const i of this.kind.params.keys()) {
|
||||||
if (!this.kind.params[i].compatibleWith(other.kind.params[i])) {
|
if (!this.kind.params[i].resolvableWith(other.kind.params[i])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this.kind.retTy.compatibleWith(other.kind.retTy)) {
|
if (!this.kind.retTy.resolvableWith(other.kind.retTy)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.is("FnStmt")) {
|
if (this.is("FnStmt")) {
|
||||||
if (!other.is("FnStmt")) {
|
// Since FnStmt tys are only compatible with itself,
|
||||||
|
// we can count on the ty cache for this check.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!this.kind.ty.compatibleWith(other.kind.ty)) {
|
if (this.is("Any")) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// redundant; sanity check
|
|
||||||
if (this.kind.stmt.id !== other.kind.stmt.id) {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (this.is("AnyInt")) {
|
||||||
|
return other.is("Int");
|
||||||
|
}
|
||||||
throw new Error(`'${this.kind.tag}' not handled`);
|
throw new Error(`'${this.kind.tag}' not handled`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +177,15 @@ export class Ty {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
innerFn(): Ty & { kind: { tag: "Fn" } } {
|
||||||
|
this.assertIs("FnStmt");
|
||||||
|
return this.kind.ty.as("Fn");
|
||||||
|
}
|
||||||
|
|
||||||
|
isCheckerInternal(): boolean {
|
||||||
|
return this.is("Any") || this.is("AnyInt");
|
||||||
|
}
|
||||||
|
|
||||||
pretty(colors?: stringify.PrettyColors): string {
|
pretty(colors?: stringify.PrettyColors): string {
|
||||||
return stringify.tyPretty(this, colors);
|
return stringify.tyPretty(this, colors);
|
||||||
}
|
}
|
||||||
@ -153,7 +194,6 @@ export class Ty {
|
|||||||
export type TyKind =
|
export type TyKind =
|
||||||
| { tag: "Error" }
|
| { tag: "Error" }
|
||||||
| { tag: "Void" }
|
| { tag: "Void" }
|
||||||
| { tag: "IntLiteral" }
|
|
||||||
| { tag: "Int"; intTy: ast.IntTy }
|
| { tag: "Int"; intTy: ast.IntTy }
|
||||||
| { tag: "Bool" }
|
| { tag: "Bool" }
|
||||||
| { tag: "Ptr"; ty: Ty }
|
| { tag: "Ptr"; ty: Ty }
|
||||||
@ -162,4 +202,5 @@ export type TyKind =
|
|||||||
| { tag: "Slice"; ty: Ty }
|
| { tag: "Slice"; ty: Ty }
|
||||||
| { tag: "Range" }
|
| { tag: "Range" }
|
||||||
| { tag: "Fn"; params: Ty[]; retTy: Ty }
|
| { tag: "Fn"; params: Ty[]; retTy: Ty }
|
||||||
| { tag: "FnStmt"; ty: Ty; stmt: ast.NodeWithKind<"FnStmt"> };
|
| { tag: "FnStmt"; ty: Ty; stmt: ast.NodeWithKind<"FnStmt"> }
|
||||||
|
| { tag: "Any" | "AnyInt" };
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user