go: types.sx — call type-checking + 8 tests; recursive funcs now type [nothing]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 36s

Phase 3 cont. The expression-synth :app dispatch is now bifurcated:

  * go-is-binop-call? — head is :var with an operator name AND 2 args
    AND the operator is in one of the binop tables. Short-circuits to
    go-synth-binop as before.
  * Everything else routes to go-synth-call.

go-synth-call:
  1. Synth the callee. Must produce a (list :ty-func PARAMS RESULTS).
     Otherwise → (:type-error :not-callable TYPE).
  2. Arity-check args vs params. Mismatch → (:type-error :arity-mismatch).
  3. go-check-args-against: each arg assignable to corresponding param
     (untyped-constant flow works — `f(42)` accepts the untyped int
     into an int param).
  4. Result by count:
       0 results → (list :ty-void)
       1 result  → that result directly
       N results → (list :ty-tuple TYPES)   for multi-return

The recursive case lights up: go-check-func-decl binds the function
in its own body's ctx before checking. So:

  func fib(n int) int { return fib(n) + fib(n) }

now type-checks because `fib` resolves inside the body, synth-call
sees its `:ty-func` and verifies the recursive call. Multi-return
functions destructure into `:ty-tuple` which short-decl will need to
consume next iteration.

types 55/55, total 360/360.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 20:56:10 +00:00
parent 9f4c6787e4
commit 5b4a8be689
5 changed files with 173 additions and 15 deletions

View File

@@ -1,11 +1,11 @@
{ {
"language": "go", "language": "go",
"total_pass": 352, "total_pass": 360,
"total": 352, "total": 360,
"suites": [ "suites": [
{"name":"lex","pass":129,"total":129,"status":"ok"}, {"name":"lex","pass":129,"total":129,"status":"ok"},
{"name":"parse","pass":176,"total":176,"status":"ok"}, {"name":"parse","pass":176,"total":176,"status":"ok"},
{"name":"types","pass":47,"total":47,"status":"ok"}, {"name":"types","pass":55,"total":55,"status":"ok"},
{"name":"eval","pass":0,"total":0,"status":"pending"}, {"name":"eval","pass":0,"total":0,"status":"pending"},
{"name":"runtime","pass":0,"total":0,"status":"pending"}, {"name":"runtime","pass":0,"total":0,"status":"pending"},
{"name":"stdlib","pass":0,"total":0,"status":"pending"}, {"name":"stdlib","pass":0,"total":0,"status":"pending"},

View File

@@ -1,12 +1,12 @@
# Go-on-SX Scoreboard # Go-on-SX Scoreboard
**Total: 352 / 352 tests passing** **Total: 360 / 360 tests passing**
| | Suite | Pass | Total | | | Suite | Pass | Total |
|---|---|---|---| |---|---|---|---|
| ✅ | lex | 129 | 129 | | ✅ | lex | 129 | 129 |
| ✅ | parse | 176 | 176 | | ✅ | parse | 176 | 176 |
| ✅ | types | 47 | 47 | | ✅ | types | 55 | 55 |
| ⬜ | eval | 0 | 0 | | ⬜ | eval | 0 | 0 |
| ⬜ | runtime | 0 | 0 | | ⬜ | runtime | 0 | 0 |
| ⬜ | stdlib | 0 | 0 | | ⬜ | stdlib | 0 | 0 |

View File

@@ -326,6 +326,95 @@
"g") "g")
(list :ty-func (list) (list (list :ty-name "int")))) (list :ty-func (list) (list (list :ty-name "int"))))
(go-types-test
"call: synth result of typed func"
(go-synth
(go-ctx-extend
go-ctx-empty
"double"
(list
:ty-func (list (list :ty-name "int"))
(list (list :ty-name "int"))))
(go-parse "double(5)"))
(list :ty-name "int"))
(go-types-test
"call: arg-count mismatch"
(go-synth
(go-ctx-extend
go-ctx-empty
"double"
(list
:ty-func (list (list :ty-name "int"))
(list (list :ty-name "int"))))
(go-parse "double(1, 2)"))
(list :type-error :arity-mismatch 1 2))
(go-types-test
"call: arg-type mismatch"
(go-synth
(go-ctx-extend
go-ctx-empty
"f"
(list
:ty-func (list (list :ty-name "int"))
(list (list :ty-name "int"))))
(go-parse "f(\"hi\")"))
(list
:type-error :mismatch
(list :ty-name "int")
(list :ty-untyped-string)))
(go-types-test
"call: not callable (calling an int)"
(go-synth
(go-ctx-extend go-ctx-empty "x" (list :ty-name "int"))
(go-parse "x(1)"))
(list :type-error :not-callable (list :ty-name "int")))
(go-types-test
"call: no-result func (void) call"
(go-synth
(go-ctx-extend
go-ctx-empty
"log"
(list :ty-func (list (list :ty-name "string")) (list)))
(go-parse "log(\"hi\")"))
(list :ty-void))
(go-types-test
"call: multi-return → :ty-tuple"
(go-synth
(go-ctx-extend
go-ctx-empty
"divmod"
(list
:ty-func (list (list :ty-name "int") (list :ty-name "int"))
(list (list :ty-name "int") (list :ty-name "int"))))
(go-parse "divmod(10, 3)"))
(list :ty-tuple (list (list :ty-name "int") (list :ty-name "int"))))
(go-types-test
"call: recursive func works (fib)"
(go-ctx-lookup
(go-check-decl
go-ctx-empty
(go-parse "func fib(n int) int { return fib(n) + fib(n) }"))
"fib")
(list :ty-func (list (list :ty-name "int")) (list (list :ty-name "int"))))
(go-types-test
"call: untyped-int arg accepted into int param"
(go-synth
(go-ctx-extend
go-ctx-empty
"double"
(list
:ty-func (list (list :ty-name "int"))
(list (list :ty-name "int"))))
(go-parse "double(42)"))
(list :ty-name "int"))
(define (define
go-types-test-summary go-types-test-summary
(str "types " go-types-test-pass "/" go-types-test-count)) (str "types " go-types-test-pass "/" go-types-test-count))

