@@ -1481,22 +1481,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
14811481 /// Test if this value might be null.
14821482 /// If the machine does not support ptr-to-int casts, this is conservative.
14831483 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 ( ) ) ,
14861486 Err ( _) => {
1487- // Can only happen during CTFE.
1487+ // We can't cast this pointer to an integer. Can only happen during CTFE.
14881488 let ptr = scalar. to_pointer ( self ) ?;
14891489 match self . ptr_try_get_alloc_id ( ptr, 0 ) {
14901490 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 )
14951504 }
14961505 Err ( _offset) => bug ! ( "a non-int scalar is always a pointer" ) ,
14971506 }
14981507 }
1499- } )
1508+ }
15001509 }
15011510
15021511 /// Turning a "maybe pointer" into a proper pointer (and some information
0 commit comments