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(...@@ -1574,6 +1574,12 @@ fn failWithErrorSetCodeMissing(
1574 });1574 });
1575}1575}
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
1577/// We don't return a pointer to the new error note because the pointer1583/// We don't return a pointer to the new error note because the pointer
1578/// becomes invalid when you add another one.1584/// becomes invalid when you add another one.
1579fn errNote(1585fn errNote(
...@@ -9711,10 +9717,11 @@ fn analyzeArithmetic(...@@ -9711,10 +9717,11 @@ fn analyzeArithmetic(
9711 }9717 }
9712 if (maybe_rhs_val) |rhs_val| {9718 if (maybe_rhs_val) |rhs_val| {
9713 if (is_int) {9719 if (is_int) {
9714 return sema.addConstant(9720 const sum = try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target);
9715 resolved_type,9721 if (!sum.intFitsInType(resolved_type, target)) {
9716 try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target),9722 return sema.failWithIntegerOverflow(block, src, resolved_type, sum);
9717 );9723 }
9724 return sema.addConstant(resolved_type, sum);
9718 } else {9725 } else {
9719 return sema.addConstant(9726 return sema.addConstant(
9720 resolved_type,9727 resolved_type,
...@@ -9804,10 +9811,11 @@ fn analyzeArithmetic(...@@ -9804,10 +9811,11 @@ fn analyzeArithmetic(
9804 }9811 }
9805 if (maybe_rhs_val) |rhs_val| {9812 if (maybe_rhs_val) |rhs_val| {
9806 if (is_int) {9813 if (is_int) {
9807 return sema.addConstant(9814 const diff = try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target);
9808 resolved_type,9815 if (!diff.intFitsInType(resolved_type, target)) {
9809 try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target),9816 return sema.failWithIntegerOverflow(block, src, resolved_type, diff);
9810 );9817 }
9818 return sema.addConstant(resolved_type, diff);
9811 } else {9819 } else {
9812 return sema.addConstant(9820 return sema.addConstant(
9813 resolved_type,9821 resolved_type,
...@@ -10177,10 +10185,11 @@ fn analyzeArithmetic(...@@ -10177,10 +10185,11 @@ fn analyzeArithmetic(
10177 }10185 }
10178 }10186 }
10179 if (is_int) {10187 if (is_int) {
10180 return sema.addConstant(10188 const product = try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target);
10181 resolved_type,10189 if (!product.intFitsInType(resolved_type, target)) {
10182 try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target),10190 return sema.failWithIntegerOverflow(block, src, resolved_type, product);
10183 );10191 }
10192 return sema.addConstant(resolved_type, product);
10184 } else {10193 } else {
10185 return sema.addConstant(10194 return sema.addConstant(
10186 resolved_type,10195 resolved_type,
...@@ -10468,6 +10477,45 @@ fn analyzeArithmetic(...@@ -10468,6 +10477,45 @@ fn analyzeArithmetic(
10468 };10477 };
1046910478
10470 try sema.requireRuntimeBlock(block, rs.src);10479 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 }
10471 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);10519 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
10472}10520}
1047310521
...@@ -16702,6 +16750,7 @@ pub const PanicId = enum {...@@ -16702,6 +16750,7 @@ pub const PanicId = enum {
16702 invalid_error_code,16750 invalid_error_code,
16703 index_out_of_bounds,16751 index_out_of_bounds,
16704 cast_truncated_data,16752 cast_truncated_data,
16753 integer_overflow,
16705};16754};
1670616755
16707fn addSafetyCheck(16756fn addSafetyCheck(
...@@ -16825,6 +16874,7 @@ fn safetyPanic(...@@ -16825,6 +16874,7 @@ fn safetyPanic(
16825 .invalid_error_code => "invalid error code",16874 .invalid_error_code => "invalid error code",
16826 .index_out_of_bounds => "attempt to index out of bounds",16875 .index_out_of_bounds => "attempt to index out of bounds",
16827 .cast_truncated_data => "integer cast truncated bits",16876 .cast_truncated_data => "integer cast truncated bits",
16877 .integer_overflow => "integer overflow",
16828 };16878 };
1682916879
16830 const msg_inst = msg_inst: {16880 const msg_inst = msg_inst: {