Skip to content

Commit d57616c

Browse files
author
bors-servo
authored
Auto merge of #549 - emilio:call-conv-lost, r=fitzgen
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. r? @fitzgen
2 parents 49b4dc8 + 4c07a72 commit d57616c

File tree

7 files changed

+169
-2
lines changed

7 files changed

+169
-2
lines changed

src/clang.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -633,9 +633,10 @@ impl Eq for Type {}
633633
impl fmt::Debug for Type {
634634
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
635635
write!(fmt,
636-
"Type({}, kind: {}, decl: {:?}, canon: {:?})",
636+
"Type({}, kind: {}, cconv: {}, decl: {:?}, canon: {:?})",
637637
self.spelling(),
638638
type_to_str(self.kind()),
639+
self.call_conv(),
639640
self.declaration(),
640641
self.declaration().canonical())
641642
}
@@ -1520,6 +1521,8 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult {
15201521
return;
15211522
}
15221523

1524+
print_indent(depth, format!(" {}cconv = {}", prefix, ty.call_conv()));
1525+
15231526
print_indent(depth,
15241527
format!(" {}spelling = \"{}\"", prefix, ty.spelling()));
15251528
let num_template_args =

src/ir/function.rs

+24
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use super::dot::DotAttributes;
55
use super::item::Item;
66
use super::traversal::{EdgeKind, Trace, Tracer};
77
use super::ty::TypeKind;
8+
use ir::derive::CanDeriveDebug;
89
use clang;
910
use clang_sys::CXCallingConv;
1011
use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult};
@@ -349,3 +350,26 @@ impl Trace for FunctionSig {
349350
}
350351
}
351352
}
353+
354+
// Function pointers follow special rules, see:
355+
//
356+
// https://github.com/servo/rust-bindgen/issues/547,
357+
// https://github.com/rust-lang/rust/issues/38848,
358+
// and https://github.com/rust-lang/rust/issues/40158
359+
//
360+
// Note that copy is always derived, so we don't need to implement it.
361+
impl CanDeriveDebug for FunctionSig {
362+
type Extra = ();
363+
364+
fn can_derive_debug(&self, _ctx: &BindgenContext, _: ()) -> bool {
365+
const RUST_DERIVE_FUNPTR_LIMIT: usize = 12;
366+
if self.argument_types.len() > RUST_DERIVE_FUNPTR_LIMIT {
367+
return false;
368+
}
369+
370+
match self.abi {
371+
Some(abi::Abi::C) | None => true,
372+
_ => false,
373+
}
374+
}
375+
}

