P5.7 Step B2: remove the #compiler attribute + compiler_expr AST node

The #compiler struct attribute + #compiler-suffixed bodyless methods were
fully superseded by abi(.compiler) (P5.5) — no sx code uses them.

Remove the hash_compiler token (token/lexer/lsp), the is_compiler_struct /
struct_default_compiler parser machinery + the two compiler_expr body-
synthesis branches, the compiler_expr AST variant, and every
.builtin_expr/.compiler_expr switch arm + == .compiler_expr check across
sema/resolver/semantic_diagnostics/generic/decl/call/calls (kept .builtin_expr).
abi(.compiler) is untouched. Delete the obsolete calls.test.zig dispatch test.

500/500 unit + 706/0 corpus.
This commit is contained in:
agra
2026-06-19 17:18:45 +03:00
parent e2971f272c
commit 64eb01918a
14 changed files with 31 additions and 101 deletions

View File

@@ -320,32 +320,6 @@ test "plan: protocol dispatch selects method index + prepends receiver" {
try std.testing.expect(p.prepends_receiver);
}
test "plan: struct (UFCS) method via #compiler dispatch + prepends receiver" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
var module = ir_mod.Module.init(alloc);
defer module.deinit();
var l = Lowering.init(&module);
const cr = CallResolver{ .l = &l };
// struct Point, with a `#compiler` method Point.scale(self) -> i64.
_ = module.types.intern(.{ .@"struct" = .{ .name = module.types.internString("Point"), .fields = &.{} } });
const self_param = ast.Param{ .name = "self", .name_span = .{ .start = 0, .end = 0 }, .type_expr = typeExpr(alloc, "Point") };
const params = [_]ast.Param{self_param};
const compiler_body = mk(alloc, .{ .compiler_expr = {} });
const method_fd = ast.FnDecl{ .name = "Point.scale", .params = &params, .return_type = typeExpr(alloc, "i64"), .body = compiler_body };
l.program_index.fn_ast_map.put("Point.scale", &method_fd) catch unreachable;
const recv = callNode(alloc, ident(alloc, "cast"), &[_]*Node{ typeExpr(alloc, "Point"), intLit(alloc, 0) });
const call = callNode(alloc, fieldAccess(alloc, recv, "scale"), &.{});
const p = cr.plan(&call.data.call);
try std.testing.expectEqual(CallPlan.Kind.struct_method, p.kind);
try std.testing.expectEqualStrings("Point.scale", p.target.named);
try std.testing.expectEqual(TypeId.i64, p.return_type);
try std.testing.expect(p.prepends_receiver);
}
test "plan: runtime-class instance vs static dispatch" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();

View File

