-
Notifications
You must be signed in to change notification settings - Fork 60
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
"Any two types with size 0 and alignment 1 are ABI-compatible" vs the Windows ABI #552
Comments
could we make the function return type syntax meaningful here? so |
https://doc.rust-lang.org/stable/reference/items/functions.html#r-items.fn.implicit-return.
Also |
yeah, I was proposing thinking about changing that... another probably better option would be to have #[repr(C)]
struct Z([u8; 0]);
#[repr(transparent)]
struct S1 {
a: Z,
b: (),
}
// define dso_local void @f(ptr sret(%struct.Z) %0) local_unnamed_addr #0 {
extern "win64" f() -> S1 {
S1 { a: Z([]), b: () }
}
#[repr(transparent)]
struct S2 {
a: (),
b: Z,
}
// define dso_local void @g() local_unnamed_addr #0 {
extern "win64" g() -> S2 {
S2 { a: (), b: Z([]) }
} |
I don't think the quirks of this... special... ABI should affect what we guarantee for other, more reasonable, ABIs. |
My understanding is that ZSTs are a non-standard extension to C by clang/gcc, hence why they don't exist under MSVC. In other words, we don't necessarily have to follow the same ABI - our ABI would be "just as correct" for windows. The question is whether that would cause too many issues for interop with C. We could have an ABI-modifying attribute to apply at a function level which makes the return value be returned via pointer, to handle that edgecase. |
@rust-lang/lang is there any appetite for a post-monomorphization lint or error rejecting argument types that are not supported by a particular ABI? The context here is passing ZST across This could be seen as similar to the lint that we already have rejecting SIMD vectors passed via
This would have to be a separate |
I would love to have that. And also to have it reject any |
In the interest of minimizing breaking changes, I'd like to make a difference between "we don't guarantee this type's ABI" and "this type just can't be passed via this ABI". Please don't expand the scope of this issue further than is absolutely necessary. |
I think the answer ought to be just "All return 1-ZSTs match c |
Although, now that I think about it, we should check other ABIs. |
I think there's a more general problem here where Rust is making guarantees that are not strictly within its remit. A platform's C ABI can be arbitrarily "odd", and yet we would still want to be able to interoperate with it via Can we relax the guarantees we're making in cases where it's outside our remit. For example, At monomorphisation time we should check that an We would return an error or warning if either of the following:
(These are just examples, but there might be other cases) |
BTW, if 1-ZSTs don't all have the same ABI, what's the ABI of the following? #[repr(transparent)]
struct Transparent6; |
If there isn't a single correct answer we should be at least warning about this during compilation, if not deny by default. |
I would expect
(source, emphasis mine). However, this does give the wrinkle that, by a strict reading, if there are 1-ZSTs that are not ABI-compatible with The meaning for Footnotes
|
That applies pre-mono when type-checking |
On the one hand, using our "C" ABIs for things that don't have C equivalents on the platform is fundamentally nonsensical. On the other hand, our C ABIs unfortunately do double duty as both C ABIs and as a (limited) stable Rust ABI. I would be in favour of starting by warning when a ZST is passed or returned (except for returning If we were starting from scratch then I'd think either we should deny ZSTs or else we should try for cross-platform consistency as much possible. But we're not so the potential impact of changing the status quo has to be considered. Maybe if we had some form of stable Rust ABI separate from C ABIs then this would be less of an issue. |
Note that The issue here arises because if all fields are 1-ZST (including in the case where there is no field), it is ambiguous which field to pick.
We do have the "improper FFI types" lint but I don't know whether or when it fires for ZST, and it can't help when generics are involved. |
I've long been of the position that My current leaning would be a complete rework of the FFI safety lint(s) into ABI safety which denies-by-default any However, I know that ABI is cruel, we at least de facto guarantee that
Notably, the lint does not fire for Footnotes
|
I think if that is the philosophy than ideally ZSTs should not have any effect on the C ABI if the ABI does not support them. I.e. returning a ZST is the same as returning |
So there are a lot of comments on this issue. I can't speak to the larger questions about refactoring everything but I definitely feel that post-mono lints or warnings for things that are not supported by the target ABI sounds like it would help prevent some hair-pulling moments. |
For SIMD types we made this a future-compat lint and to my knowledge the plan is still to eventually make this a hard error -- do you think that is not the right approach? Arguably there we have to do it as otherwise it's just unsound. For the Windows ZST case, pure Rust code is fine either way, so it's not strictly speaking a soundness question. |
So apparently ZST return types are actually returned via a return pointer on windows-gnu targets -- except of course when the return type is
()
. At the same time we promise "Any two types with size 0 and alignment 1 are ABI-compatible". These two things are in direct contradiction with each other. We have to either add something like "except on platforms with a ridiculous ABI, such as windows, then you are just on your own" to the ABI docs, or decide we do not follow the likely accidental ad-hoc ABI of windows-gnu, or emit a hard error for types that the ABI simply does not support.Note that the reason for this rule of any two 1-ZST being ABI compatible is that for types like
#[repr(transparent)] ZST([u8; 0]; ())
, we promise that the type is ABI-compatible with the field that is being transparently wrapped, and that could be either field. So the only way to always deliver on ourrepr(transparent)
ABI-compatibility promise is to make all 1-ZST ABI-compatible.This came up in rust-lang/rust#135204.
Cc @programmerjake @ChrisDenton @eddyb
The text was updated successfully, but these errors were encountered: