Skip to content
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

Merged
merged 2 commits into from
Jan 26, 2017

Conversation

nikhilshagri
Copy link
Contributor

@nikhilshagri nikhilshagri commented Dec 29, 2016

@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

@@ -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,
Copy link
Contributor

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();
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

@nikhilshagri
Copy link
Contributor Author

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.

@emilio
Copy link
Contributor

emilio commented Dec 30, 2016 via email

@nikhilshagri
Copy link
Contributor Author

cursor.defintion() always returns a cursor of type StructDecl, it doesn't matter whether the cursor points to a struct definition or a declaration.

One solution, like I suggested earlier, is to modify the CompInfo::from_ty method to accept a cursor as well, and use that cursor to determine whether it points to a declaration or not.

@nikhilshagri
Copy link
Contributor Author

nikhilshagri commented Jan 5, 2017

@emilio I got two suggestions from twitter, which work somewhat:

  1. Use cursor.clang_isCursorDefinition(), but this doesn't work for templated structs. No false positives here.
  2. Parse the cursor as a token using clang_tokenize() and check if the name ends with a semicolon.

Option 2 is a hack, but it could be useful. Should I try using a combination of these two to see if it works?

@emilio
Copy link
Contributor

emilio commented Jan 6, 2017

clang_isCursorDefinition sounds great, I'd rather patch libclang than tokenize, so lets use that :)

@emilio
Copy link
Contributor

emilio commented Jan 6, 2017

BTW, clang_isCursorDefinition was mentioned in the issue, I assumed you'd tried it :)

@nikhilshagri
Copy link
Contributor Author

I'd tried it initally for the cursor we obtain from ty.declaration() in from_ty, but forgot to test it for the one in from_clang_ty.

@@ -66,6 +66,7 @@ pub mod root {
#[link_name = "_ZN1w3fooEv"]
pub fn foo() -> root::C<::std::os::raw::c_int>;
}
pub enum C { }
Copy link
Contributor Author

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.

@nikhilshagri
Copy link
Contributor Author

@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, template < typename > struct char_traits; does not resolve into an enum, because the corresponding CursorKind is ClassTemplate.

Copy link
Contributor

@emilio emilio left a 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 { }
Copy link
Contributor

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() {
Copy link
Contributor

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 }
Copy link
Contributor

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);
Copy link
Contributor

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?

Copy link
Contributor

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().

@bors-servo
Copy link

☔ The latest upstream changes (presumably #414) made this pull request unmergeable. Please resolve the merge conflicts.

@nikhilshagri
Copy link
Contributor Author

What happened here??!! :P

image

@fitzgen
Copy link
Member

fitzgen commented Jan 23, 2017

What happened here??!! :P

:) Everything that was in ./libbindgen/* should now be in ./* for the most part :P

@briansmith
Copy link

See also rust-lang/rfcs#1861 and the referenced internals discussion about why using empty enums for this is (probably) wrong.

@fitzgen
Copy link
Member

fitzgen commented Jan 23, 2017

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.

@nikhilshagri
Copy link
Contributor Author

@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) &&
Copy link
Contributor Author

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.

@briansmith
Copy link

@briansmith We decided to go ahead with this and if needed, change it later :)

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):

#[repr(C)]
pub struct Opaque([u8; 0]);

@emilio
Copy link
Contributor

emilio commented Jan 24, 2017

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!

@nikhilshagri
Copy link
Contributor Author

@emilio pushed new changes.

@emilio
Copy link
Contributor

emilio commented Jan 25, 2017

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.

@nikhilshagri
Copy link
Contributor Author

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;

@emilio
Copy link
Contributor

emilio commented Jan 26, 2017

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.

@nikhilshagri
Copy link
Contributor Author

I added a condition to generate the tuple struct for classes as well.

Copy link
Contributor

@emilio emilio left a 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|
Copy link
Contributor

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;
};

Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added :)

@emilio
Copy link
Contributor

emilio commented Jan 26, 2017

@bors-servo r+

Thanks for doing this @cynicaldevil \o/

@bors-servo
Copy link

📌 Commit 78aaa32 has been approved by emilio

@bors-servo
Copy link

⌛ Testing commit 78aaa32 with merge 5cc2506...

bors-servo pushed a commit that referenced this pull request Jan 26, 2017
Forward declared structs now generate opaque enums

@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
@bors-servo
Copy link

☀️ Test successful - status-travis

@bors-servo bors-servo merged commit 78aaa32 into rust-lang:master Jan 26, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants