Maximum array size for different targets

Currently, this code compiles only on 32-bit architectures

const _: *const [u8; isize::MAX as usize] = core::ptr::null();

For 64-bit architectures, the maximum for which this compiles is instead

const _: *const [u8; isize::MAX as usize >> 2] = core::ptr::null();

I was not able to find any documentation or reasoning for why this would be the case. I am currently writing a library which needs to deal with using many different array sizes as potential upper bounds for the size of another type (since generic_const_exprs is not an option yet). I was hoping to use a build script to generate code dealing with potentially large array sizes (using null pointers for most) in a platform independent way when I ran into this problem. Currently, I check if the pointer width is greater than 32 and if so, I divide all my maximum sizes by 4. However, this is not only a very unsatisfying solution, but it also leaves me confused as to whether there are any other targets that have these special limits (I couldn't find any), as well as to how I would figure that out programmatically at build time. Is there any information on this?

1 Like

Interestingly, they just changed this from 2^47 to 2^61. You must be on nightly or beta. rust/compiler/rustc_abi/src/lib.rs at master · rust-lang/rust · GitHub | Correct outdated object size limit by workingjubilee · Pull Request #127546 · rust-lang/rust · GitHub

/// Returns **exclusive** upper bound on object size in bytes.
///
/// The theoretical maximum object size is defined as the maximum positive `isize` value.
/// This ensures that the `offset` semantics remain well-defined by allowing it to correctly
/// index every address within an object along with one byte past the end, along with allowing
/// `isize` to store the difference between any two pointers into an object.
///
/// LLVM uses a 64-bit integer to represent object size in *bits*, but we care only for bytes,
/// so we adopt such a more-constrained size bound due to its technical limitations.
#[inline]
pub fn obj_size_bound(&self) -> u64 {
    match self.pointer_size.bits() {
        16 => 1 << 15,
        32 => 1 << 31,
        64 => 1 << 61,
        bits => panic!("obj_size_bound: unknown pointer bit size {bits}"),
    }
}

So these are definitively the maximums... but they clearly could be raised at any time, and you'd need to detect the exact rustc build.

I think it would be more sustainable to make something like:

struct BigPtr {
    ptr: *const (),
    t: PhantomData<[u8; usize::MAX]>,
}

PhantomData can hold usize::MAX on every architecture.

1 Like

Oh wow, I did go back in version to check if this was a regression because I had seen people claim that isize::MAX was the maximum size for an array, but somehow I did not check if the value used to be lower. Anyway, thank you very much! The code comment explains perfectly what's going on here. Sadly, I was able to find it myself when I tried to find the value from the point of the error message.
FYI, I was running stable 1.82 :slight_smile:
Underestimating the pointer size is also fine, the code always runs at compile time and if it encounters a value that is somehow too big, it will just panic telling the user to file an issue. I will be using min(ptr_width - 1, 61) for my maximum size with a reference to this.
Cut that, I thought mentioning the oversized type in any branch of my const would cause the error, but it actually only happens if I actually create a pointer like that, so I can just cast to *const () and erase the type in the case where it doesn't create a null pointer. This way it can go all the way up to usize::MAX without erroring, so thanks for that suggestion too!

On a different note, am I reading correctly that rustc has a hard-coded panic for exotic pointer widths, like 24-bit? I always thought it could support those.

I have read somewhere that LLVM only supports pointer sizes that are powers of two. So unless that's changed, there is little point in adding support in Rust. As for the panic, there's probably a check beforehand with a nicer error message, and the rest of the code just assumes the pointer size is something valid.

You sure? The playground doesn't agree. Rust Playground

Welp, I was using beta 1.83 and stable 1.82, must have been stale compiler output :upside_down_face:

1 Like

There go my hopes of writing an llvm backend for the toy assembler from my uni