interpret: ensure we check bool/char for validity when they are used in a cast
This commit is contained in:
parent
aec67e238d
commit
db44cae343
10 changed files with 159 additions and 36 deletions
|
@ -274,9 +274,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// Let's make sure v is sign-extended *if* it has a signed type.
|
||||
let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`.
|
||||
|
||||
let v = scalar.to_bits(src_layout.size)?;
|
||||
let v = if signed { self.sign_extend(v, src_layout) } else { v };
|
||||
trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty);
|
||||
let v = match src_layout.ty.kind() {
|
||||
Uint(_) | RawPtr(..) | FnPtr(..) => scalar.to_uint(src_layout.size)?,
|
||||
Int(_) => scalar.to_int(src_layout.size)? as u128, // we will cast back to `i128` below if the sign matters
|
||||
Bool => scalar.to_bool()?.into(),
|
||||
Char => scalar.to_char()?.into(),
|
||||
_ => span_bug!(self.cur_span(), "invalid int-like cast from {}", src_layout.ty),
|
||||
};
|
||||
|
||||
Ok(match *cast_ty.kind() {
|
||||
// int -> int
|
||||
|
|
|
@ -197,7 +197,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
|
||||
let layout_val = self.layout_of(instance_args.type_at(0))?;
|
||||
let val = self.read_scalar(&args[0])?;
|
||||
let val_bits = val.to_bits(layout_val.size)?;
|
||||
let val_bits = val.to_bits(layout_val.size)?; // sign is ignored here
|
||||
|
||||
let layout_raw_shift = self.layout_of(self.tcx.types.u32)?;
|
||||
let raw_shift = self.read_scalar(&args[1])?;
|
||||
|
@ -484,7 +484,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
ret_layout: TyAndLayout<'tcx>,
|
||||
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
|
||||
assert!(layout.ty.is_integral(), "invalid type for numeric intrinsic: {}", layout.ty);
|
||||
let bits = val.to_bits(layout.size)?;
|
||||
let bits = val.to_bits(layout.size)?; // these operations all ignore the sign
|
||||
let extra = 128 - u128::from(layout.size.bits());
|
||||
let bits_out = match name {
|
||||
sym::ctpop => u128::from(bits.count_ones()),
|
||||
|
@ -519,6 +519,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`.
|
||||
// First, check x % y != 0 (or if that computation overflows).
|
||||
let rem = self.binary_op(BinOp::Rem, a, b)?;
|
||||
// sign does not matter for 0 test, so `to_bits` is fine
|
||||
if rem.to_scalar().to_bits(a.layout.size)? != 0 {
|
||||
throw_ub_custom!(
|
||||
fluent::const_eval_exact_div_has_remainder,
|
||||
|
@ -545,22 +546,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.binary_op(mir_op.wrapping_to_overflowing().unwrap(), l, r)?.to_scalar_pair();
|
||||
Ok(if overflowed.to_bool()? {
|
||||
let size = l.layout.size;
|
||||
let num_bits = size.bits();
|
||||
if l.layout.abi.is_signed() {
|
||||
// For signed ints the saturated value depends on the sign of the first
|
||||
// term since the sign of the second term can be inferred from this and
|
||||
// the fact that the operation has overflowed (if either is 0 no
|
||||
// overflow can occur)
|
||||
let first_term: u128 = l.to_scalar().to_bits(l.layout.size)?;
|
||||
let first_term_positive = first_term & (1 << (num_bits - 1)) == 0;
|
||||
if first_term_positive {
|
||||
let first_term: i128 = l.to_scalar().to_int(l.layout.size)?;
|
||||
if first_term >= 0 {
|
||||
// Negative overflow not possible since the positive first term
|
||||
// can only increase an (in range) negative term for addition
|
||||
// or corresponding negated positive term for subtraction
|
||||
// or corresponding negated positive term for subtraction.
|
||||
Scalar::from_int(size.signed_int_max(), size)
|
||||
} else {
|
||||
// Positive overflow not possible for similar reason
|
||||
// max negative
|
||||
// Positive overflow not possible for similar reason.
|
||||
Scalar::from_int(size.signed_int_min(), size)
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -437,23 +437,24 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
};
|
||||
Ok(ImmTy::from_scalar(res, layout))
|
||||
}
|
||||
_ if layout.ty.is_integral() => {
|
||||
let val = val.to_scalar();
|
||||
let val = val.to_bits(layout.size)?;
|
||||
ty::Int(..) => {
|
||||
let val = val.to_scalar().to_int(layout.size)?;
|
||||
let res = match un_op {
|
||||
Not => self.truncate(!val, layout), // bitwise negation, then truncate
|
||||
Neg => {
|
||||
// arithmetic negation
|
||||
assert!(layout.abi.is_signed());
|
||||
let val = self.sign_extend(val, layout) as i128;
|
||||
let res = val.wrapping_neg();
|
||||
let res = res as u128;
|
||||
// Truncate to target type.
|
||||
self.truncate(res, layout)
|
||||
}
|
||||
Not => !val,
|
||||
Neg => val.wrapping_neg(),
|
||||
_ => span_bug!(self.cur_span(), "Invalid integer op {:?}", un_op),
|
||||
};
|
||||
Ok(ImmTy::from_uint(res, layout))
|
||||
let res = ScalarInt::truncate_from_int(res, layout.size).0;
|
||||
Ok(ImmTy::from_scalar(res.into(), layout))
|
||||
}
|
||||
ty::Uint(..) => {
|
||||
let val = val.to_scalar().to_uint(layout.size)?;
|
||||
let res = match un_op {
|
||||
Not => !val,
|
||||
_ => span_bug!(self.cur_span(), "Invalid unsigned integer op {:?}", un_op),
|
||||
};
|
||||
let res = ScalarInt::truncate_from_uint(res, layout.size).0;
|
||||
Ok(ImmTy::from_scalar(res.into(), layout))
|
||||
}
|
||||
ty::RawPtr(..) => {
|
||||
assert_eq!(un_op, PtrMetadata);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue