Open ahomescu opened 5 years ago
The error message could be improved to say why these hints are incompatible (that is, if this error shouldn't be removed outright)
Note that the aligned outer/packed inner workaround is still really limited. If the struct contains any struct fields with their own alignment, then you'll still hit this error. (The xregs_state
example does have this exact problem, as its i387: fxregs_state
field has an alignment of 16)
So I think it makes sense for rust to support this for repr C/FFI purposes at the very least.
note that u8 extended_state_area[0];
is not valid C code according to standard and should not be used in 2019, u8 extended_state_area[];
instead.
Having structs with repr(align(N))
(transitively) inside a struct that is repr(packed(N))
is currently not allowed because gcc and VC++ differ in their behavior. Given:
#[repr(C, align(4))]
struct Foo(u8);
#[repr(C, packed(1))]
struct Bar(Foo);
What is align_of::<Bar>()
? If we follow gcc's behavior it is 1, but if we follow VC++'s behavior it is 4. Things get even hairier when you consider MinGW which is gcc and therefore has gcc behavior despite targeting Windows which should obey the C ABI dictated by VC++.
@retep998
you consider MinGW which is gcc and therefore has gcc behavior despite targeting Windows which should obey the C ABI dictated by VC++.
Actually no, gcc can do whatever it want even targeting windows, the "only" requirement is that the final program work. Also, a structure defined in user side can only be use by user side, so gcc don't have to follow msvc rule to work on window in the user side. OFC, when gcc need to call Windows API they need to follow their rules, also note I don't talk about msvc rules but those of Windows API (they have probably the same anyway).
All of this to say, C ABI is not a standard thing, __attribute__ ((packed, aligned (64)))
is clearly not defined by the standard and compiler are allow to have extension, there are totally implemented behavior.
Actually no, gcc can do whatever it want even targeting windows, the "only" requirement is that the final program work.
If MinGW compiled binaries want to do anything they have to be able to call into dlls compiled by VC++, which means they have to match the ABI that VC++ uses. Whether the C ABI is standardized or whether the language features in question are even standard is irrelevant. If there is a dll out there that uses #pragma pack
in combination with __declspec(align(N))
then you need to be able to call into it regardless of whether you are in the VC++ world or the MinGW world.
Having pc-windows-msvc
and pc-windows-gnu
differ in their behavior around this would be a massive footgun, but at the same time having pc-windows-gnu
differ from all other -gnu
targets would also be a footgun. The lang team hasn't decided on a solution to this yet, and so this feature combination remains illegal.
Yes, but all of this is implemented side, C doesn't care of all of that, dll is not a thing in C standard, if MinGW want to handle dll produce by VC++ is their problem not C problem. I disagree about Rust trying to bet about what all C compiler should produce to have a FFI interface. C is design to be very flexible on what a compiler can do. So you are hurting a wall. Unless you want something like #[repr(C, gcc, 5.6, linux)]
, I don't think you can find a magic solution. It's look like you blame gcc for this even if gcc is allowed to do this even on windows and still be a valid C compiler.
The "best guess" is already a very good thing to have in the Rust side. I find that Rust don't have the ressource to handle every case of every implementation of C (specially with Microsoft, MSVC is not a true C compiler)
(specially with Microsoft, MSVC is not a true C compiler)
What is or isn't a "true" C compiler is not important nor is it relevant.
Rust needs to be able to interface with system libraries, which means that Rust needs to match the ABI used by system libraries. Therefore on Windows Rust has to match the VC++ ABI because that is the ABI used by system libraries. If MinGW differs from this then Rust is in a tight spot because pc-windows-gnu
still needs to be able to match the ABI used by system libraries.
In my opinion, having the behavior of mixing repr(packed(N))
and repr(align(N))
differ based on target is a huge footgun due to pc-windows-gnu
. Instead I'm in favor of allowing the user to explicitly choose which behavior they want, whether that be through alternative versions of repr(align(N))
and repr(packed(N))
or some other method.
What if repr(packed, align(N))
was allowed to compile on every target except windows? And it just remained an error there until a decision on windows was made? This would at least open it up to the most common use cases while not committing to a windows representation.
On the contrary I think Windows is the common use case and repr(packed, align(N))
should only be allowed there with VC++ semantics until a decision on other targets is made.
I'm not sure why you think Windows is necessarily the common case? The initial example above is from the Linux kernel headers and is blocking development of Rust kernel modules that use that and other similar structs. We need some way of being ABI compatible with the Linux kernel for the same reason that we need to be ABI compatible with VC++ libraries.
I'm not sure why you think Windows is necessarily the common case?
For the same reason that some people think non-Windows is the common case.
More seriously, I am opposed to implementing either behavior until the lang team decides on a solution that allows both behaviors to be used. I want this to be resolved just as much as you do, but I'm not willing to throw some platforms under the bus for the sake of other platforms.
I think the point was that there is only an ABI ambiguity on Windows and the ABI behaviour doesn't necessarily need to be consistent across platforms. Defining the same semantics for packed+aligned on both pc-windows-gnu
or pc-windows-msvc
would be a problem, but leaving those targets undefined as they currently are and defining default semantics for other targets would at least allow this combination to be used in cases where there is no ambiguity in ABI implementations. Windows would be no worse off than currently and other platforms would be able to interface with packed+aligned structs.
All that said, though, it sounds like we do need to start an RFC on defining a syntax for controlling layout for the combination of packed structs and aligned fields.
We discussed this on Discord, and here's a rough sketch of some ideas and next steps:
x86_64-pc-windows-msvc
and matches the GCC ABI for x86_64-pc-windows-gnu
.repr(C)
must interoperate with the C compiler for the target. So, if we can't get -gnu to use the same ABI as -msvc, we need to match what -gnu does when targeting -gnu.extern
block.repr(system)
to designate a structure as following the same convention as system libraries, which on Windows will mean following the Windows ABI even on -gnu targets.Did anyone talk to the MinGW and/or GCC people about the ABI issue yet? If so, links to relevant conversations would be good.
We've been discussing this issue in the bcachefs IRC recently because it's become a blocker for continuing to integrate Rust more into bcachefs. It seems like the thing holding it back is the difference in behavior between platforms.
From the example given it seems like the difference comes down to whose alignment wins when you have a parent/outer struct with an embedded child struct. One compiler uses the child struct's alignment, the other the parent.
One idea we discussed: what about creating a version of the #[repr(align(N))
] attribute to specify which semantics to use? For example, a new attribute: #[repr(align_override_packed(N))]
that can be used when the behavior of the child overriding the parent alignment is desired?
#[repr(align(N))] #[repr(packed)]
can be used together to get the opposite behavior, parent/outer alignment wins.
The exact names don't matter at this point--just that we can come up with something reasonable to move forward. I'd appreciate feedback on this idea.
Also, I take it this will require an RFC at some point to either introduce a new attribute, or change the behavior of the current attributes? I am willing to try to get that started by initiating a pre-RFC discussion, if that'd be helpful.
Note that the aligned outer/packed inner workaround is still really limited. If the struct contains any struct fields with their own alignment, then you'll still hit this error.
They should hit a different error, no? "packed type cannot transitively contain a #[repr(align)]
type" is not the same error as "conflicting packed and align representation hints". There's another bug for the "cannot contain" issue: https://github.com/rust-lang/rust/issues/100743.
@bertschingert @ojeda does bcachefs/Rust-for-Linux need #59154 or #100743?
When converting C structures that are both packed and aligned using either C2Rust or bindgen, such as:
the code that either tool produces looks something like:
This Rust code fails to compile due to error
E0587
:We can work around this in C2Rust by emitting an aligned outer/packed inner structure pair (I think bindgen could do the same), but I'm wondering if it would be better to fix this on the Rust language/compiler side.