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(...@@ -3125,11 +3125,11 @@ fn zirEnumDecl(
3125 return sema.failWithOwnedErrorMsg(msg);3125 return sema.failWithOwnedErrorMsg(msg);
3126 }3126 }
31273127
3128 if (has_tag_value) {3128 const tag_overflow = if (has_tag_value) overflow: {
3129 const tag_val_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);3129 const tag_val_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
3130 extra_index += 1;3130 extra_index += 1;
3131 const tag_inst = try sema.resolveInst(tag_val_ref);3131 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) {
3133 error.NeededSourceLocation => {3133 error.NeededSourceLocation => {
3134 const value_src = mod.fieldSrcLoc(new_decl_index, .{3134 const value_src = mod.fieldSrcLoc(new_decl_index, .{
3135 .index = field_i,3135 .index = field_i,
...@@ -3140,43 +3140,50 @@ fn zirEnumDecl(...@@ -3140,43 +3140,50 @@ fn zirEnumDecl(
3140 },3140 },
3141 else => |e| return e,3141 else => |e| return e,
3142 };3142 };
3143 last_tag_val = tag_val;3143 if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true;
3144 if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, tag_val.toIntern())) |other_index| {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| {
3145 const value_src = mod.fieldSrcLoc(new_decl_index, .{3146 const value_src = mod.fieldSrcLoc(new_decl_index, .{
3146 .index = field_i,3147 .index = field_i,
3147 .range = .value,3148 .range = .value,
3148 }).lazy;3149 }).lazy;
3149 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;3150 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;
3150 const msg = msg: {3151 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)});
3152 errdefer msg.destroy(gpa);3153 errdefer msg.destroy(gpa);
3153 try sema.errNote(block, other_field_src, msg, "other occurrence here", .{});3154 try sema.errNote(block, other_field_src, msg, "other occurrence here", .{});
3154 break :msg msg;3155 break :msg msg;
3155 };3156 };
3156 return sema.failWithOwnedErrorMsg(msg);3157 return sema.failWithOwnedErrorMsg(msg);
3157 }3158 }
3158 } else if (any_values) {3159 break :overflow false;
3159 const tag_val = if (last_tag_val) |val|3160 } else if (any_values) overflow: {
3160 try sema.intAdd(val, try mod.intValue(int_tag_ty, 1), int_tag_ty)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)
3161 else3164 else
3162 try mod.intValue(int_tag_ty, 0);3165 try mod.intValue(int_tag_ty, 0);
3163 last_tag_val = tag_val;3166 if (overflow != null) break :overflow true;
3164 if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, tag_val.toIntern())) |other_index| {3167 if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, last_tag_val.?.toIntern())) |other_index| {
3165 const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy;3168 const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy;
3166 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;3169 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;
3167 const msg = msg: {3170 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)});
3169 errdefer msg.destroy(gpa);3172 errdefer msg.destroy(gpa);
3170 try sema.errNote(block, other_field_src, msg, "other occurrence here", .{});3173 try sema.errNote(block, other_field_src, msg, "other occurrence here", .{});
3171 break :msg msg;3174 break :msg msg;
3172 };3175 };
3173 return sema.failWithOwnedErrorMsg(msg);3176 return sema.failWithOwnedErrorMsg(msg);
3174 }3177 }
3175 } else {3178 break :overflow false;
3176 last_tag_val = try mod.intValue(int_tag_ty, field_i);3179 } else overflow: {
3177 }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) {
3180 const value_src = mod.fieldSrcLoc(new_decl_index, .{3187 const value_src = mod.fieldSrcLoc(new_decl_index, .{
3181 .index = field_i,3188 .index = field_i,
3182 .range = if (has_tag_value) .value else .name,3189 .range = if (has_tag_value) .value else .name,
...@@ -9692,7 +9699,7 @@ fn intCast(...@@ -9692,7 +9699,7 @@ fn intCast(
9692 const dest_range_val = if (wanted_info.signedness == .signed) range_val: {9699 const dest_range_val = if (wanted_info.signedness == .signed) range_val: {
9693 const one = try mod.intValue(unsigned_operand_ty, 1);9700 const one = try mod.intValue(unsigned_operand_ty, 1);
9694 const range_minus_one = try dest_max_val.shl(one, unsigned_operand_ty, sema.arena, mod);9701 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);
9696 } else try mod.getCoerced(dest_max_val, unsigned_operand_ty);9703 } else try mod.getCoerced(dest_max_val, unsigned_operand_ty);
9697 const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val);9704 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...@@ -11229,7 +11236,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1122911236
11230 while (item.compareScalar(.lte, item_last, operand_ty, mod)) : ({11237 while (item.compareScalar(.lte, item_last, operand_ty, mod)) : ({
11231 // Previous validation has resolved any possible lazy values.11238 // 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 };
11233 }) {11243 }) {
11234 cases_len += 1;11244 cases_len += 1;
1123511245
...@@ -13363,10 +13373,10 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins...@@ -13363,10 +13373,10 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
1336313373
13364 if (maybe_rhs_val) |rhs_val| {13374 if (maybe_rhs_val) |rhs_val| {
13365 if (is_int) {13375 if (is_int) {
13366 const res = try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, mod);13376 var overflow_idx: ?usize = null;
13367 var vector_index: usize = undefined;13377 const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
13368 if (!(try sema.intFitsInType(res, resolved_type, &vector_index))) {13378 if (overflow_idx) |vec_idx| {
13369 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vector_index);13379 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx);
13370 }13380 }
13371 return sema.addConstant(resolved_type, res);13381 return sema.addConstant(resolved_type, res);
13372 } else {13382 } else {
...@@ -13490,10 +13500,10 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -13490,10 +13500,10 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
13490 if (!(modulus_val.compareAllWithZero(.eq, mod))) {13500 if (!(modulus_val.compareAllWithZero(.eq, mod))) {
13491 return sema.fail(block, src, "exact division produced remainder", .{});13501 return sema.fail(block, src, "exact division produced remainder", .{});
13492 }13502 }
13493 const res = try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, mod);13503 var overflow_idx: ?usize = null;
13494 var vector_index: usize = undefined;13504 const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
13495 if (!(try sema.intFitsInType(res, resolved_type, &vector_index))) {13505 if (overflow_idx) |vec_idx| {
13496 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vector_index);13506 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx);
13497 }13507 }
13498 return sema.addConstant(resolved_type, res);13508 return sema.addConstant(resolved_type, res);
13499 } else {13509 } else {
...@@ -13785,10 +13795,10 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -13785,10 +13795,10 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1378513795
13786 if (maybe_rhs_val) |rhs_val| {13796 if (maybe_rhs_val) |rhs_val| {
13787 if (is_int) {13797 if (is_int) {
13788 const res = try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, mod);13798 var overflow_idx: ?usize = null;
13789 var vector_index: usize = undefined;13799 const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod);
13790 if (!(try sema.intFitsInType(res, resolved_type, &vector_index))) {13800 if (overflow_idx) |vec_idx| {
13791 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vector_index);13801 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx);
13792 }13802 }
13793 return sema.addConstant(resolved_type, res);13803 return sema.addConstant(resolved_type, res);
13794 } else {13804 } else {
...@@ -14651,10 +14661,10 @@ fn analyzeArithmetic(...@@ -14651,10 +14661,10 @@ fn analyzeArithmetic(
14651 }14661 }
14652 if (maybe_rhs_val) |rhs_val| {14662 if (maybe_rhs_val) |rhs_val| {
14653 if (is_int) {14663 if (is_int) {
14654 const sum = try sema.intAdd(lhs_val, rhs_val, resolved_type);14664 var overflow_idx: ?usize = null;
14655 var vector_index: usize = undefined;14665 const sum = try sema.intAdd(lhs_val, rhs_val, resolved_type, &overflow_idx);
14656 if (!(try sema.intFitsInType(sum, resolved_type, &vector_index))) {14666 if (overflow_idx) |vec_idx| {
14657 return sema.failWithIntegerOverflow(block, src, resolved_type, sum, vector_index);14667 return sema.failWithIntegerOverflow(block, src, resolved_type, sum, vec_idx);
14658 }14668 }
14659 return sema.addConstant(resolved_type, sum);14669 return sema.addConstant(resolved_type, sum);
14660 } else {14670 } else {
...@@ -14709,7 +14719,7 @@ fn analyzeArithmetic(...@@ -14709,7 +14719,7 @@ fn analyzeArithmetic(
14709 }14719 }
14710 if (maybe_lhs_val) |lhs_val| {14720 if (maybe_lhs_val) |lhs_val| {
14711 const val = if (scalar_tag == .ComptimeInt)14721 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)
14713 else14723 else
14714 try lhs_val.intAddSat(rhs_val, resolved_type, sema.arena, mod);14724 try lhs_val.intAddSat(rhs_val, resolved_type, sema.arena, mod);
1471514725
...@@ -14748,10 +14758,10 @@ fn analyzeArithmetic(...@@ -14748,10 +14758,10 @@ fn analyzeArithmetic(
14748 }14758 }
14749 if (maybe_rhs_val) |rhs_val| {14759 if (maybe_rhs_val) |rhs_val| {
14750 if (is_int) {14760 if (is_int) {
14751 const diff = try sema.intSub(lhs_val, rhs_val, resolved_type);14761 var overflow_idx: ?usize = null;
14752 var vector_index: usize = undefined;14762 const diff = try sema.intSub(lhs_val, rhs_val, resolved_type, &overflow_idx);
14753 if (!(try sema.intFitsInType(diff, resolved_type, &vector_index))) {14763 if (overflow_idx) |vec_idx| {
14754 return sema.failWithIntegerOverflow(block, src, resolved_type, diff, vector_index);14764 return sema.failWithIntegerOverflow(block, src, resolved_type, diff, vec_idx);
14755 }14765 }
14756 return sema.addConstant(resolved_type, diff);14766 return sema.addConstant(resolved_type, diff);
14757 } else {14767 } else {
...@@ -14806,7 +14816,7 @@ fn analyzeArithmetic(...@@ -14806,7 +14816,7 @@ fn analyzeArithmetic(
14806 }14816 }
14807 if (maybe_rhs_val) |rhs_val| {14817 if (maybe_rhs_val) |rhs_val| {
14808 const val = if (scalar_tag == .ComptimeInt)14818 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)
14810 else14820 else
14811 try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, mod);14821 try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, mod);
1481214822
...@@ -14901,10 +14911,10 @@ fn analyzeArithmetic(...@@ -14901,10 +14911,10 @@ fn analyzeArithmetic(
14901 }14911 }
14902 }14912 }
14903 if (is_int) {14913 if (is_int) {
14904 const product = try lhs_val.intMul(rhs_val, resolved_type, sema.arena, sema.mod);14914 var overflow_idx: ?usize = null;
14905 var vector_index: usize = undefined;14915 const product = try lhs_val.intMul(rhs_val, resolved_type, &overflow_idx, sema.arena, sema.mod);
14906 if (!(try sema.intFitsInType(product, resolved_type, &vector_index))) {14916 if (overflow_idx) |vec_idx| {
14907 return sema.failWithIntegerOverflow(block, src, resolved_type, product, vector_index);14917 return sema.failWithIntegerOverflow(block, src, resolved_type, product, vec_idx);
14908 }14918 }
14909 return sema.addConstant(resolved_type, product);14919 return sema.addConstant(resolved_type, product);
14910 } else {14920 } else {
...@@ -15008,7 +15018,7 @@ fn analyzeArithmetic(...@@ -15008,7 +15018,7 @@ fn analyzeArithmetic(
15008 }15018 }
1500915019
15010 const val = if (scalar_tag == .ComptimeInt)15020 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)
15012 else15022 else
15013 try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, sema.mod);15023 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 {...@@ -33117,7 +33127,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
33117 }33127 }
3311833128
33119 if (fields_len > 0) {33129 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);
33121 if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) {33131 if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) {
33122 const msg = msg: {33132 const msg = msg: {
33123 const msg = try sema.errMsg(&block_scope, tag_ty_src, "specified integer tag type cannot represent every field", .{});33133 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 {...@@ -33217,7 +33227,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
33217 break :blk val;33227 break :blk val;
33218 } else blk: {33228 } else blk: {
33219 const val = if (last_tag_val) |val|33229 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)
33221 else33231 else
33222 try mod.intValue(int_tag_ty, 0);33232 try mod.intValue(int_tag_ty, 0);
33223 last_tag_val = val;33233 last_tag_val = val;
...@@ -34435,7 +34445,28 @@ fn queueFullTypeResolution(sema: *Sema, ty: Type) !void {...@@ -34435,7 +34445,28 @@ fn queueFullTypeResolution(sema: *Sema, ty: Type) !void {
34435 try sema.types_to_resolve.put(sema.gpa, ty.toIntern(), {});34445 try sema.types_to_resolve.put(sema.gpa, ty.toIntern(), {});
34436}34446}
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 {
34439 const mod = sema.mod;34470 const mod = sema.mod;
34440 if (ty.zigTypeTag(mod) == .Vector) {34471 if (ty.zigTypeTag(mod) == .Vector) {
34441 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));34472 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 {...@@ -34443,7 +34474,14 @@ fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
34443 for (result_data, 0..) |*scalar, i| {34474 for (result_data, 0..) |*scalar, i| {
34444 const lhs_elem = try lhs.elemValue(mod, i);34475 const lhs_elem = try lhs.elemValue(mod, i);
34445 const rhs_elem = try rhs.elemValue(mod, i);34476 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);
34447 }34485 }
34448 return (try mod.intern(.{ .aggregate = .{34486 return (try mod.intern(.{ .aggregate = .{
34449 .ty = ty.toIntern(),34487 .ty = ty.toIntern(),
...@@ -34455,6 +34493,11 @@ fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {...@@ -34455,6 +34493,11 @@ fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
3445534493
34456fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {34494fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
34457 const mod = sema.mod;34495 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 }
34458 // TODO is this a performance issue? maybe we should try the operation without34501 // TODO is this a performance issue? maybe we should try the operation without
34459 // resorting to BigInt first.34502 // resorting to BigInt first.
34460 var lhs_space: Value.BigIntSpace = undefined;34503 var lhs_space: Value.BigIntSpace = undefined;
...@@ -34467,10 +34510,6 @@ fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {...@@ -34467,10 +34510,6 @@ fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
34467 );34510 );
34468 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };34511 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
34469 result_bigint.add(lhs_bigint, rhs_bigint);34512 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 }
34474 return mod.intValue_big(scalar_ty, result_bigint.toConst());34513 return mod.intValue_big(scalar_ty, result_bigint.toConst());
34475}34514}
3447634515
...@@ -34485,7 +34524,7 @@ fn numberAddWrapScalar(...@@ -34485,7 +34524,7 @@ fn numberAddWrapScalar(
34485 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;34524 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
3448634525
34487 if (ty.zigTypeTag(mod) == .ComptimeInt) {34526 if (ty.zigTypeTag(mod) == .ComptimeInt) {
34488 return sema.intAdd(lhs, rhs, ty);34527 return sema.intAdd(lhs, rhs, ty, undefined);
34489 }34528 }
3449034529
34491 if (ty.isAnyFloat()) {34530 if (ty.isAnyFloat()) {
...@@ -34496,7 +34535,28 @@ fn numberAddWrapScalar(...@@ -34496,7 +34535,28 @@ fn numberAddWrapScalar(
34496 return overflow_result.wrapped_result;34535 return overflow_result.wrapped_result;
34497}34536}
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 {
34500 const mod = sema.mod;34560 const mod = sema.mod;
34501 if (ty.zigTypeTag(mod) == .Vector) {34561 if (ty.zigTypeTag(mod) == .Vector) {
34502 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod));34562 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 {...@@ -34504,7 +34564,14 @@ fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
34504 for (result_data, 0..) |*scalar, i| {34564 for (result_data, 0..) |*scalar, i| {
34505 const lhs_elem = try lhs.elemValue(sema.mod, i);34565 const lhs_elem = try lhs.elemValue(sema.mod, i);
34506 const rhs_elem = try rhs.elemValue(sema.mod, i);34566 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);
34508 }34575 }
34509 return (try mod.intern(.{ .aggregate = .{34576 return (try mod.intern(.{ .aggregate = .{
34510 .ty = ty.toIntern(),34577 .ty = ty.toIntern(),
...@@ -34516,6 +34583,11 @@ fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {...@@ -34516,6 +34583,11 @@ fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
3451634583
34517fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {34584fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
34518 const mod = sema.mod;34585 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 }
34519 // TODO is this a performance issue? maybe we should try the operation without34591 // TODO is this a performance issue? maybe we should try the operation without
34520 // resorting to BigInt first.34592 // resorting to BigInt first.
34521 var lhs_space: Value.BigIntSpace = undefined;34593 var lhs_space: Value.BigIntSpace = undefined;
...@@ -34542,7 +34614,7 @@ fn numberSubWrapScalar(...@@ -34542,7 +34614,7 @@ fn numberSubWrapScalar(
34542 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;34614 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
3454334615
34544 if (ty.zigTypeTag(mod) == .ComptimeInt) {34616 if (ty.zigTypeTag(mod) == .ComptimeInt) {
34545 return sema.intSub(lhs, rhs, ty);34617 return sema.intSub(lhs, rhs, ty, undefined);
34546 }34618 }
3454734619
34548 if (ty.isAnyFloat()) {34620 if (ty.isAnyFloat()) {
src/value.zig+72-5
...@@ -2430,7 +2430,7 @@ pub const Value = struct {...@@ -2430,7 +2430,7 @@ pub const Value = struct {
2430 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;2430 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
24312431
2432 if (ty.zigTypeTag(mod) == .ComptimeInt) {2432 if (ty.zigTypeTag(mod) == .ComptimeInt) {
2433 return intMul(lhs, rhs, ty, arena, mod);2433 return intMul(lhs, rhs, ty, undefined, arena, mod);
2434 }2434 }
24352435
2436 if (ty.isAnyFloat()) {2436 if (ty.isAnyFloat()) {
...@@ -2710,14 +2710,42 @@ pub const Value = struct {...@@ -2710,14 +2710,42 @@ pub const Value = struct {
2710 return mod.intValue_big(ty, result_bigint.toConst());2710 return mod.intValue_big(ty, result_bigint.toConst());
2711 }2711 }
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 {
2714 if (ty.zigTypeTag(mod) == .Vector) {2735 if (ty.zigTypeTag(mod) == .Vector) {
2715 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));2736 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2716 const scalar_ty = ty.scalarType(mod);2737 const scalar_ty = ty.scalarType(mod);
2717 for (result_data, 0..) |*scalar, i| {2738 for (result_data, 0..) |*scalar, i| {
2718 const lhs_elem = try lhs.elemValue(mod, i);2739 const lhs_elem = try lhs.elemValue(mod, i);
2719 const rhs_elem = try rhs.elemValue(mod, i);2740 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);
2721 }2749 }
2722 return (try mod.intern(.{ .aggregate = .{2750 return (try mod.intern(.{ .aggregate = .{
2723 .ty = ty.toIntern(),2751 .ty = ty.toIntern(),
...@@ -2749,6 +2777,12 @@ pub const Value = struct {...@@ -2749,6 +2777,12 @@ pub const Value = struct {
2749 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };2777 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2750 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };2778 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2751 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);2779 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 }
2752 return mod.intValue_big(ty, result_q.toConst());2786 return mod.intValue_big(ty, result_q.toConst());
2753 }2787 }
27542788
...@@ -2934,14 +2968,42 @@ pub const Value = struct {...@@ -2934,14 +2968,42 @@ pub const Value = struct {
2934 } })).toValue();2968 } })).toValue();
2935 }2969 }
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 {
2938 if (ty.zigTypeTag(mod) == .Vector) {2993 if (ty.zigTypeTag(mod) == .Vector) {
2939 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));2994 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2940 const scalar_ty = ty.scalarType(mod);2995 const scalar_ty = ty.scalarType(mod);
2941 for (result_data, 0..) |*scalar, i| {2996 for (result_data, 0..) |*scalar, i| {
2942 const lhs_elem = try lhs.elemValue(mod, i);2997 const lhs_elem = try lhs.elemValue(mod, i);
2943 const rhs_elem = try rhs.elemValue(mod, i);2998 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);
2945 }3007 }
2946 return (try mod.intern(.{ .aggregate = .{3008 return (try mod.intern(.{ .aggregate = .{
2947 .ty = ty.toIntern(),3009 .ty = ty.toIntern(),
...@@ -2952,6 +3014,11 @@ pub const Value = struct {...@@ -2952,6 +3014,11 @@ pub const Value = struct {
2952 }3014 }
29533015
2954 pub fn intMulScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {3016 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 }
2955 // TODO is this a performance issue? maybe we should try the operation without3022 // TODO is this a performance issue? maybe we should try the operation without
2956 // resorting to BigInt first.3023 // resorting to BigInt first.
2957 var lhs_space: Value.BigIntSpace = undefined;3024 var lhs_space: Value.BigIntSpace = undefined;