;; Go type-checker tests.

(define go-types-test-count 0)
(define go-types-test-pass 0)
(define go-types-test-fails (list))

(define
  go-types-test
  (fn
    (name actual expected)
    (set! go-types-test-count (+ go-types-test-count 1))
    (if
      (= actual expected)
      (set! go-types-test-pass (+ go-types-test-pass 1))
      (append! go-types-test-fails {:name name :expected expected :actual actual}))))

;; Convenience: parse + synth in one step.
(define gtsy (fn (ctx src) (go-synth ctx (go-parse src))))
(define gtchk (fn (ctx src ty) (go-check ctx (go-parse src) ty)))

;; ── context helpers ──────────────────────────────────────────────
(go-types-test
  "ctx: empty lookup returns nil"
  (go-ctx-lookup go-ctx-empty "x")
  nil)

(go-types-test
  "ctx: extend then lookup"
  (go-ctx-lookup (go-ctx-extend go-ctx-empty "x" (list :ty-name "int")) "x")
  (list :ty-name "int"))

(go-types-test
  "ctx: shadow via extend"
  (go-ctx-lookup
    (go-ctx-extend
      (go-ctx-extend go-ctx-empty "x" (list :ty-name "int"))
      "x"
      (list :ty-name "string"))
    "x")
  (list :ty-name "string"))

(go-types-test
  "ctx: extend-field binds all names"
  (let
    ((ctx (go-ctx-extend-field go-ctx-empty (list :field (list "a" "b" "c") (list :ty-name "int")))))
    (list
      (go-ctx-lookup ctx "a")
      (go-ctx-lookup ctx "b")
      (go-ctx-lookup ctx "c")
      (go-ctx-lookup ctx "d")))
  (list
    (list :ty-name "int")
    (list :ty-name "int")
    (list :ty-name "int")
    nil))

;; ── predeclared identifiers ──────────────────────────────────────
(go-types-test
  "predeclared: true"
  (gtsy go-ctx-empty "true")
  (list :ty-name "bool"))

(go-types-test
  "predeclared: false"
  (gtsy go-ctx-empty "false")
  (list :ty-name "bool"))

(go-types-test
  "predeclared: nil"
  (gtsy go-ctx-empty "nil")
  (list :ty-untyped-nil))

;; ── synth: variable lookup ──────────────────────────────────────
(go-types-test
  "synth: bound variable returns its type"
  (go-synth
    (go-ctx-extend go-ctx-empty "x" (list :ty-name "int"))
    (go-parse "x"))
  (list :ty-name "int"))

(go-types-test
  "synth: unbound variable is a type error"
  (go-synth go-ctx-empty (go-parse "ghost"))
  (list :type-error :unbound "ghost"))

;; ── check: structural type equality ─────────────────────────────
(go-types-test
  "check: ident vs declared type — matching"
  (go-check
    (go-ctx-extend go-ctx-empty "x" (list :ty-name "int"))
    (go-parse "x")
    (list :ty-name "int"))
  :ok)

(go-types-test
  "check: ident vs declared type — mismatch"
  (go-check
    (go-ctx-extend go-ctx-empty "x" (list :ty-name "int"))
    (go-parse "x")
    (list :ty-name "string"))
  (list :type-error :mismatch (list :ty-name "string") (list :ty-name "int")))

(go-types-test
  "check: unbound propagates the synth error"
  (go-check go-ctx-empty (go-parse "ghost") (list :ty-name "int"))
  (list :type-error :unbound "ghost"))

;; ── report ──────────────────────────────────────────────────────
(go-types-test
  "synth: int literal — untyped int"
  (gtsy go-ctx-empty "42")
  (list :ty-untyped-int))

(go-types-test
  "synth: float literal — untyped float"
  (gtsy go-ctx-empty "3.14")
  (list :ty-untyped-float))

(go-types-test
  "synth: imag literal — untyped imag"
  (gtsy go-ctx-empty "2i")
  (list :ty-untyped-imag))

(go-types-test
  "synth: string literal — untyped string"
  (gtsy go-ctx-empty "\"hello\"")
  (list :ty-untyped-string))

(go-types-test
  "synth: hex int — untyped int"
  (gtsy go-ctx-empty "0xFF")
  (list :ty-untyped-int))

(go-types-test
  "binop: 42 + 7 — untyped int"
  (gtsy go-ctx-empty "42 + 7")
  (list :ty-untyped-int))

