forward declaration
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
std :: #import "modules/std.sx";
|
||||
|
||||
//flat
|
||||
#import "modules/math.sx";
|
||||
#import "modules/math";
|
||||
|
||||
main :: () -> s32 {
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#import "modules/std.sx";
|
||||
math :: #import "modules/math.sx";
|
||||
math :: #import "modules/math";
|
||||
|
||||
vec3 :: (x:f32, y:f32, z:f32) -> Vector(3,f32) {
|
||||
.[x, y, z];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math.sx";
|
||||
#import "modules/math";
|
||||
|
||||
test :: () -> s32 {
|
||||
0;
|
||||
|
||||
41
examples/issue-01.sx
Normal file
41
examples/issue-01.sx
Normal file
@@ -0,0 +1,41 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
// Forward references: types, struct fields, methods, and free functions
|
||||
// can reference types declared later in the file.
|
||||
|
||||
// Free function referencing types declared later
|
||||
make_frame :: (e: EdgeInsets) -> Frame {
|
||||
Frame.{ x = e.left, y = e.top,
|
||||
w = 100.0 - e.left - e.right,
|
||||
h = 100.0 - e.top - e.bottom };
|
||||
}
|
||||
|
||||
// Struct with a field whose type is declared later
|
||||
Container :: struct {
|
||||
frame: Frame;
|
||||
insets: EdgeInsets;
|
||||
}
|
||||
|
||||
Frame :: struct {
|
||||
x, y, w, h: f32;
|
||||
|
||||
inset :: (self: Frame, insets: EdgeInsets) -> Frame {
|
||||
Frame.{ x = self.x + insets.left, y = self.y + insets.top,
|
||||
w = self.w - insets.left - insets.right,
|
||||
h = self.h - insets.top - insets.bottom };
|
||||
}
|
||||
}
|
||||
|
||||
EdgeInsets :: struct {
|
||||
top, left, bottom, right: f32;
|
||||
}
|
||||
|
||||
main :: () {
|
||||
e := EdgeInsets.{ top = 10.0, left = 10.0, bottom = 10.0, right = 10.0 };
|
||||
f := make_frame(e);
|
||||
r := f.inset(e);
|
||||
c := Container.{ frame = f, insets = e };
|
||||
print("{}", r.x);
|
||||
print(" {}", r.w);
|
||||
print(" {}", c.frame.x);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#import "std.sx";
|
||||
|
||||
dot :: (a: Vector(3,f32), b: Vector(3,f32)) -> f32 {
|
||||
return a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
|
||||
cross :: (a: Vector(3,f32), b: Vector(3,f32)) -> Vector(3,f32) {
|
||||
.[a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x];
|
||||
}
|
||||
|
||||
length :: (v: Vector(3,f32)) -> f32 {
|
||||
return sqrt(dot(v, v));
|
||||
}
|
||||
|
||||
normalize :: (v: Vector(3,f32)) -> Vector(3,f32) {
|
||||
return v / length(v);
|
||||
}
|
||||
@@ -1,5 +1,37 @@
|
||||
PI :f32: 3.14159265;
|
||||
TAU :f32: 6.28318530;
|
||||
DEG2RAD :f32: 0.01745329;
|
||||
RAD2DEG :f32: 57.2957795;
|
||||
|
||||
sqrt :: (x: $T) -> T #builtin;
|
||||
sin :: (x: $T) -> T #builtin;
|
||||
cos :: (x: $T) -> T #builtin;
|
||||
floor :: (x: $T) -> T #builtin;
|
||||
|
||||
min :: (a: $T, b: T) -> T {
|
||||
if a < b then a else b;
|
||||
}
|
||||
|
||||
max :: (a: $T, b: T) -> T {
|
||||
if a > b then a else b;
|
||||
}
|
||||
|
||||
clamp :: (val: $T, lo: T, hi: T) -> T {
|
||||
if val < lo then lo
|
||||
else if val > hi then hi
|
||||
else val;
|
||||
}
|
||||
|
||||
abs :: (x: $T) -> T {
|
||||
if x < 0 then 0 - x else x;
|
||||
}
|
||||
|
||||
lerp :: (a: f32, b: f32, t: f32) -> f32 {
|
||||
a + (b - a) * t;
|
||||
}
|
||||
|
||||
sign :: (x: $T) -> T {
|
||||
if x > 0 then 1
|
||||
else if x < 0 then 0 - 1
|
||||
else 0;
|
||||
}
|
||||
|
||||
117
examples/modules/math/matrix44.sx
Normal file
117
examples/modules/math/matrix44.sx
Normal file
@@ -0,0 +1,117 @@
|
||||
// Column-major 4x4 float matrix (OpenGL convention)
|
||||
// data[col * 4 + row]
|
||||
|
||||
Mat4 :: struct {
|
||||
data: [16]f32;
|
||||
|
||||
identity :: () -> Mat4 {
|
||||
Mat4.{ data = .[
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
]};
|
||||
}
|
||||
|
||||
zero :: () -> Mat4 {
|
||||
Mat4.{ data = .[
|
||||
0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0
|
||||
]};
|
||||
}
|
||||
|
||||
mul :: (self: Mat4, b: Mat4) -> Mat4 {
|
||||
r := Mat4.zero();
|
||||
col := 0;
|
||||
while col < 4 {
|
||||
row := 0;
|
||||
while row < 4 {
|
||||
sum : f32 = 0.0;
|
||||
k := 0;
|
||||
while k < 4 {
|
||||
sum = sum + self.data[k * 4 + row] * b.data[col * 4 + k];
|
||||
k += 1;
|
||||
}
|
||||
r.data[col * 4 + row] = sum;
|
||||
row += 1;
|
||||
}
|
||||
col += 1;
|
||||
}
|
||||
r;
|
||||
}
|
||||
|
||||
translate :: (x: f32, y: f32, z: f32) -> Mat4 {
|
||||
m := Mat4.identity();
|
||||
m.data[12] = x;
|
||||
m.data[13] = y;
|
||||
m.data[14] = z;
|
||||
m;
|
||||
}
|
||||
|
||||
scale :: (x: f32, y: f32, z: f32) -> Mat4 {
|
||||
m := Mat4.zero();
|
||||
m.data[0] = x;
|
||||
m.data[5] = y;
|
||||
m.data[10] = z;
|
||||
m.data[15] = 1.0;
|
||||
m;
|
||||
}
|
||||
|
||||
rotate_x :: (angle: f32) -> Mat4 {
|
||||
c := cos(angle);
|
||||
s := sin(angle);
|
||||
m := Mat4.identity();
|
||||
m.data[5] = c;
|
||||
m.data[6] = s;
|
||||
m.data[9] = 0.0 - s;
|
||||
m.data[10] = c;
|
||||
m;
|
||||
}
|
||||
|
||||
rotate_y :: (angle: f32) -> Mat4 {
|
||||
c := cos(angle);
|
||||
s := sin(angle);
|
||||
m := Mat4.identity();
|
||||
m.data[0] = c;
|
||||
m.data[2] = 0.0 - s;
|
||||
m.data[8] = s;
|
||||
m.data[10] = c;
|
||||
m;
|
||||
}
|
||||
|
||||
rotate_z :: (angle: f32) -> Mat4 {
|
||||
c := cos(angle);
|
||||
s := sin(angle);
|
||||
m := Mat4.identity();
|
||||
m.data[0] = c;
|
||||
m.data[1] = s;
|
||||
m.data[4] = 0.0 - s;
|
||||
m.data[5] = c;
|
||||
m;
|
||||
}
|
||||
|
||||
ortho :: (left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Mat4 {
|
||||
m := Mat4.zero();
|
||||
m.data[0] = 2.0 / (right - left);
|
||||
m.data[5] = 2.0 / (top - bottom);
|
||||
m.data[10] = 0.0 - 2.0 / (far - near);
|
||||
m.data[12] = 0.0 - (right + left) / (right - left);
|
||||
m.data[13] = 0.0 - (top + bottom) / (top - bottom);
|
||||
m.data[14] = 0.0 - (far + near) / (far - near);
|
||||
m.data[15] = 1.0;
|
||||
m;
|
||||
}
|
||||
|
||||
perspective :: (fov: f32, aspect: f32, near: f32, far: f32) -> Mat4 {
|
||||
half_tan := sin(fov * 0.5) / cos(fov * 0.5);
|
||||
m := Mat4.zero();
|
||||
m.data[0] = 1.0 / (aspect * half_tan);
|
||||
m.data[5] = 1.0 / half_tan;
|
||||
m.data[10] = 0.0 - (far + near) / (far - near);
|
||||
m.data[11] = 0.0 - 1.0;
|
||||
m.data[14] = 0.0 - 2.0 * far * near / (far - near);
|
||||
m;
|
||||
}
|
||||
}
|
||||
49
examples/modules/math/vector2.sx
Normal file
49
examples/modules/math/vector2.sx
Normal file
@@ -0,0 +1,49 @@
|
||||
Vec2 :: struct {
|
||||
x, y: f32;
|
||||
|
||||
zero :: () -> Vec2 { Vec2.{ x = 0.0, y = 0.0 }; }
|
||||
|
||||
add :: (self: Vec2, b: Vec2) -> Vec2 {
|
||||
Vec2.{ x = self.x + b.x, y = self.y + b.y };
|
||||
}
|
||||
|
||||
sub :: (self: Vec2, b: Vec2) -> Vec2 {
|
||||
Vec2.{ x = self.x - b.x, y = self.y - b.y };
|
||||
}
|
||||
|
||||
scale :: (self: Vec2, s: f32) -> Vec2 {
|
||||
Vec2.{ x = self.x * s, y = self.y * s };
|
||||
}
|
||||
|
||||
dot :: (self: Vec2, b: Vec2) -> f32 {
|
||||
self.x * b.x + self.y * b.y;
|
||||
}
|
||||
|
||||
length :: (self: Vec2) -> f32 {
|
||||
sqrt(self.x * self.x + self.y * self.y);
|
||||
}
|
||||
|
||||
normalize :: (self: Vec2) -> Vec2 {
|
||||
len := self.length();
|
||||
if len > 0.0 {
|
||||
return Vec2.{ x = self.x / len, y = self.y / len };
|
||||
}
|
||||
Vec2.zero();
|
||||
}
|
||||
|
||||
lerp :: (self: Vec2, b: Vec2, t: f32) -> Vec2 {
|
||||
Vec2.{ x = self.x + (b.x - self.x) * t, y = self.y + (b.y - self.y) * t };
|
||||
}
|
||||
|
||||
distance :: (self: Vec2, b: Vec2) -> f32 {
|
||||
self.sub(b).length();
|
||||
}
|
||||
|
||||
negate :: (self: Vec2) -> Vec2 {
|
||||
Vec2.{ x = 0.0 - self.x, y = 0.0 - self.y };
|
||||
}
|
||||
|
||||
equals :: (self: Vec2, b: Vec2) -> bool {
|
||||
self.x == b.x and self.y == b.y;
|
||||
}
|
||||
}
|
||||
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