@@ -1433,22 +1433,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
14331433 /// Test if this value might be null.
14341434 /// If the machine does not support ptr-to-int casts, this is conservative.
14351435 pub fn scalar_may_be_null ( & self , scalar : Scalar < M :: Provenance > ) -> InterpResult < ' tcx , bool > {
1436- interp_ok ( match scalar. try_to_scalar_int ( ) {
1437- Ok ( int) => int. is_null ( ) ,
1436+ match scalar. try_to_scalar_int ( ) {
1437+ Ok ( int) => interp_ok ( int. is_null ( ) ) ,
14381438 Err ( _) => {
1439- // Can only happen during CTFE.
1439+ // We can't cast this pointer to an integer. Can only happen during CTFE.
14401440 let ptr = scalar. to_pointer ( self ) ?;
14411441 match self . ptr_try_get_alloc_id ( ptr, 0 ) {
14421442 Ok ( ( alloc_id, offset, _) ) => {
1443- let size = self . get_alloc_info ( alloc_id) . size ;
1444- // If the pointer is out-of-bounds, it may be null.
1445- // Note that one-past-the-end (offset == size) is still inbounds, and never null.
1446- offset > size
1443+ let info = self . get_alloc_info ( alloc_id) ;
1444+ // If the pointer is in-bounds (including "at the end"), it is definitely not null.
1445+ if offset <= info. size {
1446+ return interp_ok ( false ) ;
1447+ }
1448+ // If the allocation is N-aligned, and the offset is not divisible by N,
1449+ // then `base + offset` has a non-zero remainder after division by `N`,
1450+ // which means `base + offset` cannot be null.
1451+ if offset. bytes ( ) % info. align . bytes ( ) != 0 {
1452+ return interp_ok ( false ) ;
1453+ }
1454+ // We don't know enough, this might be null.
1455+ interp_ok ( true )
14471456 }
14481457 Err ( _offset) => bug ! ( "a non-int scalar is always a pointer" ) ,
14491458 }
14501459 }
1451- } )
1460+ }
14521461 }
14531462
14541463 /// Turning a "maybe pointer" into a proper pointer (and some information
0 commit comments