ffi checkpoint: step 4 partial — .type_tag activated by the book
Logs the four 4.x slices (ac60d98→fd03b58): - 4.0 foundation: const_type opcode, asTypeId helper, cmp_eq arm, Zig unit tests. - 4.1 reflection arms: type_name/type_eq interp implementations reading .type_tag values; has_impl bails (snapshot work pending). - 4.2 audit + bitcast guard: box_any/unbox_any layout confirmed correct, bitcast guards against .type_tag mis-coercion. - 4.3 source construction: parser accepts $args[$i] in expression position; lowering emits const_type with the bound TypeId; resolveTypeArg + tryConstBoolCondition fold through pack_arg_types. End-to-end working: type_name($args[0]), inline-if type_eq dispatch over $args[0] per-mono. Test count 209/209. Remaining within step 4: - 4B compile_error(fmt, args) intrinsic. - 4A bare $args (whole pack as []Type) — step 5 needs this. - has_impl interp-time wiring. Step 5 (generic Into(Block) impl) needs only bare-$args from the remaining list to be fully unblocked.
This commit is contained in:
@@ -6,6 +6,69 @@ add a test and make it pass — that's two commits).
|
||||
|
||||
## Last completed step
|
||||
|
||||
**M5.A.next.4 — activate Value.type_tag (Type as a first-class value)**
|
||||
(commits `ac60d98`, `9600ba5`, `55c72af`, `fd03b58` — 4 slices).
|
||||
|
||||
Activated the dormant `Value.type_tag(TypeId)` variant in the
|
||||
interp by the book — no silent-error budget violations,
|
||||
explicit construction path through a new IR opcode, kind-honest
|
||||
helpers, source-language `$args[$i]` in expression position.
|
||||
|
||||
| Slice | Commit | What |
|
||||
|---|---|---|
|
||||
| 4.0 foundation | `ac60d98` | New `Op.const_type: TypeId` opcode (dedicated, never piggybacks on `const_int`). Interp emits `Value.type_tag(tid)`. emit_llvm bails loudly (Type is comptime-only; LLVM never sees one). `Value.asTypeId() ?TypeId` helper. `evalCmp` arm for `.type_tag, .type_tag` — TypeId equality. Mixed `.type_tag` vs `.int` falls through to `typeErrorDetail`. Zig unit tests confirm the variant. |
|
||||
| 4.1 reflection arms | `9600ba5` | `BuiltinId.type_name` / `.type_eq` / `.has_impl` for the interp-time fallback when lowering can't fold the call statically. Static-arg calls keep the existing `tryLowerReflectionCall` const-emission fast path. `has_impl` interp arm bails with "not yet wired" — interp-time has_impl needs a queryable snapshot of the host's protocol maps (its own follow-up). emit_llvm bails loudly on all three (comptime-only). |
|
||||
| 4.2 audit + bitcast guard | `55c72af` | `box_any`/`unbox_any` audit: layout was already correct (tag stays `.int`; value field can be `.type_tag`). `bitcast` interp arm guards against `.type_tag → <non-Any, non-identity>` casts — catches the `xx val to string` shape in `any_to_string`'s `case type:` arm that pre-dates type_tag and would silently mis-coerce. |
|
||||
| 4.3 source construction | `fd03b58` | Parser accepts `$<pack>[<int_literal>]` in expression position (yields the same `pack_index_type_expr` AST node already used in type positions in step 3). Lowering: `lowerExpr` arm emits `const_type(arg_tys[index])`; `resolveTypeArg` arm reads `pack_arg_types[name][index]` directly so lower-time fold paths (`tryLowerReflectionCall`, `tryConstBoolCondition`) see the bound TypeId rather than falling through to the `.s64` silent-arm default. |
|
||||
|
||||
Audit summary — every Value-switch in interp.zig was checked
|
||||
for silent fall-through. Findings:
|
||||
- All existing `else` arms are either already `bailDetail` /
|
||||
`error.TypeError` (loud) or pass-through helpers where transit-
|
||||
unchanged is semantically correct for `.type_tag`.
|
||||
- `box_any` tag field stays `.int`; value field can carry any
|
||||
Value kind including `.type_tag`. No changes needed.
|
||||
- `asInt`/`asFloat`/`asBool`/`asString` keep returning `null` for
|
||||
`.type_tag` — no silent coercion to int just because TypeId is
|
||||
internally an int.
|
||||
- Comparison op `cmp_eq` got an explicit `.type_tag, .type_tag` arm.
|
||||
- Coercion op `bitcast` got an explicit bail arm for `.type_tag →
|
||||
<runtime kind>` to catch any stale `xx val to string` paths.
|
||||
|
||||
What's now possible end-to-end (from `examples/169-pack-value-dispatch.sx`):
|
||||
|
||||
```sx
|
||||
show :: (..$args) -> string => type_name($args[0]);
|
||||
show(42) // "s64"
|
||||
show("hi") // "string"
|
||||
|
||||
describe :: (..$args) -> string {
|
||||
inline if type_eq($args[0], s64) { return "got s64"; }
|
||||
inline if type_eq($args[0], string) { return "got string"; }
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
`$args[0]` as a value flows through `const_type` → `Value.type_tag`
|
||||
→ `type_name`/`type_eq` (lower-time fold via `resolveTypeArg`)
|
||||
without losing its kind anywhere.
|
||||
|
||||
Known follow-ups:
|
||||
- `has_impl` interp arm currently bails. Needs a protocol-map
|
||||
snapshot on `Interpreter.init`.
|
||||
- `any_to_string`'s `case type:` arm in stdlib still does
|
||||
`xx val to string` — pre-`type_tag` shape. Once `.type_tag`
|
||||
flows into a print/format path, the bitcast guard fires.
|
||||
Fix is to replace with `type_name(val)` once the lowering
|
||||
supports value-shaped type_name.
|
||||
- `$args` (bare, without indexing) as a `[]Type` value —
|
||||
needed by full step-5 builder bodies. Single-element access
|
||||
works; whole-slice access deferred.
|
||||
|
||||
209/209 example tests + `zig build test` green.
|
||||
|
||||
---
|
||||
|
||||
**M5.A.next.3 — type-position `$args[$i]` + reflection intrinsics**
|
||||
(commits `69dcee8` → `8b457ff`, 5 total).
|
||||
|
||||
@@ -724,7 +787,7 @@ plus 2 codegen fixes surfaced along the way.**
|
||||
|
||||
## Current state
|
||||
|
||||
- 208/208 example tests pass; `zig build test` green.
|
||||
- 209/209 example tests pass; `zig build test` green.
|
||||
- Phase 3.0/3.1/3.2 + M1.0–M1.3 + M2.1–M2.3 + M3 + M4.0 + M4.A all landed.
|
||||
- Pack feature step 1 done (1c.A → 1d.B; commits bb6eca6 → 08feb60).
|
||||
- Pack feature step 2 done — typed `args[$i]` at literal indices
|
||||
@@ -740,6 +803,9 @@ plus 2 codegen fixes surfaced along the way.**
|
||||
- issue-0047 (#run stderr vs runtime stdout split) FILED.
|
||||
- Pack feature step 3 done — type-position `$args[$i]` +
|
||||
reflection intrinsics (`type_name`, `type_eq`, `has_impl`).
|
||||
- Pack feature step 4.0–4.3 done — `Value.type_tag` activated
|
||||
honestly; source-language `$args[$i]` in expression position
|
||||
yields a comptime Type value end-to-end.
|
||||
- iOS-sim chess running end-to-end (verified post-step-2b screencap).
|
||||
- Chess on macOS / iOS-sim / Android all build and run.
|
||||
|
||||
@@ -751,17 +817,27 @@ heterogeneous picks, OOB diagnostics, bare/runtime `args`
|
||||
access, mixed comptime+pack, `$args[$i]` in type positions,
|
||||
type-reflection intrinsics.
|
||||
|
||||
Step 4 (`#insert` pack passthrough + `compile_error`) is a
|
||||
small parser/interp tweak letting builders accept `$args` as a
|
||||
comptime `[]Type` value AND raise build-time diagnostics from
|
||||
inside `#insert` bodies. Prep work for step 5/6.
|
||||
Step 4 partially done — `.type_tag` activation + `$args[$i]`
|
||||
in expression position landed (4.0–4.3). Still open within
|
||||
step 4:
|
||||
- 4B `compile_error(fmt, args)` comptime intrinsic — raise a
|
||||
build-time diagnostic from inside a builder. Small commit
|
||||
set; not blocking step 5 but useful for builder error paths.
|
||||
- 4A's bare `$args` (whole pack as `[]Type` value) — the only
|
||||
remaining shape needed for step 5's full generic builder
|
||||
pattern; single-element `$args[$i]` works today.
|
||||
- `has_impl` interp arm — currently bails, needs a protocol-
|
||||
map snapshot on `Interpreter.init`.
|
||||
|
||||
Step 5 (generic `Into(Block)` impl) — the visible end-user
|
||||
payoff. Replaces stdlib's per-signature hand-rolled Into
|
||||
impls with ONE generic that the compiler emits per-call-shape.
|
||||
Body uses `$args[$i]` in fn-pointer type positions for the
|
||||
trampoline signature (step 3 unblocked this) + emits a
|
||||
trampoline-fn per mono.
|
||||
trampoline signature (step 3 unblocked) + `const_type` Type
|
||||
values in expression position (step 4 unblocked) + a single
|
||||
`#insert build_block_convert(...)` emission. Needs bare-`$args`
|
||||
(4A) to land first, plus a builder fn that emits the trampoline
|
||||
+ Block literal source string.
|
||||
|
||||
Step 6 (stdlib `print` / `format` refactor) — rewrite the
|
||||
existing `($fmt: string, args: ..Any)` signatures to use the
|
||||
@@ -769,12 +845,15 @@ new pack feature. Compile-time arity and type checking
|
||||
instead of runtime Any boxing.
|
||||
|
||||
Outstanding items not blocking the next slice:
|
||||
- `$args[$i]` in EXPRESSION position (parser limitation).
|
||||
- Non-literal comptime args in mixed-mode pack-fns.
|
||||
- Non-literal comptime args in mixed-mode pack-fns (degrades
|
||||
to a `?` mangle segment today).
|
||||
- LSP "undefined variable" warnings on type-name args to
|
||||
reflection intrinsics (cosmetic).
|
||||
- has_impl static-only false-negative for un-instantiated
|
||||
plain protocols.
|
||||
- `any_to_string`'s `case type:` arm in stdlib uses `xx val
|
||||
to string` — pre-`type_tag` shape. Once `.type_tag` flows
|
||||
into a print/format path, the bitcast guard fires. Fix is
|
||||
to replace with `type_name(val)` once value-form
|
||||
`type_name` is wired through `tryLowerReflectionCall`.
|
||||
|
||||
**M4.0 — context.allocator threading** (4 commits this session):
|
||||
- `__sx_allocator: Allocator` prepended at field index 0 of every
|
||||
|
||||
Reference in New Issue
Block a user