comptime-API: strip the byte-weld; pivot to a flat-memory comptime VM
The byte-weld (sx structs whose layout was validated to mirror the compiler's Zig records) plus the serialization/marshaling bridge was the wrong direction: it bolted a parallel layout regime and hand-built byte-copies onto a comptime value model that fundamentally isn't bytes. Strip the struct-weld machinery: - compiler_lib.zig loses the type registry (weldStruct / bound_types / BoundType / FieldLayout / findType / SxField / LayoutMismatch / validateStructLayout); it is now just the intern/text_of function host-call bridge (kept as the Phase-3 compiler-call seed). - nominal.zig loses validateWeldedStruct / weldedFieldOrderStr + the sd.abi == .zig validation call. - Remove the struct-weld unit tests and examples 0625/0627 (welded structs) + 1183/1186 (weld-layout diagnostics). - The #library / abi / extern syntax stays. Record the new direction: a bytecode VM over flat, byte-addressable memory so comptime values are native bytes (no weld/validation/marshal), target-aware (preserves cross-compilation) and sandboxed. See current/PLAN-COMPILER-VM.md (Phase 0 strip -> Phase 1 flat-memory value model -> Phase 2 bytecode -> Phase 3 compiler-API on flat memory). design/comptime-compiler-api.md gets a SUPERSEDED banner. Also drop the "~500 lines / split the step" rule from CLAUDE.md.
This commit is contained in:
@@ -6,7 +6,6 @@ const mod_mod = @import("../module.zig");
|
||||
const type_bridge = @import("../type_bridge.zig");
|
||||
const program_index_mod = @import("../program_index.zig");
|
||||
const resolver_mod = @import("../resolver.zig");
|
||||
const compiler_lib = @import("../compiler_lib.zig");
|
||||
const StructTemplate = program_index_mod.StructTemplate;
|
||||
const TemplateParam = program_index_mod.TemplateParam;
|
||||
|
||||
@@ -674,13 +673,7 @@ pub fn registerStructDecl(self: *Lowering, sd: *const ast.StructDecl, source_fil
|
||||
// any forward-reference stub. Same-name structs in DIFFERENT sources get
|
||||
// distinct TypeIds instead of last-wins clobbering the first.
|
||||
const info: types.TypeInfo = .{ .@"struct" = .{ .name = name_id, .fields = fields.items } };
|
||||
const struct_tid = self.internNamedTypeDecl(decl_key, name_id, info, nominal_id);
|
||||
|
||||
// Welded `struct abi(.zig) extern compiler { … }`: the sx declaration is a
|
||||
// header checked against the compiler's real Zig type — validate the layout
|
||||
// matches the binding registry (a mismatch is a build error). See
|
||||
// design/comptime-compiler-api.md.
|
||||
if (sd.abi == .zig) validateWeldedStruct(self, sd, struct_tid, fields.items);
|
||||
_ = self.internNamedTypeDecl(decl_key, name_id, info, nominal_id);
|
||||
|
||||
// Store field defaults for struct literal lowering
|
||||
if (sd.field_defaults.len > 0) {
|
||||
@@ -716,77 +709,6 @@ pub fn registerStructDecl(self: *Lowering, sd: *const ast.StructDecl, source_fil
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate a welded `struct abi(.zig) extern <lib> { … }` against the `compiler`
|
||||
/// library's binding registry: the bound library must be `compiler`, the name
|
||||
/// must be on the export list, and the sx-declared layout must match the real Zig
|
||||
/// type's (the sx side is a *header* checked against the implementation). Any
|
||||
/// failure is a build-gating `.err` diagnostic — never a silent reinterpretation.
|
||||
fn validateWeldedStruct(self: *Lowering, sd: *const ast.StructDecl, tid: TypeId, fields: []const types.TypeInfo.StructInfo.Field) void {
|
||||
const diags = self.diagnostics orelse return;
|
||||
const table = &self.module.types;
|
||||
|
||||
// A span that points into the struct (its first field, else zero) — the decl
|
||||
// has no name span of its own.
|
||||
const span: ast.Span = if (sd.field_types.len > 0) sd.field_types[0].span else .{ .start = 0, .end = 0 };
|
||||
|
||||
// The bound library must be the sole welded source.
|
||||
if (sd.extern_lib == null or !std.mem.eql(u8, sd.extern_lib.?, compiler_lib.lib_name)) {
|
||||
diags.addFmt(.err, span, "abi(.zig) struct '{s}' must bind the compiler library — write `extern {s}`", .{ sd.name, compiler_lib.lib_name });
|
||||
return;
|
||||
}
|
||||
|
||||
// The name must be on the curated export list (the safety boundary).
|
||||
const bt = compiler_lib.findType(sd.name) orelse {
|
||||
diags.addFmt(.err, span, "'{s}' is not a type exported by the '{s}' library", .{ sd.name, compiler_lib.lib_name });
|
||||
return;
|
||||
};
|
||||
|
||||
// Build the observed sx layout (field name + computed size) and total size.
|
||||
var sx_fields = std.ArrayList(compiler_lib.SxField).empty;
|
||||
defer sx_fields.deinit(self.alloc);
|
||||
for (fields) |f| {
|
||||
sx_fields.append(self.alloc, .{
|
||||
.name = table.getString(f.name),
|
||||
.size = table.typeSizeBytes(f.ty),
|
||||
}) catch return;
|
||||
}
|
||||
const total = table.typeSizeBytes(tid);
|
||||
|
||||
const mismatch = compiler_lib.validateStructLayout(bt, sx_fields.items, total) orelse return;
|
||||
// The compiler type's fields, in the memory order an sx header must mirror —
|
||||
// included in the order/count diagnostics so the fix is obvious.
|
||||
const order = weldedFieldOrderStr(self.alloc, bt);
|
||||
defer if (order.len > 0) self.alloc.free(order);
|
||||
switch (mismatch) {
|
||||
.field_count => |m| diags.addFmt(.err, span, "welded type '{s}': the compiler type has {d} field(s) but the declaration has {d} — declare them in memory order: {s}", .{ sd.name, m.expected, m.got, order }),
|
||||
.field_name => |m| {
|
||||
// Distinguish "this name isn't a field at all" from "right field set,
|
||||
// wrong order".
|
||||
const exists = blk: {
|
||||
for (bt.fields) |bf| if (std.mem.eql(u8, bf.name, m.got)) break :blk true;
|
||||
break :blk false;
|
||||
};
|
||||
if (exists)
|
||||
diags.addFmt(.err, span, "welded type '{s}': wrong field order at position {d} — found '{s}', the compiler type has '{s}' here (memory order: {s})", .{ sd.name, m.index, m.got, m.expected, order })
|
||||
else
|
||||
diags.addFmt(.err, span, "welded type '{s}': field '{s}' is not a field of the compiler type (its fields, in memory order: {s})", .{ sd.name, m.got, order });
|
||||
},
|
||||
.field_size => |m| diags.addFmt(.err, span, "welded type '{s}': type layout mismatch — field '{s}' is {d} byte(s) in the compiler type but {d} as declared", .{ sd.name, m.name, m.expected, m.got }),
|
||||
.total_size => |m| diags.addFmt(.err, span, "welded type '{s}': layout mismatch — the compiler type is {d} byte(s) but the declaration is {d} (alignment/padding)", .{ sd.name, m.expected, m.got }),
|
||||
}
|
||||
}
|
||||
|
||||
/// The bound type's field names in memory order, `, `-joined, for diagnostics.
|
||||
/// Returns an owned string; empty (no free needed) on allocation failure.
|
||||
fn weldedFieldOrderStr(alloc: std.mem.Allocator, bt: *const compiler_lib.BoundType) []const u8 {
|
||||
var buf = std.ArrayList(u8).empty;
|
||||
for (bt.fields, 0..) |bf, i| {
|
||||
if (i > 0) buf.appendSlice(alloc, ", ") catch return "";
|
||||
buf.appendSlice(alloc, bf.name) catch return "";
|
||||
}
|
||||
return buf.toOwnedSlice(alloc) catch "";
|
||||
}
|
||||
|
||||
/// Register a top-level ENUM decl under a per-decl nominal identity (E6a) —
|
||||
/// the enum twin of `registerStructDecl`. A GENUINE same-name shadow already
|
||||
/// reserved its DISTINCT slot up-front in `scanDecls` (the first at id 0, the
|
||||
|
||||
Reference in New Issue
Block a user