slice print

This commit is contained in:
agra
2026-02-10 20:23:37 +02:00
parent 3fde080092
commit bba26ca0d7
3 changed files with 129 additions and 8 deletions

View File

@@ -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"),
};
}