anon struct, union...

This commit is contained in:
agra
2026-02-09 19:22:53 +02:00
parent 55fc5790e4
commit 4b1c22d3b6
3 changed files with 203 additions and 9 deletions

View File

@@ -717,13 +717,16 @@ pub const CodeGen = struct {
}
// Call expression as type: Vec(3, f32) → generic struct/type function instantiation
if (tn.data == .call) {
if (tn.data.call.callee.data == .identifier) {
const name = tn.data.call.callee.data.identifier.name;
if (self.generic_struct_templates.get(name)) |tmpl| {
return self.instantiateGenericStruct(name, tmpl, tn.data.call.args) catch .void_type;
const name = self.calleeToQualifiedName(tn.data.call.callee);
if (name) |n| {
if (self.builtin_functions.contains(n)) {
if (self.resolveBuiltinType(n, tn.data.call.args)) |ty| return ty;
}
if (self.generic_templates.get(name)) |tmpl| {
return self.instantiateTypeFunction(name, name, tmpl, tn.data.call.args) catch .void_type;
if (self.generic_struct_templates.get(n)) |tmpl| {
return self.instantiateGenericStruct(n, tmpl, tn.data.call.args) catch .void_type;
}
if (self.generic_templates.get(n)) |tmpl| {
return self.instantiateTypeFunction(n, n, tmpl, tn.data.call.args) catch .void_type;
}
}
return .void_type;
@@ -749,6 +752,19 @@ pub const CodeGen = struct {
// Check union types
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 (tn.data == .union_decl) {
const un = tn.data.union_decl.name;
if (self.union_types.contains(un)) return .{ .union_type = un };
}
if (tn.data == .enum_decl) {
const en = tn.data.enum_decl.name;
if (self.enum_types.contains(en)) return .{ .enum_type = en };
}
return .void_type;
}
return .void_type;
@@ -1585,6 +1601,8 @@ pub const CodeGen = struct {
} else {
return self.emitError("cannot infer struct type from untyped struct literal");
}
} else if (val.data == .array_literal and val.data.array_literal.type_expr != null) {
sx_ty = self.resolveType(val.data.array_literal.type_expr);
} else {
sx_ty = self.inferType(val);
}
@@ -1788,7 +1806,13 @@ pub const CodeGen = struct {
} else {
// Field has expression default → evaluate and convert
const val = try self.genExprAsType(default_node, ft);
_ = c.LLVMBuildStore(self.builder, val, gep);
if (ft.isStruct() or ft.isUnion()) {
// Aggregate types: genExprAsType returns an alloca, load the value first
const loaded = c.LLVMBuildLoad2(self.builder, ft_llvm, val, "dinit_load");
_ = c.LLVMBuildStore(self.builder, loaded, gep);
} else {
_ = c.LLVMBuildStore(self.builder, val, gep);
}
}
} else {
// No default → zero
@@ -2141,6 +2165,12 @@ pub const CodeGen = struct {
return self.genUnionLiteral(ul, null);
},
.array_literal => |al| {
// Typed array/vector literal: Type.[elems]
if (al.type_expr) |te| {
const ty = self.resolveType(te);
if (ty.isVector()) return self.genVectorLiteral(al, ty);
if (ty.isArray()) return self.genArrayLiteral(al, ty);
}
// If current return type is vector, build as vector SSA value
if (self.current_return_type.isVector()) {
return self.genVectorLiteral(al, self.current_return_type);
@@ -2226,13 +2256,40 @@ pub const CodeGen = struct {
}
}
fn registerStructType(self: *CodeGen, sd: ast.StructDecl) !void {
fn registerStructType(self: *CodeGen, sd: ast.StructDecl) anyerror!void {
// Generic struct: store as template instead of registering now
if (sd.type_params.len > 0) {
try self.generic_struct_templates.put(sd.name, .{ .sd = sd });
return;
}
// Pre-pass: hoist inline type declarations from field types
for (sd.field_types, 0..) |ft, i| {
switch (ft.data) {
.struct_decl => |inline_sd| {
const synthetic_name = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ sd.name, sd.field_names[i] });
var hoisted = inline_sd;
hoisted.name = synthetic_name;
try self.registerStructType(hoisted);
ft.data = .{ .type_expr = .{ .name = synthetic_name } };
},
.union_decl => |inline_ud| {
const synthetic_name = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ sd.name, sd.field_names[i] });
var hoisted = inline_ud;
hoisted.name = synthetic_name;
try self.registerUnionType(hoisted);
ft.data = .{ .type_expr = .{ .name = synthetic_name } };
},
.enum_decl => |inline_ed| {
const synthetic_name = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ sd.name, sd.field_names[i] });
try self.enum_types.put(synthetic_name, inline_ed.variants);
_ = try self.getAnyTypeId(synthetic_name, .{ .enum_type = synthetic_name });
ft.data = .{ .type_expr = .{ .name = synthetic_name } };
},
else => {},
}
}
var field_sx_types = std.ArrayList(Type).empty;
var field_llvm_types = std.ArrayList(c.LLVMTypeRef).empty;
@@ -2274,6 +2331,35 @@ pub const CodeGen = struct {
}
fn registerUnionType(self: *CodeGen, ud: ast.UnionDecl) !void {
// Pre-pass: hoist inline type declarations from variant types
for (ud.variant_types, 0..) |vt_opt, i| {
if (vt_opt) |vt| {
switch (vt.data) {
.struct_decl => |inline_sd| {
const synthetic_name = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ud.name, ud.variant_names[i] });
var hoisted = inline_sd;
hoisted.name = synthetic_name;
try self.registerStructType(hoisted);
vt.data = .{ .type_expr = .{ .name = synthetic_name } };
},
.union_decl => |inline_ud| {
const synthetic_name = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ud.name, ud.variant_names[i] });
var hoisted = inline_ud;
hoisted.name = synthetic_name;
try self.registerUnionType(hoisted);
vt.data = .{ .type_expr = .{ .name = synthetic_name } };
},
.enum_decl => |inline_ed| {
const synthetic_name = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ud.name, ud.variant_names[i] });
try self.enum_types.put(synthetic_name, inline_ed.variants);
_ = try self.getAnyTypeId(synthetic_name, .{ .enum_type = synthetic_name });
vt.data = .{ .type_expr = .{ .name = synthetic_name } };
},
else => {},
}
}
}
var variant_sx_types = std.ArrayList(Type).empty;
var max_payload_size: u64 = 0;
const data_layout = c.LLVMGetModuleDataLayout(self.module);
@@ -2354,7 +2440,7 @@ pub const CodeGen = struct {
return alloca;
}
fn genStructLiteral(self: *CodeGen, sl: ast.StructLiteral, expected_struct_name: ?[]const u8) !c.LLVMValueRef {
fn genStructLiteral(self: *CodeGen, sl: ast.StructLiteral, expected_struct_name: ?[]const u8) anyerror!c.LLVMValueRef {
const raw_name = sl.struct_name orelse blk: {
if (sl.type_expr) |te| {
const ty = self.resolveType(te);
@@ -2494,6 +2580,62 @@ pub const CodeGen = struct {
return self.genUnionLiteral(node.data.union_literal, target_ty.union_type);
}
// Struct literal targeting union type: .Variant.{fields} pattern
// Parsed as struct_literal with type_expr = enum_literal("Variant")
if (node.data == .struct_literal and target_ty.isUnion()) {
const sl = node.data.struct_literal;
if (sl.struct_name == null) {
if (sl.type_expr) |te| {
if (te.data == .enum_literal) {
const variant_name = te.data.enum_literal.name;
const uname = self.type_aliases.get(target_ty.union_type) orelse target_ty.union_type;
const info = self.union_types.get(uname) orelse
return self.emitErrorFmt("unknown union type '{s}'", .{uname});
// Find variant index
var variant_idx: ?u32 = null;
for (info.variant_names, 0..) |vn, vi| {
if (std.mem.eql(u8, vn, variant_name)) {
variant_idx = @intCast(vi);
break;
}
}
const idx = variant_idx orelse
return self.emitErrorFmt("no variant '{s}' in union '{s}'", .{ variant_name, uname });
const variant_ty = info.variant_types[idx];
// Alloca union, store tag
const alloca = c.LLVMBuildAlloca(self.builder, info.llvm_type, "union_lit");
const i32_ty = c.LLVMInt32TypeInContext(self.context);
const tag_gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, 0, "tag");
_ = c.LLVMBuildStore(self.builder, c.LLVMConstInt(i32_ty, idx, 0), tag_gep);
// Store struct payload
if (variant_ty != .void_type) {
const payload_struct_name = if (variant_ty.isStruct()) variant_ty.struct_type else null;
const payload_alloca = try self.genStructLiteral(.{
.struct_name = payload_struct_name,
.type_expr = null,
.field_inits = sl.field_inits,
}, payload_struct_name);
const payload_gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, 1, "payload");
const payload_llvm_ty = self.typeToLLVM(variant_ty);
const struct_val = c.LLVMBuildLoad2(self.builder, payload_llvm_ty, payload_alloca, "struct_load");
_ = c.LLVMBuildStore(self.builder, struct_val, payload_gep);
}
return alloca;
}
}
}
}
// Struct literal targeting struct type: pass struct name context
if (node.data == .struct_literal and target_ty.isStruct()) {
return self.genStructLiteral(node.data.struct_literal, target_ty.struct_type);
}
// Array literal with target array type: generate with element conversion
if (node.data == .array_literal and target_ty.isArray()) {
return self.genArrayLiteral(node.data.array_literal, target_ty);
@@ -4701,6 +4843,16 @@ pub const CodeGen = struct {
return self.allocator.dupeZ(u8, name) catch unreachable;
}
/// Extract a qualified name from a callee expression (identifier or field_access chain).
fn calleeToQualifiedName(self: *CodeGen, callee: *Node) ?[]const u8 {
if (callee.data == .identifier) return callee.data.identifier.name;
if (callee.data == .field_access) {
const obj_name = self.calleeToQualifiedName(callee.data.field_access.object) orelse return null;
return std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ obj_name, callee.data.field_access.field }) catch null;
}
return null;
}
/// Resolve a name to a type display string, or null if not a type.
fn resolveTypeName(self: *CodeGen, name: []const u8) ?[]const u8 {
// Type aliases (e.g. x := f64, Vec3 :: Vec(3, f32)) — follow alias first