stage2: more division support
AIR: * div is renamed to div_trunc. * Add div_float, div_floor, div_exact. - Implemented in Sema and LLVM codegen. C backend has a stub. Improvements to std.math.big.Int: * Add `eqZero` function to `Mutable`. * Fix incorrect results for `divFloor`. Compiler-rt: * Add muloti4 to the stage2 section.
This commit is contained in:
@@ -135,6 +135,11 @@ pub const Mutable = struct {
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns true if `a == 0`.
|
||||
pub fn eqZero(self: Mutable) bool {
|
||||
return self.toConst().eqZero();
|
||||
}
|
||||
|
||||
/// Asserts that the allocator owns the limbs memory. If this is not the case,
|
||||
/// use `toConst().toManaged()`.
|
||||
pub fn toManaged(self: Mutable, allocator: *Allocator) Managed {
|
||||
@@ -773,12 +778,15 @@ pub const Mutable = struct {
|
||||
div(q, r, a, b, limbs_buffer, allocator);
|
||||
|
||||
// Trunc -> Floor.
|
||||
if (!q.positive) {
|
||||
if (a.positive and b.positive) return;
|
||||
|
||||
if ((!q.positive or q.eqZero()) and !r.eqZero()) {
|
||||
const one: Const = .{ .limbs = &[_]Limb{1}, .positive = true };
|
||||
q.sub(q.toConst(), one);
|
||||
r.add(q.toConst(), one);
|
||||
}
|
||||
r.positive = b.positive;
|
||||
|
||||
r.mulNoAlias(q.toConst(), b, allocator);
|
||||
r.sub(a, r.toConst());
|
||||
}
|
||||
|
||||
/// q = a / b (rem r)
|
||||
@@ -1220,12 +1228,12 @@ pub const Mutable = struct {
|
||||
|
||||
var x: Mutable = .{
|
||||
.limbs = x_limbs,
|
||||
.positive = a.positive,
|
||||
.positive = true,
|
||||
.len = a.limbs.len - ab_zero_limb_count,
|
||||
};
|
||||
var y: Mutable = .{
|
||||
.limbs = y_limbs,
|
||||
.positive = b.positive,
|
||||
.positive = true,
|
||||
.len = b.limbs.len - ab_zero_limb_count,
|
||||
};
|
||||
|
||||
|
||||
@@ -1399,6 +1399,63 @@ test "big.int div floor single-single -/-" {
|
||||
try testing.expect((try r.to(i32)) == er);
|
||||
}
|
||||
|
||||
test "big.int div floor no remainder negative quotient" {
|
||||
const u: i32 = -0x80000000;
|
||||
const v: i32 = 1;
|
||||
|
||||
var a = try Managed.initSet(testing.allocator, u);
|
||||
defer a.deinit();
|
||||
var b = try Managed.initSet(testing.allocator, v);
|
||||
defer b.deinit();
|
||||
|
||||
var q = try Managed.init(testing.allocator);
|
||||
defer q.deinit();
|
||||
var r = try Managed.init(testing.allocator);
|
||||
defer r.deinit();
|
||||
try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
|
||||
|
||||
try testing.expect((try q.to(i32)) == -0x80000000);
|
||||
try testing.expect((try r.to(i32)) == 0);
|
||||
}
|
||||
|
||||
test "big.int div floor negative close to zero" {
|
||||
const u: i32 = -2;
|
||||
const v: i32 = 12;
|
||||
|
||||
var a = try Managed.initSet(testing.allocator, u);
|
||||
defer a.deinit();
|
||||
var b = try Managed.initSet(testing.allocator, v);
|
||||
defer b.deinit();
|
||||
|
||||
var q = try Managed.init(testing.allocator);
|
||||
defer q.deinit();
|
||||
var r = try Managed.init(testing.allocator);
|
||||
defer r.deinit();
|
||||
try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
|
||||
|
||||
try testing.expect((try q.to(i32)) == -1);
|
||||
try testing.expect((try r.to(i32)) == 10);
|
||||
}
|
||||
|
||||
test "big.int div floor positive close to zero" {
|
||||
const u: i32 = 10;
|
||||
const v: i32 = 12;
|
||||
|
||||
var a = try Managed.initSet(testing.allocator, u);
|
||||
defer a.deinit();
|
||||
var b = try Managed.initSet(testing.allocator, v);
|
||||
defer b.deinit();
|
||||
|
||||
var q = try Managed.init(testing.allocator);
|
||||
defer q.deinit();
|
||||
var r = try Managed.init(testing.allocator);
|
||||
defer r.deinit();
|
||||
try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
|
||||
|
||||
try testing.expect((try q.to(i32)) == 0);
|
||||
try testing.expect((try r.to(i32)) == 10);
|
||||
}
|
||||
|
||||
test "big.int div multi-multi with rem" {
|
||||
var a = try Managed.initSet(testing.allocator, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999);
|
||||
defer a.deinit();
|
||||
|
||||
@@ -74,6 +74,11 @@ comptime {
|
||||
@export(__getf2, .{ .name = "__gttf2", .linkage = linkage });
|
||||
|
||||
@export(__extendhfsf2, .{ .name = "__gnu_h2f_ieee", .linkage = linkage });
|
||||
|
||||
const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4;
|
||||
@export(__muloti4, .{ .name = "__muloti4", .linkage = linkage });
|
||||
const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4;
|
||||
@export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage });
|
||||
}
|
||||
|
||||
if (!builtin.zig_is_stage2) {
|
||||
@@ -621,10 +626,6 @@ comptime {
|
||||
const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3;
|
||||
@export(__umodti3, .{ .name = "__umodti3", .linkage = linkage });
|
||||
}
|
||||
const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4;
|
||||
@export(__muloti4, .{ .name = "__muloti4", .linkage = linkage });
|
||||
const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4;
|
||||
@export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage });
|
||||
|
||||
_ = @import("compiler_rt/atomics.zig");
|
||||
|
||||
|
||||
25
src/Air.zig
25
src/Air.zig
@@ -80,11 +80,27 @@ pub const Inst = struct {
|
||||
/// is the same as both operands.
|
||||
/// Uses the `bin_op` field.
|
||||
mul_sat,
|
||||
/// Integer or float division. For integers, wrapping is undefined behavior.
|
||||
/// Float division.
|
||||
/// Both operands are guaranteed to be the same type, and the result type
|
||||
/// is the same as both operands.
|
||||
/// Uses the `bin_op` field.
|
||||
div,
|
||||
div_float,
|
||||
/// Truncating integer or float division. For integers, wrapping is undefined behavior.
|
||||
/// Both operands are guaranteed to be the same type, and the result type
|
||||
/// is the same as both operands.
|
||||
/// Uses the `bin_op` field.
|
||||
div_trunc,
|
||||
/// Flooring integer or float division. For integers, wrapping is undefined behavior.
|
||||
/// Both operands are guaranteed to be the same type, and the result type
|
||||
/// is the same as both operands.
|
||||
/// Uses the `bin_op` field.
|
||||
div_floor,
|
||||
/// Integer or float division. Guaranteed no remainder.
|
||||
/// For integers, wrapping is undefined behavior.
|
||||
/// Both operands are guaranteed to be the same type, and the result type
|
||||
/// is the same as both operands.
|
||||
/// Uses the `bin_op` field.
|
||||
div_exact,
|
||||
/// Integer or float remainder division.
|
||||
/// Both operands are guaranteed to be the same type, and the result type
|
||||
/// is the same as both operands.
|
||||
@@ -644,7 +660,10 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
.mul,
|
||||
.mulwrap,
|
||||
.mul_sat,
|
||||
.div,
|
||||
.div_float,
|
||||
.div_trunc,
|
||||
.div_floor,
|
||||
.div_exact,
|
||||
.rem,
|
||||
.mod,
|
||||
.bit_and,
|
||||
|
||||
@@ -233,7 +233,10 @@ fn analyzeInst(
|
||||
.mul,
|
||||
.mulwrap,
|
||||
.mul_sat,
|
||||
.div,
|
||||
.div_float,
|
||||
.div_trunc,
|
||||
.div_floor,
|
||||
.div_exact,
|
||||
.rem,
|
||||
.mod,
|
||||
.ptr_add,
|
||||
|
||||
253
src/Sema.zig
253
src/Sema.zig
@@ -641,9 +641,6 @@ pub fn analyzeBody(
|
||||
.pop_count => try sema.zirPopCount(block, inst),
|
||||
.byte_swap => try sema.zirByteSwap(block, inst),
|
||||
.bit_reverse => try sema.zirBitReverse(block, inst),
|
||||
.div_exact => try sema.zirDivExact(block, inst),
|
||||
.div_floor => try sema.zirDivFloor(block, inst),
|
||||
.div_trunc => try sema.zirDivTrunc(block, inst),
|
||||
.shr_exact => try sema.zirShrExact(block, inst),
|
||||
.bit_offset_of => try sema.zirBitOffsetOf(block, inst),
|
||||
.offset_of => try sema.zirOffsetOf(block, inst),
|
||||
@@ -683,19 +680,22 @@ pub fn analyzeBody(
|
||||
.error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon),
|
||||
.error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func),
|
||||
|
||||
.add => try sema.zirArithmetic(block, inst, .add),
|
||||
.addwrap => try sema.zirArithmetic(block, inst, .addwrap),
|
||||
.add_sat => try sema.zirArithmetic(block, inst, .add_sat),
|
||||
.div => try sema.zirArithmetic(block, inst, .div),
|
||||
.mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),
|
||||
.mod => try sema.zirArithmetic(block, inst, .mod),
|
||||
.rem => try sema.zirArithmetic(block, inst, .rem),
|
||||
.mul => try sema.zirArithmetic(block, inst, .mul),
|
||||
.mulwrap => try sema.zirArithmetic(block, inst, .mulwrap),
|
||||
.mul_sat => try sema.zirArithmetic(block, inst, .mul_sat),
|
||||
.sub => try sema.zirArithmetic(block, inst, .sub),
|
||||
.subwrap => try sema.zirArithmetic(block, inst, .subwrap),
|
||||
.sub_sat => try sema.zirArithmetic(block, inst, .sub_sat),
|
||||
.add => try sema.zirArithmetic(block, inst, .add),
|
||||
.addwrap => try sema.zirArithmetic(block, inst, .addwrap),
|
||||
.add_sat => try sema.zirArithmetic(block, inst, .add_sat),
|
||||
.div => try sema.zirArithmetic(block, inst, .div),
|
||||
.div_exact => try sema.zirArithmetic(block, inst, .div_exact),
|
||||
.div_floor => try sema.zirArithmetic(block, inst, .div_floor),
|
||||
.div_trunc => try sema.zirArithmetic(block, inst, .div_trunc),
|
||||
.mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),
|
||||
.mod => try sema.zirArithmetic(block, inst, .mod),
|
||||
.rem => try sema.zirArithmetic(block, inst, .rem),
|
||||
.mul => try sema.zirArithmetic(block, inst, .mul),
|
||||
.mulwrap => try sema.zirArithmetic(block, inst, .mulwrap),
|
||||
.mul_sat => try sema.zirArithmetic(block, inst, .mul_sat),
|
||||
.sub => try sema.zirArithmetic(block, inst, .sub),
|
||||
.subwrap => try sema.zirArithmetic(block, inst, .subwrap),
|
||||
.sub_sat => try sema.zirArithmetic(block, inst, .sub_sat),
|
||||
|
||||
.maximum => try sema.zirMinMax(block, inst, .max),
|
||||
.minimum => try sema.zirMinMax(block, inst, .min),
|
||||
@@ -7350,6 +7350,9 @@ fn analyzeArithmetic(
|
||||
} else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };
|
||||
},
|
||||
.div => {
|
||||
// TODO: emit compile error when .div is used on integers and there would be an
|
||||
// ambiguous result between div_floor and div_trunc.
|
||||
|
||||
// For integers:
|
||||
// If the lhs is zero, then zero is returned regardless of rhs.
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
@@ -7359,9 +7362,11 @@ fn analyzeArithmetic(
|
||||
// * if lhs type is signed:
|
||||
// * if rhs is comptime-known and not -1, result is undefined
|
||||
// * if rhs is -1 or runtime-known, compile error because there is a
|
||||
// possible value (-min_int * -1) for which division would be
|
||||
// possible value (-min_int / -1) for which division would be
|
||||
// illegal behavior.
|
||||
// * if lhs type is unsigned, undef is returned regardless of rhs.
|
||||
// TODO: emit runtime safety for division by zero
|
||||
//
|
||||
// For floats:
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
@@ -7407,8 +7412,198 @@ fn analyzeArithmetic(
|
||||
try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena),
|
||||
);
|
||||
}
|
||||
} else break :rs .{ .src = rhs_src, .air_tag = .div };
|
||||
} else break :rs .{ .src = lhs_src, .air_tag = .div };
|
||||
} else {
|
||||
if (is_int) {
|
||||
break :rs .{ .src = rhs_src, .air_tag = .div_trunc };
|
||||
} else {
|
||||
break :rs .{ .src = rhs_src, .air_tag = .div_float };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_int) {
|
||||
break :rs .{ .src = lhs_src, .air_tag = .div_trunc };
|
||||
} else {
|
||||
break :rs .{ .src = lhs_src, .air_tag = .div_float };
|
||||
}
|
||||
}
|
||||
},
|
||||
.div_trunc => {
|
||||
// For integers:
|
||||
// If the lhs is zero, then zero is returned regardless of rhs.
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined:
|
||||
// * if lhs type is signed:
|
||||
// * if rhs is comptime-known and not -1, result is undefined
|
||||
// * if rhs is -1 or runtime-known, compile error because there is a
|
||||
// possible value (-min_int / -1) for which division would be
|
||||
// illegal behavior.
|
||||
// * if lhs type is unsigned, undef is returned regardless of rhs.
|
||||
// TODO: emit runtime safety for division by zero
|
||||
//
|
||||
// For floats:
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined, result is undefined.
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (!lhs_val.isUndef()) {
|
||||
if (lhs_val.compareWithZero(.eq)) {
|
||||
return sema.addConstant(scalar_type, Value.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (rhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
if (rhs_val.compareWithZero(.eq)) {
|
||||
return sema.failWithDivideByZero(block, rhs_src);
|
||||
}
|
||||
}
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (lhs_val.isUndef()) {
|
||||
if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) {
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) {
|
||||
return sema.addConstUndef(scalar_type);
|
||||
}
|
||||
}
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
return sema.addConstUndef(scalar_type);
|
||||
}
|
||||
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (is_int) {
|
||||
return sema.addConstant(
|
||||
scalar_type,
|
||||
try lhs_val.intDiv(rhs_val, sema.arena),
|
||||
);
|
||||
} else {
|
||||
return sema.addConstant(
|
||||
scalar_type,
|
||||
try lhs_val.floatDivTrunc(rhs_val, scalar_type, sema.arena),
|
||||
);
|
||||
}
|
||||
} else break :rs .{ .src = rhs_src, .air_tag = .div_trunc };
|
||||
} else break :rs .{ .src = lhs_src, .air_tag = .div_trunc };
|
||||
},
|
||||
.div_floor => {
|
||||
// For integers:
|
||||
// If the lhs is zero, then zero is returned regardless of rhs.
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined:
|
||||
// * if lhs type is signed:
|
||||
// * if rhs is comptime-known and not -1, result is undefined
|
||||
// * if rhs is -1 or runtime-known, compile error because there is a
|
||||
// possible value (-min_int / -1) for which division would be
|
||||
// illegal behavior.
|
||||
// * if lhs type is unsigned, undef is returned regardless of rhs.
|
||||
// TODO: emit runtime safety for division by zero
|
||||
//
|
||||
// For floats:
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined, result is undefined.
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (!lhs_val.isUndef()) {
|
||||
if (lhs_val.compareWithZero(.eq)) {
|
||||
return sema.addConstant(scalar_type, Value.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (rhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
if (rhs_val.compareWithZero(.eq)) {
|
||||
return sema.failWithDivideByZero(block, rhs_src);
|
||||
}
|
||||
}
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (lhs_val.isUndef()) {
|
||||
if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) {
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) {
|
||||
return sema.addConstUndef(scalar_type);
|
||||
}
|
||||
}
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
return sema.addConstUndef(scalar_type);
|
||||
}
|
||||
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (is_int) {
|
||||
return sema.addConstant(
|
||||
scalar_type,
|
||||
try lhs_val.intDivFloor(rhs_val, sema.arena),
|
||||
);
|
||||
} else {
|
||||
return sema.addConstant(
|
||||
scalar_type,
|
||||
try lhs_val.floatDivFloor(rhs_val, scalar_type, sema.arena),
|
||||
);
|
||||
}
|
||||
} else break :rs .{ .src = rhs_src, .air_tag = .div_floor };
|
||||
} else break :rs .{ .src = lhs_src, .air_tag = .div_floor };
|
||||
},
|
||||
.div_exact => {
|
||||
// For integers:
|
||||
// If the lhs is zero, then zero is returned regardless of rhs.
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined, compile error because there is a possible
|
||||
// value for which the division would result in a remainder.
|
||||
// TODO: emit runtime safety for if there is a remainder
|
||||
// TODO: emit runtime safety for division by zero
|
||||
//
|
||||
// For floats:
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined, compile error because there is a possible
|
||||
// value for which the division would result in a remainder.
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (lhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
} else {
|
||||
if (lhs_val.compareWithZero(.eq)) {
|
||||
return sema.addConstant(scalar_type, Value.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (rhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
if (rhs_val.compareWithZero(.eq)) {
|
||||
return sema.failWithDivideByZero(block, rhs_src);
|
||||
}
|
||||
}
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (is_int) {
|
||||
// TODO: emit compile error if there is a remainder
|
||||
return sema.addConstant(
|
||||
scalar_type,
|
||||
try lhs_val.intDiv(rhs_val, sema.arena),
|
||||
);
|
||||
} else {
|
||||
// TODO: emit compile error if there is a remainder
|
||||
return sema.addConstant(
|
||||
scalar_type,
|
||||
try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena),
|
||||
);
|
||||
}
|
||||
} else break :rs .{ .src = rhs_src, .air_tag = .div_exact };
|
||||
} else break :rs .{ .src = lhs_src, .air_tag = .div_exact };
|
||||
},
|
||||
.mul => {
|
||||
// For integers:
|
||||
@@ -8824,7 +9019,7 @@ fn analyzeRet(
|
||||
fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
|
||||
// extend this swich as additional operators are implemented
|
||||
return switch (tag) {
|
||||
.add, .sub, .mul, .div, .mod, .rem, .mod_rem => true,
|
||||
.add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
@@ -9602,24 +9797,6 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
|
||||
return sema.fail(block, src, "TODO: Sema.zirBitReverse", .{});
|
||||
}
|
||||
|
||||
fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
return sema.fail(block, src, "TODO: Sema.zirDivExact", .{});
|
||||
}
|
||||
|
||||
fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
return sema.fail(block, src, "TODO: Sema.zirDivFloor", .{});
|
||||
}
|
||||
|
||||
fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
return sema.fail(block, src, "TODO: Sema.zirDivTrunc", .{});
|
||||
}
|
||||
|
||||
fn zirShrExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
|
||||
@@ -410,7 +410,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.mul => try self.airMul(inst),
|
||||
.mulwrap => try self.airMulWrap(inst),
|
||||
.mul_sat => try self.airMulSat(inst),
|
||||
.div => try self.airDiv(inst),
|
||||
.rem => try self.airRem(inst),
|
||||
.mod => try self.airMod(inst),
|
||||
.shl, .shl_exact => try self.airShl(inst),
|
||||
@@ -419,6 +418,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.max => try self.airMax(inst),
|
||||
.slice => try self.airSlice(inst),
|
||||
|
||||
.div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
|
||||
|
||||
.cmp_lt => try self.airCmp(inst, .lt),
|
||||
.cmp_lte => try self.airCmp(inst, .lte),
|
||||
.cmp_eq => try self.airCmp(inst, .eq),
|
||||
|
||||
@@ -758,7 +758,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.mul => try self.airMul(inst),
|
||||
.mulwrap => try self.airMulWrap(inst),
|
||||
.mul_sat => try self.airMulSat(inst),
|
||||
.div => try self.airDiv(inst),
|
||||
.rem => try self.airRem(inst),
|
||||
.mod => try self.airMod(inst),
|
||||
.shl, .shl_exact => try self.airShl(inst),
|
||||
@@ -767,6 +766,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.max => try self.airMax(inst),
|
||||
.slice => try self.airSlice(inst),
|
||||
|
||||
.div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
|
||||
|
||||
.cmp_lt => try self.airCmp(inst, .lt),
|
||||
.cmp_lte => try self.airCmp(inst, .lte),
|
||||
.cmp_eq => try self.airCmp(inst, .eq),
|
||||
|
||||
@@ -976,7 +976,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
|
||||
.mul => try airBinOp (f, inst, " * "),
|
||||
// TODO use a different strategy for div that communicates to the optimizer
|
||||
// that wrapping is UB.
|
||||
.div => try airBinOp( f, inst, " / "),
|
||||
.div_float, .div_exact, .div_trunc => try airBinOp( f, inst, " / "),
|
||||
.div_floor => try airBinOp( f, inst, " divfloor "),
|
||||
.rem => try airBinOp( f, inst, " % "),
|
||||
.mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division
|
||||
|
||||
|
||||
@@ -1667,7 +1667,10 @@ pub const FuncGen = struct {
|
||||
.mul => try self.airMul(inst),
|
||||
.mulwrap => try self.airMulWrap(inst),
|
||||
.mul_sat => try self.airMulSat(inst),
|
||||
.div => try self.airDiv(inst),
|
||||
.div_float => try self.airDivFloat(inst),
|
||||
.div_trunc => try self.airDivTrunc(inst),
|
||||
.div_floor => try self.airDivFloor(inst),
|
||||
.div_exact => try self.airDivExact(inst),
|
||||
.rem => try self.airRem(inst),
|
||||
.mod => try self.airMod(inst),
|
||||
.ptr_add => try self.airPtrAdd(inst),
|
||||
@@ -2830,9 +2833,70 @@ pub const FuncGen = struct {
|
||||
return self.builder.buildUMulFixSat(lhs, rhs, "");
|
||||
}
|
||||
|
||||
fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
return null;
|
||||
fn airDivFloat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst)) return null;
|
||||
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const lhs = try self.resolveInst(bin_op.lhs);
|
||||
const rhs = try self.resolveInst(bin_op.rhs);
|
||||
|
||||
return self.builder.buildFDiv(lhs, rhs, "");
|
||||
}
|
||||
|
||||
fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst)) return null;
|
||||
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const lhs = try self.resolveInst(bin_op.lhs);
|
||||
const rhs = try self.resolveInst(bin_op.rhs);
|
||||
const inst_ty = self.air.typeOfIndex(inst);
|
||||
|
||||
if (inst_ty.isRuntimeFloat()) {
|
||||
const result_llvm_ty = try self.dg.llvmType(inst_ty);
|
||||
const zero = result_llvm_ty.constNull();
|
||||
const result = self.builder.buildFDiv(lhs, rhs, "");
|
||||
const ceiled = try self.callCeil(result, inst_ty);
|
||||
const floored = try self.callFloor(result, inst_ty);
|
||||
const ltz = self.builder.buildFCmp(.OLT, lhs, zero, "");
|
||||
return self.builder.buildSelect(ltz, ceiled, floored, "");
|
||||
}
|
||||
if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");
|
||||
return self.builder.buildUDiv(lhs, rhs, "");
|
||||
}
|
||||
|
||||
fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst)) return null;
|
||||
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const lhs = try self.resolveInst(bin_op.lhs);
|
||||
const rhs = try self.resolveInst(bin_op.rhs);
|
||||
const inst_ty = self.air.typeOfIndex(inst);
|
||||
|
||||
if (inst_ty.isRuntimeFloat()) {
|
||||
const result = self.builder.buildFDiv(lhs, rhs, "");
|
||||
return try self.callFloor(result, inst_ty);
|
||||
}
|
||||
if (inst_ty.isSignedInt()) {
|
||||
// const d = @divTrunc(a, b);
|
||||
// const r = @rem(a, b);
|
||||
// return if (r == 0) d else d - ((a < 0) ^ (b < 0));
|
||||
const result_llvm_ty = try self.dg.llvmType(inst_ty);
|
||||
const zero = result_llvm_ty.constNull();
|
||||
const div_trunc = self.builder.buildSDiv(lhs, rhs, "");
|
||||
const rem = self.builder.buildSRem(lhs, rhs, "");
|
||||
const rem_eq_0 = self.builder.buildICmp(.EQ, rem, zero, "");
|
||||
const a_lt_0 = self.builder.buildICmp(.SLT, lhs, zero, "");
|
||||
const b_lt_0 = self.builder.buildICmp(.SLT, rhs, zero, "");
|
||||
const a_b_xor = self.builder.buildXor(a_lt_0, b_lt_0, "");
|
||||
const a_b_xor_ext = self.builder.buildZExt(a_b_xor, div_trunc.typeOf(), "");
|
||||
const d_sub_xor = self.builder.buildSub(div_trunc, a_b_xor_ext, "");
|
||||
return self.builder.buildSelect(rem_eq_0, div_trunc, d_sub_xor, "");
|
||||
}
|
||||
return self.builder.buildUDiv(lhs, rhs, "");
|
||||
}
|
||||
|
||||
fn airDivExact(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst)) return null;
|
||||
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const lhs = try self.resolveInst(bin_op.lhs);
|
||||
@@ -2840,8 +2904,8 @@ pub const FuncGen = struct {
|
||||
const inst_ty = self.air.typeOfIndex(inst);
|
||||
|
||||
if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, "");
|
||||
if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");
|
||||
return self.builder.buildUDiv(lhs, rhs, "");
|
||||
if (inst_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, "");
|
||||
return self.builder.buildExactUDiv(lhs, rhs, "");
|
||||
}
|
||||
|
||||
fn airRem(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
@@ -3546,6 +3610,33 @@ pub const FuncGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn callFloor(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value {
|
||||
return self.callFloatUnary(arg, ty, "floor");
|
||||
}
|
||||
|
||||
fn callCeil(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value {
|
||||
return self.callFloatUnary(arg, ty, "ceil");
|
||||
}
|
||||
|
||||
fn callFloatUnary(self: *FuncGen, arg: *const llvm.Value, ty: Type, name: []const u8) !*const llvm.Value {
|
||||
const target = self.dg.module.getTarget();
|
||||
|
||||
var fn_name_buf: [100]u8 = undefined;
|
||||
const llvm_fn_name = std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.f{d}", .{
|
||||
name, ty.floatBits(target),
|
||||
}) catch unreachable;
|
||||
|
||||
const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
|
||||
const operand_llvm_ty = try self.dg.llvmType(ty);
|
||||
const param_types = [_]*const llvm.Type{operand_llvm_ty};
|
||||
const fn_type = llvm.functionType(operand_llvm_ty, ¶m_types, param_types.len, .False);
|
||||
break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
|
||||
};
|
||||
|
||||
const args: [1]*const llvm.Value = .{arg};
|
||||
return self.builder.buildCall(llvm_fn, &args, args.len, .C, .Auto, "");
|
||||
}
|
||||
|
||||
fn fieldPtr(
|
||||
self: *FuncGen,
|
||||
inst: Air.Inst.Index,
|
||||
|
||||
@@ -756,6 +756,12 @@ pub const Builder = opaque {
|
||||
|
||||
pub const buildSMin = ZigLLVMBuildSMin;
|
||||
extern fn ZigLLVMBuildSMin(builder: *const Builder, LHS: *const Value, RHS: *const Value, name: [*:0]const u8) *const Value;
|
||||
|
||||
pub const buildExactUDiv = LLVMBuildExactUDiv;
|
||||
extern fn LLVMBuildExactUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
|
||||
|
||||
pub const buildExactSDiv = LLVMBuildExactSDiv;
|
||||
extern fn LLVMBuildExactSDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
|
||||
};
|
||||
|
||||
pub const IntPredicate = enum(c_uint) {
|
||||
|
||||
@@ -669,7 +669,6 @@ pub const DeclGen = struct {
|
||||
.add, .addwrap => try self.airArithOp(inst, .{.OpFAdd, .OpIAdd, .OpIAdd}),
|
||||
.sub, .subwrap => try self.airArithOp(inst, .{.OpFSub, .OpISub, .OpISub}),
|
||||
.mul, .mulwrap => try self.airArithOp(inst, .{.OpFMul, .OpIMul, .OpIMul}),
|
||||
.div => try self.airArithOp(inst, .{.OpFDiv, .OpSDiv, .OpUDiv}),
|
||||
|
||||
.bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd),
|
||||
.bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr),
|
||||
|
||||
@@ -822,7 +822,7 @@ pub const Context = struct {
|
||||
.subwrap => self.airWrapBinOp(inst, .sub),
|
||||
.mul => self.airBinOp(inst, .mul),
|
||||
.mulwrap => self.airWrapBinOp(inst, .mul),
|
||||
.div => self.airBinOp(inst, .div),
|
||||
.div_trunc => self.airBinOp(inst, .div),
|
||||
.bit_and => self.airBinOp(inst, .@"and"),
|
||||
.bit_or => self.airBinOp(inst, .@"or"),
|
||||
.bool_and => self.airBinOp(inst, .@"and"),
|
||||
|
||||
@@ -111,7 +111,10 @@ const Writer = struct {
|
||||
.mul,
|
||||
.mulwrap,
|
||||
.mul_sat,
|
||||
.div,
|
||||
.div_float,
|
||||
.div_trunc,
|
||||
.div_floor,
|
||||
.div_exact,
|
||||
.rem,
|
||||
.mod,
|
||||
.ptr_add,
|
||||
|
||||
103
src/value.zig
103
src/value.zig
@@ -2200,7 +2200,8 @@ pub const Value = extern union {
|
||||
const rhs_bigint = rhs.toBigInt(&rhs_space);
|
||||
const limbs = try arena.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
|
||||
// + 1 for negatives
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
result_bigint.bitAnd(lhs_bigint, rhs_bigint);
|
||||
@@ -2264,7 +2265,8 @@ pub const Value = extern union {
|
||||
const rhs_bigint = rhs.toBigInt(&rhs_space);
|
||||
const limbs = try arena.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
|
||||
// + 1 for negatives
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
result_bigint.bitXor(lhs_bigint, rhs_bigint);
|
||||
@@ -2352,7 +2354,7 @@ pub const Value = extern union {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
|
||||
pub fn intDivFloor(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
|
||||
// TODO is this a performance issue? maybe we should try the operation without
|
||||
// resorting to BigInt first.
|
||||
var lhs_space: Value.BigIntSpace = undefined;
|
||||
@@ -2373,6 +2375,39 @@ pub const Value = extern union {
|
||||
);
|
||||
var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
|
||||
var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
|
||||
result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);
|
||||
const result_limbs = result_q.limbs[0..result_q.len];
|
||||
|
||||
if (result_q.positive) {
|
||||
return Value.Tag.int_big_positive.create(allocator, result_limbs);
|
||||
} else {
|
||||
return Value.Tag.int_big_negative.create(allocator, result_limbs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
|
||||
// TODO is this a performance issue? maybe we should try the operation without
|
||||
// resorting to BigInt first.
|
||||
var lhs_space: Value.BigIntSpace = undefined;
|
||||
var rhs_space: Value.BigIntSpace = undefined;
|
||||
const lhs_bigint = lhs.toBigInt(&lhs_space);
|
||||
const rhs_bigint = rhs.toBigInt(&rhs_space);
|
||||
const limbs_q = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
|
||||
);
|
||||
const limbs_r = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
// TODO: audit this size, and also consider reworking Sema to re-use Values rather than
|
||||
// always producing new Value objects.
|
||||
rhs_bigint.limbs.len + 1,
|
||||
);
|
||||
const limbs_buffer = try allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
|
||||
);
|
||||
var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
|
||||
var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
|
||||
result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);
|
||||
const result_limbs = result_r.limbs[0..result_r.len];
|
||||
|
||||
@@ -2662,6 +2697,68 @@ pub const Value = extern union {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floatDivFloor(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
float_type: Type,
|
||||
arena: *Allocator,
|
||||
) !Value {
|
||||
switch (float_type.tag()) {
|
||||
.f16 => {
|
||||
const lhs_val = lhs.toFloat(f16);
|
||||
const rhs_val = rhs.toFloat(f16);
|
||||
return Value.Tag.float_16.create(arena, @divFloor(lhs_val, rhs_val));
|
||||
},
|
||||
.f32 => {
|
||||
const lhs_val = lhs.toFloat(f32);
|
||||
const rhs_val = rhs.toFloat(f32);
|
||||
return Value.Tag.float_32.create(arena, @divFloor(lhs_val, rhs_val));
|
||||
},
|
||||
.f64 => {
|
||||
const lhs_val = lhs.toFloat(f64);
|
||||
const rhs_val = rhs.toFloat(f64);
|
||||
return Value.Tag.float_64.create(arena, @divFloor(lhs_val, rhs_val));
|
||||
},
|
||||
.f128, .comptime_float, .c_longdouble => {
|
||||
const lhs_val = lhs.toFloat(f128);
|
||||
const rhs_val = rhs.toFloat(f128);
|
||||
return Value.Tag.float_128.create(arena, @divFloor(lhs_val, rhs_val));
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floatDivTrunc(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
float_type: Type,
|
||||
arena: *Allocator,
|
||||
) !Value {
|
||||
switch (float_type.tag()) {
|
||||
.f16 => {
|
||||
const lhs_val = lhs.toFloat(f16);
|
||||
const rhs_val = rhs.toFloat(f16);
|
||||
return Value.Tag.float_16.create(arena, @divTrunc(lhs_val, rhs_val));
|
||||
},
|
||||
.f32 => {
|
||||
const lhs_val = lhs.toFloat(f32);
|
||||
const rhs_val = rhs.toFloat(f32);
|
||||
return Value.Tag.float_32.create(arena, @divTrunc(lhs_val, rhs_val));
|
||||
},
|
||||
.f64 => {
|
||||
const lhs_val = lhs.toFloat(f64);
|
||||
const rhs_val = rhs.toFloat(f64);
|
||||
return Value.Tag.float_64.create(arena, @divTrunc(lhs_val, rhs_val));
|
||||
},
|
||||
.f128, .comptime_float, .c_longdouble => {
|
||||
const lhs_val = lhs.toFloat(f128);
|
||||
const rhs_val = rhs.toFloat(f128);
|
||||
return Value.Tag.float_128.create(arena, @divTrunc(lhs_val, rhs_val));
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floatMul(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
|
||||
@@ -33,6 +33,7 @@ test {
|
||||
_ = @import("behavior/error.zig");
|
||||
_ = @import("behavior/eval.zig");
|
||||
_ = @import("behavior/floatop.zig");
|
||||
_ = @import("behavior/fn.zig");
|
||||
_ = @import("behavior/for.zig");
|
||||
_ = @import("behavior/generics.zig");
|
||||
_ = @import("behavior/hasdecl.zig");
|
||||
@@ -126,7 +127,7 @@ test {
|
||||
_ = @import("behavior/eval_stage1.zig");
|
||||
_ = @import("behavior/field_parent_ptr.zig");
|
||||
_ = @import("behavior/floatop_stage1.zig");
|
||||
_ = @import("behavior/fn.zig");
|
||||
_ = @import("behavior/fn_stage1.zig");
|
||||
_ = @import("behavior/fn_delegation.zig");
|
||||
_ = @import("behavior/fn_in_struct_in_comptime.zig");
|
||||
_ = @import("behavior/for_stage1.zig");
|
||||
@@ -150,7 +151,7 @@ test {
|
||||
_ = @import("behavior/reflection.zig");
|
||||
{
|
||||
// Checklist for getting saturating_arithmetic.zig passing for stage2:
|
||||
// * add __muloti4 to compiler-rt
|
||||
// * add __udivti3 to compiler-rt
|
||||
_ = @import("behavior/saturating_arithmetic.zig");
|
||||
}
|
||||
_ = @import("behavior/select.zig");
|
||||
|
||||
@@ -112,3 +112,55 @@ test "void arrays" {
|
||||
try expect(@sizeOf(@TypeOf(array)) == 0);
|
||||
try expect(array.len == 4);
|
||||
}
|
||||
|
||||
test "nested arrays" {
|
||||
const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
|
||||
for (array_of_strings) |s, i| {
|
||||
if (i == 0) try expect(mem.eql(u8, s, "hello"));
|
||||
if (i == 1) try expect(mem.eql(u8, s, "this"));
|
||||
if (i == 2) try expect(mem.eql(u8, s, "is"));
|
||||
if (i == 3) try expect(mem.eql(u8, s, "my"));
|
||||
if (i == 4) try expect(mem.eql(u8, s, "thing"));
|
||||
}
|
||||
}
|
||||
|
||||
var s_array: [8]Sub = undefined;
|
||||
const Sub = struct { b: u8 };
|
||||
const Str = struct { a: []Sub };
|
||||
test "set global var array via slice embedded in struct" {
|
||||
var s = Str{ .a = s_array[0..] };
|
||||
|
||||
s.a[0].b = 1;
|
||||
s.a[1].b = 2;
|
||||
s.a[2].b = 3;
|
||||
|
||||
try expect(s_array[0].b == 1);
|
||||
try expect(s_array[1].b == 2);
|
||||
try expect(s_array[2].b == 3);
|
||||
}
|
||||
|
||||
test "implicit comptime in array type size" {
|
||||
var arr: [plusOne(10)]bool = undefined;
|
||||
try expect(arr.len == 11);
|
||||
}
|
||||
|
||||
fn plusOne(x: u32) u32 {
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
test "read/write through global variable array of struct fields initialized via array mult" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try expect(storage[0].term == 1);
|
||||
storage[0] = MyStruct{ .term = 123 };
|
||||
try expect(storage[0].term == 123);
|
||||
}
|
||||
|
||||
pub const MyStruct = struct {
|
||||
term: usize,
|
||||
};
|
||||
|
||||
var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1;
|
||||
};
|
||||
try S.doTheTest();
|
||||
}
|
||||
|
||||
@@ -4,32 +4,6 @@ const mem = std.mem;
|
||||
const expect = testing.expect;
|
||||
const expectEqual = testing.expectEqual;
|
||||
|
||||
test "nested arrays" {
|
||||
const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
|
||||
for (array_of_strings) |s, i| {
|
||||
if (i == 0) try expect(mem.eql(u8, s, "hello"));
|
||||
if (i == 1) try expect(mem.eql(u8, s, "this"));
|
||||
if (i == 2) try expect(mem.eql(u8, s, "is"));
|
||||
if (i == 3) try expect(mem.eql(u8, s, "my"));
|
||||
if (i == 4) try expect(mem.eql(u8, s, "thing"));
|
||||
}
|
||||
}
|
||||
|
||||
var s_array: [8]Sub = undefined;
|
||||
const Sub = struct { b: u8 };
|
||||
const Str = struct { a: []Sub };
|
||||
test "set global var array via slice embedded in struct" {
|
||||
var s = Str{ .a = s_array[0..] };
|
||||
|
||||
s.a[0].b = 1;
|
||||
s.a[1].b = 2;
|
||||
s.a[2].b = 3;
|
||||
|
||||
try expect(s_array[0].b == 1);
|
||||
try expect(s_array[1].b == 2);
|
||||
try expect(s_array[2].b == 3);
|
||||
}
|
||||
|
||||
test "single-item pointer to array indexing and slicing" {
|
||||
try testSingleItemPtrArrayIndexSlice();
|
||||
comptime try testSingleItemPtrArrayIndexSlice();
|
||||
@@ -75,15 +49,6 @@ test "comptime evaluating function that takes array by value" {
|
||||
_ = comptime testArrayByValAtComptime(arr);
|
||||
}
|
||||
|
||||
test "implicit comptime in array type size" {
|
||||
var arr: [plusOne(10)]bool = undefined;
|
||||
try expect(arr.len == 11);
|
||||
}
|
||||
|
||||
fn plusOne(x: u32) u32 {
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
test "runtime initialize array elem and then implicit cast to slice" {
|
||||
var two: i32 = 2;
|
||||
const x: []const i32 = &[_]i32{two};
|
||||
@@ -171,23 +136,6 @@ test "double nested array to const slice cast in array literal" {
|
||||
comptime try S.entry(2);
|
||||
}
|
||||
|
||||
test "read/write through global variable array of struct fields initialized via array mult" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try expect(storage[0].term == 1);
|
||||
storage[0] = MyStruct{ .term = 123 };
|
||||
try expect(storage[0].term == 123);
|
||||
}
|
||||
|
||||
pub const MyStruct = struct {
|
||||
term: usize,
|
||||
};
|
||||
|
||||
var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1;
|
||||
};
|
||||
try S.doTheTest();
|
||||
}
|
||||
|
||||
test "implicit cast zero sized array ptr to slice" {
|
||||
{
|
||||
var b = "".*;
|
||||
|
||||
@@ -402,3 +402,52 @@ test "f64 at compile time is lossy" {
|
||||
test {
|
||||
comptime try expect(@as(f128, 1 << 113) == 10384593717069655257060992658440192);
|
||||
}
|
||||
|
||||
fn copyWithPartialInline(s: []u32, b: []u8) void {
|
||||
comptime var i: usize = 0;
|
||||
inline while (i < 4) : (i += 1) {
|
||||
s[i] = 0;
|
||||
s[i] |= @as(u32, b[i * 4 + 0]) << 24;
|
||||
s[i] |= @as(u32, b[i * 4 + 1]) << 16;
|
||||
s[i] |= @as(u32, b[i * 4 + 2]) << 8;
|
||||
s[i] |= @as(u32, b[i * 4 + 3]) << 0;
|
||||
}
|
||||
}
|
||||
|
||||
test "binary math operator in partially inlined function" {
|
||||
var s: [4]u32 = undefined;
|
||||
var b: [16]u8 = undefined;
|
||||
|
||||
for (b) |*r, i|
|
||||
r.* = @intCast(u8, i + 1);
|
||||
|
||||
copyWithPartialInline(s[0..], b[0..]);
|
||||
try expect(s[0] == 0x1020304);
|
||||
try expect(s[1] == 0x5060708);
|
||||
try expect(s[2] == 0x90a0b0c);
|
||||
try expect(s[3] == 0xd0e0f10);
|
||||
}
|
||||
|
||||
test "comptime shl" {
|
||||
var a: u128 = 3;
|
||||
var b: u7 = 63;
|
||||
var c: u128 = 3 << 63;
|
||||
try expect((a << b) == c);
|
||||
}
|
||||
|
||||
test "comptime bitwise operators" {
|
||||
comptime {
|
||||
try expect(3 & 1 == 1);
|
||||
try expect(3 & -1 == 3);
|
||||
try expect(-3 & -1 == -3);
|
||||
try expect(3 | -1 == -1);
|
||||
try expect(-3 | -1 == -1);
|
||||
try expect(3 ^ -1 == -4);
|
||||
try expect(-3 ^ -1 == 2);
|
||||
try expect(~@as(i8, -1) == 0);
|
||||
try expect(~@as(i128, -1) == 0);
|
||||
try expect(18446744073709551615 & 18446744073709551611 == 18446744073709551611);
|
||||
try expect(-18446744073709551615 & -18446744073709551611 == -18446744073709551615);
|
||||
try expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,31 +132,6 @@ test "string literal used as comptime slice is memoized" {
|
||||
comptime try expect(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node);
|
||||
}
|
||||
|
||||
fn copyWithPartialInline(s: []u32, b: []u8) void {
|
||||
comptime var i: usize = 0;
|
||||
inline while (i < 4) : (i += 1) {
|
||||
s[i] = 0;
|
||||
s[i] |= @as(u32, b[i * 4 + 0]) << 24;
|
||||
s[i] |= @as(u32, b[i * 4 + 1]) << 16;
|
||||
s[i] |= @as(u32, b[i * 4 + 2]) << 8;
|
||||
s[i] |= @as(u32, b[i * 4 + 3]) << 0;
|
||||
}
|
||||
}
|
||||
|
||||
test "binary math operator in partially inlined function" {
|
||||
var s: [4]u32 = undefined;
|
||||
var b: [16]u8 = undefined;
|
||||
|
||||
for (b) |*r, i|
|
||||
r.* = @intCast(u8, i + 1);
|
||||
|
||||
copyWithPartialInline(s[0..], b[0..]);
|
||||
try expect(s[0] == 0x1020304);
|
||||
try expect(s[1] == 0x5060708);
|
||||
try expect(s[2] == 0x90a0b0c);
|
||||
try expect(s[3] == 0xd0e0f10);
|
||||
}
|
||||
|
||||
test "comptime function with mutable pointer is not memoized" {
|
||||
comptime {
|
||||
var x: i32 = 1;
|
||||
@@ -203,13 +178,6 @@ test "comptime shlWithOverflow" {
|
||||
try expect(ct_shifted == rt_shifted);
|
||||
}
|
||||
|
||||
test "comptime shl" {
|
||||
var a: u128 = 3;
|
||||
var b: u7 = 63;
|
||||
var c: u128 = 3 << 63;
|
||||
try expectEqual(a << b, c);
|
||||
}
|
||||
|
||||
test "runtime 128 bit integer division" {
|
||||
var a: u128 = 152313999999999991610955792383;
|
||||
var b: u128 = 10000000000000000000;
|
||||
@@ -278,23 +246,6 @@ test "bit shift a u1" {
|
||||
try expect(y == 1);
|
||||
}
|
||||
|
||||
test "comptime bitwise operators" {
|
||||
comptime {
|
||||
try expect(3 & 1 == 1);
|
||||
try expect(3 & -1 == 3);
|
||||
try expect(-3 & -1 == -3);
|
||||
try expect(3 | -1 == -1);
|
||||
try expect(-3 | -1 == -1);
|
||||
try expect(3 ^ -1 == -4);
|
||||
try expect(-3 ^ -1 == 2);
|
||||
try expect(~@as(i8, -1) == 0);
|
||||
try expect(~@as(i128, -1) == 0);
|
||||
try expect(18446744073709551615 & 18446744073709551611 == 18446744073709551611);
|
||||
try expect(-18446744073709551615 & -18446744073709551611 == -18446744073709551615);
|
||||
try expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff);
|
||||
}
|
||||
}
|
||||
|
||||
test "*align(1) u16 is the same as *align(1:0:2) u16" {
|
||||
comptime {
|
||||
try expect(*align(1:0:2) u16 == *align(1) u16);
|
||||
|
||||
@@ -19,17 +19,6 @@ fn testLocVars(b: i32) void {
|
||||
if (a + b != 3) unreachable;
|
||||
}
|
||||
|
||||
test "void parameters" {
|
||||
try voidFun(1, void{}, 2, {});
|
||||
}
|
||||
fn voidFun(a: i32, b: void, c: i32, d: void) !void {
|
||||
_ = d;
|
||||
const v = b;
|
||||
const vv: void = if (a == 1) v else {};
|
||||
try expect(a + c == 3);
|
||||
return vv;
|
||||
}
|
||||
|
||||
test "mutable local variables" {
|
||||
var zero: i32 = 0;
|
||||
try expect(zero == 0);
|
||||
@@ -54,14 +43,6 @@ test "separate block scopes" {
|
||||
try expect(c == 10);
|
||||
}
|
||||
|
||||
test "call function with empty string" {
|
||||
acceptsString("");
|
||||
}
|
||||
|
||||
fn acceptsString(foo: []u8) void {
|
||||
_ = foo;
|
||||
}
|
||||
|
||||
fn @"weird function name"() i32 {
|
||||
return 1234;
|
||||
}
|
||||
@@ -69,51 +50,6 @@ test "weird function name" {
|
||||
try expect(@"weird function name"() == 1234);
|
||||
}
|
||||
|
||||
test "implicit cast function unreachable return" {
|
||||
wantsFnWithVoid(fnWithUnreachable);
|
||||
}
|
||||
|
||||
fn wantsFnWithVoid(f: fn () void) void {
|
||||
_ = f;
|
||||
}
|
||||
|
||||
fn fnWithUnreachable() noreturn {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
test "function pointers" {
|
||||
const fns = [_]@TypeOf(fn1){
|
||||
fn1,
|
||||
fn2,
|
||||
fn3,
|
||||
fn4,
|
||||
};
|
||||
for (fns) |f, i| {
|
||||
try expect(f() == @intCast(u32, i) + 5);
|
||||
}
|
||||
}
|
||||
fn fn1() u32 {
|
||||
return 5;
|
||||
}
|
||||
fn fn2() u32 {
|
||||
return 6;
|
||||
}
|
||||
fn fn3() u32 {
|
||||
return 7;
|
||||
}
|
||||
fn fn4() u32 {
|
||||
return 8;
|
||||
}
|
||||
|
||||
test "number literal as an argument" {
|
||||
try numberLiteralArg(3);
|
||||
comptime try numberLiteralArg(3);
|
||||
}
|
||||
|
||||
fn numberLiteralArg(a: anytype) !void {
|
||||
try expect(a == 3);
|
||||
}
|
||||
|
||||
test "assign inline fn to const variable" {
|
||||
const a = inlineFn;
|
||||
a();
|
||||
@@ -121,64 +57,6 @@ test "assign inline fn to const variable" {
|
||||
|
||||
inline fn inlineFn() void {}
|
||||
|
||||
test "pass by non-copying value" {
|
||||
try expect(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" {
|
||||
try expect((try addPointCoordsVar(Point{ .x = 1, .y = 2 })) == 3);
|
||||
}
|
||||
|
||||
fn addPointCoordsVar(pt: anytype) !i32 {
|
||||
comptime try expect(@TypeOf(pt) == Point);
|
||||
return pt.x + pt.y;
|
||||
}
|
||||
|
||||
test "pass by non-copying value as method" {
|
||||
var pt = Point2{ .x = 1, .y = 2 };
|
||||
try expect(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 };
|
||||
try expect(pt.addPointCoords(i32) == 3);
|
||||
}
|
||||
|
||||
const Point3 = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
|
||||
fn addPointCoords(self: Point3, comptime T: type) i32 {
|
||||
_ = T;
|
||||
return self.x + self.y;
|
||||
}
|
||||
};
|
||||
|
||||
test "pass by non-copying value as method, at comptime" {
|
||||
comptime {
|
||||
var pt = Point2{ .x = 1, .y = 2 };
|
||||
try expect(pt.addPointCoords() == 3);
|
||||
}
|
||||
}
|
||||
|
||||
fn outer(y: u32) fn (u32) u32 {
|
||||
const Y = @TypeOf(y);
|
||||
const st = struct {
|
||||
@@ -194,43 +72,6 @@ test "return inner function which references comptime variable of outer function
|
||||
try expect(func(3) == 7);
|
||||
}
|
||||
|
||||
test "extern struct with stdcallcc fn pointer" {
|
||||
const S = extern struct {
|
||||
ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32,
|
||||
|
||||
fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 {
|
||||
return 1234;
|
||||
}
|
||||
};
|
||||
|
||||
var s: S = undefined;
|
||||
s.ptr = S.foo;
|
||||
try expect(s.ptr() == 1234);
|
||||
}
|
||||
|
||||
test "implicit cast fn call result to optional in field result" {
|
||||
const S = struct {
|
||||
fn entry() !void {
|
||||
var x = Foo{
|
||||
.field = optionalPtr(),
|
||||
};
|
||||
try expect(x.field.?.* == 999);
|
||||
}
|
||||
|
||||
const glob: i32 = 999;
|
||||
|
||||
fn optionalPtr() *const i32 {
|
||||
return &glob;
|
||||
}
|
||||
|
||||
const Foo = struct {
|
||||
field: ?*const i32,
|
||||
};
|
||||
};
|
||||
try S.entry();
|
||||
comptime try S.entry();
|
||||
}
|
||||
|
||||
test "discard the result of a function that returns a struct" {
|
||||
const S = struct {
|
||||
fn entry() void {
|
||||
@@ -249,45 +90,3 @@ test "discard the result of a function that returns a struct" {
|
||||
S.entry();
|
||||
comptime S.entry();
|
||||
}
|
||||
|
||||
test "function call with anon list literal" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try consumeVec(.{ 9, 8, 7 });
|
||||
}
|
||||
|
||||
fn consumeVec(vec: [3]f32) !void {
|
||||
try expect(vec[0] == 9);
|
||||
try expect(vec[1] == 8);
|
||||
try expect(vec[2] == 7);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "ability to give comptime types and non comptime types to same parameter" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var x: i32 = 1;
|
||||
try expect(foo(x) == 10);
|
||||
try expect(foo(i32) == 20);
|
||||
}
|
||||
|
||||
fn foo(arg: anytype) i32 {
|
||||
if (@typeInfo(@TypeOf(arg)) == .Type and arg == i32) return 20;
|
||||
return 9 + arg;
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "function with inferred error set but returning no error" {
|
||||
const S = struct {
|
||||
fn foo() !void {}
|
||||
};
|
||||
|
||||
const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?;
|
||||
try expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len);
|
||||
}
|
||||
|
||||
206
test/behavior/fn_stage1.zig
Normal file
206
test/behavior/fn_stage1.zig
Normal file
@@ -0,0 +1,206 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const testing = std.testing;
|
||||
const expect = testing.expect;
|
||||
const expectEqual = testing.expectEqual;
|
||||
|
||||
test "void parameters" {
|
||||
try voidFun(1, void{}, 2, {});
|
||||
}
|
||||
fn voidFun(a: i32, b: void, c: i32, d: void) !void {
|
||||
_ = d;
|
||||
const v = b;
|
||||
const vv: void = if (a == 1) v else {};
|
||||
try expect(a + c == 3);
|
||||
return vv;
|
||||
}
|
||||
|
||||
test "call function with empty string" {
|
||||
acceptsString("");
|
||||
}
|
||||
|
||||
fn acceptsString(foo: []u8) void {
|
||||
_ = foo;
|
||||
}
|
||||
|
||||
test "implicit cast function unreachable return" {
|
||||
wantsFnWithVoid(fnWithUnreachable);
|
||||
}
|
||||
|
||||
fn wantsFnWithVoid(f: fn () void) void {
|
||||
_ = f;
|
||||
}
|
||||
|
||||
fn fnWithUnreachable() noreturn {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
test "function pointers" {
|
||||
const fns = [_]@TypeOf(fn1){
|
||||
fn1,
|
||||
fn2,
|
||||
fn3,
|
||||
fn4,
|
||||
};
|
||||
for (fns) |f, i| {
|
||||
try expect(f() == @intCast(u32, i) + 5);
|
||||
}
|
||||
}
|
||||
fn fn1() u32 {
|
||||
return 5;
|
||||
}
|
||||
fn fn2() u32 {
|
||||
return 6;
|
||||
}
|
||||
fn fn3() u32 {
|
||||
return 7;
|
||||
}
|
||||
fn fn4() u32 {
|
||||
return 8;
|
||||
}
|
||||
|
||||
test "number literal as an argument" {
|
||||
try numberLiteralArg(3);
|
||||
comptime try numberLiteralArg(3);
|
||||
}
|
||||
|
||||
fn numberLiteralArg(a: anytype) !void {
|
||||
try expect(a == 3);
|
||||
}
|
||||
|
||||
test "pass by non-copying value" {
|
||||
try expect(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" {
|
||||
try expect((try addPointCoordsVar(Point{ .x = 1, .y = 2 })) == 3);
|
||||
}
|
||||
|
||||
fn addPointCoordsVar(pt: anytype) !i32 {
|
||||
comptime try expect(@TypeOf(pt) == Point);
|
||||
return pt.x + pt.y;
|
||||
}
|
||||
|
||||
test "pass by non-copying value as method" {
|
||||
var pt = Point2{ .x = 1, .y = 2 };
|
||||
try expect(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 };
|
||||
try expect(pt.addPointCoords(i32) == 3);
|
||||
}
|
||||
|
||||
const Point3 = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
|
||||
fn addPointCoords(self: Point3, comptime T: type) i32 {
|
||||
_ = T;
|
||||
return self.x + self.y;
|
||||
}
|
||||
};
|
||||
|
||||
test "pass by non-copying value as method, at comptime" {
|
||||
comptime {
|
||||
var pt = Point2{ .x = 1, .y = 2 };
|
||||
try expect(pt.addPointCoords() == 3);
|
||||
}
|
||||
}
|
||||
|
||||
test "extern struct with stdcallcc fn pointer" {
|
||||
const S = extern struct {
|
||||
ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32,
|
||||
|
||||
fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 {
|
||||
return 1234;
|
||||
}
|
||||
};
|
||||
|
||||
var s: S = undefined;
|
||||
s.ptr = S.foo;
|
||||
try expect(s.ptr() == 1234);
|
||||
}
|
||||
|
||||
test "implicit cast fn call result to optional in field result" {
|
||||
const S = struct {
|
||||
fn entry() !void {
|
||||
var x = Foo{
|
||||
.field = optionalPtr(),
|
||||
};
|
||||
try expect(x.field.?.* == 999);
|
||||
}
|
||||
|
||||
const glob: i32 = 999;
|
||||
|
||||
fn optionalPtr() *const i32 {
|
||||
return &glob;
|
||||
}
|
||||
|
||||
const Foo = struct {
|
||||
field: ?*const i32,
|
||||
};
|
||||
};
|
||||
try S.entry();
|
||||
comptime try S.entry();
|
||||
}
|
||||
|
||||
test "function call with anon list literal" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try consumeVec(.{ 9, 8, 7 });
|
||||
}
|
||||
|
||||
fn consumeVec(vec: [3]f32) !void {
|
||||
try expect(vec[0] == 9);
|
||||
try expect(vec[1] == 8);
|
||||
try expect(vec[2] == 7);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "ability to give comptime types and non comptime types to same parameter" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var x: i32 = 1;
|
||||
try expect(foo(x) == 10);
|
||||
try expect(foo(i32) == 20);
|
||||
}
|
||||
|
||||
fn foo(arg: anytype) i32 {
|
||||
if (@typeInfo(@TypeOf(arg)) == .Type and arg == i32) return 20;
|
||||
return 9 + arg;
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "function with inferred error set but returning no error" {
|
||||
const S = struct {
|
||||
fn foo() !void {}
|
||||
};
|
||||
|
||||
const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?;
|
||||
try expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len);
|
||||
}
|
||||
@@ -249,3 +249,190 @@ test "binary not" {
|
||||
fn testBinaryNot(x: u16) !void {
|
||||
try expect(~x == 0b0101010101010101);
|
||||
}
|
||||
|
||||
test "division" {
|
||||
try testDivision();
|
||||
comptime try testDivision();
|
||||
}
|
||||
fn testDivision() !void {
|
||||
try expect(div(u32, 13, 3) == 4);
|
||||
try expect(div(f16, 1.0, 2.0) == 0.5);
|
||||
try expect(div(f32, 1.0, 2.0) == 0.5);
|
||||
|
||||
try expect(divExact(u32, 55, 11) == 5);
|
||||
try expect(divExact(i32, -55, 11) == -5);
|
||||
try expect(divExact(f16, 55.0, 11.0) == 5.0);
|
||||
try expect(divExact(f16, -55.0, 11.0) == -5.0);
|
||||
try expect(divExact(f32, 55.0, 11.0) == 5.0);
|
||||
try expect(divExact(f32, -55.0, 11.0) == -5.0);
|
||||
|
||||
try expect(divFloor(i32, 5, 3) == 1);
|
||||
try expect(divFloor(i32, -5, 3) == -2);
|
||||
try expect(divFloor(f16, 5.0, 3.0) == 1.0);
|
||||
try expect(divFloor(f16, -5.0, 3.0) == -2.0);
|
||||
try expect(divFloor(f32, 5.0, 3.0) == 1.0);
|
||||
try expect(divFloor(f32, -5.0, 3.0) == -2.0);
|
||||
try expect(divFloor(i32, -0x80000000, -2) == 0x40000000);
|
||||
try expect(divFloor(i32, 0, -0x80000000) == 0);
|
||||
try expect(divFloor(i32, -0x40000001, 0x40000000) == -2);
|
||||
try expect(divFloor(i32, -0x80000000, 1) == -0x80000000);
|
||||
try expect(divFloor(i32, 10, 12) == 0);
|
||||
try expect(divFloor(i32, -14, 12) == -2);
|
||||
try expect(divFloor(i32, -2, 12) == -1);
|
||||
|
||||
try expect(divTrunc(i32, 5, 3) == 1);
|
||||
try expect(divTrunc(i32, -5, 3) == -1);
|
||||
try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
|
||||
try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
|
||||
try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
|
||||
try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
|
||||
try expect(divTrunc(f64, 5.0, 3.0) == 1.0);
|
||||
try expect(divTrunc(f64, -5.0, 3.0) == -1.0);
|
||||
try expect(divTrunc(i32, 10, 12) == 0);
|
||||
try expect(divTrunc(i32, -14, 12) == -1);
|
||||
try expect(divTrunc(i32, -2, 12) == 0);
|
||||
|
||||
try expect(mod(i32, 10, 12) == 10);
|
||||
try expect(mod(i32, -14, 12) == 10);
|
||||
try expect(mod(i32, -2, 12) == 10);
|
||||
|
||||
comptime {
|
||||
try expect(
|
||||
1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600,
|
||||
);
|
||||
try expect(
|
||||
@rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600,
|
||||
);
|
||||
try expect(
|
||||
1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,
|
||||
);
|
||||
try expect(
|
||||
@divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
|
||||
);
|
||||
try expect(
|
||||
@divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,
|
||||
);
|
||||
try expect(
|
||||
@divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,
|
||||
);
|
||||
try expect(
|
||||
4126227191251978491697987544882340798050766755606969681711 % 10 == 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
fn div(comptime T: type, a: T, b: T) T {
|
||||
return a / b;
|
||||
}
|
||||
fn divExact(comptime T: type, a: T, b: T) T {
|
||||
return @divExact(a, b);
|
||||
}
|
||||
fn divFloor(comptime T: type, a: T, b: T) T {
|
||||
return @divFloor(a, b);
|
||||
}
|
||||
fn divTrunc(comptime T: type, a: T, b: T) T {
|
||||
return @divTrunc(a, b);
|
||||
}
|
||||
fn mod(comptime T: type, a: T, b: T) T {
|
||||
return @mod(a, b);
|
||||
}
|
||||
|
||||
test "unsigned wrapping" {
|
||||
try testUnsignedWrappingEval(maxInt(u32));
|
||||
comptime try testUnsignedWrappingEval(maxInt(u32));
|
||||
}
|
||||
fn testUnsignedWrappingEval(x: u32) !void {
|
||||
const zero = x +% 1;
|
||||
try expect(zero == 0);
|
||||
const orig = zero -% 1;
|
||||
try expect(orig == maxInt(u32));
|
||||
}
|
||||
|
||||
test "signed wrapping" {
|
||||
try testSignedWrappingEval(maxInt(i32));
|
||||
comptime try testSignedWrappingEval(maxInt(i32));
|
||||
}
|
||||
fn testSignedWrappingEval(x: i32) !void {
|
||||
const min_val = x +% 1;
|
||||
try expect(min_val == minInt(i32));
|
||||
const max_val = min_val -% 1;
|
||||
try expect(max_val == maxInt(i32));
|
||||
}
|
||||
|
||||
test "signed negation wrapping" {
|
||||
try testSignedNegationWrappingEval(minInt(i16));
|
||||
comptime try testSignedNegationWrappingEval(minInt(i16));
|
||||
}
|
||||
fn testSignedNegationWrappingEval(x: i16) !void {
|
||||
try expect(x == -32768);
|
||||
const neg = -%x;
|
||||
try expect(neg == -32768);
|
||||
}
|
||||
|
||||
test "unsigned negation wrapping" {
|
||||
try testUnsignedNegationWrappingEval(1);
|
||||
comptime try testUnsignedNegationWrappingEval(1);
|
||||
}
|
||||
fn testUnsignedNegationWrappingEval(x: u16) !void {
|
||||
try expect(x == 1);
|
||||
const neg = -%x;
|
||||
try expect(neg == maxInt(u16));
|
||||
}
|
||||
|
||||
test "unsigned 64-bit division" {
|
||||
try test_u64_div();
|
||||
comptime try test_u64_div();
|
||||
}
|
||||
fn test_u64_div() !void {
|
||||
const result = divWithResult(1152921504606846976, 34359738365);
|
||||
try expect(result.quotient == 33554432);
|
||||
try expect(result.remainder == 100663296);
|
||||
}
|
||||
fn divWithResult(a: u64, b: u64) DivResult {
|
||||
return DivResult{
|
||||
.quotient = a / b,
|
||||
.remainder = a % b,
|
||||
};
|
||||
}
|
||||
const DivResult = struct {
|
||||
quotient: u64,
|
||||
remainder: u64,
|
||||
};
|
||||
|
||||
test "truncating shift right" {
|
||||
try testShrTrunc(maxInt(u16));
|
||||
comptime try testShrTrunc(maxInt(u16));
|
||||
}
|
||||
fn testShrTrunc(x: u16) !void {
|
||||
const shifted = x >> 1;
|
||||
try expect(shifted == 32767);
|
||||
}
|
||||
|
||||
test "f128" {
|
||||
try test_f128();
|
||||
comptime try test_f128();
|
||||
}
|
||||
|
||||
fn make_f128(x: f128) f128 {
|
||||
return x;
|
||||
}
|
||||
|
||||
fn test_f128() !void {
|
||||
try expect(@sizeOf(f128) == 16);
|
||||
try expect(make_f128(1.0) == 1.0);
|
||||
try expect(make_f128(1.0) != 1.1);
|
||||
try expect(make_f128(1.0) > 0.9);
|
||||
try expect(make_f128(1.0) >= 0.9);
|
||||
try expect(make_f128(1.0) >= 1.0);
|
||||
try should_not_be_zero(1.0);
|
||||
}
|
||||
|
||||
fn should_not_be_zero(x: f128) !void {
|
||||
try expect(x != 0.0);
|
||||
}
|
||||
|
||||
test "128-bit multiplication" {
|
||||
var a: i128 = 3;
|
||||
var b: i128 = 2;
|
||||
var c = a * b;
|
||||
try expect(c == 6);
|
||||
}
|
||||
|
||||
@@ -6,92 +6,6 @@ const maxInt = std.math.maxInt;
|
||||
const minInt = std.math.minInt;
|
||||
const mem = std.mem;
|
||||
|
||||
test "division" {
|
||||
try testDivision();
|
||||
comptime try testDivision();
|
||||
}
|
||||
fn testDivision() !void {
|
||||
try expect(div(u32, 13, 3) == 4);
|
||||
try expect(div(f16, 1.0, 2.0) == 0.5);
|
||||
try expect(div(f32, 1.0, 2.0) == 0.5);
|
||||
|
||||
try expect(divExact(u32, 55, 11) == 5);
|
||||
try expect(divExact(i32, -55, 11) == -5);
|
||||
try expect(divExact(f16, 55.0, 11.0) == 5.0);
|
||||
try expect(divExact(f16, -55.0, 11.0) == -5.0);
|
||||
try expect(divExact(f32, 55.0, 11.0) == 5.0);
|
||||
try expect(divExact(f32, -55.0, 11.0) == -5.0);
|
||||
|
||||
try expect(divFloor(i32, 5, 3) == 1);
|
||||
try expect(divFloor(i32, -5, 3) == -2);
|
||||
try expect(divFloor(f16, 5.0, 3.0) == 1.0);
|
||||
try expect(divFloor(f16, -5.0, 3.0) == -2.0);
|
||||
try expect(divFloor(f32, 5.0, 3.0) == 1.0);
|
||||
try expect(divFloor(f32, -5.0, 3.0) == -2.0);
|
||||
try expect(divFloor(i32, -0x80000000, -2) == 0x40000000);
|
||||
try expect(divFloor(i32, 0, -0x80000000) == 0);
|
||||
try expect(divFloor(i32, -0x40000001, 0x40000000) == -2);
|
||||
try expect(divFloor(i32, -0x80000000, 1) == -0x80000000);
|
||||
try expect(divFloor(i32, 10, 12) == 0);
|
||||
try expect(divFloor(i32, -14, 12) == -2);
|
||||
try expect(divFloor(i32, -2, 12) == -1);
|
||||
|
||||
try expect(divTrunc(i32, 5, 3) == 1);
|
||||
try expect(divTrunc(i32, -5, 3) == -1);
|
||||
try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
|
||||
try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
|
||||
try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
|
||||
try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
|
||||
try expect(divTrunc(f64, 5.0, 3.0) == 1.0);
|
||||
try expect(divTrunc(f64, -5.0, 3.0) == -1.0);
|
||||
try expect(divTrunc(i32, 10, 12) == 0);
|
||||
try expect(divTrunc(i32, -14, 12) == -1);
|
||||
try expect(divTrunc(i32, -2, 12) == 0);
|
||||
|
||||
try expect(mod(i32, 10, 12) == 10);
|
||||
try expect(mod(i32, -14, 12) == 10);
|
||||
try expect(mod(i32, -2, 12) == 10);
|
||||
|
||||
comptime {
|
||||
try expect(
|
||||
1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600,
|
||||
);
|
||||
try expect(
|
||||
@rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600,
|
||||
);
|
||||
try expect(
|
||||
1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,
|
||||
);
|
||||
try expect(
|
||||
@divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
|
||||
);
|
||||
try expect(
|
||||
@divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,
|
||||
);
|
||||
try expect(
|
||||
@divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,
|
||||
);
|
||||
try expect(
|
||||
4126227191251978491697987544882340798050766755606969681711 % 10 == 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
fn div(comptime T: type, a: T, b: T) T {
|
||||
return a / b;
|
||||
}
|
||||
fn divExact(comptime T: type, a: T, b: T) T {
|
||||
return @divExact(a, b);
|
||||
}
|
||||
fn divFloor(comptime T: type, a: T, b: T) T {
|
||||
return @divFloor(a, b);
|
||||
}
|
||||
fn divTrunc(comptime T: type, a: T, b: T) T {
|
||||
return @divTrunc(a, b);
|
||||
}
|
||||
fn mod(comptime T: type, a: T, b: T) T {
|
||||
return @mod(a, b);
|
||||
}
|
||||
|
||||
test "@addWithOverflow" {
|
||||
var result: u8 = undefined;
|
||||
try expect(@addWithOverflow(u8, 250, 100, &result));
|
||||
@@ -157,68 +71,6 @@ fn testCtzVectors() !void {
|
||||
try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16)));
|
||||
}
|
||||
|
||||
test "unsigned wrapping" {
|
||||
try testUnsignedWrappingEval(maxInt(u32));
|
||||
comptime try testUnsignedWrappingEval(maxInt(u32));
|
||||
}
|
||||
fn testUnsignedWrappingEval(x: u32) !void {
|
||||
const zero = x +% 1;
|
||||
try expect(zero == 0);
|
||||
const orig = zero -% 1;
|
||||
try expect(orig == maxInt(u32));
|
||||
}
|
||||
|
||||
test "signed wrapping" {
|
||||
try testSignedWrappingEval(maxInt(i32));
|
||||
comptime try testSignedWrappingEval(maxInt(i32));
|
||||
}
|
||||
fn testSignedWrappingEval(x: i32) !void {
|
||||
const min_val = x +% 1;
|
||||
try expect(min_val == minInt(i32));
|
||||
const max_val = min_val -% 1;
|
||||
try expect(max_val == maxInt(i32));
|
||||
}
|
||||
|
||||
test "signed negation wrapping" {
|
||||
try testSignedNegationWrappingEval(minInt(i16));
|
||||
comptime try testSignedNegationWrappingEval(minInt(i16));
|
||||
}
|
||||
fn testSignedNegationWrappingEval(x: i16) !void {
|
||||
try expect(x == -32768);
|
||||
const neg = -%x;
|
||||
try expect(neg == -32768);
|
||||
}
|
||||
|
||||
test "unsigned negation wrapping" {
|
||||
try testUnsignedNegationWrappingEval(1);
|
||||
comptime try testUnsignedNegationWrappingEval(1);
|
||||
}
|
||||
fn testUnsignedNegationWrappingEval(x: u16) !void {
|
||||
try expect(x == 1);
|
||||
const neg = -%x;
|
||||
try expect(neg == maxInt(u16));
|
||||
}
|
||||
|
||||
test "unsigned 64-bit division" {
|
||||
try test_u64_div();
|
||||
comptime try test_u64_div();
|
||||
}
|
||||
fn test_u64_div() !void {
|
||||
const result = divWithResult(1152921504606846976, 34359738365);
|
||||
try expect(result.quotient == 33554432);
|
||||
try expect(result.remainder == 100663296);
|
||||
}
|
||||
fn divWithResult(a: u64, b: u64) DivResult {
|
||||
return DivResult{
|
||||
.quotient = a / b,
|
||||
.remainder = a % b,
|
||||
};
|
||||
}
|
||||
const DivResult = struct {
|
||||
quotient: u64,
|
||||
remainder: u64,
|
||||
};
|
||||
|
||||
test "small int addition" {
|
||||
var x: u2 = 0;
|
||||
try expect(x == 0);
|
||||
@@ -346,15 +198,6 @@ fn testShlTrunc(x: u16) !void {
|
||||
try expect(shifted == 65534);
|
||||
}
|
||||
|
||||
test "truncating shift right" {
|
||||
try testShrTrunc(maxInt(u16));
|
||||
comptime try testShrTrunc(maxInt(u16));
|
||||
}
|
||||
fn testShrTrunc(x: u16) !void {
|
||||
const shifted = x >> 1;
|
||||
try expect(shifted == 32767);
|
||||
}
|
||||
|
||||
test "exact shift left" {
|
||||
try testShlExact(0b00110101);
|
||||
comptime try testShlExact(0b00110101);
|
||||
@@ -392,29 +235,6 @@ test "shift left/right on u0 operand" {
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "f128" {
|
||||
try test_f128();
|
||||
comptime try test_f128();
|
||||
}
|
||||
|
||||
fn make_f128(x: f128) f128 {
|
||||
return x;
|
||||
}
|
||||
|
||||
fn test_f128() !void {
|
||||
try expect(@sizeOf(f128) == 16);
|
||||
try expect(make_f128(1.0) == 1.0);
|
||||
try expect(make_f128(1.0) != 1.1);
|
||||
try expect(make_f128(1.0) > 0.9);
|
||||
try expect(make_f128(1.0) >= 0.9);
|
||||
try expect(make_f128(1.0) >= 1.0);
|
||||
try should_not_be_zero(1.0);
|
||||
}
|
||||
|
||||
fn should_not_be_zero(x: f128) !void {
|
||||
try expect(x != 0.0);
|
||||
}
|
||||
|
||||
test "comptime float rem int" {
|
||||
comptime {
|
||||
var x = @as(f32, 1) % 2;
|
||||
@@ -614,13 +434,6 @@ fn testNanEqNan(comptime F: type) !void {
|
||||
try expect(!(nan1 <= nan2));
|
||||
}
|
||||
|
||||
test "128-bit multiplication" {
|
||||
var a: i128 = 3;
|
||||
var b: i128 = 2;
|
||||
var c = a * b;
|
||||
try expect(c == 6);
|
||||
}
|
||||
|
||||
test "vector comparison" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
|
||||
Reference in New Issue
Block a user