| ... | ... | @@ -6975,6 +6975,7 @@ fn intCast( |
| 6975 | 6975 | operand_src: LazySrcLoc, |
| 6976 | 6976 | runtime_safety: bool, |
| 6977 | 6977 | ) CompileError!Air.Inst.Ref { |
| 6978 | // TODO: Add support for vectors |
| 6978 | 6979 | const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty); |
| 6979 | 6980 | _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand)); |
| 6980 | 6981 | |
| ... | ... | @@ -7010,27 +7011,44 @@ fn intCast( |
| 7010 | 7011 | const wanted_info = dest_ty.intInfo(target); |
| 7011 | 7012 | const actual_bits = actual_info.bits; |
| 7012 | 7013 | const wanted_bits = wanted_info.bits; |
| 7013 | | |
| 7014 | const actual_value_bits = actual_bits - @boolToInt(actual_info.signedness == .signed); |
| 7015 | const wanted_value_bits = wanted_bits - @boolToInt(wanted_info.signedness == .signed); |
| 7016 | |
| 7017 | // range shrinkage |
| 7018 | // requirement: int value fits into target type |
| 7019 | if (wanted_value_bits < actual_value_bits) { |
| 7020 | const dest_max_val = try dest_ty.maxInt(sema.arena, target); |
| 7021 | const dest_max = try sema.addConstant(operand_ty, dest_max_val); |
| 7022 | const diff = try block.addBinOp(.subwrap, dest_max, operand); |
| 7023 | |
| 7024 | if (actual_info.signedness == .signed) { |
| 7025 | // Reinterpret the sign-bit as part of the value. This will make |
| 7026 | // negative differences (`operand` > `dest_max`) appear too big. |
| 7027 | const unsigned_operand_ty = try Type.Tag.int_unsigned.create(sema.arena, actual_bits); |
| 7028 | const diff_unsigned = try block.addBitCast(unsigned_operand_ty, diff); |
| 7029 | |
| 7030 | // If the destination type is signed, then we need to double its |
| 7031 | // range to account for negative values. |
| 7032 | const dest_range_val = if (wanted_info.signedness == .signed) range_val: { |
| 7033 | const range_minus_one = try dest_max_val.shl(Value.one, unsigned_operand_ty, sema.arena, target); |
| 7034 | break :range_val try range_minus_one.intAdd(Value.one, unsigned_operand_ty, sema.arena, target); |
| 7035 | } else dest_max_val; |
| 7036 | const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val); |
| 7037 | |
| 7038 | const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range); |
| 7039 | try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); |
| 7040 | } else { |
| 7041 | const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max); |
| 7042 | try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); |
| 7043 | } |
| 7044 | } |
| 7045 | // no shrinkage, yes sign loss |
| 7014 | 7046 | // requirement: signed to unsigned >= 0 |
| 7015 | | if (actual_info.signedness == .signed and |
| 7016 | | wanted_info.signedness == .unsigned) |
| 7017 | | { |
| 7018 | | const zero_inst = try sema.addConstant(sema.typeOf(operand), Value.zero); |
| 7047 | else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) { |
| 7048 | const zero_inst = try sema.addConstant(operand_ty, Value.zero); |
| 7019 | 7049 | const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst); |
| 7020 | 7050 | try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); |
| 7021 | 7051 | } |
| 7022 | | |
| 7023 | | // requirement: unsigned int value fits into target type |
| 7024 | | if (actual_bits > wanted_bits or |
| 7025 | | (actual_bits == wanted_bits and |
| 7026 | | actual_info.signedness == .unsigned and |
| 7027 | | wanted_info.signedness == .signed)) |
| 7028 | | { |
| 7029 | | const max_int = try dest_ty.maxInt(sema.arena, target); |
| 7030 | | const max_int_inst = try sema.addConstant(operand_ty, max_int); |
| 7031 | | const is_in_range = try block.addBinOp(.cmp_lte, operand, max_int_inst); |
| 7032 | | try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); |
| 7033 | | } |
| 7034 | 7052 | } |
| 7035 | 7053 | return block.addTyOp(.intcast, dest_ty, operand); |
| 7036 | 7054 | } |