This commit is contained in:
agra
2026-03-04 17:12:56 +02:00
parent 67e02a20a5
commit 22bc2439ce
7 changed files with 217 additions and 18 deletions

View File

@@ -1143,12 +1143,19 @@ pub const Lowering = struct {
// Fallback: global variable assignment
if (!handled) {
if (self.global_names.get(id.name)) |gi| {
const val_ty = self.builder.getRefType(val);
const store_val = if (val_ty != gi.ty and val_ty != .void and gi.ty != .void)
self.coerceToType(val, val_ty, gi.ty)
else
val;
self.builder.emitVoid(.{ .global_set = .{ .global = gi.id, .value = store_val } }, .void);
if (asgn.op == .assign) {
const val_ty = self.builder.getRefType(val);
const store_val = if (val_ty != gi.ty and val_ty != .void and gi.ty != .void)
self.coerceToType(val, val_ty, gi.ty)
else
val;
self.builder.emitVoid(.{ .global_set = .{ .global = gi.id, .value = store_val } }, .void);
} else {
// Compound assignment: load current value, apply op, store back
const loaded = self.builder.emit(.{ .global_get = gi.id }, gi.ty);
const result = self.emitCompoundOp(loaded, val, asgn.op, gi.ty);
self.builder.emitVoid(.{ .global_set = .{ .global = gi.id, .value = result } }, .void);
}
}
}
},
@@ -1228,7 +1235,12 @@ pub const Lowering = struct {
break;
}
}
const gep = self.builder.structGepTyped(obj_ptr, field_idx, field_ty, obj_ty);
// Wrap in ptrTo so the store handler sees *field_ty (consistent
// with index_gep which uses ptrTo(elem_ty)). Without this, a
// [*]BigNode field makes the store handler extract BigNode as the
// target type, storing element-sized bytes instead of a pointer.
const gep_ty = self.module.types.ptrTo(field_ty);
const gep = self.builder.structGepTyped(obj_ptr, field_idx, gep_ty, obj_ty);
// Coerce value to field type
const src_ty = self.inferExprType(asgn.value);
const coerced = self.coerceToType(val, src_ty, field_ty);
@@ -1434,6 +1446,13 @@ pub const Lowering = struct {
break :blk binding.ref;
}
}
// Check compile-time constants (OS, ARCH, POINTER_SIZE) before globals
if (self.comptime_constants.get(id.name)) |cv| {
switch (cv) {
.int_val => |iv| break :blk self.builder.constInt(iv, .s64),
.enum_tag => |et| break :blk self.builder.constInt(@intCast(et.tag), et.ty),
}
}
// Check globals (#run constants)
if (self.global_names.get(id.name)) |gi| {
break :blk self.builder.emit(.{ .global_get = gi.id }, gi.ty);
@@ -2672,7 +2691,7 @@ pub const Lowering = struct {
for (lowered.items) |l| {
if (std.mem.eql(u8, l.name, sf_name)) {
var val = l.val;
const src_ty = self.inferExprType(l.node);
const src_ty = self.builder.getRefType(val);
val = self.coerceToType(val, src_ty, sf.ty);
fields.append(self.alloc, val) catch unreachable;
found = true;
@@ -3548,10 +3567,32 @@ pub const Lowering = struct {
}
}
// Handle closure(lambda) — just return the lambda's closure_create result
// Handle closure(fn_or_lambda) — wrap bare functions into closures
if (c.callee.data == .identifier and std.mem.eql(u8, c.callee.data.identifier.name, "closure")) {
if (c.args.len >= 1) {
return self.lowerExpr(c.args[0]);
const arg = c.args[0];
// If argument is a bare function name, create a proper closure from it
if (arg.data == .identifier) {
const fn_name = arg.data.identifier.name;
if (!self.lowered_functions.contains(fn_name)) {
self.lazyLowerFunction(fn_name);
}
if (self.resolveFuncByName(fn_name)) |fid| {
const func = &self.module.functions.items[@intFromEnum(fid)];
// Build closure type from function signature
var param_types_list = std.ArrayList(TypeId).empty;
defer param_types_list.deinit(self.alloc);
for (func.params) |p| {
param_types_list.append(self.alloc, p.ty) catch unreachable;
}
const closure_ty = self.module.types.closureType(param_types_list.items, func.ret);
const closure_info = self.module.types.get(closure_ty).closure;
const tramp_id = self.createBareFnTrampoline(fid, closure_info);
return self.builder.closureCreate(tramp_id, Ref.none, closure_ty);
}
}
// Lambda or other expression — already produces closure_create
return self.lowerExpr(arg);
}
}
@@ -6343,18 +6384,31 @@ pub const Lowering = struct {
/// Check if a match expression is a type-category match (patterns are type/category names).
fn inferMatchResultType(self: *Lowering, me: *const ast.MatchExpr) TypeId {
// Infer result type from the first non-default arm body
// Infer result type from the first non-null arm body.
// If we skip null_literal arms and find a concrete type T, and there
// were null arms, the result is ?T (optional).
var has_null = false;
for (me.arms) |arm| {
if (arm.body.data == .block) {
// Block — check last statement
const last_node = if (arm.body.data == .block) blk: {
if (arm.body.data.block.stmts.len > 0) {
const last = arm.body.data.block.stmts[arm.body.data.block.stmts.len - 1];
return self.inferExprType(last);
break :blk arm.body.data.block.stmts[arm.body.data.block.stmts.len - 1];
}
break :blk arm.body;
} else arm.body;
if (last_node.data == .null_literal) {
has_null = true;
continue;
}
return self.inferExprType(arm.body);
// First non-null arm determines the type (same as old behavior)
const arm_ty = self.inferExprType(last_node);
if (has_null and arm_ty != .void) {
return self.module.types.optionalOf(arm_ty);
}
return arm_ty;
}
return .s64;
return .void;
}
fn isTypeCategoryMatch(me: *const ast.MatchExpr) bool {