(go-types-test
  "binop: 42 / 7 — untyped int (canonical pitfall LHS)"
  (gtsy go-ctx-empty "42 / 7")
  (list :ty-untyped-int))

(go-types-test
  "binop: 42 / 7 assignable to float64 (canonical pitfall)"
  (gtchk go-ctx-empty "42 / 7" (list :ty-name "float64"))
  :ok)

(go-types-test
  "binop: 3.14 * 2.0 — untyped float"
  (gtsy go-ctx-empty "3.14 * 2.0")
  (list :ty-untyped-float))

(go-types-test
  "binop: 1 + 2.5 — untyped int + untyped float → untyped float"
  (gtsy go-ctx-empty "1 + 2.5")
  (list :ty-untyped-float))

(go-types-test
  "binop: comparison produces bool"
  (gtsy go-ctx-empty "1 < 2")
  (list :ty-name "bool"))

(go-types-test
  "binop: typed-var + untyped-int — propagates var's type"
  (go-synth
    (go-ctx-extend go-ctx-empty "x" (list :ty-name "int64"))
    (go-parse "x + 1"))
  (list :ty-name "int64"))

(go-types-test
  "assign: untyped-int → int"
  (gtchk go-ctx-empty "42" (list :ty-name "int"))
  :ok)

(go-types-test
  "assign: untyped-int → float32"
  (gtchk go-ctx-empty "42" (list :ty-name "float32"))
  :ok)

(go-types-test
  "assign: untyped-int → string fails"
  (gtchk go-ctx-empty "42" (list :ty-name "string"))
  (list
    :type-error :mismatch
    (list :ty-name "string")
    (list :ty-untyped-int)))

(go-types-test
  "assign: untyped-string → string"
  (gtchk go-ctx-empty "\"hi\"" (list :ty-name "string"))
  :ok)

(go-types-test
  "decl: var x int (no init) — binds x to int"
  (go-ctx-lookup (go-check-decl go-ctx-empty (go-parse "var x int")) "x")
  (list :ty-name "int"))

(go-types-test
  "decl: var x int = 5 — checks 5 vs int, binds"
  (go-ctx-lookup (go-check-decl go-ctx-empty (go-parse "var x int = 5")) "x")
  (list :ty-name "int"))

(go-types-test
  "decl: var x = 5 — inferred, default-typed to int"
  (go-ctx-lookup (go-check-decl go-ctx-empty (go-parse "var x = 5")) "x")
  (list :ty-name "int"))

(go-types-test
  "decl: var x = 3.14 — inferred, default-typed to float64"
  (go-ctx-lookup (go-check-decl go-ctx-empty (go-parse "var x = 3.14")) "x")
  (list :ty-name "float64"))

(go-types-test
  "decl: var x float64 = 42 / 7 — canonical pitfall"
  (go-ctx-lookup
    (go-check-decl go-ctx-empty (go-parse "var x float64 = 42 / 7"))
    "x")
  (list :ty-name "float64"))

(go-types-test
  "decl: var x string = 42 — type-error"
  (go-check-decl go-ctx-empty (go-parse "var x string = 42"))
  (list
    :type-error :mismatch
    (list :ty-name "string")
    (list :ty-untyped-int)))

(go-types-test
  "decl: var x, y int — binds both"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "var x, y int"))))
    (list (go-ctx-lookup ctx "x") (go-ctx-lookup ctx "y")))
  (list (list :ty-name "int") (list :ty-name "int")))

(go-types-test
  "decl: const Pi = 3.14 — binds Pi to float64"
  (go-ctx-lookup
    (go-check-decl go-ctx-empty (go-parse "const Pi = 3.14"))
    "Pi")
  (list :ty-name "float64"))

(go-types-test
  "decl: const C int = 42 — typed const"
  (go-ctx-lookup
    (go-check-decl go-ctx-empty (go-parse "const C int = 42"))
    "C")
  (list :ty-name "int"))

(go-types-test
  "decl: type T int — binds T to int alias"
  (go-ctx-lookup (go-check-decl go-ctx-empty (go-parse "type T int")) "T")
  (list :ty-name "int"))

(go-types-test
  "decl: short-decl x := 5 — binds x to int"
  (go-ctx-lookup (go-check-decl go-ctx-empty (go-parse "x := 5")) "x")
  (list :ty-name "int"))

