diff --git a/doc/langref.html.in b/doc/langref.html.in index 814de721a..b32c8165e 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2797,39 +2797,30 @@ fn foo() void { } {#code_end#} {#header_open|Pass-by-value Parameters#}

- In Zig, structs, unions, and enums with payloads cannot be passed by value - to a function. -

- {#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#} -

- Instead, one must use *const. 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:

{#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#}

- 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. +

+

+ For extern functions, Zig follows the C ABI for passing structs and unions by value.

{#header_close#} {#header_open|Function Reflection#} diff --git a/src/analyze.cpp b/src/analyze.cpp index cbeac7bc2..758bc1a04 100644 --- a/src/analyze.cpp +++ b/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]; diff --git a/test/cases/fn.zig b/test/cases/fn.zig index dfb254c6a..2426a411d 100644 --- a/test/cases/fn.zig +++ b/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; +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 06f17a37e..60ba25517 100644 --- a/test/compile_errors.zig +++ b/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 {