Skip to content

Commit 7515187

Browse files
authored
Merge pull request #1109 from ziglang/pass-by-non-copying-value
allow passing by non-copying value
2 parents 3ee4d23 + 472b7ef commit 7515187

16 files changed

+306
-344
lines changed

doc/langref.html.in

+14-23
Original file line numberDiff line numberDiff line change
@@ -2818,39 +2818,30 @@ fn foo() void { }
28182818
{#code_end#}
28192819
{#header_open|Pass-by-value Parameters#}
28202820
<p>
2821-
In Zig, structs, unions, and enums with payloads cannot be passed by value
2822-
to a function.
2821+
In Zig, structs, unions, and enums with payloads can be passed directly to a function:
28232822
</p>
2824-
{#code_begin|test_err|not copyable; cannot pass by value#}
2825-
const Foo = struct {
2823+
{#code_begin|test#}
2824+
const Point = struct {
28262825
x: i32,
2826+
y: i32,
28272827
};
28282828

2829-
fn bar(foo: Foo) void {}
2830-
2831-
test "pass aggregate type by value to function" {
2832-
bar(Foo {.x = 12,});
2829+
fn foo(point: Point) i32 {
2830+
return point.x + point.y;
28332831
}
2834-
{#code_end#}
2835-
<p>
2836-
Instead, one must use <code>*const</code>. Zig allows implicitly casting something
2837-
to a const pointer to it:
2838-
</p>
2839-
{#code_begin|test#}
2840-
const Foo = struct {
2841-
x: i32,
2842-
};
28432832

2844-
fn bar(foo: *const Foo) void {}
2833+
const assert = @import("std").debug.assert;
28452834

2846-
test "implicitly cast to const pointer" {
2847-
bar(Foo {.x = 12,});
2835+
test "pass aggregate type by non-copy value to function" {
2836+
assert(foo(Point{ .x = 1, .y = 2 }) == 3);
28482837
}
28492838
{#code_end#}
28502839
<p>
2851-
However,
2852-
the C ABI does allow passing structs and unions by value. So functions which
2853-
use the C calling convention may pass structs and unions by value.
2840+
In this case, the value may be passed by reference, or by value, whichever way
2841+
Zig decides will be faster.
2842+
</p>
2843+
<p>
2844+
For extern functions, Zig follows the C ABI for passing structs and unions by value.
28542845
</p>
28552846
{#header_close#}
28562847
{#header_open|Function Reflection#}

src/analyze.cpp

+4-7
Original file line numberDiff line numberDiff line change
@@ -1135,7 +1135,10 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
11351135
gen_param_info->src_index = i;
11361136
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+
11391142
if (type_has_bits(type_entry)) {
11401143
TypeTableEntry *gen_type;
11411144
if (handle_is_ptr(type_entry)) {
@@ -1546,12 +1549,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
15461549
case TypeTableEntryIdUnion:
15471550
case TypeTableEntryIdFn:
15481551
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-
}
15551552
break;
15561553
}
15571554
FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];

src/codegen.cpp

-21
Original file line numberDiff line numberDiff line change
@@ -326,13 +326,6 @@ static void addLLVMArgAttr(LLVMValueRef arg_val, unsigned param_index, const cha
326326
return addLLVMAttr(arg_val, param_index + 1, attr_name);
327327
}
328328

329-
static void addLLVMCallsiteAttr(LLVMValueRef call_instr, unsigned param_index, const char *attr_name) {
330-
unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name));
331-
assert(kind_id != 0);
332-
LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), kind_id, 0);
333-
LLVMAddCallSiteAttribute(call_instr, param_index + 1, llvm_attr);
334-
}
335-
336329
static bool is_symbol_available(CodeGen *g, Buf *name) {
337330
return g->exported_symbol_names.maybe_get(name) == nullptr && g->external_prototypes.maybe_get(name) == nullptr;
338331
}
@@ -581,11 +574,6 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
581574
if (param_type->id == TypeTableEntryIdPointer) {
582575
addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "nonnull");
583576
}
584-
// Note: byval is disabled on windows due to an LLVM bug:
585-
// https://github.com/ziglang/zig/issues/536
586-
if (is_byval && g->zig_target.os != OsWindows) {
587-
addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "byval");
588-
}
589577
}
590578

591579
uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
@@ -3114,15 +3102,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
31143102
}
31153103

31163104

3117-
for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
3118-
FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i];
3119-
// Note: byval is disabled on windows due to an LLVM bug:
3120-
// https://github.com/ziglang/zig/issues/536
3121-
if (gen_info->is_byval && g->zig_target.os != OsWindows) {
3122-
addLLVMCallsiteAttr(result, (unsigned)gen_info->gen_index, "byval");
3123-
}
3124-
}
3125-
31263105
if (instruction->is_async) {
31273106
LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, "");
31283107
LLVMBuildStore(g->builder, result, payload_ptr);

src/ir.cpp

+34-23
Original file line numberDiff line numberDiff line change
@@ -10463,13 +10463,6 @@ static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, Typ
1046310463
zig_unreachable();
1046410464
}
1046510465