View File

@@ -253,15 +253,67 @@
(cond (cond
(= t nil) (list :type-error :unbound name) (= t nil) (list :type-error :unbound name)
:else t))))) :else t)))))
;; (:app (:var OP) [LHS RHS]) — binary operator ;; (:app HEAD ARGS) — function application:
(and (list? expr) (= (first expr) :app) ;; binop if HEAD is :var with an operator name + 2 args
(list? (nth expr 1)) (= (first (nth expr 1)) :var) ;; else: general function call
(= (len (nth expr 2)) 2)) (and (list? expr) (= (first expr) :app))
(let ((op (nth (nth expr 1) 1)) (let ((head (nth expr 1)) (args (nth expr 2)))
(args (nth expr 2))) (cond
(go-synth-binop ctx op (first args) (nth args 1))) (go-is-binop-call? head args)
(go-synth-binop ctx (nth head 1) (first args) (nth args 1))
:else (go-synth-call ctx head args)))
:else (list :type-error :unsupported-synth expr)))) :else (list :type-error :unsupported-synth expr))))
(define
go-is-binop-call?
(fn (head args)
(and (list? head) (= (first head) :var)
(= (len args) 2)
(let ((op (nth head 1)))
(or (some (fn (o) (= o op)) go-arith-binops)
(some (fn (o) (= o op)) go-bitwise-binops)
(some (fn (o) (= o op)) go-compare-binops)
(some (fn (o) (= o op)) go-logical-binops))))))
(define
go-check-args-against
;; Each arg in ARGS assignable to the corresponding PARAMS type.
;; Caller already verified arities match.
(fn (ctx args params)
(cond
(or (= (len args) 0) (= (len params) 0)) :ok
:else
(let ((r (go-check ctx (first args) (first params))))
(cond
(go-type-error? r) r
:else (go-check-args-against ctx (rest args) (rest params)))))))
(define
go-synth-call
;; Synth a function call. Returns the result type, or :type-error.
;; 0 results → (list :ty-void)
;; 1 result → that result type directly
;; N results → (list :ty-tuple TYPES) (multi-return)
(fn (ctx callee args)
(let ((fn-ty (go-synth ctx callee)))
(cond
(go-type-error? fn-ty) fn-ty
(not (and (list? fn-ty) (= (first fn-ty) :ty-func)))
(list :type-error :not-callable fn-ty)
:else
(let ((params (nth fn-ty 1)) (results (nth fn-ty 2)))
(cond
(not (= (len args) (len params)))
(list :type-error :arity-mismatch
(len params) (len args))
:else
(let ((err (go-check-args-against ctx args params)))
(cond
(go-type-error? err) err
(= (len results) 0) (list :ty-void)
(= (len results) 1) (first results)
:else (list :ty-tuple results)))))))))
(define (define
go-synth-binop go-synth-binop
(fn (ctx op lhs rhs) (fn (ctx op lhs rhs)

View File

@@ -239,14 +239,17 @@ Progress-log line → push `origin/loops/go`.
signature, assignments verify RHS assignable to LHS). The function signature, assignments verify RHS assignable to LHS). The function
itself is bound in the body's ctx so recursion will work once itself is bound in the body's ctx so recursion will work once
call-checking lands. Signature-only (no body) just binds. call-checking lands. Signature-only (no body) just binds.
- [ ] Call type-checking (synth callee, check args against param types, - [x] Call type-checking. `go-synth-call`: synth callee → expect
synth result). `:ty-func`, arity-check, check each arg assignable to param,
then return type by result count (0 → `:ty-void`, 1 → that type,
N → `:ty-tuple`). Recursive calls now type-check because the func
is bound in the body's ctx. Untyped-constant args flow through.
- [ ] Composite type element checking (slice / map / chan). - [ ] Composite type element checking (slice / map / chan).
- [ ] Interface satisfaction (structural match against method sets). - [ ] Interface satisfaction (structural match against method sets).
- [ ] Short variable declaration `:=` (synth RHS into LHS bindings). - [ ] Short variable declaration `:=` (synth RHS into LHS bindings).
- Defer: generics (Phase 7), full conversion rules, type assertions, - Defer: generics (Phase 7), full conversion rules, type assertions,
type switches. type switches.
- **Acceptance:** types/ suite at 60+ tests. Current: 47/47. Chisel note - **Acceptance:** types/ suite at 60+ tests. Current: 55/55. Chisel note
`shapes-static-types-bidirectional` — sister-plan design diary is the `shapes-static-types-bidirectional` — sister-plan design diary is the
cross-language record. cross-language record.
@@ -547,6 +550,20 @@ Minimal repro: see `lib/go/lex.sx#gl-oct-digit?` and `#gl-match-op`.
_Newest first. Append one dated entry per commit._ _Newest first. Append one dated entry per commit._
- 2026-05-27 — Phase 3 cont.: call type-checking. `go-synth-call`
synthesises the callee's type, asserts it's a `:ty-func`, arity-
checks args, then `go-check-args-against` runs each arg through
`go-check` against the corresponding param type (untyped-constant
flow works). Result: `:ty-void` for 0-result funcs, the result type
for 1-result, `(list :ty-tuple TYPES)` for multi-return. The
`:app` dispatch in `go-synth` now routes via `go-is-binop-call?`
(operator name + 2 args + op in the binop tables) — binops short-
circuit; everything else goes through the call path. **Recursive
functions now type-check** because the func is bound in its own
body's ctx by `go-check-func-decl`. +8 tests, types 55/55, total
360/360. `[nothing]` — Go-side composition on top of established
primitives; no new kit-relevant shapes (call semantics are uniform
across statically-typed guests).
- 2026-05-27 — Phase 3 cont.: function-declaration checking + - 2026-05-27 — Phase 3 cont.: function-declaration checking +
statement-level dispatch. `go-check-func-decl` binds the function in 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 the outer ctx (so the body can see itself), extends the body's ctx