zig/test/stage2/cbe.zig
Andrew Kelley 1d3f76bbda stage2: skip recursion test on some hosts
In order for this test to pass, the host linking/start code needs to
support explicitly setting the stack size. Zig defaults to 16 MiB stack
size, which is enough to pass the test in Debug builds, however, most
operating systems do not honor the stack size we request for and give a
smaller amount.

Eventually the goal is to pass this test on all hosts.
2021-05-17 19:14:13 -07:00

875 lines
26 KiB
Zig

const std = @import("std");
const TestContext = @import("../../src/test.zig").TestContext;
// These tests should work with all platforms, but we're using linux_x64 for
// now for consistency. Will be expanded eventually.
const linux_x64 = std.zig.CrossTarget{
.cpu_arch = .x86_64,
.os_tag = .linux,
};
pub fn addCases(ctx: *TestContext) !void {
{
var case = ctx.exeFromCompiledC("hello world with updates", .{});
// Regular old hello world
case.addCompareOutput(
\\extern fn puts(s: [*:0]const u8) c_int;
\\pub export fn main() c_int {
\\ _ = puts("hello world!");
\\ return 0;
\\}
, "hello world!" ++ std.cstr.line_sep);
// Now change the message only
case.addCompareOutput(
\\extern fn puts(s: [*:0]const u8) c_int;
\\pub export fn main() c_int {
\\ _ = puts("yo");
\\ return 0;
\\}
, "yo" ++ std.cstr.line_sep);
// Add an unused Decl
case.addCompareOutput(
\\extern fn puts(s: [*:0]const u8) c_int;
\\pub export fn main() c_int {
\\ _ = puts("yo!");
\\ return 0;
\\}
\\fn unused() void {}
, "yo!" ++ std.cstr.line_sep);
// Comptime return type and calling convention expected.
case.addError(
\\var x: i32 = 1234;
\\pub export fn main() x {
\\ return 0;
\\}
\\export fn foo() callconv(y) c_int {
\\ return 0;
\\}
\\var y: i32 = 1234;
, &.{
":2:22: error: unable to resolve comptime value",
":5:26: error: unable to resolve comptime value",
});
}
{
var case = ctx.exeFromCompiledC("var args", .{});
case.addCompareOutput(
\\extern fn printf(format: [*:0]const u8, ...) c_int;
\\
\\pub export fn main() c_int {
\\ _ = printf("Hello, %s!\n", "world");
\\ return 0;
\\}
, "Hello, world!" ++ std.cstr.line_sep);
}
{
var case = ctx.exeFromCompiledC("@intToError", .{});
case.addCompareOutput(
\\pub export fn main() c_int {
\\ // comptime checks
\\ const a = error.A;
\\ const b = error.B;
\\ const c = @intToError(2);
\\ const d = @intToError(1);
\\ if (!(c == b)) unreachable;
\\ if (!(a == d)) unreachable;
\\ // runtime checks
\\ var x = error.A;
\\ var y = error.B;
\\ var z = @intToError(2);
\\ var f = @intToError(1);
\\ if (!(y == z)) unreachable;
\\ if (!(x == f)) unreachable;
\\ return 0;
\\}
, "");
case.addError(
\\pub export fn main() c_int {
\\ const c = @intToError(0);
\\ return 0;
\\}
, &.{":2:27: error: integer value 0 represents no error"});
case.addError(
\\pub export fn main() c_int {
\\ const c = @intToError(3);
\\ return 0;
\\}
, &.{":2:27: error: integer value 3 represents no error"});
}
{
var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64);
// Exit with 0
case.addCompareOutput(
\\fn exitGood() noreturn {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (0)
\\ );
\\ unreachable;
\\}
\\
\\pub export fn main() c_int {
\\ exitGood();
\\}
, "");
// Pass a usize parameter to exit
case.addCompareOutput(
\\pub export fn main() c_int {
\\ exit(0);
\\}
\\
\\fn exit(code: usize) noreturn {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (code)
\\ );
\\ unreachable;
\\}
, "");
// Change the parameter to u8
case.addCompareOutput(
\\pub export fn main() c_int {
\\ exit(0);
\\}
\\
\\fn exit(code: u8) noreturn {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (code)
\\ );
\\ unreachable;
\\}
, "");
// Do some arithmetic at the exit callsite
case.addCompareOutput(
\\pub export fn main() c_int {
\\ exitMath(1);
\\}
\\
\\fn exitMath(a: u8) noreturn {
\\ exit(0 + a - a);
\\}
\\
\\fn exit(code: u8) noreturn {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (code)
\\ );
\\ unreachable;
\\}
\\
, "");
// Invert the arithmetic
case.addCompareOutput(
\\pub export fn main() c_int {
\\ exitMath(1);
\\}
\\
\\fn exitMath(a: u8) noreturn {
\\ exit(a + 0 - a);
\\}
\\
\\fn exit(code: u8) noreturn {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (code)
\\ );
\\ unreachable;
\\}
\\
, "");
}
{
var case = ctx.exeFromCompiledC("alloc and retptr", .{});
case.addCompareOutput(
\\fn add(a: i32, b: i32) i32 {
\\ return a + b;
\\}
\\
\\fn addIndirect(a: i32, b: i32) i32 {
\\ return add(a, b);
\\}
\\
\\pub export fn main() c_int {
\\ return addIndirect(1, 2) - 3;
\\}
, "");
}
{
var case = ctx.exeFromCompiledC("inferred local const and var", .{});
case.addCompareOutput(
\\fn add(a: i32, b: i32) i32 {
\\ return a + b;
\\}
\\
\\pub export fn main() c_int {
\\ const x = add(1, 2);
\\ var y = add(3, 0);
\\ y -= x;
\\ return y;
\\}
, "");
}
// This will make a pretty deep call stack, so this test can only be enabled
// on hosts where Zig's linking strategy can honor the 16 MiB (default) we
// link the self-hosted compiler with.
const host_supports_custom_stack_size = @import("builtin").target.os.tag == .linux;
if (host_supports_custom_stack_size) {
var case = ctx.exeFromCompiledC("@setEvalBranchQuota", .{});
case.addCompareOutput(
\\pub export fn main() i32 {
\\ @setEvalBranchQuota(1001);
\\ const y = rec(1001);
\\ return y - 1;
\\}
\\
\\fn rec(n: usize) callconv(.Inline) usize {
\\ if (n <= 1) return n;
\\ return rec(n - 1);
\\}
, "");
}
{
var case = ctx.exeFromCompiledC("control flow", .{});
// Simple while loop
case.addCompareOutput(
\\pub export fn main() c_int {
\\ var a: c_int = 0;
\\ while (a < 5) : (a+=1) {}
\\ return a - 5;
\\}
, "");
case.addCompareOutput(
\\pub export fn main() c_int {
\\ var a = true;
\\ while (!a) {}
\\ return 0;
\\}
, "");
// If expression
case.addCompareOutput(
\\pub export fn main() c_int {
\\ var cond: c_int = 0;
\\ var a: c_int = @as(c_int, if (cond == 0)
\\ 2
\\ else
\\ 3) + 9;
\\ return a - 11;
\\}
, "");
// If expression with breakpoint that does not get hit
case.addCompareOutput(
\\pub export fn main() c_int {
\\ var x: i32 = 1;
\\ if (x != 1) @breakpoint();
\\ return 0;
\\}
, "");
// Switch expression
case.addCompareOutput(
\\pub export fn main() c_int {
\\ var cond: c_int = 0;
\\ var a: c_int = switch (cond) {
\\ 1 => 1,
\\ 2 => 2,
\\ 99...300, 12 => 3,
\\ 0 => 4,
\\ else => 5,
\\ };
\\ return a - 4;
\\}
, "");
// Switch expression missing else case.
case.addError(
\\pub export fn main() c_int {
\\ var cond: c_int = 0;
\\ const a: c_int = switch (cond) {
\\ 1 => 1,
\\ 2 => 2,
\\ 3 => 3,
\\ 4 => 4,
\\ };
\\ return a - 4;
\\}
, &.{":3:22: error: switch must handle all possibilities"});
// Switch expression, has an unreachable prong.
case.addCompareOutput(
\\pub export fn main() c_int {
\\ var cond: c_int = 0;
\\ const a: c_int = switch (cond) {
\\ 1 => 1,
\\ 2 => 2,
\\ 99...300, 12 => 3,
\\ 0 => 4,
\\ 13 => unreachable,
\\ else => 5,
\\ };
\\ return a - 4;
\\}
, "");
// Switch expression, has an unreachable prong and prongs write
// to result locations.
case.addCompareOutput(
\\pub export fn main() c_int {
\\ var cond: c_int = 0;
\\ var a: c_int = switch (cond) {
\\ 1 => 1,
\\ 2 => 2,
\\ 99...300, 12 => 3,
\\ 0 => 4,
\\ 13 => unreachable,
\\ else => 5,
\\ };
\\ return a - 4;
\\}
, "");
// Integer switch expression has duplicate case value.
case.addError(
\\pub export fn main() c_int {
\\ var cond: c_int = 0;
\\ const a: c_int = switch (cond) {
\\ 1 => 1,
\\ 2 => 2,
\\ 96, 11...13, 97 => 3,
\\ 0 => 4,
\\ 90, 12 => 100,
\\ else => 5,
\\ };
\\ return a - 4;
\\}
, &.{
":8:13: error: duplicate switch value",
":6:15: note: previous value here",
});
// Boolean switch expression has duplicate case value.
case.addError(
\\pub export fn main() c_int {
\\ var a: bool = false;
\\ const b: c_int = switch (a) {
\\ false => 1,
\\ true => 2,
\\ false => 3,
\\ };
\\}
, &.{
":6:9: error: duplicate switch value",
});
// Sparse (no range capable) switch expression has duplicate case value.
case.addError(
\\pub export fn main() c_int {
\\ const A: type = i32;
\\ const b: c_int = switch (A) {
\\ i32 => 1,
\\ bool => 2,
\\ f64, i32 => 3,
\\ else => 4,
\\ };
\\}
, &.{
":6:14: error: duplicate switch value",
":4:9: note: previous value here",
});
// Ranges not allowed for some kinds of switches.
case.addError(
\\pub export fn main() c_int {
\\ const A: type = i32;
\\ const b: c_int = switch (A) {
\\ i32 => 1,
\\ bool => 2,
\\ f16...f64 => 3,
\\ else => 4,
\\ };
\\}
, &.{
":3:30: error: ranges not allowed when switching on type 'type'",
":6:12: note: range here",
});
// Switch expression has unreachable else prong.
case.addError(
\\pub export fn main() c_int {
\\ var a: u2 = 0;
\\ const b: i32 = switch (a) {
\\ 0 => 10,
\\ 1 => 20,
\\ 2 => 30,
\\ 3 => 40,
\\ else => 50,
\\ };
\\}
, &.{
":8:14: error: unreachable else prong; all cases already handled",
});
}
//{
// var case = ctx.exeFromCompiledC("optionals", .{});
// // Simple while loop
// case.addCompareOutput(
// \\pub export fn main() c_int {
// \\ var count: c_int = 0;
// \\ var opt_ptr: ?*c_int = &count;
// \\ while (opt_ptr) |_| : (count += 1) {
// \\ if (count == 4) opt_ptr = null;
// \\ }
// \\ return count - 5;
// \\}
// , "");
// // Same with non pointer optionals
// case.addCompareOutput(
// \\pub export fn main() c_int {
// \\ var count: c_int = 0;
// \\ var opt_ptr: ?c_int = count;
// \\ while (opt_ptr) |_| : (count += 1) {
// \\ if (count == 4) opt_ptr = null;
// \\ }
// \\ return count - 5;
// \\}
// , "");
//}
{
var case = ctx.exeFromCompiledC("errors", .{});
case.addCompareOutput(
\\pub export fn main() c_int {
\\ var e1 = error.Foo;
\\ var e2 = error.Bar;
\\ assert(e1 != e2);
\\ assert(e1 == error.Foo);
\\ assert(e2 == error.Bar);
\\ return 0;
\\}
\\fn assert(b: bool) void {
\\ if (!b) unreachable;
\\}
, "");
case.addCompareOutput(
\\pub export fn main() c_int {
\\ var e: anyerror!c_int = 0;
\\ const i = e catch 69;
\\ return i;
\\}
, "");
case.addCompareOutput(
\\pub export fn main() c_int {
\\ var e: anyerror!c_int = error.Foo;
\\ const i = e catch 69;
\\ return 69 - i;
\\}
, "");
}
{
var case = ctx.exeFromCompiledC("structs", .{});
case.addError(
\\const Point = struct { x: i32, y: i32 };
\\pub export fn main() c_int {
\\ var p: Point = .{
\\ .y = 24,
\\ .x = 12,
\\ .y = 24,
\\ };
\\ return p.y - p.x - p.x;
\\}
, &.{
":6:10: error: duplicate field",
":4:10: note: other field here",
});
case.addError(
\\const Point = struct { x: i32, y: i32 };
\\pub export fn main() c_int {
\\ var p: Point = .{
\\ .y = 24,
\\ };
\\ return p.y - p.x - p.x;
\\}
, &.{
":3:21: error: missing struct field: x",
":1:15: note: struct 'test_case.Point' declared here",
});
case.addError(
\\const Point = struct { x: i32, y: i32 };
\\pub export fn main() c_int {
\\ var p: Point = .{
\\ .x = 12,
\\ .y = 24,
\\ .z = 48,
\\ };
\\ return p.y - p.x - p.x;
\\}
, &.{
":6:10: error: no field named 'z' in struct 'test_case.Point'",
":1:15: note: struct declared here",
});
case.addCompareOutput(
\\const Point = struct { x: i32, y: i32 };
\\pub export fn main() c_int {
\\ var p: Point = .{
\\ .x = 12,
\\ .y = 24,
\\ };
\\ return p.y - p.x - p.x;
\\}
, "");
}
{
var case = ctx.exeFromCompiledC("enums", .{});
case.addError(
\\const E1 = packed enum { a, b, c };
\\const E2 = extern enum { a, b, c };
\\export fn foo() void {
\\ const x = E1.a;
\\}
\\export fn bar() void {
\\ const x = E2.a;
\\}
, &.{
":1:12: error: enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type",
":2:12: error: enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type",
});
// comptime and types are caught in AstGen.
case.addError(
\\const E1 = enum {
\\ a,
\\ comptime b,
\\ c,
\\};
\\const E2 = enum {
\\ a,
\\ b: i32,
\\ c,
\\};
\\export fn foo() void {
\\ const x = E1.a;
\\}
\\export fn bar() void {
\\ const x = E2.a;
\\}
, &.{
":3:5: error: enum fields cannot be marked comptime",
":8:8: error: enum fields do not have types",
});
// @enumToInt, @intToEnum, enum literal coercion, field access syntax, comparison, switch
case.addCompareOutput(
\\const Number = enum { One, Two, Three };
\\
\\pub export fn main() c_int {
\\ var number1 = Number.One;
\\ var number2: Number = .Two;
\\ const number3 = @intToEnum(Number, 2);
\\ if (number1 == number2) return 1;
\\ if (number2 == number3) return 1;
\\ if (@enumToInt(number1) != 0) return 1;
\\ if (@enumToInt(number2) != 1) return 1;
\\ if (@enumToInt(number3) != 2) return 1;
\\ var x: Number = .Two;
\\ if (number2 != x) return 1;
\\ switch (x) {
\\ .One => return 1,
\\ .Two => return 0,
\\ number3 => return 2,
\\ }
\\}
, "");
// Specifying alignment is a parse error.
// This also tests going from a successful build to a parse error.
case.addError(
\\const E1 = enum {
\\ a,
\\ b align(4),
\\ c,
\\};
\\export fn foo() void {
\\ const x = E1.a;
\\}
, &.{
":3:7: error: expected ',', found 'align'",
});
// Redundant non-exhaustive enum mark.
// This also tests going from a parse error to an AstGen error.
case.addError(
\\const E1 = enum {
\\ a,
\\ _,
\\ b,
\\ c,
\\ _,
\\};
\\export fn foo() void {
\\ const x = E1.a;
\\}
, &.{
":6:5: error: redundant non-exhaustive enum mark",
":3:5: note: other mark here",
});
case.addError(
\\const E1 = enum {
\\ a,
\\ b,
\\ c,
\\ _ = 10,
\\};
\\export fn foo() void {
\\ const x = E1.a;
\\}
, &.{
":5:9: error: '_' is used to mark an enum as non-exhaustive and cannot be assigned a value",
});
case.addError(
\\const E1 = enum {};
\\export fn foo() void {
\\ const x = E1.a;
\\}
, &.{
":1:12: error: enum declarations must have at least one tag",
});
case.addError(
\\const E1 = enum { a, b, _ };
\\export fn foo() void {
\\ const x = E1.a;
\\}
, &.{
":1:12: error: non-exhaustive enum missing integer tag type",
":1:25: note: marked non-exhaustive here",
});
case.addError(
\\const E1 = enum { a, b, c, b, d };
\\pub export fn main() c_int {
\\ const x = E1.a;
\\}
, &.{
":1:28: error: duplicate enum tag",
":1:22: note: other tag here",
});
case.addError(
\\pub export fn main() c_int {
\\ const a = true;
\\ const b = @enumToInt(a);
\\}
, &.{
":3:26: error: expected enum or tagged union, found bool",
});
case.addError(
\\pub export fn main() c_int {
\\ const a = 1;
\\ const b = @intToEnum(bool, a);
\\}
, &.{
":3:26: error: expected enum, found bool",
});
case.addError(
\\const E = enum { a, b, c };
\\pub export fn main() c_int {
\\ const b = @intToEnum(E, 3);
\\}
, &.{
":3:15: error: enum 'test_case.E' has no tag with value 3",
":1:11: note: enum declared here",
});
case.addError(
\\const E = enum { a, b, c };
\\pub export fn main() c_int {
\\ var x: E = .a;
\\ switch (x) {
\\ .a => {},
\\ .c => {},
\\ }
\\}
, &.{
":4:5: error: switch must handle all possibilities",
":4:5: note: unhandled enumeration value: 'b'",
":1:11: note: enum 'test_case.E' declared here",
});
case.addError(
\\const E = enum { a, b, c };
\\pub export fn main() c_int {
\\ var x: E = .a;
\\ switch (x) {
\\ .a => {},
\\ .b => {},
\\ .b => {},
\\ .c => {},
\\ }
\\}
, &.{
":7:10: error: duplicate switch value",
":6:10: note: previous value here",
});
case.addError(
\\const E = enum { a, b, c };
\\pub export fn main() c_int {
\\ var x: E = .a;
\\ switch (x) {
\\ .a => {},
\\ .b => {},
\\ .c => {},
\\ else => {},
\\ }
\\}
, &.{
":8:14: error: unreachable else prong; all cases already handled",
});
case.addError(
\\const E = enum { a, b, c };
\\pub export fn main() c_int {
\\ var x: E = .a;
\\ switch (x) {
\\ .a => {},
\\ .b => {},
\\ _ => {},
\\ }
\\}
, &.{
":4:5: error: '_' prong only allowed when switching on non-exhaustive enums",
":7:11: note: '_' prong here",
});
case.addError(
\\const E = enum { a, b, c };
\\pub export fn main() c_int {
\\ var x = E.d;
\\}
, &.{
":3:14: error: enum 'test_case.E' has no member named 'd'",
":1:11: note: enum declared here",
});
case.addError(
\\const E = enum { a, b, c };
\\pub export fn main() c_int {
\\ var x: E = .d;
\\}
, &.{
":3:17: error: enum 'test_case.E' has no field named 'd'",
":1:11: note: enum declared here",
});
}
ctx.c("empty start function", linux_x64,
\\export fn _start() noreturn {
\\ unreachable;
\\}
,
\\ZIG_EXTERN_C zig_noreturn void _start(void);
\\
\\zig_noreturn void _start(void) {
\\ zig_breakpoint();
\\ zig_unreachable();
\\}
\\
);
ctx.h("simple header", linux_x64,
\\export fn start() void{}
,
\\ZIG_EXTERN_C void start(void);
\\
);
ctx.h("header with single param function", linux_x64,
\\export fn start(a: u8) void{}
,
\\ZIG_EXTERN_C void start(uint8_t a0);
\\
);
ctx.h("header with multiple param function", linux_x64,
\\export fn start(a: u8, b: u8, c: u8) void{}
,
\\ZIG_EXTERN_C void start(uint8_t a0, uint8_t a1, uint8_t a2);
\\
);
ctx.h("header with u32 param function", linux_x64,
\\export fn start(a: u32) void{}
,
\\ZIG_EXTERN_C void start(uint32_t a0);
\\
);
ctx.h("header with usize param function", linux_x64,
\\export fn start(a: usize) void{}
,
\\ZIG_EXTERN_C void start(uintptr_t a0);
\\
);
ctx.h("header with bool param function", linux_x64,
\\export fn start(a: bool) void{}
,
\\ZIG_EXTERN_C void start(bool a0);
\\
);
ctx.h("header with noreturn function", linux_x64,
\\export fn start() noreturn {
\\ unreachable;
\\}
,
\\ZIG_EXTERN_C zig_noreturn void start(void);
\\
);
ctx.h("header with multiple functions", linux_x64,
\\export fn a() void{}
\\export fn b() void{}
\\export fn c() void{}
,
\\ZIG_EXTERN_C void a(void);
\\ZIG_EXTERN_C void b(void);
\\ZIG_EXTERN_C void c(void);
\\
);
ctx.h("header with multiple includes", linux_x64,
\\export fn start(a: u32, b: usize) void{}
,
\\ZIG_EXTERN_C void start(uint32_t a0, uintptr_t a1);
\\
);
}