diff --git a/lib/go/scoreboard.json b/lib/go/scoreboard.json index 4aaa3cab..f8361de8 100644 --- a/lib/go/scoreboard.json +++ b/lib/go/scoreboard.json @@ -1,11 +1,11 @@ { "language": "go", - "total_pass": 345, - "total": 345, + "total_pass": 352, + "total": 352, "suites": [ {"name":"lex","pass":129,"total":129,"status":"ok"}, {"name":"parse","pass":176,"total":176,"status":"ok"}, - {"name":"types","pass":40,"total":40,"status":"ok"}, + {"name":"types","pass":47,"total":47,"status":"ok"}, {"name":"eval","pass":0,"total":0,"status":"pending"}, {"name":"runtime","pass":0,"total":0,"status":"pending"}, {"name":"stdlib","pass":0,"total":0,"status":"pending"}, diff --git a/lib/go/scoreboard.md b/lib/go/scoreboard.md index 385464cd..5c69aeb4 100644 --- a/lib/go/scoreboard.md +++ b/lib/go/scoreboard.md @@ -1,12 +1,12 @@ # Go-on-SX Scoreboard -**Total: 345 / 345 tests passing** +**Total: 352 / 352 tests passing** | | Suite | Pass | Total | |---|---|---|---| | ✅ | lex | 129 | 129 | | ✅ | parse | 176 | 176 | -| ✅ | types | 40 | 40 | +| ✅ | types | 47 | 47 | | ⬜ | eval | 0 | 0 | | ⬜ | runtime | 0 | 0 | | ⬜ | stdlib | 0 | 0 | diff --git a/lib/go/tests/types.sx b/lib/go/tests/types.sx index 896ad0c9..7496673f 100644 --- a/lib/go/tests/types.sx +++ b/lib/go/tests/types.sx @@ -264,6 +264,68 @@ (list (go-ctx-lookup ctx "a") (go-ctx-lookup ctx "b"))) (list (list :ty-name "int") (list :ty-name "int"))) +(go-types-test + "fdecl: func empty() — binds empty to func type" + (go-ctx-lookup + (go-check-decl go-ctx-empty (go-parse "func empty() {}")) + "empty") + (list :ty-func (list) (list))) + +(go-types-test + "fdecl: func add(x, y int) int { return x + y } — ok" + (go-ctx-lookup + (go-check-decl + go-ctx-empty + (go-parse "func add(x, y int) int { return x + y }")) + "add") + (list + :ty-func (list (list :ty-name "int") (list :ty-name "int")) + (list (list :ty-name "int")))) + +(go-types-test + "fdecl: func bad() int { return \"hi\" } — type error" + (go-check-decl go-ctx-empty (go-parse "func bad() int { return \"hi\" }")) + (list + :type-error :mismatch + (list :ty-name "int") + (list :ty-untyped-string))) + +(go-types-test + "fdecl: signature-only (no body)" + (go-ctx-lookup + (go-check-decl go-ctx-empty (go-parse "func sig(x int) int")) + "sig") + (list :ty-func (list (list :ty-name "int")) (list (list :ty-name "int")))) + +(go-types-test + "fdecl: param-bound — body sees x and y" + (go-ctx-lookup + (go-check-decl + go-ctx-empty + (go-parse "func sumsq(x, y int) int { return x*x + y*y }")) + "sumsq") + (list :ty-func + (list (list :ty-name "int") (list :ty-name "int")) + (list (list :ty-name "int")))) + +(go-types-test + "fdecl: nested decl in body extends ctx for later stmts" + (go-ctx-lookup + (go-check-decl + go-ctx-empty + (go-parse "func two() int { var x int = 1; var y int = 2; return x + y }")) + "two") + (list :ty-func (list) (list (list :ty-name "int")))) + +(go-types-test + "fdecl: assign inside body — type-checks RHS vs LHS" + (go-ctx-lookup + (go-check-decl + go-ctx-empty + (go-parse "func g() int { var x int; x = 5; return x }")) + "g") + (list :ty-func (list) (list (list :ty-name "int")))) + (define go-types-test-summary (str "types " go-types-test-pass "/" go-types-test-count)) diff --git a/lib/go/types.sx b/lib/go/types.sx index 98002a2f..cfa6d7ce 100644 --- a/lib/go/types.sx +++ b/lib/go/types.sx @@ -415,4 +415,148 @@ (and (list? decl) (= (first decl) :type-decl)) (let ((name (nth decl 1)) (ty (nth decl 2))) (go-ctx-extend ctx name ty)) + (and (list? decl) (= (first decl) :func-decl)) + (go-check-func-decl ctx decl) :else ctx))) + +;; ── function-decl checking ────────────────────────────────────── + +(define + go-repeat-ty + (fn (n ty acc) + (cond + (<= n 0) acc + :else (go-repeat-ty (- n 1) ty (cons ty acc))))) + +(define + go-decl-params-to-ty-list + ;; Flatten (:field NAMES TYPE) param groups into a list of types, + ;; one entry per name. For func-type signatures. + (fn (params) + (cond + (or (= params nil) (= (len params) 0)) (list) + :else + (let ((field (first params))) + (let ((names (nth field 1)) (ty (nth field 2))) + (let ((rest-tys (go-decl-params-to-ty-list (rest params)))) + (go-repeat-ty (len names) ty rest-tys))))))) + +(define + go-extend-with-params + ;; Extend CTX with every binding in every (:field NAMES TYPE) param group. + (fn (ctx params) + (cond + (or (= params nil) (= (len params) 0)) ctx + :else + (go-extend-with-params + (go-ctx-extend-field ctx (first params)) + (rest params))))) + +(define + go-check-return-list + ;; Each EXPR assignable to the corresponding RESULTS type. + ;; v0: lengths must match; multi-return funcs deferred. + (fn (ctx exprs results) + (cond + (and (= (len exprs) 0) (= (len results) 0)) :ok + (not (= (len exprs) (len results))) + (list :type-error :return-count-mismatch + (len exprs) (len results)) + :else + (let ((r (go-check ctx (first exprs) (first results)))) + (cond + (go-type-error? r) r + :else (go-check-return-list ctx (rest exprs) (rest results))))))) + +(define + go-check-assign + (fn (ctx stmt) + (let ((lhs-list (nth stmt 1)) (rhs-list (nth stmt 2))) + (cond + (not (= (len lhs-list) (len rhs-list))) + (list :type-error :assign-count-mismatch + (len lhs-list) (len rhs-list)) + :else (go-check-assign-pairs ctx lhs-list rhs-list))))) + +(define + go-check-assign-pairs + (fn (ctx lhs-list rhs-list) + (cond + (= (len lhs-list) 0) :ok + :else + (let ((lhs-ty (go-synth ctx (first lhs-list)))) + (cond + (go-type-error? lhs-ty) lhs-ty + :else + (let ((r (go-check ctx (first rhs-list) lhs-ty))) + (cond + (go-type-error? r) r + :else + (go-check-assign-pairs ctx (rest lhs-list) + (rest rhs-list))))))))) + +(define + go-check-stmt + ;; Returns either an extended CTX (decls), :ok (sealed stmts), or + ;; :type-error. RESULTS is the enclosing func's declared return types + ;; (used by :return). + (fn (ctx stmt results) + (cond + (and (list? stmt) (= (first stmt) :var-decl)) + (go-check-decl ctx stmt) + (and (list? stmt) (= (first stmt) :const-decl)) + (go-check-decl ctx stmt) + (and (list? stmt) (= (first stmt) :short-decl)) + (go-check-decl ctx stmt) + (and (list? stmt) (= (first stmt) :type-decl)) + (go-check-decl ctx stmt) + (and (list? stmt) (= (first stmt) :return)) + (let ((exprs (nth stmt 1))) + (let ((err (go-check-return-list ctx exprs results))) + (cond (go-type-error? err) err :else ctx))) + (and (list? stmt) (= (first stmt) :block)) + (let ((err (go-check-block ctx (nth stmt 1) results))) + (cond (go-type-error? err) err :else ctx)) + (and (list? stmt) (= (first stmt) :assign)) + (let ((err (go-check-assign ctx stmt))) + (cond (go-type-error? err) err :else ctx)) + :else + (let ((t (go-synth ctx stmt))) + (cond (go-type-error? t) t :else ctx))))) + +(define + go-check-block + ;; Thread ctx through stmts; if any stmt is a decl, its extension + ;; propagates to subsequent stmts. Returns :ok or :type-error. + (fn (ctx stmts results) + (cond + (or (= stmts nil) (= (len stmts) 0)) :ok + :else + (let ((r (go-check-stmt ctx (first stmts) results))) + (cond + (go-type-error? r) r + :else (go-check-block r (rest stmts) results)))))) + +(define + go-check-func-decl + ;; Bind the function in the outer ctx (so recursion works), extend + ;; ctx with params, check the body. Returns the outer ctx with the + ;; function bound, or :type-error. + (fn (ctx decl) + (let ((name (nth decl 1)) (params (nth decl 2)) + (results (nth decl 3)) (body (nth decl 4))) + (let ((fn-ty + (list :ty-func + (go-decl-params-to-ty-list params) results))) + (let ((ctx-with-fn (go-ctx-extend ctx name fn-ty))) + (cond + (= body nil) ctx-with-fn + (and (list? body) (= (first body) :block)) + (let ((body-ctx + (go-extend-with-params ctx-with-fn params))) + (let ((err + (go-check-block body-ctx (nth body 1) results))) + (cond + (go-type-error? err) err + :else ctx-with-fn))) + :else ctx-with-fn)))))) diff --git a/plans/go-on-sx.md b/plans/go-on-sx.md index fdae1796..b7077179 100644 --- a/plans/go-on-sx.md +++ b/plans/go-on-sx.md @@ -234,8 +234,11 @@ Progress-log line → push `origin/loops/go`. the extended context or a `:type-error`. Untyped synthesized types get their default-type (`untyped-int → int`, `untyped-float → float64`, etc.) when bound in inferred-type decls. -- [ ] Function declaration: extend ctx with params via `:field` group, - check body, verify return-list types match signature. +- [x] Function declaration: extends ctx with each `:field` param group, + checks block body (decls thread through, returns verify against + signature, assignments verify RHS assignable to LHS). The function + itself is bound in the body's ctx so recursion will work once + call-checking lands. Signature-only (no body) just binds. - [ ] Call type-checking (synth callee, check args against param types, synth result). - [ ] Composite type element checking (slice / map / chan). @@ -243,7 +246,7 @@ Progress-log line → push `origin/loops/go`. - [ ] Short variable declaration `:=` (synth RHS into LHS bindings). - Defer: generics (Phase 7), full conversion rules, type assertions, type switches. -- **Acceptance:** types/ suite at 60+ tests. Current: 40/40. Chisel note +- **Acceptance:** types/ suite at 60+ tests. Current: 47/47. Chisel note `shapes-static-types-bidirectional` — sister-plan design diary is the cross-language record. @@ -544,6 +547,19 @@ Minimal repro: see `lib/go/lex.sx#gl-oct-digit?` and `#gl-match-op`. _Newest first. Append one dated entry per commit._ +- 2026-05-27 — Phase 3 cont.: function-declaration checking + + statement-level dispatch. `go-check-func-decl` binds the function in + the outer ctx (so the body can see itself), extends the body's ctx + with each `:field` param group via `go-ctx-extend-field` (the + binding-group shape's third consumer in the type checker — now + five total across parser+typer combined), then runs `go-check-block` + through every statement. `go-check-stmt` dispatches on `:return`, + `:assign`, `:var-decl`/`:const-decl`/`:short-decl`/`:type-decl`, + `:block`, falling back to `go-synth` for expression statements. + Return-list and assign-pair count mismatches are typed errors. +7 + tests, types 47/47, total 352/352. `[nothing]` — pure Go-side + composition; the kit-relevant insights are already in the sister- + plan diary. - 2026-05-27 — Phase 3 cont.: declaration checking — `var`/`const`/`type` + short-decl `:=`. `go-check-decl` returns the extended context (or a `:type-error`). New helpers: `go-default-type` (untyped-int → int,