diff --git a/book/src/objc.md b/book/src/objc.md index 7c2892aaa9..c7c97811fd 100644 --- a/book/src/objc.md +++ b/book/src/objc.md @@ -17,6 +17,15 @@ The objective-c classes will be represented as a `struct Foo(id)` and a trait objc::runtime::Object` (the pointer to the objective-c instance). The trait `IFoo` is needed to allow for the generated inheritance. +Functions that use or return objective-c pointers of instance `Foo` will return +`Foo`. The reason this works is beacuse `Foo` represented as `transparent`. +This will be helpful for a lot of objective-c frameworks however there are some +cases where functions return `instancetype` which is a type alias for `id` so +an occasional `foo.0` may be required. An example of this would in the UIKit +framework should you want to add a `UILabel` to a +[UIStackView](https://developer.apple.com/documentation/uikit/uistackview/1616227-addarrangedsubview?language=objc) +you will need to convert the `UILabel` to a `UIView` via `UIView(label.0)`. + Each class (struct) has an `alloc` and a `dealloc` to match that of some of the alloc methods found in `NSObject`. diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 20bf54c48d..4bc185979c 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -3466,6 +3466,14 @@ impl TryToRustTy for Type { inner.into_resolver().through_type_refs().resolve(ctx); let inner_ty = inner.expect_type(); + let is_objc_pointer = + inner.kind().as_type().map_or(false, |ty| { + match ty.kind() { + TypeKind::ObjCInterface(..) => true, + _ => false, + } + }); + // Regardless if we can properly represent the inner type, we // should always generate a proper pointer here, so use // infallible conversion of the inner type. @@ -3474,7 +3482,8 @@ impl TryToRustTy for Type { // Avoid the first function pointer level, since it's already // represented in Rust. - if inner_ty.canonical_type(ctx).is_function() { + if inner_ty.canonical_type(ctx).is_function() || is_objc_pointer + { Ok(ty) } else { Ok(ty.to_ptr(is_const)) @@ -3493,9 +3502,12 @@ impl TryToRustTy for Type { TypeKind::ObjCId => Ok(quote! { id }), - TypeKind::ObjCInterface(..) => Ok(quote! { - objc::runtime::Object - }), + TypeKind::ObjCInterface(ref interface) => { + let name = ctx.rust_ident(interface.name()); + Ok(quote! { + #name + }) + } ref u @ TypeKind::UnresolvedTypeRef(..) => { unreachable!("Should have been resolved after parsing {:?}!", u) } @@ -4349,11 +4361,12 @@ mod utils { TypeKind::Pointer(inner) => { let inner = ctx.resolve_item(inner); let inner_ty = inner.expect_type(); - if let TypeKind::ObjCInterface(_) = + if let TypeKind::ObjCInterface(ref interface) = *inner_ty.canonical_type(ctx).kind() { + let name = ctx.rust_ident(interface.name()); quote! { - id + #name } } else { arg_item.to_rust_ty_or_opaque(ctx, &()) diff --git a/tests/expectations/tests/objc_class.rs b/tests/expectations/tests/objc_class.rs index 237b86b4ec..a1de91fb99 100644 --- a/tests/expectations/tests/objc_class.rs +++ b/tests/expectations/tests/objc_class.rs @@ -11,7 +11,7 @@ extern crate objc; #[allow(non_camel_case_types)] pub type id = *mut objc::runtime::Object; extern "C" { - pub static mut fooVar: *mut objc::runtime::Object; + pub static mut fooVar: Foo; } #[repr(transparent)] #[derive(Clone, Copy)] diff --git a/tests/expectations/tests/objc_class_method.rs b/tests/expectations/tests/objc_class_method.rs index e0a3803c26..d28a233da2 100644 --- a/tests/expectations/tests/objc_class_method.rs +++ b/tests/expectations/tests/objc_class_method.rs @@ -39,7 +39,7 @@ pub trait IFoo: Sized + std::ops::Deref { { msg_send!(class!(Foo), methodWithInt: foo) } - unsafe fn methodWithFoo_(foo: id) + unsafe fn methodWithFoo_(foo: Foo) where ::Target: objc::Message + Sized, { @@ -51,7 +51,7 @@ pub trait IFoo: Sized + std::ops::Deref { { msg_send!(class!(Foo), methodReturningInt) } - unsafe fn methodReturningFoo() -> *mut objc::runtime::Object + unsafe fn methodReturningFoo() -> Foo where ::Target: objc::Message + Sized, { diff --git a/tests/expectations/tests/objc_interface_type.rs b/tests/expectations/tests/objc_interface_type.rs index edbf412ca6..e8a1596378 100644 --- a/tests/expectations/tests/objc_interface_type.rs +++ b/tests/expectations/tests/objc_interface_type.rs @@ -30,7 +30,7 @@ pub trait IFoo: Sized + std::ops::Deref {} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct FooStruct { - pub foo: *mut objc::runtime::Object, + pub foo: Foo, } #[test] fn bindgen_test_layout_FooStruct() { @@ -63,8 +63,8 @@ impl Default for FooStruct { } } extern "C" { - pub fn fooFunc(foo: id); + pub fn fooFunc(foo: Foo); } extern "C" { - pub static mut kFoo: *const objc::runtime::Object; + pub static mut kFoo: Foo; } diff --git a/tests/expectations/tests/objc_method.rs b/tests/expectations/tests/objc_method.rs index 13e4d9f86d..e24768d28e 100644 --- a/tests/expectations/tests/objc_method.rs +++ b/tests/expectations/tests/objc_method.rs @@ -39,7 +39,7 @@ pub trait IFoo: Sized + std::ops::Deref { { msg_send!(self, methodWithInt: foo) } - unsafe fn methodWithFoo_(self, foo: id) + unsafe fn methodWithFoo_(self, foo: Foo) where ::Target: objc::Message + Sized, { @@ -51,7 +51,7 @@ pub trait IFoo: Sized + std::ops::Deref { { msg_send!(self, methodReturningInt) } - unsafe fn methodReturningFoo(self) -> *mut objc::runtime::Object + unsafe fn methodReturningFoo(self) -> Foo where ::Target: objc::Message + Sized, { diff --git a/tests/expectations/tests/objc_pointer_return_types.rs b/tests/expectations/tests/objc_pointer_return_types.rs new file mode 100644 index 0000000000..0f222c8b48 --- /dev/null +++ b/tests/expectations/tests/objc_pointer_return_types.rs @@ -0,0 +1,59 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] +#![cfg(target_os = "macos")] + +#[macro_use] +extern crate objc; +#[allow(non_camel_case_types)] +pub type id = *mut objc::runtime::Object; +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct Bar(pub id); +impl std::ops::Deref for Bar { + type Target = objc::runtime::Object; + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } +} +unsafe impl objc::Message for Bar {} +impl Bar { + pub fn alloc() -> Self { + Self(unsafe { msg_send!(objc::class!(Bar), alloc) }) + } +} +impl IBar for Bar {} +pub trait IBar: Sized + std::ops::Deref {} +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct Foo(pub id); +impl std::ops::Deref for Foo { + type Target = objc::runtime::Object; + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } +} +unsafe impl objc::Message for Foo {} +impl Foo { + pub fn alloc() -> Self { + Self(unsafe { msg_send!(objc::class!(Foo), alloc) }) + } +} +impl IFoo for Foo {} +pub trait IFoo: Sized + std::ops::Deref { + unsafe fn methodUsingBar_(self, my_bar: Bar) + where + ::Target: objc::Message + Sized, + { + msg_send!(self, methodUsingBar: my_bar) + } + unsafe fn methodReturningBar() -> Bar + where + ::Target: objc::Message + Sized, + { + msg_send!(class!(Foo), methodReturningBar) + } +} diff --git a/tests/headers/objc_pointer_return_types.h b/tests/headers/objc_pointer_return_types.h new file mode 100644 index 0000000000..e289a8a53d --- /dev/null +++ b/tests/headers/objc_pointer_return_types.h @@ -0,0 +1,10 @@ +// bindgen-flags: --objc-extern-crate -- -x objective-c +// bindgen-osx-only + +@interface Bar +@end + +@interface Foo ++ (Bar*)methodReturningBar; +- (void)methodUsingBar:(Bar *)my_bar; +@end