This commit is contained in:
agra
2026-03-05 16:20:36 +02:00
parent 22bc2439ce
commit f9dda972d2
36 changed files with 1063 additions and 7 deletions

View File

@@ -452,6 +452,7 @@ pub const Lowering = struct {
.bool_literal => |bl| .{ .boolean = bl.value },
.float_literal => |fl| .{ .float = fl.value },
.string_literal => |sl| .{ .string = self.module.types.internString(sl.raw) },
.array_literal => |al| self.constArrayLiteral(al.elements),
else => null,
} else null;
const gid = self.module.addGlobal(.{
@@ -467,6 +468,31 @@ pub const Lowering = struct {
}
}
/// Try to convert an array literal's elements into a compile-time ConstantValue.aggregate.
/// Returns null if any element is not a compile-time constant.
fn constArrayLiteral(self: *Lowering, elements: []const *const Node) ?inst_mod.ConstantValue {
const vals = self.alloc.alloc(inst_mod.ConstantValue, elements.len) catch return null;
for (elements, 0..) |elem, i| {
vals[i] = switch (elem.data) {
.int_literal => |il| .{ .int = il.value },
.bool_literal => |bl| .{ .boolean = bl.value },
.float_literal => |fl| .{ .float = fl.value },
.string_literal => |sl| .{ .string = self.module.types.internString(sl.raw) },
.unary_op => |uo| switch (uo.op) {
.negate => switch (uo.operand.data) {
.int_literal => |il| .{ .int = -il.value },
.float_literal => |fl| .{ .float = -fl.value },
else => return null,
},
else => return null,
},
.array_literal => |al| self.constArrayLiteral(al.elements) orelse return null,
else => return null,
};
}
return .{ .aggregate = vals };
}
/// Pass 2: Lower main function body and comptime side-effects.
fn lowerMainAndComptime(self: *Lowering, decls: []const *const Node) void {
for (decls) |decl| {
@@ -514,19 +540,23 @@ pub const Lowering = struct {
}) catch unreachable;
}
const cc: Function.CallingConvention = if (fd.call_conv == .c) .c else .default;
// For #foreign with C name override, declare under C name and map sx name → C name
if (fd.body.data == .foreign_expr) {
const fe = fd.body.data.foreign_expr;
if (fe.c_name) |c_name| {
const c_name_id = self.module.types.internString(c_name);
_ = self.builder.declareExtern(c_name_id, params.items, ret_ty);
const fid = self.builder.declareExtern(c_name_id, params.items, ret_ty);
self.module.getFunctionMut(fid).call_conv = cc;
self.foreign_name_map.put(name, c_name) catch {};
return;
}
}
const name_id = self.module.types.internString(name);
_ = self.builder.declareExtern(name_id, params.items, ret_ty);
const fid = self.builder.declareExtern(name_id, params.items, ret_ty);
self.module.getFunctionMut(fid).call_conv = cc;
}
/// Lazily lower a function body on demand. Called when lowerCall can't find
@@ -607,6 +637,7 @@ pub const Lowering = struct {
}
func.is_extern = false; // promote from extern stub to real function
func.linkage = if (std.mem.eql(u8, name, "main")) .external else .internal;
if (fd.call_conv == .c) func.call_conv = .c;
// Set inst_counter to param count (params occupy refs 0..N-1)
std.debug.assert(func.params.len == fd.params.len); // AST and IR param counts must match
self.builder.inst_counter = @intCast(func.params.len);
@@ -717,6 +748,11 @@ pub const Lowering = struct {
self.builder.currentFunc().linkage = .external;
}
// Set calling convention
if (fd.call_conv == .c) {
self.builder.currentFunc().call_conv = .c;
}
// Create entry block
const entry_name = self.module.types.internString("entry");
const entry = self.builder.appendBlock(entry_name, &.{});
@@ -1070,12 +1106,14 @@ pub const Lowering = struct {
// Set target_type from LHS for RHS lowering (enum literals, struct literals, etc.)
const old_target = self.target_type;
if (asgn.target.data == .identifier) {
var found_local = false;
if (self.scope) |scope| {
if (scope.lookup(asgn.target.data.identifier.name)) |binding| {
self.target_type = binding.ty;
found_local = true;
}
}
if (self.target_type == null) {
if (!found_local) {
if (self.global_names.get(asgn.target.data.identifier.name)) |gi| {
self.target_type = gi.ty;
}
@@ -4387,6 +4425,9 @@ pub const Lowering = struct {
};
const name_id = self.module.types.internString(name);
const func_id = self.builder.beginFunction(name_id, params.items, ret_ty);
if (lam.call_conv == .c) {
self.module.getFunctionMut(func_id).call_conv = .c;
}
// Create entry block
const entry_name = self.module.types.internString("entry");
@@ -6436,6 +6477,58 @@ pub const Lowering = struct {
/// Resolve parameter types for a call expression (for target_type context).
/// Returns empty slice if the function can't be resolved.
fn resolveCallParamTypes(self: *Lowering, c: *const ast.Call) []const TypeId {
// Method calls: obj.method(args) — resolve param types from the method signature,
// skipping the first param (self) since it's prepended later.
if (c.callee.data == .field_access) {
const fa = c.callee.data.field_access;
const obj_ty = self.inferExprType(fa.object);
if (self.getStructTypeName(obj_ty)) |sname| {
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ sname, fa.field }) catch return &.{};
// Try already-lowered functions first
if (self.resolveFuncByName(qualified)) |fid| {
const func = &self.module.functions.items[@intFromEnum(fid)];
if (func.params.len > 0) {
// Skip self param — caller args don't include self
var types_list = std.ArrayList(TypeId).empty;
for (func.params[1..]) |p| {
types_list.append(self.alloc, p.ty) catch unreachable;
}
return types_list.items;
}
}
// Try AST map (not yet lowered)
if (self.fn_ast_map.get(qualified)) |fd| {
if (fd.params.len > 0) {
var types_list = std.ArrayList(TypeId).empty;
for (fd.params[1..]) |p| {
types_list.append(self.alloc, self.resolveParamType(&p)) catch unreachable;
}
return types_list.items;
}
}
// Try generic struct template method: List__Container.append → List.append
// with type bindings from the struct instantiation
if (self.struct_instance_template.get(sname)) |tmpl_name| {
const tmpl_qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ tmpl_name, fa.field }) catch return &.{};
if (self.fn_ast_map.get(tmpl_qualified)) |fd| {
if (fd.params.len > 0) {
// Temporarily set type_bindings so resolveParamType can substitute T → concrete type
const saved_bindings = self.type_bindings;
if (self.struct_instance_bindings.getPtr(sname)) |bindings| {
self.type_bindings = bindings.*;
}
var types_list = std.ArrayList(TypeId).empty;
for (fd.params[1..]) |p| {
types_list.append(self.alloc, self.resolveParamType(&p)) catch unreachable;
}
self.type_bindings = saved_bindings;
return types_list.items;
}
}
}
}
return &.{};
}
if (c.callee.data != .identifier) return &.{};
const bare_name = c.callee.data.identifier.name;
const name = blk: {