...
This commit is contained in:
178
src/codegen.zig
178
src/codegen.zig
@@ -2673,9 +2673,16 @@ pub const CodeGen = struct {
|
||||
const elem_llvm_ty = self.typeToLLVM(elem_sx_ty);
|
||||
const len = @min(al.elements.len, arr_info.length);
|
||||
for (0..len) |i| {
|
||||
const val = try self.genExprAsType(al.elements[i], elem_sx_ty);
|
||||
const elem_node = al.elements[i];
|
||||
const val = try self.genExprAsType(elem_node, elem_sx_ty);
|
||||
const gep = self.gepArrayElement(llvm_arr_ty, arr_alloca, self.constInt32(@intCast(i)), "arr_elem");
|
||||
_ = c.LLVMBuildStore(self.builder, val, gep);
|
||||
// Array literals return allocas via genArrayLiteral — load value before storing
|
||||
if (elem_node.data == .array_literal) {
|
||||
const loaded = c.LLVMBuildLoad2(self.builder, elem_llvm_ty, val, "agg_load");
|
||||
_ = c.LLVMBuildStore(self.builder, loaded, gep);
|
||||
} else {
|
||||
_ = c.LLVMBuildStore(self.builder, val, gep);
|
||||
}
|
||||
}
|
||||
// Zero-init remaining elements
|
||||
for (len..arr_info.length) |i| {
|
||||
@@ -2917,9 +2924,13 @@ pub const CodeGen = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// Target must be an identifier (or type_expr for names like s2, u8 that match type patterns)
|
||||
const name = if (asgn.target.data == .identifier)
|
||||
asgn.target.data.identifier.name
|
||||
else if (asgn.target.data == .type_expr)
|
||||
asgn.target.data.type_expr.name
|
||||
else
|
||||
return self.emitError("assignment target must be a variable");
|
||||
const lookup = self.lookupValue(name) orelse
|
||||
return self.emitErrorFmt("undefined variable '{s}'", .{name});
|
||||
const entry = lookup.asNamedValue() orelse
|
||||
@@ -3127,12 +3138,14 @@ pub const CodeGen = struct {
|
||||
return null;
|
||||
}
|
||||
if (obj_ty.isArray()) {
|
||||
const arr_info = obj_ty.array_type;
|
||||
const elem_ty = self.resolveTypeFromName(arr_info.element_name) orelse return self.emitError("unknown array element type");
|
||||
if (ie.object.data == .identifier) {
|
||||
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 val = try self.genExprAsType(asgn.value, elem_ty);
|
||||
const gep_ptr = self.gepArrayElement(self.typeToLLVM(obj_ty), entry.ptr, idx, "arridx");
|
||||
_ = c.LLVMBuildStore(self.builder, val, gep_ptr);
|
||||
self.storeOrCompound(asgn.op, gep_ptr, val, elem_ty, "arrcur");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -3140,9 +3153,9 @@ 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 val = try self.genExpr(asgn.value);
|
||||
const val = try self.genExprAsType(asgn.value, elem_ty);
|
||||
const gep_ptr = self.gepArrayElement(self.typeToLLVM(obj_ty), field_ptr, idx, "field_arridx");
|
||||
_ = c.LLVMBuildStore(self.builder, val, gep_ptr);
|
||||
self.storeOrCompound(asgn.op, gep_ptr, val, elem_ty, "farrcur");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -3262,6 +3275,13 @@ pub const CodeGen = struct {
|
||||
return self.icmp(pred, lhs_tag, rhs_tag, "tag_cmp");
|
||||
}
|
||||
|
||||
// String comparison: compare lengths, then memcmp content
|
||||
if ((result_type == .string_type or result_type.isSlice()) and (binop.op == .eq or binop.op == .neq)) {
|
||||
const lhs = try self.genExpr(binop.lhs);
|
||||
const rhs = try self.genExpr(binop.rhs);
|
||||
return self.genStringComparison(binop.op, lhs, rhs);
|
||||
}
|
||||
|
||||
const lhs = try self.genExprAsType(binop.lhs, result_type);
|
||||
const rhs = try self.genExprAsType(binop.rhs, result_type);
|
||||
return self.genBinaryOp(binop.op, lhs, rhs, result_type);
|
||||
@@ -3413,6 +3433,21 @@ pub const CodeGen = struct {
|
||||
.comptime_expr => |ct| {
|
||||
return self.genExpr(ct.expr);
|
||||
},
|
||||
.type_expr => |te| {
|
||||
// type_expr can appear when a variable name matches a type (e.g. s2, u8)
|
||||
// Fall back to identifier behavior: check named_values first
|
||||
if (self.lookupValue(te.name)) |v| {
|
||||
switch (v) {
|
||||
.local => |nv| return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(nv.ty), nv.ptr, "loadtmp"),
|
||||
.comptime_global => |ct| {
|
||||
if (!ct.is_resolved) try self.resolveComptimeGlobal(ct);
|
||||
return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(ct.ty), ct.global, "ct_load");
|
||||
},
|
||||
.global_mutable => |gm| return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(gm.ty), gm.ptr, "global_load"),
|
||||
}
|
||||
}
|
||||
return self.emitErrorFmt("type '{s}' used as expression", .{te.name});
|
||||
},
|
||||
else => return self.emitError("unsupported expression"),
|
||||
}
|
||||
}
|
||||
@@ -3969,9 +4004,17 @@ pub const CodeGen = struct {
|
||||
|
||||
const len = @min(al.elements.len, arr_info.length);
|
||||
for (0..len) |i| {
|
||||
const val = try self.genExprAsType(al.elements[i], elem_sx_ty);
|
||||
const elem_node = al.elements[i];
|
||||
const val = try self.genExprAsType(elem_node, elem_sx_ty);
|
||||
const gep = self.gepArrayElement(llvm_arr_ty, alloca, self.constInt32(@intCast(i)), "arr_elem");
|
||||
_ = c.LLVMBuildStore(self.builder, val, gep);
|
||||
// Array literals return allocas via genArrayLiteral — load value before storing
|
||||
if (elem_node.data == .array_literal) {
|
||||
const elem_llvm_ty = self.typeToLLVM(elem_sx_ty);
|
||||
const loaded = c.LLVMBuildLoad2(self.builder, elem_llvm_ty, val, "agg_load");
|
||||
_ = c.LLVMBuildStore(self.builder, loaded, gep);
|
||||
} else {
|
||||
_ = c.LLVMBuildStore(self.builder, val, gep);
|
||||
}
|
||||
}
|
||||
return alloca;
|
||||
}
|
||||
@@ -5071,6 +5114,31 @@ pub const CodeGen = struct {
|
||||
if (obj_ty == .string_type) {
|
||||
return self.extractFatPtrField(obj_val, fa.field, "string");
|
||||
}
|
||||
if (obj_ty.isSlice()) {
|
||||
return self.extractFatPtrField(obj_val, fa.field, "slice");
|
||||
}
|
||||
if (obj_ty.isStruct()) {
|
||||
const sname = obj_ty.struct_type;
|
||||
const info = try self.getStructInfo(sname);
|
||||
const idx = try self.findFieldIndex(info.field_names, fa.field, sname);
|
||||
// Store the struct value to a temp alloca, then GEP to load the field
|
||||
const tmp = self.buildEntryBlockAlloca(info.llvm_type, "tmp_struct");
|
||||
_ = c.LLVMBuildStore(self.builder, obj_val, tmp);
|
||||
return self.loadStructField(info.llvm_type, tmp, @intCast(idx), self.typeToLLVM(info.field_types[idx]));
|
||||
}
|
||||
if (obj_ty.isUnion()) {
|
||||
if (self.lookupTaggedEnumInfo(obj_ty.union_type)) |info| {
|
||||
for (info.variant_names, 0..) |vn, i| {
|
||||
if (std.mem.eql(u8, vn, fa.field)) {
|
||||
const variant_ty = info.variant_types[i];
|
||||
if (variant_ty == .void_type) return self.emitErrorFmt("cannot access payload of void variant '{s}'", .{fa.field});
|
||||
const tmp = self.buildEntryBlockAlloca(info.llvm_type, "tmp_enum");
|
||||
_ = c.LLVMBuildStore(self.builder, obj_val, tmp);
|
||||
return self.loadStructField(info.llvm_type, tmp, info.payload_field_index, self.typeToLLVM(variant_ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return self.emitError("field access on non-struct/non-vector expression");
|
||||
}
|
||||
|
||||
@@ -5117,6 +5185,16 @@ pub const CodeGen = struct {
|
||||
const gep = self.gepArrayElement(self.typeToLLVM(obj_ty), field_ptr, idx, "field_arridx");
|
||||
return self.loadTyped(elem_ty, gep, "field_arrval");
|
||||
}
|
||||
// General fallback: store value to temp alloca and GEP into it
|
||||
{
|
||||
const arr_val = try self.genExpr(ie.object);
|
||||
const arr_llvm_ty = self.typeToLLVM(obj_ty);
|
||||
const tmp = self.buildEntryBlockAlloca(arr_llvm_ty, "arr_tmp");
|
||||
_ = c.LLVMBuildStore(self.builder, arr_val, tmp);
|
||||
const idx = try self.genExpr(ie.index);
|
||||
const gep = self.gepArrayElement(arr_llvm_ty, tmp, idx, "gen_arridx");
|
||||
return self.loadTyped(elem_ty, gep, "gen_arrval");
|
||||
}
|
||||
}
|
||||
if (obj_ty == .string_type) {
|
||||
// String indexing: extract ptr from slice, GEP + load u8
|
||||
@@ -5255,6 +5333,55 @@ pub const CodeGen = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn genStringComparison(self: *CodeGen, op: ast.BinaryOp.Op, lhs: c.LLVMValueRef, rhs: c.LLVMValueRef) !c.LLVMValueRef {
|
||||
const b = self.builder;
|
||||
const i64_ty = self.i64Type();
|
||||
const i32_ty = self.i32Type();
|
||||
const i1_ty = self.i1Type();
|
||||
const ptr_ty = self.ptrType();
|
||||
|
||||
// Extract ptr and len from both fat pointers
|
||||
const lhs_ptr = self.extractValue(lhs, 0, "lptr");
|
||||
const lhs_len = self.extractValue(lhs, 1, "llen");
|
||||
const rhs_ptr = self.extractValue(rhs, 0, "rptr");
|
||||
const rhs_len = self.extractValue(rhs, 1, "rlen");
|
||||
|
||||
// Compare lengths first
|
||||
const len_eq = c.LLVMBuildICmp(b, c.LLVMIntEQ, lhs_len, rhs_len, "len_eq");
|
||||
|
||||
// Set up basic blocks for conditional memcmp
|
||||
const cur_bb = self.getCurrentBlock();
|
||||
const memcmp_bb = self.appendBB("str.memcmp");
|
||||
const merge_bb = self.appendBB("str.merge");
|
||||
|
||||
_ = c.LLVMBuildCondBr(b, len_eq, memcmp_bb, merge_bb);
|
||||
|
||||
// memcmp block: lengths match, compare content
|
||||
c.LLVMPositionBuilderAtEnd(b, memcmp_bb);
|
||||
const memcmp_fn = c.LLVMGetNamedFunction(self.module, "memcmp") orelse blk: {
|
||||
var params = [_]c.LLVMTypeRef{ ptr_ty, ptr_ty, i64_ty };
|
||||
const fn_type = c.LLVMFunctionType(i32_ty, ¶ms, 3, 0);
|
||||
break :blk c.LLVMAddFunction(self.module, "memcmp", fn_type);
|
||||
};
|
||||
var args = [_]c.LLVMValueRef{ lhs_ptr, rhs_ptr, lhs_len };
|
||||
const cmp_result = c.LLVMBuildCall2(b, c.LLVMGlobalGetValueType(memcmp_fn), memcmp_fn, &args, 3, "memcmp");
|
||||
const content_eq = c.LLVMBuildICmp(b, c.LLVMIntEQ, cmp_result, c.LLVMConstInt(i32_ty, 0, 0), "content_eq");
|
||||
_ = c.LLVMBuildBr(b, merge_bb);
|
||||
|
||||
// Merge: phi(len_mismatch=false, memcmp_result)
|
||||
c.LLVMPositionBuilderAtEnd(b, merge_bb);
|
||||
const phi = c.LLVMBuildPhi(b, i1_ty, "str_eq");
|
||||
const false_val = c.LLVMConstInt(i1_ty, 0, 0);
|
||||
var phi_vals = [_]c.LLVMValueRef{ false_val, content_eq };
|
||||
var phi_bbs = [_]c.LLVMBasicBlockRef{ cur_bb, memcmp_bb };
|
||||
c.LLVMAddIncoming(phi, &phi_vals, &phi_bbs, 2);
|
||||
|
||||
if (op == .neq) {
|
||||
return c.LLVMBuildNot(b, phi, "str_neq");
|
||||
}
|
||||
return phi;
|
||||
}
|
||||
|
||||
fn genShortCircuitOp(self: *CodeGen, binop: ast.BinaryOp, is_and: bool) !c.LLVMValueRef {
|
||||
const lhs_val = self.valueToBool(try self.genExpr(binop.lhs));
|
||||
const lhs_bb = self.getCurrentBlock();
|
||||
@@ -6585,8 +6712,8 @@ pub const CodeGen = struct {
|
||||
null;
|
||||
|
||||
const i64_type = self.i64Type();
|
||||
// Enum/union case constants use the backing type; Any dispatch uses i64
|
||||
const case_int_type = if (enum_name) |en| self.getEnumLLVMType(en) else if (union_name) |un| self.getEnumLLVMType(un) else i64_type;
|
||||
// Enum/union case constants use the backing type; bool uses i1; others use i64
|
||||
const case_int_type = if (enum_name) |en| self.getEnumLLVMType(en) else if (union_name) |un| self.getEnumLLVMType(un) else if (subject_ty == .boolean) self.i1Type() else i64_type;
|
||||
const merge_bb = self.appendBB("match_end");
|
||||
|
||||
// Create case basic blocks
|
||||
@@ -6838,6 +6965,13 @@ pub const CodeGen = struct {
|
||||
return std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ fa.object.data.identifier.name, fa.field }) catch return null;
|
||||
}
|
||||
}
|
||||
// UFCS: obj.method(args) → method is the callee name
|
||||
const method_z = self.allocator.dupeZ(u8, fa.field) catch return null;
|
||||
if (self.generic_templates.contains(fa.field) or
|
||||
c.LLVMGetNamedFunction(self.module, method_z.ptr) != null)
|
||||
{
|
||||
return fa.field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -7037,6 +7171,15 @@ pub const CodeGen = struct {
|
||||
};
|
||||
},
|
||||
};
|
||||
// Array display name: "[N]T"
|
||||
if (name.len > 2 and name[0] == '[') {
|
||||
if (std.mem.indexOfScalar(u8, name[1..], ']')) |close| {
|
||||
const n_str = name[1 .. 1 + close];
|
||||
const elem = name[2 + close ..];
|
||||
const length = std.fmt.parseInt(u32, n_str, 10) catch return null;
|
||||
return .{ .array_type = .{ .element_name = elem, .length = length } };
|
||||
}
|
||||
}
|
||||
// Vector display name: "Vector(N,T)"
|
||||
if (name.len > 8 and std.mem.startsWith(u8, name, "Vector(") and name[name.len - 1] == ')') {
|
||||
const inner = name[7 .. name.len - 1]; // "N,T"
|
||||
@@ -7075,6 +7218,11 @@ pub const CodeGen = struct {
|
||||
if (self.lookupValue(ident.name)) |v| return v.ty();
|
||||
return Type.s(64);
|
||||
},
|
||||
.type_expr => |te| {
|
||||
// type_expr can appear when a variable name matches a type (e.g. s2, u8)
|
||||
if (self.lookupValue(te.name)) |v| return v.ty();
|
||||
return Type.s(64);
|
||||
},
|
||||
.if_expr => |ie| {
|
||||
return self.inferType(ie.then_branch);
|
||||
},
|
||||
@@ -7317,6 +7465,10 @@ pub const CodeGen = struct {
|
||||
const elem_name = elem_ty.displayName(self.allocator) catch return Type.s(64);
|
||||
return .{ .array_type = .{ .element_name = elem_name, .length = @intCast(al.elements.len) } };
|
||||
},
|
||||
.struct_literal => |sl| {
|
||||
if (sl.struct_name) |sname| return .{ .struct_type = sname };
|
||||
return Type.s(64);
|
||||
},
|
||||
.while_expr, .for_expr, .break_expr, .continue_expr => .void_type,
|
||||
else => Type.s(64),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user