-
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 MSVC targets does not always match MSVC type layout when ZST are involved #81996
Comments
@mahkoh can you reproduce this without |
@jyn514 Yes |
repr(C) has served two purposes:
The first purpose is advertised both in the reference and in stdlib code e.g. in The second purpose is also advertised in the reference. However, these purposes are not compatible as shown above. |
The layout algorithm is not the same across all targets, it is always supposed to be whatever the C ABI mandates on that particular target |
Does this reproduce with clang? |
Let's make sure the windows notification group is aware of this: @rustbot ping windows |
Hey Windows Group! This bug has been identified as a good "Windows candidate". cc @arlosi @danielframpton @gdr-at-ms @kennykerr @luqmana @lzybkr @nico-abram @retep998 @rylev @sivadeilra |
Assigning P-high as discussed as part of the Prioritization Working Group procedure and removing I-prioritize. |
So to summarize:
|
One more thing #[repr(C, packed(1))]
struct X {
c: u8,
m: std::arch::x86_64::__m128,
}
#[no_mangle]
pub fn f() -> usize {
// Expected 32
// Actual 17
std::mem::size_of::<X>()
} |
That is, if we assume |
Yikes, I think a lot of folks do assume that, yeah. |
Basically all types that have a This particular problem could be fixed by adding such an annotation to __m128 on MSVC targets. The definition of __m128 is broken anyway because it has to be 16 byte aligned on MSVC targets but is defined as 4 byte aligned in the stdlib. So in the end this is not an inherent problem of the layout algorithm because you're not supposed to be able to write this anyway. |
C/C++ do not permit zero-sized structs or arrays. Aggregates (structures and classes) that have no members still have a non-zero size. MSVC, Clang, and GCC all have different extensions to control the behavior of zero-sized arrays. It is unfortunate that In the near term, perhaps the best thing is to add a new diagnostic, which is "you asked for |
GCC and Clang do in fact accept even completely empty structs and unions and such types have size 0 when compiled with these compilers. (Except that Clang tries to emulate MSVC when compiling for
That seems to be the most pragmatic solution. Alternatively one could deprecate repr(C) completely and replace it by repr(NativeC) and repr(stable).
Maybe only warn on msvc targets. |
In C++ they have size 1:
Actually the nomicon says:
So it seems that this (unsound) behavior is even documented. |
repr(C) is about C compatibility not about C++ compatibility.
Otherwise empty structs in msvc have size at least 4 bytes not 1 byte in C mode. |
These are non-standard extensions, and they deviate from the C/C++ specification. If I understand the desire for compatibility with the de-facto standard behavior of these compilers, from a practical point of view. At the same time, these are areas where they do deviate from the language standard. Figuring out the best solution for Rust will require some careful consideration, and the short-term solution of "make it work like Clang / MSVC / GCC" should not be chosen without due consideration. |
Small update after checking in with some very helpful MSVC folk:
So, basically, considering this feature is
Maybe we should not look into fixing this at all, but instead start reporting a future incompatibility error and strongly suggest people move away from it, irrespective on what platform they are compiling against? Even if we never make it a hard error, a strongly worded message like this may help move people away from it. So in short: |
Doing this in general would break the most obvious way to represent flexible array member (either the spec-supported |
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
Fix UB from misalignment and provenance widening in `std::sys::windows` This fixes two types of UB: 1. Reading past the end of a reference in types like `&c::REPARSE_DATA_BUFFER` (see rust-lang/unsafe-code-guidelines#256). This is fixed by using `addr_of!`. I think there are probably a couple more cases where we do this for other structures, and will look into it in a bit. 2. Failing to ensure that a `[u8; N]` on the stack is sufficiently aligned to convert to a `REPARSE_DATA_BUFFER`. ~~This was done by introducing a new `AlignedAs` struct that allows aligning one type to the alignment of another type. I expect there are other places where we have this issue too, or I wouldn't introduce this type, but will get to them after this lands.~~ ~~Worth noting, it *is* implemented in a way that can cause problems depending on how we fix rust-lang#81996, but this would be caught by the test I added (and presumably if we decide to fix that in a way that would break this code, we'd also introduce a `#[repr(simple)]` or `#[repr(linear)]` as a replacement for this usage of `#[repr(C)]`).~~ Edit: None of that is still in the code, I just went with a `Align8` since that's all we'll need for almost everything we want to call. These are more or less "potential UB" since it's likely at the moment everything works fine, although the alignment not causing issues might just be down to luck (and x86 being forgiving). ~~NB: I've only ensured this check builds, but will run tests soon.~~ All tests pass, including stage2 compiler tests. r? `@ChrisDenton`
This comment was marked as resolved.
This comment was marked as resolved.
(prepare for more accidental closures of this issue when that commit lands on the beta and stable branches -- at least that's how things went in the past) |
… MSVC abi fix. Tried fixing marker type declarations to be zero sized after MSVC abi. This commit assumes that this issue will get resolved to "#[repr(C)] structs can't be zero-sized": rust-lang/rust#81996
This issue would probably benefit from being split up into one issue per problem following this summary as well as this comment. |
Looking at it, it actually seems to be largely two issues -- enums with too big discriminants, and then everything around size 0. I have opened #124403 for the enums, so this issue is now about the case of structs / unions with no fields / zero-sized fields. |
In general it probably makes sense to consider this together with #100743, in the wider question of -- how do we deal with the differences between MSVC's layout algorithm and the one everyone else uses. There seem to be two differences that surfaced so far: how to deal with structs/unions where all fields have size 0, and how to deal with explicitly aligned types occurring as (potentially nested) fields of packed types.
Option 1 seems unlikely as there's a lot of code we'd have to exclude to be sure that we are in the fragment where all our current targets agree. Option 1 is also extremely unsatisfying for the aligned-field-in-packed-struct case as it leaves a gap in what can be expressed with repr(C) types in Rust. Option 3 seems closer to the original goal of |
Also see this summary below.
Consider
and the corresponding MSVC output: https://godbolt.org/z/csv4qc
The behavior of MSVC is described here as far as it is known to me: https://github.com/mahkoh/repr-c/blob/a04e931b67eed500aea672587492bd7335ea549d/repc/impl/src/builder/msvc.rs#L215-L236
The text was updated successfully, but these errors were encountered: