authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-29 11:03:27-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-07-29 11:03:27-07:00
log64dc1b05d6ea6f1a6c275d33800e8fc2454aab5d
treea61a5e356f7d4f8165745f7f362aa242750a5e27
parentfdaf9c40d6a351477aacb1af27871f3de12d485e
parent932d1f785ea4ca2b6d0094e0f2217eb3e335b0d7
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12286 from ziglang/division-safety

Division safety improvements

8 files changed, 887 insertions(+), 565 deletions(-)

ci/zinc/linux_test.sh+1-2
......@@ -63,8 +63,7 @@ stage3/bin/zig build test-translate-c -fqemu -fwasmtime -Denable-llvm
6363stage3/bin/zig build test-run-translated-c -fqemu -fwasmtime -Denable-llvm
6464stage3/bin/zig build test-standalone -fqemu -fwasmtime -Denable-llvm
6565stage3/bin/zig build test-cli -fqemu -fwasmtime -Denable-llvm
66# https://github.com/ziglang/zig/issues/12144
67stage3/bin/zig build test-cases -fqemu -fwasmtime
66stage3/bin/zig build test-cases -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
6867stage3/bin/zig build test-link -fqemu -fwasmtime -Denable-llvm
6968
7069$STAGE1_ZIG build test-stack-traces -fqemu -fwasmtime
lib/std/debug.zig+2
......@@ -1784,6 +1784,7 @@ pub fn updateSegfaultHandler(act: ?*const os.Sigaction) error{OperationNotSuppor
17841784 try os.sigaction(os.SIG.SEGV, act, null);
17851785 try os.sigaction(os.SIG.ILL, act, null);
17861786 try os.sigaction(os.SIG.BUS, act, null);
1787 try os.sigaction(os.SIG.FPE, act, null);
17871788}
17881789
17891790/// Attaches a global SIGSEGV handler which calls @panic("segmentation fault");
......@@ -1845,6 +1846,7 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any
18451846 os.SIG.SEGV => stderr.print("Segmentation fault at address 0x{x}\n", .{addr}),
18461847 os.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}),
18471848 os.SIG.BUS => stderr.print("Bus error at address 0x{x}\n", .{addr}),
1849 os.SIG.FPE => stderr.print("Arithmetic exception at address 0x{x}\n", .{addr}),
18481850 else => unreachable,
18491851 } catch os.abort();
18501852 }
src/Air.zig+3-2
......@@ -111,8 +111,9 @@ pub const Inst = struct {
111111 div_floor,
112112 /// Same as `div_floor` with optimized float mode.
113113 div_floor_optimized,
114 /// Integer or float division. Guaranteed no remainder.
115 /// For integers, wrapping is undefined behavior.
114 /// Integer or float division.
115 /// If a remainder would be produced, undefined behavior occurs.
116 /// For integers, overflow is undefined behavior.
116117 /// Both operands are guaranteed to be the same type, and the result type
117118 /// is the same as both operands.
118119 /// Uses the `bin_op` field.
src/Sema.zig+865-533
......@@ -875,10 +875,6 @@ fn analyzeBodyInner(
875875 .add => try sema.zirArithmetic(block, inst, .add),
876876 .addwrap => try sema.zirArithmetic(block, inst, .addwrap),
877877 .add_sat => try sema.zirArithmetic(block, inst, .add_sat),
878 .div => try sema.zirArithmetic(block, inst, .div),
879 .div_exact => try sema.zirArithmetic(block, inst, .div_exact),
880 .div_floor => try sema.zirArithmetic(block, inst, .div_floor),
881 .div_trunc => try sema.zirArithmetic(block, inst, .div_trunc),
882878 .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),
883879 .mod => try sema.zirArithmetic(block, inst, .mod),
884880 .rem => try sema.zirArithmetic(block, inst, .rem),
......@@ -889,6 +885,11 @@ fn analyzeBodyInner(
889885 .subwrap => try sema.zirArithmetic(block, inst, .subwrap),
890886 .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat),
891887
888 .div => try sema.zirDiv(block, inst),
889 .div_exact => try sema.zirDivExact(block, inst),
890 .div_floor => try sema.zirDivFloor(block, inst),
891 .div_trunc => try sema.zirDivTrunc(block, inst),
892
892893 .maximum => try sema.zirMinMax(block, inst, .max),
893894 .minimum => try sema.zirMinMax(block, inst, .min),
894895
......@@ -10920,226 +10921,856 @@ fn zirArithmetic(
1092010921 return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src);
1092110922}
1092210923
10923fn zirOverflowArithmetic(
10924 sema: *Sema,
10925 block: *Block,
10926 extended: Zir.Inst.Extended.InstData,
10927 zir_tag: Zir.Inst.Extended,
10928) CompileError!Air.Inst.Ref {
10929 const tracy = trace(@src());
10930 defer tracy.end();
10931
10932 const extra = sema.code.extraData(Zir.Inst.OverflowArithmetic, extended.operand).data;
10933 const src = LazySrcLoc.nodeOffset(extra.node);
10934
10935 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
10936 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
10937 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node };
10938
10924fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
10925 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
10926 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
10927 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
10928 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
10929 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
1093910930 const lhs = try sema.resolveInst(extra.lhs);
1094010931 const rhs = try sema.resolveInst(extra.rhs);
10941 const ptr = try sema.resolveInst(extra.ptr);
10942
1094310932 const lhs_ty = sema.typeOf(lhs);
1094410933 const rhs_ty = sema.typeOf(rhs);
10945 const mod = sema.mod;
10946 const target = mod.getTarget();
10947
10948 // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen.
10934 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
10935 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
1094910936 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
10950 const dest_ty = lhs_ty;
10951 if (dest_ty.scalarType().zigTypeTag() != .Int) {
10952 return sema.fail(block, src, "expected vector of integers or integer tag type, found '{}'", .{dest_ty.fmt(mod)});
10953 }
10937 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div);
1095410938
10955 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
10956 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
10939 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
10940 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
10941 .override = &[_]LazySrcLoc{ lhs_src, rhs_src },
10942 });
1095710943
10958 const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty);
10959 const ov_ty = tuple_ty.tupleFields().types[1];
10960 // TODO: Remove and use `ov_ty` instead.
10961 // This is a temporary type used until overflow arithmetic properly returns `u1` instead of `bool`.
10962 const overflowed_ty = if (dest_ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, dest_ty.vectorLen(), Type.@"bool") else Type.@"bool";
10944 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
10945 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
1096310946
10964 const result: struct {
10965 /// TODO: Rename to `overflow_bit` and make of type `u1`.
10966 overflowed: Air.Inst.Ref,
10967 wrapped: Air.Inst.Ref,
10968 } = result: {
10969 switch (zir_tag) {
10970 .add_with_overflow => {
10971 // If either of the arguments is zero, `false` is returned and the other is stored
10972 // to the result, even if it is undefined..
10973 // Otherwise, if either of the argument is undefined, undefined is returned.
10974 if (maybe_lhs_val) |lhs_val| {
10975 if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
10976 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
10977 }
10978 }
10979 if (maybe_rhs_val) |rhs_val| {
10980 if (!rhs_val.isUndef() and (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
10981 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
10982 }
10983 }
10984 if (maybe_lhs_val) |lhs_val| {
10985 if (maybe_rhs_val) |rhs_val| {
10986 if (lhs_val.isUndef() or rhs_val.isUndef()) {
10987 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
10988 }
10947 const lhs_scalar_ty = lhs_ty.scalarType();
10948 const rhs_scalar_ty = rhs_ty.scalarType();
10949 const scalar_tag = resolved_type.scalarType().zigTypeTag();
1098910950
10990 const result = try sema.intAddWithOverflow(block, src, lhs_val, rhs_val, dest_ty);
10991 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
10992 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
10993 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
10994 }
10995 }
10996 },
10997 .sub_with_overflow => {
10998 // If the rhs is zero, then the result is lhs and no overflow occured.
10999 // Otherwise, if either result is undefined, both results are undefined.
11000 if (maybe_rhs_val) |rhs_val| {
11001 if (rhs_val.isUndef()) {
11002 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
11003 } else if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11004 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
11005 } else if (maybe_lhs_val) |lhs_val| {
11006 if (lhs_val.isUndef()) {
11007 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
11008 }
10951 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
1100910952
11010 const result = try sema.intSubWithOverflow(block, src, lhs_val, rhs_val, dest_ty);
11011 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
11012 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
11013 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
10953 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div);
10954
10955 const mod = sema.mod;
10956 const target = mod.getTarget();
10957 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs);
10958 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs);
10959
10960 // TODO: emit compile error when .div is used on integers and there would be an
10961 // ambiguous result between div_floor and div_trunc.
10962
10963 // For integers:
10964 // If the lhs is zero, then zero is returned regardless of rhs.
10965 // If the rhs is zero, compile error for division by zero.
10966 // If the rhs is undefined, compile error because there is a possible
10967 // value (zero) for which the division would be illegal behavior.
10968 // If the lhs is undefined:
10969 // * if lhs type is signed:
10970 // * if rhs is comptime-known and not -1, result is undefined
10971 // * if rhs is -1 or runtime-known, compile error because there is a
10972 // possible value (-min_int / -1) for which division would be
10973 // illegal behavior.
10974 // * if lhs type is unsigned, undef is returned regardless of rhs.
10975 //
10976 // For floats:
10977 // If the rhs is zero:
10978 // * comptime_float: compile error for division by zero.
10979 // * other float type:
10980 // * if the lhs is zero: QNaN
10981 // * otherwise: +Inf or -Inf depending on lhs sign
10982 // If the rhs is undefined:
10983 // * comptime_float: compile error because there is a possible
10984 // value (zero) for which the division would be illegal behavior.
10985 // * other float type: result is undefined
10986 // If the lhs is undefined, result is undefined.
10987 switch (scalar_tag) {
10988 .Int, .ComptimeInt, .ComptimeFloat => {
10989 if (maybe_lhs_val) |lhs_val| {
10990 if (!lhs_val.isUndef()) {
10991 if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
10992 return sema.addConstant(resolved_type, Value.zero);
1101410993 }
1101510994 }
11016 },
11017 .mul_with_overflow => {
11018 // If either of the arguments is zero, the result is zero and no overflow occured.
11019 // If either of the arguments is one, the result is the other and no overflow occured.
11020 // Otherwise, if either of the arguments is undefined, both results are undefined.
11021 if (maybe_lhs_val) |lhs_val| {
11022 if (!lhs_val.isUndef()) {
11023 if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11024 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
11025 } else if (try sema.compare(block, src, lhs_val, .eq, Value.one, dest_ty)) {
11026 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
11027 }
11028 }
10995 }
10996 if (maybe_rhs_val) |rhs_val| {
10997 if (rhs_val.isUndef()) {
10998 return sema.failWithUseOfUndef(block, rhs_src);
1102910999 }
11030
11031 if (maybe_rhs_val) |rhs_val| {
11032 if (!rhs_val.isUndef()) {
11033 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11034 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
11035 } else if (try sema.compare(block, src, rhs_val, .eq, Value.one, dest_ty)) {
11036 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
11037 }
11038 }
11000 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11001 return sema.failWithDivideByZero(block, rhs_src);
1103911002 }
11003 // TODO: if the RHS is one, return the LHS directly
11004 }
11005 },
11006 else => {},
11007 }
1104011008
11041 if (maybe_lhs_val) |lhs_val| {
11009 const runtime_src = rs: {
11010 if (maybe_lhs_val) |lhs_val| {
11011 if (lhs_val.isUndef()) {
11012 if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
1104211013 if (maybe_rhs_val) |rhs_val| {
11043 if (lhs_val.isUndef() or rhs_val.isUndef()) {
11044 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
11014 if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
11015 return sema.addConstUndef(resolved_type);
1104511016 }
11046
11047 const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target);
11048 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
11049 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
11050 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
11051 }
11052 }
11053 },
11054 .shl_with_overflow => {
11055 // If lhs is zero, the result is zero and no overflow occurred.
11056 // If rhs is zero, the result is lhs (even if undefined) and no overflow occurred.
11057 // Oterhwise if either of the arguments is undefined, both results are undefined.
11058 if (maybe_lhs_val) |lhs_val| {
11059 if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
11060 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
11061 }
11062 }
11063 if (maybe_rhs_val) |rhs_val| {
11064 if (!rhs_val.isUndef() and (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
11065 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
1106611017 }
11018 return sema.failWithUseOfUndef(block, rhs_src);
1106711019 }
11068 if (maybe_lhs_val) |lhs_val| {
11069 if (maybe_rhs_val) |rhs_val| {
11070 if (lhs_val.isUndef() or rhs_val.isUndef()) {
11071 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
11072 }
11020 return sema.addConstUndef(resolved_type);
11021 }
1107311022
11074 const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target);
11075 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
11076 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
11077 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
11078 }
11023 if (maybe_rhs_val) |rhs_val| {
11024 if (is_int) {
11025 return sema.addConstant(
11026 resolved_type,
11027 try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
11028 );
11029 } else {
11030 return sema.addConstant(
11031 resolved_type,
11032 try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target),
11033 );
1107911034 }
11080 },
11081 else => unreachable,
11035 } else {
11036 break :rs rhs_src;
11037 }
11038 } else {
11039 break :rs lhs_src;
1108211040 }
11041 };
1108311042
11084 const air_tag: Air.Inst.Tag = switch (zir_tag) {
11085 .add_with_overflow => .add_with_overflow,
11086 .mul_with_overflow => .mul_with_overflow,
11087 .sub_with_overflow => .sub_with_overflow,
11088 .shl_with_overflow => .shl_with_overflow,
11089 else => unreachable,
11090 };
11091
11092 const runtime_src = if (maybe_lhs_val == null) lhs_src else rhs_src;
11093 try sema.requireRuntimeBlock(block, src, runtime_src);
11094
11095 const tuple = try block.addInst(.{
11096 .tag = air_tag,
11097 .data = .{ .ty_pl = .{
11098 .ty = try block.sema.addType(tuple_ty),
11099 .payload = try block.sema.addExtra(Air.Bin{
11100 .lhs = lhs,
11101 .rhs = rhs,
11102 }),
11103 } },
11104 });
11105
11106 const wrapped = try sema.tupleFieldValByIndex(block, src, tuple, 0, tuple_ty);
11107 try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store);
11043 try sema.requireRuntimeBlock(block, src, runtime_src);
1110811044
11109 const overflow_bit = try sema.tupleFieldValByIndex(block, src, tuple, 1, tuple_ty);
11110 const zero_ov_val = if (dest_ty.zigTypeTag() == .Vector) try Value.Tag.repeated.create(sema.arena, Value.zero) else Value.zero;
11111 const zero_ov = try sema.addConstant(ov_ty, zero_ov_val);
11045 if (block.wantSafety()) {
11046 try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
11047 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
11048 }
1111211049
11113 const overflowed_inst = if (dest_ty.zigTypeTag() == .Vector)
11114 block.addCmpVector(overflow_bit, .zero, .neq, try sema.addType(ov_ty))
11115 else
11116 block.addBinOp(.cmp_neq, overflow_bit, zero_ov);
11117 return overflowed_inst;
11050 const air_tag = if (is_int) Air.Inst.Tag.div_trunc else switch (block.float_mode) {
11051 .Optimized => Air.Inst.Tag.div_float_optimized,
11052 .Strict => Air.Inst.Tag.div_float,
1111811053 };
11119
11120 try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store);
11121 return result.overflowed;
11054 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
1112211055}
1112311056
11124fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type {
11125 const ov_ty = if (ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, ty.vectorLen(), Type.@"u1") else Type.@"u1";
11057fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11058 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
11059 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
11060 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
11061 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
11062 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
11063 const lhs = try sema.resolveInst(extra.lhs);
11064 const rhs = try sema.resolveInst(extra.rhs);
11065 const lhs_ty = sema.typeOf(lhs);
11066 const rhs_ty = sema.typeOf(rhs);
11067 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
11068 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
11069 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
11070 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div_exact);
1112611071
11127 const types = try sema.arena.alloc(Type, 2);
11128 const values = try sema.arena.alloc(Value, 2);
11129 const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
11130 .types = types,
11131 .values = values,
11072 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
11073 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
11074 .override = &[_]LazySrcLoc{ lhs_src, rhs_src },
1113211075 });
1113311076
11134 types[0] = ty;
11135 types[1] = ov_ty;
11136 values[0] = Value.initTag(.unreachable_value);
11137 values[1] = Value.initTag(.unreachable_value);
11077 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
11078 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
1113811079
11139 return tuple_ty;
11140}
11080 const lhs_scalar_ty = lhs_ty.scalarType();
11081 const scalar_tag = resolved_type.scalarType().zigTypeTag();
1114111082
11142fn analyzeArithmetic(
11083 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
11084
11085 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact);
11086
11087 const mod = sema.mod;
11088 const target = mod.getTarget();
11089 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs);
11090 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs);
11091
11092 const runtime_src = rs: {
11093 // For integers:
11094 // If the lhs is zero, then zero is returned regardless of rhs.
11095 // If the rhs is zero, compile error for division by zero.
11096 // If the rhs is undefined, compile error because there is a possible
11097 // value (zero) for which the division would be illegal behavior.
11098 // If the lhs is undefined, compile error because there is a possible
11099 // value for which the division would result in a remainder.
11100 // TODO: emit runtime safety for if there is a remainder
11101 // TODO: emit runtime safety for division by zero
11102 //
11103 // For floats:
11104 // If the rhs is zero, compile error for division by zero.
11105 // If the rhs is undefined, compile error because there is a possible
11106 // value (zero) for which the division would be illegal behavior.
11107 // If the lhs is undefined, compile error because there is a possible
11108 // value for which the division would result in a remainder.
11109 if (maybe_lhs_val) |lhs_val| {
11110 if (lhs_val.isUndef()) {
11111 return sema.failWithUseOfUndef(block, rhs_src);
11112 } else {
11113 if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11114 return sema.addConstant(resolved_type, Value.zero);
11115 }
11116 }
11117 }
11118 if (maybe_rhs_val) |rhs_val| {
11119 if (rhs_val.isUndef()) {
11120 return sema.failWithUseOfUndef(block, rhs_src);
11121 }
11122 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11123 return sema.failWithDivideByZero(block, rhs_src);
11124 }
11125 // TODO: if the RHS is one, return the LHS directly
11126 }
11127 if (maybe_lhs_val) |lhs_val| {
11128 if (maybe_rhs_val) |rhs_val| {
11129 if (is_int) {
11130 // TODO: emit compile error if there is a remainder
11131 return sema.addConstant(
11132 resolved_type,
11133 try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
11134 );
11135 } else {
11136 // TODO: emit compile error if there is a remainder
11137 return sema.addConstant(
11138 resolved_type,
11139 try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target),
11140 );
11141 }
11142 } else break :rs rhs_src;
11143 } else break :rs lhs_src;
11144 };
11145
11146 try sema.requireRuntimeBlock(block, src, runtime_src);
11147
11148 // Depending on whether safety is enabled, we will have a slightly different strategy
11149 // here. The `div_exact` AIR instruction causes undefined behavior if a remainder
11150 // is produced, so in the safety check case, it cannot be used. Instead we do a
11151 // div_trunc and check for remainder.
11152
11153 if (block.wantSafety()) {
11154 try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
11155 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
11156
11157 const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs);
11158 const ok = if (!is_int) ok: {
11159 const floored = try block.addUnOp(.floor, result);
11160
11161 if (resolved_type.zigTypeTag() == .Vector) {
11162 const eql = try block.addCmpVector(result, floored, .eq, try sema.addType(resolved_type));
11163 break :ok try block.addInst(.{
11164 .tag = switch (block.float_mode) {
11165 .Strict => .reduce,
11166 .Optimized => .reduce_optimized,
11167 },
11168 .data = .{ .reduce = .{
11169 .operand = eql,
11170 .operation = .And,
11171 } },
11172 });
11173 } else {
11174 const is_in_range = try block.addBinOp(switch (block.float_mode) {
11175 .Strict => .cmp_eq,
11176 .Optimized => .cmp_eq_optimized,
11177 }, result, floored);
11178 break :ok is_in_range;
11179 }
11180 } else ok: {
11181 const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs);
11182
11183 if (resolved_type.zigTypeTag() == .Vector) {
11184 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
11185 const zero = try sema.addConstant(resolved_type, zero_val);
11186 const eql = try block.addCmpVector(remainder, zero, .eq, try sema.addType(resolved_type));
11187 break :ok try block.addInst(.{
11188 .tag = .reduce,
11189 .data = .{ .reduce = .{
11190 .operand = eql,
11191 .operation = .And,
11192 } },
11193 });
11194 } else {
11195 const zero = try sema.addConstant(resolved_type, Value.zero);
11196 const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero);
11197 break :ok is_in_range;
11198 }
11199 };
11200 try sema.addSafetyCheck(block, ok, .exact_division_remainder);
11201 return result;
11202 }
11203
11204 return block.addBinOp(airTag(block, is_int, .div_exact, .div_exact_optimized), casted_lhs, casted_rhs);
11205}
11206
11207fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11208 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
11209 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
11210 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
11211 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
11212 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
11213 const lhs = try sema.resolveInst(extra.lhs);
11214 const rhs = try sema.resolveInst(extra.rhs);
11215 const lhs_ty = sema.typeOf(lhs);
11216 const rhs_ty = sema.typeOf(rhs);
11217 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
11218 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
11219 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
11220 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div_floor);
11221
11222 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
11223 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
11224 .override = &[_]LazySrcLoc{ lhs_src, rhs_src },
11225 });
11226
11227 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
11228 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
11229
11230 const lhs_scalar_ty = lhs_ty.scalarType();
11231 const rhs_scalar_ty = rhs_ty.scalarType();
11232 const scalar_tag = resolved_type.scalarType().zigTypeTag();
11233
11234 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
11235
11236 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor);
11237
11238 const mod = sema.mod;
11239 const target = mod.getTarget();
11240 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs);
11241 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs);
11242
11243 const runtime_src = rs: {
11244 // For integers:
11245 // If the lhs is zero, then zero is returned regardless of rhs.
11246 // If the rhs is zero, compile error for division by zero.
11247 // If the rhs is undefined, compile error because there is a possible
11248 // value (zero) for which the division would be illegal behavior.
11249 // If the lhs is undefined:
11250 // * if lhs type is signed:
11251 // * if rhs is comptime-known and not -1, result is undefined
11252 // * if rhs is -1 or runtime-known, compile error because there is a
11253 // possible value (-min_int / -1) for which division would be
11254 // illegal behavior.
11255 // * if lhs type is unsigned, undef is returned regardless of rhs.
11256 // TODO: emit runtime safety for division by zero
11257 //
11258 // For floats:
11259 // If the rhs is zero, compile error for division by zero.
11260 // If the rhs is undefined, compile error because there is a possible
11261 // value (zero) for which the division would be illegal behavior.
11262 // If the lhs is undefined, result is undefined.
11263 if (maybe_lhs_val) |lhs_val| {
11264 if (!lhs_val.isUndef()) {
11265 if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11266 return sema.addConstant(resolved_type, Value.zero);
11267 }
11268 }
11269 }
11270 if (maybe_rhs_val) |rhs_val| {
11271 if (rhs_val.isUndef()) {
11272 return sema.failWithUseOfUndef(block, rhs_src);
11273 }
11274 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11275 return sema.failWithDivideByZero(block, rhs_src);
11276 }
11277 // TODO: if the RHS is one, return the LHS directly
11278 }
11279 if (maybe_lhs_val) |lhs_val| {
11280 if (lhs_val.isUndef()) {
11281 if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
11282 if (maybe_rhs_val) |rhs_val| {
11283 if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
11284 return sema.addConstUndef(resolved_type);
11285 }
11286 }
11287 return sema.failWithUseOfUndef(block, rhs_src);
11288 }
11289 return sema.addConstUndef(resolved_type);
11290 }
11291
11292 if (maybe_rhs_val) |rhs_val| {
11293 if (is_int) {
11294 return sema.addConstant(
11295 resolved_type,
11296 try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena, target),
11297 );
11298 } else {
11299 return sema.addConstant(
11300 resolved_type,
11301 try lhs_val.floatDivFloor(rhs_val, resolved_type, sema.arena, target),
11302 );
11303 }
11304 } else break :rs rhs_src;
11305 } else break :rs lhs_src;
11306 };
11307
11308 try sema.requireRuntimeBlock(block, src, runtime_src);
11309
11310 if (block.wantSafety()) {
11311 try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
11312 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
11313 }
11314
11315 return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);
11316}
11317
11318fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11319 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
11320 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
11321 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
11322 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
11323 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
11324 const lhs = try sema.resolveInst(extra.lhs);
11325 const rhs = try sema.resolveInst(extra.rhs);
11326 const lhs_ty = sema.typeOf(lhs);
11327 const rhs_ty = sema.typeOf(rhs);
11328 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
11329 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
11330 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
11331 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div_trunc);
11332
11333 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
11334 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
11335 .override = &[_]LazySrcLoc{ lhs_src, rhs_src },
11336 });
11337
11338 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
11339 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
11340
11341 const lhs_scalar_ty = lhs_ty.scalarType();
11342 const rhs_scalar_ty = rhs_ty.scalarType();
11343 const scalar_tag = resolved_type.scalarType().zigTypeTag();
11344
11345 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
11346
11347 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc);
11348
11349 const mod = sema.mod;
11350 const target = mod.getTarget();
11351 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs);
11352 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs);
11353
11354 const runtime_src = rs: {
11355 // For integers:
11356 // If the lhs is zero, then zero is returned regardless of rhs.
11357 // If the rhs is zero, compile error for division by zero.
11358 // If the rhs is undefined, compile error because there is a possible
11359 // value (zero) for which the division would be illegal behavior.
11360 // If the lhs is undefined:
11361 // * if lhs type is signed:
11362 // * if rhs is comptime-known and not -1, result is undefined
11363 // * if rhs is -1 or runtime-known, compile error because there is a
11364 // possible value (-min_int / -1) for which division would be
11365 // illegal behavior.
11366 // * if lhs type is unsigned, undef is returned regardless of rhs.
11367 // TODO: emit runtime safety for division by zero
11368 //
11369 // For floats:
11370 // If the rhs is zero, compile error for division by zero.
11371 // If the rhs is undefined, compile error because there is a possible
11372 // value (zero) for which the division would be illegal behavior.
11373 // If the lhs is undefined, result is undefined.
11374 if (maybe_lhs_val) |lhs_val| {
11375 if (!lhs_val.isUndef()) {
11376 if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11377 return sema.addConstant(resolved_type, Value.zero);
11378 }
11379 }
11380 }
11381 if (maybe_rhs_val) |rhs_val| {
11382 if (rhs_val.isUndef()) {
11383 return sema.failWithUseOfUndef(block, rhs_src);
11384 }
11385 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11386 return sema.failWithDivideByZero(block, rhs_src);
11387 }
11388 }
11389 if (maybe_lhs_val) |lhs_val| {
11390 if (lhs_val.isUndef()) {
11391 if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
11392 if (maybe_rhs_val) |rhs_val| {
11393 if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
11394 return sema.addConstUndef(resolved_type);
11395 }
11396 }
11397 return sema.failWithUseOfUndef(block, rhs_src);
11398 }
11399 return sema.addConstUndef(resolved_type);
11400 }
11401
11402 if (maybe_rhs_val) |rhs_val| {
11403 if (is_int) {
11404 return sema.addConstant(
11405 resolved_type,
11406 try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
11407 );
11408 } else {
11409 return sema.addConstant(
11410 resolved_type,
11411 try lhs_val.floatDivTrunc(rhs_val, resolved_type, sema.arena, target),
11412 );
11413 }
11414 } else break :rs rhs_src;
11415 } else break :rs lhs_src;
11416 };
11417
11418 try sema.requireRuntimeBlock(block, src, runtime_src);
11419
11420 if (block.wantSafety()) {
11421 try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
11422 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
11423 }
11424
11425 return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs);
11426}
11427
11428fn addDivIntOverflowSafety(
11429 sema: *Sema,
11430 block: *Block,
11431 resolved_type: Type,
11432 lhs_scalar_ty: Type,
11433 maybe_lhs_val: ?Value,
11434 maybe_rhs_val: ?Value,
11435 casted_lhs: Air.Inst.Ref,
11436 casted_rhs: Air.Inst.Ref,
11437 is_int: bool,
11438) CompileError!void {
11439 if (!is_int) return;
11440
11441 // If the LHS is unsigned, it cannot cause overflow.
11442 if (!lhs_scalar_ty.isSignedInt()) return;
11443
11444 const mod = sema.mod;
11445 const target = mod.getTarget();
11446
11447 // If the LHS is widened to a larger integer type, no overflow is possible.
11448 if (lhs_scalar_ty.intInfo(target).bits < resolved_type.intInfo(target).bits) {
11449 return;
11450 }
11451
11452 const min_int = try resolved_type.minInt(sema.arena, target);
11453 const neg_one_scalar = try Value.Tag.int_i64.create(sema.arena, -1);
11454 const neg_one = if (resolved_type.zigTypeTag() == .Vector)
11455 try Value.Tag.repeated.create(sema.arena, neg_one_scalar)
11456 else
11457 neg_one_scalar;
11458
11459 // If the LHS is comptime-known to be not equal to the min int,
11460 // no overflow is possible.
11461 if (maybe_lhs_val) |lhs_val| {
11462 if (!lhs_val.compare(.eq, min_int, resolved_type, mod)) return;
11463 }
11464
11465 // If the RHS is comptime-known to not be equal to -1, no overflow is possible.
11466 if (maybe_rhs_val) |rhs_val| {
11467 if (!rhs_val.compare(.eq, neg_one, resolved_type, mod)) return;
11468 }
11469
11470 var ok: Air.Inst.Ref = .none;
11471 if (resolved_type.zigTypeTag() == .Vector) {
11472 const vector_ty_ref = try sema.addType(resolved_type);
11473 if (maybe_lhs_val == null) {
11474 const min_int_ref = try sema.addConstant(resolved_type, min_int);
11475 ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq, vector_ty_ref);
11476 }
11477 if (maybe_rhs_val == null) {
11478 const neg_one_ref = try sema.addConstant(resolved_type, neg_one);
11479 const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq, vector_ty_ref);
11480 if (ok == .none) {
11481 ok = rhs_ok;
11482 } else {
11483 ok = try block.addBinOp(.bool_or, ok, rhs_ok);
11484 }
11485 }
11486 assert(ok != .none);
11487 ok = try block.addInst(.{
11488 .tag = .reduce,
11489 .data = .{ .reduce = .{
11490 .operand = ok,
11491 .operation = .And,
11492 } },
11493 });
11494 } else {
11495 if (maybe_lhs_val == null) {
11496 const min_int_ref = try sema.addConstant(resolved_type, min_int);
11497 ok = try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref);
11498 }
11499 if (maybe_rhs_val == null) {
11500 const neg_one_ref = try sema.addConstant(resolved_type, neg_one);
11501 const rhs_ok = try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref);
11502 if (ok == .none) {
11503 ok = rhs_ok;
11504 } else {
11505 ok = try block.addBinOp(.bool_or, ok, rhs_ok);
11506 }
11507 }
11508 assert(ok != .none);
11509 }
11510 try sema.addSafetyCheck(block, ok, .integer_overflow);
11511}
11512
11513fn addDivByZeroSafety(
11514 sema: *Sema,
11515 block: *Block,
11516 resolved_type: Type,
11517 maybe_rhs_val: ?Value,
11518 casted_rhs: Air.Inst.Ref,
11519 is_int: bool,
11520) CompileError!void {
11521 // Strict IEEE floats have well-defined division by zero.
11522 if (!is_int and block.float_mode == .Strict) return;
11523
11524 // If rhs was comptime-known to be zero a compile error would have been
11525 // emitted above.
11526 if (maybe_rhs_val != null) return;
11527
11528 const ok = if (resolved_type.zigTypeTag() == .Vector) ok: {
11529 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
11530 const zero = try sema.addConstant(resolved_type, zero_val);
11531 const ok = try block.addCmpVector(casted_rhs, zero, .neq, try sema.addType(resolved_type));
11532 break :ok try block.addInst(.{
11533 .tag = if (is_int) .reduce else .reduce_optimized,
11534 .data = .{ .reduce = .{
11535 .operand = ok,
11536 .operation = .And,
11537 } },
11538 });
11539 } else ok: {
11540 const zero = try sema.addConstant(resolved_type, Value.zero);
11541 break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero);
11542 };
11543 try sema.addSafetyCheck(block, ok, .divide_by_zero);
11544}
11545
11546fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag {
11547 if (is_int) return normal;
11548 return switch (block.float_mode) {
11549 .Strict => normal,
11550 .Optimized => optimized,
11551 };
11552}
11553
11554fn zirOverflowArithmetic(
11555 sema: *Sema,
11556 block: *Block,
11557 extended: Zir.Inst.Extended.InstData,
11558 zir_tag: Zir.Inst.Extended,
11559) CompileError!Air.Inst.Ref {
11560 const tracy = trace(@src());
11561 defer tracy.end();
11562
11563 const extra = sema.code.extraData(Zir.Inst.OverflowArithmetic, extended.operand).data;
11564 const src = LazySrcLoc.nodeOffset(extra.node);
11565
11566 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
11567 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
11568 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node };
11569
11570 const lhs = try sema.resolveInst(extra.lhs);
11571 const rhs = try sema.resolveInst(extra.rhs);
11572 const ptr = try sema.resolveInst(extra.ptr);
11573
11574 const lhs_ty = sema.typeOf(lhs);
11575 const rhs_ty = sema.typeOf(rhs);
11576 const mod = sema.mod;
11577 const target = mod.getTarget();
11578
11579 // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen.
11580 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
11581 const dest_ty = lhs_ty;
11582 if (dest_ty.scalarType().zigTypeTag() != .Int) {
11583 return sema.fail(block, src, "expected vector of integers or integer tag type, found '{}'", .{dest_ty.fmt(mod)});
11584 }
11585
11586 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
11587 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
11588
11589 const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty);
11590 const ov_ty = tuple_ty.tupleFields().types[1];
11591 // TODO: Remove and use `ov_ty` instead.
11592 // This is a temporary type used until overflow arithmetic properly returns `u1` instead of `bool`.
11593 const overflowed_ty = if (dest_ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, dest_ty.vectorLen(), Type.@"bool") else Type.@"bool";
11594
11595 const result: struct {
11596 /// TODO: Rename to `overflow_bit` and make of type `u1`.
11597 overflowed: Air.Inst.Ref,
11598 wrapped: Air.Inst.Ref,
11599 } = result: {
11600 switch (zir_tag) {
11601 .add_with_overflow => {
11602 // If either of the arguments is zero, `false` is returned and the other is stored
11603 // to the result, even if it is undefined..
11604 // Otherwise, if either of the argument is undefined, undefined is returned.
11605 if (maybe_lhs_val) |lhs_val| {
11606 if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
11607 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
11608 }
11609 }
11610 if (maybe_rhs_val) |rhs_val| {
11611 if (!rhs_val.isUndef() and (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
11612 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
11613 }
11614 }
11615 if (maybe_lhs_val) |lhs_val| {
11616 if (maybe_rhs_val) |rhs_val| {
11617 if (lhs_val.isUndef() or rhs_val.isUndef()) {
11618 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
11619 }
11620
11621 const result = try sema.intAddWithOverflow(block, src, lhs_val, rhs_val, dest_ty);
11622 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
11623 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
11624 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
11625 }
11626 }
11627 },
11628 .sub_with_overflow => {
11629 // If the rhs is zero, then the result is lhs and no overflow occured.
11630 // Otherwise, if either result is undefined, both results are undefined.
11631 if (maybe_rhs_val) |rhs_val| {
11632 if (rhs_val.isUndef()) {
11633 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
11634 } else if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11635 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
11636 } else if (maybe_lhs_val) |lhs_val| {
11637 if (lhs_val.isUndef()) {
11638 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
11639 }
11640
11641 const result = try sema.intSubWithOverflow(block, src, lhs_val, rhs_val, dest_ty);
11642 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
11643 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
11644 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
11645 }
11646 }
11647 },
11648 .mul_with_overflow => {
11649 // If either of the arguments is zero, the result is zero and no overflow occured.
11650 // If either of the arguments is one, the result is the other and no overflow occured.
11651 // Otherwise, if either of the arguments is undefined, both results are undefined.
11652 if (maybe_lhs_val) |lhs_val| {
11653 if (!lhs_val.isUndef()) {
11654 if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11655 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
11656 } else if (try sema.compare(block, src, lhs_val, .eq, Value.one, dest_ty)) {
11657 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
11658 }
11659 }
11660 }
11661
11662 if (maybe_rhs_val) |rhs_val| {
11663 if (!rhs_val.isUndef()) {
11664 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11665 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
11666 } else if (try sema.compare(block, src, rhs_val, .eq, Value.one, dest_ty)) {
11667 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
11668 }
11669 }
11670 }
11671
11672 if (maybe_lhs_val) |lhs_val| {
11673 if (maybe_rhs_val) |rhs_val| {
11674 if (lhs_val.isUndef() or rhs_val.isUndef()) {
11675 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
11676 }
11677
11678 const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target);
11679 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
11680 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
11681 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
11682 }
11683 }
11684 },
11685 .shl_with_overflow => {
11686 // If lhs is zero, the result is zero and no overflow occurred.
11687 // If rhs is zero, the result is lhs (even if undefined) and no overflow occurred.
11688 // Oterhwise if either of the arguments is undefined, both results are undefined.
11689 if (maybe_lhs_val) |lhs_val| {
11690 if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
11691 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
11692 }
11693 }
11694 if (maybe_rhs_val) |rhs_val| {
11695 if (!rhs_val.isUndef() and (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
11696 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
11697 }
11698 }
11699 if (maybe_lhs_val) |lhs_val| {
11700 if (maybe_rhs_val) |rhs_val| {
11701 if (lhs_val.isUndef() or rhs_val.isUndef()) {
11702 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
11703 }
11704
11705 const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target);
11706 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
11707 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
11708 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
11709 }
11710 }
11711 },
11712 else => unreachable,
11713 }
11714
11715 const air_tag: Air.Inst.Tag = switch (zir_tag) {
11716 .add_with_overflow => .add_with_overflow,
11717 .mul_with_overflow => .mul_with_overflow,
11718 .sub_with_overflow => .sub_with_overflow,
11719 .shl_with_overflow => .shl_with_overflow,
11720 else => unreachable,
11721 };
11722
11723 const runtime_src = if (maybe_lhs_val == null) lhs_src else rhs_src;
11724 try sema.requireRuntimeBlock(block, src, runtime_src);
11725
11726 const tuple = try block.addInst(.{
11727 .tag = air_tag,
11728 .data = .{ .ty_pl = .{
11729 .ty = try block.sema.addType(tuple_ty),
11730 .payload = try block.sema.addExtra(Air.Bin{
11731 .lhs = lhs,
11732 .rhs = rhs,
11733 }),
11734 } },
11735 });
11736
11737 const wrapped = try sema.tupleFieldValByIndex(block, src, tuple, 0, tuple_ty);
11738 try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store);
11739
11740 const overflow_bit = try sema.tupleFieldValByIndex(block, src, tuple, 1, tuple_ty);
11741 const zero_ov_val = if (dest_ty.zigTypeTag() == .Vector) try Value.Tag.repeated.create(sema.arena, Value.zero) else Value.zero;
11742 const zero_ov = try sema.addConstant(ov_ty, zero_ov_val);
11743
11744 const overflowed_inst = if (dest_ty.zigTypeTag() == .Vector)
11745 block.addCmpVector(overflow_bit, .zero, .neq, try sema.addType(ov_ty))
11746 else
11747 block.addBinOp(.cmp_neq, overflow_bit, zero_ov);
11748 return overflowed_inst;
11749 };
11750
11751 try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store);
11752 return result.overflowed;
11753}
11754
11755fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type {
11756 const ov_ty = if (ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, ty.vectorLen(), Type.@"u1") else Type.@"u1";
11757
11758 const types = try sema.arena.alloc(Type, 2);
11759 const values = try sema.arena.alloc(Value, 2);
11760 const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
11761 .types = types,
11762 .values = values,
11763 });
11764
11765 types[0] = ty;
11766 types[1] = ov_ty;
11767 values[0] = Value.initTag(.unreachable_value);
11768 values[1] = Value.initTag(.unreachable_value);
11769
11770 return tuple_ty;
11771}
11772
11773fn analyzeArithmetic(
1114311774 sema: *Sema,
1114411775 block: *Block,
1114511776 /// TODO performance investigation: make this comptime?
......@@ -11185,14 +11816,9 @@ fn analyzeArithmetic(
1118511816 const rhs_scalar_ty = rhs_ty.scalarType();
1118611817 const scalar_tag = resolved_type.scalarType().zigTypeTag();
1118711818
11188 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
11189 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
11190
11191 if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {
11192 return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{
11193 @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag),
11194 });
11195 }
11819 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
11820
11821 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag);
1119611822
1119711823 const mod = sema.mod;
1119811824 const target = mod.getTarget();
......@@ -11399,277 +12025,6 @@ fn analyzeArithmetic(
1139912025 } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat };
1140012026 } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };
1140112027 },
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 .div_trunc => {
11493 // For integers:
11494 // If the lhs is zero, then zero is returned regardless of rhs.
11495 // If the rhs is zero, compile error for division by zero.
11496 // If the rhs is undefined, compile error because there is a possible
11497 // value (zero) for which the division would be illegal behavior.
11498 // If the lhs is undefined:
11499 // * if lhs type is signed:
11500 // * if rhs is comptime-known and not -1, result is undefined
11501 // * if rhs is -1 or runtime-known, compile error because there is a
11502 // possible value (-min_int / -1) for which division would be
11503 // illegal behavior.
11504 // * if lhs type is unsigned, undef is returned regardless of rhs.
11505 // TODO: emit runtime safety for division by zero
11506 //
11507 // For floats:
11508 // If the rhs is zero, compile error for division by zero.
11509 // If the rhs is undefined, compile error because there is a possible
11510 // value (zero) for which the division would be illegal behavior.
11511 // If the lhs is undefined, result is undefined.
11512 if (maybe_lhs_val) |lhs_val| {
11513 if (!lhs_val.isUndef()) {
11514 if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11515 return sema.addConstant(resolved_type, Value.zero);
11516 }
11517 }
11518 }
11519 if (maybe_rhs_val) |rhs_val| {
11520 if (rhs_val.isUndef()) {
11521 return sema.failWithUseOfUndef(block, rhs_src);
11522 }
11523 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11524 return sema.failWithDivideByZero(block, rhs_src);
11525 }
11526 }
11527 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .div_trunc_optimized else .div_trunc;
11528 if (maybe_lhs_val) |lhs_val| {
11529 if (lhs_val.isUndef()) {
11530 if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
11531 if (maybe_rhs_val) |rhs_val| {
11532 if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
11533 return sema.addConstUndef(resolved_type);
11534 }
11535 }
11536 return sema.failWithUseOfUndef(block, rhs_src);
11537 }
11538 return sema.addConstUndef(resolved_type);
11539 }
11540
11541 if (maybe_rhs_val) |rhs_val| {
11542 if (is_int) {
11543 return sema.addConstant(
11544 resolved_type,
11545 try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
11546 );
11547 } else {
11548 return sema.addConstant(
11549 resolved_type,
11550 try lhs_val.floatDivTrunc(rhs_val, resolved_type, sema.arena, target),
11551 );
11552 }
11553 } else break :rs .{ .src = rhs_src, .air_tag = air_tag };
11554 } else break :rs .{ .src = lhs_src, .air_tag = air_tag };
11555 },
11556 .div_floor => {
11557 // For integers:
11558 // If the lhs is zero, then zero is returned regardless of rhs.
11559 // If the rhs is zero, compile error for division by zero.
11560 // If the rhs is undefined, compile error because there is a possible
11561 // value (zero) for which the division would be illegal behavior.
11562 // If the lhs is undefined:
11563 // * if lhs type is signed:
11564 // * if rhs is comptime-known and not -1, result is undefined
11565 // * if rhs is -1 or runtime-known, compile error because there is a
11566 // possible value (-min_int / -1) for which division would be
11567 // illegal behavior.
11568 // * if lhs type is unsigned, undef is returned regardless of rhs.
11569 // TODO: emit runtime safety for division by zero
11570 //
11571 // For floats:
11572 // If the rhs is zero, compile error for division by zero.
11573 // If the rhs is undefined, compile error because there is a possible
11574 // value (zero) for which the division would be illegal behavior.
11575 // If the lhs is undefined, result is undefined.
11576 if (maybe_lhs_val) |lhs_val| {
11577 if (!lhs_val.isUndef()) {
11578 if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11579 return sema.addConstant(resolved_type, Value.zero);
11580 }
11581 }
11582 }
11583 if (maybe_rhs_val) |rhs_val| {
11584 if (rhs_val.isUndef()) {
11585 return sema.failWithUseOfUndef(block, rhs_src);
11586 }
11587 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11588 return sema.failWithDivideByZero(block, rhs_src);
11589 }
11590 }
11591 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .div_floor_optimized else .div_floor;
11592 if (maybe_lhs_val) |lhs_val| {
11593 if (lhs_val.isUndef()) {
11594 if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
11595 if (maybe_rhs_val) |rhs_val| {
11596 if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
11597 return sema.addConstUndef(resolved_type);
11598 }
11599 }
11600 return sema.failWithUseOfUndef(block, rhs_src);
11601 }
11602 return sema.addConstUndef(resolved_type);
11603 }
11604
11605 if (maybe_rhs_val) |rhs_val| {
11606 if (is_int) {
11607 return sema.addConstant(
11608 resolved_type,
11609 try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena, target),
11610 );
11611 } else {
11612 return sema.addConstant(
11613 resolved_type,
11614 try lhs_val.floatDivFloor(rhs_val, resolved_type, sema.arena, target),
11615 );
11616 }
11617 } else break :rs .{ .src = rhs_src, .air_tag = air_tag };
11618 } else break :rs .{ .src = lhs_src, .air_tag = air_tag };
11619 },
11620 .div_exact => {
11621 // For integers:
11622 // If the lhs is zero, then zero is returned regardless of rhs.
11623 // If the rhs is zero, compile error for division by zero.
11624 // If the rhs is undefined, compile error because there is a possible
11625 // value (zero) for which the division would be illegal behavior.
11626 // If the lhs is undefined, compile error because there is a possible
11627 // value for which the division would result in a remainder.
11628 // TODO: emit runtime safety for if there is a remainder
11629 // TODO: emit runtime safety for division by zero
11630 //
11631 // For floats:
11632 // If the rhs is zero, compile error for division by zero.
11633 // If the rhs is undefined, compile error because there is a possible
11634 // value (zero) for which the division would be illegal behavior.
11635 // If the lhs is undefined, compile error because there is a possible
11636 // value for which the division would result in a remainder.
11637 if (maybe_lhs_val) |lhs_val| {
11638 if (lhs_val.isUndef()) {
11639 return sema.failWithUseOfUndef(block, rhs_src);
11640 } else {
11641 if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11642 return sema.addConstant(resolved_type, Value.zero);
11643 }
11644 }
11645 }
11646 if (maybe_rhs_val) |rhs_val| {
11647 if (rhs_val.isUndef()) {
11648 return sema.failWithUseOfUndef(block, rhs_src);
11649 }
11650 if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
11651 return sema.failWithDivideByZero(block, rhs_src);
11652 }
11653 }
11654 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .div_exact_optimized else .div_exact;
11655 if (maybe_lhs_val) |lhs_val| {
11656 if (maybe_rhs_val) |rhs_val| {
11657 if (is_int) {
11658 // TODO: emit compile error if there is a remainder
11659 return sema.addConstant(
11660 resolved_type,
11661 try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
11662 );
11663 } else {
11664 // TODO: emit compile error if there is a remainder
11665 return sema.addConstant(
11666 resolved_type,
11667 try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target),
11668 );
11669 }
11670 } else break :rs .{ .src = rhs_src, .air_tag = air_tag };
11671 } else break :rs .{ .src = lhs_src, .air_tag = air_tag };
11672 },
1167312028 .mul => {
1167412029 // For integers:
1167512030 // If either of the operands are zero, the result is zero.
......@@ -12048,28 +12403,6 @@ fn analyzeArithmetic(
1204812403 }
1204912404 }
1205012405 switch (rs.air_tag) {
12051 // zig fmt: off
12052 .div_float, .div_exact, .div_trunc, .div_floor, .div_float_optimized,
12053 .div_exact_optimized, .div_trunc_optimized, .div_floor_optimized
12054 // zig fmt: on
12055 => if (scalar_tag == .Int or block.float_mode == .Optimized) {
12056 const ok = if (resolved_type.zigTypeTag() == .Vector) ok: {
12057 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
12058 const zero = try sema.addConstant(sema.typeOf(casted_rhs), zero_val);
12059 const ok = try block.addCmpVector(casted_rhs, zero, .neq, try sema.addType(resolved_type));
12060 break :ok try block.addInst(.{
12061 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
12062 .data = .{ .reduce = .{
12063 .operand = ok,
12064 .operation = .And,
12065 } },
12066 });
12067 } else ok: {
12068 const zero = try sema.addConstant(sema.typeOf(casted_rhs), Value.zero);
12069 break :ok try block.addBinOp(if (block.float_mode == .Optimized) .cmp_neq_optimized else .cmp_neq, casted_rhs, zero);
12070 };
12071 try sema.addSafetyCheck(block, ok, .divide_by_zero);
12072 },
1207312406 .rem, .mod, .rem_optimized, .mod_optimized => {
1207412407 const ok = if (resolved_type.zigTypeTag() == .Vector) ok: {
1207512408 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
......@@ -12096,47 +12429,6 @@ fn analyzeArithmetic(
1209612429 },
1209712430 else => {},
1209812431 }
12099 if (rs.air_tag == .div_exact or rs.air_tag == .div_exact_optimized) {
12100 const result = try block.addBinOp(.div_exact, casted_lhs, casted_rhs);
12101 const ok = if (scalar_tag == .Float) ok: {
12102 const floored = try block.addUnOp(.floor, result);
12103
12104 if (resolved_type.zigTypeTag() == .Vector) {
12105 const eql = try block.addCmpVector(result, floored, .eq, try sema.addType(resolved_type));
12106 break :ok try block.addInst(.{
12107 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
12108 .data = .{ .reduce = .{
12109 .operand = eql,
12110 .operation = .And,
12111 } },
12112 });
12113 } else {
12114 const is_in_range = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, result, floored);
12115 break :ok is_in_range;
12116 }
12117 } else ok: {
12118 const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs);
12119
12120 if (resolved_type.zigTypeTag() == .Vector) {
12121 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
12122 const zero = try sema.addConstant(sema.typeOf(casted_rhs), zero_val);
12123 const eql = try block.addCmpVector(remainder, zero, .eq, try sema.addType(resolved_type));
12124 break :ok try block.addInst(.{
12125 .tag = .reduce,
12126 .data = .{ .reduce = .{
12127 .operand = eql,
12128 .operation = .And,
12129 } },
12130 });
12131 } else {
12132 const zero = try sema.addConstant(sema.typeOf(casted_rhs), Value.zero);
12133 const is_in_range = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, remainder, zero);
12134 break :ok is_in_range;
12135 }
12136 };
12137 try sema.addSafetyCheck(block, ok, .exact_division_remainder);
12138 return result;
12139 }
1214012432 }
1214112433 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
1214212434}
......@@ -16804,6 +17096,46 @@ fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileEr
1680417096 }
1680517097}
1680617098
17099fn checkInvalidPtrArithmetic(
17100 sema: *Sema,
17101 block: *Block,
17102 src: LazySrcLoc,
17103 ty: Type,
17104 zir_tag: Zir.Inst.Tag,
17105) CompileError!void {
17106 switch (try ty.zigTypeTagOrPoison()) {
17107 .Pointer => switch (ty.ptrSize()) {
17108 .One, .Slice => return,
17109 .Many, .C => return sema.fail(
17110 block,
17111 src,
17112 "invalid pointer arithmetic operand: '{s}''",
17113 .{@tagName(zir_tag)},
17114 ),
17115 },
17116 else => return,
17117 }
17118}
17119
17120fn checkArithmeticOp(
17121 sema: *Sema,
17122 block: *Block,
17123 src: LazySrcLoc,
17124 scalar_tag: std.builtin.TypeId,
17125 lhs_zig_ty_tag: std.builtin.TypeId,
17126 rhs_zig_ty_tag: std.builtin.TypeId,
17127 zir_tag: Zir.Inst.Tag,
17128) CompileError!void {
17129 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
17130 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
17131
17132 if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {
17133 return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{
17134 @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag),
17135 });
17136 }
17137}
17138
1680717139fn checkPtrOperand(
1680817140 sema: *Sema,
1680917141 block: *Block,
src/type.zig+13-3
......@@ -5201,10 +5201,20 @@ pub const Type = extern union {
52015201 };
52025202 }
52035203
5204 // Works for vectors and vectors of integers.
5205 pub fn minInt(ty: Type, arena: Allocator, target: Target) !Value {
5206 const scalar = try minIntScalar(ty.scalarType(), arena, target);
5207 if (ty.zigTypeTag() == .Vector) {
5208 return Value.Tag.repeated.create(arena, scalar);
5209 } else {
5210 return scalar;
5211 }
5212 }
5213
52045214 /// Asserts that self.zigTypeTag() == .Int.
5205 pub fn minInt(self: Type, arena: Allocator, target: Target) !Value {
5206 assert(self.zigTypeTag() == .Int);
5207 const info = self.intInfo(target);
5215 pub fn minIntScalar(ty: Type, arena: Allocator, target: Target) !Value {
5216 assert(ty.zigTypeTag() == .Int);
5217 const info = ty.intInfo(target);
52085218
52095219 if (info.signedness == .unsigned) {
52105220 return Value.zero;
test/cases/compile_errors/method_call_with_first_arg_type_wrong_container.zig+3-3
......@@ -3,14 +3,14 @@ pub const List = struct {
33 allocator: *Allocator,
44
55 pub fn init(allocator: *Allocator) List {
6 return List {
6 return List{
77 .len = 0,
88 .allocator = allocator,
99 };
1010 }
1111};
1212
13pub var global_allocator = Allocator {
13pub var global_allocator = Allocator{
1414 .field = 1234,
1515};
1616
......@@ -28,4 +28,4 @@ export fn foo() void {
2828// target=native
2929//
3030// :23:6: error: no field or member function named 'init' in 'tmp.List'
31// :1:14: note: struct declared here
31// :1:18: note: struct declared here
test/cases/llvm/shift_right_plus_left.0.zig deleted-12
......@@ -1,12 +0,0 @@
1pub fn main() void {
2 var i: u32 = 16;
3 assert(i >> 1, 8);
4}
5fn assert(a: u32, b: u32) void {
6 if (a != b) unreachable;
7}
8
9// run
10// backend=llvm
11// target=x86_64-linux,x86_64-macos
12//
test/cases/llvm/shift_right_plus_left.1.zig deleted-10
......@@ -1,10 +0,0 @@
1pub fn main() void {
2 var i: u32 = 16;
3 assert(i << 1, 32);
4}
5fn assert(a: u32, b: u32) void {
6 if (a != b) unreachable;
7}
8
9// run
10//