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:
agra
2026-06-17 13:31:11 +03:00
parent 3a9b508502
commit cd5b958d19
100 changed files with 1490 additions and 298 deletions

View File

@@ -122,14 +122,26 @@ pub const Root = struct {
decls: []const *Node,
};
pub const CallingConvention = enum { default, c };
/// ABI / calling-convention annotation written as the postfix `abi(.x)` form on a
/// function declaration, function-type literal, or lambda. Subsumes the old
/// `callconv(...)` spelling.
/// - `.default` — no annotation: the ordinary sx-internal convention (implicit
/// context, sx ABI). There is no surface spelling for `.default`; it is the
/// value when `abi(...)` is absent.
/// - `.c` — C ABI / cdecl, no implicit context (what `callconv(.c)` meant).
/// - `.zig` — welded to the real internal Zig type/fn: layout follows the bound
/// Zig type, functions dispatch over the comptime host-call bridge. The
/// `compiler` library (`design/comptime-compiler-api.md`) binds via `abi(.zig)`.
/// - `.pure` — a pure / naked function (inline asm body), no calling-convention
/// prologue/epilogue.
pub const ABI = enum { default, c, zig, pure };
/// Linkage modifier written in the postfix slot after `callconv(...)`:
/// `name :: (sig) -> Ret [callconv(.x)] [extern | export] [;|{…}];`
/// `extern` = import (external linkage, C ABI, no sx ctx — `extern`'s role);
/// `export` = define + expose (body + external linkage + C ABI + no ctx).
/// Both imply `callconv(.c)`. Variants carry a trailing `_` to dodge the Zig
/// keywords. `.none` = no linkage modifier (the ordinary sx-internal decl).
/// Linkage modifier written in the postfix slot before `abi(...)`:
/// `name :: (sig) -> Ret [extern | export] [abi(.x)] [lib] [;|{…}];`
/// `extern` = import (external linkage, no sx ctx — `extern`'s role);
/// `export` = define + expose (body + external linkage + no ctx).
/// Variants carry a trailing `_` to dodge the Zig keywords. `.none` = no linkage
/// modifier (the ordinary sx-internal decl).
pub const ExternExportModifier = enum { none, extern_, export_ };
pub const FnDecl = struct {
@@ -139,10 +151,15 @@ pub const FnDecl = struct {
body: *Node,
type_params: []const StructTypeParam = &.{},
is_arrow: bool = false,
call_conv: CallingConvention = .default,
/// Postfix linkage modifier (`extern`/`export`) written after the
/// `callconv(...)` slot. `.none` for an ordinary sx-internal function.
/// Parsed in Phase 0.1; not consumed by the fn-decl path until Phase 1.
/// ABI / calling-convention annotation (`abi(.c)` / `abi(.zig)` / `abi(.pure)`)
/// in the postfix slot after `extern`/`export`. `.default` = unannotated.
/// `.zig` marks a function bound to the comptime `compiler` library — its
/// signature is welded to the real internal Zig fn and it dispatches over the
/// host-call bridge at comptime (consumed by the binding registry + host-call
/// bridge in later phases).
abi: ABI = .default,
/// Postfix linkage modifier (`extern`/`export`) written before the `abi(...)`
/// slot. `.none` for an ordinary sx-internal function.
extern_export: ExternExportModifier = .none,
/// Optional library reference + symbol-name override for an `extern`/`export`
/// function, the optional library + symbol-name override. Both
@@ -510,6 +527,15 @@ pub const StructDecl = struct {
using_entries: []const UsingEntry = &.{},
methods: []const *Node = &.{}, // fn_decl nodes for struct methods
constants: []const *Node = &.{}, // const_decl nodes for struct-level constants
/// ABI / layout annotation (`struct abi(.zig) extern <lib> { … }`). `.default`
/// for an ordinary struct. `.zig` marks a layout-welded binding to the named
/// `compiler` library's real Zig type — its field offsets are taken from the
/// bound Zig type (`@offsetOf`) and asserted equal at compiler-build time.
/// Parsed in Phase 1; consumed by the binding registry + layout engine later.
abi: ABI = .default,
/// The bound library handle for an `abi(.zig) extern <lib>` welded struct
/// (e.g. `compiler`); null for an ordinary struct.
extern_lib: ?[]const u8 = null,
/// True when the declared NAME was a backtick raw identifier
/// (`` `i2 :: struct { … } ``) — exempt from the reserved-type-name decl
/// check. A bare reserved-name decl still errors.
@@ -533,7 +559,7 @@ pub const Lambda = struct {
return_type: ?*Node,
body: *Node,
type_params: []const StructTypeParam = &.{},
call_conv: CallingConvention = .default,
abi: ABI = .default,
};
pub const TypeExpr = struct {
@@ -805,7 +831,7 @@ pub const FunctionTypeExpr = struct {
param_types: []const *Node,
param_names: ?[]const ?[]const u8 = null, // optional documentation names
return_type: ?*Node, // null = void return
call_conv: CallingConvention = .default,
abi: ABI = .default,
};
pub const ClosureTypeExpr = struct {