aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorVeikka Tuominen <git@vexu.eu>2022-08-30 14:04:13 +0300
committerAndrew Kelley <andrew@ziglang.org>2022-08-30 12:22:07 -0700
commit67a44211f7a442d33096cc0dfff059eee9315bc6 (patch)
tree7cdb35205977cab28a9ed8cc5846096d2a57ed4d /test
parent0a42602418dcaf08f13b4220b6c216356f87cbfc (diff)
downloadzig-67a44211f7a442d33096cc0dfff059eee9315bc6.tar.gz
zig-67a44211f7a442d33096cc0dfff059eee9315bc6.zip
Sema: improve handling of always_tail call modifier
Closes #4301 Closes #5692 Closes #6281 Closes #10786 Closes #11149 Closes #11776
Diffstat (limited to 'test')
-rw-r--r--test/behavior/call.zig54
-rw-r--r--test/cases/compile_errors/invalid_tail_call.zig12
-rw-r--r--test/cases/taill_call_noreturn.zig18
3 files changed, 84 insertions, 0 deletions
diff --git a/test/behavior/call.zig b/test/behavior/call.zig
index eafd2ef4e9..4c697ed542 100644
--- a/test/behavior/call.zig
+++ b/test/behavior/call.zig
@@ -261,3 +261,57 @@ test "arguments to comptime parameters generated in comptime blocks" {
};
S.foo(S.fortyTwo());
}
+
+test "forced tail call" {
+ if (builtin.zig_backend == .stage1) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
+ const S = struct {
+ fn fibonacciTailInternal(n: u16, a: u16, b: u16) u16 {
+ if (n == 0) return a;
+ if (n == 1) return b;
+ return @call(
+ .{ .modifier = .always_tail },
+ fibonacciTailInternal,
+ .{ n - 1, b, a + b },
+ );
+ }
+
+ fn fibonacciTail(n: u16) u16 {
+ return fibonacciTailInternal(n, 0, 1);
+ }
+ };
+ try expect(S.fibonacciTail(10) == 55);
+}
+
+test "inline call preserves tail call" {
+ if (builtin.zig_backend == .stage1) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
+ const max = std.math.maxInt(u16);
+ const S = struct {
+ var a: u16 = 0;
+ fn foo() void {
+ return bar();
+ }
+
+ inline fn bar() void {
+ if (a == max) return;
+ // Stack overflow if not tail called
+ var buf: [max]u16 = undefined;
+ buf[a] = a;
+ a += 1;
+ return @call(.{ .modifier = .always_tail }, foo, .{});
+ }
+ };
+ S.foo();
+ try expect(S.a == std.math.maxInt(u16));
+}
diff --git a/test/cases/compile_errors/invalid_tail_call.zig b/test/cases/compile_errors/invalid_tail_call.zig
new file mode 100644
index 0000000000..cdeb9df930
--- /dev/null
+++ b/test/cases/compile_errors/invalid_tail_call.zig
@@ -0,0 +1,12 @@
+fn myFn(_: usize) void {
+ return;
+}
+pub export fn entry() void {
+ @call(.{ .modifier = .always_tail }, myFn, .{0});
+}
+
+// error
+// backend=llvm
+// target=native
+//
+// :5:5: error: unable to perform tail call: type of function being called 'fn(usize) void' does not match type of calling function 'fn() callconv(.C) void'
diff --git a/test/cases/taill_call_noreturn.zig b/test/cases/taill_call_noreturn.zig
new file mode 100644
index 0000000000..0c2497d6ce
--- /dev/null
+++ b/test/cases/taill_call_noreturn.zig
@@ -0,0 +1,18 @@
+const std = @import("std");
+const builtin = std.builtin;
+pub fn foo(message: []const u8, stack_trace: ?*builtin.StackTrace) noreturn {
+ @call(.{ .modifier = .always_tail }, bar, .{ message, stack_trace });
+}
+pub fn bar(message: []const u8, stack_trace: ?*builtin.StackTrace) noreturn {
+ _ = message;
+ _ = stack_trace;
+ std.process.exit(0);
+}
+
+pub fn main() void {
+ foo("foo", null);
+}
+
+// run
+// backend=llvm
+// target=native