From 59b3dc8907f76b93caa689732e878a5bfa2f65c2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 13 Jun 2018 22:40:38 -0400 Subject: [PATCH 1/3] allow passing by non-copying value closes #733 --- doc/langref.html.in | 37 ++++++++++++++----------------------- src/analyze.cpp | 11 ++++------- test/cases/fn.zig | 13 +++++++++++++ test/compile_errors.zig | 23 ----------------------- 4 files changed, 31 insertions(+), 53 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 814de721a694..b32c8165e228 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. + In Zig, structs, unions, and enums with payloads can be passed directly to a function:

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

- Instead, one must use *const. Zig allows implicitly casting something - to a const pointer to it: -

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

- 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 cbeac7bc212e..758bc1a045b5 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 dfb254c6aaa1..2426a411dfd0 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 06f17a37eedf..60ba2551726e 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 { From e311cd562b47529bdcd2423658915539ddb6bc36 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 15 Jun 2018 13:49:39 -0400 Subject: [PATCH 2/3] don't automatically take pointer when passing by non-copying value this commit does not have all tests passing --- src/ir.cpp | 57 ++++--- std/array_list.zig | 26 +-- std/build.zig | 2 +- std/fmt/index.zig | 8 +- std/json.zig | 2 +- std/math/big/int.zig | 346 +++++++++++++++++----------------------- std/mem.zig | 10 +- test/cases/cast.zig | 8 - test/cases/fn.zig | 48 +++++- test/cases/var_args.zig | 12 -- test/compile_errors.zig | 2 +- 11 files changed, 249 insertions(+), 272 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index e5e8dcbb9dd0..d008ead1132e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10463,13 +10463,6 @@ static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, Typ zig_unreachable(); } -static IrInstruction *ir_implicit_byval_const_ref_cast(IrAnalyze *ira, IrInstruction *inst) { - if (type_is_copyable(ira->codegen, inst->value.type)) - return inst; - TypeTableEntry *const_ref_type = get_pointer_to_type(ira->codegen, inst->value.type, true); - return ir_implicit_cast(ira, inst, const_ref_type); -} - static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) { TypeTableEntry *type_entry = ptr->value.type; if (type_is_invalid(type_entry)) { @@ -12283,7 +12276,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod IrInstruction *casted_arg; if (is_var_args) { arg_part_of_generic_id = true; - casted_arg = ir_implicit_byval_const_ref_cast(ira, arg); + casted_arg = arg; } else { if (param_decl_node->data.param_decl.var_token == nullptr) { 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 return false; } else { arg_part_of_generic_id = true; - casted_arg = ir_implicit_byval_const_ref_cast(ira, arg); + casted_arg = arg; } } @@ -12515,9 +12508,18 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal size_t next_proto_i = 0; if (first_arg_ptr) { - IrInstruction *first_arg; assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer); - if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { + + bool first_arg_known_bare = false; + if (fn_type_id->next_param_index >= 1) { + TypeTableEntry *param_type = fn_type_id->param_info[next_proto_i].type; + if (type_is_invalid(param_type)) + return ira->codegen->builtin_types.entry_invalid; + first_arg_known_bare = param_type->id != TypeTableEntryIdPointer; + } + + IrInstruction *first_arg; + if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { 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 size_t next_proto_i = 0; if (first_arg_ptr) { - IrInstruction *first_arg; assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer); - if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { + + bool first_arg_known_bare = false; + if (fn_type_id->next_param_index >= 1) { + TypeTableEntry *param_type = fn_type_id->param_info[next_proto_i].type; + if (type_is_invalid(param_type)) + return ira->codegen->builtin_types.entry_invalid; + first_arg_known_bare = param_type->id != TypeTableEntryIdPointer; + } + + IrInstruction *first_arg; + if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { 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 return ira->codegen->builtin_types.entry_invalid; } if (inst_fn_type_id.async_allocator_type == nullptr) { - IrInstruction *casted_inst = ir_implicit_byval_const_ref_cast(ira, uncasted_async_allocator_inst); - if (type_is_invalid(casted_inst->value.type)) - return ira->codegen->builtin_types.entry_invalid; - inst_fn_type_id.async_allocator_type = casted_inst->value.type; + inst_fn_type_id.async_allocator_type = uncasted_async_allocator_inst->value.type; } async_allocator_inst = ir_implicit_cast(ira, uncasted_async_allocator_inst, inst_fn_type_id.async_allocator_type); if (type_is_invalid(async_allocator_inst->value.type)) @@ -12866,9 +12874,16 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal IrInstruction **casted_args = allocate(call_param_count); size_t next_arg_index = 0; if (first_arg_ptr) { - IrInstruction *first_arg; assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer); - if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { + + TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type; + if (type_is_invalid(param_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *first_arg; + if (param_type->id == TypeTableEntryIdPointer && + handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) + { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); @@ -12876,10 +12891,6 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal return ira->codegen->builtin_types.entry_invalid; } - TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type; - if (type_is_invalid(param_type)) - return ira->codegen->builtin_types.entry_invalid; - IrInstruction *casted_arg = ir_implicit_cast(ira, first_arg, param_type); if (type_is_invalid(casted_arg->value.type)) return ira->codegen->builtin_types.entry_invalid; diff --git a/std/array_list.zig b/std/array_list.zig index 1a235d28a354..fd1d5cbe26d9 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -29,36 +29,36 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { }; } - pub fn deinit(self: *const Self) void { + pub fn deinit(self: Self) void { self.allocator.free(self.items); } - pub fn toSlice(self: *const Self) []align(A) T { + pub fn toSlice(self: Self) []align(A) T { return self.items[0..self.len]; } - pub fn toSliceConst(self: *const Self) []align(A) const T { + pub fn toSliceConst(self: Self) []align(A) const T { return self.items[0..self.len]; } - pub fn at(self: *const Self, n: usize) T { + pub fn at(self: Self, n: usize) T { return self.toSliceConst()[n]; } /// Sets the value at index `i`, or returns `error.OutOfBounds` if /// the index is not in range. - pub fn setOrError(self: *const Self, i: usize, item: *const T) !void { + pub fn setOrError(self: Self, i: usize, item: T) !void { if (i >= self.len) return error.OutOfBounds; - self.items[i] = item.*; + self.items[i] = item; } /// Sets the value at index `i`, asserting that the value is in range. - pub fn set(self: *const Self, i: usize, item: *const T) void { + pub fn set(self: *Self, i: usize, item: T) void { assert(i < self.len); - self.items[i] = item.*; + self.items[i] = item; } - pub fn count(self: *const Self) usize { + pub fn count(self: Self) usize { return self.len; } @@ -81,12 +81,12 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { return result; } - pub fn insert(self: *Self, n: usize, item: *const T) !void { + pub fn insert(self: *Self, n: usize, item: T) !void { try self.ensureCapacity(self.len + 1); self.len += 1; mem.copy(T, self.items[n + 1 .. self.len], self.items[n .. self.len - 1]); - self.items[n] = item.*; + self.items[n] = item; } 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 { mem.copy(T, self.items[n .. n + items.len], items); } - pub fn append(self: *Self, item: *const T) !void { + pub fn append(self: *Self, item: T) !void { const new_item_ptr = try self.addOne(); - new_item_ptr.* = item.*; + new_item_ptr.* = item; } pub fn appendSlice(self: *Self, items: []align(A) const T) !void { diff --git a/std/build.zig b/std/build.zig index 16ce426bcb96..92454a183ae4 100644 --- a/std/build.zig +++ b/std/build.zig @@ -234,7 +234,7 @@ pub const Builder = struct { defer wanted_steps.deinit(); if (step_names.len == 0) { - try wanted_steps.append(&self.default_step); + try wanted_steps.append(self.default_step); } else { for (step_names) |step_name| { const s = try self.getTopLevelStepByName(step_name); diff --git a/std/fmt/index.zig b/std/fmt/index.zig index cfc0948d2cfb..90d3a559c445 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -162,8 +162,6 @@ pub fn formatType( }, builtin.TypeInfo.Pointer.Size.Many => { if (ptr_info.child == u8) { - //This is a bit of a hack, but it made more sense to - // do this check here than have formatText do it if (fmt[0] == 's') { const len = std.cstr.len(value); return formatText(value[0..len], fmt, context, Errors, output); @@ -176,6 +174,12 @@ pub fn formatType( return output(context, casted_value); }, }, + builtin.TypeId.Array => |info| { + if (info.child == u8) { + return formatText(value, fmt, context, Errors, output); + } + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); + }, else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), } } diff --git a/std/json.zig b/std/json.zig index 75ea2eee1c75..8bbee981e387 100644 --- a/std/json.zig +++ b/std/json.zig @@ -1326,7 +1326,7 @@ pub const Parser = struct { }, // Array Parent -> [ ..., , value ] Value.Array => |*array| { - try array.append(value); + try array.append(value.*); p.state = State.ArrayValue; }, else => { diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 19af10e69569..5e15cfb895e3 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -18,39 +18,6 @@ comptime { debug.assert(Limb.is_signed == false); } -const wrapped_buffer_size = 512; - -// Converts primitive integer values onto a stack-based big integer, or passes through existing -// Int types with no modifications. This can fail at runtime if using a very large dynamic -// integer but it is very unlikely and is considered a user error. -fn wrapInt(allocator: *Allocator, bn: var) *const Int { - const T = @typeOf(bn); - switch (@typeInfo(T)) { - TypeId.Pointer => |info| { - if (info.child == Int) { - return bn; - } else { - @compileError("cannot set Int using type " ++ @typeName(T)); - } - }, - else => { - var s = allocator.create(Int) catch unreachable; - s.* = Int{ - .allocator = allocator, - .positive = false, - .limbs = block: { - var limbs = allocator.alloc(Limb, Int.default_capacity) catch unreachable; - limbs[0] = 0; - break :block limbs; - }, - .len = 1, - }; - s.set(bn) catch unreachable; - return s; - }, - } -} - pub const Int = struct { allocator: *Allocator, positive: bool, @@ -93,11 +60,11 @@ pub const Int = struct { self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity); } - pub fn deinit(self: *const Int) void { + pub fn deinit(self: Int) void { self.allocator.free(self.limbs); } - pub fn clone(other: *const Int) !Int { + pub fn clone(other: Int) !Int { return Int{ .allocator = other.allocator, .positive = other.positive, @@ -110,8 +77,8 @@ pub const Int = struct { }; } - pub fn copy(self: *Int, other: *const Int) !void { - if (self == other) { + pub fn copy(self: *Int, other: Int) !void { + if (self == &other) { return; } @@ -125,7 +92,7 @@ pub const Int = struct { mem.swap(Int, self, other); } - pub fn dump(self: *const Int) void { + pub fn dump(self: Int) void { for (self.limbs) |limb| { debug.warn("{x} ", limb); } @@ -140,20 +107,20 @@ pub const Int = struct { r.positive = true; } - pub fn isOdd(r: *const Int) bool { + pub fn isOdd(r: Int) bool { return r.limbs[0] & 1 != 0; } - pub fn isEven(r: *const Int) bool { + pub fn isEven(r: Int) bool { return !r.isOdd(); } - fn bitcount(self: *const Int) usize { + fn bitcount(self: Int) usize { const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); return usize(!self.positive) + u_bit_count; } - pub fn sizeInBase(self: *const Int, base: usize) usize { + pub fn sizeInBase(self: Int, base: usize) usize { return (self.bitcount() / math.log2(base)) + 1; } @@ -219,7 +186,7 @@ pub const Int = struct { TargetTooSmall, }; - pub fn to(self: *const Int, comptime T: type) ConvertError!T { + pub fn to(self: Int, comptime T: type) ConvertError!T { switch (@typeId(T)) { TypeId.Int => { const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T; @@ -286,16 +253,28 @@ pub const Int = struct { i += 1; } + // TODO values less than limb size should guarantee non allocating + var base_buffer: [512]u8 = undefined; + const base_al = &std.heap.FixedBufferAllocator.init(base_buffer[0..]).allocator; + const base_ap = try Int.initSet(base_al, base); + + var d_buffer: [512]u8 = undefined; + var d_fba = std.heap.FixedBufferAllocator.init(d_buffer[0..]); + const d_al = &d_fba.allocator; + try self.set(0); for (value[i..]) |ch| { const d = try charToDigit(ch, base); - try self.mul(self, base); - try self.add(self, d); + d_fba.end_index = 0; + const d_ap = try Int.initSet(d_al, d); + + try self.mul(self.*, base_ap); + try self.add(self.*, d_ap); } self.positive = positive; } - pub fn toString(self: *const Int, allocator: *Allocator, base: u8) ![]const u8 { + pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 { if (base < 2 or base > 16) { return error.InvalidBase; } @@ -345,7 +324,7 @@ pub const Int = struct { var b = try Int.initSet(allocator, limb_base); while (q.len >= 2) { - try Int.divTrunc(&q, &r, &q, &b); + try Int.divTrunc(&q, &r, q, b); var r_word = r.limbs[0]; var i: usize = 0; @@ -378,12 +357,7 @@ pub const Int = struct { } // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively. - pub fn cmpAbs(a: *const Int, bv: var) i8 { - // TODO: Thread-local buffer. - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var b = wrapInt(&stack.allocator, bv); - + pub fn cmpAbs(a: Int, b: Int) i8 { if (a.len < b.len) { return -1; } @@ -408,11 +382,7 @@ pub const Int = struct { } // returns -1, 0, 1 if a < b, a == b or a > b respectively. - pub fn cmp(a: *const Int, bv: var) i8 { - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var b = wrapInt(&stack.allocator, bv); - + pub fn cmp(a: Int, b: Int) i8 { if (a.positive != b.positive) { return if (a.positive) i8(1) else -1; } else { @@ -422,17 +392,17 @@ pub const Int = struct { } // if a == 0 - pub fn eqZero(a: *const Int) bool { + pub fn eqZero(a: Int) bool { return a.len == 1 and a.limbs[0] == 0; } // if |a| == |b| - pub fn eqAbs(a: *const Int, b: var) bool { + pub fn eqAbs(a: Int, b: Int) bool { return cmpAbs(a, b) == 0; } // if a == b - pub fn eq(a: *const Int, b: var) bool { + pub fn eq(a: Int, b: Int) bool { return cmp(a, b) == 0; } @@ -473,12 +443,7 @@ pub const Int = struct { } // r = a + b - pub fn add(r: *Int, av: var, bv: var) Allocator.Error!void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void { if (a.eqZero()) { try r.copy(b); return; @@ -547,12 +512,7 @@ pub const Int = struct { } // r = a - b - pub fn sub(r: *Int, av: var, bv: var) !void { - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn sub(r: *Int, a: Int, b: Int) !void { if (a.positive != b.positive) { if (a.positive) { // (a) - (-b) => a + b @@ -632,14 +592,9 @@ pub const Int = struct { // rma = a * b // // For greatest efficiency, ensure rma does not alias a or b. - pub fn mul(rma: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn mul(rma: *Int, a: Int, b: Int) !void { var r = rma; - var aliased = rma == a or rma == b; + var aliased = rma.limbs.ptr == a.limbs.ptr or rma.limbs.ptr == b.limbs.ptr; var sr: Int = undefined; if (aliased) { @@ -714,29 +669,29 @@ pub const Int = struct { } } - pub fn divFloor(q: *Int, r: *Int, a: var, b: var) !void { + pub fn divFloor(q: *Int, r: *Int, a: Int, b: Int) !void { try div(q, r, a, b); // Trunc -> Floor. if (!q.positive) { - try q.sub(q, 1); - try r.add(q, 1); + // TODO values less than limb size should guarantee non allocating + var one_buffer: [512]u8 = undefined; + const one_al = &std.heap.FixedBufferAllocator.init(one_buffer[0..]).allocator; + const one_ap = try Int.initSet(one_al, 1); + + try q.sub(q.*, one_ap); + try r.add(q.*, one_ap); } r.positive = b.positive; } - pub fn divTrunc(q: *Int, r: *Int, a: var, b: var) !void { + pub fn divTrunc(q: *Int, r: *Int, a: Int, b: Int) !void { try div(q, r, a, b); r.positive = a.positive; } // Truncates by default. - fn div(quo: *Int, rem: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + fn div(quo: *Int, rem: *Int, a: Int, b: Int) !void { if (b.eqZero()) { @panic("division by zero"); } @@ -821,8 +776,8 @@ pub const Int = struct { // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) const norm_shift = @clz(y.limbs[y.len - 1]); - try x.shiftLeft(x, norm_shift); - try y.shiftLeft(y, norm_shift); + try x.shiftLeft(x.*, norm_shift); + try y.shiftLeft(y.*, norm_shift); const n = x.len - 1; const t = y.len - 1; @@ -832,10 +787,10 @@ pub const Int = struct { mem.set(Limb, q.limbs[0..q.len], 0); // 2. - try tmp.shiftLeft(y, Limb.bit_count * (n - t)); - while (x.cmp(&tmp) >= 0) { + try tmp.shiftLeft(y.*, Limb.bit_count * (n - t)); + while (x.cmp(tmp) >= 0) { q.limbs[n - t] += 1; - try x.sub(x, tmp); + try x.sub(x.*, tmp); } // 3. @@ -864,7 +819,7 @@ pub const Int = struct { r.limbs[2] = carry; r.normN(3); - if (r.cmpAbs(&tmp) <= 0) { + if (r.cmpAbs(tmp) <= 0) { break; } @@ -873,13 +828,13 @@ pub const Int = struct { // 3.3 try tmp.set(q.limbs[i - t - 1]); - try tmp.mul(&tmp, y); - try tmp.shiftLeft(&tmp, Limb.bit_count * (i - t - 1)); - try x.sub(x, &tmp); + try tmp.mul(tmp, y.*); + try tmp.shiftLeft(tmp, Limb.bit_count * (i - t - 1)); + try x.sub(x.*, tmp); if (!x.positive) { - try tmp.shiftLeft(y, Limb.bit_count * (i - t - 1)); - try x.add(x, &tmp); + try tmp.shiftLeft(y.*, Limb.bit_count * (i - t - 1)); + try x.add(x.*, tmp); q.limbs[i - t - 1] -= 1; } } @@ -887,16 +842,12 @@ pub const Int = struct { // Denormalize q.normN(q.len); - try r.shiftRight(x, norm_shift); + try r.shiftRight(x.*, norm_shift); r.normN(r.len); } // r = a << shift, in other words, r = a * 2^shift - pub fn shiftLeft(r: *Int, av: var, shift: usize) !void { - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - + pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void { try r.ensureCapacity(a.len + (shift / Limb.bit_count) + 1); llshl(r.limbs[0..], a.limbs[0..a.len], shift); r.norm1(a.len + (shift / Limb.bit_count) + 1); @@ -927,11 +878,7 @@ pub const Int = struct { } // r = a >> shift - pub fn shiftRight(r: *Int, av: var, shift: usize) !void { - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - + pub fn shiftRight(r: *Int, a: Int, shift: usize) !void { if (a.len <= shift / Limb.bit_count) { r.len = 1; r.limbs[0] = 0; @@ -966,12 +913,7 @@ pub const Int = struct { } // r = a | b - pub fn bitOr(r: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn bitOr(r: *Int, a: Int, b: Int) !void { if (a.len > b.len) { try r.ensureCapacity(a.len); llor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]); @@ -998,12 +940,7 @@ pub const Int = struct { } // r = a & b - pub fn bitAnd(r: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn bitAnd(r: *Int, a: Int, b: Int) !void { if (a.len > b.len) { try r.ensureCapacity(b.len); lland(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]); @@ -1027,12 +964,7 @@ pub const Int = struct { } // r = a ^ b - pub fn bitXor(r: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn bitXor(r: *Int, a: Int, b: Int) !void { if (a.len > b.len) { try r.ensureCapacity(a.len); llxor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]); @@ -1065,7 +997,7 @@ pub const Int = struct { // may be untested in some cases. const u256 = @IntType(false, 256); -var al = debug.global_allocator; +const al = debug.global_allocator; test "big.int comptime_int set" { comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab; @@ -1198,7 +1130,7 @@ test "big.int bitcount + sizeInBase" { debug.assert(a.sizeInBase(2) >= 32); debug.assert(a.sizeInBase(10) >= 10); - try a.shiftLeft(&a, 5000); + try a.shiftLeft(a, 5000); debug.assert(a.bitcount() == 5032); debug.assert(a.sizeInBase(2) >= 5032); a.positive = false; @@ -1320,40 +1252,40 @@ test "big.int compare" { var a = try Int.initSet(al, -11); var b = try Int.initSet(al, 10); - debug.assert(a.cmpAbs(&b) == 1); - debug.assert(a.cmp(&b) == -1); + debug.assert(a.cmpAbs(b) == 1); + debug.assert(a.cmp(b) == -1); } test "big.int compare similar" { var a = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeee); var b = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeef); - debug.assert(a.cmpAbs(&b) == -1); - debug.assert(b.cmpAbs(&a) == 1); + debug.assert(a.cmpAbs(b) == -1); + debug.assert(b.cmpAbs(a) == 1); } test "big.int compare different limb size" { var a = try Int.initSet(al, @maxValue(Limb) + 1); var b = try Int.initSet(al, 1); - debug.assert(a.cmpAbs(&b) == 1); - debug.assert(b.cmpAbs(&a) == -1); + debug.assert(a.cmpAbs(b) == 1); + debug.assert(b.cmpAbs(a) == -1); } test "big.int compare multi-limb" { var a = try Int.initSet(al, -0x7777777799999999ffffeeeeffffeeeeffffeeeef); var b = try Int.initSet(al, 0x7777777799999999ffffeeeeffffeeeeffffeeeee); - debug.assert(a.cmpAbs(&b) == 1); - debug.assert(a.cmp(&b) == -1); + debug.assert(a.cmpAbs(b) == 1); + debug.assert(a.cmp(b) == -1); } test "big.int equality" { var a = try Int.initSet(al, 0xffffffff1); var b = try Int.initSet(al, -0xffffffff1); - debug.assert(a.eqAbs(&b)); - debug.assert(!a.eq(&b)); + debug.assert(a.eqAbs(b)); + debug.assert(!a.eq(b)); } test "big.int abs" { @@ -1381,7 +1313,7 @@ test "big.int add single-single" { var b = try Int.initSet(al, 5); var c = try Int.init(al); - try c.add(&a, &b); + try c.add(a, b); debug.assert((try c.to(u32)) == 55); } @@ -1392,10 +1324,10 @@ test "big.int add multi-single" { var c = try Int.init(al); - try c.add(&a, &b); + try c.add(a, b); debug.assert((try c.to(DoubleLimb)) == @maxValue(Limb) + 2); - try c.add(&b, &a); + try c.add(b, a); debug.assert((try c.to(DoubleLimb)) == @maxValue(Limb) + 2); } @@ -1406,7 +1338,7 @@ test "big.int add multi-multi" { var b = try Int.initSet(al, op2); var c = try Int.init(al); - try c.add(&a, &b); + try c.add(a, b); debug.assert((try c.to(u128)) == op1 + op2); } @@ -1416,7 +1348,7 @@ test "big.int add zero-zero" { var b = try Int.initSet(al, 0); var c = try Int.init(al); - try c.add(&a, &b); + try c.add(a, b); debug.assert((try c.to(u32)) == 0); } @@ -1426,7 +1358,7 @@ test "big.int add alias multi-limb nonzero-zero" { var a = try Int.initSet(al, op1); var b = try Int.initSet(al, 0); - try a.add(&a, &b); + try a.add(a, b); debug.assert((try a.to(u128)) == op1); } @@ -1434,16 +1366,21 @@ test "big.int add alias multi-limb nonzero-zero" { test "big.int add sign" { var a = try Int.init(al); - try a.add(1, 2); + const one = try Int.initSet(al, 1); + const two = try Int.initSet(al, 2); + const neg_one = try Int.initSet(al, -1); + const neg_two = try Int.initSet(al, -2); + + try a.add(one, two); debug.assert((try a.to(i32)) == 3); - try a.add(-1, 2); + try a.add(neg_one, two); debug.assert((try a.to(i32)) == 1); - try a.add(1, -2); + try a.add(one, neg_two); debug.assert((try a.to(i32)) == -1); - try a.add(-1, -2); + try a.add(neg_one, neg_two); debug.assert((try a.to(i32)) == -3); } @@ -1452,7 +1389,7 @@ test "big.int sub single-single" { var b = try Int.initSet(al, 5); var c = try Int.init(al); - try c.sub(&a, &b); + try c.sub(a, b); debug.assert((try c.to(u32)) == 45); } @@ -1462,7 +1399,7 @@ test "big.int sub multi-single" { var b = try Int.initSet(al, 1); var c = try Int.init(al); - try c.sub(&a, &b); + try c.sub(a, b); debug.assert((try c.to(Limb)) == @maxValue(Limb)); } @@ -1475,7 +1412,7 @@ test "big.int sub multi-multi" { var b = try Int.initSet(al, op2); var c = try Int.init(al); - try c.sub(&a, &b); + try c.sub(a, b); debug.assert((try c.to(u128)) == op1 - op2); } @@ -1485,7 +1422,7 @@ test "big.int sub equal" { var b = try Int.initSet(al, 0x11efefefefefefefefefefefef); var c = try Int.init(al); - try c.sub(&a, &b); + try c.sub(a, b); debug.assert((try c.to(u32)) == 0); } @@ -1493,19 +1430,24 @@ test "big.int sub equal" { test "big.int sub sign" { var a = try Int.init(al); - try a.sub(1, 2); + const one = try Int.initSet(al, 1); + const two = try Int.initSet(al, 2); + const neg_one = try Int.initSet(al, -1); + const neg_two = try Int.initSet(al, -2); + + try a.sub(one, two); debug.assert((try a.to(i32)) == -1); - try a.sub(-1, 2); + try a.sub(neg_one, two); debug.assert((try a.to(i32)) == -3); - try a.sub(1, -2); + try a.sub(one, neg_two); debug.assert((try a.to(i32)) == 3); - try a.sub(-1, -2); + try a.sub(neg_one, neg_two); debug.assert((try a.to(i32)) == 1); - try a.sub(-2, -1); + try a.sub(neg_two, neg_one); debug.assert((try a.to(i32)) == -1); } @@ -1514,7 +1456,7 @@ test "big.int mul single-single" { var b = try Int.initSet(al, 5); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(u64)) == 250); } @@ -1524,7 +1466,7 @@ test "big.int mul multi-single" { var b = try Int.initSet(al, 2); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(DoubleLimb)) == 2 * @maxValue(Limb)); } @@ -1536,7 +1478,7 @@ test "big.int mul multi-multi" { var b = try Int.initSet(al, op2); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(u256)) == op1 * op2); } @@ -1545,7 +1487,7 @@ test "big.int mul alias r with a" { var a = try Int.initSet(al, @maxValue(Limb)); var b = try Int.initSet(al, 2); - try a.mul(&a, &b); + try a.mul(a, b); debug.assert((try a.to(DoubleLimb)) == 2 * @maxValue(Limb)); } @@ -1554,7 +1496,7 @@ test "big.int mul alias r with b" { var a = try Int.initSet(al, @maxValue(Limb)); var b = try Int.initSet(al, 2); - try a.mul(&b, &a); + try a.mul(b, a); debug.assert((try a.to(DoubleLimb)) == 2 * @maxValue(Limb)); } @@ -1562,7 +1504,7 @@ test "big.int mul alias r with b" { test "big.int mul alias r with a and b" { var a = try Int.initSet(al, @maxValue(Limb)); - try a.mul(&a, &a); + try a.mul(a, a); debug.assert((try a.to(DoubleLimb)) == @maxValue(Limb) * @maxValue(Limb)); } @@ -1572,7 +1514,7 @@ test "big.int mul a*0" { var b = try Int.initSet(al, 0); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(u32)) == 0); } @@ -1582,7 +1524,7 @@ test "big.int mul 0*0" { var b = try Int.initSet(al, 0); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(u32)) == 0); } @@ -1593,7 +1535,7 @@ test "big.int div single-single no rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u32)) == 10); debug.assert((try r.to(u32)) == 0); @@ -1605,7 +1547,7 @@ test "big.int div single-single with rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u32)) == 9); debug.assert((try r.to(u32)) == 4); @@ -1620,7 +1562,7 @@ test "big.int div multi-single no rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u64)) == op1 / op2); debug.assert((try r.to(u64)) == 0); @@ -1635,7 +1577,7 @@ test "big.int div multi-single with rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u64)) == op1 / op2); debug.assert((try r.to(u64)) == 3); @@ -1650,7 +1592,7 @@ test "big.int div multi>2-single" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == op1 / op2); debug.assert((try r.to(u32)) == 0x3e4e); @@ -1662,7 +1604,7 @@ test "big.int div single-single q < r" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u64)) == 0); debug.assert((try r.to(u64)) == 0x0078f432); @@ -1674,7 +1616,7 @@ test "big.int div single-single q == r" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u64)) == 1); debug.assert((try r.to(u64)) == 0); @@ -1684,7 +1626,7 @@ test "big.int div q=0 alias" { var a = try Int.initSet(al, 3); var b = try Int.initSet(al, 10); - try Int.divTrunc(&a, &b, &a, &b); + try Int.divTrunc(&a, &b, a, b); debug.assert((try a.to(u64)) == 0); debug.assert((try b.to(u64)) == 3); @@ -1698,7 +1640,7 @@ test "big.int div multi-multi q < r" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0); debug.assert((try r.to(u128)) == op1); @@ -1713,7 +1655,7 @@ test "big.int div trunc single-single +/+" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); // n = q * d + r // 5 = 1 * 3 + 2 @@ -1733,7 +1675,7 @@ test "big.int div trunc single-single -/+" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); // n = q * d + r // -5 = 1 * -3 - 2 @@ -1753,7 +1695,7 @@ test "big.int div trunc single-single +/-" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); // n = q * d + r // 5 = -1 * -3 + 2 @@ -1773,7 +1715,7 @@ test "big.int div trunc single-single -/-" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); // n = q * d + r // -5 = 1 * -3 - 2 @@ -1793,7 +1735,7 @@ test "big.int div floor single-single +/+" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divFloor(&q, &r, &a, &b); + try Int.divFloor(&q, &r, a, b); // n = q * d + r // 5 = 1 * 3 + 2 @@ -1813,7 +1755,7 @@ test "big.int div floor single-single -/+" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divFloor(&q, &r, &a, &b); + try Int.divFloor(&q, &r, a, b); // n = q * d + r // -5 = -2 * 3 + 1 @@ -1833,7 +1775,7 @@ test "big.int div floor single-single +/-" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divFloor(&q, &r, &a, &b); + try Int.divFloor(&q, &r, a, b); // n = q * d + r // 5 = -2 * -3 - 1 @@ -1853,7 +1795,7 @@ test "big.int div floor single-single -/-" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divFloor(&q, &r, &a, &b); + try Int.divFloor(&q, &r, a, b); // n = q * d + r // -5 = 2 * -3 + 1 @@ -1870,7 +1812,7 @@ test "big.int div multi-multi with rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); debug.assert((try r.to(u128)) == 0x28de0acacd806823638); @@ -1882,7 +1824,7 @@ test "big.int div multi-multi no rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); debug.assert((try r.to(u128)) == 0); @@ -1894,7 +1836,7 @@ test "big.int div multi-multi (2 branch)" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0x10000000000000000); debug.assert((try r.to(u128)) == 0x44444443444444431111111111111111); @@ -1906,7 +1848,7 @@ test "big.int div multi-multi (3.1/3.3 branch)" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0xfffffffffffffffffff); debug.assert((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282); @@ -1943,17 +1885,17 @@ test "big.int shift-left multi" { test "big.int shift-right negative" { var a = try Int.init(al); - try a.shiftRight(-20, 2); + try a.shiftRight(try Int.initSet(al, -20), 2); debug.assert((try a.to(i32)) == -20 >> 2); - try a.shiftRight(-5, 10); + try a.shiftRight(try Int.initSet(al, -5), 10); debug.assert((try a.to(i32)) == -5 >> 10); } test "big.int shift-left negative" { var a = try Int.init(al); - try a.shiftRight(-10, 1232); + try a.shiftRight(try Int.initSet(al, -10), 1232); debug.assert((try a.to(i32)) == -10 >> 1232); } @@ -1961,7 +1903,7 @@ test "big.int bitwise and simple" { var a = try Int.initSet(al, 0xffffffff11111111); var b = try Int.initSet(al, 0xeeeeeeee22222222); - try a.bitAnd(&a, &b); + try a.bitAnd(a, b); debug.assert((try a.to(u64)) == 0xeeeeeeee00000000); } @@ -1970,7 +1912,7 @@ test "big.int bitwise and multi-limb" { var a = try Int.initSet(al, @maxValue(Limb) + 1); var b = try Int.initSet(al, @maxValue(Limb)); - try a.bitAnd(&a, &b); + try a.bitAnd(a, b); debug.assert((try a.to(u128)) == 0); } @@ -1979,7 +1921,7 @@ test "big.int bitwise xor simple" { var a = try Int.initSet(al, 0xffffffff11111111); var b = try Int.initSet(al, 0xeeeeeeee22222222); - try a.bitXor(&a, &b); + try a.bitXor(a, b); debug.assert((try a.to(u64)) == 0x1111111133333333); } @@ -1988,7 +1930,7 @@ test "big.int bitwise xor multi-limb" { var a = try Int.initSet(al, @maxValue(Limb) + 1); var b = try Int.initSet(al, @maxValue(Limb)); - try a.bitXor(&a, &b); + try a.bitXor(a, b); debug.assert((try a.to(DoubleLimb)) == (@maxValue(Limb) + 1) ^ @maxValue(Limb)); } @@ -1997,7 +1939,7 @@ test "big.int bitwise or simple" { var a = try Int.initSet(al, 0xffffffff11111111); var b = try Int.initSet(al, 0xeeeeeeee22222222); - try a.bitOr(&a, &b); + try a.bitOr(a, b); debug.assert((try a.to(u64)) == 0xffffffff33333333); } @@ -2006,7 +1948,7 @@ test "big.int bitwise or multi-limb" { var a = try Int.initSet(al, @maxValue(Limb) + 1); var b = try Int.initSet(al, @maxValue(Limb)); - try a.bitOr(&a, &b); + try a.bitOr(a, b); // TODO: big.int.cpp or is wrong on multi-limb. debug.assert((try a.to(DoubleLimb)) == (@maxValue(Limb) + 1) + @maxValue(Limb)); @@ -2015,9 +1957,9 @@ test "big.int bitwise or multi-limb" { test "big.int var args" { var a = try Int.initSet(al, 5); - try a.add(&a, 6); + try a.add(a, try Int.initSet(al, 6)); debug.assert((try a.to(u64)) == 11); - debug.assert(a.cmp(11) == 0); - debug.assert(a.cmp(14) <= 0); + debug.assert(a.cmp(try Int.initSet(al, 11)) == 0); + debug.assert(a.cmp(try Int.initSet(al, 14)) <= 0); } diff --git a/std/mem.zig b/std/mem.zig index f961c7862bd5..10b3eb8fef1a 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -40,16 +40,12 @@ pub const Allocator = struct { /// Call destroy with the result /// TODO once #733 is solved, this will replace create - pub fn construct(self: *Allocator, init: var) t: { - // TODO this is a workaround for type getting parsed as Error!&const T - const T = @typeOf(init).Child; - break :t Error!*T; - } { - const T = @typeOf(init).Child; + pub fn construct(self: *Allocator, init: var) Error!*@typeOf(init) { + const T = @typeOf(init); if (@sizeOf(T) == 0) return &{}; const slice = try self.alloc(T, 1); const ptr = &slice[0]; - ptr.* = init.*; + ptr.* = init; return ptr; } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index ade1cf78aac7..4c216010eb00 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -318,14 +318,6 @@ fn testCastConstArrayRefToConstSlice() void { assert(mem.eql(u8, slice, "aoeu")); } -test "var args implicitly casts by value arg to const ref" { - foo("hello"); -} - -fn foo(args: ...) void { - assert(@typeOf(args[0]) == *const [5]u8); -} - test "peer type resolution: error and [N]T" { // TODO: implicit error!T to error!U where T can implicitly cast to U //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); diff --git a/test/cases/fn.zig b/test/cases/fn.zig index 2426a411dfd0..12f22bfc356c 100644 --- a/test/cases/fn.zig +++ b/test/cases/fn.zig @@ -121,7 +121,7 @@ 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); + assert(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); } const Point = struct { @@ -129,6 +129,50 @@ const Point = struct { y: i32, }; -fn bar(pt: Point) i32 { +fn addPointCoords(pt: Point) i32 { return pt.x + pt.y; } + +test "pass by non-copying value through var arg" { + assert(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); +} + +fn addPointCoordsVar(pt: var) i32 { + comptime assert(@typeOf(pt) == Point); + return pt.x + pt.y; +} + +test "pass by non-copying value as method" { + var pt = Point2{ .x = 1, .y = 2 }; + assert(pt.addPointCoords() == 3); +} + +const Point2 = struct { + x: i32, + y: i32, + + fn addPointCoords(self: Point2) i32 { + return self.x + self.y; + } +}; + +test "pass by non-copying value as method, which is generic" { + var pt = Point3{ .x = 1, .y = 2 }; + assert(pt.addPointCoords(i32) == 3); +} + +const Point3 = struct { + x: i32, + y: i32, + + fn addPointCoords(self: Point3, comptime T: type) i32 { + return self.x + self.y; + } +}; + +test "pass by non-copying value as method, at comptime" { + comptime { + var pt = Point2{ .x = 1, .y = 2 }; + assert(pt.addPointCoords() == 3); + } +} diff --git a/test/cases/var_args.zig b/test/cases/var_args.zig index 5ef41f52ba6c..3eb6e304486b 100644 --- a/test/cases/var_args.zig +++ b/test/cases/var_args.zig @@ -75,18 +75,6 @@ test "array of var args functions" { assert(!foos[1]()); } -test "pass array and slice of same array to var args should have same pointers" { - const array = "hi"; - const slice: []const u8 = array; - return assertSlicePtrsEql(array, slice); -} - -fn assertSlicePtrsEql(args: ...) void { - const s1 = ([]const u8)(args[0]); - const s2 = args[1]; - assert(s1.ptr == s2.ptr); -} - test "pass zero length array to var args param" { doNothingWithFirstArg(""); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 60ba2551726e..ed46e43066b8 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2215,7 +2215,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ derp.init(); \\} , - ".tmp_source.zig:14:5: error: expected type 'i32', found '*const Foo'", + ".tmp_source.zig:14:5: error: expected type 'i32', found 'Foo'", ); cases.add( From 472b7ef7e6db4bdd4717ff5b44b63e233853bb06 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jun 2018 19:14:14 -0400 Subject: [PATCH 3/3] disable byval --- src/codegen.cpp | 21 --------------------- test/behavior.zig | 1 + test/cases/byval_arg_var.zig | 27 +++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 test/cases/byval_arg_var.zig diff --git a/src/codegen.cpp b/src/codegen.cpp index fedfcfa7440f..425cdac0249f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -326,13 +326,6 @@ static void addLLVMArgAttr(LLVMValueRef arg_val, unsigned param_index, const cha return addLLVMAttr(arg_val, param_index + 1, attr_name); } -static void addLLVMCallsiteAttr(LLVMValueRef call_instr, unsigned param_index, const char *attr_name) { - unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); - assert(kind_id != 0); - LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), kind_id, 0); - LLVMAddCallSiteAttribute(call_instr, param_index + 1, llvm_attr); -} - static bool is_symbol_available(CodeGen *g, Buf *name) { return g->exported_symbol_names.maybe_get(name) == nullptr && g->external_prototypes.maybe_get(name) == nullptr; } @@ -581,11 +574,6 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { if (param_type->id == TypeTableEntryIdPointer) { addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "nonnull"); } - // Note: byval is disabled on windows due to an LLVM bug: - // https://github.com/ziglang/zig/issues/536 - if (is_byval && g->zig_target.os != OsWindows) { - addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "byval"); - } } 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 } - for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { - FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; - // Note: byval is disabled on windows due to an LLVM bug: - // https://github.com/ziglang/zig/issues/536 - if (gen_info->is_byval && g->zig_target.os != OsWindows) { - addLLVMCallsiteAttr(result, (unsigned)gen_info->gen_index, "byval"); - } - } - if (instruction->is_async) { LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, ""); LLVMBuildStore(g->builder, result, payload_ptr); diff --git a/test/behavior.zig b/test/behavior.zig index eb8b643bb737..096c07b2e026 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -13,6 +13,7 @@ comptime { _ = @import("cases/bugs/656.zig"); _ = @import("cases/bugs/828.zig"); _ = @import("cases/bugs/920.zig"); + _ = @import("cases/byval_arg_var.zig"); _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); _ = @import("cases/coroutines.zig"); diff --git a/test/cases/byval_arg_var.zig b/test/cases/byval_arg_var.zig new file mode 100644 index 000000000000..826b9cc9e593 --- /dev/null +++ b/test/cases/byval_arg_var.zig @@ -0,0 +1,27 @@ +const std = @import("std"); + +var result: []const u8 = "wrong"; + +test "aoeu" { + start(); + blowUpStack(10); + + std.debug.assert(std.mem.eql(u8, result, "string literal")); +} + +fn start() void { + foo("string literal"); +} + +fn foo(x: var) void { + bar(x); +} + +fn bar(x: var) void { + result = x; +} + +fn blowUpStack(x: u32) void { + if (x == 0) return; + blowUpStack(x - 1); +}