-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
repr(C) on an enum accepts discriminants that do not fit into the default C enum size #124403
Comments
The standard did not say much about enum width until recently. Specifically the standard called out only
In reality many compilers just silently expanded the width (Godbolt link to an old Clang from before C23) to accommodate values. So your enum would be fine in reality on GCC, Clang, TCC, and many others (though on Godbolt CompCert & TenDRA reject it with an error). Obviously MSVC accepts it, but then silently truncates it to the one true (pre-C23) enum type. Plus the new standard text says
Meaning that as long as the value can fit in a unsigned long long then it should be fine (on a C23 compiler). MSVC has not implemented this, or really much of C23 yet. There is also this new syntax in C23: enum Foo : uint64_t {
A = 1111111111111111111,
}; Which is implemented in GCC and Clang for specifying the underlying type exactly. |
I think we have the equivalent of that, via
Thanks for checking! But the standard also doesn't say that truncation is wrong, or does it? I guess our options are similar to here. Except for this issue I feel completely fine with just erroring when the discriminant does not fit into the default enum type; that should be very rare and warrants an explicit type annotation in any case... |
I would say that C23 does say that is wrong. But pre-C23 silently truncating I would also say is a valid behavior. So it depends on what we are targeting, the C standard or C compilers. If we say "we target C23" then having 64-bit enums would be valid. If we say "we want to allow any weird C compiler to link with us," then erroring is valid too. |
Where does it say that? The text you quoted just says that the given discriminant value has to fit some integer type, but it does not say that it has to fit the concrete integer type chosen for the enum. Ah, but there's more text:
The thing is, this says that the different enumeration constants of the same enumeration can have different types! That's not possible in Rust -- so maybe we should indeed reject everything that does not fit into an |
That wording is fairly irrelevant in actuality as the type has to be stored in a fixed width. It is much worse than you think, Ralf. There is nothing actually requiring the C compiler to use int. That is, it has always been standard-conformant to use only 8- or 16-bit types even when the type of int is defined to be a 32-bit integer. Thus we have to match the actual compiler behaviors on this one, as we cannot look to the standard for saying anything about ABI, because it doesn't. |
I seemed to remember that compilers use the smallest type that fits, but the standard seemed fairly clear about using
Okay so what is the actual compiler behavior for GCC, clang, MSVC? Does MSVC always use |
It says representable by. GCC conceals the arbitrary-selection rule behind the |
For some of our targets, they are specified as using |
I believe the MSVC situation is expressed by https://learn.microsoft.com/en-us/cpp/build/reference/zc-enumtypes?view=msvc-170 |
@kennykerr Vibe check since you're the MSVC bindings expert: I get a strong sense from skimming the current bindings and the codegen patterns in windows-rs that you're avoiding |
The only reason I avoid |
That's part of C enums being basically just integer types with named constants, whereas Rust enums are proper algebraic sum types. Seems like there could be a macro to provide the "named constants" kind of enum in Rust? Anyway, that's a different discussion from this issue. |
I would always recommend using an explicit underlying type for enums to avoid this kind of ambiguity in C++. |
Example:
Rust currently gives
size_of::<OverflowingEnum>() = 8
. MSVC givessizeof(enum overflowing_enum) = 4
. Additionally, Rust givesOverflowingEnum::A as usize = 1111111111111111111
and MSVC givesOVERFLOWING_ENUM_A = 734294471
.This should probably be a hard error (forward compatibility lint). The nomicon currently states
and the reference currently states
The nomicon is clearly wrong here:
#[repr(c_int)]
on the enum would fail to compile (wherec_int
is the correct primitive) (because of the overflowing literal). I think erroring (forward compatibility linting) and pointing the user to use#[repr(u64)]
in this case makes sense.(Summary by @CAD97)
@CAD97 this should error on all targets, not just MSVC, I assume? What do non-MSVC C compilers do with such an enum?
The text was updated successfully, but these errors were encountered: