authorgravatar for william@sengir.comWilliam Sengir <william@sengir.com> 2022-03-26 16:04:17-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-16 13:55:26-07:00
logc2980f332ed46e6ad7e8ac81b4dbef6d363447fb
treea204d6879d77493b6ccada2621f318a6ac3d3b65
parenteb06c78a8ac472b3406075dcbddf7fce63e98597

Sema: implement integer overflow safety for add, sub, mul


1 files changed, 62 insertions(+), 12 deletions(-)

src/Sema.zig+62-12
......@@ -1574,6 +1574,12 @@ fn failWithErrorSetCodeMissing(
15741574 });
15751575}
15761576
1577fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value) CompileError {
1578 return sema.fail(block, src, "overflow of integer type '{}' with value '{}'", .{
1579 int_ty.fmt(sema.mod), val.fmtValue(Type.@"comptime_int", sema.mod),
1580 });
1581}
1582
15771583/// We don't return a pointer to the new error note because the pointer
15781584/// becomes invalid when you add another one.
15791585fn errNote(
......@@ -9711,10 +9717,11 @@ fn analyzeArithmetic(
97119717 }
97129718 if (maybe_rhs_val) |rhs_val| {
97139719 if (is_int) {
9714 return sema.addConstant(
9715 resolved_type,
9716 try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target),
9717 );
9720 const sum = try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target);
9721 if (!sum.intFitsInType(resolved_type, target)) {
9722 return sema.failWithIntegerOverflow(block, src, resolved_type, sum);
9723 }
9724 return sema.addConstant(resolved_type, sum);
97189725 } else {
97199726 return sema.addConstant(
97209727 resolved_type,
......@@ -9804,10 +9811,11 @@ fn analyzeArithmetic(
98049811 }
98059812 if (maybe_rhs_val) |rhs_val| {
98069813 if (is_int) {
9807 return sema.addConstant(
9808 resolved_type,
9809 try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target),
9810 );
9814 const diff = try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target);
9815 if (!diff.intFitsInType(resolved_type, target)) {
9816 return sema.failWithIntegerOverflow(block, src, resolved_type, diff);
9817 }
9818 return sema.addConstant(resolved_type, diff);
98119819 } else {
98129820 return sema.addConstant(
98139821 resolved_type,
......@@ -10177,10 +10185,11 @@ fn analyzeArithmetic(
1017710185 }
1017810186 }
1017910187 if (is_int) {
10180 return sema.addConstant(
10181 resolved_type,
10182 try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target),
10183 );
10188 const product = try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target);
10189 if (!product.intFitsInType(resolved_type, target)) {
10190 return sema.failWithIntegerOverflow(block, src, resolved_type, product);
10191 }
10192 return sema.addConstant(resolved_type, product);
1018410193 } else {
1018510194 return sema.addConstant(
1018610195 resolved_type,
......@@ -10468,6 +10477,45 @@ fn analyzeArithmetic(
1046810477 };
1046910478
1047010479 try sema.requireRuntimeBlock(block, rs.src);
10480 if (block.wantSafety()) {
10481 if (scalar_tag == .Int) {
10482 const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) {
10483 .add => .add_with_overflow,
10484 .sub => .sub_with_overflow,
10485 .mul => .mul_with_overflow,
10486 else => null,
10487 };
10488 if (maybe_op_ov) |op_ov_tag| {
10489 const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(resolved_type);
10490 const op_ov = try block.addInst(.{
10491 .tag = op_ov_tag,
10492 .data = .{ .ty_pl = .{
10493 .ty = try sema.addType(op_ov_tuple_ty),
10494 .payload = try sema.addExtra(Air.Bin{
10495 .lhs = casted_lhs,
10496 .rhs = casted_rhs,
10497 }),
10498 } },
10499 });
10500 const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty);
10501 const any_ov_bit = if (resolved_type.zigTypeTag() == .Vector)
10502 try block.addInst(.{
10503 .tag = .reduce,
10504 .data = .{ .reduce = .{
10505 .operand = ov_bit,
10506 .operation = .Or,
10507 } },
10508 })
10509 else
10510 ov_bit;
10511 const zero_ov = try sema.addConstant(Type.@"u1", Value.zero);
10512 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);
10513
10514 try sema.addSafetyCheck(block, no_ov, .integer_overflow);
10515 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
10516 }
10517 }
10518 }
1047110519 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
1047210520}
1047310521
......@@ -16702,6 +16750,7 @@ pub const PanicId = enum {
1670216750 invalid_error_code,
1670316751 index_out_of_bounds,
1670416752 cast_truncated_data,
16753 integer_overflow,
1670516754};
1670616755
1670716756fn addSafetyCheck(
......@@ -16825,6 +16874,7 @@ fn safetyPanic(
1682516874 .invalid_error_code => "invalid error code",
1682616875 .index_out_of_bounds => "attempt to index out of bounds",
1682716876 .cast_truncated_data => "integer cast truncated bits",
16877 .integer_overflow => "integer overflow",
1682816878 };
1682916879
1683016880 const msg_inst = msg_inst: {