This commit is contained in:
agra
2026-02-15 17:26:15 +02:00
parent 2727b5509e
commit a3be9cce7c
4 changed files with 356 additions and 260 deletions

View File

@@ -117,6 +117,8 @@ pub const CodeGen = struct {
tagged_enum_types: std.StringHashMap(TaggedEnumInfo),
// Union registry: maps name to field info + LLVM type (untagged, C-style)
union_types: std.StringHashMap(UnionInfo),
// Unified type registry: single lookup for all named types
type_registry: std.StringHashMap(TypeRegistryEntry),
// Flags enum registry: tracks which enum names are flags
flags_enum_types: std.StringHashMap(void),
// Enum variant values: maps enum name → resolved i64 values per variant
@@ -271,6 +273,14 @@ pub const CodeGen = struct {
promoted_fields: std.StringHashMap(PromotedField),
};
const TypeRegistryEntry = union(enum) {
struct_info: StructInfo,
tagged_enum: TaggedEnumInfo,
union_info: UnionInfo,
plain_enum: []const []const u8,
alias: []const u8,
};
// Scope stack entry: records what a name mapped to before being shadowed
const ScopeEntry = struct {
name: []const u8,
@@ -326,6 +336,7 @@ pub const CodeGen = struct {
.struct_types = std.StringHashMap(StructInfo).init(allocator),
.tagged_enum_types = std.StringHashMap(TaggedEnumInfo).init(allocator),
.union_types = std.StringHashMap(UnionInfo).init(allocator),
.type_registry = std.StringHashMap(TypeRegistryEntry).init(allocator),
.flags_enum_types = std.StringHashMap(void).init(allocator),
.enum_variant_values = std.StringHashMap([]const i64).init(allocator),
.enum_backing_types = std.StringHashMap(c.LLVMTypeRef).init(allocator),
@@ -361,6 +372,7 @@ pub const CodeGen = struct {
self.struct_types.deinit();
self.tagged_enum_types.deinit();
self.union_types.deinit();
self.type_registry.deinit();
self.comptime_globals.deinit();
self.enum_backing_types.deinit();
self.generic_templates.deinit();
@@ -422,9 +434,21 @@ pub const CodeGen = struct {
return c.LLVMBuildAlloca(tmp_builder, ty, name);
}
/// Convert a Zig slice to a null-terminated C string using a caller-provided stack buffer.
/// Returns the stack-based result when it fits, or falls back to allocator.dupeZ.
fn nameToCStr(self: *CodeGen, name: []const u8, buf: *[256]u8) [*:0]const u8 {
if (name.len < 256) {
@memcpy(buf[0..name.len], name);
buf[name.len] = 0;
return @ptrCast(buf[0..name.len :0]);
}
const duped = self.allocator.dupeZ(u8, name) catch unreachable;
return duped.ptr;
}
fn buildNamedAlloca(self: *CodeGen, ty: c.LLVMTypeRef, name: []const u8) !c.LLVMValueRef {
const name_z = try self.allocator.dupeZ(u8, name);
return self.buildEntryBlockAlloca(ty, name_z.ptr);
var buf: [256]u8 = undefined;
return self.buildEntryBlockAlloca(ty, self.nameToCStr(name, &buf));
}
pub fn typeToLLVM(self: *CodeGen, ty: Type) c.LLVMTypeRef {
@@ -860,8 +884,12 @@ pub const CodeGen = struct {
}
fn pushScope(self: *CodeGen) !void {
try self.scope_saves.append(self.allocator, std.ArrayList(ScopeEntry).empty);
try self.defer_stack.append(self.allocator, std.ArrayList(*Node).empty);
var saves = std.ArrayList(ScopeEntry).empty;
try saves.ensureTotalCapacity(self.allocator, 8);
try self.scope_saves.append(self.allocator, saves);
var defers = std.ArrayList(*Node).empty;
try defers.ensureTotalCapacity(self.allocator, 4);
try self.defer_stack.append(self.allocator, defers);
}
fn popScope(self: *CodeGen) !void {
@@ -955,6 +983,7 @@ pub const CodeGen = struct {
} else {
// Payload-less enum
try self.enum_types.put(ed.name, ed.variant_names);
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) {
@@ -997,6 +1026,7 @@ pub const CodeGen = struct {
try self.registerLambdaAsFunction(cd.name, cd.value.data.lambda);
} else if (cd.value.data == .type_expr) {
try self.type_aliases.put(cd.name, cd.value.data.type_expr.name);
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)
@@ -1009,20 +1039,24 @@ pub const CodeGen = struct {
const result_ty = try self.instantiateGenericStruct(cn, tmpl, cd.value.data.call.args);
if (result_ty.isStruct()) {
try self.type_aliases.put(cd.name, result_ty.struct_type);
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_aliases.put(cd.name, result_ty.struct_type);
try self.type_registry.put(cd.name, .{ .alias = result_ty.struct_type });
} else if (result_ty.isUnion()) {
try self.type_aliases.put(cd.name, result_ty.union_type);
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_aliases.put(cd.name, display);
try self.type_registry.put(cd.name, .{ .alias = display });
} else {
try self.registerTopLevelConstant(cd);
}
@@ -1054,26 +1088,6 @@ pub const CodeGen = struct {
}
}
// Pre-register all known types for Any type ID assignment
{
var it = self.struct_types.iterator();
while (it.next()) |entry| {
_ = try self.getAnyTypeId(entry.key_ptr.*, .{ .struct_type = entry.key_ptr.* });
}
}
{
var it = self.enum_types.iterator();
while (it.next()) |entry| {
_ = try self.getAnyTypeId(entry.key_ptr.*, .{ .enum_type = entry.key_ptr.* });
}
}
{
var it = self.tagged_enum_types.iterator();
while (it.next()) |entry| {
_ = try self.getAnyTypeId(entry.key_ptr.*, .{ .union_type = entry.key_ptr.* });
}
}
// 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.
@@ -1311,30 +1325,42 @@ pub const CodeGen = struct {
if (self.type_param_bindings) |bindings| {
if (bindings.get(name)) |t| return t;
}
// Check type aliases
if (self.type_aliases.get(name)) |target| {
if (Type.fromName(target)) |t| return t;
if (self.struct_types.contains(target)) return .{ .struct_type = target };
if (self.tagged_enum_types.contains(target)) return .{ .union_type = target };
if (self.union_types.contains(target)) return .{ .union_type = target };
// Unified type registry lookup
if (self.type_registry.get(name)) |entry| {
switch (entry) {
.struct_info => return .{ .struct_type = name },
.tagged_enum => return .{ .union_type = name },
.union_info => return .{ .union_type = name },
.plain_enum => return .{ .enum_type = name },
.alias => |target| {
if (Type.fromName(target)) |t| return t;
if (self.type_registry.get(target)) |inner| {
switch (inner) {
.struct_info => return .{ .struct_type = target },
.tagged_enum => return .{ .union_type = target },
.union_info => return .{ .union_type = target },
.plain_enum => return .{ .enum_type = target },
.alias => {},
}
}
},
}
}
// Check enum types
if (self.enum_types.contains(name)) return .{ .enum_type = name };
// Check struct types
if (self.struct_types.contains(name)) return .{ .struct_type = name };
// Check union types (tagged enums and C-style unions)
if (self.tagged_enum_types.contains(name)) return .{ .union_type = name };
if (self.union_types.contains(name)) return .{ .union_type = name };
}
// Safety net: inline declarations that should have been hoisted
if (tn.data == .struct_decl) {
const sn = tn.data.struct_decl.name;
if (self.struct_types.contains(sn)) return .{ .struct_type = sn };
if (self.type_registry.get(sn)) |e| {
if (e == .struct_info) return .{ .struct_type = sn };
}
}
if (tn.data == .enum_decl) {
const en = tn.data.enum_decl.name;
if (self.tagged_enum_types.contains(en)) return .{ .union_type = en };
if (self.enum_types.contains(en)) return .{ .enum_type = en };
if (self.type_registry.get(en)) |e| switch (e) {
.tagged_enum => return .{ .union_type = en },
.plain_enum => return .{ .enum_type = en },
else => {},
};
}
return .void_type;
}
@@ -1432,7 +1458,7 @@ pub const CodeGen = struct {
}
}
try self.struct_types.put(mangled_name, .{
const si = StructInfo{
.field_names = sd.field_names,
.field_types = build.field_sx_types,
.field_defaults = resolved_defaults,
@@ -1441,7 +1467,9 @@ pub const CodeGen = struct {
.type_param_names = try tp_names.toOwnedSlice(self.allocator),
.type_param_types = try tp_types.toOwnedSlice(self.allocator),
.template_name = template_name,
});
};
try self.struct_types.put(mangled_name, si);
try self.type_registry.put(mangled_name, .{ .struct_info = si });
_ = try self.getAnyTypeId(mangled_name, .{ .struct_type = mangled_name });
return .{ .struct_type = mangled_name };
@@ -1492,13 +1520,15 @@ pub const CodeGen = struct {
const resolved_defaults = try self.allocator.dupe(?*Node, struct_decl.field_defaults);
const display_name = try self.allocator.dupe(u8, alias_name);
try self.struct_types.put(mangled_name, .{
const si2 = StructInfo{
.field_names = struct_decl.field_names,
.field_types = build.field_sx_types,
.field_defaults = resolved_defaults,
.llvm_type = build.llvm_type,
.display_name = display_name,
});
};
try self.struct_types.put(mangled_name, si2);
try self.type_registry.put(mangled_name, .{ .struct_info = si2 });
_ = try self.getAnyTypeId(mangled_name, .{ .struct_type = mangled_name });
return .{ .struct_type = mangled_name };
@@ -1507,13 +1537,15 @@ pub const CodeGen = struct {
fn registerInstantiatedTaggedEnum(self: *CodeGen, mangled_name: []const u8, union_decl: ast.EnumDecl) !Type {
const build = try self.buildUnionFields(mangled_name, union_decl.variant_types);
try self.tagged_enum_types.put(mangled_name, .{
const tei = TaggedEnumInfo{
.variant_names = union_decl.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.tagged_enum_types.put(mangled_name, tei);
try self.type_registry.put(mangled_name, .{ .tagged_enum = tei });
_ = try self.getAnyTypeId(mangled_name, .{ .union_type = mangled_name });
return .{ .union_type = mangled_name };
@@ -1868,9 +1900,12 @@ pub const CodeGen = struct {
try self.registerTaggedEnum(ed);
const qualified_u = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ed.name });
try self.type_aliases.put(qualified_u, 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.enum_types.put(qualified, ed.variant_names);
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));
@@ -1882,11 +1917,13 @@ pub const CodeGen = struct {
// 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_aliases.put(qualified_s, 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_aliases.put(qualified_u, ud.name);
try self.type_registry.put(qualified_u, .{ .alias = ud.name });
},
.const_decl => |cd| {
if (cd.value.data == .builtin_expr) {
@@ -1897,6 +1934,7 @@ pub const CodeGen = struct {
} else if (cd.value.data == .type_expr) {
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, cd.name });
try self.type_aliases.put(qualified, cd.value.data.type_expr.name);
try self.type_registry.put(qualified, .{ .alias = cd.value.data.type_expr.name });
}
},
.library_decl => |ld| {
@@ -1948,8 +1986,8 @@ pub const CodeGen = struct {
// For function calls, look up the registered function's return type
if (expr.data == .call) {
if (self.resolveCalleeName(expr.data.call)) |callee_name| {
const callee_name_z = self.allocator.dupeZ(u8, callee_name) catch return Type.s(64);
const callee_fn = c.LLVMGetNamedFunction(self.module, callee_name_z.ptr) orelse return Type.s(64);
var cnbuf: [256]u8 = undefined;
const callee_fn = c.LLVMGetNamedFunction(self.module, self.nameToCStr(callee_name, &cnbuf)) orelse return Type.s(64);
const fn_type = c.LLVMGlobalGetValueType(callee_fn);
const ret_llvm = c.LLVMGetReturnType(fn_type);
return self.llvmTypeToSxType(ret_llvm);
@@ -2947,14 +2985,14 @@ pub const CodeGen = struct {
}
// Fall back to function name → function pointer value
{
const name_z = try self.allocator.dupeZ(u8, ident.name);
var fn_val = c.LLVMGetNamedFunction(self.module, name_z.ptr);
var nbuf: [256]u8 = undefined;
var fn_val = c.LLVMGetNamedFunction(self.module, self.nameToCStr(ident.name, &nbuf));
if (fn_val == null) {
// Try qualified name with current namespace
if (self.current_namespace) |ns| {
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, ident.name });
const q_z = try self.allocator.dupeZ(u8, qualified);
fn_val = c.LLVMGetNamedFunction(self.module, q_z.ptr);
var qbuf: [256]u8 = undefined;
fn_val = c.LLVMGetNamedFunction(self.module, self.nameToCStr(qualified, &qbuf));
}
}
if (fn_val != null) return fn_val.?;
@@ -3301,6 +3339,7 @@ pub const CodeGen = struct {
try self.registerTaggedEnum(hoisted);
} else {
try self.enum_types.put(synthetic_name, inline_ed.variant_names);
try self.type_registry.put(synthetic_name, .{ .plain_enum = inline_ed.variant_names });
_ = try self.getAnyTypeId(synthetic_name, .{ .enum_type = synthetic_name });
if (inline_ed.backing_type) |bt_node| {
const bt = self.resolveType(bt_node);
@@ -3342,12 +3381,14 @@ pub const CodeGen = struct {
}
}
try self.struct_types.put(sd.name, .{
const sinfo = StructInfo{
.field_names = sd.field_names,
.field_types = build.field_sx_types,
.field_defaults = resolved_defaults,
.llvm_type = build.llvm_type,
});
};
try self.struct_types.put(sd.name, sinfo);
try self.type_registry.put(sd.name, .{ .struct_info = sinfo });
_ = try self.getAnyTypeId(sd.name, .{ .struct_type = sd.name });
}
@@ -3376,13 +3417,15 @@ pub const CodeGen = struct {
}
}
try self.tagged_enum_types.put(ud.name, .{
const tei_layout = TaggedEnumInfo{
.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.tagged_enum_types.put(ud.name, tei_layout);
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| {
@@ -3392,13 +3435,15 @@ pub const CodeGen = struct {
const build = try self.buildUnionFields(ud.name, ud.variant_types);
try self.tagged_enum_types.put(ud.name, .{
const tei_build = TaggedEnumInfo{
.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.tagged_enum_types.put(ud.name, tei_build);
try self.type_registry.put(ud.name, .{ .tagged_enum = tei_build });
}
_ = try self.getAnyTypeId(ud.name, .{ .union_type = ud.name });
@@ -3575,13 +3620,15 @@ pub const CodeGen = struct {
}
}
try self.union_types.put(ud.name, .{
const uinfo = UnionInfo{
.field_names = ud.field_names,
.field_types = resolved_field_types,
.llvm_type = llvm_type,
.total_size = max_size,
.promoted_fields = promoted,
});
};
try self.union_types.put(ud.name, uinfo);
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.
}
@@ -5112,22 +5159,22 @@ pub const CodeGen = struct {
return self.genMemcpy(call_node.args);
}
const name_z = try self.allocator.dupeZ(u8, callee_name);
var callee_fn = c.LLVMGetNamedFunction(self.module, name_z.ptr);
var nbuf: [256]u8 = undefined;
var callee_fn = c.LLVMGetNamedFunction(self.module, self.nameToCStr(callee_name, &nbuf));
// Foreign function fallback: qualified name "ns.Func" → try unqualified "Func" (the C symbol)
if (callee_fn == null) {
if (std.mem.lastIndexOfScalar(u8, callee_name, '.')) |dot_idx| {
const base_name = callee_name[dot_idx + 1 ..];
const base_z = try self.allocator.dupeZ(u8, base_name);
callee_fn = c.LLVMGetNamedFunction(self.module, base_z.ptr);
var bbuf: [256]u8 = undefined;
callee_fn = c.LLVMGetNamedFunction(self.module, self.nameToCStr(base_name, &bbuf));
}
}
// Intra-namespace fallback: try qualified name
if (callee_fn == null) {
if (self.current_namespace) |ns| {
const qualified2 = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name });
const qualified_z = try self.allocator.dupeZ(u8, qualified2);
callee_fn = c.LLVMGetNamedFunction(self.module, qualified_z.ptr);
var qbuf: [256]u8 = undefined;
callee_fn = c.LLVMGetNamedFunction(self.module, self.nameToCStr(qualified2, &qbuf));
}
}
// Function pointer indirect call: callee is a variable with function_type
@@ -6677,13 +6724,23 @@ pub const CodeGen = struct {
fn resolveTypeFromName(self: *CodeGen, name: []const u8) ?Type {
// Primitives
if (Type.fromName(name)) |t| return t;
// Structs
if (self.struct_types.contains(name)) return .{ .struct_type = name };
// Unions (tagged enums and C-style)
if (self.tagged_enum_types.contains(name)) return .{ .union_type = name };
if (self.union_types.contains(name)) return .{ .union_type = name };
// Enums
if (self.enum_types.contains(name)) return .{ .enum_type = name };
// Unified type registry lookup
if (self.type_registry.get(name)) |entry| switch (entry) {
.struct_info => return .{ .struct_type = name },
.tagged_enum => return .{ .union_type = name },
.union_info => return .{ .union_type = name },
.plain_enum => return .{ .enum_type = name },
.alias => |target| {
if (Type.fromName(target)) |t| return t;
if (self.type_registry.get(target)) |inner| switch (inner) {
.struct_info => return .{ .struct_type = target },
.tagged_enum => return .{ .union_type = target },
.union_info => return .{ .union_type = target },
.plain_enum => return .{ .enum_type = target },
.alias => {},
};
},
};
// Vector display name: "Vector(N,T)"
if (name.len > 8 and std.mem.startsWith(u8, name, "Vector(") and name[name.len - 1] == ')') {
const inner = name[7 .. name.len - 1]; // "N,T"
@@ -6847,14 +6904,14 @@ pub const CodeGen = struct {
if (self.function_return_types.get(qualified)) |ret_ty| return ret_ty;
}
// Fallback: check non-generic LLVM functions
const callee_name_z = self.allocator.dupeZ(u8, callee_name) catch return Type.s(64);
var callee_fn_opt = c.LLVMGetNamedFunction(self.module, callee_name_z.ptr);
var cnbuf2: [256]u8 = undefined;
var callee_fn_opt = c.LLVMGetNamedFunction(self.module, self.nameToCStr(callee_name, &cnbuf2));
// Intra-namespace fallback
if (callee_fn_opt == null) {
if (self.current_namespace) |ns2| {
const q = std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns2, callee_name }) catch return Type.s(64);
const qz = self.allocator.dupeZ(u8, q) catch return Type.s(64);
callee_fn_opt = c.LLVMGetNamedFunction(self.module, qz.ptr);
var qbuf2: [256]u8 = undefined;
callee_fn_opt = c.LLVMGetNamedFunction(self.module, self.nameToCStr(q, &qbuf2));
}
}
if (callee_fn_opt) |callee_fn| {