@@ -7,7 +7,7 @@ use std::convert::TryFrom;
77use rustc_hir:: def_id:: DefId ;
88use rustc_middle:: mir:: {
99 self ,
10- interpret:: { ConstValue , GlobalId , InterpResult , Scalar } ,
10+ interpret:: { ConstValue , GlobalId , InterpResult , PointerArithmetic , Scalar } ,
1111 BinOp ,
1212} ;
1313use rustc_middle:: ty;
@@ -328,15 +328,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
328328 // We managed to find a valid allocation for one pointer, but not the other.
329329 // That means they are definitely not pointing to the same allocation.
330330 throw_ub_format ! (
331- "{} called on pointers into different allocations" ,
331+ "`{}` called on pointers into different allocations" ,
332332 intrinsic_name
333333 ) ;
334334 }
335335 ( Ok ( ( a_alloc_id, a_offset, _) ) , Ok ( ( b_alloc_id, b_offset, _) ) ) => {
336336 // Found allocation for both. They must be into the same allocation.
337337 if a_alloc_id != b_alloc_id {
338338 throw_ub_format ! (
339- "{} called on pointers into different allocations" ,
339+ "`{}` called on pointers into different allocations" ,
340340 intrinsic_name
341341 ) ;
342342 }
@@ -346,47 +346,71 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
346346 } ;
347347
348348 // Compute distance.
349- let distance = {
350- // The subtraction is always done in `isize` to enforce
351- // the "no more than `isize::MAX` apart" requirement.
352- let a_offset = ImmTy :: from_uint ( a_offset, isize_layout) ;
353- let b_offset = ImmTy :: from_uint ( b_offset, isize_layout) ;
354- let ( val, overflowed, _ty) =
355- self . overflowing_binary_op ( BinOp :: Sub , & a_offset, & b_offset) ?;
349+ let dist = {
350+ // Addresses are unsigned, so this is a `usize` computation. We have to do the
351+ // overflow check separately anyway.
352+ let ( val, overflowed, _ty) = {
353+ let a_offset = ImmTy :: from_uint ( a_offset, usize_layout) ;
354+ let b_offset = ImmTy :: from_uint ( b_offset, usize_layout) ;
355+ self . overflowing_binary_op ( BinOp :: Sub , & a_offset, & b_offset) ?
356+ } ;
356357 if overflowed {
357- throw_ub_format ! ( "pointers were too far apart for {}" , intrinsic_name) ;
358+ // a < b
359+ if intrinsic_name == sym:: ptr_offset_from_unsigned {
360+ throw_ub_format ! (
361+ "`{}` called when first pointer has smaller offset than second: {} < {}" ,
362+ intrinsic_name,
363+ a_offset,
364+ b_offset,
365+ ) ;
366+ }
367+ // The signed form of the intrinsic allows this. If we interpret the
368+ // difference as isize, we'll get the proper signed difference. If that
369+ // seems *positive*, they were more than isize::MAX apart.
370+ let dist = val. to_machine_isize ( self ) ?;
371+ if dist >= 0 {
372+ throw_ub_format ! (
373+ "`{}` called when first pointer is too far before second" ,
374+ intrinsic_name
375+ ) ;
376+ }
377+ dist
378+ } else {
379+ // b >= a
380+ let dist = val. to_machine_isize ( self ) ?;
381+ // If converting to isize produced a *negative* result, we had an overflow
382+ // because they were more than isize::MAX apart.
383+ if dist < 0 {
384+ throw_ub_format ! (
385+ "`{}` called when first pointer is too far ahead of second" ,
386+ intrinsic_name
387+ ) ;
388+ }
389+ dist
358390 }
359- val. to_machine_isize ( self ) ?
360391 } ;
361392
362393 // Check that the range between them is dereferenceable ("in-bounds or one past the
363394 // end of the same allocation"). This is like the check in ptr_offset_inbounds.
364- let min_ptr = if distance >= 0 { b } else { a } ;
395+ let min_ptr = if dist >= 0 { b } else { a } ;
365396 self . check_ptr_access_align (
366397 min_ptr,
367- Size :: from_bytes ( distance . unsigned_abs ( ) ) ,
398+ Size :: from_bytes ( dist . unsigned_abs ( ) ) ,
368399 Align :: ONE ,
369400 CheckInAllocMsg :: OffsetFromTest ,
370401 ) ?;
371402
372- if intrinsic_name == sym:: ptr_offset_from_unsigned && distance < 0 {
373- throw_ub_format ! (
374- "{} called when first pointer has smaller offset than second: {} < {}" ,
375- intrinsic_name,
376- a_offset,
377- b_offset,
378- ) ;
379- }
380-
381403 // Perform division by size to compute return value.
382404 let ret_layout = if intrinsic_name == sym:: ptr_offset_from_unsigned {
405+ assert ! ( 0 <= dist && dist <= self . machine_isize_max( ) ) ;
383406 usize_layout
384407 } else {
408+ assert ! ( self . machine_isize_min( ) <= dist && dist <= self . machine_isize_max( ) ) ;
385409 isize_layout
386410 } ;
387411 let pointee_layout = self . layout_of ( substs. type_at ( 0 ) ) ?;
388412 // If ret_layout is unsigned, we checked that so is the distance, so we are good.
389- let val = ImmTy :: from_int ( distance , ret_layout) ;
413+ let val = ImmTy :: from_int ( dist , ret_layout) ;
390414 let size = ImmTy :: from_int ( pointee_layout. size . bytes ( ) , ret_layout) ;
391415 self . exact_div ( & val, & size, dest) ?;
392416 }
0 commit comments