| ... | ... | @@ -876,9 +876,6 @@ fn analyzeBodyInner( |
| 876 | 876 | .add => try sema.zirArithmetic(block, inst, .add), |
| 877 | 877 | .addwrap => try sema.zirArithmetic(block, inst, .addwrap), |
| 878 | 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 | 879 | .mul => try sema.zirArithmetic(block, inst, .mul), |
| 883 | 880 | .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap), |
| 884 | 881 | .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat), |
| ... | ... | @@ -891,6 +888,10 @@ fn analyzeBodyInner( |
| 891 | 888 | .div_floor => try sema.zirDivFloor(block, inst), |
| 892 | 889 | .div_trunc => try sema.zirDivTrunc(block, inst), |
| 893 | 890 | |
| 891 | .mod_rem => try sema.zirModRem(block, inst), |
| 892 | .mod => try sema.zirMod(block, inst), |
| 893 | .rem => try sema.zirRem(block, inst), |
| 894 | |
| 894 | 895 | .maximum => try sema.zirMinMax(block, inst, .max), |
| 895 | 896 | .minimum => try sema.zirMinMax(block, inst, .min), |
| 896 | 897 | |
| ... | ... | @@ -11621,6 +11622,341 @@ fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst |
| 11621 | 11622 | }; |
| 11622 | 11623 | } |
| 11623 | 11624 | |
| 11625 | fn 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 | |
| 11756 | fn 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 | |
| 11858 | fn 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 | |
| 11624 | 11960 | fn zirOverflowArithmetic( |
| 11625 | 11961 | sema: *Sema, |
| 11626 | 11962 | block: *Block, |
| ... | ... | @@ -11882,8 +12218,6 @@ fn analyzeArithmetic( |
| 11882 | 12218 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 11883 | 12219 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 11884 | 12220 | |
| 11885 | | const lhs_scalar_ty = lhs_ty.scalarType(); |
| 11886 | | const rhs_scalar_ty = rhs_ty.scalarType(); |
| 11887 | 12221 | const scalar_tag = resolved_type.scalarType().zigTypeTag(); |
| 11888 | 12222 | |
| 11889 | 12223 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; |
| ... | ... | @@ -12229,206 +12563,6 @@ fn analyzeArithmetic( |
| 12229 | 12563 | } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat }; |
| 12230 | 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 | 12566 | else => unreachable, |
| 12433 | 12567 | } |
| 12434 | 12568 | }; |
| ... | ... | @@ -12472,33 +12606,6 @@ fn analyzeArithmetic( |
| 12472 | 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 | 12610 | return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); |
| 12504 | 12611 | } |
| ... | ... | @@ -19965,7 +20072,6 @@ pub const PanicId = enum { |
| 19965 | 20072 | shl_overflow, |
| 19966 | 20073 | shr_overflow, |
| 19967 | 20074 | divide_by_zero, |
| 19968 | | remainder_division_zero_negative, |
| 19969 | 20075 | exact_division_remainder, |
| 19970 | 20076 | /// TODO make this call `std.builtin.panicInactiveUnionField`. |
| 19971 | 20077 | inactive_union_field, |
| ... | ... | @@ -20261,7 +20367,6 @@ fn safetyPanic( |
| 20261 | 20367 | .shl_overflow => "left shift overflowed bits", |
| 20262 | 20368 | .shr_overflow => "right shift overflowed bits", |
| 20263 | 20369 | .divide_by_zero => "division by zero", |
| 20264 | | .remainder_division_zero_negative => "remainder division by zero or negative value", |
| 20265 | 20370 | .exact_division_remainder => "exact division produced remainder", |
| 20266 | 20371 | .inactive_union_field => "access of inactive union field", |
| 20267 | 20372 | .integer_part_out_of_bounds => "integer part of floating point value out of bounds", |