-
Notifications
You must be signed in to change notification settings - Fork 707
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
Add option to generate safe and unsafe conversions for rustified enums #2908
Open
jbaublitz
wants to merge
1
commit into
rust-lang:main
Choose a base branch
from
jbaublitz:issue-2646
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// bindgen-flags: --rustified-enum 'Plain.*' --rustified-enum 'TryFromRaw.*=try_from_raw' --rustified-enum='FromRawUnchecked.*=from_raw_unchecked' --rustified-enum='Both.*=try_from_raw,from_raw_unchecked' --rustified-enum 'NonExhaustive.*=non_exhaustive' | ||
|
||
enum Plain { | ||
Plain1, | ||
Plain2, | ||
Plain3 | ||
}; | ||
|
||
enum TryFromRaw { | ||
TFR1 = -1, | ||
TFR2 = 5, | ||
TFR3 | ||
}; | ||
|
||
enum FromRawUnchecked { | ||
FRU1 = 6, | ||
FRU2 = 10, | ||
FRU3 = 11, | ||
}; | ||
|
||
enum Both { | ||
Both1, | ||
Both2 = -1, | ||
Both3, | ||
}; | ||
|
||
enum NonExhaustive { | ||
Ex1, | ||
Ex2, | ||
}; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 a good idea to add a few functions here, e.g.
enum Plain f(enum Plain);
and so on, to double-check the generated type inexpectations
.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.
Oof, it appears after adding some tests that it is converting it to the type that will result in undefined behavior. I'll have to take a look at how to avoid that. I think the code to handle that is in a place I haven't touched, so I'll have to track down where that's happening.
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 is what I feared, yeah. Thanks!
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.
So is what you're recommending only doing this in the case of TryFrom generation like with the constants?
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 am not sure what you mean -- do you have an example?
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.
Maybe, one alternative to roll this change slowly would be to add another flag like
--use-enum-ctypes-on-fns
or--return-enum-ctypes
(name is a WIP 🤣) that when enabled, makes all functions return the ctype instead of the rustified enum. We could keep it disabled by default and eventually enable it by default as unsoundness is a pretty good basis for it.@jbaublitz @ojeda what do you think?
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.
How would a user call
takes_plain
with a value that is not in the Rust-sideenum
? i.e. a C function may need to be used with more values too. Of course, perhaps thebindgen
user should avoid this kind ofenum
in this 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.
I think the only case where a Rust user would call
takes_plain
with a value outside the rustified enum would be if there is a common pattern in the C library like:So doing that from the rust side would be weird as you'd have:
However, if you just use the
Plain_ctype
everywhere, that's not cumbersome anymoreYes maybe in that case you'd be better not using
--rustified-enum
at all. However, I could see a case where you'd want to offer both the rustified enum and safe wrappers to those functions.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.
(My previous comment was intended to reply to your "two functions", rather than the
--use-enum-ctypes-on-fns
one; our messages crossed :)Exactly, that is why I think using the Rust
enum
for parameters may not be great, unless the expectation is that users avoid it in some cases. So they would probably still not use it as their "default" or "catch all" case, i.e.--... *
.Hmm... What do you mean by safe wrappers? i.e. the raw functions would still need to be marked unsafe, even if we took Rust
enum
s, so the user would still need to wrap it.Having said that, I would like to eventually have a
[[safe]]
attribute on the C side that allows us to do things like that -- for the kernel for instance, we are now at the point were starting to add annotations on the C side for several things may be OK for some subsystems at least. Similarly, we could have a[[enum ...]]
thing that givesbindgen
this information without having to maintain the lists outside the source code. I don't know if nowadayslibclang
preserves unknown attributes -- if so, we could already do it without having to modify and wait for Clang.The other extreme is to have an "easy-to-use" mode, where by default all is provided: the raw C type and its constants, plus a Rustified
enum
type, plus a non-exhaustive one, plus the conversion functions (fallible, infallible and unsafe) to convert to both of those... Then users can use whatever they think it is best for eachenum
, without having to know about thebindgen
kinds, and it is sound. Then when they are sure how they use eachenum
, they can restrict it to one kind via the arguments (or via attributes if we ever get there) so that they avoidbindgen
generating code that will not be needed. So it would be easy to start using, and allow people to customize later. But it would be more "expensive" by default.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.
Yeah I think that the extra convenience of using the rustified enum would only work if you want to expose the enum itself. And if you want to expose safe bindings for functions that use the c enum, then you have to do those yourself accordingly to the invariants of the library itself.
That's true, even if the function takes the rustified enum then you still have to figure out its safety requirements and expose it accordingly. So you'd still have to do things like
So, having to add an extra
plain.to_raw_ctype()
or whatever to obtain the C value seems like little effort. Maybe @emilio or @kulp have a better idea on why bindings of functions that take C enums become functions that take Rustified enums instead of using the integer type directly as they have been around for longer.That sounds like a sensible addition. In the meantime, it is already possible to embed html-like tags in the comments of the headers and bindgen can read those to introduce custom behavior.
Yeah ideally you should be able to decide if a C enum should be translated in different ways without interference between each representation. I think the API would need some bike-shedding, specially from the CLI side where there isn't much flexibility. Maybe the idea of having a
bindgen.toml
file where we can store more structured information would be useful here as you could do something likeThen anything that matches the
Foo
regex would get all of those immediately. Maybe a CLI equivalent could be--enum-style "Foo=rustified(try_from_raw,from_raw_unchecked),constified"
It would also be nice to be able to replace the multitude of
--newtype-enum
--newtype-global-enum
,--constified-enum
, etc, by a single option that can be extended. This would also give @jbaublitz some flexibility as it wouldn't be necessary to include the raw type constants with this feature as those would be enabled automatically if you pass the extraconstified
style.