comptime VM arc: abi(.compiler) ABI, out as sx fn, VM-native diagnostics, BuildConfig threaded

Lands the full VM/compiler-API arc on branch reify (701/0 both gates):
- abi(.compiler) ABI replaces abi(.zig) extern compiler + the fake
  #library "compiler"; bodiless decl = compiler-API surface, bodied =
  user compiler-domain fn (lowered for VM eval, emit-skipped).
- out is a plain sx fn (libc write) — the out builtin deleted; the VM
  handles it via host-FFI. trace_resolve + interp_print_frames ported.
- 4B VM-native diagnostics: 1179/1180 render proper comptime type
  construction failed: under strict.
- S5a: build_options/set_post_link_callback on abi(.compiler) with
  BuildConfig threaded into the VM (green intermediate).
- 0522 fixed (describe(args: []Type)); regression 0638.

Strict deletion-gate down to 4 compiler_call bails (1609/1614/1615/1616)
+ 1654 (legitimate unresolvable-symbol diagnostic).
This commit is contained in:
agra
2026-06-19 07:04:10 +03:00
parent fdc4ee2331
commit 2060373c16
80 changed files with 12684 additions and 11922 deletions

View File

@@ -499,6 +499,12 @@ pub fn detectContextDecl(decls: []const *const Node) bool {
pub fn funcWantsImplicitCtx(self: *const Lowering, fd: *const ast.FnDecl) bool {
if (!self.implicit_ctx_enabled) return false;
if (fd.abi == .c) return false;
// A BODILESS `abi(.compiler)` decl (compiler-API surface) is dispatched by name
// to a Zig/VM handler with exactly the declared args; an implicit `__sx_ctx`
// prepend would shift every arg (breaking the handler's arity check). No sx
// context, like an extern import. (A BODIED `abi(.compiler)` function is a real
// sx function the VM runs — it gets the normal implicit-ctx treatment.)
if (fnIsBodilessCompiler(fd)) return false;
// `extern` imports and `export` defines are external C symbols —
// C ABI, no sx context (Phase 2, gap iv).
if (fd.extern_export != .none) return false;
@@ -2229,7 +2235,13 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8)
// declareExtern routing below; the optional `extern LIB "csym"` lib/rename
// axis is extern_lib/extern_name. (`export` defines take the beginFunction
// path, not here.) The `#import c` auto-synthesis also produces this shape.
const is_extern_decl = fd.extern_export == .extern_;
// A bodiless `abi(.compiler)` decl (the compiler-API surface) has no runtime
// body — the Zig/VM handler is the impl — so it lowers exactly like an `extern`
// import (declared, never defined; the comptime evaluator dispatches it via
// `compiler_welded`). A BODIED `abi(.compiler)` function (a compiler-domain
// callback) is NOT extern — it has a body the VM evaluates — and is handled
// below (`is_compiler_domain` + `is_comptime`, body lowered, emit-skipped).
const is_extern_decl = fd.extern_export == .extern_ or fnIsBodilessCompiler(fd);
var is_variadic = false;
var effective_params = fd.params;
// A lib-less C-import with a C-variadic `...` tail: drop the trailing slice
@@ -2298,6 +2310,15 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8)
func.is_variadic = is_variadic;
func.has_implicit_ctx = wants_ctx;
if (weldedCompilerFn(self, fd, name)) func.compiler_welded = true;
// A BODIED `abi(.compiler)` function is a user compiler-domain function (e.g. a
// post-link callback): the VM runs its sx body, but it NEVER runs in the binary
// so the backend skips it (emit_llvm Pass 2). Flag `is_compiler_domain` (the
// emit-skip) + `is_comptime` (so any compiler-API calls inside it are permitted
// by the `emitCall` gate, and its dead LLVM decl is treated like a #run wrapper).
if (fd.abi == .compiler and !fnIsBodilessCompiler(fd)) {
func.is_compiler_domain = true;
func.is_comptime = true;
}
// A non-generic `-> Type` builder is a comptime type constructor — only ever
// evaluated at lowering time (`runComptimeTypeFunc`) to mint a type, never
// called at runtime. Flag it `is_comptime` so its emitted body is dead: the
@@ -2309,20 +2330,25 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8)
self.fn_decl_fids.put(fd, fid) catch {};
}
/// A `fn abi(.zig) extern <lib>` binds the comptime `compiler` library. Validate
/// it (the bound lib must be `compiler`; the name must be on the function-export
/// list) and return whether it is a welded compiler function — the interpreter
/// dispatches such a call to its registered Zig handler instead of dlsym. Any
/// failure is a build-gating `.err` (never a silent fall-through to dlsym).
/// A BODILESS `abi(.compiler)` decl (ends in `;`, no sx body) — the compiler-API
/// surface (`intern`/`find_type`/`build_options`/…), whose Zig/VM handler is the
/// impl. Distinguished from a BODIED `abi(.compiler)` function (a user
/// compiler-domain function, e.g. a post-link callback) by its synthesized
/// empty-block body. The two lower differently: bodiless = declared-not-defined
/// (extern-like); bodied = body lowered for VM eval but emit-skipped (S3).
fn fnIsBodilessCompiler(fd: *const ast.FnDecl) bool {
return fd.abi == .compiler and fd.body.data == .block and fd.body.data.block.stmts.len == 0;
}
/// A bodiless `abi(.compiler)` decl is a compiler-API function: the comptime
/// evaluator dispatches the call to its registered Zig/VM handler instead of dlsym.
/// The ABI alone marks it (no `extern <lib>`, no fake `#library`). Validate the name
/// is on the function-export list; failure is a build-gating `.err` (never a silent
/// fall-through to dlsym).
fn weldedCompilerFn(self: *Lowering, fd: *const ast.FnDecl, name: []const u8) bool {
if (fd.abi != .zig) return false;
const diags = self.diagnostics;
if (fd.extern_lib == null or !std.mem.eql(u8, fd.extern_lib.?, compiler_lib.lib_name)) {
if (diags) |d| d.addFmt(.err, fd.name_span, "abi(.zig) function '{s}' must bind the compiler library — write `extern {s}`", .{ name, compiler_lib.lib_name });
return false;
}
if (!fnIsBodilessCompiler(fd)) return false;
if (compiler_lib.findFn(name) == null) {
if (diags) |d| d.addFmt(.err, fd.name_span, "'{s}' is not a function exported by the '{s}' library", .{ name, compiler_lib.lib_name });
if (self.diagnostics) |d| d.addFmt(.err, fd.name_span, "'{s}' is not a function exported by the compiler", .{name});
return false;
}
return true;
@@ -2570,8 +2596,11 @@ pub fn lowerFunctionBodyInto(self: *Lowering, fd: *const ast.FnDecl, fid: FuncId
// `extern` imports are pure declarations — never promote the stub to a real
// function or lower the (empty placeholder) body. Mirrors the declare-only
// handling in lowerFunction / lazyLowerFunction.
if (fd.extern_export == .extern_) return;
// handling in lowerFunction / lazyLowerFunction. A bodiless `abi(.compiler)`
// decl (the compiler-API surface) is declare-only too — the Zig/VM handler is
// the impl. A BODIED `abi(.compiler)` function DOES lower its body (for VM eval);
// it is emit-skipped later via `is_compiler_domain`, not here.
if (fd.extern_export == .extern_ or fnIsBodilessCompiler(fd)) return;
const ret_ty = self.resolveReturnType(fd);
@@ -2681,7 +2710,13 @@ pub fn lowerFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8, i
// Check if the function body is a builtin or extern declaration (no body
// needed). `extern` imports are declare-only too (empty placeholder body).
if (fd.body.data == .builtin_expr or fd.body.data == .compiler_expr or fd.extern_export == .extern_) {
// A bodiless `abi(.compiler)` decl (the compiler-API surface) is likewise
// declare-only — its Zig/VM handler is the impl. A BODIED `abi(.compiler)`
// function DOES need its body lowered for VM eval (emit-skipped later via
// `is_compiler_domain`), so it falls through to normal lowering below.
if (fd.body.data == .builtin_expr or fd.body.data == .compiler_expr or
fd.extern_export == .extern_ or fnIsBodilessCompiler(fd))
{
// Already declared by scanDecls/declareFunction (which handles #extern renames)
return;
}