diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2023-03-09 18:22:51 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2023-03-15 10:48:14 -0700 |
| commit | 29cfd47d6509fc6ee1a165b3bc03180f6cf351a5 (patch) | |
| tree | b5c91bf3715f488713e24fc61dff030d7ccbad28 /test/cbe.zig | |
| parent | 7cc4a6965c28e427cbfba57a985f837734d6257e (diff) | |
| download | zig-29cfd47d6509fc6ee1a165b3bc03180f6cf351a5.tar.gz zig-29cfd47d6509fc6ee1a165b3bc03180f6cf351a5.zip | |
re-enable test-cases and get them all passing
Instead of using `zig test` to build a special version of the compiler
that runs all the test-cases, the zig build system is now used as much
as possible - all with the basic steps found in the standard library.
For incremental compilation tests (the ones that look like foo.0.zig,
foo.1.zig, foo.2.zig, etc.), a special version of the compiler is
compiled into a utility executable called "check-case" which checks
exactly one sequence of incremental updates in an independent
subprocess. Previously, all incremental and non-incremental test cases
were done in the same test runner process.
The compile error checking code is now simpler, but also a bit
rudimentary, and so it additionally makes sure that the actual compile
errors do not include *extra* messages, and it makes sure that the
actual compile errors output in the same order as expected. It is also
based on the "ends-with" property of each line rather than the previous
logic, which frankly I didn't want to touch with a ten-meter pole. The
compile error test cases have been updated to pass in light of these
differences.
Previously, 'error' mode with 0 compile errors was used to shoehorn in a
different kind of test-case - one that only checks if a piece of code
compiles without errors. Now there is a 'compile' mode of test-cases,
and 'error' must be only used when there are greater than 0 errors.
link test cases are updated to omit the target object format argument
when calling checkObject since that is no longer needed.
The test/stage2 directory is removed; the 2 files within are moved to be
directly in the test/ directory.
Diffstat (limited to 'test/cbe.zig')
| -rw-r--r-- | test/cbe.zig | 950 |
1 files changed, 950 insertions, 0 deletions
diff --git a/test/cbe.zig b/test/cbe.zig new file mode 100644 index 0000000000..25ac3cb137 --- /dev/null +++ b/test/cbe.zig @@ -0,0 +1,950 @@ +const std = @import("std"); +const Cases = @import("src/Cases.zig"); + +// 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: *Cases) !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: @import("std").builtin.CallingConvention = .C; + , &.{ + ":2:22: error: expected type 'type', found 'i32'", + ":5:26: error: unable to resolve comptime value", + ":5:26: note: calling convention must be comptime-known", + }); + } + + { + 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 { + \\ _ = @intToError(0); + \\ return 0; + \\} + , &.{":2:21: error: integer value '0' represents no error"}); + case.addError( + \\pub export fn main() c_int { + \\ _ = @intToError(3); + \\ return 0; + \\} + , &.{":2:21: 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; + \\} + , ""); + } + { + 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, + \\ }; + \\ _ = b; + \\} + , &.{ + ":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, + \\ }; + \\ _ = b; + \\} + , &.{ + ":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, + \\ }; + \\ _ = b; + \\} + , &.{ + ":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, + \\ }; + \\ _ = b; + \\} + , &.{ + ":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; + \\} + , ""); + case.addCompareOutput( + \\const E = error{e}; + \\const S = struct { x: u32 }; + \\fn f() E!u32 { + \\ const x = (try @as(E!S, S{ .x = 1 })).x; + \\ return x; + \\} + \\pub export fn main() c_int { + \\ const x = f() catch @as(u32, 0); + \\ if (x != 1) unreachable; + \\ return 0; + \\} + , ""); + } + + { + 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 'tmp.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 'tmp.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; + \\} + , ""); + case.addCompareOutput( + \\const Point = struct { x: i32, y: i32, z: i32, a: i32, b: i32 }; + \\pub export fn main() c_int { + \\ var p: Point = .{ + \\ .x = 18, + \\ .y = 24, + \\ .z = 1, + \\ .a = 2, + \\ .b = 3, + \\ }; + \\ return p.y - p.x - p.z - p.a - p.b; + \\} + , ""); + } + + { + var case = ctx.exeFromCompiledC("unions", .{}); + + case.addError( + \\const U = union { + \\ a: u32, + \\ b + \\}; + , &.{ + ":3:5: error: union field missing type", + }); + + case.addError( + \\const E = enum { a, b }; + \\const U = union(E) { + \\ a: u32 = 1, + \\ b: f32 = 2, + \\}; + , &.{ + ":2:11: error: explicitly valued tagged union requires inferred enum tag type", + ":3:14: note: tag value specified here", + }); + + case.addError( + \\const U = union(enum) { + \\ a: u32 = 1, + \\ b: f32 = 2, + \\}; + , &.{ + ":1:11: error: explicitly valued tagged union missing integer tag type", + ":2:14: note: tag value specified here", + }); + } + + { + 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 { + \\ _ = E1.a; + \\} + \\export fn bar() void { + \\ _ = 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 { + \\ _ = E1.a; + \\} + \\export fn bar() void { + \\ _ = E2.a; + \\} + , &.{ + ":3:5: error: enum fields cannot be marked comptime", + ":8:8: error: enum fields do not have types", + ":6:12: note: consider 'union(enum)' here to make it a tagged union", + }); + + // @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 { + \\ _ = E1.a; + \\} + , &.{ + ":3:13: error: enum fields cannot be aligned", + }); + + // 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 { + \\ _ = 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 { + \\ _ = 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 { a, b, _ }; + \\export fn foo() void { + \\ _ = 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 { + \\ _ = E1.a; + \\} + , &.{ + ":1:28: error: duplicate enum field 'b'", + ":1:22: note: other field here", + }); + + case.addError( + \\pub export fn main() c_int { + \\ const a = true; + \\ _ = @enumToInt(a); + \\} + , &.{ + ":3:20: error: expected enum or tagged union, found 'bool'", + }); + + case.addError( + \\pub export fn main() c_int { + \\ const a = 1; + \\ _ = @intToEnum(bool, a); + \\} + , &.{ + ":3:20: error: expected enum, found 'bool'", + }); + + case.addError( + \\const E = enum { a, b, c }; + \\pub export fn main() c_int { + \\ _ = @intToEnum(E, 3); + \\} + , &.{ + ":3:9: error: enum 'tmp.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", + ":1:21: note: unhandled enumeration value: 'b'", + ":1:11: note: enum 'tmp.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 { + \\ _ = E.d; + \\} + , &.{ + ":3:11: error: enum 'tmp.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; + \\ _ = x; + \\} + , &.{ + ":3:17: error: no field named 'd' in enum 'tmp.E'", + ":1:11: note: enum declared here", + }); + } + + { + var case = ctx.exeFromCompiledC("shift right and left", .{}); + case.addCompareOutput( + \\pub export fn main() c_int { + \\ var i: u32 = 16; + \\ assert(i >> 1, 8); + \\ return 0; + \\} + \\fn assert(a: u32, b: u32) void { + \\ if (a != b) unreachable; + \\} + , ""); + + case.addCompareOutput( + \\pub export fn main() c_int { + \\ var i: u32 = 16; + \\ assert(i << 1, 32); + \\ return 0; + \\} + \\fn assert(a: u32, b: u32) void { + \\ if (a != b) unreachable; + \\} + , ""); + } + + { + var case = ctx.exeFromCompiledC("inferred error sets", .{}); + + case.addCompareOutput( + \\pub export fn main() c_int { + \\ if (foo()) |_| { + \\ @panic("test fail"); + \\ } else |err| { + \\ if (err != error.ItBroke) { + \\ @panic("test fail"); + \\ } + \\ } + \\ return 0; + \\} + \\fn foo() !void { + \\ return error.ItBroke; + \\} + , ""); + } + + { + // TODO: add u64 tests, ran into issues with the literal generated for std.math.maxInt(u64) + var case = ctx.exeFromCompiledC("add and sub wrapping operations", .{}); + case.addCompareOutput( + \\pub export fn main() c_int { + \\ // Addition + \\ if (!add_u3(1, 1, 2)) return 1; + \\ if (!add_u3(7, 1, 0)) return 1; + \\ if (!add_i3(1, 1, 2)) return 1; + \\ if (!add_i3(3, 2, -3)) return 1; + \\ if (!add_i3(-3, -2, 3)) return 1; + \\ if (!add_c_int(1, 1, 2)) return 1; + \\ // TODO enable these when stage2 supports std.math.maxInt + \\ //if (!add_c_int(maxInt(c_int), 2, minInt(c_int) + 1)) return 1; + \\ //if (!add_c_int(maxInt(c_int) + 1, -2, maxInt(c_int))) return 1; + \\ + \\ // Subtraction + \\ if (!sub_u3(2, 1, 1)) return 1; + \\ if (!sub_u3(0, 1, 7)) return 1; + \\ if (!sub_i3(2, 1, 1)) return 1; + \\ if (!sub_i3(3, -2, -3)) return 1; + \\ if (!sub_i3(-3, 2, 3)) return 1; + \\ if (!sub_c_int(2, 1, 1)) return 1; + \\ // TODO enable these when stage2 supports std.math.maxInt + \\ //if (!sub_c_int(maxInt(c_int), -2, minInt(c_int) + 1)) return 1; + \\ //if (!sub_c_int(minInt(c_int) + 1, 2, maxInt(c_int))) return 1; + \\ + \\ return 0; + \\} + \\fn add_u3(lhs: u3, rhs: u3, expected: u3) bool { + \\ return expected == lhs +% rhs; + \\} + \\fn add_i3(lhs: i3, rhs: i3, expected: i3) bool { + \\ return expected == lhs +% rhs; + \\} + \\fn add_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool { + \\ return expected == lhs +% rhs; + \\} + \\fn sub_u3(lhs: u3, rhs: u3, expected: u3) bool { + \\ return expected == lhs -% rhs; + \\} + \\fn sub_i3(lhs: i3, rhs: i3, expected: i3) bool { + \\ return expected == lhs -% rhs; + \\} + \\fn sub_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool { + \\ return expected == lhs -% rhs; + \\} + , ""); + } + + { + var case = ctx.exeFromCompiledC("rem", linux_x64); + case.addCompareOutput( + \\fn assert(ok: bool) void { + \\ if (!ok) unreachable; + \\} + \\fn rem(lhs: i32, rhs: i32, expected: i32) bool { + \\ return @rem(lhs, rhs) == expected; + \\} + \\pub export fn main() c_int { + \\ assert(rem(-5, 3, -2)); + \\ assert(rem(5, 3, 2)); + \\ return 0; + \\} + , ""); + } +} |
