summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <emilio@crisal.io>2017-03-01 01:43:37 +0100
committerEmilio Cobos Álvarez <emilio@crisal.io>2017-03-01 01:45:36 +0100
commit56021b28a5150d2f7f7b662a261956d9eff8fe9d (patch)
tree6843a409cb911dd861453dee6037a08ac8a9a103
parent4e4f092d603828647bae12754a57c59efdfee005 (diff)
Fix calling convention propagation for function pointers.
This sucks, but works. The full solution is a refactoring that needs more thought than the time I'm able to dedicate to bindgen right now, see the comment for details. Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
-rw-r--r--src/clang.rs5
-rw-r--r--src/ir/ty.rs28
-rw-r--r--tests/expectations/tests/call-conv-field.rs36
-rw-r--r--tests/headers/call-conv-field.h7
4 files changed, 74 insertions, 2 deletions
diff --git a/src/clang.rs b/src/clang.rs
index 1a45eefa..94463723 100644
--- a/src/clang.rs
+++ b/src/clang.rs
@@ -633,9 +633,10 @@ impl Eq for Type {}
impl fmt::Debug for Type {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt,
- "Type({}, kind: {}, decl: {:?}, canon: {:?})",
+ "Type({}, kind: {}, cconv: {}, decl: {:?}, canon: {:?})",
self.spelling(),
type_to_str(self.kind()),
+ self.call_conv(),
self.declaration(),
self.declaration().canonical())
}
@@ -1520,6 +1521,8 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult {
return;
}
+ print_indent(depth, format!(" {}cconv = {}", prefix, ty.call_conv()));
+
print_indent(depth,
format!(" {}spelling = \"{}\"", prefix, ty.spelling()));
let num_template_args =
diff --git a/src/ir/ty.rs b/src/ir/ty.rs
index ce42a171..a664fa79 100644
--- a/src/ir/ty.rs
+++ b/src/ir/ty.rs
@@ -877,6 +877,7 @@ impl Type {
let kind = match ty_kind {
CXType_Unexposed if *ty != canonical_ty &&
canonical_ty.kind() != CXType_Invalid &&
+ ty.ret_type().is_none() &&
// Sometime clang desugars some types more than
// what we need, specially with function
// pointers.
@@ -1132,7 +1133,32 @@ impl Type {
CXType_ObjCObjectPointer |
CXType_MemberPointer |
CXType_Pointer => {
- let inner = Item::from_ty_or_ref(ty.pointee_type().unwrap(),
+ // Fun fact: the canonical type of a pointer type may sometimes
+ // contain information we need but isn't present in the concrete
+ // type (yeah, I'm equally wat'd).
+ //
+ // Yet we still have trouble if we unconditionally trust the
+ // canonical type, like too-much desugaring (sigh).
+ //
+ // See tests/headers/call-conv-field.h for an example.
+ //
+ // Since for now the only identifier cause of breakage is the
+ // ABI for function pointers, and different ABI mixed with
+ // problematic stuff like that one is _extremely_ unlikely and
+ // can be bypassed via blacklisting, we do the check explicitly
+ // (as hacky as it is).
+ //
+ // Yet we should probably (somehow) get the best of both worlds,
+ // presumably special-casing function pointers as a whole, yet
+ // someone is going to need to care about typedef'd function
+ // pointers, etc, which isn't trivial given function pointers
+ // are mostly unexposed. I don't have the time for it right now.
+ let mut pointee = ty.pointee_type().unwrap();
+ let canonical_pointee = canonical_ty.pointee_type().unwrap();
+ if pointee.call_conv() != canonical_pointee.call_conv() {
+ pointee = canonical_pointee;
+ }
+ let inner = Item::from_ty_or_ref(pointee,
location,
None,
ctx);
diff --git a/tests/expectations/tests/call-conv-field.rs b/tests/expectations/tests/call-conv-field.rs
new file mode 100644
index 00000000..e9a4d873
--- /dev/null
+++ b/tests/expectations/tests/call-conv-field.rs
@@ -0,0 +1,36 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(non_snake_case)]
+
+
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct JNINativeInterface_ {
+ pub GetVersion: ::std::option::Option<unsafe extern "stdcall" fn(env:
+ *mut ::std::os::raw::c_void)
+ -> ::std::os::raw::c_int>,
+}
+#[test]
+fn bindgen_test_layout_JNINativeInterface_() {
+ assert_eq!(::std::mem::size_of::<JNINativeInterface_>() , 4usize , concat
+ ! ( "Size of: " , stringify ! ( JNINativeInterface_ ) ));
+ assert_eq! (::std::mem::align_of::<JNINativeInterface_>() , 4usize ,
+ concat ! (
+ "Alignment of " , stringify ! ( JNINativeInterface_ ) ));
+ assert_eq! (unsafe {
+ & ( * ( 0 as * const JNINativeInterface_ ) ) . GetVersion as *
+ const _ as usize } , 0usize , concat ! (
+ "Alignment of field: " , stringify ! ( JNINativeInterface_ ) ,
+ "::" , stringify ! ( GetVersion ) ));
+}
+impl Clone for JNINativeInterface_ {
+ fn clone(&self) -> Self { *self }
+}
+impl Default for JNINativeInterface_ {
+ fn default() -> Self { unsafe { ::std::mem::zeroed() } }
+}
+extern "stdcall" {
+ #[link_name = "_bar@0"]
+ pub fn bar();
+}
diff --git a/tests/headers/call-conv-field.h b/tests/headers/call-conv-field.h
new file mode 100644
index 00000000..b6d306eb
--- /dev/null
+++ b/tests/headers/call-conv-field.h
@@ -0,0 +1,7 @@
+// bindgen-flags: -- -target i686-pc-win32
+
+struct JNINativeInterface_ {
+ int (__stdcall *GetVersion)(void *env);
+};
+
+__stdcall void bar();