Skip to content

Commit b438e46

Browse files
committed
const-eval: detect more pointers as definitely not-null
1 parent 6c76ed5 commit b438e46

File tree

3 files changed

+25
-10
lines changed

3 files changed

+25
-10
lines changed

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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

tests/ui/consts/const-ptr-is-null.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ const MAYBE_NULL: () = {
1212
let ptr = &x as *const i32;
1313
// This one is still unambiguous...
1414
assert!(!ptr.is_null());
15-
// but once we shift outside the allocation, we might become null.
15+
// and in fact, any offset not visible by 4 (the alignment) cannot be null,
16+
// even if it goes out-of-bounds...
17+
assert!(!ptr.wrapping_byte_add(13).is_null());
18+
assert!(!ptr.wrapping_byte_add(18).is_null());
19+
assert!(!ptr.wrapping_byte_sub(1).is_null());
20+
// ... but once we shift outside the allocation, with an offset divisible by 4,
21+
// we might become null.
1622
assert!(!ptr.wrapping_sub(512).is_null()); //~inside `MAYBE_NULL`
1723
};
1824

tests/ui/consts/const-ptr-is-null.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ note: inside `std::ptr::const_ptr::<impl *const T>::is_null::compiletime`
88
note: inside `std::ptr::const_ptr::<impl *const i32>::is_null`
99
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
1010
note: inside `MAYBE_NULL`
11-
--> $DIR/const-ptr-is-null.rs:16:14
11+
--> $DIR/const-ptr-is-null.rs:22:14
1212
|
1313
LL | assert!(!ptr.wrapping_sub(512).is_null());
1414
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)