rust-lang/rfcs

make `sizeof`, `alignof`, `offsetof` and other type calculations available for use in constants

Closed this issue ยท 21 comments

Currently, the standard library offers functions like size_of, but they are not available for use in constant expressions. It would be useful to be able to call such functions from constants, but it raises some interesting questions. For example:

  1. only the backend can faithfully compute the size of a type, but sometimes the front-end may need to know the actual value of a constant to decide (e.g.) if two constants are equal. (Some of these issues also arise with associated constants.)
  2. do we want keywords for these computations (like C), or should we try to adapt functions (perhaps via const fn)?
  3. etc

Related RFCs and comments:

  • #591 -- add sizeof, alignof, offsetof intrinsics, etc
  • This comment by @eddyb, which sketches out how associated constants and some compiler magic could be used instead of keywords.
  • #1062 -- constants that depend on type parameters in generic code

For consistency, I'd be in favor of these being const fn instead of actual keywords.

eddyb commented

There is a compromise we can make to avoid going the static structural reflection route:

Allow dereferencing raw pointers which were cast from an integer, and produce an internal AddressLvalue(addr) constant value, equivalent to *(addr as *const _) in an unsafe context.
Only operations which are arithmetic on addr are accepted, e.g. field access, indexing, .offset.

Taking a reference to such lvalue results in Ref(AddressLvalue(addr)), which can be coerced to *const T, producing addr as *const, which can, in turn, be cast back to an integer type to retrieve addr.

At the API "membrane" level, constants (global/associated/generic const and const fn arguments/returns, maybe excluding intrinsics) have to be pure, which means no AddressLvalue present anywhere in them, and the origin of raw pointers cannot be assumed to be a safe reference, nor an integer address, so both dereferencing and casting back to integers is disallowed at that point.

This is enough to implement the "traditional" offsetof macro, with the representation logic lifted up from trans/LLVM to rustc proper.

comex commented

๐Ÿ‘ was just reminded that some of my code would really benefit from this.

In rust-lang/rust#32021, @huonw wrote

Unfortunately, the feature has some (possibly) surprising subtleties that make it trickier than one might hope.

I'm not sure what kind of subtleties are there in making mem::size_of (not size_of_val) a const fn. It doesn't involve any kind of AddressLvalue AFAICT.

eddyb commented

@kennytm FWIW I was talking about offsetof.

For size_of and align_of, I'd like to see them as functions, not keywords. offset_of seems like it requires a macro, to accept a field name.

Is there an issue with making these associated constants?

As an aside, I noticed the associated constants i16::MIN and i16::MAX are painful to access, while min_value() and max_value() remain easy.

Could be made accessible through a trait so you can be generic over integers with different reaches.

Is there any update on the status of offsetof? I would love to have access to it for the purpose of kernel internal linked lists.

would be great to have constant variant of mem::size_of_val() for macros that allocate byte-array on stack depending on size of macro-parameter: see https://crates.io/crates/releasetag
right now compiler-plugin is required to request size of b-string during compile time.

I'd love an associated type and/or macro that provided a type-level type_of. It would help track interface churn without without creating and importing numerous type aliases. A syntax might be :

enum Foo {
    Foo1(FooType),
}
struct AltFoo(type_of!<Foo::Foo1::0>, ..);

struct Bar { field: BarType }
struct AltBar { field: type_of!<Bar::field>, ... }

It's not as useful as say a ::LENGTH associated constant for fixed length arrays, or perhaps [S; size_of::<ArrayOfT>() / size_of::<T>], but still pretty handy.

eddyb commented

@burdges We have the typeof keyword reserved for if and the compiler has "accidentally" grown an almost full implementation of it - in that it's not a hard problem in the design we're moving towards - the same design that can evaluate size_of even early on.

All of these features have been ready for an ah-hoc implementation for a few months, but someone needs to write the RFCs.

What do you mean with "someone needs to write the RFCs"? No such RFC for size_of/typeof existing so far?? Can you give me a short sample, how the ad-hoc impl would be used in practice?

eddyb commented

@frehberg If a RFC has been done, it has been postponed. RFCs don't stay open waiting for the technology to arrive AFAIK. The functions would be ad-hoc in the compiler but they'd be used like at runtime - just some calls to std::mem::{size_of, align_of}.

typeof if trickier because you need to come up with the syntax for it, but say, typeof(f()) would get you the type returned by calling f with no arguments.
Type-checking can proceed just as it does in regular constants/functions, but the code would never be executed or required to obey e.g. constant-checking/borrow-checking rules.

size_of and align_of are const fn since a few months.

I'm going to close this issue, MIRI will be fully merged in rustc very soon and after that making specific functions or features constexpr is better tracked by separate issues in rust-lang/rust repo.

@petrochenkov did you open equivalent issues in rust-lang/rust after closing this one? I didn't find any in my cursory search. I'm happy to create them if not.

did you open equivalent issues in rust-lang/rust after closing this one?

No, I didn't, IIRC.
I'm not sure what here requires opening an issue given that sizeof/alignof are stable, and offsetof is an early phase RFC material.

I was actually interested in offsetof specifically, I wasn't sure if there was any development on that front yet.

Worth mentioning that I found this issue via a link from the original RFC discussion for adding offsetof, which suggested continuing the discussion here. If there's no other place currently tracking the addition of offsetof, I might at least make an issue in the RFCs repo here.