@@ -12,7 +12,7 @@ use rustc::mir::BinOp;
1212use rustc:: mir:: interpret:: { InterpResult , Scalar , GlobalId , ConstValue } ;
1313
1414use super :: {
15- Machine , PlaceTy , OpTy , InterpCx ,
15+ Machine , PlaceTy , OpTy , InterpCx , ImmTy ,
1616} ;
1717
1818mod type_name;
@@ -236,6 +236,34 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
236236 let result = Scalar :: from_uint ( truncated_bits, layout. size ) ;
237237 self . write_scalar ( result, dest) ?;
238238 }
239+
240+ "ptr_offset_from" => {
241+ let a = self . read_immediate ( args[ 0 ] ) ?. to_scalar ( ) ?. to_ptr ( ) ?;
242+ let b = self . read_immediate ( args[ 1 ] ) ?. to_scalar ( ) ?. to_ptr ( ) ?;
243+ if a. alloc_id != b. alloc_id {
244+ throw_ub_format ! (
245+ "ptr_offset_from cannot compute offset of pointers into different \
246+ allocations.",
247+ ) ;
248+ }
249+ let usize_layout = self . layout_of ( self . tcx . types . usize ) ?;
250+ let a_offset = ImmTy :: from_uint ( a. offset . bytes ( ) , usize_layout) ;
251+ let b_offset = ImmTy :: from_uint ( b. offset . bytes ( ) , usize_layout) ;
252+ let ( val, overflowed, _) = self . overflowing_binary_op (
253+ BinOp :: Sub , a_offset, b_offset,
254+ ) ?;
255+ if overflowed {
256+ throw_ub_format ! (
257+ "second argument to `ptr_offset_from` must be smaller than first" ,
258+ ) ;
259+ }
260+ let pointee_layout = self . layout_of ( substs. type_at ( 0 ) ) ?;
261+ let isize_layout = self . layout_of ( self . tcx . types . isize ) ?;
262+ let val = ImmTy :: from_scalar ( val, isize_layout) ;
263+ let size = ImmTy :: from_int ( pointee_layout. size . bytes ( ) , isize_layout) ;
264+ self . exact_div ( val, size, dest) ?;
265+ }
266+
239267 "transmute" => {
240268 self . copy_op_transmute ( args[ 0 ] , dest) ?;
241269 }
@@ -340,4 +368,30 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
340368 return Ok ( false ) ;
341369 }
342370 }
371+
372+ pub fn exact_div (
373+ & mut self ,
374+ a : ImmTy < ' tcx , M :: PointerTag > ,
375+ b : ImmTy < ' tcx , M :: PointerTag > ,
376+ dest : PlaceTy < ' tcx , M :: PointerTag > ,
377+ ) -> InterpResult < ' tcx > {
378+ // Performs an exact division, resulting in undefined behavior where
379+ // `x % y != 0` or `y == 0` or `x == T::min_value() && y == -1`.
380+ // First, check x % y != 0.
381+ if self . binary_op ( BinOp :: Rem , a, b) ?. to_bits ( ) ? != 0 {
382+ // Then, check if `b` is -1, which is the "min_value / -1" case.
383+ let minus1 = Scalar :: from_int ( -1 , dest. layout . size ) ;
384+ let b = b. to_scalar ( ) . unwrap ( ) ;
385+ if b == minus1 {
386+ throw_ub_format ! ( "exact_div: result of dividing MIN by -1 cannot be represented" )
387+ } else {
388+ throw_ub_format ! (
389+ "exact_div: {} cannot be divided by {} without remainder" ,
390+ a. to_scalar( ) . unwrap( ) ,
391+ b,
392+ )
393+ }
394+ }
395+ self . binop_ignore_overflow ( BinOp :: Div , a, b, dest)
396+ }
343397}
0 commit comments