-
Notifications
You must be signed in to change notification settings - Fork 13.8k
document ABI compatibility #115476
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
document ABI compatibility #115476
Changes from 3 commits
281d8cc
044d057
52d22ea
8f03a55
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -1480,7 +1480,7 @@ mod prim_ref {} | |||||
/// | ||||||
/// ### Casting to and from integers | ||||||
/// | ||||||
/// You cast function pointers directly to integers: | ||||||
/// You can cast function pointers directly to integers: | ||||||
/// | ||||||
/// ```rust | ||||||
/// let fnptr: fn(i32) -> i32 = |x| x+2; | ||||||
|
@@ -1506,6 +1506,113 @@ mod prim_ref {} | |||||
/// Note that all of this is not portable to platforms where function pointers and data pointers | ||||||
/// have different sizes. | ||||||
/// | ||||||
/// ### ABI compatibility | ||||||
/// | ||||||
/// Generally, when a function is declared with one signature and called via a function pointer with | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It isn't just about function pointers. If a function is declared There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where do we document and guarantee what one has to do when linking multiple Rust objects together? Do we even support that with We have to put these docs somewhere, and function pointers are the only way to trigger these issues inside the language (without exotic things such as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand from seeing other issues why you see "inside the language" as a useful initial scope because that's what seems to matter for the SIMD stuff. I don't object to that. IME people are much more likely to run into ABI issues in cross-language situations since there are no guardrails at all, so I hope we at least are open to solving the issue for cross-language cases too. Perhaps that means adding similar language to the documentation of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Fully agreed. It's also a much less defined space, you basically have to define "which C type and ABI is compatible with which Rust type and ABI" (and then potentially also which C++/Julia/whatever type, though I guess we can rely on those languages saying which C types their types are compatible with). This depends on the target and honestly I'm quickly out of my depth for those questions. ABI still surprises me with new nightmares every few weeks, so I'm sure there's more to come. I do hope someone will pick this up eventually.
Of course we are, I didn't want to give any indication to the contrary! It's just not the problem I want to solve right now. It's not on my own personal todo list either, at the moment. |
||||||
/// a different signature, the two signatures must be *ABI-compatible* or else calling the function | ||||||
/// via that function pointer is Undefined Behavior. ABI compatibility is a lot stricter than merely | ||||||
/// having the same memory layout; for example, even if `i32` and `f32` have the same size and | ||||||
/// alignment, they might be passed in different registers and hence not be ABI-compatible. | ||||||
/// | ||||||
/// ABI compatibility as a concern only arises in code that alters the type of function pointers, | ||||||
/// code that imports functions via `extern` blocks, and in code that combines `#[target_feature]` | ||||||
/// with `extern fn`. Altering the type of function pointers is wildly unsafe (as in, a lot more | ||||||
/// unsafe than even [`transmute_copy`][mem::transmute_copy]), and should only occur in the most | ||||||
/// exceptional circumstances. Most Rust code just imports functions via `use`. `#[target_feature]` | ||||||
/// is also used rarely. So, most likely you do not have to worry about ABI compatibility. | ||||||
/// | ||||||
/// But assuming such circumstances, what are the rules? For this section, we are specifically | ||||||
/// concerned with the case where both the caller and the callee are defined in Rust. | ||||||
|
/// concerned with the case where both the caller and the callee are defined in Rust. | |
/// concerned with the case where both the caller and the callee are defined in Rust (and using the same compiler version). |
Right? We're not making guarantees about inter-op between future and past compiler versions here, in terms of what the ABI of some primitive is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you start linking separately compiled Rust objects with each other there's a ton of things you need to consider. Using the same Rust version is necessary but far from sufficient. I don't even know the full list of things, but at the very least you must use the same target triple, and there's a bunch of other flags that must be set the same, as far as I understand.
I'm not sure if we want to get into listing all these requirements here. I want to discuss ABI, not linking.
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
Mark-Simulacrum marked this conversation as resolved.
Show resolved
Hide resolved
tmandry marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be declared stable and relied on for code that is #[cfg]
'd for a specific target. Though it sounds like we aren't declaring any of those stable right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it could, but this PR for now takes the stance that we shouldn't do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see why we shouldn't, but also not opposed to leaving this until we find a use case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One obvious case would be usize
vs u64
on 64 bit platforms (and u32
, u16
etc).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds more like a case we might want to add to the list: usize
is compatible with the uN
type of the same size; and similar for isize
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see why we shouldn't, but also not opposed to leaving this until we find a use case.
Which wording would you propose here? "There might be other stable things but we won't tell you which" is useless. And we certainly don't want to promise "anything that's incidentally ABI-compat on some target will remain ABI-compat on that target". So I think we only have two options:
- the thing I wrote
- an extra list of guaranteed target-specific extensions to ABI-compatibility
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chorman0773 can you double-checking this new section, does this sound accurate?
@rust-lang/opsem please also take a look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean we need to declare the target features in every extern "C"
declaration?
In general it would be helpful to generalize the discussion here to also handle cases where pointers are not involved but instead extern "ABI"
is used to declare a non-Rust function that is then called from Rust.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned above I strongly want to avoid scope creep to non-Rust calls here. That's a much more complicated discussion.
Uh oh!
There was an error while loading. Please reload this page.