authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-05 17:10:01-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-05 21:13:18-07:00
log5c9826630dfb8a2f59663a569bb8e452359c9524
tree41d554db1f8483214520c598a33ac18507d4d86e
parent40babaa537329c8425579a47ba4767f298f5d869

Sema: elide safety of modulus and remainder division sometimes

Piggybacking on 40f8f0134f5da9baaefd0fdab529d5585fa46199, remainder division, modulus, and `%` syntax no longer emit safety checks for a comptime-known denominator.

5 files changed, 399 insertions(+), 254 deletions(-)

src/Sema.zig+339-234
...@@ -876,9 +876,6 @@ fn analyzeBodyInner(...@@ -876,9 +876,6 @@ fn analyzeBodyInner(
876 .add => try sema.zirArithmetic(block, inst, .add),876 .add => try sema.zirArithmetic(block, inst, .add),
877 .addwrap => try sema.zirArithmetic(block, inst, .addwrap),877 .addwrap => try sema.zirArithmetic(block, inst, .addwrap),
878 .add_sat => try sema.zirArithmetic(block, inst, .add_sat),878 .add_sat => try sema.zirArithmetic(block, inst, .add_sat),
879 .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),
880 .mod => try sema.zirArithmetic(block, inst, .mod),
881 .rem => try sema.zirArithmetic(block, inst, .rem),
882 .mul => try sema.zirArithmetic(block, inst, .mul),879 .mul => try sema.zirArithmetic(block, inst, .mul),
883 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap),880 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap),
884 .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat),881 .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat),
...@@ -891,6 +888,10 @@ fn analyzeBodyInner(...@@ -891,6 +888,10 @@ fn analyzeBodyInner(
891 .div_floor => try sema.zirDivFloor(block, inst),888 .div_floor => try sema.zirDivFloor(block, inst),
892 .div_trunc => try sema.zirDivTrunc(block, inst),889 .div_trunc => try sema.zirDivTrunc(block, inst),
893890
891 .mod_rem => try sema.zirModRem(block, inst),
892 .mod => try sema.zirMod(block, inst),
893 .rem => try sema.zirRem(block, inst),
894
894 .maximum => try sema.zirMinMax(block, inst, .max),895 .maximum => try sema.zirMinMax(block, inst, .max),
895 .minimum => try sema.zirMinMax(block, inst, .min),896 .minimum => try sema.zirMinMax(block, inst, .min),
896897
...@@ -11621,6 +11622,341 @@ fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst...@@ -11621,6 +11622,341 @@ fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst
11621 };11622 };
11622}11623}
1162311624
11625fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11626 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
11627 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
11628 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
11629 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
11630 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
11631 const lhs = try sema.resolveInst(extra.lhs);
11632 const rhs = try sema.resolveInst(extra.rhs);
11633 const lhs_ty = sema.typeOf(lhs);
11634 const rhs_ty = sema.typeOf(rhs);
11635 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
11636 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
11637 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
11638 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .mod_rem);
11639
11640 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
11641 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
11642 .override = &[_]LazySrcLoc{ lhs_src, rhs_src },
11643 });
11644
11645 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
11646 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
11647
11648 const lhs_scalar_ty = lhs_ty.scalarType();
11649 const rhs_scalar_ty = rhs_ty.scalarType();
11650 const scalar_tag = resolved_type.scalarType().zigTypeTag();
11651
11652 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
11653
11654 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem);
11655
11656 const mod = sema.mod;
11657 const target = mod.getTarget();
11658 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs);
11659 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs);
11660
11661 const runtime_src = rs: {
11662 // For integers:
11663 // Either operand being undef is a compile error because there exists
11664 // a possible value (TODO what is it?) that would invoke illegal behavior.
11665 // TODO: can lhs undef be handled better?
11666 //
11667 // For floats:
11668 // If the rhs is zero, compile error for division by zero.
11669 // If the rhs is undefined, compile error because there is a possible
11670 // value (zero) for which the division would be illegal behavior.
11671 // If the lhs is undefined, result is undefined.
11672 //
11673 // For either one: if the result would be different between @mod and @rem,
11674 // then emit a compile error saying you have to pick one.
11675 if (is_int) {
11676 if (maybe_lhs_val) |lhs_val| {
11677 if (lhs_val.isUndef()) {
11678 return sema.failWithUseOfUndef(block, lhs_src);
11679 }
11680 if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11681 return sema.addConstant(resolved_type, Value.zero);
11682 }
11683 } else if (lhs_scalar_ty.isSignedInt()) {
11684 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
11685 }
11686 if (maybe_rhs_val) |rhs_val| {
11687 if (rhs_val.isUndef()) {
11688 return sema.failWithUseOfUndef(block, rhs_src);
11689 }
11690 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11691 return sema.failWithDivideByZero(block, rhs_src);
11692 }
11693 if (maybe_lhs_val) |lhs_val| {
11694 const rem_result = try lhs_val.intRem(rhs_val, resolved_type, sema.arena, target);
11695 // If this answer could possibly be different by doing `intMod`,
11696 // we must emit a compile error. Otherwise, it's OK.
11697 if ((try rhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) != (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) and
11698 !(try rem_result.compareWithZeroAdvanced(.eq, sema.kit(block, src))))
11699 {
11700 const bad_src = if (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src)))
11701 lhs_src
11702 else
11703 rhs_src;
11704 return sema.failWithModRemNegative(block, bad_src, lhs_ty, rhs_ty);
11705 }
11706 if (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) {
11707 // Negative
11708 return sema.addConstant(resolved_type, Value.zero);
11709 }
11710 return sema.addConstant(resolved_type, rem_result);
11711 }
11712 break :rs lhs_src;
11713 } else if (rhs_scalar_ty.isSignedInt()) {
11714 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
11715 } else {
11716 break :rs rhs_src;
11717 }
11718 }
11719 // float operands
11720 if (maybe_rhs_val) |rhs_val| {
11721 if (rhs_val.isUndef()) {
11722 return sema.failWithUseOfUndef(block, rhs_src);
11723 }
11724 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11725 return sema.failWithDivideByZero(block, rhs_src);
11726 }
11727 if (try rhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) {
11728 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
11729 }
11730 if (maybe_lhs_val) |lhs_val| {
11731 if (lhs_val.isUndef() or (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src)))) {
11732 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
11733 }
11734 return sema.addConstant(
11735 resolved_type,
11736 try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target),
11737 );
11738 } else {
11739 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
11740 }
11741 } else {
11742 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
11743 }
11744 };
11745
11746 try sema.requireRuntimeBlock(block, src, runtime_src);
11747
11748 if (block.wantSafety()) {
11749 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
11750 }
11751
11752 const air_tag = airTag(block, is_int, .rem, .rem_optimized);
11753 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
11754}
11755
11756fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11757 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
11758 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
11759 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
11760 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
11761 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
11762 const lhs = try sema.resolveInst(extra.lhs);
11763 const rhs = try sema.resolveInst(extra.rhs);
11764 const lhs_ty = sema.typeOf(lhs);
11765 const rhs_ty = sema.typeOf(rhs);
11766 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
11767 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
11768 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
11769 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .mod);
11770
11771 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
11772 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
11773 .override = &[_]LazySrcLoc{ lhs_src, rhs_src },
11774 });
11775
11776 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
11777 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
11778
11779 const scalar_tag = resolved_type.scalarType().zigTypeTag();
11780
11781 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
11782
11783 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod);
11784
11785 const mod = sema.mod;
11786 const target = mod.getTarget();
11787 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs);
11788 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs);
11789
11790 const runtime_src = rs: {
11791 // For integers:
11792 // Either operand being undef is a compile error because there exists
11793 // a possible value (TODO what is it?) that would invoke illegal behavior.
11794 // TODO: can lhs zero be handled better?
11795 // TODO: can lhs undef be handled better?
11796 //
11797 // For floats:
11798 // If the rhs is zero, compile error for division by zero.
11799 // If the rhs is undefined, compile error because there is a possible
11800 // value (zero) for which the division would be illegal behavior.
11801 // If the lhs is undefined, result is undefined.
11802 if (is_int) {
11803 if (maybe_lhs_val) |lhs_val| {
11804 if (lhs_val.isUndef()) {
11805 return sema.failWithUseOfUndef(block, lhs_src);
11806 }
11807 }
11808 if (maybe_rhs_val) |rhs_val| {
11809 if (rhs_val.isUndef()) {
11810 return sema.failWithUseOfUndef(block, rhs_src);
11811 }
11812 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11813 return sema.failWithDivideByZero(block, rhs_src);
11814 }
11815 if (maybe_lhs_val) |lhs_val| {
11816 return sema.addConstant(
11817 resolved_type,
11818 try lhs_val.intMod(rhs_val, resolved_type, sema.arena, target),
11819 );
11820 }
11821 break :rs lhs_src;
11822 } else {
11823 break :rs rhs_src;
11824 }
11825 }
11826 // float operands
11827 if (maybe_rhs_val) |rhs_val| {
11828 if (rhs_val.isUndef()) {
11829 return sema.failWithUseOfUndef(block, rhs_src);
11830 }
11831 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11832 return sema.failWithDivideByZero(block, rhs_src);
11833 }
11834 }
11835 if (maybe_lhs_val) |lhs_val| {
11836 if (lhs_val.isUndef()) {
11837 return sema.addConstUndef(resolved_type);
11838 }
11839 if (maybe_rhs_val) |rhs_val| {
11840 return sema.addConstant(
11841 resolved_type,
11842 try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, target),
11843 );
11844 } else break :rs rhs_src;
11845 } else break :rs lhs_src;
11846 };
11847
11848 try sema.requireRuntimeBlock(block, src, runtime_src);
11849
11850 if (block.wantSafety()) {
11851 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
11852 }
11853
11854 const air_tag = airTag(block, is_int, .mod, .mod_optimized);
11855 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
11856}
11857
11858fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11859 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
11860 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
11861 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
11862 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
11863 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
11864 const lhs = try sema.resolveInst(extra.lhs);
11865 const rhs = try sema.resolveInst(extra.rhs);
11866 const lhs_ty = sema.typeOf(lhs);
11867 const rhs_ty = sema.typeOf(rhs);
11868 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
11869 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
11870 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
11871 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .rem);
11872
11873 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
11874 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
11875 .override = &[_]LazySrcLoc{ lhs_src, rhs_src },
11876 });
11877
11878 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
11879 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
11880
11881 const scalar_tag = resolved_type.scalarType().zigTypeTag();
11882
11883 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
11884
11885 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem);
11886
11887 const mod = sema.mod;
11888 const target = mod.getTarget();
11889 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs);
11890 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs);
11891
11892 const runtime_src = rs: {
11893 // For integers:
11894 // Either operand being undef is a compile error because there exists
11895 // a possible value (TODO what is it?) that would invoke illegal behavior.
11896 // TODO: can lhs zero be handled better?
11897 // TODO: can lhs undef be handled better?
11898 //
11899 // For floats:
11900 // If the rhs is zero, compile error for division by zero.
11901 // If the rhs is undefined, compile error because there is a possible
11902 // value (zero) for which the division would be illegal behavior.
11903 // If the lhs is undefined, result is undefined.
11904 if (is_int) {
11905 if (maybe_lhs_val) |lhs_val| {
11906 if (lhs_val.isUndef()) {
11907 return sema.failWithUseOfUndef(block, lhs_src);
11908 }
11909 }
11910 if (maybe_rhs_val) |rhs_val| {
11911 if (rhs_val.isUndef()) {
11912 return sema.failWithUseOfUndef(block, rhs_src);
11913 }
11914 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11915 return sema.failWithDivideByZero(block, rhs_src);
11916 }
11917 if (maybe_lhs_val) |lhs_val| {
11918 return sema.addConstant(
11919 resolved_type,
11920 try lhs_val.intRem(rhs_val, resolved_type, sema.arena, target),
11921 );
11922 }
11923 break :rs lhs_src;
11924 } else {
11925 break :rs rhs_src;
11926 }
11927 }
11928 // float operands
11929 if (maybe_rhs_val) |rhs_val| {
11930 if (rhs_val.isUndef()) {
11931 return sema.failWithUseOfUndef(block, rhs_src);
11932 }
11933 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11934 return sema.failWithDivideByZero(block, rhs_src);
11935 }
11936 }
11937 if (maybe_lhs_val) |lhs_val| {
11938 if (lhs_val.isUndef()) {
11939 return sema.addConstUndef(resolved_type);
11940 }
11941 if (maybe_rhs_val) |rhs_val| {
11942 return sema.addConstant(
11943 resolved_type,
11944 try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target),
11945 );
11946 } else break :rs rhs_src;
11947 } else break :rs lhs_src;
11948 };
11949
11950 try sema.requireRuntimeBlock(block, src, runtime_src);
11951
11952 if (block.wantSafety()) {
11953 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
11954 }
11955
11956 const air_tag = airTag(block, is_int, .rem, .rem_optimized);
11957 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
11958}
11959
11624fn zirOverflowArithmetic(11960fn zirOverflowArithmetic(
11625 sema: *Sema,11961 sema: *Sema,
11626 block: *Block,11962 block: *Block,
...@@ -11882,8 +12218,6 @@ fn analyzeArithmetic(...@@ -11882,8 +12218,6 @@ fn analyzeArithmetic(
11882 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);12218 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
11883 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);12219 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
1188412220
11885 const lhs_scalar_ty = lhs_ty.scalarType();
11886 const rhs_scalar_ty = rhs_ty.scalarType();
11887 const scalar_tag = resolved_type.scalarType().zigTypeTag();12221 const scalar_tag = resolved_type.scalarType().zigTypeTag();
1188812222
11889 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;12223 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
...@@ -12229,206 +12563,6 @@ fn analyzeArithmetic(...@@ -12229,206 +12563,6 @@ fn analyzeArithmetic(
12229 } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat };12563 } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat };
12230 } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat };12564 } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat };
12231 },12565 },
12232 .mod_rem => {
12233 // For integers:
12234 // Either operand being undef is a compile error because there exists
12235 // a possible value (TODO what is it?) that would invoke illegal behavior.
12236 // TODO: can lhs undef be handled better?
12237 //
12238 // For floats:
12239 // If the rhs is zero, compile error for division by zero.
12240 // If the rhs is undefined, compile error because there is a possible
12241 // value (zero) for which the division would be illegal behavior.
12242 // If the lhs is undefined, result is undefined.
12243 //
12244 // For either one: if the result would be different between @mod and @rem,
12245 // then emit a compile error saying you have to pick one.
12246 if (is_int) {
12247 if (maybe_lhs_val) |lhs_val| {
12248 if (lhs_val.isUndef()) {
12249 return sema.failWithUseOfUndef(block, lhs_src);
12250 }
12251 if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
12252 return sema.addConstant(resolved_type, Value.zero);
12253 }
12254 } else if (lhs_scalar_ty.isSignedInt()) {
12255 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
12256 }
12257 if (maybe_rhs_val) |rhs_val| {
12258 if (rhs_val.isUndef()) {
12259 return sema.failWithUseOfUndef(block, rhs_src);
12260 }
12261 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
12262 return sema.failWithDivideByZero(block, rhs_src);
12263 }
12264 if (maybe_lhs_val) |lhs_val| {
12265 const rem_result = try lhs_val.intRem(rhs_val, resolved_type, sema.arena, target);
12266 // If this answer could possibly be different by doing `intMod`,
12267 // we must emit a compile error. Otherwise, it's OK.
12268 if ((try rhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) != (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) and
12269 !(try rem_result.compareWithZeroAdvanced(.eq, sema.kit(block, src))))
12270 {
12271 const bad_src = if (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src)))
12272 lhs_src
12273 else
12274 rhs_src;
12275 return sema.failWithModRemNegative(block, bad_src, lhs_ty, rhs_ty);
12276 }
12277 if (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) {
12278 // Negative
12279 return sema.addConstant(resolved_type, Value.zero);
12280 }
12281 return sema.addConstant(resolved_type, rem_result);
12282 }
12283 break :rs .{ .src = lhs_src, .air_tag = .rem };
12284 } else if (rhs_scalar_ty.isSignedInt()) {
12285 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
12286 } else {
12287 break :rs .{ .src = rhs_src, .air_tag = .rem };
12288 }
12289 }
12290 // float operands
12291 if (maybe_rhs_val) |rhs_val| {
12292 if (rhs_val.isUndef()) {
12293 return sema.failWithUseOfUndef(block, rhs_src);
12294 }
12295 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
12296 return sema.failWithDivideByZero(block, rhs_src);
12297 }
12298 if (try rhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) {
12299 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
12300 }
12301 if (maybe_lhs_val) |lhs_val| {
12302 if (lhs_val.isUndef() or (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src)))) {
12303 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
12304 }
12305 return sema.addConstant(
12306 resolved_type,
12307 try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target),
12308 );
12309 } else {
12310 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
12311 }
12312 } else {
12313 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
12314 }
12315 },
12316 .rem => {
12317 // For integers:
12318 // Either operand being undef is a compile error because there exists
12319 // a possible value (TODO what is it?) that would invoke illegal behavior.
12320 // TODO: can lhs zero be handled better?
12321 // TODO: can lhs undef be handled better?
12322 //
12323 // For floats:
12324 // If the rhs is zero, compile error for division by zero.
12325 // If the rhs is undefined, compile error because there is a possible
12326 // value (zero) for which the division would be illegal behavior.
12327 // If the lhs is undefined, result is undefined.
12328 if (is_int) {
12329 if (maybe_lhs_val) |lhs_val| {
12330 if (lhs_val.isUndef()) {
12331 return sema.failWithUseOfUndef(block, lhs_src);
12332 }
12333 }
12334 if (maybe_rhs_val) |rhs_val| {
12335 if (rhs_val.isUndef()) {
12336 return sema.failWithUseOfUndef(block, rhs_src);
12337 }
12338 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
12339 return sema.failWithDivideByZero(block, rhs_src);
12340 }
12341 if (maybe_lhs_val) |lhs_val| {
12342 return sema.addConstant(
12343 resolved_type,
12344 try lhs_val.intRem(rhs_val, resolved_type, sema.arena, target),
12345 );
12346 }
12347 break :rs .{ .src = lhs_src, .air_tag = .rem };
12348 } else {
12349 break :rs .{ .src = rhs_src, .air_tag = .rem };
12350 }
12351 }
12352 // float operands
12353 if (maybe_rhs_val) |rhs_val| {
12354 if (rhs_val.isUndef()) {
12355 return sema.failWithUseOfUndef(block, rhs_src);
12356 }
12357 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
12358 return sema.failWithDivideByZero(block, rhs_src);
12359 }
12360 }
12361 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .rem_optimized else .rem;
12362 if (maybe_lhs_val) |lhs_val| {
12363 if (lhs_val.isUndef()) {
12364 return sema.addConstUndef(resolved_type);
12365 }
12366 if (maybe_rhs_val) |rhs_val| {
12367 return sema.addConstant(
12368 resolved_type,
12369 try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target),
12370 );
12371 } else break :rs .{ .src = rhs_src, .air_tag = air_tag };
12372 } else break :rs .{ .src = lhs_src, .air_tag = air_tag };
12373 },
12374 .mod => {
12375 // For integers:
12376 // Either operand being undef is a compile error because there exists
12377 // a possible value (TODO what is it?) that would invoke illegal behavior.
12378 // TODO: can lhs zero be handled better?
12379 // TODO: can lhs undef be handled better?
12380 //
12381 // For floats:
12382 // If the rhs is zero, compile error for division by zero.
12383 // If the rhs is undefined, compile error because there is a possible
12384 // value (zero) for which the division would be illegal behavior.
12385 // If the lhs is undefined, result is undefined.
12386 if (is_int) {
12387 if (maybe_lhs_val) |lhs_val| {
12388 if (lhs_val.isUndef()) {
12389 return sema.failWithUseOfUndef(block, lhs_src);
12390 }
12391 }
12392 if (maybe_rhs_val) |rhs_val| {
12393 if (rhs_val.isUndef()) {
12394 return sema.failWithUseOfUndef(block, rhs_src);
12395 }
12396 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
12397 return sema.failWithDivideByZero(block, rhs_src);
12398 }
12399 if (maybe_lhs_val) |lhs_val| {
12400 return sema.addConstant(
12401 resolved_type,
12402 try lhs_val.intMod(rhs_val, resolved_type, sema.arena, target),
12403 );
12404 }
12405 break :rs .{ .src = lhs_src, .air_tag = .mod };
12406 } else {
12407 break :rs .{ .src = rhs_src, .air_tag = .mod };
12408 }
12409 }
12410 // float operands
12411 if (maybe_rhs_val) |rhs_val| {
12412 if (rhs_val.isUndef()) {
12413 return sema.failWithUseOfUndef(block, rhs_src);
12414 }
12415 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
12416 return sema.failWithDivideByZero(block, rhs_src);
12417 }
12418 }
12419 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .mod_optimized else .mod;
12420 if (maybe_lhs_val) |lhs_val| {
12421 if (lhs_val.isUndef()) {
12422 return sema.addConstUndef(resolved_type);
12423 }
12424 if (maybe_rhs_val) |rhs_val| {
12425 return sema.addConstant(
12426 resolved_type,
12427 try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, target),
12428 );
12429 } else break :rs .{ .src = rhs_src, .air_tag = air_tag };
12430 } else break :rs .{ .src = lhs_src, .air_tag = air_tag };
12431 },
12432 else => unreachable,12566 else => unreachable,
12433 }12567 }
12434 };12568 };
...@@ -12472,33 +12606,6 @@ fn analyzeArithmetic(...@@ -12472,33 +12606,6 @@ fn analyzeArithmetic(
12472 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);12606 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
12473 }12607 }
12474 }12608 }
12475 switch (rs.air_tag) {
12476 .rem, .mod, .rem_optimized, .mod_optimized => {
12477 const ok = if (resolved_type.zigTypeTag() == .Vector) ok: {
12478 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
12479 const zero = try sema.addConstant(sema.typeOf(casted_rhs), zero_val);
12480 const ok = try block.addCmpVector(casted_rhs, zero, if (scalar_tag == .Int) .gt else .neq, try sema.addType(resolved_type));
12481 break :ok try block.addInst(.{
12482 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
12483 .data = .{ .reduce = .{
12484 .operand = ok,
12485 .operation = .And,
12486 } },
12487 });
12488 } else ok: {
12489 const zero = try sema.addConstant(sema.typeOf(casted_rhs), Value.zero);
12490 const air_tag = if (scalar_tag == .Int)
12491 Air.Inst.Tag.cmp_gt
12492 else if (block.float_mode == .Optimized)
12493 Air.Inst.Tag.cmp_neq_optimized
12494 else
12495 Air.Inst.Tag.cmp_neq;
12496 break :ok try block.addBinOp(air_tag, casted_rhs, zero);
12497 };
12498 try sema.addSafetyCheck(block, ok, .remainder_division_zero_negative);
12499 },
12500 else => {},
12501 }
12502 }12609 }
12503 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);12610 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
12504}12611}
...@@ -19965,7 +20072,6 @@ pub const PanicId = enum {...@@ -19965,7 +20072,6 @@ pub const PanicId = enum {
19965 shl_overflow,20072 shl_overflow,
19966 shr_overflow,20073 shr_overflow,
19967 divide_by_zero,20074 divide_by_zero,
19968 remainder_division_zero_negative,
19969 exact_division_remainder,20075 exact_division_remainder,
19970 /// TODO make this call `std.builtin.panicInactiveUnionField`.20076 /// TODO make this call `std.builtin.panicInactiveUnionField`.
19971 inactive_union_field,20077 inactive_union_field,
...@@ -20261,7 +20367,6 @@ fn safetyPanic(...@@ -20261,7 +20367,6 @@ fn safetyPanic(
20261 .shl_overflow => "left shift overflowed bits",20367 .shl_overflow => "left shift overflowed bits",
20262 .shr_overflow => "right shift overflowed bits",20368 .shr_overflow => "right shift overflowed bits",
20263 .divide_by_zero => "division by zero",20369 .divide_by_zero => "division by zero",
20264 .remainder_division_zero_negative => "remainder division by zero or negative value",
20265 .exact_division_remainder => "exact division produced remainder",20370 .exact_division_remainder => "exact division produced remainder",
20266 .inactive_union_field => "access of inactive union field",20371 .inactive_union_field => "access of inactive union field",
20267 .integer_part_out_of_bounds => "integer part of floating point value out of bounds",20372 .integer_part_out_of_bounds => "integer part of floating point value out of bounds",
test/cases/safety/modrem by zero.zig created+20
...@@ -0,0 +1,20 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "division by zero")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10pub fn main() !void {
11 const x = div0(999, 0);
12 _ = x;
13 return error.TestFailed;
14}
15fn div0(a: u32, b: u32) u32 {
16 return a / b;
17}
18// run
19// backend=llvm
20// target=native
test/cases/safety/modulus by zero.zig created+20
...@@ -0,0 +1,20 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "division by zero")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10pub fn main() !void {
11 const x = mod0(999, 0);
12 _ = x;
13 return error.TestFailed;
14}
15fn mod0(a: i32, b: i32) i32 {
16 return @mod(a, b);
17}
18// run
19// backend=llvm
20// target=native
test/cases/safety/remainder division by negative number.zig deleted-20
...@@ -1,20 +0,0 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "remainder division by zero or negative value")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10pub fn main() !void {
11 const x = div0(999, -1);
12 _ = x;
13 return error.TestFailed;
14}
15fn div0(a: i32, b: i32) i32 {
16 return @rem(a, b);
17}
18// run
19// backend=llvm
20// target=native
test/cases/safety/remainder division by zero.zig created+20
...@@ -0,0 +1,20 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "division by zero")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10pub fn main() !void {
11 const x = rem0(999, 0);
12 _ = x;
13 return error.TestFailed;
14}
15fn rem0(a: i32, b: i32) i32 {
16 return @rem(a, b);
17}
18// run
19// backend=llvm
20// target=native