| author | |
| committer | |
| log | ef83358eb6702e8541816817e98c3e7279033672 |
| tree | 2d57f0633a84f188cc4bd0c9e25fc0484a39b6f5 |
| parent | 1f602fe8c5b3dea9f00f96e70dad73ebce405b49 |
| signature | Commit is signed but in an unrecognized format. |
7 files changed, 106 insertions(+), 42 deletions(-)
doc/langref.html.in+66-24| ... | ... | @@ -6841,6 +6841,71 @@ async fn func(y: *i32) void { |
| 6841 | 6841 | </p> |
| 6842 | 6842 | {#header_close#} |
| 6843 | 6843 | |
| 6844 | {#header_open|@call#} | |
| 6845 | <pre>{#syntax#}@call(options: std.builtin.CallOptions, function: var, args: var) var{#endsyntax#}</pre> | |
| 6846 | <p> | |
| 6847 | Calls a function, in the same way that invoking an expression with parentheses does: | |
| 6848 | </p> | |
| 6849 | {#code_begin|test|call#} | |
| 6850 | const assert = @import("std").debug.assert; | |
| 6851 | ||
| 6852 | test "noinline function call" { | |
| 6853 | assert(@call(.{}, add, .{3, 9}) == 12); | |
| 6854 | } | |
| 6855 | ||
| 6856 | fn add(a: i32, b: i32) i32 { | |
| 6857 | return a + b; | |
| 6858 | } | |
| 6859 | {#code_end#} | |
| 6860 | <p> | |
| 6861 | {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The | |
| 6862 | {#syntax#}CallOptions{#endsyntax#} struct is reproduced here: | |
| 6863 | </p> | |
| 6864 | {#code_begin|syntax#} | |
| 6865 | pub const CallOptions = struct { | |
| 6866 | modifier: Modifier = .auto, | |
| 6867 | stack: ?[]align(std.Target.stack_align) u8 = null, | |
| 6868 | ||
| 6869 | pub const Modifier = enum { | |
| 6870 | /// Equivalent to function call syntax. | |
| 6871 | auto, | |
| 6872 | ||
| 6873 | /// Asserts that the function call will not suspend. This allows a | |
| 6874 | /// non-async function to call an async function. | |
| 6875 | no_async, | |
| 6876 | ||
| 6877 | /// The function call will return an async function frame instead of | |
| 6878 | /// the function's result, which is expected to then be awaited. | |
| 6879 | /// This is equivalent to using the `async` keyword in front of function | |
| 6880 | /// call syntax. | |
| 6881 | async_call, | |
| 6882 | ||
| 6883 | /// Prevents tail call optimization. This guarantees that the return | |
| 6884 | /// address will point to the callsite, as opposed to the callsite's | |
| 6885 | /// callsite. If the call is otherwise required to be tail-called | |
| 6886 | /// or inlined, a compile error is emitted instead. | |
| 6887 | never_tail, | |
| 6888 | ||
| 6889 | /// Guarantees that the call will not be inlined. If the call is | |
| 6890 | /// otherwise required to be inlined, a compile error is emitted instead. | |
| 6891 | never_inline, | |
| 6892 | ||
| 6893 | /// Guarantees that the call will be generated with tail call optimization. | |
| 6894 | /// If this is not possible, a compile error is emitted instead. | |
| 6895 | always_tail, | |
| 6896 | ||
| 6897 | /// Guarantees that the call will inlined at the callsite. | |
| 6898 | /// If this is not possible, a compile error is emitted instead. | |
| 6899 | always_inline, | |
| 6900 | ||
| 6901 | /// Evaluates the call at compile-time. If the call cannot be completed at | |
| 6902 | /// compile-time, a compile error is emitted instead. | |
| 6903 | compile_time, | |
| 6904 | }; | |
| 6905 | }; | |
| 6906 | {#code_end#} | |
| 6907 | {#header_close#} | |
| 6908 | ||
| 6844 | 6909 | {#header_open|@cDefine#} |
| 6845 | 6910 | <pre>{#syntax#}@cDefine(comptime name: []u8, value){#endsyntax#}</pre> |
| 6846 | 6911 | <p> |
| ... | ... | @@ -7445,7 +7510,7 @@ fn add(a: i32, b: i32) i32 { return a + b; } |
| 7445 | 7510 | Unlike a normal function call, however, {#syntax#}@inlineCall{#endsyntax#} guarantees that the call |
| 7446 | 7511 | will be inlined. If the call cannot be inlined, a compile error is emitted. |
| 7447 | 7512 | </p> |
| 7448 | {#see_also|@noInlineCall#} | |
| 7513 | {#see_also|@call#} | |
| 7449 | 7514 | {#header_close#} |
| 7450 | 7515 | |
| 7451 | 7516 | {#header_open|@intCast#} |
| ... | ... | @@ -7647,29 +7712,6 @@ fn targetFunction(x: i32) usize { |
| 7647 | 7712 | {#code_end#} |
| 7648 | 7713 | {#header_close#} |
| 7649 | 7714 | |
| 7650 | {#header_open|@noInlineCall#} | |
| 7651 | <pre>{#syntax#}@noInlineCall(function: var, args: ...) var{#endsyntax#}</pre> | |
| 7652 | <p> | |
| 7653 | This calls a function, in the same way that invoking an expression with parentheses does: | |
| 7654 | </p> | |
| 7655 | {#code_begin|test#} | |
| 7656 | const assert = @import("std").debug.assert; | |
| 7657 | ||
| 7658 | test "noinline function call" { | |
| 7659 | assert(@noInlineCall(add, 3, 9) == 12); | |
| 7660 | } | |
| 7661 | ||
| 7662 | fn add(a: i32, b: i32) i32 { | |
| 7663 | return a + b; | |
| 7664 | } | |
| 7665 | {#code_end#} | |
| 7666 | <p> | |
| 7667 | Unlike a normal function call, however, {#syntax#}@noInlineCall{#endsyntax#} guarantees that the call | |
| 7668 | will not be inlined. If the call must be inlined, a compile error is emitted. | |
| 7669 | </p> | |
| 7670 | {#see_also|@inlineCall#} | |
| 7671 | {#header_close#} | |
| 7672 | ||
| 7673 | 7715 | {#header_open|@OpaqueType#} |
| 7674 | 7716 | <pre>{#syntax#}@OpaqueType() type{#endsyntax#}</pre> |
| 7675 | 7717 | <p> |
lib/std/builtin.zig+26| ... | ... | @@ -379,13 +379,39 @@ pub const CallOptions = struct { |
| 379 | 379 | stack: ?[]align(std.Target.stack_align) u8 = null, |
| 380 | 380 | |
| 381 | 381 | pub const Modifier = enum { |
| 382 | /// Equivalent to function call syntax. | |
| 382 | 383 | auto, |
| 384 | ||
| 385 | /// Asserts that the function call will not suspend. This allows a | |
| 386 | /// non-async function to call an async function. | |
| 383 | 387 | no_async, |
| 388 | ||
| 389 | /// The function call will return an async function frame instead of | |
| 390 | /// the function's result, which is expected to then be awaited. | |
| 391 | /// This is equivalent to using the `async` keyword in front of function | |
| 392 | /// call syntax. | |
| 384 | 393 | async_call, |
| 394 | ||
| 395 | /// Prevents tail call optimization. This guarantees that the return | |
| 396 | /// address will point to the callsite, as opposed to the callsite's | |
| 397 | /// callsite. If the call is otherwise required to be tail-called | |
| 398 | /// or inlined, a compile error is emitted instead. | |
| 385 | 399 | never_tail, |
| 400 | ||
| 401 | /// Guarantees that the call will not be inlined. If the call is | |
| 402 | /// otherwise required to be inlined, a compile error is emitted instead. | |
| 386 | 403 | never_inline, |
| 404 | ||
| 405 | /// Guarantees that the call will be generated with tail call optimization. | |
| 406 | /// If this is not possible, a compile error is emitted instead. | |
| 387 | 407 | always_tail, |
| 408 | ||
| 409 | /// Guarantees that the call will inlined at the callsite. | |
| 410 | /// If this is not possible, a compile error is emitted instead. | |
| 388 | 411 | always_inline, |
| 412 | ||
| 413 | /// Evaluates the call at compile-time. If the call cannot be completed at | |
| 414 | /// compile-time, a compile error is emitted instead. | |
| 389 | 415 | compile_time, |
| 390 | 416 | }; |
| 391 | 417 | }; |
lib/std/special/start.zig+1-1| ... | ... | @@ -125,7 +125,7 @@ nakedcc fn _start() noreturn { |
| 125 | 125 | } |
| 126 | 126 | // If LLVM inlines stack variables into _start, they will overwrite |
| 127 | 127 | // the command line argument data. |
| 128 | @noInlineCall(posixCallMainAndExit); | |
| 128 | @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{}); | |
| 129 | 129 | } |
| 130 | 130 | |
| 131 | 131 | stdcallcc fn WinMainCRTStartup() noreturn { |
src/all_types.hpp-1| ... | ... | @@ -1701,7 +1701,6 @@ enum BuiltinFnId { |
| 1701 | 1701 | BuiltinFnIdByteOffsetOf, |
| 1702 | 1702 | BuiltinFnIdBitOffsetOf, |
| 1703 | 1703 | BuiltinFnIdInlineCall, |
| 1704 | BuiltinFnIdNoInlineCall, | |
| 1705 | 1704 | BuiltinFnIdNewStackCall, |
| 1706 | 1705 | BuiltinFnIdAsyncCall, |
| 1707 | 1706 | BuiltinFnIdTypeId, |
src/codegen.cpp-1| ... | ... | @@ -8133,7 +8133,6 @@ static void define_builtin_fns(CodeGen *g) { |
| 8133 | 8133 | create_builtin_fn(g, BuiltinFnIdRound, "round", 2); |
| 8134 | 8134 | create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4); |
| 8135 | 8135 | create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); |
| 8136 | create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); | |
| 8137 | 8136 | create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX); |
| 8138 | 8137 | create_builtin_fn(g, BuiltinFnIdAsyncCall, "asyncCall", SIZE_MAX); |
| 8139 | 8138 | create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1); |
src/ir.cpp+1-4| ... | ... | @@ -6014,7 +6014,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 6014 | 6014 | return ir_lval_wrap(irb, scope, offset_of, lval, result_loc); |
| 6015 | 6015 | } |
| 6016 | 6016 | case BuiltinFnIdInlineCall: |
| 6017 | case BuiltinFnIdNoInlineCall: | |
| 6018 | 6017 | { |
| 6019 | 6018 | if (node->data.fn_call_expr.params.length == 0) { |
| 6020 | 6019 | 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 |
| 6035 | 6034 | if (args[i] == irb->codegen->invalid_instruction) |
| 6036 | 6035 | return args[i]; |
| 6037 | 6036 | } |
| 6038 | CallModifier modifier = (builtin_fn->id == BuiltinFnIdInlineCall) ? | |
| 6039 | CallModifierAlwaysInline : CallModifierNeverInline; | |
| 6040 | 6037 | |
| 6041 | 6038 | IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, |
| 6042 | nullptr, modifier, false, nullptr, result_loc); | |
| 6039 | nullptr, CallModifierAlwaysInline, false, nullptr, result_loc); | |
| 6043 | 6040 | return ir_lval_wrap(irb, scope, call, lval, result_loc); |
| 6044 | 6041 | } |
| 6045 | 6042 | case BuiltinFnIdNewStackCall: |
test/compile_errors.zig+12-11| ... | ... | @@ -13,11 +13,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 13 | 13 | \\export fn entry3() void { |
| 14 | 14 | \\ comptime @call(.{ .modifier = .never_tail }, foo, .{}); |
| 15 | 15 | \\} |
| 16 | \\export fn entry4() void { | |
| 17 | \\ @call(.{ .modifier = .never_inline }, bar, .{}); | |
| 18 | \\} | |
| 19 | \\export fn entry5(c: bool) void { | |
| 20 | \\ var baz = if (c) baz1 else baz2; | |
| 21 | \\ @call(.{ .modifier = .compile_time }, baz, .{}); | |
| 22 | \\} | |
| 16 | 23 | \\fn foo() void {} |
| 24 | \\inline fn bar() void {} | |
| 25 | \\fn baz1() void {} | |
| 26 | \\fn baz2() void {} | |
| 17 | 27 | , |
| 18 | 28 | "tmp.zig:2:21: error: expected tuple or struct, found 'void'", |
| 19 | 29 | "tmp.zig:5:58: error: unable to perform 'never_inline' call at compile-time", |
| 20 | 30 | "tmp.zig:8:56: error: unable to perform 'never_tail' call at compile-time", |
| 31 | "tmp.zig:11:5: error: no-inline call of inline function", | |
| 32 | "tmp.zig:15:43: error: unable to evaluate constant expression", | |
| 21 | 33 | ); |
| 22 | 34 | |
| 23 | 35 | cases.add( |
| ... | ... | @@ -1945,17 +1957,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1945 | 1957 | "tmp.zig:2:12: error: use of undeclared identifier 'SomeNonexistentType'", |
| 1946 | 1958 | ); |
| 1947 | 1959 | |
| 1948 | cases.add( | |
| 1949 | "@noInlineCall on an inline function", | |
| 1950 | \\inline fn foo() void {} | |
| 1951 | \\ | |
| 1952 | \\export fn entry() void { | |
| 1953 | \\ @noInlineCall(foo); | |
| 1954 | \\} | |
| 1955 | , | |
| 1956 | "tmp.zig:4:5: error: no-inline call of inline function", | |
| 1957 | ); | |
| 1958 | ||
| 1959 | 1960 | cases.add( |
| 1960 | 1961 | "comptime continue inside runtime catch", |
| 1961 | 1962 | \\export fn entry(c: bool) void { |