anon struct, union...
This commit is contained in:
170
src/codegen.zig
170
src/codegen.zig
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user