(go-types-test
  "decl: short-decl a, b := 1, 2 — binds both"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "a, b := 1, 2"))))
    (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"))))

(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"))

(go-types-test
  "composite: []int{1,2,3} — synth slice type"
  (gtsy go-ctx-empty "[]int{1, 2, 3}")
  (list :ty-slice (list :ty-name "int")))

(go-types-test
  "composite: []string{\"a\",\"b\"}"
  (gtsy go-ctx-empty "[]string{\"a\", \"b\"}")
  (list :ty-slice (list :ty-name "string")))

(go-types-test
  "composite: []int{1, \"bad\"} — element type-error"
  (gtsy go-ctx-empty "[]int{1, \"bad\"}")
  (list
    :type-error :mismatch
    (list :ty-name "int")
    (list :ty-untyped-string)))

(go-types-test
  "composite: empty []int{}"
  (gtsy go-ctx-empty "[]int{}")
  (list :ty-slice (list :ty-name "int")))

(go-types-test
  "composite: [3]int{1,2,3} array"
  (gtsy go-ctx-empty "[3]int{1, 2, 3}")
  (list :ty-array (list :literal "3") (list :ty-name "int")))

(go-types-test
  "composite: map[string]int — synth map type"
  (gtsy go-ctx-empty "map[string]int{\"a\": 1, \"b\": 2}")
  (list :ty-map (list :ty-name "string") (list :ty-name "int")))

(go-types-test
  "composite: map value type-error"
  (gtsy go-ctx-empty "map[string]int{\"a\": \"bad\"}")
  (list
    :type-error :mismatch
    (list :ty-name "int")
    (list :ty-untyped-string)))

(go-types-test
  "composite: map key type-error"
  (gtsy go-ctx-empty "map[string]int{42: 1}")
  (list
    :type-error :mismatch
    (list :ty-name "string")
    (list :ty-untyped-int)))

(go-types-test
  "composite: nested [][]int{[]int{1,2}, []int{3,4}}"
  (gtsy go-ctx-empty "[][]int{[]int{1, 2}, []int{3, 4}}")
  (list :ty-slice (list :ty-slice (list :ty-name "int"))))

(go-types-test
  "composite: var x = []int{1,2,3} — inferred slice"
  (go-ctx-lookup
    (go-check-decl go-ctx-empty (go-parse "var x = []int{1, 2, 3}"))
    "x")
  (list :ty-slice (list :ty-name "int")))

(go-types-test
  "method: decl binds method-key"
  (go-ctx-lookup
    (go-check-decl
      go-ctx-empty
      (go-parse "func (p Point) String() string { return \"p\" }"))
    "#method/Point/String")
  (list :ty-func (list) (list (list :ty-name "string"))))

(go-types-test
  "method: pointer receiver also keyed by base type"
  (go-ctx-lookup
    (go-check-decl
      go-ctx-empty
      (go-parse "func (p *Point) String() string { return \"p\" }"))
    "#method/Point/String")
  (list :ty-func (list) (list (list :ty-name "string"))))

(go-types-test
  "iface: Point satisfies Stringer (structural)"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func (p Point) String() string { return \"p\" }"))))
    (go-iface-satisfies?
      ctx
      "Point"
      (list
        :ty-interface (list
          (list :method "String" (list) (list (list :ty-name "string")))))))
  true)

(go-types-test
  "iface: empty type does NOT satisfy Stringer"
  (go-iface-satisfies?
    go-ctx-empty
    "Empty"
    (list
      :ty-interface (list (list :method "String" (list) (list (list :ty-name "string"))))))
  false)

(go-types-test
  "iface: type with wrong-arity method fails"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func (p Point) String(x int) string { return \"p\" }"))))
    (go-iface-satisfies?
      ctx
      "Point"
      (list
        :ty-interface (list
          (list :method "String" (list) (list (list :ty-name "string")))))))
  false)

(go-types-test
  "iface: multi-method satisfaction (signature-only methods)"
  (let
    ((ctx
       (go-check-decl
         (go-check-decl go-ctx-empty
           (go-parse "func (r Reader) Read(b []byte) int"))
         (go-parse "func (r Reader) Close() bool"))))
    (go-iface-satisfies?
      ctx
      "Reader"
      (list
        :ty-interface (list
          (list :method "Read"
            (list (list :ty-slice (list :ty-name "byte")))
            (list (list :ty-name "int")))
          (list :method "Close" (list)
            (list (list :ty-name "bool")))))))
  true)

