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

@@ -253,15 +253,67 @@
(cond
(= t nil) (list :type-error :unbound name)
:else t)))))
;; (:app (:var OP) [LHS RHS]) — binary operator
(and (list? expr) (= (first expr) :app)
(list? (nth expr 1)) (= (first (nth expr 1)) :var)
(= (len (nth expr 2)) 2))
(let ((op (nth (nth expr 1) 1))
(args (nth expr 2)))
(go-synth-binop ctx op (first args) (nth args 1)))
;; (:app HEAD ARGS) — function application:
;; binop if HEAD is :var with an operator name + 2 args
;; else: general function call
(and (list? expr) (= (first expr) :app))
(let ((head (nth expr 1)) (args (nth expr 2)))
(cond
(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))))
(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
go-synth-binop
(fn (ctx op lhs rhs)