RFC: result_ffi_guarantees by Lokathor · Pull Request #3391 · rust-lang/rfcs · G...
source link: https://github.com/rust-lang/rfcs/pull/3391
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
RFC: result_ffi_guarantees #3391
Conversation
text/0000-result_ffi_guarantees.md
Outdated
# Summary |
||
[summary]: #summary |
||
This RFC gives specific layout and ABI guarantees when wrapping "non-zero" data types from `core` in `Option` or `Result`. This allows those data types to be used directly in FFI, in place of the primitive form of the data (eg: `Result<(), NonZeroI32>` instead of `i32`). |
This RFC gives specific layout and ABI guarantees when wrapping "non-zero" data types from `core` in `Option` or `Result`. This allows those data types to be used directly in FFI, in place of the primitive form of the data (eg: `Result<(), NonZeroI32>` instead of `i32`). | |
This RFC gives specific layout and ABI guarantees when wrapping "non-zero" data types from `core` in `Result`. This allows those data types to be used directly in FFI, in place of the primitive form of the data (eg: `Result<(), NonZeroI32>` instead of `i32`). |
Contributor
Author
To the best of my knowledge, having these layout+ABI guarantees on Option hasn't actually been made official by any RFC or similar. The standard library has some examples of where Option wrapping some type will give a same layout, but I'm not sure of anywhere that actually specifies an ABI compatibility.
Contributor
Author
Re-reading that, it seems to assure memory layout not ABI.
I can remove the mentions of Option stuff if folks think it's absolutely necessary, but it's probably better to have things clear and all in one place. I'm very interested in doing low level and ffi work in Rust, and if even I don't know where to find this info, then we probably haven't written it down enough.
Option
-like enums have their layout and ABI guaranteed by a FCP in rust-lang/rust#60300. Although, this is not currently documented in Rust reference but in UCG only.
added T-lang Relevant to the language subteam, which will review and decide on the RFC. I-lang-nominated Indicates that an issue has been nominated for prioritizing at the next lang team meeting.
labels
Contributor
Is there any particular downside to just making this guarantee for all enums similar to |
Contributor
Author
Yes that was mentioned in the Future Work portion. Basically it can probably become a semver hazard sort of thing if it's automatically done for all enums and people aren't opting in to the behavior. Keeping the RFC scoped small makes it more likely to be accepted and completed on the types people use most often. |
Contributor
Ahh, I see; I completely missed that part. I guess that what I'm failing to understand is how you can actually run into that kind of hazard unless you use something like |
The examples need a bit more diversity. They are all of the form This needs some more discussion on which exact zero-sized types should have layout optimization guarantees, because it could be a semver hazard, particularly in FFI. A type I have doubts whether the layout guarantees would be useful in practice, though. The primary motivation in the RFC isn't memory usage but FFI ergonomics. With This means that a separate ABI-stable struct as the return type, with functions |
Tbh, I'm not particularly keen on having guarentees on |
@ChrisDenton Rust guarantees their specific behaviour, so this seems like a non-issue. On the FFI side none of those types would exist anyway, just like |
Contributor
Author
repr(transparent) newtypes of |
Why would you make it a newtype of
|
Contributor
Author
I had thought that a So sure, |
Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members: Concerns:
Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns. |
added proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. disposition-merge This RFC is in PFCP or FCP with a disposition to merge it.
labels
Member
Overall seems reasonable. @rfcbot concern needs-to-mention-non_exhaustive It's implied by the definition of |
Member
Zero fields was FCPed in rust-lang/rust#77841 |
There's been some ideas of potentially modifying the calling convention to support Result more cheaply. Does this RFC interfere with that? |
Contributor
Author
This RFC requires that the combined type be treated as the primitive type, so I guess "yes, it could interfere with that". However, since the primitive types are generally single-register (except and wide pointers and ints bigger than a machine register), this doesn't seem like a strong problem. Other forms of Result (eg:
It seems that the reference didn't get an update.
Updated. |
Member
one thing i'd specifically want to reserve space for is representing this may need a specific carve-out exception in the defined ABI for |
Contributor
Author
Hm, could you say more about what you'd expect to be changed within the RFC to have that carved out? Some mentioning in Future Possibilities? I'm completely unfamiliar with how unwinding interacts with the foreign ABI modes. |
text/0000-result_ffi_guarantees.md
Outdated
* `Result<T, E>` where either `T` or `E`: |
||
* Are a zero-sized type with alignment 1 (a "1-ZST"). |
||
* Either have no fields (eg: `()` or `struct Foo;`) or have `repr(transparent)` if there are fields. |
||
* Do not have the `#[non_exhaustive]` attribute. |
- I assume the nested list of requirements is a conjunction, but that should be made explicit.
- I am surprised to see
repr(transparent)
here. In the past we stated that arepr(transparent)
in the standard library does not mean this type will not change in the future -- basicallyrepr(transparent)
is a private attribute that the library may use, but not clients of the library. If this is intended to deal with issues like repr(transparent) should not consider non_exhaustive types as 1-ZSTs outside their crate rust#78586, I would have expected "has no private field" (which is then trivially true for fieldless types)
Contributor
Author
-
yeah it's a "must meet all of these", i can clarify the wording.
-
I'd be fine changing to "must have no fields" if necessary. Since it's in FCP though maybe a T-lang member can make a clear call here before I touch that part?
The issue is that it's very easy to add a private field and accidentally break the guaranteed layout. Imho there should be some explicit way to opt into such optimizations for ZSTs which are guaranteed to remain ZST. I don't think #[repr(transparent)]
is a good solution to the issue, but it's the closest approximation in current Rust, so it's at least something to consider.
Is the "private attribute" opinion documented somewhere? Afaik it isn't, and it's not uncommon to treat it as an API guarantee.
Contributor
Author
I asked Josh via Zulip and he said it would be fine to further restrict the rules here even while FCP is active.
I will make an update later today to simply require no fields at all (and no non_exhaustive). Then it will be the usual semver breaking change if a field is added. I believe that should satisfy things?
No. The thing that really worries me is that, being a guaranteed optimization, breaking the layout won't produce any errors. The optimization will just silently stop applying. This will lead to UB on the FFI side, and I don't see any reliable way to check for such breakage.
An upstream crate makes a semver breaking change by adding some inner field (possibly even zero-sized one, but perhaps not in a guaranteed way). A downstream crate which used the changed type in extern fn() -> Result<&T, ZST>
now silently gets UB, and there is no way for them to avoid it other than tracking all changes in all dependencies.
Currently the solution would involve explicitly ABI-stable types, and conversions between them and native Rust types. Such conversions would either work correctly, fail to compile or fail at runtime (assuming they are properly written and verify their preconditions).
Also note that this isn't an issue for current types with guaranteed layout optimizations, since their number is very small and they are all provided by the language. There is only a finite number of non-generic NonZeroX
types, and they are all guaranteed by the language. If user-defined niches become a thing, I would be just as worried.
For this reason I would prefer to have an explicit way to specify that the layout optimization must apply, rather than deduce it from implicit properties of code, which may be changed by the programmer without even considering that someone could rely on them.
Contributor
Author
If the crate you're getting a type from puts some attribute on their type, can't they just remove that attribute (presumably as a semver break) in a future version? Then isn't it just as bad for the downstream crates relying on it?
Honestly I don't think people should do this in the first place with any type that's not their own type or ()
. However, I don't want to disallow user ZSTs entirely because it makes for much better internal error handling compared to having ()
as the error type.
the point of having an attribute is that explicitly having to remove it makes people consider it a semver break since it's obvious, whereas having it completely implicit makes it much more likely people won't realize they need a semvar break to change their library type -- most people aren't going to think about Result
's ABI when they change their library type.
An upstream crate makes a semver breaking change by adding some inner field (possibly even zero-sized one, but perhaps not in a guaranteed way). A downstream crate which used the changed type in
extern fn() -> Result<&T, ZST>
now silently gets UB, and there is no way for them to avoid it other than tracking all changes in all dependencies.
We do have improper_ctypes
warning though.
improper_ctypes
is far too noisy to be useful, so we shouldn't rely on a fairly substandard lint.
Member
This RFC covers the FFI case, which wouldn't use that optimisation. And if it did then the future RFC proposing that would have a lot of specifying to do, and I think that specifying belongs there rather than here. |
I'm basically expecting that the list of requirements to have
this way we tell users they can't just just check the current list of requirements for any arbitrary type and be guaranteed to always forevermore have stable ABI when those requirements are met, future RFCs may introduce exceptions for types using new features. |
Member
basically I want to say that the following code is unsound:
|
Contributor
Author
I think I'm with josh, if there was some new way to work with results that would be some new abi, not the existing abis. If the rfcs says "you need to meet these requirements, and also maybe we'll add more requirements later", that's not a stable guarantee at all. Your example code is basically exactly what I want to have be allowed: Assuming some T that's trivially constructable. |
it is a stable guarantee, because it's guaranteed to work with all types not using features defined in any future RFCs adding exceptions, e.g. it always works with
but the point is that example code doesn't check for any of the new features that might be on some |
if ZST types had to be explicitly marked as ABI stable for |
It wouldn't matter in this case, since the RFC is only specifying the behavior of ...But it's an interesting question whether, in general, Admittedly, it's a topic for another thread, especially now that the RFC no longer mentions |
Contributor
Author
@scottmcm I believe your concern has been resolved if you want to review again. |
Member
@rfcbot resolve needs-to-mention-non_exhaustive |
removed the I-lang-nominated Indicates that an issue has been nominated for prioritizing at the next lang team meeting. label
Suggestion: add a new layout attribute, More specifically,
In particular, ZST-based layout optimizations, like the ones proposed in this RFC, apply only to
Is this fine? Perhaps this should be extracted into a separate RFC, but it doesn't have any current purpose without the layout optimization guarantees, so I'd add it to the current RFC. |
there is a current purpose:
|
Member
@afetisov One problem there is that ZST-ness is necessary, but not sufficient. Also, I think that |
Contributor
@rfcbot fcp reviewed This makes sense to me. |
added final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised.
and removed proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period.
labels
This is now entering its final comment period, as per the review above. |
Slight correction: function item types. Function pointers have sizes. |
added finished-final-comment-period The final comment period is finished for this RFC. to-announce
and removed final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised.
labels
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon. |
Member
This RFC was discussed in this week's lang team triage meeting. We (I) weren't sure whether the compiler already treats types like
If As far as I could tell, the former is the case, at least in my usual suspect ABI (32-bit x86).
|
Contributor
The @rust-lang/lang team has decided to accept this RFC. To track further discussion, subscribe to the tracking issue here: |
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
No one assigned
disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this RFC. T-lang Relevant to the language subteam, which will review and decide on the RFC. to-announce
None yet
No milestone
Successfully merging this pull request may close these issues.
None yet
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK