Merge pull request #1109 from ziglang/pass-by-non-copying-value
allow passing by non-copying value
This commit is contained in:
commit
751518787a
@ -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#}
|
||||
|
@ -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];
|
||||
|
@ -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);
|
||||
|
57
src/ir.cpp
57
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<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;
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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) ++ "'"),
|
||||
}
|
||||
}
|
||||
|
@ -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 => {
|
||||
|
@ -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);
|
||||
}
|
||||
|
10
std/mem.zig
10
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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
27
test/cases/byval_arg_var.zig
Normal file
27
test/cases/byval_arg_var.zig
Normal 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);
|
||||
}
|
@ -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"));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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("");
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user