Skip to content

Commit 59b3dc8

Browse files
committed
allow passing by non-copying value
closes #733
1 parent a7d5908 commit 59b3dc8

File tree

4 files changed

+31
-53
lines changed

4 files changed

+31
-53
lines changed

doc/langref.html.in

+14-23
Original file line numberDiff line numberDiff line change
@@ -2797,39 +2797,30 @@ fn foo() void { }
27972797
{#code_end#}
27982798
{#header_open|Pass-by-value Parameters#}
27992799
<p>
2800-
In Zig, structs, unions, and enums with payloads cannot be passed by value
2801-
to a function.
2800+
In Zig, structs, unions, and enums with payloads can be passed directly to a function:
28022801
</p>
2803-
{#code_begin|test_err|not copyable; cannot pass by value#}
2804-
const Foo = struct {
2802+
{#code_begin|test#}
2803+
const Point = struct {
28052804
x: i32,
2805+
y: i32,
28062806
};
28072807

2808-
fn bar(foo: Foo) void {}
2809-
2810-
test "pass aggregate type by value to function" {
2811-
bar(Foo {.x = 12,});
2808+
fn foo(point: Point) i32 {
2809+
return point.x + point.y;
28122810
}
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-
};
28222811

2823-
fn bar(foo: *const Foo) void {}
2812+
const assert = @import("std").debug.assert;
28242813

2825-
test "implicitly cast to const pointer" {
2826-
bar(Foo {.x = 12,});
2814+
test "pass aggregate type by non-copy value to function" {
2815+
assert(foo(Point{ .x = 1, .y = 2 }) == 3);
28272816
}
28282817
{#code_end#}
28292818
<p>
2830-
However,
2831-
the C ABI does allow passing structs and unions by value. So functions which
2832-
use the C calling convention may pass structs and unions by value.
2819+
In this case, the value may be passed by reference, or by value, whichever way
2820+
Zig decides will be faster.
2821+
</p>
2822+
<p>
2823+
For extern functions, Zig follows the C ABI for passing structs and unions by value.
28332824
</p>
28342825
{#header_close#}
28352826
{#header_open|Function Reflection#}

src/analyze.cpp

+4-7
Original file line numberDiff line numberDiff line change
@@ -1135,7 +1135,10 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
11351135
gen_param_info->src_index = i;
11361136
gen_param_info->gen_index = SIZE_MAX;
11371137

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+
11391142
if (type_has_bits(type_entry)) {
11401143
TypeTableEntry *gen_type;
11411144
if (handle_is_ptr(type_entry)) {
@@ -1546,12 +1549,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
15461549
case TypeTableEntryIdUnion:
15471550
case TypeTableEntryIdFn:
15481551
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-
}
15551552
break;
15561553
}
15571554
FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];

test/cases/fn.zig

+13
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,16 @@ test "assign inline fn to const variable" {
119119
}
120120

121121
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
Original file line numberDiff line numberDiff line change
@@ -2573,15 +2573,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
25732573
break :x tc;
25742574
});
25752575

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-
25852576
cases.add(
25862577
"implicit cast from array to mutable slice",
25872578
\\var global_array: [10]i32 = undefined;
@@ -4066,20 +4057,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
40664057
".tmp_source.zig:3:5: note: field 'A' has type 'i32'",
40674058
);
40684059

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-
40834060
cases.add(
40844061
"taking offset of void field in struct",
40854062
\\const Empty = struct {

0 commit comments

Comments
 (0)