Merge pull request #1109 from ziglang/pass-by-non-copying-value

allow passing by non-copying value
This commit is contained in:
Andrew Kelley 2018-06-16 21:13:10 -04:00 committed by GitHub
commit 751518787a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 307 additions and 345 deletions

View File

@ -2818,39 +2818,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.
</p>
{#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#}
<p>
Instead, one must use <code>*const</code>. 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:
</p>
{#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#}
<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#}

View File

@ -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];

View File

@ -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);

View File

@ -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<IrInstruction *>(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;

View File

@ -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 {

View File

@ -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);

View File

@ -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) ++ "'"),
}
}

View File

@ -1326,7 +1326,7 @@ pub const Parser = struct {
},
// Array Parent -> [ ..., <array>, value ]
Value.Array => |*array| {
try array.append(value);
try array.append(value.*);
p.state = State.ArrayValue;
},
else => {

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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");

View File

@ -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);
}

View File

@ -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"));

View File

@ -119,3 +119,60 @@ test "assign inline fn to const variable" {
}
inline fn inlineFn() void {}
test "pass by non-copying value" {
assert(addPointCoords(Point{ .x = 1, .y = 2 }) == 3);
}
const Point = struct {
x: i32,
y: 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);
}
}

View File

@ -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("");
}

View File

@ -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(
@ -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 {