@@ -134,9 +134,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
134
134
let mut r = r as u32 ;
135
135
let size = left_layout. size ;
136
136
oflo |= r >= size. bits ( ) as u32 ;
137
- if oflo {
138
- r %= size. bits ( ) as u32 ;
139
- }
137
+ r %= size. bits ( ) as u32 ;
140
138
let result = if signed {
141
139
let l = self . sign_extend ( l, left_layout) as i128 ;
142
140
let result = match bin_op {
@@ -168,6 +166,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
168
166
)
169
167
}
170
168
169
+ let size = left_layout. size ;
170
+
171
171
// Operations that need special treatment for signed integers
172
172
if left_layout. abi . is_signed ( ) {
173
173
let op: Option < fn ( & i128 , & i128 ) -> bool > = match bin_op {
@@ -195,32 +195,31 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
195
195
if let Some ( op) = op {
196
196
let l128 = self . sign_extend ( l, left_layout) as i128 ;
197
197
let r = self . sign_extend ( r, right_layout) as i128 ;
198
- let size = left_layout. size ;
198
+ // We need a special check for overflowing remainder:
199
+ // "int_min % -1" overflows and returns 0, but after casting things to a larger int
200
+ // type it does *not* overflow nor give an unrepresentable result!
199
201
match bin_op {
200
- Rem | Div => {
201
- // int_min / -1
202
+ Rem => {
202
203
if r == -1 && l == ( 1 << ( size. bits ( ) - 1 ) ) {
203
- return Ok ( ( Scalar :: from_uint ( l , size) , true , left_layout. ty ) ) ;
204
+ return Ok ( ( Scalar :: from_int ( 0 , size) , true , left_layout. ty ) ) ;
204
205
}
205
206
}
206
207
_ => { }
207
208
}
208
- trace ! ( "{}, {}, {}" , l, l128, r) ;
209
- let ( result, mut oflo) = op ( l128, r) ;
210
- trace ! ( "{}, {}" , result, oflo) ;
211
- if !oflo && size. bits ( ) != 128 {
212
- let max = 1 << ( size. bits ( ) - 1 ) ;
213
- oflo = result >= max || result < -max;
214
- }
215
- // this may be out-of-bounds for the result type, so we have to truncate ourselves
209
+
210
+ let ( result, oflo) = op ( l128, r) ;
211
+ // This may be out-of-bounds for the result type, so we have to truncate ourselves.
212
+ // If that truncation loses any information, we have an overflow.
216
213
let result = result as u128 ;
217
214
let truncated = self . truncate ( result, left_layout) ;
218
- return Ok ( ( Scalar :: from_uint ( truncated, size) , oflo, left_layout. ty ) ) ;
215
+ return Ok ( (
216
+ Scalar :: from_uint ( truncated, size) ,
217
+ oflo || self . sign_extend ( truncated, left_layout) != result,
218
+ left_layout. ty ,
219
+ ) ) ;
219
220
}
220
221
}
221
222
222
- let size = left_layout. size ;
223
-
224
223
let ( val, ty) = match bin_op {
225
224
Eq => ( Scalar :: from_bool ( l == r) , self . tcx . types . bool ) ,
226
225
Ne => ( Scalar :: from_bool ( l != r) , self . tcx . types . bool ) ,
@@ -247,6 +246,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
247
246
_ => bug ! ( ) ,
248
247
} ;
249
248
let ( result, oflo) = op ( l, r) ;
249
+ // Truncate to target type.
250
+ // If that truncation loses any information, we have an overflow.
250
251
let truncated = self . truncate ( result, left_layout) ;
251
252
return Ok ( (
252
253
Scalar :: from_uint ( truncated, size) ,
@@ -341,7 +342,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
341
342
}
342
343
}
343
344
344
- /// 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.
345
346
#[ inline]
346
347
pub fn binary_op (
347
348
& self ,
@@ -353,11 +354,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
353
354
Ok ( ImmTy :: from_scalar ( val, self . layout_of ( ty) ?) )
354
355
}
355
356
356
- 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 (
357
360
& self ,
358
361
un_op : mir:: UnOp ,
359
362
val : ImmTy < ' tcx , M :: PointerTag > ,
360
- ) -> InterpResult < ' tcx , ImmTy < ' tcx , M :: PointerTag > > {
363
+ ) -> InterpResult < ' tcx , ( Scalar < M :: PointerTag > , bool , Ty < ' tcx > ) > {
361
364
use rustc:: mir:: UnOp :: * ;
362
365
363
366
let layout = val. layout ;
@@ -371,29 +374,44 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
371
374
Not => !val,
372
375
_ => bug ! ( "Invalid bool op {:?}" , un_op) ,
373
376
} ;
374
- 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 ) )
375
378
}
376
379
ty:: Float ( fty) => {
377
380
let res = match ( un_op, fty) {
378
381
( Neg , FloatTy :: F32 ) => Scalar :: from_f32 ( -val. to_f32 ( ) ?) ,
379
382
( Neg , FloatTy :: F64 ) => Scalar :: from_f64 ( -val. to_f64 ( ) ?) ,
380
383
_ => bug ! ( "Invalid float op {:?}" , un_op) ,
381
384
} ;
382
- Ok ( ImmTy :: from_scalar ( res, layout) )
385
+ Ok ( ( res, false , layout. ty ) )
383
386
}
384
387
_ => {
385
388
assert ! ( layout. ty. is_integral( ) ) ;
386
389
let val = self . force_bits ( val, layout. size ) ?;
387
- let res = match un_op {
388
- Not => !val,
390
+ let ( res, overflow ) = match un_op {
391
+ Not => ( self . truncate ( !val, layout ) , false ) , // bitwise negation, then truncate
389
392
Neg => {
393
+ // arithmetic negation
390
394
assert ! ( layout. abi. is_signed( ) ) ;
391
- ( -( 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)
392
402
}
393
403
} ;
394
- // res needs tuncating
395
- Ok ( ImmTy :: from_uint ( self . truncate ( res, layout) , layout) )
404
+ Ok ( ( Scalar :: from_uint ( res, layout. size ) , overflow, layout. ty ) )
396
405
}
397
406
}
398
407
}
408
+
409
+ pub fn unary_op (
410
+ & self ,
411
+ un_op : mir:: UnOp ,
412
+ val : ImmTy < ' tcx , M :: PointerTag > ,
413
+ ) -> InterpResult < ' tcx , ImmTy < ' tcx , M :: PointerTag > > {
414
+ let ( val, _overflow, ty) = self . overflowing_unary_op ( un_op, val) ?;
415
+ Ok ( ImmTy :: from_scalar ( val, self . layout_of ( ty) ?) )
416
+ }
399
417
}
0 commit comments