This commit is contained in:
agra
2026-02-20 23:18:17 +02:00
parent efa60fa670
commit be99b26c1d
5 changed files with 127 additions and 27 deletions

View File

@@ -2207,6 +2207,9 @@ pub const CodeGen = struct {
.var_decl => |vd| {
try self.registerGlobalVar(vd);
},
.ufcs_alias => |ua| {
try self.ufcs_aliases.put(ua.name, ua.target);
},
else => {},
}
}
@@ -3560,6 +3563,45 @@ pub const CodeGen = struct {
return self.genStringComparison(binop.op, lhs, rhs);
}
// Single-element tuple vs scalar: unwrap (T,) → T
{
const unwrap_lhs = lhs_ty.isTuple() and lhs_ty.tuple_type.field_types.len == 1 and !rhs_ty.isTuple();
const unwrap_rhs = rhs_ty.isTuple() and rhs_ty.tuple_type.field_types.len == 1 and !lhs_ty.isTuple();
if (unwrap_lhs or unwrap_rhs) {
const eff_lhs_ty = if (unwrap_lhs) lhs_ty.tuple_type.field_types[0] else lhs_ty;
const eff_rhs_ty = if (unwrap_rhs) rhs_ty.tuple_type.field_types[0] else rhs_ty;
const cmp_type = Type.widen(eff_lhs_ty, eff_rhs_ty);
var lhs_val: c.LLVMValueRef = undefined;
if (unwrap_lhs) {
const alloca = try self.genExpr(binop.lhs);
const elem_llvm = self.typeToLLVM(eff_lhs_ty);
var ftypes = [_]c.LLVMTypeRef{elem_llvm};
const tup_llvm = c.LLVMStructTypeInContext(self.context, &ftypes, 1, 0);
const gep = self.structGEP(tup_llvm, alloca, 0, "unwrap_l");
const loaded = c.LLVMBuildLoad2(self.builder, elem_llvm, gep, "elem_l");
lhs_val = self.convertValue(loaded, eff_lhs_ty, cmp_type);
} else {
lhs_val = try self.genExprAsType(binop.lhs, cmp_type);
}
var rhs_val: c.LLVMValueRef = undefined;
if (unwrap_rhs) {
const alloca = try self.genExpr(binop.rhs);
const elem_llvm = self.typeToLLVM(eff_rhs_ty);
var ftypes = [_]c.LLVMTypeRef{elem_llvm};
const tup_llvm = c.LLVMStructTypeInContext(self.context, &ftypes, 1, 0);
const gep = self.structGEP(tup_llvm, alloca, 0, "unwrap_r");
const loaded = c.LLVMBuildLoad2(self.builder, elem_llvm, gep, "elem_r");
rhs_val = self.convertValue(loaded, eff_rhs_ty, cmp_type);
} else {
rhs_val = try self.genExprAsType(binop.rhs, cmp_type);
}
return self.genBinaryOp(binop.op, lhs_val, rhs_val, cmp_type);
}
}
// Tuple comparison: element-wise
if (lhs_ty.isTuple() and rhs_ty.isTuple() and
(binop.op == .eq or binop.op == .neq or binop.op == .lt or binop.op == .lte or binop.op == .gt or binop.op == .gte))
@@ -6197,9 +6239,22 @@ pub const CodeGen = struct {
const method_name = fa.field;
const resolved_method = self.ufcs_aliases.get(method_name) orelse method_name;
const method_z = self.allocator.dupeZ(u8, resolved_method) catch resolved_method;
// Also check namespace-qualified name for intra-namespace UFCS
const ns_qualified = if (self.current_namespace) |ns|
(std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, resolved_method }) catch null)
else
null;
const ns_qualified_z = if (ns_qualified) |q| (self.allocator.dupeZ(u8, q) catch null) else null;
if (self.generic_templates.contains(resolved_method) or
c.LLVMGetNamedFunction(self.module, method_z.ptr) != null)
c.LLVMGetNamedFunction(self.module, method_z.ptr) != null or
(if (ns_qualified) |q| self.generic_templates.contains(q) else false) or
(if (ns_qualified_z) |qz| c.LLVMGetNamedFunction(self.module, qz.ptr) != null else false))
{
// Use namespace-qualified name if that's what exists
const effective_method = if (c.LLVMGetNamedFunction(self.module, method_z.ptr) != null or self.generic_templates.contains(resolved_method))
resolved_method
else
ns_qualified orelse resolved_method;
// Check if receiver is a tuple — if so, splat its elements as leading args
const receiver_type = self.inferType(fa.object);
const is_tuple = receiver_type == .tuple_type;
@@ -6223,7 +6278,7 @@ pub const CodeGen = struct {
for (call_node.args, 0..) |arg, i| {
ufcs_args[n_tuple + i] = arg;
}
return self.genCallByName(resolved_method, .{
return self.genCallByName(effective_method, .{
.callee = call_node.callee,
.args = ufcs_args,
});
@@ -6234,7 +6289,7 @@ pub const CodeGen = struct {
for (call_node.args, 0..) |arg, i| {
ufcs_args[i + 1] = arg;
}
return self.genCallByName(resolved_method, .{
return self.genCallByName(effective_method, .{
.callee = call_node.callee,
.args = ufcs_args,
});
@@ -6242,7 +6297,9 @@ pub const CodeGen = struct {
}
// Resolve callee — must be an identifier
if (call_node.callee.data != .identifier) return self.emitError("callee must be an identifier");
if (call_node.callee.data != .identifier) {
return self.emitError("callee must be an identifier");
}
const callee_name = call_node.callee.data.identifier.name;
return self.genCallByName(callee_name, call_node);
}
@@ -7758,11 +7815,22 @@ pub const CodeGen = struct {
}
}
// UFCS: obj.method(args) → method is the callee name
const method_z = self.allocator.dupeZ(u8, fa.field) catch return null;
if (self.generic_templates.contains(fa.field) or
const resolved = self.ufcs_aliases.get(fa.field) orelse fa.field;
const method_z = self.allocator.dupeZ(u8, resolved) catch return null;
if (self.generic_templates.contains(resolved) or
c.LLVMGetNamedFunction(self.module, method_z.ptr) != null)
{
return fa.field;
return resolved;
}
// Intra-namespace fallback
if (self.current_namespace) |ns| {
const qualified = std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, resolved }) catch return null;
const qz = self.allocator.dupeZ(u8, qualified) catch return null;
if (self.generic_templates.contains(qualified) or
c.LLVMGetNamedFunction(self.module, qz.ptr) != null)
{
return qualified;
}
}
}
return null;