comptime compiler-API: Phase 1 foundation + Phase 2.1 weld plan
Introduce the welded comptime `compiler` library (`#library "compiler"` +
`abi(.zig) extern compiler`), per design/comptime-compiler-api.md, and unify
`callconv(...)` into the new `abi(...)` annotation.
abi(...) replaces callconv(...):
- New ABI enum { default, c, zig, pure }; `abi(.c|.zig|.pure)` parses in the
postfix slot before extern/export (and standalone). `kw_callconv` -> `kw_abi`.
- Migrated 52 sx files, the call-convention-mismatch diagnostic, and docs
(readme/specs) from `callconv(.c)` to `abi(.c)`.
Phase 1 — welded compiler library (parse -> registry -> validation -> bridge):
- `abi(.zig) extern compiler` parses on fn decls (carries abi/extern_lib) and
struct decls (StructDecl.abi/extern_lib).
- `#library "compiler"` is the comptime-only internal surface — never dlopen'd.
- src/ir/compiler_lib.zig: the binding registry (the safety boundary). `Field`
welded to StructInfo.Field with layout baked from the real Zig type
(@offsetOf/@sizeOf); `findType`/`findFn`. Welded structs are layout-validated
at registration (field set + total size) as a header checked against the impl.
- Host-call bridge: a `fn abi(.zig) extern compiler` dispatches under the
comptime interp to its registered Zig handler (intern/text_of round-trip),
never dlsym. IR Function.compiler_welded; validated in declareFunction.
- Comptime-only enforcement: a runtime call to a welded fn is a clean
build-gating error (emitCall), not an undefined-symbol link failure.
Phase 2.1 — byte-layout weld foundation:
- Decision: full byte-layout weld (sx struct laid out byte-identically to the
bound Zig type). Registered StructInfo (first non-natural / Zig-reordered
layout). `computeWeldPlan` — pure offset-ordered element plan + padding +
sx-field->LLVM-element remap; unit-tested. Emit/interp wiring is the next
sub-step (2.2+, see current/CHECKPOINT-COMPILER-API.md).
Examples: 0625/0626 (welded struct + fn round-trip), 1183/1184/1185
(layout-mismatch, unexported-fn, runtime-call diagnostics).
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# Comptime Compiler API — `#library "compiler"` + `extern(.zig)`
|
||||
# Comptime Compiler API — `#library "compiler"` + `abi(.zig) extern`
|
||||
|
||||
> **Status: design-of-record (not yet an active stream).** Captures a unified
|
||||
> mechanism for sx↔compiler binding that subsumes the metatype `declare`/`define`
|
||||
@@ -49,32 +49,44 @@ internal surface (Zig types + functions). Two defining properties:
|
||||
`is_comptime` boundary. (Welded *types* are still usable as plain runtime data;
|
||||
only the *functions* are comptime-gated.)
|
||||
|
||||
### `extern(.zig) <lib>` — postfix attribute
|
||||
### `abi(.zig)` + `extern <lib>` — the binding surface
|
||||
|
||||
Slots where `#builtin` / `#compiler` go (postfix, after the return type for fns,
|
||||
after `struct` for types), with the library handle following:
|
||||
> **Syntax decision (2026-06-17, supersedes the original `extern(.zig) <lib>`
|
||||
> single-qualifier form).** The ABI/layout selector and the linkage keyword are
|
||||
> two orthogonal things, so they are two annotations, not one fused qualifier:
|
||||
> - `abi(.x)` — the ABI / calling-convention annotation, in the postfix slot
|
||||
> **before** `extern`/`export`. It is the unified replacement for the old
|
||||
> `callconv(...)` (which is removed): `ABI = { default, c, zig, pure }` —
|
||||
> `.c` (C ABI / cdecl), `.zig` (Zig-layout weld → the `compiler` library),
|
||||
> `.pure` (naked asm). `.default` = unannotated (ordinary sx convention).
|
||||
> - `extern <lib>` — the linkage keyword + binding source (the named library).
|
||||
|
||||
`abi(...)` sits where `callconv(...)` went (after the return type for fns); the
|
||||
`extern`/`export` keyword and the library handle follow. For welded types, the
|
||||
same `abi(.zig)` + `extern <lib>` pair sits after `struct`:
|
||||
|
||||
```sx
|
||||
// functions:
|
||||
text_of :: (id: StringId) -> string extern(.zig) compiler;
|
||||
intern :: (s: string) -> StringId extern(.zig) compiler;
|
||||
register_type :: (info: StructInfo) -> Type extern(.zig) compiler;
|
||||
find_type :: (name: StringId) -> ?Type extern(.zig) compiler;
|
||||
text_of :: (id: StringId) -> string abi(.zig) extern compiler;
|
||||
intern :: (s: string) -> StringId abi(.zig) extern compiler;
|
||||
register_type :: (info: StructInfo) -> Type abi(.zig) extern compiler;
|
||||
find_type :: (name: StringId) -> ?Type abi(.zig) extern compiler;
|
||||
|
||||
// types (layout-welded to the lib's real Zig type):
|
||||
Field :: struct extern(.zig) compiler { name: StringId; ty: Type; };
|
||||
StructInfo :: struct extern(.zig) compiler {
|
||||
Field :: struct abi(.zig) extern compiler { name: StringId; ty: Type; };
|
||||
StructInfo :: struct abi(.zig) extern compiler {
|
||||
name: StringId; fields: []Field; is_protocol: bool; nominal_id: u32;
|
||||
};
|
||||
```
|
||||
|
||||
`extern(.zig)` = "Zig ABI / Zig layout"; `<lib>` = the binding source.
|
||||
`abi(.zig)` = "Zig ABI / Zig layout"; `extern compiler` = the linkage + binding
|
||||
source.
|
||||
|
||||
### Layout welding — why it's exact, not brittle
|
||||
|
||||
The sx compiler is itself a Zig program; `types.zig` is part of it. So at
|
||||
**compiler-build time** the real record's layout is available via
|
||||
`@offsetOf` / `@sizeOf` / `@alignOf`. An `extern(.zig) compiler` struct is laid out
|
||||
`@offsetOf` / `@sizeOf` / `@alignOf`. An `abi(.zig) extern compiler` struct is laid out
|
||||
to the bound Zig type's EXACT offsets (queried, not guessed), and the compiler
|
||||
ASSERTS the sx declaration matches the Zig type byte-for-byte (a mismatch is a
|
||||
build error — the sx side is a header checked against the implementation). Because
|
||||
@@ -134,8 +146,8 @@ instead of the user threading `declare`→forward-slot→`define`→eval-timing
|
||||
## BuildOptions migration
|
||||
|
||||
`BuildOptions :: struct #compiler { ... }` + `build_options() #compiler` →
|
||||
`extern(.zig) compiler`: the setter/getter hook-methods become `extern(.zig)
|
||||
compiler` functions (or methods on a welded/handle `BuildOptions`), backed by the
|
||||
`abi(.zig) extern compiler`: the setter/getter hook-methods become `abi(.zig)
|
||||
extern compiler` functions (or methods on a welded/handle `BuildOptions`), backed by the
|
||||
same `BuildConfig` state. The `compiler_hooks.zig` registry becomes the `compiler`
|
||||
lib's function/type registry. Net: the build DSL and the metatype API ride one
|
||||
mechanism.
|
||||
@@ -157,12 +169,17 @@ Foundation that ALREADY exists:
|
||||
- `extern` / `export` are keywords (`src/token.zig:46`, `kw_extern`/`kw_export`).
|
||||
|
||||
New work for Phase 1:
|
||||
- **Lexer/parser**: the `(.zig)` ABI qualifier on `extern`, and the trailing
|
||||
`<lib>` identifier — `… extern(.zig) <lib>` postfix on FN decls (after the
|
||||
return type, beside `#builtin`/`#compiler` at `src/parser.zig:233`/`:315`) and
|
||||
STRUCT decls (beside `struct #compiler`, `src/parser.zig:953`).
|
||||
- **AST**: an abi/layout-binding field on `FnDecl` and the struct decl (`abi:
|
||||
.c | .zig`, `lib: ?name`).
|
||||
- **Lexer/parser**: the `abi(.zig)` annotation (a new `abi` keyword replacing
|
||||
`callconv`; `ABI = { default, c, zig, pure }`) in the slot before `extern`,
|
||||
followed by the `<lib>` handle — `… abi(.zig) extern <lib>` postfix on FN decls
|
||||
(after the return type, before `extern`) and STRUCT decls (beside
|
||||
`struct #compiler`). **DONE (parse-only)** — `parseOptionalAbi`
|
||||
(`src/parser.zig`) wired on fn decls AND struct decls, `ast.ABI`, parser unit
|
||||
tests; the `callconv`→`abi` rename migrated 52 sx files + the compiler's
|
||||
CC-mismatch diagnostic.
|
||||
- **AST**: the `abi: ABI` field lives on `FnDecl` / `Lambda` / `FunctionTypeExpr`
|
||||
(carries `.zig` for a welded fn); `StructDecl` gained `abi: ABI` +
|
||||
`extern_lib: ?[]const u8`. **DONE.**
|
||||
- **Binding registry**: re-home / generalize `src/ir/compiler_hooks.zig` (today's
|
||||
`#compiler` registry) into the `compiler` lib's type+function registry, keyed by
|
||||
exported sx name → Zig type (`@offsetOf` layout) / Zig fn (host-call).
|
||||
@@ -174,12 +191,13 @@ New work for Phase 1:
|
||||
|
||||
## Build order (each phase keeps `zig build test` green)
|
||||
|
||||
1. **`extern(.zig)` + `#library` foundation** — parse the postfix attribute (the
|
||||
`#library` decl already exists); a binding registry (sx name → Zig type/fn);
|
||||
the layout engine honoring the bound type's `@offsetOf` offsets + LLVM emission
|
||||
that hits them; **build-time layout-equality assertion**. Prove with `Field`
|
||||
(two u32s). First testable sub-step: `extern(.zig) <lib>` PARSES on a fn decl
|
||||
(parser unit test), AST carries the binding — no semantics yet.
|
||||
1. **`abi(.zig) extern <lib>` + `#library` foundation** — parse the postfix
|
||||
annotation (the `#library` decl already exists); a binding registry (sx name →
|
||||
Zig type/fn); the layout engine honoring the bound type's `@offsetOf` offsets +
|
||||
LLVM emission that hits them; **build-time layout-equality assertion**. Prove
|
||||
with `Field` (two u32s). First testable sub-step **DONE**: `abi(.zig) extern
|
||||
<lib>` PARSES on a fn decl (parser unit test), AST carries the binding (`abi ==
|
||||
.zig`, `extern_lib`) — no semantics yet.
|
||||
2. **Weld `StructInfo`** + `StringId` accessors (`intern`/`text_of`) over the
|
||||
host-call bridge.
|
||||
3. **Re-express `type_info`/`define` (struct)** as sx over `register_struct`/
|
||||
@@ -187,7 +205,7 @@ New work for Phase 1:
|
||||
4. **Widen to enum/tuple** — weld `EnumInfo`/`TaggedUnionInfo`/`TupleInfo`
|
||||
(optional fields → sentinels: `backing_type` `.unresolved`, `explicit_values`
|
||||
len-0); migrate `examples/0619`/`0623`; delete the enum/tuple interp arms.
|
||||
5. **Migrate `BuildOptions`** to `extern(.zig) compiler`.
|
||||
5. **Migrate `BuildOptions`** to `abi(.zig) extern compiler`.
|
||||
6. **Delete `#compiler`**; suite green.
|
||||
|
||||
## Risks / open questions
|
||||
|
||||
Reference in New Issue
Block a user