@@ -1481,22 +1481,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
1481
1481
/// Test if this value might be null.
1482
1482
/// If the machine does not support ptr-to-int casts, this is conservative.
1483
1483
pub fn scalar_may_be_null ( & self , scalar : Scalar < M :: Provenance > ) -> InterpResult < ' tcx , bool > {
1484
- interp_ok ( match scalar. try_to_scalar_int ( ) {
1485
- Ok ( int) => int. is_null ( ) ,
1484
+ match scalar. try_to_scalar_int ( ) {
1485
+ Ok ( int) => interp_ok ( int. is_null ( ) ) ,
1486
1486
Err ( _) => {
1487
- // Can only happen during CTFE.
1487
+ // We can't cast this pointer to an integer. Can only happen during CTFE.
1488
1488
let ptr = scalar. to_pointer ( self ) ?;
1489
1489
match self . ptr_try_get_alloc_id ( ptr, 0 ) {
1490
1490
Ok ( ( alloc_id, offset, _) ) => {
1491
- let size = self . get_alloc_info ( alloc_id) . size ;
1492
- // If the pointer is out-of-bounds, it may be null.
1493
- // Note that one-past-the-end (offset == size) is still inbounds, and never null.
1494
- offset > size
1491
+ let info = self . get_alloc_info ( alloc_id) ;
1492
+ // If the pointer is in-bounds (including "at the end"), it is definitely not null.
1493
+ if offset <= info. size {
1494
+ return interp_ok ( false ) ;
1495
+ }
1496
+ // If the allocation is N-aligned, and the offset is not divisible by N,
1497
+ // then `base + offset` has a non-zero remainder after division by `N`,
1498
+ // which means `base + offset` cannot be null.
1499
+ if offset. bytes ( ) % info. align . bytes ( ) != 0 {
1500
+ return interp_ok ( false ) ;
1501
+ }
1502
+ // We don't know enough, this might be null.
1503
+ interp_ok ( true )
1495
1504
}
1496
1505
Err ( _offset) => bug ! ( "a non-int scalar is always a pointer" ) ,
1497
1506
}
1498
1507
}
1499
- } )
1508
+ }
1500
1509
}
1501
1510
1502
1511
/// Turning a "maybe pointer" into a proper pointer (and some information
0 commit comments