Skip to content

Added Bindgen names to objective-c pointer return types for #1835. #1847

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

Merged
merged 1 commit into from
Aug 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions book/src/objc.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Expand Down
25 changes: 19 additions & 6 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Copy link
Contributor

Choose a reason for hiding this comment

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

You can use inner_ty here, which is just a type already, right?

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.
Expand All @@ -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))
Expand All @@ -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)
}
Expand Down Expand Up @@ -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, &())
Expand Down
2 changes: 1 addition & 1 deletion tests/expectations/tests/objc_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
4 changes: 2 additions & 2 deletions tests/expectations/tests/objc_class_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
<Self as std::ops::Deref>::Target: objc::Message + Sized,
{
Expand All @@ -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
<Self as std::ops::Deref>::Target: objc::Message + Sized,
{
Expand Down
6 changes: 3 additions & 3 deletions tests/expectations/tests/objc_interface_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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;
}
4 changes: 2 additions & 2 deletions tests/expectations/tests/objc_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
<Self as std::ops::Deref>::Target: objc::Message + Sized,
{
Expand All @@ -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
<Self as std::ops::Deref>::Target: objc::Message + Sized,
{
Expand Down
59 changes: 59 additions & 0 deletions tests/expectations/tests/objc_pointer_return_types.rs
Original file line number Diff line number Diff line change
@@ -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
<Self as std::ops::Deref>::Target: objc::Message + Sized,
{
msg_send!(self, methodUsingBar: my_bar)
}
unsafe fn methodReturningBar() -> Bar
where
<Self as std::ops::Deref>::Target: objc::Message + Sized,
{
msg_send!(class!(Foo), methodReturningBar)
}
}
10 changes: 10 additions & 0 deletions tests/headers/objc_pointer_return_types.h
Original file line number Diff line number Diff line change
@@ -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