authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-06-03 18:22:43+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-10 20:51:10-07:00
log7702af5eb2d986d46b6978dafcf4b174313167e4
treee18aec9e4935644c6dbb200820c803c286076ba5
parent2a6b91874ae970c0fba63f8c1357da5a57feec27

Sema: fix int arithmetic overflow checks

Previously, these checks worked by performing the arithmetic operation, then checking whether the result fit in the type in question. Since all values are now typed, this approach was no longer valid, and was tripping some assertions due to trying to store too-large values in smaller types. Now, `intAdd`, `intSub`, `intMul` and `intDiv` all check for overflow, and if it happens, re-do the operation with the result being a `comptime_int`, and reporting the error (and vector index) to the caller so that the error can be reported. After this change, all test cases are passing.

2 files changed, 200 insertions(+), 61 deletions(-)

src/Sema.zig+128-56
......@@ -3125,11 +3125,11 @@ fn zirEnumDecl(
31253125 return sema.failWithOwnedErrorMsg(msg);
31263126 }
31273127
3128 if (has_tag_value) {
3128 const tag_overflow = if (has_tag_value) overflow: {
31293129 const tag_val_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
31303130 extra_index += 1;
31313131 const tag_inst = try sema.resolveInst(tag_val_ref);
3132 const tag_val = sema.resolveConstValue(block, .unneeded, tag_inst, "") catch |err| switch (err) {
3132 last_tag_val = sema.resolveConstValue(block, .unneeded, tag_inst, "") catch |err| switch (err) {
31333133 error.NeededSourceLocation => {
31343134 const value_src = mod.fieldSrcLoc(new_decl_index, .{
31353135 .index = field_i,
......@@ -3140,43 +3140,50 @@ fn zirEnumDecl(
31403140 },
31413141 else => |e| return e,
31423142 };
3143 last_tag_val = tag_val;
3144 if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, tag_val.toIntern())) |other_index| {
3143 if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true;
3144 last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty);
3145 if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, last_tag_val.?.toIntern())) |other_index| {
31453146 const value_src = mod.fieldSrcLoc(new_decl_index, .{
31463147 .index = field_i,
31473148 .range = .value,
31483149 }).lazy;
31493150 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;
31503151 const msg = msg: {
3151 const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{tag_val.fmtValue(int_tag_ty, sema.mod)});
3152 const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)});
31523153 errdefer msg.destroy(gpa);
31533154 try sema.errNote(block, other_field_src, msg, "other occurrence here", .{});
31543155 break :msg msg;
31553156 };
31563157 return sema.failWithOwnedErrorMsg(msg);
31573158 }
3158 } else if (any_values) {
3159 const tag_val = if (last_tag_val) |val|
3160 try sema.intAdd(val, try mod.intValue(int_tag_ty, 1), int_tag_ty)
3159 break :overflow false;
3160 } else if (any_values) overflow: {
3161 var overflow: ?usize = null;
3162 last_tag_val = if (last_tag_val) |val|
3163 try sema.intAdd(val, try mod.intValue(int_tag_ty, 1), int_tag_ty, &overflow)
31613164 else
31623165 try mod.intValue(int_tag_ty, 0);
3163 last_tag_val = tag_val;
3164 if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, tag_val.toIntern())) |other_index| {
3166 if (overflow != null) break :overflow true;
3167 if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, last_tag_val.?.toIntern())) |other_index| {
31653168 const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy;
31663169 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;
31673170 const msg = msg: {
3168 const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{tag_val.fmtValue(int_tag_ty, sema.mod)});
3171 const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)});
31693172 errdefer msg.destroy(gpa);
31703173 try sema.errNote(block, other_field_src, msg, "other occurrence here", .{});
31713174 break :msg msg;
31723175 };
31733176 return sema.failWithOwnedErrorMsg(msg);
31743177 }
3175 } else {
3176 last_tag_val = try mod.intValue(int_tag_ty, field_i);
3177 }
3178 break :overflow false;
3179 } else overflow: {
3180 last_tag_val = try mod.intValue(Type.comptime_int, field_i);
3181 if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true;
3182 last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty);
3183 break :overflow false;
3184 };
31783185
3179 if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) {
3186 if (tag_overflow) {
31803187 const value_src = mod.fieldSrcLoc(new_decl_index, .{
31813188 .index = field_i,
31823189 .range = if (has_tag_value) .value else .name,
......@@ -9692,7 +9699,7 @@ fn intCast(
96929699 const dest_range_val = if (wanted_info.signedness == .signed) range_val: {
96939700 const one = try mod.intValue(unsigned_operand_ty, 1);
96949701 const range_minus_one = try dest_max_val.shl(one, unsigned_operand_ty, sema.arena, mod);
9695 break :range_val try sema.intAdd(range_minus_one, one, unsigned_operand_ty);
9702 break :range_val try sema.intAdd(range_minus_one, one, unsigned_operand_ty, undefined);
96969703 } else try mod.getCoerced(dest_max_val, unsigned_operand_ty);
96979704 const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val);
96989705
......@@ -11229,7 +11236,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1122911236
1123011237 while (item.compareScalar(.lte, item_last, operand_ty, mod)) : ({
1123111238 // Previous validation has resolved any possible lazy values.
11232 item = try sema.intAddScalar(item, try mod.intValue(operand_ty, 1), operand_ty);
11239 item = sema.intAddScalar(item, try mod.intValue(operand_ty, 1), operand_ty) catch |err| switch (err) {
11240 error.Overflow => unreachable,
11241 else => |e| return e,
11242 };
1123311243 }) {
1123411244 cases_len += 1;
1123511245
......@@ -13363,10 +13373,10 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
1336313373
1336413374 if (maybe_rhs_val) |rhs_val| {
1336513375 if (is_int) {
13366 const res = try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, mod);
13367 var vector_index: usize = undefined;
13368 if (!(try sema.intFitsInType(res, resolved_type, &vector_index))) {
13369 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vector_index);
13376 var overflow_idx: ?usize = null;
13377 const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
13378 if (overflow_idx) |vec_idx| {
13379 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx);
1337013380 }
1337113381 return sema.addConstant(resolved_type, res);
1337213382 } else {
......@@ -13490,10 +13500,10 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1349013500 if (!(modulus_val.compareAllWithZero(.eq, mod))) {
1349113501 return sema.fail(block, src, "exact division produced remainder", .{});
1349213502 }
13493 const res = try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, mod);
13494 var vector_index: usize = undefined;
13495 if (!(try sema.intFitsInType(res, resolved_type, &vector_index))) {
13496 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vector_index);
13503 var overflow_idx: ?usize = null;
13504 const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
13505 if (overflow_idx) |vec_idx| {
13506 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx);
1349713507 }
1349813508 return sema.addConstant(resolved_type, res);
1349913509 } else {
......@@ -13785,10 +13795,10 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1378513795
1378613796 if (maybe_rhs_val) |rhs_val| {
1378713797 if (is_int) {
13788 const res = try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, mod);
13789 var vector_index: usize = undefined;
13790 if (!(try sema.intFitsInType(res, resolved_type, &vector_index))) {
13791 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vector_index);
13798 var overflow_idx: ?usize = null;
13799 const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
13800 if (overflow_idx) |vec_idx| {
13801 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx);
1379213802 }
1379313803 return sema.addConstant(resolved_type, res);
1379413804 } else {
......@@ -14651,10 +14661,10 @@ fn analyzeArithmetic(
1465114661 }
1465214662 if (maybe_rhs_val) |rhs_val| {
1465314663 if (is_int) {
14654 const sum = try sema.intAdd(lhs_val, rhs_val, resolved_type);
14655 var vector_index: usize = undefined;
14656 if (!(try sema.intFitsInType(sum, resolved_type, &vector_index))) {
14657 return sema.failWithIntegerOverflow(block, src, resolved_type, sum, vector_index);
14664 var overflow_idx: ?usize = null;
14665 const sum = try sema.intAdd(lhs_val, rhs_val, resolved_type, &overflow_idx);
14666 if (overflow_idx) |vec_idx| {
14667 return sema.failWithIntegerOverflow(block, src, resolved_type, sum, vec_idx);
1465814668 }
1465914669 return sema.addConstant(resolved_type, sum);
1466014670 } else {
......@@ -14709,7 +14719,7 @@ fn analyzeArithmetic(
1470914719 }
1471014720 if (maybe_lhs_val) |lhs_val| {
1471114721 const val = if (scalar_tag == .ComptimeInt)
14712 try sema.intAdd(lhs_val, rhs_val, resolved_type)
14722 try sema.intAdd(lhs_val, rhs_val, resolved_type, undefined)
1471314723 else
1471414724 try lhs_val.intAddSat(rhs_val, resolved_type, sema.arena, mod);
1471514725
......@@ -14748,10 +14758,10 @@ fn analyzeArithmetic(
1474814758 }
1474914759 if (maybe_rhs_val) |rhs_val| {
1475014760 if (is_int) {
14751 const diff = try sema.intSub(lhs_val, rhs_val, resolved_type);
14752 var vector_index: usize = undefined;
14753 if (!(try sema.intFitsInType(diff, resolved_type, &vector_index))) {
14754 return sema.failWithIntegerOverflow(block, src, resolved_type, diff, vector_index);
14761 var overflow_idx: ?usize = null;
14762 const diff = try sema.intSub(lhs_val, rhs_val, resolved_type, &overflow_idx);
14763 if (overflow_idx) |vec_idx| {
14764 return sema.failWithIntegerOverflow(block, src, resolved_type, diff, vec_idx);
1475514765 }
1475614766 return sema.addConstant(resolved_type, diff);
1475714767 } else {
......@@ -14806,7 +14816,7 @@ fn analyzeArithmetic(
1480614816 }
1480714817 if (maybe_rhs_val) |rhs_val| {
1480814818 const val = if (scalar_tag == .ComptimeInt)
14809 try sema.intSub(lhs_val, rhs_val, resolved_type)
14819 try sema.intSub(lhs_val, rhs_val, resolved_type, undefined)
1481014820 else
1481114821 try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, mod);
1481214822
......@@ -14901,10 +14911,10 @@ fn analyzeArithmetic(
1490114911 }
1490214912 }
1490314913 if (is_int) {
14904 const product = try lhs_val.intMul(rhs_val, resolved_type, sema.arena, sema.mod);
14905 var vector_index: usize = undefined;
14906 if (!(try sema.intFitsInType(product, resolved_type, &vector_index))) {
14907 return sema.failWithIntegerOverflow(block, src, resolved_type, product, vector_index);
14914 var overflow_idx: ?usize = null;
14915 const product = try lhs_val.intMul(rhs_val, resolved_type, &overflow_idx, sema.arena, sema.mod);
14916 if (overflow_idx) |vec_idx| {
14917 return sema.failWithIntegerOverflow(block, src, resolved_type, product, vec_idx);
1490814918 }
1490914919 return sema.addConstant(resolved_type, product);
1491014920 } else {
......@@ -15008,7 +15018,7 @@ fn analyzeArithmetic(
1500815018 }
1500915019
1501015020 const val = if (scalar_tag == .ComptimeInt)
15011 try lhs_val.intMul(rhs_val, resolved_type, sema.arena, sema.mod)
15021 try lhs_val.intMul(rhs_val, resolved_type, undefined, sema.arena, sema.mod)
1501215022 else
1501315023 try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, sema.mod);
1501415024
......@@ -33117,7 +33127,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
3311733127 }
3311833128
3311933129 if (fields_len > 0) {
33120 const field_count_val = try mod.intValue(int_tag_ty, fields_len - 1);
33130 const field_count_val = try mod.intValue(Type.comptime_int, fields_len - 1);
3312133131 if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) {
3312233132 const msg = msg: {
3312333133 const msg = try sema.errMsg(&block_scope, tag_ty_src, "specified integer tag type cannot represent every field", .{});
......@@ -33217,7 +33227,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
3321733227 break :blk val;
3321833228 } else blk: {
3321933229 const val = if (last_tag_val) |val|
33220 try sema.intAdd(val, Value.one_comptime_int, int_tag_ty)
33230 try sema.intAdd(val, Value.one_comptime_int, int_tag_ty, undefined)
3322133231 else
3322233232 try mod.intValue(int_tag_ty, 0);
3322333233 last_tag_val = val;
......@@ -34435,7 +34445,28 @@ fn queueFullTypeResolution(sema: *Sema, ty: Type) !void {
3443534445 try sema.types_to_resolve.put(sema.gpa, ty.toIntern(), {});
3443634446}
3443734447
34438fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
34448/// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
34449/// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
34450fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value {
34451 var overflow: usize = undefined;
34452 return sema.intAddInner(lhs, rhs, ty, &overflow) catch |err| switch (err) {
34453 error.Overflow => {
34454 const is_vec = ty.isVector(sema.mod);
34455 overflow_idx.* = if (is_vec) overflow else 0;
34456 const safe_ty = if (is_vec) try sema.mod.vectorType(.{
34457 .len = ty.vectorLen(sema.mod),
34458 .child = .comptime_int_type,
34459 }) else Type.comptime_int;
34460 return sema.intAddInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) {
34461 error.Overflow => unreachable,
34462 else => |e| return e,
34463 };
34464 },
34465 else => |e| return e,
34466 };
34467}
34468
34469fn intAddInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value {
3443934470 const mod = sema.mod;
3444034471 if (ty.zigTypeTag(mod) == .Vector) {
3444134472 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));
......@@ -34443,7 +34474,14 @@ fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
3444334474 for (result_data, 0..) |*scalar, i| {
3444434475 const lhs_elem = try lhs.elemValue(mod, i);
3444534476 const rhs_elem = try rhs.elemValue(mod, i);
34446 scalar.* = try (try sema.intAddScalar(lhs_elem, rhs_elem, scalar_ty)).intern(scalar_ty, mod);
34477 const val = sema.intAddScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) {
34478 error.Overflow => {
34479 overflow_idx.* = i;
34480 return error.Overflow;
34481 },
34482 else => |e| return e,
34483 };
34484 scalar.* = try val.intern(scalar_ty, mod);
3444734485 }
3444834486 return (try mod.intern(.{ .aggregate = .{
3444934487 .ty = ty.toIntern(),
......@@ -34455,6 +34493,11 @@ fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
3445534493
3445634494fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
3445734495 const mod = sema.mod;
34496 if (scalar_ty.toIntern() != .comptime_int_type) {
34497 const res = try sema.intAddWithOverflowScalar(lhs, rhs, scalar_ty);
34498 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
34499 return res.wrapped_result;
34500 }
3445834501 // TODO is this a performance issue? maybe we should try the operation without
3445934502 // resorting to BigInt first.
3446034503 var lhs_space: Value.BigIntSpace = undefined;
......@@ -34467,10 +34510,6 @@ fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
3446734510 );
3446834511 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
3446934512 result_bigint.add(lhs_bigint, rhs_bigint);
34470 if (scalar_ty.toIntern() != .comptime_int_type) {
34471 const int_info = scalar_ty.intInfo(mod);
34472 result_bigint.truncate(result_bigint.toConst(), int_info.signedness, int_info.bits);
34473 }
3447434513 return mod.intValue_big(scalar_ty, result_bigint.toConst());
3447534514}
3447634515
......@@ -34485,7 +34524,7 @@ fn numberAddWrapScalar(
3448534524 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
3448634525
3448734526 if (ty.zigTypeTag(mod) == .ComptimeInt) {
34488 return sema.intAdd(lhs, rhs, ty);
34527 return sema.intAdd(lhs, rhs, ty, undefined);
3448934528 }
3449034529
3449134530 if (ty.isAnyFloat()) {
......@@ -34496,7 +34535,28 @@ fn numberAddWrapScalar(
3449634535 return overflow_result.wrapped_result;
3449734536}
3449834537
34499fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
34538/// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
34539/// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
34540fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value {
34541 var overflow: usize = undefined;
34542 return sema.intSubInner(lhs, rhs, ty, &overflow) catch |err| switch (err) {
34543 error.Overflow => {
34544 const is_vec = ty.isVector(sema.mod);
34545 overflow_idx.* = if (is_vec) overflow else 0;
34546 const safe_ty = if (is_vec) try sema.mod.vectorType(.{
34547 .len = ty.vectorLen(sema.mod),
34548 .child = .comptime_int_type,
34549 }) else Type.comptime_int;
34550 return sema.intSubInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) {
34551 error.Overflow => unreachable,
34552 else => |e| return e,
34553 };
34554 },
34555 else => |e| return e,
34556 };
34557}
34558
34559fn intSubInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value {
3450034560 const mod = sema.mod;
3450134561 if (ty.zigTypeTag(mod) == .Vector) {
3450234562 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));
......@@ -34504,7 +34564,14 @@ fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
3450434564 for (result_data, 0..) |*scalar, i| {
3450534565 const lhs_elem = try lhs.elemValue(sema.mod, i);
3450634566 const rhs_elem = try rhs.elemValue(sema.mod, i);
34507 scalar.* = try (try sema.intSubScalar(lhs_elem, rhs_elem, scalar_ty)).intern(scalar_ty, mod);
34567 const val = sema.intSubScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) {
34568 error.Overflow => {
34569 overflow_idx.* = i;
34570 return error.Overflow;
34571 },
34572 else => |e| return e,
34573 };
34574 scalar.* = try val.intern(scalar_ty, mod);
3450834575 }
3450934576 return (try mod.intern(.{ .aggregate = .{
3451034577 .ty = ty.toIntern(),
......@@ -34516,6 +34583,11 @@ fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
3451634583
3451734584fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
3451834585 const mod = sema.mod;
34586 if (scalar_ty.toIntern() != .comptime_int_type) {
34587 const res = try sema.intSubWithOverflowScalar(lhs, rhs, scalar_ty);
34588 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
34589 return res.wrapped_result;
34590 }
3451934591 // TODO is this a performance issue? maybe we should try the operation without
3452034592 // resorting to BigInt first.
3452134593 var lhs_space: Value.BigIntSpace = undefined;
......@@ -34542,7 +34614,7 @@ fn numberSubWrapScalar(
3454234614 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
3454334615
3454434616 if (ty.zigTypeTag(mod) == .ComptimeInt) {
34545 return sema.intSub(lhs, rhs, ty);
34617 return sema.intSub(lhs, rhs, ty, undefined);
3454634618 }
3454734619
3454834620 if (ty.isAnyFloat()) {
src/value.zig+72-5
......@@ -2430,7 +2430,7 @@ pub const Value = struct {
24302430 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
24312431
24322432 if (ty.zigTypeTag(mod) == .ComptimeInt) {
2433 return intMul(lhs, rhs, ty, arena, mod);
2433 return intMul(lhs, rhs, ty, undefined, arena, mod);
24342434 }
24352435
24362436 if (ty.isAnyFloat()) {
......@@ -2710,14 +2710,42 @@ pub const Value = struct {
27102710 return mod.intValue_big(ty, result_bigint.toConst());
27112711 }
27122712
2713 pub fn intDiv(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2713 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
2714 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
2715 pub fn intDiv(lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize, allocator: Allocator, mod: *Module) !Value {
2716 var overflow: usize = undefined;
2717 return intDivInner(lhs, rhs, ty, &overflow, allocator, mod) catch |err| switch (err) {
2718 error.Overflow => {
2719 const is_vec = ty.isVector(mod);
2720 overflow_idx.* = if (is_vec) overflow else 0;
2721 const safe_ty = if (is_vec) try mod.vectorType(.{
2722 .len = ty.vectorLen(mod),
2723 .child = .comptime_int_type,
2724 }) else Type.comptime_int;
2725 return intDivInner(lhs, rhs, safe_ty, undefined, allocator, mod) catch |err1| switch (err1) {
2726 error.Overflow => unreachable,
2727 else => |e| return e,
2728 };
2729 },
2730 else => |e| return e,
2731 };
2732 }
2733
2734 fn intDivInner(lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize, allocator: Allocator, mod: *Module) !Value {
27142735 if (ty.zigTypeTag(mod) == .Vector) {
27152736 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
27162737 const scalar_ty = ty.scalarType(mod);
27172738 for (result_data, 0..) |*scalar, i| {
27182739 const lhs_elem = try lhs.elemValue(mod, i);
27192740 const rhs_elem = try rhs.elemValue(mod, i);
2720 scalar.* = try (try intDivScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2741 const val = intDivScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod) catch |err| switch (err) {
2742 error.Overflow => {
2743 overflow_idx.* = i;
2744 return error.Overflow;
2745 },
2746 else => |e| return e,
2747 };
2748 scalar.* = try val.intern(scalar_ty, mod);
27212749 }
27222750 return (try mod.intern(.{ .aggregate = .{
27232751 .ty = ty.toIntern(),
......@@ -2749,6 +2777,12 @@ pub const Value = struct {
27492777 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
27502778 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
27512779 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2780 if (ty.toIntern() != .comptime_int_type) {
2781 const info = ty.intInfo(mod);
2782 if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
2783 return error.Overflow;
2784 }
2785 }
27522786 return mod.intValue_big(ty, result_q.toConst());
27532787 }
27542788
......@@ -2934,14 +2968,42 @@ pub const Value = struct {
29342968 } })).toValue();
29352969 }
29362970
2937 pub fn intMul(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2971 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
2972 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
2973 pub fn intMul(lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize, allocator: Allocator, mod: *Module) !Value {
2974 var overflow: usize = undefined;
2975 return intMulInner(lhs, rhs, ty, &overflow, allocator, mod) catch |err| switch (err) {
2976 error.Overflow => {
2977 const is_vec = ty.isVector(mod);
2978 overflow_idx.* = if (is_vec) overflow else 0;
2979 const safe_ty = if (is_vec) try mod.vectorType(.{
2980 .len = ty.vectorLen(mod),
2981 .child = .comptime_int_type,
2982 }) else Type.comptime_int;
2983 return intMulInner(lhs, rhs, safe_ty, undefined, allocator, mod) catch |err1| switch (err1) {
2984 error.Overflow => unreachable,
2985 else => |e| return e,
2986 };
2987 },
2988 else => |e| return e,
2989 };
2990 }
2991
2992 fn intMulInner(lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize, allocator: Allocator, mod: *Module) !Value {
29382993 if (ty.zigTypeTag(mod) == .Vector) {
29392994 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
29402995 const scalar_ty = ty.scalarType(mod);
29412996 for (result_data, 0..) |*scalar, i| {
29422997 const lhs_elem = try lhs.elemValue(mod, i);
29432998 const rhs_elem = try rhs.elemValue(mod, i);
2944 scalar.* = try (try intMulScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2999 const val = intMulScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod) catch |err| switch (err) {
3000 error.Overflow => {
3001 overflow_idx.* = i;
3002 return error.Overflow;
3003 },
3004 else => |e| return e,
3005 };
3006 scalar.* = try val.intern(scalar_ty, mod);
29453007 }
29463008 return (try mod.intern(.{ .aggregate = .{
29473009 .ty = ty.toIntern(),
......@@ -2952,6 +3014,11 @@ pub const Value = struct {
29523014 }
29533015
29543016 pub fn intMulScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
3017 if (ty.toIntern() != .comptime_int_type) {
3018 const res = try intMulWithOverflowScalar(lhs, rhs, ty, allocator, mod);
3019 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
3020 return res.wrapped_result;
3021 }
29553022 // TODO is this a performance issue? maybe we should try the operation without
29563023 // resorting to BigInt first.
29573024 var lhs_space: Value.BigIntSpace = undefined;