diff --git a/src/codegen.zig b/src/codegen.zig index c529cdb..46bf4a2 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -11,6 +11,7 @@ const Parser = @import("parser.zig").Parser; const errors = @import("errors.zig"); const sema = @import("sema.zig"); const comptime_mod = @import("comptime.zig"); +const unescape = @import("unescape.zig"); pub const TargetConfig = struct { /// Target triple (e.g. "aarch64-apple-darwin"). Null = host default. @@ -375,6 +376,14 @@ pub const CodeGen = struct { c.LLVMContextDispose(self.context); } + fn getStructInfo(self: *CodeGen, name: []const u8) !StructInfo { + return self.struct_types.get(name) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{name}); + } + + fn getTaggedEnumInfo(self: *CodeGen, name: []const u8) !TaggedEnumInfo { + return self.tagged_enum_types.get(name) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{name}); + } + fn emitError(self: *CodeGen, msg: []const u8) error{CodeGenError} { if (self.diagnostics) |diags| diags.add(.err, msg, self.current_span); return error.CodeGenError; @@ -621,7 +630,7 @@ pub const CodeGen = struct { .pointer_type, .many_pointer_type => c.LLVMBuildPtrToInt(self.builder, val, i64_ty, "any_ptr"), .meta_type => |mt| blk: { // Meta type: wrap raw char ptr in string slice {ptr, len} for extraction - const str_slice = self.buildStringSlice(val, mt.name.len); + const str_slice = self.buildStringSlice(val, self.constInt64(mt.name.len)); const alloca = self.buildEntryBlockAlloca(self.getStringStructType(), "any_type_tmp"); _ = c.LLVMBuildStore(self.builder, str_slice, alloca); break :blk c.LLVMBuildPtrToInt(self.builder, alloca, i64_ty, "any_type"); @@ -641,21 +650,31 @@ pub const CodeGen = struct { return self.string_struct_type.?; } - /// Build a string slice {ptr, len} from a raw pointer and a constant length. - fn buildStringSlice(self: *CodeGen, ptr: c.LLVMValueRef, len: u64) c.LLVMValueRef { + /// Build a string slice {ptr, len} from a raw pointer and a length value. + fn buildStringSlice(self: *CodeGen, ptr: c.LLVMValueRef, len_val: c.LLVMValueRef) c.LLVMValueRef { const str_ty = self.getStringStructType(); const undef = c.LLVMGetUndef(str_ty); const with_ptr = c.LLVMBuildInsertValue(self.builder, undef, ptr, 0, "str_ptr"); - const len_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), len, 0); return c.LLVMBuildInsertValue(self.builder, with_ptr, len_val, 1, "str_slice"); } - /// Build a string slice {ptr, len} from a raw pointer and a runtime length value. - fn buildStringSliceRT(self: *CodeGen, ptr: c.LLVMValueRef, len_val: c.LLVMValueRef) c.LLVMValueRef { - const str_ty = self.getStringStructType(); - const undef = c.LLVMGetUndef(str_ty); - const with_ptr = c.LLVMBuildInsertValue(self.builder, undef, ptr, 0, "str_ptr"); - return c.LLVMBuildInsertValue(self.builder, with_ptr, len_val, 1, "str_slice"); + fn constInt64(self: *CodeGen, val: u64) c.LLVMValueRef { + return c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), val, 0); + } + + fn constInt32(self: *CodeGen, val: u32) c.LLVMValueRef { + return c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), val, 0); + } + + /// Extract .len or .ptr from a fat pointer value ({ptr, len} struct). + fn extractFatPtrField(self: *CodeGen, val: c.LLVMValueRef, field: []const u8, type_name: []const u8) !c.LLVMValueRef { + if (std.mem.eql(u8, field, "len")) { + return c.LLVMBuildExtractValue(self.builder, val, 1, "len"); + } + if (std.mem.eql(u8, field, "ptr")) { + return c.LLVMBuildExtractValue(self.builder, val, 0, "ptr"); + } + return self.emitErrorFmt("no field '{s}' on {s} (available: .len, .ptr)", .{ field, type_name }); } fn pushScope(self: *CodeGen) !void { @@ -731,11 +750,11 @@ pub const CodeGen = struct { 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); + try self.registerFnDecl(fd, fd.name); } else if (fd.type_params.len > 0) { try self.generic_templates.put(fd.name, .{ .fd = fd }); } else { - try self.registerFnDecl(fd); + try self.registerFnDecl(fd, fd.name); } try self.fn_signatures.put(fd.name, self.buildFnSignature(fd)); }, @@ -880,7 +899,7 @@ pub const CodeGen = struct { if (shouldDeferFnBody(fd)) { try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = fd.name }); } else { - try self.genFnBody(fd); + try self.genFnBody(fd, fd.name); } } }, @@ -901,7 +920,7 @@ pub const CodeGen = struct { const saved_ns = self.current_namespace; self.current_namespace = deferred.namespace; defer self.current_namespace = saved_ns; - try self.genFnBodyAs(deferred.fd, deferred.name); + try self.genFnBody(deferred.fd, deferred.name); } // Execute comptime side effects via bytecode VM (e.g., #run main();) @@ -990,9 +1009,9 @@ pub const CodeGen = struct { .string_val => |v| blk: { const z = self.allocator.dupeZ(u8, v) catch unreachable; const ptr = c.LLVMBuildGlobalStringPtr(self.builder, z.ptr, "comptime_str"); - break :blk self.buildStringSlice(ptr, @intCast(v.len)); + break :blk self.buildStringSlice(ptr, self.constInt64(@intCast(v.len))); }, - .void_val => c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), + .void_val => self.constInt32(0), .pointer_val => c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)), .null_val => c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)), .struct_val, .array_val, .type_val, .function_val => unreachable, @@ -1365,11 +1384,7 @@ pub const CodeGen = struct { return null; } - fn buildFnType(self: *CodeGen, params: []const ast.Param, return_type: ?*Node, name: []const u8) !c.LLVMTypeRef { - return self.buildFnTypeEx(params, return_type, name, false); - } - - fn buildFnTypeEx(self: *CodeGen, params: []const ast.Param, return_type: ?*Node, name: []const u8, is_foreign: bool) !c.LLVMTypeRef { + fn buildFnType(self: *CodeGen, params: []const ast.Param, return_type: ?*Node, name: []const u8, is_foreign: bool) !c.LLVMTypeRef { const ret_sx_type = self.resolveType(return_type); const is_main = std.mem.eql(u8, name, "main"); const ret_llvm_type = if (is_main) @@ -1599,13 +1614,9 @@ pub const CodeGen = struct { } } - fn registerFnDecl(self: *CodeGen, fd: ast.FnDecl) !void { - return self.registerFnDeclAs(fd, fd.name); - } - - fn registerFnDeclAs(self: *CodeGen, fd: ast.FnDecl, llvm_name: []const u8) !void { + fn registerFnDecl(self: *CodeGen, fd: ast.FnDecl, llvm_name: []const u8) !void { const is_foreign = fd.body.data == .foreign_expr; - const fn_type = try self.buildFnTypeEx(fd.params, fd.return_type, fd.name, is_foreign); + const fn_type = try self.buildFnType(fd.params, fd.return_type, fd.name, is_foreign); const name_z = try self.allocator.dupeZ(u8, llvm_name); _ = c.LLVMAddFunction(self.module, name_z.ptr, fn_type); // Track foreign functions for ABI lowering at call sites @@ -1651,7 +1662,7 @@ 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 with C name only - try self.registerFnDeclAs(fd, fd.name); + try self.registerFnDecl(fd, fd.name); // Also track qualified name as foreign for ABI lowering at call sites try self.foreign_fns.put(qualified, {}); // Store param types under qualified name so call-site type resolution works @@ -1664,7 +1675,7 @@ pub const CodeGen = struct { } else if (fd.type_params.len > 0) { try self.generic_templates.put(qualified, .{ .fd = fd }); } else { - try self.registerFnDeclAs(fd, qualified); + try self.registerFnDecl(fd, qualified); } }, .enum_decl => |ed| { @@ -1730,7 +1741,7 @@ pub const CodeGen = struct { if (shouldDeferFnBody(fd)) { try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = qualified, .namespace = ns.name }); } else { - try self.genFnBodyAs(fd, qualified); + try self.genFnBody(fd, qualified); } } }, @@ -1885,11 +1896,27 @@ pub const CodeGen = struct { try self.named_values.put(name, .{ .ptr = alloca, .ty = sx_ty }); } - fn genFnBody(self: *CodeGen, fd: ast.FnDecl) !void { - return self.genFnBodyAs(fd, fd.name); + /// Prepare a return value: load structs/unions from alloca pointers, convert types. + fn prepareReturnValue(self: *CodeGen, raw_val: c.LLVMValueRef, ret_type: Type) !c.LLVMValueRef { + if (ret_type.isStruct()) { + const sname = self.type_aliases.get(ret_type.struct_type) orelse ret_type.struct_type; + const info = try self.getStructInfo(sname); + return c.LLVMBuildLoad2(self.builder, info.llvm_type, raw_val, "retval"); + } else if (ret_type.isUnion()) { + const uname = ret_type.union_type; + const resolved = self.type_aliases.get(uname) orelse uname; + const info = try self.getTaggedEnumInfo(resolved); + return if (c.LLVMGetTypeKind(c.LLVMTypeOf(raw_val)) == c.LLVMPointerTypeKind) + c.LLVMBuildLoad2(self.builder, info.llvm_type, raw_val, "retval") + else + raw_val; + } else { + const src_ty = self.llvmTypeToSxType(c.LLVMTypeOf(raw_val)); + return self.convertValue(raw_val, src_ty, ret_type); + } } - fn genFnBodyAs(self: *CodeGen, fd: ast.FnDecl, llvm_name: []const u8) !void { + fn genFnBody(self: *CodeGen, fd: ast.FnDecl, llvm_name: []const u8) !void { self.named_values.clearRetainingCapacity(); const ret_sx_type = self.resolveType(fd.return_type); @@ -1944,27 +1971,8 @@ pub const CodeGen = struct { if (ret_sx_type == .void_type and !is_main) { _ = c.LLVMBuildRetVoid(self.builder); } else if (effective_last_val) |val| { - if (ret_sx_type.isStruct()) { - // Struct implicit return: val is an alloca pointer, load the value - const sname = ret_sx_type.struct_type; - const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); - const loaded = c.LLVMBuildLoad2(self.builder, info.llvm_type, val, "retval"); - _ = c.LLVMBuildRet(self.builder, loaded); - } else if (ret_sx_type.isUnion()) { - // Tagged enum implicit return: val may be alloca or loaded value - const uname = ret_sx_type.union_type; - const resolved = self.type_aliases.get(uname) orelse uname; - const info = self.tagged_enum_types.get(resolved) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{resolved}); - const ret_val2 = if (c.LLVMGetTypeKind(c.LLVMTypeOf(val)) == c.LLVMPointerTypeKind) - c.LLVMBuildLoad2(self.builder, info.llvm_type, val, "retval") - else - val; - _ = c.LLVMBuildRet(self.builder, ret_val2); - } else { - const src_ty = self.llvmTypeToSxType(c.LLVMTypeOf(val)); - const ret_val = self.convertValue(val, src_ty, self.current_return_type); - _ = c.LLVMBuildRet(self.builder, ret_val); - } + const ret_val = try self.prepareReturnValue(val, ret_sx_type); + _ = c.LLVMBuildRet(self.builder, ret_val); } else if (is_main) { _ = c.LLVMBuildRet(self.builder, c.LLVMConstInt(ret_llvm_type, 0, 0)); } else if (ret_sx_type != .void_type) { @@ -2053,7 +2061,7 @@ pub const CodeGen = struct { self.named_values = std.StringHashMap(NamedValue).init(self.allocator); // Register with correct types (null return_type = void) - try self.registerFnDeclAs(fd, fd.name); + try self.registerFnDecl(fd, fd.name); // Generate body inline const ret_sx_type = self.resolveType(fd.return_type); @@ -2115,30 +2123,9 @@ pub const CodeGen = struct { // Evaluate return value first, then emit all defers, then return if (rs.value) |val_node| { const raw_val = try self.genExpr(val_node); - if (self.current_return_type.isStruct()) { - // Struct return: raw_val is an alloca pointer, load the value - const sname = self.current_return_type.struct_type; - const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); - const loaded = c.LLVMBuildLoad2(self.builder, info.llvm_type, raw_val, "retval"); - try self.emitAllDefers(); - _ = c.LLVMBuildRet(self.builder, loaded); - } else if (self.current_return_type.isUnion()) { - // Tagged enum return: raw_val may be alloca (enum literal) or loaded value (identifier/call) - const uname = self.current_return_type.union_type; - const resolved = self.type_aliases.get(uname) orelse uname; - const info = self.tagged_enum_types.get(resolved) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{resolved}); - const ret_val = if (c.LLVMGetTypeKind(c.LLVMTypeOf(raw_val)) == c.LLVMPointerTypeKind) - c.LLVMBuildLoad2(self.builder, info.llvm_type, raw_val, "retval") - else - raw_val; - try self.emitAllDefers(); - _ = c.LLVMBuildRet(self.builder, ret_val); - } else { - const src_ty = self.llvmTypeToSxType(c.LLVMTypeOf(raw_val)); - const val = self.convertValue(raw_val, src_ty, self.current_return_type); - try self.emitAllDefers(); - _ = c.LLVMBuildRet(self.builder, val); - } + const ret_val = try self.prepareReturnValue(raw_val, self.current_return_type); + try self.emitAllDefers(); + _ = c.LLVMBuildRet(self.builder, ret_val); } else { try self.emitAllDefers(); _ = c.LLVMBuildRetVoid(self.builder); @@ -2225,7 +2212,7 @@ pub const CodeGen = struct { // Resolve type aliases (e.g. Vec3 -> Vec__3_f32) const sname = self.type_aliases.get(sx_ty.struct_type) orelse sx_ty.struct_type; sx_ty = .{ .struct_type = sname }; - const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); + const info = try self.getStructInfo(sname); const name_z = try self.allocator.dupeZ(u8, vd.name); const alloca = self.buildEntryBlockAlloca(info.llvm_type, name_z.ptr); @@ -2280,7 +2267,7 @@ pub const CodeGen = struct { } // Tagged enum - const info = self.tagged_enum_types.get(uname) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{uname}); + const info = try self.getTaggedEnumInfo(uname); const name_z = try self.allocator.dupeZ(u8, vd.name); const alloca = self.buildEntryBlockAlloca(info.llvm_type, name_z.ptr); @@ -2339,8 +2326,8 @@ pub const CodeGen = struct { for (0..len) |i| { const val = try self.genExprAsType(al.elements[i], elem_sx_ty); var indices = [_]c.LLVMValueRef{ - c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), - c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(i), 0), + self.constInt32(0), + self.constInt32(@intCast(i)), }; const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &indices, 2, "arr_elem"); _ = c.LLVMBuildStore(self.builder, val, gep); @@ -2348,8 +2335,8 @@ pub const CodeGen = struct { // Zero-init remaining elements for (len..arr_info.length) |i| { var indices = [_]c.LLVMValueRef{ - c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), - c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(i), 0), + self.constInt32(0), + self.constInt32(@intCast(i)), }; const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &indices, 2, "arr_elem"); _ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(elem_llvm_ty), gep); @@ -2656,8 +2643,8 @@ pub const CodeGen = struct { return self.emitError("unknown pointee type for field assignment"); if (pointee_ty.isStruct()) { const sname = pointee_ty.struct_type; - const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); - const fi = self.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); + const info = try self.getStructInfo(sname); + const fi = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); const field_ty = info.field_types[fi]; const loaded_ptr = c.LLVMBuildLoad2(self.builder, c.LLVMPointerTypeInContext(self.context, 0), entry.ptr, "ptr_load"); @@ -2679,7 +2666,7 @@ pub const CodeGen = struct { if (entry.ty.isUnion()) { const uname = entry.ty.union_type; if (self.union_types.get(uname)) |info| { - if (self.findUnionFieldIndex(info, fa.field)) |fidx| { + if (self.findNameIndex(info.field_names, fa.field)) |fidx| { const field_ty = info.field_types[fidx]; const rhs = try self.genExprAsType(asgn.value, field_ty); if (asgn.op == .assign) { @@ -2714,8 +2701,8 @@ pub const CodeGen = struct { if (!entry.ty.isStruct()) return self.emitErrorFmt("field access on non-struct variable '{s}'", .{obj_name}); const sname = entry.ty.struct_type; - const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); - const fi = self.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); + const info = try self.getStructInfo(sname); + const fi = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); const field_ty = info.field_types[fi]; const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, @intCast(fi), "fassign"); @@ -2754,7 +2741,7 @@ pub const CodeGen = struct { if (self.named_values.get(ie.object.data.identifier.name)) |entry| { const idx = try self.genExpr(ie.index); const val = try self.genExpr(asgn.value); - const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); + const zero = self.constInt32(0); var indices = [_]c.LLVMValueRef{ zero, idx }; const gep_ptr = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), entry.ptr, &indices, 2, "arridx"); _ = c.LLVMBuildStore(self.builder, val, gep_ptr); @@ -2766,7 +2753,7 @@ pub const CodeGen = struct { const field_ptr = try self.genAddressOf(ie.object); const idx = try self.genExpr(ie.index); const val = try self.genExpr(asgn.value); - const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); + const zero = self.constInt32(0); var indices = [_]c.LLVMValueRef{ zero, idx }; const gep_ptr = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), field_ptr, &indices, 2, "field_arridx"); _ = c.LLVMBuildStore(self.builder, val, gep_ptr); @@ -2809,50 +2796,6 @@ pub const CodeGen = struct { return self.emitError("index assignment requires a string, array, slice, or [*] pointer target"); } - fn unescapeString(allocator: std.mem.Allocator, raw: []const u8) ![]u8 { - var result = try allocator.alloc(u8, raw.len); - var i: usize = 0; - var j: usize = 0; - while (i < raw.len) { - if (raw[i] == '\\' and i + 1 < raw.len) { - i += 1; - switch (raw[i]) { - 'n' => { - result[j] = '\n'; - }, - 't' => { - result[j] = '\t'; - }, - 'r' => { - result[j] = '\r'; - }, - '\\' => { - result[j] = '\\'; - }, - '"' => { - result[j] = '"'; - }, - '0' => { - result[j] = 0; - }, - '`' => { - result[j] = '`'; - }, - else => { - result[j] = raw[i]; - }, - } - j += 1; - i += 1; - } else { - result[j] = raw[i]; - j += 1; - i += 1; - } - } - return result[0..j]; - } - fn genExpr(self: *CodeGen, node: *Node) anyerror!c.LLVMValueRef { self.current_span = node.span; switch (node.data) { @@ -2869,10 +2812,10 @@ pub const CodeGen = struct { return c.LLVMConstInt(i1_type, if (lit.value) 1 else 0, 0); }, .string_literal => |lit| { - const content = if (lit.is_raw) lit.raw else try unescapeString(self.allocator, lit.raw); + const content = if (lit.is_raw) lit.raw else try unescape.unescapeString(self.allocator, lit.raw); const str_z = try self.allocator.dupeZ(u8, content); const ptr = c.LLVMBuildGlobalStringPtr(self.builder, str_z.ptr, "str"); - return self.buildStringSlice(ptr, @intCast(content.len)); + return self.buildStringSlice(ptr, self.constInt64(@intCast(content.len))); }, .identifier => |ident| { if (self.named_values.get(ident.name)) |entry| { @@ -2909,8 +2852,8 @@ pub const CodeGen = struct { return self.emitErrorFmt("undefined identifier '{s}'", .{ident.name}); }, .binary_op => |binop| { - if (binop.op == .and_op) return self.genAndOp(binop); - if (binop.op == .or_op) return self.genOrOp(binop); + if (binop.op == .and_op) return self.genShortCircuitOp(binop, true); + if (binop.op == .or_op) return self.genShortCircuitOp(binop, false); const lhs_ty = self.inferType(binop.lhs); const rhs_ty = self.inferType(binop.rhs); const result_type = Type.widen(lhs_ty, rhs_ty); @@ -3066,29 +3009,9 @@ pub const CodeGen = struct { .return_stmt => |rs| { if (rs.value) |val_node| { const raw_val = try self.genExpr(val_node); - if (self.current_return_type.isStruct()) { - const sname = self.current_return_type.struct_type; - const resolved = self.type_aliases.get(sname) orelse sname; - const sinfo = self.struct_types.get(resolved) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{resolved}); - const loaded = c.LLVMBuildLoad2(self.builder, sinfo.llvm_type, raw_val, "retval"); - try self.emitAllDefers(); - _ = c.LLVMBuildRet(self.builder, loaded); - } else if (self.current_return_type.isUnion()) { - const uname = self.current_return_type.union_type; - const resolved = self.type_aliases.get(uname) orelse uname; - const info = self.tagged_enum_types.get(resolved) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{resolved}); - const ret_val = if (c.LLVMGetTypeKind(c.LLVMTypeOf(raw_val)) == c.LLVMPointerTypeKind) - c.LLVMBuildLoad2(self.builder, info.llvm_type, raw_val, "retval") - else - raw_val; - try self.emitAllDefers(); - _ = c.LLVMBuildRet(self.builder, ret_val); - } else { - const src_ty = self.llvmTypeToSxType(c.LLVMTypeOf(raw_val)); - const val = self.convertValue(raw_val, src_ty, self.current_return_type); - try self.emitAllDefers(); - _ = c.LLVMBuildRet(self.builder, val); - } + const ret_val = try self.prepareReturnValue(raw_val, self.current_return_type); + try self.emitAllDefers(); + _ = c.LLVMBuildRet(self.builder, ret_val); } else { try self.emitAllDefers(); _ = c.LLVMBuildRetVoid(self.builder); @@ -3133,7 +3056,7 @@ pub const CodeGen = struct { if (obj_ty.isArray()) { if (ie.object.data == .identifier) { if (self.named_values.get(ie.object.data.identifier.name)) |entry| { - const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); + const zero = self.constInt32(0); var indices = [_]c.LLVMValueRef{ zero, idx }; return c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), entry.ptr, &indices, 2, "addr_elem"); } @@ -3157,14 +3080,14 @@ pub const CodeGen = struct { if (self.named_values.get(fa.object.data.identifier.name)) |entry| { if (entry.ty.isStruct()) { const sname = entry.ty.struct_type; - const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); - const idx = self.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); + const info = try self.getStructInfo(sname); + const idx = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); return c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, @intCast(idx), "addr_field"); } // &u.field where u is a C-style union — all fields at offset 0 if (entry.ty.isUnion()) { if (self.union_types.get(entry.ty.union_type)) |info| { - if (self.findUnionFieldIndex(info, fa.field) != null) { + if (self.findNameIndex(info.field_names, fa.field) != null) { return entry.ptr; } if (info.promoted_fields.get(fa.field)) |pf| { @@ -3180,7 +3103,7 @@ pub const CodeGen = struct { if (self.struct_types.get(pointee_name)) |info| { const loaded_ptr = c.LLVMBuildLoad2(self.builder, c.LLVMPointerTypeInContext(self.context, 0), entry.ptr, "ptr_load"); - const idx = self.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, pointee_name }); + const idx = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, pointee_name }); return c.LLVMBuildStructGEP2(self.builder, info.llvm_type, loaded_ptr, @intCast(idx), "addr_pfield"); } } @@ -3571,7 +3494,7 @@ pub const CodeGen = struct { (if (self.current_return_type.isUnion()) self.current_return_type.union_type else null) orelse return self.emitError("cannot infer enum type for literal"); const resolved_name = self.type_aliases.get(uname) orelse uname; - const info = self.tagged_enum_types.get(resolved_name) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{resolved_name}); + const info = try self.getTaggedEnumInfo(resolved_name); // Find variant index var variant_idx: ?u32 = null; @@ -3616,7 +3539,7 @@ pub const CodeGen = struct { }; // Resolve type aliases (e.g. Vec3 -> Vec__3_f32) const sname = self.type_aliases.get(raw_name) orelse raw_name; - const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); + const info = try self.getStructInfo(sname); // Alloca the struct and default-init all fields (zero or declared defaults) const name_z = try self.allocator.dupeZ(u8, sname); @@ -3638,7 +3561,7 @@ pub const CodeGen = struct { const fname = fi.name orelse { // Positional field mixed with named — treat as identifier shorthand if (fi.value.data == .identifier) { - const idx = self.findFieldIndex(info, fi.value.data.identifier.name) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fi.value.data.identifier.name, sname }); + const idx = self.findNameIndex(info.field_names, fi.value.data.identifier.name) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fi.value.data.identifier.name, sname }); const val = try self.genExprAsType(fi.value, info.field_types[idx]); const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, @intCast(idx), "field"); _ = c.LLVMBuildStore(self.builder, val, gep); @@ -3646,7 +3569,7 @@ pub const CodeGen = struct { } return self.emitError("mixed positional and named fields in struct literal"); }; - const idx = self.findFieldIndex(info, fname) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fname, sname }); + const idx = self.findNameIndex(info.field_names, fname) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fname, sname }); const val = try self.genExprAsType(fi.value, info.field_types[idx]); const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, @intCast(idx), "field"); _ = c.LLVMBuildStore(self.builder, val, gep); @@ -3684,8 +3607,8 @@ pub const CodeGen = struct { for (0..len) |i| { const val = try self.genExprAsType(al.elements[i], elem_sx_ty); var indices = [_]c.LLVMValueRef{ - c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), - c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(i), 0), + self.constInt32(0), + self.constInt32(@intCast(i)), }; const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, alloca, &indices, 2, "arr_elem"); _ = c.LLVMBuildStore(self.builder, val, gep); @@ -3707,8 +3630,8 @@ pub const CodeGen = struct { for (0..n) |i| { const val = try self.genExprAsType(al.elements[i], elem_sx_ty); var indices = [_]c.LLVMValueRef{ - c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), - c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(i), 0), + self.constInt32(0), + self.constInt32(@intCast(i)), }; const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &indices, 2, "slice_elem"); _ = c.LLVMBuildStore(self.builder, val, gep); @@ -3736,7 +3659,7 @@ pub const CodeGen = struct { const len = @min(al.elements.len, vec_info.length); for (0..len) |i| { const elem_val = try self.genExprAsType(al.elements[i], elem_sx_ty); - const idx = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(i), 0); + const idx = self.constInt32(@intCast(i)); vec_val = c.LLVMBuildInsertElement(self.builder, vec_val, elem_val, idx, "vec_ins"); } return vec_val; @@ -3747,7 +3670,7 @@ pub const CodeGen = struct { const llvm_vec_ty = self.typeToLLVM(vec_ty); // Insert scalar at index 0 of undef vector var vec = c.LLVMGetUndef(llvm_vec_ty); - const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); + const zero = self.constInt32(0); vec = c.LLVMBuildInsertElement(self.builder, vec, scalar, zero, "splat_ins"); // Shuffle with zeroinitializer mask to broadcast element 0 to all lanes const mask_ty = c.LLVMVectorType(c.LLVMInt32TypeInContext(self.context), vec_info.length); @@ -3773,7 +3696,7 @@ pub const CodeGen = struct { // String literal → pointer context: produce raw pointer directly (no {ptr, len} wrapping) if (node.data == .string_literal and target_ty.isPointer()) { const lit = node.data.string_literal; - const content = if (lit.is_raw) lit.raw else try unescapeString(self.allocator, lit.raw); + const content = if (lit.is_raw) lit.raw else try unescape.unescapeString(self.allocator, lit.raw); const str_z = try self.allocator.dupeZ(u8, content); return c.LLVMBuildGlobalStringPtr(self.builder, str_z.ptr, "str"); } @@ -4197,16 +4120,9 @@ pub const CodeGen = struct { return val; } - fn findFieldIndex(_: *CodeGen, info: StructInfo, name: []const u8) ?usize { - for (info.field_names, 0..) |fn_name, i| { - if (std.mem.eql(u8, fn_name, name)) return i; - } - return null; - } - - fn findUnionFieldIndex(_: *CodeGen, info: UnionInfo, name: []const u8) ?usize { - for (info.field_names, 0..) |fn_name, i| { - if (std.mem.eql(u8, fn_name, name)) return i; + fn findNameIndex(_: *CodeGen, names: []const []const u8, name: []const u8) ?usize { + for (names, 0..) |n, i| { + if (std.mem.eql(u8, n, name)) return i; } return null; } @@ -4221,47 +4137,14 @@ pub const CodeGen = struct { }; } - fn genSqrt(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef { - if (call_node.args.len != 1) return self.emitError("sqrt expects exactly 1 argument"); + fn genMathIntrinsic(self: *CodeGen, call_node: ast.Call, comptime name: []const u8) !c.LLVMValueRef { + if (call_node.args.len != 1) return self.emitError(name ++ " expects exactly 1 argument"); const arg_val = try self.genExpr(call_node.args[0]); const arg_ty = self.inferType(call_node.args[0]); - // Pick the right LLVM intrinsic based on float type - const intrinsic_name: [*c]const u8 = if (std.meta.eql(arg_ty, Type.f64)) "llvm.sqrt.f64" else "llvm.sqrt.f32"; - const llvm_float_ty = if (std.meta.eql(arg_ty, Type.f64)) - c.LLVMDoubleTypeInContext(self.context) - else - c.LLVMFloatTypeInContext(self.context); - - // Get or declare the intrinsic - 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, - "sqrt", - ); - } - - 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); + const is_f64 = std.meta.eql(arg_ty, Type.f64); + const intrinsic_name: [*c]const u8 = if (is_f64) "llvm." ++ name ++ ".f64" else "llvm." ++ name ++ ".f32"; + const llvm_float_ty = if (is_f64) c.LLVMDoubleTypeInContext(self.context) else c.LLVMFloatTypeInContext(self.context); var intrinsic_fn = c.LLVMGetNamedFunction(self.module, intrinsic_name); if (intrinsic_fn == null) { @@ -4271,29 +4154,7 @@ pub const CodeGen = struct { } 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"); + return c.LLVMBuildCall2(self.builder, c.LLVMGlobalGetValueType(intrinsic_fn.?), intrinsic_fn.?, &args, 1, name.ptr); } fn genSizeOf(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef { @@ -4413,7 +4274,7 @@ pub const CodeGen = struct { // GEP into the array with runtime index const idx = try self.genExpr(call_node.args[1]); - const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); + const zero = self.constInt32(0); var indices = [_]c.LLVMValueRef{ zero, idx }; const elem_ptr = c.LLVMBuildGEP2(self.builder, arr_ty, global, &indices, 2, "field_name_ptr"); return c.LLVMBuildLoad2(self.builder, str_ty, elem_ptr, "field_name"); @@ -4525,7 +4386,7 @@ pub const CodeGen = struct { _ = c.LLVMBuildStore(self.builder, val, arr_alloca); const idx = try self.genExpr(call_node.args[1]); var gep_indices = [_]c.LLVMValueRef{ - c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), + self.constInt32(0), idx, }; const elem_ptr = c.LLVMBuildGEP2(self.builder, arr_llvm_ty, arr_alloca, &gep_indices, 2, "fv_aelem"); @@ -4731,7 +4592,7 @@ pub const CodeGen = struct { var calloc_args = [_]c.LLVMValueRef{ size_plus_one, one_i64 }; const ptr = c.LLVMBuildCall2(self.builder, calloc_ty, calloc_fn, &calloc_args, 2, "alloc_ptr"); // Build string slice: {ptr, size} - return self.buildStringSliceRT(ptr, size_val); + return self.buildStringSlice(ptr, size_val); } fn genMalloc(self: *CodeGen, args: []const *Node) !c.LLVMValueRef { @@ -4768,7 +4629,7 @@ pub const CodeGen = struct { fn genVectorExtract(self: *CodeGen, vec_val: c.LLVMValueRef, field: []const u8) !c.LLVMValueRef { if (field.len == 1) { const idx_val = componentToIndex(field[0]) orelse return self.emitErrorFmt("invalid vector component '{c}'", .{field[0]}); - const idx = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), idx_val, 0); + const idx = self.constInt32(idx_val); return c.LLVMBuildExtractElement(self.builder, vec_val, idx, "comp"); } return self.emitErrorFmt("unsupported vector swizzle '{s}'", .{field}); @@ -4788,7 +4649,7 @@ pub const CodeGen = struct { const sname = pointee_ty.struct_type; const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); - const idx = self.findFieldIndex(info, fa.field) orelse + const idx = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, loaded_ptr, @intCast(idx), "pfield"); @@ -4796,20 +4657,14 @@ pub const CodeGen = struct { } if (pointee_ty.isSlice()) { const slice_val = c.LLVMBuildLoad2(self.builder, self.getStringStructType(), loaded_ptr, "pslice_load"); - if (std.mem.eql(u8, fa.field, "len")) { - return c.LLVMBuildExtractValue(self.builder, slice_val, 1, "pslice_len"); - } - if (std.mem.eql(u8, fa.field, "ptr")) { - return c.LLVMBuildExtractValue(self.builder, slice_val, 0, "pslice_ptr"); - } - return self.emitErrorFmt("no field '{s}' on *slice (available: .len, .ptr)", .{fa.field}); + return self.extractFatPtrField(slice_val, fa.field, "*slice"); } return self.emitErrorFmt("no field '{s}' on pointer", .{fa.field}); } if (entry.ty.isStruct()) { const sname = entry.ty.struct_type; - const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); - const idx = self.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); + const info = try self.getStructInfo(sname); + const idx = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, @intCast(idx), "field"); return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(info.field_types[idx]), gep, "fieldval"); } @@ -4817,7 +4672,7 @@ pub const CodeGen = struct { const uname = entry.ty.union_type; // C-style (untagged) union: bitcast pointer and load if (self.union_types.get(uname)) |info| { - if (self.findUnionFieldIndex(info, fa.field)) |fidx| { + if (self.findNameIndex(info.field_names, fa.field)) |fidx| { const field_ty = info.field_types[fidx]; return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(field_ty), entry.ptr, "union_field"); } @@ -4832,7 +4687,7 @@ pub const CodeGen = struct { return self.emitErrorFmt("no field '{s}' in union '{s}'", .{ fa.field, uname }); } // Tagged enum: GEP to payload area - const info = self.tagged_enum_types.get(uname) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{uname}); + const info = try self.getTaggedEnumInfo(uname); // Find variant by name to determine payload type var vidx: ?usize = null; for (info.variant_names, 0..) |vn, i| { @@ -4854,23 +4709,11 @@ pub const CodeGen = struct { } if (entry.ty == .string_type) { const str_val = c.LLVMBuildLoad2(self.builder, self.getStringStructType(), entry.ptr, "str_load"); - if (std.mem.eql(u8, fa.field, "len")) { - return c.LLVMBuildExtractValue(self.builder, str_val, 1, "str_len"); - } - if (std.mem.eql(u8, fa.field, "ptr")) { - return c.LLVMBuildExtractValue(self.builder, str_val, 0, "str_ptr"); - } - return self.emitErrorFmt("no field '{s}' on string (available: .len, .ptr)", .{fa.field}); + return self.extractFatPtrField(str_val, fa.field, "string"); } if (entry.ty.isSlice()) { const slice_val = c.LLVMBuildLoad2(self.builder, self.getStringStructType(), entry.ptr, "slice_load"); - if (std.mem.eql(u8, fa.field, "len")) { - return c.LLVMBuildExtractValue(self.builder, slice_val, 1, "slice_len"); - } - if (std.mem.eql(u8, fa.field, "ptr")) { - return c.LLVMBuildExtractValue(self.builder, slice_val, 0, "slice_ptr"); - } - return self.emitErrorFmt("no field '{s}' on slice (available: .len, .ptr)", .{fa.field}); + return self.extractFatPtrField(slice_val, fa.field, "slice"); } if (entry.ty.isArray()) { if (std.mem.eql(u8, fa.field, "len")) { @@ -4897,13 +4740,7 @@ pub const CodeGen = struct { return self.genVectorExtract(obj_val, fa.field); } if (obj_ty == .string_type) { - if (std.mem.eql(u8, fa.field, "len")) { - return c.LLVMBuildExtractValue(self.builder, obj_val, 1, "str_len"); - } - if (std.mem.eql(u8, fa.field, "ptr")) { - return c.LLVMBuildExtractValue(self.builder, obj_val, 0, "str_ptr"); - } - return self.emitErrorFmt("no field '{s}' on string (available: .len, .ptr)", .{fa.field}); + return self.extractFatPtrField(obj_val, fa.field, "string"); } return self.emitError("field access on non-struct/non-vector expression"); } @@ -4915,9 +4752,9 @@ pub const CodeGen = struct { else (if (op == .eq) c.LLVMBuildICmp(self.builder, c.LLVMIntEQ, lhs, rhs, "vcmp") else c.LLVMBuildICmp(self.builder, c.LLVMIntNE, lhs, rhs, "vcmp")); // Reduce: extract each i1 and AND (eq) or OR (neq) - var result = c.LLVMBuildExtractElement(self.builder, cmp, c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), "cmp0"); + var result = c.LLVMBuildExtractElement(self.builder, cmp, self.constInt32(0), "cmp0"); for (1..vec_info.length) |i| { - const elem = c.LLVMBuildExtractElement(self.builder, cmp, c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(i), 0), "cmpi"); + const elem = c.LLVMBuildExtractElement(self.builder, cmp, self.constInt32(@intCast(i)), "cmpi"); result = if (op == .eq) c.LLVMBuildAnd(self.builder, result, elem, "andcmp") else @@ -4940,7 +4777,7 @@ pub const CodeGen = struct { if (ie.object.data == .identifier) { if (self.named_values.get(ie.object.data.identifier.name)) |entry| { const idx = try self.genExpr(ie.index); - const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); + const zero = self.constInt32(0); var indices = [_]c.LLVMValueRef{ zero, idx }; const gep = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), entry.ptr, &indices, 2, "arridx"); return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(elem_ty), gep, "arrval"); @@ -4950,7 +4787,7 @@ pub const CodeGen = struct { if (ie.object.data == .field_access) { const field_ptr = try self.genAddressOf(ie.object); const idx = try self.genExpr(ie.index); - const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); + const zero = self.constInt32(0); var indices = [_]c.LLVMValueRef{ zero, idx }; const gep = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), field_ptr, &indices, 2, "field_arridx"); return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(elem_ty), gep, "field_arrval"); @@ -5104,7 +4941,7 @@ pub const CodeGen = struct { }; } - fn genAndOp(self: *CodeGen, binop: ast.BinaryOp) !c.LLVMValueRef { + fn genShortCircuitOp(self: *CodeGen, binop: ast.BinaryOp, is_and: bool) !c.LLVMValueRef { const function = self.current_function; const i1_type = c.LLVMInt1TypeInContext(self.context); @@ -5114,10 +4951,17 @@ pub const CodeGen = struct { } const lhs_bb = c.LLVMGetInsertBlock(self.builder); - const rhs_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "and.rhs"); - const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "and.merge"); + const rhs_label: [*c]const u8 = if (is_and) "and.rhs" else "or.rhs"; + const merge_label: [*c]const u8 = if (is_and) "and.merge" else "or.merge"; + const rhs_bb = c.LLVMAppendBasicBlockInContext(self.context, function, rhs_label); + const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, function, merge_label); - _ = c.LLVMBuildCondBr(self.builder, lhs_val, rhs_bb, merge_bb); + // AND: true → evaluate rhs, false → short-circuit to merge + // OR: true → short-circuit to merge, false → evaluate rhs + if (is_and) + _ = c.LLVMBuildCondBr(self.builder, lhs_val, rhs_bb, merge_bb) + else + _ = c.LLVMBuildCondBr(self.builder, lhs_val, merge_bb, rhs_bb); c.LLVMPositionBuilderAtEnd(self.builder, rhs_bb); var rhs_val = try self.genExpr(binop.rhs); @@ -5128,40 +4972,10 @@ pub const CodeGen = struct { _ = c.LLVMBuildBr(self.builder, merge_bb); c.LLVMPositionBuilderAtEnd(self.builder, merge_bb); - const phi = c.LLVMBuildPhi(self.builder, i1_type, "and.result"); - var vals = [2]c.LLVMValueRef{ c.LLVMConstInt(i1_type, 0, 0), rhs_val }; - var blocks = [2]c.LLVMBasicBlockRef{ lhs_bb, rhs_end_bb }; - c.LLVMAddIncoming(phi, &vals, &blocks, 2); - - return phi; - } - - fn genOrOp(self: *CodeGen, binop: ast.BinaryOp) !c.LLVMValueRef { - const function = self.current_function; - const i1_type = c.LLVMInt1TypeInContext(self.context); - - var lhs_val = try self.genExpr(binop.lhs); - if (c.LLVMTypeOf(lhs_val) != i1_type) { - lhs_val = c.LLVMBuildICmp(self.builder, c.LLVMIntNE, lhs_val, c.LLVMConstInt(c.LLVMTypeOf(lhs_val), 0, 0), "tobool"); - } - const lhs_bb = c.LLVMGetInsertBlock(self.builder); - - const rhs_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "or.rhs"); - const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "or.merge"); - - _ = c.LLVMBuildCondBr(self.builder, lhs_val, merge_bb, rhs_bb); - - c.LLVMPositionBuilderAtEnd(self.builder, rhs_bb); - var rhs_val = try self.genExpr(binop.rhs); - if (c.LLVMTypeOf(rhs_val) != i1_type) { - rhs_val = c.LLVMBuildICmp(self.builder, c.LLVMIntNE, rhs_val, c.LLVMConstInt(c.LLVMTypeOf(rhs_val), 0, 0), "tobool"); - } - const rhs_end_bb = c.LLVMGetInsertBlock(self.builder); - _ = c.LLVMBuildBr(self.builder, merge_bb); - - c.LLVMPositionBuilderAtEnd(self.builder, merge_bb); - const phi = c.LLVMBuildPhi(self.builder, i1_type, "or.result"); - var vals = [2]c.LLVMValueRef{ c.LLVMConstInt(i1_type, 1, 0), rhs_val }; + const short_circuit_val: u64 = if (is_and) 0 else 1; + const result_label: [*c]const u8 = if (is_and) "and.result" else "or.result"; + const phi = c.LLVMBuildPhi(self.builder, i1_type, result_label); + var vals = [2]c.LLVMValueRef{ c.LLVMConstInt(i1_type, short_circuit_val, 0), rhs_val }; var blocks = [2]c.LLVMBasicBlockRef{ lhs_bb, rhs_end_bb }; c.LLVMAddIncoming(phi, &vals, &blocks, 2); @@ -5285,13 +5099,13 @@ pub const CodeGen = struct { // Compiler intrinsics (always available, no #builtin declaration needed) if (std.mem.eql(u8, callee_name, "sqrt")) { - return self.genSqrt(call_node); + return self.genMathIntrinsic(call_node, "sqrt"); } if (std.mem.eql(u8, callee_name, "sin")) { - return self.genSin(call_node); + return self.genMathIntrinsic(call_node, "sin"); } if (std.mem.eql(u8, callee_name, "cos")) { - return self.genCos(call_node); + return self.genMathIntrinsic(call_node, "cos"); } if (std.mem.eql(u8, callee_name, "cast")) { return self.genCast(call_node); @@ -5387,7 +5201,7 @@ pub const CodeGen = struct { var ptr_indices = [_]c.LLVMValueRef{ zero, zero }; const arr_ptr = c.LLVMBuildGEP2(self.builder, arr_llvm_ty, entry.ptr, &ptr_indices, 2, "spread_ptr"); const len_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), arr_info.length, 0); - const slice_val = self.buildStringSliceRT(arr_ptr, len_val); + const slice_val = self.buildStringSlice(arr_ptr, len_val); try arg_vals.append(self.allocator, slice_val); } else { return self.emitError("spread operand not found"); @@ -5414,8 +5228,8 @@ pub const CodeGen = struct { const arg_ty = self.inferType(call_node.args[fixed_count + vi_idx]); break :blk try self.buildAnyValue(raw_val, arg_ty); } else try self.genExprAsType(call_node.args[fixed_count + vi_idx], elem_ty); - const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); - const idx_val = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(vi_idx), 0); + const zero = self.constInt32(0); + const idx_val = self.constInt32(@intCast(vi_idx)); var indices = [_]c.LLVMValueRef{ zero, idx_val }; const gep = c.LLVMBuildGEP2(self.builder, arr_ty, arr_alloca, &indices, 2, "vararg_elem"); _ = c.LLVMBuildStore(self.builder, arg_val, gep); @@ -5425,13 +5239,13 @@ pub const CodeGen = struct { var ptr_indices = [_]c.LLVMValueRef{ zero, zero }; const arr_ptr = c.LLVMBuildGEP2(self.builder, arr_ty, arr_alloca, &ptr_indices, 2, "varargs_ptr"); const len_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), @intCast(var_arg_count), 0); - const slice_val = self.buildStringSliceRT(arr_ptr, len_val); + const slice_val = self.buildStringSlice(arr_ptr, len_val); try arg_vals.append(self.allocator, slice_val); } else { // Zero variadic args: pass empty slice {null, 0} const null_ptr = c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)); const zero_len = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0); - const slice_val = self.buildStringSliceRT(null_ptr, zero_len); + const slice_val = self.buildStringSlice(null_ptr, zero_len); try arg_vals.append(self.allocator, slice_val); } } else { @@ -6008,7 +5822,7 @@ pub const CodeGen = struct { break :blk c.LLVMBuildLoad2(self.builder, self.getStringStructType(), ptr, "any_to_str"); }, .struct_type => |sname| blk: { - const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct '{s}'", .{sname}); + const info = try self.getStructInfo(sname); const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_struct_ptr"); break :blk c.LLVMBuildLoad2(self.builder, info.llvm_type, ptr, "any_to_struct"); }, @@ -6021,7 +5835,7 @@ pub const CodeGen = struct { break :blk any_i64; }, .union_type => |uname| blk: { - const info = self.tagged_enum_types.get(uname) orelse return self.emitErrorFmt("unknown enum '{s}'", .{uname}); + const info = try self.getTaggedEnumInfo(uname); const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_union_ptr"); break :blk c.LLVMBuildLoad2(self.builder, info.llvm_type, ptr, "any_to_union"); }, @@ -6107,7 +5921,7 @@ pub const CodeGen = struct { defer self.type_param_bindings = saved_bindings; // Build the specialized function type - const fn_type = try self.buildFnType(fd.params, fd.return_type, mangled); + const fn_type = try self.buildFnType(fd.params, fd.return_type, mangled, false); const mangled_z = try self.allocator.dupeZ(u8, mangled); const function = c.LLVMAddFunction(self.module, mangled_z.ptr, fn_type); @@ -6135,7 +5949,7 @@ pub const CodeGen = struct { raw[1 .. raw.len - 1] else raw; - const content = if (slit.is_raw) inner else try unescapeString(self.allocator, inner); + const content = if (slit.is_raw) inner else try unescape.unescapeString(self.allocator, inner); const str_val = self.buildConstStr(content); const param_name_z = try self.allocator.dupeZ(u8, param.name); const alloca = c.LLVMBuildAlloca(self.builder, self.getStringStructType(), param_name_z.ptr); @@ -6154,7 +5968,7 @@ pub const CodeGen = struct { } continue; } - // Variadic params: use slice_type (same as genFnBodyAs) + // Variadic params: use slice_type (same as genFnBody) const sx_ty = if (param.is_variadic) blk: { const elem_name = if (param.type_expr.data == .type_expr) param.type_expr.data.type_expr.name else "s32"; break :blk Type{ .slice_type = .{ .element_name = elem_name } }; @@ -6183,7 +5997,7 @@ pub const CodeGen = struct { } else if (last_val) |val| { if (ret_sx_type.isStruct()) { const sname = ret_sx_type.struct_type; - const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); + const info = try self.getStructInfo(sname); const loaded = c.LLVMBuildLoad2(self.builder, info.llvm_type, val, "retval"); _ = c.LLVMBuildRet(self.builder, loaded); } else { @@ -6774,9 +6588,9 @@ pub const CodeGen = struct { // Extract base name (strip namespace prefix) 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, "sqrt")) return self.genMathIntrinsic(call_node, "sqrt"); + if (std.mem.eql(u8, base, "sin")) return self.genMathIntrinsic(call_node, "sin"); + if (std.mem.eql(u8, base, "cos")) return self.genMathIntrinsic(call_node, "cos"); 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); @@ -6815,7 +6629,7 @@ pub const CodeGen = struct { fn buildConstStr(self: *CodeGen, s: []const u8) c.LLVMValueRef { const sz = self.allocator.dupeZ(u8, s) catch unreachable; const ptr = c.LLVMBuildGlobalStringPtr(self.builder, sz.ptr, "cstr"); - return self.buildStringSlice(ptr, @intCast(s.len)); + return self.buildStringSlice(ptr, self.constInt64(@intCast(s.len))); } /// Helper: build a constant string slice as a global constant (no builder needed). @@ -7154,14 +6968,14 @@ pub const CodeGen = struct { } if (obj_ty.isStruct()) { if (self.struct_types.get(obj_ty.struct_type)) |info| { - if (self.findFieldIndex(info, fa.field)) |idx| { + if (self.findNameIndex(info.field_names, fa.field)) |idx| { return info.field_types[idx]; } } } if (obj_ty.isUnion()) { if (self.union_types.get(obj_ty.union_type)) |info| { - if (self.findUnionFieldIndex(info, fa.field)) |idx| { + if (self.findNameIndex(info.field_names, fa.field)) |idx| { return info.field_types[idx]; } if (info.promoted_fields.get(fa.field)) |pf| { diff --git a/src/comptime.zig b/src/comptime.zig index ead96be..9ac0d32 100644 --- a/src/comptime.zig +++ b/src/comptime.zig @@ -1,6 +1,7 @@ const std = @import("std"); const types = @import("types.zig"); const Type = types.Type; +const unescape = @import("unescape.zig"); /// Runtime value for comptime evaluation. /// Replaces codegen's JitResult with richer type support. @@ -351,34 +352,6 @@ pub const Compiler = struct { return null; } - /// Process escape sequences in a raw string literal. - fn unescapeString(allocator: std.mem.Allocator, raw: []const u8) ![]u8 { - var result = try allocator.alloc(u8, raw.len); - var i: usize = 0; - var j: usize = 0; - while (i < raw.len) { - if (raw[i] == '\\' and i + 1 < raw.len) { - i += 1; - switch (raw[i]) { - 'n' => result[j] = '\n', - 't' => result[j] = '\t', - 'r' => result[j] = '\r', - '\\' => result[j] = '\\', - '"' => result[j] = '"', - '0' => result[j] = 0, - else => result[j] = raw[i], - } - j += 1; - i += 1; - } else { - result[j] = raw[i]; - j += 1; - i += 1; - } - } - return result[0..j]; - } - /// Compile a string literal with escape sequences and interpolation support. /// Handles `{expr}` patterns by parsing and compiling the inner expressions, /// then concatenating all segments together. @@ -389,7 +362,7 @@ pub const Compiler = struct { fn compileStringLiteral(self: *Compiler, raw: []const u8) !void { // String literals are plain text — {} is NOT interpolated here. // String interpolation is handled by print() at the call site. - const unescaped = try unescapeString(self.allocator, raw); + const unescaped = try unescape.unescapeString(self.allocator, raw); const idx = try self.addString(unescaped); try self.emit(.{ .push_string = idx }); } diff --git a/src/sema.zig b/src/sema.zig index ff0a297..c3c900c 100644 --- a/src/sema.zig +++ b/src/sema.zig @@ -455,10 +455,7 @@ pub const Analyzer = struct { switch (node.data) { .fn_decl => |fd| { try self.pushScope(); - for (fd.params) |param| { - const param_type = Type.fromTypeExpr(param.type_expr); - try self.addSymbol(param.name, .param, param_type, param.name_span); - } + try self.analyzeParams(fd.params); try self.analyzeNode(fd.body); self.popScope(); }, @@ -499,6 +496,13 @@ pub const Analyzer = struct { } } + fn analyzeParams(self: *Analyzer, params: []const ast.Param) !void { + for (params) |param| { + const param_type = Type.fromTypeExpr(param.type_expr); + try self.addSymbol(param.name, .param, param_type, param.name_span); + } + } + fn addSymbol(self: *Analyzer, name: []const u8, kind: SymbolKind, ty: ?Type, span: Span) !void { // Check for duplicate only within the current scope window. const scope_start: usize = if (self.scope_starts.items.len > 0) @@ -563,11 +567,7 @@ pub const Analyzer = struct { .fn_decl => |fd| { try self.addSymbol(fd.name, .function, resolveReturnType(fd), node.span); try self.pushScope(); - // Add params as symbols - for (fd.params) |param| { - const param_type = Type.fromTypeExpr(param.type_expr); - try self.addSymbol(param.name, .param, param_type, param.name_span); - } + try self.analyzeParams(fd.params); try self.analyzeNode(fd.body); self.popScope(); }, @@ -675,10 +675,7 @@ pub const Analyzer = struct { }, .lambda => |lam| { try self.pushScope(); - for (lam.params) |param| { - const param_type = Type.fromTypeExpr(param.type_expr); - try self.addSymbol(param.name, .param, param_type, param.name_span); - } + try self.analyzeParams(lam.params); try self.analyzeNode(lam.body); self.popScope(); }, @@ -824,24 +821,22 @@ pub fn analyzeSource(allocator: std.mem.Allocator, root: *Node) !SemaResult { return analyzer.analyze(root); } -/// Find the symbol whose definition span contains the given byte offset. -pub fn findSymbolAtOffset(symbols: []const Symbol, offset: u32) ?usize { - for (symbols, 0..) |sym, i| { - if (offset >= sym.def_span.start and offset < sym.def_span.end) { - return i; - } +fn findSpanAtOffset(comptime T: type, items: []const T, offset: u32, comptime span_field: []const u8) ?usize { + for (items, 0..) |item, i| { + const span = @field(item, span_field); + if (offset >= span.start and offset < span.end) return i; } return null; } +/// Find the symbol whose definition span contains the given byte offset. +pub fn findSymbolAtOffset(symbols: []const Symbol, offset: u32) ?usize { + return findSpanAtOffset(Symbol, symbols, offset, "def_span"); +} + /// Find the reference at the given byte offset. pub fn findReferenceAtOffset(references: []const Reference, offset: u32) ?usize { - for (references, 0..) |ref_, i| { - if (offset >= ref_.span.start and offset < ref_.span.end) { - return i; - } - } - return null; + return findSpanAtOffset(Reference, references, offset, "span"); } /// Walk the AST to find the innermost node whose span contains the offset. diff --git a/src/unescape.zig b/src/unescape.zig new file mode 100644 index 0000000..5c1fef7 --- /dev/null +++ b/src/unescape.zig @@ -0,0 +1,46 @@ +const std = @import("std"); + +/// Process escape sequences in a raw string literal. +pub fn unescapeString(allocator: std.mem.Allocator, raw: []const u8) ![]u8 { + var result = try allocator.alloc(u8, raw.len); + var i: usize = 0; + var j: usize = 0; + while (i < raw.len) { + if (raw[i] == '\\' and i + 1 < raw.len) { + i += 1; + switch (raw[i]) { + 'n' => { + result[j] = '\n'; + }, + 't' => { + result[j] = '\t'; + }, + 'r' => { + result[j] = '\r'; + }, + '\\' => { + result[j] = '\\'; + }, + '"' => { + result[j] = '"'; + }, + '0' => { + result[j] = 0; + }, + '`' => { + result[j] = '`'; + }, + else => { + result[j] = raw[i]; + }, + } + j += 1; + i += 1; + } else { + result[j] = raw[i]; + j += 1; + i += 1; + } + } + return result[0..j]; +}