fixes
This commit is contained in:
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user