| ... | ... | @@ -875,7 +875,7 @@ fn analyzeBodyInner( |
| 875 | 875 | .add => try sema.zirArithmetic(block, inst, .add), |
| 876 | 876 | .addwrap => try sema.zirArithmetic(block, inst, .addwrap), |
| 877 | 877 | .add_sat => try sema.zirArithmetic(block, inst, .add_sat), |
| 878 | | .div => try sema.zirArithmetic(block, inst, .div), |
| 878 | .div => try sema.zirDiv(block, inst), |
| 879 | 879 | .div_exact => try sema.zirArithmetic(block, inst, .div_exact), |
| 880 | 880 | .div_floor => try sema.zirArithmetic(block, inst, .div_floor), |
| 881 | 881 | .div_trunc => try sema.zirArithmetic(block, inst, .div_trunc), |
| ... | ... | @@ -10920,6 +10920,243 @@ fn zirArithmetic( |
| 10920 | 10920 | return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src); |
| 10921 | 10921 | } |
| 10922 | 10922 | |
| 10923 | fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 10924 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 10925 | const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; |
| 10926 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; |
| 10927 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; |
| 10928 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 10929 | const lhs = try sema.resolveInst(extra.lhs); |
| 10930 | const rhs = try sema.resolveInst(extra.rhs); |
| 10931 | const lhs_ty = sema.typeOf(lhs); |
| 10932 | const rhs_ty = sema.typeOf(rhs); |
| 10933 | const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); |
| 10934 | const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); |
| 10935 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 10936 | try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div); |
| 10937 | |
| 10938 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 10939 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ |
| 10940 | .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, |
| 10941 | }); |
| 10942 | |
| 10943 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 10944 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 10945 | |
| 10946 | const lhs_scalar_ty = lhs_ty.scalarType(); |
| 10947 | const rhs_scalar_ty = rhs_ty.scalarType(); |
| 10948 | const scalar_tag = resolved_type.scalarType().zigTypeTag(); |
| 10949 | |
| 10950 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; |
| 10951 | |
| 10952 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div); |
| 10953 | |
| 10954 | const mod = sema.mod; |
| 10955 | const target = mod.getTarget(); |
| 10956 | const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs); |
| 10957 | const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs); |
| 10958 | |
| 10959 | // TODO: emit compile error when .div is used on integers and there would be an |
| 10960 | // ambiguous result between div_floor and div_trunc. |
| 10961 | |
| 10962 | // For integers: |
| 10963 | // If the lhs is zero, then zero is returned regardless of rhs. |
| 10964 | // If the rhs is zero, compile error for division by zero. |
| 10965 | // If the rhs is undefined, compile error because there is a possible |
| 10966 | // value (zero) for which the division would be illegal behavior. |
| 10967 | // If the lhs is undefined: |
| 10968 | // * if lhs type is signed: |
| 10969 | // * if rhs is comptime-known and not -1, result is undefined |
| 10970 | // * if rhs is -1 or runtime-known, compile error because there is a |
| 10971 | // possible value (-min_int / -1) for which division would be |
| 10972 | // illegal behavior. |
| 10973 | // * if lhs type is unsigned, undef is returned regardless of rhs. |
| 10974 | // |
| 10975 | // For floats: |
| 10976 | // If the rhs is zero: |
| 10977 | // * comptime_float: compile error for division by zero. |
| 10978 | // * other float type: |
| 10979 | // * if the lhs is zero: QNaN |
| 10980 | // * otherwise: +Inf or -Inf depending on lhs sign |
| 10981 | // If the rhs is undefined: |
| 10982 | // * comptime_float: compile error because there is a possible |
| 10983 | // value (zero) for which the division would be illegal behavior. |
| 10984 | // * other float type: result is undefined |
| 10985 | // If the lhs is undefined, result is undefined. |
| 10986 | switch (scalar_tag) { |
| 10987 | .Int, .ComptimeInt, .ComptimeFloat => { |
| 10988 | if (maybe_lhs_val) |lhs_val| { |
| 10989 | if (!lhs_val.isUndef()) { |
| 10990 | if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 10991 | return sema.addConstant(resolved_type, Value.zero); |
| 10992 | } |
| 10993 | } |
| 10994 | } |
| 10995 | if (maybe_rhs_val) |rhs_val| { |
| 10996 | if (rhs_val.isUndef()) { |
| 10997 | return sema.failWithUseOfUndef(block, rhs_src); |
| 10998 | } |
| 10999 | if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11000 | return sema.failWithDivideByZero(block, rhs_src); |
| 11001 | } |
| 11002 | } |
| 11003 | }, |
| 11004 | else => {}, |
| 11005 | } |
| 11006 | |
| 11007 | const runtime_src = rs: { |
| 11008 | if (maybe_lhs_val) |lhs_val| { |
| 11009 | if (lhs_val.isUndef()) { |
| 11010 | if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { |
| 11011 | if (maybe_rhs_val) |rhs_val| { |
| 11012 | if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) { |
| 11013 | return sema.addConstUndef(resolved_type); |
| 11014 | } |
| 11015 | } |
| 11016 | return sema.failWithUseOfUndef(block, rhs_src); |
| 11017 | } |
| 11018 | return sema.addConstUndef(resolved_type); |
| 11019 | } |
| 11020 | |
| 11021 | if (maybe_rhs_val) |rhs_val| { |
| 11022 | if (is_int) { |
| 11023 | return sema.addConstant( |
| 11024 | resolved_type, |
| 11025 | try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target), |
| 11026 | ); |
| 11027 | } else { |
| 11028 | return sema.addConstant( |
| 11029 | resolved_type, |
| 11030 | try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target), |
| 11031 | ); |
| 11032 | } |
| 11033 | } else { |
| 11034 | break :rs rhs_src; |
| 11035 | } |
| 11036 | } else { |
| 11037 | break :rs lhs_src; |
| 11038 | } |
| 11039 | }; |
| 11040 | |
| 11041 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 11042 | |
| 11043 | if (block.wantSafety()) { |
| 11044 | int_overflow: { |
| 11045 | if (!is_int) break :int_overflow; |
| 11046 | |
| 11047 | // If the LHS is unsigned, it cannot cause overflow. |
| 11048 | if (!lhs_scalar_ty.isSignedInt()) break :int_overflow; |
| 11049 | |
| 11050 | // If the LHS is widened to a larger integer type, no overflow is possible. |
| 11051 | if (lhs_scalar_ty.intInfo(target).bits < resolved_type.intInfo(target).bits) { |
| 11052 | break :int_overflow; |
| 11053 | } |
| 11054 | |
| 11055 | const min_int = try resolved_type.minInt(sema.arena, target); |
| 11056 | const neg_one = try Value.Tag.int_i64.create(sema.arena, -1); |
| 11057 | |
| 11058 | // If the LHS is comptime-known to be not equal to the min int, |
| 11059 | // no overflow is possible. |
| 11060 | if (maybe_lhs_val) |lhs_val| { |
| 11061 | if (!lhs_val.compare(.eq, min_int, resolved_type, mod)) break :int_overflow; |
| 11062 | } |
| 11063 | |
| 11064 | // If the RHS is comptime-known to not be equal to -1, no overflow is possible. |
| 11065 | if (maybe_rhs_val) |rhs_val| { |
| 11066 | if (!rhs_val.compare(.eq, neg_one, resolved_type, mod)) break :int_overflow; |
| 11067 | } |
| 11068 | |
| 11069 | var ok: Air.Inst.Ref = .none; |
| 11070 | if (resolved_type.zigTypeTag() == .Vector) { |
| 11071 | const vector_ty_ref = try sema.addType(resolved_type); |
| 11072 | if (maybe_lhs_val == null) { |
| 11073 | const min_int_ref = try sema.addConstant( |
| 11074 | resolved_type, |
| 11075 | try Value.Tag.repeated.create(sema.arena, min_int), |
| 11076 | ); |
| 11077 | ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq, vector_ty_ref); |
| 11078 | } |
| 11079 | if (maybe_rhs_val == null) { |
| 11080 | const neg_one_ref = try sema.addConstant( |
| 11081 | resolved_type, |
| 11082 | try Value.Tag.repeated.create(sema.arena, neg_one), |
| 11083 | ); |
| 11084 | const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq, vector_ty_ref); |
| 11085 | if (ok == .none) { |
| 11086 | ok = rhs_ok; |
| 11087 | } else { |
| 11088 | ok = try block.addBinOp(.bool_or, ok, rhs_ok); |
| 11089 | } |
| 11090 | } |
| 11091 | assert(ok != .none); |
| 11092 | ok = try block.addInst(.{ |
| 11093 | .tag = .reduce, |
| 11094 | .data = .{ .reduce = .{ |
| 11095 | .operand = ok, |
| 11096 | .operation = .And, |
| 11097 | } }, |
| 11098 | }); |
| 11099 | } else { |
| 11100 | if (maybe_lhs_val == null) { |
| 11101 | const min_int_ref = try sema.addConstant(resolved_type, min_int); |
| 11102 | ok = try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref); |
| 11103 | } |
| 11104 | if (maybe_rhs_val == null) { |
| 11105 | const neg_one_ref = try sema.addConstant(resolved_type, neg_one); |
| 11106 | const rhs_ok = try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref); |
| 11107 | if (ok == .none) { |
| 11108 | ok = rhs_ok; |
| 11109 | } else { |
| 11110 | ok = try block.addBinOp(.bool_or, ok, rhs_ok); |
| 11111 | } |
| 11112 | } |
| 11113 | assert(ok != .none); |
| 11114 | } |
| 11115 | try sema.addSafetyCheck(block, ok, .integer_overflow); |
| 11116 | } |
| 11117 | |
| 11118 | div_by_zero: { |
| 11119 | // Strict IEEE floats have well-defined division by zero. |
| 11120 | if (!is_int and block.float_mode == .Strict) break :div_by_zero; |
| 11121 | |
| 11122 | // If rhs was comptime-known to be zero a compile error would have been |
| 11123 | // emitted above. |
| 11124 | if (maybe_rhs_val != null) break :div_by_zero; |
| 11125 | |
| 11126 | const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { |
| 11127 | const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); |
| 11128 | const zero = try sema.addConstant(resolved_type, zero_val); |
| 11129 | const ok = try block.addCmpVector(casted_rhs, zero, .neq, try sema.addType(resolved_type)); |
| 11130 | break :ok try block.addInst(.{ |
| 11131 | .tag = if (is_int) .reduce else .reduce_optimized, |
| 11132 | .data = .{ .reduce = .{ |
| 11133 | .operand = ok, |
| 11134 | .operation = .And, |
| 11135 | } }, |
| 11136 | }); |
| 11137 | } else ok: { |
| 11138 | const zero = try sema.addConstant(resolved_type, Value.zero); |
| 11139 | break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero); |
| 11140 | }; |
| 11141 | try sema.addSafetyCheck(block, ok, .divide_by_zero); |
| 11142 | } |
| 11143 | } |
| 11144 | |
| 11145 | const air_tag = if (is_int) Air.Inst.Tag.div_trunc else switch (block.float_mode) { |
| 11146 | .Optimized => Air.Inst.Tag.div_float_optimized, |
| 11147 | .Strict => Air.Inst.Tag.div_float, |
| 11148 | }; |
| 11149 | return block.addBinOp(air_tag, casted_lhs, casted_rhs); |
| 11150 | } |
| 11151 | |
| 11152 | fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag { |
| 11153 | if (is_int) return normal; |
| 11154 | return switch (block.float_mode) { |
| 11155 | .Strict => normal, |
| 11156 | .Optimized => optimized, |
| 11157 | }; |
| 11158 | } |
| 11159 | |
| 10923 | 11160 | fn zirOverflowArithmetic( |
| 10924 | 11161 | sema: *Sema, |
| 10925 | 11162 | block: *Block, |
| ... | ... | @@ -11399,96 +11636,6 @@ fn analyzeArithmetic( |
| 11399 | 11636 | } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat }; |
| 11400 | 11637 | } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat }; |
| 11401 | 11638 | }, |
| 11402 | | .div => { |
| 11403 | | // TODO: emit compile error when .div is used on integers and there would be an |
| 11404 | | // ambiguous result between div_floor and div_trunc. |
| 11405 | | |
| 11406 | | // For integers: |
| 11407 | | // If the lhs is zero, then zero is returned regardless of rhs. |
| 11408 | | // If the rhs is zero, compile error for division by zero. |
| 11409 | | // If the rhs is undefined, compile error because there is a possible |
| 11410 | | // value (zero) for which the division would be illegal behavior. |
| 11411 | | // If the lhs is undefined: |
| 11412 | | // * if lhs type is signed: |
| 11413 | | // * if rhs is comptime-known and not -1, result is undefined |
| 11414 | | // * if rhs is -1 or runtime-known, compile error because there is a |
| 11415 | | // possible value (-min_int / -1) for which division would be |
| 11416 | | // illegal behavior. |
| 11417 | | // * if lhs type is unsigned, undef is returned regardless of rhs. |
| 11418 | | // TODO: emit runtime safety for division by zero |
| 11419 | | // |
| 11420 | | // For floats: |
| 11421 | | // If the rhs is zero: |
| 11422 | | // * comptime_float: compile error for division by zero. |
| 11423 | | // * other float type: |
| 11424 | | // * if the lhs is zero: QNaN |
| 11425 | | // * otherwise: +Inf or -Inf depending on lhs sign |
| 11426 | | // If the rhs is undefined: |
| 11427 | | // * comptime_float: compile error because there is a possible |
| 11428 | | // value (zero) for which the division would be illegal behavior. |
| 11429 | | // * other float type: result is undefined |
| 11430 | | // If the lhs is undefined, result is undefined. |
| 11431 | | switch (scalar_tag) { |
| 11432 | | .Int, .ComptimeInt, .ComptimeFloat => { |
| 11433 | | if (maybe_lhs_val) |lhs_val| { |
| 11434 | | if (!lhs_val.isUndef()) { |
| 11435 | | if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11436 | | return sema.addConstant(resolved_type, Value.zero); |
| 11437 | | } |
| 11438 | | } |
| 11439 | | } |
| 11440 | | if (maybe_rhs_val) |rhs_val| { |
| 11441 | | if (rhs_val.isUndef()) { |
| 11442 | | return sema.failWithUseOfUndef(block, rhs_src); |
| 11443 | | } |
| 11444 | | if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { |
| 11445 | | return sema.failWithDivideByZero(block, rhs_src); |
| 11446 | | } |
| 11447 | | } |
| 11448 | | }, |
| 11449 | | else => {}, |
| 11450 | | } |
| 11451 | | |
| 11452 | | if (maybe_lhs_val) |lhs_val| { |
| 11453 | | if (lhs_val.isUndef()) { |
| 11454 | | if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { |
| 11455 | | if (maybe_rhs_val) |rhs_val| { |
| 11456 | | if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) { |
| 11457 | | return sema.addConstUndef(resolved_type); |
| 11458 | | } |
| 11459 | | } |
| 11460 | | return sema.failWithUseOfUndef(block, rhs_src); |
| 11461 | | } |
| 11462 | | return sema.addConstUndef(resolved_type); |
| 11463 | | } |
| 11464 | | |
| 11465 | | if (maybe_rhs_val) |rhs_val| { |
| 11466 | | if (is_int) { |
| 11467 | | return sema.addConstant( |
| 11468 | | resolved_type, |
| 11469 | | try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target), |
| 11470 | | ); |
| 11471 | | } else { |
| 11472 | | return sema.addConstant( |
| 11473 | | resolved_type, |
| 11474 | | try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target), |
| 11475 | | ); |
| 11476 | | } |
| 11477 | | } else { |
| 11478 | | if (is_int) { |
| 11479 | | break :rs .{ .src = rhs_src, .air_tag = .div_trunc }; |
| 11480 | | } else { |
| 11481 | | break :rs .{ .src = rhs_src, .air_tag = if (block.float_mode == .Optimized) .div_float_optimized else .div_float }; |
| 11482 | | } |
| 11483 | | } |
| 11484 | | } else { |
| 11485 | | if (is_int) { |
| 11486 | | break :rs .{ .src = lhs_src, .air_tag = .div_trunc }; |
| 11487 | | } else { |
| 11488 | | break :rs .{ .src = lhs_src, .air_tag = if (block.float_mode == .Optimized) .div_float_optimized else .div_float }; |
| 11489 | | } |
| 11490 | | } |
| 11491 | | }, |
| 11492 | 11639 | .div_trunc => { |
| 11493 | 11640 | // For integers: |
| 11494 | 11641 | // If the lhs is zero, then zero is returned regardless of rhs. |
| ... | ... | @@ -16804,6 +16951,46 @@ fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileEr |
| 16804 | 16951 | } |
| 16805 | 16952 | } |
| 16806 | 16953 | |
| 16954 | fn checkInvalidPtrArithmetic( |
| 16955 | sema: *Sema, |
| 16956 | block: *Block, |
| 16957 | src: LazySrcLoc, |
| 16958 | ty: Type, |
| 16959 | zir_tag: Zir.Inst.Tag, |
| 16960 | ) CompileError!void { |
| 16961 | switch (try ty.zigTypeTagOrPoison()) { |
| 16962 | .Pointer => switch (ty.ptrSize()) { |
| 16963 | .One, .Slice => return, |
| 16964 | .Many, .C => return sema.fail( |
| 16965 | block, |
| 16966 | src, |
| 16967 | "invalid pointer arithmetic operand: '{s}''", |
| 16968 | .{@tagName(zir_tag)}, |
| 16969 | ), |
| 16970 | }, |
| 16971 | else => return, |
| 16972 | } |
| 16973 | } |
| 16974 | |
| 16975 | fn checkArithmeticOp( |
| 16976 | sema: *Sema, |
| 16977 | block: *Block, |
| 16978 | src: LazySrcLoc, |
| 16979 | scalar_tag: std.builtin.TypeId, |
| 16980 | lhs_zig_ty_tag: std.builtin.TypeId, |
| 16981 | rhs_zig_ty_tag: std.builtin.TypeId, |
| 16982 | zir_tag: Zir.Inst.Tag, |
| 16983 | ) CompileError!void { |
| 16984 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; |
| 16985 | const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; |
| 16986 | |
| 16987 | if (!is_int and !(is_float and floatOpAllowed(zir_tag))) { |
| 16988 | return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ |
| 16989 | @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag), |
| 16990 | }); |
| 16991 | } |
| 16992 | } |
| 16993 | |
| 16807 | 16994 | fn checkPtrOperand( |
| 16808 | 16995 | sema: *Sema, |
| 16809 | 16996 | block: *Block, |