From 4b5f8bca5e8fb13f36850e2581dd7c413a6b1958 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 15 Dec 2021 16:05:25 +0100 Subject: [PATCH 1/3] stage2: clean up tests * move darwin tests into respective architecture test files: `x86_64` and `aarch64` * run majority of `x86_64` tests on macOS --- src/arch/x86_64/CodeGen.zig | 3 +- test/cases.zig | 1837 +------------------------------- test/stage2/aarch64.zig | 122 ++- test/stage2/darwin.zig | 154 --- test/stage2/x86_64.zig | 1976 +++++++++++++++++++++++++++++++++++ 5 files changed, 2100 insertions(+), 1992 deletions(-) delete mode 100644 test/stage2/darwin.zig create mode 100644 test/stage2/x86_64.zig diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 4580395fd8..2de37b05bc 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2835,11 +2835,12 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro } const abi_size = ty.abiSize(self.target.*); const adj_off = stack_offset + abi_size; + // TODO select instruction size _ = try self.addInst(.{ .tag = .mov, .ops = (Mir.Ops{ .reg1 = registerAlias(reg, @intCast(u32, abi_size)), - .reg2 = .ebp, + .reg2 = .rbp, .flags = 0b10, }).encode(), .data = .{ .imm = -@intCast(i32, adj_off) }, diff --git a/test/cases.zig b/test/cases.zig index 4a0810ef8a..20dece3c7d 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -6,11 +6,6 @@ const TestContext = @import("../src/test.zig").TestContext; // a set of targets that all test cases run on unless specifically overridden. For now, each test // case applies to only the specified target. -const linux_x64 = std.zig.CrossTarget{ - .cpu_arch = .x86_64, - .os_tag = .linux, -}; - pub fn addCases(ctx: *TestContext) !void { try @import("compile_errors.zig").addCases(ctx); try @import("stage2/cbe.zig").addCases(ctx); @@ -18,1837 +13,7 @@ pub fn addCases(ctx: *TestContext) !void { try @import("stage2/aarch64.zig").addCases(ctx); try @import("stage2/llvm.zig").addCases(ctx); try @import("stage2/wasm.zig").addCases(ctx); - try @import("stage2/darwin.zig").addCases(ctx); try @import("stage2/riscv64.zig").addCases(ctx); try @import("stage2/plan9.zig").addCases(ctx); - - { - var case = ctx.exe("hello world with updates", linux_x64); - - case.addError("", &[_][]const u8{ - ":99:9: error: struct 'tmp.tmp' has no member named 'main'", - }); - - // Incorrect return type - case.addError( - \\pub export fn _start() noreturn { - \\} - , &[_][]const u8{":2:1: error: expected noreturn, found void"}); - - // Regular old hello world - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ print(); - \\ - \\ exit(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{rdx}" (14) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "Hello, World!\n", - ); - - // Convert to pub fn main - case.addCompareOutput( - \\pub fn main() void { - \\ print(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{rdx}" (14) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - "Hello, World!\n", - ); - - // Now change the message only - case.addCompareOutput( - \\pub fn main() void { - \\ print(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")), - \\ [arg3] "{rdx}" (104) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", - ); - // Now we print it twice. - case.addCompareOutput( - \\pub fn main() void { - \\ print(); - \\ print(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")), - \\ [arg3] "{rdx}" (104) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - \\What is up? This is a longer message that will force the data to be relocated in virtual address space. - \\What is up? This is a longer message that will force the data to be relocated in virtual address space. - \\ - ); - } - - { - var case = ctx.exe("adding numbers at comptime", linux_x64); - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{rdx}" (10 + 4) - \\ : "rcx", "r11", "memory" - \\ ); - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (@as(usize, 230) + @as(usize, 1)), - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "Hello, World!\n", - ); - } - - { - var case = ctx.exe("adding numbers at runtime and comptime", linux_x64); - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ add(3, 4); - \\ - \\ exit(); - \\} - \\ - \\fn add(a: u32, b: u32) void { - \\ if (a + b != 7) unreachable; - \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - // comptime function call - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ exit(); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ return a + b; - \\} - \\ - \\const x = add(3, 4); - \\ - \\fn exit() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (x - 7) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - // Inline function call - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ var x: usize = 3; - \\ const y = add(1, 2, x); - \\ exit(y - 6); - \\} - \\ - \\fn add(a: usize, b: usize, c: usize) callconv(.Inline) usize { - \\ return a + b + c; - \\} - \\ - \\fn exit(code: usize) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("subtracting numbers at runtime", linux_x64); - case.addCompareOutput( - \\pub fn main() void { - \\ sub(7, 4); - \\} - \\ - \\fn sub(a: u32, b: u32) void { - \\ if (a - b != 3) unreachable; - \\} - , - "", - ); - } - { - var case = ctx.exe("unused vars", linux_x64); - case.addError( - \\pub fn main() void { - \\ const x = 1; - \\} - , &.{":2:11: error: unused local constant"}); - } - { - var case = ctx.exe("@TypeOf", linux_x64); - case.addCompareOutput( - \\pub fn main() void { - \\ var x: usize = 0; - \\ _ = x; - \\ const z = @TypeOf(x, @as(u128, 5)); - \\ assert(z == u128); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - case.addCompareOutput( - \\pub fn main() void { - \\ const z = @TypeOf(true); - \\ assert(z == bool); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - case.addError( - \\pub fn main() void { - \\ _ = @TypeOf(true, 1); - \\} - , &[_][]const u8{ - ":2:9: error: incompatible types: 'bool' and 'comptime_int'", - ":2:17: note: type 'bool' here", - ":2:23: note: type 'comptime_int' here", - }); - } - - { - var case = ctx.exe("multiplying numbers at runtime and comptime", linux_x64); - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ mul(3, 4); - \\ - \\ exit(); - \\} - \\ - \\fn mul(a: u32, b: u32) void { - \\ if (a * b != 12) unreachable; - \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - // comptime function call - case.addCompareOutput( - \\pub fn _start() noreturn { - \\ exit(); - \\} - \\ - \\fn mul(a: u32, b: u32) u32 { - \\ return a * b; - \\} - \\ - \\const x = mul(3, 4); - \\ - \\fn exit() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (x - 12) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - // Inline function call - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ var x: usize = 5; - \\ const y = mul(2, 3, x); - \\ exit(y - 30); - \\} - \\ - \\fn mul(a: usize, b: usize, c: usize) callconv(.Inline) usize { - \\ return a * b * c; - \\} - \\ - \\fn exit(code: usize) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("assert function", linux_x64); - case.addCompareOutput( - \\pub fn main() void { - \\ add(3, 4); - \\} - \\ - \\fn add(a: u32, b: u32) void { - \\ assert(a + b == 7); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - - // Tests copying a register. For the `c = a + b`, it has to - // preserve both a and b, because they are both used later. - case.addCompareOutput( - \\pub fn main() void { - \\ add(3, 4); - \\} - \\ - \\fn add(a: u32, b: u32) void { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ assert(e == 14); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // More stress on the liveness detection. - case.addCompareOutput( - \\pub fn main() void { - \\ add(3, 4); - \\} - \\ - \\fn add(a: u32, b: u32) void { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ const f = d + e; // 24 - \\ const g = e + f; // 38 - \\ const h = f + g; // 62 - \\ const i = g + h; // 100 - \\ assert(i == 100); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Requires a second move. The register allocator should figure out to re-use rax. - case.addCompareOutput( - \\pub fn main() void { - \\ add(3, 4); - \\} - \\ - \\fn add(a: u32, b: u32) void { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ const f = d + e; // 24 - \\ const g = e + f; // 38 - \\ const h = f + g; // 62 - \\ const i = g + h; // 100 - \\ const j = i + d; // 110 - \\ assert(j == 110); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Now we test integer return values. - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 7); - \\ assert(add(20, 10) == 30); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ return a + b; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Local mutable variables. - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 7); - \\ assert(add(20, 10) == 30); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ var x: u32 = undefined; - \\ x = 0; - \\ x += a; - \\ x += b; - \\ return x; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Optionals - case.addCompareOutput( - \\pub fn main() void { - \\ const a: u32 = 2; - \\ const b: ?u32 = a; - \\ const c = b.?; - \\ if (c != 2) unreachable; - \\} - , - "", - ); - - // While loops - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u32 = 0; - \\ while (i < 4) : (i += 1) print(); - \\ assert(i == 4); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("hello\n")), - \\ [arg3] "{rdx}" (6) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "hello\nhello\nhello\nhello\n", - ); - - // inline while requires the condition to be comptime known. - case.addError( - \\pub fn main() void { - \\ var i: u32 = 0; - \\ inline while (i < 4) : (i += 1) print(); - \\ assert(i == 4); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("hello\n")), - \\ [arg3] "{rdx}" (6) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , &[_][]const u8{":3:21: error: unable to resolve comptime value"}); - - // Labeled blocks (no conditional branch) - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 20); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ const x: u32 = blk: { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ break :blk e; - \\ }; - \\ const y = x + a; // 17 - \\ const z = y + a; // 20 - \\ return z; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // This catches a possible bug in the logic for re-using dying operands. - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 116); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ const x: u32 = blk: { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ const f = d + e; // 24 - \\ const g = e + f; // 38 - \\ const h = f + g; // 62 - \\ const i = g + h; // 100 - \\ const j = i + d; // 110 - \\ break :blk j; - \\ }; - \\ const y = x + a; // 113 - \\ const z = y + a; // 116 - \\ return z; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Spilling registers to the stack. - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 1221); - \\ assert(mul(3, 4) == 21609); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ const x: u32 = blk: { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ const f = d + e; // 24 - \\ const g = e + f; // 38 - \\ const h = f + g; // 62 - \\ const i = g + h; // 100 - \\ const j = i + d; // 110 - \\ const k = i + j; // 210 - \\ const l = j + k; // 320 - \\ const m = l + c; // 327 - \\ const n = m + d; // 337 - \\ const o = n + e; // 351 - \\ const p = o + f; // 375 - \\ const q = p + g; // 413 - \\ const r = q + h; // 475 - \\ const s = r + i; // 575 - \\ const t = s + j; // 685 - \\ const u = t + k; // 895 - \\ const v = u + l; // 1215 - \\ break :blk v; - \\ }; - \\ const y = x + a; // 1218 - \\ const z = y + a; // 1221 - \\ return z; - \\} - \\ - \\fn mul(a: u32, b: u32) u32 { - \\ const x: u32 = blk: { - \\ const c = a * a * a * a; // 81 - \\ const d = a * a * a * b; // 108 - \\ const e = a * a * b * a; // 108 - \\ const f = a * a * b * b; // 144 - \\ const g = a * b * a * a; // 108 - \\ const h = a * b * a * b; // 144 - \\ const i = a * b * b * a; // 144 - \\ const j = a * b * b * b; // 192 - \\ const k = b * a * a * a; // 108 - \\ const l = b * a * a * b; // 144 - \\ const m = b * a * b * a; // 144 - \\ const n = b * a * b * b; // 192 - \\ const o = b * b * a * a; // 144 - \\ const p = b * b * a * b; // 192 - \\ const q = b * b * b * a; // 192 - \\ const r = b * b * b * b; // 256 - \\ const s = c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r; // 2401 - \\ break :blk s; - \\ }; - \\ const y = x * a; // 7203 - \\ const z = y * a; // 21609 - \\ return z; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Reusing the registers of dead operands playing nicely with conditional branching. - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 791); - \\ assert(add(4, 3) == 79); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ const x: u32 = if (a < b) blk: { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ const f = d + e; // 24 - \\ const g = e + f; // 38 - \\ const h = f + g; // 62 - \\ const i = g + h; // 100 - \\ const j = i + d; // 110 - \\ const k = i + j; // 210 - \\ const l = k + c; // 217 - \\ const m = l + d; // 227 - \\ const n = m + e; // 241 - \\ const o = n + f; // 265 - \\ const p = o + g; // 303 - \\ const q = p + h; // 365 - \\ const r = q + i; // 465 - \\ const s = r + j; // 575 - \\ const t = s + k; // 785 - \\ break :blk t; - \\ } else blk: { - \\ const t = b + b + a; // 10 - \\ const c = a + t; // 14 - \\ const d = c + t; // 24 - \\ const e = d + t; // 34 - \\ const f = e + t; // 44 - \\ const g = f + t; // 54 - \\ const h = c + g; // 68 - \\ break :blk h + b; // 71 - \\ }; - \\ const y = x + a; // 788, 75 - \\ const z = y + a; // 791, 79 - \\ return z; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Character literals and multiline strings. - case.addCompareOutput( - \\pub fn main() void { - \\ const ignore = - \\ \\ cool thx - \\ \\ - \\ ; - \\ _ = ignore; - \\ add('ぁ', '\x03'); - \\} - \\ - \\fn add(a: u32, b: u32) void { - \\ assert(a + b == 12356); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Global const. - case.addCompareOutput( - \\pub fn main() void { - \\ add(aa, bb); - \\} - \\ - \\const aa = 'ぁ'; - \\const bb = '\x03'; - \\ - \\fn add(a: u32, b: u32) void { - \\ assert(a + b == 12356); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Array access. - case.addCompareOutput( - \\pub fn main() void { - \\ assert("hello"[0] == 'h'); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Array access to a global array. - case.addCompareOutput( - \\const hello = "hello".*; - \\pub fn main() void { - \\ assert(hello[1] == 'e'); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // 64bit set stack - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u64 = 0xFFEEDDCCBBAA9988; - \\ assert(i == 0xFFEEDDCCBBAA9988); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Basic for loop - case.addCompareOutput( - \\pub fn main() void { - \\ for ("hello") |_| print(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("hello\n")), - \\ [arg3] "{rdx}" (6) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - "hello\nhello\nhello\nhello\nhello\n", - ); - } - - { - var case = ctx.exe("basic import", linux_x64); - case.addCompareOutput( - \\pub fn main() void { - \\ @import("print.zig").print(); - \\} - , - "Hello, World!\n", - ); - try case.files.append(.{ - .src = - \\pub fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (@as(usize, 1)), - \\ [arg1] "{rdi}" (@as(usize, 1)), - \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{rdx}" (@as(usize, 14)) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - .path = "print.zig", - }); - } - { - var case = ctx.exe("redundant comptime", linux_x64); - case.addError( - \\pub fn main() void { - \\ var a: comptime u32 = 0; - \\} - , - &.{":2:12: error: redundant comptime keyword in already comptime scope"}, - ); - case.addError( - \\pub fn main() void { - \\ comptime { - \\ var a: u32 = comptime 0; - \\ } - \\} - , - &.{":3:22: error: redundant comptime keyword in already comptime scope"}, - ); - } - { - var case = ctx.exe("try in comptime in struct in test", linux_x64); - case.addError( - \\test "@unionInit on union w/ tag but no fields" { - \\ const S = struct { - \\ comptime { - \\ try expect(false); - \\ } - \\ }; - \\ _ = S; - \\} - , - &.{":4:13: error: 'try' outside function scope"}, - ); - } - { - var case = ctx.exe("import private", linux_x64); - case.addError( - \\pub fn main() void { - \\ @import("print.zig").print(); - \\} - , - &.{ - ":2:25: error: 'print' is not marked 'pub'", - "print.zig:2:1: note: declared here", - }, - ); - try case.files.append(.{ - .src = - \\// dummy comment to make print be on line 2 - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (@as(usize, 1)), - \\ [arg1] "{rdi}" (@as(usize, 1)), - \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{rdx}" (@as(usize, 14)) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - .path = "print.zig", - }); - } - - ctx.compileError("function redeclaration", linux_x64, - \\// dummy comment - \\fn entry() void {} - \\fn entry() void {} - \\ - \\fn foo() void { - \\ var foo = 1234; - \\} - , &[_][]const u8{ - ":3:1: error: redeclaration of 'entry'", - ":2:1: note: other declaration here", - ":6:9: error: local shadows declaration of 'foo'", - ":5:1: note: declared here", - }); - - ctx.compileError("returns in try", linux_x64, - \\pub fn main() !void { - \\ try a(); - \\ try b(); - \\} - \\ - \\pub fn a() !void { - \\ defer try b(); - \\} - \\pub fn b() !void { - \\ defer return a(); - \\} - , &[_][]const u8{ - ":7:8: error: 'try' not allowed inside defer expression", - ":10:8: error: cannot return from defer expression", - }); - - ctx.compileError("ambiguous references", linux_x64, - \\const T = struct { - \\ const T = struct { - \\ fn f() void { - \\ _ = T; - \\ } - \\ }; - \\}; - , &.{ - ":4:17: error: ambiguous reference", - ":2:5: note: declared here", - ":1:1: note: also declared here", - }); - - ctx.compileError("inner func accessing outer var", linux_x64, - \\pub fn f() void { - \\ var bar: bool = true; - \\ const S = struct { - \\ fn baz() bool { - \\ return bar; - \\ } - \\ }; - \\ _ = S; - \\} - , &.{ - ":5:20: error: mutable 'bar' not accessible from here", - ":2:9: note: declared mutable here", - ":3:15: note: crosses namespace boundary here", - }); - - ctx.compileError("global variable redeclaration", linux_x64, - \\// dummy comment - \\var foo = false; - \\var foo = true; - , &[_][]const u8{ - ":3:1: error: redeclaration of 'foo'", - ":2:1: note: other declaration here", - }); - - ctx.compileError("compileError", linux_x64, - \\export fn foo() void { - \\ @compileError("this is an error"); - \\} - , &[_][]const u8{":2:3: error: this is an error"}); - - { - var case = ctx.exe("intToPtr", linux_x64); - case.addError( - \\pub fn main() void { - \\ _ = @intToPtr(*u8, 0); - \\} - , &[_][]const u8{ - ":2:24: error: pointer type '*u8' does not allow address zero", - }); - case.addError( - \\pub fn main() void { - \\ _ = @intToPtr(*u32, 2); - \\} - , &[_][]const u8{ - ":2:25: error: pointer type '*u32' requires aligned address", - }); - } - - { - var case = ctx.obj("variable shadowing", linux_x64); - case.addError( - \\pub fn main() void { - \\ var i: u32 = 10; - \\ var i: u32 = 10; - \\} - , &[_][]const u8{ - ":3:9: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\var testing: i64 = 10; - \\pub fn main() void { - \\ var testing: i64 = 20; - \\} - , &[_][]const u8{ - ":3:9: error: local shadows declaration of 'testing'", - ":1:1: note: declared here", - }); - case.addError( - \\fn a() type { - \\ return struct { - \\ pub fn b() void { - \\ const c = 6; - \\ const c = 69; - \\ } - \\ }; - \\} - , &[_][]const u8{ - ":5:19: error: redeclaration of local constant 'c'", - ":4:19: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ for ("n") |_, i| { - \\ } - \\} - , &[_][]const u8{ - ":3:19: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ for ("n") |i| { - \\ } - \\} - , &[_][]const u8{ - ":3:16: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ while ("n") |i| { - \\ } - \\} - , &[_][]const u8{ - ":3:18: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ while ("n") |bruh| { - \\ _ = bruh; - \\ } else |i| { - \\ - \\ } - \\} - , &[_][]const u8{ - ":5:13: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ if (true) |i| {} - \\} - , &[_][]const u8{ - ":3:16: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ if (true) |i| {} else |e| {} - \\} - , &[_][]const u8{ - ":3:16: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ if (true) |_| {} else |i| {} - \\} - , &[_][]const u8{ - ":3:28: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - } - - { - // TODO make the test harness support checking the compile log output too - var case = ctx.obj("@compileLog", linux_x64); - // The other compile error prevents emission of a "found compile log" statement. - case.addError( - \\export fn _start() noreturn { - \\ const b = true; - \\ var f: u32 = 1; - \\ @compileLog(b, 20, f, x); - \\ @compileLog(1000); - \\ var bruh: usize = true; - \\ _ = bruh; - \\ unreachable; - \\} - \\export fn other() void { - \\ @compileLog(1234); - \\} - \\fn x() void {} - , &[_][]const u8{ - ":6:23: error: expected usize, found bool", - }); - - // Now only compile log statements remain. One per Decl. - case.addError( - \\export fn _start() noreturn { - \\ const b = true; - \\ var f: u32 = 1; - \\ @compileLog(b, 20, f, x); - \\ @compileLog(1000); - \\ unreachable; - \\} - \\export fn other() void { - \\ @compileLog(1234); - \\} - \\fn x() void {} - , &[_][]const u8{ - ":9:5: error: found compile log statement", - ":4:5: note: also here", - }); - } - - { - var case = ctx.obj("extern variable has no type", linux_x64); - case.addError( - \\comptime { - \\ const x = foo + foo; - \\ _ = x; - \\} - \\extern var foo: i32; - , &[_][]const u8{":2:15: error: unable to resolve comptime value"}); - case.addError( - \\export fn entry() void { - \\ _ = foo; - \\} - \\extern var foo; - , &[_][]const u8{":4:8: error: unable to infer variable type"}); - } - - { - var case = ctx.exe("break/continue", linux_x64); - - // Break out of loop - case.addCompareOutput( - \\pub fn main() void { - \\ while (true) { - \\ break; - \\ } - \\} - , - "", - ); - case.addCompareOutput( - \\pub fn main() void { - \\ foo: while (true) { - \\ break :foo; - \\ } - \\} - , - "", - ); - - // Continue in loop - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ var i: u64 = 0; - \\ while (true) : (i+=1) { - \\ if (i == 4) exit(); - \\ continue; - \\ } - \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ var i: u64 = 0; - \\ foo: while (true) : (i+=1) { - \\ if (i == 4) exit(); - \\ continue :foo; - \\ } - \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("unused labels", linux_x64); - case.addError( - \\comptime { - \\ foo: {} - \\} - , &[_][]const u8{":2:5: error: unused block label"}); - case.addError( - \\comptime { - \\ foo: while (true) {} - \\} - , &[_][]const u8{":2:5: error: unused while loop label"}); - case.addError( - \\comptime { - \\ foo: for ("foo") |_| {} - \\} - , &[_][]const u8{":2:5: error: unused for loop label"}); - case.addError( - \\comptime { - \\ blk: {blk: {}} - \\} - , &[_][]const u8{ - ":2:11: error: redefinition of label 'blk'", - ":2:5: note: previous definition here", - }); - } - - { - var case = ctx.exe("bad inferred variable type", linux_x64); - case.addError( - \\pub fn main() void { - \\ var x = null; - \\ _ = x; - \\} - , &[_][]const u8{ - ":2:9: error: variable of type '@Type(.Null)' must be const or comptime", - }); - } - - { - var case = ctx.exe("compile error in inline fn call fixed", linux_x64); - case.addError( - \\pub export fn _start() noreturn { - \\ var x: usize = 3; - \\ const y = add(10, 2, x); - \\ exit(y - 6); - \\} - \\ - \\fn add(a: usize, b: usize, c: usize) callconv(.Inline) usize { - \\ if (a == 10) @compileError("bad"); - \\ return a + b + c; - \\} - \\ - \\fn exit(code: usize) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , &[_][]const u8{":8:18: error: bad"}); - - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ var x: usize = 3; - \\ const y = add(1, 2, x); - \\ exit(y - 6); - \\} - \\ - \\fn add(a: usize, b: usize, c: usize) callconv(.Inline) usize { - \\ if (a == 10) @compileError("bad"); - \\ return a + b + c; - \\} - \\ - \\fn exit(code: usize) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - } - { - var case = ctx.exe("recursive inline function", linux_x64); - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ const y = fibonacci(7); - \\ exit(y - 21); - \\} - \\ - \\fn fibonacci(n: usize) callconv(.Inline) usize { - \\ if (n <= 2) return n; - \\ return fibonacci(n - 2) + fibonacci(n - 1); - \\} - \\ - \\fn exit(code: usize) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - // This additionally tests that the compile error reports the correct source location. - // Without storing source locations relative to the owner decl, the compile error - // here would be off by 2 bytes (from the "7" -> "999"). - case.addError( - \\pub export fn _start() noreturn { - \\ const y = fibonacci(999); - \\ exit(y - 21); - \\} - \\ - \\fn fibonacci(n: usize) callconv(.Inline) usize { - \\ if (n <= 2) return n; - \\ return fibonacci(n - 2) + fibonacci(n - 1); - \\} - \\ - \\fn exit(code: usize) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , &[_][]const u8{":8:21: error: evaluation exceeded 1000 backwards branches"}); - } - { - var case = ctx.exe("orelse at comptime", linux_x64); - case.addCompareOutput( - \\pub fn main() void { - \\ const i: ?u64 = 0; - \\ const result = i orelse 5; - \\ assert(result == 0); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - case.addCompareOutput( - \\pub fn main() void { - \\ const i: ?u64 = null; - \\ const result = i orelse 5; - \\ assert(result == 5); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("only 1 function and it gets updated", linux_x64); - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (60), // exit - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), // exit_group - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - } - { - var case = ctx.exe("passing u0 to function", linux_x64); - case.addCompareOutput( - \\pub fn main() void { - \\ doNothing(0); - \\} - \\fn doNothing(arg: u0) void { - \\ _ = arg; - \\} - , - "", - ); - } - { - var case = ctx.exe("catch at comptime", linux_x64); - case.addCompareOutput( - \\pub fn main() void { - \\ const i: anyerror!u64 = 0; - \\ const caught = i catch 5; - \\ assert(caught == 0); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - - case.addCompareOutput( - \\pub fn main() void { - \\ const i: anyerror!u64 = error.B; - \\ const caught = i catch 5; - \\ assert(caught == 5); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - - case.addCompareOutput( - \\pub fn main() void { - \\ const a: anyerror!comptime_int = 42; - \\ const b: *const comptime_int = &(a catch unreachable); - \\ assert(b.* == 42); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; // assertion failure - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ const a: anyerror!u32 = error.B; - \\ _ = &(a catch |err| assert(err == error.B)); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ const a: anyerror!u32 = error.Bar; - \\ a catch |err| assert(err == error.Bar); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , ""); - } - { - var case = ctx.exe("runtime bitwise and", linux_x64); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u32 = 10; - \\ var j: u32 = 11; - \\ assert(i & 1 == 0); - \\ assert(j & 1 == 1); - \\ var m1: u32 = 0b1111; - \\ var m2: u32 = 0b0000; - \\ assert(m1 & 0b1010 == 0b1010); - \\ assert(m2 & 0b1010 == 0b0000); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - } - { - var case = ctx.exe("runtime bitwise or", linux_x64); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u32 = 10; - \\ var j: u32 = 11; - \\ assert(i | 1 == 11); - \\ assert(j | 1 == 11); - \\ var m1: u32 = 0b1111; - \\ var m2: u32 = 0b0000; - \\ assert(m1 | 0b1010 == 0b1111); - \\ assert(m2 | 0b1010 == 0b1010); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - } - { - var case = ctx.exe("merge error sets", linux_x64); - - case.addCompareOutput( - \\pub fn main() void { - \\ const E = error{ A, B, D } || error { A, B, C }; - \\ E.A catch {}; - \\ E.B catch {}; - \\ E.C catch {}; - \\ E.D catch {}; - \\ const E2 = error { X, Y } || @TypeOf(error.Z); - \\ E2.X catch {}; - \\ E2.Y catch {}; - \\ E2.Z catch {}; - \\ assert(anyerror || error { Z } == anyerror); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - case.addError( - \\pub fn main() void { - \\ const z = true || false; - \\ _ = z; - \\} - , &.{ - ":2:15: error: expected error set type, found 'bool'", - ":2:20: note: '||' merges error sets; 'or' performs boolean OR", - }); - } - { - var case = ctx.exe("error set equality", linux_x64); - - case.addCompareOutput( - \\pub fn main() void { - \\ assert(@TypeOf(error.Foo) == @TypeOf(error.Foo)); - \\ assert(@TypeOf(error.Bar) != @TypeOf(error.Foo)); - \\ assert(anyerror == anyerror); - \\ assert(error{Foo} != error{Foo}); - \\ // TODO put inferred error sets here when @typeInfo works - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - } - { - var case = ctx.exe("inline assembly", linux_x64); - - case.addError( - \\pub fn main() void { - \\ const number = 1234; - \\ const x = asm volatile ("syscall" - \\ : [o] "{rax}" (-> number) - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (60) - \\ : "rcx", "r11", "memory" - \\ ); - \\ _ = x; - \\} - , &[_][]const u8{":4:27: error: expected type, found comptime_int"}); - case.addError( - \\const S = struct { - \\ comptime { - \\ asm volatile ( - \\ \\zig_moment: - \\ \\syscall - \\ ); - \\ } - \\}; - \\pub fn main() void { - \\ _ = S; - \\} - , &.{":3:13: error: volatile is meaningless on global assembly"}); - case.addError( - \\pub fn main() void { - \\ var bruh: u32 = 1; - \\ asm ("" - \\ : - \\ : [bruh] "{rax}" (4) - \\ : "memory" - \\ ); - \\} - , &.{":3:5: error: assembly expression with no output must be marked volatile"}); - case.addError( - \\pub fn main() void {} - \\comptime { - \\ asm ("" - \\ : - \\ : [bruh] "{rax}" (4) - \\ : "memory" - \\ ); - \\} - , &.{":3:5: error: global assembly cannot have inputs, outputs, or clobbers"}); - } - { - var case = ctx.exe("comptime var", linux_x64); - - case.addError( - \\pub fn main() void { - \\ var a: u32 = 0; - \\ comptime var b: u32 = 0; - \\ if (a == 0) b = 3; - \\} - , &.{ - ":4:21: error: store to comptime variable depends on runtime condition", - ":4:11: note: runtime condition here", - }); - - case.addError( - \\pub fn main() void { - \\ var a: u32 = 0; - \\ comptime var b: u32 = 0; - \\ switch (a) { - \\ 0 => {}, - \\ else => b = 3, - \\ } - \\} - , &.{ - ":6:21: error: store to comptime variable depends on runtime condition", - ":4:13: note: runtime condition here", - }); - - case.addCompareOutput( - \\pub fn main() void { - \\ comptime var len: u32 = 5; - \\ print(len); - \\ len += 9; - \\ print(len); - \\} - \\ - \\fn print(len: usize) void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{rdx}" (len) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , "HelloHello, World!\n"); - - case.addError( - \\comptime { - \\ var x: i32 = 1; - \\ x += 1; - \\ if (x != 1) unreachable; - \\} - \\pub fn main() void {} - , &.{":4:17: error: unable to resolve comptime value"}); - - case.addError( - \\pub fn main() void { - \\ comptime var i: u64 = 0; - \\ while (i < 5) : (i += 1) {} - \\} - , &.{ - ":3:24: error: cannot store to comptime variable in non-inline loop", - ":3:5: note: non-inline loop here", - }); - - case.addCompareOutput( - \\pub fn main() void { - \\ var a: u32 = 0; - \\ if (a == 0) { - \\ comptime var b: u32 = 0; - \\ b = 1; - \\ } - \\} - \\comptime { - \\ var x: i32 = 1; - \\ x += 1; - \\ if (x != 2) unreachable; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ comptime var i: u64 = 2; - \\ inline while (i < 6) : (i+=1) { - \\ print(i); - \\ } - \\} - \\fn print(len: usize) void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("Hello")), - \\ [arg3] "{rdx}" (len) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , "HeHelHellHello"); - } - - { - var case = ctx.exe("double ampersand", linux_x64); - - case.addError( - \\pub const a = if (true && false) 1 else 2; - , &[_][]const u8{":1:24: error: `&&` is invalid; note that `and` is boolean AND"}); - - case.addError( - \\pub fn main() void { - \\ const a = true; - \\ const b = false; - \\ _ = a & &b; - \\} - , &[_][]const u8{ - ":4:11: error: incompatible types: 'bool' and '*const bool'", - ":4:9: note: type 'bool' here", - ":4:13: note: type '*const bool' here", - }); - - case.addCompareOutput( - \\pub fn main() void { - \\ const b: u8 = 1; - \\ _ = &&b; - \\} - , ""); - } - - { - var case = ctx.exe("setting an address space on a local variable", linux_x64); - case.addError( - \\export fn entry() i32 { - \\ var foo: i32 addrspace(".general") = 1234; - \\ return foo; - \\} - , &[_][]const u8{ - ":2:28: error: cannot set address space of local variable 'foo'", - }); - } - { - var case = ctx.exe("issue 10138: callee preserved regs working", linux_x64); - case.addCompareOutput( - \\pub fn main() void { - \\ const fd = open(); - \\ _ = write(fd, "a", 1); - \\ _ = close(fd); - \\} - \\ - \\fn open() usize { - \\ return 42; - \\} - \\ - \\fn write(fd: usize, a: [*]const u8, len: usize) usize { - \\ return syscall4(.WRITE, fd, @ptrToInt(a), len); - \\} - \\ - \\fn syscall4(n: enum { WRITE }, a: usize, b: usize, c: usize) usize { - \\ _ = n; - \\ _ = a; - \\ _ = b; - \\ _ = c; - \\ return 23; - \\} - \\ - \\fn close(fd: usize) usize { - \\ if (fd != 42) - \\ unreachable; - \\ return 0; - \\} - , ""); - } + try @import("stage2/x86_64.zig").addCases(ctx); } diff --git a/test/stage2/aarch64.zig b/test/stage2/aarch64.zig index e399dbbb8d..db4539eca9 100644 --- a/test/stage2/aarch64.zig +++ b/test/stage2/aarch64.zig @@ -1,12 +1,18 @@ const std = @import("std"); +const CrossTarget = std.zig.CrossTarget; const TestContext = @import("../../src/test.zig").TestContext; -const linux_aarch64 = std.zig.CrossTarget{ +const linux_aarch64 = CrossTarget{ .cpu_arch = .aarch64, .os_tag = .linux, }; +const macos_aarch64 = CrossTarget{ + .cpu_arch = .aarch64, + .os_tag = .macos, +}; pub fn addCases(ctx: *TestContext) !void { + // Linux tests { var case = ctx.exe("linux_aarch64 hello world", linux_aarch64); // Regular old hello world @@ -97,4 +103,118 @@ pub fn addCases(ctx: *TestContext) !void { "Hello, World!\n", ); } + + // macOS tests + { + var case = ctx.exe("hello world with updates", macos_aarch64); + case.addError("", &[_][]const u8{ + ":99:9: error: struct 'tmp.tmp' has no member named 'main'", + }); + + // Incorrect return type + case.addError( + \\pub export fn main() noreturn { + \\} + , &[_][]const u8{ + ":2:1: error: expected noreturn, found void", + }); + + // Regular old hello world + case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\extern fn exit(usize) noreturn; + \\ + \\pub export fn main() noreturn { + \\ print(); + \\ + \\ exit(0); + \\} + \\ + \\fn print() void { + \\ const msg = @ptrToInt("Hello, World!\n"); + \\ const len = 14; + \\ _ = write(1, msg, len); + \\} + , + "Hello, World!\n", + ); + + // Now using start.zig without an explicit extern exit fn + case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn main() void { + \\ print(); + \\} + \\ + \\fn print() void { + \\ const msg = @ptrToInt("Hello, World!\n"); + \\ const len = 14; + \\ _ = write(1, msg, len); + \\} + , + "Hello, World!\n", + ); + + // Print it 4 times and force growth and realloc. + case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn main() void { + \\ print(); + \\ print(); + \\ print(); + \\ print(); + \\} + \\ + \\fn print() void { + \\ const msg = @ptrToInt("Hello, World!\n"); + \\ const len = 14; + \\ _ = write(1, msg, len); + \\} + , + \\Hello, World! + \\Hello, World! + \\Hello, World! + \\Hello, World! + \\ + ); + + // Print it once, and change the message. + case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn main() void { + \\ print(); + \\} + \\ + \\fn print() void { + \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); + \\ const len = 104; + \\ _ = write(1, msg, len); + \\} + , + "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", + ); + + // Now we print it twice. + case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn main() void { + \\ print(); + \\ print(); + \\} + \\ + \\fn print() void { + \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); + \\ const len = 104; + \\ _ = write(1, msg, len); + \\} + , + \\What is up? This is a longer message that will force the data to be relocated in virtual address space. + \\What is up? This is a longer message that will force the data to be relocated in virtual address space. + \\ + ); + } } diff --git a/test/stage2/darwin.zig b/test/stage2/darwin.zig deleted file mode 100644 index 3976dda08f..0000000000 --- a/test/stage2/darwin.zig +++ /dev/null @@ -1,154 +0,0 @@ -const std = @import("std"); -const TestContext = @import("../../src/test.zig").TestContext; - -const archs = [2]std.Target.Cpu.Arch{ - .aarch64, .x86_64, -}; - -pub fn addCases(ctx: *TestContext) !void { - for (archs) |arch| { - const target: std.zig.CrossTarget = .{ - .cpu_arch = arch, - .os_tag = .macos, - }; - { - var case = ctx.exe("darwin hello world with updates", target); - case.addError("", &[_][]const u8{ - ":99:9: error: struct 'tmp.tmp' has no member named 'main'", - }); - - // Incorrect return type - case.addError( - \\pub export fn main() noreturn { - \\} - , &[_][]const u8{ - ":2:1: error: expected noreturn, found void", - }); - - // Regular old hello world - case.addCompareOutput( - \\extern fn write(usize, usize, usize) usize; - \\extern fn exit(usize) noreturn; - \\ - \\pub export fn main() noreturn { - \\ print(); - \\ - \\ exit(0); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("Hello, World!\n"); - \\ const len = 14; - \\ _ = write(1, msg, len); - \\} - , - "Hello, World!\n", - ); - - // Now using start.zig without an explicit extern exit fn - case.addCompareOutput( - \\extern fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ print(); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("Hello, World!\n"); - \\ const len = 14; - \\ _ = write(1, msg, len); - \\} - , - "Hello, World!\n", - ); - - // Print it 4 times and force growth and realloc. - case.addCompareOutput( - \\extern fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ print(); - \\ print(); - \\ print(); - \\ print(); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("Hello, World!\n"); - \\ const len = 14; - \\ _ = write(1, msg, len); - \\} - , - \\Hello, World! - \\Hello, World! - \\Hello, World! - \\Hello, World! - \\ - ); - - // Print it once, and change the message. - case.addCompareOutput( - \\extern fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ print(); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); - \\ const len = 104; - \\ _ = write(1, msg, len); - \\} - , - "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", - ); - - // Now we print it twice. - case.addCompareOutput( - \\extern fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ print(); - \\ print(); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); - \\ const len = 104; - \\ _ = write(1, msg, len); - \\} - , - \\What is up? This is a longer message that will force the data to be relocated in virtual address space. - \\What is up? This is a longer message that will force the data to be relocated in virtual address space. - \\ - ); - } - { - var case = ctx.exe("corner case - update existing, singular TextBlock", target); - - // This test case also covers an infrequent scenario where the string table *may* be relocated - // into the position preceeding the symbol table which results in a dyld error. - case.addCompareOutput( - \\extern fn exit(usize) noreturn; - \\ - \\pub export fn main() noreturn { - \\ exit(0); - \\} - , - "", - ); - - case.addCompareOutput( - \\extern fn exit(usize) noreturn; - \\extern fn write(usize, usize, usize) usize; - \\ - \\pub export fn main() noreturn { - \\ _ = write(1, @ptrToInt("Hey!\n"), 5); - \\ exit(0); - \\} - , - "Hey!\n", - ); - } - } -} diff --git a/test/stage2/x86_64.zig b/test/stage2/x86_64.zig new file mode 100644 index 0000000000..2725767e95 --- /dev/null +++ b/test/stage2/x86_64.zig @@ -0,0 +1,1976 @@ +const std = @import("std"); +const CrossTarget = std.zig.CrossTarget; +const TestContext = @import("../../src/test.zig").TestContext; + +const linux_x64 = std.zig.CrossTarget{ + .cpu_arch = .x86_64, + .os_tag = .linux, +}; +const macos_x64 = CrossTarget{ + .cpu_arch = .x86_64, + .os_tag = .macos, +}; +const all_targets: []const CrossTarget = &[_]CrossTarget{ + linux_x64, + macos_x64, +}; + +pub fn addCases(ctx: *TestContext) !void { + try addLinuxTestCases(ctx); + try addMacOsTestCases(ctx); + + // Common tests + for (all_targets) |target| { + { + var case = ctx.exe("adding numbers at runtime and comptime", target); + case.addCompareOutput( + \\pub fn main() void { + \\ add(3, 4); + \\} + \\ + \\fn add(a: u32, b: u32) void { + \\ if (a + b != 7) unreachable; + \\} + , + "", + ); + // comptime function call + case.addCompareOutput( + \\pub fn main() void { + \\ if (x - 7 != 0) unreachable; + \\} + \\ + \\fn add(a: u32, b: u32) u32 { + \\ return a + b; + \\} + \\ + \\const x = add(3, 4); + , + "", + ); + // Inline function call + case.addCompareOutput( + \\pub fn main() void { + \\ var x: usize = 3; + \\ const y = add(1, 2, x); + \\ if (y - 6 != 0) unreachable; + \\} + \\ + \\fn add(a: usize, b: usize, c: usize) callconv(.Inline) usize { + \\ return a + b + c; + \\} + , + "", + ); + } + + { + var case = ctx.exe("subtracting numbers at runtime", target); + case.addCompareOutput( + \\pub fn main() void { + \\ sub(7, 4); + \\} + \\ + \\fn sub(a: u32, b: u32) void { + \\ if (a - b != 3) unreachable; + \\} + , + "", + ); + } + + { + var case = ctx.exe("unused vars", target); + case.addError( + \\pub fn main() void { + \\ const x = 1; + \\} + , &.{":2:11: error: unused local constant"}); + } + + { + var case = ctx.exe("multiplying numbers at runtime and comptime", target); + case.addCompareOutput( + \\pub fn main() void { + \\ mul(3, 4); + \\} + \\ + \\fn mul(a: u32, b: u32) void { + \\ if (a * b != 12) unreachable; + \\} + , + "", + ); + // comptime function call + case.addCompareOutput( + \\pub fn main() void { + \\ if (x - 12 != 0) unreachable; + \\} + \\ + \\fn mul(a: u32, b: u32) u32 { + \\ return a * b; + \\} + \\ + \\const x = mul(3, 4); + , + "", + ); + // Inline function call + case.addCompareOutput( + \\pub fn main() void { + \\ var x: usize = 5; + \\ const y = mul(2, 3, x); + \\ if (y - 30 != 0) unreachable; + \\} + \\ + \\fn mul(a: usize, b: usize, c: usize) callconv(.Inline) usize { + \\ return a * b * c; + \\} + , + "", + ); + } + + { + var case = ctx.exe("assert function", target); + case.addCompareOutput( + \\pub fn main() void { + \\ add(3, 4); + \\} + \\ + \\fn add(a: u32, b: u32) void { + \\ assert(a + b == 7); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // Tests copying a register. For the `c = a + b`, it has to + // preserve both a and b, because they are both used later. + case.addCompareOutput( + \\pub fn main() void { + \\ add(3, 4); + \\} + \\ + \\fn add(a: u32, b: u32) void { + \\ const c = a + b; // 7 + \\ const d = a + c; // 10 + \\ const e = d + b; // 14 + \\ assert(e == 14); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // More stress on the liveness detection. + case.addCompareOutput( + \\pub fn main() void { + \\ add(3, 4); + \\} + \\ + \\fn add(a: u32, b: u32) void { + \\ const c = a + b; // 7 + \\ const d = a + c; // 10 + \\ const e = d + b; // 14 + \\ const f = d + e; // 24 + \\ const g = e + f; // 38 + \\ const h = f + g; // 62 + \\ const i = g + h; // 100 + \\ assert(i == 100); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // Requires a second move. The register allocator should figure out to re-use rax. + case.addCompareOutput( + \\pub fn main() void { + \\ add(3, 4); + \\} + \\ + \\fn add(a: u32, b: u32) void { + \\ const c = a + b; // 7 + \\ const d = a + c; // 10 + \\ const e = d + b; // 14 + \\ const f = d + e; // 24 + \\ const g = e + f; // 38 + \\ const h = f + g; // 62 + \\ const i = g + h; // 100 + \\ const j = i + d; // 110 + \\ assert(j == 110); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // Now we test integer return values. + case.addCompareOutput( + \\pub fn main() void { + \\ assert(add(3, 4) == 7); + \\ assert(add(20, 10) == 30); + \\} + \\ + \\fn add(a: u32, b: u32) u32 { + \\ return a + b; + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // Local mutable variables. + case.addCompareOutput( + \\pub fn main() void { + \\ assert(add(3, 4) == 7); + \\ assert(add(20, 10) == 30); + \\} + \\ + \\fn add(a: u32, b: u32) u32 { + \\ var x: u32 = undefined; + \\ x = 0; + \\ x += a; + \\ x += b; + \\ return x; + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // Optionals + case.addCompareOutput( + \\pub fn main() void { + \\ const a: u32 = 2; + \\ const b: ?u32 = a; + \\ const c = b.?; + \\ if (c != 2) unreachable; + \\} + , + "", + ); + + switch (target.getOsTag()) { + .linux => { + // While loops + case.addCompareOutput( + \\pub fn main() void { + \\ var i: u32 = 0; + \\ while (i < 4) : (i += 1) print(); + \\ assert(i == 4); + \\} + \\ + \\fn print() void { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (1), + \\ [arg1] "{rdi}" (1), + \\ [arg2] "{rsi}" (@ptrToInt("hello\n")), + \\ [arg3] "{rdx}" (6) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "hello\nhello\nhello\nhello\n", + ); + + // inline while requires the condition to be comptime known. + case.addError( + \\pub fn main() void { + \\ var i: u32 = 0; + \\ inline while (i < 4) : (i += 1) print(); + \\ assert(i == 4); + \\} + \\ + \\fn print() void { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (1), + \\ [arg1] "{rdi}" (1), + \\ [arg2] "{rsi}" (@ptrToInt("hello\n")), + \\ [arg3] "{rdx}" (6) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , &[_][]const u8{":3:21: error: unable to resolve comptime value"}); + }, + .macos => { + // While loops + case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn main() void { + \\ var i: u32 = 0; + \\ while (i < 4) : (i += 1) print(); + \\ assert(i == 4); + \\} + \\ + \\fn print() void { + \\ _ = write(1, @ptrToInt("hello\n"), 6); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "hello\nhello\nhello\nhello\n", + ); + + // inline while requires the condition to be comptime known. + case.addError( + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn main() void { + \\ var i: u32 = 0; + \\ inline while (i < 4) : (i += 1) print(); + \\ assert(i == 4); + \\} + \\ + \\fn print() void { + \\ _ = write(1, @ptrToInt("hello\n"), 6); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , &[_][]const u8{":5:21: error: unable to resolve comptime value"}); + }, + else => unreachable, + } + + // Labeled blocks (no conditional branch) + case.addCompareOutput( + \\pub fn main() void { + \\ assert(add(3, 4) == 20); + \\} + \\ + \\fn add(a: u32, b: u32) u32 { + \\ const x: u32 = blk: { + \\ const c = a + b; // 7 + \\ const d = a + c; // 10 + \\ const e = d + b; // 14 + \\ break :blk e; + \\ }; + \\ const y = x + a; // 17 + \\ const z = y + a; // 20 + \\ return z; + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // This catches a possible bug in the logic for re-using dying operands. + case.addCompareOutput( + \\pub fn main() void { + \\ assert(add(3, 4) == 116); + \\} + \\ + \\fn add(a: u32, b: u32) u32 { + \\ const x: u32 = blk: { + \\ const c = a + b; // 7 + \\ const d = a + c; // 10 + \\ const e = d + b; // 14 + \\ const f = d + e; // 24 + \\ const g = e + f; // 38 + \\ const h = f + g; // 62 + \\ const i = g + h; // 100 + \\ const j = i + d; // 110 + \\ break :blk j; + \\ }; + \\ const y = x + a; // 113 + \\ const z = y + a; // 116 + \\ return z; + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // Spilling registers to the stack. + case.addCompareOutput( + \\pub fn main() void { + \\ assert(add(3, 4) == 1221); + \\ assert(mul(3, 4) == 21609); + \\} + \\ + \\fn add(a: u32, b: u32) u32 { + \\ const x: u32 = blk: { + \\ const c = a + b; // 7 + \\ const d = a + c; // 10 + \\ const e = d + b; // 14 + \\ const f = d + e; // 24 + \\ const g = e + f; // 38 + \\ const h = f + g; // 62 + \\ const i = g + h; // 100 + \\ const j = i + d; // 110 + \\ const k = i + j; // 210 + \\ const l = j + k; // 320 + \\ const m = l + c; // 327 + \\ const n = m + d; // 337 + \\ const o = n + e; // 351 + \\ const p = o + f; // 375 + \\ const q = p + g; // 413 + \\ const r = q + h; // 475 + \\ const s = r + i; // 575 + \\ const t = s + j; // 685 + \\ const u = t + k; // 895 + \\ const v = u + l; // 1215 + \\ break :blk v; + \\ }; + \\ const y = x + a; // 1218 + \\ const z = y + a; // 1221 + \\ return z; + \\} + \\ + \\fn mul(a: u32, b: u32) u32 { + \\ const x: u32 = blk: { + \\ const c = a * a * a * a; // 81 + \\ const d = a * a * a * b; // 108 + \\ const e = a * a * b * a; // 108 + \\ const f = a * a * b * b; // 144 + \\ const g = a * b * a * a; // 108 + \\ const h = a * b * a * b; // 144 + \\ const i = a * b * b * a; // 144 + \\ const j = a * b * b * b; // 192 + \\ const k = b * a * a * a; // 108 + \\ const l = b * a * a * b; // 144 + \\ const m = b * a * b * a; // 144 + \\ const n = b * a * b * b; // 192 + \\ const o = b * b * a * a; // 144 + \\ const p = b * b * a * b; // 192 + \\ const q = b * b * b * a; // 192 + \\ const r = b * b * b * b; // 256 + \\ const s = c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r; // 2401 + \\ break :blk s; + \\ }; + \\ const y = x * a; // 7203 + \\ const z = y * a; // 21609 + \\ return z; + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // Reusing the registers of dead operands playing nicely with conditional branching. + case.addCompareOutput( + \\pub fn main() void { + \\ assert(add(3, 4) == 791); + \\ assert(add(4, 3) == 79); + \\} + \\ + \\fn add(a: u32, b: u32) u32 { + \\ const x: u32 = if (a < b) blk: { + \\ const c = a + b; // 7 + \\ const d = a + c; // 10 + \\ const e = d + b; // 14 + \\ const f = d + e; // 24 + \\ const g = e + f; // 38 + \\ const h = f + g; // 62 + \\ const i = g + h; // 100 + \\ const j = i + d; // 110 + \\ const k = i + j; // 210 + \\ const l = k + c; // 217 + \\ const m = l + d; // 227 + \\ const n = m + e; // 241 + \\ const o = n + f; // 265 + \\ const p = o + g; // 303 + \\ const q = p + h; // 365 + \\ const r = q + i; // 465 + \\ const s = r + j; // 575 + \\ const t = s + k; // 785 + \\ break :blk t; + \\ } else blk: { + \\ const t = b + b + a; // 10 + \\ const c = a + t; // 14 + \\ const d = c + t; // 24 + \\ const e = d + t; // 34 + \\ const f = e + t; // 44 + \\ const g = f + t; // 54 + \\ const h = c + g; // 68 + \\ break :blk h + b; // 71 + \\ }; + \\ const y = x + a; // 788, 75 + \\ const z = y + a; // 791, 79 + \\ return z; + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // Character literals and multiline strings. + case.addCompareOutput( + \\pub fn main() void { + \\ const ignore = + \\ \\ cool thx + \\ \\ + \\ ; + \\ _ = ignore; + \\ add('ぁ', '\x03'); + \\} + \\ + \\fn add(a: u32, b: u32) void { + \\ assert(a + b == 12356); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // Global const. + case.addCompareOutput( + \\pub fn main() void { + \\ add(aa, bb); + \\} + \\ + \\const aa = 'ぁ'; + \\const bb = '\x03'; + \\ + \\fn add(a: u32, b: u32) void { + \\ assert(a + b == 12356); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // Array access. + case.addCompareOutput( + \\pub fn main() void { + \\ assert("hello"[0] == 'h'); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // Array access to a global array. + case.addCompareOutput( + \\const hello = "hello".*; + \\pub fn main() void { + \\ assert(hello[1] == 'e'); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + // 64bit set stack + case.addCompareOutput( + \\pub fn main() void { + \\ var i: u64 = 0xFFEEDDCCBBAA9988; + \\ assert(i == 0xFFEEDDCCBBAA9988); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + + switch (target.getOsTag()) { + .linux => { + // Basic for loop + case.addCompareOutput( + \\pub fn main() void { + \\ for ("hello") |_| print(); + \\} + \\ + \\fn print() void { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (1), + \\ [arg1] "{rdi}" (1), + \\ [arg2] "{rsi}" (@ptrToInt("hello\n")), + \\ [arg3] "{rdx}" (6) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + , + "hello\nhello\nhello\nhello\nhello\n", + ); + }, + .macos => { + // Basic for loop + case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn main() void { + \\ for ("hello") |_| print(); + \\} + \\ + \\fn print() void { + \\ _ = write(1, @ptrToInt("hello\n"), 6); + \\} + , + "hello\nhello\nhello\nhello\nhello\n", + ); + }, + else => unreachable, + } + } + + { + var case = ctx.exe("@TypeOf", target); + case.addCompareOutput( + \\pub fn main() void { + \\ var x: usize = 0; + \\ _ = x; + \\ const z = @TypeOf(x, @as(u128, 5)); + \\ assert(z == u128); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + case.addCompareOutput( + \\pub fn main() void { + \\ const z = @TypeOf(true); + \\ assert(z == bool); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + , + "", + ); + case.addError( + \\pub fn main() void { + \\ _ = @TypeOf(true, 1); + \\} + , &[_][]const u8{ + ":2:9: error: incompatible types: 'bool' and 'comptime_int'", + ":2:17: note: type 'bool' here", + ":2:23: note: type 'comptime_int' here", + }); + } + + { + var case = ctx.exe("basic import", target); + case.addCompareOutput( + \\pub fn main() void { + \\ @import("print.zig").print(); + \\} + , + "Hello, World!\n", + ); + switch (target.getOsTag()) { + .linux => try case.files.append(.{ + .src = + \\pub fn print() void { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (@as(usize, 1)), + \\ [arg1] "{rdi}" (@as(usize, 1)), + \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{rdx}" (@as(usize, 14)) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + , + .path = "print.zig", + }), + .macos => try case.files.append(.{ + .src = + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn print() void { + \\ _ = write(1, @ptrToInt("Hello, World!\n"), 14); + \\} + , + .path = "print.zig", + }), + else => unreachable, + } + } + + { + var case = ctx.exe("redundant comptime", target); + case.addError( + \\pub fn main() void { + \\ var a: comptime u32 = 0; + \\} + , + &.{":2:12: error: redundant comptime keyword in already comptime scope"}, + ); + case.addError( + \\pub fn main() void { + \\ comptime { + \\ var a: u32 = comptime 0; + \\ } + \\} + , + &.{":3:22: error: redundant comptime keyword in already comptime scope"}, + ); + } + { + var case = ctx.exe("try in comptime in struct in test", target); + case.addError( + \\test "@unionInit on union w/ tag but no fields" { + \\ const S = struct { + \\ comptime { + \\ try expect(false); + \\ } + \\ }; + \\ _ = S; + \\} + , + &.{":4:13: error: 'try' outside function scope"}, + ); + } + { + var case = ctx.exe("import private", target); + case.addError( + \\pub fn main() void { + \\ @import("print.zig").print(); + \\} + , + &.{ + ":2:25: error: 'print' is not marked 'pub'", + "print.zig:2:1: note: declared here", + }, + ); + switch (target.getOsTag()) { + .linux => try case.files.append(.{ + .src = + \\// dummy comment to make print be on line 2 + \\fn print() void { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (@as(usize, 1)), + \\ [arg1] "{rdi}" (@as(usize, 1)), + \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{rdx}" (@as(usize, 14)) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + , + .path = "print.zig", + }), + .macos => try case.files.append(.{ + .src = + \\extern fn write(usize, usize, usize) usize; + \\fn print() void { + \\ _ = write(1, @ptrToInt("Hello, World!\n"), 14); + \\} + , + .path = "print.zig", + }), + else => unreachable, + } + } + + ctx.compileError("function redeclaration", target, + \\// dummy comment + \\fn entry() void {} + \\fn entry() void {} + \\ + \\fn foo() void { + \\ var foo = 1234; + \\} + , &[_][]const u8{ + ":3:1: error: redeclaration of 'entry'", + ":2:1: note: other declaration here", + ":6:9: error: local shadows declaration of 'foo'", + ":5:1: note: declared here", + }); + + ctx.compileError("returns in try", target, + \\pub fn main() !void { + \\ try a(); + \\ try b(); + \\} + \\ + \\pub fn a() !void { + \\ defer try b(); + \\} + \\pub fn b() !void { + \\ defer return a(); + \\} + , &[_][]const u8{ + ":7:8: error: 'try' not allowed inside defer expression", + ":10:8: error: cannot return from defer expression", + }); + + ctx.compileError("ambiguous references", target, + \\const T = struct { + \\ const T = struct { + \\ fn f() void { + \\ _ = T; + \\ } + \\ }; + \\}; + , &.{ + ":4:17: error: ambiguous reference", + ":2:5: note: declared here", + ":1:1: note: also declared here", + }); + + ctx.compileError("inner func accessing outer var", target, + \\pub fn f() void { + \\ var bar: bool = true; + \\ const S = struct { + \\ fn baz() bool { + \\ return bar; + \\ } + \\ }; + \\ _ = S; + \\} + , &.{ + ":5:20: error: mutable 'bar' not accessible from here", + ":2:9: note: declared mutable here", + ":3:15: note: crosses namespace boundary here", + }); + + ctx.compileError("global variable redeclaration", target, + \\// dummy comment + \\var foo = false; + \\var foo = true; + , &[_][]const u8{ + ":3:1: error: redeclaration of 'foo'", + ":2:1: note: other declaration here", + }); + + ctx.compileError("compileError", target, + \\export fn foo() void { + \\ @compileError("this is an error"); + \\} + , &[_][]const u8{":2:3: error: this is an error"}); + + { + var case = ctx.exe("intToPtr", target); + case.addError( + \\pub fn main() void { + \\ _ = @intToPtr(*u8, 0); + \\} + , &[_][]const u8{ + ":2:24: error: pointer type '*u8' does not allow address zero", + }); + case.addError( + \\pub fn main() void { + \\ _ = @intToPtr(*u32, 2); + \\} + , &[_][]const u8{ + ":2:25: error: pointer type '*u32' requires aligned address", + }); + } + + { + var case = ctx.obj("variable shadowing", target); + case.addError( + \\pub fn main() void { + \\ var i: u32 = 10; + \\ var i: u32 = 10; + \\} + , &[_][]const u8{ + ":3:9: error: redeclaration of local variable 'i'", + ":2:9: note: previous declaration here", + }); + case.addError( + \\var testing: i64 = 10; + \\pub fn main() void { + \\ var testing: i64 = 20; + \\} + , &[_][]const u8{ + ":3:9: error: local shadows declaration of 'testing'", + ":1:1: note: declared here", + }); + case.addError( + \\fn a() type { + \\ return struct { + \\ pub fn b() void { + \\ const c = 6; + \\ const c = 69; + \\ } + \\ }; + \\} + , &[_][]const u8{ + ":5:19: error: redeclaration of local constant 'c'", + ":4:19: note: previous declaration here", + }); + case.addError( + \\pub fn main() void { + \\ var i = 0; + \\ for ("n") |_, i| { + \\ } + \\} + , &[_][]const u8{ + ":3:19: error: redeclaration of local variable 'i'", + ":2:9: note: previous declaration here", + }); + case.addError( + \\pub fn main() void { + \\ var i = 0; + \\ for ("n") |i| { + \\ } + \\} + , &[_][]const u8{ + ":3:16: error: redeclaration of local variable 'i'", + ":2:9: note: previous declaration here", + }); + case.addError( + \\pub fn main() void { + \\ var i = 0; + \\ while ("n") |i| { + \\ } + \\} + , &[_][]const u8{ + ":3:18: error: redeclaration of local variable 'i'", + ":2:9: note: previous declaration here", + }); + case.addError( + \\pub fn main() void { + \\ var i = 0; + \\ while ("n") |bruh| { + \\ _ = bruh; + \\ } else |i| { + \\ + \\ } + \\} + , &[_][]const u8{ + ":5:13: error: redeclaration of local variable 'i'", + ":2:9: note: previous declaration here", + }); + case.addError( + \\pub fn main() void { + \\ var i = 0; + \\ if (true) |i| {} + \\} + , &[_][]const u8{ + ":3:16: error: redeclaration of local variable 'i'", + ":2:9: note: previous declaration here", + }); + case.addError( + \\pub fn main() void { + \\ var i = 0; + \\ if (true) |i| {} else |e| {} + \\} + , &[_][]const u8{ + ":3:16: error: redeclaration of local variable 'i'", + ":2:9: note: previous declaration here", + }); + case.addError( + \\pub fn main() void { + \\ var i = 0; + \\ if (true) |_| {} else |i| {} + \\} + , &[_][]const u8{ + ":3:28: error: redeclaration of local variable 'i'", + ":2:9: note: previous declaration here", + }); + } + + { + // TODO make the test harness support checking the compile log output too + var case = ctx.obj("@compileLog", target); + // The other compile error prevents emission of a "found compile log" statement. + case.addError( + \\export fn _start() noreturn { + \\ const b = true; + \\ var f: u32 = 1; + \\ @compileLog(b, 20, f, x); + \\ @compileLog(1000); + \\ var bruh: usize = true; + \\ _ = bruh; + \\ unreachable; + \\} + \\export fn other() void { + \\ @compileLog(1234); + \\} + \\fn x() void {} + , &[_][]const u8{ + ":6:23: error: expected usize, found bool", + }); + + // Now only compile log statements remain. One per Decl. + case.addError( + \\export fn _start() noreturn { + \\ const b = true; + \\ var f: u32 = 1; + \\ @compileLog(b, 20, f, x); + \\ @compileLog(1000); + \\ unreachable; + \\} + \\export fn other() void { + \\ @compileLog(1234); + \\} + \\fn x() void {} + , &[_][]const u8{ + ":9:5: error: found compile log statement", + ":4:5: note: also here", + }); + } + + { + var case = ctx.obj("extern variable has no type", target); + case.addError( + \\comptime { + \\ const x = foo + foo; + \\ _ = x; + \\} + \\extern var foo: i32; + , &[_][]const u8{":2:15: error: unable to resolve comptime value"}); + case.addError( + \\export fn entry() void { + \\ _ = foo; + \\} + \\extern var foo; + , &[_][]const u8{":4:8: error: unable to infer variable type"}); + } + + { + var case = ctx.exe("break/continue", target); + + // Break out of loop + case.addCompareOutput( + \\pub fn main() void { + \\ while (true) { + \\ break; + \\ } + \\} + , + "", + ); + case.addCompareOutput( + \\pub fn main() void { + \\ foo: while (true) { + \\ break :foo; + \\ } + \\} + , + "", + ); + + // Continue in loop + case.addCompareOutput( + \\pub fn main() void { + \\ var i: u64 = 0; + \\ while (true) : (i+=1) { + \\ if (i == 4) return; + \\ continue; + \\ } + \\} + , + "", + ); + case.addCompareOutput( + \\pub fn main() void { + \\ var i: u64 = 0; + \\ foo: while (true) : (i+=1) { + \\ if (i == 4) return; + \\ continue :foo; + \\ } + \\} + , + "", + ); + } + + { + var case = ctx.exe("unused labels", target); + case.addError( + \\comptime { + \\ foo: {} + \\} + , &[_][]const u8{":2:5: error: unused block label"}); + case.addError( + \\comptime { + \\ foo: while (true) {} + \\} + , &[_][]const u8{":2:5: error: unused while loop label"}); + case.addError( + \\comptime { + \\ foo: for ("foo") |_| {} + \\} + , &[_][]const u8{":2:5: error: unused for loop label"}); + case.addError( + \\comptime { + \\ blk: {blk: {}} + \\} + , &[_][]const u8{ + ":2:11: error: redefinition of label 'blk'", + ":2:5: note: previous definition here", + }); + } + + { + var case = ctx.exe("bad inferred variable type", target); + case.addError( + \\pub fn main() void { + \\ var x = null; + \\ _ = x; + \\} + , &[_][]const u8{ + ":2:9: error: variable of type '@Type(.Null)' must be const or comptime", + }); + } + + { + var case = ctx.exe("compile error in inline fn call fixed", target); + case.addError( + \\pub fn main() void { + \\ var x: usize = 3; + \\ const y = add(10, 2, x); + \\ if (y - 6 != 0) unreachable; + \\} + \\ + \\fn add(a: usize, b: usize, c: usize) callconv(.Inline) usize { + \\ if (a == 10) @compileError("bad"); + \\ return a + b + c; + \\} + , &[_][]const u8{":8:18: error: bad"}); + + case.addCompareOutput( + \\pub fn main() void { + \\ var x: usize = 3; + \\ const y = add(1, 2, x); + \\ if (y - 6 != 0) unreachable; + \\} + \\ + \\fn add(a: usize, b: usize, c: usize) callconv(.Inline) usize { + \\ if (a == 10) @compileError("bad"); + \\ return a + b + c; + \\} + , + "", + ); + } + { + var case = ctx.exe("recursive inline function", target); + case.addCompareOutput( + \\pub fn main() void { + \\ const y = fibonacci(7); + \\ if (y - 21 != 0) unreachable; + \\} + \\ + \\fn fibonacci(n: usize) callconv(.Inline) usize { + \\ if (n <= 2) return n; + \\ return fibonacci(n - 2) + fibonacci(n - 1); + \\} + , + "", + ); + // This additionally tests that the compile error reports the correct source location. + // Without storing source locations relative to the owner decl, the compile error + // here would be off by 2 bytes (from the "7" -> "999"). + case.addError( + \\pub fn main() void { + \\ const y = fibonacci(999); + \\ if (y - 21 != 0) unreachable; + \\} + \\ + \\fn fibonacci(n: usize) callconv(.Inline) usize { + \\ if (n <= 2) return n; + \\ return fibonacci(n - 2) + fibonacci(n - 1); + \\} + , &[_][]const u8{":8:21: error: evaluation exceeded 1000 backwards branches"}); + } + { + var case = ctx.exe("orelse at comptime", target); + case.addCompareOutput( + \\pub fn main() void { + \\ const i: ?u64 = 0; + \\ const result = i orelse 5; + \\ assert(result == 0); + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; + \\} + , + "", + ); + case.addCompareOutput( + \\pub fn main() void { + \\ const i: ?u64 = null; + \\ const result = i orelse 5; + \\ assert(result == 5); + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; + \\} + , + "", + ); + } + + { + var case = ctx.exe("passing u0 to function", target); + case.addCompareOutput( + \\pub fn main() void { + \\ doNothing(0); + \\} + \\fn doNothing(arg: u0) void { + \\ _ = arg; + \\} + , + "", + ); + } + + { + var case = ctx.exe("catch at comptime", target); + case.addCompareOutput( + \\pub fn main() void { + \\ const i: anyerror!u64 = 0; + \\ const caught = i catch 5; + \\ assert(caught == 0); + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; + \\} + , + "", + ); + + case.addCompareOutput( + \\pub fn main() void { + \\ const i: anyerror!u64 = error.B; + \\ const caught = i catch 5; + \\ assert(caught == 5); + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; + \\} + , + "", + ); + + case.addCompareOutput( + \\pub fn main() void { + \\ const a: anyerror!comptime_int = 42; + \\ const b: *const comptime_int = &(a catch unreachable); + \\ assert(b.* == 42); + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; // assertion failure + \\} + , ""); + + case.addCompareOutput( + \\pub fn main() void { + \\ const a: anyerror!u32 = error.B; + \\ _ = &(a catch |err| assert(err == error.B)); + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; + \\} + , ""); + + case.addCompareOutput( + \\pub fn main() void { + \\ const a: anyerror!u32 = error.Bar; + \\ a catch |err| assert(err == error.Bar); + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; + \\} + , ""); + } + + { + var case = ctx.exe("runtime bitwise and", target); + + case.addCompareOutput( + \\pub fn main() void { + \\ var i: u32 = 10; + \\ var j: u32 = 11; + \\ assert(i & 1 == 0); + \\ assert(j & 1 == 1); + \\ var m1: u32 = 0b1111; + \\ var m2: u32 = 0b0000; + \\ assert(m1 & 0b1010 == 0b1010); + \\ assert(m2 & 0b1010 == 0b0000); + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; + \\} + , + "", + ); + } + + { + var case = ctx.exe("runtime bitwise or", target); + + case.addCompareOutput( + \\pub fn main() void { + \\ var i: u32 = 10; + \\ var j: u32 = 11; + \\ assert(i | 1 == 11); + \\ assert(j | 1 == 11); + \\ var m1: u32 = 0b1111; + \\ var m2: u32 = 0b0000; + \\ assert(m1 | 0b1010 == 0b1111); + \\ assert(m2 | 0b1010 == 0b1010); + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; + \\} + , + "", + ); + } + + { + var case = ctx.exe("merge error sets", target); + + case.addCompareOutput( + \\pub fn main() void { + \\ const E = error{ A, B, D } || error { A, B, C }; + \\ E.A catch {}; + \\ E.B catch {}; + \\ E.C catch {}; + \\ E.D catch {}; + \\ const E2 = error { X, Y } || @TypeOf(error.Z); + \\ E2.X catch {}; + \\ E2.Y catch {}; + \\ E2.Z catch {}; + \\ assert(anyerror || error { Z } == anyerror); + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; + \\} + , + "", + ); + case.addError( + \\pub fn main() void { + \\ const z = true || false; + \\ _ = z; + \\} + , &.{ + ":2:15: error: expected error set type, found 'bool'", + ":2:20: note: '||' merges error sets; 'or' performs boolean OR", + }); + } + + { + var case = ctx.exe("error set equality", target); + + case.addCompareOutput( + \\pub fn main() void { + \\ assert(@TypeOf(error.Foo) == @TypeOf(error.Foo)); + \\ assert(@TypeOf(error.Bar) != @TypeOf(error.Foo)); + \\ assert(anyerror == anyerror); + \\ assert(error{Foo} != error{Foo}); + \\ // TODO put inferred error sets here when @typeInfo works + \\} + \\fn assert(b: bool) void { + \\ if (!b) unreachable; + \\} + , + "", + ); + } + + { + var case = ctx.exe("comptime var", target); + + case.addError( + \\pub fn main() void { + \\ var a: u32 = 0; + \\ comptime var b: u32 = 0; + \\ if (a == 0) b = 3; + \\} + , &.{ + ":4:21: error: store to comptime variable depends on runtime condition", + ":4:11: note: runtime condition here", + }); + + case.addError( + \\pub fn main() void { + \\ var a: u32 = 0; + \\ comptime var b: u32 = 0; + \\ switch (a) { + \\ 0 => {}, + \\ else => b = 3, + \\ } + \\} + , &.{ + ":6:21: error: store to comptime variable depends on runtime condition", + ":4:13: note: runtime condition here", + }); + + switch (target.getOsTag()) { + .linux => case.addCompareOutput( + \\pub fn main() void { + \\ comptime var len: u32 = 5; + \\ print(len); + \\ len += 9; + \\ print(len); + \\} + \\ + \\fn print(len: usize) void { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (1), + \\ [arg1] "{rdi}" (1), + \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{rdx}" (len) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + , "HelloHello, World!\n"), + .macos => case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn main() void { + \\ comptime var len: u32 = 5; + \\ print(len); + \\ len += 9; + \\ print(len); + \\} + \\ + \\fn print(len: usize) void { + \\ _ = write(1, @ptrToInt("Hello, World!\n"), len); + \\} + , "HelloHello, World!\n"), + + else => unreachable, + } + + case.addError( + \\comptime { + \\ var x: i32 = 1; + \\ x += 1; + \\ if (x != 1) unreachable; + \\} + \\pub fn main() void {} + , &.{":4:17: error: unable to resolve comptime value"}); + + case.addError( + \\pub fn main() void { + \\ comptime var i: u64 = 0; + \\ while (i < 5) : (i += 1) {} + \\} + , &.{ + ":3:24: error: cannot store to comptime variable in non-inline loop", + ":3:5: note: non-inline loop here", + }); + + case.addCompareOutput( + \\pub fn main() void { + \\ var a: u32 = 0; + \\ if (a == 0) { + \\ comptime var b: u32 = 0; + \\ b = 1; + \\ } + \\} + \\comptime { + \\ var x: i32 = 1; + \\ x += 1; + \\ if (x != 2) unreachable; + \\} + , ""); + + switch (target.getOsTag()) { + .linux => case.addCompareOutput( + \\pub fn main() void { + \\ comptime var i: u64 = 2; + \\ inline while (i < 6) : (i+=1) { + \\ print(i); + \\ } + \\} + \\fn print(len: usize) void { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (1), + \\ [arg1] "{rdi}" (1), + \\ [arg2] "{rsi}" (@ptrToInt("Hello")), + \\ [arg3] "{rdx}" (len) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + , "HeHelHellHello"), + .macos => case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn main() void { + \\ comptime var i: u64 = 2; + \\ inline while (i < 6) : (i+=1) { + \\ print(i); + \\ } + \\} + \\fn print(len: usize) void { + \\ _ = write(1, @ptrToInt("Hello"), len); + \\} + , "HeHelHellHello"), + else => unreachable, + } + } + + { + var case = ctx.exe("double ampersand", target); + + case.addError( + \\pub const a = if (true && false) 1 else 2; + , &[_][]const u8{":1:24: error: `&&` is invalid; note that `and` is boolean AND"}); + + case.addError( + \\pub fn main() void { + \\ const a = true; + \\ const b = false; + \\ _ = a & &b; + \\} + , &[_][]const u8{ + ":4:11: error: incompatible types: 'bool' and '*const bool'", + ":4:9: note: type 'bool' here", + ":4:13: note: type '*const bool' here", + }); + + case.addCompareOutput( + \\pub fn main() void { + \\ const b: u8 = 1; + \\ _ = &&b; + \\} + , ""); + } + + { + var case = ctx.exe("setting an address space on a local variable", target); + case.addError( + \\export fn entry() i32 { + \\ var foo: i32 addrspace(".general") = 1234; + \\ return foo; + \\} + , &[_][]const u8{ + ":2:28: error: cannot set address space of local variable 'foo'", + }); + } + } +} + +fn addLinuxTestCases(ctx: *TestContext) !void { + // Linux tests + { + var case = ctx.exe("hello world with updates", linux_x64); + + case.addError("", &[_][]const u8{ + ":99:9: error: struct 'tmp.tmp' has no member named 'main'", + }); + + // Incorrect return type + case.addError( + \\pub export fn _start() noreturn { + \\} + , &[_][]const u8{":2:1: error: expected noreturn, found void"}); + + // Regular old hello world + case.addCompareOutput( + \\pub export fn _start() noreturn { + \\ print(); + \\ + \\ exit(); + \\} + \\ + \\fn print() void { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (1), + \\ [arg1] "{rdi}" (1), + \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{rdx}" (14) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + \\ + \\fn exit() noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (0) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "Hello, World!\n", + ); + + // Convert to pub fn main + case.addCompareOutput( + \\pub fn main() void { + \\ print(); + \\} + \\ + \\fn print() void { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (1), + \\ [arg1] "{rdi}" (1), + \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{rdx}" (14) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + , + "Hello, World!\n", + ); + + // Now change the message only + case.addCompareOutput( + \\pub fn main() void { + \\ print(); + \\} + \\ + \\fn print() void { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (1), + \\ [arg1] "{rdi}" (1), + \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")), + \\ [arg3] "{rdx}" (104) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + , + "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", + ); + // Now we print it twice. + case.addCompareOutput( + \\pub fn main() void { + \\ print(); + \\ print(); + \\} + \\ + \\fn print() void { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (1), + \\ [arg1] "{rdi}" (1), + \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")), + \\ [arg3] "{rdx}" (104) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + , + \\What is up? This is a longer message that will force the data to be relocated in virtual address space. + \\What is up? This is a longer message that will force the data to be relocated in virtual address space. + \\ + ); + } + + { + var case = ctx.exe("adding numbers at comptime", linux_x64); + case.addCompareOutput( + \\pub export fn _start() noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (1), + \\ [arg1] "{rdi}" (1), + \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{rdx}" (10 + 4) + \\ : "rcx", "r11", "memory" + \\ ); + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (@as(usize, 230) + @as(usize, 1)), + \\ [arg1] "{rdi}" (0) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "Hello, World!\n", + ); + } + + { + var case = ctx.exe("only 1 function and it gets updated", linux_x64); + case.addCompareOutput( + \\pub export fn _start() noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (60), // exit + \\ [arg1] "{rdi}" (0) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + case.addCompareOutput( + \\pub export fn _start() noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), // exit_group + \\ [arg1] "{rdi}" (0) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + } + { + var case = ctx.exe("inline assembly", linux_x64); + + case.addError( + \\pub fn main() void { + \\ const number = 1234; + \\ const x = asm volatile ("syscall" + \\ : [o] "{rax}" (-> number) + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (60) + \\ : "rcx", "r11", "memory" + \\ ); + \\ _ = x; + \\} + , &[_][]const u8{":4:27: error: expected type, found comptime_int"}); + case.addError( + \\const S = struct { + \\ comptime { + \\ asm volatile ( + \\ \\zig_moment: + \\ \\syscall + \\ ); + \\ } + \\}; + \\pub fn main() void { + \\ _ = S; + \\} + , &.{":3:13: error: volatile is meaningless on global assembly"}); + case.addError( + \\pub fn main() void { + \\ var bruh: u32 = 1; + \\ asm ("" + \\ : + \\ : [bruh] "{rax}" (4) + \\ : "memory" + \\ ); + \\} + , &.{":3:5: error: assembly expression with no output must be marked volatile"}); + case.addError( + \\pub fn main() void {} + \\comptime { + \\ asm ("" + \\ : + \\ : [bruh] "{rax}" (4) + \\ : "memory" + \\ ); + \\} + , &.{":3:5: error: global assembly cannot have inputs, outputs, or clobbers"}); + } + + { + var case = ctx.exe("issue 10138: callee preserved regs working", linux_x64); + case.addCompareOutput( + \\pub fn main() void { + \\ const fd = open(); + \\ _ = write(fd, "a", 1); + \\ _ = close(fd); + \\} + \\ + \\fn open() usize { + \\ return 42; + \\} + \\ + \\fn write(fd: usize, a: [*]const u8, len: usize) usize { + \\ return syscall4(.WRITE, fd, @ptrToInt(a), len); + \\} + \\ + \\fn syscall4(n: enum { WRITE }, a: usize, b: usize, c: usize) usize { + \\ _ = n; + \\ _ = a; + \\ _ = b; + \\ _ = c; + \\ return 23; + \\} + \\ + \\fn close(fd: usize) usize { + \\ if (fd != 42) + \\ unreachable; + \\ return 0; + \\} + , ""); + } +} + +fn addMacOsTestCases(ctx: *TestContext) !void { + // macOS tests + { + var case = ctx.exe("darwin hello world with updates", macos_x64); + case.addError("", &[_][]const u8{ + ":99:9: error: struct 'tmp.tmp' has no member named 'main'", + }); + + // Incorrect return type + case.addError( + \\pub export fn main() noreturn { + \\} + , &[_][]const u8{ + ":2:1: error: expected noreturn, found void", + }); + + // Regular old hello world + case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\extern fn exit(usize) noreturn; + \\ + \\pub export fn main() noreturn { + \\ print(); + \\ + \\ exit(0); + \\} + \\ + \\fn print() void { + \\ const msg = @ptrToInt("Hello, World!\n"); + \\ const len = 14; + \\ _ = write(1, msg, len); + \\} + , + "Hello, World!\n", + ); + + // Now using start.zig without an explicit extern exit fn + case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn main() void { + \\ print(); + \\} + \\ + \\fn print() void { + \\ const msg = @ptrToInt("Hello, World!\n"); + \\ const len = 14; + \\ _ = write(1, msg, len); + \\} + , + "Hello, World!\n", + ); + + // Print it 4 times and force growth and realloc. + case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn main() void { + \\ print(); + \\ print(); + \\ print(); + \\ print(); + \\} + \\ + \\fn print() void { + \\ const msg = @ptrToInt("Hello, World!\n"); + \\ const len = 14; + \\ _ = write(1, msg, len); + \\} + , + \\Hello, World! + \\Hello, World! + \\Hello, World! + \\Hello, World! + \\ + ); + + // Print it once, and change the message. + case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn main() void { + \\ print(); + \\} + \\ + \\fn print() void { + \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); + \\ const len = 104; + \\ _ = write(1, msg, len); + \\} + , + "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", + ); + + // Now we print it twice. + case.addCompareOutput( + \\extern fn write(usize, usize, usize) usize; + \\ + \\pub fn main() void { + \\ print(); + \\ print(); + \\} + \\ + \\fn print() void { + \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); + \\ const len = 104; + \\ _ = write(1, msg, len); + \\} + , + \\What is up? This is a longer message that will force the data to be relocated in virtual address space. + \\What is up? This is a longer message that will force the data to be relocated in virtual address space. + \\ + ); + } +} From 510357355a3ca597daf13fadb6aa8f7df72ea26a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 15 Dec 2021 16:27:16 +0100 Subject: [PATCH 2/3] stage2: fix premature atom deinit in macho linker --- src/link/MachO.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index d7457ec549..efc1330597 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -4823,8 +4823,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m // should be deleted because the atom that it points to has grown to take up // more of the extra capacity. if (!big_atom.freeListEligible(self.*)) { - const bl = free_list.swapRemove(i); - bl.deinit(self.base.allocator); + _ = free_list.swapRemove(i); } else { i += 1; } From 04784463772decb18d70734747d3fc89c043b979 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 15 Dec 2021 17:18:38 +0100 Subject: [PATCH 3/3] stage2: fix register size selection This actually needs proper rework, and I'll get to that when refactoring MIR. --- src/arch/x86_64/CodeGen.zig | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 2de37b05bc..378184f70a 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1635,7 +1635,7 @@ fn genBinMathOpMir( .tag = mir_tag, .ops = (Mir.Ops{ .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)), - .reg2 = .ebp, + .reg2 = registerAlias(.rbp, @intCast(u32, abi_size)), .flags = 0b01, }).encode(), .data = .{ .imm = -@intCast(i32, adj_off) }, @@ -1666,7 +1666,7 @@ fn genBinMathOpMir( .tag = mir_tag, .ops = (Mir.Ops{ .reg1 = registerAlias(src_reg, @intCast(u32, abi_size)), - .reg2 = .ebp, + .reg2 = registerAlias(.rbp, @intCast(u32, abi_size)), .flags = 0b10, }).encode(), .data = .{ .imm = -@intCast(i32, adj_off) }, @@ -2835,12 +2835,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro } const abi_size = ty.abiSize(self.target.*); const adj_off = stack_offset + abi_size; - // TODO select instruction size _ = try self.addInst(.{ .tag = .mov, .ops = (Mir.Ops{ .reg1 = registerAlias(reg, @intCast(u32, abi_size)), - .reg2 = .rbp, + .reg2 = registerAlias(.rbp, @intCast(u32, abi_size)), .flags = 0b10, }).encode(), .data = .{ .imm = -@intCast(i32, adj_off) }, @@ -3061,7 +3060,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .tag = .mov, .ops = (Mir.Ops{ .reg1 = registerAlias(reg, @intCast(u32, abi_size)), - .reg2 = .ebp, + .reg2 = registerAlias(.rbp, @intCast(u32, abi_size)), .flags = 0b01, }).encode(), .data = .{ .imm = ioff },