Skip to content

Commit

Permalink
Change extern_class! syntax
Browse files Browse the repository at this point in the history
Instead of specifying `impl ClassType for ...`, we instead parse the
custom attributes `#[unsafe(super(...))]`, `#[thread_kind = ...]` and
`#[name = ...]`.

This is nice because:
- It's more concise.
- It more closely matches what we might end up with once it can become
  and attribute macro:
  rust-lang/rfcs#3697
- We need to parse the attributes anyhow to override derives:
  #267

  (The extern_class! part of that issue is now resolved).
- It makes it easier to change ClassType in the future without having to
  change the macro API as well.

Additionally, this commit also adds incomplete support for generics, to
avoid the framework crates depending on an internal macro, and it
improves rust-analyzer support in extern_class! by having more relaxed
parsing.
  • Loading branch information
madsmtm committed Nov 22, 2024
1 parent ae9e1f5 commit b8c24dc
Show file tree
Hide file tree
Showing 69 changed files with 1,396 additions and 744 deletions.
7 changes: 2 additions & 5 deletions crates/block2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,9 @@
//! #
//! # use objc2::ClassType;
//! # objc2::extern_class!(
//! # #[unsafe(super(objc2::runtime::NSObject))]
//! # #[name = "NSObject"]
//! # struct MyClass;
//! #
//! # unsafe impl ClassType for MyClass {
//! # type Super = objc2::runtime::NSObject;
//! # const NAME: &'static str = "NSObject";
//! # }
//! # );
//!
//! extern_methods!(
Expand Down
98 changes: 18 additions & 80 deletions crates/header-translator/src/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1658,18 +1658,23 @@ impl Stmt {
return Ok(());
}

let macro_name = if generics.is_empty() {
"extern_class"
} else {
"__inner_extern_class"
};

let (superclass, superclasses_rest) = superclasses.split_at(1);
let (superclass, superclass_generics) = superclass
.first()
.expect("must have a least one superclass");

writeln!(f, "{macro_name}!(")?;
writeln!(f, "extern_class!(")?;
write!(f, " #[unsafe(super(")?;
for (i, (superclass, generics)) in superclasses.iter().enumerate() {
if 0 < i {
write!(f, ", ")?;
}
write!(
f,
"{}{}",
superclass.path_in_relation_to(id.location()),
GenericTyHelper(generics)
)?;
}
writeln!(f, "))]")?;
if *main_thread_only {
writeln!(f, " #[thread_kind = MainThreadOnly]")?;
}
writeln!(f, " {derives}")?;
write!(f, " {}", self.cfg_gate_ln(config))?;
write!(f, " {availability}")?;
Expand All @@ -1681,74 +1686,7 @@ impl Stmt {
}
write!(f, ">")?;
};
if generics.is_empty() {
writeln!(f, ";")?;
} else {
writeln!(f, " {{")?;
writeln!(
f,
"__superclass: {}{},",
superclass.path_in_relation_to(id.location()),
GenericTyHelper(superclass_generics),
)?;
for (i, generic) in generics.iter().enumerate() {
// Invariant over the generic by default
writeln!(f, "_inner{i}: PhantomData<*mut {generic}>,")?;
}
writeln!(f, "notunwindsafe: PhantomData<&'static mut ()>,")?;
writeln!(f, "}}")?;
}

writeln!(f)?;

