Skip to content
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

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

Merged
merged 11 commits into from Sep 16, 2020
54 changes: 51 additions & 3 deletions src/codegen/mod.rs
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;
simlay marked this conversation as resolved.
Show resolved Hide resolved
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);
simlay marked this conversation as resolved.
Show resolved Hide resolved
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))
simlay marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
};
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
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 {

simlay marked this conversation as resolved.
Show resolved Hide resolved
+ 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
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
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 {}