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

Added Objc category inheritance #1784

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0bedb67
Added Categories to objective-c inheritance.
simlay May 14, 2020
bcd3d7f
Removed derive Clones in favor of a tupple.
simlay May 15, 2020
1367006
Removed unneeded methods in tests
simlay May 15, 2020
079571d
A pretty gross implementation :/
simlay May 19, 2020
3ad80d9
Updated comment
simlay May 25, 2020
70f38c6
cargo fmt
simlay May 25, 2020
7a642c3
Added get_parent_ty to clean up the option branches
simlay May 27, 2020
a04e151
Merge branch 'master' of github.com:rust-lang/rust-bindgen into objc-…
simlay Jun 11, 2020
5118d3e
Merge branch 'master' of github.com:rust-lang/rust-bindgen into objc-…
simlay Jun 15, 2020
ade5901
Merge branch 'master' of github.com:rust-lang/rust-bindgen into objc-…
simlay Jul 5, 2020
c162325
Merge branch 'master' of github.com:rust-lang/rust-bindgen into objc-…
simlay Jul 27, 2020
670331c
Merge branch 'master' of github.com:rust-lang/rust-bindgen into objc-…
simlay Aug 4, 2020
996af2e
Remove autogenerated comment
simlay Aug 4, 2020
924182b
Merge branch 'master' of github.com:rust-lang/rust-bindgen into objc-…
simlay Aug 17, 2020
5c93c5c
Merge branch 'master' of github.com:rust-lang/rust-bindgen into objc-…
simlay Aug 19, 2020
a2287a3
Added fixed stuff from minor nits
simlay Aug 21, 2020
b9615e6
fix for rustfmt
simlay Aug 24, 2020
d19f52e
Merge branch 'master' of github.com:rust-lang/rust-bindgen into objc-…
simlay Aug 26, 2020
999133e
Merge branch 'objc-category-inheritance' of github.com:simlay/rust-bi…
simlay Aug 26, 2020
84a3de9
Added loop for resolving typerefs.
simlay Sep 1, 2020
6a2c809
Merge branch 'objc-category-inheritance' of github.com:simlay/rust-bi…
simlay Sep 1, 2020
a70adff
Merge branch 'master' of github.com:rust-lang/rust-bindgen into objc-…
simlay Sep 17, 2020
0ce8926
Fixes from merging master into feature branch
simlay Sep 17, 2020
78bf091
rustfmt and updated changelog
simlay Sep 17, 2020
6663c80
Merge branch 'master' of github.com:rust-lang/rust-bindgen into objc-…
simlay Dec 23, 2021
bd5f919
maybe fix cargo fmt
simlay Dec 23, 2021
4c00a97
fix cargo fmt
simlay Dec 23, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/codegen/mod.rs
Expand Up @@ -3962,6 +3962,28 @@ impl CodeGenerator for ObjCInterface {
}
};
result.push(impl_trait);
for (category_name, category_template_names) in
Copy link
Contributor

Choose a reason for hiding this comment

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

So here's a thought, is there any reason we can't just implement this when generating code for the ObjCInterface?

It feels weird that stuff that would be blacklisted would implement these traits and so on. It should also avoid the weird lookup, right?

As in, instead of keeping the category as a String in the interface, you would keep the ItemId. Then when doing code generation you can do the lookup just fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is an interesting idea. I'm not a big fan of String for the type of category. I'll try this out.

&parent.categories
{
let category_name = ctx.rust_ident(category_name);
let impl_trait = if !category_template_names.is_empty()
{
let template_names: Vec<Ident> =
category_template_names
.iter()
.map(|g| ctx.rust_ident(g))
.collect();
quote! {
impl <#(#template_names :'static),*> #category_name <#(#template_names),*> for #class_name {
}
}
} else {
quote! {
impl #category_name for #class_name { }
}
};
result.push(impl_trait);
}
parent.parent_class
} else {
None
Expand Down
5 changes: 5 additions & 0 deletions src/ir/context.rs
Expand Up @@ -854,6 +854,11 @@ If you encounter an error missing from this list, please file an issue or a PR!"
})
}

