forward declaration
This commit is contained in:
406
src/codegen.zig
406
src/codegen.zig
@@ -1239,13 +1239,27 @@ pub const CodeGen = struct {
|
||||
|
||||
pub fn generate(self: *CodeGen, root: *Node) !void {
|
||||
if (root.data != .root) return self.emitError("expected root node for code generation");
|
||||
// Store root decls for VM on-demand function compilation
|
||||
self.root_decls = root.data.root.decls;
|
||||
// Initialize built-in function declarations (printf, etc.)
|
||||
self.builtins = Builtins.init(self.module, self.context);
|
||||
const decls = root.data.root.decls;
|
||||
|
||||
// Pre-scan: collect named library constants (handles forward references)
|
||||
for (root.data.root.decls) |decl| {
|
||||
try self.collectLibraries(decls);
|
||||
|
||||
// Each phase depends on the previous: types must exist before fields can
|
||||
// reference them, fields must be resolved before function signatures can
|
||||
// use them as parameter/return types, and all signatures must be registered
|
||||
// before emitting function bodies.
|
||||
try self.registerTypes(decls);
|
||||
try self.resolveFields(decls);
|
||||
try self.registerFunctions(decls);
|
||||
try self.generateBodies(decls);
|
||||
try self.generateDeferred();
|
||||
}
|
||||
|
||||
// ── Phase 0: collect library constants ──────────────────────────────
|
||||
|
||||
fn collectLibraries(self: *CodeGen, decls: []const *Node) !void {
|
||||
for (decls) |decl| {
|
||||
self.current_source_file = decl.source_file;
|
||||
switch (decl.data) {
|
||||
.library_decl => |ld| {
|
||||
@@ -1253,33 +1267,29 @@ pub const CodeGen = struct {
|
||||
},
|
||||
.namespace_decl => |ns| {
|
||||
for (ns.decls) |nd| {
|
||||
switch (nd.data) {
|
||||
.library_decl => |nld| {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, nld.name });
|
||||
try self.library_constants.put(qualified, nld.lib_name);
|
||||
},
|
||||
else => {},
|
||||
if (nd.data == .library_decl) {
|
||||
const nld = nd.data.library_decl;
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, nld.name });
|
||||
try self.library_constants.put(qualified, nld.lib_name);
|
||||
}
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 1: Register all declarations (signatures only, no bodies)
|
||||
for (root.data.root.decls) |decl| {
|
||||
// ── Phase 1: register type names (no field resolution yet) ──────────
|
||||
|
||||
fn registerTypes(self: *CodeGen, decls: []const *Node) !void {
|
||||
for (decls) |decl| {
|
||||
self.current_source_file = decl.source_file;
|
||||
switch (decl.data) {
|
||||
.fn_decl => |fd| {
|
||||
if (fd.body.data == .builtin_expr) {
|
||||
try self.builtin_functions.put(fd.name, {});
|
||||
} else if (fd.body.data == .foreign_expr) {
|
||||
// External C function — register LLVM declaration (no body)
|
||||
try self.registerFnDecl(fd, fd.name);
|
||||
} else if (fd.type_params.len > 0) {
|
||||
try self.generic_templates.put(fd.name, fd);
|
||||
} else {
|
||||
try self.registerFnDecl(fd, fd.name);
|
||||
}
|
||||
try self.fn_signatures.put(fd.name, self.buildFnSignature(fd));
|
||||
},
|
||||
@@ -1288,68 +1298,48 @@ pub const CodeGen = struct {
|
||||
},
|
||||
.enum_decl => |ed| {
|
||||
if (ed.variant_types.len > 0) {
|
||||
// Tagged enum with payloads
|
||||
try self.registerTaggedEnum(ed);
|
||||
try self.registerTaggedEnumName(ed);
|
||||
} else {
|
||||
// Payload-less enum
|
||||
try self.type_registry.put(ed.name, .{ .plain_enum = ed.variant_names });
|
||||
_ = try self.getAnyTypeId(ed.name, .{ .enum_type = ed.name });
|
||||
|
||||
if (ed.is_flags) {
|
||||
try self.flags_enum_types.put(ed.name, {});
|
||||
}
|
||||
|
||||
// Register backing type if specified
|
||||
if (ed.backing_type) |bt_node| {
|
||||
const bt = self.resolveType(bt_node);
|
||||
try self.enum_backing_types.put(ed.name, self.typeToLLVM(bt));
|
||||
}
|
||||
|
||||
// Compute and store variant values
|
||||
if (ed.is_flags) try self.flags_enum_types.put(ed.name, {});
|
||||
// enum variant values (no resolveType needed)
|
||||
const values = try self.allocator.alloc(i64, ed.variant_names.len);
|
||||
for (ed.variant_names, 0..) |_, i| {
|
||||
if (ed.variant_values.len > i and ed.variant_values[i] != null) {
|
||||
// Explicit value: evaluate comptime int literal
|
||||
const val_node = ed.variant_values[i].?;
|
||||
values[i] = switch (val_node.data) {
|
||||
.int_literal => |il| il.value,
|
||||
else => @as(i64, @intCast(i)), // fallback
|
||||
else => @as(i64, @intCast(i)),
|
||||
};
|
||||
} else if (ed.is_flags) {
|
||||
// Auto power-of-2: 1, 2, 4, 8, ...
|
||||
values[i] = @as(i64, 1) << @intCast(i);
|
||||
} else {
|
||||
// Regular enum: sequential 0, 1, 2, ...
|
||||
values[i] = @intCast(i);
|
||||
}
|
||||
}
|
||||
try self.enum_variant_values.put(ed.name, values);
|
||||
}
|
||||
},
|
||||
.struct_decl => |sd| try self.registerStructType(sd),
|
||||
.union_decl => |ud| try self.registerUnionType(ud),
|
||||
.struct_decl => |sd| try self.registerStructName(sd),
|
||||
.union_decl => |ud| try self.registerUnionName(ud),
|
||||
.const_decl => |cd| {
|
||||
if (cd.value.data == .builtin_expr) {
|
||||
// #builtin constant — skip codegen
|
||||
} else if (cd.value.data == .lambda) {
|
||||
try self.registerLambdaAsFunction(cd.name, cd.value.data.lambda);
|
||||
// skip
|
||||
} else if (cd.value.data == .type_expr) {
|
||||
try self.type_registry.put(cd.name, .{ .alias = cd.value.data.type_expr.name });
|
||||
} else if (cd.value.data == .call) {
|
||||
// Check if this is a generic struct or type function instantiation
|
||||
const callee_name = if (cd.value.data.call.callee.data == .identifier)
|
||||
cd.value.data.call.callee.data.identifier.name
|
||||
else
|
||||
null;
|
||||
if (callee_name) |cn| {
|
||||
if (self.generic_struct_templates.get(cn)) |tmpl| {
|
||||
// Generic struct instantiation: Vec3 :: Vec(3, f32);
|
||||
const result_ty = try self.instantiateGenericStruct(cn, tmpl, cd.value.data.call.args);
|
||||
if (result_ty.isStruct()) {
|
||||
try self.type_registry.put(cd.name, .{ .alias = result_ty.struct_type });
|
||||
}
|
||||
} else if (self.generic_templates.get(cn)) |tmpl| {
|
||||
// Type-returning function: Foo :: Complex(u32);
|
||||
const result_ty = try self.instantiateTypeFunction(cd.name, cn, tmpl, cd.value.data.call.args);
|
||||
if (result_ty.isStruct()) {
|
||||
try self.type_registry.put(cd.name, .{ .alias = result_ty.struct_type });
|
||||
@@ -1357,11 +1347,74 @@ pub const CodeGen = struct {
|
||||
try self.type_registry.put(cd.name, .{ .alias = result_ty.union_type });
|
||||
}
|
||||
} else if (self.builtin_functions.contains(cn)) {
|
||||
// Builtin type function (e.g., Vector(4, f32), Array(5, s32))
|
||||
if (self.resolveBuiltinType(cn, cd.value.data.call.args)) |result_ty| {
|
||||
const display = try result_ty.displayName(self.allocator);
|
||||
try self.type_registry.put(cd.name, .{ .alias = display });
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
.namespace_decl => |ns| try self.registerNamespaceTypes(ns),
|
||||
.protocol_decl => |pd| try self.registerProtocolDecl(pd),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Phase 2: resolve struct/union/enum field types ──────────────────
|
||||
|
||||
fn resolveFields(self: *CodeGen, decls: []const *Node) !void {
|
||||
for (decls) |decl| {
|
||||
self.current_source_file = decl.source_file;
|
||||
switch (decl.data) {
|
||||
.enum_decl => |ed| {
|
||||
if (ed.variant_types.len > 0) {
|
||||
try self.resolveTaggedEnumFields(ed);
|
||||
} else if (ed.backing_type) |bt_node| {
|
||||
const bt = self.resolveType(bt_node);
|
||||
try self.enum_backing_types.put(ed.name, self.typeToLLVM(bt));
|
||||
}
|
||||
},
|
||||
.struct_decl => |sd| try self.resolveStructFields(sd),
|
||||
.union_decl => |ud| try self.resolveUnionFields(ud),
|
||||
.namespace_decl => |ns| try self.resolveNamespaceFields(ns),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Phase 3: register function signatures ───────────────────────────
|
||||
|
||||
fn registerFunctions(self: *CodeGen, decls: []const *Node) !void {
|
||||
for (decls) |decl| {
|
||||
self.current_source_file = decl.source_file;
|
||||
switch (decl.data) {
|
||||
.fn_decl => |fd| {
|
||||
if (fd.body.data != .builtin_expr and fd.type_params.len == 0) {
|
||||
try self.registerFnDecl(fd, fd.name);
|
||||
}
|
||||
},
|
||||
.struct_decl => |sd| try self.registerStructMethods(sd),
|
||||
.const_decl => |cd| {
|
||||
if (cd.value.data == .builtin_expr or cd.value.data == .type_expr) {
|
||||
// already handled
|
||||
} else if (cd.value.data == .lambda) {
|
||||
try self.registerLambdaAsFunction(cd.name, cd.value.data.lambda);
|
||||
} else if (cd.value.data == .call) {
|
||||
const callee_name = if (cd.value.data.call.callee.data == .identifier)
|
||||
cd.value.data.call.callee.data.identifier.name
|
||||
else
|
||||
null;
|
||||
if (callee_name) |cn| {
|
||||
if (self.generic_struct_templates.contains(cn)) {
|
||||
// type instantiation, already handled
|
||||
} else if (self.generic_templates.contains(cn)) {
|
||||
if (!self.type_registry.contains(cd.name)) {
|
||||
try self.registerTopLevelConstant(cd);
|
||||
}
|
||||
} else if (self.builtin_functions.contains(cn)) {
|
||||
if (!self.type_registry.contains(cd.name)) {
|
||||
try self.registerTopLevelConstant(cd);
|
||||
}
|
||||
} else {
|
||||
@@ -1371,45 +1424,33 @@ pub const CodeGen = struct {
|
||||
try self.registerTopLevelConstant(cd);
|
||||
}
|
||||
} else if (cd.value.data == .comptime_expr) {
|
||||
// Use explicit type annotation if available
|
||||
const ct_type_override: ?Type = if (cd.type_annotation) |te| Type.fromTypeExpr(te) else null;
|
||||
try self.registerComptimeGlobal(cd.name, cd.value.data.comptime_expr.expr, ct_type_override);
|
||||
} else {
|
||||
// Top-level value constant (e.g., SPECIAL_VALUE :u8: 42;)
|
||||
try self.registerTopLevelConstant(cd);
|
||||
}
|
||||
},
|
||||
.comptime_expr => |ct| {
|
||||
try self.comptime_side_effects.append(self.allocator, ct.expr);
|
||||
},
|
||||
.namespace_decl => |ns| {
|
||||
try self.registerNamespace(ns);
|
||||
},
|
||||
.var_decl => |vd| {
|
||||
try self.registerGlobalVar(vd);
|
||||
},
|
||||
.ufcs_alias => |ua| {
|
||||
try self.ufcs_aliases.put(ua.name, ua.target);
|
||||
},
|
||||
.protocol_decl => |pd| {
|
||||
try self.registerProtocolDecl(pd);
|
||||
},
|
||||
.impl_block => |ib| {
|
||||
try self.registerImplBlock(ib);
|
||||
},
|
||||
.namespace_decl => |ns| try self.registerNamespaceFunctions(ns),
|
||||
.var_decl => |vd| try self.registerGlobalVar(vd),
|
||||
.ufcs_alias => |ua| try self.ufcs_aliases.put(ua.name, ua.target),
|
||||
.impl_block => |ib| try self.registerImplBlock(ib),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2: Generate all function bodies
|
||||
// Functions with Any parameters (like any_to_string) are deferred to Pass 3
|
||||
// so that all types are registered before their type-match expressions are compiled.
|
||||
for (root.data.root.decls) |decl| {
|
||||
// ── Phase 4: generate function bodies ───────────────────────────────
|
||||
|
||||
fn generateBodies(self: *CodeGen, decls: []const *Node) !void {
|
||||
for (decls) |decl| {
|
||||
self.current_source_file = decl.source_file;
|
||||
switch (decl.data) {
|
||||
.fn_decl => |fd| {
|
||||
if (fd.body.data == .builtin_expr or fd.body.data == .foreign_expr) {
|
||||
// skip — no body to generate
|
||||
// no body to generate
|
||||
} else if (fd.type_params.len == 0) {
|
||||
if (shouldDeferFnBody(fd)) {
|
||||
try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = fd.name, .source_file = self.current_source_file });
|
||||
@@ -1425,24 +1466,22 @@ pub const CodeGen = struct {
|
||||
},
|
||||
.namespace_decl => |ns| {
|
||||
try self.genNamespaceBodies(ns);
|
||||
// Generate struct method bodies within namespace
|
||||
for (ns.decls) |nd| {
|
||||
if (nd.data == .struct_decl) {
|
||||
try self.genStructMethodBodies(nd.data.struct_decl);
|
||||
}
|
||||
}
|
||||
},
|
||||
.struct_decl => |sd| {
|
||||
try self.genStructMethodBodies(sd);
|
||||
},
|
||||
.impl_block => |ib| {
|
||||
try self.genImplMethodBodies(ib);
|
||||
},
|
||||
.struct_decl => |sd| try self.genStructMethodBodies(sd),
|
||||
.impl_block => |ib| try self.genImplMethodBodies(ib),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 3: Compile deferred function bodies (after all types are registered)
|
||||
// ── Phase 5: deferred bodies + comptime side effects ────────────────
|
||||
|
||||
fn generateDeferred(self: *CodeGen) !void {
|
||||
for (self.deferred_fn_bodies.items) |deferred| {
|
||||
const saved_ns = self.current_namespace;
|
||||
const saved_sf = self.current_source_file;
|
||||
@@ -1453,7 +1492,6 @@ pub const CodeGen = struct {
|
||||
try self.genFnBody(deferred.fd, deferred.name);
|
||||
}
|
||||
|
||||
// Execute comptime side effects via bytecode VM (e.g., #run main();)
|
||||
for (self.comptime_side_effects.items) |expr| {
|
||||
_ = try self.comptimeEval(expr, .void_type);
|
||||
}
|
||||
@@ -2358,8 +2396,73 @@ pub const CodeGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn registerNamespace(self: *CodeGen, ns: ast.NamespaceDecl) !void {
|
||||
/// registerTypes helper: register type names within a namespace.
|
||||
fn registerNamespaceTypes(self: *CodeGen, ns: ast.NamespaceDecl) !void {
|
||||
try self.namespaces.put(ns.name, {});
|
||||
for (ns.decls) |decl| {
|
||||
switch (decl.data) {
|
||||
.enum_decl => |ed| {
|
||||
if (ed.variant_types.len > 0) {
|
||||
try self.registerTaggedEnumName(ed);
|
||||
const qualified_u = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ed.name });
|
||||
try self.type_registry.put(qualified_u, .{ .alias = ed.name });
|
||||
} else {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ed.name });
|
||||
try self.type_registry.put(qualified, .{ .plain_enum = ed.variant_names });
|
||||
_ = try self.getAnyTypeId(qualified, .{ .enum_type = qualified });
|
||||
}
|
||||
},
|
||||
.struct_decl => |sd| {
|
||||
try self.registerStructName(sd);
|
||||
const qualified_s = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, sd.name });
|
||||
try self.type_registry.put(qualified_s, .{ .alias = sd.name });
|
||||
},
|
||||
.union_decl => |ud| {
|
||||
try self.registerUnionName(ud);
|
||||
const qualified_u = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ud.name });
|
||||
try self.type_registry.put(qualified_u, .{ .alias = ud.name });
|
||||
},
|
||||
.const_decl => |cd| {
|
||||
if (cd.value.data == .type_expr) {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, cd.name });
|
||||
try self.type_registry.put(qualified, .{ .alias = cd.value.data.type_expr.name });
|
||||
}
|
||||
},
|
||||
.library_decl => |ld| {
|
||||
try self.foreign_libraries.append(self.allocator, ld.lib_name);
|
||||
},
|
||||
.protocol_decl => |pd| {
|
||||
try self.registerProtocolDecl(pd);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// resolveFields helper: resolve field types within a namespace.
|
||||
fn resolveNamespaceFields(self: *CodeGen, ns: ast.NamespaceDecl) !void {
|
||||
for (ns.decls) |decl| {
|
||||
switch (decl.data) {
|
||||
.enum_decl => |ed| {
|
||||
if (ed.variant_types.len > 0) {
|
||||
try self.resolveTaggedEnumFields(ed);
|
||||
} else {
|
||||
if (ed.backing_type) |bt_node| {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ed.name });
|
||||
const bt = self.resolveType(bt_node);
|
||||
try self.enum_backing_types.put(qualified, self.typeToLLVM(bt));
|
||||
}
|
||||
}
|
||||
},
|
||||
.struct_decl => |sd| try self.resolveStructFields(sd),
|
||||
.union_decl => |ud| try self.resolveUnionFields(ud),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// registerFunctions helper: register function signatures within a namespace.
|
||||
fn registerNamespaceFunctions(self: *CodeGen, ns: ast.NamespaceDecl) !void {
|
||||
for (ns.decls) |decl| {
|
||||
switch (decl.data) {
|
||||
.fn_decl => |fd| {
|
||||
@@ -2370,18 +2473,14 @@ pub const CodeGen = struct {
|
||||
}
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, fd.name });
|
||||
if (fd.body.data == .foreign_expr) {
|
||||
// External C function in namespace — register LLVM declaration
|
||||
try self.registerFnDecl(fd, fd.name);
|
||||
// Also track qualified name as foreign for ABI lowering at call sites
|
||||
try self.foreign_fns.put(qualified, {});
|
||||
// Track qualified rename mapping if C name differs
|
||||
const fe = fd.body.data.foreign_expr;
|
||||
if (fe.c_name) |c_name| {
|
||||
if (!std.mem.eql(u8, c_name, fd.name)) {
|
||||
try self.foreign_name_map.put(qualified, c_name);
|
||||
}
|
||||
}
|
||||
// Store param types under qualified name so call-site type resolution works
|
||||
var param_types = std.ArrayList(Type).empty;
|
||||
for (fd.params) |param| {
|
||||
if (param.is_comptime) continue;
|
||||
@@ -2394,32 +2493,8 @@ pub const CodeGen = struct {
|
||||
try self.registerFnDecl(fd, qualified);
|
||||
}
|
||||
},
|
||||
.enum_decl => |ed| {
|
||||
if (ed.variant_types.len > 0) {
|
||||
// Tagged enum with payloads
|
||||
try self.registerTaggedEnum(ed);
|
||||
const qualified_u = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ed.name });
|
||||
try self.type_registry.put(qualified_u, .{ .alias = ed.name });
|
||||
} else {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ed.name });
|
||||
try self.type_registry.put(qualified, .{ .plain_enum = ed.variant_names });
|
||||
_ = try self.getAnyTypeId(qualified, .{ .enum_type = qualified });
|
||||
if (ed.backing_type) |bt_node| {
|
||||
const bt = self.resolveType(bt_node);
|
||||
try self.enum_backing_types.put(qualified, self.typeToLLVM(bt));
|
||||
}
|
||||
}
|
||||
},
|
||||
.struct_decl => |sd| {
|
||||
try self.registerStructType(sd);
|
||||
// Register qualified alias so rl.Color resolves to Color
|
||||
const qualified_s = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, sd.name });
|
||||
try self.type_registry.put(qualified_s, .{ .alias = sd.name });
|
||||
},
|
||||
.union_decl => |ud| {
|
||||
try self.registerUnionType(ud);
|
||||
const qualified_u = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ud.name });
|
||||
try self.type_registry.put(qualified_u, .{ .alias = ud.name });
|
||||
try self.registerStructMethods(sd);
|
||||
},
|
||||
.const_decl => |cd| {
|
||||
if (cd.value.data == .builtin_expr) {
|
||||
@@ -2427,13 +2502,8 @@ pub const CodeGen = struct {
|
||||
} else if (cd.value.data == .lambda) {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, cd.name });
|
||||
try self.registerLambdaAsFunction(qualified, cd.value.data.lambda);
|
||||
} else if (cd.value.data == .type_expr) {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, cd.name });
|
||||
try self.type_registry.put(qualified, .{ .alias = cd.value.data.type_expr.name });
|
||||
}
|
||||
},
|
||||
.library_decl => |ld| {
|
||||
try self.foreign_libraries.append(self.allocator, ld.lib_name);
|
||||
// type aliases already handled in registerNamespaceTypes
|
||||
},
|
||||
.var_decl => |vd| {
|
||||
try self.registerGlobalVar(vd);
|
||||
@@ -2441,8 +2511,8 @@ pub const CodeGen = struct {
|
||||
.ufcs_alias => |ua| {
|
||||
try self.ufcs_aliases.put(ua.name, ua.target);
|
||||
},
|
||||
.protocol_decl => |pd| {
|
||||
try self.registerProtocolDecl(pd);
|
||||
.protocol_decl => {
|
||||
// already registered in registerNamespaceTypes
|
||||
},
|
||||
.impl_block => |ib| {
|
||||
try self.registerImplBlock(ib);
|
||||
@@ -4503,6 +4573,10 @@ pub const CodeGen = struct {
|
||||
};
|
||||
|
||||
fn buildStructFields(self: *CodeGen, name: []const u8, field_type_nodes: []const *Node) !StructBuildResult {
|
||||
return self.buildStructFieldsWithType(name, field_type_nodes, null);
|
||||
}
|
||||
|
||||
fn buildStructFieldsWithType(self: *CodeGen, name: []const u8, field_type_nodes: []const *Node, existing_llvm_type: ?c.LLVMTypeRef) !StructBuildResult {
|
||||
var field_sx_types = std.ArrayList(Type).empty;
|
||||
var field_llvm_types = std.ArrayList(c.LLVMTypeRef).empty;
|
||||
|
||||
@@ -4513,8 +4587,10 @@ pub const CodeGen = struct {
|
||||
}
|
||||
|
||||
const llvm_types_slice = try field_llvm_types.toOwnedSlice(self.allocator);
|
||||
const name_z = try self.allocator.dupeZ(u8, name);
|
||||
const struct_ty = c.LLVMStructCreateNamed(self.context, name_z.ptr);
|
||||
const struct_ty = existing_llvm_type orelse blk: {
|
||||
const name_z = try self.allocator.dupeZ(u8, name);
|
||||
break :blk c.LLVMStructCreateNamed(self.context, name_z.ptr);
|
||||
};
|
||||
c.LLVMStructSetBody(struct_ty, if (llvm_types_slice.len > 0) llvm_types_slice.ptr else null, @intCast(llvm_types_slice.len), 0);
|
||||
|
||||
return .{
|
||||
@@ -4592,7 +4668,7 @@ pub const CodeGen = struct {
|
||||
}
|
||||
|
||||
/// Expand #using entries: interleave used struct fields with declared fields.
|
||||
fn expandStructUsing(self: *CodeGen, sd: ast.StructDecl) !StructInfo {
|
||||
fn expandStructUsing(self: *CodeGen, sd: ast.StructDecl, existing_llvm_type: ?c.LLVMTypeRef) !StructInfo {
|
||||
var all_names = std.ArrayList([]const u8).empty;
|
||||
var all_types = std.ArrayList(Type).empty;
|
||||
var all_defaults = std.ArrayList(?*Node).empty;
|
||||
@@ -4628,8 +4704,10 @@ pub const CodeGen = struct {
|
||||
for (all_types.items) |ty| {
|
||||
try llvm_types.append(self.allocator, self.typeToLLVM(ty));
|
||||
}
|
||||
const name_z = try self.allocator.dupeZ(u8, sd.name);
|
||||
const struct_ty = c.LLVMStructCreateNamed(self.context, name_z.ptr);
|
||||
const struct_ty = existing_llvm_type orelse blk: {
|
||||
const name_z = try self.allocator.dupeZ(u8, sd.name);
|
||||
break :blk c.LLVMStructCreateNamed(self.context, name_z.ptr);
|
||||
};
|
||||
const llvm_slice = try llvm_types.toOwnedSlice(self.allocator);
|
||||
c.LLVMStructSetBody(struct_ty, if (llvm_slice.len > 0) llvm_slice.ptr else null, @intCast(llvm_slice.len), 0);
|
||||
|
||||
@@ -4641,32 +4719,43 @@ pub const CodeGen = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn registerStructType(self: *CodeGen, sd: ast.StructDecl) anyerror!void {
|
||||
// Generic struct: store as template instead of registering now
|
||||
/// Register a struct type name with an opaque LLVM struct (no fields yet).
|
||||
/// Called during registerTypes phase so other types can reference this name.
|
||||
fn registerStructName(self: *CodeGen, sd: ast.StructDecl) anyerror!void {
|
||||
if (sd.type_params.len > 0) {
|
||||
// Register methods for generic structs (as generic templates)
|
||||
try self.registerStructMethods(sd);
|
||||
try self.generic_struct_templates.put(sd.name, sd);
|
||||
return;
|
||||
}
|
||||
|
||||
// Forward declaration: register name early so self-referential *T works
|
||||
// Create opaque named LLVM struct (body set later in resolveStructFields)
|
||||
const name_z = try self.allocator.dupeZ(u8, sd.name);
|
||||
const struct_ty = c.LLVMStructCreateNamed(self.context, name_z.ptr);
|
||||
|
||||
try self.type_registry.put(sd.name, .{ .struct_info = .{
|
||||
.field_names = &.{},
|
||||
.field_types = &.{},
|
||||
.field_defaults = &.{},
|
||||
.llvm_type = null,
|
||||
.llvm_type = struct_ty,
|
||||
} });
|
||||
|
||||
// Pre-pass: hoist inline type declarations from field types
|
||||
// Hoist inline type declarations (anonymous struct/union/enum in field types)
|
||||
for (sd.field_types, 0..) |ft, i| {
|
||||
try self.hoistInlineTypeDecl(sd.name, sd.field_names[i], ft);
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve struct field types and set the LLVM struct body.
|
||||
/// Called during resolveFields phase after all type names are registered.
|
||||
fn resolveStructFields(self: *CodeGen, sd: ast.StructDecl) anyerror!void {
|
||||
if (sd.type_params.len > 0) return;
|
||||
|
||||
const existing = self.lookupStructInfo(sd.name) orelse return;
|
||||
const struct_ty = existing.llvm_type orelse return;
|
||||
|
||||
const sinfo = if (sd.using_entries.len > 0)
|
||||
try self.expandStructUsing(sd)
|
||||
try self.expandStructUsing(sd, struct_ty)
|
||||
else blk: {
|
||||
const build = try self.buildStructFields(sd.name, sd.field_types);
|
||||
const build = try self.buildStructFieldsWithType(sd.name, sd.field_types, struct_ty);
|
||||
|
||||
// Process field defaults: replace #run expressions with comptime global references
|
||||
var resolved_defaults = try self.allocator.alloc(?*Node, sd.field_defaults.len);
|
||||
@@ -4693,9 +4782,23 @@ pub const CodeGen = struct {
|
||||
|
||||
try self.type_registry.put(sd.name, .{ .struct_info = sinfo });
|
||||
_ = try self.getAnyTypeId(sd.name, .{ .struct_type = sd.name });
|
||||
}
|
||||
|
||||
// Register struct methods after struct is fully registered (methods reference the struct type)
|
||||
try self.registerStructMethods(sd);
|
||||
/// Convenience: register name + resolve fields in one call.
|
||||
/// Used by hoistInlineTypeDecl and resolveEnumLayout for on-demand type creation.
|
||||
fn registerStructType(self: *CodeGen, sd: ast.StructDecl) anyerror!void {
|
||||
try self.registerStructName(sd);
|
||||
try self.resolveStructFields(sd);
|
||||
}
|
||||
|
||||
fn registerUnionType(self: *CodeGen, ud: ast.UnionDecl) !void {
|
||||
try self.registerUnionName(ud);
|
||||
try self.resolveUnionFields(ud);
|
||||
}
|
||||
|
||||
fn registerTaggedEnum(self: *CodeGen, ud: ast.EnumDecl) !void {
|
||||
try self.registerTaggedEnumName(ud);
|
||||
try self.resolveTaggedEnumFields(ud);
|
||||
}
|
||||
|
||||
/// Register methods declared inside a struct body.
|
||||
@@ -5400,8 +5503,9 @@ pub const CodeGen = struct {
|
||||
return result;
|
||||
}
|
||||
|
||||
fn registerTaggedEnum(self: *CodeGen, ud: ast.EnumDecl) !void {
|
||||
// Forward declaration: register name early so self-referential *T works
|
||||
/// Register a tagged enum name with a placeholder entry.
|
||||
/// Called during registerTypes phase.
|
||||
fn registerTaggedEnumName(self: *CodeGen, ud: ast.EnumDecl) !void {
|
||||
try self.type_registry.put(ud.name, .{ .tagged_enum = .{
|
||||
.variant_names = &.{},
|
||||
.variant_types = &.{},
|
||||
@@ -5410,21 +5514,21 @@ pub const CodeGen = struct {
|
||||
.payload_field_index = 0,
|
||||
} });
|
||||
|
||||
// Pre-pass: hoist inline type declarations from variant types
|
||||
for (ud.variant_types, 0..) |vt_opt, i| {
|
||||
if (vt_opt) |vt| {
|
||||
try self.hoistInlineTypeDecl(ud.name, ud.variant_names[i], vt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if backing type is a struct layout specification
|
||||
/// Resolve tagged enum variant types and compute LLVM layout.
|
||||
/// Called during resolveFields phase after all type names are registered.
|
||||
fn resolveTaggedEnumFields(self: *CodeGen, ud: ast.EnumDecl) !void {
|
||||
const layout_info = try self.resolveEnumLayout(ud);
|
||||
|
||||
if (layout_info) |layout| {
|
||||
// Struct-backed layout: use the struct's LLVM type directly
|
||||
try self.enum_backing_types.put(ud.name, layout.tag_llvm_type);
|
||||
|
||||
// Resolve variant sx types
|
||||
var variant_sx_types = std.ArrayList(Type).empty;
|
||||
for (ud.variant_types) |vt| {
|
||||
if (vt) |type_node| {
|
||||
@@ -5434,16 +5538,14 @@ pub const CodeGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const tei_layout = TaggedEnumInfo{
|
||||
try self.type_registry.put(ud.name, .{ .tagged_enum = .{
|
||||
.variant_names = ud.variant_names,
|
||||
.variant_types = try variant_sx_types.toOwnedSlice(self.allocator),
|
||||
.llvm_type = layout.llvm_type,
|
||||
.max_payload_size = layout.payload_size,
|
||||
.payload_field_index = layout.payload_field_index,
|
||||
};
|
||||
try self.type_registry.put(ud.name, .{ .tagged_enum = tei_layout });
|
||||
} });
|
||||
} else {
|
||||
// Primitive backing type (e.g. enum u32 { ... })
|
||||
if (ud.backing_type) |bt_node| {
|
||||
const bt = self.resolveType(bt_node);
|
||||
try self.enum_backing_types.put(ud.name, self.typeToLLVM(bt));
|
||||
@@ -5451,19 +5553,17 @@ pub const CodeGen = struct {
|
||||
|
||||
const build = try self.buildUnionFields(ud.name, ud.variant_types);
|
||||
|
||||
const tei_build = TaggedEnumInfo{
|
||||
try self.type_registry.put(ud.name, .{ .tagged_enum = .{
|
||||
.variant_names = ud.variant_names,
|
||||
.variant_types = build.variant_sx_types,
|
||||
.llvm_type = build.llvm_type,
|
||||
.max_payload_size = build.max_payload_size,
|
||||
.payload_field_index = build.payload_field_index,
|
||||
};
|
||||
try self.type_registry.put(ud.name, .{ .tagged_enum = tei_build });
|
||||
} });
|
||||
}
|
||||
|
||||
_ = try self.getAnyTypeId(ud.name, .{ .union_type = ud.name });
|
||||
|
||||
// Compute and store variant values (explicit or sequential)
|
||||
const values = try self.allocator.alloc(i64, ud.variant_names.len);
|
||||
for (ud.variant_names, 0..) |_, i| {
|
||||
if (ud.variant_values.len > i and ud.variant_values[i] != null) {
|
||||
@@ -5590,8 +5690,9 @@ pub const CodeGen = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn registerUnionType(self: *CodeGen, ud: ast.UnionDecl) !void {
|
||||
// Forward declaration: register name early so self-referential *T works
|
||||
/// Register a union type name with a placeholder entry.
|
||||
/// Called during registerTypes phase.
|
||||
fn registerUnionName(self: *CodeGen, ud: ast.UnionDecl) !void {
|
||||
try self.type_registry.put(ud.name, .{ .union_info = .{
|
||||
.field_names = &.{},
|
||||
.field_types = &.{},
|
||||
@@ -5600,12 +5701,14 @@ pub const CodeGen = struct {
|
||||
.promoted_fields = std.StringHashMap(PromotedField).init(self.allocator),
|
||||
} });
|
||||
|
||||
// Hoist inline type declarations from field types
|
||||
for (ud.field_types, 0..) |ft, i| {
|
||||
try self.hoistInlineTypeDecl(ud.name, ud.field_names[i], ft);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute max field size and resolve field types
|
||||
/// Resolve union field types and compute LLVM layout.
|
||||
/// Called during resolveFields phase after all type names are registered.
|
||||
fn resolveUnionFields(self: *CodeGen, ud: ast.UnionDecl) !void {
|
||||
const data_layout = c.LLVMGetModuleDataLayout(self.module);
|
||||
var field_sx_types = std.ArrayList(Type).empty;
|
||||
var max_size: u64 = 0;
|
||||
@@ -5617,10 +5720,8 @@ pub const CodeGen = struct {
|
||||
if (size > max_size) max_size = size;
|
||||
}
|
||||
|
||||
// LLVM type: byte array sized to the largest field
|
||||
const byte_ty = self.i8Type();
|
||||
const llvm_type = c.LLVMArrayType(byte_ty, @intCast(max_size));
|
||||
|
||||
const resolved_field_types = try field_sx_types.toOwnedSlice(self.allocator);
|
||||
|
||||
// Build promoted fields map from anonymous struct members
|
||||
@@ -5628,7 +5729,6 @@ pub const CodeGen = struct {
|
||||
for (ud.field_names, 0..) |_, i| {
|
||||
const fty = resolved_field_types[i];
|
||||
if (fty.isStruct()) {
|
||||
// Check if this is an anonymous struct (name contains __anon_)
|
||||
const sname = fty.struct_type;
|
||||
if (std.mem.indexOf(u8, sname, ".__anon_") != null) {
|
||||
if (self.lookupStructInfo(sname)) |sinfo| {
|
||||
@@ -5652,8 +5752,6 @@ pub const CodeGen = struct {
|
||||
.promoted_fields = promoted,
|
||||
};
|
||||
try self.type_registry.put(ud.name, .{ .union_info = uinfo });
|
||||
// Note: C-style unions are not registered with the Any type system.
|
||||
// They can't be meaningfully printed as a whole — access individual fields instead.
|
||||
}
|
||||
|
||||
fn genTaggedEnumLiteral(self: *CodeGen, el: ast.EnumLiteral, expected_union_name: ?[]const u8) !c.LLVMValueRef {
|
||||
|
||||
Reference in New Issue
Block a user