authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-13 22:40:38-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-16 19:36:33-04:00
log59b3dc8907f76b93caa689732e878a5bfa2f65c2
treeced7464c4d054ebeaf413e46454b8dc820b1249e
parenta7d59086b49b0ae11a4830d2ea72b63be05fab94

allow passing by non-copying value

closes #733

4 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 value2800 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#}
2804const Foo = struct {2803const Point = struct {
2805 x: i32,2804 x: i32,
2805 y: i32,
2806};2806};
28072807
2808fn bar(foo: Foo) void {}2808fn foo(point: Point) i32 {
28092809 return point.x + point.y;
2810test "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#}
2819const Foo = struct {
2820 x: i32,
2821};
28222811
2823fn bar(foo: *const Foo) void {}2812const assert = @import("std").debug.assert;
28242813
2825test "implicitly cast to const pointer" {2814test "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 which2820 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;
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
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}
120120
121inline fn inlineFn() void {}121inline fn inlineFn() void {}
122
123test "pass by non-copying value" {
124 assert(bar(Point{ .x = 1, .y = 2 }) == 3);
125}
126
127const Point = struct {
128 x: i32,
129 y: i32,
130};
131
132fn 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 });
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
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 );
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
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 {