Commit 7f70c27e9d
Changed files (24)
lib
std
math
special
src
lib/std/math/big/int.zig
@@ -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,
};
lib/std/math/big/int_test.zig
@@ -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();
lib/std/special/compiler_rt.zig
@@ -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");
src/arch/aarch64/CodeGen.zig
@@ -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),
src/codegen/llvm/bindings.zig
@@ -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) {
src/codegen/c.zig
@@ -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
src/codegen/llvm.zig
@@ -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,20 +2833,81 @@ 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()) return self.builder.buildFDiv(lhs, rhs, "");
+ 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);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ const inst_ty = self.air.typeOfIndex(inst);
+
+ if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(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 {
if (self.liveness.isUnused(inst)) return null;
@@ -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,
src/codegen/spirv.zig
@@ -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),
src/codegen/wasm.zig
@@ -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"),
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,
src/codegen.zig
@@ -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),
src/Liveness.zig
@@ -233,7 +233,10 @@ fn analyzeInst(
.mul,
.mulwrap,
.mul_sat,
- .div,
+ .div_float,
+ .div_trunc,
+ .div_floor,
+ .div_exact,
.rem,
.mod,
.ptr_add,
src/print_air.zig
@@ -111,7 +111,10 @@ const Writer = struct {
.mul,
.mulwrap,
.mul_sat,
- .div,
+ .div_float,
+ .div_trunc,
+ .div_floor,
+ .div_exact,
.rem,
.mod,
.ptr_add,
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();
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,
test/behavior/array.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();
+}
test/behavior/array_stage1.zig
@@ -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 = "".*;
test/behavior/eval.zig
@@ -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);
+ }
+}
test/behavior/eval_stage1.zig
@@ -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);
test/behavior/fn.zig
@@ -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);
-}
test/behavior/fn_stage1.zig
@@ -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);
+}
test/behavior/math.zig
@@ -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);
+}
test/behavior/math_stage1.zig
@@ -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 {
test/behavior.zig
@@ -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");