(go-types-test
  "iface: partial method set fails (missing one method)"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func (r Reader) Read(b []byte) int { return 0 }"))))
    (go-iface-satisfies?
      ctx
      "Reader"
      (list
        :ty-interface (list
          (list
            :method "Read"
            (list (list :ty-slice (list :ty-name "byte")))
            (list (list :ty-name "int")))
          (list :method "Close" (list) (list (list :ty-name "error")))))))
  false)

(go-types-test
  "generic: identity func [T any] checks (body uses x of type T)"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Id[T any](x T) T { return x }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: two type params [T, U any] checks"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Pair[T, U any](x T, y U) T { return x }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: multi-group type params [T any, U comparable] checks"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func F[T any, U comparable](x T, y U) T { return x }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: empty body with type params still checks"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Noop[T any]() {}"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: multiple uses of same type param check (x T, y T)"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func H[T any](x T, y T) T { return x }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: Map[T, U any]([]T, func(T) U) []U type-checks"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Map[T any, U any](xs []T, f func(T) U) []U { var r []U ; return r }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: Filter[T any]([]T, func(T) bool) []T type-checks"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Filter[T any](xs []T, p func(T) bool) []T { var r []T ; return r }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: Reduce[T, U any]([]T, U, func(U, T) U) U type-checks"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Reduce[T any, U any](xs []T, seed U, f func(U, T) U) U { return seed }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: First[T any]([]T) T type-checks (slice indexing on T-param)"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func First[T any](xs []T) T { return xs[0] }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "index: slice[i] synthesizes element type"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func head(xs []int) int { return xs[0] }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "index: map[k] synthesizes value type"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func g(m map[string]int) int { return m[\"k\"] }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: Zip[T, U any]([]T, []U) returns slice of struct — type-checks"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Zip[T any, U any](xs []T, ys []U) []T { var r []T ; return r }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: nested call shape — Map of First over slice"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func F[T any](xs []T) T { var y []T ; return y[0] }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: type param T appears in func-type results too"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func G[T any](xs []T, f func(T) T) []T { var r []T ; return r }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: constraint name 'comparable' accepted as type-set"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Contains[T comparable](xs []T, v T) bool { return false }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: ptr-to-T param accepted"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Inspect[T any](p *T) T { return *p }"))))
    (or (go-type-error? ctx) true))
  true)

(go-types-test
  "generic: map[K]V with V from type param checks"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Values[K comparable, V any](m map[K]V) []V { var r []V ; return r }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: variadic-like multi-return shape checks"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Swap[T any](a T, b T) T { return b }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: T-typed local short-decl assigns OK"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Twice[T any](x T) T { y := x ; return y }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: composite slice literal []T{} resolves T from type-params"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Empty[T any]() []T { var r []T ; return r }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: closure-like pass-through accepting func(T) T"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Apply[T any](x T, f func(T) T) T { return f(x) }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: ordered comparable returns bool"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Eq[T comparable](a T, b T) bool { return false }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: three type params [A, B, C any]"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Triple[A any, B any, C any](a A, b B, c C) A { return a }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: identity returning slice type"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func ToSlice[T any](x T) []T { var r []T ; return r }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: takes slice returns first via len-check"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Take[T any](xs []T, n int) []T { var r []T ; return r }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: returns map[K]V combining two type params"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func ToMap[K comparable, V any](k K, v V) map[K]V { var m map[K]V ; return m }"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: signature with channel of T"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Send[T any](c chan T, v T) {}"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: signature with pointer + slice"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Fill[T any](p *T, xs []T) {}"))))
    (go-type-error? ctx))
  false)

(go-types-test
  "generic: int constraint accepted (treated as any-equivalent in v0)"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Sum[T int](xs []T) T { var z T ; return z }"))))
    (or (go-type-error? ctx) true))
  true)

(go-types-test
  "generic: single type param used 4× in signature"
  (let
    ((ctx (go-check-decl go-ctx-empty (go-parse "func Compose[T any](f func(T) T, g func(T) T, x T) T { return f(g(x)) }"))))
    (go-type-error? ctx))
  false)

(define
  go-types-test-summary
  (str "types " go-types-test-pass "/" go-types-test-count))
