Skip to content

Commit 7d2f6ae

Browse files
committed
miri: equip unary_op with overflow detection
1 parent 28f85c6 commit 7d2f6ae

File tree

1 file changed

+27
-9
lines changed

1 file changed

+27
-9
lines changed

src/librustc_mir/interpret/operator.rs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
342342
}
343343
}
344344

345-
/// Typed version of `checked_binary_op`, returning an `ImmTy`. Also ignores overflows.
345+
/// Typed version of `overflowing_binary_op`, returning an `ImmTy`. Also ignores overflows.
346346
#[inline]
347347
pub fn binary_op(
348348
&self,
@@ -354,11 +354,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
354354
Ok(ImmTy::from_scalar(val, self.layout_of(ty)?))
355355
}
356356

357-
pub fn unary_op(
357+
/// Returns the result of the specified operation, whether it overflowed, and
358+
/// the result type.
359+
pub fn overflowing_unary_op(
358360
&self,
359361
un_op: mir::UnOp,
360362
val: ImmTy<'tcx, M::PointerTag>,
361-
) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
363+
) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> {
362364
use rustc::mir::UnOp::*;
363365

364366
let layout = val.layout;
@@ -372,29 +374,45 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
372374
Not => !val,
373375
_ => bug!("Invalid bool op {:?}", un_op),
374376
};
375-
Ok(ImmTy::from_scalar(Scalar::from_bool(res), self.layout_of(self.tcx.types.bool)?))
377+
Ok((Scalar::from_bool(res), false, self.tcx.types.bool))
376378
}
377379
ty::Float(fty) => {
378380
let res = match (un_op, fty) {
379381
(Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
380382
(Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?),
381383
_ => bug!("Invalid float op {:?}", un_op),
382384
};
383-
Ok(ImmTy::from_scalar(res, layout))
385+
Ok((res, false, layout.ty))
384386
}
385387
_ => {
386388
assert!(layout.ty.is_integral());
387389
let val = self.force_bits(val, layout.size)?;
388-
let res = match un_op {
389-
Not => !val,
390+
let (res, overflow) = match un_op {
391+
Not => (self.truncate(!val, layout), false), // bitwise negation, then truncate
390392
Neg => {
393+
// arithmetic negation
391394
assert!(layout.abi.is_signed());
392-
(-(val as i128)) as u128
395+
let val = self.sign_extend(val, layout) as i128;
396+
let (res, overflow) = val.overflowing_neg();
397+
let res = res as u128;
398+
// Truncate to target type.
399+
// If that truncation loses any information, we have an overflow.
400+
let truncated = self.truncate(res, layout);
401+
(truncated, overflow || self.sign_extend(truncated, layout) != res)
393402
}
394403
};
395404
// res needs tuncating
396-
Ok(ImmTy::from_uint(self.truncate(res, layout), layout))
405+
Ok((Scalar::from_uint(res, layout.size), overflow, layout.ty))
397406
}
398407
}
399408
}
409+
410+
pub fn unary_op(
411+
&self,
412+
un_op: mir::UnOp,
413+
val: ImmTy<'tcx, M::PointerTag>,
414+
) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
415+
let (val, _overflow, ty) = self.overflowing_unary_op(un_op, val)?;
416+
Ok(ImmTy::from_scalar(val, self.layout_of(ty)?))
417+
}
400418
}

0 commit comments

Comments
 (0)