lang: rename signed integer types sN -> iN

Surface rename of the signed integer family: s1..s64 become i1..i64
(u1..u64, usize, isize unchanged). 'string' keeps the s-prefix arm in
name classification; width parsing moves to the i-prefix arm next to
isize.

Internal TypeId tags follow the surface (.s8/.s16/.s32/.s64 ->
.i8/.i16/.i32/.i64), as do mono-key mangle fragments (ptr_i64,
tu_i64_bool) and all display/diagnostic formatting (i{d}).

Migrated in the same sweep: stdlib + examples + issue repros + FFI C
companions (shared symbol names like ffi_id_i64), expected
stdout/stderr/ir snapshots, specs.md, readme.md, CLAUDE.md/AGENTS.md,
implementation_plan.md, docs/, issue writeups. Vendored stb_image and
historical flow state left untouched.

zig build test: 426/426; examples suite: 595/595.
This commit is contained in:
agra
2026-06-12 09:31:53 +03:00
parent 515ecebea7
commit d8076b9333
1054 changed files with 6836 additions and 6839 deletions

View File

@@ -29,12 +29,12 @@ test "emit: main() returns 42" {
var b = Builder.init(&module);
// func main() -> s64 { return 42; }
_ = b.beginFunction(str(&module, "main"), &.{}, .s64);
// func main() -> i64 { return 42; }
_ = b.beginFunction(str(&module, "main"), &.{}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
const c42 = b.constInt(42, .s64);
b.ret(c42, .s64);
const c42 = b.constInt(42, .i64);
b.ret(c42, .i64);
b.finalize();
// Emit to LLVM
@@ -49,7 +49,7 @@ test "emit: main() returns 42" {
const ir_str = emitter.dumpToString();
try std.testing.expect(std.mem.indexOf(u8, ir_str, "define") != null);
// `main` is emitted with the C entry-point convention: it returns i32, so
// the s64 const 42 is truncated to `ret i32 42`.
// the i64 const 42 is truncated to `ret i32 42`.
try std.testing.expect(std.mem.indexOf(u8, ir_str, "ret i32 42") != null);
}
@@ -60,12 +60,12 @@ test "emit: add(a, b) returns a + b" {
var b = Builder.init(&module);
// func add(a: s64, b: s64) -> s64 { return a + b; }
// func add(a: i64, b: i64) -> i64 { return a + b; }
const params = &[_]Function.Param{
.{ .name = str(&module, "a"), .ty = .s64 },
.{ .name = str(&module, "b"), .ty = .s64 },
.{ .name = str(&module, "a"), .ty = .i64 },
.{ .name = str(&module, "b"), .ty = .i64 },
};
_ = b.beginFunction(str(&module, "add"), params, .s64);
_ = b.beginFunction(str(&module, "add"), params, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
@@ -78,10 +78,10 @@ test "emit: add(a, b) returns a + b" {
// at 0, and params are accessed differently. The lowering pass emits
// alloca+store for params. For this test, we use const_int to test
// the add instruction directly.
const a = b.constInt(10, .s64);
const a_b = b.constInt(32, .s64);
const sum = b.add(a, a_b, .s64);
b.ret(sum, .s64);
const a = b.constInt(10, .i64);
const a_b = b.constInt(32, .i64);
const sum = b.add(a, a_b, .i64);
b.ret(sum, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_add", .{});
@@ -138,14 +138,14 @@ test "emit: negation" {
// Negating a constant folds; negate a param so `sub 0, %x` is emitted.
_ = b.beginFunction(str(&module, "negate"), &[_]Function.Param{
.{ .name = str(&module, "x"), .ty = .s64 },
}, .s64);
.{ .name = str(&module, "x"), .ty = .i64 },
}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
const val = Ref.fromIndex(0);
const neg = b.emit(.{ .neg = .{ .operand = val } }, .s64);
b.ret(neg, .s64);
const neg = b.emit(.{ .neg = .{ .operand = val } }, .i64);
b.ret(neg, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_neg", .{});
@@ -189,16 +189,16 @@ test "emit: alloca, store, load" {
var b = Builder.init(&module);
// func f() -> s64 { var x: s64 = 10; return x; }
_ = b.beginFunction(str(&module, "f"), &.{}, .s64);
// func f() -> i64 { var x: i64 = 10; return x; }
_ = b.beginFunction(str(&module, "f"), &.{}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
const x_ptr = b.alloca(.s64); // alloca s64 → *s64
const ten = b.constInt(10, .s64);
const x_ptr = b.alloca(.i64); // alloca i64 → *i64
const ten = b.constInt(10, .i64);
b.store(x_ptr, ten); // store 10 → *x
const loaded = b.load(x_ptr, .s64); // load *x → s64
b.ret(loaded, .s64);
const loaded = b.load(x_ptr, .i64); // load *x → i64
b.ret(loaded, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_mem", .{});
@@ -221,12 +221,12 @@ test "emit: comparison and branch" {
var b = Builder.init(&module);
// func f(a, b) -> s64 { if (a < b) return 1; else return 0; }
// func f(a, b) -> i64 { if (a < b) return 1; else return 0; }
// Params (not constants) so the icmp isn't folded.
_ = b.beginFunction(str(&module, "cmpfn"), &[_]Function.Param{
.{ .name = str(&module, "a"), .ty = .s64 },
.{ .name = str(&module, "b"), .ty = .s64 },
}, .s64);
.{ .name = str(&module, "a"), .ty = .i64 },
.{ .name = str(&module, "b"), .ty = .i64 },
}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
const then_bb = b.appendBlock(str(&module, "then"), &.{});
const else_bb = b.appendBlock(str(&module, "else"), &.{});
@@ -238,12 +238,12 @@ test "emit: comparison and branch" {
b.condBr(cond, then_bb, &.{}, else_bb, &.{});
b.switchToBlock(then_bb);
const one = b.constInt(1, .s64);
b.ret(one, .s64);
const one = b.constInt(1, .i64);
b.ret(one, .i64);
b.switchToBlock(else_bb);
const zero = b.constInt(0, .s64);
b.ret(zero, .s64);
const zero = b.constInt(0, .i64);
b.ret(zero, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_cmp", .{});
@@ -264,27 +264,27 @@ test "emit: function call" {
var b = Builder.init(&module);
// func add(a: s64, b: s64) -> s64 { return a + b; } (using constants)
// func add(a: i64, b: i64) -> i64 { return a + b; } (using constants)
const add_id = b.beginFunction(str(&module, "addfn"), &[_]Function.Param{
.{ .name = str(&module, "a"), .ty = .s64 },
.{ .name = str(&module, "b"), .ty = .s64 },
}, .s64);
.{ .name = str(&module, "a"), .ty = .i64 },
.{ .name = str(&module, "b"), .ty = .i64 },
}, .i64);
const add_entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(add_entry);
const p0 = b.constInt(0, .s64); // placeholder
const p1 = b.constInt(0, .s64);
const sum = b.add(p0, p1, .s64);
b.ret(sum, .s64);
const p0 = b.constInt(0, .i64); // placeholder
const p1 = b.constInt(0, .i64);
const sum = b.add(p0, p1, .i64);
b.ret(sum, .i64);
b.finalize();
// func main() -> s64 { return addfn(3, 4); }
_ = b.beginFunction(str(&module, "main"), &.{}, .s64);
// func main() -> i64 { return addfn(3, 4); }
_ = b.beginFunction(str(&module, "main"), &.{}, .i64);
const main_entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(main_entry);
const three = b.constInt(3, .s64);
const four = b.constInt(4, .s64);
const result = b.call(add_id, &.{ three, four }, .s64);
b.ret(result, .s64);
const three = b.constInt(3, .i64);
const four = b.constInt(4, .i64);
const result = b.call(add_id, &.{ three, four }, .i64);
b.ret(result, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_call", .{});
@@ -298,7 +298,7 @@ test "emit: function call" {
try std.testing.expect(std.mem.indexOf(u8, ir_str, "addfn") != null);
}
test "emit: widen conversion s32 to s64" {
test "emit: widen conversion i32 to i64" {
const alloc = std.testing.allocator;
var module = Module.init(alloc);
defer module.deinit();
@@ -307,14 +307,14 @@ test "emit: widen conversion s32 to s64" {
// sext of a constant folds; widen a param so `sext` is emitted.
_ = b.beginFunction(str(&module, "wfn"), &[_]Function.Param{
.{ .name = str(&module, "x"), .ty = .s32 },
}, .s64);
.{ .name = str(&module, "x"), .ty = .i32 },
}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
const val = Ref.fromIndex(0);
const wide = b.widen(val, .s32, .s64);
b.ret(wide, .s64);
const wide = b.widen(val, .i32, .i64);
b.ret(wide, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_widen", .{});
@@ -338,10 +338,10 @@ test "emit: type conversion toLLVMType" {
// Just verify toLLVMType doesn't crash for all builtin types
_ = emitter.toLLVMType(.void);
_ = emitter.toLLVMType(.bool);
_ = emitter.toLLVMType(.s8);
_ = emitter.toLLVMType(.s16);
_ = emitter.toLLVMType(.s32);
_ = emitter.toLLVMType(.s64);
_ = emitter.toLLVMType(.i8);
_ = emitter.toLLVMType(.i16);
_ = emitter.toLLVMType(.i32);
_ = emitter.toLLVMType(.i64);
_ = emitter.toLLVMType(.u8);
_ = emitter.toLLVMType(.u16);
_ = emitter.toLLVMType(.u32);
@@ -381,12 +381,12 @@ test "emit: abiCoerceParamType coerces C-ABI structs by size bucket" {
defer module.deinit();
// Intern the shapes before building the emitter (toLLVMType reads live).
const small = internStruct(&module, "Small", &.{ .s32, .s32 }); // 8 bytes
const mid = internStruct(&module, "Mid", &.{ .s64, .s64 }); // 16 bytes
const big = internStruct(&module, "Big", &.{ .s64, .s64, .s64 }); // 24 bytes
const small = internStruct(&module, "Small", &.{ .i32, .i32 }); // 8 bytes
const mid = internStruct(&module, "Mid", &.{ .i64, .i64 }); // 16 bytes
const big = internStruct(&module, "Big", &.{ .i64, .i64, .i64 }); // 24 bytes
const hfa_f = internStruct(&module, "HfaF", &.{ .f32, .f32, .f32, .f32 }); // 16, all-float
const hfa_d = internStruct(&module, "HfaD", &.{ .f64, .f64 }); // 16, all-double
const sl = module.types.sliceOf(.s32);
const sl = module.types.sliceOf(.i32);
var emitter = LLVMEmitter.init(alloc, &module, "test_abi", .{});
defer emitter.deinit();
@@ -404,7 +404,7 @@ test "emit: abiCoerceParamType coerces C-ABI structs by size bucket" {
try std.testing.expect(emitter.abiCoerceParamType(.string, emitter.toLLVMType(.string)) == emitter.cached_ptr);
try std.testing.expect(emitter.abiCoerceParamType(sl, emitter.toLLVMType(sl)) == emitter.cached_ptr);
// Scalars pass through unchanged.
try std.testing.expect(emitter.abiCoerceParamType(.s32, emitter.toLLVMType(.s32)) == emitter.toLLVMType(.s32));
try std.testing.expect(emitter.abiCoerceParamType(.i32, emitter.toLLVMType(.i32)) == emitter.toLLVMType(.i32));
}
test "emit: needsByval only for > 16-byte non-HFA structs" {
@@ -412,11 +412,11 @@ test "emit: needsByval only for > 16-byte non-HFA structs" {
var module = Module.init(alloc);
defer module.deinit();
const small = internStruct(&module, "Small", &.{ .s32, .s32 });
const mid = internStruct(&module, "Mid", &.{ .s64, .s64 });
const big = internStruct(&module, "Big", &.{ .s64, .s64, .s64 });
const small = internStruct(&module, "Small", &.{ .i32, .i32 });
const mid = internStruct(&module, "Mid", &.{ .i64, .i64 });
const big = internStruct(&module, "Big", &.{ .i64, .i64, .i64 });
const hfa_d = internStruct(&module, "HfaD", &.{ .f64, .f64 });
const sl = module.types.sliceOf(.s32);
const sl = module.types.sliceOf(.i32);
var emitter = LLVMEmitter.init(alloc, &module, "test_byval", .{});
defer emitter.deinit();
@@ -427,7 +427,7 @@ test "emit: needsByval only for > 16-byte non-HFA structs" {
try std.testing.expect(!emitter.needsByval(hfa_d, emitter.toLLVMType(hfa_d))); // HFA
try std.testing.expect(!emitter.needsByval(.string, emitter.toLLVMType(.string)));
try std.testing.expect(!emitter.needsByval(sl, emitter.toLLVMType(sl)));
try std.testing.expect(!emitter.needsByval(.s32, emitter.toLLVMType(.s32))); // non-struct
try std.testing.expect(!emitter.needsByval(.i32, emitter.toLLVMType(.i32))); // non-struct
}
// ── Struct/Enum/Union tests ─────────────────────────────────────────
@@ -437,10 +437,10 @@ test "emit: struct_init and struct_get" {
var module = Module.init(alloc);
defer module.deinit();
// Create a struct type: Point { x: s64, y: s64 }
// Create a struct type: Point { x: i64, y: i64 }
const fields = &[_]types.TypeInfo.StructInfo.Field{
.{ .name = str(&module, "x"), .ty = .s64 },
.{ .name = str(&module, "y"), .ty = .s64 },
.{ .name = str(&module, "x"), .ty = .i64 },
.{ .name = str(&module, "y"), .ty = .i64 },
};
const owned_fields = alloc.dupe(types.TypeInfo.StructInfo.Field, fields) catch unreachable;
defer alloc.free(owned_fields);
@@ -451,20 +451,20 @@ test "emit: struct_init and struct_get" {
var b = Builder.init(&module);
// func f(v) -> s64 { p = Point{v, 20}; return p.y; }
// func f(v) -> i64 { p = Point{v, 20}; return p.y; }
// A param operand keeps the aggregate non-constant so insertvalue /
// extractvalue survive (a fully-constant struct would be folded).
_ = b.beginFunction(str(&module, "f"), &[_]Function.Param{
.{ .name = str(&module, "v"), .ty = .s64 },
}, .s64);
.{ .name = str(&module, "v"), .ty = .i64 },
}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
const x = Ref.fromIndex(0);
const y = b.constInt(20, .s64);
const y = b.constInt(20, .i64);
const p = b.structInit(&.{ x, y }, point_ty);
const py = b.structGet(p, 1, .s64);
b.ret(py, .s64);
const py = b.structGet(p, 1, .i64);
b.ret(py, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_struct", .{});
@@ -486,8 +486,8 @@ test "emit: struct_gep (pointer to field)" {
// Create struct type
const fields = &[_]types.TypeInfo.StructInfo.Field{
.{ .name = str(&module, "x"), .ty = .s64 },
.{ .name = str(&module, "y"), .ty = .s64 },
.{ .name = str(&module, "x"), .ty = .i64 },
.{ .name = str(&module, "y"), .ty = .i64 },
};
const owned_fields = alloc.dupe(types.TypeInfo.StructInfo.Field, fields) catch unreachable;
defer alloc.free(owned_fields);
@@ -495,21 +495,21 @@ test "emit: struct_gep (pointer to field)" {
.name = str(&module, "Point"),
.fields = owned_fields,
} });
const ptr_s64 = module.types.ptrTo(.s64);
const ptr_i64 = module.types.ptrTo(.i64);
var b = Builder.init(&module);
// func f() -> s64 { var p: Point; p.y = 42; return p.y; }
_ = b.beginFunction(str(&module, "gepfn"), &.{}, .s64);
// func f() -> i64 { var p: Point; p.y = 42; return p.y; }
_ = b.beginFunction(str(&module, "gepfn"), &.{}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
const p_ptr = b.alloca(point_ty);
const y_ptr = b.structGepTyped(p_ptr, 1, ptr_s64, point_ty);
const c42 = b.constInt(42, .s64);
const y_ptr = b.structGepTyped(p_ptr, 1, ptr_i64, point_ty);
const c42 = b.constInt(42, .i64);
b.store(y_ptr, c42);
const loaded = b.load(y_ptr, .s64);
b.ret(loaded, .s64);
const loaded = b.load(y_ptr, .i64);
b.ret(loaded, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_gep", .{});
@@ -544,16 +544,16 @@ test "emit: enum_init and enum_tag (plain enum)" {
var b = Builder.init(&module);
// func f() -> s64 { c = Color.Green; return tag(c); }
_ = b.beginFunction(str(&module, "enumfn"), &.{}, .s64);
// func f() -> i64 { c = Color.Green; return tag(c); }
_ = b.beginFunction(str(&module, "enumfn"), &.{}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
const green = b.enumInit(1, Ref.none, color_ty); // Green = tag 1
const tag = b.enumTag(green, .s32);
// Widen tag from s32 to s64 for the return
const wide = b.widen(tag, .s32, .s64);
b.ret(wide, .s64);
const tag = b.enumTag(green, .i32);
// Widen tag from i32 to i64 for the return
const wide = b.widen(tag, .i32, .i64);
b.ret(wide, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_enum", .{});
@@ -572,17 +572,17 @@ test "emit: tagged union (enum_init with payload, enum_tag, enum_payload)" {
var module = Module.init(alloc);
defer module.deinit();
// Create a tagged union: Shape { Circle: f64, Rect: s64 }
// Create a tagged union: Shape { Circle: f64, Rect: i64 }
const ufields = &[_]types.TypeInfo.StructInfo.Field{
.{ .name = str(&module, "Circle"), .ty = .f64 },
.{ .name = str(&module, "Rect"), .ty = .s64 },
.{ .name = str(&module, "Rect"), .ty = .i64 },
};
const owned_ufields = alloc.dupe(types.TypeInfo.StructInfo.Field, ufields) catch unreachable;
defer alloc.free(owned_ufields);
const shape_ty = module.types.intern(.{ .tagged_union = .{
.name = str(&module, "Shape"),
.fields = owned_ufields,
.tag_type = .s64,
.tag_type = .i64,
} });
var b = Builder.init(&module);
@@ -597,7 +597,7 @@ test "emit: tagged union (enum_init with payload, enum_tag, enum_payload)" {
const radius = Ref.fromIndex(0);
const shape = b.enumInit(0, radius, shape_ty); // Circle = tag 0
const tag = b.emit(.{ .enum_tag = .{ .operand = shape } }, .s64);
const tag = b.emit(.{ .enum_tag = .{ .operand = shape } }, .i64);
_ = tag; // tag is used but we just check it doesn't crash
const payload = b.emit(.{ .enum_payload = .{ .base = shape, .field_index = 0 } }, .f64);
b.ret(payload, .f64);
@@ -623,9 +623,9 @@ test "emit: union_get (reinterpret union field)" {
var module = Module.init(alloc);
defer module.deinit();
// Untagged union: Data { as_int: s64, as_float: f64 }
// Untagged union: Data { as_int: i64, as_float: f64 }
const ufields = &[_]types.TypeInfo.StructInfo.Field{
.{ .name = str(&module, "as_int"), .ty = .s64 },
.{ .name = str(&module, "as_int"), .ty = .i64 },
.{ .name = str(&module, "as_float"), .ty = .f64 },
};
const owned_ufields = alloc.dupe(types.TypeInfo.StructInfo.Field, ufields) catch unreachable;
@@ -637,15 +637,15 @@ test "emit: union_get (reinterpret union field)" {
var b = Builder.init(&module);
// func f() -> s64 { d = Data.as_int(42); return union_get(d, 0) as s64; }
_ = b.beginFunction(str(&module, "ugfn"), &.{}, .s64);
// func f() -> i64 { d = Data.as_int(42); return union_get(d, 0) as i64; }
_ = b.beginFunction(str(&module, "ugfn"), &.{}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
const val = b.constInt(42, .s64);
const val = b.constInt(42, .i64);
const d = b.enumInit(0, val, data_ty);
const got = b.emit(.{ .union_get = .{ .base = d, .field_index = 0 } }, .s64);
b.ret(got, .s64);
const got = b.emit(.{ .union_get = .{ .base = d, .field_index = 0 } }, .i64);
b.ret(got, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_union_get", .{});
@@ -667,19 +667,19 @@ test "emit: array index_get" {
var module = Module.init(alloc);
defer module.deinit();
const arr_ty = module.types.arrayOf(.s64, 3);
const arr_ty = module.types.arrayOf(.i64, 3);
var b = Builder.init(&module);
// func f() -> s64 { arr: [3]s64 = ---; return arr[1]; }
_ = b.beginFunction(str(&module, "arr_idx"), &.{}, .s64);
// func f() -> i64 { arr: [3]i64 = ---; return arr[1]; }
_ = b.beginFunction(str(&module, "arr_idx"), &.{}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
const undef_arr = b.emit(.{ .const_undef = {} }, arr_ty);
const idx = b.constInt(1, .s64);
const elem = b.emit(.{ .index_get = .{ .lhs = undef_arr, .rhs = idx } }, .s64);
b.ret(elem, .s64);
const idx = b.constInt(1, .i64);
const elem = b.emit(.{ .index_get = .{ .lhs = undef_arr, .rhs = idx } }, .i64);
b.ret(elem, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_arr_idx", .{});
@@ -700,17 +700,17 @@ test "emit: length on slice" {
var b = Builder.init(&module);
// func f(s: string) -> s64 { return s.len; }
// func f(s: string) -> i64 { return s.len; }
// A string param keeps the value non-constant so extractvalue survives.
_ = b.beginFunction(str(&module, "strlen"), &[_]Function.Param{
.{ .name = str(&module, "s"), .ty = .string },
}, .s64);
}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
const s = Ref.fromIndex(0);
const len = b.emit(.{ .length = .{ .operand = s } }, .s64);
b.ret(len, .s64);
const len = b.emit(.{ .length = .{ .operand = s } }, .i64);
b.ret(len, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_len", .{});
@@ -762,12 +762,12 @@ test "emit: array_to_slice" {
var module = Module.init(alloc);
defer module.deinit();
const arr_ty = module.types.arrayOf(.s64, 4);
const slice_ty = module.types.sliceOf(.s64);
const arr_ty = module.types.arrayOf(.i64, 4);
const slice_ty = module.types.sliceOf(.i64);
var b = Builder.init(&module);
// func f() -> []s64 { var arr: [4]s64 = ---; return arr[:]; }
// func f() -> []i64 { var arr: [4]i64 = ---; return arr[:]; }
_ = b.beginFunction(str(&module, "a2s"), &.{}, slice_ty);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
@@ -798,13 +798,13 @@ test "emit: subslice" {
var b = Builder.init(&module);
// func f(s: []u8, lo: s64, hi: s64) -> []u8 { return s[lo..hi]; }
// func f(s: []u8, lo: i64, hi: i64) -> []u8 { return s[lo..hi]; }
// All operands are params: a constant base folds the GEP, and constant
// lo/hi fold the `hi - lo` subtraction.
_ = b.beginFunction(str(&module, "ssfn"), &[_]Function.Param{
.{ .name = str(&module, "s"), .ty = slice_ty },
.{ .name = str(&module, "lo"), .ty = .s64 },
.{ .name = str(&module, "hi"), .ty = .s64 },
.{ .name = str(&module, "lo"), .ty = .i64 },
.{ .name = str(&module, "hi"), .ty = .i64 },
}, slice_ty);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
@@ -836,22 +836,22 @@ test "emit: optional_wrap and optional_unwrap (value type)" {
var module = Module.init(alloc);
defer module.deinit();
const opt_ty = module.types.optionalOf(.s64);
const opt_ty = module.types.optionalOf(.i64);
var b = Builder.init(&module);
// func f(v) -> s64 { opt = wrap(v); return unwrap(opt); }
// func f(v) -> i64 { opt = wrap(v); return unwrap(opt); }
// Param value keeps the optional non-constant (else insertvalue folds).
_ = b.beginFunction(str(&module, "optfn"), &[_]Function.Param{
.{ .name = str(&module, "v"), .ty = .s64 },
}, .s64);
.{ .name = str(&module, "v"), .ty = .i64 },
}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
const val = Ref.fromIndex(0);
const wrapped = b.optionalWrap(val, opt_ty);
const unwrapped = b.optionalUnwrap(wrapped, .s64);
b.ret(unwrapped, .s64);
const unwrapped = b.optionalUnwrap(wrapped, .i64);
b.ret(unwrapped, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_opt", .{});
@@ -872,13 +872,13 @@ test "emit: optional_has_value" {
var module = Module.init(alloc);
defer module.deinit();
const opt_ty = module.types.optionalOf(.s64);
const opt_ty = module.types.optionalOf(.i64);
var b = Builder.init(&module);
// Param value keeps the optional non-constant (else extractvalue folds).
_ = b.beginFunction(str(&module, "hasfn"), &[_]Function.Param{
.{ .name = str(&module, "v"), .ty = .s64 },
.{ .name = str(&module, "v"), .ty = .i64 },
}, .bool);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
@@ -909,15 +909,15 @@ test "emit: switch_br" {
var b = Builder.init(&module);
// func f(x: s64) -> s64 { match x { 0 => 10, 1 => 20, _ => 30 } }
_ = b.beginFunction(str(&module, "swfn"), &.{}, .s64);
// func f(x: i64) -> i64 { match x { 0 => 10, 1 => 20, _ => 30 } }
_ = b.beginFunction(str(&module, "swfn"), &.{}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
const case0 = b.appendBlock(str(&module, "case0"), &.{});
const case1 = b.appendBlock(str(&module, "case1"), &.{});
const default_bb = b.appendBlock(str(&module, "default"), &.{});
b.switchToBlock(entry);
const x = b.constInt(1, .s64);
const x = b.constInt(1, .i64);
const cases = alloc.dupe(inst_mod.SwitchBranch.Case, &.{
.{ .value = 0, .target = case0, .args = &.{} },
.{ .value = 1, .target = case1, .args = &.{} },
@@ -931,13 +931,13 @@ test "emit: switch_br" {
} }, .void);
b.switchToBlock(case0);
b.ret(b.constInt(10, .s64), .s64);
b.ret(b.constInt(10, .i64), .i64);
b.switchToBlock(case1);
b.ret(b.constInt(20, .s64), .s64);
b.ret(b.constInt(20, .i64), .i64);
b.switchToBlock(default_bb);
b.ret(b.constInt(30, .s64), .s64);
b.ret(b.constInt(30, .i64), .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_switch", .{});
@@ -957,18 +957,18 @@ test "emit: closure_create" {
var module = Module.init(alloc);
defer module.deinit();
const closure_ty = module.types.closureType(&.{.s64}, .s64);
const closure_ty = module.types.closureType(&.{.i64}, .i64);
var b = Builder.init(&module);
// Create a dummy trampoline function
const tramp_id = b.beginFunction(str(&module, "tramp"), &[_]inst_mod.Function.Param{
.{ .name = str(&module, "env"), .ty = .s64 },
.{ .name = str(&module, "x"), .ty = .s64 },
}, .s64);
.{ .name = str(&module, "env"), .ty = .i64 },
.{ .name = str(&module, "x"), .ty = .i64 },
}, .i64);
const tramp_entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(tramp_entry);
b.ret(b.constInt(0, .s64), .s64);
b.ret(b.constInt(0, .i64), .i64);
b.finalize();
// func f(e: *void) -> closure { return closure_create(tramp, e); }
@@ -1004,18 +1004,18 @@ test "emit: box_any and unbox_any" {
var b = Builder.init(&module);
// func f(v) -> s64 { a = box(v); return unbox(a); }
// func f(v) -> i64 { a = box(v); return unbox(a); }
// Param value keeps the boxed Any non-constant (else insertvalue folds).
_ = b.beginFunction(str(&module, "anyfn"), &[_]Function.Param{
.{ .name = str(&module, "v"), .ty = .s64 },
}, .s64);
.{ .name = str(&module, "v"), .ty = .i64 },
}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
const val = Ref.fromIndex(0);
const boxed = b.emit(.{ .box_any = .{ .operand = val, .source_type = .s64 } }, .any);
const unboxed = b.emit(.{ .unbox_any = .{ .operand = boxed } }, .s64);
b.ret(unboxed, .s64);
const boxed = b.emit(.{ .box_any = .{ .operand = val, .source_type = .i64 } }, .any);
const unboxed = b.emit(.{ .unbox_any = .{ .operand = boxed } }, .i64);
b.ret(unboxed, .i64);
b.finalize();
var emitter = LLVMEmitter.init(alloc, &module, "test_any", .{});
@@ -1036,15 +1036,15 @@ test "emit: ERR E3.0 — DWARF debug info (compile unit + subprogram + per-inst
var b = Builder.init(&module);
// func main() -> s64 { return 42; } — with the `return` instruction
// func main() -> i64 { return 42; } — with the `return` instruction
// carrying a span that lands on line 3 of the source map below.
_ = b.beginFunction(str(&module, "main"), &.{}, .s64);
_ = b.beginFunction(str(&module, "main"), &.{}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
// "a\nb\nXYZ" — byte offset 4 ('X') is line 3, col 1.
b.current_span = .{ .start = 4, .end = 5 };
const c42 = b.constInt(42, .s64);
b.ret(c42, .s64);
const c42 = b.constInt(42, .i64);
b.ret(c42, .i64);
b.finalize();
// Source map keyed on the main file. setDebugContext + opt none
@@ -1080,10 +1080,10 @@ test "emit: ERR E3.0 — no DWARF without a debug context (unit-test default)" {
defer module.deinit();
var b = Builder.init(&module);
_ = b.beginFunction(str(&module, "main"), &.{}, .s64);
_ = b.beginFunction(str(&module, "main"), &.{}, .i64);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
b.ret(b.constInt(42, .s64), .s64);
b.ret(b.constInt(42, .i64), .i64);
b.finalize();
// No setDebugContext call → no source map → debug info off even at
@@ -1111,9 +1111,9 @@ test "emit: argIRTypeOrFail surfaces .unresolved for an unresolvable FFI arg ref
var b = Builder.init(&module);
// func ffifn(a: s64, b: f64) -> void { <entry> }
// func ffifn(a: i64, b: f64) -> void { <entry> }
const fid = b.beginFunction(str(&module, "ffifn"), &[_]Function.Param{
.{ .name = str(&module, "a"), .ty = .s64 },
.{ .name = str(&module, "a"), .ty = .i64 },
.{ .name = str(&module, "b"), .ty = .f64 },
}, .void);
const entry = b.appendBlock(str(&module, "entry"), &.{});
@@ -1127,7 +1127,7 @@ test "emit: argIRTypeOrFail surfaces .unresolved for an unresolvable FFI arg ref
// Happy path: a real arg ref (param 0 / param 1) resolves byte-identically
// to its declared IR type — the FFI fast path is unchanged.
try std.testing.expectEqual(TypeId.s64, emitter.argIRTypeOrFail(Ref.fromIndex(0)));
try std.testing.expectEqual(TypeId.i64, emitter.argIRTypeOrFail(Ref.fromIndex(0)));
try std.testing.expectEqual(TypeId.f64, emitter.argIRTypeOrFail(Ref.fromIndex(1)));
// A ref past every param and instruction is unresolvable.
@@ -1145,12 +1145,12 @@ test "emit: argIRTypeOrFail surfaces .unresolved for an unresolvable FFI arg ref
try std.testing.expect(emitter.argIRTypeOrFail(bogus) != .void);
}
// ── reflection-builtin arg-type lookup must fail loudly, never `.s64` ──
// ── reflection-builtin arg-type lookup must fail loudly, never `.i64` ──
// `reflectArgRepr` backs the `type_name` / `type_eq` reflection builtins, which read
// their `Type` arg as a boxed `Any` aggregate (`.any` → extract value field) or a bare
// i64 TypeId index. A ref it cannot resolve is a codegen invariant violation; it must
// surface `.unresolved` (which the emit site hard-panics on) instead of the old silent
// `getRefIRType(arg) orelse .s64` default that would mis-classify a boxed arg as bare
// `getRefIRType(arg) orelse .i64` default that would mis-classify a boxed arg as bare
// and read the wrong value with no diagnostic.
test "emit: reflectArgRepr surfaces .unresolved for an unresolvable reflection arg ref (issue 0075)" {
const alloc = std.testing.allocator;
@@ -1159,10 +1159,10 @@ test "emit: reflectArgRepr surfaces .unresolved for an unresolvable reflection a
var b = Builder.init(&module);
// func reflfn(boxed: any, bare: s64) -> void { <entry> }
// func reflfn(boxed: any, bare: i64) -> void { <entry> }
const fid = b.beginFunction(str(&module, "reflfn"), &[_]Function.Param{
.{ .name = str(&module, "boxed"), .ty = .any },
.{ .name = str(&module, "bare"), .ty = .s64 },
.{ .name = str(&module, "bare"), .ty = .i64 },
}, .void);
const entry = b.appendBlock(str(&module, "entry"), &.{});
b.switchToBlock(entry);
@@ -1174,7 +1174,7 @@ test "emit: reflectArgRepr surfaces .unresolved for an unresolvable reflection a
emitter.current_func_idx = fid.index();
// Happy path: a boxed `.any` Type arg classifies as `.boxed` (extract value
// field); a bare `.s64` TypeId arg classifies as `.bare` (use directly).
// field); a bare `.i64` TypeId arg classifies as `.bare` (use directly).
// These decisions are byte-identical to the pre-fix `== .any` gate.
try std.testing.expectEqual(LLVMEmitter.ReflectArgRepr.boxed, emitter.reflectArgRepr(Ref.fromIndex(0)));
try std.testing.expectEqual(LLVMEmitter.ReflectArgRepr.bare, emitter.reflectArgRepr(Ref.fromIndex(1)));
@@ -1183,11 +1183,11 @@ test "emit: reflectArgRepr surfaces .unresolved for an unresolvable reflection a
const bogus = Ref.fromIndex(100_000);
try std.testing.expectEqual(@as(?TypeId, null), emitter.getRefIRType(bogus));
// Fail-before: the old `getRefIRType(arg) orelse .s64` would silently yield
// `.s64` here — which `!= .any`, so the reflection arm would treat a failed
// Fail-before: the old `getRefIRType(arg) orelse .i64` would silently yield
// `.i64` here — which `!= .any`, so the reflection arm would treat a failed
// lookup as a bare i64 and read the wrong value with no diagnostic.
try std.testing.expectEqual(TypeId.s64, emitter.getRefIRType(bogus) orelse TypeId.s64);
try std.testing.expect((emitter.getRefIRType(bogus) orelse TypeId.s64) != .any);
try std.testing.expectEqual(TypeId.i64, emitter.getRefIRType(bogus) orelse TypeId.i64);
try std.testing.expect((emitter.getRefIRType(bogus) orelse TypeId.i64) != .any);
// Pass-after: the classifier returns the dedicated `.unresolved` variant,
// never `.bare`, so the emit site trips its hard panic instead of silently