go: types.sx — composite-literal element checking; Phase 3 bar crossed + 10 tests [nothing]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 32s

Phase 3 cont. Adds composite-literal type-checking via go-synth-composite:

  []T{...}     — go-check-composite-elems with VAL-TY=T, KEY-TY=nil.
                 Each plain elem assignable to T; :kv element accepted
                 (Go's index-keyed shorthand: `[]int{0: 5, 1: 10}`)
                 with only the value checked.
  [N]T{...}    — same as slice; result :ty-array N T.
  map[K]V{...} — KEY-TY=K, VAL-TY=V. Each :kv pair: key assignable
                 to K, value to V. Non-:kv elements in maps are
                 (:type-error :map-elem-missing-key).

The literal's *synthesised* type is the type expression itself, so
nested composites fall out by recursion:

  [][]int{[]int{1,2}, []int{3,4}}
    → outer: go-check-composite-elems with VAL-TY=[]int
    → each inner []int{1,2} goes through go-synth-composite recursively,
      yielding :ty-slice :ty-name "int" — assignable-equal to VAL-TY.

Coverage: positive cases (homogeneous slices/arrays/maps, empty
slice, nested), and three negative cases (slice element mismatch,
map key mismatch, map value mismatch). Also a decl test:
  var x = []int{1, 2, 3}  →  binds x to :ty-slice :ty-name "int"

Named-type literals (`Point{1,2}`, `pkg.T{...}`) need type-decl-driven
field resolution; deferred. Interface satisfaction and AST-path error
context also remain — neither gates Phase 4.

**Phase 3 acceptance bar (60+) crossed: types 65/65, total 370/370.**

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 20:59:38 +00:00
parent 5b4a8be689
commit 4bd9262060
5 changed files with 150 additions and 7 deletions

View File

@@ -1,11 +1,11 @@
{
"language": "go",
"total_pass": 360,
"total": 360,
"total_pass": 370,
"total": 370,
"suites": [
{"name":"lex","pass":129,"total":129,"status":"ok"},
{"name":"parse","pass":176,"total":176,"status":"ok"},
{"name":"types","pass":55,"total":55,"status":"ok"},
{"name":"types","pass":65,"total":65,"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"},

View File

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

View File

@@ -415,6 +415,67 @@
(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")))
(define
go-types-test-summary
(str "types " go-types-test-pass "/" go-types-test-count))

View File

@@ -262,6 +262,9 @@
(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)))
;; (:composite TYPE-OR-EXPR ELEMS) — composite literal
(and (list? expr) (= (first expr) :composite))
(go-synth-composite ctx (nth expr 1) (nth expr 2))
:else (list :type-error :unsupported-synth expr))))
(define
@@ -288,6 +291,65 @@
(go-type-error? r) r
:else (go-check-args-against ctx (rest args) (rest params)))))))
(define
go-check-composite-elems
;; KEY-TY is nil for slice/array; non-nil for map.
;; For maps, each elem must be (:kv KEY VALUE) — KEY assignable to
;; KEY-TY, VALUE to VAL-TY.
;; For slice/array, plain exprs assignable to VAL-TY; (:kv K V) is
;; Go's index-keyed shorthand (`[]int{0: 5, 1: 10}`) — we type-check
;; only the value in v0.
(fn (ctx elems val-ty key-ty)
(cond
(or (= elems nil) (= (len elems) 0)) :ok
:else
(let ((e (first elems)))
(let ((err
(cond
(and (list? e) (= (first e) :kv))
(let ((k (nth e 1)) (v (nth e 2)))
(cond
(= key-ty nil) (go-check ctx v val-ty)
:else
(let ((kerr (go-check ctx k key-ty)))
(cond
(go-type-error? kerr) kerr
:else (go-check ctx v val-ty)))))
:else
(cond
(= key-ty nil) (go-check ctx e val-ty)
:else
(list :type-error :map-elem-missing-key e)))))
(cond
(go-type-error? err) err
:else
(go-check-composite-elems ctx (rest elems) val-ty key-ty)))))))
(define
go-synth-composite
;; Composite literal: (:composite TYPE-OR-EXPR ELEMS).
;; []T{...} — each elem assignable to T; result :ty-slice T
;; [N]T{...} — same; result :ty-array N T
;; map[K]V{...} — each :kv key:K, value:V; result :ty-map K V
;; Named-type literals (Point{...}, pkg.T{...}) require type-decl
;; resolution; v0 returns the literal's type-expr as-is without
;; element checking.
(fn (ctx ty elems)
(cond
(and (list? ty) (= (first ty) :ty-slice))
(let ((elem-ty (nth ty 1)))
(let ((err (go-check-composite-elems ctx elems elem-ty nil)))
(cond (go-type-error? err) err :else ty)))
(and (list? ty) (= (first ty) :ty-array))
(let ((elem-ty (nth ty 2)))
(let ((err (go-check-composite-elems ctx elems elem-ty nil)))
(cond (go-type-error? err) err :else ty)))
(and (list? ty) (= (first ty) :ty-map))
(let ((key-ty (nth ty 1)) (val-ty (nth ty 2)))
(let ((err (go-check-composite-elems ctx elems val-ty key-ty)))
(cond (go-type-error? err) err :else ty)))
:else ty)))
(define
go-synth-call
;; Synth a function call. Returns the result type, or :type-error.

View File

@@ -244,12 +244,19 @@ Progress-log line → push `origin/loops/go`.
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).
- [x] Composite literal element checking slice `[]T{...}`, array
`[N]T{...}`, map `map[K]V{...}` (key + value checked).
`:kv` element with no key on slice/array is permitted (Go's
index-keyed shorthand). Nested composite literals work
(`[][]int{[]int{1,2}, []int{3,4}}`). Named-type composite
literals (`Point{...}`) need type-decl resolution; deferred.
- [ ] Interface satisfaction (structural match against method sets).
- [ ] 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: 55/55. Chisel note
- **Acceptance:** types/ suite at 60+ tests. **Bar crossed: 65/65.**
Remaining sub-items (interface satisfaction, error reporting carrying
AST paths) refine but don't gate Phase 4. Chisel note
`shapes-static-types-bidirectional` — sister-plan design diary is the
cross-language record.
@@ -550,6 +557,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.: composite-literal element checking.
`go-synth-composite` dispatches on the literal's type expression:
`:ty-slice` and `:ty-array` check each element assignable to the
element type; `:ty-map` checks each `:kv` pair (key against K, value
against V) and rejects non-`:kv` map elements. The literal's
synthesised type is the type-expression itself, so nested composites
fall out by recursion: `[][]int{[]int{1,2}, []int{3,4}}` checks each
inner `[]int{...}` as a value of type `[]int`. Named-type literals
(`Point{1,2}`, `pkg.T{...}`) need type-decl-driven field resolution;
deferred. **Phase 3 acceptance bar (60+ tests) crossed: 65/65, total
370/370.** `[nothing]` — composite-literal semantics are mostly Go-
specific. Remaining Phase 3 items (interface satisfaction; AST-path
error context) sharpen the surface but don't gate Phase 4 (evaluator).
- 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