sdl phase 1
This commit is contained in:
@@ -63,6 +63,7 @@ pub const Node = struct {
|
||||
builtin_expr: void,
|
||||
foreign_expr: void,
|
||||
library_decl: LibraryDecl,
|
||||
function_type_expr: FunctionTypeExpr,
|
||||
|
||||
pub fn declName(self: Data) ?[]const u8 {
|
||||
return switch (self) {
|
||||
@@ -359,3 +360,8 @@ pub const NamespaceDecl = struct {
|
||||
pub const LibraryDecl = struct {
|
||||
lib_name: []const u8,
|
||||
};
|
||||
|
||||
pub const FunctionTypeExpr = struct {
|
||||
param_types: []const *Node,
|
||||
return_type: ?*Node, // null = void return
|
||||
};
|
||||
|
||||
287
src/codegen.zig
287
src/codegen.zig
@@ -177,6 +177,10 @@ pub const CodeGen = struct {
|
||||
foreign_libraries: std.ArrayList([]const u8),
|
||||
// Set of foreign function names (for ABI lowering at call sites)
|
||||
foreign_fns: std.StringHashMap(void),
|
||||
// Global mutable variables (from top-level var_decl, e.g. function pointers loaded at runtime)
|
||||
global_mutable_vars: std.StringHashMap(NamedValue),
|
||||
// Declared return types for non-generic functions (preserves signedness lost by LLVM round-trip)
|
||||
function_return_types: std.StringHashMap(Type),
|
||||
// Target configuration (triple, cpu, opt level, lib paths, linker)
|
||||
target_config: TargetConfig = .{},
|
||||
|
||||
@@ -293,6 +297,8 @@ pub const CodeGen = struct {
|
||||
.deferred_fn_bodies = std.ArrayList(DeferredFn).empty,
|
||||
.foreign_libraries = std.ArrayList([]const u8).empty,
|
||||
.foreign_fns = std.StringHashMap(void).init(allocator),
|
||||
.global_mutable_vars = std.StringHashMap(NamedValue).init(allocator),
|
||||
.function_return_types = std.StringHashMap(Type).init(allocator),
|
||||
.target_config = target_config,
|
||||
};
|
||||
}
|
||||
@@ -366,7 +372,7 @@ pub const CodeGen = struct {
|
||||
const elem_ty = Type.fromName(info.element_name) orelse unreachable;
|
||||
return c.LLVMVectorType(self.typeToLLVM(elem_ty), info.length);
|
||||
},
|
||||
.pointer_type, .many_pointer_type => c.LLVMPointerTypeInContext(self.context, 0),
|
||||
.pointer_type, .many_pointer_type, .function_type => c.LLVMPointerTypeInContext(self.context, 0),
|
||||
.any_type => self.getAnyStructType(),
|
||||
.meta_type => c.LLVMPointerTypeInContext(self.context, 0),
|
||||
};
|
||||
@@ -741,6 +747,9 @@ pub const CodeGen = struct {
|
||||
.namespace_decl => |ns| {
|
||||
try self.registerNamespace(ns);
|
||||
},
|
||||
.var_decl => |vd| {
|
||||
try self.registerGlobalVar(vd);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
@@ -937,6 +946,21 @@ pub const CodeGen = struct {
|
||||
const elem_name = elem_type.displayName(self.allocator) catch unreachable;
|
||||
return .{ .many_pointer_type = .{ .element_name = elem_name } };
|
||||
}
|
||||
// Function pointer type: (ParamTypes) -> ReturnType
|
||||
if (tn.data == .function_type_expr) {
|
||||
const fte = tn.data.function_type_expr;
|
||||
var param_types = std.ArrayList(Type).empty;
|
||||
for (fte.param_types) |pt| {
|
||||
param_types.append(self.allocator, self.resolveType(pt)) catch return .void_type;
|
||||
}
|
||||
const ret_ty = if (fte.return_type) |rt| self.resolveType(rt) else Type.void_type;
|
||||
const ret_ptr = self.allocator.create(Type) catch return .void_type;
|
||||
ret_ptr.* = ret_ty;
|
||||
return .{ .function_type = .{
|
||||
.param_types = param_types.toOwnedSlice(self.allocator) catch return .void_type,
|
||||
.return_type = ret_ptr,
|
||||
} };
|
||||
}
|
||||
// Parameterized type: Vector(N, T) or generic struct instantiation
|
||||
if (tn.data == .parameterized_type_expr) {
|
||||
const pte = tn.data.parameterized_type_expr;
|
||||
@@ -1482,6 +1506,9 @@ pub const CodeGen = struct {
|
||||
}
|
||||
}
|
||||
try self.fn_param_types.put(llvm_name, try param_types.toOwnedSlice(self.allocator));
|
||||
// Track declared return type (preserves signedness lost by LLVM round-trip)
|
||||
const ret_ty = if (fd.return_type) |rt| self.resolveType(rt) else Type.void_type;
|
||||
try self.function_return_types.put(llvm_name, ret_ty);
|
||||
// Track variadic function info for call site packing
|
||||
for (fd.params, 0..) |param, i| {
|
||||
if (param.is_variadic) {
|
||||
@@ -1553,6 +1580,9 @@ pub const CodeGen = struct {
|
||||
.library_decl => |ld| {
|
||||
try self.foreign_libraries.append(self.allocator, ld.lib_name);
|
||||
},
|
||||
.var_decl => |vd| {
|
||||
try self.registerGlobalVar(vd);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
@@ -1703,6 +1733,22 @@ pub const CodeGen = struct {
|
||||
});
|
||||
}
|
||||
|
||||
fn registerGlobalVar(self: *CodeGen, vd: ast.VarDecl) !void {
|
||||
const ta = vd.type_annotation orelse return;
|
||||
const sx_ty = self.resolveType(ta);
|
||||
if (sx_ty == .void_type) return;
|
||||
|
||||
const llvm_ty = self.typeToLLVM(sx_ty);
|
||||
const name_z = try self.allocator.dupeZ(u8, vd.name);
|
||||
const global = c.LLVMAddGlobal(self.module, llvm_ty, name_z.ptr);
|
||||
// Initialize with undef (will be set at runtime, e.g. by load_gl)
|
||||
c.LLVMSetInitializer(global, c.LLVMGetUndef(llvm_ty));
|
||||
// NOT constant — this is a mutable global
|
||||
c.LLVMSetGlobalConstant(global, 0);
|
||||
|
||||
try self.global_mutable_vars.put(vd.name, .{ .ptr = global, .ty = sx_ty });
|
||||
}
|
||||
|
||||
fn bindParam(self: *CodeGen, function: c.LLVMValueRef, name: []const u8, sx_ty: Type, param_idx: u32) !void {
|
||||
const llvm_ty = self.typeToLLVM(sx_ty);
|
||||
const param_name_z = try self.allocator.dupeZ(u8, name);
|
||||
@@ -2175,6 +2221,34 @@ pub const CodeGen = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Function pointer typed variable
|
||||
if (sx_ty.isFunctionType()) {
|
||||
const llvm_ty = c.LLVMPointerTypeInContext(self.context, 0);
|
||||
const name_z = try self.allocator.dupeZ(u8, vd.name);
|
||||
const alloca = self.buildEntryBlockAlloca(llvm_ty, name_z.ptr);
|
||||
|
||||
if (vd.value == null) {
|
||||
_ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(llvm_ty), alloca);
|
||||
} else if (vd.value.?.data == .undef_literal) {
|
||||
_ = c.LLVMBuildStore(self.builder, c.LLVMGetUndef(llvm_ty), alloca);
|
||||
} else if (vd.value.?.data == .unary_op and vd.value.?.data.unary_op.op == .xx) {
|
||||
// xx cast: e.g. xx SDL_GL_GetProcAddress("glClear")
|
||||
const inner = vd.value.?.data.unary_op.operand;
|
||||
const val = try self.genExpr(inner);
|
||||
const src_ty = self.inferType(inner);
|
||||
const converted = self.convertValue(val, src_ty, sx_ty);
|
||||
_ = c.LLVMBuildStore(self.builder, converted, alloca);
|
||||
} else {
|
||||
// Direct assignment: identifier (function name) or other expression
|
||||
const val = try self.genExpr(vd.value.?);
|
||||
_ = c.LLVMBuildStore(self.builder, val, alloca);
|
||||
}
|
||||
|
||||
try self.saveShadowed(vd.name);
|
||||
try self.named_values.put(vd.name, .{ .ptr = alloca, .ty = sx_ty });
|
||||
return null;
|
||||
}
|
||||
|
||||
// Guard: void type cannot be allocated (would crash LLVM)
|
||||
if (sx_ty == .void_type) {
|
||||
return self.emitErrorFmt("cannot declare variable '{s}' with void type", .{vd.name});
|
||||
@@ -2261,6 +2335,26 @@ pub const CodeGen = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Function pointer typed constant
|
||||
if (sx_ty.isFunctionType()) {
|
||||
const llvm_ty = c.LLVMPointerTypeInContext(self.context, 0);
|
||||
const name_z = try self.allocator.dupeZ(u8, cd.name);
|
||||
const alloca = self.buildEntryBlockAlloca(llvm_ty, name_z.ptr);
|
||||
if (cd.value.data == .unary_op and cd.value.data.unary_op.op == .xx) {
|
||||
const inner = cd.value.data.unary_op.operand;
|
||||
const val = try self.genExpr(inner);
|
||||
const src_inner_ty = self.inferType(inner);
|
||||
const converted = self.convertValue(val, src_inner_ty, sx_ty);
|
||||
_ = c.LLVMBuildStore(self.builder, converted, alloca);
|
||||
} else {
|
||||
const val = try self.genExpr(cd.value);
|
||||
_ = c.LLVMBuildStore(self.builder, val, alloca);
|
||||
}
|
||||
try self.saveShadowed(cd.name);
|
||||
try self.named_values.put(cd.name, .{ .ptr = alloca, .ty = sx_ty });
|
||||
return null;
|
||||
}
|
||||
|
||||
const enum_name: ?[]const u8 = if (sx_ty.isEnum()) sx_ty.enum_type else null;
|
||||
const init_val = if (cd.value.data == .enum_literal and enum_name != null)
|
||||
self.genEnumLiteral(cd.value.data.enum_literal.name, enum_name.?)
|
||||
@@ -2313,7 +2407,8 @@ pub const CodeGen = struct {
|
||||
// Target must be an identifier
|
||||
if (asgn.target.data != .identifier) return self.emitError("assignment target must be a variable");
|
||||
const name = asgn.target.data.identifier.name;
|
||||
const entry = self.named_values.get(name) orelse {
|
||||
const entry = self.named_values.get(name) orelse
|
||||
self.global_mutable_vars.get(name) orelse {
|
||||
return self.emitErrorFmt("undefined variable '{s}'", .{name});
|
||||
};
|
||||
|
||||
@@ -2339,6 +2434,21 @@ pub const CodeGen = struct {
|
||||
return self.emitErrorFmt("cannot assign non-type value to Type variable '{s}'", .{name});
|
||||
}
|
||||
|
||||
// Function pointer reassignment
|
||||
if (entry.ty.isFunctionType() and asgn.op == .assign) {
|
||||
if (asgn.value.data == .unary_op and asgn.value.data.unary_op.op == .xx) {
|
||||
const inner = asgn.value.data.unary_op.operand;
|
||||
const val = try self.genExpr(inner);
|
||||
const src_ty = self.inferType(inner);
|
||||
const converted = self.convertValue(val, src_ty, entry.ty);
|
||||
_ = c.LLVMBuildStore(self.builder, converted, entry.ptr);
|
||||
} else {
|
||||
const val = try self.genExpr(asgn.value);
|
||||
_ = c.LLVMBuildStore(self.builder, val, entry.ptr);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Union reassignment: s = .circle(3.14) or s = .none
|
||||
if (entry.ty.isUnion() and asgn.op == .assign) {
|
||||
const new_alloca = try self.genExprAsType(asgn.value, entry.ty);
|
||||
@@ -2556,6 +2666,25 @@ pub const CodeGen = struct {
|
||||
const llvm_ty = self.typeToLLVM(ct.ty);
|
||||
return c.LLVMBuildLoad2(self.builder, llvm_ty, ct.global, "ct_load");
|
||||
}
|
||||
// Fall back to global mutable variables (e.g. function pointers from opengl.sx)
|
||||
if (self.global_mutable_vars.get(ident.name)) |entry| {
|
||||
const llvm_ty = self.typeToLLVM(entry.ty);
|
||||
return c.LLVMBuildLoad2(self.builder, llvm_ty, entry.ptr, "global_load");
|
||||
}
|
||||
// Fall back to function name → function pointer value
|
||||
{
|
||||
const name_z = try self.allocator.dupeZ(u8, ident.name);
|
||||
var fn_val = c.LLVMGetNamedFunction(self.module, name_z.ptr);
|
||||
if (fn_val == null) {
|
||||
// Try qualified name with current namespace
|
||||
if (self.current_namespace) |ns| {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, ident.name });
|
||||
const q_z = try self.allocator.dupeZ(u8, qualified);
|
||||
fn_val = c.LLVMGetNamedFunction(self.module, q_z.ptr);
|
||||
}
|
||||
}
|
||||
if (fn_val != null) return fn_val.?;
|
||||
}
|
||||
return self.emitErrorFmt("undefined identifier '{s}'", .{ident.name});
|
||||
},
|
||||
.binary_op => |binop| {
|
||||
@@ -3115,6 +3244,11 @@ pub const CodeGen = struct {
|
||||
return self.convertValue(val, src_ty, target_ty);
|
||||
}
|
||||
|
||||
// Function pointer target: bypass narrowing check, just produce the pointer value
|
||||
if (target_ty.isFunctionType()) {
|
||||
return try self.genExpr(node);
|
||||
}
|
||||
|
||||
// String literal → pointer context: produce raw pointer directly (no {ptr, len} wrapping)
|
||||
if (node.data == .string_literal and target_ty.isPointer()) {
|
||||
const unescaped = try unescapeString(self.allocator, node.data.string_literal.raw);
|
||||
@@ -3431,6 +3565,19 @@ pub const CodeGen = struct {
|
||||
return val;
|
||||
}
|
||||
|
||||
// Int → pointer/function_type: IntToPtr (for xx cast from integer to pointer)
|
||||
if (src_ty.isInt() and (target_ty.isPointer() or target_ty.isManyPointer() or target_ty.isFunctionType())) {
|
||||
return c.LLVMBuildIntToPtr(self.builder, val, c.LLVMPointerTypeInContext(self.context, 0), "inttoptr");
|
||||
}
|
||||
|
||||
// Pointer → function_type or function_type → pointer: both are opaque pointers, no-op
|
||||
if ((src_ty.isPointer() or src_ty.isManyPointer()) and target_ty.isFunctionType()) {
|
||||
return val;
|
||||
}
|
||||
if (src_ty.isFunctionType() and (target_ty.isPointer() or target_ty.isManyPointer())) {
|
||||
return val;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -3482,6 +3629,50 @@ pub const CodeGen = struct {
|
||||
);
|
||||
}
|
||||
|
||||
fn genSin(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef {
|
||||
if (call_node.args.len != 1) return self.emitError("sin expects exactly 1 argument");
|
||||
const arg_val = try self.genExpr(call_node.args[0]);
|
||||
const arg_ty = self.inferType(call_node.args[0]);
|
||||
|
||||
const intrinsic_name: [*c]const u8 = if (std.meta.eql(arg_ty, Type.f64)) "llvm.sin.f64" else "llvm.sin.f32";
|
||||
const llvm_float_ty = if (std.meta.eql(arg_ty, Type.f64))
|
||||
c.LLVMDoubleTypeInContext(self.context)
|
||||
else
|
||||
c.LLVMFloatTypeInContext(self.context);
|
||||
|
||||
var intrinsic_fn = c.LLVMGetNamedFunction(self.module, intrinsic_name);
|
||||
if (intrinsic_fn == null) {
|
||||
var param_types = [_]c.LLVMTypeRef{llvm_float_ty};
|
||||
const fn_type = c.LLVMFunctionType(llvm_float_ty, ¶m_types, 1, 0);
|
||||
intrinsic_fn = c.LLVMAddFunction(self.module, intrinsic_name, fn_type);
|
||||
}
|
||||
|
||||
var args = [_]c.LLVMValueRef{arg_val};
|
||||
return c.LLVMBuildCall2(self.builder, c.LLVMGlobalGetValueType(intrinsic_fn.?), intrinsic_fn.?, &args, 1, "sin");
|
||||
}
|
||||
|
||||
fn genCos(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef {
|
||||
if (call_node.args.len != 1) return self.emitError("cos expects exactly 1 argument");
|
||||
const arg_val = try self.genExpr(call_node.args[0]);
|
||||
const arg_ty = self.inferType(call_node.args[0]);
|
||||
|
||||
const intrinsic_name: [*c]const u8 = if (std.meta.eql(arg_ty, Type.f64)) "llvm.cos.f64" else "llvm.cos.f32";
|
||||
const llvm_float_ty = if (std.meta.eql(arg_ty, Type.f64))
|
||||
c.LLVMDoubleTypeInContext(self.context)
|
||||
else
|
||||
c.LLVMFloatTypeInContext(self.context);
|
||||
|
||||
var intrinsic_fn = c.LLVMGetNamedFunction(self.module, intrinsic_name);
|
||||
if (intrinsic_fn == null) {
|
||||
var param_types = [_]c.LLVMTypeRef{llvm_float_ty};
|
||||
const fn_type = c.LLVMFunctionType(llvm_float_ty, ¶m_types, 1, 0);
|
||||
intrinsic_fn = c.LLVMAddFunction(self.module, intrinsic_name, fn_type);
|
||||
}
|
||||
|
||||
var args = [_]c.LLVMValueRef{arg_val};
|
||||
return c.LLVMBuildCall2(self.builder, c.LLVMGlobalGetValueType(intrinsic_fn.?), intrinsic_fn.?, &args, 1, "cos");
|
||||
}
|
||||
|
||||
fn genSizeOf(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef {
|
||||
if (call_node.args.len != 1) return self.emitError("size_of expects exactly 1 argument");
|
||||
const ty = self.resolveType(call_node.args[0]);
|
||||
@@ -4318,6 +4509,12 @@ pub const CodeGen = struct {
|
||||
if (std.mem.eql(u8, callee_name, "sqrt")) {
|
||||
return self.genSqrt(call_node);
|
||||
}
|
||||
if (std.mem.eql(u8, callee_name, "sin")) {
|
||||
return self.genSin(call_node);
|
||||
}
|
||||
if (std.mem.eql(u8, callee_name, "cos")) {
|
||||
return self.genCos(call_node);
|
||||
}
|
||||
if (std.mem.eql(u8, callee_name, "cast")) {
|
||||
return self.genCast(call_node);
|
||||
}
|
||||
@@ -4349,7 +4546,17 @@ pub const CodeGen = struct {
|
||||
callee_fn = c.LLVMGetNamedFunction(self.module, qualified_z.ptr);
|
||||
}
|
||||
}
|
||||
if (callee_fn == null) return self.emitErrorFmt("undefined function '{s}'", .{callee_name});
|
||||
// Function pointer indirect call: callee is a variable with function_type
|
||||
if (callee_fn == null) {
|
||||
const fp_entry = if (self.named_values.get(callee_name)) |e| e
|
||||
else self.global_mutable_vars.get(callee_name);
|
||||
if (fp_entry) |entry| {
|
||||
if (entry.ty.isFunctionType()) {
|
||||
return self.genIndirectCall(entry, call_node);
|
||||
}
|
||||
}
|
||||
return self.emitErrorFmt("undefined function '{s}'", .{callee_name});
|
||||
}
|
||||
|
||||
// Get function type (opaque pointers: use LLVMGlobalGetValueType)
|
||||
const fn_type = c.LLVMGlobalGetValueType(callee_fn.?);
|
||||
@@ -4511,6 +4718,48 @@ 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 = c.LLVMPointerTypeInContext(self.context, 0);
|
||||
const fn_ptr = c.LLVMBuildLoad2(self.builder, ptr_ty, entry.ptr, "fn_ptr");
|
||||
|
||||
// Build LLVM function type from FunctionTypeInfo
|
||||
var param_llvm_types: [64]c.LLVMTypeRef = undefined;
|
||||
for (fti.param_types, 0..) |pt, i| {
|
||||
param_llvm_types[i] = self.typeToLLVM(pt);
|
||||
}
|
||||
const ret_llvm = self.typeToLLVM(fti.return_type.*);
|
||||
const fn_type = c.LLVMFunctionType(
|
||||
ret_llvm,
|
||||
if (fti.param_types.len > 0) ¶m_llvm_types else null,
|
||||
@intCast(fti.param_types.len),
|
||||
0,
|
||||
);
|
||||
|
||||
// Generate arguments with type conversion
|
||||
var arg_vals = std.ArrayList(c.LLVMValueRef).empty;
|
||||
for (call_node.args, 0..) |arg, i| {
|
||||
if (i < fti.param_types.len) {
|
||||
try arg_vals.append(self.allocator, try self.genExprAsType(arg, fti.param_types[i]));
|
||||
} else {
|
||||
try arg_vals.append(self.allocator, try self.genExpr(arg));
|
||||
}
|
||||
}
|
||||
const args_slice = try arg_vals.toOwnedSlice(self.allocator);
|
||||
|
||||
const call_name: [*c]const u8 = if (ret_llvm == c.LLVMVoidTypeInContext(self.context)) "" else "calltmp";
|
||||
return c.LLVMBuildCall2(
|
||||
self.builder,
|
||||
fn_type,
|
||||
fn_ptr,
|
||||
if (args_slice.len > 0) args_slice.ptr else null,
|
||||
@intCast(args_slice.len),
|
||||
call_name,
|
||||
);
|
||||
}
|
||||
|
||||
fn genGenericCall(self: *CodeGen, qualified_name: []const u8, template: GenericTemplate, call_node: ast.Call) !c.LLVMValueRef {
|
||||
const fd = template.fd;
|
||||
|
||||
@@ -5690,6 +5939,8 @@ pub const CodeGen = struct {
|
||||
const base = if (std.mem.lastIndexOfScalar(u8, name, '.')) |idx| name[idx + 1 ..] else name;
|
||||
if (std.mem.eql(u8, base, "write")) return self.genWriteCall(call_node.args);
|
||||
if (std.mem.eql(u8, base, "sqrt")) return self.genSqrt(call_node);
|
||||
if (std.mem.eql(u8, base, "sin")) return self.genSin(call_node);
|
||||
if (std.mem.eql(u8, base, "cos")) return self.genCos(call_node);
|
||||
if (std.mem.eql(u8, base, "size_of")) return self.genSizeOf(call_node);
|
||||
if (std.mem.eql(u8, base, "cast")) return self.genCast(call_node);
|
||||
if (std.mem.eql(u8, base, "alloc")) return self.genAlloc(call_node.args);
|
||||
@@ -5871,6 +6122,7 @@ pub const CodeGen = struct {
|
||||
.identifier => |ident| {
|
||||
if (self.named_values.get(ident.name)) |entry| return entry.ty;
|
||||
if (self.comptime_globals.get(ident.name)) |ct| return ct.ty;
|
||||
if (self.global_mutable_vars.get(ident.name)) |entry| return entry.ty;
|
||||
return Type.s(64);
|
||||
},
|
||||
.if_expr => |ie| {
|
||||
@@ -5915,8 +6167,11 @@ pub const CodeGen = struct {
|
||||
}
|
||||
const callee_name = self.resolveCalleeName(call_node) orelse return Type.s(64);
|
||||
const base_name = if (std.mem.lastIndexOfScalar(u8, callee_name, '.')) |idx| callee_name[idx + 1 ..] else callee_name;
|
||||
// Built-in: sqrt returns same type as argument
|
||||
if (std.mem.eql(u8, base_name, "sqrt")) {
|
||||
// Built-in: sqrt/sin/cos returns same type as argument
|
||||
if (std.mem.eql(u8, base_name, "sqrt") or
|
||||
std.mem.eql(u8, base_name, "sin") or
|
||||
std.mem.eql(u8, base_name, "cos"))
|
||||
{
|
||||
if (call_node.args.len > 0) return self.inferType(call_node.args[0]);
|
||||
return .f32;
|
||||
}
|
||||
@@ -5985,13 +6240,19 @@ pub const CodeGen = struct {
|
||||
}
|
||||
return Type.s(64);
|
||||
}
|
||||
// Check non-generic LLVM functions
|
||||
// Check declared return types (preserves signedness)
|
||||
if (self.function_return_types.get(callee_name)) |ret_ty| return ret_ty;
|
||||
if (self.current_namespace) |ns| {
|
||||
const qualified = std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name }) catch return Type.s(64);
|
||||
if (self.function_return_types.get(qualified)) |ret_ty| return ret_ty;
|
||||
}
|
||||
// Fallback: check non-generic LLVM functions
|
||||
const callee_name_z = self.allocator.dupeZ(u8, callee_name) catch return Type.s(64);
|
||||
var callee_fn_opt = c.LLVMGetNamedFunction(self.module, callee_name_z.ptr);
|
||||
// Intra-namespace fallback
|
||||
if (callee_fn_opt == null) {
|
||||
if (self.current_namespace) |ns| {
|
||||
const q = std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name }) catch return Type.s(64);
|
||||
if (self.current_namespace) |ns2| {
|
||||
const q = std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns2, callee_name }) catch return Type.s(64);
|
||||
const qz = self.allocator.dupeZ(u8, q) catch return Type.s(64);
|
||||
callee_fn_opt = c.LLVMGetNamedFunction(self.module, qz.ptr);
|
||||
}
|
||||
@@ -6001,6 +6262,16 @@ pub const CodeGen = struct {
|
||||
const ret_llvm = c.LLVMGetReturnType(fn_type);
|
||||
return self.llvmTypeToSxType(ret_llvm);
|
||||
}
|
||||
// Check if callee is a variable with function pointer type
|
||||
{
|
||||
const fp_entry = if (self.named_values.get(callee_name)) |e| e
|
||||
else self.global_mutable_vars.get(callee_name);
|
||||
if (fp_entry) |entry| {
|
||||
if (entry.ty.isFunctionType()) {
|
||||
return entry.ty.function_type.return_type.*;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Type.s(64);
|
||||
},
|
||||
.unary_op => |unop| {
|
||||
|
||||
@@ -292,6 +292,28 @@ pub const Parser = struct {
|
||||
self.advance();
|
||||
return try self.createNode(start, .{ .type_expr = .{ .name = name, .is_generic = true } });
|
||||
}
|
||||
// Function pointer type: (ParamTypes) -> ReturnType
|
||||
if (self.current.tag == .l_paren) {
|
||||
self.advance(); // skip '('
|
||||
var param_types = std.ArrayList(*Node).empty;
|
||||
while (self.current.tag != .r_paren and self.current.tag != .eof) {
|
||||
if (param_types.items.len > 0) {
|
||||
try self.expect(.comma);
|
||||
}
|
||||
try param_types.append(self.allocator, try self.parseTypeExpr());
|
||||
}
|
||||
try self.expect(.r_paren);
|
||||
var return_type: ?*Node = null;
|
||||
if (self.current.tag == .arrow) {
|
||||
self.advance(); // skip '->'
|
||||
return_type = try self.parseTypeExpr();
|
||||
}
|
||||
return try self.createNode(start, .{ .function_type_expr = .{
|
||||
.param_types = try param_types.toOwnedSlice(self.allocator),
|
||||
.return_type = return_type,
|
||||
} });
|
||||
}
|
||||
|
||||
if (self.current.tag.isTypeKeyword() or self.current.tag == .identifier) {
|
||||
var name = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
|
||||
24
src/sema.zig
24
src/sema.zig
@@ -245,6 +245,21 @@ pub const Analyzer = struct {
|
||||
const elem_name = elem_type.displayName(self.allocator) catch return .void_type;
|
||||
return .{ .many_pointer_type = .{ .element_name = elem_name } };
|
||||
}
|
||||
// Function pointer type: (ParamTypes) -> ReturnType
|
||||
if (tn.data == .function_type_expr) {
|
||||
const fte = tn.data.function_type_expr;
|
||||
var param_types = std.ArrayList(Type).empty;
|
||||
for (fte.param_types) |pt| {
|
||||
param_types.append(self.allocator, self.resolveTypeNode(pt)) catch return .void_type;
|
||||
}
|
||||
const ret_ty = if (fte.return_type) |rt| self.resolveTypeNode(rt) else Type.void_type;
|
||||
const ret_ptr = self.allocator.create(Type) catch return .void_type;
|
||||
ret_ptr.* = ret_ty;
|
||||
return .{ .function_type = .{
|
||||
.param_types = param_types.toOwnedSlice(self.allocator) catch return .void_type,
|
||||
.return_type = ret_ptr,
|
||||
} };
|
||||
}
|
||||
// Sema does not resolve generics; codegen handles instantiation
|
||||
if (tn.data == .parameterized_type_expr) {
|
||||
return .void_type;
|
||||
@@ -320,9 +335,12 @@ pub const Analyzer = struct {
|
||||
if (self.fn_signatures.get(callee_name)) |sig| {
|
||||
return sig.return_type;
|
||||
}
|
||||
// Built-in: sqrt returns same type as argument
|
||||
// Built-in: sqrt/sin/cos returns same type as argument
|
||||
const base = if (std.mem.lastIndexOfScalar(u8, callee_name, '.')) |idx| callee_name[idx + 1 ..] else callee_name;
|
||||
if (std.mem.eql(u8, base, "sqrt")) {
|
||||
if (std.mem.eql(u8, base, "sqrt") or
|
||||
std.mem.eql(u8, base, "sin") or
|
||||
std.mem.eql(u8, base, "cos"))
|
||||
{
|
||||
if (call_node.args.len > 0) return self.inferExprType(call_node.args[0]);
|
||||
return .f32;
|
||||
}
|
||||
@@ -673,6 +691,7 @@ pub const Analyzer = struct {
|
||||
.builtin_expr,
|
||||
.foreign_expr,
|
||||
.library_decl,
|
||||
.function_type_expr,
|
||||
.import_decl,
|
||||
.array_type_expr,
|
||||
.slice_type_expr,
|
||||
@@ -929,6 +948,7 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node {
|
||||
.builtin_expr,
|
||||
.foreign_expr,
|
||||
.library_decl,
|
||||
.function_type_expr,
|
||||
.enum_decl,
|
||||
.struct_decl,
|
||||
.union_decl,
|
||||
|
||||
@@ -21,6 +21,7 @@ pub const Type = union(enum) {
|
||||
pointer_type: PointerTypeInfo,
|
||||
many_pointer_type: ManyPointerTypeInfo,
|
||||
vector_type: VectorTypeInfo,
|
||||
function_type: FunctionTypeInfo,
|
||||
any_type,
|
||||
meta_type: MetaTypeInfo,
|
||||
|
||||
@@ -36,6 +37,11 @@ pub const Type = union(enum) {
|
||||
element_name: []const u8,
|
||||
};
|
||||
|
||||
pub const FunctionTypeInfo = struct {
|
||||
param_types: []const Type,
|
||||
return_type: *const Type,
|
||||
};
|
||||
|
||||
pub const ArrayTypeInfo = struct {
|
||||
element_name: []const u8,
|
||||
length: u32,
|
||||
@@ -182,6 +188,13 @@ pub const Type = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isFunctionType(self: Type) bool {
|
||||
return switch (self) {
|
||||
.function_type => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isArray(self: Type) bool {
|
||||
return switch (self) {
|
||||
.array_type => true,
|
||||
@@ -235,6 +248,7 @@ pub const Type = union(enum) {
|
||||
.f32 => 32,
|
||||
.f64 => 64,
|
||||
.boolean => 1,
|
||||
.pointer_type, .many_pointer_type, .function_type => 64,
|
||||
else => 0,
|
||||
};
|
||||
}
|
||||
@@ -382,6 +396,20 @@ pub const Type = union(enum) {
|
||||
try buf.append(allocator, ')');
|
||||
return try buf.toOwnedSlice(allocator);
|
||||
},
|
||||
.function_type => |info| {
|
||||
var buf = std.ArrayList(u8).empty;
|
||||
try buf.append(allocator, '(');
|
||||
for (info.param_types, 0..) |pt, i| {
|
||||
if (i > 0) try buf.appendSlice(allocator, ", ");
|
||||
try buf.appendSlice(allocator, try pt.displayName(allocator));
|
||||
}
|
||||
try buf.append(allocator, ')');
|
||||
if (!std.meta.eql(info.return_type.*, Type.void_type)) {
|
||||
try buf.appendSlice(allocator, " -> ");
|
||||
try buf.appendSlice(allocator, try info.return_type.displayName(allocator));
|
||||
}
|
||||
return try buf.toOwnedSlice(allocator);
|
||||
},
|
||||
.meta_type => |info| info.name,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user