10466-
static IrInstruction *ir_implicit_byval_const_ref_cast(IrAnalyze *ira, IrInstruction *inst) {
10467-
if (type_is_copyable(ira->codegen, inst->value.type))
10468-
return inst;
10469-
TypeTableEntry *const_ref_type = get_pointer_to_type(ira->codegen, inst->value.type, true);
10470-
return ir_implicit_cast(ira, inst, const_ref_type);
10471-
}
10472-
1047310466
static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) {
1047410467
TypeTableEntry *type_entry = ptr->value.type;
1047510468
if (type_is_invalid(type_entry)) {
@@ -12283,7 +12276,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
1228312276
IrInstruction *casted_arg;
1228412277
if (is_var_args) {
1228512278
arg_part_of_generic_id = true;
12286-
casted_arg = ir_implicit_byval_const_ref_cast(ira, arg);
12279+
casted_arg = arg;
1228712280
} else {
1228812281
if (param_decl_node->data.param_decl.var_token == nullptr) {
1228912282
AstNode *param_type_node = param_decl_node->data.param_decl.type;
@@ -12296,7 +12289,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
1229612289
return false;
1229712290
} else {
1229812291
arg_part_of_generic_id = true;
12299-
casted_arg = ir_implicit_byval_const_ref_cast(ira, arg);
12292+
casted_arg = arg;
1230012293
}
1230112294
}
1230212295

@@ -12515,9 +12508,18 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1251512508

1251612509
size_t next_proto_i = 0;
1251712510
if (first_arg_ptr) {
12518-
IrInstruction *first_arg;
1251912511
assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer);
12520-
if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) {
12512+
12513+
bool first_arg_known_bare = false;
12514+
if (fn_type_id->next_param_index >= 1) {
12515+
TypeTableEntry *param_type = fn_type_id->param_info[next_proto_i].type;
12516+
if (type_is_invalid(param_type))
12517+
return ira->codegen->builtin_types.entry_invalid;
12518+
first_arg_known_bare = param_type->id != TypeTableEntryIdPointer;
12519+
}
12520+
12521+
IrInstruction *first_arg;
12522+
if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) {
1252112523
first_arg = first_arg_ptr;
1252212524
} else {
1252312525
first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr);
@@ -12667,9 +12669,18 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1266712669
size_t next_proto_i = 0;
1266812670

1266912671
if (first_arg_ptr) {
12670-
IrInstruction *first_arg;
1267112672
assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer);
12672-
if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) {
12673+
12674+
bool first_arg_known_bare = false;
12675+
if (fn_type_id->next_param_index >= 1) {
12676+
TypeTableEntry *param_type = fn_type_id->param_info[next_proto_i].type;
12677+
if (type_is_invalid(param_type))
12678+
return ira->codegen->builtin_types.entry_invalid;
12679+
first_arg_known_bare = param_type->id != TypeTableEntryIdPointer;
12680+
}
12681+
12682+
IrInstruction *first_arg;
12683+
if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) {
1267312684
first_arg = first_arg_ptr;
1267412685
} else {
1267512686
first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr);
@@ -12802,10 +12813,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1280212813
return ira->codegen->builtin_types.entry_invalid;
1280312814
}
1280412815
if (inst_fn_type_id.async_allocator_type == nullptr) {
12805-
IrInstruction *casted_inst = ir_implicit_byval_const_ref_cast(ira, uncasted_async_allocator_inst);
12806-
if (type_is_invalid(casted_inst->value.type))
12807-
return ira->codegen->builtin_types.entry_invalid;
12808-
inst_fn_type_id.async_allocator_type = casted_inst->value.type;
12816+
inst_fn_type_id.async_allocator_type = uncasted_async_allocator_inst->value.type;
1280912817
}
1281012818
async_allocator_inst = ir_implicit_cast(ira, uncasted_async_allocator_inst, inst_fn_type_id.async_allocator_type);
1281112819
if (type_is_invalid(async_allocator_inst->value.type))
@@ -12866,20 +12874,23 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1286612874
IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count);
1286712875
size_t next_arg_index = 0;
1286812876
if (first_arg_ptr) {
12869-
IrInstruction *first_arg;
1287012877
assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer);
12871-
if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) {
12878+
12879+
TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type;
12880+
if (type_is_invalid(param_type))
12881+
return ira->codegen->builtin_types.entry_invalid;
12882+
12883+
IrInstruction *first_arg;
12884+
if (param_type->id == TypeTableEntryIdPointer &&
12885+
handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type))
12886+
{
1287212887
first_arg = first_arg_ptr;
1287312888
} else {
1287412889
first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr);
1287512890
if (type_is_invalid(first_arg->value.type))
1287612891
return ira->codegen->builtin_types.entry_invalid;
1287712892
}
1287812893

