Skip to content

Commit da2af7e

Browse files
committed
Pointers to the same static allocation are equal if and only if they have the same offset.
Strictly in-bounds (in-bounds and not one-past-the-end) pointers to different static allocations are always unequal.
1 parent 59948c6 commit da2af7e

File tree

2 files changed

+75
-67
lines changed

2 files changed

+75
-67
lines changed

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 57 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -336,79 +336,70 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
336336
// so they could be equal. Try the other checks.
337337
}
338338

339-
if a_allocid == b_allocid {
340-
match self.tcx.try_get_global_alloc(a_allocid) {
341-
None => 2,
342-
// A static cannot be duplicated, so if two pointers are into the same
343-
// static, they are equal if and only if their offsets into the static
344-
// are equal
345-
Some(GlobalAlloc::Static(_)) => (a_offset == b_offset) as u8,
346-
// Functions and vtables can be duplicated (and deduplicated), so we
347-
// cannot be sure of runtime equality of pointers to the same one, (or the
348-
// runtime inequality of pointers to different ones) (see e.g. #73722).
349-
Some(GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..)) => 2,
350-
// FIXME: Revisit this once https://github.com/rust-lang/rust/issues/128775
351-
// is fixed.
352-
Some(GlobalAlloc::Memory(..)) => 2,
353-
// `GlobalAlloc::TypeId` exists mostly to prevent consteval from comparing
354-
// `TypeId`s, always return 2
355-
Some(GlobalAlloc::TypeId { .. }) => 2,
356-
}
357-
} else {
358-
if let (Some(GlobalAlloc::Static(a_did)), Some(GlobalAlloc::Static(b_did))) = (
359-
self.tcx.try_get_global_alloc(a_allocid),
360-
self.tcx.try_get_global_alloc(b_allocid),
361-
) {
339+
if let (Some(GlobalAlloc::Static(a_did)), Some(GlobalAlloc::Static(b_did))) = (
340+
self.tcx.try_get_global_alloc(a_allocid),
341+
self.tcx.try_get_global_alloc(b_allocid),
342+
) {
343+
if a_allocid == b_allocid {
344+
debug_assert_eq!(
345+
a_did, b_did,
346+
"different static item DefIds had same AllocId? {a_allocid:?} == {b_allocid:?}, {a_did:?} != {b_did:?}"
347+
);
348+
// Comparing two pointers into the same static. As per
349+
// https://doc.rust-lang.org/nightly/reference/items/static-items.html#r-items.static.intro
350+
// a static cannot be duplicated, so if two pointers are into the same
351+
// static, they are equal if and only if their offsets are equal.
352+
(a_offset == b_offset) as u8
353+
} else {
362354
debug_assert_ne!(
363355
a_did, b_did,
364356
"same static item DefId had two different AllocIds? {a_allocid:?} != {b_allocid:?}, {a_did:?} == {b_did:?}"
365357
);
366-
367-
if a_info.size == Size::ZERO || b_info.size == Size::ZERO {
368-
// One or both allocations is zero-sized, so we can't know if the
369-
// pointers are (in)equal.
370-
// FIXME: Can zero-sized static be "within" non-zero-sized statics?
371-
// Conservatively we say yes, since that doesn't cause them to
372-
// "overlap" any bytes, but if not, then we could delete this branch;
373-
// the other branches would already handle ZST allocations correctly.
374-
2
375-
} else if a_offset > a_info.size || b_offset > b_info.size {
376-
// One or both pointers are out of bounds of their allocation,
377-
// so conservatively say we don't know.
378-
// FIXME: we could reason about how far out of bounds the pointers are,
379-
// e.g. two pointers cannot be equal if them being equal would require
380-
// their statics to overlap.
381-
2
382-
} else if (a_offset == Size::ZERO && b_offset == b_info.size)
383-
|| (a_offset == a_info.size && b_offset == Size::ZERO)
384-
{
385-
// The pointers are on opposite ends of different allocations, we
386-
// cannot know if they are equal, since the allocations may end up
387-
// adjacent at runtime.
388-
2
389-
} else {
390-
// The pointers are within (or one past the end of) different
391-
// non-zero-sized static allocations, and they are not at oppotiste
392-
// ends, so we know they are not equal because non-zero-sized statics
393-
// cannot overlap or be deduplicated, as per
394-
// https://doc.rust-lang.org/nightly/reference/items/static-items.html#r-items.static.intro
395-
// (non-deduplication), and
396-
// https://doc.rust-lang.org/nightly/reference/items/static-items.html#r-items.static.storage-disjointness
397-
// (non-overlapping)
358+
// Comparing two pointers into the different statics.
359+
// We can never determine for sure that two pointers into different statics
360+
// are *equal*, but we can know that they are *inequal* if they are both
361+
// strictly in-bounds (i.e. in-bounds and not one-past-the-end) of
362+
// their respective static, as different non-zero-sized statics cannot
363+
// overlap or be deduplicated as per
364+
// https://doc.rust-lang.org/nightly/reference/items/static-items.html#r-items.static.intro
365+
// (non-deduplication), and
366+
// https://doc.rust-lang.org/nightly/reference/items/static-items.html#r-items.static.storage-disjointness
367+
// (non-overlapping).
368+
if a_offset < a_info.size && b_offset < b_info.size {
398369
0
370+
} else {
371+
// Otherwise, conservatively say we don't know.
372+
// There are some cases we could still return `0` for, e.g.
373+
// if the pointers being equal would require their statics to overlap
374+
// one or more bytes, but for simplicity we currently only check
375+
// strictly in-bounds pointers.
376+
2
399377
}
400-
} else {
401-
// Even if one of them is a static, as per https://doc.rust-lang.org/nightly/reference/items/static-items.html#r-items.static.storage-disjointness
402-
// immutable statics can overlap with other kinds of allocations sometimes.
403-
// FIXME: We could be more decisive for (non-zero-sized) mutable statics,
404-
// which cannot overlap with other kinds of allocations.
405-
// `GlobalAlloc::{Memory, Function, Vtable}` can at least be deduplicated with
406-
// the same kind, so comparing two of the same kind of those should return 2.
407-
// `GlobalAlloc::TypeId` exists mostly to prevent consteval from comparing
408-
// `TypeId`s, so comparing two of those should always return 2.
409-
// FIXME: Can we determine any other cases?
410-
2
411378
}
379+
} else {
380+
// All other cases we conservatively say we don't know.
381+
//
382+
// For comparing statics to non-statics, as per https://doc.rust-lang.org/nightly/reference/items/static-items.html#r-items.static.storage-disjointness
383+
// immutable statics can overlap with other kinds of allocations sometimes.
384+
//
385+
// FIXME: We could be more decisive for (non-zero-sized) mutable statics,
386+
// which cannot overlap with other kinds of allocations.
387+
//
388+
// Functions and vtables can be duplicated and deduplicated, so we
389+
// cannot be sure of runtime equality of pointers to the same one, or the
390+
// runtime inequality of pointers to different ones (see e.g. #73722),
391+
// so comparing those should return 2, whether they are the same allocation
392+
// or not.
393+
//
394+
// `GlobalAlloc::TypeId` exists mostly to prevent consteval from comparing
395+
// `TypeId`s, so comparing those should always return 2, whether they are the
396+
// same allocation or not.
397+
//
398+
// FIXME: We could revisit comparing pointers into the same
399+
// `GlobalAlloc::Memory` once https://github.com/rust-lang/rust/issues/128775
400+
// is fixed (but they can be deduplicated, so comparing pointers into different
401+
// ones should return 2).
402+
2
412403
}
413404
}
414405
})

