zig/test/cases.zig

1855 lines
57 KiB
Zig
Raw Normal View History

const std = @import("std");
avoid calling into stage1 backend when AstGen fails The motivation for this commit is that there exists source files which produce ast-check errors, but crash stage1 or otherwise trigger stage1 bugs. Previously to this commit, Zig would run AstGen, collect the compile errors, run stage1, report stage1 compile errors and exit if any, and then report AstGen compile errors. The main change in this commit is to report AstGen errors prior to invoking stage1, and in fact if any AstGen errors occur, do not invoke stage1 at all. This caused most of the compile error tests to fail due to things such as unused local variables and mismatched stage1/stage2 error messages. It was taking a long time to update the test cases one-by-one, so I took this opportunity to unify the stage1 and stage2 testing harness, specifically with regards to compile errors. In this way we can start keeping track of which tests pass for 1, 2, or both. `zig build test-compile-errors` no longer works; it is now integrated into `zig build test-stage2`. This is one step closer to executing compile error tests in parallel; in fact the ThreadPool object is already in scope. There are some cases where the stage1 compile errors were actually better; those are left failing in this commit, to be addressed in a follow-up commit. Other changes in this commit: * build.zig: improve support for -Dstage1 used with the test step. * AstGen: minor cosmetic changes to error messages. * stage2: add -fstage1 and -fno-stage1 flags. This now allows one to download a binary of the zig compiler and use the llvm backend of self-hosted. This was also needed for hooking up the test harness. However, I realized that stage1 calls exit() and also has memory leaks, so had to complicate the test harness by not using this flag after all and instead invoking as a child process. - These CLI flags will disappear once we start shipping the self-hosted compiler as the main compiler. Until then, they can be used to try out the work-in-progress stage2. * stage2: select the LLVM backend by default for release modes, as long as the target architecture is supported by LLVM. * test harness: support setting the optimize mode
2021-06-30 19:27:39 +01:00
const TestContext = @import("../src/test.zig").TestContext;
2019-11-23 20:56:05 +00:00
stage2: caching system integration & Module/Compilation splitting * update to the new cache hash API * std.Target defaultVersionRange moves to std.Target.Os.Tag * std.Target.Os gains getVersionRange which returns a tagged union * start the process of splitting Module into Compilation and "zig module". - The parts of Module having to do with only compiling zig code are extracted into ZigModule.zig. - Next step is to rename Module to Compilation. - After that rename ZigModule back to Module. * implement proper cache hash usage when compiling C objects, and properly manage the file lock of the build artifacts. * make versions optional to match recent changes to master branch. * proper cache hash integration for compiling zig code * proper cache hash integration for linking even when not compiling zig code. * ELF LLD linking integrates with the caching system. A comment from the source code: Here we want to determine whether we can save time by not invoking LLD when the output is unchanged. None of the linker options or the object files that are being linked are in the hash that namespaces the directory we are outputting to. Therefore, we must hash those now, and the resulting digest will form the "id" of the linking job we are about to perform. After a successful link, we store the id in the metadata of a symlink named "id.txt" in the artifact directory. So, now, we check if this symlink exists, and if it matches our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD. * implement disable_c_depfile option * add tracy to a few more functions
2020-09-14 03:17:58 +01:00
// Self-hosted has differing levels of support for various architectures. For now we pass explicit
// target parameters to each test case. At some point we will take this to the next level and have
// 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,
};
2019-11-23 20:56:05 +00:00
pub fn addCases(ctx: *TestContext) !void {
try @import("compile_errors.zig").addCases(ctx);
try @import("stage2/cbe.zig").addCases(ctx);
try @import("stage2/arm.zig").addCases(ctx);
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);
2021-05-14 04:44:34 +01:00
case.addError("", &[_][]const u8{
":99:9: error: struct 'tmp.tmp' has no member named 'main'",
2021-05-14 04:44:34 +01:00
});
// 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;
\\}
,
"",
);
}
2021-06-09 01:32:44 +01:00
{
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;
2021-06-12 19:48:13 +01:00
\\ _ = 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 {
2021-06-12 19:48:13 +01:00
\\ _ = @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(
2021-05-14 04:44:34 +01:00
\\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(
2021-05-14 04:44:34 +01:00
\\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)
2021-03-25 03:58:38 +00:00
case.addCompareOutput(
\\pub fn main() void {
2021-03-25 03:58:38 +00:00
\\ 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.
2021-03-25 03:58:38 +00:00
case.addCompareOutput(
\\pub fn main() void {
2021-03-25 03:58:38 +00:00
\\ 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.
2021-03-25 03:58:38 +00:00
case.addCompareOutput(
\\pub fn main() void {
\\ assert(add(3, 4) == 1221);
\\ assert(mul(3, 4) == 21609);
2021-03-25 03:58:38 +00:00
\\}
\\
\\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;
2021-03-25 03:58:38 +00:00
\\ };
\\ const y = x + a; // 1218
\\ const z = y + a; // 1221
2021-03-25 03:58:38 +00:00
\\ 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;
2021-03-25 03:58:38 +00:00
\\ };
\\ const y = x * a; // 7203
\\ const z = y * a; // 21609
2021-03-25 03:58:38 +00:00
\\ return z;
\\}
\\
\\pub fn assert(ok: bool) void {
\\ if (!ok) unreachable; // assertion failure
\\}
,
"",
);
// Reusing the registers of dead operands playing nicely with conditional branching.
2021-03-25 03:58:38 +00:00
case.addCompareOutput(
\\pub fn main() void {
2021-03-25 03:58:38 +00:00
\\ 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
\\ \\
\\ ;
2021-06-12 19:48:13 +01:00
\\ _ = 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
\\}
,
2020-08-25 17:59:35 +01:00
"",
);
// Array access.
2021-03-25 20:03:54 +00:00
case.addCompareOutput(
\\pub fn main() void {
2021-03-25 20:03:54 +00:00
\\ 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",
);
}
2021-03-26 02:39:30 +00:00
{
var case = ctx.exe("basic import", linux_x64);
case.addCompareOutput(
\\pub fn main() void {
2021-03-26 02:39:30 +00:00
\\ @import("print.zig").print();
\\}
,
"Hello, World!\n",
);
try case.files.append(.{
2021-08-29 18:41:57 +01:00
.src =
2021-03-26 02:39:30 +00:00
\\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",
});
}
2021-04-28 22:01:24 +01:00
{
var case = ctx.exe("redundant comptime", linux_x64);
case.addError(
\\pub fn main() void {
2021-04-28 22:01:24 +01:00
\\ var a: comptime u32 = 0;
\\}
,
&.{":2:12: error: redundant comptime keyword in already comptime scope"},
2021-04-28 22:01:24 +01:00
);
case.addError(
\\pub fn main() void {
2021-04-28 22:01:24 +01:00
\\ comptime {
\\ var a: u32 = comptime 0;
\\ }
\\}
,
&.{":3:22: error: redundant comptime keyword in already comptime scope"},
2021-04-28 22:01:24 +01:00
);
}
{
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();
\\}
,
stage2: entry point via std lib and proper updated file detection Instead of Module setting up the root_scope with the root source file, instead, Module relies on the package table graph being set up properly, and inside `update()`, it does the equivalent of `_ = @import("std");`. This, in term, imports start.zig, which has the logic to call main (or not). `Module` no longer has `root_scope` - the root source file is no longer special, it's just in the package table mapped to "root". I also went ahead and implemented proper detection of updated files. mtime, inode, size, and source hash are kept in `Scope.File`. During an update, iterate over `import_table` and stat each file to find out which ones are updated. The source hash is redundant with the source hash used by the struct decl that corresponds to the file, so it should be removed in a future commit before merging the branch. * AstGen: add "previously declared here" notes for variables shadowing decls. * Parse imports as structs. Module now calls `AstGen.structDeclInner`, which is called by `AstGen.containerDecl`. - `importFile` is a bit kludgy with how it handles the top level Decl that kinda gets merged into the struct decl at the end of the function. Be on the look out for bugs related to that as well as possibly cleaner ways to implement this. * Module: factor out lookupDeclName into lookupIdentifier and lookupNa * Rename `Scope.Container` to `Scope.Namespace`. * Delete some dead code. This branch won't work until `usingnamespace` is implemented because it relies on `@import("builtin").OutputMode` and `OutputMode` comes from a `usingnamespace`.
2021-04-10 07:17:50 +01:00
&.{
":2:25: error: 'print' is not marked 'pub'",
"print.zig:2:1: note: declared here",
},
);
try case.files.append(.{
2021-08-29 18:41:57 +01:00
.src =
stage2: entry point via std lib and proper updated file detection Instead of Module setting up the root_scope with the root source file, instead, Module relies on the package table graph being set up properly, and inside `update()`, it does the equivalent of `_ = @import("std");`. This, in term, imports start.zig, which has the logic to call main (or not). `Module` no longer has `root_scope` - the root source file is no longer special, it's just in the package table mapped to "root". I also went ahead and implemented proper detection of updated files. mtime, inode, size, and source hash are kept in `Scope.File`. During an update, iterate over `import_table` and stat each file to find out which ones are updated. The source hash is redundant with the source hash used by the struct decl that corresponds to the file, so it should be removed in a future commit before merging the branch. * AstGen: add "previously declared here" notes for variables shadowing decls. * Parse imports as structs. Module now calls `AstGen.structDeclInner`, which is called by `AstGen.containerDecl`. - `importFile` is a bit kludgy with how it handles the top level Decl that kinda gets merged into the struct decl at the end of the function. Be on the look out for bugs related to that as well as possibly cleaner ways to implement this. * Module: factor out lookupDeclName into lookupIdentifier and lookupNa * Rename `Scope.Container` to `Scope.Namespace`. * Delete some dead code. This branch won't work until `usingnamespace` is implemented because it relies on `@import("builtin").OutputMode` and `OutputMode` comes from a `usingnamespace`.
2021-04-10 07:17:50 +01:00
\\// 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",
});
}
stage2: entry point via std lib and proper updated file detection Instead of Module setting up the root_scope with the root source file, instead, Module relies on the package table graph being set up properly, and inside `update()`, it does the equivalent of `_ = @import("std");`. This, in term, imports start.zig, which has the logic to call main (or not). `Module` no longer has `root_scope` - the root source file is no longer special, it's just in the package table mapped to "root". I also went ahead and implemented proper detection of updated files. mtime, inode, size, and source hash are kept in `Scope.File`. During an update, iterate over `import_table` and stat each file to find out which ones are updated. The source hash is redundant with the source hash used by the struct decl that corresponds to the file, so it should be removed in a future commit before merging the branch. * AstGen: add "previously declared here" notes for variables shadowing decls. * Parse imports as structs. Module now calls `AstGen.structDeclInner`, which is called by `AstGen.containerDecl`. - `importFile` is a bit kludgy with how it handles the top level Decl that kinda gets merged into the struct decl at the end of the function. Be on the look out for bugs related to that as well as possibly cleaner ways to implement this. * Module: factor out lookupDeclName into lookupIdentifier and lookupNa * Rename `Scope.Container` to `Scope.Namespace`. * Delete some dead code. This branch won't work until `usingnamespace` is implemented because it relies on `@import("builtin").OutputMode` and `OutputMode` comes from a `usingnamespace`.
2021-04-10 07:17:50 +01:00
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;
\\ }
\\ };
2021-06-12 19:48:13 +01:00
\\ _ = S;
\\}
, &.{
":5:20: error: mutable 'bar' not accessible from here",
":2:9: note: declared mutable here",
":3:15: note: crosses namespace boundary here",
});
stage2: entry point via std lib and proper updated file detection Instead of Module setting up the root_scope with the root source file, instead, Module relies on the package table graph being set up properly, and inside `update()`, it does the equivalent of `_ = @import("std");`. This, in term, imports start.zig, which has the logic to call main (or not). `Module` no longer has `root_scope` - the root source file is no longer special, it's just in the package table mapped to "root". I also went ahead and implemented proper detection of updated files. mtime, inode, size, and source hash are kept in `Scope.File`. During an update, iterate over `import_table` and stat each file to find out which ones are updated. The source hash is redundant with the source hash used by the struct decl that corresponds to the file, so it should be removed in a future commit before merging the branch. * AstGen: add "previously declared here" notes for variables shadowing decls. * Parse imports as structs. Module now calls `AstGen.structDeclInner`, which is called by `AstGen.containerDecl`. - `importFile` is a bit kludgy with how it handles the top level Decl that kinda gets merged into the struct decl at the end of the function. Be on the look out for bugs related to that as well as possibly cleaner ways to implement this. * Module: factor out lookupDeclName into lookupIdentifier and lookupNa * Rename `Scope.Container` to `Scope.Namespace`. * Delete some dead code. This branch won't work until `usingnamespace` is implemented because it relies on `@import("builtin").OutputMode` and `OutputMode` comes from a `usingnamespace`.
2021-04-10 07:17:50 +01:00
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",
});
}
2021-03-26 02:39:30 +00:00
{
var case = ctx.obj("variable shadowing", linux_x64);
case.addError(
\\pub fn main() void {
2021-03-26 02:39:30 +00:00
\\ var i: u32 = 10;
\\ var i: u32 = 10;
\\}
, &[_][]const u8{
":3:9: error: redeclaration of local variable 'i'",
":2:9: note: previous declaration here",
2021-03-26 02:39:30 +00:00
});
case.addError(
\\var testing: i64 = 10;
\\pub fn main() void {
2021-03-26 02:39:30 +00:00
\\ 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",
});
2021-03-26 02:39:30 +00:00
}
stage2: implement error notes and regress -femit-zir * Implement error notes - note: other symbol exported here - note: previous else prong is here - note: previous '_' prong is here * Add Compilation.CObject.ErrorMsg. This object properly converts to AllErrors.Message when the time comes. * Add Compilation.CObject.failure_retryable. Properly handles out-of-memory and other transient failures. * Introduce Module.SrcLoc which has not only a byte offset but also references the file which the byte offset applies to. * Scope.Block now contains both a pointer to the "owner" Decl and the "source" Decl. As an example, during inline function call, the "owner" will be the Decl of the caller and the "source" will be the Decl of the callee. * Module.ErrorMsg now sports a `file_scope` field so that notes can refer to source locations in a file other than the parent error message. * Some instances where a `*Scope` was stored, now store a `*Scope.Container`. * Some methods in the `Scope` namespace were moved to the more specific type, since there was only an implementation for one particular tag. - `removeDecl` moved to `Scope.Container` - `destroy` moved to `Scope.File` * Two kinds of Scope deleted: - zir_module - decl * astgen: properly use DeclVal / DeclRef. DeclVal was incorrectly changed to be a reference; this commit fixes it. Fewer ZIR instructions processed as a result. - declval_in_module is renamed to declval - previous declval ZIR instruction is deleted; it was only for .zir files. * Test harness: friendlier diagnostics when an unexpected set of errors is encountered. * zir_sema: fix analyzeInstBlockFlat by properly calling resolvingInst on the last zir instruction in the block. Compile log implementation: * Write to a buffer rather than directly to stderr. * Only keep track of 1 callsite per Decl. * No longer mutate the ZIR Inst struct data. * "Compile log statement found" errors are only emitted when there are no other compile errors. -femit-zir and support for .zir source files is regressed. If we wanted to support this again, outputting .zir would need to be done as yet another backend rather than in the haphazard way it was previously implemented. For parsing .zir, it was implemented previously in a way that was not helpful for debugging. We need tighter integration with the test harness for it to be useful; so clearly a rewrite is needed. Given that a rewrite is needed, and it was getting in the way of progress and organization of the rest of stage2, I regressed the feature.
2021-01-17 05:51:01 +00:00
2021-03-26 03:11:23 +00:00
{
// 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 {
2021-03-26 03:11:23 +00:00
\\ const b = true;
\\ var f: u32 = 1;
\\ @compileLog(b, 20, f, x);
\\ @compileLog(1000);
\\ var bruh: usize = true;
2021-06-12 19:48:13 +01:00
\\ _ = bruh;
2021-03-26 03:11:23 +00:00
\\ unreachable;
\\}
\\export fn other() void {
\\ @compileLog(1234);
\\}
\\fn x() void {}
, &[_][]const u8{
":6:23: error: expected usize, found bool",
});
stage2: implement error notes and regress -femit-zir * Implement error notes - note: other symbol exported here - note: previous else prong is here - note: previous '_' prong is here * Add Compilation.CObject.ErrorMsg. This object properly converts to AllErrors.Message when the time comes. * Add Compilation.CObject.failure_retryable. Properly handles out-of-memory and other transient failures. * Introduce Module.SrcLoc which has not only a byte offset but also references the file which the byte offset applies to. * Scope.Block now contains both a pointer to the "owner" Decl and the "source" Decl. As an example, during inline function call, the "owner" will be the Decl of the caller and the "source" will be the Decl of the callee. * Module.ErrorMsg now sports a `file_scope` field so that notes can refer to source locations in a file other than the parent error message. * Some instances where a `*Scope` was stored, now store a `*Scope.Container`. * Some methods in the `Scope` namespace were moved to the more specific type, since there was only an implementation for one particular tag. - `removeDecl` moved to `Scope.Container` - `destroy` moved to `Scope.File` * Two kinds of Scope deleted: - zir_module - decl * astgen: properly use DeclVal / DeclRef. DeclVal was incorrectly changed to be a reference; this commit fixes it. Fewer ZIR instructions processed as a result. - declval_in_module is renamed to declval - previous declval ZIR instruction is deleted; it was only for .zir files. * Test harness: friendlier diagnostics when an unexpected set of errors is encountered. * zir_sema: fix analyzeInstBlockFlat by properly calling resolvingInst on the last zir instruction in the block. Compile log implementation: * Write to a buffer rather than directly to stderr. * Only keep track of 1 callsite per Decl. * No longer mutate the ZIR Inst struct data. * "Compile log statement found" errors are only emitted when there are no other compile errors. -femit-zir and support for .zir source files is regressed. If we wanted to support this again, outputting .zir would need to be done as yet another backend rather than in the haphazard way it was previously implemented. For parsing .zir, it was implemented previously in a way that was not helpful for debugging. We need tighter integration with the test harness for it to be useful; so clearly a rewrite is needed. Given that a rewrite is needed, and it was getting in the way of progress and organization of the rest of stage2, I regressed the feature.
2021-01-17 05:51:01 +00:00
2021-03-26 03:11:23 +00:00
// Now only compile log statements remain. One per Decl.
case.addError(
\\export fn _start() noreturn {
2021-03-26 03:11:23 +00:00
\\ 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 {
stage2: more principled approach to comptime references * AIR no longer has a `variables` array. Instead of the `varptr` instruction, Sema emits a constant with a `decl_ref`. * AIR no longer has a `ref` instruction. There is no longer any instruction that takes a value and returns a pointer to it. If this is desired, Sema must either create an anynomous Decl and return a constant `decl_ref`, or in the case of a runtime value, emit an `alloc` instruction, `store` the value to it, and then return the `alloc`. * The `ref_val` Value Tag is eliminated. `decl_ref` should be used instead. Also added is `eu_payload_ptr` which points to the payload of an error union, given an error union pointer. In general, Sema should avoid calling `analyzeRef` if it can be helped. For example in the case of field_val and elem_val, there should never be a reason to create a temporary (alloc or decl). Recent previous commits made progress along that front. There is a new abstraction in Sema, which looks like this: var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); // here 'anon_decl.arena()` may be used const decl = try anon_decl.finish(ty, val); // decl is typically now used with `decl_ref`. This pattern is used to upgrade `ref_val` usages to `decl_ref` usages. Additional improvements: * Sema: fix source location resolution for calling convention expression. * Sema: properly report "unable to resolve comptime value" for loads of global variables. There is now a set of functions which can be called if the callee wants to obtain the Value even if the tag is `variable` (indicating comptime-known address but runtime-known value). * Sema: `coerce` resolves builtin types before checking equality. * Sema: fix `u1_type` missing from `addType`, making this type have a slightly more efficient representation in AIR. * LLVM backend: fix `genTypedValue` for tags `decl_ref` and `variable` to properly do an LLVMConstBitCast. * Remove unused parameter from `Value.toEnum`. After this commit, some test cases are no longer passing. This is due to the more principled approach to comptime references causing more anonymous decls to get sent to the linker for codegen. However, in all these cases the decls are not actually referenced by the runtime machine code. A future commit in this branch will implement garbage collection of decls so that unused decls do not get sent to the linker for codegen. This will make the tests go back to passing.
2021-07-29 23:59:51 +01:00
\\ const x = foo + foo;
\\ _ = x;
\\}
\\extern var foo: i32;
stage2: more principled approach to comptime references * AIR no longer has a `variables` array. Instead of the `varptr` instruction, Sema emits a constant with a `decl_ref`. * AIR no longer has a `ref` instruction. There is no longer any instruction that takes a value and returns a pointer to it. If this is desired, Sema must either create an anynomous Decl and return a constant `decl_ref`, or in the case of a runtime value, emit an `alloc` instruction, `store` the value to it, and then return the `alloc`. * The `ref_val` Value Tag is eliminated. `decl_ref` should be used instead. Also added is `eu_payload_ptr` which points to the payload of an error union, given an error union pointer. In general, Sema should avoid calling `analyzeRef` if it can be helped. For example in the case of field_val and elem_val, there should never be a reason to create a temporary (alloc or decl). Recent previous commits made progress along that front. There is a new abstraction in Sema, which looks like this: var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); // here 'anon_decl.arena()` may be used const decl = try anon_decl.finish(ty, val); // decl is typically now used with `decl_ref`. This pattern is used to upgrade `ref_val` usages to `decl_ref` usages. Additional improvements: * Sema: fix source location resolution for calling convention expression. * Sema: properly report "unable to resolve comptime value" for loads of global variables. There is now a set of functions which can be called if the callee wants to obtain the Value even if the tag is `variable` (indicating comptime-known address but runtime-known value). * Sema: `coerce` resolves builtin types before checking equality. * Sema: fix `u1_type` missing from `addType`, making this type have a slightly more efficient representation in AIR. * LLVM backend: fix `genTypedValue` for tags `decl_ref` and `variable` to properly do an LLVMConstBitCast. * Remove unused parameter from `Value.toEnum`. After this commit, some test cases are no longer passing. This is due to the more principled approach to comptime references causing more anonymous decls to get sent to the linker for codegen. However, in all these cases the decls are not actually referenced by the runtime machine code. A future commit in this branch will implement garbage collection of decls so that unused decls do not get sent to the linker for codegen. This will make the tests go back to passing.
2021-07-29 23:59:51 +01:00
, &[_][]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"});
}
2021-03-27 01:26:39 +00:00
{
var case = ctx.exe("break/continue", linux_x64);
2020-12-26 00:36:12 +00:00
2021-03-27 01:26:39 +00:00
// Break out of loop
case.addCompareOutput(
\\pub fn main() void {
2021-03-27 01:26:39 +00:00
\\ while (true) {
\\ break;
\\ }
\\}
,
"",
);
case.addCompareOutput(
\\pub fn main() void {
2021-03-27 01:26:39 +00:00
\\ foo: while (true) {
\\ break :foo;
\\ }
\\}
,
"",
);
2021-03-27 01:26:39 +00:00
// Continue in loop
case.addCompareOutput(
\\pub export fn _start() noreturn {
2021-03-27 01:26:39 +00:00
\\ 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 {
2021-03-27 01:26:39 +00:00
\\ 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;
\\}
,
"",
);
}
2020-12-26 00:36:12 +00:00
{
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",
});
2020-12-26 00:36:12 +00:00
}
{
var case = ctx.exe("bad inferred variable type", linux_x64);
case.addError(
\\pub fn main() void {
\\ var x = null;
2021-06-12 19:48:13 +01:00
\\ _ = 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;
\\}
,
"",
);
}
stage2: support recursive inline/comptime functions zir.Inst no longer has an `analyzed_inst` field. This is previously how we mapped ZIR to their TZIR counterparts, however with the way inline and comptime function calls work, we can potentially have the same ZIR structure being analyzed by multiple different analyses, such as during a recursive inline function call. This would cause the `analyzed_inst` field to become clobbered. So instead, we use a table to map the instructions to their semantically analyzed counterparts. This will help with multi-threaded compilation as well. Scope.Block.Inlining is split into 2 different layers of "sharedness". The first layer is shared by the whole inline/comptime function call stack. It contains the callsite where something is being inlined and the branch count/quota. The second layer is different per function call but shared by all the blocks within the function being inlined. Add support for debug dumping br and brvoid TZIR instructions. Remove the "unreachable code" error. It was happening even for this case: ```zig if (comptime_condition) return; bar(); // error: unreachable code ``` We will need smarter logic for when it is legal to emit this compile error. Remove the ZIR test cases. These are redundant with other higher level Zig source tests we have, and maintaining support for ZIRModule as a first-class top level abstraction is getting in the way of clean compiler design for the main use case. We will have ZIR/TZIR based test cases someday to help with testing optimization passes and ZIR to TZIR analysis, but as is, these test cases are not accomplishing that, and they are getting in the way.
2021-01-03 05:42:07 +00:00
{
var case = ctx.exe("recursive inline function", linux_x64);
case.addCompareOutput(
\\pub export fn _start() noreturn {
stage2: support recursive inline/comptime functions zir.Inst no longer has an `analyzed_inst` field. This is previously how we mapped ZIR to their TZIR counterparts, however with the way inline and comptime function calls work, we can potentially have the same ZIR structure being analyzed by multiple different analyses, such as during a recursive inline function call. This would cause the `analyzed_inst` field to become clobbered. So instead, we use a table to map the instructions to their semantically analyzed counterparts. This will help with multi-threaded compilation as well. Scope.Block.Inlining is split into 2 different layers of "sharedness". The first layer is shared by the whole inline/comptime function call stack. It contains the callsite where something is being inlined and the branch count/quota. The second layer is different per function call but shared by all the blocks within the function being inlined. Add support for debug dumping br and brvoid TZIR instructions. Remove the "unreachable code" error. It was happening even for this case: ```zig if (comptime_condition) return; bar(); // error: unreachable code ``` We will need smarter logic for when it is legal to emit this compile error. Remove the ZIR test cases. These are redundant with other higher level Zig source tests we have, and maintaining support for ZIRModule as a first-class top level abstraction is getting in the way of clean compiler design for the main use case. We will have ZIR/TZIR based test cases someday to help with testing optimization passes and ZIR to TZIR analysis, but as is, these test cases are not accomplishing that, and they are getting in the way.
2021-01-03 05:42:07 +00:00
\\ const y = fibonacci(7);
\\ exit(y - 21);
\\}
\\
\\fn fibonacci(n: usize) callconv(.Inline) usize {
stage2: support recursive inline/comptime functions zir.Inst no longer has an `analyzed_inst` field. This is previously how we mapped ZIR to their TZIR counterparts, however with the way inline and comptime function calls work, we can potentially have the same ZIR structure being analyzed by multiple different analyses, such as during a recursive inline function call. This would cause the `analyzed_inst` field to become clobbered. So instead, we use a table to map the instructions to their semantically analyzed counterparts. This will help with multi-threaded compilation as well. Scope.Block.Inlining is split into 2 different layers of "sharedness". The first layer is shared by the whole inline/comptime function call stack. It contains the callsite where something is being inlined and the branch count/quota. The second layer is different per function call but shared by all the blocks within the function being inlined. Add support for debug dumping br and brvoid TZIR instructions. Remove the "unreachable code" error. It was happening even for this case: ```zig if (comptime_condition) return; bar(); // error: unreachable code ``` We will need smarter logic for when it is legal to emit this compile error. Remove the ZIR test cases. These are redundant with other higher level Zig source tests we have, and maintaining support for ZIRModule as a first-class top level abstraction is getting in the way of clean compiler design for the main use case. We will have ZIR/TZIR based test cases someday to help with testing optimization passes and ZIR to TZIR analysis, but as is, these test cases are not accomplishing that, and they are getting in the way.
2021-01-03 05:42:07 +00:00
\\ 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").
stage2: support recursive inline/comptime functions zir.Inst no longer has an `analyzed_inst` field. This is previously how we mapped ZIR to their TZIR counterparts, however with the way inline and comptime function calls work, we can potentially have the same ZIR structure being analyzed by multiple different analyses, such as during a recursive inline function call. This would cause the `analyzed_inst` field to become clobbered. So instead, we use a table to map the instructions to their semantically analyzed counterparts. This will help with multi-threaded compilation as well. Scope.Block.Inlining is split into 2 different layers of "sharedness". The first layer is shared by the whole inline/comptime function call stack. It contains the callsite where something is being inlined and the branch count/quota. The second layer is different per function call but shared by all the blocks within the function being inlined. Add support for debug dumping br and brvoid TZIR instructions. Remove the "unreachable code" error. It was happening even for this case: ```zig if (comptime_condition) return; bar(); // error: unreachable code ``` We will need smarter logic for when it is legal to emit this compile error. Remove the ZIR test cases. These are redundant with other higher level Zig source tests we have, and maintaining support for ZIRModule as a first-class top level abstraction is getting in the way of clean compiler design for the main use case. We will have ZIR/TZIR based test cases someday to help with testing optimization passes and ZIR to TZIR analysis, but as is, these test cases are not accomplishing that, and they are getting in the way.
2021-01-03 05:42:07 +00:00
case.addError(
\\pub export fn _start() noreturn {
stage2: support recursive inline/comptime functions zir.Inst no longer has an `analyzed_inst` field. This is previously how we mapped ZIR to their TZIR counterparts, however with the way inline and comptime function calls work, we can potentially have the same ZIR structure being analyzed by multiple different analyses, such as during a recursive inline function call. This would cause the `analyzed_inst` field to become clobbered. So instead, we use a table to map the instructions to their semantically analyzed counterparts. This will help with multi-threaded compilation as well. Scope.Block.Inlining is split into 2 different layers of "sharedness". The first layer is shared by the whole inline/comptime function call stack. It contains the callsite where something is being inlined and the branch count/quota. The second layer is different per function call but shared by all the blocks within the function being inlined. Add support for debug dumping br and brvoid TZIR instructions. Remove the "unreachable code" error. It was happening even for this case: ```zig if (comptime_condition) return; bar(); // error: unreachable code ``` We will need smarter logic for when it is legal to emit this compile error. Remove the ZIR test cases. These are redundant with other higher level Zig source tests we have, and maintaining support for ZIRModule as a first-class top level abstraction is getting in the way of clean compiler design for the main use case. We will have ZIR/TZIR based test cases someday to help with testing optimization passes and ZIR to TZIR analysis, but as is, these test cases are not accomplishing that, and they are getting in the way.
2021-01-03 05:42:07 +00:00
\\ const y = fibonacci(999);
\\ exit(y - 21);
\\}
\\
\\fn fibonacci(n: usize) callconv(.Inline) usize {
stage2: support recursive inline/comptime functions zir.Inst no longer has an `analyzed_inst` field. This is previously how we mapped ZIR to their TZIR counterparts, however with the way inline and comptime function calls work, we can potentially have the same ZIR structure being analyzed by multiple different analyses, such as during a recursive inline function call. This would cause the `analyzed_inst` field to become clobbered. So instead, we use a table to map the instructions to their semantically analyzed counterparts. This will help with multi-threaded compilation as well. Scope.Block.Inlining is split into 2 different layers of "sharedness". The first layer is shared by the whole inline/comptime function call stack. It contains the callsite where something is being inlined and the branch count/quota. The second layer is different per function call but shared by all the blocks within the function being inlined. Add support for debug dumping br and brvoid TZIR instructions. Remove the "unreachable code" error. It was happening even for this case: ```zig if (comptime_condition) return; bar(); // error: unreachable code ``` We will need smarter logic for when it is legal to emit this compile error. Remove the ZIR test cases. These are redundant with other higher level Zig source tests we have, and maintaining support for ZIRModule as a first-class top level abstraction is getting in the way of clean compiler design for the main use case. We will have ZIR/TZIR based test cases someday to help with testing optimization passes and ZIR to TZIR analysis, but as is, these test cases are not accomplishing that, and they are getting in the way.
2021-01-03 05:42:07 +00:00
\\ 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);
\\}
2021-06-20 02:10:22 +01:00
\\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;
\\}
,
"",
);
}
2021-03-29 03:38:19 +01:00
{
var case = ctx.exe("merge error sets", linux_x64);
2021-03-29 03:38:19 +01:00
case.addCompareOutput(
\\pub fn main() void {
2021-03-29 03:38:19 +01:00
\\ const E = error{ A, B, D } || error { A, B, C };
2021-06-12 19:48:13 +01:00
\\ E.A catch {};
\\ E.B catch {};
\\ E.C catch {};
\\ E.D catch {};
2021-03-29 03:38:19 +01:00
\\ const E2 = error { X, Y } || @TypeOf(error.Z);
2021-06-12 19:48:13 +01:00
\\ E2.X catch {};
\\ E2.Y catch {};
\\ E2.Z catch {};
2021-03-29 03:38:19 +01:00
\\ 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",
});
2021-03-29 03:38:19 +01:00
}
2021-08-17 15:49:17 +01:00
{
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),
stage2 generics improvements: anytype and param type exprs AstGen result locations now have a `coerced_ty` tag which is the same as `ty` except it assumes that Sema will do a coercion, so it does not redundantly add an `as` instruction into the ZIR code. This results in cleaner ZIR and about a 14% reduction of ZIR bytes. param and param_comptime ZIR instructions now have a block body for their type expressions. This allows Sema to skip evaluation of the block in the case that the parameter is comptime-provided. It also allows a new mechanism to function: when evaluating type expressions of generic functions, if it would depend on another parameter, it returns `error.GenericPoison` which bubbles up and then is caught by the param/param_comptime instruction and then handled. This allows parameters to be evaluated independently so that the type info for functions which have comptime or anytype parameters will still have types populated for parameters that do not depend on values of previous parameters (because evaluation of their param blocks will return successfully instead of `error.GenericPoison`). It also makes iteration over the block that contains function parameters slightly more efficient since it now only contains the param instructions. Finally, it fixes the case where a generic function type expression contains a function prototype. Formerly, this situation would cause shared state to clobber each other; now it is in a proper tree structure so that can't happen. This fix also required adding a field to Sema `comptime_args_fn_inst` to make sure that the `comptime_args` field passed into Sema is applied to the correct `func` instruction. Source location for `node_offset_asm_ret_ty` is fixed; it was pointing at the asm output name rather than the return type as intended. Generic function instantiation is fixed, notably with respect to parameter type expressions that depend on previous parameters, and with respect to types which must be always comptime-known. This involves passing all the comptime arguments at a callsite of a generic function, and allowing the generic function semantic analysis to coerce the values to the proper types (since it has access to the evaluated parameter type expressions) and then decide based on the type whether the parameter is runtime known or not. In the case of explicitly marked `comptime` parameters, there is a check at the semantic analysis of the `call` instruction. Semantic analysis of `call` instructions does type coercion on the arguments, which is needed both for generic functions and to make up for using `coerced_ty` result locations (mentioned above). Tasks left in this branch: * Implement the memoization table. * Add test coverage. * Improve error reporting and source locations for compile errors.
2021-08-05 05:11:31 +01:00
\\ [arg1] "{rdi}" (60)
\\ : "rcx", "r11", "memory"
\\ );
2021-06-12 19:48:13 +01:00
\\ _ = 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"});
}
2021-06-06 19:08:31 +01:00
{
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 {}
2021-06-06 19:08:31 +01:00
, &.{":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");
2021-06-06 19:08:31 +01:00
}
{
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'",
});
}
2021-11-13 17:52:17 +00:00
{
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;
\\}
, "");
}
2019-11-23 20:56:05 +00:00
}