Commit ef83358eb6
Changed files (7)
lib
std
special
doc/langref.html.in
@@ -6841,6 +6841,71 @@ async fn func(y: *i32) void {
</p>
{#header_close#}
+ {#header_open|@call#}
+ <pre>{#syntax#}@call(options: std.builtin.CallOptions, function: var, args: var) var{#endsyntax#}</pre>
+ <p>
+ Calls a function, in the same way that invoking an expression with parentheses does:
+ </p>
+ {#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#}
+ <p>
+ {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The
+ {#syntax#}CallOptions{#endsyntax#} struct is reproduced here:
+ </p>
+ {#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#}
<pre>{#syntax#}@cDefine(comptime name: []u8, value){#endsyntax#}</pre>
<p>
@@ -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.
</p>
- {#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#}
- <pre>{#syntax#}@noInlineCall(function: var, args: ...) var{#endsyntax#}</pre>
- <p>
- This calls a function, in the same way that invoking an expression with parentheses does:
- </p>
- {#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#}
- <p>
- 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.
- </p>
- {#see_also|@inlineCall#}
- {#header_close#}
-
{#header_open|@OpaqueType#}
<pre>{#syntax#}@OpaqueType() type{#endsyntax#}</pre>
<p>
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 {
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,
};
};
src/all_types.hpp
@@ -1701,7 +1701,6 @@ enum BuiltinFnId {
BuiltinFnIdByteOffsetOf,
BuiltinFnIdBitOffsetOf,
BuiltinFnIdInlineCall,
- BuiltinFnIdNoInlineCall,
BuiltinFnIdNewStackCall,
BuiltinFnIdAsyncCall,
BuiltinFnIdTypeId,
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);
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:
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 {