12

I have a pointer buf: *const T pointing to the start of an allocation of n elements of T, and I define the following check:

let in_alloc = buf <= ptr && ptr < unsafe { buf.add(n) };

Is it guaranteed that in_alloc is true for any ptr that lies in the allocation of buf, and false in any other case? We may assume that ptr is a valid pointer to a T object (so not misaligned/null/dangling), however it may or may not be from the same allocation as buf. Finally we may assume T is not zero-sized.

orlp
  • 112,504
  • 36
  • 218
  • 315

3 Answers3

8

Answering the title, comparing any two pointers is well-defined since pointers implement Ord.

With pointers being totally ordered, the body of the question follows easily from this. You have a set of n distinct pointers, starting at buf + 0 and ending at buf + (n - 1). If ptr is less than buf it can't be equal to any of them. If ptr is greater than buf + (n - 1) it also cannot be equal to them. If ptr is one of them, both expressions evaluate to true.

You can somewhat sidestep the issue and use Range instead:

let end = unsafe { buf.add(n) };
let in_alloc = (buf..end).contains(ptr);

This is often used, for example, to check if a slice contains a pointer.

GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • 2
    I like this answer because it depends only on the mathematical properties implied by the pointer type's trait implementations (well, and knowing that allocations can't be _interleaved_ somehow). – Kevin Reid Mar 07 '22 at 18:53
  • _With pointers being totally ordered, the body of the question follows easily from this_ Is knowing that pointers are totally ordered enough? What guarantees that the total order is consistent with arithmetic? In C++, `std::less` gives total order for pointers, but it is not enough to implement a check that some pointer belongs to an allocated block. – Language Lawyer Mar 07 '22 at 23:20
  • _A total order on aligned pointers to T does not allow overlapping ranges_ What does «aligned pointer» mean here to guarantee this? – Language Lawyer Mar 08 '22 at 01:49
  • @LanguageLawyer Actually, I thought alignment was required but even that is not needed. Specifically, `Range<*const T>` relates ordering with arithmetic. This is explicitly leveraged in documentation examples, e.g. slice [`as_ptr_range`](https://doc.rust-lang.org/std/primitive.slice.html#method.as_ptr_range). More generally, Rust pointers (mostly due to LLVM) implement integer-like semantics (sometimes to a fault, like over-aggressive optimizations). There is some discussion around making this more formal in UCG WG (though, stalled afaik). – GManNickG Mar 08 '22 at 21:58
  • I think it's important to remember Rust doesn't have a formal spec. It's semantics are defined as a mix of documentation, what LLVM does, what it probably wants to do instead, and what people expect out of it. The integer-esque nature is an example - people (including the language implementation) will use `(x..y).contains(p)` to check for slice membership, or `p.wrapping_add` followed by a comparison to check for wrapping. When a formal spec does eventually land, it will need to provide the explicit, rather than implicit, connection. I'll edit the answer to provide some examples. – GManNickG Mar 08 '22 at 22:01
1

According to the official documentation, it is valid to produce a raw pointer one byte past the allocation of the object the pointer points to (so +1). Obviously you cannot dereference the pointer, but it can be used for comparisons, eg. for bounds checking in a loop.

Beyond that, it's undefined behaviour, so you're not guaranteed anything at all. In your case adding an arbitrary offset to the pointer would not be a good idea for this reason.

So, to answer more specifically, it will return the value you expect as long as buf.add(n) points to an address that is at most 1 byte past the allocation of the object buf points to.

See https://doc.rust-lang.org/1.59.0/std/primitive.pointer.html#method.offset for more details.

Diego Veralli
  • 1,011
  • 6
  • 13
0

Yes, this code works as you would except. Comparing pointers that point to different allocations should behave as expected as implied by the documentation of std::ptr::eq.

AnttiP
  • 306
  • 2
  • 6