Skip to content

[Objc] Add From<ChildClass>, TryFrom<ParentClass>, function ownership updates and Protocol inheritance #1883

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 11 commits into from
Sep 16, 2020
54 changes: 51 additions & 3 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ use crate::{Entry, HashMap, HashSet};
use std;
use std::borrow::Cow;
use std::cell::Cell;
use std::collections::HashSet as CollectionHashSet;
use std::collections::VecDeque;
use std::fmt::Write;
use std::iter;
Expand Down Expand Up @@ -3806,7 +3807,7 @@ fn objc_method_codegen(
}
} else {
let fn_args = fn_args.clone();
let args = iter::once(quote! { self }).chain(fn_args.into_iter());
let args = iter::once(quote! { &self }).chain(fn_args.into_iter());
quote! {
( #( #args ),* ) #fn_ret
}
Expand All @@ -3825,7 +3826,7 @@ fn objc_method_codegen(
}
} else {
quote! {
msg_send!(self, #methods_and_args)
msg_send!(*self, #methods_and_args)
}
};

Expand Down Expand Up @@ -3901,7 +3902,7 @@ impl CodeGenerator for ObjCInterface {
if !self.is_category() && !self.is_protocol() {
let struct_block = quote! {
#[repr(transparent)]
#[derive(Clone, Copy)]
#[derive(Clone)]
pub struct #class_name(pub id);
impl std::ops::Deref for #class_name {
type Target = objc::runtime::Object;
Expand All @@ -3921,7 +3922,10 @@ impl CodeGenerator for ObjCInterface {
}
};
result.push(struct_block);
let mut protocol_set: CollectionHashSet<ItemId> =
CollectionHashSet::new();
for protocol_id in self.conforms_to.iter() {
protocol_set.insert(*protocol_id);
let protocol_name = ctx.rust_ident(
ctx.resolve_type(protocol_id.expect_type_id(ctx))
.name()
Expand Down Expand Up @@ -3962,6 +3966,50 @@ impl CodeGenerator for ObjCInterface {
}
};
result.push(impl_trait);
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 getting kinda gnarly, it may be worth factoring this if let ... { .. } else { .. } to its own function. Follow-up is fine.

for protocol_id in parent.conforms_to.iter() {
if !protocol_set.contains(protocol_id) {
protocol_set.insert(*protocol_id);
let protocol_name = ctx.rust_ident(
ctx.resolve_type(
protocol_id.expect_type_id(ctx),
)
.name()
.unwrap(),
);
let impl_trait = quote! {
impl #protocol_name for #class_name { }
};
result.push(impl_trait);
}
}
if !parent.is_template() {
let parent_struct_name = parent.name();
let child_struct_name = self.name();
let parent_struct = ctx.rust_ident(parent_struct_name);
let from_block = quote! {
impl From<#class_name> for #parent_struct {
fn from(child: #class_name) -> #parent_struct {
#parent_struct(child.0)
}
}
};
result.push(from_block);

let try_into_block = quote! {
impl std::convert::TryFrom<#parent_struct> for #class_name {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this can't use std if the no_std flag is set, so this should use trait_prefix. Also, probably should use the old style root paths (::std::convert::TryFrom) for consistency with other code and to support Rust 2015.

type Error = String;
fn try_from(parent: #parent_struct) -> Result<#class_name, Self::Error> {
let is_kind_of : bool = unsafe { msg_send!(parent, isKindOfClass:class!(#class_name))};
if is_kind_of {
Ok(#class_name(parent.0))
} else {
Err(format!("This {} is not an cannot be downcasted to {}", #parent_struct_name, #child_struct_name))
}
}
}
};
result.push(try_into_block);
}
parent.parent_class
} else {
None
Expand Down
76 changes: 71 additions & 5 deletions tests/expectations/tests/libclang-3.9/objc_inheritance.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;
#[repr(transparent)]
#[derive(Clone, Copy)]
#[derive(Clone)]
pub struct Foo(pub id);
impl std::ops::Deref for Foo {
type Target = objc::runtime::Object;
Expand All @@ -28,7 +28,7 @@ impl Foo {
impl IFoo for Foo {}
pub trait IFoo: Sized + std::ops::Deref {}
#[repr(transparent)]
#[derive(Clone, Copy)]
#[derive(Clone)]
pub struct Bar(pub id);
impl std::ops::Deref for Bar {
type Target = objc::runtime::Object;
Expand All @@ -43,10 +43,78 @@ impl Bar {
}
}
impl IFoo for Bar {}

mpl From<Bar> for Foo {

+ fn from(child: Bar) -> Foo {

+ Foo(child.0)

+ }

+}

impl std::convert::TryFrom<Foo> for Bar {
type Error = String;
fn try_from(parent: Foo) -> Result<Bar, Self::Error> {
let is_kind_of: bool =
unsafe { msg_send!(parent, isKindOfClass: class!(Bar)) };
if is_kind_of {
Ok(Bar(parent.0))
} else {
Err(format!(
"This {} is not an cannot be downcasted to {}",
"Foo", "Bar"
))
}
}
}
impl IBar for Bar {}
impl From<Baz> for Bar {
fn from(child: Baz) -> Bar {
Bar(child.0)
}
}

impl std::convert::TryFrom<Bar> for Baz {
type Error = String;
fn try_from(parent: Bar) -> Result<Baz, Self::Error> {
let is_kind_of: bool =
unsafe { msg_send!(parent, isKindOfClass: class!(Baz)) };
if is_kind_of {
Ok(Baz(parent.0))
} else {
Err(format!(
"This {} is not an cannot be downcasted to {}",
"Bar", "Baz"
))
}
}
}
impl IFoo for Baz {}
impl From<Baz> for Foo {
fn from(child: Baz) -> Foo {
Foo(child.0)
}
}
impl std::convert::TryFrom<Foo> for Baz {
type Error = String;
fn try_from(parent: Foo) -> Result<Baz, Self::Error> {
let is_kind_of: bool =
unsafe { msg_send!(parent, isKindOfClass: class!(Baz)) };
if is_kind_of {
Ok(Baz(parent.0))
} else {
Err(format!(
"This {} is not an cannot be downcasted to {}",
"Foo", "Baz"
))
}
}
}
pub trait IBar: Sized + std::ops::Deref {}
#[repr(transparent)]
#[derive(Clone, Copy)]
#[derive(Clone)]
pub struct Baz(pub id);
impl std::ops::Deref for Baz {
type Target = objc::runtime::Object;
Expand All @@ -60,7 +128,5 @@ impl Baz {
Self(unsafe { msg_send!(objc::class!(Baz), alloc) })
}
}
impl IBar for Baz {}
impl IFoo for Baz {}
impl IBaz for Baz {}
pub trait IBaz: Sized + std::ops::Deref {}
66 changes: 63 additions & 3 deletions tests/expectations/tests/libclang-4/objc_inheritance.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;
#[repr(transparent)]
#[derive(Clone, Copy)]
#[derive(Clone)]
pub struct Foo(pub id);
impl std::ops::Deref for Foo {
type Target = objc::runtime::Object;
Expand All @@ -28,7 +28,7 @@ impl Foo {
impl IFoo for Foo {}
pub trait IFoo: Sized + std::ops::Deref {}
#[repr(transparent)]
#[derive(Clone, Copy)]
#[derive(Clone)]
pub struct Bar(pub id);
impl std::ops::Deref for Bar {
type Target = objc::runtime::Object;
Expand All @@ -43,10 +43,30 @@ impl Bar {
}
}
impl IFoo for Bar {}
impl From<Bar> for Foo {
fn from(child: Bar) -> Foo {
Foo(child.0)
}
}
impl std::convert::TryFrom<Foo> for Bar {
type Error = String;
fn try_from(parent: Foo) -> Result<Bar, Self::Error> {
let is_kind_of: bool =
unsafe { msg_send!(parent, isKindOfClass: class!(Bar)) };
if is_kind_of {
Ok(Bar(parent.0))
} else {
Err(format!(
"This {} is not an cannot be downcasted to {}",
"Foo", "Bar"
))
}
}
}
impl IBar for Bar {}
pub trait IBar: Sized + std::ops::Deref {}
#[repr(transparent)]
#[derive(Clone, Copy)]
#[derive(Clone)]
pub struct Baz(pub id);
impl std::ops::Deref for Baz {
type Target = objc::runtime::Object;
Expand All @@ -61,6 +81,46 @@ impl Baz {
}
}
impl IBar for Baz {}
impl From<Baz> for Bar {
fn from(child: Baz) -> Bar {
Bar(child.0)
}
}
impl std::convert::TryFrom<Bar> for Baz {
type Error = String;
fn try_from(parent: Bar) -> Result<Baz, Self::Error> {
let is_kind_of: bool =
unsafe { msg_send!(parent, isKindOfClass: class!(Baz)) };
if is_kind_of {
Ok(Baz(parent.0))
} else {
Err(format!(
"This {} is not an cannot be downcasted to {}",
"Bar", "Baz"
))
}
}
}
impl IFoo for Baz {}
impl From<Baz> for Foo {
fn from(child: Baz) -> Foo {
Foo(child.0)
}
}
impl std::convert::TryFrom<Foo> for Baz {
type Error = String;
fn try_from(parent: Foo) -> Result<Baz, Self::Error> {
let is_kind_of: bool =
unsafe { msg_send!(parent, isKindOfClass: class!(Baz)) };
if is_kind_of {
Ok(Baz(parent.0))
} else {
Err(format!(
"This {} is not an cannot be downcasted to {}",
"Foo", "Baz"
))
}
}
}
impl IBaz for Baz {}
pub trait IBaz: Sized + std::ops::Deref {}
66 changes: 63 additions & 3 deletions tests/expectations/tests/libclang-5/objc_inheritance.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;
#[repr(transparent)]
#[derive(Clone, Copy)]
#[derive(Clone)]
pub struct Foo(pub id);
impl std::ops::Deref for Foo {
type Target = objc::runtime::Object;
Expand All @@ -28,7 +28,7 @@ impl Foo {
impl IFoo for Foo {}
pub trait IFoo: Sized + std::ops::Deref {}
#[repr(transparent)]
#[derive(Clone, Copy)]
#[derive(Clone)]
pub struct Bar(pub id);
impl std::ops::Deref for Bar {
type Target = objc::runtime::Object;
Expand All @@ -43,10 +43,30 @@ impl Bar {
}
}
impl IFoo for Bar {}
impl From<Bar> for Foo {
fn from(child: Bar) -> Foo {
Foo(child.0)
}
}
impl std::convert::TryFrom<Foo> for Bar {
type Error = String;
fn try_from(parent: Foo) -> Result<Bar, Self::Error> {
let is_kind_of: bool =
unsafe { msg_send!(parent, isKindOfClass: class!(Bar)) };
if is_kind_of {
Ok(Bar(parent.0))
} else {
Err(format!(
"This {} is not an cannot be downcasted to {}",
"Foo", "Bar"
))
}
}
}
impl IBar for Bar {}
pub trait IBar: Sized + std::ops::Deref {}
#[repr(transparent)]
#[derive(Clone, Copy)]
#[derive(Clone)]
pub struct Baz(pub id);
impl std::ops::Deref for Baz {
type Target = objc::runtime::Object;
Expand All @@ -61,6 +81,46 @@ impl Baz {
}
}
impl IBar for Baz {}
impl From<Baz> for Bar {
fn from(child: Baz) -> Bar {
Bar(child.0)
}
}
impl std::convert::TryFrom<Bar> for Baz {
type Error = String;
fn try_from(parent: Bar) -> Result<Baz, Self::Error> {
let is_kind_of: bool =
unsafe { msg_send!(parent, isKindOfClass: class!(Baz)) };
if is_kind_of {
Ok(Baz(parent.0))
} else {
Err(format!(
"This {} is not an cannot be downcasted to {}",
"Bar", "Baz"
))
}
}
}
impl IFoo for Baz {}
impl From<Baz> for Foo {
fn from(child: Baz) -> Foo {
Foo(child.0)
}
}
impl std::convert::TryFrom<Foo> for Baz {
type Error = String;
fn try_from(parent: Foo) -> Result<Baz, Self::Error> {
let is_kind_of: bool =
unsafe { msg_send!(parent, isKindOfClass: class!(Baz)) };
if is_kind_of {
Ok(Baz(parent.0))
} else {
Err(format!(
"This {} is not an cannot be downcasted to {}",
"Foo", "Baz"
))
}
}
}
impl IBaz for Baz {}
pub trait IBaz: Sized + std::ops::Deref {}
Loading