aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Sema.zig34
-rw-r--r--src/codegen/llvm.zig2
2 files changed, 30 insertions, 6 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index a4e2106afb..823ae9baf2 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -6152,9 +6152,23 @@ fn analyzeCall(
if (ensure_result_used) {
try sema.ensureResultUsed(block, result, call_src);
}
+ if (call_tag == .call_always_tail) {
+ return sema.handleTailCall(block, call_src, func_ty, result);
+ }
return result;
}
+fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref {
+ const func_decl = sema.mod.declPtr(sema.owner_func.?.owner_decl);
+ if (!func_ty.eql(func_decl.ty, sema.mod)) {
+ return sema.fail(block, call_src, "unable to perform tail call: type of function being called '{}' does not match type of calling function '{}'", .{
+ func_ty.fmt(sema.mod), func_decl.ty.fmt(sema.mod),
+ });
+ }
+ _ = try block.addUnOp(.ret, result);
+ return Air.Inst.Ref.unreachable_value;
+}
+
fn analyzeInlineCallArg(
sema: *Sema,
arg_block: *Block,
@@ -6670,7 +6684,8 @@ fn instantiateGenericCall(
try sema.requireFunctionBlock(block, call_src);
const comptime_args = callee.comptime_args.?;
- const new_fn_info = mod.declPtr(callee.owner_decl).ty.fnInfo();
+ const func_ty = mod.declPtr(callee.owner_decl).ty;
+ const new_fn_info = func_ty.fnInfo();
const runtime_args_len = @intCast(u32, new_fn_info.param_types.len);
const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len);
{
@@ -6717,7 +6732,7 @@ fn instantiateGenericCall(
try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
runtime_args_len);
- const func_inst = try block.addInst(.{
+ const result = try block.addInst(.{
.tag = call_tag,
.data = .{ .pl_op = .{
.operand = callee_inst,
@@ -6729,9 +6744,12 @@ fn instantiateGenericCall(
sema.appendRefsAssumeCapacity(runtime_args);
if (ensure_result_used) {
- try sema.ensureResultUsed(block, func_inst, call_src);
+ try sema.ensureResultUsed(block, result, call_src);
}
- return func_inst;
+ if (call_tag == .call_always_tail) {
+ return sema.handleTailCall(block, call_src, func_ty, result);
+ }
+ return result;
}
fn emitDbgInline(
@@ -19262,7 +19280,7 @@ fn resolveCallOptions(
return wanted_modifier;
},
// These can be upgraded to comptime. nosuspend bit can be safely ignored.
- .always_tail, .always_inline, .compile_time => {
+ .always_inline, .compile_time => {
_ = (try sema.resolveDefinedValue(block, func_src, func)) orelse {
return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(wanted_modifier)});
};
@@ -19272,6 +19290,12 @@ fn resolveCallOptions(
}
return wanted_modifier;
},
+ .always_tail => {
+ if (is_comptime) {
+ return .compile_time;
+ }
+ return wanted_modifier;
+ },
.async_kw => {
if (is_nosuspend) {
return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{});
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index d1c68b430c..a431c14d5a 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -4522,7 +4522,7 @@ pub const FuncGen = struct {
"",
);
- if (return_type.isNoReturn()) {
+ if (return_type.isNoReturn() and attr != .AlwaysTail) {
_ = self.builder.buildUnreachable();
return null;
}