From 4b1c22d3b62d8be102ba78d8b9c1c1d56ba0d538 Mon Sep 17 00:00:00 2001 From: agra Date: Mon, 9 Feb 2026 19:22:53 +0200 Subject: [PATCH] anon struct, union... --- examples/03-structs.sx | 14 ++++ src/codegen.zig | 170 ++++++++++++++++++++++++++++++++++++++--- src/parser.zig | 28 +++++++ 3 files changed, 203 insertions(+), 9 deletions(-) diff --git a/examples/03-structs.sx b/examples/03-structs.sx index 7acca06..a088c52 100644 --- a/examples/03-structs.sx +++ b/examples/03-structs.sx @@ -3,6 +3,15 @@ Vec4 :: struct { x, y, z, w: f32; } +Complex :: struct { + foo : union { + S: s32; + B: struct { + val: string; + }; + } = .B.{val = "hello"}; +} + main :: () { v1 : Vec4 = .{ 1, 2, 3, 0}; v2 := Vec4.{ 4, 1, 1, 3}; @@ -13,6 +22,9 @@ main :: () { v4.y = 0; print("v1: {}\nv2: {}\nv3: {}\nv4: {}\n", v1, v2, v3, v4); + + complex : Complex = .{}; + print("\n{}\n", complex); } // ** stdout ** @@ -20,3 +32,5 @@ main :: () { //v2: Vec4{x:4.0, y:1.0, z:1.0, w:3.0} //v3: Vec4{x:2.0, y:3.0, z:4.0, w:0.0} //v4: Vec4{x:9.0, y:3.0, z:5.0, w:6.0} +// +//Complex{foo: .B(Complex.foo.B{val: hello})} diff --git a/src/codegen.zig b/src/codegen.zig index d9c1381..3293d25 100644 --- a/src/codegen.zig +++ b/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 diff --git a/src/parser.zig b/src/parser.zig index d1c227f..5a35ff6 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -266,6 +266,18 @@ pub const Parser = struct { return try self.createNode(start, .{ .type_expr = .{ .name = name } }); } + // Inline struct type in type position: struct { ... } + if (self.current.tag == .kw_struct) { + return try self.parseStructDecl("__anon", start); + } + // Inline union type in type position: union { ... } + if (self.current.tag == .kw_union) { + return try self.parseUnionDecl("__anon", start); + } + // Inline enum type in type position: enum { ... } + if (self.current.tag == .kw_enum) { + return try self.parseEnumDecl("__anon", start); + } return self.fail("expected type name"); } @@ -843,6 +855,22 @@ pub const Parser = struct { // Expression type: Vec(3, f32).{ ... } expr = try self.parseStructLiteral(null, expr, expr.span.start); } + } else if (self.current.tag == .l_bracket) { + // Typed array/vector literal: Type.[elem, ...] + self.advance(); // skip '[' + var elements = std.ArrayList(*Node).empty; + while (self.current.tag != .r_bracket and self.current.tag != .eof) { + if (elements.items.len > 0) { + try self.expect(.comma); + } + const elem = try self.parseExpr(); + try elements.append(self.allocator, elem); + } + try self.expect(.r_bracket); + expr = try self.createNode(expr.span.start, .{ .array_literal = .{ + .elements = try elements.toOwnedSlice(self.allocator), + .type_expr = expr, + } }); } else { // Field access if (self.current.tag != .identifier) {