Feature 699 constified enum module#741
Conversation
The name used for the type is generated by |
ab6b6f4 to
5762339
Compare
emilio
left a comment
There was a problem hiding this comment.
Looks generally ok, but needs some more tests.
In particular:
- Test that it works with C++ namespaces.
- Test that it works with
typedef enum foo bar; bar ty - Test that it works in other contexts like pointers, function arguments, function return values...
src/ir/item.rs
Outdated
| -> Vec<String> { | ||
| let path = self.canonical_path(ctx); | ||
| if let super::item_kind::ItemKind::Type(ref type_) = self.kind { | ||
| if let &TypeKind::Enum(ref enum_) = type_.kind() { |
There was a problem hiding this comment.
nit: if let TypeKind::Enum(ref enum_) = *type.kind() looks more consistent with the rest of the code.
src/ir/item.rs
Outdated
| if let &TypeKind::Enum(ref enum_) = type_.kind() { | ||
| if enum_.is_constified_enum_module(ctx, self) { | ||
| // Type alias is inside a module | ||
| return vec![path.last().unwrap().clone(), |
There was a problem hiding this comment.
This probably won't work for C++ namespaces, you need to use let mut path above and path.push(CONSTIFIED_ENUM_MODULE_REPR_NAME.into()) here, then keep executing the rest of the logic of this function.
Though it feels somewhat hacky to put it here...
src/ir/item.rs
Outdated
| -> Vec<String> { | ||
| let path = self.canonical_path(ctx); | ||
| let mut path = self.canonical_path(ctx); | ||
| let mut is_constified_module_enum = false; |
There was a problem hiding this comment.
Let's add a helper function that returns this instead.
src/ir/item.rs
Outdated
| } | ||
| if ctx.options().enable_cxx_namespaces { | ||
| if is_constified_module_enum { | ||
| path.push(CONSTIFIED_ENUM_MODULE_REPR_NAME.into()); |
There was a problem hiding this comment.
I think this should be in canonical_path instead, and we'd just keep this as it was. This will make this function way less error-prone, see below.
There was a problem hiding this comment.
I think the check should be in namespace_aware_canonical_path. If canonical_path appends ::Type to the path, then namespace_aware_canonical_path will have to also check if the item is a constified module enum. Both functions would need to be changed instead of just namespace_aware_canonical_path.
There was a problem hiding this comment.
Well, I guess, but that relies on canonical_path users to not care about this... Which I guess holds, for now. Fine then.
| typedef foo_alias1 foo_alias2; | ||
|
|
||
| typedef struct bar { | ||
| foo member1; |
There was a problem hiding this comment.
Add the new option to ns1::foo2, and add an usage of it here, I'm pretty sure it'd be broken.
There was a problem hiding this comment.
I added the test; the behavior seems to be correct
There was a problem hiding this comment.
Not with namespaces disabled. The following test still fails:
// bindgen-flags: --constified-enum-module one_Foo
namespace one {
enum class Foo {
Variant1, Variant2,
};
}
class Bar {
one::Foo* baz;
};| return vec![path.last().unwrap().clone(), | ||
| CONSTIFIED_ENUM_MODULE_REPR_NAME.into()]; | ||
| } | ||
| if ctx.options().disable_name_namespacing { |
There was a problem hiding this comment.
I don't think this branch is correct.
|
Looks pretty good, btw! |
src/ir/item.rs
Outdated
| } | ||
| return path; | ||
| } | ||
| if is_constified_module_enum { |
There was a problem hiding this comment.
To fix the last test-case I commented about, you should remove this early return and let it arrive to the last return.
For that to work, you need to add the REPR_NAME to the path, so this method could be structured like:
if is_constified_module_enum {
path.push(CONSTIFIED_ENUM_MODULE_REPR_NAME.into());
}
if ctx.options().enable_cxx_namespaces {
return path;
}
if ctx.options().disable_name_namespacing {
let trailing_segments = if is_constified_module_enum { 2 } else { 1 };
return path[..path.len() - trailing_segments].iter().cloned().collect();
}
return vec![path[1..].join("_")];
emilio
left a comment
There was a problem hiding this comment.
Oh, another thing we should test is when the name of the variant is the same as the current REPR_NAME. I guess we should mangle the variant in that case.
Test-case:
// bindgen-flags: --constified-enum-module MyEnum
enum MyEnum {
Type,
Type_,
Bar,
};Though actually, that just works on rust1, apparently, so let's just add the test-case.
Item::is_constified_enum_module() only returns true for the base type, not for "layers" of aliases. Added a "simple alias" test and added content to the types test.
|
@emilio I hit the points you mentioned; I ended up handling |
| } | ||
|
|
||
| /// Returns whether the item is a constified module enum | ||
| fn is_constified_enum_module(&self, ctx: &BindgenContext) -> bool { |
There was a problem hiding this comment.
So what you're trying to do here is resolving through ResolvedTypeRefs, but not through Alias, right?
If so, instead of reinventing the wheel, you can do something like:
self.id.into_resolver()
.through_type_refs()
.resolve(ctx)And keep the comment on why not through type alias.
We should arguably unify that and the canonical_type business, but that also handles some template stuff that may be nontrivial, so it's worth doing on a followup.
There was a problem hiding this comment.
The check needs to "peel back" one layer of Aliases. The issue with the resolve() function is that it resolves through all of the Aliases.
I think that implementation should remain as is.
There was a problem hiding this comment.
Hmm... I see. I guess I'm not sure why would making an alias claim it's a constified enum module be necessary.
I guess I'll poke a bit at the code this evening when I have the chance and see which tests break with that.
There was a problem hiding this comment.
Hmm... Ok, so what breaks in that case is the typedef enum case... fun. I still think we should be able to do better, though...
There was a problem hiding this comment.
What about modifying ItemResolver to optionally disable recursive resolving? More generally, ItemResolver could even take a positive integer indicating through how many layers to resolve.
This way, is_constified_enum_module() could be simplified to use ItemResolver.
There was a problem hiding this comment.
I don't think this is about the number of layers to resolve. I think the logic in is_constified_enum_module is wrong, and the "alias to resolved type ref to alias" logic is just masking it.
There was a problem hiding this comment.
Here's what I came up with, I think this is what you're really trying to do with that function:
/// Returns whether the item is a constified module enum.
fn is_constified_enum_module(&self, ctx: &BindgenContext) -> bool {
// Do not jump through aliases, except for aliases that point to a type
// with the same name, since we don't generate code for them.
let item = self.id.into_resolver().through_type_refs().resolve(ctx);
let type_ = match *item.kind() {
ItemKind::Type(ref type_) => type_,
_ => return false,
};
match *type_.kind() {
TypeKind::Enum(ref enum_) => {
enum_.is_constified_enum_module(ctx, self)
}
TypeKind::Alias(inner) => {
// TODO(emilio): Make this "hop through type aliases that aren't
// really generated" an option in `ItemResolver`?
let inner_item = ctx.resolve_item(inner);
let name = item.canonical_name(ctx);
if inner_item.canonical_name(ctx) != name {
return false;
}
return inner_item.is_constified_enum_module(ctx)
}
_ => false,
}
}There was a problem hiding this comment.
Feel free to address the TODO if you want, btw :)
emilio
left a comment
There was a problem hiding this comment.
r=me, with that nasty bit of code simplified :)
Used suggested code from @emilio and also added a test for an alias to an anonymous enum.
emilio
left a comment
There was a problem hiding this comment.
Looks great, thanks!
And sorry it took so long to get landed :)
|
@bors-servo r+ |
|
📌 Commit 814d28e has been approved by |
Feature 699 constified enum module This is a work in progress for issue #699 that adds the `--constified-enum-module` option to bindgen. @emilio, could you give me some guidance on fixing the uses of the enum variant types? In the example below, `foo` should be replaced with `foo::Type`. I'm not sure of the proper way to rename `Item`s after the structures have been defined. My initial thought was to redefine the `CodeGenerator` trait to take a mutable reference to `item`, but that will not work because of the borrow checker. Thoughts? Todo: - [x] put constified enum variants in a `mod` - [x] ensure references to constified enum `foo` are changed to `foo::Type` - [x] handle `typedef` enums ----- Given the input header `tests/headers/constify-module-enums.h`: ~~~c // bindgen-flags: --constified-enum-module foo enum foo { THIS, SHOULD_BE, A_CONSTANT, }; struct bar { enum foo this_should_work; }; ~~~ `$ cargo run -- tests/headers/constify-module-enums.h --constified-enum-module foo --no-layout-tests` will output: ~~~rust /* automatically generated by rust-bindgen */ pub mod foo { pub type Type = ::std::os::raw::c_uint; pub const THIS: Type = 0; pub const SHOULD_BE: Type = 1; pub const A_CONSTANT: Type = 2; } #[repr(C)] #[derive(Debug, Copy)] pub struct bar { pub this_should_work: foo, } impl Clone for bar { fn clone(&self) -> Self { *self } } ~~~
|
☀️ Test successful - status-travis |
This is a work in progress for issue #699 that adds the
--constified-enum-moduleoption to bindgen.@emilio, could you give me some guidance on fixing the uses of the enum variant types? In the example below,
fooshould be replaced withfoo::Type. I'm not sure of the proper way to renameItems after the structures have been defined. My initial thought was to redefine theCodeGeneratortrait to take a mutable reference toitem, but that will not work because of the borrow checker. Thoughts?Todo:
modfooare changed tofoo::TypetypedefenumsGiven the input header
tests/headers/constify-module-enums.h:$ cargo run -- tests/headers/constify-module-enums.h --constified-enum-module foo --no-layout-testswill output: