allow passing by non-copying value

closes #733
This commit is contained in:
Andrew Kelley 2018-06-13 22:40:38 -04:00
parent a7d59086b4
commit 59b3dc8907
4 changed files with 32 additions and 54 deletions

View File

@ -2797,39 +2797,30 @@ fn foo() void { }
{#code_end#}
{#header_open|Pass-by-value Parameters#}
<p>
In Zig, structs, unions, and enums with payloads cannot be passed by value
to a function.
</p>
{#code_begin|test_err|not copyable; cannot pass by value#}
const Foo = struct {
x: i32,
};
fn bar(foo: Foo) void {}
test "pass aggregate type by value to function" {
bar(Foo {.x = 12,});
}
{#code_end#}
<p>
Instead, one must use <code>*const</code>. Zig allows implicitly casting something
to a const pointer to it:
In Zig, structs, unions, and enums with payloads can be passed directly to a function:
</p>
{#code_begin|test#}
const Foo = struct {
const Point = struct {
x: i32,
y: i32,
};
fn bar(foo: *const Foo) void {}
fn foo(point: Point) i32 {
return point.x + point.y;
}
test "implicitly cast to const pointer" {
bar(Foo {.x = 12,});
const assert = @import("std").debug.assert;
test "pass aggregate type by non-copy value to function" {
assert(foo(Point{ .x = 1, .y = 2 }) == 3);
}
{#code_end#}
<p>
However,
the C ABI does allow passing structs and unions by value. So functions which
use the C calling convention may pass structs and unions by value.
In this case, the value may be passed by reference, or by value, whichever way
Zig decides will be faster.
</p>
<p>
For extern functions, Zig follows the C ABI for passing structs and unions by value.
</p>
{#header_close#}
{#header_open|Function Reflection#}

View File

@ -1135,7 +1135,10 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
gen_param_info->src_index = i;
gen_param_info->gen_index = SIZE_MAX;
type_ensure_zero_bits_known(g, type_entry);
ensure_complete_type(g, type_entry);
if (type_is_invalid(type_entry))
return g->builtin_types.entry_invalid;
if (type_has_bits(type_entry)) {
TypeTableEntry *gen_type;
if (handle_is_ptr(type_entry)) {
@ -1546,12 +1549,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdPromise:
ensure_complete_type(g, type_entry);
if (calling_convention_allows_zig_types(fn_type_id.cc) && !type_is_copyable(g, type_entry)) {
add_node_error(g, param_node->data.param_decl.type,
buf_sprintf("type '%s' is not copyable; cannot pass by value", buf_ptr(&type_entry->name)));
return g->builtin_types.entry_invalid;
}
break;
}
FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];

View File

@ -119,3 +119,16 @@ test "assign inline fn to const variable" {
}
inline fn inlineFn() void {}
test "pass by non-copying value" {
assert(bar(Point{ .x = 1, .y = 2 }) == 3);
}
const Point = struct {
x: i32,
y: i32,
};
fn bar(pt: Point) i32 {
return pt.x + pt.y;
}

View File

@ -2573,15 +2573,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
break :x tc;
});
cases.add(
"pass non-copyable type by value to function",
\\const Point = struct { x: i32, y: i32, };
\\fn foo(p: Point) void { }
\\export fn entry() usize { return @sizeOf(@typeOf(foo)); }
,
".tmp_source.zig:2:11: error: type 'Point' is not copyable; cannot pass by value",
);
cases.add(
"implicit cast from array to mutable slice",
\\var global_array: [10]i32 = undefined;
@ -4066,20 +4057,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
".tmp_source.zig:3:5: note: field 'A' has type 'i32'",
);
cases.add(
"self-referencing function pointer field",
\\const S = struct {
\\ f: fn(_: S) void,
\\};
\\fn f(_: S) void {
\\}
\\export fn entry() void {
\\ var _ = S { .f = f };
\\}
,
".tmp_source.zig:4:9: error: type 'S' is not copyable; cannot pass by value",
);
cases.add(
"taking offset of void field in struct",
\\const Empty = struct {