tests/ui/consts/ptr_comparisons.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,15 @@ do_test!(FN_PTR, 0 as *const u8, Some(false));
9595

9696
// aside from 0, these pointers might end up pretty much anywhere.
9797
do_test!(&A, align_of::<T>() as *const u8, None);
98-
do_test!(&A, 1 as *const u8, Some(false)); // this one takes into account alignment, so we know that
98+
do_test!((&raw const A).wrapping_byte_add(1), (align_of::<T>() + 1) as *const u8, None);
99+
100+
// except that they must still be aligned
101+
do_test!(&A, 1 as *const u8, Some(false));
102+
do_test!((&raw const A).wrapping_byte_add(1), align_of::<T>() as *const u8, Some(false));
103+
104+
// This one could return `Some(false)` by noticing that it would require `A` to be at address `0`,
105+
// but we don't currently check for that.
106+
do_test!((&raw const A).wrapping_byte_add(1), 1 as *const u8, None);
99107

100108
// When pointers go out-of-bounds, they *might* become null, so these comparions cannot work.
101109
do_test!((&raw const A).wrapping_add(2), 0 as *const u8, None);
@@ -141,6 +149,15 @@ do_test!(&A, (&T(42) as *const T).wrapping_byte_add(1), Some(false));
141149
do_test!(&A, (const { &T(42) } as *const T).wrapping_byte_add(1), Some(false));
142150
do_test!(&A, ({ const X: T = T(42); &X } as *const T).wrapping_byte_add(1), Some(false));
143151

152+
// We could return `Some(false)` here, as pointers to different statics can never be equal if that
153+
// would require the statics to overlap, even if the pointers themselves are offset out of bounds.
154+
// We currently only check strictly in-bounds pointers, however.
155+
do_test!(
156+
(&raw const LARGE_WORD_ALIGNED).cast::<usize>().wrapping_add(2),
157+
(&raw const MUT_LARGE_WORD_ALIGNED).cast::<usize>().wrapping_add(1),
158+
None
159+
);
160+
144161
// Pointers into the same static are equal if and only if their offset is the same,
145162
// even if either is out-of-bounds.
146163
do_test!(&A, &A, Some(true));

0 commit comments

Comments
 (0)