12879-
TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type;
12880-
if (type_is_invalid(param_type))
12881-
return ira->codegen->builtin_types.entry_invalid;
12882-
1288312894
IrInstruction *casted_arg = ir_implicit_cast(ira, first_arg, param_type);
1288412895
if (type_is_invalid(casted_arg->value.type))
1288512896
return ira->codegen->builtin_types.entry_invalid;

std/array_list.zig

+13-13
Original file line numberDiff line numberDiff line change
@@ -29,36 +29,36 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
2929
};
3030
}
3131

32-
pub fn deinit(self: *const Self) void {
32+
pub fn deinit(self: Self) void {
3333
self.allocator.free(self.items);
3434
}
3535

36-
pub fn toSlice(self: *const Self) []align(A) T {
36+
pub fn toSlice(self: Self) []align(A) T {
3737
return self.items[0..self.len];
3838
}
3939

40-
pub fn toSliceConst(self: *const Self) []align(A) const T {
40+
pub fn toSliceConst(self: Self) []align(A) const T {
4141
return self.items[0..self.len];
4242
}
4343

44-
pub fn at(self: *const Self, n: usize) T {
44+
pub fn at(self: Self, n: usize) T {
4545
return self.toSliceConst()[n];
4646
}
4747

4848
/// Sets the value at index `i`, or returns `error.OutOfBounds` if
4949
/// the index is not in range.
50-
pub fn setOrError(self: *const Self, i: usize, item: *const T) !void {
50+
pub fn setOrError(self: Self, i: usize, item: T) !void {
5151
if (i >= self.len) return error.OutOfBounds;
52-
self.items[i] = item.*;
52+
self.items[i] = item;
5353
}
5454

5555
/// Sets the value at index `i`, asserting that the value is in range.
56-
pub fn set(self: *const Self, i: usize, item: *const T) void {
56+
pub fn set(self: *Self, i: usize, item: T) void {
5757
assert(i < self.len);
58-
self.items[i] = item.*;
58+
self.items[i] = item;
5959
}
6060

61-
pub fn count(self: *const Self) usize {
61+
pub fn count(self: Self) usize {
6262
return self.len;
6363
}
6464

@@ -81,12 +81,12 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
8181
return result;
8282
}
8383

84-
pub fn insert(self: *Self, n: usize, item: *const T) !void {
84+
pub fn insert(self: *Self, n: usize, item: T) !void {
8585
try self.ensureCapacity(self.len + 1);
8686
self.len += 1;
8787

8888
mem.copy(T, self.items[n + 1 .. self.len], self.items[n .. self.len - 1]);
89-
self.items[n] = item.*;
89+
self.items[n] = item;
9090
}
9191

9292
pub fn insertSlice(self: *Self, n: usize, items: []align(A) const T) !void {
@@ -97,9 +97,9 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
9797
mem.copy(T, self.items[n .. n + items.len], items);
9898
}
9999

100-
pub fn append(self: *Self, item: *const T) !void {
100+
pub fn append(self: *Self, item: T) !void {
101101
const new_item_ptr = try self.addOne();
102-
new_item_ptr.* = item.*;
102+
new_item_ptr.* = item;
103103
}
104104

105105
pub fn appendSlice(self: *Self, items: []align(A) const T) !void {

std/build.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ pub const Builder = struct {
234234
defer wanted_steps.deinit();
235235

236236
if (step_names.len == 0) {
237-
try wanted_steps.append(&self.default_step);
237+
try wanted_steps.append(self.default_step);
238238
} else {
239239
for (step_names) |step_name| {
240240
const s = try self.getTopLevelStepByName(step_name);

std/fmt/index.zig

+6-2
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,6 @@ pub fn formatType(
162162
},
163163
builtin.TypeInfo.Pointer.Size.Many => {
164164
if (ptr_info.child == u8) {
165-
//This is a bit of a hack, but it made more sense to
166-
// do this check here than have formatText do it
167165
if (fmt[0] == 's') {
168166
const len = std.cstr.len(value);
169167
return formatText(value[0..len], fmt, context, Errors, output);
@@ -176,6 +174,12 @@ pub fn formatType(
176174
return output(context, casted_value);
177175
},
178176
},
177+
builtin.TypeId.Array => |info| {
178+
if (info.child == u8) {
179+
return formatText(value, fmt, context, Errors, output);
180+
}
181+
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value));
182+
},
179183
else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"),
180184
}
181185
}

std/json.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -1326,7 +1326,7 @@ pub const Parser = struct {
13261326
},
13271327
// Array Parent -> [ ..., <array>, value ]
13281328
Value.Array => |*array| {
1329-
try array.append(value);
1329+
try array.append(value.*);
13301330
p.state = State.ArrayValue;
13311331
},
13321332
else => {

0 commit comments

Comments
 (0)