/// Get a mutable reference to a given ItemId.
pub fn get_item_mut(&mut self, id: ItemId) -> Option<&mut Item> {
self.items.get_mut(id.0)?.as_mut()
}

/// Have we collected all unresolved type references yet?
pub fn collected_typerefs(&self) -> bool {
self.collected_typerefs
Expand Down
71 changes: 68 additions & 3 deletions src/ir/objc.rs
@@ -1,10 +1,10 @@
//! Objective C types

use super::context::{BindgenContext, ItemId};
use super::context::{BindgenContext, ItemId, TypeId};
use super::function::FunctionSig;
use super::item::Item;
use super::traversal::{Trace, Tracer};
use super::ty::TypeKind;
use super::ty::{Type, TypeKind};
use crate::clang;
use crate::parse::ClangItemParser;
use clang_sys::CXChildVisit_Continue;
Expand Down Expand Up @@ -37,6 +37,9 @@ pub struct ObjCInterface {
/// The list of protocols that this interface conforms to.
pub conforms_to: Vec<ItemId>,

/// The list of categories (and the template tags) that this interface is extended by.
pub categories: Vec<(String, Vec<String>)>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why shouldn't these be ItemIds?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These should probably be ItemIds. After seeing #1889, it looks like you can use categories to add more protocol conformance :/


/// The direct parent for this interface.
pub parent_class: Option<ItemId>,

Expand Down Expand Up @@ -68,6 +71,7 @@ impl ObjCInterface {
ObjCInterface {
name: name.to_owned(),
category: None,
categories: Vec::new(),
is_protocol: false,
template_names: Vec::new(),
parent_class: None,
Expand Down Expand Up @@ -135,14 +139,23 @@ impl ObjCInterface {
interface.is_protocol = true;
}

// This is the ItemId for the real interface to which a category extends. We must make it
// an optional, set it when we visit the ObjCCategoryDecl and then use it after we've
// visited the entire tree. We must do it in this order to ensure that this interface has
// all the template tags assigned to it.
let mut real_interface_id_for_category: Option<ItemId> = None;

cursor.visit(|c| {
match c.kind() {
CXCursor_ObjCClassRef => {
if cursor.kind() == CXCursor_ObjCCategoryDecl {
// We are actually a category extension, and we found the reference
// to the original interface, so name this interface approriately
// to the original interface, so name this interface approriately.

interface.name = c.spelling();
interface.category = Some(cursor.spelling());
real_interface_id_for_category = Some(Item::from_ty_or_ref(c.cur_type(), c, None, ctx).into());
simlay marked this conversation as resolved.
Show resolved Hide resolved

}
}
CXCursor_ObjCProtocolRef => {
Expand Down Expand Up @@ -194,9 +207,61 @@ impl ObjCInterface {
}
CXChildVisit_Continue
});

if interface.is_category() {
// If this interface is a category, we need to find the interface that this category
// extends.
if let Some(ref mut ty) =
Self::get_parent_ty(ctx, real_interface_id_for_category)
{
if let TypeKind::ObjCInterface(ref mut real_interface) =
ty.kind_mut()
{
if !real_interface.is_category() {
real_interface.categories.push((
interface.rust_name(),
interface.template_names.clone(),
));
}
}
}
}
Some(interface)
}

fn get_parent_ty(
ctx: &mut BindgenContext,
parent_id: Option<ItemId>,
) -> Option<&mut Type> {
// This is pretty gross but using the ItemResolver doesn't yield a mutable reference.
let mut ty = ctx.resolve_item_fallible(parent_id?)?.kind().as_type()?;
let mut item_id: Option<&TypeId> = None;
loop {
match ty.kind() {
TypeKind::ResolvedTypeRef(ref_id) => {
let ref_item: ItemId = ref_id.into();
ty = ctx
.resolve_item_fallible(ref_item)?
.kind()
.as_type()?;
//ty = ref_item.kind().as_type()()?;;
item_id = Some(ref_id);
}
TypeKind::ObjCInterface(..) => {
simlay marked this conversation as resolved.
Show resolved Hide resolved
break;
}
_ => return None,
};
}

let real_interface_id: ItemId = item_id?.into();
let ty = ctx
.get_item_mut(real_interface_id)?
.kind_mut()
.as_type_mut()?;
return Some(ty);
}

fn add_method(&mut self, method: ObjCMethod) {
if method.is_class_method {
self.class_methods.push(method);
Expand Down
2 changes: 1 addition & 1 deletion src/ir/ty.rs
Expand Up @@ -627,7 +627,7 @@ pub enum TypeKind {
/// A compound type, that is, a class, struct, or union.
Comp(CompInfo),

/// An opaque type that we just don't understand. All usage of this shoulf
/// An opaque type that we just don't understand. All usage of this should
/// result in an opaque blob of bytes generated from the containing type's
/// layout.
Opaque,
Expand Down
50 changes: 50 additions & 0 deletions tests/expectations/tests/objc_category_inheritance.rs
@@ -0,0 +1,50 @@
#![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 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 {}
impl Foo_BarCategory for Foo {}
pub trait Foo_BarCategory: Sized + std::ops::Deref {}
#[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 IFoo for Bar {}
impl Foo_BarCategory for Bar {}
impl IBar for Bar {}
pub trait IBar: Sized + std::ops::Deref {}
50 changes: 50 additions & 0 deletions tests/expectations/tests/objc_category_template_inheritance.rs
@@ -0,0 +1,50 @@
#![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 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<ObjectType: 'static> IFoo<ObjectType> for Foo {}
pub trait IFoo<ObjectType>: Sized + std::ops::Deref {}
impl<ObjectType: 'static> Foo_Baz<ObjectType> for Foo {}
pub trait Foo_Baz<ObjectType>: Sized + std::ops::Deref {}
#[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<ObjectType: 'static> IFoo<ObjectType> for Bar {}
impl<ObjectType: 'static> Foo_Baz<ObjectType> for Bar {}
impl<ObjectType: 'static> IBar<ObjectType> for Bar {}
pub trait IBar<ObjectType>: Sized + std::ops::Deref {}
47 changes: 47 additions & 0 deletions tests/expectations/tests/objc_template_inheritance.rs
@@ -0,0 +1,47 @@
#![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 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<ObjectType: 'static> IFoo<ObjectType> for Foo {}
pub trait IFoo<ObjectType>: Sized + std::ops::Deref {}
#[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<ObjectType: 'static> IFoo<ObjectType> for Bar {}
impl<ObjectType: 'static> IBar<ObjectType> for Bar {}
pub trait IBar<ObjectType>: Sized + std::ops::Deref {}
11 changes: 11 additions & 0 deletions tests/headers/objc_category_inheritance.h
@@ -0,0 +1,11 @@
// bindgen-flags: --objc-extern-crate -- -x objective-c
// bindgen-osx-only

@interface Foo
@end

@interface Foo (BarCategory)
@end

@interface Bar: Foo
@end
11 changes: 11 additions & 0 deletions tests/headers/objc_category_template_inheritance.h
@@ -0,0 +1,11 @@
// bindgen-flags: --objc-extern-crate -- -x objective-c
// bindgen-osx-only

@interface Foo<__covariant ObjectType>
@end

@interface Foo<__covariant ObjectType> (Baz)
@end

@interface Bar<__covariant ObjectType>: Foo
@end
8 changes: 8 additions & 0 deletions tests/headers/objc_template_inheritance.h
@@ -0,0 +1,8 @@
// bindgen-flags: --objc-extern-crate -- -x objective-c
// bindgen-osx-only

@interface Foo<__covariant ObjectType>
@end

@interface Bar<__covariant ObjectType>: Foo
@end