Skip to content

Commit 66f5f77

Browse files
committed
allow passing by non-copying value
closes #733
1 parent fc87f6e commit 66f5f77

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
@@ -2795,39 +2795,30 @@ fn foo() void { }
27952795
{#code_end#}
27962796
{#header_open|Pass-by-value Parameters#}
27972797
<p>
2798-
In Zig, structs, unions, and enums with payloads cannot be passed by value
2799-
to a function.
2798+
In Zig, structs, unions, and enums with payloads can be passed directly to a function:
28002799
</p>
2801-
{#code_begin|test_err|not copyable; cannot pass by value#}
2802-
const Foo = struct {
2800+
{#code_begin|test#}
2801+
const Point = struct {
28032802
x: i32,
2803+
y: i32,
28042804
};
28052805

2806-
fn bar(foo: Foo) void {}
2807-
2808-
test "pass aggregate type by value to function" {
2809-
bar(Foo {.x = 12,});
2806+
fn foo(point: Point) i32 {
2807+
return point.x + point.y;
28102808
}
2811-
{#code_end#}
2812-
<p>
2813-
Instead, one must use <code>*const</code>. Zig allows implicitly casting something
2814-
to a const pointer to it:
2815-
</p>
2816-
{#code_begin|test#}
2817-
const Foo = struct {
2818-
x: i32,
2819-
};
28202809

2821-
fn bar(foo: *const Foo) void {}
2810+
const assert = @import("std").debug.assert;
28222811

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

src/analyze.cpp

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

1137-
type_ensure_zero_bits_known(g, type_entry);
1137+
ensure_complete_type(g, type_entry);
1138+
if (type_is_invalid(type_entry))
1139+
return g->builtin_types.entry_invalid;
1140+
11381141
if (type_has_bits(type_entry)) {
11391142
TypeTableEntry *gen_type;
11401143
if (handle_is_ptr(type_entry)) {
@@ -1545,12 +1548,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
15451548
case TypeTableEntryIdUnion:
15461549
case TypeTableEntryIdFn:
15471550
case TypeTableEntryIdPromise:
1548-
ensure_complete_type(g, type_entry);
1549-
if (calling_convention_allows_zig_types(fn_type_id.cc) && !type_is_copyable(g, type_entry)) {
1550-
add_node_error(g, param_node->data.param_decl.type,
1551-
buf_sprintf("type '%s' is not copyable; cannot pass by value", buf_ptr(&type_entry->name)));
1552-
return g->builtin_types.entry_invalid;
1553-
}
15541551
break;
15551552
}
15561553
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)