Commit 59b3dc8907

Andrew Kelley <superjoe30@gmail.com>
2018-06-14 04:40:38
allow passing by non-copying value
closes #733
1 parent a7d5908
Changed files (4)
doc/langref.html.in
@@ -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.
+      In Zig, structs, unions, and enums with payloads can be passed directly to a function:
       </p>
-      {#code_begin|test_err|not copyable; cannot pass by value#}
-const Foo = struct {
+      {#code_begin|test#}
+const Point = struct {
     x: i32,
+    y: i32,
 };
 
-fn bar(foo: Foo) void {}
-
-test "pass aggregate type by value to function" {
-    bar(Foo {.x = 12,});
+fn foo(point: Point) i32 {
+    return point.x + point.y;
 }
-      {#code_end#}
-      <p>
-      Instead, one must use <code>*const</code>. Zig allows implicitly casting something
-      to a const pointer to it:
-      </p>
-      {#code_begin|test#}
-const Foo = struct {
-    x: i32,
-};
 
-fn bar(foo: *const Foo) void {}
+const assert = @import("std").debug.assert;
 
-test "implicitly cast to const pointer" {
-    bar(Foo {.x = 12,});
+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#}
src/analyze.cpp
@@ -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];
test/cases/fn.zig
@@ -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;
+}
test/compile_errors.zig
@@ -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 {