fix: type-safe stores + Any unbox/eq; finish multi-return deferrals

Type-checking gaps (segfault/corruption → compile errors):

- 0197: reject a store into an annotated slot whose value has no modeled
  coercion AND a different byte width (a 16-byte string into a 4-byte i32
  overran the slot and segfaulted). New checkAssignable / noneReinterpretIsUnsafe
  (coerce.zig, width via the LLVM-accurate typeSizeBytes) wired into every store
  site: var/const-decl, single + multi assignment (identifier/field/index/
  element/deref), named-return defaults. Same-width reinterpretations (*T→[*]T,
  i64→isize, fn-ref) and explicit xx/cast stay allowed; cascades suppressed via
  externalErrorsExist. Examples 1205, 1206.
- 0198: an implicit `Any → T` unbox is now a compile error (it blindly
  reinterpreted the boxed payload — silent garbage for a wrong scalar, a segfault
  for an aggregate). xx and compiler-generated match/pack unboxes are unaffected.
  Example 1207.
- 0199: `Any == <concrete>` (one operand Any) aborted the LLVM verifier — the
  comparison arm now fires when either operand is Any, boxing the concrete side
  first. Example 0654.

Multi-return deferrals (PLAN-MULTIRET #6 + named-order + D3 + generic):

- Reorder named return elements by name instead of requiring slot order; error on
  unknown/duplicate/missing (value-only AND full-failable-tuple forms). Examples
  0210, 0214.
- Reject a bare-paren (A, B) multi-return signature in generic-arg position
  (return-position-only). Example 0215.
- Multi-return closure types / lambda literals work via the reused tuple
  machinery (destructure, single-bind+field, lambda arg). Example 0216.
- Generic multi-return: positional works (0217); 0200: the named-slot
  implicit-return form now works for generic free fns + struct methods —
  monomorphizeFunction now calls bindNamedReturnSlots. Example 0218.

readme.md documents the annotated-store coercion rule; CHECKPOINT-MULTIRET.md
updated. Full corpus green (850/0).
This commit is contained in:
agra
2026-06-27 17:28:27 +03:00
parent 97772abf54
commit b322dcfe61
51 changed files with 1000 additions and 56 deletions

View File

@@ -76,20 +76,73 @@ An adversarial review found 8 issues; fixed the soundness + silent-wrong ones:
bodies count; defer correctly does NOT, as it runs after the implicit return).
## Known limitations / next
- **#6 (design gap, NOT UB)**: a `ReturnTypeExpr` is still silently accepted in
struct-field / var-annotation / generic-arg / closure-RETURN positions (resolves
to a coherent tuple). Only the PARAM position is rejected. Rejecting the rest
needs checks at several value-resolution sites; deferred (no soundness impact).
- **Reordering named return elements by name** (vs requiring slot order) — future.
- **PRE-EXISTING**: annotated-assignment type mismatch (`x: i32 = "hi"`) segfaults
— a general type-checking gap surfaced by the review; may warrant an issue.
- Multi-return CLOSURE-TYPE values / lambda literals deferred (D3).
- ~~**#6 (design gap)**: `ReturnTypeExpr` silently accepted in non-return positions~~
**DONE** (2026-06-27): generic-type-arg position now rejected
(`rejectMultiReturnValueType` at both `instantiateGenericStruct` arg-resolution
sites, generic.zig). Param / field / variable already rejected. Type-alias
`T :: (A,B)` is value-parsed → already rejected. Closure-RETURN `(A,B)` is a
legitimate return position → see D3 below (works as a multi-return closure).
Lock: 0215 (negative generic-arg).
- ~~**Reordering named return elements by name** (vs requiring slot order)~~ —
**DONE** (2026-06-27): `reorderNamedReturn` (stmt.zig) permutes a fully-named
multi-return list to slot order by name (value-only AND full-failable-tuple
forms); errors on unknown / duplicate / missing-slot names; positional & mixed
lists pass through unchanged. `validateMultiReturn`'s old slot-order check was
removed. Adversarial review caught a silent mis-permute in the full-failable-
tuple named form (now reordered/validated, not positionally dropped). Lock:
0210 (positive reorder, incl. failable) + 0214 (negative: unknown / duplicate).
- ~~**PRE-EXISTING**: annotated-assignment type mismatch (`x: i32 = "hi"`) segfaults~~
**RESOLVED** as issue 0197 (2026-06-27): width-mismatch guard
(`checkAssignable` / `noneReinterpretIsUnsafe`, coerce.zig) at every
annotated-slot store site; the named-return-default guard now shares it. Locked
by `examples/diagnostics/1205` + `1206`.
- ~~Multi-return CLOSURE-TYPE values / lambda literals deferred (D3).~~ —
**RESOLVED** (2026-06-27): they ALREADY WORK via the reused tuple machinery. A
`Closure() -> (A, B)` value's call result destructures (`a, b := cb()`),
single-binds + field-accesses (`c := cb(); c.0`), and a `() => { return v1, v2; }`
lambda literal satisfies a multi-return closure param — verified identical to
the function-decl surface. NO `ClosureInfo.multi_return` marker needed (the
destructure-only rule was reversed, so there's nothing extra to enforce). Lock:
0216.
- **Generic multi-return (Task 2d): DONE.** POSITIONAL works — `(a: $T, b: $U) -> (T, U)`
(inferred) and `($T: Type, …) -> (T, U)` (explicit); lock 0217. NAMED-slot
implicit-return form now works too (issue **0200 RESOLVED**
`monomorphizeFunction` now calls `bindNamedReturnSlots`; covers free fns +
generic struct methods, defaults, failable); lock 0218.
- Docs: readme.md / specs.md not yet updated for multi-return (docs-track rule).
## Known issues
- (none yet)
- ~~**issue 0198**: implicit `Any → T` unbox unchecked (segfault / silent garbage)~~
**RESOLVED** (2026-06-27): implicit `Any → T` is now a compile error
(`coerceMode` `.unbox_any` arm, mode == .implicit); `xx` + match dispatch
unaffected. Locked by `examples/diagnostics/1207`.
- ~~**issue 0199**: `Any == <concrete>` aborts the LLVM verifier~~ — **RESOLVED**
(2026-06-27): the `Any`-shaped `==`/`!=` arm (expr.zig) now fires when EITHER
operand is `.any`, boxing the concrete side first. Lock 0654.
- ~~**issue 0200**: NAMED generic multi-return implicit-return "produces no value"~~
**RESOLVED** (2026-06-27): `monomorphizeFunction` now calls
`bindNamedReturnSlots` (it previously bound params but skipped named-return
slots). Covers generic free fns + struct methods, defaults, failable. Lock 0218.
## Log
- **2026-06-27 session** (handover: issue 0197 → finish multi-return → Io Phase 3):
- **issue 0197 RESOLVED** — width-mismatch guard at every annotated-slot store
site (var/const-decl, single + multi assignment for identifier/field/index/
element/deref, named-return defaults). Examples 1205 + 1206. Adversarial review
caught & fixed: a bare-fn-ref false-positive (size-discriminator via
`typeSizeBytes`, not the wrong fn-ref typing) and an aggregate-overrun
false-negative (sx-padded `sizeOf` → LLVM-accurate `typeSizeBytes`); cascade
suppression via `externalErrorsExist` (guard tallies its own diagnostics).
- **issue 0198 RESOLVED** — implicit `Any → T` unbox is now a compile error
(reviewer-confirmed sound). Example 1207. **issue 0199 FILED** (Any==concrete
LLVM-verify abort, loud, open).
- **multi-return Task 2 DONE** (2a reorder 0210/0214; 2b reject in generic-arg
0215; 2c D3 closures already work 0216; 2d positional generic works 0217 +
named-generic gap filed as 0200). Multi-return feature surface complete.
- **REMAINING** (next session): **Task 3 Io-unification Phase 3** (the
capture-typing blocker below + true cancellation — needs fresh context + both
macOS & aarch64-linux validation per PLAN-IO-UNIFY.md). (0198/0199/0200 all
resolved this session; no open multi-return/type-check issues remain.)
- Pivoted here from the Io-unification Phase 3 (true cancellation), which is
PAUSED at its blocker: capturing a failable closure into a nested closure loses
its failability (`worker() catch` → operand type 'unresolved'; repro