arena
This commit is contained in:
140
src/codegen.zig
140
src/codegen.zig
@@ -506,6 +506,12 @@ pub const CodeGen = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Lookup a named value in locals or global mutables (for field access/assignment).
|
||||
fn getNamedOrGlobal(self: *CodeGen, name: []const u8) ?NamedValue {
|
||||
if (self.named_values.get(name)) |nv| return nv;
|
||||
return self.global_mutable_vars.get(name);
|
||||
}
|
||||
|
||||
/// Build an alloca in the entry block of the current function so that
|
||||
/// stack space is reserved once, not on every loop iteration.
|
||||
fn buildEntryBlockAlloca(self: *CodeGen, ty: c.LLVMTypeRef, name: [*:0]const u8) c.LLVMValueRef {
|
||||
@@ -2266,6 +2272,14 @@ pub const CodeGen = struct {
|
||||
const info = try self.getTaggedEnumInfo(resolved);
|
||||
return self.loadIfPointer(raw_val, info.llvm_type, "retval");
|
||||
} else {
|
||||
// If ret_type is a pointer/many-pointer/fn-pointer and the LLVM value is already
|
||||
// an opaque ptr, return it directly. llvmTypeToSxType would misclassify LLVM ptr
|
||||
// as string_type, causing a bogus slice_to_ptr conversion and crash.
|
||||
if ((ret_type.isPointer() or ret_type.isManyPointer() or ret_type == .function_type) and
|
||||
c.LLVMGetTypeKind(c.LLVMTypeOf(raw_val)) == c.LLVMPointerTypeKind)
|
||||
{
|
||||
return raw_val;
|
||||
}
|
||||
const src_ty = self.llvmTypeToSxType(c.LLVMTypeOf(raw_val));
|
||||
return self.convertValue(raw_val, src_ty, ret_type);
|
||||
}
|
||||
@@ -2512,6 +2526,9 @@ pub const CodeGen = struct {
|
||||
}
|
||||
return null;
|
||||
},
|
||||
.push_stmt => |ps| {
|
||||
return self.genPushStmt(ps);
|
||||
},
|
||||
.insert_expr => |ins| {
|
||||
// Substitute comptime param nodes before evaluation (e.g., replace $fmt identifier with literal)
|
||||
const expr = if (self.comptime_param_nodes != null)
|
||||
@@ -3063,7 +3080,7 @@ pub const CodeGen = struct {
|
||||
// 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});
|
||||
const entry = self.getNamedOrGlobal(obj_name) orelse return self.emitErrorFmt("undefined variable '{s}'", .{obj_name});
|
||||
|
||||
// Pointer auto-deref: p.field = val
|
||||
if (entry.ty.isPointer()) {
|
||||
@@ -3487,7 +3504,7 @@ pub const CodeGen = struct {
|
||||
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 (self.getNamedOrGlobal(fa.object.data.identifier.name)) |entry| {
|
||||
if (entry.ty.isStruct()) {
|
||||
const sname = entry.ty.struct_type;
|
||||
const info = try self.getStructInfo(sname);
|
||||
@@ -3616,6 +3633,56 @@ pub const CodeGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// Expand #using entries: interleave used struct fields with declared fields.
|
||||
fn expandStructUsing(self: *CodeGen, sd: ast.StructDecl) !StructInfo {
|
||||
var all_names = std.ArrayList([]const u8).empty;
|
||||
var all_types = std.ArrayList(Type).empty;
|
||||
var all_defaults = std.ArrayList(?*Node).empty;
|
||||
|
||||
var using_idx: usize = 0;
|
||||
for (0..sd.field_names.len + 1) |i| {
|
||||
// Splice #using entries whose insert_index matches current position
|
||||
while (using_idx < sd.using_entries.len and
|
||||
sd.using_entries[using_idx].insert_index == i)
|
||||
{
|
||||
const entry = sd.using_entries[using_idx];
|
||||
const used_name = self.resolveAlias(entry.type_name);
|
||||
const used_info = self.lookupStructInfo(used_name) orelse
|
||||
return self.emitErrorFmt("#using: struct '{s}' not found (declared before '{s}'?)", .{ entry.type_name, sd.name });
|
||||
|
||||
for (used_info.field_names, 0..) |fname, fi| {
|
||||
try all_names.append(self.allocator, fname);
|
||||
try all_types.append(self.allocator, used_info.field_types[fi]);
|
||||
try all_defaults.append(self.allocator, used_info.field_defaults[fi]);
|
||||
}
|
||||
using_idx += 1;
|
||||
}
|
||||
// Append own field at position i
|
||||
if (i < sd.field_names.len) {
|
||||
try all_names.append(self.allocator, sd.field_names[i]);
|
||||
try all_types.append(self.allocator, self.resolveType(sd.field_types[i]));
|
||||
try all_defaults.append(self.allocator, sd.field_defaults[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Build LLVM struct type with expanded fields
|
||||
var llvm_types = std.ArrayList(c.LLVMTypeRef).empty;
|
||||
for (all_types.items) |ty| {
|
||||
try llvm_types.append(self.allocator, self.typeToLLVM(ty));
|
||||
}
|
||||
const name_z = try self.allocator.dupeZ(u8, sd.name);
|
||||
const struct_ty = c.LLVMStructCreateNamed(self.context, name_z.ptr);
|
||||
const llvm_slice = try llvm_types.toOwnedSlice(self.allocator);
|
||||
c.LLVMStructSetBody(struct_ty, if (llvm_slice.len > 0) llvm_slice.ptr else null, @intCast(llvm_slice.len), 0);
|
||||
|
||||
return StructInfo{
|
||||
.field_names = try all_names.toOwnedSlice(self.allocator),
|
||||
.field_types = try all_types.toOwnedSlice(self.allocator),
|
||||
.field_defaults = try all_defaults.toOwnedSlice(self.allocator),
|
||||
.llvm_type = struct_ty,
|
||||
};
|
||||
}
|
||||
|
||||
fn registerStructType(self: *CodeGen, sd: ast.StructDecl) anyerror!void {
|
||||
// Generic struct: store as template instead of registering now
|
||||
if (sd.type_params.len > 0) {
|
||||
@@ -3628,29 +3695,34 @@ pub const CodeGen = struct {
|
||||
try self.hoistInlineTypeDecl(sd.name, sd.field_names[i], ft);
|
||||
}
|
||||
|
||||
const build = try self.buildStructFields(sd.name, sd.field_types);
|
||||
const sinfo = if (sd.using_entries.len > 0)
|
||||
try self.expandStructUsing(sd)
|
||||
else blk: {
|
||||
const build = try self.buildStructFields(sd.name, sd.field_types);
|
||||
|
||||
// Process field defaults: replace #run expressions with comptime global references
|
||||
var resolved_defaults = try self.allocator.alloc(?*Node, sd.field_defaults.len);
|
||||
for (sd.field_defaults, 0..) |fd, i| {
|
||||
if (fd != null and fd.?.data == .comptime_expr) {
|
||||
const synthetic_name = try std.fmt.allocPrint(self.allocator, "__struct_{s}_field_{d}", .{ sd.name, i });
|
||||
const field_type_override: ?Type = if (i < build.field_sx_types.len) build.field_sx_types[i] else null;
|
||||
try self.registerComptimeGlobal(synthetic_name, fd.?.data.comptime_expr.expr, field_type_override);
|
||||
const id_node = try self.allocator.create(Node);
|
||||
id_node.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .identifier = .{ .name = synthetic_name } } };
|
||||
resolved_defaults[i] = id_node;
|
||||
} else {
|
||||
resolved_defaults[i] = fd;
|
||||
// Process field defaults: replace #run expressions with comptime global references
|
||||
var resolved_defaults = try self.allocator.alloc(?*Node, sd.field_defaults.len);
|
||||
for (sd.field_defaults, 0..) |fd, i| {
|
||||
if (fd != null and fd.?.data == .comptime_expr) {
|
||||
const synthetic_name = try std.fmt.allocPrint(self.allocator, "__struct_{s}_field_{d}", .{ sd.name, i });
|
||||
const field_type_override: ?Type = if (i < build.field_sx_types.len) build.field_sx_types[i] else null;
|
||||
try self.registerComptimeGlobal(synthetic_name, fd.?.data.comptime_expr.expr, field_type_override);
|
||||
const id_node = try self.allocator.create(Node);
|
||||
id_node.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .identifier = .{ .name = synthetic_name } } };
|
||||
resolved_defaults[i] = id_node;
|
||||
} else {
|
||||
resolved_defaults[i] = fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const sinfo = StructInfo{
|
||||
.field_names = sd.field_names,
|
||||
.field_types = build.field_sx_types,
|
||||
.field_defaults = resolved_defaults,
|
||||
.llvm_type = build.llvm_type,
|
||||
break :blk StructInfo{
|
||||
.field_names = sd.field_names,
|
||||
.field_types = build.field_sx_types,
|
||||
.field_defaults = resolved_defaults,
|
||||
.llvm_type = build.llvm_type,
|
||||
};
|
||||
};
|
||||
|
||||
try self.type_registry.put(sd.name, .{ .struct_info = sinfo });
|
||||
_ = try self.getAnyTypeId(sd.name, .{ .struct_type = sd.name });
|
||||
}
|
||||
@@ -5016,7 +5088,7 @@ pub const CodeGen = struct {
|
||||
fn genFieldAccess(self: *CodeGen, fa: ast.FieldAccess) !c.LLVMValueRef {
|
||||
// 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| {
|
||||
if (self.getNamedOrGlobal(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
|
||||
@@ -6528,6 +6600,29 @@ pub const CodeGen = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn genPushStmt(self: *CodeGen, ps: ast.PushStmt) !c.LLVMValueRef {
|
||||
// Look up the 'context' global mutable variable
|
||||
const ctx_entry = self.global_mutable_vars.get("context") orelse
|
||||
return self.emitError("push requires a global 'context' variable");
|
||||
const ctx_ty = ctx_entry.ty;
|
||||
const llvm_ty = self.typeToLLVM(ctx_ty);
|
||||
|
||||
// Save current context value
|
||||
const saved = c.LLVMBuildLoad2(self.builder, llvm_ty, ctx_entry.ptr, "saved_ctx");
|
||||
|
||||
// Evaluate new context expression and store to global
|
||||
const new_ctx = try self.genExprAsType(ps.context_expr, ctx_ty);
|
||||
_ = c.LLVMBuildStore(self.builder, new_ctx, ctx_entry.ptr);
|
||||
|
||||
// Generate body
|
||||
_ = try self.genExpr(ps.body);
|
||||
|
||||
// Restore saved context
|
||||
_ = c.LLVMBuildStore(self.builder, saved, ctx_entry.ptr);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn genForExpr(self: *CodeGen, for_expr: ast.ForExpr) !c.LLVMValueRef {
|
||||
const i64_type = self.i64Type();
|
||||
|
||||
@@ -7202,6 +7297,7 @@ pub const CodeGen = struct {
|
||||
.bool_literal => .boolean,
|
||||
.string_literal => .string_type,
|
||||
.insert_expr => .void_type,
|
||||
.push_stmt => .void_type,
|
||||
.comptime_expr => |ct| self.inferType(ct.expr),
|
||||
.binary_op => |binop| {
|
||||
switch (binop.op) {
|
||||
|
||||
Reference in New Issue
Block a user