test: make zig build test actually run all tests + fix latent rot
root.zig had no `test` block, so the test binary discovered zero tests and trivially "passed" — every src test had silently rotted. Add `refAllDecls(@This())` to root.zig so all 185 tests run, then fix the rot it surfaced: - emit_llvm.test: operands were constants, so LLVM folded the very instructions being asserted (fadd/sub/icmp/insertvalue/extractvalue/sext). Rewrite to use function-parameter operands; `main` now returns i32 (entry convention); tagged-union enum_init lowers via memory, not insertvalue. - interp.test: switch the per-test allocator to an arena (the interpreter is arena-style and intentionally frees little) — clears the transient-Value leaks without an ownership-ambiguous source change. - lower.test: pass `is_imported` to lowerFunction; mark two helpers `pub`; the if/else block test now uses a runtime (param) condition since lowering folds `if true`. - print.test: SSA numbering — params occupy %0/%1, so consts start at %2. - jni_java_emit.test: nested-class refs render in Java source form (`SurfaceHolder.Callback`), not the JNI `$` form. Leaks fixed at the source where ownership was clear: Module gains an arena for the operand slices the Builder dupes (struct/call/branch/switch args, block params, lowerFunction params); objcDefinedStateStructType builds its field slice in that arena and frees its temp name string.
This commit is contained in:
@@ -1216,17 +1216,21 @@ pub const Lowering = struct {
|
||||
|
||||
const wants_ctx = self.funcWantsImplicitCtx(fd);
|
||||
|
||||
// Build param list
|
||||
// Build param list. `Function.init` borrows the slice (it does not
|
||||
// dupe), so this storage must outlive the local — build it in the
|
||||
// module's slice arena (freed at module deinit) rather than via
|
||||
// `self.alloc`, which would leak (Function.deinit never frees params).
|
||||
const param_alloc = self.module.slice_arena.allocator();
|
||||
var params = std.ArrayList(Function.Param).empty;
|
||||
if (wants_ctx) {
|
||||
params.append(self.alloc, .{
|
||||
params.append(param_alloc, .{
|
||||
.name = self.module.types.internString("__sx_ctx"),
|
||||
.ty = self.module.types.ptrTo(.void),
|
||||
}) catch unreachable;
|
||||
}
|
||||
for (fd.params) |p| {
|
||||
const pty = self.resolveParamType(&p);
|
||||
params.append(self.alloc, .{
|
||||
params.append(param_alloc, .{
|
||||
.name = self.module.types.internString(p.name),
|
||||
.ty = pty,
|
||||
}) catch unreachable;
|
||||
@@ -5054,7 +5058,7 @@ pub const Lowering = struct {
|
||||
/// patterns rule).
|
||||
///
|
||||
/// Returns an allocator-owned slice; caller frees via `self.alloc`.
|
||||
fn objcTypeEncodingFromSignature(
|
||||
pub fn objcTypeEncodingFromSignature(
|
||||
self: *Lowering,
|
||||
return_ty: TypeId,
|
||||
param_tys: []const TypeId,
|
||||
@@ -5268,11 +5272,16 @@ pub const Lowering = struct {
|
||||
/// Foreign-class members other than `.field` are ignored here —
|
||||
/// methods / `#extends` / `#implements` don't contribute to the
|
||||
/// state layout.
|
||||
fn objcDefinedStateStructType(self: *Lowering, fcd: *const ast.ForeignClassDecl) TypeId {
|
||||
pub fn objcDefinedStateStructType(self: *Lowering, fcd: *const ast.ForeignClassDecl) TypeId {
|
||||
const state_name = std.fmt.allocPrint(self.alloc, "__{s}State", .{fcd.name}) catch unreachable;
|
||||
defer self.alloc.free(state_name); // internString copies; the temp isn't needed after.
|
||||
const name_id = self.module.types.internString(state_name);
|
||||
if (self.module.types.findByName(name_id)) |existing| return existing;
|
||||
|
||||
// The interned struct's `fields` slice lives for the module's lifetime;
|
||||
// allocate it (and the building ArrayList) in the module arena so it's
|
||||
// freed at module deinit rather than leaking through `self.alloc`.
|
||||
const field_alloc = self.module.slice_arena.allocator();
|
||||
var fields = std.ArrayList(types.TypeInfo.StructInfo.Field).empty;
|
||||
// M4.0: prepend __sx_allocator at field index 0 — captured at +alloc
|
||||
// time, read at -dealloc time to free the state struct through the
|
||||
@@ -5280,7 +5289,7 @@ pub const Lowering = struct {
|
||||
// emitObjcDefinedClassPropertyImps + lookupObjcDefinedStateFieldOnPointer)
|
||||
// naturally finds user fields at their post-shift indices.
|
||||
if (self.objcStateAllocatorType()) |allocator_ty| {
|
||||
fields.append(self.alloc, .{
|
||||
fields.append(field_alloc, .{
|
||||
.name = self.module.types.internString("__sx_allocator"),
|
||||
.ty = allocator_ty,
|
||||
}) catch unreachable;
|
||||
@@ -5290,14 +5299,14 @@ pub const Lowering = struct {
|
||||
.field => |f| {
|
||||
const f_name_id = self.module.types.internString(f.name);
|
||||
const f_ty = self.resolveType(f.field_type);
|
||||
fields.append(self.alloc, .{ .name = f_name_id, .ty = f_ty }) catch unreachable;
|
||||
fields.append(field_alloc, .{ .name = f_name_id, .ty = f_ty }) catch unreachable;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
return self.module.types.intern(.{ .@"struct" = .{
|
||||
.name = name_id,
|
||||
.fields = fields.toOwnedSlice(self.alloc) catch unreachable,
|
||||
.fields = fields.toOwnedSlice(field_alloc) catch unreachable,
|
||||
} });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user