From ef83358eb6702e8541816817e98c3e7279033672 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
{#syntax#}@call(options: std.builtin.CallOptions, function: var, args: var) var{#endsyntax#}+
+ Calls a function, in the same way that invoking an expression with parentheses does: +
+ {#code_begin|test|call#} +const assert = @import("std").debug.assert; + +test "noinline function call" { + assert(@call(.{}, add, .{3, 9}) == 12); +} + +fn add(a: i32, b: i32) i32 { + return a + b; +} + {#code_end#} ++ {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The + {#syntax#}CallOptions{#endsyntax#} struct is reproduced here: +
+ {#code_begin|syntax#} +pub const CallOptions = struct { + modifier: Modifier = .auto, + stack: ?[]align(std.Target.stack_align) u8 = null, + + pub const Modifier = enum { + /// Equivalent to function call syntax. + auto, + + /// Asserts that the function call will not suspend. This allows a + /// non-async function to call an async function. + no_async, + + /// The function call will return an async function frame instead of + /// the function's result, which is expected to then be awaited. + /// This is equivalent to using the `async` keyword in front of function + /// call syntax. + async_call, + + /// Prevents tail call optimization. This guarantees that the return + /// address will point to the callsite, as opposed to the callsite's + /// callsite. If the call is otherwise required to be tail-called + /// or inlined, a compile error is emitted instead. + never_tail, + + /// Guarantees that the call will not be inlined. If the call is + /// otherwise required to be inlined, a compile error is emitted instead. + never_inline, + + /// Guarantees that the call will be generated with tail call optimization. + /// If this is not possible, a compile error is emitted instead. + always_tail, + + /// Guarantees that the call will inlined at the callsite. + /// If this is not possible, a compile error is emitted instead. + always_inline, + + /// Evaluates the call at compile-time. If the call cannot be completed at + /// compile-time, a compile error is emitted instead. + compile_time, + }; +}; + {#code_end#} + {#header_close#} + {#header_open|@cDefine#}{#syntax#}@cDefine(comptime name: []u8, value){#endsyntax#}
@@ -7445,7 +7510,7 @@ fn add(a: i32, b: i32) i32 { return a + b; } Unlike a normal function call, however, {#syntax#}@inlineCall{#endsyntax#} guarantees that the call will be inlined. If the call cannot be inlined, a compile error is emitted.
- {#see_also|@noInlineCall#} + {#see_also|@call#} {#header_close#} {#header_open|@intCast#} @@ -7647,29 +7712,6 @@ fn targetFunction(x: i32) usize { {#code_end#} {#header_close#} - {#header_open|@noInlineCall#} -{#syntax#}@noInlineCall(function: var, args: ...) var{#endsyntax#}-
- This calls a function, in the same way that invoking an expression with parentheses does: -
- {#code_begin|test#} -const assert = @import("std").debug.assert; - -test "noinline function call" { - assert(@noInlineCall(add, 3, 9) == 12); -} - -fn add(a: i32, b: i32) i32 { - return a + b; -} - {#code_end#} -- Unlike a normal function call, however, {#syntax#}@noInlineCall{#endsyntax#} guarantees that the call - will not be inlined. If the call must be inlined, a compile error is emitted. -
- {#see_also|@inlineCall#} - {#header_close#} - {#header_open|@OpaqueType#}{#syntax#}@OpaqueType() type{#endsyntax#}
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 791d46bc3..8ac58ad2f 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -379,13 +379,39 @@ pub const CallOptions = struct { stack: ?[]align(std.Target.stack_align) u8 = null, pub const Modifier = enum { + /// Equivalent to function call syntax. auto, + + /// Asserts that the function call will not suspend. This allows a + /// non-async function to call an async function. no_async, + + /// The function call will return an async function frame instead of + /// the function's result, which is expected to then be awaited. + /// This is equivalent to using the `async` keyword in front of function + /// call syntax. async_call, + + /// Prevents tail call optimization. This guarantees that the return + /// address will point to the callsite, as opposed to the callsite's + /// callsite. If the call is otherwise required to be tail-called + /// or inlined, a compile error is emitted instead. never_tail, + + /// Guarantees that the call will not be inlined. If the call is + /// otherwise required to be inlined, a compile error is emitted instead. never_inline, + + /// Guarantees that the call will be generated with tail call optimization. + /// If this is not possible, a compile error is emitted instead. always_tail, + + /// Guarantees that the call will inlined at the callsite. + /// If this is not possible, a compile error is emitted instead. always_inline, + + /// Evaluates the call at compile-time. If the call cannot be completed at + /// compile-time, a compile error is emitted instead. compile_time, }; }; diff --git a/lib/std/special/start.zig b/lib/std/special/start.zig index a93b01c29..8815e17a9 100644 --- a/lib/std/special/start.zig +++ b/lib/std/special/start.zig @@ -125,7 +125,7 @@ nakedcc fn _start() noreturn { } // If LLVM inlines stack variables into _start, they will overwrite // the command line argument data. - @noInlineCall(posixCallMainAndExit); + @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{}); } stdcallcc fn WinMainCRTStartup() noreturn { diff --git a/src/all_types.hpp b/src/all_types.hpp index c3d5793d7..c350b04c9 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1701,7 +1701,6 @@ enum BuiltinFnId { BuiltinFnIdByteOffsetOf, BuiltinFnIdBitOffsetOf, BuiltinFnIdInlineCall, - BuiltinFnIdNoInlineCall, BuiltinFnIdNewStackCall, BuiltinFnIdAsyncCall, BuiltinFnIdTypeId, diff --git a/src/codegen.cpp b/src/codegen.cpp index 35ad039d6..c1d3dc332 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8133,7 +8133,6 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdRound, "round", 2); create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4); create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); - create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdAsyncCall, "asyncCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1); diff --git a/src/ir.cpp b/src/ir.cpp index 23114f9b5..e40fb854d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6014,7 +6014,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_lval_wrap(irb, scope, offset_of, lval, result_loc); } case BuiltinFnIdInlineCall: - case BuiltinFnIdNoInlineCall: { if (node->data.fn_call_expr.params.length == 0) { add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0")); @@ -6035,11 +6034,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (args[i] == irb->codegen->invalid_instruction) return args[i]; } - CallModifier modifier = (builtin_fn->id == BuiltinFnIdInlineCall) ? - CallModifierAlwaysInline : CallModifierNeverInline; IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, - nullptr, modifier, false, nullptr, result_loc); + nullptr, CallModifierAlwaysInline, false, nullptr, result_loc); return ir_lval_wrap(irb, scope, call, lval, result_loc); } case BuiltinFnIdNewStackCall: diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 49c7094fb..d1244188e 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -13,11 +13,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry3() void { \\ comptime @call(.{ .modifier = .never_tail }, foo, .{}); \\} + \\export fn entry4() void { + \\ @call(.{ .modifier = .never_inline }, bar, .{}); + \\} + \\export fn entry5(c: bool) void { + \\ var baz = if (c) baz1 else baz2; + \\ @call(.{ .modifier = .compile_time }, baz, .{}); + \\} \\fn foo() void {} + \\inline fn bar() void {} + \\fn baz1() void {} + \\fn baz2() void {} , "tmp.zig:2:21: error: expected tuple or struct, found 'void'", "tmp.zig:5:58: error: unable to perform 'never_inline' call at compile-time", "tmp.zig:8:56: error: unable to perform 'never_tail' call at compile-time", + "tmp.zig:11:5: error: no-inline call of inline function", + "tmp.zig:15:43: error: unable to evaluate constant expression", ); cases.add( @@ -1945,17 +1957,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:2:12: error: use of undeclared identifier 'SomeNonexistentType'", ); - cases.add( - "@noInlineCall on an inline function", - \\inline fn foo() void {} - \\ - \\export fn entry() void { - \\ @noInlineCall(foo); - \\} - , - "tmp.zig:4:5: error: no-inline call of inline function", - ); - cases.add( "comptime continue inside runtime catch", \\export fn entry(c: bool) void {