...
This commit is contained in:
110
src/codegen.zig
110
src/codegen.zig
@@ -99,6 +99,15 @@ fn baseName(name: []const u8) []const u8 {
|
||||
return if (std.mem.lastIndexOfScalar(u8, name, '.')) |idx| name[idx + 1 ..] else name;
|
||||
}
|
||||
|
||||
/// Detect `$T: Type` parameter declarations (not `s: $T` references).
|
||||
/// For `$T: Type`, the parser sets param.name = "T" and type_expr = {name="T", is_generic=true}.
|
||||
/// For `s: $T`, param.name = "s" and type_expr = {name="T", is_generic=true}.
|
||||
fn isTypeParamDecl(param: ast.Param) bool {
|
||||
return param.type_expr.data == .type_expr and
|
||||
param.type_expr.data.type_expr.is_generic and
|
||||
std.mem.eql(u8, param.name, param.type_expr.data.type_expr.name);
|
||||
}
|
||||
|
||||
pub const CodeGen = struct {
|
||||
context: c.LLVMContextRef,
|
||||
module: c.LLVMModuleRef,
|
||||
@@ -1271,6 +1280,7 @@ pub const CodeGen = struct {
|
||||
};
|
||||
|
||||
var vm = comptime_mod.VM.init(self.allocator, if (self.sema_result) |sr| sr else null, self.root_decls, self);
|
||||
vm.setupComptimeContext() catch {};
|
||||
return vm.execute(&chunk) catch |err| {
|
||||
return self.emitErrorFmt("comptime execution failed: {s}", .{@errorName(err)});
|
||||
};
|
||||
@@ -1307,6 +1317,7 @@ pub const CodeGen = struct {
|
||||
|
||||
// Set up VM and push all args onto the stack
|
||||
var vm = comptime_mod.VM.init(self.allocator, if (self.sema_result) |sr| sr else null, self.root_decls, self);
|
||||
vm.setupComptimeContext() catch {};
|
||||
for (arg_values) |val| {
|
||||
vm.push(val) catch return null;
|
||||
}
|
||||
@@ -1842,6 +1853,8 @@ pub const CodeGen = struct {
|
||||
var param_llvm_types = std.ArrayList(c.LLVMTypeRef).empty;
|
||||
for (params) |param| {
|
||||
if (param.is_comptime) continue;
|
||||
// Skip $T: Type params — erased at instantiation time (param name == type name)
|
||||
if (isTypeParamDecl(param)) continue;
|
||||
if (param.is_variadic) {
|
||||
// Variadic param becomes a slice {ptr, i32} in the LLVM signature
|
||||
try param_llvm_types.append(self.allocator, self.getStringStructType());
|
||||
@@ -3333,6 +3346,26 @@ pub const CodeGen = struct {
|
||||
return self.emitErrorFmt("field assignment not supported on tagged enum '{s}'", .{uname});
|
||||
}
|
||||
|
||||
// Slice/string field assignment: s.ptr = val, s.len = val
|
||||
if (entry.ty == .string_type or entry.ty.isSlice()) {
|
||||
const struct_ty = self.getStringStructType();
|
||||
if (std.mem.eql(u8, fa.field, "ptr")) {
|
||||
const gep = self.structGEP(struct_ty, entry.ptr, 0, "slice_ptr");
|
||||
const elem_name = if (entry.ty == .string_type) "u8" else entry.ty.slice_type.element_name;
|
||||
const ptr_ty = Type{ .many_pointer_type = .{ .element_name = elem_name } };
|
||||
const rhs = try self.genExprAsType(asgn.value, ptr_ty);
|
||||
_ = c.LLVMBuildStore(self.builder, rhs, gep);
|
||||
return null;
|
||||
}
|
||||
if (std.mem.eql(u8, fa.field, "len")) {
|
||||
const gep = self.structGEP(struct_ty, entry.ptr, 1, "slice_len");
|
||||
const rhs = try self.genExprAsType(asgn.value, Type.s(64));
|
||||
_ = c.LLVMBuildStore(self.builder, rhs, gep);
|
||||
return null;
|
||||
}
|
||||
return self.emitErrorFmt("no field '{s}' on slice (available: .ptr, .len)", .{fa.field});
|
||||
}
|
||||
|
||||
if (!entry.ty.isStruct()) return self.emitErrorFmt("field access on non-struct variable '{s}'", .{obj_name});
|
||||
|
||||
const sname = entry.ty.struct_type;
|
||||
@@ -3741,6 +3774,11 @@ pub const CodeGen = struct {
|
||||
Type.u(8);
|
||||
return self.gepPointerElement(self.typeToLLVM(elem_ty), ptr, idx, "addr_elem");
|
||||
}
|
||||
if (obj_ty.isManyPointer()) {
|
||||
const raw_ptr = try self.genExpr(ie.object);
|
||||
const elem_ty = self.resolveTypeFromName(obj_ty.many_pointer_type.element_name) orelse Type.u(8);
|
||||
return self.gepPointerElement(self.typeToLLVM(elem_ty), raw_ptr, idx, "addr_elem");
|
||||
}
|
||||
}
|
||||
// &s.field — return GEP pointer to the struct field
|
||||
if (operand.data == .field_access) {
|
||||
@@ -6072,6 +6110,26 @@ pub const CodeGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// Struct field function pointer call: obj.field(args)
|
||||
// Checked before UFCS so that struct fields shadow free functions of the same name.
|
||||
{
|
||||
var obj_ty = self.inferType(fa.object);
|
||||
if (obj_ty.isPointer()) {
|
||||
obj_ty = self.resolveTypeFromName(obj_ty.pointer_type.pointee_name) orelse obj_ty;
|
||||
}
|
||||
if (obj_ty.isStruct()) {
|
||||
if (self.lookupStructInfo(obj_ty.struct_type)) |info| {
|
||||
if (self.findNameIndex(info.field_names, fa.field)) |idx| {
|
||||
const field_ty = info.field_types[idx];
|
||||
if (field_ty.isFunctionType()) {
|
||||
const fn_ptr = try self.genFieldAccess(fa);
|
||||
return self.genIndirectCallFromPtr(fn_ptr, field_ty.function_type, call_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UFCS: obj.method(args...) → method(obj, args...)
|
||||
const method_name = fa.field;
|
||||
const resolved_method = self.ufcs_aliases.get(method_name) orelse method_name;
|
||||
@@ -6373,11 +6431,12 @@ pub const CodeGen = struct {
|
||||
|
||||
fn genIndirectCall(self: *CodeGen, entry: NamedValue, call_node: ast.Call) !c.LLVMValueRef {
|
||||
const fti = entry.ty.function_type;
|
||||
|
||||
// Load the function pointer from the alloca
|
||||
const ptr_ty = self.ptrType();
|
||||
const fn_ptr = c.LLVMBuildLoad2(self.builder, ptr_ty, entry.ptr, "fn_ptr");
|
||||
return self.genIndirectCallFromPtr(fn_ptr, fti, call_node);
|
||||
}
|
||||
|
||||
fn genIndirectCallFromPtr(self: *CodeGen, fn_ptr: c.LLVMValueRef, fti: Type.FunctionTypeInfo, call_node: ast.Call) !c.LLVMValueRef {
|
||||
// Build LLVM function type from FunctionTypeInfo
|
||||
const ptr_ty_llvm = self.ptrType();
|
||||
if (fti.param_types.len > 64) return self.emitErrorFmt("indirect call has {d} parameters, exceeding maximum of 64", .{fti.param_types.len});
|
||||
@@ -6482,6 +6541,22 @@ pub const CodeGen = struct {
|
||||
var bindings = std.StringHashMap(Type).init(self.allocator);
|
||||
// Track bindings derived from parameterized struct types — these are authoritative and should not be widened
|
||||
var firm_bindings = std.StringHashMap(void).init(self.allocator);
|
||||
// Bind explicit $T: Type params from type expression args
|
||||
for (fd.params, 0..) |param, i| {
|
||||
if (!param.is_comptime) continue;
|
||||
if (i >= call_node.args.len) continue;
|
||||
const arg = call_node.args[i];
|
||||
if (arg.data != .type_expr) continue;
|
||||
for (fd.type_params) |tp| {
|
||||
if (std.mem.eql(u8, tp.name, param.name)) {
|
||||
const constraint = if (tp.constraint.data == .type_expr) tp.constraint.data.type_expr.name else "";
|
||||
if (std.mem.eql(u8, constraint, "Type")) {
|
||||
try bindings.put(tp.name, self.resolveType(arg));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (fd.params, 0..) |param, i| {
|
||||
if (param.is_comptime) continue;
|
||||
// Direct type param: (a: $T) introduces/widens, (a: T) only binds if not yet bound
|
||||
@@ -6580,10 +6655,16 @@ pub const CodeGen = struct {
|
||||
try self.instantiateGeneric(fd, bindings, mangled);
|
||||
|
||||
// Generate arguments with type conversion to match parameter types
|
||||
// Skip $T: Type params (arg is a type expression, not a runtime value)
|
||||
const saved_call_bindings = self.type_param_bindings;
|
||||
self.type_param_bindings = bindings;
|
||||
var arg_vals = std.ArrayList(c.LLVMValueRef).empty;
|
||||
for (call_node.args, 0..) |arg, i| {
|
||||
// Skip $T: Type params — the arg is a type expression, not a runtime value
|
||||
if (i < fd.params.len) {
|
||||
const p = fd.params[i];
|
||||
if (isTypeParamDecl(p)) continue;
|
||||
}
|
||||
if (i < fd.params.len) {
|
||||
const param_ty = self.resolveType(fd.params[i].type_expr);
|
||||
try arg_vals.append(self.allocator, try self.genExprAsType(arg, param_ty));
|
||||
@@ -6979,6 +7060,8 @@ pub const CodeGen = struct {
|
||||
// Create allocas for parameters
|
||||
var llvm_param_idx: u32 = 0;
|
||||
for (fd.params) |param| {
|
||||
// Skip $T: Type params — type is resolved via bindings, not passed at runtime
|
||||
if (isTypeParamDecl(param)) continue;
|
||||
if (param.is_comptime) {
|
||||
// Comptime param: create a constant in named_values from the call-site value
|
||||
if (self.comptime_param_nodes) |cpn| {
|
||||
@@ -7930,6 +8013,24 @@ pub const CodeGen = struct {
|
||||
break :blk @as(?Type, null);
|
||||
};
|
||||
if (obj_ty) |uty| return uty;
|
||||
|
||||
// Struct field function pointer call: obj.fn_field(args)
|
||||
{
|
||||
var fa_obj_ty = self.inferType(fa.object);
|
||||
if (fa_obj_ty.isPointer()) {
|
||||
fa_obj_ty = self.resolveTypeFromName(fa_obj_ty.pointer_type.pointee_name) orelse fa_obj_ty;
|
||||
}
|
||||
if (fa_obj_ty.isStruct()) {
|
||||
if (self.lookupStructInfo(fa_obj_ty.struct_type)) |info| {
|
||||
if (self.findNameIndex(info.field_names, fa.field)) |idx| {
|
||||
const field_ty = info.field_types[idx];
|
||||
if (field_ty.isFunctionType()) {
|
||||
return field_ty.function_type.return_type.*;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const callee_name = self.resolveCalleeName(call_node) orelse return Type.s(64);
|
||||
const base_name = baseName(callee_name);
|
||||
@@ -8005,8 +8106,11 @@ pub const CodeGen = struct {
|
||||
return bound_ty;
|
||||
}
|
||||
}
|
||||
// Try resolving as a concrete type (e.g. -> string, -> s32)
|
||||
// Resolve with inferred bindings so []T, *T etc. substitute correctly
|
||||
const saved_bindings = self.type_param_bindings;
|
||||
self.type_param_bindings = inferred_bindings;
|
||||
const resolved = self.resolveType(rt);
|
||||
self.type_param_bindings = saved_bindings;
|
||||
if (!std.meta.eql(resolved, Type.void_type)) return resolved;
|
||||
}
|
||||
return Type.s(64);
|
||||
|
||||
Reference in New Issue
Block a user