-
Notifications
You must be signed in to change notification settings - Fork 706
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
Forward declared structs now generate opaque enums #370
Conversation
@@ -236,6 +236,8 @@ pub struct CompInfo { | |||
/// Used to detect if we've run in a has_destructor cycle while cycling | |||
/// around the template arguments. | |||
detect_has_destructor_cycle: Cell<bool>, | |||
|
|||
is_declaration: bool, |
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.
let's rename this and the method to is_forward_declaration
, and also add a doc comment here describing what this indicates.
@@ -506,6 +509,7 @@ impl CompInfo { | |||
debug!("CompInfo::from_ty({:?}, {:?})", kind, cursor); | |||
|
|||
let mut ci = CompInfo::new(kind); | |||
ci.is_declaration = cursor.definition().unwrap().is_declaration(); |
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.
This is maybe not enough? The definition is also a declaration, we need to search for a declaration that has no definition.
Note that clang has CXCursor_NoDeclFound
, so we can probably do something like:
ci.is_forward_declaration = cursor.definition().map_or(true, |d| d.kind() == CXCursor_NoDeclFound);
That might be enough.
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.
Why should the default value be true here?
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.
what if we modify the from_ty
function to accept a cursor as well? We could simple use clang_isDeclaration()
then.
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's only true if the definition is invalid.
Is there any way I can print out the cursor kind and run transformations on a single header file? This might give us some more information. |
You can run bindgen on a header file with the --emit-clang-ast option,
and also with RUST_LOG=libbindgen to see pretty much everything you
could need.
…On Fri, Dec 30, 2016 at 06:11:12AM -0800, Nikhil Shagrithaya wrote:
Is there any way I can print out the cursor kind and run transformations on a single header file? This might give us some more information.
--
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub:
#370 (comment)
|
One solution, like I suggested earlier, is to modify the |
@emilio I got two suggestions from twitter, which work somewhat:
Option 2 is a hack, but it could be useful. Should I try using a combination of these two to see if it works? |
clang_isCursorDefinition sounds great, I'd rather patch libclang than tokenize, so lets use that :) |
BTW, clang_isCursorDefinition was mentioned in the issue, I assumed you'd tried it :) |
I'd tried it initally for the cursor we obtain from |
@@ -66,6 +66,7 @@ pub mod root { | |||
#[link_name = "_ZN1w3fooEv"] | |||
pub fn foo() -> root::C<::std::os::raw::c_int>; | |||
} | |||
pub enum C { } |
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 know why this is appearing here, even though the struct has already been declared a few lines above.
@emilio I've updated the PR, but like I said, there's still a few false negatives that I can't get around. For 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.
Looks way better! :)
pub _address: u8, | ||
pub _phantom_0: ::std::marker::PhantomData<T>, | ||
} | ||
pub enum TErrorResult_Message { } |
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.
This is wrong, you still need to make it take a template parameter, there is no way this is compiling otherwise.
@@ -723,6 +723,17 @@ impl CodeGenerator for CompInfo { | |||
return; | |||
} | |||
|
|||
// generate an opaque enum if struct is a forward declaration | |||
if self.kind() == CompKind::Struct && self.is_forward_declaration() { |
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.
Given that, let's skip also structs with template parameters.
Why only check for Struct
? instead of both struct and union?
@@ -190,6 +190,10 @@ impl Cursor { | |||
unsafe { clang_getCursorKind(self.x) } | |||
} | |||
|
|||
pub fn is_definition(&self) -> bool { | |||
unsafe { clang_isCursorDefinition(self.x) == 0 } |
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.
This is returning true if the cursor is not a definition, which causes a lot of confusion. Can you change this check to != 0
, and invert the conditional it in other places?
Also, this is going to need a line of documentation or this won't pass CI.
@@ -506,6 +512,8 @@ impl CompInfo { | |||
debug!("CompInfo::from_ty({:?}, {:?})", kind, cursor); | |||
|
|||
let mut ci = CompInfo::new(kind); | |||
ci.is_forward_declaration = location.map_or(true, |cur| | |||
cur.is_definition() && cur.kind() == CXCursor_StructDecl); |
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.
Why only if the cursor is CXCursor_StructDecl
?
Also, I think this should work ok if you just do:
ci.is_forward_declaration = !cursor.is_definition();
That should make it work for structs with template parameters too. Does that work?
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.
Alternatively, worth trying !cursor.definition().is_valid()
.
☔ The latest upstream changes (presumably #414) made this pull request unmergeable. Please resolve the merge conflicts. |
:) Everything that was in |
See also rust-lang/rfcs#1861 and the referenced internals discussion about why using empty enums for this is (probably) wrong. |
I'm not 100% sure that argument applies since we would be manipulating pointers to such enums, not the enum directly. |
1716936
to
c4ade05
Compare
@briansmith We decided to go ahead with this and if needed, change it later :) |
@@ -481,6 +486,9 @@ impl CompInfo { | |||
debug!("CompInfo::from_ty({:?}, {:?})", kind, cursor); | |||
|
|||
let mut ci = CompInfo::new(kind); | |||
ci.is_forward_declaration = location.map_or(true, |cur| | |||
(cur.kind() == CXCursor_StructDecl || cur.kind() == CXCursor_UnionDecl) && |
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.
@emilio It might be a good idea to add a test for unions as well, since none of the existing tests are testing it.
If rust-bindgen starts using the empty enum pattern then it will create a lot of inertia preventing any deprecation of that pattern, which would be bad. Why not use the similar thing that the Rust lang team recommends instead, according to rust-lang/rfcs#1861 (comment):
|
That sounds fine to me Brian. @cynicaldevil, it shouldn't be hard to change, instead of EnumBuilder you can use the tuple_struct method from aster, or the quote_item! macro, at your choice. There are examples of both in that same file. Thanks for commenting here @briansmith, I was the one initially proposing opaque enums but I wasn't aware of the issues. Whether those issues are more or less likely to happen in practice for bindgen doesn't seem too relevant if there's a better and equally simple to implement alternative. Thanks again! |
c4ade05
to
4e0a624
Compare
@emilio pushed new changes. |
I'm surprised this only hits once, may you add a few more tests for this? The common patters of forward-declaring a struct or union, then defining functions taking a pointer to that should be enough. |
How about this: struct Foo;
struct Bar {
Foo *f;
};
void baz_struct(Foo* f);
union Union;
void baz_union(Union* u);
// conversion to tuple struct does not work on classes
class Quux; |
That looks quite fine to me. We should be able to make it work on classes without template parameters though. But we can leave that as a followup if you want. |
4e0a624
to
9866229
Compare
I added a condition to generate the tuple struct for classes as well. |
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.
The rest looks great! Thanks for doing this :)
@@ -481,6 +486,10 @@ impl CompInfo { | |||
debug!("CompInfo::from_ty({:?}, {:?})", kind, cursor); | |||
|
|||
let mut ci = CompInfo::new(kind); | |||
ci.is_forward_declaration = location.map_or(true, |cur| |
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.
we could write this as:
ci.is_forward_declaration = location.map_or(true, |cur| {
match cur.kind() {
CXCursor_StructDecl |
CXCursor_UnionDecl |
CXCursor_ClassDecl => !cur.is_definition(),
_ => false,
}
});
Which I think it's a bit more legible, what do you think?
struct Bar { | ||
Foo *f; | ||
}; | ||
|
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.
Could you also add an empty struct (struct Baz {}
), and verify that it doesn't produce an opaque enum?
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.
Added :)
9866229
to
78aaa32
Compare
@bors-servo r+ Thanks for doing this @cynicaldevil \o/ |
📌 Commit 78aaa32 has been approved by |
☀️ Test successful - status-travis |
@emilio : I checked the test outputs again, and it seems that these changes are affecting struct definitions as well. Hence, I have not committed the test changes yet.
Fixes #62