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:
@@ -598,12 +598,12 @@ pub const Parser = struct {
|
||||
// '->' present: function type
|
||||
self.advance(); // skip '->'
|
||||
const return_type = try self.parseTypeExpr();
|
||||
const call_conv = try self.parseOptionalCallConv();
|
||||
const abi = try self.parseOptionalAbi();
|
||||
return try self.createNode(start, .{ .function_type_expr = .{
|
||||
.param_types = try param_types.toOwnedSlice(self.allocator),
|
||||
.param_names = if (has_names) try param_names.toOwnedSlice(self.allocator) else null,
|
||||
.return_type = return_type,
|
||||
.call_conv = call_conv,
|
||||
.abi = abi,
|
||||
} });
|
||||
}
|
||||
// No '->': tuple type (even for single element). Keep field names
|
||||
@@ -959,6 +959,18 @@ pub const Parser = struct {
|
||||
self.advance();
|
||||
}
|
||||
|
||||
// Optional welded-binding annotation: `struct abi(.zig) extern <lib> { … }`.
|
||||
// `abi(...)` (the ABI/layout selector) sits before the `extern` linkage
|
||||
// keyword, mirroring the fn-decl slot order; the library handle follows.
|
||||
// Parse-only for now — no layout/registry semantics yet.
|
||||
const struct_abi = try self.parseOptionalAbi();
|
||||
const struct_extern = self.parseOptionalExternExport();
|
||||
var struct_extern_lib: ?[]const u8 = null;
|
||||
if (struct_extern != .none and self.current.tag == .identifier) {
|
||||
struct_extern_lib = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
}
|
||||
|
||||
// Optional type params: struct($N: u32, $T: Type) { ... }
|
||||
var type_params = std.ArrayList(ast.StructTypeParam).empty;
|
||||
if (self.current.tag == .l_paren) {
|
||||
@@ -1146,6 +1158,8 @@ pub const Parser = struct {
|
||||
.using_entries = try using_entries.toOwnedSlice(self.allocator),
|
||||
.methods = try methods.toOwnedSlice(self.allocator),
|
||||
.constants = try constants.toOwnedSlice(self.allocator),
|
||||
.abi = struct_abi,
|
||||
.extern_lib = struct_extern_lib,
|
||||
.is_raw = name_is_raw,
|
||||
} });
|
||||
}
|
||||
@@ -1937,8 +1951,11 @@ pub const Parser = struct {
|
||||
return_type = try self.parseTypeExpr();
|
||||
}
|
||||
|
||||
// Optional calling convention: callconv(.c)
|
||||
const call_conv = try self.parseOptionalCallConv();
|
||||
// Optional ABI / calling-convention annotation: `abi(.c)` / `abi(.zig)` /
|
||||
// `abi(.pure)`. Sits in the postfix slot BEFORE the `extern`/`export`
|
||||
// linkage keyword (it is part of the function declaration). `abi(.zig)`
|
||||
// marks a binding to the comptime `compiler` library.
|
||||
const abi = try self.parseOptionalAbi();
|
||||
|
||||
// Optional postfix linkage modifier: `extern` (import) / `export` (define).
|
||||
const extern_export = self.parseOptionalExternExport();
|
||||
@@ -2018,7 +2035,7 @@ pub const Parser = struct {
|
||||
.body = body,
|
||||
.type_params = type_params,
|
||||
.is_arrow = is_arrow,
|
||||
.call_conv = call_conv,
|
||||
.abi = abi,
|
||||
.extern_export = extern_export,
|
||||
.extern_lib = extern_lib,
|
||||
.extern_name = extern_name,
|
||||
@@ -3688,8 +3705,8 @@ pub const Parser = struct {
|
||||
return_type = try self.parseTypeExpr();
|
||||
}
|
||||
|
||||
// Optional calling convention: callconv(.c)
|
||||
const call_conv = try self.parseOptionalCallConv();
|
||||
// Optional ABI annotation: abi(.c) / abi(.zig) / abi(.pure)
|
||||
const abi = try self.parseOptionalAbi();
|
||||
|
||||
// A closure is its own function boundary: clear the cleanup-body flags
|
||||
// so control-flow exits inside the closure body (`return` from the
|
||||
@@ -3719,7 +3736,7 @@ pub const Parser = struct {
|
||||
.return_type = return_type,
|
||||
.body = body,
|
||||
.type_params = type_params,
|
||||
.call_conv = call_conv,
|
||||
.abi = abi,
|
||||
} });
|
||||
}
|
||||
|
||||
@@ -3745,8 +3762,8 @@ pub const Parser = struct {
|
||||
// builtin marker) is a function-type literal, not a function def.
|
||||
if (tag == .arrow) return self.hasFnBodyAfterArrow();
|
||||
// `kw_extern`/`kw_export`: a postfix linkage modifier (e.g. `f :: () extern;`
|
||||
// with no return type) marks a fn decl just like `callconv`.
|
||||
return tag == .l_brace or tag == .hash_builtin or tag == .hash_compiler or tag == .fat_arrow or tag == .kw_callconv or tag == .kw_extern or tag == .kw_export;
|
||||
// with no return type) marks a fn decl just like `abi(...)`.
|
||||
return tag == .l_brace or tag == .hash_builtin or tag == .hash_compiler or tag == .fat_arrow or tag == .kw_abi or tag == .kw_extern or tag == .kw_export;
|
||||
}
|
||||
|
||||
fn hasFnBodyAfterArrow(self: *Parser) bool {
|
||||
@@ -3773,9 +3790,9 @@ pub const Parser = struct {
|
||||
if (self.current.tag == .fat_arrow) return true;
|
||||
if (self.current.tag == .l_brace) return true;
|
||||
if (self.current.tag == .hash_builtin or self.current.tag == .hash_compiler) return true;
|
||||
if (self.current.tag == .kw_callconv) return true;
|
||||
if (self.current.tag == .kw_abi) return true;
|
||||
// Postfix linkage modifier after the return type: `-> R extern;` /
|
||||
// `-> R export { … }` (and `-> R callconv(.c) extern`). Marks a fn def.
|
||||
// `-> R export { … }` (and `-> R abi(.c) extern`). Marks a fn def.
|
||||
if (self.current.tag == .kw_extern or self.current.tag == .kw_export) return true;
|
||||
// Inside a `struct #compiler` block, a `(...) -> Ret;` ending
|
||||
// with `;` after the return type is a `#compiler` method
|
||||
@@ -3806,25 +3823,32 @@ pub const Parser = struct {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn parseOptionalCallConv(self: *Parser) anyerror!ast.CallingConvention {
|
||||
if (self.current.tag != .kw_callconv) return .default;
|
||||
/// Optional ABI / calling-convention annotation `abi(.c)` / `abi(.zig)` /
|
||||
/// `abi(.pure)` in the postfix slot before `extern`/`export`. `.default` when
|
||||
/// absent. Subsumes the old `callconv(...)` spelling.
|
||||
fn parseOptionalAbi(self: *Parser) anyerror!ast.ABI {
|
||||
if (self.current.tag != .kw_abi) return .default;
|
||||
self.advance();
|
||||
try self.expect(.l_paren);
|
||||
try self.expect(.dot);
|
||||
if (self.current.tag != .identifier)
|
||||
return self.fail("expected calling convention name after '.'");
|
||||
const cc_name = self.tokenSlice(self.current);
|
||||
const cc: ast.CallingConvention = if (std.mem.eql(u8, cc_name, "c")) .c else return self.fail("unknown calling convention");
|
||||
return self.fail("expected ABI name ('.c', '.zig', or '.pure') after '.'");
|
||||
const abi_name = self.tokenSlice(self.current);
|
||||
const abi: ast.ABI = if (std.mem.eql(u8, abi_name, "c"))
|
||||
.c
|
||||
else if (std.mem.eql(u8, abi_name, "zig"))
|
||||
.zig
|
||||
else if (std.mem.eql(u8, abi_name, "pure"))
|
||||
.pure
|
||||
else
|
||||
return self.fail("unknown ABI (expected '.c', '.zig', or '.pure')");
|
||||
self.advance();
|
||||
try self.expect(.r_paren);
|
||||
return cc;
|
||||
return abi;
|
||||
}
|
||||
|
||||
/// Postfix linkage modifier in the slot after `callconv(...)`:
|
||||
/// Postfix linkage modifier in the slot after `abi(...)`:
|
||||
/// `extern` (import) or `export` (define + expose), or `.none` if neither.
|
||||
/// Mirrors `parseOptionalCallConv`. Bare-keyword today; the optional
|
||||
/// `"csym"` symbol-name override lands in Phase 1.2/2.2. Defined here in
|
||||
/// Phase 0.1 but NOT yet called from any decl path (wired in Phase 1.0).
|
||||
fn parseOptionalExternExport(self: *Parser) ast.ExternExportModifier {
|
||||
switch (self.current.tag) {
|
||||
.kw_extern => {
|
||||
|
||||
Reference in New Issue
Block a user