write!(f, " {}", self.cfg_gate_ln(config))?;
writeln!(
f,
" unsafe impl{} ClassType for {}{} {{",
GenericParamsHelper(generics, "?Sized + Message"),
id.name,
GenericTyHelper(generics),
)?;
if !superclasses_rest.is_empty() {
write!(f, " #[inherits(")?;
let mut iter = superclasses_rest.iter();
// Using generics in here is not technically correct, but
// should work for our use-cases.
if let Some((superclass, generics)) = iter.next() {
write!(
f,
"{}{}",
superclass.path_in_relation_to(id.location()),
GenericTyHelper(generics)
)?;
}
for (superclass, generics) in iter {
write!(
f,
", {}{}",
superclass.path_in_relation_to(id.location()),
GenericTyHelper(generics)
)?;
}
writeln!(f, ")]")?;
}
writeln!(
f,
" type Super = {}{};",
superclass.path_in_relation_to(id.location()),
GenericTyHelper(superclass_generics),
)?;
if *main_thread_only {
writeln!(f, " type ThreadKind = dyn MainThreadOnly;")?;
}
if !generics.is_empty() {
writeln!(f)?;
writeln!(
f,
" fn as_super(&self) -> &Self::Super {{ &self.__superclass }}"
)?;
}
writeln!(f, " }}")?;
writeln!(f, ";")?;
writeln!(f, ");")?;

if *sendable && generics.is_empty() {
Expand Down
4 changes: 2 additions & 2 deletions crates/objc2/src/__framework_prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ pub use crate::runtime::{
AnyClass, AnyObject, Bool, NSObject, NSObjectProtocol, ProtocolObject, Sel,
};
pub use crate::{
MainThreadOnly, __inner_extern_class, extern_category, extern_class, extern_methods,
extern_protocol, ClassType, MainThreadMarker, Message, ProtocolType,
extern_category, extern_class, extern_methods, extern_protocol, ClassType, MainThreadMarker,
MainThreadOnly, Message, ProtocolType,
};

// TODO
Expand Down
43 changes: 14 additions & 29 deletions crates/objc2/src/__macro_helpers/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,54 +54,39 @@ mod tests {
use crate::runtime::NSObject;

extern_class!(
#[unsafe(super(NSObject))]
#[thread_kind = AllocAnyThread]
#[name = "NSObject"]
struct SetAnyThread;

unsafe impl ClassType for SetAnyThread {
type Super = NSObject;
type ThreadKind = dyn AllocAnyThread;
const NAME: &'static str = "NSObject";
}
);

extern_class!(
#[unsafe(super(NSObject))]
#[thread_kind = AllocAnyThread]
#[name = "NSObject"]
struct SendSync;

unsafe impl ClassType for SendSync {
type Super = NSObject;
type ThreadKind = dyn AllocAnyThread;
const NAME: &'static str = "NSObject";
}
);

unsafe impl Send for SendSync {}
unsafe impl Sync for SendSync {}

extern_class!(
#[unsafe(super(NSObject))]
#[thread_kind = MainThreadOnly]
#[name = "NSObject"]
struct OnlyMain;

unsafe impl ClassType for OnlyMain {
type Super = NSObject;
type ThreadKind = dyn MainThreadOnly;
const NAME: &'static str = "NSObject";
}
);

extern_class!(
#[unsafe(super(OnlyMain))]
#[name = "NSObject"]
struct OnlyMainSubDefault;

unsafe impl ClassType for OnlyMainSubDefault {
type Super = OnlyMain;
const NAME: &'static str = "NSObject";
}
);

extern_class!(
#[unsafe(super(OnlyMain))]
#[thread_kind = MainThreadOnly]
#[name = "NSObject"]
struct OnlyMainSubExplicit;

unsafe impl ClassType for OnlyMainSubExplicit {
type Super = OnlyMain;
type ThreadKind = dyn MainThreadOnly;
const NAME: &'static str = "NSObject";
}
);
}
3 changes: 3 additions & 0 deletions crates/objc2/src/__macro_helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
pub use core::borrow::Borrow;
pub use core::cell::UnsafeCell;
pub use core::cmp::{Eq, PartialEq};
pub use core::convert::AsRef;
pub use core::default::Default;
pub use core::fmt;
pub use core::hash::{Hash, Hasher};
pub use core::marker::{PhantomData, Sized};
pub use core::mem::{size_of, ManuallyDrop, MaybeUninit};
pub use core::ops::Deref;
Expand Down
Loading

0 comments on commit b8c24dc

Please sign in to comment.