This commit is contained in:
agra
2026-02-10 22:47:43 +02:00
parent ef14144d49
commit 70435d3c85
11 changed files with 443 additions and 5 deletions

View File

@@ -240,6 +240,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),
.any_type => self.getAnyStructType(),
.meta_type => c.LLVMPointerTypeInContext(self.context, 0),
};
@@ -347,6 +348,8 @@ pub const CodeGen = struct {
.vector_type => |info| try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "vec[{d}]{s}", .{ info.length, info.element_name }), ty),
.array_type => |info| try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "[{d}]{s}", .{ info.length, info.element_name }), ty),
.slice_type => |info| try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "[]{s}", .{info.element_name}), ty),
.pointer_type => |info| try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "*{s}", .{info.pointee_name}), ty),
.many_pointer_type => |info| try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "[*]{s}", .{info.element_name}), ty),
.meta_type => ANY_TAG_TYPE,
else => ANY_TAG_S32,
};
@@ -409,6 +412,7 @@ pub const CodeGen = struct {
_ = c.LLVMBuildStore(self.builder, val, alloca);
break :blk c.LLVMBuildPtrToInt(self.builder, alloca, i64_ty, "any_slice");
},
.pointer_type, .many_pointer_type => c.LLVMBuildPtrToInt(self.builder, val, i64_ty, "any_ptr"),
.meta_type => blk: {
// Meta type is a pointer (global string) — convert via ptrtoint
break :blk c.LLVMBuildPtrToInt(self.builder, val, i64_ty, "any_type");
@@ -754,6 +758,20 @@ pub const CodeGen = struct {
const elem_name = elem_type.displayName(self.allocator) catch unreachable;
return .{ .slice_type = .{ .element_name = elem_name } };
}
// Pointer type: *T
if (tn.data == .pointer_type_expr) {
const pte = tn.data.pointer_type_expr;
const pointee_type = self.resolveType(pte.pointee_type);
const pointee_name = pointee_type.displayName(self.allocator) catch unreachable;
return .{ .pointer_type = .{ .pointee_name = pointee_name } };
}
// Many-pointer type: [*]T
if (tn.data == .many_pointer_type_expr) {
const mpte = tn.data.many_pointer_type_expr;
const elem_type = self.resolveType(mpte.element_type);
const elem_name = elem_type.displayName(self.allocator) catch unreachable;
return .{ .many_pointer_type = .{ .element_name = elem_name } };
}
// Parameterized type: Vector(N, T) or generic struct instantiation
if (tn.data == .parameterized_type_expr) {
const pte = tn.data.parameterized_type_expr;
@@ -1952,6 +1970,18 @@ pub const CodeGen = struct {
return self.genIndexAssignment(asgn);
}
// Deref assignment: p.* = value;
if (asgn.target.data == .deref_expr) {
const de = asgn.target.data.deref_expr;
const ptr_val = try self.genExpr(de.operand);
const ptr_ty = self.inferType(de.operand);
if (!ptr_ty.isPointer()) return self.emitError("dereference assignment requires a pointer");
const pointee_ty = self.resolveTypeFromName(ptr_ty.pointer_type.pointee_name) orelse return self.emitError("unknown pointee type");
const new_val = try self.genExprAsType(asgn.value, pointee_ty);
_ = c.LLVMBuildStore(self.builder, new_val, ptr_val);
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;
@@ -2034,10 +2064,37 @@ pub const CodeGen = struct {
fn genFieldAssignment(self: *CodeGen, asgn: ast.Assignment) !c.LLVMValueRef {
const fa = asgn.target.data.field_access;
// Handle deref assignment: p.* = val
if (fa.object.data == .identifier and std.mem.eql(u8, fa.field, "*")) {
// This won't happen — p.* is parsed as deref_expr, not field_access
// Kept as safeguard
}
// Object must be an identifier for now
if (fa.object.data != .identifier) return self.emitError("field assignment target must be a variable");
const obj_name = fa.object.data.identifier.name;
const entry = self.named_values.get(obj_name) orelse return self.emitErrorFmt("undefined variable '{s}'", .{obj_name});
// Pointer auto-deref: p.field = val
if (entry.ty.isPointer()) {
const pointee_ty = self.resolveTypeFromName(entry.ty.pointer_type.pointee_name) orelse
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 field_ty = info.field_types[fi];
const loaded_ptr = c.LLVMBuildLoad2(self.builder,
c.LLVMPointerTypeInContext(self.context, 0), entry.ptr, "ptr_load");
const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, loaded_ptr, @intCast(fi), "pfield_ptr");
const rhs = try self.genExprAsType(asgn.value, field_ty);
_ = c.LLVMBuildStore(self.builder, rhs, gep);
return null;
}
return self.emitError("field assignment through pointer requires a struct pointee");
}
if (!entry.ty.isStruct()) return self.emitErrorFmt("field access on non-struct variable '{s}'", .{obj_name});
const sname = entry.ty.struct_type;
@@ -2141,7 +2198,19 @@ pub const CodeGen = struct {
_ = c.LLVMBuildStore(self.builder, val, gep_ptr);
return null;
}
return self.emitError("index assignment requires a string, array, or slice target");
// Many-pointer index assignment: mp[i] = val
if (obj_ty.isManyPointer()) {
const elem_ty = self.resolveTypeFromName(obj_ty.many_pointer_type.element_name) orelse return self.emitError("unknown many-pointer element type");
const elem_llvm_ty = self.typeToLLVM(elem_ty);
const ptr_val = try self.genExpr(ie.object);
const idx = try self.genExpr(ie.index);
const val = try self.genExprAsType(asgn.value, elem_ty);
var gep_indices = [_]c.LLVMValueRef{idx};
const gep_ptr = c.LLVMBuildGEP2(self.builder, elem_llvm_ty, ptr_val, &gep_indices, 1, "mptridx");
_ = c.LLVMBuildStore(self.builder, val, gep_ptr);
return null;
}
return self.emitError("index assignment requires a string, array, slice, or [*] pointer target");
}
fn unescapeString(allocator: std.mem.Allocator, raw: []const u8) ![]u8 {
@@ -2239,6 +2308,9 @@ pub const CodeGen = struct {
// xx requires a target type context (assignment, declaration, argument, return)
return self.emitError("'xx' cast requires a target type context");
}
if (unop.op == .address_of) {
return self.genAddressOf(unop.operand);
}
const operand = try self.genExpr(unop.operand);
return switch (unop.op) {
.negate => blk: {
@@ -2256,7 +2328,7 @@ pub const CodeGen = struct {
c.LLVMBuildNeg(self.builder, operand, "negtmp");
},
.not => c.LLVMBuildNot(self.builder, operand, "nottmp"),
.xx => unreachable,
.xx, .address_of => unreachable,
};
},
.struct_literal => |sl| {
@@ -2355,6 +2427,18 @@ pub const CodeGen = struct {
c.LLVMPositionBuilderAtEnd(self.builder, dead_bb);
return null;
},
.deref_expr => |de| {
const ptr_val = try self.genExpr(de.operand);
const ptr_ty = self.inferType(de.operand);
if (ptr_ty.isPointer()) {
const pointee_ty = self.resolveTypeFromName(ptr_ty.pointer_type.pointee_name) orelse return self.emitError("unknown pointee type");
return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(pointee_ty), ptr_val, "deref");
}
return self.emitError("dereference requires a pointer type");
},
.null_literal => {
return c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0));
},
.comptime_expr => |ct| {
return self.genExpr(ct.expr);
},
@@ -2362,6 +2446,57 @@ pub const CodeGen = struct {
}
}
fn genAddressOf(self: *CodeGen, operand: *Node) !c.LLVMValueRef {
// &x — return the alloca pointer of the variable
if (operand.data == .identifier) {
if (self.named_values.get(operand.data.identifier.name)) |entry| {
return entry.ptr;
}
return self.emitErrorFmt("undefined variable '{s}'", .{operand.data.identifier.name});
}
// &expr[i] — return GEP pointer to the indexed element
if (operand.data == .index_expr) {
const ie = operand.data.index_expr;
const obj_ty = self.inferType(ie.object);
const idx = try self.genExpr(ie.index);
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);
var indices = [_]c.LLVMValueRef{ zero, idx };
return c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), entry.ptr, &indices, 2, "addr_elem");
}
}
}
if (obj_ty.isSlice() or obj_ty == .string_type) {
const slice_val = try self.genExpr(ie.object);
const ptr = c.LLVMBuildExtractValue(self.builder, slice_val, 0, "slice_ptr");
const elem_ty = if (obj_ty.isSlice())
obj_ty.sliceElementType() orelse return self.emitError("unknown slice element type")
else
Type.u(8);
var gep_indices = [_]c.LLVMValueRef{idx};
return c.LLVMBuildGEP2(self.builder, self.typeToLLVM(elem_ty), ptr, &gep_indices, 1, "addr_elem");
}
}
// &s.field — return GEP pointer to the struct field
if (operand.data == .field_access) {
const fa = operand.data.field_access;
if (fa.object.data == .identifier) {
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 });
return c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, @intCast(idx), "addr_field");
}
}
}
}
return self.emitError("address-of requires a variable, index, or field expression");
}
fn registerStructType(self: *CodeGen, sd: ast.StructDecl) anyerror!void {
// Generic struct: store as template instead of registering now
if (sd.type_params.len > 0) {
@@ -3335,6 +3470,34 @@ pub const CodeGen = struct {
// Check if the object is a struct or vector variable
if (fa.object.data == .identifier) {
if (self.named_values.get(fa.object.data.identifier.name)) |entry| {
// Pointer auto-deref: p.field → p.*.field
if (entry.ty.isPointer()) {
const pointee_ty = self.resolveTypeFromName(entry.ty.pointer_type.pointee_name) orelse
return self.emitError("unknown pointee type for auto-deref");
const loaded_ptr = c.LLVMBuildLoad2(self.builder,
c.LLVMPointerTypeInContext(self.context, 0), entry.ptr, "ptr_load");
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 idx = self.findFieldIndex(info, 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");
return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(info.field_types[idx]), gep, "pfieldval");
}
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.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});
@@ -3494,7 +3657,17 @@ pub const CodeGen = struct {
const gep = c.LLVMBuildGEP2(self.builder, elem_llvm_ty, ptr, &gep_indices, 1, "sliceidx");
return c.LLVMBuildLoad2(self.builder, elem_llvm_ty, gep, "sliceval");
}
return self.emitError("index expression requires an array, vector, string, or slice");
// Many-pointer indexing: [*]T — GEP + load
if (obj_ty.isManyPointer()) {
const elem_ty = self.resolveTypeFromName(obj_ty.many_pointer_type.element_name) orelse return self.emitError("unknown many-pointer element type");
const elem_llvm_ty = self.typeToLLVM(elem_ty);
const ptr_val = try self.genExpr(ie.object);
const idx = try self.genExpr(ie.index);
var gep_indices = [_]c.LLVMValueRef{idx};
const gep = c.LLVMBuildGEP2(self.builder, elem_llvm_ty, ptr_val, &gep_indices, 1, "mptridx");
return c.LLVMBuildLoad2(self.builder, elem_llvm_ty, gep, "mptrval");
}
return self.emitError("index expression requires an array, vector, string, slice, or [*] pointer");
}
fn genSliceExpr(self: *CodeGen, se: ast.SliceExpr) !c.LLVMValueRef {
@@ -5224,6 +5397,22 @@ pub const CodeGen = struct {
return null;
}
/// Resolve a type name to a Type, checking primitives + registered structs/unions/enums.
/// Unlike Type.fromName which only handles primitives.
fn resolveTypeFromName(self: *CodeGen, name: []const u8) ?Type {
// Primitives
if (Type.fromName(name)) |t| return t;
// Structs
if (self.struct_types.contains(name)) return .{ .struct_type = name };
// Unions
if (self.union_types.contains(name)) return .{ .union_type = name };
// Enums
if (self.enum_types.contains(name)) return .{ .enum_type = name };
// Type aliases
if (self.type_aliases.get(name)) |target| return self.resolveTypeFromName(target);
return null;
}
fn inferType(self: *CodeGen, node: *Node) Type {
return switch (node.data) {
.int_literal => Type.s(32),
@@ -5376,10 +5565,25 @@ pub const CodeGen = struct {
return Type.s(32);
},
.unary_op => |unop| {
if (unop.op == .address_of) {
const operand_ty = self.inferType(unop.operand);
const name = operand_ty.displayName(self.allocator) catch return Type.s(32);
return .{ .pointer_type = .{ .pointee_name = name } };
}
return self.inferType(unop.operand);
},
.deref_expr => |de| {
const ptr_ty = self.inferType(de.operand);
if (ptr_ty.isPointer()) return self.resolveTypeFromName(ptr_ty.pointer_type.pointee_name) orelse Type.s(32);
return Type.s(32);
},
.null_literal => return .{ .pointer_type = .{ .pointee_name = "void" } },
.field_access => |fa| {
const obj_ty = self.inferType(fa.object);
var obj_ty = self.inferType(fa.object);
// Auto-deref: if pointer, unwrap to pointee
if (obj_ty.isPointer()) {
obj_ty = self.resolveTypeFromName(obj_ty.pointer_type.pointee_name) orelse Type.s(32);
}
if (obj_ty == .string_type) {
if (std.mem.eql(u8, fa.field, "len")) return Type.s(32);
if (std.mem.eql(u8, fa.field, "ptr")) return .string_type;
@@ -5426,6 +5630,9 @@ pub const CodeGen = struct {
if (obj_ty.isSlice()) {
return obj_ty.sliceElementType() orelse Type.s(32);
}
if (obj_ty.isManyPointer()) {
return self.resolveTypeFromName(obj_ty.many_pointer_type.element_name) orelse Type.s(32);
}
return Type.s(32);
},
.slice_expr => |se| {