From f09858a41d5ff41c7c8f119ef6ba921aa9ed889b Mon Sep 17 00:00:00 2001
From: SimonFJ20 <simonfromjakobsen@gmail.com>
Date: Wed, 5 Feb 2025 14:41:29 +0100
Subject: [PATCH] compiler: make reasonable resolving

---
 slige/compiler/check/checker.ts    | 89 ++++++++++++++++++++++++++----
 slige/compiler/common/ids.ts       | 70 +++++++++++++++++++++++
 slige/compiler/middle/ast_lower.ts |  6 +-
 slige/compiler/resolve/cx.ts       | 15 +++--
 slige/compiler/resolve/resolver.ts | 42 +++++++++-----
 5 files changed, 185 insertions(+), 37 deletions(-)

diff --git a/slige/compiler/check/checker.ts b/slige/compiler/check/checker.ts
index 51209c7..eb67b33 100644
--- a/slige/compiler/check/checker.ts
+++ b/slige/compiler/check/checker.ts
@@ -5,24 +5,28 @@ import {
     exhausted,
     File,
     IdMap,
+    IdSet,
+    Ok,
     Res,
     Span,
     todo,
 } from "@slige/common";
-import { Resols } from "@slige/resolve";
+import * as resolve from "@slige/resolve";
 import { Ty, tyToString } from "@slige/ty";
 
 export class Checker {
+    private stmtChecked = new IdSet<AstId>();
     private itemTys = new IdMap<AstId, Ty>();
     private exprTys = new IdMap<AstId, Ty>();
     private tyTys = new IdMap<AstId, Ty>();
+    private patTys = new IdMap<AstId, Ty>();
 
     private currentFile: File;
 
     public constructor(
         private ctx: Ctx,
         private entryFileAst: ast.File,
-        private resols: Resols,
+        private resols: resolve.Resols,
     ) {
         this.currentFile = ctx.entryFile();
     }
@@ -37,8 +41,51 @@ export class Checker {
     private checkStmts(stmts: ast.Stmt[]) {
     }
 
+    private checkLetStmtTy(stmt: ast.Stmt, kind: ast.LetStmt) {
+        if (this.stmtChecked.has(stmt.id)) {
+            return;
+        }
+
+        const exprTy = kind.expr && Ok(this.exprTy(kind.expr));
+        const tyTy = kind.ty && Ok(this.tyTy(kind.ty));
+        const ty = exprTy && tyTy && this.resolveTys(exprTy.val, tyTy.val);
+
+        if (ty === undefined) {
+            this.report("type amfibious, specify type or value", stmt.span);
+            return Ty({ tag: "error" });
+        }
+
+        if (!ty.ok) {
+            this.report(ty.val, stmt.span);
+            return Ty({ tag: "error" });
+        }
+
+        const res = this.assignPatTy(kind.pat, ty.val);
+        if (!res.ok) {
+            this.report(res.val, stmt.span);
+            return Ty({ tag: "error" });
+        }
+
+        this.stmtChecked.add(stmt.id);
+    }
+
+    private assignPatTy(pat: ast.Pat, ty: Ty): Res<void, string> {
+        const k = pat.kind;
+        switch (k.tag) {
+            case "error":
+                // don't report, already reported
+                return Res.Ok(undefined);
+            case "bind":
+                this.patTys.set(pat.id, ty);
+                return Ok(undefined);
+            case "path":
+                return todo();
+        }
+        exhausted(k);
+    }
+
     public fnItemTy(item: ast.Item, kind: ast.FnItem): Ty {
-        return this.itemTys.get(item.id) || this.checkFnItem(item, kind);
+        return this.itemTys.get(item.id) ?? this.checkFnItem(item, kind);
     }
 
     private checkFnItem(item: ast.Item, kind: ast.FnItem): Ty {
@@ -128,7 +175,8 @@ export class Checker {
                 return resu.val;
             }
             case "local": {
-                const patResu = this.resols.patRes();
+                const patRes = this.resols.patRes(res.kind.id);
+                const ty = this.patTy(patRes.pat);
                 const resu = this.resolveTys(ty, expected);
                 if (!resu.ok) {
                     this.report(resu.val, expr.span);
@@ -166,13 +214,23 @@ export class Checker {
         exhausted(k);
     }
 
-    private report(msg: string, span: Span) {
-        this.ctx.report({
-            severity: "error",
-            file: this.currentFile,
-            span,
-            msg,
-        });
+    public patTy(pat: ast.Pat): Ty {
+        return this.patTys.get(pat.id) ||
+            this.checkPat(pat);
+    }
+
+    private checkPat(pat: ast.Pat): Ty {
+        const patRes = this.resols.patRes(pat.id);
+        const k = pat.kind;
+        switch (k.tag) {
+            case "error":
+                return todo();
+            case "bind":
+                return todo();
+            case "path":
+                return todo();
+        }
+        exhausted(k);
     }
 
     private resolveTys(a: Ty, b: Ty): Res<Ty, string> {
@@ -211,4 +269,13 @@ export class Checker {
         }
         exhausted(a.kind);
     }
+
+    private report(msg: string, span: Span) {
+        this.ctx.report({
+            severity: "error",
+            file: this.currentFile,
+            span,
+            msg,
+        });
+    }
 }
diff --git a/slige/compiler/common/ids.ts b/slige/compiler/common/ids.ts
index abcaade..f17fcec 100644
--- a/slige/compiler/common/ids.ts
+++ b/slige/compiler/common/ids.ts
@@ -1,3 +1,5 @@
+import { todo } from "./util.ts";
+
 //
 
 export type File = IdBase & { readonly _: unique symbol };
@@ -27,6 +29,74 @@ export class Ids<IdType extends IdBase> {
         return idFromRaw(rawId);
     }
 }
+export class IdSet<Id extends IdBase> implements Set<Id> {
+    private set = new Set<IdRaw<Id>>();
+
+    add(value: Id): this {
+        this.set.add(idRaw(value));
+        return this;
+    }
+    clear(): void {
+        this.set.clear();
+    }
+    delete(value: Id): boolean {
+        return this.set.delete(idRaw(value));
+    }
+    forEach(
+        callbackfn: (value: Id, value2: Id, set: Set<Id>) => void,
+        thisArg?: unknown,
+    ): void {
+        this.set.forEach(
+            (v1, v2) => callbackfn(idFromRaw(v1), idFromRaw(v2), this),
+            thisArg,
+        );
+    }
+    has(value: Id): boolean {
+        return this.set.has(idRaw(value));
+    }
+    get size(): number {
+        return this.set.size;
+    }
+    entries(): SetIterator<[Id, Id]> {
+        return this.set.entries()
+            .map(([v1, v2]) => [idFromRaw(v1), idFromRaw(v2)]);
+    }
+    keys(): SetIterator<Id> {
+        return this.set.keys()
+            .map((v) => idFromRaw(v));
+    }
+    values(): SetIterator<Id> {
+        return this.set.values()
+            .map((v) => idFromRaw(v));
+    }
+    union<U>(_other: ReadonlySetLike<U>): Set<Id | U> {
+        return todo();
+    }
+    intersection<U>(_other: ReadonlySetLike<U>): Set<Id & U> {
+        return todo();
+    }
+    difference<U>(_other: ReadonlySetLike<U>): Set<Id> {
+        return todo();
+    }
+    symmetricDifference<U>(_other: ReadonlySetLike<U>): Set<Id | U> {
+        return todo();
+    }
+    isSubsetOf(_other: ReadonlySetLike<unknown>): boolean {
+        return todo();
+    }
+    isSupersetOf(_other: ReadonlySetLike<unknown>): boolean {
+        return todo();
+    }
+    isDisjointFrom(_other: ReadonlySetLike<unknown>): boolean {
+        return todo();
+    }
+    [Symbol.iterator](): SetIterator<Id> {
+        return this.set[Symbol.iterator]().map((v) => idFromRaw(v));
+    }
+    get [Symbol.toStringTag](): string {
+        return this.set[Symbol.toStringTag];
+    }
+}
 
 export class IdMap<Id extends IdBase, V> implements Map<Id, V> {
     private map = new Map<IdRaw<Id>, V>();
diff --git a/slige/compiler/middle/ast_lower.ts b/slige/compiler/middle/ast_lower.ts
index 38824e9..2c970e1 100644
--- a/slige/compiler/middle/ast_lower.ts
+++ b/slige/compiler/middle/ast_lower.ts
@@ -1,7 +1,7 @@
 import * as ast from "@slige/ast";
 import { Checker } from "@slige/check";
-import { Ctx, exhausted, IdMap, Ids, Res, todo } from "@slige/common";
-import { LocalId as ReLocalId, Resols } from "@slige/resolve";
+import { AstId, Ctx, exhausted, IdMap, Ids, Res, todo } from "@slige/common";
+import { Resols } from "@slige/resolve";
 import { Ty } from "@slige/ty";
 import { BinaryType, Operand, StmtKind, TerKind } from "./mir.ts";
 import { Block, BlockId, Fn, Local, LocalId, RVal, Stmt, Ter } from "./mir.ts";
@@ -32,7 +32,7 @@ export class FnLowerer {
 
     private currentBlock?: Block;
 
-    private reLocals = new IdMap<ReLocalId, LocalId>();
+    private reLocals = new IdMap<AstId, LocalId>();
 
     public constructor(
         private ctx: Ctx,
diff --git a/slige/compiler/resolve/cx.ts b/slige/compiler/resolve/cx.ts
index d02a240..e782633 100644
--- a/slige/compiler/resolve/cx.ts
+++ b/slige/compiler/resolve/cx.ts
@@ -17,16 +17,15 @@ export type Resolve = {
 export type ResolveKind =
     | { tag: "error" }
     | { tag: "fn"; item: ast.Item; kind: ast.FnItem }
-    | { tag: "local"; id: LocalId };
+    | { tag: "local"; id: AstId };
 
-export type PatResolve =
-    | { tag: "param"; paramIdx: number }
-    | { tag: "let"; stmt: ast.Stmt; kind: ast.LetStmt };
+export type PatResolve = {
+    pat: ast.Pat;
+    kind: PatResolveKind;
+};
 
-export type LocalId = IdBase & { readonly _: unique symbol };
-
-export type Local =
-    | { tag: "param"; paramIdx: number }
+export type PatResolveKind =
+    | { tag: "fn_param"; paramIdx: number }
     | { tag: "let"; stmt: ast.Stmt; kind: ast.LetStmt };
 
 export const ResolveError = (ident: ast.Ident): Resolve => ({
diff --git a/slige/compiler/resolve/resolver.ts b/slige/compiler/resolve/resolver.ts
index 588267e..ecc0e71 100644
--- a/slige/compiler/resolve/resolver.ts
+++ b/slige/compiler/resolve/resolver.ts
@@ -1,16 +1,26 @@
 import * as ast from "@slige/ast";
-import { AstId, Ctx, exhausted, File, IdMap, Ids, todo } from "@slige/common";
+import {
+    AstId,
+    Ctx,
+    exhausted,
+    File,
+    IdMap,
+    Ids,
+    Res,
+    Span,
+    todo,
+} from "@slige/common";
 import {
     FnSyms,
-    LocalId,
     LocalSyms,
     PatResolve,
+    PatResolveKind,
+    Redef,
     Resolve,
     ResolveError,
     RootSyms,
     Syms,
 } from "./cx.ts";
-export { type LocalId } from "./cx.ts";
 
 export class Resols {
     public constructor(
@@ -41,9 +51,7 @@ export class Resolver implements ast.Visitor {
     private exprResols = new IdMap<AstId, Resolve>();
     private patResols = new IdMap<AstId, PatResolve>();
 
-    private patResolveStack: PatResolve[] = [];
-
-    private localIds = new Ids<LocalId>();
+    private patResolveStack: PatResolveKind[] = [];
 
     public constructor(
         private ctx: Ctx,
@@ -115,7 +123,7 @@ export class Resolver implements ast.Visitor {
             this.syms = new FnSyms(this.syms);
             this.syms = new LocalSyms(this.syms);
             for (const [paramIdx, param] of kind.params.entries()) {
-                this.patResolveStack.push({ tag: "param", paramIdx });
+                this.patResolveStack.push({ tag: "fn_param", paramIdx });
                 ast.visitParam(this, param);
                 this.patResolveStack.pop();
             }
@@ -164,19 +172,14 @@ export class Resolver implements ast.Visitor {
     }
 
     visitBindPat(pat: ast.Pat, kind: ast.BindPat): ast.VisitRes {
-        this.patResols.set(pat.id, this.patResolveStack.at(-1)!);
+        this.patResols.set(pat.id, { pat, kind: this.patResolveStack.at(-1)! });
         const res = this.syms.defVal(kind.ident, {
             tag: "local",
-            id: this.localIds.nextThenStep(),
+            id: pat.id,
         });
         if (!res.ok) {
             const text = this.ctx.identText(kind.ident.id);
-            this.ctx.report({
-                severity: "error",
-                file: this.currentFile,
-                span: kind.ident.span,
-                msg: `redefinition of value '${text}'`,
-            });
+            this.report(`redefinition of value '${text}'`, kind.ident.span);
         }
         return "stop";
     }
@@ -221,4 +224,13 @@ export class Resolver implements ast.Visitor {
             }, this.syms.getTy(path.segments[0].ident));
         return res;
     }
+
+    private report(msg: string, span: Span) {
+        this.ctx.report({
+            severity: "error",
+            file: this.currentFile,
+            span,
+            msg,
+        });
+    }
 }