| author | |
| committer | |
| log | 59b3dc8907f76b93caa689732e878a5bfa2f65c2 |
| tree | ced7464c4d054ebeaf413e46454b8dc820b1249e |
| parent | a7d59086b49b0ae11a4830d2ea72b63be05fab94 |
closes #7334 files changed, 31 insertions(+), 53 deletions(-)
doc/langref.html.in+14-23| ... | @@ -2797,39 +2797,30 @@ fn foo() void { } | ... | @@ -2797,39 +2797,30 @@ fn foo() void { } |
| 2797 | {#code_end#} | 2797 | {#code_end#} |
| 2798 | {#header_open|Pass-by-value Parameters#} | 2798 | {#header_open|Pass-by-value Parameters#} |
| 2799 | <p> | 2799 | <p> |
| 2800 | In Zig, structs, unions, and enums with payloads cannot be passed by value | 2800 | In Zig, structs, unions, and enums with payloads can be passed directly to a function: |
| 2801 | to a function. | ||
| 2802 | </p> | 2801 | </p> |
| 2803 | {#code_begin|test_err|not copyable; cannot pass by value#} | 2802 | {#code_begin|test#} |
| 2804 | const Foo = struct { | 2803 | const Point = struct { |
| 2805 | x: i32, | 2804 | x: i32, |
| 2805 | y: i32, | ||
| 2806 | }; | 2806 | }; |
| 2807 | 2807 | ||
| 2808 | fn bar(foo: Foo) void {} | 2808 | fn foo(point: Point) i32 { |
| 2809 | 2809 | return point.x + point.y; | |
| 2810 | test "pass aggregate type by value to function" { | ||
| 2811 | bar(Foo {.x = 12,}); | ||
| 2812 | } | 2810 | } |
| 2813 | {#code_end#} | ||
| 2814 | <p> | ||
| 2815 | Instead, one must use <code>*const</code>. Zig allows implicitly casting something | ||
| 2816 | to a const pointer to it: | ||
| 2817 | </p> | ||
| 2818 | {#code_begin|test#} | ||
| 2819 | const Foo = struct { | ||
| 2820 | x: i32, | ||
| 2821 | }; | ||
| 2822 | 2811 | ||
| 2823 | fn bar(foo: *const Foo) void {} | 2812 | const assert = @import("std").debug.assert; |
| 2824 | 2813 | ||
| 2825 | test "implicitly cast to const pointer" { | 2814 | test "pass aggregate type by non-copy value to function" { |
| 2826 | bar(Foo {.x = 12,}); | 2815 | assert(foo(Point{ .x = 1, .y = 2 }) == 3); |
| 2827 | } | 2816 | } |
| 2828 | {#code_end#} | 2817 | {#code_end#} |
| 2829 | <p> | 2818 | <p> |
| 2830 | However, | 2819 | In this case, the value may be passed by reference, or by value, whichever way |
| 2831 | the C ABI does allow passing structs and unions by value. So functions which | 2820 | Zig decides will be faster. |
| 2832 | use the C calling convention may pass structs and unions by value. | 2821 | </p> |
| 2822 | <p> | ||
| 2823 | For extern functions, Zig follows the C ABI for passing structs and unions by value. | ||
| 2833 | </p> | 2824 | </p> |
| 2834 | {#header_close#} | 2825 | {#header_close#} |
| 2835 | {#header_open|Function Reflection#} | 2826 | {#header_open|Function Reflection#} |
src/analyze.cpp+4-7| ... | @@ -1135,7 +1135,10 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { | ... | @@ -1135,7 +1135,10 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 1135 | gen_param_info->src_index = i; | 1135 | gen_param_info->src_index = i; |
| 1136 | gen_param_info->gen_index = SIZE_MAX; | 1136 | gen_param_info->gen_index = SIZE_MAX; |
| 1137 | 1137 | ||
| 1138 | type_ensure_zero_bits_known(g, type_entry); | 1138 | ensure_complete_type(g, type_entry); |
| 1139 | if (type_is_invalid(type_entry)) | ||
| 1140 | return g->builtin_types.entry_invalid; | ||
| 1141 | |||
| 1139 | if (type_has_bits(type_entry)) { | 1142 | if (type_has_bits(type_entry)) { |
| 1140 | TypeTableEntry *gen_type; | 1143 | TypeTableEntry *gen_type; |
| 1141 | if (handle_is_ptr(type_entry)) { | 1144 | if (handle_is_ptr(type_entry)) { |
| ... | @@ -1546,12 +1549,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c | ... | @@ -1546,12 +1549,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c |
| 1546 | case TypeTableEntryIdUnion: | 1549 | case TypeTableEntryIdUnion: |
| 1547 | case TypeTableEntryIdFn: | 1550 | case TypeTableEntryIdFn: |
| 1548 | case TypeTableEntryIdPromise: | 1551 | case TypeTableEntryIdPromise: |
| 1549 | ensure_complete_type(g, type_entry); | ||
| 1550 | if (calling_convention_allows_zig_types(fn_type_id.cc) && !type_is_copyable(g, type_entry)) { | ||
| 1551 | add_node_error(g, param_node->data.param_decl.type, | ||
| 1552 | buf_sprintf("type '%s' is not copyable; cannot pass by value", buf_ptr(&type_entry->name))); | ||
| 1553 | return g->builtin_types.entry_invalid; | ||
| 1554 | } | ||
| 1555 | break; | 1552 | break; |
| 1556 | } | 1553 | } |
| 1557 | FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; | 1554 | FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; |
test/cases/fn.zig+13| ... | @@ -119,3 +119,16 @@ test "assign inline fn to const variable" { | ... | @@ -119,3 +119,16 @@ test "assign inline fn to const variable" { |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | inline fn inlineFn() void {} | 121 | inline fn inlineFn() void {} |
| 122 | |||
| 123 | test "pass by non-copying value" { | ||
| 124 | assert(bar(Point{ .x = 1, .y = 2 }) == 3); | ||
| 125 | } | ||
| 126 | |||
| 127 | const Point = struct { | ||
| 128 | x: i32, | ||
| 129 | y: i32, | ||
| 130 | }; | ||
| 131 | |||
| 132 | fn bar(pt: Point) i32 { | ||
| 133 | return pt.x + pt.y; | ||
| 134 | } |
test/compile_errors.zig-23| ... | @@ -2573,15 +2573,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -2573,15 +2573,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2573 | break :x tc; | 2573 | break :x tc; |
| 2574 | }); | 2574 | }); |
| 2575 | 2575 | ||
| 2576 | cases.add( | ||
| 2577 | "pass non-copyable type by value to function", | ||
| 2578 | \\const Point = struct { x: i32, y: i32, }; | ||
| 2579 | \\fn foo(p: Point) void { } | ||
| 2580 | \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } | ||
| 2581 | , | ||
| 2582 | ".tmp_source.zig:2:11: error: type 'Point' is not copyable; cannot pass by value", | ||
| 2583 | ); | ||
| 2584 | |||
| 2585 | cases.add( | 2576 | cases.add( |
| 2586 | "implicit cast from array to mutable slice", | 2577 | "implicit cast from array to mutable slice", |
| 2587 | \\var global_array: [10]i32 = undefined; | 2578 | \\var global_array: [10]i32 = undefined; |
| ... | @@ -4066,20 +4057,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -4066,20 +4057,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4066 | ".tmp_source.zig:3:5: note: field 'A' has type 'i32'", | 4057 | ".tmp_source.zig:3:5: note: field 'A' has type 'i32'", |
| 4067 | ); | 4058 | ); |
| 4068 | 4059 | ||
| 4069 | cases.add( | ||
| 4070 | "self-referencing function pointer field", | ||
| 4071 | \\const S = struct { | ||
| 4072 | \\ f: fn(_: S) void, | ||
| 4073 | \\}; | ||
| 4074 | \\fn f(_: S) void { | ||
| 4075 | \\} | ||
| 4076 | \\export fn entry() void { | ||
| 4077 | \\ var _ = S { .f = f }; | ||
| 4078 | \\} | ||
| 4079 | , | ||
| 4080 | ".tmp_source.zig:4:9: error: type 'S' is not copyable; cannot pass by value", | ||
| 4081 | ); | ||
| 4082 | |||
| 4083 | cases.add( | 4060 | cases.add( |
| 4084 | "taking offset of void field in struct", | 4061 | "taking offset of void field in struct", |
| 4085 | \\const Empty = struct { | 4062 | \\const Empty = struct { |