...
This commit is contained in:
@@ -1414,11 +1414,11 @@ END;
|
||||
{
|
||||
gpa_state : GPA = .{ alloc_count = 0 };
|
||||
gpa := gpa_create(@gpa_state);
|
||||
p1 := gpa.alloc(gpa.ctx, 64);
|
||||
p2 := gpa.alloc(gpa.ctx, 128);
|
||||
p1 := gpa.alloc(64);
|
||||
p2 := gpa.alloc(128);
|
||||
print("gpa allocs: {}\n", gpa_state.alloc_count); // gpa allocs: 2
|
||||
gpa.free(gpa.ctx, p1);
|
||||
gpa.free(gpa.ctx, p2);
|
||||
gpa.dealloc(p1);
|
||||
gpa.dealloc(p2);
|
||||
print("gpa final: {}\n", gpa_state.alloc_count); // gpa final: 0
|
||||
}
|
||||
|
||||
@@ -1429,11 +1429,11 @@ END;
|
||||
arena_state : Arena = ---;
|
||||
arena := arena_create(@arena_state, gpa3, 32);
|
||||
// First chunk fits 80 usable bytes
|
||||
a1 := arena.alloc(arena.ctx, 40);
|
||||
a2 := arena.alloc(arena.ctx, 40);
|
||||
a1 := arena.alloc(40);
|
||||
a2 := arena.alloc(40);
|
||||
print("arena chunks: {}\n", gpa_state3.alloc_count); // arena chunks: 1
|
||||
// Overflow → new chunk
|
||||
a3 := arena.alloc(arena.ctx, 16);
|
||||
a3 := arena.alloc(16);
|
||||
print("arena overflow: {}\n", gpa_state3.alloc_count); // arena overflow: 2
|
||||
// Verify memory works across chunks
|
||||
p1 : [*]u8 = xx a1;
|
||||
@@ -1456,15 +1456,29 @@ END;
|
||||
stack_buf : [128]u8 = ---;
|
||||
buf_state : BufAlloc = ---;
|
||||
bufalloc := buf_create(@buf_state, @stack_buf[0], 128);
|
||||
b1 := bufalloc.alloc(bufalloc.ctx, 24);
|
||||
b2 := bufalloc.alloc(bufalloc.ctx, 24);
|
||||
b1 := bufalloc.alloc(24);
|
||||
b2 := bufalloc.alloc(24);
|
||||
print("buf pos: {}\n", buf_state.pos); // buf pos: 48
|
||||
b3 := bufalloc.alloc(bufalloc.ctx, 200);
|
||||
b3 := bufalloc.alloc(200);
|
||||
b3_i : s64 = xx b3;
|
||||
print("buf overflow: {}\n", b3_i); // buf overflow: 0
|
||||
buf_reset(@buf_state);
|
||||
print("buf reset: {}\n", buf_state.pos); // buf reset: 0
|
||||
}
|
||||
|
||||
{
|
||||
if 1 == (1,) {
|
||||
print("1 == (1)\n");
|
||||
}
|
||||
|
||||
if (1,) == (1) {
|
||||
print("(1) == 1\n");
|
||||
}
|
||||
|
||||
if (1,) == 1 {
|
||||
print("1 == 1\n");
|
||||
}
|
||||
}
|
||||
|
||||
print("=== DONE ===\n");
|
||||
}
|
||||
|
||||
@@ -4,10 +4,21 @@
|
||||
|
||||
Allocator :: struct {
|
||||
ctx: *void;
|
||||
alloc: (*void, s64) -> *void;
|
||||
free: (*void, *void) -> void;
|
||||
alloc_fn: (*void, s64) -> *void;
|
||||
free_fn: (*void, *void) -> void;
|
||||
}
|
||||
|
||||
allocator_alloc :: (a: Allocator, size: s64) -> *void {
|
||||
a.alloc_fn(a.ctx, size);
|
||||
}
|
||||
|
||||
allocator_dealloc :: (a: Allocator, ptr: *void) {
|
||||
a.free_fn(a.ctx, ptr);
|
||||
}
|
||||
|
||||
alloc :: ufcs allocator_alloc;
|
||||
dealloc :: ufcs allocator_dealloc;
|
||||
|
||||
// --- GPA: general purpose allocator (malloc/free wrapper) ---
|
||||
|
||||
GPA :: struct {
|
||||
@@ -27,7 +38,7 @@ gpa_free :: (ctx: *void, ptr: *void) {
|
||||
}
|
||||
|
||||
gpa_create :: (gpa: *GPA) -> Allocator {
|
||||
Allocator.{ ctx = gpa, alloc = gpa_alloc, free = gpa_free };
|
||||
Allocator.{ ctx = gpa, alloc_fn = gpa_alloc, free_fn = gpa_free };
|
||||
}
|
||||
|
||||
// --- Arena: multi-chunk bump allocator (Zig-inspired) ---
|
||||
@@ -47,7 +58,7 @@ arena_add_chunk :: (a: *Arena, min_size: s64) {
|
||||
prev_cap := if a.first != null then a.first.cap else 0;
|
||||
needed := min_size + 16 + 16;
|
||||
len := (prev_cap + needed) * 3 / 2;
|
||||
raw := a.parent.alloc(a.parent.ctx, len);
|
||||
raw := a.parent.alloc(len);
|
||||
chunk : *ArenaChunk = xx raw;
|
||||
chunk.next = a.first;
|
||||
chunk.cap = len;
|
||||
@@ -82,7 +93,7 @@ arena_create :: (a: *Arena, parent: Allocator, size: s64) -> Allocator {
|
||||
a.end_index = 0;
|
||||
a.parent = parent;
|
||||
arena_add_chunk(a, size);
|
||||
Allocator.{ ctx = a, alloc = arena_alloc, free = arena_free };
|
||||
Allocator.{ ctx = a, alloc_fn = arena_alloc, free_fn = arena_free };
|
||||
}
|
||||
|
||||
arena_reset :: (a: *Arena) {
|
||||
@@ -91,7 +102,7 @@ arena_reset :: (a: *Arena) {
|
||||
it := a.first.next;
|
||||
while it != null {
|
||||
next := it.next;
|
||||
a.parent.free(a.parent.ctx, it);
|
||||
a.parent.dealloc(it);
|
||||
it = next;
|
||||
}
|
||||
a.first.next = null;
|
||||
@@ -103,7 +114,7 @@ arena_deinit :: (a: *Arena) {
|
||||
it := a.first;
|
||||
while it != null {
|
||||
next := it.next;
|
||||
a.parent.free(a.parent.ctx, it);
|
||||
a.parent.dealloc(it);
|
||||
it = next;
|
||||
}
|
||||
a.first = null;
|
||||
@@ -136,7 +147,7 @@ buf_create :: (b: *BufAlloc, buf: [*]u8, len: s64) -> Allocator {
|
||||
b.buf = buf;
|
||||
b.len = len;
|
||||
b.pos = 0;
|
||||
Allocator.{ ctx = b, alloc = buf_alloc, free = buf_free };
|
||||
Allocator.{ ctx = b, alloc_fn = buf_alloc, free_fn = buf_free };
|
||||
}
|
||||
|
||||
buf_reset :: (b: *BufAlloc) {
|
||||
|
||||
@@ -31,8 +31,12 @@ context : Context = ---;
|
||||
|
||||
// --- Slice & string allocation ---
|
||||
|
||||
context_alloc :: (size: s64) -> *void {
|
||||
if context.allocator.ctx != null then context.allocator.alloc(size) else malloc(size);
|
||||
}
|
||||
|
||||
cstring :: (size: s64) -> string {
|
||||
raw := if context.allocator.ctx != null then context.allocator.alloc(context.allocator.ctx, size + 1) else malloc(size + 1);
|
||||
raw := context_alloc(size + 1);
|
||||
memset(raw, 0, size + 1);
|
||||
s : string = ---;
|
||||
s.ptr = xx raw;
|
||||
@@ -41,7 +45,7 @@ cstring :: (size: s64) -> string {
|
||||
}
|
||||
|
||||
alloc_slice :: ($T: Type, count: s64) -> []T {
|
||||
raw := if context.allocator.ctx != null then context.allocator.alloc(context.allocator.ctx, count * size_of(T)) else malloc(count * size_of(T));
|
||||
raw := context_alloc(count * size_of(T));
|
||||
memset(raw, 0, count * size_of(T));
|
||||
s : []T = ---;
|
||||
s.ptr = xx raw;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -383,4 +383,7 @@ arena deinit: 0
|
||||
buf pos: 48
|
||||
buf overflow: 0
|
||||
buf reset: 0
|
||||
1 == (1)
|
||||
(1) == 1
|
||||
1 == 1
|
||||
=== DONE ===
|
||||
|
||||
Reference in New Issue
Block a user