slice print
This commit is contained in:
117
src/codegen.zig
117
src/codegen.zig
@@ -87,6 +87,13 @@ pub const CodeGen = struct {
|
||||
any_type_entries: std.StringHashMap(AnyTypeEntry),
|
||||
// Current match arm type entries (set during category match arm body generation)
|
||||
current_match_tags: ?[]const u64 = null,
|
||||
// Functions deferred to compile after all types are registered (e.g. any_to_string)
|
||||
deferred_fn_bodies: std.ArrayList(DeferredFn),
|
||||
|
||||
const DeferredFn = struct {
|
||||
fd: ast.FnDecl,
|
||||
name: []const u8, // qualified name (may differ from fd.name for namespaced functions)
|
||||
};
|
||||
|
||||
const TypeCategory = enum {
|
||||
struct_cat,
|
||||
@@ -178,6 +185,7 @@ pub const CodeGen = struct {
|
||||
.fn_param_types = std.StringHashMap([]const Type).init(allocator),
|
||||
.any_type_id_map = std.StringHashMap(u64).init(allocator),
|
||||
.any_type_entries = std.StringHashMap(AnyTypeEntry).init(allocator),
|
||||
.deferred_fn_bodies = std.ArrayList(DeferredFn).empty,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -196,6 +204,7 @@ pub const CodeGen = struct {
|
||||
self.variadic_functions.deinit();
|
||||
self.any_type_id_map.deinit();
|
||||
self.any_type_entries.deinit();
|
||||
self.deferred_fn_bodies.deinit(self.allocator);
|
||||
c.LLVMDisposeBuilder(self.builder);
|
||||
c.LLVMDisposeModule(self.module);
|
||||
c.LLVMContextDispose(self.context);
|
||||
@@ -286,6 +295,34 @@ pub const CodeGen = struct {
|
||||
return gop.value_ptr.*;
|
||||
}
|
||||
|
||||
/// Check if a function should have its body compilation deferred until after all types are registered.
|
||||
/// Functions with non-variadic Any parameters (like any_to_string) use type-based match expressions
|
||||
/// that need all types registered before compilation.
|
||||
fn shouldDeferFnBody(fd: ast.FnDecl) bool {
|
||||
for (fd.params) |param| {
|
||||
if (!param.is_variadic and param.type_expr.data == .type_expr and
|
||||
std.mem.eql(u8, param.type_expr.data.type_expr.name, "Any"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Pre-register a type in the Any type system so category matching (case slice:, case array:, etc.)
|
||||
/// works in any_to_string even before buildAnyValue is called for this type.
|
||||
fn preRegisterAnyType(self: *CodeGen, sx_type: Type) !void {
|
||||
switch (sx_type) {
|
||||
.struct_type => |name| _ = try self.getAnyTypeId(name, sx_type),
|
||||
.enum_type => |name| _ = try self.getAnyTypeId(name, sx_type),
|
||||
.union_type => |name| _ = try self.getAnyTypeId(name, sx_type),
|
||||
.vector_type => |info| _ = try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "vec[{d}]{s}", .{ info.length, info.element_name }), sx_type),
|
||||
.array_type => |info| _ = try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "[{d}]{s}", .{ info.length, info.element_name }), sx_type),
|
||||
.slice_type => |info| _ = try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "[]{s}", .{info.element_name}), sx_type),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
/// Build an Any value { tag: i32, value: i64 } from a typed LLVM value.
|
||||
/// Small values (ints, floats, bools, enums) are stored inline in the i64.
|
||||
/// Complex values (strings, structs, unions) are stored via pointer (alloca + ptr-to-int).
|
||||
@@ -309,6 +346,7 @@ pub const CodeGen = struct {
|
||||
.union_type => |name| try self.getAnyTypeId(name, ty),
|
||||
.vector_type => |info| try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "vec[{d}]{s}", .{ info.length, info.element_name }), ty),
|
||||
.array_type => |info| try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "[{d}]{s}", .{ info.length, info.element_name }), ty),
|
||||
.slice_type => |info| try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "[]{s}", .{info.element_name}), ty),
|
||||
.meta_type => ANY_TAG_TYPE,
|
||||
else => ANY_TAG_S32,
|
||||
};
|
||||
@@ -365,6 +403,12 @@ pub const CodeGen = struct {
|
||||
_ = c.LLVMBuildStore(self.builder, val, alloca);
|
||||
break :blk c.LLVMBuildPtrToInt(self.builder, alloca, i64_ty, "any_vec");
|
||||
},
|
||||
.slice_type => blk: {
|
||||
// Slice {ptr, i32} — store to alloca, pass pointer as i64
|
||||
const alloca = c.LLVMBuildAlloca(self.builder, self.getStringStructType(), "any_slice_tmp");
|
||||
_ = c.LLVMBuildStore(self.builder, val, alloca);
|
||||
break :blk c.LLVMBuildPtrToInt(self.builder, alloca, i64_ty, "any_slice");
|
||||
},
|
||||
.meta_type => blk: {
|
||||
// Meta type is a pointer (global string) — convert via ptrtoint
|
||||
break :blk c.LLVMBuildPtrToInt(self.builder, val, i64_ty, "any_type");
|
||||
@@ -558,13 +602,19 @@ pub const CodeGen = struct {
|
||||
}
|
||||
|
||||
// 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| {
|
||||
switch (decl.data) {
|
||||
.fn_decl => |fd| {
|
||||
if (fd.body.data == .builtin_expr) {
|
||||
// skip
|
||||
} else if (fd.type_params.len == 0) {
|
||||
try self.genFnBody(fd);
|
||||
if (shouldDeferFnBody(fd)) {
|
||||
try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = fd.name });
|
||||
} else {
|
||||
try self.genFnBody(fd);
|
||||
}
|
||||
}
|
||||
},
|
||||
.const_decl => |cd| {
|
||||
@@ -579,6 +629,11 @@ pub const CodeGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 3: Compile deferred function bodies (after all types are registered)
|
||||
for (self.deferred_fn_bodies.items) |deferred| {
|
||||
try self.genFnBodyAs(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);
|
||||
@@ -1174,7 +1229,11 @@ pub const CodeGen = struct {
|
||||
// skip
|
||||
} else if (fd.type_params.len == 0) {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, fd.name });
|
||||
try self.genFnBodyAs(fd, qualified);
|
||||
if (shouldDeferFnBody(fd)) {
|
||||
try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = qualified });
|
||||
} else {
|
||||
try self.genFnBodyAs(fd, qualified);
|
||||
}
|
||||
}
|
||||
},
|
||||
.const_decl => |cd| {
|
||||
@@ -3145,6 +3204,21 @@ pub const CodeGen = struct {
|
||||
return phi;
|
||||
}
|
||||
|
||||
// Slice: extract ptr, GEP to element, load, box as Any
|
||||
if (val_ty.isSlice()) {
|
||||
const sinfo = val_ty.slice_type;
|
||||
const elem_ty = Type.fromName(sinfo.element_name) orelse
|
||||
return self.emitErrorFmt("unknown slice element type '{s}'", .{sinfo.element_name});
|
||||
const elem_llvm_ty = self.typeToLLVM(elem_ty);
|
||||
// val is {ptr, i32} — extract ptr
|
||||
const data_ptr = c.LLVMBuildExtractValue(self.builder, val, 0, "fv_sdata");
|
||||
const idx = try self.genExpr(call_node.args[1]);
|
||||
var gep_indices = [_]c.LLVMValueRef{idx};
|
||||
const elem_ptr = c.LLVMBuildGEP2(self.builder, elem_llvm_ty, data_ptr, &gep_indices, 1, "fv_selem");
|
||||
const elem = c.LLVMBuildLoad2(self.builder, elem_llvm_ty, elem_ptr, "fv_seval");
|
||||
return self.buildAnyValue(elem, elem_ty);
|
||||
}
|
||||
|
||||
// Array: GEP + load + box as Any
|
||||
if (val_ty.isArray()) {
|
||||
const ainfo = val_ty.array_type;
|
||||
@@ -4020,6 +4094,22 @@ pub const CodeGen = struct {
|
||||
}
|
||||
defer self.current_namespace = saved_namespace;
|
||||
|
||||
// Pre-register Any type IDs for variadic args before function instantiation,
|
||||
// so type category matching (case slice:, case array:, etc.) in any_to_string
|
||||
// can find registered types during compilation of the function body.
|
||||
for (fd.params, 0..) |param, pi| {
|
||||
if (param.is_variadic) {
|
||||
const elem_name_raw = if (param.type_expr.data == .type_expr) param.type_expr.data.type_expr.name else "";
|
||||
if (std.mem.eql(u8, elem_name_raw, "Any") or std.mem.eql(u8, elem_name_raw, "std.Any")) {
|
||||
for (pi..call_node.args.len) |ai| {
|
||||
const arg_ty = self.inferType(call_node.args[ai]);
|
||||
try self.preRegisterAnyType(arg_ty);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_ = try self.instantiateGeneric(fd, type_bindings, mangled);
|
||||
|
||||
// Register variadic info for the mangled function (adjusted for removed comptime params)
|
||||
@@ -4145,6 +4235,25 @@ pub const CodeGen = struct {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (fd.params[cast_arg_idx].type_expr.data == .slice_type_expr) {
|
||||
// Slice type param: (items: []$T) — extract element type from concrete slice
|
||||
const elem_node = fd.params[cast_arg_idx].type_expr.data.slice_type_expr.element_type;
|
||||
if (elem_node.data == .type_expr) {
|
||||
const tp_name = elem_node.data.type_expr.name;
|
||||
for (fd.type_params) |tp| {
|
||||
if (std.mem.eql(u8, tp.name, tp_name)) {
|
||||
// Extract element type from concrete slice type
|
||||
const elem_ty = if (sx_type.isSlice())
|
||||
Type.fromName(sx_type.slice_type.element_name) orelse sx_type
|
||||
else if (sx_type.isArray())
|
||||
Type.fromName(sx_type.array_type.element_name) orelse sx_type
|
||||
else
|
||||
sx_type;
|
||||
try bindings.put(tp.name, elem_ty);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4247,6 +4356,10 @@ pub const CodeGen = struct {
|
||||
const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_vec_ptr");
|
||||
break :blk c.LLVMBuildLoad2(self.builder, llvm_ty, ptr, "any_to_vec");
|
||||
},
|
||||
.slice_type => blk: {
|
||||
const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_slice_ptr");
|
||||
break :blk c.LLVMBuildLoad2(self.builder, self.getStringStructType(), ptr, "any_to_slice");
|
||||
},
|
||||
else => c.LLVMBuildTrunc(self.builder, any_i64, c.LLVMInt32TypeInContext(self.context), "any_to_default"),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user