src/ir/ty.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,13 @@ impl CanDeriveDebug for Type {
605605
TypeKind::Comp(ref info) => {
606606
info.can_derive_debug(ctx, self.layout(ctx))
607607
}
608+
TypeKind::Pointer(inner) => {
609+
let inner = ctx.resolve_type(inner);
610+
if let TypeKind::Function(ref sig) = *inner.canonical_type(ctx).kind() {
611+
return sig.can_derive_debug(ctx, ());
612+
}
613+
return true;
614+
}
608615
_ => true,
609616
}
610617
}
@@ -636,6 +643,7 @@ impl CanDeriveDefault for Type {
636643
TypeKind::ObjCSel |
637644
TypeKind::ObjCInterface(..) |
638645
TypeKind::Enum(..) => false,
646+
639647
TypeKind::Function(..) |
640648
TypeKind::Int(..) |
641649
TypeKind::Float(..) |
@@ -877,6 +885,7 @@ impl Type {
877885
let kind = match ty_kind {
878886
CXType_Unexposed if *ty != canonical_ty &&
879887
canonical_ty.kind() != CXType_Invalid &&
888+
ty.ret_type().is_none() &&
880889
// Sometime clang desugars some types more than
881890
// what we need, specially with function
882891
// pointers.
@@ -1132,7 +1141,32 @@ impl Type {
11321141
CXType_ObjCObjectPointer |
11331142
CXType_MemberPointer |
11341143
CXType_Pointer => {
1135-
let inner = Item::from_ty_or_ref(ty.pointee_type().unwrap(),
1144+
// Fun fact: the canonical type of a pointer type may sometimes
1145+
// contain information we need but isn't present in the concrete
1146+
// type (yeah, I'm equally wat'd).
1147+
//
1148+
// Yet we still have trouble if we unconditionally trust the
1149+
// canonical type, like too-much desugaring (sigh).
1150+
//
1151+
// See tests/headers/call-conv-field.h for an example.
1152+
//
1153+
// Since for now the only identifier cause of breakage is the
1154+
// ABI for function pointers, and different ABI mixed with
1155+
// problematic stuff like that one is _extremely_ unlikely and
1156+
// can be bypassed via blacklisting, we do the check explicitly
1157+
// (as hacky as it is).
1158+
//
1159+
// Yet we should probably (somehow) get the best of both worlds,
1160+
// presumably special-casing function pointers as a whole, yet
1161+
// someone is going to need to care about typedef'd function
1162+
// pointers, etc, which isn't trivial given function pointers
1163+
// are mostly unexposed. I don't have the time for it right now.
1164+
let mut pointee = ty.pointee_type().unwrap();
1165+
let canonical_pointee = canonical_ty.pointee_type().unwrap();
1166+
if pointee.call_conv() != canonical_pointee.call_conv() {
1167+
pointee = canonical_pointee;
1168+
}
1169+
let inner = Item::from_ty_or_ref(pointee,
11361170
location,
11371171
None,
11381172
ctx);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
4+
#![allow(non_snake_case)]
5+
6+
7+
#[repr(C)]
8+
#[derive(Copy)]
9+
pub struct JNINativeInterface_ {
10+
pub GetVersion: ::std::option::Option<unsafe extern "stdcall" fn(env:
11+
*mut ::std::os::raw::c_void)
12+
-> ::std::os::raw::c_int>,
13+
pub __hack: ::std::os::raw::c_ulonglong,
14+
}
15+
#[test]
16+
fn bindgen_test_layout_JNINativeInterface_() {
17+
assert_eq!(::std::mem::size_of::<JNINativeInterface_>() , 16usize , concat
18+
! ( "Size of: " , stringify ! ( JNINativeInterface_ ) ));
19+
assert_eq! (::std::mem::align_of::<JNINativeInterface_>() , 8usize ,
20+
concat ! (
21+
"Alignment of " , stringify ! ( JNINativeInterface_ ) ));
22+
assert_eq! (unsafe {
23+
& ( * ( 0 as * const JNINativeInterface_ ) ) . GetVersion as *
24+
const _ as usize } , 0usize , concat ! (
25+
"Alignment of field: " , stringify ! ( JNINativeInterface_ ) ,
26+
"::" , stringify ! ( GetVersion ) ));
27+
assert_eq! (unsafe {
28+
& ( * ( 0 as * const JNINativeInterface_ ) ) . __hack as *
29+
const _ as usize } , 8usize , concat ! (
30+
"Alignment of field: " , stringify ! ( JNINativeInterface_ ) ,
31+
"::" , stringify ! ( __hack ) ));
32+
}
33+
impl Clone for JNINativeInterface_ {
34+
fn clone(&self) -> Self { *self }
35+
}
36+
impl Default for JNINativeInterface_ {
37+
fn default() -> Self { unsafe { ::std::mem::zeroed() } }
38+
}
39+
extern "stdcall" {
40+
#[link_name = "_bar@0"]
41+
pub fn bar();
42+
}
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
4+
#![allow(non_snake_case)]
5+
6+
7+
pub type my_fun_t =
8+
::std::option::Option<unsafe extern "C" fn(arg1: ::std::os::raw::c_int,
9+
arg2: ::std::os::raw::c_int,
10+
arg3: ::std::os::raw::c_int,
11+
arg4: ::std::os::raw::c_int,
12+
arg5: ::std::os::raw::c_int,
13+
arg6: ::std::os::raw::c_int,
14+
arg7: ::std::os::raw::c_int,
15+
arg8: ::std::os::raw::c_int,
16+
arg9: ::std::os::raw::c_int,
17+
arg10: ::std::os::raw::c_int,
18+
arg11: ::std::os::raw::c_int,
19+
arg12: ::std::os::raw::c_int,
20+
arg13: ::std::os::raw::c_int,
21+
arg14: ::std::os::raw::c_int,
22+
arg15: ::std::os::raw::c_int,
23+
arg16: ::std::os::raw::c_int)>;
24+
#[repr(C)]
25+
#[derive(Copy)]
26+
pub struct Foo {
27+
pub callback: my_fun_t,
28+
}
29+
#[test]
30+
fn bindgen_test_layout_Foo() {
31+
assert_eq!(::std::mem::size_of::<Foo>() , 8usize , concat ! (
32+
"Size of: " , stringify ! ( Foo ) ));
33+
assert_eq! (::std::mem::align_of::<Foo>() , 8usize , concat ! (
34+
"Alignment of " , stringify ! ( Foo ) ));
35+
assert_eq! (unsafe {
36+
& ( * ( 0 as * const Foo ) ) . callback as * const _ as usize
37+
} , 0usize , concat ! (
38+
"Alignment of field: " , stringify ! ( Foo ) , "::" ,
39+
stringify ! ( callback ) ));
40+
}
41+
impl Clone for Foo {
42+
fn clone(&self) -> Self { *self }
43+
}
44+
impl Default for Foo {
45+
fn default() -> Self { unsafe { ::std::mem::zeroed() } }
46+
}

tests/headers/call-conv-field.h

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// bindgen-flags: -- -target i686-pc-win32
2+
// bindgen-unstable
3+
4+
struct JNINativeInterface_ {
5+
int (__stdcall *GetVersion)(void *env);
6+
unsigned long long __hack; // A hack so the field alignment is the same than
7+
// for 64-bit, where we run CI.
8+
};
9+
10+
__stdcall void bar();

tests/headers/derive-fn-ptr.h

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
typedef void (*my_fun_t)(int, int, int, int,
2+
int, int, int, int,
3+
int, int, int, int,
4+
int, int, int, int);
5+
6+
struct Foo {
7+
my_fun_t callback;
8+
};

0 commit comments

Comments
 (0)