@@ -282,18 +282,6 @@ pub const CallResolver = struct {
if (oi == .@"struct") {
const struct_name = self.l.module.types.getString(oi.@"struct".name);
const qualified = std.fmt.allocPrint(self.l.alloc, "{s}.{s}", .{ struct_name, cfa.field }) catch cfa.field;
// Generic #compiler method dispatch — return type from declaration.
if (self.l.program_index.fn_ast_map.get(qualified)) |method_fd| {
if (method_fd.body.data == .compiler_expr) {
return .{
.kind = .struct_method,
.return_type = if (method_fd.return_type) |rt| type_bridge.resolveAstType(rt, &self.l.module.types, &self.l.program_index.type_alias_map, &self.l.program_index.module_const_map) else .void,
.target = .{ .named = qualified },
.prepends_receiver = true,
.expands_defaults = defaultsFor(method_fd, c.args.len + 1),
};
}
}
if (self.l.resolveFuncByName(qualified)) |fid| {
const func = &self.l.module.functions.items[@intFromEnum(fid)];
return .{

View File

@@ -718,7 +718,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
// literal global symbol — the existing bare-name
// machinery below resolves them.
switch (fd.body.data) {
.builtin_expr, .compiler_expr => break :gate,
.builtin_expr => break :gate,
else => {},
}
if (hasComptimeParams(fd)) return self.lowerComptimeCall(fd, c);
@@ -919,7 +919,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
// Generic method on a non-template struct: `obj.method($T, ...)`
// or inferred form `obj.method(val)` where val's type pins $T.
if (self.program_index.fn_ast_map.get(qualified)) |gen_fd| {
if (gen_fd.type_params.len > 0 and gen_fd.body.data != .compiler_expr) {
if (gen_fd.type_params.len > 0) {
// Effective AST args: prepend receiver so positions
// line up with fd.params (which has self at index 0).
var eff_args = std.ArrayList(*const Node).empty;
@@ -2090,7 +2090,7 @@ pub fn checkCallArity(self: *Lowering, fd: *const ast.FnDecl, callee_name: []con
if (fd.type_params.len > 0 or hasComptimeParams(fd) or isPackFn(fd)) return false;
switch (fd.body.data) {
.compiler_expr, .builtin_expr => return false,
.builtin_expr => return false,
else => {},
}
var min: usize = 0;

View File

@@ -523,7 +523,7 @@ pub fn funcWantsImplicitCtx(self: *const Lowering, fd: *const ast.FnDecl) bool {
// C ABI, no sx context (Phase 2, gap iv).
if (fd.extern_export != .none) return false;
return switch (fd.body.data) {
.builtin_expr, .compiler_expr => false,
.builtin_expr => false,
else => !isExportedEntryName(fd.name),
};
}
@@ -2402,10 +2402,10 @@ pub fn registerQualifiedFn(self: *Lowering, ns_name: []const u8, fd: *const ast.
// collision assert; registering a qualified alias for them
// would divert that machinery and strand a per-call type binding.
if (fd.type_params.len > 0 or hasComptimeParams(fd) or isPackFn(fd)) return;
// Extern / builtin / #compiler bodies keep their literal name; a
// qualified alias has no distinct symbol to resolve to.
// Extern / builtin bodies keep their literal name; a qualified alias has
// no distinct symbol to resolve to.
switch (fd.body.data) {
.builtin_expr, .compiler_expr => return,
.builtin_expr => return,
else => {},
}
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ ns_name, short }) catch return;
@@ -2521,8 +2521,8 @@ pub fn lazyLowerFunction(self: *Lowering, name: []const u8) void {
}
return;
}
// Builtins / #compiler bodies stay as compiler-handled — no extern stub needed.
if (fd.body.data == .builtin_expr or fd.body.data == .compiler_expr) return;
// Builtin bodies stay as compiler-handled — no extern stub needed.
if (fd.body.data == .builtin_expr) return;
if (fd.type_params.len > 0) return; // generics handled by monomorphization (Step 3.13)
// Defer functions with type-category matches until all types are registered.
@@ -2728,7 +2728,7 @@ pub fn lowerFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8, i
// 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
if (fd.body.data == .builtin_expr or
fd.extern_export == .extern_ or fnIsBodilessCompiler(fd))
{
// Already declared by scanDecls/declareFunction (which handles #extern renames)

View File

@@ -822,7 +822,7 @@ pub fn isPlainFreeFn(fd: *const ast.FnDecl) bool {
// fn. `export` DEFINES a real body, so it stays plain-free.
if (fd.extern_export == .extern_) return false;
return switch (fd.body.data) {
.builtin_expr, .compiler_expr => false,
.builtin_expr => false,
else => true,
};
}

View File

@@ -184,7 +184,7 @@ pub fn isPlainFreeFnDecl(fd: *const ast.FnDecl) bool {
// body, so it stays plain-free.
if (fd.extern_export == .extern_) return false;
return switch (fd.body.data) {
.builtin_expr, .compiler_expr => false,
.builtin_expr => false,
else => true,
};
}

View File

@@ -355,7 +355,6 @@ pub const UnknownTypeChecker = struct {
.undef_literal,
.inferred_type,
.builtin_expr,
.compiler_expr,
.framework_decl,
.function_type_expr,
.closure_type_expr,