diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b382c1196..7ce6bf29a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -144,6 +144,9 @@ jobs: key: cargo-${{ github.job }}-${{ matrix.name }}-${{ hashFiles('**/Cargo.lock') }} - name: cargo doc + # Disable cargo doc checking for now, `NSEnumerator2<'a, T>` is broken + # on current nightly. + if: false run: cargo doc --no-deps --document-private-items ${{ matrix.args }} - name: cargo clippy diff --git a/Cargo.lock b/Cargo.lock index 2a1b10cc4..0b27129e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,6 +177,7 @@ dependencies = [ "block2", "dispatch", "objc2", + "static_assertions", ] [[package]] @@ -257,6 +258,7 @@ dependencies = [ "objc-sys", "objc2-encode", "objc2-proc-macros", + "static_assertions", ] [[package]] @@ -393,6 +395,12 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" version = "2.0.15" diff --git a/LAYERED_SAFETY.md b/LAYERED_SAFETY.md index a2d140c54..018020b9a 100644 --- a/LAYERED_SAFETY.md +++ b/LAYERED_SAFETY.md @@ -144,7 +144,7 @@ correctly. The `NSData` example again. ```rust -let obj: Id = unsafe { msg_send_id![class!(NSData), new] }; +let obj: Id = unsafe { msg_send_id![class!(NSData), new] }; let length: NSUInteger = unsafe { msg_send![&obj, length] }; // `obj` goes out of scope, `release` is automatically sent to the object ``` @@ -181,13 +181,14 @@ extern_class!( unsafe impl ClassType for NSData { type Super = NSObject; + type Mutability = ImmutableWithMutableSubclass; } ); extern_methods!( unsafe impl NSData { #[method_id(new)] - fn new() -> Id; + fn new() -> Id; #[method(length)] fn length(&self) -> NSUInteger; diff --git a/crates/header-translator/src/cache.rs b/crates/header-translator/src/cache.rs index dd22bc82e..ddde48152 100644 --- a/crates/header-translator/src/cache.rs +++ b/crates/header-translator/src/cache.rs @@ -7,7 +7,6 @@ use crate::file::File; use crate::id::ItemIdentifier; use crate::method::Method; use crate::output::Output; -use crate::rust_type::Ownership; use crate::stmt::Stmt; #[derive(Debug, PartialEq, Clone)] @@ -37,14 +36,12 @@ impl ClassCache { #[derive(Debug, PartialEq, Clone)] pub struct Cache<'a> { classes: BTreeMap, - ownership_map: BTreeMap, config: &'a Config, } impl<'a> Cache<'a> { pub fn new(output: &Output, config: &'a Config) -> Self { let mut classes: BTreeMap<_, ClassCache> = BTreeMap::new(); - let mut ownership_map: BTreeMap<_, Ownership> = BTreeMap::new(); for (name, library) in &output.libraries { let _span = debug_span!("library", name).entered(); @@ -55,20 +52,11 @@ impl<'a> Cache<'a> { let cache = classes.entry(cls.clone()).or_default(); cache.to_emit.push(method_cache); } - if let Stmt::ClassDecl { id, ownership, .. } = stmt { - if *ownership != Ownership::default() { - ownership_map.insert(id.name.clone(), ownership.clone()); - } - } } } } - Self { - classes, - ownership_map, - config, - } + Self { classes, config } } fn cache_stmt(stmt: &Stmt) -> Option<(&ItemIdentifier, MethodCache)> { @@ -159,6 +147,7 @@ impl<'a> Cache<'a> { let mut new_stmts = Vec::new(); for stmt in &mut file.stmts { + #[allow(clippy::single_match)] // There will be others match stmt { Stmt::ClassDecl { id, @@ -182,7 +171,7 @@ impl<'a> Cache<'a> { for (superclass, _) in &*superclasses { if let Some(cache) = self.classes.get(superclass) { new_stmts.extend(cache.to_emit.iter().filter_map(|cache| { - let mut methods: Vec<_> = cache + let methods: Vec<_> = cache .methods .iter() .filter(|method| !seen_methods.contains(&method.id())) @@ -197,8 +186,6 @@ impl<'a> Cache<'a> { return None; } - self.update_methods(&mut methods, &id.name); - Some(Stmt::Methods { cls: id.clone(), generics: generics.clone(), @@ -217,12 +204,6 @@ impl<'a> Cache<'a> { } } } - Stmt::Methods { cls, methods, .. } => { - self.update_methods(methods, &cls.name); - } - Stmt::ProtocolDecl { id, methods, .. } => { - self.update_methods(methods, &id.name); - } _ => {} } } @@ -257,17 +238,4 @@ impl<'a> Cache<'a> { file.stmts.push(stmt); } } - - fn update_methods(&self, methods: &mut [Method], self_means: &str) { - for method in methods { - // Beware! We make instance methods return `Owned` as well, though - // those are basically never safe (since they'd refer to mutable - // data without a lifetime tied to the primary owner). - method.result_type.set_ownership(|name| { - let name = if name == "Self" { self_means } else { name }; - - self.ownership_map.get(name).cloned().unwrap_or_default() - }); - } - } } diff --git a/crates/header-translator/src/config.rs b/crates/header-translator/src/config.rs index 06b2ec170..72ca2c8db 100644 --- a/crates/header-translator/src/config.rs +++ b/crates/header-translator/src/config.rs @@ -6,8 +6,7 @@ use std::path::Path; use serde::Deserialize; use crate::data; -use crate::rust_type::Ownership; -use crate::stmt::Derives; +use crate::stmt::{Derives, Mutability}; #[derive(Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(deny_unknown_fields)] @@ -96,9 +95,8 @@ pub struct ClassData { pub categories: HashMap, #[serde(default)] pub derives: Derives, - #[serde(rename = "owned")] - #[serde(default)] - pub ownership: Ownership, + #[serde(skip)] + pub mutability: Mutability, #[serde(rename = "skipped-protocols")] #[serde(default)] pub skipped_protocols: HashSet, @@ -156,8 +154,7 @@ pub struct MethodData { pub unsafe_: bool, #[serde(default = "skipped_default")] pub skipped: bool, - #[serde(default = "mutating_default")] - pub mutating: bool, + pub mutating: Option, } #[derive(Deserialize, Debug, Clone, PartialEq, Eq)] @@ -191,16 +188,12 @@ fn skipped_default() -> bool { false } -fn mutating_default() -> bool { - false -} - impl Default for MethodData { fn default() -> Self { Self { unsafe_: unsafe_default(), skipped: skipped_default(), - mutating: mutating_default(), + mutating: None, } } } diff --git a/crates/header-translator/src/data/AppKit.rs b/crates/header-translator/src/data/AppKit.rs index 89a871f29..df517b303 100644 --- a/crates/header-translator/src/data/AppKit.rs +++ b/crates/header-translator/src/data/AppKit.rs @@ -1,2 +1,5 @@ data! { + // Subclasses `NSMutableAttributedString`, though I think this should + // actually be `InteriorMutable`? + class NSTextStorage: Mutable {} } diff --git a/crates/header-translator/src/data/Foundation.rs b/crates/header-translator/src/data/Foundation.rs index 0e26ce1f1..95b128753 100644 --- a/crates/header-translator/src/data/Foundation.rs +++ b/crates/header-translator/src/data/Foundation.rs @@ -1,19 +1,16 @@ data! { - class NSArray { + // SAFETY: `new` or `initWithObjects:` may choose to deduplicate arrays, + // and returning mutable references to those would be unsound - hence + // `NSArray` cannot be mutable. + class NSArray: ImmutableWithMutableSubclass { unsafe -count; } - class NSMutableArray: Owned { - unsafe mut -removeAllObjects; - mut -addObject; - mut -insertObject_atIndex; - mut -replaceObjectAtIndex_withObject; - mut -removeObjectAtIndex; - mut -removeLastObject; - mut -sortUsingFunction_context; + class NSMutableArray: MutableWithImmutableSuperclass { + unsafe -removeAllObjects; } - class NSString { + class NSString: ImmutableWithMutableSubclass { unsafe -init; unsafe -compare; unsafe -hasPrefix; @@ -30,26 +27,32 @@ data! { unsafe +stringWithString; } - class NSMutableString: Owned { + class NSMutableString: MutableWithImmutableSuperclass { unsafe -initWithCapacity; unsafe +stringWithCapacity; unsafe -initWithString; unsafe +stringWithString; - unsafe mut -appendString; - unsafe mut -setString; + unsafe -appendString; + unsafe -setString; } - class NSAttributedString { + // Allowed to be just `Immutable` since we've removed the `NSCopying` and + // `NSMutableCopying` impls from these for now (they'd return the wrong + // type). + class NSSimpleCString: Immutable {} + class NSConstantString: Immutable {} + + class NSAttributedString: ImmutableWithMutableSubclass { unsafe -initWithString; unsafe -initWithAttributedString; unsafe -string; unsafe -length; } - class NSMutableAttributedString: Owned { + class NSMutableAttributedString: MutableWithImmutableSuperclass { unsafe -initWithString; unsafe -initWithAttributedString; - unsafe mut -setAttributedString; + unsafe -setAttributedString; } class NSBundle { @@ -57,32 +60,33 @@ data! { unsafe -infoDictionary; } - class NSData { + class NSData: ImmutableWithMutableSubclass { unsafe -initWithData; unsafe +dataWithData; unsafe -length; unsafe -bytes; } - class NSMutableData: Owned { + class NSMutableData: MutableWithImmutableSuperclass { unsafe +dataWithData; unsafe -initWithCapacity; unsafe +dataWithCapacity; - unsafe mut -setLength; - unsafe mut -mutableBytes; + unsafe -setLength; + unsafe -mutableBytes; } - class NSDictionary { + // Allowed to be just `Mutable` since we've removed the `NSCopying` and + // `NSMutableCopying` impls from this for now (since they'd return the + // wrong type). + class NSPurgeableData: Mutable {} + + class NSDictionary: ImmutableWithMutableSubclass { unsafe -count; - unsafe -objectForKey; - unsafe -allKeys; - unsafe -allValues; } - class NSMutableDictionary: Owned { - unsafe mut -setDictionary; - unsafe mut -removeObjectForKey; - unsafe mut -removeAllObjects; + class NSMutableDictionary: MutableWithImmutableSuperclass { + unsafe -removeObjectForKey; + unsafe -removeAllObjects; } class NSError { @@ -130,20 +134,22 @@ data! { unsafe -operatingSystemVersion; } - class NSSet { + class NSSet: ImmutableWithMutableSubclass { unsafe -count; } - class NSMutableSet: Owned { - unsafe mut -removeAllObjects; - mut -addObject; + class NSMutableSet: MutableWithImmutableSuperclass { + unsafe -removeAllObjects; } - class NSMutableCharacterSet: Owned {} + class NSCharacterSet: ImmutableWithMutableSubclass {} + class NSMutableCharacterSet: MutableWithImmutableSuperclass {} - class NSMutableOrderedSet: Owned {} + class NSOrderedSet: ImmutableWithMutableSubclass {} + class NSMutableOrderedSet: MutableWithImmutableSuperclass {} - class NSMutableIndexSet: Owned {} + class NSIndexSet: ImmutableWithMutableSubclass {} + class NSMutableIndexSet: MutableWithImmutableSuperclass {} class NSNumber { unsafe -initWithChar; @@ -196,5 +202,6 @@ data! { unsafe -stringValue; } - class NSMutableURLRequest: Owned {} + class NSURLRequest: ImmutableWithMutableSubclass {} + class NSMutableURLRequest: MutableWithImmutableSuperclass {} } diff --git a/crates/header-translator/src/data/README.md b/crates/header-translator/src/data/README.md index eb8ccf87d..d619b133d 100644 --- a/crates/header-translator/src/data/README.md +++ b/crates/header-translator/src/data/README.md @@ -41,16 +41,16 @@ marked safe, so such an improvement can be made in a minor version! ```rust , ignore data! { - // Everywhere that the class is returned, it is as - // `Id`. - // - // Specifying this is _not_ unsafe, but every method that returns this - // class must be checked for safety with it in mind. - class MyMutableClass: Owned { + class MyClass { // The class method `new` is safe. unsafe +new; // The method `init` is safe. unsafe -init; + } + + class MyImmutableClass: ImmutableWithMutableSubclass {} + + class MyMutableClass: MutableWithImmutableSuperclass { // The method `mutate` takes &mut self, and is safe. unsafe mut -mutate; // The method `mutateUnchecked` takes &mut self, but is not safe. diff --git a/crates/header-translator/src/data/macros.rs b/crates/header-translator/src/data/macros.rs index 6aea60394..817e76612 100644 --- a/crates/header-translator/src/data/macros.rs +++ b/crates/header-translator/src/data/macros.rs @@ -45,6 +45,32 @@ macro_rules! data { }; } +macro_rules! __set_mutability { + ($data:expr;) => {}; + ($data:expr; ImmutableWithMutableSubclass<$framework:ident::$subclass:ident>) => { + $data.mutability = $crate::stmt::Mutability::ImmutableWithMutableSubclass( + $crate::ItemIdentifier::from_raw( + stringify!($subclass).to_string(), + stringify!($framework).to_string(), + ), + ); + }; + ($data:expr; MutableWithImmutableSuperclass<$framework:ident::$superclass:ident>) => { + $data.mutability = $crate::stmt::Mutability::MutableWithImmutableSuperclass( + $crate::ItemIdentifier::from_raw( + stringify!($superclass).to_string(), + stringify!($framework).to_string(), + ), + ); + }; + ($data:expr; Immutable) => { + $data.mutability = $crate::stmt::Mutability::Immutable; + }; + ($data:expr; Mutable) => { + $data.mutability = $crate::stmt::Mutability::Mutable; + }; +} + macro_rules! __data_inner { // Base case ( @@ -54,7 +80,7 @@ macro_rules! __data_inner { ( @($config:expr) - class $class:ident $(: $ownership:ident)? { + class $class:ident $(: $mutability:ident $(<$framework:ident::$ty:ident>)?)? { $($methods:tt)* } @@ -63,7 +89,10 @@ macro_rules! __data_inner { #[allow(unused_mut)] let mut data = $config.class_data.entry(stringify!($class).to_string()).or_default(); - $(data.ownership = $crate::rust_type::Ownership::$ownership;)? + __set_mutability! { + data; + $($mutability $(<$framework::$ty>)?)? + } __data_methods! { @(data) @@ -166,7 +195,7 @@ macro_rules! __data_methods { ) => { let mut method_data = $data.methods.entry(stringify!($name).to_string()).or_default(); - method_data.mutating = true; + method_data.mutating = Some(true); __data_methods! { @($data) @@ -200,7 +229,7 @@ macro_rules! __data_methods { let mut method_data = $data.methods.entry(stringify!($name).to_string()).or_default(); method_data.unsafe_ = false; - method_data.mutating = true; + method_data.mutating = Some(true); __data_methods! { @($data) diff --git a/crates/header-translator/src/id.rs b/crates/header-translator/src/id.rs index 833d8e80a..5ec83cdb9 100644 --- a/crates/header-translator/src/id.rs +++ b/crates/header-translator/src/id.rs @@ -32,6 +32,14 @@ pub struct ItemIdentifier { } impl ItemIdentifier { + pub fn from_raw(name: N, library: String) -> Self { + Self { + name, + library, + file_name: None, + } + } + pub fn with_name(name: N, entity: &Entity<'_>, context: &Context<'_>) -> Self { let (mut library_name, mut file_name) = context .get_library_and_file_name(entity) diff --git a/crates/header-translator/src/lib.rs b/crates/header-translator/src/lib.rs index 2b87a7e2d..365798d69 100644 --- a/crates/header-translator/src/lib.rs +++ b/crates/header-translator/src/lib.rs @@ -34,7 +34,7 @@ pub use self::file::File; pub use self::id::ItemIdentifier; pub use self::library::Library; pub use self::output::Output; -pub use self::stmt::Stmt; +pub use self::stmt::{Mutability, Stmt}; pub fn compare_btree( data1: &BTreeMap, diff --git a/crates/header-translator/src/method.rs b/crates/header-translator/src/method.rs index 70ff1270c..dcd12775c 100644 --- a/crates/header-translator/src/method.rs +++ b/crates/header-translator/src/method.rs @@ -10,6 +10,7 @@ use crate::id::ItemIdentifier; use crate::immediate_children; use crate::objc2_utils::in_selector_family; use crate::rust_type::{MethodArgumentQualifier, Ty}; +use crate::stmt::get_category_cls; use crate::unexposed_attr::UnexposedAttr; impl MethodArgumentQualifier { @@ -245,6 +246,32 @@ impl Method { (self.is_class, &self.selector) } + fn parent_type_data(entity: &Entity<'_>, context: &Context<'_>) -> (bool, bool) { + let parent = entity.get_semantic_parent().expect("method parent"); + let (parent, is_protocol) = match parent.get_kind() { + EntityKind::ObjCInterfaceDecl => (parent, false), + EntityKind::ObjCCategoryDecl => (get_category_cls(&parent), false), + EntityKind::ObjCProtocolDecl => (parent, true), + kind => { + error!(?kind, "unknown method parent kind"); + (parent, false) + } + }; + let parent_id = ItemIdentifier::new(&parent, context); + + let is_mutable = if !is_protocol { + context + .class_data + .get(&parent_id.name) + .map(|data| data.mutability.is_mutable()) + .unwrap_or(false) + } else { + false + }; + + (is_mutable, is_protocol) + } + /// Takes one of `EntityKind::ObjCInstanceMethodDecl` or /// `EntityKind::ObjCClassMethodDecl`. pub fn partial(entity: Entity<'_>) -> PartialMethod<'_> { @@ -298,7 +325,7 @@ impl Method { return None; } - self.mutating = data.mutating; + self.mutating = data.mutating.unwrap_or(false); self.safe = !data.unsafe_; Some(self) @@ -323,12 +350,7 @@ pub struct PartialMethod<'tu> { } impl<'tu> PartialMethod<'tu> { - pub fn parse( - self, - data: MethodData, - is_protocol: bool, - context: &Context<'_>, - ) -> Option<(bool, Method)> { + pub fn parse(self, data: MethodData, context: &Context<'_>) -> Option<(bool, Method)> { let Self { entity, selector, @@ -346,6 +368,8 @@ impl<'tu> PartialMethod<'tu> { return None; } + let (parent_is_mutable, is_protocol) = Method::parent_type_data(&entity, context); + let availability = Availability::parse(&entity, context); let modifiers = MethodModifiers::parse(&entity, context); @@ -440,7 +464,10 @@ impl<'tu> PartialMethod<'tu> { arguments, result_type, safe: !data.unsafe_, - mutating: data.mutating, + // Mutable if the parent is mutable is a reasonable default, + // since immutable methods are usually either declared on an + // immutable subclass, or as a property. + mutating: data.mutating.unwrap_or(parent_is_mutable), is_protocol, }, )) @@ -463,7 +490,6 @@ impl PartialProperty<'_> { self, getter_data: MethodData, setter_data: Option, - is_protocol: bool, context: &Context<'_>, ) -> (Option, Option) { let Self { @@ -483,6 +509,8 @@ impl PartialProperty<'_> { return (None, None); } + let (parent_is_mutable, is_protocol) = Method::parent_type_data(&entity, context); + let availability = Availability::parse(&entity, context); let modifiers = MethodModifiers::parse(&entity, context); @@ -512,7 +540,9 @@ impl PartialProperty<'_> { arguments: Vec::new(), result_type: ty, safe: !getter_data.unsafe_, - mutating: getter_data.mutating, + // Getters are usually not mutable, even if the class itself + // is, so let's default to immutable. + mutating: getter_data.mutating.unwrap_or(false), is_protocol, }) } else { @@ -539,7 +569,8 @@ impl PartialProperty<'_> { arguments: vec![(name, ty)], result_type: Ty::VOID_RESULT, safe: !setter_data.unsafe_, - mutating: setter_data.mutating, + // Setters are usually mutable if the class itself is. + mutating: setter_data.mutating.unwrap_or(parent_is_mutable), is_protocol, }) } else { @@ -612,14 +643,8 @@ impl fmt::Display for Method { // Receiver if let MemoryManagement::IdInit = self.memory_management { - if self.mutating { - error!("invalid mutating method"); - } write!(f, "this: Option>, ")?; } else if self.is_class { - if self.mutating { - error!("invalid mutating method"); - } // Insert nothing; a class method is assumed } else if self.mutating { write!(f, "&mut self, ")?; diff --git a/crates/header-translator/src/rust_type.rs b/crates/header-translator/src/rust_type.rs index 84196c103..e9579210d 100644 --- a/crates/header-translator/src/rust_type.rs +++ b/crates/header-translator/src/rust_type.rs @@ -3,7 +3,6 @@ use std::str::FromStr; use clang::{CallingConvention, EntityKind, Nullability, Type, TypeKind}; use proc_macro2::{TokenStream, TokenTree}; -use serde::Deserialize; use crate::context::Context; use crate::id::ItemIdentifier; @@ -152,24 +151,6 @@ impl Drop for AttributeParser<'_, '_> { } } -#[derive(Deserialize, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)] -#[serde(from = "bool")] -pub enum Ownership { - Owned, - #[default] - Shared, -} - -impl From for Ownership { - fn from(b: bool) -> Self { - if b { - Self::Owned - } else { - Self::Shared - } - } -} - #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] enum TypeParams { Empty, @@ -183,7 +164,6 @@ enum IdType { Class { id: ItemIdentifier, params: TypeParams, - ownership: Option, }, TypeDef { id: ItemIdentifier, @@ -198,9 +178,7 @@ enum IdType { AnyClass { protocols: Vec, }, - Self_ { - ownership: Option, - }, + Self_, } impl IdType { @@ -218,28 +196,6 @@ impl IdType { } } - fn ownership(&self) -> impl fmt::Display + '_ { - struct IdTypeOwnership<'a>(&'a IdType); - - impl fmt::Display for IdTypeOwnership<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 { - IdType::Class { - ownership: Some(Ownership::Owned), - .. - } - | IdType::Self_ { - ownership: Some(Ownership::Owned), - } => write!(f, ", Owned"), - IdType::GenericParam { name } => write!(f, ", {name}Ownership"), - _ => Ok(()), - } - } - } - - IdTypeOwnership(self) - } - fn parse_objc_pointer( ty: Type<'_>, pointer_name: &str, @@ -305,7 +261,6 @@ impl IdType { Self::Class { id, params: TypeParams::Empty, - ownership: None, } } } @@ -357,7 +312,6 @@ impl IdType { } else { TypeParams::Protocols(protocols) }, - ownership: None, } } TypeKind::ObjCClass => { @@ -828,7 +782,7 @@ impl Inner { "Float96" => panic!("can't handle 96 bit 68881 float"), "instancetype" => Self::Id { - ty: IdType::Self_ { ownership: None }, + ty: IdType::Self_, is_const, lifetime, nullability, @@ -1431,21 +1385,6 @@ impl Ty { } } - pub(crate) fn set_ownership(&mut self, mut get_ownership: impl FnMut(&str) -> Ownership) { - assert!(matches!(self.kind, TyKind::MethodReturn { .. })); - if let Inner::Id { ty, .. } = &mut self.ty { - match ty { - IdType::Class { id, ownership, .. } => { - *ownership = Some(get_ownership(&id.name)); - } - IdType::Self_ { ownership } => { - *ownership = Some(get_ownership("Self")); - } - _ => {} - } - } - } - pub fn visit_required_types(&self, f: &mut impl FnMut(&ItemIdentifier)) { if let TyKind::MethodReturn { with_error: true } = &self.kind { f(&ItemIdentifier::nserror()); @@ -1468,7 +1407,6 @@ impl Ty { IdType::Class { id, params: TypeParams::Empty, - ownership: None, }, is_const: id_is_const, lifetime, @@ -1544,7 +1482,7 @@ impl Ty { return; } - *ty = IdType::Self_ { ownership: None }; + *ty = IdType::Self_; } else { // Only fix if the type is `id` } @@ -1587,9 +1525,9 @@ impl fmt::Display for Ty { nullability, } => { if *nullability == Nullability::NonNull { - write!(f, "Id<{ty}{}>", ty.ownership()) + write!(f, "Id<{ty}>") } else { - write!(f, "Option>", ty.ownership()) + write!(f, "Option>") } } Inner::Class { nullability } => { @@ -1616,8 +1554,7 @@ impl fmt::Display for Ty { // NULL -> error write!( f, - " -> Result, Id<{}>>", - ty.ownership(), + " -> Result, Id<{}>>", ItemIdentifier::nserror().path(), ) } @@ -1665,7 +1602,6 @@ impl fmt::Display for Ty { ty: ty @ IdType::Class { params: TypeParams::Empty, - ownership: None, .. }, is_const: _, diff --git a/crates/header-translator/src/stmt.rs b/crates/header-translator/src/stmt.rs index 1bf0004e5..1350c64a9 100644 --- a/crates/header-translator/src/stmt.rs +++ b/crates/header-translator/src/stmt.rs @@ -14,7 +14,7 @@ use crate::expr::Expr; use crate::id::ItemIdentifier; use crate::immediate_children; use crate::method::{handle_reserved, Method}; -use crate::rust_type::{Ownership, Ty}; +use crate::rust_type::Ty; use crate::unexposed_attr::UnexposedAttr; #[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)] @@ -152,9 +152,7 @@ fn parse_objc_decl( if !properties.remove(&(partial.is_class, partial.fn_name.clone())) { let data = get_data(&partial.fn_name); - if let Some((designated_initializer, method)) = - partial.parse(data, generics.is_none(), context) - { + if let Some((designated_initializer, method)) = partial.parse(data, context) { if designated_initializer { designated_initializers.push(method.fn_name.clone()); } @@ -172,8 +170,7 @@ fn parse_objc_decl( .as_ref() .map(|setter_name| get_data(setter_name)); - let (getter, setter) = - partial.parse(getter_data, setter_data, generics.is_none(), context); + let (getter, setter) = partial.parse(getter_data, setter_data, context); if let Some(getter) = getter { if !properties.insert((getter.is_class, getter.fn_name.clone())) { error!(?setter, "already exisiting property"); @@ -212,6 +209,42 @@ fn parse_objc_decl( (protocols, methods, designated_initializers) } +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub enum Mutability { + Immutable, + Mutable, + ImmutableWithMutableSubclass(ItemIdentifier), + MutableWithImmutableSuperclass(ItemIdentifier), + #[default] + InteriorMutable, + // MainThreadOnly, +} + +impl Mutability { + pub fn is_mutable(&self) -> bool { + matches!( + self, + Mutability::Mutable | Mutability::MutableWithImmutableSuperclass(_) + ) + } +} + +impl fmt::Display for Mutability { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Immutable => write!(f, "Immutable"), + Self::Mutable => write!(f, "Mutable"), + Self::ImmutableWithMutableSubclass(subclass) => { + write!(f, "ImmutableWithMutableSubclass<{}>", subclass.path()) + } + Self::MutableWithImmutableSuperclass(superclass) => { + write!(f, "MutableWithImmutableSuperclass<{}>", superclass.path()) + } + Self::InteriorMutable => write!(f, "InteriorMutable"), + } + } +} + #[derive(Debug, Clone, PartialEq)] pub enum Stmt { /// @interface name: superclass @@ -224,7 +257,8 @@ pub enum Stmt { superclasses: Vec<(ItemIdentifier, Vec)>, designated_initializers: Vec, derives: Derives, - ownership: Ownership, + mutability: Mutability, + skipped: bool, }, /// @interface class_name (name) /// -> @@ -378,6 +412,25 @@ fn parse_fn_param_children(entity: &Entity<'_>, context: &Context<'_>) { }); } +pub(crate) fn get_category_cls<'tu>(entity: &Entity<'tu>) -> Entity<'tu> { + let mut cls = None; + entity.visit_children(|entity, _parent| { + if entity.get_kind() == EntityKind::ObjCClassRef { + if cls.is_some() { + panic!("could not find unique category class") + } + let definition = entity + .get_definition() + .expect("category class ref definition"); + cls = Some(definition); + EntityVisitResult::Break + } else { + EntityVisitResult::Continue + } + }); + cls.expect("could not find category class") +} + impl Stmt { pub fn parse(entity: &Entity<'_>, context: &Context<'_>) -> Vec { let _span = debug_span!( @@ -438,48 +491,30 @@ impl Stmt { description: None, }; - if !data.map(|data| data.definition_skipped).unwrap_or_default() { - iter::once(Self::ClassDecl { - id: id.clone(), - generics: generics.clone(), - availability: availability.clone(), - superclasses, - designated_initializers, - derives: data.map(|data| data.derives.clone()).unwrap_or_default(), - ownership: data.map(|data| data.ownership.clone()).unwrap_or_default(), - }) - .chain(protocols.into_iter().map(|protocol| Self::ProtocolImpl { - cls: id.clone(), - protocol, - generics: generics.clone(), - availability: availability.clone(), - })) - .chain(iter::once(methods)) - .collect() - } else { - vec![methods] - } + iter::once(Self::ClassDecl { + id: id.clone(), + generics: generics.clone(), + availability: availability.clone(), + superclasses, + designated_initializers, + derives: data.map(|data| data.derives.clone()).unwrap_or_default(), + mutability: data.map(|data| data.mutability.clone()).unwrap_or_default(), + skipped: data.map(|data| data.definition_skipped).unwrap_or_default(), + }) + .chain(protocols.into_iter().map(|protocol| Self::ProtocolImpl { + cls: id.clone(), + protocol, + generics: generics.clone(), + availability: availability.clone(), + })) + .chain(iter::once(methods)) + .collect() } EntityKind::ObjCCategoryDecl => { let category = ItemIdentifier::new_optional(entity, context); let availability = Availability::parse(entity, context); - let mut cls = None; - entity.visit_children(|entity, _parent| { - if entity.get_kind() == EntityKind::ObjCClassRef { - if cls.is_some() { - panic!("could not find unique category class") - } - let definition = entity - .get_definition() - .expect("category class ref definition"); - cls = Some(ItemIdentifier::new(&definition, context)); - EntityVisitResult::Break - } else { - EntityVisitResult::Continue - } - }); - let cls = cls.expect("could not find category class"); + let cls = ItemIdentifier::new(&get_category_cls(entity), context); let data = context.class_data.get(&cls.name); if data.map(|data| data.skipped).unwrap_or_default() { @@ -974,7 +1009,13 @@ impl Stmt { pub(crate) fn declared_types(&self) -> impl Iterator { match self { - Stmt::ClassDecl { id, .. } => Some(&*id.name), + Stmt::ClassDecl { id, skipped, .. } => { + if *skipped { + None + } else { + Some(&*id.name) + } + } Stmt::Methods { .. } => None, Stmt::ProtocolDecl { id, .. } => Some(&*id.name), Stmt::ProtocolImpl { .. } => None, @@ -1010,26 +1051,20 @@ impl fmt::Display for Stmt { for generic in self.0 { write!(f, "{generic}, ")?; } - for generic in self.0 { - write!(f, "{generic}Ownership, ")?; - } write!(f, ">")?; } Ok(()) } } - struct GenericParamsHelper<'a>(&'a [String]); + struct GenericParamsHelper<'a>(&'a [String], &'a str); impl fmt::Display for GenericParamsHelper<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if !self.0.is_empty() { write!(f, "<")?; for generic in self.0 { - write!(f, "{generic}: Message, ")?; - } - for generic in self.0 { - write!(f, "{generic}Ownership: Ownership, ")?; + write!(f, "{generic}: {}, ", self.1)?; } write!(f, ">")?; } @@ -1037,6 +1072,22 @@ impl fmt::Display for Stmt { } } + struct WhereBoundHelper<'a>(&'a [String], Option<&'a str>); + + impl fmt::Display for WhereBoundHelper<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(bound) = self.1 { + if !self.0.is_empty() { + writeln!(f, "where")?; + for generic in self.0 { + writeln!(f, "{generic}{bound},")?; + } + } + } + Ok(()) + } + } + match self { Self::ClassDecl { id, @@ -1045,17 +1096,34 @@ impl fmt::Display for Stmt { superclasses, designated_initializers: _, derives, - ownership: _, + mutability, + skipped, } => { + if *skipped { + return Ok(()); + } + let macro_name = if generics.is_empty() { "extern_class" } else { "__inner_extern_class" }; + let main_feature_gate = match mutability { + Mutability::MutableWithImmutableSuperclass(superclass) => superclass.feature(), + Mutability::Immutable + | Mutability::Mutable + | Mutability::ImmutableWithMutableSubclass(_) + | Mutability::InteriorMutable => id.feature(), + }; + + let (superclass, superclasses_rest) = superclasses.split_at(1); + let (superclass, superclass_generics) = + superclass.get(0).expect("must have a least one superclass"); + writeln!(f, "{macro_name}!(")?; writeln!(f, " {derives}")?; - if let Some(feature) = id.feature() { + if let Some(feature) = &main_feature_gate { writeln!(f, " #[cfg(feature = \"{feature}\")]")?; } write!(f, "{availability}")?; @@ -1065,21 +1133,21 @@ impl fmt::Display for Stmt { for generic in generics { write!(f, "{generic}: Message = Object, ")?; } - for generic in generics { - write!(f, "{generic}Ownership: Ownership = Shared, ")?; - } write!(f, ">")?; }; if generics.is_empty() { writeln!(f, ";")?; } else { writeln!(f, " {{")?; + writeln!( + f, + "__superclass: {}{},", + superclass.path_in_relation_to(id), + GenericTyHelper(superclass_generics), + )?; for (i, generic) in generics.iter().enumerate() { - // Invariant over the generic (for now) - writeln!( - f, - "_inner{i}: PhantomData<*mut ({generic}, {generic}Ownership)>," - )?; + // Invariant over the generic by default + writeln!(f, "_inner{i}: PhantomData<*mut {generic}>,")?; } writeln!(f, "notunwindsafe: PhantomData<&'static mut ()>,")?; writeln!(f, "}}")?; @@ -1087,22 +1155,19 @@ impl fmt::Display for Stmt { writeln!(f)?; - if let Some(feature) = id.feature() { + if let Some(feature) = &main_feature_gate { writeln!(f, " #[cfg(feature = \"{feature}\")]")?; } writeln!( f, " unsafe impl{} ClassType for {}{} {{", - GenericParamsHelper(generics), + GenericParamsHelper(generics, "Message"), id.name, GenericTyHelper(generics), )?; - let (superclass, rest) = superclasses.split_at(1); - let (superclass, generics) = - superclass.get(0).expect("must have a least one superclass"); - if !rest.is_empty() { - write!(f, " #[inherits(")?; - let mut iter = rest.iter(); + 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() { @@ -1127,8 +1192,18 @@ impl fmt::Display for Stmt { f, " type Super = {}{};", superclass.path_in_relation_to(id), - GenericTyHelper(generics) + GenericTyHelper(superclass_generics), )?; + writeln!(f, " type Mutability = {mutability};")?; + if !generics.is_empty() { + writeln!(f)?; + writeln!( + f, + " fn as_super(&self) -> &Self::Super {{ &self.__superclass }}" + )?; + writeln!(f)?; + writeln!(f, " fn as_super_mut(&mut self) -> &mut Self::Super {{ &mut self.__superclass }}")?; + } writeln!(f, " }}")?; writeln!(f, ");")?; } @@ -1158,7 +1233,7 @@ impl fmt::Display for Stmt { writeln!( f, " unsafe impl{} {}{} {{", - GenericParamsHelper(generics), + GenericParamsHelper(generics, "Message"), cls.path_in_relation_to(category), GenericTyHelper(generics), )?; @@ -1205,30 +1280,62 @@ impl fmt::Display for Stmt { writeln!(f, " }}")?; writeln!(f, ");")?; } - Self::ProtocolImpl { - cls: _, - generics: _, - protocol, - availability: _, - } if protocol.name == "NSCopying" || protocol.name == "NSMutableCopying" => { - // TODO - } Self::ProtocolImpl { cls, generics, protocol, availability: _, } => { + let (generic_bound, where_bound) = if !generics.is_empty() { + match (&*protocol.library, &*protocol.name) { + // The object inherits from `NSObject` or `NSProxy` no + // matter what the generic type is, so this must be + // safe. + (_, _) if protocol.is_nsobject() => ("Message", None), + // Encoding and decoding requires that the inner types + // are codable as well. + ("Foundation", "NSCoding") => ("Message + NSCoding", None), + ("Foundation", "NSSecureCoding") => ("Message + NSSecureCoding", None), + // Copying collections is done as a shallow copy: + // + // + // E.g. it simply does a retain count bump, and hence + // does not require the inner type to implement + // `NSCopying`. + // + // The types does have to be cloneable, since generic + // types effectively store an `Id` of the type. + ("Foundation", "NSCopying") => ("IsIdCloneable", None), + ("Foundation", "NSMutableCopying") => ("IsIdCloneable", None), + // TODO: Do we need further tweaks to this? + ("Foundation", "NSFastEnumeration") => ("Message", None), + // AppKit fixes. TODO: Should we add more bounds here? + ("AppKit", "NSCollectionViewDataSource") => ("Message", None), + ("AppKit", "NSTableViewDataSource") => ("Message", None), + _ => { + error!( + ?protocol, + ?cls, + "unknown where bound for generic protocol impl" + ); + ("Message", None) + } + } + } else { + ("Message", None) + }; + if let Some(feature) = cls.feature() { writeln!(f, "#[cfg(feature = \"{feature}\")]")?; } writeln!( f, - "unsafe impl{} {} for {}{} {{}}", - GenericParamsHelper(generics), + "unsafe impl{} {} for {}{} {}{{}}", + GenericParamsHelper(generics, generic_bound), protocol.path_in_relation_to(cls), cls.path(), GenericTyHelper(generics), + WhereBoundHelper(generics, where_bound) )?; } Self::ProtocolDecl { diff --git a/crates/header-translator/translation-config.toml b/crates/header-translator/translation-config.toml index b9c2d12f1..c215e4486 100644 --- a/crates/header-translator/translation-config.toml +++ b/crates/header-translator/translation-config.toml @@ -1286,32 +1286,20 @@ derives = "PartialEq, Eq, Hash" derives = "PartialEq, Eq, Hash" [class.NSMutableData] derives = "PartialEq, Eq, Hash" -[class.NSMutableArray] -derives = "PartialEq, Eq, Hash" [class.NSMutableAttributedString] derives = "PartialEq, Eq, Hash" -[class.NSMutableSet] -derives = "PartialEq, Eq, Hash" [class.NSMutableString] derives = "PartialEq, Eq, Hash" -[class.NSMutableDictionary] -derives = "PartialEq, Eq, Hash" # Overridden fmt::Debug because it's prettier -[class.NSArray] -derives = "PartialEq, Eq, Hash" [class.NSData] derives = "PartialEq, Eq, Hash" -[class.NSDictionary] -derives = "PartialEq, Eq, Hash" [class.NSError] derives = "PartialEq, Eq, Hash" [class.NSException] derives = "PartialEq, Eq, Hash" [class.NSProcessInfo] derives = "PartialEq, Eq, Hash" -[class.NSSet] -derives = "PartialEq, Eq, Hash" [class.NSString] derives = "PartialEq, Eq, Hash" [class.NSUUID] @@ -1332,27 +1320,6 @@ skipped = true [class.NSValue.methods.new] skipped = true -# Returns `NSMyType` instead of `Self` or `NSMyType` -# See fixes/generic_return.rs -[class.NSArray.methods.initWithContentsOfURL_error] -skipped = true -[class.NSArray.methods.initWithContentsOfFile] -skipped = true -[class.NSArray.methods.initWithContentsOfURL] -skipped = true -[class.NSMutableArray.methods.initWithContentsOfFile] -skipped = true -[class.NSMutableArray.methods.initWithContentsOfURL] -skipped = true -[class.NSDictionary.methods.initWithContentsOfFile] -skipped = true -[class.NSDictionary.methods.initWithContentsOfURL] -skipped = true -[class.NSMutableDictionary.methods.initWithContentsOfFile] -skipped = true -[class.NSMutableDictionary.methods.initWithContentsOfURL] -skipped = true - # Manual definitions [class.NSException.methods.raise] skipped = true @@ -1542,3 +1509,33 @@ skipped = true # `os_log_t` not defined; skip for now [class.MXMetricManager.methods.makeLogHandleWithCategory] skipped = true + +# Custom generics because of auto traits +[class.NSArray] +definition-skipped = true +[class.NSMutableArray] +definition-skipped = true +[class.NSDictionary] +definition-skipped = true +[class.NSMutableDictionary] +definition-skipped = true +[class.NSSet] +definition-skipped = true +[class.NSMutableSet] +definition-skipped = true +[class.NSCountedSet] +definition-skipped = true +[class.NSOrderedSet] +definition-skipped = true +[class.NSMutableOrderedSet] +definition-skipped = true + +# These protocol impls would return the wrong types +[class.NSSimpleCString] +skipped-protocols = ["NSCopying", "NSMutableCopying"] +[class.NSConstantString] +skipped-protocols = ["NSCopying", "NSMutableCopying"] +[class.NSTextStorage] +skipped-protocols = ["NSCopying", "NSMutableCopying"] +[class.NSPurgeableData] +skipped-protocols = ["NSCopying", "NSMutableCopying"] diff --git a/crates/icrate/CHANGELOG.md b/crates/icrate/CHANGELOG.md index 3840c42eb..281e44362 100644 --- a/crates/icrate/CHANGELOG.md +++ b/crates/icrate/CHANGELOG.md @@ -16,9 +16,40 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - `MediaPlayer` - `MetricKit` - `PhotoKit` +* Added `NSCopying` and `NSMutableCopying` implementations for the classes + that implement those protocols. +* Added the following methods: + - `NSArray::get_retained` + - `NSArray::first_retained` + - `NSArray::last_retained` + - `NSSet::get_retained` + - `NSSet::to_vec` + - `NSSet::to_vec_retained` + - `NSDictionary::get_retained` + - `NSDictionary::keys_retained` + - `NSDictionary::values_retained` +* Added `MainThreadMarker::alloc` for allocating objects that need to be so on + the main thread. + +### Changed +* **BREAKING**: Renamed the `from_slice` method on `NSArray`, `NSSet`, + `NSMutableArray` and `NSMutableSet` to `from_id_slice`, and provided a new + `from_slice` method that takes `&[&T]` instead. +* **BREAKING**: Changed `NSMutableArray::replace` to return an `Result` in + case the index was out of bounds. +* **BREAKING**: Changed `NSMutableArray::remove` to return an `Option` in case + the index was out of bounds. +* **BREAKING**: Removed ownership parameter from generic types, since the + ownership/mutability information is now stored in `ClassType::Mutability`. +* **BREAKING**: Renamed `NSMutableCopying::mutable_copy` to `::mutableCopy`. ### Removed * **BREAKING**: Removed various redundant `NSProxy` methods. +* **BREAKING**: Removed `NSArray::to_shared_vec` and `NSArray::into_vec`, use + `NSArray::to_vec` or `NSArray::to_vec_retained` instead. +* **BREAKING**: Removed associated types from `NSCopying` and + `NSMutableCopying`, that information is now specified in + `ClassType::Mutability` instead. ## icrate 0.0.2 - 2023-02-07 diff --git a/crates/icrate/Cargo.toml b/crates/icrate/Cargo.toml index 43fb215cb..b44e19d5f 100644 --- a/crates/icrate/Cargo.toml +++ b/crates/icrate/Cargo.toml @@ -24,6 +24,9 @@ objc2 = { path = "../objc2", version = "=0.3.0-beta.5", default-features = false block2 = { path = "../block2", version = "=0.2.0-alpha.8", default-features = false, optional = true } dispatch = { version = "0.2.0", optional = true } +[dev-dependencies] +static_assertions = "1.1.0" + [package.metadata.docs.rs] default-target = "x86_64-apple-darwin" features = ["block", "objective-c", "dispatch", "unstable-frameworks-all", "unstable-private", "unstable-docsrs"] @@ -137,6 +140,8 @@ unstable-example-nspasteboard = [ "Foundation_NSArray", "Foundation_NSDictionary", "Foundation_NSString", + "AppKit", + "AppKit_NSPasteboard", ] unstable-example-browser = [ "apple", @@ -166,13 +171,8 @@ unstable-frameworks-gnustep-32bit = ["Foundation_all"] # TODO: Autogenerate this unstable-frameworks-ios = ["Foundation_all", "unstable-example-basic_usage", "unstable-example-speech_synthesis"] -# Temporary fixes until we can autogenerate these +# Temporary fix Foundation_NSProxy = [] -AuthenticationServices_ASCredentialProviderViewController = [] -AuthenticationServices_ASAccountAuthenticationModificationViewController = [] -AuthenticationServices_ASAuthorizationAppleIDButton = [] -AppKit_NSLayoutAnchor = [] -AppKit_NSPopover = ["AppKit_NSResponder"] # This section has been automatically generated by `objc2`'s `header-translator`. # DO NOT EDIT @@ -438,6 +438,7 @@ AppKit_NSImageView = [ ] AppKit_NSInputManager = [] AppKit_NSInputServer = [] +AppKit_NSLayoutAnchor = [] AppKit_NSLayoutConstraint = [] AppKit_NSLayoutDimension = [ "AppKit_NSLayoutAnchor", @@ -531,6 +532,9 @@ AppKit_NSPopUpButton = [ AppKit_NSPopUpButtonCell = [ "AppKit_NSMenuItemCell", ] +AppKit_NSPopover = [ + "AppKit_NSResponder", +] AppKit_NSPopoverTouchBarItem = [ "AppKit_NSTouchBarItem", ] @@ -920,6 +924,7 @@ AppKit_all = [ "AppKit_NSImageView", "AppKit_NSInputManager", "AppKit_NSInputServer", + "AppKit_NSLayoutAnchor", "AppKit_NSLayoutConstraint", "AppKit_NSLayoutDimension", "AppKit_NSLayoutGuide", @@ -961,6 +966,7 @@ AppKit_all = [ "AppKit_NSPickerTouchBarItem", "AppKit_NSPopUpButton", "AppKit_NSPopUpButtonCell", + "AppKit_NSPopover", "AppKit_NSPopoverTouchBarItem", "AppKit_NSPredicateEditor", "AppKit_NSPredicateEditorRowTemplate", @@ -1113,7 +1119,13 @@ AuthenticationServices_ASAccountAuthenticationModificationRequest = [] AuthenticationServices_ASAccountAuthenticationModificationUpgradePasswordToStrongPasswordRequest = [ "AuthenticationServices_ASAccountAuthenticationModificationRequest", ] +AuthenticationServices_ASAccountAuthenticationModificationViewController = [ + "AppKit_NSViewController", +] AuthenticationServices_ASAuthorization = [] +AuthenticationServices_ASAuthorizationAppleIDButton = [ + "AppKit_NSControl", +] AuthenticationServices_ASAuthorizationAppleIDCredential = [] AuthenticationServices_ASAuthorizationAppleIDProvider = [] AuthenticationServices_ASAuthorizationAppleIDRequest = [ @@ -1164,6 +1176,9 @@ AuthenticationServices_ASCredentialIdentityStoreState = [] AuthenticationServices_ASCredentialProviderExtensionContext = [ "Foundation_NSExtensionContext", ] +AuthenticationServices_ASCredentialProviderViewController = [ + "AppKit_NSViewController", +] AuthenticationServices_ASCredentialServiceIdentifier = [] AuthenticationServices_ASPasswordCredential = [] AuthenticationServices_ASPasswordCredentialIdentity = [] @@ -1177,7 +1192,9 @@ AuthenticationServices_all = [ "AuthenticationServices_ASAccountAuthenticationModificationReplacePasswordWithSignInWithAppleRequest", "AuthenticationServices_ASAccountAuthenticationModificationRequest", "AuthenticationServices_ASAccountAuthenticationModificationUpgradePasswordToStrongPasswordRequest", + "AuthenticationServices_ASAccountAuthenticationModificationViewController", "AuthenticationServices_ASAuthorization", + "AuthenticationServices_ASAuthorizationAppleIDButton", "AuthenticationServices_ASAuthorizationAppleIDCredential", "AuthenticationServices_ASAuthorizationAppleIDProvider", "AuthenticationServices_ASAuthorizationAppleIDRequest", @@ -1210,6 +1227,7 @@ AuthenticationServices_all = [ "AuthenticationServices_ASCredentialIdentityStore", "AuthenticationServices_ASCredentialIdentityStoreState", "AuthenticationServices_ASCredentialProviderExtensionContext", + "AuthenticationServices_ASCredentialProviderViewController", "AuthenticationServices_ASCredentialServiceIdentifier", "AuthenticationServices_ASPasswordCredential", "AuthenticationServices_ASPasswordCredentialIdentity", diff --git a/crates/icrate/README.md b/crates/icrate/README.md index 35eb589fc..dc1cb50dc 100644 --- a/crates/icrate/README.md +++ b/crates/icrate/README.md @@ -33,7 +33,7 @@ use icrate::ns_string; let string = ns_string!("world"); println!("hello {string}"); -let array = NSArray::from_slice(&[string.copy()]); +let array = NSArray::from_id_slice(&[string.copy()]); println!("{array:?}"); ``` diff --git a/crates/icrate/examples/basic_usage.rs b/crates/icrate/examples/basic_usage.rs index 5029b5a5f..21f4fc631 100644 --- a/crates/icrate/examples/basic_usage.rs +++ b/crates/icrate/examples/basic_usage.rs @@ -22,7 +22,7 @@ fn main() { println!("{}", array.len()); // Turn the NSArray back into a Vec - let mut objs = NSArray::into_vec(array); + let mut objs = array.to_vec_retained(); let obj = objs.pop().unwrap(); // Create a static NSString diff --git a/crates/icrate/examples/browser.rs b/crates/icrate/examples/browser.rs index d127b5127..a088cf429 100644 --- a/crates/icrate/examples/browser.rs +++ b/crates/icrate/examples/browser.rs @@ -2,13 +2,6 @@ use icrate::{ ns_string, - objc2::{ - declare::{Ivar, IvarDrop}, - declare_class, extern_methods, msg_send, - rc::{Allocated, Id}, - runtime::{Object, ProtocolObject, Sel}, - sel, ClassType, - }, AppKit::{ NSApplication, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSBezelStyleShadowlessSquare, NSButton, NSColor, NSControl, NSControlTextEditingDelegate, @@ -21,6 +14,14 @@ use icrate::{ Foundation::{NSObject, NSObjectProtocol, NSPoint, NSRect, NSSize, NSURLRequest, NSURL}, WebKit::{WKNavigation, WKNavigationDelegate, WKWebView}, }; +use objc2::{ + declare::{Ivar, IvarDrop}, + declare_class, extern_methods, msg_send, + mutability::InteriorMutable, + rc::{Allocated, Id}, + runtime::{Object, ProtocolObject, Sel}, + sel, ClassType, +}; declare_class!( struct Delegate { @@ -31,6 +32,7 @@ declare_class!( unsafe impl ClassType for Delegate { type Super = NSObject; + type Mutability = InteriorMutable; const NAME: &'static str = "Delegate"; } @@ -39,13 +41,13 @@ declare_class!( #[allow(non_snake_case)] unsafe fn __init_withTextField_andWebView( self: &mut Self, - text_field: *mut NSTextField, - web_view: *mut WKWebView, + text_field: &NSTextField, + web_view: &WKWebView, ) -> Option<&mut Self> { let this: Option<&mut Self> = msg_send![super(self), init]; let this = this?; - Ivar::write(&mut this.text_field, unsafe { Id::retain(text_field) }?); - Ivar::write(&mut this.web_view, unsafe { Id::retain(web_view) }?); + Ivar::write(&mut this.text_field, text_field.retain()); + Ivar::write(&mut this.web_view, web_view.retain()); Some(this) } } diff --git a/crates/icrate/examples/delegate.rs b/crates/icrate/examples/delegate.rs index f689e318e..37a35716b 100644 --- a/crates/icrate/examples/delegate.rs +++ b/crates/icrate/examples/delegate.rs @@ -1,10 +1,13 @@ +#![deny(unsafe_op_in_unsafe_fn)] #![cfg_attr(not(target_os = "macos"), allow(unused))] +use std::ptr::NonNull; + use icrate::ns_string; -use icrate::objc2::declare::{Ivar, IvarBool, IvarDrop, IvarEncode}; -use icrate::objc2::rc::Id; -use icrate::objc2::runtime::Object; -use icrate::objc2::{declare_class, msg_send, msg_send_id, ClassType}; use icrate::Foundation::{NSCopying, NSObject, NSString}; +use objc2::declare::{Ivar, IvarBool, IvarDrop, IvarEncode}; +use objc2::rc::Id; +use objc2::runtime::Object; +use objc2::{declare_class, msg_send, msg_send_id, mutability, ClassType}; #[cfg(target_os = "macos")] #[link(name = "AppKit", kind = "framework")] @@ -27,13 +30,18 @@ declare_class!( unsafe impl ClassType for CustomAppDelegate { #[inherits(NSObject)] type Super = icrate::AppKit::NSResponder; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "MyCustomAppDelegate"; } unsafe impl CustomAppDelegate { #[method(initWith:another:)] - fn init_with(self: &mut Self, ivar: u8, another_ivar: bool) -> Option<&mut Self> { - let this: Option<&mut Self> = unsafe { msg_send![super(self), init] }; + unsafe fn init_with( + this: *mut Self, + ivar: u8, + another_ivar: bool, + ) -> Option> { + let this: Option<&mut Self> = unsafe { msg_send![super(this), init] }; // TODO: `ns_string` can't be used inside closures; investigate! let s = ns_string!("def"); @@ -45,7 +53,7 @@ declare_class!( *this.maybe_id_ivar = Some(s.copy()); Ivar::write(&mut this.box_ivar, Box::new(2)); Ivar::write(&mut this.id_ivar, NSString::from_str("abc")); - this + NonNull::from(this) }) } diff --git a/crates/icrate/examples/nspasteboard.rs b/crates/icrate/examples/nspasteboard.rs index 730a01016..b179ef546 100644 --- a/crates/icrate/examples/nspasteboard.rs +++ b/crates/icrate/examples/nspasteboard.rs @@ -2,143 +2,63 @@ //! //! Works on macOS 10.7+ #![deny(unsafe_op_in_unsafe_fn)] -#![cfg_attr(not(target_os = "macos"), allow(unused))] -use std::mem::ManuallyDrop; +use icrate::AppKit::{NSPasteboard, NSPasteboardTypeString}; +use icrate::Foundation::{NSArray, NSCopying, NSString}; +use objc2::rc::Id; +use objc2::runtime::{Class, Object, ProtocolObject}; +use objc2::ClassType; -use icrate::objc2::rc::Id; -use icrate::objc2::runtime::{Class, Object}; -use icrate::objc2::{extern_class, msg_send, msg_send_id, ClassType}; -use icrate::Foundation::{NSArray, NSDictionary, NSInteger, NSObject, NSString}; - -type NSPasteboardType = NSString; -type NSPasteboardReadingOptionKey = NSString; - -#[cfg(target_os = "macos")] -#[link(name = "AppKit", kind = "framework")] -extern "C" { - /// - static NSPasteboardTypeString: Option<&'static NSPasteboardType>; +/// Simple, straightforward implementation +pub fn get_text_1(pasteboard: &NSPasteboard) -> Option> { + unsafe { pasteboard.stringForType(NSPasteboardTypeString) } } -#[cfg(target_os = "macos")] -extern_class!( - /// - pub struct NSPasteboard; - - // SAFETY: NSPasteboard actually inherits from NSObject. - unsafe impl ClassType for NSPasteboard { - type Super = NSObject; - } -); - -#[cfg(target_os = "macos")] -impl NSPasteboard { - /// We return a `Shared` `Id` because `general` can easily be called - /// again, and it would return the same object, resulting in two aliasing - /// mutable references if we returned an `Owned` Id. - /// - /// Besides, even if we could prevent this, there might be Objective-C - /// code somewhere else that accesses this instance. - /// - /// TODO: Is this safe to call outside the main thread? - /// - /// - pub fn general() -> Id { - unsafe { msg_send_id![Self::class(), generalPasteboard] } - } - - /// Simple, straightforward implementation - /// - /// - pub fn text_impl_1(&self) -> Id { - let s = unsafe { NSPasteboardTypeString }.unwrap(); - unsafe { msg_send_id![self, stringForType: s] } - } - - /// More complex implementation using `readObjectsForClasses:options:`, - /// intended to show some how some patterns might require more knowledge - /// of nitty-gritty details. - /// - /// - pub fn text_impl_2(&self) -> Id { - // The NSPasteboard API is a bit weird, it requires you to pass - // classes as objects, which `icrate::Foundation::NSArray` was not - // really made for - so we convert the class to an `Object` type - // instead. Also, we wrap it in `ManuallyDrop` because I'm not sure - // how classes handle `release` calls? - // - // TODO: Investigate and find a better way to express this in objc2. - let string_classes: ManuallyDrop<[Id; 1]> = { - let cls: *const Class = NSString::class(); - let cls = cls as *mut Object; - unsafe { ManuallyDrop::new([Id::new(cls).unwrap()]) } - }; - // Temporary, see https://github.com/rust-lang/rust-clippy/issues/9101 - #[allow(unknown_lints, clippy::explicit_auto_deref)] - let class_array = NSArray::from_slice(&*string_classes); - let options = NSDictionary::new(); - let objects = unsafe { self.read_objects_for_classes(&class_array, &options) }; - - // TODO: Should perhaps return Id? - let ptr: *const Object = objects.first().unwrap(); - - // And this part is weird as well, since we now have to convert the - // object into an NSString, which we know it to be since that's what - // we told `readObjectsForClasses:options:`. - let ptr = ptr as *mut NSString; - unsafe { Id::retain(ptr) }.unwrap() - } - - /// Defined here to make it easier to declare which types are expected. - /// This is a common pattern that I can wholeheartedly recommend! - /// - /// SAFETY: `class_array` must contain classes! - unsafe fn read_objects_for_classes( - &self, - class_array: &NSArray, - options: &NSDictionary, - ) -> Id> { - unsafe { msg_send_id![self, readObjectsForClasses: class_array, options: options] } - } +/// More complex implementation using `readObjectsForClasses:options:`, +/// intended to show how some patterns might require more knowledge of +/// nitty-gritty details. +pub fn get_text_2(pasteboard: &NSPasteboard) -> Option> { + // The NSPasteboard API is a bit weird, it requires you to pass classes as + // objects, which `icrate::Foundation::NSArray` was not really made for - + // so we convert the class to an `Object` type instead. + // + // TODO: Investigate and find a better way to express this in `objc2`. + let string_class = { + let cls: *const Class = NSString::class(); + let cls = cls as *mut Object; + unsafe { Id::new(cls).unwrap() } + }; + let class_array = NSArray::from_vec(vec![string_class]); + let objects = unsafe { pasteboard.readObjectsForClasses_options(&class_array, None) }; + + let obj: *const Object = objects?.first()?; + // And this part is weird as well, since we now have to convert the object + // into an NSString, which we know it to be since that's what we told + // `readObjectsForClasses:options:`. + let obj = obj as *mut NSString; + Some(unsafe { Id::retain(obj) }.unwrap()) +} - /// This takes `&self` even though `writeObjects:` would seem to mutate - /// the pasteboard. "What is going on?", you might rightfully ask! - /// - /// We do this because we can't soundly get a mutable reference to the - /// global `NSPasteboard` instance, see [`NSPasteboard::general`]. - /// - /// This is sound because `NSPasteboard` contains `NSObject`, which in - /// turn contains `UnsafeCell`, allowing interior mutability. - /// - /// - /// - pub fn set_text(&self, text: Id) { - let _: NSInteger = unsafe { msg_send![self, clearContents] }; - let string_array = NSArray::from_slice(&[text]); - let res: bool = unsafe { msg_send![self, writeObjects: &*string_array] }; - if !res { - panic!("Failed writing to pasteboard"); - } +pub fn set_text(pasteboard: &NSPasteboard, text: &NSString) { + let _ = unsafe { pasteboard.clearContents() }; + let obj = ProtocolObject::from_id(text.copy()); + let objects = NSArray::from_vec(vec![obj]); + let res = unsafe { pasteboard.writeObjects(&objects) }; + if !res { + panic!("Failed writing to pasteboard"); } } -#[cfg(target_os = "macos")] fn main() { - let pasteboard = NSPasteboard::general(); - let impl_1 = pasteboard.text_impl_1(); - let impl_2 = pasteboard.text_impl_2(); + let pasteboard = unsafe { NSPasteboard::generalPasteboard() }; + let impl_1 = get_text_1(&pasteboard); + let impl_2 = get_text_2(&pasteboard); println!("Pasteboard text from implementation 1 was: {impl_1:?}"); println!("Pasteboard text from implementation 2 was: {impl_2:?}"); assert_eq!(impl_1, impl_2); let s = NSString::from_str("Hello, world!"); - pasteboard.set_text(s.clone()); + set_text(&pasteboard, &s); println!("Now the pasteboard text should be: {s:?}"); - assert_eq!(s, pasteboard.text_impl_1()); -} - -#[cfg(not(target_os = "macos"))] -fn main() { - panic!("this example only works on macOS"); + assert_eq!(Some(s), get_text_1(&pasteboard)); } diff --git a/crates/icrate/examples/speech_synthesis.rs b/crates/icrate/examples/speech_synthesis.rs index 1b42c5ba6..8cb151a25 100644 --- a/crates/icrate/examples/speech_synthesis.rs +++ b/crates/icrate/examples/speech_synthesis.rs @@ -4,8 +4,6 @@ //! other Apple platforms. Note that `AVSpeechSynthesizer` _is_ available on //! macOS, but only since 10.15! //! -//! TODO: Unsure about when to use `&mut` here? -//! //! Works on macOS >= 10.7 and iOS > 7.0. #![deny(unsafe_op_in_unsafe_fn)] @@ -13,13 +11,15 @@ use std::thread; use std::time::Duration; use icrate::ns_string; -use icrate::objc2::rc::{Id, Owned}; -use icrate::objc2::{extern_class, msg_send, msg_send_id, ClassType}; use icrate::Foundation::{NSObject, NSString}; +use objc2::mutability::InteriorMutable; +use objc2::rc::Id; +use objc2::{extern_class, msg_send, msg_send_id, ClassType}; #[cfg(target_os = "macos")] mod appkit { use icrate::Foundation::NSCopying; + use std::cell::Cell; use super::*; @@ -32,36 +32,37 @@ mod appkit { unsafe impl ClassType for Synthesizer { type Super = NSObject; + type Mutability = InteriorMutable; const NAME: &'static str = "NSSpeechSynthesizer"; } ); impl Synthesizer { // Uses default voice - pub fn new() -> Id { + pub fn new() -> Id { unsafe { msg_send_id![Self::class(), new] } } - fn set_rate(&mut self, rate: f32) { + fn set_rate(&self, rate: f32) { unsafe { msg_send![self, setRate: rate] } } - fn set_volume(&mut self, volume: f32) { + fn set_volume(&self, volume: f32) { unsafe { msg_send![self, setVolume: volume] } } - fn start_speaking(&mut self, s: &NSString) { - unsafe { msg_send![self, startSpeakingString: s] } + fn start_speaking(&self, s: &NSString) { + let _: bool = unsafe { msg_send![self, startSpeakingString: s] }; } - pub fn speak(&mut self, utterance: &Utterance) { + pub fn speak(&self, utterance: &Utterance) { // Convert to the range 90-720 that `NSSpeechSynthesizer` seems to // support // // Note that you'd probably want a nonlinear conversion here to // make it match `AVSpeechSynthesizer`. - self.set_rate(90.0 + (utterance.rate * (360.0 - 90.0))); - self.set_volume(utterance.volume); + self.set_rate(90.0 + (utterance.rate.get() * (360.0 - 90.0))); + self.set_volume(utterance.volume.get()); self.start_speaking(&utterance.string); } @@ -72,26 +73,26 @@ mod appkit { // Shim to make NSSpeechSynthesizer work similar to AVSpeechSynthesizer pub struct Utterance { - rate: f32, - volume: f32, + rate: Cell, + volume: Cell, string: Id, } impl Utterance { pub fn new(string: &NSString) -> Self { Self { - rate: 0.5, - volume: 1.0, + rate: Cell::new(0.5), + volume: Cell::new(1.0), string: string.copy(), } } - pub fn set_rate(&mut self, rate: f32) { - self.rate = rate; + pub fn set_rate(&self, rate: f32) { + self.rate.set(rate); } - pub fn set_volume(&mut self, volume: f32) { - self.volume = volume; + pub fn set_volume(&self, volume: f32) { + self.volume.set(volume); } } } @@ -110,16 +111,17 @@ mod avfaudio { unsafe impl ClassType for Synthesizer { type Super = NSObject; + type Mutability = InteriorMutable; const NAME: &'static str = "AVSpeechSynthesizer"; } ); impl Synthesizer { - pub fn new() -> Id { + pub fn new() -> Id { unsafe { msg_send_id![Self::class(), new] } } - pub fn speak(&mut self, utterance: &Utterance) { + pub fn speak(&self, utterance: &Utterance) { unsafe { msg_send![self, speakUtterance: utterance] } } @@ -135,20 +137,21 @@ mod avfaudio { unsafe impl ClassType for Utterance { type Super = NSObject; + type Mutability = InteriorMutable; const NAME: &'static str = "AVSpeechUtterance"; } ); impl Utterance { - pub fn new(string: &NSString) -> Id { + pub fn new(string: &NSString) -> Id { unsafe { msg_send_id![Self::alloc(), initWithString: string] } } - pub fn set_rate(&mut self, rate: f32) { + pub fn set_rate(&self, rate: f32) { unsafe { msg_send![self, setRate: rate] } } - pub fn set_volume(&mut self, volume: f32) { + pub fn set_volume(&self, volume: f32) { unsafe { msg_send![self, setVolume: volume] } } } @@ -160,8 +163,8 @@ use appkit::{Synthesizer, Utterance}; use avfaudio::{Synthesizer, Utterance}; fn main() { - let mut synthesizer = Synthesizer::new(); - let mut utterance = Utterance::new(ns_string!("Hello from Rust!")); + let synthesizer = Synthesizer::new(); + let utterance = Utterance::new(ns_string!("Hello from Rust!")); utterance.set_rate(0.5); utterance.set_volume(0.5); synthesizer.speak(&utterance); diff --git a/crates/icrate/src/AppKit/fixes/mod.rs b/crates/icrate/src/AppKit/fixes/mod.rs index eec3311e5..96eb04b3f 100644 --- a/crates/icrate/src/AppKit/fixes/mod.rs +++ b/crates/icrate/src/AppKit/fixes/mod.rs @@ -36,22 +36,31 @@ extern_class!( unsafe impl ClassType for NSPopover { #[inherits(NSObject)] type Super = crate::AppKit::NSResponder; + type Mutability = InteriorMutable; } ); __inner_extern_class!( #[cfg(feature = "AppKit_NSLayoutAnchor")] #[derive(Debug, PartialEq, Eq, Hash)] - pub struct NSLayoutAnchor { - _inner0: PhantomData<*mut (AnchorType, AnchorTypeOwnership)>, + pub struct NSLayoutAnchor { + __superclass: NSObject, + _inner0: PhantomData<*mut AnchorType>, notunwindsafe: PhantomData<&'static mut ()>, } #[cfg(feature = "AppKit_NSLayoutAnchor")] - unsafe impl ClassType - for NSLayoutAnchor - { + unsafe impl ClassType for NSLayoutAnchor { type Super = NSObject; + type Mutability = InteriorMutable; + + fn as_super(&self) -> &Self::Super { + &self.__superclass + } + + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__superclass + } } ); diff --git a/crates/icrate/src/AuthenticationServices/fixes/mod.rs b/crates/icrate/src/AuthenticationServices/fixes/mod.rs index 2092e7b7c..3de214181 100644 --- a/crates/icrate/src/AuthenticationServices/fixes/mod.rs +++ b/crates/icrate/src/AuthenticationServices/fixes/mod.rs @@ -19,6 +19,7 @@ extern_class!( #[cfg(feature = "AuthenticationServices_ASCredentialProviderViewController")] unsafe impl ClassType for ASCredentialProviderViewController { type Super = ASViewController; + type Mutability = InteriorMutable; } ); @@ -30,6 +31,7 @@ extern_class!( #[cfg(feature = "AuthenticationServices_ASAccountAuthenticationModificationViewController")] unsafe impl ClassType for ASAccountAuthenticationModificationViewController { type Super = ASViewController; + type Mutability = InteriorMutable; } ); @@ -41,5 +43,6 @@ extern_class!( #[cfg(feature = "AuthenticationServices_ASAuthorizationAppleIDButton")] unsafe impl ClassType for ASAuthorizationAppleIDButton { type Super = ASControl; + type Mutability = InteriorMutable; } ); diff --git a/crates/icrate/src/Foundation/additions/array.rs b/crates/icrate/src/Foundation/additions/array.rs index caedc7e1d..49429d695 100644 --- a/crates/icrate/src/Foundation/additions/array.rs +++ b/crates/icrate/src/Foundation/additions/array.rs @@ -4,100 +4,67 @@ use core::fmt; use core::mem; use core::ops::{Index, IndexMut, Range}; use core::panic::{RefUnwindSafe, UnwindSafe}; -use core::ptr::NonNull; -use objc2::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId}; -use objc2::runtime::Object; -use objc2::{extern_methods, msg_send, msg_send_id, ClassType, Message}; +use objc2::msg_send; +use objc2::mutability::{IsMutable, IsRetainable}; +use objc2::rc::DefaultId; +use super::util; +use crate::common::*; use crate::Foundation::{self, NSArray}; -// SAFETY: Same as Id (what NSArray and NSMutableArray effectively store). -unsafe impl Sync for NSArray {} -unsafe impl Send for NSArray {} -unsafe impl Sync for NSArray {} -unsafe impl Send for NSArray {} - -#[cfg(feature = "Foundation_NSMutableArray")] -unsafe impl Sync for Foundation::NSMutableArray {} -#[cfg(feature = "Foundation_NSMutableArray")] -unsafe impl Send for Foundation::NSMutableArray {} -#[cfg(feature = "Foundation_NSMutableArray")] -unsafe impl Sync for Foundation::NSMutableArray {} -#[cfg(feature = "Foundation_NSMutableArray")] -unsafe impl Send for Foundation::NSMutableArray {} - -// Also same as Id -impl RefUnwindSafe for NSArray {} -impl UnwindSafe for NSArray {} -impl UnwindSafe for NSArray {} - -#[cfg(feature = "Foundation_NSMutableArray")] -impl RefUnwindSafe for Foundation::NSMutableArray {} -#[cfg(feature = "Foundation_NSMutableArray")] -impl UnwindSafe for Foundation::NSMutableArray {} -#[cfg(feature = "Foundation_NSMutableArray")] -impl UnwindSafe for Foundation::NSMutableArray {} - -#[track_caller] -pub(crate) unsafe fn with_objects( - objects: &[&T], -) -> Id { - unsafe { - msg_send_id![ - R::alloc(), - initWithObjects: objects.as_ptr(), - count: objects.len(), - ] - } -} - extern_methods!( - /// Generic creation methods. - unsafe impl NSArray { + /// Creation methods. + unsafe impl NSArray { /// Get an empty array. - // SAFETY: - // - `new` may not create a new object, but instead return a shared - // instance. We remedy this by returning `Id`. - // - `O` don't actually matter here! E.g. `NSArray` is - // perfectly legal, since the array doesn't have any elements, and - // hence the notion of ownership over the elements is void. #[method_id(new)] - pub fn new() -> Id; - - pub fn from_vec(vec: Vec>) -> Id { - // SAFETY: - // `initWithObjects:` may choose to deduplicate arrays (I could - // imagine it having a special case for arrays with one `NSNumber` - // object), and returning mutable references to those would be - // unsound! - // However, when we know that we have ownership over the variables, we - // also know that there cannot be another array in existence with the - // same objects, so `Id, Owned>` is safe to return. + pub fn new() -> Id; + + pub fn from_vec(mut vec: Vec>) -> Id { + // We intentionally extract the length before we access the + // pointer as mutable, to not invalidate that mutable pointer. + let len = vec.len(); + let ptr = util::id_ptr_cast(vec.as_mut_ptr()); + // SAFETY: We've consumed the `Id`s, which means that we can + // now safely take ownership (even if `T` is mutable). + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } + // The drop of `Vec` here would invalidate our mutable pointer, + // except for the fact that we're using `UnsafeCell` in `Object`. + } + + pub fn from_id_slice(slice: &[Id]) -> Id + where + T: IsIdCloneable, + { + let len = slice.len(); + let ptr = util::id_ptr_cast_const(slice.as_ptr()); + // SAFETY: Because of the `T: IsIdCloneable` bound, and since we + // take `&[Id]` (effectively `&Id`), we are allowed to give + // the slice to Objective-C, which will retain it internally. // - // In essence, we can choose between always returning `Id` - // or `Id`, and the latter is probably the most useful, as we - // would like to know when we're the only owner of the array, to - // allow mutation of the array's items. - unsafe { with_objects(vec.as_slice_ref()) } + // Faster version of: + // Self::from_vec(slice.iter().map(|obj| obj.clone()).collect()) + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } } - } - /// Creation methods that produce shared arrays. - unsafe impl NSArray { - pub fn from_slice(slice: &[Id]) -> Id { - // SAFETY: Taking `&T` would not be sound, since the `&T` could come - // from an `Id` that would now no longer be owned! + pub fn from_slice(slice: &[&T]) -> Id + where + T: IsRetainable, + { + let len = slice.len(); + let ptr = util::ref_ptr_cast_const(slice.as_ptr()); + // SAFETY: Because of the `T: IsRetainable` bound, we are allowed + // to give the slice to Objective-C, which will retain it + // internally. // - // (Note that NSArray internally retains all the objects it is given, - // effectively making the safety requirements the same as - // `Id::retain`). - unsafe { with_objects(slice.as_slice_ref()) } + // Faster version of: + // Self::from_vec(slice.iter().map(|obj| obj.retain()).collect()) + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } } } - /// Generic accessor methods. - unsafe impl NSArray { + /// Accessor methods. + unsafe impl NSArray { #[doc(alias = "count")] pub fn len(&self) -> usize { self.count() @@ -121,14 +88,75 @@ extern_methods!( } } + #[doc(alias = "objectAtIndex:")] + pub fn get_retained(&self, index: usize) -> Option> + where + T: IsIdCloneable, + { + // SAFETY: The object is stored in the array + self.get(index) + .map(|obj| unsafe { util::collection_retain_id(obj) }) + } + + #[method(objectAtIndex:)] + unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T; + + #[doc(alias = "objectAtIndex:")] + pub fn get_mut(&mut self, index: usize) -> Option<&mut T> + where + T: IsMutable, + { + // TODO: Replace this check with catching the thrown NSRangeException + if index < self.len() { + // SAFETY: The index is checked to be in bounds, and the + // reference is safe as mutable because of the `T: IsMutable` + // bound. + Some(unsafe { self.get_unchecked_mut(index) }) + } else { + None + } + } + #[doc(alias = "firstObject")] #[method(firstObject)] pub fn first(&self) -> Option<&T>; + #[doc(alias = "firstObject")] + pub fn first_retained(&self) -> Option> + where + T: IsIdCloneable, + { + // SAFETY: The object is stored in the array + self.first() + .map(|obj| unsafe { util::collection_retain_id(obj) }) + } + + #[doc(alias = "firstObject")] + #[method(firstObject)] + pub fn first_mut(&mut self) -> Option<&mut T> + where + T: IsMutable; + #[doc(alias = "lastObject")] #[method(lastObject)] pub fn last(&self) -> Option<&T>; + #[doc(alias = "lastObject")] + pub fn last_retained(&self) -> Option> + where + T: IsIdCloneable, + { + // SAFETY: The object is stored in the array + self.last() + .map(|obj| unsafe { util::collection_retain_id(obj) }) + } + + #[doc(alias = "lastObject")] + #[method(lastObject)] + pub fn last_mut(&mut self) -> Option<&mut T> + where + T: IsMutable; + #[doc(alias = "objectEnumerator")] #[cfg(feature = "Foundation_NSEnumerator")] pub fn iter(&self) -> Foundation::NSEnumerator2<'_, T> { @@ -148,6 +176,7 @@ extern_methods!( } } + #[doc(alias = "getObjects:range:")] pub fn objects_in_range(&self, range: Range) -> Option> { if range.end > self.len() { return None; @@ -156,68 +185,38 @@ extern_methods!( Some(unsafe { self.objects_in_range_unchecked(range) }) } + #[doc(alias = "getObjects:range:")] pub fn to_vec(&self) -> Vec<&T> { // SAFETY: The range is know to be in bounds unsafe { self.objects_in_range_unchecked(0..self.len()) } } - // TODO: Take Id ? - pub fn into_vec(array: Id) -> Vec> { - array - .to_vec() - .into_iter() - .map(|obj| unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() }) - .collect() - } - } - - /// Accessor methods that work on shared arrays. - unsafe impl NSArray { - #[doc(alias = "objectAtIndex:")] - pub fn get_retained(&self, index: usize) -> Id { - let obj = self.get(index).unwrap(); - // SAFETY: The object is originally shared (see `where` bound). - unsafe { Id::retain_autoreleased(obj as *const T as *mut T).unwrap_unchecked() } - } - - pub fn to_shared_vec(&self) -> Vec> { + #[doc(alias = "getObjects:range:")] + pub fn to_vec_retained(&self) -> Vec> + where + T: IsIdCloneable, + { + // SAFETY: The objects are stored in the array self.to_vec() .into_iter() - .map(|obj| unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() }) + .map(|obj| unsafe { util::collection_retain_id(obj) }) .collect() } - } - /// Accessor methods that work on owned arrays. - unsafe impl NSArray { - #[doc(alias = "objectAtIndex:")] - pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { - // TODO: Replace this check with catching the thrown NSRangeException - if index < self.len() { - // SAFETY: The index is checked to be in bounds. - Some(unsafe { msg_send![self, objectAtIndex: index] }) - } else { - None - } - } - - #[doc(alias = "firstObject")] - #[method(firstObject)] - pub fn first_mut(&mut self) -> Option<&mut T>; - - #[doc(alias = "lastObject")] - #[method(lastObject)] - pub fn last_mut(&mut self) -> Option<&mut T>; + // `fn into_vec(Id) -> Vec>` would not be safe, since + // the array itself is unconditionally `IsIdCloneable`, even when + // containing mutable elements, and hence we would be able to + // duplicate those. } ); -unsafe impl Foundation::NSFastEnumeration2 for NSArray { +unsafe impl Foundation::NSFastEnumeration2 for NSArray { type Item = T; } -impl<'a, T: Message, O: Ownership> IntoIterator for &'a NSArray { +impl<'a, T: Message> IntoIterator for &'a NSArray { type Item = &'a T; - type IntoIter = Foundation::NSFastEnumerator2<'a, NSArray>; + type IntoIter = Foundation::NSFastEnumerator2<'a, NSArray>; fn into_iter(self) -> Self::IntoIter { use Foundation::NSFastEnumeration2; @@ -225,7 +224,7 @@ impl<'a, T: Message, O: Ownership> IntoIterator for &'a NSArray { } } -impl Index for NSArray { +impl Index for NSArray { type Output = T; fn index(&self, index: usize) -> &T { @@ -233,22 +232,20 @@ impl Index for NSArray { } } -impl IndexMut for NSArray { +impl IndexMut for NSArray { fn index_mut(&mut self, index: usize) -> &mut T { self.get_mut(index).unwrap() } } -impl DefaultId for NSArray { - type Ownership = Shared; - +impl DefaultId for NSArray { #[inline] - fn default_id() -> Id { + fn default_id() -> Id { Self::new() } } -impl fmt::Debug for NSArray { +impl fmt::Debug for NSArray { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use Foundation::NSFastEnumeration2; diff --git a/crates/icrate/src/Foundation/additions/attributed_string.rs b/crates/icrate/src/Foundation/additions/attributed_string.rs index 756b26e35..a935211d3 100644 --- a/crates/icrate/src/Foundation/additions/attributed_string.rs +++ b/crates/icrate/src/Foundation/additions/attributed_string.rs @@ -1,10 +1,10 @@ #![cfg(feature = "Foundation_NSAttributedString")] use core::panic::{RefUnwindSafe, UnwindSafe}; -use objc2::rc::{DefaultId, Id, Shared}; -use objc2::runtime::Object; -use objc2::{extern_methods, ClassType}; +use objc2::extern_methods; +use objc2::rc::DefaultId; +use crate::common::*; use crate::Foundation::{self, NSAttributedString, NSAttributedStringKey}; // SAFETY: `NSAttributedString` is immutable and `NSMutableAttributedString` @@ -51,10 +51,8 @@ extern_methods!( ); impl DefaultId for NSAttributedString { - type Ownership = Shared; - #[inline] - fn default_id() -> Id { + fn default_id() -> Id { Self::new() } } diff --git a/crates/icrate/src/Foundation/additions/copying.rs b/crates/icrate/src/Foundation/additions/copying.rs deleted file mode 100644 index bed083ddb..000000000 --- a/crates/icrate/src/Foundation/additions/copying.rs +++ /dev/null @@ -1,49 +0,0 @@ -use objc2::rc::{Id, Owned, Ownership}; -use objc2::{msg_send_id, Message}; - -pub unsafe trait NSCopying: Message { - /// Indicates whether the type is mutable or immutable. - /// - /// This can be [`Owned`] if and only if `copy` creates a new instance, - /// see the following example: - /// - /// ```ignore - /// let x: Id = MyObject::new(); - /// // This is valid only if `y` is a new instance. Otherwise `x` and `y` - /// // would be able to create aliasing mutable references! - /// let y: Id = x.copy(); - /// ``` - /// - /// Note that for the same reason, you should be careful when defining - /// `new` methods on your object; e.g. immutable types like [`NSString`] - /// don't return `Id`, because that would allow this - /// trait to create an aliasing `Id` (since sending the - /// `copy` message (and others) does not create a new instance, but - /// instead just retains the instance). - /// - /// [`NSString`]: crate::Foundation::NSString - type Ownership: Ownership; - - /// The output type. - /// - /// This is usually `Self`, but e.g. `NSMutableString` returns `NSString`. - type Output: Message; - - fn copy(&self) -> Id { - unsafe { msg_send_id![self, copy] } - } -} - -/// TODO -/// -/// Note that the `mutableCopy` selector must return an owned object! -pub unsafe trait NSMutableCopying: Message { - /// The output type. - /// - /// This is usually `Self`, but e.g. `NSString` returns `NSMutableString`. - type Output: Message; - - fn mutable_copy(&self) -> Id { - unsafe { msg_send_id![self, mutableCopy] } - } -} diff --git a/crates/icrate/src/Foundation/additions/data.rs b/crates/icrate/src/Foundation/additions/data.rs index 042d32896..6c213dd14 100644 --- a/crates/icrate/src/Foundation/additions/data.rs +++ b/crates/icrate/src/Foundation/additions/data.rs @@ -6,7 +6,6 @@ use core::ops::Index; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::slice::{self, SliceIndex}; -use objc2::msg_send_id; use objc2::rc::DefaultId; use crate::common::*; @@ -27,7 +26,8 @@ extern_methods!( pub fn new() -> Id; pub fn with_bytes(bytes: &[u8]) -> Id { - unsafe { Id::cast(with_slice(Self::class(), bytes)) } + let bytes_ptr = bytes.as_ptr() as *mut c_void; + unsafe { Self::initWithBytes_length(Self::alloc(), bytes_ptr, bytes.len()) } } #[cfg(feature = "block")] @@ -94,10 +94,8 @@ impl> Index for NSData { } impl DefaultId for NSData { - type Ownership = Shared; - #[inline] - fn default_id() -> Id { + fn default_id() -> Id { Self::new() } } @@ -119,28 +117,18 @@ impl<'a> IntoIterator for &'a NSData { } } -pub(crate) unsafe fn with_slice(cls: &Class, bytes: &[u8]) -> Id { - let bytes_ptr: *const c_void = bytes.as_ptr().cast(); - unsafe { - msg_send_id![ - msg_send_id![cls, alloc], - initWithBytes: bytes_ptr, - length: bytes.len(), - ] - } -} - #[cfg(feature = "block")] pub(crate) unsafe fn with_vec(cls: &Class, bytes: Vec) -> Id { use core::mem::ManuallyDrop; use block2::{Block, ConcreteBlock}; + use objc2::msg_send_id; let capacity = bytes.capacity(); let dealloc = ConcreteBlock::new(move |bytes: *mut c_void, len: usize| unsafe { // Recreate the Vec and let it drop - let _ = Vec::::from_raw_parts(bytes.cast(), len, capacity); + let _ = >::from_raw_parts(bytes.cast(), len, capacity); }); let dealloc = dealloc.copy(); let dealloc: &Block<(*mut c_void, usize), ()> = &dealloc; diff --git a/crates/icrate/src/Foundation/additions/dictionary.rs b/crates/icrate/src/Foundation/additions/dictionary.rs index 99d72e27b..995d840ff 100644 --- a/crates/icrate/src/Foundation/additions/dictionary.rs +++ b/crates/icrate/src/Foundation/additions/dictionary.rs @@ -7,43 +7,33 @@ use core::ops::Index; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::ptr::{self, NonNull}; -use objc2::rc::{DefaultId, SliceId}; -use objc2::{msg_send, msg_send_id}; +use objc2::msg_send; +use objc2::mutability::IsMutable; +use objc2::rc::DefaultId; +use objc2::runtime::Object; +use super::util; use crate::common::*; -use crate::Foundation::{self, NSDictionary}; - -// TODO: SAFETY -// Approximately same as `NSArray` -unsafe impl Sync for NSDictionary {} -unsafe impl Send for NSDictionary {} - -#[cfg(feature = "Foundation_NSMutableDictionary")] -unsafe impl Sync - for Foundation::NSMutableDictionary -{ -} -#[cfg(feature = "Foundation_NSMutableDictionary")] -unsafe impl Send - for Foundation::NSMutableDictionary -{ -} - -// Approximately same as `NSArray` -impl UnwindSafe for NSDictionary {} -impl RefUnwindSafe for NSDictionary {} - -#[cfg(feature = "Foundation_NSMutableDictionary")] -impl UnwindSafe - for Foundation::NSMutableDictionary -{ -} -// impl RefUnwindSafe for NSMutableDictionary {} +use crate::Foundation::{self, Copyhelper, NSDictionary}; extern_methods!( unsafe impl NSDictionary { #[method_id(new)] - pub fn new() -> Id; + pub fn new() -> Id; + + pub fn from_keys_and_objects(keys: &[&T], mut vals: Vec>) -> Id + where + T: ClassType + Foundation::NSCopying, + T::Mutability: Copyhelper, + { + let count = min(keys.len(), vals.len()); + + let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); + let keys: *mut NonNull = keys.cast(); + let vals = util::id_ptr_cast(vals.as_mut_ptr()); + + unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), vals, keys, count) } + } pub fn len(&self) -> usize { self.count() @@ -57,6 +47,47 @@ extern_methods!( #[method(objectForKey:)] pub fn get(&self, key: &K) -> Option<&V>; + #[doc(alias = "objectForKey:")] + pub fn get_retained(&self, key: &K) -> Option> + where + V: IsIdCloneable, + { + // SAFETY: The object is stored in the dictionary + self.get(key) + .map(|obj| unsafe { util::collection_retain_id(obj) }) + } + + /// Returns a mutable reference to the value corresponding to the key. + /// + /// # Examples + /// + #[cfg_attr( + all( + feature = "Foundation_NSMutableDictionary", + feature = "Foundation_NSMutableString" + ), + doc = "```" + )] + #[cfg_attr( + not(all( + feature = "Foundation_NSMutableDictionary", + feature = "Foundation_NSMutableString" + )), + doc = "```ignore" + )] + /// use icrate::Foundation::{NSMutableDictionary, NSMutableString, NSString}; + /// use icrate::ns_string; + /// + /// let mut dict = NSMutableDictionary::new(); + /// dict.insert(NSString::from_str("one"), NSMutableString::new()); + /// println!("{:?}", dict.get_mut(ns_string!("one"))); + /// ``` + #[doc(alias = "objectForKey:")] + #[method(objectForKey:)] + pub fn get_mut(&mut self, key: &K) -> Option<&mut V> + where + V: IsMutable; + #[doc(alias = "getObjects:andKeys:")] pub fn keys(&self) -> Vec<&K> { let len = self.len(); @@ -69,6 +100,21 @@ extern_methods!( } } + // We don't provide `keys_mut`, since keys are expected to be + // immutable. + + #[doc(alias = "getObjects:andKeys:")] + pub fn keys_retained(&self) -> Vec> + where + K: IsIdCloneable, + { + // SAFETY: The keys are stored in the array + self.keys() + .into_iter() + .map(|obj| unsafe { util::collection_retain_id(obj) }) + .collect() + } + #[doc(alias = "getObjects:andKeys:")] pub fn values(&self) -> Vec<&V> { let len = self.len(); @@ -81,6 +127,59 @@ extern_methods!( } } + #[doc(alias = "getObjects:andKeys:")] + pub fn values_retained(&self) -> Vec> + where + V: IsIdCloneable, + { + // SAFETY: The values are stored in the array + self.values() + .into_iter() + .map(|obj| unsafe { util::collection_retain_id(obj) }) + .collect() + } + + /// Returns a vector of mutable references to the values in the dictionary. + /// + /// # Examples + /// + #[cfg_attr( + all( + feature = "Foundation_NSMutableDictionary", + feature = "Foundation_NSMutableString" + ), + doc = "```" + )] + #[cfg_attr( + not(all( + feature = "Foundation_NSMutableDictionary", + feature = "Foundation_NSMutableString" + )), + doc = "```ignore" + )] + /// use icrate::Foundation::{NSMutableDictionary, NSMutableString, NSString}; + /// + /// let mut dict = NSMutableDictionary::new(); + /// dict.insert(NSString::from_str("one"), NSMutableString::from_str("two")); + /// for val in dict.values_mut() { + /// println!("{:?}", val); + /// } + /// ``` + #[doc(alias = "getObjects:andKeys:")] + pub fn values_mut(&mut self) -> Vec<&mut V> + where + V: IsMutable, + { + let len = self.len(); + let mut vals: Vec> = Vec::with_capacity(len); + unsafe { + #[allow(deprecated)] + self.getObjects_andKeys(vals.as_mut_ptr(), ptr::null_mut()); + vals.set_len(len); + mem::transmute(vals) + } + } + #[doc(alias = "getObjects:andKeys:")] pub fn keys_and_objects(&self) -> (Vec<&K>, Vec<&V>) { let len = self.len(); @@ -112,31 +211,12 @@ extern_methods!( Foundation::NSEnumerator2::from_ptr(result) } } - - pub fn from_keys_and_objects(keys: &[&T], vals: Vec>) -> Id - where - T: Foundation::NSCopying, - { - let vals = vals.as_slice_ref(); - - let count = min(keys.len(), vals.len()); - unsafe { - msg_send_id![ - Self::alloc(), - initWithObjects: vals.as_ptr(), - forKeys: keys.as_ptr(), - count: count, - ] - } - } } ); impl DefaultId for NSDictionary { - type Ownership = Shared; - #[inline] - fn default_id() -> Id { + fn default_id() -> Id { Self::new() } } diff --git a/crates/icrate/src/Foundation/additions/enumerator.rs b/crates/icrate/src/Foundation/additions/enumerator.rs index 5918f5e54..1352a4b44 100644 --- a/crates/icrate/src/Foundation/additions/enumerator.rs +++ b/crates/icrate/src/Foundation/additions/enumerator.rs @@ -4,13 +4,13 @@ use core::ptr; use core::slice; use std::os::raw::c_ulong; -use objc2::rc::{Id, Owned}; +use objc2::rc::Id; use objc2::runtime::Object; use objc2::{msg_send, Encode, Encoding, Message, RefEncode}; // TODO: https://doc.rust-lang.org/stable/reference/trait-bounds.html#lifetime-bounds pub struct NSEnumerator2<'a, T: Message> { - id: Id, + id: Id, item: PhantomData<&'a T>, } @@ -19,8 +19,7 @@ impl<'a, T: Message> NSEnumerator2<'a, T> { /// /// # Safety /// - /// The object pointer must be a valid `NSEnumerator2` with `Owned` - /// ownership. + /// The object pointer must be a valid `NSEnumerator2`. pub unsafe fn from_ptr(ptr: *mut Object) -> Self { Self { id: unsafe { Id::retain_autoreleased(ptr) }.unwrap(), @@ -33,7 +32,7 @@ impl<'a, T: Message> Iterator for NSEnumerator2<'a, T> { type Item = &'a T; fn next(&mut self) -> Option<&'a T> { - unsafe { msg_send![&mut self.id, nextObject] } + unsafe { msg_send![&self.id, nextObject] } } } diff --git a/crates/icrate/src/Foundation/additions/exception.rs b/crates/icrate/src/Foundation/additions/exception.rs index a31282dcb..e6ee23521 100644 --- a/crates/icrate/src/Foundation/additions/exception.rs +++ b/crates/icrate/src/Foundation/additions/exception.rs @@ -53,8 +53,8 @@ extern_methods!( /// /// Same as `objc2::exception::throw`. pub unsafe fn raise(&self) -> ! { - // SAFETY: We only create `Shared` NSExceptions, so it is safe to give - // to the place where `@catch` receives it. + // SAFETY: `NSException` is immutable, so it is safe to give to + // the place where `@catch` receives it. unsafe { self.raise_raw() }; // SAFETY: `raise` will throw an exception, or abort if something // unexpected happened. diff --git a/crates/icrate/src/Foundation/additions/lock.rs b/crates/icrate/src/Foundation/additions/lock.rs index 1865e7218..aadfa0718 100644 --- a/crates/icrate/src/Foundation/additions/lock.rs +++ b/crates/icrate/src/Foundation/additions/lock.rs @@ -7,6 +7,6 @@ use crate::Foundation::NSLock; extern_methods!( unsafe impl NSLock { #[method_id(new)] - pub fn new() -> Id; + pub fn new() -> Id; } ); diff --git a/crates/icrate/src/Foundation/additions/mod.rs b/crates/icrate/src/Foundation/additions/mod.rs index d9ff70a47..e4e656119 100644 --- a/crates/icrate/src/Foundation/additions/mod.rs +++ b/crates/icrate/src/Foundation/additions/mod.rs @@ -1,5 +1,4 @@ pub use self::comparison_result::NSComparisonResult; -pub use self::copying::{NSCopying, NSMutableCopying}; pub use self::enumerator::{NSEnumerator2, NSFastEnumeration2, NSFastEnumerator2}; pub use self::geometry::{ CGFloat, CGPoint, CGRect, CGSize, NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge, NSPoint, @@ -16,7 +15,6 @@ mod array; mod attributed_string; mod bundle; mod comparison_result; -mod copying; mod data; mod dictionary; mod enumerator; @@ -36,5 +34,6 @@ mod range; mod set; mod string; mod thread; +mod util; mod uuid; mod value; diff --git a/crates/icrate/src/Foundation/additions/mutable_array.rs b/crates/icrate/src/Foundation/additions/mutable_array.rs index 1b05f640a..e042f5702 100644 --- a/crates/icrate/src/Foundation/additions/mutable_array.rs +++ b/crates/icrate/src/Foundation/additions/mutable_array.rs @@ -1,55 +1,64 @@ #![cfg(feature = "Foundation_NSMutableArray")] use alloc::vec::Vec; use core::cmp::Ordering; -use core::ffi::c_void; use core::ops::{Index, IndexMut}; -use core::ptr::NonNull; -use objc2::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId}; -use objc2::{extern_methods, Message}; +use objc2::mutability::{IsMutable, IsRetainable}; +use objc2::rc::DefaultId; -use super::array::with_objects; +use super::util; +use crate::common::*; use crate::Foundation::{self, NSArray, NSComparisonResult, NSInteger, NSMutableArray}; extern_methods!( - /// Generic creation methods. - unsafe impl NSMutableArray { - // SAFETY: Same as `NSArray::new`, except mutable arrays are always - // unique. + /// Creation methods. + unsafe impl NSMutableArray { #[method_id(new)] - pub fn new() -> Id; + pub fn new() -> Id; - pub fn from_vec(vec: Vec>) -> Id { - // SAFETY: Same as `NSArray::from_vec`, except mutable arrays are - // always unique. - unsafe { with_objects(vec.as_slice_ref()) } + pub fn from_vec(mut vec: Vec>) -> Id { + let len = vec.len(); + let ptr = util::id_ptr_cast(vec.as_mut_ptr()); + // SAFETY: Same as `NSArray::from_vec`. + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } + } + + pub fn from_id_slice(slice: &[Id]) -> Id + where + T: IsIdCloneable, + { + let len = slice.len(); + let ptr = util::id_ptr_cast_const(slice.as_ptr()); + // SAFETY: Same as `NSArray::from_id_slice` + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } } - } - /// Creation methods that produce shared arrays. - unsafe impl NSMutableArray { - pub fn from_slice(slice: &[Id]) -> Id { - // SAFETY: Same as `NSArray::from_slice`, except mutable arrays are - // always unique. - unsafe { with_objects(slice.as_slice_ref()) } + pub fn from_slice(slice: &[&T]) -> Id + where + T: IsRetainable, + { + let len = slice.len(); + let ptr = util::ref_ptr_cast_const(slice.as_ptr()); + // SAFETY: Same as `NSArray::from_slice`. + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } } } - /// Generic accessor methods. - unsafe impl NSMutableArray { + /// Accessor methods. + unsafe impl NSMutableArray { #[doc(alias = "addObject:")] - pub fn push(&mut self, obj: Id) { - // SAFETY: The object has correct ownership. + pub fn push(&mut self, obj: Id) { + // SAFETY: We've consumed ownership of the object. unsafe { self.addObject(&obj) } } #[doc(alias = "insertObject:atIndex:")] - pub fn insert(&mut self, index: usize, obj: Id) { + pub fn insert(&mut self, index: usize, obj: Id) { // TODO: Replace this check with catching the thrown NSRangeException let len = self.len(); if index < len { - // SAFETY: The object has correct ownership, and the index is - // checked to be in bounds. + // SAFETY: We've consumed ownership of the object, and the + // index is checked to be in bounds. unsafe { self.insertObject_atIndex(&obj, index) } } else { panic!( @@ -60,37 +69,37 @@ extern_methods!( } #[doc(alias = "replaceObjectAtIndex:withObject:")] - pub fn replace(&mut self, index: usize, obj: Id) -> Id { - let old_obj = unsafe { - let obj = self.get(index).unwrap(); - Id::retain_autoreleased(obj as *const T as *mut T).unwrap_unchecked() - }; - // SAFETY: The object has correct ownership. - unsafe { self.replaceObjectAtIndex_withObject(index, &obj) }; - old_obj + pub fn replace(&mut self, index: usize, obj: Id) -> Result, Id> { + if let Some(old_obj) = self.get(index) { + // SAFETY: We remove the object from the array below. + let old_obj = unsafe { util::mutable_collection_retain_removed_id(old_obj) }; + // SAFETY: The index is checked to be in bounds, and we've + // consumed ownership of the new object. + unsafe { self.replaceObjectAtIndex_withObject(index, &obj) }; + Ok(old_obj) + } else { + Err(obj) + } } #[doc(alias = "removeObjectAtIndex:")] - pub fn remove(&mut self, index: usize) -> Id { - let obj = if let Some(obj) = self.get(index) { - unsafe { Id::retain_autoreleased(obj as *const T as *mut T).unwrap_unchecked() } - } else { - panic!("removal index should be < len"); - }; + pub fn remove(&mut self, index: usize) -> Option> { + let obj = self.get(index)?; + // SAFETY: We remove the object from the array below. + let obj = unsafe { util::mutable_collection_retain_removed_id(obj) }; // SAFETY: The index is checked to be in bounds. unsafe { self.removeObjectAtIndex(index) }; - obj + Some(obj) } #[doc(alias = "removeLastObject")] - pub fn pop(&mut self) -> Option> { - self.last() - .map(|obj| unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() }) - .map(|obj| { - // SAFETY: `Self::last` just checked that there is an object - unsafe { self.removeLastObject() }; - obj - }) + pub fn pop(&mut self) -> Option> { + let obj = self.last()?; + // SAFETY: We remove the object from the array below. + let obj = unsafe { util::mutable_collection_retain_removed_id(obj) }; + // SAFETY: Just checked that there is an object. + unsafe { self.removeLastObject() }; + Some(obj) } #[doc(alias = "sortUsingFunction:context:")] @@ -124,16 +133,26 @@ extern_methods!( // Keep the closure alive until the function has run. drop(closure); } + + pub fn into_vec(array: Id) -> Vec> { + // SAFETY: We've consumed the array, so taking ownership of the + // returned values is safe. + array + .to_vec() + .into_iter() + .map(|obj| unsafe { util::mutable_collection_retain_removed_id(obj) }) + .collect() + } } ); -unsafe impl Foundation::NSFastEnumeration2 for NSMutableArray { +unsafe impl Foundation::NSFastEnumeration2 for NSMutableArray { type Item = T; } -impl<'a, T: Message, O: Ownership> IntoIterator for &'a NSMutableArray { +impl<'a, T: Message> IntoIterator for &'a NSMutableArray { type Item = &'a T; - type IntoIter = Foundation::NSFastEnumerator2<'a, NSMutableArray>; + type IntoIter = Foundation::NSFastEnumerator2<'a, NSMutableArray>; fn into_iter(self) -> Self::IntoIter { use Foundation::NSFastEnumeration2; @@ -141,14 +160,14 @@ impl<'a, T: Message, O: Ownership> IntoIterator for &'a NSMutableArray { } } -impl Extend> for NSMutableArray { - fn extend>>(&mut self, iter: I) { +impl Extend> for NSMutableArray { + fn extend>>(&mut self, iter: I) { let iterator = iter.into_iter(); iterator.for_each(move |item| self.push(item)); } } -impl Index for NSMutableArray { +impl Index for NSMutableArray { type Output = T; fn index(&self, index: usize) -> &T { @@ -156,17 +175,15 @@ impl Index for NSMutableArray { } } -impl IndexMut for NSMutableArray { +impl IndexMut for NSMutableArray { fn index_mut(&mut self, index: usize) -> &mut T { self.get_mut(index).unwrap() } } -impl DefaultId for NSMutableArray { - type Ownership = Owned; - +impl DefaultId for NSMutableArray { #[inline] - fn default_id() -> Id { + fn default_id() -> Id { Self::new() } } diff --git a/crates/icrate/src/Foundation/additions/mutable_attributed_string.rs b/crates/icrate/src/Foundation/additions/mutable_attributed_string.rs index da16f4ae8..5e55909e9 100644 --- a/crates/icrate/src/Foundation/additions/mutable_attributed_string.rs +++ b/crates/icrate/src/Foundation/additions/mutable_attributed_string.rs @@ -1,7 +1,7 @@ #![cfg(feature = "Foundation_NSMutableAttributedString")] -use objc2::rc::{DefaultId, Id, Owned}; -use objc2::{extern_methods, msg_send_id, ClassType}; +use objc2::rc::DefaultId; +use crate::common::*; use crate::Foundation::{self, NSAttributedString, NSMutableAttributedString}; extern_methods!( @@ -9,28 +9,26 @@ extern_methods!( unsafe impl NSMutableAttributedString { /// Construct an empty mutable attributed string. #[method_id(new)] - pub fn new() -> Id; + pub fn new() -> Id; // TODO: new_with_attributes #[doc(alias = "initWithString:")] #[cfg(feature = "Foundation_NSString")] - pub fn from_nsstring(string: &Foundation::NSString) -> Id { - unsafe { msg_send_id![Self::alloc(), initWithString: string] } + pub fn from_nsstring(string: &Foundation::NSString) -> Id { + Self::initWithString(Self::alloc(), string) } #[doc(alias = "initWithAttributedString:")] - pub fn from_attributed_nsstring(attributed_string: &NSAttributedString) -> Id { - unsafe { msg_send_id![Self::alloc(), initWithAttributedString: attributed_string] } + pub fn from_attributed_nsstring(attributed_string: &NSAttributedString) -> Id { + Self::initWithAttributedString(Self::alloc(), attributed_string) } } ); impl DefaultId for NSMutableAttributedString { - type Ownership = Owned; - #[inline] - fn default_id() -> Id { + fn default_id() -> Id { Self::new() } } diff --git a/crates/icrate/src/Foundation/additions/mutable_data.rs b/crates/icrate/src/Foundation/additions/mutable_data.rs index 2f10920bb..2fa596826 100644 --- a/crates/icrate/src/Foundation/additions/mutable_data.rs +++ b/crates/icrate/src/Foundation/additions/mutable_data.rs @@ -1,31 +1,29 @@ #![cfg(feature = "Foundation_NSMutableData")] #[cfg(feature = "block")] use alloc::vec::Vec; -use core::ffi::c_void; use core::ops::{Index, IndexMut, Range}; -use core::ptr::NonNull; use core::slice::{self, SliceIndex}; use std::io; -use objc2::rc::{DefaultId, Id, Owned}; -use objc2::{extern_methods, ClassType}; +use objc2::rc::DefaultId; -use super::data::with_slice; +use crate::common::*; use crate::Foundation::{NSMutableData, NSRange}; extern_methods!( /// Creation methods unsafe impl NSMutableData { #[method_id(new)] - pub fn new() -> Id; + pub fn new() -> Id; - pub fn with_bytes(bytes: &[u8]) -> Id { - unsafe { Id::from_shared(Id::cast(with_slice(Self::class(), bytes))) } + pub fn with_bytes(bytes: &[u8]) -> Id { + let bytes_ptr = bytes.as_ptr() as *mut c_void; + unsafe { Self::initWithBytes_length(Self::alloc(), bytes_ptr, bytes.len()) } } #[cfg(feature = "block")] - pub fn from_vec(bytes: Vec) -> Id { - unsafe { Id::from_shared(Id::cast(super::data::with_vec(Self::class(), bytes))) } + pub fn from_vec(bytes: Vec) -> Id { + unsafe { Id::cast(super::data::with_vec(Self::class(), bytes)) } } } @@ -97,7 +95,7 @@ impl> IndexMut for NSMutableData { } } -// impl FromIterator for Id { +// impl FromIterator for Id { // fn from_iter>(iter: T) -> Self { // let iter = iter.into_iter(); // let (lower, _) = iter.size_hint(); @@ -145,10 +143,8 @@ impl io::Write for NSMutableData { } impl DefaultId for NSMutableData { - type Ownership = Owned; - #[inline] - fn default_id() -> Id { + fn default_id() -> Id { Self::new() } } diff --git a/crates/icrate/src/Foundation/additions/mutable_dictionary.rs b/crates/icrate/src/Foundation/additions/mutable_dictionary.rs index 20e21692e..b23f31d01 100644 --- a/crates/icrate/src/Foundation/additions/mutable_dictionary.rs +++ b/crates/icrate/src/Foundation/additions/mutable_dictionary.rs @@ -1,13 +1,17 @@ #![cfg(feature = "Foundation_NSMutableDictionary")] use alloc::vec::Vec; +use core::cmp::min; +use core::mem; use core::ops::{Index, IndexMut}; use core::ptr; -use objc2::msg_send_id; +use objc2::mutability::{IsMutable, IsRetainable}; use objc2::rc::DefaultId; +use objc2::runtime::Object; +use super::util; use crate::common::*; -use crate::Foundation::{self, NSDictionary, NSMutableDictionary}; +use crate::Foundation::{self, Copyhelper, NSDictionary, NSMutableDictionary}; extern_methods!( unsafe impl NSMutableDictionary { @@ -20,11 +24,8 @@ extern_methods!( /// /// let dict = NSMutableDictionary::::new(); /// ``` - // SAFETY: - // Mutable dictionaries are always unique, so it's safe to return - // `Id` #[method_id(new)] - pub fn new() -> Id; + pub fn new() -> Id; /// Creates an [`NSMutableDictionary`] from a slice of keys and a /// vector of values. @@ -43,62 +44,20 @@ extern_methods!( /// vec![NSObject::new(), NSObject::new(), NSObject::new()], /// ); /// ``` - pub fn from_keys_and_objects(keys: &[&T], vals: Vec>) -> Id + pub fn from_keys_and_objects(keys: &[&T], mut vals: Vec>) -> Id where - T: Foundation::NSCopying, + T: ClassType + Foundation::NSCopying, + T::Mutability: Copyhelper, { - let mut dict = NSMutableDictionary::new(); - dict.setDictionary(&*NSDictionary::from_keys_and_objects(keys, vals)); - dict - } - - /// Returns a mutable reference to the value corresponding to the key. - /// - /// # Examples - /// - /// ``` - /// use icrate::Foundation::{NSMutableDictionary, NSObject, NSString}; - /// use icrate::ns_string; - /// - /// let mut dict = NSMutableDictionary::new(); - /// dict.insert(NSString::from_str("one"), NSObject::new()); - /// println!("{:?}", dict.get_mut(ns_string!("one"))); - /// ``` - #[doc(alias = "objectForKey:")] - #[method(objectForKey:)] - pub fn get_mut(&mut self, key: &K) -> Option<&mut V>; + let count = min(keys.len(), vals.len()); - #[method(getObjects:andKeys:)] - unsafe fn get_objects_and_keys(&self, objects: *mut &mut V, keys: *mut &K); + let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); + let keys: *mut NonNull = keys.cast(); + let vals = util::id_ptr_cast(vals.as_mut_ptr()); - /// Returns a vector of mutable references to the values in the dictionary. - /// - /// # Examples - /// - /// ``` - /// use icrate::Foundation::{NSMutableDictionary, NSObject, NSString}; - /// - /// let mut dict = NSMutableDictionary::new(); - /// dict.insert(NSString::from_str("one"), NSObject::new()); - /// for val in dict.values_mut() { - /// println!("{:?}", val); - /// } - /// ``` - #[doc(alias = "getObjects:andKeys:")] - pub fn values_mut(&mut self) -> Vec<&mut V> { - let len = self.len(); - let mut vals: Vec<&mut V> = Vec::with_capacity(len); - // SAFETY: `vals` is not null - unsafe { - self.get_objects_and_keys(vals.as_mut_ptr(), ptr::null_mut()); - vals.set_len(len); - } - vals + unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), vals, keys, count) } } - #[method(setObject:forKey:)] - fn set_object_for_key(&mut self, object: &V, key: &K); - /// Inserts a key-value pair into the dictionary. /// /// If the dictionary did not have this key present, None is returned. @@ -114,15 +73,17 @@ extern_methods!( /// dict.insert(NSString::from_str("one"), NSObject::new()); /// ``` #[doc(alias = "setObject:forKey:")] - pub fn insert(&mut self, key: Id, value: Id) -> Option> { - // SAFETY: - // `obj` is a reference to a value in the dictionary so it's safe - // to cast it to a pointer and pass it to `Id::retain_autoreleased` - let obj = self.get(&*key).map(|obj| unsafe { - Id::retain_autoreleased(obj as *const V as *mut V).unwrap_unchecked() - }); - self.set_object_for_key(&*value, &*key); - obj + pub fn insert(&mut self, key: Id, value: Id) -> Option> { + // SAFETY: We remove the object from the dictionary below + let old_obj = self + .get(&key) + .map(|old_obj| unsafe { util::mutable_collection_retain_removed_id(old_obj) }); + + // SAFETY: It is always safe to transmute an `Id` to `Object`. + let key: Id = unsafe { Id::cast(key) }; + // SAFETY: We have ownership over both the key and the value. + unsafe { self.setObject_forKey(&value, &key) }; + old_obj } /// Removes a key from the dictionary, returning the value at the key @@ -140,15 +101,13 @@ extern_methods!( /// assert!(dict.is_empty()); /// ``` #[doc(alias = "removeObjectForKey:")] - pub fn remove(&mut self, key: &K) -> Option> { - // SAFETY: - // `obj` is a reference to a value in the dictionary so it's safe - // to cast it to a pointer and pass it to `Id::retain_autoreleased` - let obj = self.get(key).map(|obj| unsafe { - Id::retain_autoreleased(obj as *const V as *mut V).unwrap_unchecked() - }); + pub fn remove(&mut self, key: &K) -> Option> { + // SAFETY: We remove the object from the dictionary below + let old_obj = self + .get(key) + .map(|old_obj| unsafe { util::mutable_collection_retain_removed_id(old_obj) }); self.removeObjectForKey(key); - obj + old_obj } /// Returns an [`NSArray`] containing the dictionary's values, @@ -168,10 +127,10 @@ extern_methods!( /// println!("{:?}", array); /// ``` #[cfg(feature = "Foundation_NSArray")] - pub fn into_values_array( - dict: Id, - ) -> Id, Shared> { - unsafe { msg_send_id![&dict, allValues] } + pub fn into_values_array(this: Id) -> Id> { + // SAFETY: We've consumed the dictionary, so getting an array from + // it is safe. + unsafe { this.allValues() } } } ); @@ -188,17 +147,15 @@ impl<'a, K: Message, V: Message> Index<&'a K> for NSMutableDictionary { } } -impl<'a, K: Message, V: Message> IndexMut<&'a K> for NSMutableDictionary { +impl<'a, K: Message, V: IsMutable> IndexMut<&'a K> for NSMutableDictionary { fn index_mut<'s>(&'s mut self, index: &'a K) -> &'s mut V { self.get_mut(index).unwrap() } } impl DefaultId for NSMutableDictionary { - type Ownership = Owned; - #[inline] - fn default_id() -> Id { + fn default_id() -> Id { Self::new() } } diff --git a/crates/icrate/src/Foundation/additions/mutable_set.rs b/crates/icrate/src/Foundation/additions/mutable_set.rs index 402c6ccdb..a2be89e2b 100644 --- a/crates/icrate/src/Foundation/additions/mutable_set.rs +++ b/crates/icrate/src/Foundation/additions/mutable_set.rs @@ -1,14 +1,15 @@ #![cfg(feature = "Foundation_NSMutableSet")] use alloc::vec::Vec; -use objc2::rc::{DefaultId, SliceId}; +use objc2::mutability::IsRetainable; +use objc2::rc::DefaultId; -use super::set::with_objects; +use super::util; use crate::common::*; use crate::Foundation::{self, NSMutableSet}; extern_methods!( - unsafe impl NSMutableSet { + unsafe impl NSMutableSet { /// Creates an empty [`NSMutableSet`]. /// /// # Examples @@ -18,10 +19,8 @@ extern_methods!( /// /// let set = NSMutableSet::::new(); /// ``` - // SAFETY: - // Same as `NSSet::new`, except mutable sets are always unique. #[method_id(new)] - pub fn new() -> Id; + pub fn new() -> Id; /// Creates an [`NSMutableSet`] from a vector. /// @@ -33,11 +32,41 @@ extern_methods!( /// let strs = ["one", "two", "three"].map(NSString::from_str).to_vec(); /// let set = NSMutableSet::from_vec(strs); /// ``` - pub fn from_vec(vec: Vec>) -> Id { - // SAFETY: - // We always return `Id, Owned>` because mutable - // sets are always unique. - unsafe { with_objects(vec.as_slice_ref()) } + pub fn from_vec(mut vec: Vec>) -> Id { + let len = vec.len(); + let ptr = util::id_ptr_cast(vec.as_mut_ptr()); + // SAFETY: Same as `NSArray::from_vec`. + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } + } + + /// Creates an [`NSMutableSet`] from a slice of `Id`s. + /// + /// # Examples + /// + /// ``` + /// use icrate::Foundation::{NSMutableSet, NSString}; + /// + /// let strs = ["one", "two", "three"].map(NSString::from_str); + /// let set = NSMutableSet::from_id_slice(&strs); + /// ``` + pub fn from_id_slice(slice: &[Id]) -> Id + where + T: IsIdCloneable, + { + let len = slice.len(); + let ptr = util::id_ptr_cast_const(slice.as_ptr()); + // SAFETY: Same as `NSArray::from_id_slice` + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } + } + + pub fn from_slice(slice: &[&T]) -> Id + where + T: IsRetainable, + { + let len = slice.len(); + let ptr = util::ref_ptr_cast_const(slice.as_ptr()); + // SAFETY: Same as `NSArray::from_slice`. + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } } /// Returns a [`Vec`] containing the set's elements, consuming the set. @@ -56,42 +85,18 @@ extern_methods!( /// let vec = NSMutableSet::into_vec(set); /// assert_eq!(vec.len(), 3); /// ``` - pub fn into_vec(set: Id) -> Vec> { + pub fn into_vec(set: Id) -> Vec> { + // SAFETY: Same as `NSMutableArray::into_vec` set.into_iter() - .map(|obj| unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() }) + .map(|obj| unsafe { util::mutable_collection_retain_removed_id(obj) }) .collect() } } - unsafe impl NSMutableSet { - /// Creates an [`NSMutableSet`] from a slice. - /// - /// # Examples - /// - /// ``` - /// use icrate::Foundation::{NSMutableSet, NSString}; - /// - /// let strs = ["one", "two", "three"].map(NSString::from_str); - /// let set = NSMutableSet::from_slice(&strs); - /// ``` - pub fn from_slice(slice: &[Id]) -> Id { - // SAFETY: - // Taking `&T` would not be sound, since the `&T` could come from - // an `Id` that would now no longer be owned! - // - // We always return `Id, Owned>` because - // the elements are shared and mutable sets are always unique. - unsafe { with_objects(slice.as_slice_ref()) } - } - } - // We're explicit about `T` being `PartialEq` for these methods because the // set compares the input value with elements in the set // For comparison: Rust's HashSet requires similar methods to be `Hash` + `Eq` - unsafe impl NSMutableSet { - #[method(addObject:)] - fn add_object(&mut self, value: &T); - + unsafe impl NSMutableSet { /// Adds a value to the set. Returns whether the value was /// newly inserted. /// @@ -107,21 +112,13 @@ extern_methods!( /// assert_eq!(set.len(), 1); /// ``` #[doc(alias = "addObject:")] - pub fn insert(&mut self, value: Id) -> bool { - // SAFETY: - // We take `Id` instead of `&T` because `&T` could be a - // reference to an owned object which would cause us to have a copy - // of an owned object in our set. By taking `Id`, we force the - // caller to transfer ownership of the value to us, making it safe - // to insert the owned object into the set. + pub fn insert(&mut self, value: Id) -> bool { let contains_value = self.contains(&value); - self.add_object(&*value); + // SAFETY: We've consumed ownership of the object. + unsafe { self.addObject(&value) }; !contains_value } - #[method(removeObject:)] - fn remove_object(&mut self, value: &T); - /// Removes a value from the set. Returns whether the value was present /// in the set. /// @@ -140,19 +137,19 @@ extern_methods!( #[doc(alias = "removeObject:")] pub fn remove(&mut self, value: &T) -> bool { let contains_value = self.contains(value); - self.remove_object(value); + unsafe { self.removeObject(value) }; contains_value } } ); -unsafe impl Foundation::NSFastEnumeration2 for NSMutableSet { +unsafe impl Foundation::NSFastEnumeration2 for NSMutableSet { type Item = T; } -impl<'a, T: Message, O: Ownership> IntoIterator for &'a NSMutableSet { +impl<'a, T: Message> IntoIterator for &'a NSMutableSet { type Item = &'a T; - type IntoIter = Foundation::NSFastEnumerator2<'a, NSMutableSet>; + type IntoIter = Foundation::NSFastEnumerator2<'a, NSMutableSet>; fn into_iter(self) -> Self::IntoIter { use Foundation::NSFastEnumeration2; @@ -160,19 +157,17 @@ impl<'a, T: Message, O: Ownership> IntoIterator for &'a NSMutableSet { } } -impl Extend> for NSMutableSet { - fn extend>>(&mut self, iter: I) { +impl Extend> for NSMutableSet { + fn extend>>(&mut self, iter: I) { for item in iter { self.insert(item); } } } -impl DefaultId for NSMutableSet { - type Ownership = Owned; - +impl DefaultId for NSMutableSet { #[inline] - fn default_id() -> Id { + fn default_id() -> Id { Self::new() } } diff --git a/crates/icrate/src/Foundation/additions/mutable_string.rs b/crates/icrate/src/Foundation/additions/mutable_string.rs index 59328a068..1b7aa0696 100644 --- a/crates/icrate/src/Foundation/additions/mutable_string.rs +++ b/crates/icrate/src/Foundation/additions/mutable_string.rs @@ -14,12 +14,12 @@ extern_methods!( unsafe impl NSMutableString { /// Construct an empty [`NSMutableString`]. #[method_id(new)] - pub fn new() -> Id; + pub fn new() -> Id; /// Creates a new [`NSMutableString`] by copying the given string slice. #[doc(alias = "initWithBytes:length:encoding:")] #[allow(clippy::should_implement_trait)] // Not really sure of a better name - pub fn from_str(string: &str) -> Id { + pub fn from_str(string: &str) -> Id { unsafe { let obj = super::string::from_str(Self::class(), string); Id::new(obj.cast()).unwrap() @@ -29,10 +29,8 @@ extern_methods!( ); impl DefaultId for NSMutableString { - type Ownership = Owned; - #[inline] - fn default_id() -> Id { + fn default_id() -> Id { Self::new() } } diff --git a/crates/icrate/src/Foundation/additions/set.rs b/crates/icrate/src/Foundation/additions/set.rs index 5d2224bdc..cda85ad14 100644 --- a/crates/icrate/src/Foundation/additions/set.rs +++ b/crates/icrate/src/Foundation/additions/set.rs @@ -3,53 +3,16 @@ use alloc::vec::Vec; use core::fmt; use core::panic::{RefUnwindSafe, UnwindSafe}; -use objc2::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId}; -use objc2::{extern_methods, msg_send, msg_send_id, ClassType, Message}; +use objc2::msg_send; +use objc2::mutability::IsRetainable; +use objc2::rc::DefaultId; +use super::util; +use crate::common::*; use crate::Foundation::{self, NSSet}; -// SAFETY: Same as NSArray -unsafe impl Sync for NSSet {} -unsafe impl Send for NSSet {} -unsafe impl Sync for NSSet {} -unsafe impl Send for NSSet {} - -#[cfg(feature = "Foundation_NSMutableSet")] -unsafe impl Sync for Foundation::NSMutableSet {} -#[cfg(feature = "Foundation_NSMutableSet")] -unsafe impl Send for Foundation::NSMutableSet {} -#[cfg(feature = "Foundation_NSMutableSet")] -unsafe impl Sync for Foundation::NSMutableSet {} -#[cfg(feature = "Foundation_NSMutableSet")] -unsafe impl Send for Foundation::NSMutableSet {} - -// SAFETY: Same as NSArray -impl RefUnwindSafe for NSSet {} -impl UnwindSafe for NSSet {} -impl UnwindSafe for NSSet {} - -#[cfg(feature = "Foundation_NSMutableSet")] -impl RefUnwindSafe for Foundation::NSMutableSet {} -#[cfg(feature = "Foundation_NSMutableSet")] -impl UnwindSafe for Foundation::NSMutableSet {} -#[cfg(feature = "Foundation_NSMutableSet")] -impl UnwindSafe for Foundation::NSMutableSet {} - -#[track_caller] -pub(crate) unsafe fn with_objects( - objects: &[&T], -) -> Id { - unsafe { - msg_send_id![ - R::alloc(), - initWithObjects: objects.as_ptr(), - count: objects.len() - ] - } -} - extern_methods!( - unsafe impl NSSet { + unsafe impl NSSet { /// Creates an empty [`NSSet`]. /// /// # Examples @@ -59,14 +22,8 @@ extern_methods!( /// /// let set = NSSet::::new(); /// ``` - // SAFETY: - // - `new` may not create a new object, but instead return a shared - // instance. We remedy this by returning `Id`. - // - `O` don't actually matter here! E.g. `NSSet` is - // perfectly legal, since the set doesn't have any elements, and - // hence the notion of ownership over the elements is void. #[method_id(new)] - pub fn new() -> Id; + pub fn new() -> Id; /// Creates an [`NSSet`] from a vector. /// @@ -78,13 +35,41 @@ extern_methods!( /// let strs = ["one", "two", "three"].map(NSString::from_str).to_vec(); /// let set = NSSet::from_vec(strs); /// ``` - pub fn from_vec(vec: Vec>) -> Id { - // SAFETY: - // When we know that we have ownership over the variables, we also - // know that there cannot be another set in existence with the same - // objects, so `Id, Owned>` is safe to return when - // we receive `Vec>`. - unsafe { with_objects(vec.as_slice_ref()) } + pub fn from_vec(mut vec: Vec>) -> Id { + let len = vec.len(); + let ptr = util::id_ptr_cast(vec.as_mut_ptr()); + // SAFETY: Same as `NSArray::from_vec`. + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } + } + + /// Creates an [`NSSet`] from a slice of `Id`s. + /// + /// # Examples + /// + /// ``` + /// use icrate::Foundation::{NSSet, NSString}; + /// + /// let strs = ["one", "two", "three"].map(NSString::from_str); + /// let set = NSSet::from_id_slice(&strs); + /// ``` + pub fn from_id_slice(slice: &[Id]) -> Id + where + T: IsIdCloneable, + { + let len = slice.len(); + let ptr = util::id_ptr_cast_const(slice.as_ptr()); + // SAFETY: Same as `NSArray::from_id_slice` + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } + } + + pub fn from_slice(slice: &[&T]) -> Id + where + T: IsRetainable, + { + let len = slice.len(); + let ptr = util::ref_ptr_cast_const(slice.as_ptr()); + // SAFETY: Same as `NSArray::from_slice`. + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } } /// Returns the number of elements in the set. @@ -95,7 +80,7 @@ extern_methods!( /// use icrate::Foundation::{NSSet, NSString}; /// /// let strs = ["one", "two", "three"].map(NSString::from_str); - /// let set = NSSet::from_slice(&strs); + /// let set = NSSet::from_id_slice(&strs); /// assert_eq!(set.len(), 3); /// ``` #[doc(alias = "count")] @@ -126,7 +111,7 @@ extern_methods!( /// use icrate::Foundation::{NSSet, NSString}; /// /// let strs = ["one", "two", "three"].map(NSString::from_str); - /// let set = NSSet::from_slice(&strs); + /// let set = NSSet::from_id_slice(&strs); /// let any = set.get_any().unwrap(); /// assert!(any == &*strs[0] || any == &*strs[1] || any == &*strs[2]); /// ``` @@ -142,7 +127,7 @@ extern_methods!( /// use icrate::Foundation::{NSSet, NSString}; /// /// let strs = ["one", "two", "three"].map(NSString::from_str); - /// let set = NSSet::from_slice(&strs); + /// let set = NSSet::from_id_slice(&strs); /// for s in set.iter() { /// println!("{s}"); /// } @@ -156,7 +141,7 @@ extern_methods!( } } - /// Returns a [`Vec`] containing the set's elements, consuming the set. + /// Returns a [`Vec`] containing the set's elements. /// /// # Examples /// @@ -169,35 +154,22 @@ extern_methods!( /// NSMutableString::from_str("three"), /// ]; /// let set = NSSet::from_vec(strs); - /// let vec = NSSet::into_vec(set); + /// let vec = set.to_vec(); /// assert_eq!(vec.len(), 3); /// ``` - pub fn into_vec(set: Id) -> Vec> { - set.into_iter() - .map(|obj| unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() }) - .collect() + pub fn to_vec(&self) -> Vec<&T> { + self.into_iter().collect() } - } - unsafe impl NSSet { - /// Creates an [`NSSet`] from a slice. - /// - /// # Examples - /// - /// ``` - /// use icrate::Foundation::{NSSet, NSString}; - /// - /// let strs = ["one", "two", "three"].map(NSString::from_str); - /// let set = NSSet::from_slice(&strs); - /// ``` - pub fn from_slice(slice: &[Id]) -> Id { - // SAFETY: - // Taking `&T` would not be sound, since the `&T` could come from - // an `Id` that would now no longer be owned! - // - // We always return `Id, Shared>` because the - // elements are shared. - unsafe { with_objects(slice.as_slice_ref()) } + #[doc(alias = "getObjects:range:")] + pub fn to_vec_retained(&self) -> Vec> + where + T: IsIdCloneable, + { + // SAFETY: The objects are stored in the set + self.into_iter() + .map(|obj| unsafe { util::collection_retain_id(obj) }) + .collect() } /// Returns an [`NSArray`] containing the set's elements, or an empty @@ -211,15 +183,22 @@ extern_methods!( /// use icrate::Foundation::{NSNumber, NSSet, NSString}; /// /// let nums = [1, 2, 3]; - /// let set = NSSet::from_slice(&nums.map(NSNumber::new_i32)); + /// let set = NSSet::from_id_slice(&nums.map(NSNumber::new_i32)); /// /// assert_eq!(set.to_array().len(), 3); /// assert!(set.to_array().iter().all(|i| nums.contains(&i.as_i32()))); /// ``` #[doc(alias = "allObjects")] #[cfg(feature = "Foundation_NSArray")] - pub fn to_array(&self) -> Id, Shared> { - // SAFETY: The set's elements are shared + pub fn to_array(&self) -> Id> + where + T: IsIdCloneable, + { + // SAFETY: The `T: IsIdCloneable` bound ensures that it is safe to + // create what is effectively a copy from an `&self` reference. + // + // Could be implemented as: + // NSArray::from_vec(self.to_vec_retained()) unsafe { self.allObjects() } } } @@ -227,7 +206,7 @@ extern_methods!( // We're explicit about `T` being `PartialEq` for these methods because the // set compares the input value(s) with elements in the set // For comparison: Rust's HashSet requires similar methods to be `Hash` + `Eq` - unsafe impl NSSet { + unsafe impl NSSet { /// Returns `true` if the set contains a value. /// /// # Examples @@ -237,7 +216,7 @@ extern_methods!( /// use icrate::ns_string; /// /// let strs = ["one", "two", "three"].map(NSString::from_str); - /// let set = NSSet::from_slice(&strs); + /// let set = NSSet::from_id_slice(&strs); /// assert!(set.contains(ns_string!("one"))); /// ``` #[doc(alias = "containsObject:")] @@ -255,7 +234,7 @@ extern_methods!( /// use icrate::ns_string; /// /// let strs = ["one", "two", "three"].map(NSString::from_str); - /// let set = NSSet::from_slice(&strs); + /// let set = NSSet::from_id_slice(&strs); /// assert_eq!(set.get(ns_string!("one")), Some(&*strs[0])); /// assert_eq!(set.get(ns_string!("four")), None); /// ``` @@ -263,6 +242,18 @@ extern_methods!( #[method(member:)] pub fn get(&self, value: &T) -> Option<&T>; + #[doc(alias = "member:")] + pub fn get_retained(&self, value: &T) -> Option> + where + T: IsIdCloneable, + { + self.get(value) + .map(|obj| unsafe { util::collection_retain_id(obj) }) + } + + // Note: No `get_mut` method exposed on sets, since their objects' + // hashes are supposed to be immutable. + /// Returns `true` if the set is a subset of another, i.e., `other` /// contains at least all the values in `self`. /// @@ -271,15 +262,15 @@ extern_methods!( /// ``` /// use icrate::Foundation::{NSSet, NSString}; /// - /// let set1 = NSSet::from_slice(&["one", "two"].map(NSString::from_str)); - /// let set2 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str)); + /// let set1 = NSSet::from_id_slice(&["one", "two"].map(NSString::from_str)); + /// let set2 = NSSet::from_id_slice(&["one", "two", "three"].map(NSString::from_str)); /// /// assert!(set1.is_subset(&set2)); /// assert!(!set2.is_subset(&set1)); /// ``` #[doc(alias = "isSubsetOfSet:")] #[method(isSubsetOfSet:)] - pub fn is_subset(&self, other: &NSSet) -> bool; + pub fn is_subset(&self, other: &NSSet) -> bool; /// Returns `true` if the set is a superset of another, i.e., `self` /// contains at least all the values in `other`. @@ -289,18 +280,18 @@ extern_methods!( /// ``` /// use icrate::Foundation::{NSSet, NSString}; /// - /// let set1 = NSSet::from_slice(&["one", "two"].map(NSString::from_str)); - /// let set2 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str)); + /// let set1 = NSSet::from_id_slice(&["one", "two"].map(NSString::from_str)); + /// let set2 = NSSet::from_id_slice(&["one", "two", "three"].map(NSString::from_str)); /// /// assert!(!set1.is_superset(&set2)); /// assert!(set2.is_superset(&set1)); /// ``` - pub fn is_superset(&self, other: &NSSet) -> bool { + pub fn is_superset(&self, other: &NSSet) -> bool { other.is_subset(self) } #[method(intersectsSet:)] - fn intersects_set(&self, other: &NSSet) -> bool; + fn intersects_set(&self, other: &NSSet) -> bool; /// Returns `true` if `self` has no elements in common with `other`. /// @@ -309,27 +300,27 @@ extern_methods!( /// ``` /// use icrate::Foundation::{NSSet, NSString}; /// - /// let set1 = NSSet::from_slice(&["one", "two"].map(NSString::from_str)); - /// let set2 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str)); - /// let set3 = NSSet::from_slice(&["four", "five", "six"].map(NSString::from_str)); + /// let set1 = NSSet::from_id_slice(&["one", "two"].map(NSString::from_str)); + /// let set2 = NSSet::from_id_slice(&["one", "two", "three"].map(NSString::from_str)); + /// let set3 = NSSet::from_id_slice(&["four", "five", "six"].map(NSString::from_str)); /// /// assert!(!set1.is_disjoint(&set2)); /// assert!(set1.is_disjoint(&set3)); /// assert!(set2.is_disjoint(&set3)); /// ``` - pub fn is_disjoint(&self, other: &NSSet) -> bool { + pub fn is_disjoint(&self, other: &NSSet) -> bool { !self.intersects_set(other) } } ); -unsafe impl Foundation::NSFastEnumeration2 for NSSet { +unsafe impl Foundation::NSFastEnumeration2 for NSSet { type Item = T; } -impl<'a, T: Message, O: Ownership> IntoIterator for &'a NSSet { +impl<'a, T: Message> IntoIterator for &'a NSSet { type Item = &'a T; - type IntoIter = Foundation::NSFastEnumerator2<'a, NSSet>; + type IntoIter = Foundation::NSFastEnumerator2<'a, NSSet>; fn into_iter(self) -> Self::IntoIter { use Foundation::NSFastEnumeration2; @@ -337,17 +328,15 @@ impl<'a, T: Message, O: Ownership> IntoIterator for &'a NSSet { } } -impl DefaultId for NSSet { - type Ownership = Shared; - +impl DefaultId for NSSet { #[inline] - fn default_id() -> Id { + fn default_id() -> Id { Self::new() } } #[cfg(feature = "Foundation_NSEnumerator")] -impl fmt::Debug for NSSet { +impl fmt::Debug for NSSet { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use Foundation::NSFastEnumeration2; diff --git a/crates/icrate/src/Foundation/additions/string.rs b/crates/icrate/src/Foundation/additions/string.rs index dd9fe6a8e..e16914cb3 100644 --- a/crates/icrate/src/Foundation/additions/string.rs +++ b/crates/icrate/src/Foundation/additions/string.rs @@ -13,7 +13,7 @@ use core::str; use std::os::raw::c_char; use objc2::msg_send; -use objc2::rc::{autoreleasepool_leaking, AutoreleasePool, DefaultId, Id, Shared}; +use objc2::rc::{autoreleasepool_leaking, AutoreleasePool, DefaultId}; use objc2::runtime::__nsstring::{nsstring_len, nsstring_to_str, UTF8_ENCODING}; use crate::common::*; @@ -169,10 +169,8 @@ impl Ord for NSString { // https://github.com/nvzqz/fruity/blob/320efcf715c2c5fbd2f3084f671f2be2e03a6f2b/src/foundation/ns_string/mod.rs#L69-L163 impl DefaultId for NSString { - type Ownership = Shared; - #[inline] - fn default_id() -> Id { + fn default_id() -> Id { Self::new() } } diff --git a/crates/icrate/src/Foundation/additions/thread.rs b/crates/icrate/src/Foundation/additions/thread.rs index a20da7235..2007c9db2 100644 --- a/crates/icrate/src/Foundation/additions/thread.rs +++ b/crates/icrate/src/Foundation/additions/thread.rs @@ -7,6 +7,8 @@ use core::panic::{RefUnwindSafe, UnwindSafe}; use crate::common::*; use crate::Foundation::NSThread; +use objc2::msg_send_id; + unsafe impl Send for NSThread {} unsafe impl Sync for NSThread {} @@ -120,6 +122,43 @@ impl MainThreadMarker { Self { _priv: PhantomData } } + /// Allocate a new instance of the specified class on the main thread. + /// + /// This is essentially the same as [`ClassType::alloc`], the difference + /// being that it is also callable with classes that can only be used on + /// the main thread. + /// + /// + /// # Example + /// + /// Create an object on the main thread. + /// + /// ``` + /// use icrate::Foundation::MainThreadMarker; + /// # use icrate::Foundation::NSObject as SomeClass; + /// # #[cfg(for_example)] + /// use icrate::SomeFramework::SomeClass; + /// use objc2::rc::Id; + /// use objc2::msg_send_id; + /// + /// # let mtm = unsafe { MainThreadMarker::new_unchecked() }; + /// # #[cfg(doctests_not_always_run_on_main_thread)] + /// let mtm = MainThreadMarker::new().expect("must be on the main thread"); + /// + /// // _All_ objects are safe to allocate on the main thread! + /// let obj = mtm.alloc::(); + /// + /// // Though more knowledge is required for safe initialization + /// let obj: Id = unsafe { msg_send_id![obj, init] }; + /// ``` + #[inline] + pub fn alloc(self) -> Option> { + // SAFETY: Same as `ClassType::alloc`, with the addition that since we + // take `self: MainThreadMarker`, the `IsAllocableAnyThread` bound is + // not required. + unsafe { msg_send_id![T::class(), alloc] } + } + /// Submit the given closure to the runloop on the main thread. /// /// If the current thread is the main thread, this simply runs the diff --git a/crates/icrate/src/Foundation/additions/util.rs b/crates/icrate/src/Foundation/additions/util.rs new file mode 100644 index 000000000..ad5db7a50 --- /dev/null +++ b/crates/icrate/src/Foundation/additions/util.rs @@ -0,0 +1,96 @@ +#![allow(dead_code)] +use core::ptr::NonNull; + +use objc2::mutability::IsIdCloneable; +use objc2::rc::Id; +use objc2::Message; + +pub(crate) fn id_ptr_cast(objects: *mut Id) -> *mut NonNull { + // SAFETY: `Id` has the same memory layout as `NonNull`, and + // stronger guarantees. + objects.cast() +} + +fn ref_ptr_cast(objects: *mut &T) -> *mut NonNull { + // SAFETY: `&T` has the same memory layout as `NonNull`, and stronger + // guarantees. + objects.cast() +} + +pub(crate) fn ref_ptr_cast_const(objects: *const &T) -> *mut NonNull { + ref_ptr_cast(objects as _) +} + +pub(crate) fn id_ptr_cast_const(objects: *const Id) -> *mut NonNull { + id_ptr_cast(objects as _) +} + +// Foundation collection types store their items in such a way that they can +// give out references to their data without having to autorelease it first: +// +// +// Hence functions can safely return references that are not bound to a pool +// (provided they are not modified outside `&mut`, which we implement them not +// to be). +// +// --- +// +// A way to verify the safety of the collection interfaces is to imagine their +// storage as an equivalent Rust type, and then try to write a (possibly +// inefficient) implementation of the method with that type. +// +// In table form: +// | Objective-C | Equivalent Rust storage | +// | ------------------------------ | ------------------------ | +// | Id> | Arc<[Id]> | +// | Id> | Arc<[Id]> | +// | Id> | Arc<[(Id, Id)]> | +// | Id> | Vec> | +// | Id> | Vec> | +// | Id> | Vec<(Id, Id)> | +// | ------------------------------ | ------------------------ | +// | &NSArray | &[Id] | +// | &NSDictionary | &[(Id, Id)] | +// | &mut NSArray | &mut [Id] | +// | &mut NSMutableArray | &mut Vec> | +// | &mut NSDictionary | &mut [(Id, Id)] | +// | &mut NSMutableDictionary | &mut Vec<(Id, Id)> | + +/// We should be able to access `&Id` from `&self` in collection types, +/// though that is not directly possible since we don't have raw access to the +/// internal allocation; so instead we use this helper function to allow +/// roughly the same functionality. +/// +/// +/// # Safety +/// +/// The object must be stored inside a collection. +pub(crate) unsafe fn collection_retain_id(obj: &T) -> Id +where + T: IsIdCloneable, +{ + // SAFETY: We're allowed to access `&Id` from `&self` in collections, + // and since `T: IsIdCloneable`, we can convert that to `Id`. + unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() } +} + +/// The mutable variants give us the extra benefit that we may get `Id` +/// out, provided we've removed it from the collection first. +/// +/// +/// # Safety +/// +/// The object must have just been removed from the mutable collection, or the +/// collection must be consumed right after this. +// +// TODO: Take a pointer here to avoid assuming `T` is immutable - this only +// works now because `T` is usually `UnsafeCell` anyway. +pub(crate) unsafe fn mutable_collection_retain_removed_id(obj: &T) -> Id +where + T: Message, +{ + // SAFETY: The collection had mutable ownership over the object, and since + // the object will never again be accessed from the collection, we can + // convert it to `Id`. + unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() } +} diff --git a/crates/icrate/src/Foundation/additions/uuid.rs b/crates/icrate/src/Foundation/additions/uuid.rs index 90050b440..b21023697 100644 --- a/crates/icrate/src/Foundation/additions/uuid.rs +++ b/crates/icrate/src/Foundation/additions/uuid.rs @@ -85,8 +85,7 @@ impl fmt::Debug for NSUUID { // } impl DefaultId for NSUUID { - type Ownership = Shared; - fn default_id() -> Id { + fn default_id() -> Id { Self::nil() } } diff --git a/crates/icrate/src/Foundation/fixes/copy.rs b/crates/icrate/src/Foundation/fixes/copy.rs index c7143da3c..4f4f03b28 100644 --- a/crates/icrate/src/Foundation/fixes/copy.rs +++ b/crates/icrate/src/Foundation/fixes/copy.rs @@ -3,280 +3,113 @@ use alloc::borrow::ToOwned; use crate::common::*; use crate::Foundation::{self, NSCopying, NSMutableCopying}; -// Arrays - -/// This is implemented as a shallow copy. -/// -/// As such, it is only possible when the array's contents are `Shared`. -#[cfg(feature = "Foundation_NSArray")] -unsafe impl NSCopying for Foundation::NSArray { - type Ownership = Shared; - type Output = Foundation::NSArray; -} - -/// This is implemented as a shallow copy. #[cfg(feature = "Foundation_NSArray")] -#[cfg(feature = "Foundation_NSMutableArray")] -unsafe impl NSMutableCopying for Foundation::NSArray { - type Output = Foundation::NSMutableArray; -} - -#[cfg(feature = "Foundation_NSArray")] -impl ToOwned for Foundation::NSArray { - type Owned = Id, Shared>; +impl ToOwned for Foundation::NSArray { + type Owned = Id; fn to_owned(&self) -> Self::Owned { self.copy() } } -// Copying only possible when ItemOwnership = Shared - -/// This is implemented as a shallow copy. -#[cfg(feature = "Foundation_NSMutableArray")] -unsafe impl NSCopying for Foundation::NSMutableArray { - type Ownership = Shared; - type Output = Foundation::NSArray; -} - -/// This is implemented as a shallow copy. -#[cfg(feature = "Foundation_NSMutableArray")] -unsafe impl NSMutableCopying for Foundation::NSMutableArray { - type Output = Foundation::NSMutableArray; -} - #[cfg(feature = "Foundation_NSMutableArray")] -impl ToOwned for Foundation::NSMutableArray { - type Owned = Id, Owned>; +impl ToOwned for Foundation::NSMutableArray { + type Owned = Id; fn to_owned(&self) -> Self::Owned { - self.mutable_copy() + self.mutableCopy() } } -// Data - -#[cfg(feature = "Foundation_NSData")] -unsafe impl NSCopying for Foundation::NSData { - type Ownership = Shared; - type Output = Foundation::NSData; -} - -#[cfg(feature = "Foundation_NSData")] -#[cfg(feature = "Foundation_NSMutableData")] -unsafe impl NSMutableCopying for Foundation::NSData { - type Output = Foundation::NSMutableData; -} - #[cfg(feature = "Foundation_NSData")] impl ToOwned for Foundation::NSData { - type Owned = Id; + type Owned = Id; fn to_owned(&self) -> Self::Owned { self.copy() } } -#[cfg(feature = "Foundation_NSMutableData")] -unsafe impl NSCopying for Foundation::NSMutableData { - type Ownership = Shared; - type Output = Foundation::NSData; -} - -#[cfg(feature = "Foundation_NSMutableData")] -unsafe impl NSMutableCopying for Foundation::NSMutableData { - type Output = Foundation::NSMutableData; -} - #[cfg(feature = "Foundation_NSMutableData")] impl ToOwned for Foundation::NSMutableData { - type Owned = Id; + type Owned = Id; fn to_owned(&self) -> Self::Owned { - self.mutable_copy() + self.mutableCopy() } } -// Errors - -#[cfg(feature = "Foundation_NSError")] -unsafe impl NSCopying for Foundation::NSError { - type Ownership = Shared; - type Output = Self; -} - -#[cfg(feature = "Foundation_NSException")] -unsafe impl NSCopying for Foundation::NSException { - type Ownership = Shared; - type Output = Foundation::NSException; -} - #[cfg(feature = "Foundation_NSException")] impl ToOwned for Foundation::NSException { - type Owned = Id; + type Owned = Id; fn to_owned(&self) -> Self::Owned { self.copy() } } -// Sets - -#[cfg(feature = "Foundation_NSSet")] -unsafe impl NSCopying for Foundation::NSSet { - type Ownership = Shared; - type Output = Foundation::NSSet; -} - #[cfg(feature = "Foundation_NSSet")] -#[cfg(feature = "Foundation_NSMutableSet")] -unsafe impl NSMutableCopying for Foundation::NSSet { - type Output = Foundation::NSMutableSet; -} - -#[cfg(feature = "Foundation_NSSet")] -impl ToOwned for Foundation::NSSet { - type Owned = Id, Shared>; +impl ToOwned for Foundation::NSSet { + type Owned = Id; fn to_owned(&self) -> Self::Owned { self.copy() } } #[cfg(feature = "Foundation_NSMutableSet")] -unsafe impl NSCopying for Foundation::NSMutableSet { - type Ownership = Shared; - type Output = Foundation::NSSet; -} - -#[cfg(feature = "Foundation_NSMutableSet")] -unsafe impl NSMutableCopying for Foundation::NSMutableSet { - type Output = Foundation::NSMutableSet; -} - -#[cfg(feature = "Foundation_NSMutableSet")] -impl ToOwned for Foundation::NSMutableSet { - type Owned = Id, Owned>; +impl ToOwned for Foundation::NSMutableSet { + type Owned = Id; fn to_owned(&self) -> Self::Owned { - self.mutable_copy() + self.mutableCopy() } } -// Strings - -#[cfg(feature = "Foundation_NSString")] -unsafe impl NSCopying for Foundation::NSString { - type Ownership = Shared; - type Output = Foundation::NSString; -} - -#[cfg(feature = "Foundation_NSString")] -#[cfg(feature = "Foundation_NSMutableString")] -unsafe impl NSMutableCopying for Foundation::NSString { - type Output = Foundation::NSMutableString; -} - #[cfg(feature = "Foundation_NSString")] impl ToOwned for Foundation::NSString { - type Owned = Id; + type Owned = Id; fn to_owned(&self) -> Self::Owned { self.copy() } } -#[cfg(feature = "Foundation_NSMutableString")] -unsafe impl NSCopying for Foundation::NSMutableString { - type Ownership = Shared; - type Output = Foundation::NSString; -} - -#[cfg(feature = "Foundation_NSMutableString")] -unsafe impl NSMutableCopying for Foundation::NSMutableString { - type Output = Foundation::NSMutableString; -} - #[cfg(feature = "Foundation_NSMutableString")] impl ToOwned for Foundation::NSMutableString { - type Owned = Id; + type Owned = Id; fn to_owned(&self) -> Self::Owned { - self.mutable_copy() + self.mutableCopy() } } -#[cfg(feature = "Foundation_NSAttributedString")] -unsafe impl NSCopying for Foundation::NSAttributedString { - type Ownership = Shared; - type Output = Foundation::NSAttributedString; -} - -#[cfg(feature = "Foundation_NSAttributedString")] -#[cfg(feature = "Foundation_NSMutableAttributedString")] -unsafe impl NSMutableCopying for Foundation::NSAttributedString { - type Output = Foundation::NSMutableAttributedString; -} - #[cfg(feature = "Foundation_NSAttributedString")] impl ToOwned for Foundation::NSAttributedString { - type Owned = Id; + type Owned = Id; fn to_owned(&self) -> Self::Owned { self.copy() } } -#[cfg(feature = "Foundation_NSMutableAttributedString")] -unsafe impl NSCopying for Foundation::NSMutableAttributedString { - type Ownership = Shared; - type Output = Foundation::NSAttributedString; -} - -#[cfg(feature = "Foundation_NSMutableAttributedString")] -unsafe impl NSMutableCopying for Foundation::NSMutableAttributedString { - type Output = Foundation::NSMutableAttributedString; -} - #[cfg(feature = "Foundation_NSMutableAttributedString")] impl ToOwned for Foundation::NSMutableAttributedString { - type Owned = Id; + type Owned = Id; fn to_owned(&self) -> Self::Owned { - self.mutable_copy() + self.mutableCopy() } } -// UUID - -#[cfg(feature = "Foundation_NSUUID")] -unsafe impl NSCopying for Foundation::NSUUID { - type Ownership = Shared; - type Output = Foundation::NSUUID; -} - #[cfg(feature = "Foundation_NSUUID")] impl ToOwned for Foundation::NSUUID { - type Owned = Id; + type Owned = Id; fn to_owned(&self) -> Self::Owned { self.copy() } } -// Value - -#[cfg(feature = "Foundation_NSValue")] -unsafe impl NSCopying for Foundation::NSValue { - type Ownership = Shared; - type Output = Foundation::NSValue; -} - #[cfg(feature = "Foundation_NSValue")] impl ToOwned for Foundation::NSValue { - type Owned = Id; + type Owned = Id; fn to_owned(&self) -> Self::Owned { self.copy() } } -#[cfg(feature = "Foundation_NSNumber")] -unsafe impl NSCopying for Foundation::NSNumber { - type Ownership = Shared; - type Output = Foundation::NSNumber; -} - #[cfg(feature = "Foundation_NSNumber")] impl ToOwned for Foundation::NSNumber { - type Owned = Id; + type Owned = Id; fn to_owned(&self) -> Self::Owned { self.copy() } diff --git a/crates/icrate/src/Foundation/fixes/debug.rs b/crates/icrate/src/Foundation/fixes/debug.rs index 68a5d464b..9066ddb9e 100644 --- a/crates/icrate/src/Foundation/fixes/debug.rs +++ b/crates/icrate/src/Foundation/fixes/debug.rs @@ -21,7 +21,7 @@ impl fmt::Debug for Foundation::NSBundle { } #[cfg(feature = "Foundation_NSMutableArray")] -impl fmt::Debug for Foundation::NSMutableArray { +impl fmt::Debug for Foundation::NSMutableArray { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) @@ -54,7 +54,7 @@ impl fmt::Debug } #[cfg(feature = "Foundation_NSMutableSet")] -impl fmt::Debug for Foundation::NSMutableSet { +impl fmt::Debug for Foundation::NSMutableSet { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) diff --git a/crates/icrate/src/Foundation/fixes/generic_return.rs b/crates/icrate/src/Foundation/fixes/generic_return.rs deleted file mode 100644 index f212ad72f..000000000 --- a/crates/icrate/src/Foundation/fixes/generic_return.rs +++ /dev/null @@ -1,114 +0,0 @@ -use crate::common::*; -use crate::Foundation; - -extern_methods!( - /// NSArrayCreation - #[cfg(feature = "Foundation_NSArray")] - unsafe impl - Foundation::NSArray - { - #[cfg(all(feature = "Foundation_NSURL", feature = "Foundation_NSError"))] - #[method_id(initWithContentsOfURL:error:_)] - pub unsafe fn initWithContentsOfURL_error( - this: Option>, - url: &Foundation::NSURL, - ) -> Result, Id>; - } -); - -extern_methods!( - /// NSDeprecated - #[cfg(feature = "Foundation_NSArray")] - unsafe impl - Foundation::NSArray - { - #[cfg(feature = "Foundation_NSString")] - #[method_id(initWithContentsOfFile:)] - pub unsafe fn initWithContentsOfFile( - this: Option>, - path: &Foundation::NSString, - ) -> Option>; - - #[cfg(feature = "Foundation_NSURL")] - #[method_id(initWithContentsOfURL:)] - pub unsafe fn initWithContentsOfURL( - this: Option>, - url: &Foundation::NSURL, - ) -> Option>; - } -); - -extern_methods!( - /// NSMutableArrayCreation - #[cfg(feature = "Foundation_NSMutableArray")] - unsafe impl - Foundation::NSMutableArray - { - #[cfg(feature = "Foundation_NSString")] - #[method_id(initWithContentsOfFile:)] - pub unsafe fn initWithContentsOfFile( - this: Option>, - path: &Foundation::NSString, - ) -> Option>; - - #[cfg(feature = "Foundation_NSURL")] - #[method_id(initWithContentsOfURL:)] - pub unsafe fn initWithContentsOfURL( - this: Option>, - url: &Foundation::NSURL, - ) -> Option>; - } -); - -extern_methods!( - /// NSDeprecated - #[cfg(feature = "Foundation_NSDictionary")] - unsafe impl< - KeyType: Message, - ObjectType: Message, - KeyTypeOwnership: Ownership, - ObjectTypeOwnership: Ownership, - > Foundation::NSDictionary - { - #[cfg(feature = "Foundation_NSString")] - #[method_id(initWithContentsOfFile:)] - pub unsafe fn initWithContentsOfFile( - this: Option>, - path: &Foundation::NSString, - ) -> Option>; - - #[cfg(feature = "Foundation_NSURL")] - #[method_id(initWithContentsOfURL:)] - pub unsafe fn initWithContentsOfURL( - this: Option>, - url: &Foundation::NSURL, - ) -> Option>; - } -); - -extern_methods!( - /// NSMutableDictionaryCreation - #[cfg(feature = "Foundation_NSMutableDictionary")] - unsafe impl< - KeyType: Message, - ObjectType: Message, - KeyTypeOwnership: Ownership, - ObjectTypeOwnership: Ownership, - > - Foundation::NSMutableDictionary - { - #[cfg(feature = "Foundation_NSString")] - #[method_id(initWithContentsOfFile:)] - pub unsafe fn initWithContentsOfFile( - this: Option>, - path: &Foundation::NSString, - ) -> Option>; - - #[cfg(feature = "Foundation_NSURL")] - #[method_id(initWithContentsOfURL:)] - pub unsafe fn initWithContentsOfURL( - this: Option>, - url: &Foundation::NSURL, - ) -> Option>; - } -); diff --git a/crates/icrate/src/Foundation/fixes/generics.rs b/crates/icrate/src/Foundation/fixes/generics.rs new file mode 100644 index 000000000..fc3e05414 --- /dev/null +++ b/crates/icrate/src/Foundation/fixes/generics.rs @@ -0,0 +1,297 @@ +#![allow(dead_code)] +use core::panic::{RefUnwindSafe, UnwindSafe}; + +use crate::common::*; + +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Copy, Debug)] +#[repr(transparent)] +struct UnsafeIgnoreAutoTraits(T); +// SAFETY: Moved to the name of the struct +unsafe impl Send for UnsafeIgnoreAutoTraits {} +// SAFETY: Moved to the name of the struct +unsafe impl Sync for UnsafeIgnoreAutoTraits {} +impl RefUnwindSafe for UnsafeIgnoreAutoTraits {} +impl UnwindSafe for UnsafeIgnoreAutoTraits {} +// TODO +// impl Unpin for UnsafeIgnoreAutoTraits {} + +__inner_extern_class!( + #[derive(PartialEq, Eq, Hash)] + #[cfg(feature = "Foundation_NSArray")] + pub struct NSArray { + // SAFETY: Auto traits specified below. + __superclass: UnsafeIgnoreAutoTraits, + /// `NSArray` and `NSMutableArray` have `Id`-like storage. + /// + /// This fairly trivially true for `NSMutableArray` and + /// `NSArray`, but let's explore the two other cases + /// a bit: + /// + /// + /// # `NSMutableArray` + /// + /// Must have `Arc`-like auto traits, since `NSMutableArray` is + /// `NSMutableCopying` in that case (which is a shallow clone). + /// + /// E.g. if we had `NSMutableArray: Send`, `T` would be + /// accessible from multiple threads, therefore we require `T: Sync`: + /// ```ignore + /// std::thread::scope(|s| { + /// let obj: Id>; + /// let obj2 = obj.mutableCopy(); + /// s.spawn(move || { + /// let obj = obj; + /// }); + /// s.spawn(move || { + /// let obj2 = obj2; + /// }); + /// }) + /// ``` + /// + /// Similarly, if we had `NSMutableArray: Sync`, `T` would be + /// dropable from other threads, therefore we require `T: Send`: + /// ```ignore + /// let obj: Id>; + /// std::thread::spawn(|| { + /// let obj2 = obj.mutableCopy(); + /// + /// // Do some long-lived work, the original `obj` is dropped in + /// // the original thread in the meantime. + /// + /// // And now, the destructor is run on the wrong thread. + /// drop(obj2); + /// }); + /// // (note: example does not work due to lifetime issues, but the + /// // idea still stands). + /// ``` + /// + /// + /// # `NSArray` + /// + /// This could safely be `Arc`-like, but for consistency with + /// `NSMutableArray` (esp. since `get_mut` is available on `NSArray` + /// and not `NSMutableArray`), we make it `Box`-like. + /// + /// This may seem wrong since arrays are unconditionally immutable, + /// e.g. `ClassType::Mutability` is always + /// `ImmutableWithMutableSubclass`. + /// + /// But since we already require `T: IsCloneable` on `NSCopying`, and + /// already prevent other forms of `&NSArray -> Id>`, + /// this is actually just fine, since `Id>: Send | Sync` + /// requires `NSArray: Send + Sync` (and hence `T: Send + Sync`). + __inner: PhantomData>, + } + + #[cfg(feature = "Foundation_NSArray")] + unsafe impl ClassType for NSArray { + type Super = NSObject; + type Mutability = ImmutableWithMutableSubclass>; + + fn as_super(&self) -> &Self::Super { + &self.__superclass.0 + } + + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__superclass.0 + } + } +); + +__inner_extern_class!( + #[derive(PartialEq, Eq, Hash)] + #[cfg(feature = "Foundation_NSArray")] + pub struct NSMutableArray { + // Inherit auto traits from superclass. + __superclass: NSArray, + } + + #[cfg(feature = "Foundation_NSArray")] + unsafe impl ClassType for NSMutableArray { + #[inherits(NSObject)] + type Super = NSArray; + type Mutability = MutableWithImmutableSuperclass>; + + fn as_super(&self) -> &Self::Super { + &self.__superclass + } + + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__superclass + } + } +); + +__inner_extern_class!( + #[derive(PartialEq, Eq, Hash)] + #[cfg(feature = "Foundation_NSDictionary")] + pub struct NSDictionary { + // SAFETY: Auto traits specified below. + __superclass: UnsafeIgnoreAutoTraits, + // Same as if the dictionary was just: + // `(NSArray, NSArray)` + __inner: PhantomData<(Id, Id)>, + } + + #[cfg(feature = "Foundation_NSDictionary")] + unsafe impl ClassType for NSDictionary { + type Super = NSObject; + type Mutability = ImmutableWithMutableSubclass>; + + fn as_super(&self) -> &Self::Super { + &self.__superclass.0 + } + + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__superclass.0 + } + } +); + +__inner_extern_class!( + #[derive(PartialEq, Eq, Hash)] + #[cfg(feature = "Foundation_NSDictionary")] + pub struct NSMutableDictionary { + // Inherit auto traits from superclass. + __superclass: NSDictionary, + } + + #[cfg(feature = "Foundation_NSDictionary")] + unsafe impl ClassType + for NSMutableDictionary + { + #[inherits(NSObject)] + type Super = NSDictionary; + type Mutability = MutableWithImmutableSuperclass>; + + fn as_super(&self) -> &Self::Super { + &self.__superclass + } + + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__superclass + } + } +); + +__inner_extern_class!( + #[derive(PartialEq, Eq, Hash)] + #[cfg(feature = "Foundation_NSSet")] + pub struct NSSet { + // SAFETY: Auto traits specified below. + __superclass: UnsafeIgnoreAutoTraits, + // Same as if the set was just `NSArray`. + __inner: PhantomData>, + } + + #[cfg(feature = "Foundation_NSSet")] + unsafe impl ClassType for NSSet { + type Super = NSObject; + type Mutability = ImmutableWithMutableSubclass>; + + fn as_super(&self) -> &Self::Super { + &self.__superclass.0 + } + + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__superclass.0 + } + } +); + +__inner_extern_class!( + #[derive(PartialEq, Eq, Hash)] + #[cfg(feature = "Foundation_NSSet")] + pub struct NSMutableSet { + // Inherit auto traits from superclass. + __superclass: NSSet, + } + + #[cfg(feature = "Foundation_NSSet")] + unsafe impl ClassType for NSMutableSet { + #[inherits(NSObject)] + type Super = NSSet; + type Mutability = MutableWithImmutableSuperclass>; + + fn as_super(&self) -> &Self::Super { + &self.__superclass + } + + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__superclass + } + } +); + +__inner_extern_class!( + #[derive(Debug, PartialEq, Eq, Hash)] + #[cfg(feature = "Foundation_NSCountedSet")] + pub struct NSCountedSet { + // Inherit auto traits from superclass. + __superclass: NSMutableSet, + } + + #[cfg(feature = "Foundation_NSCountedSet")] + unsafe impl ClassType for NSCountedSet { + #[inherits(NSSet, NSObject)] + type Super = NSMutableSet; + type Mutability = Mutable; + + fn as_super(&self) -> &Self::Super { + &self.__superclass + } + + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__superclass + } + } +); + +__inner_extern_class!( + #[derive(PartialEq, Eq, Hash)] + #[cfg(feature = "Foundation_NSOrderedSet")] + pub struct NSOrderedSet { + // SAFETY: Auto traits specified below. + __superclass: UnsafeIgnoreAutoTraits, + // Same as if the set was just `NSArray`. + __inner: PhantomData>, + } + + #[cfg(feature = "Foundation_NSOrderedSet")] + unsafe impl ClassType for NSOrderedSet { + type Super = NSObject; + type Mutability = ImmutableWithMutableSubclass>; + + fn as_super(&self) -> &Self::Super { + &self.__superclass.0 + } + + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__superclass.0 + } + } +); + +__inner_extern_class!( + #[derive(PartialEq, Eq, Hash)] + #[cfg(feature = "Foundation_NSOrderedSet")] + pub struct NSMutableOrderedSet { + // Inherit auto traits from superclass. + __superclass: NSOrderedSet, + } + + #[cfg(feature = "Foundation_NSOrderedSet")] + unsafe impl ClassType for NSMutableOrderedSet { + #[inherits(NSObject)] + type Super = NSOrderedSet; + type Mutability = MutableWithImmutableSuperclass>; + + fn as_super(&self) -> &Self::Super { + &self.__superclass + } + + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__superclass + } + } +); diff --git a/crates/icrate/src/Foundation/fixes/mod.rs b/crates/icrate/src/Foundation/fixes/mod.rs index 269ad765c..ace0c49bb 100644 --- a/crates/icrate/src/Foundation/fixes/mod.rs +++ b/crates/icrate/src/Foundation/fixes/mod.rs @@ -5,12 +5,13 @@ mod __NSDecimal; mod __NSNotFound; mod copy; mod debug; -mod generic_return; +mod generics; mod gnustep; mod ns_consumed; pub use self::__NSDecimal::NSDecimal; pub use self::__NSNotFound::NSNotFound; +pub use self::generics::*; #[cfg(feature = "Foundation_NSMapTable")] pub use self::ns_consumed::NSFreeMapTable; #[cfg(feature = "Foundation_NSUUID")] diff --git a/crates/icrate/src/Foundation/mod.rs b/crates/icrate/src/Foundation/mod.rs index a16335a80..56abe549b 100644 --- a/crates/icrate/src/Foundation/mod.rs +++ b/crates/icrate/src/Foundation/mod.rs @@ -52,52 +52,27 @@ //! //! TODO. //! -//! While `NSArray` _itself_ is immutable, i.e. the number of objects it -//! contains can't change, it is still possible to modify the contained -//! objects themselves, if you know you're the sole owner of them - -//! quite similar to how you can modify elements in `Box<[T]>`. -//! -//! To mutate the contained objects the ownership must be `O = Owned`. A -//! summary of what the different "types" of arrays allow you to do can be -//! found below. `Array` refers to either `NSArray` or `NSMutableArray`. -//! - `Id, Owned>`: Allows you to mutate the -//! objects, and the array itself. -//! - `Id, Owned>`: Allows you to mutate the -//! array itself, but not it's contents. -//! - `Id, Owned>`: Allows you to mutate the objects, -//! but not the array itself. -//! - `Id, Owned>`: Effectively the same as the below. -//! - `Id, Shared>`: Allows you to copy the array, but -//! does not allow you to modify it in any way. -//! - `Id, Shared>`: Pretty useless compared to the -//! others, avoid this. -//! //! //! # Rust vs. Objective-C types //! //! | Objective-C | (approximately) equivalent Rust | //! | --- | --- | -//! | `NSData` | `Box<[u8]>` | -//! | `NSMutableData` | `Vec` | -//! | `NSString` | `Box` | -//! | `NSMutableString` | `String` | -//! | `NSValue` | `Box` | -//! | `NSNumber` | `enum { I8(i8), U8(u8), I16(i16), U16(u16), I32(i32), U32(u32), I64(i64), U64(u64), F32(f32), F64(f64), CLong(ffi::c_long), CULong(ffi::c_ulong) }` | -//! | `NSError` | `Box` | -//! | `NSException` | `Box` | +//! | `NSData*` | `Arc<[u8]>` | +//! | `NSMutableData*` | `Vec` | +//! | `NSString*` | `Arc` | +//! | `NSMutableString*` | `String` | +//! | `NSValue*` | `Arc` | +//! | `NSNumber*` | `Arc` | +//! | `NSError*` | `Arc` | +//! | `NSException*` | `Arc` | //! | `NSRange` | `ops::Range` | //! | `NSComparisonResult` | `cmp::Ordering` | -//! | `NSArray` | `Box<[Arc]>` | -//! | `NSArray` | `Box<[T]>` | -//! | `NSMutableArray` | `Vec>` | -//! | `NSMutableArray` | `Vec` | -//! | `NSDictionary` | `ImmutableMap, Arc>` | -//! | `NSDictionary` | `ImmutableMap` | -//! | `NSMutableDictionary` | `Map, Arc>` | -//! | `NSMutableDictionary` | `Map` | -//! | `NSEnumerator` | `impl Iterator>` | -//! | `NSEnumerator` | `impl Iterator` | -//! | `@protocol NSCopying` | `trait Clone` | +//! | `NSArray*` | `Arc<[T]>` | +//! | `NSMutableArray*` | `Vec` | +//! | `NSDictionary*` | `Arc>` | +//! | `NSMutableDictionary*` | `HashMap` | +//! | `NSEnumerator*` | `Box>` | +//! | `NSCopying*` | `Box` | #![allow(unused_imports)] #[doc(hidden)] @@ -112,13 +87,19 @@ pub use self::additions::*; pub use self::fixes::*; pub use self::generated::*; +// Available under Foundation, so makes sense here as well: +// https://developer.apple.com/documentation/foundation/numbers_data_and_basic_values?language=objc +pub use objc2::ffi::{NSInteger, NSUInteger}; + +// Special types that are stored in `objc2`, but really belong here #[doc(inline)] #[cfg(feature = "Foundation_NSProxy")] pub use objc2::runtime::__NSProxy as NSProxy; pub use objc2::runtime::{NSObject, NSObjectProtocol, NSZone}; -// Available under Foundation, so makes sense here as well: -// https://developer.apple.com/documentation/foundation/numbers_data_and_basic_values?language=objc -pub use objc2::ffi::{NSInteger, NSUInteger}; +#[doc(inline)] +pub use objc2::runtime::{ + __Copyhelper as Copyhelper, __NSCopying as NSCopying, __NSMutableCopying as NSMutableCopying, +}; // Link to the correct framework #[cfg_attr(feature = "apple", link(name = "Foundation", kind = "framework"))] diff --git a/crates/icrate/src/MetricKit/fixes/mod.rs b/crates/icrate/src/MetricKit/fixes/mod.rs index 540031f9b..9835de061 100644 --- a/crates/icrate/src/MetricKit/fixes/mod.rs +++ b/crates/icrate/src/MetricKit/fixes/mod.rs @@ -18,16 +18,16 @@ extern_methods!( pub unsafe fn extendLaunchMeasurementForTaskID_error( &self, task_id: &MXLaunchTaskID, - ) -> Result<(), Id>; + ) -> Result<(), Id>; #[method(finishExtendedLaunchMeasurementForTaskID:error:_)] pub unsafe fn finishExtendedLaunchMeasurementForTaskID_error( &self, task_id: &MXLaunchTaskID, - ) -> Result<(), Id>; + ) -> Result<(), Id>; // #[cfg(feature = "Foundation_NSString")] // #[method(makeLogHandleWithCategory:)] - // pub unsafe fn makeLogHandleWithCategory(category: &NSString) -> Id; + // pub unsafe fn makeLogHandleWithCategory(category: &NSString) -> Id; } ); diff --git a/crates/icrate/src/common.rs b/crates/icrate/src/common.rs index ff9b588b2..ec72c5654 100644 --- a/crates/icrate/src/common.rs +++ b/crates/icrate/src/common.rs @@ -13,7 +13,12 @@ pub(crate) use std::os::raw::{ #[cfg(feature = "objective-c")] pub(crate) use objc2::ffi::{NSInteger, NSIntegerMax, NSUInteger, NSUIntegerMax, IMP}; #[cfg(feature = "objective-c")] -pub(crate) use objc2::rc::{Allocated, Id, Owned, Ownership, Shared}; +pub(crate) use objc2::mutability::{ + Immutable, ImmutableWithMutableSubclass, InteriorMutable, IsIdCloneable, Mutable, + MutableWithImmutableSuperclass, +}; +#[cfg(feature = "objective-c")] +pub(crate) use objc2::rc::{Allocated, Id}; #[cfg(feature = "objective-c")] pub(crate) use objc2::runtime::{Bool, Class, Object, Sel}; #[cfg(feature = "objective-c")] diff --git a/crates/icrate/src/generated b/crates/icrate/src/generated index 46fbfc138..8fba95577 160000 --- a/crates/icrate/src/generated +++ b/crates/icrate/src/generated @@ -1 +1 @@ -Subproject commit 46fbfc13869f718f7e6bd480ae407cfe001cbbb9 +Subproject commit 8fba9557708f9d8d4bcd635e5b278760f7ae4f66 diff --git a/crates/icrate/tests/array.rs b/crates/icrate/tests/array.rs index 2d5738d4b..4310ee2cb 100644 --- a/crates/icrate/tests/array.rs +++ b/crates/icrate/tests/array.rs @@ -1,10 +1,10 @@ #![cfg(feature = "Foundation_NSArray")] #![cfg(feature = "Foundation_NSNumber")] use icrate::Foundation::{NSArray, NSNumber, NSObject}; -use objc2::rc::{Id, Owned, Ownership, Shared}; +use objc2::rc::Id; use objc2::rc::{__RcTestObject, __ThreadTestData}; -fn sample_array(len: usize) -> Id, Owned> { +fn sample_array(len: usize) -> Id> { let mut vec = Vec::with_capacity(len); for _ in 0..len { vec.push(NSObject::new()); @@ -72,14 +72,14 @@ fn test_get() { #[test] fn test_retains_stored() { - let obj = Id::into_shared(__RcTestObject::new()); + let obj = __RcTestObject::new(); let mut expected = __ThreadTestData::current(); let input = [obj.clone(), obj.clone()]; expected.retain += 2; expected.assert_current(); - let array = NSArray::from_slice(&input); + let array = NSArray::from_id_slice(&input); expected.retain += 2; expected.assert_current(); @@ -115,14 +115,14 @@ fn test_retains_stored() { fn test_nscopying_uses_retain() { use icrate::Foundation::{NSCopying, NSMutableCopying}; - let obj = Id::into_shared(__RcTestObject::new()); - let array = NSArray::from_slice(&[obj]); + let obj = __RcTestObject::new(); + let array = NSArray::from_id_slice(&[obj]); let mut expected = __ThreadTestData::current(); let _copy = array.copy(); expected.assert_current(); - let _copy = array.mutable_copy(); + let _copy = array.mutableCopy(); expected.retain += 1; expected.assert_current(); } @@ -136,8 +136,8 @@ fn test_nscopying_uses_retain() { fn test_iter_no_retain() { use icrate::Foundation::NSFastEnumeration2; - let obj = Id::into_shared(__RcTestObject::new()); - let array = NSArray::from_slice(&[obj]); + let obj = __RcTestObject::new(); + let array = NSArray::from_id_slice(&[obj]); let mut expected = __ThreadTestData::current(); let iter = array.iter(); @@ -181,14 +181,6 @@ fn test_objects_in_range() { assert_eq!(all_objs.len(), 4); } -#[test] -fn test_into_vec() { - let array = sample_array(4); - - let vec = NSArray::into_vec(array); - assert_eq!(vec.len(), 4); -} - #[test] #[cfg(feature = "Foundation_NSString")] fn test_generic_ownership_traits() { @@ -196,13 +188,5 @@ fn test_generic_ownership_traits() { fn assert_partialeq() {} - assert_partialeq::>(); - assert_partialeq::>(); - - fn test_ownership_implies_partialeq() { - assert_partialeq::>(); - } - - test_ownership_implies_partialeq::(); - test_ownership_implies_partialeq::(); + assert_partialeq::>(); } diff --git a/crates/icrate/tests/attributed_string.rs b/crates/icrate/tests/attributed_string.rs index 87ae76daa..703068d3b 100644 --- a/crates/icrate/tests/attributed_string.rs +++ b/crates/icrate/tests/attributed_string.rs @@ -1,6 +1,6 @@ #![cfg(feature = "Foundation_NSAttributedString")] #![cfg(feature = "Foundation_NSString")] -use objc2::rc::{autoreleasepool, Id, Owned}; +use objc2::rc::{autoreleasepool, Id}; use objc2::runtime::Object; use icrate::Foundation::{self, NSAttributedString, NSObject, NSString}; @@ -39,7 +39,7 @@ fn test_copy() { // assert_eq!(Id::as_ptr(&s1), Id::as_ptr(&s2)); assert!(s2.is_kind_of::()); - let s3 = s1.mutable_copy(); + let s3 = s1.mutableCopy(); assert_ne!(Id::as_ptr(&s1), Id::as_ptr(&s3).cast()); assert!(s3.is_kind_of::()); } @@ -55,7 +55,7 @@ fn test_debug() { }; assert_eq!(format!("{s:?}"), expected); - let obj: Id = unsafe { Id::cast(NSObject::new()) }; + let obj: Id = unsafe { Id::cast(NSObject::new()) }; let ptr: *const Object = &*obj; let s = unsafe { NSAttributedString::new_with_attributes( @@ -91,7 +91,7 @@ fn test_copy_mutable() { assert_ne!(Id::as_ptr(&s1).cast(), Id::as_ptr(&s2)); assert!(s2.is_kind_of::()); - let s3 = s1.mutable_copy(); + let s3 = s1.mutableCopy(); assert_ne!(Id::as_ptr(&s1), Id::as_ptr(&s3)); assert!(s3.is_kind_of::()); } diff --git a/crates/icrate/tests/auto_traits.rs b/crates/icrate/tests/auto_traits.rs index fcececdd8..015719bcd 100644 --- a/crates/icrate/tests/auto_traits.rs +++ b/crates/icrate/tests/auto_traits.rs @@ -1,8 +1,13 @@ #![cfg(feature = "Foundation_all")] // TODO: More precise use core::panic::{RefUnwindSafe, UnwindSafe}; +use static_assertions::{assert_impl_all, assert_not_impl_any}; + use icrate::Foundation::*; -use objc2::rc::{Id, Owned, Shared}; +use objc2::mutability::{Immutable, Mutable}; +use objc2::rc::Id; +use objc2::runtime::Object; +use objc2::{declare_class, ClassType}; // We expect most Foundation types to be UnwindSafe and RefUnwindSafe, // since they follow Rust's usual mutability rules (&T = immutable). @@ -17,7 +22,7 @@ use objc2::rc::{Id, Owned, Shared}; // been mutated, it is not exception safe). // // Also note that this is still just a speed bump, not actually part of -// any unsafe contract; we really protect against it if something is not +// any unsafe contract; we can't really protect against it if something is not // exception safe, since `UnwindSafe` is a safe trait. fn assert_unwindsafe() {} @@ -25,39 +30,122 @@ fn assert_auto_traits() { assert_unwindsafe::(); } +macro_rules! helper { + ($name:ident, $mutability:ty) => { + declare_class!( + struct $name; + + unsafe impl ClassType for $name { + type Super = NSObject; + type Mutability = $mutability; + const NAME: &'static str = concat!(stringify!($name), "Test"); + } + ); + }; +} + +helper!(ImmutableObject, Immutable); +helper!(ImmutableSendObject, Immutable); +unsafe impl Send for ImmutableSendObject {} +helper!(ImmutableSyncObject, Immutable); +unsafe impl Sync for ImmutableSyncObject {} +helper!(ImmutableSendSyncObject, Immutable); +unsafe impl Send for ImmutableSendSyncObject {} +unsafe impl Sync for ImmutableSendSyncObject {} + +helper!(MutableObject, Mutable); +helper!(MutableSendObject, Mutable); +unsafe impl Send for MutableSendObject {} +helper!(MutableSyncObject, Mutable); +unsafe impl Sync for MutableSyncObject {} +helper!(MutableSendSyncObject, Mutable); +unsafe impl Send for MutableSendSyncObject {} +unsafe impl Sync for MutableSendSyncObject {} + #[test] -fn send_sync_unwindsafe() { - assert_auto_traits::>(); - assert_auto_traits::>(); - assert_auto_traits::, Shared>>(); - assert_auto_traits::, Shared>>(); - assert_auto_traits::, Owned>>(); - assert_auto_traits::, Owned>>(); +fn test_generic_auto_traits() { + assert_auto_traits::>(); + assert_auto_traits::>>(); + assert_auto_traits::>(); + assert_auto_traits::>>(); + assert_auto_traits::>(); + assert_auto_traits::>>(); + + macro_rules! assert_id_like { + ($wrapper:ident) => { + assert_not_impl_any!($wrapper: Send, Sync); + assert_not_impl_any!($wrapper: Send, Sync); + assert_not_impl_any!($wrapper: Send, Sync); + assert_impl_all!($wrapper: Send, Sync); + + assert_not_impl_any!($wrapper: Send, Sync); + assert_not_impl_any!($wrapper: Sync); + assert_impl_all!($wrapper: Send); + assert_not_impl_any!($wrapper: Send); + assert_impl_all!($wrapper: Sync); + assert_impl_all!($wrapper: Send, Sync); + }; + } + macro_rules! assert_arc_like { + ($wrapper:ident) => { + assert_not_impl_any!($wrapper: Send, Sync); + assert_not_impl_any!($wrapper: Send, Sync); + assert_not_impl_any!($wrapper: Send, Sync); + assert_impl_all!($wrapper: Send, Sync); + + assert_not_impl_any!($wrapper: Send, Sync); + assert_not_impl_any!($wrapper: Send, Sync); + assert_not_impl_any!($wrapper: Send, Sync); + assert_impl_all!($wrapper: Send, Sync); + }; + } + + // TODO + assert_not_impl_any!(NSArray: Unpin); + assert_not_impl_any!(NSMutableArray: Unpin); + assert_not_impl_any!(NSDictionary: Unpin); + + assert_id_like!(NSArray); + #[allow(dead_code)] + type NSArrayId = Id>; + assert_arc_like!(NSArrayId); + + assert_id_like!(NSMutableArray); + #[allow(dead_code)] + type NSMutableArrayId = Id>; + assert_id_like!(NSMutableArrayId); + + #[allow(dead_code)] + type NSDictionarySingle = NSDictionary; + assert_id_like!(NSDictionarySingle); + #[allow(dead_code)] + type NSDictionarySingleId = Id>; + assert_arc_like!(NSDictionarySingleId); +} + +#[test] +fn send_sync_unwindsafe() { assert_auto_traits::(); assert_auto_traits::(); assert_auto_traits::(); assert_auto_traits::>(); - assert_auto_traits::>(); - assert_auto_traits::>(); - assert_auto_traits::, Shared>>(); - assert_auto_traits::, Shared>>(); - assert_auto_traits::, Owned>>(); - assert_auto_traits::, Owned>>(); + assert_auto_traits::>(); + assert_auto_traits::>>(); // TODO: Figure out if Send + Sync is safe? // assert_auto_traits::>(); - // assert_auto_traits::>>(); + // assert_auto_traits::>>(); assert_auto_traits::(); assert_auto_traits::(); assert_auto_traits::(); assert_auto_traits::(); assert_auto_traits::(); assert_auto_traits::(); - assert_auto_traits::>(); + assert_auto_traits::>(); assert_auto_traits::(); assert_auto_traits::(); assert_auto_traits::>(); - assert_auto_traits::>(); + assert_auto_traits::>(); assert_auto_traits::(); assert_auto_traits::(); // assert_auto_traits::(); // Intentional diff --git a/crates/icrate/tests/dictionary.rs b/crates/icrate/tests/dictionary.rs index 2de37cebb..57f9a7a26 100644 --- a/crates/icrate/tests/dictionary.rs +++ b/crates/icrate/tests/dictionary.rs @@ -81,7 +81,7 @@ fn test_iter_values() { fn test_arrays() { let dict = sample_dict("abcd"); - let keys = dict.allKeys(); + let keys = unsafe { dict.allKeys() }; assert_eq!(keys.len(), 1); autoreleasepool(|pool| { assert_eq!(keys[0].as_str(pool), "abcd"); @@ -95,8 +95,7 @@ fn test_arrays() { #[cfg(feature = "Foundation_NSEnumerator")] fn test_debug() { let key = NSString::from_str("a"); - // TODO: Fix this - let val = unsafe { Id::from_shared(NSString::from_str("b")) }; + let val = NSString::from_str("b"); let dict = NSDictionary::from_keys_and_objects(&[&*key], vec![val]); assert_eq!(format!("{dict:?}"), r#"{"a": "b"}"#); } diff --git a/crates/icrate/tests/mutable_array.rs b/crates/icrate/tests/mutable_array.rs index 5868373d6..2ff822bd0 100644 --- a/crates/icrate/tests/mutable_array.rs +++ b/crates/icrate/tests/mutable_array.rs @@ -32,7 +32,7 @@ fn test_replace() { array.push(obj1); let mut expected = __ThreadTestData::current(); - let old_obj = array.replace(0, obj2); + let old_obj = array.replace(0, obj2).unwrap(); expected.retain += 2; expected.release += 2; expected.assert_current(); @@ -66,6 +66,15 @@ fn test_remove() { assert_eq!(array.len(), 0); } +#[test] +#[cfg(feature = "Foundation_NSString")] +fn test_into_vec() { + let array = NSMutableArray::from_id_slice(&[Foundation::NSString::new()]); + + let vec = NSMutableArray::into_vec(array); + assert_eq!(vec.len(), 1); +} + #[test] #[cfg(feature = "Foundation_NSString")] fn test_sort() { diff --git a/crates/icrate/tests/mutable_data.rs b/crates/icrate/tests/mutable_data.rs index de78dcb28..d4415ad5b 100644 --- a/crates/icrate/tests/mutable_data.rs +++ b/crates/icrate/tests/mutable_data.rs @@ -1,6 +1,6 @@ #![cfg(feature = "Foundation_NSMutableData")] -use objc2::rc::{Id, Owned}; +use objc2::rc::Id; use objc2::runtime::Object; use icrate::Foundation::{NSData, NSMutableData, NSObject}; @@ -82,8 +82,8 @@ fn test_as_ref_borrow() { fn impls_borrow_mut + BorrowMut + ?Sized, U: ?Sized>(_: &mut T) {} let mut obj = NSMutableData::new(); - impls_borrow::, NSMutableData>(&obj); - impls_borrow_mut::, NSMutableData>(&mut obj); + impls_borrow::, NSMutableData>(&obj); + impls_borrow_mut::, NSMutableData>(&mut obj); impls_borrow::(&obj); impls_borrow_mut::(&mut obj); diff --git a/crates/icrate/tests/mutable_dictionary.rs b/crates/icrate/tests/mutable_dictionary.rs index aa0e935d6..dc95b779c 100644 --- a/crates/icrate/tests/mutable_dictionary.rs +++ b/crates/icrate/tests/mutable_dictionary.rs @@ -1,10 +1,10 @@ #![cfg(feature = "Foundation_NSMutableDictionary")] #![cfg(feature = "Foundation_NSNumber")] -use objc2::rc::{Id, Owned, __RcTestObject, __ThreadTestData}; +use objc2::rc::{Id, __RcTestObject, __ThreadTestData}; use icrate::Foundation::{NSMutableDictionary, NSNumber, NSObject}; -fn sample_dict() -> Id, Owned> { +fn sample_dict() -> Id> { NSMutableDictionary::from_keys_and_objects( &[ &*NSNumber::new_i32(1), @@ -15,6 +15,22 @@ fn sample_dict() -> Id, Owned> { ) } +#[cfg(feature = "Foundation_NSMutableString")] +fn sample_dict_mut() -> Id> { + NSMutableDictionary::from_keys_and_objects( + &[ + &*NSNumber::new_i32(1), + &*NSNumber::new_i32(2), + &*NSNumber::new_i32(3), + ], + vec![ + icrate::Foundation::NSMutableString::from_str("a"), + icrate::Foundation::NSMutableString::from_str("b"), + icrate::Foundation::NSMutableString::from_str("c"), + ], + ) +} + #[test] fn test_new() { let dict = NSMutableDictionary::::new(); @@ -22,16 +38,19 @@ fn test_new() { } #[test] +#[cfg(feature = "Foundation_NSMutableString")] fn test_get_mut() { - let mut dict = sample_dict(); + let mut dict = sample_dict_mut(); + assert!(dict.get_mut(&NSNumber::new_i32(1)).is_some()); assert!(dict.get_mut(&NSNumber::new_i32(2)).is_some()); assert!(dict.get_mut(&NSNumber::new_i32(4)).is_none()); } #[test] +#[cfg(feature = "Foundation_NSMutableString")] fn test_values_mut() { - let mut dict = sample_dict(); + let mut dict = sample_dict_mut(); let vec = dict.values_mut(); assert_eq!(vec.len(), 3); } diff --git a/crates/icrate/tests/mutable_set.rs b/crates/icrate/tests/mutable_set.rs index 960e12163..92715a163 100644 --- a/crates/icrate/tests/mutable_set.rs +++ b/crates/icrate/tests/mutable_set.rs @@ -18,7 +18,7 @@ fn test_insert() { #[test] fn test_remove() { let strs = ["one", "two", "three"].map(NSString::from_str); - let mut set = NSMutableSet::from_slice(&strs); + let mut set = NSMutableSet::from_id_slice(&strs); assert!(set.remove(ns_string!("one"))); assert!(!set.remove(ns_string!("one"))); @@ -27,7 +27,7 @@ fn test_remove() { #[test] fn test_clear() { let strs = ["one", "two", "three"].map(NSString::from_str); - let mut set = NSMutableSet::from_slice(&strs); + let mut set = NSMutableSet::from_id_slice(&strs); assert_eq!(set.len(), 3); set.removeAllObjects(); @@ -67,12 +67,12 @@ fn test_extend() { fn test_mutable_copy() { use Foundation::NSMutableCopying; - let set1 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str)); - let mut set2 = set1.mutable_copy(); + let set1 = NSSet::from_id_slice(&["one", "two", "three"].map(NSString::from_str)); + let mut set2 = set1.mutableCopy(); set2.insert(NSString::from_str("four")); assert!(set1.is_subset(&set2)); - assert_ne!(set1.mutable_copy(), set2); + assert_ne!(set1.mutableCopy(), set2); } #[test] diff --git a/crates/icrate/tests/mutable_string.rs b/crates/icrate/tests/mutable_string.rs index 0129d1655..4e00ba9e2 100644 --- a/crates/icrate/tests/mutable_string.rs +++ b/crates/icrate/tests/mutable_string.rs @@ -49,7 +49,7 @@ fn test_copy() { assert_ne!(Id::as_ptr(&s1), Id::as_ptr(&s2).cast()); assert!(s2.is_kind_of::()); - let s3 = s1.mutable_copy(); + let s3 = s1.mutableCopy(); assert_ne!(Id::as_ptr(&s1), Id::as_ptr(&s3)); assert!(s3.is_kind_of::()); } diff --git a/crates/icrate/tests/set.rs b/crates/icrate/tests/set.rs index 629cbe0ea..c81f9c382 100644 --- a/crates/icrate/tests/set.rs +++ b/crates/icrate/tests/set.rs @@ -2,7 +2,7 @@ #![cfg(feature = "Foundation_NSString")] #![cfg(feature = "Foundation_NSNumber")] -use objc2::rc::{Id, __RcTestObject, __ThreadTestData}; +use objc2::rc::{__RcTestObject, __ThreadTestData}; use icrate::ns_string; use icrate::Foundation::{self, NSFastEnumeration2, NSNumber, NSObject, NSSet, NSString}; @@ -28,16 +28,16 @@ fn test_from_vec() { } #[test] -fn test_from_slice() { - let set = NSSet::::from_slice(&[]); +fn test_from_id_slice() { + let set = NSSet::::from_id_slice(&[]); assert!(set.is_empty()); let strs = ["one", "two", "three"].map(NSString::from_str); - let set = NSSet::from_slice(&strs); + let set = NSSet::from_id_slice(&strs); assert!(strs.into_iter().all(|s| set.contains(&s))); let nums = [1, 2, 3].map(NSNumber::new_i32); - let set = NSSet::from_slice(&nums); + let set = NSSet::from_id_slice(&nums); assert!(nums.into_iter().all(|n| set.contains(&n))); } @@ -46,7 +46,7 @@ fn test_len() { let set = NSSet::::new(); assert!(set.is_empty()); - let set = NSSet::from_slice(&["one", "two", "two"].map(NSString::from_str)); + let set = NSSet::from_id_slice(&["one", "two", "two"].map(NSString::from_str)); assert_eq!(set.len(), 2); let set = NSSet::from_vec(vec![NSObject::new(), NSObject::new(), NSObject::new()]); @@ -58,14 +58,14 @@ fn test_get() { let set = NSSet::::new(); assert!(set.get(ns_string!("one")).is_none()); - let set = NSSet::from_slice(&["one", "two", "two"].map(NSString::from_str)); + let set = NSSet::from_id_slice(&["one", "two", "two"].map(NSString::from_str)); assert!(set.get(ns_string!("two")).is_some()); assert!(set.get(ns_string!("three")).is_none()); } #[test] fn test_get_return_lifetime() { - let set = NSSet::from_slice(&["one", "two", "two"].map(NSString::from_str)); + let set = NSSet::from_id_slice(&["one", "two", "two"].map(NSString::from_str)); let res = { let value = NSString::from_str("one"); @@ -81,7 +81,7 @@ fn test_get_any() { assert!(set.get_any().is_none()); let strs = ["one", "two", "three"].map(NSString::from_str); - let set = NSSet::from_slice(&strs); + let set = NSSet::from_id_slice(&strs); let any = set.get_any().unwrap(); assert!(any == &*strs[0] || any == &*strs[1] || any == &*strs[2]); } @@ -91,15 +91,15 @@ fn test_contains() { let set = NSSet::::new(); assert!(!set.contains(ns_string!("one"))); - let set = NSSet::from_slice(&["one", "two", "two"].map(NSString::from_str)); + let set = NSSet::from_id_slice(&["one", "two", "two"].map(NSString::from_str)); assert!(set.contains(ns_string!("one"))); assert!(!set.contains(ns_string!("three"))); } #[test] fn test_is_subset() { - let set1 = NSSet::from_slice(&["one", "two"].map(NSString::from_str)); - let set2 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str)); + let set1 = NSSet::from_id_slice(&["one", "two"].map(NSString::from_str)); + let set2 = NSSet::from_id_slice(&["one", "two", "three"].map(NSString::from_str)); assert!(set1.is_subset(&set2)); assert!(!set2.is_subset(&set1)); @@ -107,8 +107,8 @@ fn test_is_subset() { #[test] fn test_is_superset() { - let set1 = NSSet::from_slice(&["one", "two"].map(NSString::from_str)); - let set2 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str)); + let set1 = NSSet::from_id_slice(&["one", "two"].map(NSString::from_str)); + let set2 = NSSet::from_id_slice(&["one", "two", "three"].map(NSString::from_str)); assert!(!set1.is_superset(&set2)); assert!(set2.is_superset(&set1)); @@ -116,9 +116,9 @@ fn test_is_superset() { #[test] fn test_is_disjoint() { - let set1 = NSSet::from_slice(&["one", "two"].map(NSString::from_str)); - let set2 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str)); - let set3 = NSSet::from_slice(&["four", "five", "six"].map(NSString::from_str)); + let set1 = NSSet::from_id_slice(&["one", "two"].map(NSString::from_str)); + let set2 = NSSet::from_id_slice(&["one", "two", "three"].map(NSString::from_str)); + let set3 = NSSet::from_id_slice(&["four", "five", "six"].map(NSString::from_str)); assert!(!set1.is_disjoint(&set2)); assert!(set1.is_disjoint(&set3)); @@ -128,7 +128,7 @@ fn test_is_disjoint() { #[test] fn test_to_array() { let nums = [1, 2, 3]; - let set = NSSet::from_slice(&nums.map(NSNumber::new_i32)); + let set = NSSet::from_id_slice(&nums.map(NSNumber::new_i32)); assert_eq!(set.to_array().len(), 3); assert!(set.to_array().iter().all(|i| nums.contains(&i.as_i32()))); @@ -137,7 +137,7 @@ fn test_to_array() { #[test] fn test_iter() { let nums = [1, 2, 3]; - let set = NSSet::from_slice(&nums.map(NSNumber::new_i32)); + let set = NSSet::from_id_slice(&nums.map(NSNumber::new_i32)); assert_eq!(set.iter().count(), 3); assert!(set.iter().all(|i| nums.contains(&i.as_i32()))); @@ -146,7 +146,7 @@ fn test_iter() { #[test] fn test_iter_fast() { let nums = [1, 2, 3]; - let set = NSSet::from_slice(&nums.map(NSNumber::new_i32)); + let set = NSSet::from_id_slice(&nums.map(NSNumber::new_i32)); assert_eq!(set.iter_fast().count(), 3); assert!(set.iter_fast().all(|i| nums.contains(&i.as_i32()))); @@ -155,7 +155,7 @@ fn test_iter_fast() { #[test] fn test_into_iter() { let nums = [1, 2, 3]; - let set = NSSet::from_slice(&nums.map(NSNumber::new_i32)); + let set = NSSet::from_id_slice(&nums.map(NSNumber::new_i32)); assert!(set.into_iter().all(|i| nums.contains(&i.as_i32()))); } @@ -164,20 +164,14 @@ fn test_into_iter() { #[cfg(feature = "Foundation_NSMutableString")] fn test_into_vec() { let strs = vec![ - Foundation::NSMutableString::from_str("one"), - Foundation::NSMutableString::from_str("two"), - Foundation::NSMutableString::from_str("three"), + Foundation::NSString::from_str("one"), + Foundation::NSString::from_str("two"), + Foundation::NSString::from_str("three"), ]; let set = NSSet::from_vec(strs); - let mut vec = NSSet::into_vec(set); - for str in vec.iter_mut() { - str.appendString(ns_string!(" times zero is zero")); - } - - assert_eq!(vec.len(), 3); - let suffix = ns_string!("zero"); - assert!(vec.iter().all(|str| str.hasSuffix(suffix))); + assert_eq!(set.len(), 3); + assert_eq!(set.to_vec().len(), 3); } #[test] @@ -191,7 +185,7 @@ fn test_equality() { fn test_copy() { use Foundation::NSCopying; - let set1 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str)); + let set1 = NSSet::from_id_slice(&["one", "two", "three"].map(NSString::from_str)); let set2 = set1.copy(); assert_eq!(set1, set2); } @@ -201,7 +195,7 @@ fn test_debug() { let set = NSSet::::new(); assert_eq!(format!("{set:?}"), "{}"); - let set = NSSet::from_slice(&["one", "two"].map(NSString::from_str)); + let set = NSSet::from_id_slice(&["one", "two"].map(NSString::from_str)); assert!(matches!( format!("{set:?}").as_str(), "{\"one\", \"two\"}" | "{\"two\", \"one\"}" @@ -210,14 +204,14 @@ fn test_debug() { #[test] fn test_retains_stored() { - let obj = Id::into_shared(__RcTestObject::new()); + let obj = __RcTestObject::new(); let mut expected = __ThreadTestData::current(); let input = [obj.clone(), obj.clone()]; expected.retain += 2; expected.assert_current(); - let set = NSSet::from_slice(&input); + let set = NSSet::from_id_slice(&input); expected.retain += 1; expected.assert_current(); @@ -247,14 +241,14 @@ fn test_retains_stored() { fn test_nscopying_uses_retain() { use Foundation::{NSCopying, NSMutableCopying}; - let obj = Id::into_shared(__RcTestObject::new()); - let set = NSSet::from_slice(&[obj]); + let obj = __RcTestObject::new(); + let set = NSSet::from_id_slice(&[obj]); let mut expected = __ThreadTestData::current(); let _copy = set.copy(); expected.assert_current(); - let _copy = set.mutable_copy(); + let _copy = set.mutableCopy(); expected.retain += 1; expected.assert_current(); } @@ -265,8 +259,8 @@ fn test_nscopying_uses_retain() { ignore = "this works differently on different framework versions" )] fn test_iter_no_retain() { - let obj = Id::into_shared(__RcTestObject::new()); - let set = NSSet::from_slice(&[obj]); + let obj = __RcTestObject::new(); + let set = NSSet::from_id_slice(&[obj]); let mut expected = __ThreadTestData::current(); let iter = set.iter(); diff --git a/crates/icrate/tests/string.rs b/crates/icrate/tests/string.rs index c5f642a60..3b23a6051 100644 --- a/crates/icrate/tests/string.rs +++ b/crates/icrate/tests/string.rs @@ -82,7 +82,7 @@ fn test_copy() { assert_eq!(Id::as_ptr(&s1), Id::as_ptr(&s2)); assert!(s2.is_kind_of::()); - let s3 = s1.mutable_copy(); + let s3 = s1.mutableCopy(); assert_ne!(Id::as_ptr(&s1), Id::as_ptr(&s3).cast()); assert!(s3.is_kind_of::()); } diff --git a/crates/objc2-encode/src/encoding_box.rs b/crates/objc2-encode/src/encoding_box.rs index 8efd24dc4..d3e05f766 100644 --- a/crates/objc2-encode/src/encoding_box.rs +++ b/crates/objc2-encode/src/encoding_box.rs @@ -17,7 +17,7 @@ use crate::Encoding; /// In `Encoding`, the data is stored in static memory, while in `EncodingBox` /// it is stored on the heap. The former allows storing in constants (which is /// required by the `objc2::encode::Encode` and `objc2::encode::RefEncode` -/// traits), while the latter allows dynamically, such as in the case of +/// traits), while the latter allows dynamic creation, such as in the case of /// parsing encodings. /// /// **This should be considered a _temporary_ restriction**. `Encoding` and diff --git a/crates/objc2/CHANGELOG.md b/crates/objc2/CHANGELOG.md index 5b2f04653..aa134ad48 100644 --- a/crates/objc2/CHANGELOG.md +++ b/crates/objc2/CHANGELOG.md @@ -9,10 +9,51 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added * Added `objc2::rc::autoreleasepool_leaking`, and improve performance of objects `Debug` impls. +* **BREAKING**: Added associated type `ClassType::Mutability`, which replaces + the ownership type on `Id`, and must be specified for all class types. + + An example: + ```rust + // Before + use icrate::Foundation::NSObject; + use objc2::{declare_class, ClassType}; + + declare_class!( + struct MyDelegate; + + unsafe impl ClassType for MyDelegate { + type Super = NSObject; + } + + // ... methods + ); + + // After + use icrate::Foundation::NSObject; + use objc2::mutability::InteriorMutable; + use objc2::{declare_class, ClassType}; + + declare_class!( + struct MyDelegate; + + unsafe impl ClassType for MyDelegate { + type Super = NSObject; + type Mutability = InteriorMutable; // Added + } + + // ... methods + ); + ``` +* Added `ClassType::retain`, which is a safe way to go from a reference `&T` + to an `Id`. +* Added `mutability` module, containing various types that can be specified + for the above. +* Preliminary support for specifying `where` bounds on methods inside + `extern_protocol!` and `extern_methods!`. +* Allow arbitary expressions in `const NAME` in `extern_class!`, + `extern_protocol!` and `declare_class!`. ### Changed -* Made the default ownership in `Id` be `Shared`. This means that you can now - write `Id`, and it'll mean `Id`. * **BREAKING**: `objc2::rc::AutoreleasePool` is now a zero-sized `Copy` type with a lifetime parameter, instead of the lifetime parameter being the reference it was behind. @@ -23,10 +64,44 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Additionally, rename the mutable version to `Id::autorelease_mut`. * **BREAKING**: Moved `VerificationError`, `ProtocolObject` and `ImplementedBy` into the `runtime` module. +* Relaxed a `fmt::Debug` bound on `WeakId`'s own `fmt::Debug` impl. ### Fixed * Fixed using autorelease pools on 32bit macOS and older macOS versions. +### Removed +* **BREAKING**: Removed `rc::SliceId`, since it is trivially implementable + outside `objc2` from the layout guarantees of `rc::Id`. +* **BREAKING**: Removed `Ownership` type parameter from `Id`, as well as + `rc::Ownership`, `rc::Owned`, `rc::Shared`, `Id::from_shared` and + `Id::into_shared`. This functionality has been moved from being at the + "usage-level", to being moved to the "type-level" in the associated type + `ClassType::Mutability`. + + While being slightly more restrictive, it should vastly help you avoid + making mistakes around mutability (e.g. it is usually a mistake to make a + mutable reference `&mut` to an Objective-C object). + + An example: + ```rust + // Before + use objc2::rc::{Id, Shared}; + use objc2::runtime::NSObject; + use objc2::msg_send_id; + + let obj: Id = unsafe { msg_send_id![NSObject::class(), new] }; + + // After + use objc2::rc::Id; + use objc2::runtime::NSObject; + use objc2::msg_send_id; + + let obj: Id = unsafe { msg_send_id![NSObject::class(), new] }; + ``` +* **BREAKING**: Removed `impl TryFrom> for Id` impl since it + did not have a proper error type, making it less useful than just using + `WeakId::load`. + ## 0.3.0-beta.5 - 2023-02-07 diff --git a/crates/objc2/Cargo.toml b/crates/objc2/Cargo.toml index 2bd7b7085..860a3ebd6 100644 --- a/crates/objc2/Cargo.toml +++ b/crates/objc2/Cargo.toml @@ -83,6 +83,7 @@ objc2-proc-macros = { path = "../objc2-proc-macros", version = "0.1.1", optional [dev-dependencies] iai = { version = "0.1", git = "https://github.com/madsmtm/iai", branch = "callgrind" } +static_assertions = "1.1.0" [[bench]] name = "autorelease" diff --git a/crates/objc2/examples/class_with_lifetime.rs b/crates/objc2/examples/class_with_lifetime.rs index 7836a7e32..47e884b89 100644 --- a/crates/objc2/examples/class_with_lifetime.rs +++ b/crates/objc2/examples/class_with_lifetime.rs @@ -7,7 +7,8 @@ use std::marker::PhantomData; use std::sync::Once; use objc2::declare::{ClassBuilder, Ivar, IvarEncode, IvarType}; -use objc2::rc::{Id, Owned}; +use objc2::mutability::Mutable; +use objc2::rc::Id; use objc2::runtime::{Class, NSObject, Sel}; use objc2::{msg_send, msg_send_id, sel}; use objc2::{ClassType, Encoding, Message, RefEncode}; @@ -57,7 +58,7 @@ impl<'a> MyObject<'a> { }) } - pub fn new(number: &'a mut u8) -> Id { + pub fn new(number: &'a mut u8) -> Id { // SAFETY: The lifetime of the reference is properly bound to the // returned type unsafe { msg_send_id![Self::alloc(), initWithPtr: number] } @@ -74,6 +75,7 @@ impl<'a> MyObject<'a> { unsafe impl<'a> ClassType for MyObject<'a> { type Super = NSObject; + type Mutability = Mutable; const NAME: &'static str = "MyObject"; fn class() -> &'static Class { @@ -112,9 +114,13 @@ fn main() { let mut number = 54; let mut obj = MyObject::new(&mut number); - // It is not possible to convert to `Id` since that would - // loose the lifetime information that `MyObject` stores + // It is not possible to convert to `Id` since that would loose + // the lifetime information that `MyObject` stores. // let obj = Id::into_super(obj); + // + // Neither is it possible to clone the object, since it is marked as + // `Mutable` in `ClassType::Mutability`. + // let obj2 = obj.clone(); println!("Number: {}", obj.get()); @@ -123,16 +129,6 @@ fn main() { // println!("Number: {}", number); println!("Number: {}", obj.get()); - let obj = Id::into_shared(obj); - let obj2 = obj.clone(); - - // We gave up ownership above, so can't edit the number any more! - // obj.set(7); - - println!("Number: {}", obj.get()); - println!("Number: {}", obj2.get()); - drop(obj); - drop(obj2); println!("Number: {number}"); } diff --git a/crates/objc2/src/__macro_helpers/declare_class.rs b/crates/objc2/src/__macro_helpers/declare_class.rs index 016193053..371fb63c4 100644 --- a/crates/objc2/src/__macro_helpers/declare_class.rs +++ b/crates/objc2/src/__macro_helpers/declare_class.rs @@ -2,10 +2,11 @@ use core::mem::ManuallyDrop; use core::ptr; use crate::declare::__IdReturnValue; -use crate::rc::{Allocated, Id, Ownership}; -use crate::{Message, MessageReceiver}; +use crate::rc::{Allocated, Id}; +use crate::{ClassType, Message, MessageReceiver}; use super::{CopyOrMutCopy, Init, MaybeUnwrap, New, Other}; +use crate::mutability; // One could imagine a different design where we simply had a method like // `fn convert_receiver()`, but that won't work in `declare_class!` since we @@ -38,11 +39,10 @@ where // // Additionally, the receiver and return type must have the same generic // generic parameter `T`. -impl MessageRecieveId, Ret> for Init +impl MessageRecieveId, Ret> for Init where T: Message, - O: Ownership, - Ret: MaybeOptionId>, + Ret: MaybeOptionId>, { #[inline] fn into_return(obj: Ret) -> __IdReturnValue { @@ -74,7 +74,7 @@ where } } -/// Helper trait for specifying an `Id` or an `Option>`. +/// Helper trait for specifying an `Id` or an `Option>`. /// /// (Both of those are valid return types from declare_class! `#[method_id]`). pub trait MaybeOptionId: MaybeUnwrap { @@ -82,7 +82,7 @@ pub trait MaybeOptionId: MaybeUnwrap { fn autorelease_return(self) -> __IdReturnValue; } -impl MaybeOptionId for Id { +impl MaybeOptionId for Id { #[inline] fn consumed_return(self) -> __IdReturnValue { let ptr: *mut T = Id::consume_as_ptr(ManuallyDrop::new(self)); @@ -96,7 +96,7 @@ impl MaybeOptionId for Id { } } -impl MaybeOptionId for Option> { +impl MaybeOptionId for Option> { #[inline] fn consumed_return(self) -> __IdReturnValue { let ptr: *mut T = self @@ -111,3 +111,70 @@ impl MaybeOptionId for Option> { __IdReturnValue(ptr.cast()) } } + +/// Helper for ensuring that `ClassType::Mutability` is implemented correctly +/// for subclasses. +pub trait ValidSubclassMutability {} + +// Root +impl ValidSubclassMutability for mutability::Root {} +impl ValidSubclassMutability for mutability::Root {} +impl ValidSubclassMutability> + for mutability::Root +where + MS: ?Sized + ClassType>, + IS: ?Sized + ClassType>, +{ +} +impl ValidSubclassMutability for mutability::Root {} +impl ValidSubclassMutability for mutability::Root {} + +// Immutable +impl ValidSubclassMutability for mutability::Immutable {} + +// Mutable +impl ValidSubclassMutability for mutability::Mutable {} + +// ImmutableWithMutableSubclass +impl ValidSubclassMutability> + for mutability::ImmutableWithMutableSubclass +where + MS: ?Sized + ClassType>, + IS: ?Sized + ClassType>, +{ +} +// Only valid when `NSCopying`/`NSMutableCopying` is not implemented! +impl ValidSubclassMutability + for mutability::ImmutableWithMutableSubclass +{ +} + +// MutableWithImmutableSuperclass +// Only valid when `NSCopying`/`NSMutableCopying` is not implemented! +impl ValidSubclassMutability + for mutability::MutableWithImmutableSuperclass +{ +} + +// InteriorMutable +impl ValidSubclassMutability for mutability::InteriorMutable {} +impl ValidSubclassMutability for mutability::InteriorMutable {} + +// MainThreadOnly +impl ValidSubclassMutability for mutability::MainThreadOnly {} + +/// Ensure that: +/// 1. The type is not a root class (it's superclass implements `ClassType`, +/// and it's mutability is not `Root`), and therefore also implements basic +/// memory management methods, as required by `unsafe impl Message`. +/// 2. The mutability is valid according to the superclass' mutability. +#[inline] +pub fn assert_mutability_matches_superclass_mutability() +where + T: ?Sized + ClassType, + T::Super: ClassType, + T::Mutability: mutability::Mutability, + ::Mutability: ValidSubclassMutability, +{ + // Noop +} diff --git a/crates/objc2/src/__macro_helpers/mod.rs b/crates/objc2/src/__macro_helpers/mod.rs index 8601037f2..0cda0d4ce 100644 --- a/crates/objc2/src/__macro_helpers/mod.rs +++ b/crates/objc2/src/__macro_helpers/mod.rs @@ -8,7 +8,7 @@ use crate::declare::ClassBuilder; use crate::declare::MethodImplementation; use crate::encode::Encode; use crate::message::__TupleExtender; -use crate::rc::{Allocated, Id, Ownership}; +use crate::rc::{Allocated, Id}; #[cfg(all(debug_assertions, feature = "verify"))] use crate::runtime::MethodDescription; use crate::runtime::{Class, Object, Protocol, Sel}; @@ -32,7 +32,10 @@ mod cache; mod declare_class; pub use self::cache::{CachedClass, CachedSel}; -pub use self::declare_class::{MaybeOptionId, MessageRecieveId}; +pub use self::declare_class::{ + assert_mutability_matches_superclass_mutability, MaybeOptionId, MessageRecieveId, + ValidSubclassMutability, +}; // Common selectors. // @@ -71,8 +74,8 @@ pub fn new_sel() -> Sel { /// retainable object pointer type - we ensure this by making /// `message_send_id` return `Id`. /// - `init`: The method must be an instance method and must return an -/// Objective-C pointer type - We ensure this by taking `Id`, which -/// means it can't be a class method! +/// Objective-C pointer type - We ensure this by taking +/// `Option>`, which means it can't be a class method! /// /// // TODO: Use an enum instead of u8 here when stable @@ -162,9 +165,9 @@ unsafe fn encountered_error(err: *mut E) -> Id { unsafe { Id::retain(err) }.expect("error parameter should be set if the method returns NULL") } -impl MsgSendId> for New { +impl MsgSendId> for New { #[inline] - unsafe fn send_message_id>>( + unsafe fn send_message_id>>( obj: T, sel: Sel, args: A, @@ -196,9 +199,9 @@ impl MsgSendId<&'_ Class, Allocated> for Alloc { } } -impl MsgSendId>, Id> for Init { +impl MsgSendId>, Id> for Init { #[inline] - unsafe fn send_message_id>>( + unsafe fn send_message_id>>( obj: Option>, sel: Sel, args: A, @@ -217,11 +220,9 @@ impl MsgSendId>, Id } } -impl MsgSendId> - for CopyOrMutCopy -{ +impl MsgSendId> for CopyOrMutCopy { #[inline] - unsafe fn send_message_id>>( + unsafe fn send_message_id>>( obj: T, sel: Sel, args: A, @@ -235,9 +236,9 @@ impl MsgSendId MsgSendId> for Other { +impl MsgSendId> for Other { #[inline] - unsafe fn send_message_id>>( + unsafe fn send_message_id>>( obj: T, sel: Sel, args: A, @@ -264,20 +265,20 @@ pub trait MaybeUnwrap { fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option, args: F::Args) -> Self; } -impl MaybeUnwrap for Option> { - type Input = Id; +impl MaybeUnwrap for Option> { + type Input = Id; #[inline] - fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option>, _args: F::Args) -> Self { + fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option>, _args: F::Args) -> Self { obj } } -impl MaybeUnwrap for Id { - type Input = Id; +impl MaybeUnwrap for Id { + type Input = Id; #[inline] - fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option>, args: F::Args) -> Self { + fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option>, args: F::Args) -> Self { match obj { Some(obj) => obj, None => F::failed(args), @@ -830,21 +831,21 @@ mod tests { #[test] #[should_panic = "failed copying object"] fn test_copy_with_null() { - let obj = Id::into_shared(__RcTestObject::new()); + let obj = __RcTestObject::new(); let _obj: Id<__RcTestObject> = unsafe { msg_send_id![&obj, copyReturningNull] }; } #[test] #[should_panic = "unexpected NULL returned from -[__RcTestObject methodReturningNull]"] fn test_normal_with_null() { - let obj = Id::into_shared(__RcTestObject::new()); + let obj = __RcTestObject::new(); let _obj: Id<__RcTestObject> = unsafe { msg_send_id![&obj, methodReturningNull] }; } #[test] #[should_panic = "unexpected NULL returned from -[__RcTestObject aMethod:]"] fn test_normal_with_param_and_null() { - let obj = Id::into_shared(__RcTestObject::new()); + let obj = __RcTestObject::new(); let _obj: Id<__RcTestObject> = unsafe { msg_send_id![&obj, aMethod: false] }; } diff --git a/crates/objc2/src/class_type.rs b/crates/objc2/src/class_type.rs index fc4a221be..1d1c3ffa4 100644 --- a/crates/objc2/src/class_type.rs +++ b/crates/objc2/src/class_type.rs @@ -1,56 +1,69 @@ use crate::msg_send_id; -use crate::rc::Allocated; +use crate::mutability::{IsAllocableAnyThread, IsRetainable, Mutability}; +use crate::rc::{Allocated, Id}; use crate::runtime::Class; use crate::Message; /// Marks types that represent specific classes. /// -/// Usually it is enough to generically know that a type is messageable, e.g. -/// [`rc::Id`][crate::rc::Id] works with any type that implements the +/// Sometimes it is enough to generically know that a type is messageable, +/// e.g. [`rc::Id`][crate::rc::Id] works with any type that implements the /// [`Message`] trait. But often, you have an object that you know represents -/// a specific Objective-C class - this trait allows you to communicate that -/// to the rest of the type-system. +/// a specific Objective-C class - this trait allows you to communicate that, +/// as well as a few properties of the class to the rest of the type-system. /// -/// This is implemented automatically by the +/// This is implemented automatically for your type by the /// [`declare_class!`][crate::declare_class] and /// [`extern_class!`][crate::extern_class] macros. /// /// /// # Safety /// -/// The class returned by [`Self::class`] must be a subclass of the class that -/// [`Self::Super`] represents, and `as_super`/`as_super_mut` must be -/// implemented correctly. Finally [`Self::NAME`] must be correct. +/// 1. The type must represent a specific class. +/// 2. [`Self::Super`] must be a superclass of the class (or something that +/// represents any object, like [`Object`][crate::runtime::Object]). +/// 3. [`Self::Mutability`] must be specified correctly. /// -/// In pseudocode: -/// ```ignore -/// Self::class().superclass() == ::class() -/// Self::class().name() == Self::NAME -/// ``` +/// Note that very little Objective-C code follows Rust's usual ownership +/// model. If you think your type's mutability should be [`Mutable`], think +/// again, it _very_ rarely should! +/// +/// If you're unsure of what to do, [`InteriorMutable`] + avoiding `&mut` +/// is usually a good starting point. +/// 4. [`Self::NAME`] must be correct. +/// 5. The class returned by [`Self::class`] must be the class that this type +/// represents. +/// +/// [`Mutable`]: crate::mutability::Mutable +/// [`InteriorMutable`]: crate::mutability::InteriorMutable /// /// /// # Examples /// -/// Use the trait to access the [`Class`] of different objects. +/// Use the trait to access the [`Class`] of an object. /// /// ``` /// use objc2::ClassType; /// use objc2::runtime::NSObject; -/// // Get a class object representing `NSObject` +/// +/// // Get the class of `NSObject` /// let cls = ::class(); // Or just `NSObject::class()` +/// assert_eq!(cls.name(), NSObject::NAME); /// ``` /// /// Use the [`extern_class!`][crate::extern_class] macro to implement this /// trait for a type. /// -/// ```ignore -/// use objc2::{extern_class, ClassType}; +/// ```no_run +/// use objc2::runtime::NSObject; +/// use objc2::{extern_class, mutability, ClassType}; /// /// extern_class!( /// struct MyClass; /// /// unsafe impl ClassType for MyClass { /// type Super = NSObject; +/// type Mutability = mutability::InteriorMutable; /// } /// ); /// @@ -62,14 +75,23 @@ pub unsafe trait ClassType: Message { /// If you have implemented [`Deref`] for your type, it is highly /// recommended that this is equal to [`Deref::Target`]. /// - /// This may be [`runtime::Object`] if the class is a root class. + /// This may be [`Object`] if the class is a root class. /// /// [`Deref`]: std::ops::Deref /// [`Deref::Target`]: std::ops::Deref::Target - /// [`runtime::Object`]: crate::runtime::Object + /// [`Object`]: crate::runtime::Object type Super: Message; + /// Whether the type is mutable or immutable. + /// + /// See the [`mutability`][crate::mutability] module for further details + /// about class mutability. + type Mutability: Mutability; + /// The name of the Objective-C class that this type represents. + /// + /// `T::NAME` is essentially just the `const` version of + /// `T::class().name()`. const NAME: &'static str; /// Get a reference to the Objective-C class that this type represents. @@ -90,20 +112,87 @@ pub unsafe trait ClassType: Message { fn as_super(&self) -> &Self::Super; /// Get a mutable reference to the superclass. + // Note: No `Self: IsMutable` bound required here, since there is no way + // to get `&mut self` in the first place. + // + // Or at least, if we have `&mut NSMutableString`, we're allowed to get + // `&mut NSString`, and from that it will also make sense to allow getting + // `&mut NSObject`. fn as_super_mut(&mut self) -> &mut Self::Super; + /// Increment the reference count of the receiver. + /// + /// This extends the duration in which the receiver is alive by detaching + /// it from the lifetime information carried by the reference. + /// + /// This is similar to using [`Clone` on `Id`][clone-id], with the + /// addition that it can be used on a plain reference. Note however that + /// this is not possible to use on certain types like `NSString`, since + /// if you only hold `&NSString`, that may have come from + /// `&mut NSMutableString`, in which case it would be unsound to erase the + /// lifetime information carried by the reference. + /// + /// In cases like that, you should rather use `NSCopying::copy` (since + /// that gives you a `NSString` whether the string was originally a + /// `NSString` or a `NSMutableString`). + /// + /// [clone-id]: crate::rc::Id#impl-Clone-for-Id + // + // Note: We could have placed this on `mutability::IsRetainable`, but + // `ClassType` is more often already in scope, allowing easier access to + // `obj.retain()`. + #[inline] + #[doc(alias = "objc_retain")] + fn retain(&self) -> Id + where + Self: IsRetainable, + Self: Sized, // Temporary + { + let ptr: *const Self = self; + let ptr: *mut Self = ptr as _; + // SAFETY: + // - The object is known to not be mutable (or have a mutable + // subclass) due to the `IsRetainable` bound. + // - The pointer is valid since it came from `&self`. + // - The lifetime of the pointer itself is, naturally, extended, + // but any lifetime that the object may carry is still kept within + // the type itself. + let obj = unsafe { Id::retain(ptr) }; + // SAFETY: The pointer came from `&self`, which is always non-null + // (and objc_retain always returns the same value). + unsafe { obj.unwrap_unchecked() } + } + /// Allocate a new instance of the class. /// /// The return value can be used directly inside [`msg_send_id!`] to /// initialize the object. /// + /// For classes that are only usable on the main thread, you can use + /// `MainThreadMarker::alloc` instead. + /// /// [`msg_send_id!`]: crate::msg_send_id + // + // Note: We could have placed this on `mutability::IsAllocableAnyThread`, + // but `ClassType` is more often already in scope, allowing easier access + // to `T::alloc()`. #[inline] - fn alloc() -> Option> { + fn alloc() -> Option> + where + Self: IsAllocableAnyThread, + { // SAFETY: // - It is always safe to (attempt to) allocate an object. // - The object is of the correct type, since we've used the class // from `Self::class`. + // - The object is safe to `dealloc` on the current thread (due to the + // `IsAllocableAnyThread` bound which guarantees it is not + // `MainThreadOnly`). + // + // See also `MainThreadMarker::alloc`. unsafe { msg_send_id![Self::class(), alloc] } } + + // TODO: `fn alloc_on_main(mtm: MainThreadMarker)` + // TODO: `fn mtm(&self) -> MainThreadMarker where T::Mutability: MainThreadOnly` } diff --git a/crates/objc2/src/declare/declare_class_tests.rs b/crates/objc2/src/declare/declare_class_tests.rs index 971f0dca8..e4932ad87 100644 --- a/crates/objc2/src/declare/declare_class_tests.rs +++ b/crates/objc2/src/declare/declare_class_tests.rs @@ -2,7 +2,8 @@ use core::ptr; use crate::declare::IvarEncode; -use crate::rc::{Id, Owned}; +use crate::mutability::{Immutable, Mutable}; +use crate::rc::Id; use crate::runtime::NSObject; use crate::{declare_class, extern_methods, sel, ClassType}; @@ -14,6 +15,7 @@ declare_class!( unsafe impl ClassType for DeclareClassDepreactedMethod { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "DeclareClassDepreactedMethod"; } @@ -44,6 +46,7 @@ declare_class!( unsafe impl ClassType for DeclareClassCfg { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "DeclareClassCfg"; } @@ -185,6 +188,7 @@ declare_class!( unsafe impl ClassType for TestMultipleColonSelector { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "TestMultipleColonSelector"; } @@ -257,6 +261,7 @@ declare_class!( unsafe impl ClassType for DeclareClassAllTheBool { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "DeclareClassAllTheBool"; } @@ -319,6 +324,7 @@ declare_class!( unsafe impl ClassType for DeclareClassUnreachable { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "DeclareClassUnreachable"; } @@ -374,6 +380,7 @@ fn test_duplicate_ivar() { unsafe impl ClassType for DeclareClassDuplicateIvar { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "DeclareClassDuplicateIvar"; } ); @@ -393,6 +400,7 @@ fn test_subclass_duplicate_ivar() { unsafe impl ClassType for Cls { type Super = NSObject; + type Mutability = Mutable; const NAME: &'static str = "DeclareClassDuplicateIvarSuperclass"; } ); @@ -406,6 +414,7 @@ fn test_subclass_duplicate_ivar() { unsafe impl ClassType for SubCls { type Super = Cls; + type Mutability = Mutable; const NAME: &'static str = "DeclareClassDuplicateIvarSubclass"; } ); @@ -413,7 +422,7 @@ fn test_subclass_duplicate_ivar() { extern_methods!( unsafe impl SubCls { #[method_id(new)] - fn new() -> Id; + fn new() -> Id; } ); @@ -450,6 +459,7 @@ declare_class!( unsafe impl ClassType for OutParam { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "OutParam"; } @@ -492,27 +502,27 @@ mod out_param { ); #[test] - #[should_panic = "`&mut Id<_, _>` is not supported in `declare_class!` yet"] + #[should_panic = "`&mut Id<_>` is not supported in `declare_class!` yet"] fn out_param1() { let mut param = OutParam::new(); OutParam::unsupported1(&mut param); } #[test] - #[should_panic = "`Option<&mut Id<_, _>>` is not supported in `declare_class!` yet"] + #[should_panic = "`Option<&mut Id<_>>` is not supported in `declare_class!` yet"] fn out_param2() { OutParam::unsupported2(None); } #[test] - #[should_panic = "`&mut Option>` is not supported in `declare_class!` yet"] + #[should_panic = "`&mut Option>` is not supported in `declare_class!` yet"] fn out_param3() { let mut param = Some(OutParam::new()); OutParam::unsupported3(&mut param); } #[test] - #[should_panic = "`Option<&mut Option>>` is not supported in `declare_class!` yet"] + #[should_panic = "`Option<&mut Option>>` is not supported in `declare_class!` yet"] fn out_param4() { OutParam::unsupported4(None); } diff --git a/crates/objc2/src/declare/ivar.rs b/crates/objc2/src/declare/ivar.rs index 34865ed9e..3d0847fb1 100644 --- a/crates/objc2/src/declare/ivar.rs +++ b/crates/objc2/src/declare/ivar.rs @@ -318,6 +318,7 @@ mod tests { use super::*; use crate::declare::{IvarBool, IvarEncode}; + use crate::mutability::Mutable; use crate::rc::Id; use crate::runtime::NSObject; use crate::{declare_class, msg_send, msg_send_id, test_utils, ClassType, MessageReceiver}; @@ -374,6 +375,7 @@ mod tests { unsafe impl ClassType for CustomDrop { type Super = NSObject; + type Mutability = Mutable; const NAME: &'static str = "CustomDrop"; } ); diff --git a/crates/objc2/src/declare/ivar_drop.rs b/crates/objc2/src/declare/ivar_drop.rs index af030c383..e1a9d6905 100644 --- a/crates/objc2/src/declare/ivar_drop.rs +++ b/crates/objc2/src/declare/ivar_drop.rs @@ -2,7 +2,7 @@ use alloc::boxed::Box; use core::ffi::c_void; use crate::encode::{Encode, Encoding}; -use crate::rc::{Id, Ownership}; +use crate::rc::Id; use crate::Message; use super::InnerIvarType; @@ -21,8 +21,8 @@ mod private { /// This currently works with the following types: /// - `Box` /// - `Option>` -/// - `Id` -/// - `Option>` +/// - `Id` +/// - `Option>` /// /// Further may be added when the standard library guarantee their layout. #[repr(transparent)] @@ -95,13 +95,13 @@ unsafe impl InnerIvarType for IvarDrop>> { } } -unsafe impl Encode for IvarDrop> { +unsafe impl Encode for IvarDrop> { const ENCODING: Encoding = <*const T>::ENCODING; } -// SAFETY: `Option>` is safe to zero-initialize -unsafe impl private::IvarDropHelper for Id { - type Inner = Option>; +// SAFETY: `Option>` is safe to zero-initialize +unsafe impl private::IvarDropHelper for Id { + type Inner = Option>; } // SAFETY: `Id` is `NonNull`, and hence safe to store as a pointer. @@ -109,11 +109,11 @@ unsafe impl private::IvarDropHelper for Id { // The user ensures that the Id has been initialized in an `init` method // before being used. // -// Note: We could technically do `impl InnerIvarType for Ivar>` +// Note: We could technically do `impl InnerIvarType for Ivar>` // directly today, but since we can't do so for `Box` (because that is // `#[fundamental]`), I think it makes sense to handle them similarly. -unsafe impl InnerIvarType for IvarDrop> { - type Output = Id; +unsafe impl InnerIvarType for IvarDrop> { + type Output = Id; #[inline] unsafe fn __deref(&self) -> &Self::Output { @@ -132,21 +132,21 @@ unsafe impl InnerIvarType for IvarDrop> { } } -unsafe impl Encode for IvarDrop>> { +unsafe impl Encode for IvarDrop>> { const ENCODING: Encoding = <*const T>::ENCODING; } -// SAFETY: `Option>` is safe to zero-initialize -unsafe impl private::IvarDropHelper for Option> { - type Inner = Option>; +// SAFETY: `Option>` is safe to zero-initialize +unsafe impl private::IvarDropHelper for Option> { + type Inner = Option>; } -// SAFETY: `Id` guarantees the null-pointer optimization. +// SAFETY: `Id` guarantees the null-pointer optimization. // // This is valid to initialize as all-zeroes, so the user doesn't have to do // anything to initialize it. -unsafe impl InnerIvarType for IvarDrop>> { - type Output = Option>; +unsafe impl InnerIvarType for IvarDrop>> { + type Output = Option>; #[inline] unsafe fn __deref(&self) -> &Self::Output { @@ -204,9 +204,9 @@ unsafe fn box_unreachable() -> ! { mod tests { use super::*; use crate::declare::{Ivar, IvarType}; - use crate::rc::{Allocated, Owned, __RcTestObject, __ThreadTestData}; + use crate::mutability::Mutable; + use crate::rc::{Allocated, __RcTestObject, __ThreadTestData}; use crate::runtime::NSObject; - use crate::runtime::Object; use crate::{declare_class, msg_send, msg_send_id, ClassType}; struct TestIvar1; @@ -223,13 +223,13 @@ mod tests { struct TestIvar3; unsafe impl IvarType for TestIvar3 { - type Type = IvarDrop>; + type Type = IvarDrop>; const NAME: &'static str = "_abc"; } struct TestIvar4; unsafe impl IvarType for TestIvar4 { - type Type = IvarDrop>>; + type Type = IvarDrop>>; const NAME: &'static str = "_abc"; } @@ -237,15 +237,16 @@ mod tests { #[derive(Debug, PartialEq, Eq)] struct IvarTester { ivar1: IvarDrop, "_ivar1">, - ivar2: IvarDrop>, "_ivar2">, - ivar3: IvarDrop>, "_ivar3">, - ivar4: IvarDrop>>, "_ivar4">, + ivar2: IvarDrop>, "_ivar2">, + ivar3: IvarDrop>, "_ivar3">, + ivar4: IvarDrop>>, "_ivar4">, } mod ivartester; unsafe impl ClassType for IvarTester { type Super = NSObject; + type Mutability = Mutable; const NAME: &'static str = "IvarTester"; } @@ -254,7 +255,7 @@ mod tests { fn init(&mut self) -> Option<&mut Self> { let this: Option<&mut Self> = unsafe { msg_send![super(self), init] }; this.map(|this| { - Ivar::write(&mut this.ivar1, Id::into_shared(__RcTestObject::new())); + Ivar::write(&mut this.ivar1, __RcTestObject::new()); *this.ivar2 = Some(__RcTestObject::new()); Ivar::write(&mut this.ivar3, Box::new(__RcTestObject::new())); *this.ivar4 = Some(Box::new(__RcTestObject::new())); @@ -282,6 +283,7 @@ mod tests { unsafe impl ClassType for IvarTesterSubclass { type Super = IvarTester; + type Mutability = Mutable; const NAME: &'static str = "IvarTesterSubclass"; } @@ -290,7 +292,7 @@ mod tests { fn init(&mut self) -> Option<&mut Self> { let this: Option<&mut Self> = unsafe { msg_send![super(self), init] }; this.map(|this| { - Ivar::write(&mut this.ivar5, Id::into_shared(__RcTestObject::new())); + Ivar::write(&mut this.ivar5, __RcTestObject::new()); this }) } @@ -312,7 +314,7 @@ mod tests { fn test_init_drop() { let mut expected = __ThreadTestData::current(); - let mut obj: Id = unsafe { msg_send_id![IvarTester::class(), new] }; + let mut obj: Id = unsafe { msg_send_id![IvarTester::class(), new] }; expected.alloc += 4; expected.init += 4; expected.assert_current(); @@ -337,7 +339,7 @@ mod tests { fn test_subclass() { let mut expected = __ThreadTestData::current(); - let mut obj: Id = + let mut obj: Id = unsafe { msg_send_id![IvarTesterSubclass::class(), new] }; expected.alloc += 5; expected.init += 5; @@ -359,7 +361,7 @@ mod tests { #[cfg_attr(not(debug_assertions), ignore = "only panics in debug mode")] #[should_panic = "an Id in instance variables must always be initialized before use"] fn test_init_invalid_ref() { - let obj: Id = unsafe { msg_send_id![IvarTester::alloc(), initInvalid] }; + let obj: Id = unsafe { msg_send_id![IvarTester::alloc(), initInvalid] }; std::println!("{:?}", obj.ivar1); } @@ -368,9 +370,8 @@ mod tests { #[cfg_attr(not(debug_assertions), ignore = "only panics in debug mode")] #[should_panic = "an Id in instance variables must always be initialized before use"] fn test_init_invalid_mut() { - let mut obj: Id = - unsafe { msg_send_id![IvarTester::alloc(), initInvalid] }; + let mut obj: Id = unsafe { msg_send_id![IvarTester::alloc(), initInvalid] }; - *obj.ivar1 = __RcTestObject::new().into(); + *obj.ivar1 = __RcTestObject::new(); } } diff --git a/crates/objc2/src/declare/mod.rs b/crates/objc2/src/declare/mod.rs index d563eac30..66a1322b7 100644 --- a/crates/objc2/src/declare/mod.rs +++ b/crates/objc2/src/declare/mod.rs @@ -1,4 +1,4 @@ -//! Functionality for dynamically declaring Objective-C classes. +//! # Dynamically creating classes and protocols. //! //! Classes can be declared using the [`ClassBuilder`] struct. Instance //! variables and methods can then be added before the class is ultimately @@ -8,15 +8,18 @@ //! Consider using the [`declare_class!`][crate::declare_class] macro instead. //! //! -//! # Example +//! ## Example //! //! The following example demonstrates declaring a class named `MyNumber` that //! has one ivar, a `u32` named `_number` and a few methods for constructor -//! methods and methods for interfacing with the number. +//! methods and methods for interfacing with the number (using interior +//! mutability, as is common for Objective-C objects). //! //! ``` +//! use core::cell::Cell; +//! //! use objc2::declare::ClassBuilder; -//! use objc2::rc::{Id, Owned}; +//! use objc2::rc::Id; //! use objc2::runtime::{Class, Object, NSObject, Sel}; //! use objc2::{sel, msg_send, msg_send_id, ClassType}; //! @@ -25,10 +28,14 @@ //! let mut builder = ClassBuilder::new("MyNumber", NSObject::class()) //! .expect("a class with the name MyNumber likely already exists"); //! -//! // Add an instance variable of type `u32` -//! builder.add_ivar::("_number"); +//! // Add an instance variable of type `Cell` +//! builder.add_ivar::>("_number"); //! //! // Add an Objective-C method for initializing an instance with a number +//! // +//! // We "cheat" a bit here, and use `Object` instead of `NSObject`, +//! // since only the former is allowed to be a mutable receiver (which is +//! // always safe in `init` methods, but not in others). //! unsafe extern "C" fn init_with_number( //! this: &mut Object, //! _cmd: Sel, @@ -37,7 +44,7 @@ //! let this: Option<&mut Object> = msg_send![super(this, NSObject::class()), init]; //! this.map(|this| { //! // SAFETY: The ivar is added with the same type above -//! this.set_ivar::("_number", number); +//! this.set_ivar::>("_number", Cell::new(number)); //! this //! }) //! } @@ -53,8 +60,8 @@ //! cls: &Class, //! _cmd: Sel, //! number: u32, -//! ) -> *mut Object { -//! let obj: Option> = unsafe { +//! ) -> *mut NSObject { +//! let obj: Option> = unsafe { //! msg_send_id![ //! msg_send_id![cls, alloc], //! initWithNumber: number, @@ -70,18 +77,18 @@ //! } //! //! // Add an Objective-C method for setting the number -//! extern "C" fn my_number_set(this: &mut Object, _cmd: Sel, number: u32) { +//! extern "C" fn my_number_set(this: &NSObject, _cmd: Sel, number: u32) { //! // SAFETY: The ivar is added with the same type above -//! unsafe { this.set_ivar::("_number", number) } +//! unsafe { this.ivar::>("_number") }.set(number); //! } //! unsafe { //! builder.add_method(sel!(setNumber:), my_number_set as extern "C" fn(_, _, _)); //! } //! //! // Add an Objective-C method for getting the number -//! extern "C" fn my_number_get(this: &Object, _cmd: Sel) -> u32 { +//! extern "C" fn my_number_get(this: &NSObject, _cmd: Sel) -> u32 { //! // SAFETY: The ivar is added with the same type above -//! unsafe { *this.ivar::("_number") } +//! unsafe { this.ivar::>("_number") }.get() //! } //! unsafe { //! builder.add_method(sel!(number), my_number_get as extern "C" fn(_, _) -> _); @@ -96,14 +103,14 @@ //! // with `std::sync::Once` or the `once_cell` crate. //! let cls = register_class(); //! -//! let mut obj: Id = unsafe { +//! let obj: Id = unsafe { //! msg_send_id![cls, withNumber: 42u32] //! }; //! //! let n: u32 = unsafe { msg_send![&obj, number] }; //! assert_eq!(n, 42); //! -//! let _: () = unsafe { msg_send![&mut obj, setNumber: 12u32] }; +//! let _: () = unsafe { msg_send![&obj, setNumber: 12u32] }; //! let n: u32 = unsafe { msg_send![&obj, number] }; //! assert_eq!(n, 12); //! ``` @@ -127,6 +134,7 @@ use std::ffi::CString; use crate::encode::__unstable::{EncodeArguments, EncodeReturn}; use crate::encode::{Encode, Encoding, RefEncode}; use crate::ffi; +use crate::mutability::IsMutable; use crate::rc::Allocated; use crate::runtime::{Bool, Class, Imp, Object, Protocol, Sel}; use crate::sel; @@ -158,17 +166,17 @@ pub trait MethodImplementation: private::Sealed { } macro_rules! method_decl_impl { - (@<$($l:lifetime),*> T, $r:ident, $f:ty, $($t:ident),*) => { + (@<$($l:lifetime),*> T: $t_bound:ident, $r:ident, $f:ty, $($t:ident),*) => { impl<$($l,)* T, $r, $($t),*> private::Sealed for $f where - T: Message + ?Sized, + T: ?Sized + $t_bound, $r: EncodeReturn, $($t: Encode,)* {} impl<$($l,)* T, $r, $($t),*> MethodImplementation for $f where - T: Message + ?Sized, + T: ?Sized + $t_bound, $r: EncodeReturn, $($t: Encode,)* { @@ -181,7 +189,7 @@ macro_rules! method_decl_impl { } } }; - (@<$($l:lifetime),*> Class, $r:ident, $f:ty, $($t:ident),*) => { + (@<$($l:lifetime),*> $callee:ident, $r:ident, $f:ty, $($t:ident),*) => { impl<$($l,)* $r, $($t),*> private::Sealed for $f where $r: EncodeReturn, @@ -193,7 +201,7 @@ macro_rules! method_decl_impl { $r: EncodeReturn, $($t: Encode,)* { - type Callee = Class; + type Callee = $callee; type Ret = $r; type Args = ($($t,)*); @@ -206,14 +214,14 @@ macro_rules! method_decl_impl { #[doc(hidden)] impl private::Sealed for $f where - T: Message + ?Sized, + T: ?Sized + Message, $($t: Encode,)* {} #[doc(hidden)] impl MethodImplementation for $f where - T: Message + ?Sized, + T: ?Sized + Message, $($t: Encode,)* { type Callee = T; @@ -234,12 +242,15 @@ macro_rules! method_decl_impl { } }; (# $abi:literal; $($t:ident),*) => { - method_decl_impl!(@<'a> T, R, extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(@<'a> T, R, extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(@<> T, R, unsafe extern $abi fn(*const T, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(@<> T, R, unsafe extern $abi fn(*mut T, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(@<'a> T, R, unsafe extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(@<'a> T, R, unsafe extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(@<'a> T: Message, R, extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(@<'a> T: IsMutable, R, extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(@<> T: Message, R, unsafe extern $abi fn(*const T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(@<> T: Message, R, unsafe extern $abi fn(*mut T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(@<'a> T: Message, R, unsafe extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(@<'a> T: IsMutable, R, unsafe extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*); + + method_decl_impl!(@<'a> Object, R, extern $abi fn(&'a mut Object, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(@<'a> Object, R, unsafe extern $abi fn(&'a mut Object, Sel $(, $t)*) -> R, $($t),*); method_decl_impl!(@<'a> Class, R, extern $abi fn(&'a Class, Sel $(, $t)*) -> R, $($t),*); method_decl_impl!(@<> Class, R, unsafe extern $abi fn(*const Class, Sel $(, $t)*) -> R, $($t),*); @@ -692,22 +703,11 @@ impl ProtocolBuilder { #[cfg(test)] mod tests { use super::*; + use crate::mutability::Immutable; use crate::rc::Id; - use crate::runtime::{NSObject, NSZone}; + use crate::runtime::{NSObject, NSZone, __NSCopying as NSCopying}; use crate::test_utils; - use crate::{declare_class, extern_protocol, msg_send, ClassType, ProtocolType}; - - extern_protocol!( - #[allow(clippy::missing_safety_doc)] - unsafe trait NSCopying { - #[method_id(copyWithZone:)] - fn copy_with_zone(&self, _zone: *const NSZone) -> Id; - } - - unsafe impl ProtocolType for dyn NSCopying { - const NAME: &'static str = "NSCopying"; - } - ); + use crate::{declare_class, msg_send, ClassType, ProtocolType}; #[test] fn test_classbuilder_duplicate() { @@ -851,6 +851,7 @@ mod tests { unsafe impl ClassType for Custom1 { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "TestDeclareClassDuplicate"; } ); @@ -860,6 +861,7 @@ mod tests { unsafe impl ClassType for Custom2 { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "TestDeclareClassDuplicate"; } ); @@ -876,6 +878,7 @@ mod tests { unsafe impl ClassType for Custom { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "TestDeclareClassProtocolNotFound"; } @@ -902,6 +905,7 @@ mod tests { unsafe impl ClassType for Custom { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "TestDeclareClassInvalidMethod"; } @@ -926,6 +930,7 @@ mod tests { unsafe impl ClassType for Custom { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "TestDeclareClassMissingProtocolMethod"; } @@ -945,6 +950,7 @@ mod tests { unsafe impl ClassType for Custom { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "TestDeclareClassInvalidProtocolMethod"; } @@ -971,6 +977,7 @@ mod tests { unsafe impl ClassType for Custom { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "TestDeclareClassExtraProtocolMethod"; } @@ -997,10 +1004,12 @@ mod tests { unsafe impl RefEncode for GenericDeclareClass { const ENCODING_REF: Encoding = Encoding::Object; } + unsafe impl Message for GenericDeclareClass {} unsafe impl ClassType for GenericDeclareClass { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "GenericDeclareClass"; #[inline] diff --git a/crates/objc2/src/encode/__unstable.rs b/crates/objc2/src/encode/__unstable.rs index 0bd1f7f95..9f1a1e4ba 100644 --- a/crates/objc2/src/encode/__unstable.rs +++ b/crates/objc2/src/encode/__unstable.rs @@ -18,7 +18,7 @@ )] use crate::encode::{Encode, Encoding}; -use crate::rc::{Id, Ownership}; +use crate::rc::Id; use crate::runtime::Bool; use crate::Message; @@ -56,10 +56,10 @@ impl convert_private::Sealed for T {} impl convert_private::Sealed for bool {} // Implemented in rc/writeback.rs -impl convert_private::Sealed for &mut Id {} -impl convert_private::Sealed for Option<&mut Id> {} -impl convert_private::Sealed for &mut Option> {} -impl convert_private::Sealed for Option<&mut Option>> {} +impl convert_private::Sealed for &mut Id {} +impl convert_private::Sealed for Option<&mut Id> {} +impl convert_private::Sealed for &mut Option> {} +impl convert_private::Sealed for Option<&mut Option>> {} /// Represents types that can easily be converted to/from an [`Encode`] type. /// @@ -67,7 +67,7 @@ impl convert_private::Sealed for Option<&mut Option`-like arguments, to allow +/// This is also done specially for `&mut Id<_>`-like arguments, to allow /// using those as "out" parameters. pub trait EncodeConvertArgument: convert_private::Sealed { /// The inner type that this can be converted to and from. diff --git a/crates/objc2/src/encode/mod.rs b/crates/objc2/src/encode/mod.rs index fc7373c64..30a4d1c4b 100644 --- a/crates/objc2/src/encode/mod.rs +++ b/crates/objc2/src/encode/mod.rs @@ -1,4 +1,4 @@ -//! # Support for type-encodings +//! # Support for type-encodings. //! //! This module contains traits for annotating types that has an Objective-C //! type-encoding: Specifically [`Encode`] for structs/numeric types and diff --git a/crates/objc2/src/exception.rs b/crates/objc2/src/exception.rs index a5e0266b9..1e95cc74e 100644 --- a/crates/objc2/src/exception.rs +++ b/crates/objc2/src/exception.rs @@ -1,4 +1,4 @@ -//! Objective-C's @throw and @try/@catch. +//! # `@throw` and `@try/@catch` exceptions. //! //! By default, if the [`msg_send!`] macro causes an exception to be thrown, //! this will unwind into Rust, resulting in undefined behavior. However, this @@ -6,8 +6,8 @@ //! [`msg_send!`] in a `@catch` and panics if an exception is caught, //! preventing Objective-C from unwinding into Rust. //! -//! The `@try`/`@catch` functionality in this module is only available when -//! the `"exception"` feature is enabled. +//! Most of the functionality in this module is only available when the +//! `"exception"` feature is enabled. //! //! See the following links for more information: //! - [Exception Programming Topics for Cocoa](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Exceptions/Exceptions.html) @@ -181,6 +181,8 @@ impl RefUnwindSafe for Exception {} #[inline] #[cfg(feature = "exception")] // For consistency, not strictly required pub unsafe fn throw(exception: Id) -> ! { + // We consume the exception object since we can't make any guarantees + // about its mutability. let ptr = exception.0.as_ptr() as *mut ffi::objc_object; // SAFETY: Object is valid and non-null (nil exceptions are not valid in // the old runtime). @@ -235,11 +237,9 @@ unsafe fn try_no_ret(closure: F) -> Result<(), Option // SAFETY: // The exception is always a valid object or NULL. // - // The ownership is safe as Shared; Objective-C code throwing an - // exception knows that they don't hold sole access to that exception - // instance any more, and Rust code is forbidden by requiring a Shared - // Id in `throw` (instead of just a shared reference, which could have - // come from an Owned Id). + // Code throwing an exception know that they don't hold sole access to + // that object any more, so even if the type was originally mutable, + // it is okay to create a new `Id` to it here. Err(unsafe { Id::new(exception.cast()) }) } } @@ -257,7 +257,8 @@ unsafe fn try_no_ret(closure: F) -> Result<(), Option /// /// The exception is `None` in the extremely exceptional case that the /// exception object is `nil`. This should basically never happen, but is -/// technically possible on some systems with `@throw nil`. +/// technically possible on some systems with `@throw nil`, or in OOM +/// situations. /// /// [`catch_unwind`]: std::panic::catch_unwind /// @@ -294,7 +295,6 @@ mod tests { use super::*; use crate::runtime::NSObject; - use crate::{msg_send_id, ClassType}; #[test] fn test_catch() { @@ -328,9 +328,10 @@ mod tests { #[test] fn test_throw_catch_object() { - let obj: Id = unsafe { msg_send_id![NSObject::class(), new] }; + let obj = NSObject::new(); // TODO: Investigate why this is required on GNUStep! let _obj2 = obj.clone(); + let obj: Id = unsafe { Id::cast(obj) }; let ptr: *const Exception = &*obj; let result = unsafe { catch(|| throw(obj)) }; diff --git a/crates/objc2/src/lib.rs b/crates/objc2/src/lib.rs index 72f884ebc..595a99d5c 100644 --- a/crates/objc2/src/lib.rs +++ b/crates/objc2/src/lib.rs @@ -212,6 +212,7 @@ pub mod encode; pub mod exception; mod macros; mod message; +pub mod mutability; mod protocol_type; pub mod rc; pub mod runtime; diff --git a/crates/objc2/src/macros/declare_class.rs b/crates/objc2/src/macros/declare_class.rs index d4392a85c..d8a611679 100644 --- a/crates/objc2/src/macros/declare_class.rs +++ b/crates/objc2/src/macros/declare_class.rs @@ -1,31 +1,44 @@ -/// Declare a new Objective-C class. +/// Declare a new class. /// -/// This is mostly just a convenience macro on top of [`extern_class!`] and -/// the functionality in the [`declare`] module, but it can really help -/// with cutting down on boilerplate, in particular when defining delegate -/// classes! +/// This is useful in many cases since Objective-C frameworks tend to favour a +/// design pattern using "delegates", where to hook into a piece of +/// functionality in a class, you implement that class' delegate protocol in +/// a custom class. +/// +/// This macro is the declarative way of creating classes, in contrast with +/// the [`declare`] module which mostly contain ways of declaring classes in +/// an imperative fashion. It is highly recommended that you use this macro +/// though, since it contains a lot of extra debug assertions and niceties +/// that help ensure the soundness of your code. /// -/// [`extern_class!`]: crate::extern_class /// [`declare`]: crate::declare /// /// /// # Specification /// -/// This macro consists of three parts: -/// - The class definition + ivar definition + inheritance specification. -/// - A set of method definitions. -/// - A set of protocol definitions. +/// This macro consists of roughly four parts: +/// - The type and ivar definition. +/// - The [`ClassType`] implementation. +/// - Any number of method definitions. +/// - Any number of protocol implementations. +/// +/// With the syntax generally resembling a combination of that in +/// [`extern_class!`] and [`extern_methods!`]. /// +/// [`ClassType`]: crate::ClassType +/// [`extern_class!`]: crate::extern_class +/// [`extern_methods!`]: crate::extern_methods /// -/// ## Class and ivar definition /// -/// The class definition works a lot like [`extern_class!`], with the added -/// functionality that you can define custom instance variables on your class, -/// which are then wrapped in a [`declare::Ivar`] with the given name, and -/// made accessible through the class. (E.g. you can use `self.my_ivar` as if -/// it was a normal Rust struct). +/// ## Ivar definition /// -/// The instance variable names are specified as such: +/// The type definition works a lot like [`extern_class!`] (including the +/// allowed attributes), with the added capability that struct fields are +/// automatically defined as custom instance variables, which are then +/// accessible on instances of the class. (E.g. you can use `self.my_ivar` as +/// if the class was a normal Rust struct). +/// +/// The instance variables are specified as such: /// - [`IvarEncode`](crate::declare::IvarEncode) /// - [`IvarBool<"my_crate_ivar">`](crate::declare::IvarBool) /// - [`IvarDrop`](crate::declare::IvarDrop) @@ -37,19 +50,24 @@ /// superclass' instance variables - this means is is good practice to name /// them with a prefix of your crate name, or similar. /// -/// The class name must be specified in `ClassType::NAME`, and it must be -/// unique across the entire application. Good practice here is similarly to -/// include your crate name in the prefix. +/// [`declare::IvarType`]: crate::declare::IvarType +/// +/// +/// ## `ClassType` implementation +/// +/// This also resembles that in [`extern_class!`], except that +/// [`ClassType::NAME`] must be specified, and it must be unique across the +/// entire application. Good practice here is to include your crate name in +/// the prefix. /// /// The class is guaranteed to have been created and registered with the /// Objective-C runtime after the [`ClassType::class`] function has been /// called. /// /// The macro will generate a `dealloc` method for you, which will call any -/// `Drop` impl the class may have. +/// [`Drop`] impl you may have defined on the type. /// -/// [`declare::Ivar`]: crate::declare::Ivar -/// [`declare::IvarType`]: crate::declare::IvarType +/// [`ClassType::NAME`]: crate::ClassType::NAME /// [`ClassType::class`]: crate::ClassType::class /// /// @@ -62,12 +80,19 @@ /// your method will be registered as an instance method, and if you don't it /// will be registered as a class method. /// +/// On instance methods, you can freely choose between different types of +/// receivers, e.g. `&self`, `this: *const Self`, `&mut self`, and so on. Note +/// though that using raw pointers requires the function to be `unsafe`, and +/// using `&mut self` requires the class' mutability to be [`IsMutable`]. +/// If you require mutating your class' instance variables, consider using +/// [`Cell`] or similar instead. +/// /// The desired selector can be specified using the `#[method(my:selector:)]` /// or `#[method_id(my:selector:)]` attribute, similar to the /// [`extern_methods!`] macro. /// /// If the `#[method_id(...)]` attribute is used, the return type must be -/// `Option>` or `Id`. Additionally, if the selector is in the +/// `Option>` or `Id`. Additionally, if the selector is in the /// "init"-family, the `self`/`this` argument must be `Allocated`. /// /// Putting other attributes on the method such as `cfg`, `allow`, `doc`, @@ -84,18 +109,19 @@ /// make it behave similarly to the Objective-C `BOOL`. Use [`runtime::Bool`] /// if you want to control this manually. /// -/// Note that `&mut Id<_, _>` and other such out parameters are not yet +/// Note that `&mut Id<_>` and other such out parameters are not yet /// supported, and may generate a panic at runtime. /// /// ["associated functions"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods /// ["methods"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods -/// [`extern_methods!`]: crate::extern_methods +/// [`IsMutable`]: crate::mutability::IsMutable +/// [`Cell`]: core::cell::Cell /// [open an issue]: https://github.com/madsmtm/objc2/issues/new /// [`msg_send!`]: crate::msg_send /// [`runtime::Bool`]: crate::runtime::Bool /// /// -/// ## Protocol definitions +/// ## Protocol implementations /// /// You can specify protocols that the class should implement, along with any /// required/optional methods for said protocols. @@ -129,21 +155,26 @@ /// Using this macro requires writing a few `unsafe` markers: /// /// `unsafe impl ClassType for T` has the following safety requirements: -/// - Same as [`extern_class!`] (the inheritance chain has to be correct). +/// - Any invariants that the overridden class [`ClassType::Super`] may have +/// must be upheld. +/// - [`ClassType::Mutability`] must be correct. /// - Any instance variables you specify under the struct definition must /// either be able to be created using [`MaybeUninit::zeroed`], or be /// properly initialized in an `init` method. /// /// `unsafe impl T { ... }` asserts that the types match those that are -/// expected when the method is invoked from Objective-C. Note that there are -/// no safe-guards here; you can easily write `i8`, but if Objective-C thinks -/// it's an `u32`, it will cause UB when called! +/// expected when the method is invoked from Objective-C. Note that unlike +/// with [`extern_methods!`], there are no safe-guards here; you can easily +/// write `i8`, but if Objective-C thinks it's an `u32`, it will cause UB when +/// called! /// /// `unsafe impl P for T { ... }` requires that all required methods of the /// specified protocol is implemented, and that any extra requirements /// (implicit or explicit) that the protocol has are upheld. The methods in /// this definition has the same safety requirements as above. /// +/// [`ClassType::Super`]: crate::ClassType::Super +/// [`ClassType::Mutability`]: crate::ClassType::Mutability /// [`MaybeUninit::zeroed`]: core::mem::MaybeUninit::zeroed /// /// @@ -154,30 +185,16 @@ /// /// ``` /// use std::os::raw::c_int; +/// +/// # use objc2::runtime::{__NSCopying as NSCopying, NSObject, NSObjectProtocol, NSZone}; +/// # #[cfg(available_elsewhere)] +/// use icrate::Foundation::{NSCopying, NSObject, NSObjectProtocol, NSZone}; /// use objc2::declare::{Ivar, IvarDrop, IvarEncode}; -/// use objc2::rc::{Id, Owned}; -/// use objc2::runtime::{NSObject, NSObjectProtocol, NSZone}; +/// use objc2::rc::Id; /// use objc2::{ -/// declare_class, extern_protocol, msg_send, msg_send_id, ClassType, -/// ProtocolType, +/// declare_class, extern_protocol, msg_send, msg_send_id, mutability, ClassType, ProtocolType, /// }; /// -/// // Declare the NSCopying protocol so that we can implement it. -/// // -/// // In practice, you wouldn't have to do this, since it is done for you in -/// // `icrate`. -/// extern_protocol!( -/// unsafe trait NSCopying { -/// #[method(copyWithZone:)] -/// fn copy_with_zone(&self, _zone: *const NSZone) -> *mut Self; -/// } -/// -/// unsafe impl ProtocolType for dyn NSCopying { -/// const NAME: &'static str = "NSCopying"; -/// } -/// ); -/// -/// /// declare_class!( /// struct MyCustomObject { /// foo: IvarEncode, @@ -189,6 +206,7 @@ /// /// unsafe impl ClassType for MyCustomObject { /// type Super = NSObject; +/// type Mutability = mutability::Mutable; /// const NAME: &'static str = "MyCustomObject"; /// } /// @@ -203,14 +221,14 @@ /// // Initialize instance variables /// /// // Some types like `u8`, `bool`, `Option>` and -/// // `Option>` are safe to zero-initialize, and -/// // we can simply write to the variable as normal: +/// // `Option>` are safe to zero-initialize, and we can +/// // simply write to the variable as normal: /// *this.foo = foo; /// *this.bar = 42; /// -/// // For others like `&u8`, `Box` or `Id`, we have -/// // to initialize them with `Ivar::write`: -/// Ivar::write(&mut this.object, Id::into_shared(NSObject::new())); +/// // For others like `&u8`, `Box` or `Id`, we have to +/// // initialize them with `Ivar::write`: +/// Ivar::write(&mut this.object, NSObject::new()); /// /// // All the instance variables have been initialized; our /// // initializer is sound @@ -236,7 +254,7 @@ /// /// unsafe impl NSCopying for MyCustomObject { /// #[method_id(copyWithZone:)] -/// fn copy_with_zone(&self, _zone: *const NSZone) -> Id { +/// fn copy_with_zone(&self, _zone: *const NSZone) -> Id { /// let mut obj = Self::new(*self.foo); /// *obj.bar = *self.bar; /// obj @@ -248,8 +266,11 @@ /// } /// ); /// +/// // TODO: Allow moving this inside `declare_class!` +/// unsafe impl NSObjectProtocol for MyCustomObject {} +/// /// impl MyCustomObject { -/// pub fn new(foo: u8) -> Id { +/// pub fn new(foo: u8) -> Id { /// unsafe { msg_send_id![Self::alloc(), initWithFoo: foo] } /// } /// @@ -266,12 +287,6 @@ /// } /// } /// -/// // TODO: `NSCopying` from `icrate` works a bit differently -/// // unsafe impl icrate::Foundation::NSCopying for MyCustomObject { -/// // type Ownership = Owned; -/// // type Output = Self; -/// // } -/// /// fn main() { /// let obj = MyCustomObject::new(3); /// assert_eq!(*obj.foo, 3); @@ -362,7 +377,9 @@ macro_rules! declare_class { $(#[inherits($($inheritance_rest:ty),+)])? type Super = $superclass:ty; - const NAME: &'static str = $name_const:literal; + type Mutability = $mutability:ty; + + const NAME: &'static str = $name_const:expr; } $($methods:tt)* @@ -375,7 +392,7 @@ macro_rules! declare_class { ($($fields)*) ( // Superclasses are deallocated by calling `[super dealloc]`. - __inner: $crate::__macro_helpers::ManuallyDrop<$superclass>, + __superclass: $crate::__macro_helpers::ManuallyDrop<$superclass>, ) } @@ -386,6 +403,8 @@ macro_rules! declare_class { $(#[inherits($($inheritance_rest),+)])? type Super = $superclass; + type Mutability = $mutability; + const NAME: &'static str = $name_const; } @@ -404,7 +423,9 @@ macro_rules! declare_class { $(#[inherits($($inheritance_rest:ty),+)])? type Super = $superclass:ty; - const NAME: &'static str = $name_const:literal; + type Mutability = $mutability:ty; + + const NAME: &'static str = $name_const:expr; } $($methods:tt)* @@ -417,7 +438,7 @@ macro_rules! declare_class { ($($fields)*) ( // Superclasses are deallocated by calling `[super dealloc]`. - __inner: $crate::__macro_helpers::ManuallyDrop<$superclass>, + __superclass: $crate::__macro_helpers::ManuallyDrop<$superclass>, ) } @@ -428,6 +449,8 @@ macro_rules! declare_class { $(#[inherits($($inheritance_rest),+)])? type Super = $superclass; + type Mutability = $mutability; + const NAME: &'static str = $name_const; } @@ -444,7 +467,9 @@ macro_rules! declare_class { $(#[inherits($($inheritance_rest:ty),+)])? type Super = $superclass:ty; - const NAME: &'static str = $name_const:literal; + type Mutability = $mutability:ty; + + const NAME: &'static str = $name_const:expr; } $($methods:tt)* @@ -457,7 +482,7 @@ macro_rules! declare_class { () ( // Superclasses are deallocated by calling `[super dealloc]`. - __inner: $crate::__macro_helpers::ManuallyDrop<$superclass>, + __superclass: $crate::__macro_helpers::ManuallyDrop<$superclass>, ) } @@ -468,6 +493,8 @@ macro_rules! declare_class { $(#[inherits($($inheritance_rest),+)])? type Super = $superclass; + type Mutability = $mutability; + const NAME: &'static str = $name_const; } @@ -486,7 +513,9 @@ macro_rules! __inner_declare_class { $(#[inherits($($inheritance_rest:ty),+)])? type Super = $superclass:ty; - const NAME: &'static str = $name_const:literal; + type Mutability = $mutability:ty; + + const NAME: &'static str = $name_const:expr; } $($methods:tt)* @@ -495,15 +524,26 @@ macro_rules! __inner_declare_class { // SAFETY: Upheld by caller unsafe impl () for $for { INHERITS = [$superclass, $($($inheritance_rest,)+)? $crate::runtime::Object]; + + fn as_super(&self) { + &*self.__superclass + } + + fn as_super_mut(&mut self) { + &mut *self.__superclass + } } } // Creation unsafe impl ClassType for $for { type Super = $superclass; + type Mutability = $mutability; const NAME: &'static $crate::__macro_helpers::str = $name_const; fn class() -> &'static $crate::runtime::Class { + $crate::__macro_helpers::assert_mutability_matches_superclass_mutability::(); + // TODO: Use `core::cell::LazyCell` static REGISTER_CLASS: $crate::__macro_helpers::Once = $crate::__macro_helpers::Once::new(); @@ -575,12 +615,12 @@ macro_rules! __inner_declare_class { #[inline] fn as_super(&self) -> &Self::Super { - &self.__inner + &*self.__superclass } #[inline] fn as_super_mut(&mut self) -> &mut Self::Super { - &mut self.__inner + &mut *self.__superclass } } @@ -594,7 +634,7 @@ macro_rules! __inner_declare_class { #[doc(hidden)] #[macro_export] macro_rules! __select_name { - ($_name:ident; $name_const:literal) => { + ($_name:ident; $name_const:expr) => { $name_const }; ($name:ident;) => { diff --git a/crates/objc2/src/macros/extern_class.rs b/crates/objc2/src/macros/extern_class.rs index 05d5840ab..3d6572eef 100644 --- a/crates/objc2/src/macros/extern_class.rs +++ b/crates/objc2/src/macros/extern_class.rs @@ -1,30 +1,21 @@ -/// Create a new type to represent an Objective-C class. +/// Create a new type to represent a class. /// /// This is similar to an `@interface` declaration in Objective-C. /// -/// The given struct name should correspond to a valid Objective-C class, -/// whose instances have the encoding [`Encoding::Object`]. (as an example: -/// `NSAutoreleasePool` does not have this!) -/// -/// You must specify the superclass of this class, similar to how you would -/// in Objective-C. -/// -/// Due to Rust trait limitations, specifying e.g. the superclass `NSData` -/// would not give you easy access to `NSObject`'s functionality. Therefore, -/// you may specify additional parts of the inheritance chain using the -/// `#[inherits(...)]` attribute. -/// -/// [`Encoding::Object`]: crate::Encoding::Object +/// It is useful for things like `icrate`, which needs to create interfaces to +/// existing, externally defined classes like `NSString`, `NSURL` and so on, +/// but can also be useful for users that have custom classes written in +/// Objective-C that they want to access from Rust. /// /// /// # Specification /// /// The syntax is similar enough to Rust syntax that if you invoke the macro /// with parentheses (as opposed to curly brackets), [`rustfmt` will be able to -/// format the contents][rustfmt-macros]. +/// format the contents][rustfmt-macros] (so e.g. as `extern_class!( ... );`). /// -/// This creates an opaque struct containing the superclass (which means that -/// auto traits are inherited from the superclass), and implements the +/// The macro creates an opaque struct containing the superclass (which means +/// that auto traits are inherited from the superclass), and implements the /// following traits for it to allow easier usage as an Objective-C object: /// /// - [`RefEncode`][crate::RefEncode] @@ -38,34 +29,74 @@ /// - [`BorrowMut<$inheritance_chain>`][core::borrow::BorrowMut] /// /// The macro allows specifying fields on the struct, but _only_ zero-sized -/// types like [`PhantomData`] and [`declare::Ivar`] are allowed here! +/// types like [`PhantomData`] and [`declare::Ivar`] are allowed here. /// -/// You may add a `#[cfg(...)]` attribute to the class and `ClassType` impl, -/// and then it will work as expected. Only `#[cfg(...)]` attributes are -/// supported on the `ClassType` impl! +/// You can add most attributes to the class, including `#[cfg(...)]`, +/// `#[derive(...)]` and doc comments (but not ABI-modifying attributes like +/// `#[repr(...)]`). /// /// [rustfmt-macros]: https://github.com/rust-lang/rustfmt/discussions/5437 /// [`PhantomData`]: core::marker::PhantomData /// [`declare::Ivar`]: crate::declare::Ivar /// /// +/// ## `ClassType` implementation +/// +/// The syntax of this macro neatly documents that it implements the +/// [`ClassType`] trait for you, though to do so you need to provide it the +/// following: +/// - The [`Super`] class. +/// +/// Due to Rust trait limitations, specifying e.g. the superclass `NSData` +/// would not give you easy access to `NSObject`'s functionality. Therefore, +/// you may optionally specify additional parts of the inheritance chain +/// using an `#[inherits(...)]` attribute. +/// - The class' [`Mutability`]. +/// - Optionally, the class' [`NAME`] - if not specified, this will default to +/// the struct name. +/// +/// You may add `#[cfg(...)]` attributes to the `ClassType` impl, and then it +/// will work as expected. No other attributes are supported. +/// +/// [`ClassType`]: crate::ClassType +/// [`Super`]: crate::ClassType::Super +/// [`Mutability`]: crate::ClassType::Mutability +/// [`NAME`]: crate::ClassType::NAME +/// +/// /// # Safety /// -/// The specified superclass must be correct. The object must also respond to -/// standard memory management messages (this is upheld if [`NSObject`] is -/// part of its inheritance chain). +/// This macro implements the three unsafe traits [`RefEncode`], [`Message`] +/// and [`ClassType`] for you, and while it can ensure most of the required +/// properties in those, it cannot ensure all of them. +/// +/// In particular, when writing `unsafe` on `impl ClassType`, you must ensure +/// that: +/// 1. [`ClassType::Super`] is correct. +/// 2. [`ClassType::Mutability`] is correct. +/// +/// See [`ClassType`'s safety section][ClassType#safety] for further +/// details on what this entails. /// -/// [`NSObject`]: crate::runtime::NSObject +/// [`RefEncode`]: crate::encode::RefEncode +/// [`Message`]: crate::Message +/// [`ClassType::Super`]: crate::ClassType::Super +/// [`ClassType::Mutability`]: crate::ClassType::Mutability +/// [ClassType#safety]: crate::ClassType#safety /// /// /// # Examples /// -/// Create a new type to represent the `NSFormatter` class. +/// Create a new type to represent the `NSFormatter` class (of course, we +/// could have just used `icrate::Foundation::NSFormatter`). /// /// ``` -/// use objc2::runtime::NSObject; +/// # #[cfg(not_available)] +/// use icrate::Foundation::{NSCoding, NSCopying, NSObjectProtocol}; +/// # use objc2::runtime::{NSObjectProtocol, __NSCopying as NSCopying}; /// use objc2::rc::Id; -/// use objc2::{ClassType, extern_class, msg_send_id}; +/// use objc2::runtime::NSObject; +/// use objc2::{extern_class, msg_send_id, mutability, ClassType}; /// /// extern_class!( /// /// An example description. @@ -76,31 +107,38 @@ /// // Specify the superclass, in this case `NSObject` /// unsafe impl ClassType for NSFormatter { /// type Super = NSObject; +/// type Mutability = mutability::InteriorMutable; /// // Optionally, specify the name of the class, if it differs from /// // the struct name. /// // const NAME: &'static str = "NSFormatter"; /// } /// ); /// -/// // We can specify the protocols that `NSFormatter` conforms to like this. -/// // (These should be created using the `extern_protocol!` macro). -/// // -/// // unsafe impl NSCoding for NSFormatter {} -/// // unsafe impl NSCopying for NSFormatter {} +/// // Note: We have to specify the protocols for the superclasses as well, +/// // since Rust doesn't do inheritance. +/// unsafe impl NSObjectProtocol for NSFormatter {} +/// unsafe impl NSCopying for NSFormatter {} +/// # #[cfg(not_available)] +/// unsafe impl NSCoding for NSFormatter {} /// -/// // Provided by the implementation of `ClassType` -/// let cls = NSFormatter::class(); +/// fn main() { +/// // Provided by the implementation of `ClassType` +/// let cls = NSFormatter::class(); /// -/// // `NSFormatter` implements `Message`: -/// let obj: Id = unsafe { msg_send_id![cls, new] }; +/// // `NSFormatter` implements `Message`: +/// let obj: Id = unsafe { msg_send_id![cls, new] }; +/// } /// ``` /// /// Represent the `NSDateFormatter` class, using the `NSFormatter` type we /// declared previously to specify as its superclass. /// /// ``` +/// # #[cfg(not_available)] +/// use icrate::Foundation::{NSCoding, NSCopying, NSObjectProtocol}; +/// # use objc2::runtime::{NSObjectProtocol, __NSCopying as NSCopying}; /// use objc2::runtime::NSObject; -/// use objc2::{extern_class, ClassType}; +/// use objc2::{extern_class, mutability, ClassType}; /// # /// # extern_class!( /// # #[derive(PartialEq, Eq, Hash)] @@ -108,6 +146,7 @@ /// # /// # unsafe impl ClassType for NSFormatter { /// # type Super = NSObject; +/// # type Mutability = mutability::InteriorMutable; /// # } /// # ); /// @@ -119,13 +158,16 @@ /// // Specify the correct inheritance chain /// #[inherits(NSObject)] /// type Super = NSFormatter; +/// type Mutability = mutability::InteriorMutable; /// } /// ); /// /// // Similarly, we can specify the protocols that this implements here: -/// // -/// // unsafe impl NSCoding for NSFormatter {} -/// // unsafe impl NSCopying for NSFormatter {} +/// unsafe impl NSObjectProtocol for NSFormatter {} +/// # #[cfg(not_available)] +/// unsafe impl NSCopying for NSDateFormatter {} +/// # #[cfg(not_available)] +/// unsafe impl NSCoding for NSDateFormatter {} /// ``` /// /// See the source code of `icrate` for many more examples. @@ -141,8 +183,9 @@ macro_rules! extern_class { unsafe impl ClassType for $for:ty { $(#[inherits($($inheritance_rest:ty),+)])? type Super = $superclass:ty; + type Mutability = $mutability:ty; - $(const NAME: &'static str = $name_const:literal;)? + $(const NAME: &'static str = $name_const:expr;)? } ) => { // Just shorthand syntax for the following @@ -154,6 +197,7 @@ macro_rules! extern_class { unsafe impl ClassType for $for { $(#[inherits($($inheritance_rest),+)])? type Super = $superclass; + type Mutability = $mutability; $(const NAME: &'static str = $name_const;)? } @@ -169,13 +213,15 @@ macro_rules! extern_class { unsafe impl ClassType for $for:ty { $(#[inherits($($inheritance_rest:ty),+)])? type Super = $superclass:ty; + type Mutability = $mutability:ty; - $(const NAME: &'static str = $name_const:literal;)? + $(const NAME: &'static str = $name_const:expr;)? } ) => { $crate::__inner_extern_class!( $(#[$m])* $v struct $name<> { + __superclass: $superclass, $($field_vis $field: $field_ty,)* } @@ -183,6 +229,15 @@ macro_rules! extern_class { unsafe impl<> ClassType for $for { $(#[inherits($($inheritance_rest),+)])? type Super = $superclass; + type Mutability = $mutability; + + fn as_super(&self) -> &Self::Super { + &self.__superclass + } + + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__superclass + } $(const NAME: &'static str = $name_const;)? } @@ -266,6 +321,7 @@ macro_rules! __inner_extern_class { ( $(#[$m:meta])* $v:vis struct $name:ident<$($t_struct:ident $(: $b_struct:ident $(= $default:ty)?)?),* $(,)?> { + $superclass_field:ident: $superclass_field_ty:ty, $($fields:tt)* } @@ -273,8 +329,12 @@ macro_rules! __inner_extern_class { unsafe impl<$($t_for:ident $(: $b_for:ident)?),* $(,)?> ClassType for $for:ty { $(#[inherits($($inheritance_rest:ty),+ $(,)?)])? type Super = $superclass:ty; + type Mutability = $mutability:ty; + + fn as_super(&$as_super_self:ident) -> &Self::Super $as_super:block + fn as_super_mut(&mut $as_super_mut_self:ident) -> &mut Self::Super $as_super_mut:block - $(const NAME: &'static str = $name_const:literal;)? + $(const NAME: &'static str = $name_const:expr;)? } ) => { $crate::__emit_struct! { @@ -282,7 +342,7 @@ macro_rules! __inner_extern_class { ($v) ($name<$($t_struct $(: $b_struct $(= $default)?)?),*>) ( - __inner: $superclass, + $superclass_field: $superclass_field_ty, $($fields)* ) } @@ -291,16 +351,22 @@ macro_rules! __inner_extern_class { $(#[$impl_m])* unsafe impl ($($t_for $(: $b_for)?),*) for $for { INHERITS = [$superclass, $($($inheritance_rest,)+)? $crate::runtime::Object]; + + fn as_super(&$as_super_self) $as_super + fn as_super_mut(&mut $as_super_mut_self) $as_super_mut } } $(#[$impl_m])* unsafe impl<$($t_for $(: $b_for)?),*> ClassType for $for { type Super = $superclass; + type Mutability = $mutability; const NAME: &'static $crate::__macro_helpers::str = $crate::__select_name!($name; $($name_const)?); #[inline] fn class() -> &'static $crate::runtime::Class { + $crate::__macro_helpers::assert_mutability_matches_superclass_mutability::(); + $crate::__class_inner!( $crate::__select_name!($name; $($name_const)?), $crate::__hash_idents!($name), @@ -308,14 +374,10 @@ macro_rules! __inner_extern_class { } #[inline] - fn as_super(&self) -> &Self::Super { - &self.__inner - } + fn as_super(&$as_super_self) -> &Self::Super $as_super #[inline] - fn as_super_mut(&mut self) -> &mut Self::Super { - &mut self.__inner - } + fn as_super_mut(&mut $as_super_mut_self) -> &mut Self::Super $as_super_mut } }; } @@ -327,6 +389,9 @@ macro_rules! __extern_class_impl_traits { $(#[$impl_m:meta])* unsafe impl ($($t:tt)*) for $for:ty { INHERITS = [$superclass:ty $(, $inheritance_rest:ty)*]; + + fn as_super(&$as_super_self:ident) $as_super:block + fn as_super_mut(&mut $as_super_mut_self:ident) $as_super_mut:block } ) => { // SAFETY: @@ -335,6 +400,14 @@ macro_rules! __extern_class_impl_traits { // that it actually inherits said object. // - The rest of the struct's fields are ZSTs, so they don't influence // the layout. + // + // Be aware that very rarely, this implementation is wrong because the + // class' instances do not have the encoding `Encoding::Object`. + // + // A known case is that `NSAutoreleasePool` has a different encoding. + // This should be fairly problem-free though, since that is still + // valid in Objective-C to represent that class' instances as + // `NSObject*`. $(#[$impl_m])* unsafe impl<$($t)*> $crate::RefEncode for $for { const ENCODING_REF: $crate::Encoding @@ -345,8 +418,12 @@ macro_rules! __extern_class_impl_traits { // (we even ensure that `Object` is always last in our inheritance // tree), so it is always safe to reinterpret as that. // - // That the object must work with standard memory management is upheld - // by the caller. + // That the object must work with standard memory management is + // properly upheld by the fact that the superclass is required by + // `assert_mutability_matches_superclass_mutability` to implement + // `ClassType`, and hence must be a subclass of one of `NSObject`, + // `NSProxy` or some other class that ensures this (e.g. the object + // itself is not a root class). $(#[$impl_m])* unsafe impl<$($t)*> $crate::Message for $for {} @@ -361,9 +438,11 @@ macro_rules! __extern_class_impl_traits { // Any lifetime information that the object may have been holding is // safely kept in the returned reference. // - // Generics are discarded (for example in the case of `&NSArray` - // to `&NSObject`), but if the generic contained a lifetime, that - // lifetime is still included in the returned reference. + // Generics are discarded (for example in the case of `&NSArray` to + // `&NSObject`), but if the generic contained a lifetime, that + // lifetime is still included in the returned reference, and is not + // erasable by e.g. `ClassType::retain` since `NSObject` does not + // allow that. // // Note that you can easily have two different variables pointing to // the same object, `x: &T` and `y: &T::Target`, and this would be @@ -373,9 +452,7 @@ macro_rules! __extern_class_impl_traits { type Target = $superclass; #[inline] - fn deref(&self) -> &Self::Target { - &self.__inner - } + fn deref(&$as_super_self) -> &Self::Target $as_super } // SAFETY: Mutability does not change anything in the above @@ -392,9 +469,7 @@ macro_rules! __extern_class_impl_traits { $(#[$impl_m])* impl<$($t)*> $crate::__macro_helpers::DerefMut for $for { #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.__inner - } + fn deref_mut(&mut $as_super_mut_self) -> &mut Self::Target $as_super_mut } $(#[$impl_m])* diff --git a/crates/objc2/src/macros/extern_methods.rs b/crates/objc2/src/macros/extern_methods.rs index aefd7e3df..3eb155460 100644 --- a/crates/objc2/src/macros/extern_methods.rs +++ b/crates/objc2/src/macros/extern_methods.rs @@ -62,7 +62,7 @@ /// use objc2::ffi::NSUInteger; /// use objc2::rc::{Allocated, Id}; /// use objc2::runtime::NSObject; -/// use objc2::{declare_class, extern_methods, ClassType}; +/// use objc2::{declare_class, extern_methods, mutability, ClassType}; /// /// // Shim /// type NSError = NSObject; @@ -72,6 +72,7 @@ /// /// unsafe impl ClassType for MyObject { /// type Super = NSObject; +/// type Mutability = mutability::Immutable; /// const NAME: &'static str = "MyObject"; /// } /// @@ -115,7 +116,7 @@ /// # use objc2::ffi::NSUInteger; /// # use objc2::rc::{Allocated, Id}; /// # use objc2::runtime::NSObject; -/// # use objc2::{declare_class, extern_methods, ClassType}; +/// # use objc2::{declare_class, extern_methods, mutability, ClassType}; /// # /// # // Shim /// # type NSError = NSObject; @@ -125,6 +126,7 @@ /// # /// # unsafe impl ClassType for MyObject { /// # type Super = NSObject; +/// # type Mutability = mutability::InteriorMutable; /// # const NAME: &'static str = "MyObject2"; /// # } /// # @@ -215,7 +217,9 @@ macro_rules! __extern_methods_rewrite_methods { // Unsafe variant { $(#[$($m:tt)*])* - $v:vis unsafe fn $name:ident($($args:tt)*) $(-> $ret:ty)?; + $v:vis unsafe fn $name:ident($($args:tt)*) $(-> $ret:ty)? + // TODO: Handle where bounds better + $(where $($where:ty : $bound:path),+ $(,)?)?; $($rest:tt)* } => { @@ -228,6 +232,7 @@ macro_rules! __extern_methods_rewrite_methods { ($crate::__extern_methods_method_out) ($v unsafe fn $name($($args)*) $(-> $ret)?) + ($($($where : $bound ,)+)?) } $crate::__extern_methods_rewrite_methods! { @@ -238,7 +243,9 @@ macro_rules! __extern_methods_rewrite_methods { // Safe variant { $(#[$($m:tt)*])* - $v:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?; + $v:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)? + // TODO: Handle where bounds better + $(where $($where:ty : $bound:path),+ $(,)?)?; $($rest:tt)* } => { @@ -251,6 +258,7 @@ macro_rules! __extern_methods_rewrite_methods { ($crate::__extern_methods_method_out) ($v fn $name($($args)*) $(-> $ret)?) + ($($($where : $bound ,)+)?) } $crate::__extern_methods_rewrite_methods! { @@ -279,6 +287,7 @@ macro_rules! __extern_methods_method_out { // #[method(...)] { ($($function_start:tt)*) + ($($where:ty : $bound:path ,)*) ($__builder_method:ident) ($receiver:expr) @@ -291,7 +300,10 @@ macro_rules! __extern_methods_method_out { ($($m_checked:tt)*) } => { $($m_checked)* - $($function_start)* { + $($function_start)* + where + $($where : $bound,)* + { #[allow(unused_unsafe)] unsafe { $crate::__method_msg_send! { @@ -309,6 +321,7 @@ macro_rules! __extern_methods_method_out { // #[method_id(...)] { ($($function_start:tt)*) + ($($where:ty : $bound:path ,)*) ($__builder_method:ident) ($receiver:expr) @@ -321,7 +334,10 @@ macro_rules! __extern_methods_method_out { ($($m_checked:tt)*) } => { $($m_checked)* - $($function_start)* { + $($function_start)* + where + $($where : $bound,)* + { #[allow(unused_unsafe)] unsafe { $crate::__method_msg_send_id! { @@ -340,6 +356,7 @@ macro_rules! __extern_methods_method_out { // #[optional] { ($($function_start:tt)*) + ($($where:ty : $bound:path ,)*) ($__builder_method:ident) ($receiver:expr) diff --git a/crates/objc2/src/macros/extern_protocol.rs b/crates/objc2/src/macros/extern_protocol.rs index 9e36f17ca..da4a71fb0 100644 --- a/crates/objc2/src/macros/extern_protocol.rs +++ b/crates/objc2/src/macros/extern_protocol.rs @@ -1,4 +1,4 @@ -/// Create a new trait to represent an Objective-C protocol. +/// Create a new trait to represent a protocol. /// /// This is similar to a `@protocol` declaration in Objective-C. /// @@ -87,7 +87,7 @@ /// /// This comment will appear on the trait as expected. /// pub unsafe trait NSItemProviderWriting: NSObjectProtocol { /// // ^^^^^^^^^^^^^^^^ -/// // This trait inherits from `NSObject` +/// // This protocol inherits from the `NSObject` protocol /// /// // This method we mark as `unsafe`, since we aren't using the correct /// // type for the completion handler @@ -154,7 +154,7 @@ macro_rules! extern_protocol { $(#[$impl_m:meta])* unsafe impl ProtocolType for dyn $for:ident { - $(const NAME: &'static str = $name_const:literal;)? + $(const NAME: &'static str = $name_const:expr;)? } ) => { $(#[$m])* @@ -164,6 +164,24 @@ macro_rules! extern_protocol { } } + $crate::__inner_extern_protocol!( + ($(#[$impl_m])*) + ($name) + (dyn $for) + ($crate::__select_name!($name; $($name_const)?)) + ); + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __inner_extern_protocol { + ( + ($(#[$impl_m:meta])*) + ($name:ident) + (dyn $for:ident) + ($name_str:expr) + ) => { $(#[$impl_m])* unsafe impl $name for $crate::runtime::ProtocolObject where @@ -174,12 +192,12 @@ macro_rules! extern_protocol { // and is correctly defined. $(#[$impl_m])* unsafe impl ProtocolType for dyn $for { - const NAME: &'static $crate::__macro_helpers::str = $crate::__select_name!($name; $($name_const)?); + const NAME: &'static $crate::__macro_helpers::str = $name_str; const __INNER: () = (); } - // SAFETY: Anything that implements the protocol `$name` is valid to - // convert to `ProtocolObject`. + // SAFETY: Anything that implements the protocol is valid to convert + // to `ProtocolObject`. $(#[$impl_m])* unsafe impl $crate::runtime::ImplementedBy for dyn $for where @@ -187,7 +205,7 @@ macro_rules! extern_protocol { { const __INNER: () = (); } - } + }; } /// tt-munch each protocol method. @@ -200,7 +218,9 @@ macro_rules! __extern_protocol_rewrite_methods { // Unsafe variant { $(#[$($m:tt)*])* - $v:vis unsafe fn $name:ident($($args:tt)*) $(-> $ret:ty)?; + $v:vis unsafe fn $name:ident($($args:tt)*) $(-> $ret:ty)? + // TODO: Handle where bounds better + $(where $($where:ty : $bound:path),+ $(,)?)?; $($rest:tt)* } => { @@ -213,6 +233,7 @@ macro_rules! __extern_protocol_rewrite_methods { ($crate::__extern_protocol_method_out) ($v unsafe fn $name($($args)*) $(-> $ret)?) + ($($($where : $bound ,)+)?) } $crate::__extern_protocol_rewrite_methods! { @@ -223,7 +244,9 @@ macro_rules! __extern_protocol_rewrite_methods { // Safe variant { $(#[$($m:tt)*])* - $v:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?; + $v:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)? + // TODO: Handle where bounds better + $(where $($where:ty : $bound:path),+ $(,)?)?; $($rest:tt)* } => { @@ -236,6 +259,7 @@ macro_rules! __extern_protocol_rewrite_methods { ($crate::__extern_protocol_method_out) ($v fn $name($($args)*) $(-> $ret)?) + ($($($where : $bound ,)+)?) } $crate::__extern_protocol_rewrite_methods! { @@ -250,6 +274,7 @@ macro_rules! __extern_protocol_method_out { // Instance #[method(...)] { ($($function_start:tt)*) + ($($where:ty : $bound:path ,)*) (add_method) ($receiver:expr) @@ -265,6 +290,7 @@ macro_rules! __extern_protocol_method_out { $($function_start)* where Self: $crate::__macro_helpers::Sized + $crate::Message + $(, $where : $bound)* { #[allow(unused_unsafe)] unsafe { @@ -283,6 +309,7 @@ macro_rules! __extern_protocol_method_out { // Instance #[method_id(...)] { ($($function_start:tt)*) + ($($where:ty : $bound:path ,)*) (add_method) ($receiver:expr) @@ -298,6 +325,7 @@ macro_rules! __extern_protocol_method_out { $($function_start)* where Self: $crate::__macro_helpers::Sized + $crate::Message + $(, $where : $bound)* { #[allow(unused_unsafe)] unsafe { @@ -317,6 +345,7 @@ macro_rules! __extern_protocol_method_out { // Class #[method(...)] { ($($function_start:tt)*) + ($($where:ty : $bound:path ,)*) (add_class_method) ($receiver:expr) @@ -332,6 +361,7 @@ macro_rules! __extern_protocol_method_out { $($function_start)* where Self: $crate::__macro_helpers::Sized + $crate::ClassType + $(, $where : $bound)* { #[allow(unused_unsafe)] unsafe { @@ -350,6 +380,7 @@ macro_rules! __extern_protocol_method_out { // Class #[method_id(...)] { ($($function_start:tt)*) + ($($where:ty : $bound:path ,)*) (add_class_method) ($receiver:expr) @@ -365,6 +396,7 @@ macro_rules! __extern_protocol_method_out { $($function_start)* where Self: $crate::__macro_helpers::Sized + $crate::ClassType + $(, $where : $bound)* { #[allow(unused_unsafe)] unsafe { diff --git a/crates/objc2/src/macros/mod.rs b/crates/objc2/src/macros/mod.rs index 8366233ef..95a8aca3d 100644 --- a/crates/objc2/src/macros/mod.rs +++ b/crates/objc2/src/macros/mod.rs @@ -22,7 +22,7 @@ mod extern_protocol; /// /// Panics if no class with the given name can be found. /// -/// To check for a class that may not exist, use [`Class::get`]. +/// To dynamically check for a class that may not exist, use [`Class::get`]. /// /// [`Class::get`]: crate::runtime::Class::get /// @@ -47,11 +47,17 @@ mod extern_protocol; /// [`sel!`]: crate::sel /// /// -/// # Examples +/// # Example +/// +/// Get and compare the class with one returned from [`ClassType::class`]. /// -/// ```no_run -/// # use objc2::class; -/// let cls = class!(NSObject); +/// ``` +/// use objc2::runtime::NSObject; +/// use objc2::{class, ClassType}; +/// +/// let cls1 = class!(NSObject); +/// let cls2 = NSObject::class(); +/// assert_eq!(cls1, cls2); /// ``` #[macro_export] macro_rules! class { @@ -722,7 +728,8 @@ macro_rules! __class_inner { /// as the second argument. If no specific superclass is specified, the /// direct superclass is retrieved from [`ClassType`]. /// -/// All arguments, and the return type, must implement [`Encode`]. +/// All arguments, as well as the return type, must implement [`Encode`] (bar +/// the exceptions below). /// /// If the last argument is the special marker `_`, the macro will return a /// `Result<(), Id>`, see below. @@ -769,10 +776,10 @@ macro_rules! __class_inner { /// /// This macro has support for passing such parameters using the following /// types: -/// - `&mut Id<_, _>` -/// - `Option<&mut Id<_, _>>` -/// - `&mut Option>`, -/// - `Option<&mut Option>>` +/// - `&mut Id<_>` +/// - `Option<&mut Id<_>>` +/// - `&mut Option>`, +/// - `Option<&mut Option>>` /// /// Beware with the first two, since they will cause undefined behaviour if /// the method overwrites the value with `nil`. @@ -887,14 +894,16 @@ macro_rules! __class_inner { /// /// ```no_run /// use objc2::msg_send; +/// # /// # use objc2::runtime::NSObject; -/// # use objc2::{declare_class, ClassType}; +/// # use objc2::{declare_class, mutability, ClassType}; /// # /// # declare_class!( /// # struct MyObject; /// # /// # unsafe impl ClassType for MyObject { /// # type Super = NSObject; +/// # type Mutability = mutability::InteriorMutable; /// # const NAME: &'static str = "MyObject"; /// # } /// # ); @@ -1088,32 +1097,31 @@ macro_rules! msg_send_bool { /// /// The accepted receiver and return types, and how we handle them, differ /// depending on which, if any, of the [recognized selector -/// families][sel-families] the selector belongs to (here `T: Message` and -/// `O: Ownership`): +/// families][sel-families] the selector belongs to: /// /// - The `new` family: The receiver may be anything that implements /// [`MessageReceiver`] (though often you'll want to use `&Class`). The -/// return type is a generic `Id` or `Option>`. +/// return type is a generic `Id` or `Option>`. /// /// - The `alloc` family: The receiver must be `&Class`, and the return type /// is a generic `Allocated` or `Option>`. /// -/// - The `init` family: The receiver must be `Option>` -/// as returned from `alloc`. The receiver is consumed, and a the -/// now-initialized `Id` or `Option>` (with the same `T`) is +/// - The `init` family: The receiver must be `Option>` as +/// returned from `alloc`. The receiver is consumed, and a the +/// now-initialized `Id` or `Option>` (with the same `T`) is /// returned. /// /// - The `copy` family: The receiver may be anything that implements -/// [`MessageReceiver`] and the return type is a generic `Id` or -/// `Option>`. +/// [`MessageReceiver`] and the return type is a generic `Id` or +/// `Option>`. /// /// - The `mutableCopy` family: Same as the `copy` family. /// /// - No family: The receiver may be anything that implements /// [`MessageReceiver`]. The result is retained using -/// [`Id::retain_autoreleased`], and a generic `Id` or -/// `Option>` is returned. This retain is in most cases faster than -/// using autorelease pools! +/// [`Id::retain_autoreleased`], and a generic `Id` or `Option>` is +/// returned. This retain is in most cases faster than using autorelease +/// pools! /// /// See [the clang documentation][arc-retainable] for the precise /// specification of Objective-C's ownership rules. @@ -1126,7 +1134,7 @@ macro_rules! msg_send_bool { /// with an error message if it couldn't be retrieved. /// /// Though as a special case, if the last argument is the marker `_`, the -/// macro will return a `Result, Id>`, see below. +/// macro will return a `Result, Id>`, see below. /// /// This macro doesn't support super methods yet, see [#173]. /// The `retain`, `release` and `autorelease` selectors are not supported, use @@ -1149,7 +1157,7 @@ macro_rules! msg_send_bool { /// equivalent, the [`Result`] type. /// /// In particular, you can make the last argument the special marker `_`, and -/// then the macro will return a `Result, Id>` (where you must +/// then the macro will return a `Result, Id>` (where you must /// specify `E` yourself, usually you'd use `icrate::Foundation::NSError`). /// /// diff --git a/crates/objc2/src/message/mod.rs b/crates/objc2/src/message/mod.rs index fbb5cc6cd..a4fbb7b94 100644 --- a/crates/objc2/src/message/mod.rs +++ b/crates/objc2/src/message/mod.rs @@ -6,7 +6,8 @@ use crate::encode::__unstable::{ EncodeArguments, EncodeConvertArgument, EncodeConvertReturn, EncodeReturn, }; use crate::encode::{Encode, RefEncode}; -use crate::rc::{Id, Owned, Ownership}; +use crate::mutability::IsMutable; +use crate::rc::Id; use crate::runtime::{Class, Imp, Object, Sel}; use crate::ClassType; @@ -103,11 +104,30 @@ use self::platform::{send_super_unverified, send_unverified}; /// This trait also allows the object to be used in [`rc::Id`][`Id`]. /// /// This is a subtrait of [`RefEncode`], meaning the type must also implement -/// that, almost always as [`Encoding::Object`]. +/// that, almost always with [`RefEncode::ENCODING_REF`] being +/// [`Encoding::Object`]. +/// +/// This can be implemented for unsized (`!Sized`) types, but the intention is +/// not to support dynamically sized types like slices, only `extern type`s +/// (which is currently unstable). /// /// [`Encoding::Object`]: crate::Encoding::Object /// /// +/// # `Drop` interaction +/// +/// If the inner type implements [`Drop`], that implementation will very +/// likely not be called, since there is no way to ensure that the Objective-C +/// runtime will do so. If you need to run some code when the object is +/// destroyed, implement the `dealloc` method instead. +/// +/// The [`declare_class!`] macro does this for you, but the [`extern_class!`] +/// macro fundamentally cannot. +/// +/// [`declare_class!`]: crate::declare_class +/// [`extern_class!`]: crate::extern_class +/// +/// /// # Safety /// /// The type must represent an Objective-C object, meaning it: @@ -116,9 +136,9 @@ use self::platform::{send_super_unverified, send_unverified}; /// [`objc_msgSend`] or similar. /// - Must respond to the standard memory management `retain`, `release` and /// `autorelease` messages. -/// - Must support weak references. TODO: Make a new trait for this, for -/// example `NSTextView` only supports weak references on macOS 10.12 or -/// above. +/// - Must support weak references. (In the future we should probably make a +/// new trait for this, for example `NSTextView` only supports weak +/// references on macOS 10.12 or above). /// /// [`objc_msgSend`]: https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend /// @@ -144,37 +164,18 @@ use self::platform::{send_super_unverified, send_unverified}; /// // `*mut MyObject` and other pointer/reference types to the object can /// // now be used in `msg_send!` /// // -/// // And `Id` can now be constructed. +/// // And `Id` can now be constructed. /// ``` pub unsafe trait Message: RefEncode {} -unsafe impl Message for Object {} - // TODO: Make this fully private pub(crate) mod private { - use super::*; - pub trait Sealed {} - - impl Sealed for *const T {} - impl Sealed for *mut T {} - impl Sealed for NonNull {} - - impl<'a, T: Message + ?Sized> Sealed for &'a T {} - impl<'a, T: Message + ?Sized> Sealed for &'a mut T {} - - impl<'a, T: Message + ?Sized, O: Ownership> Sealed for &'a Id {} - impl<'a, T: Message + ?Sized> Sealed for &'a mut Id {} - - impl Sealed for ManuallyDrop> {} - - impl Sealed for *const Class {} - impl<'a> Sealed for &'a Class {} } /// Types that can directly be used as the receiver of Objective-C messages. /// -/// Examples include objects, classes, and blocks. +/// Examples include objects pointers, class pointers, and block pointers. /// /// This is a sealed trait (for now) that is automatically implemented for /// pointers to types implementing [`Message`], so that code can be generic @@ -195,7 +196,7 @@ pub unsafe trait MessageReceiver: private::Sealed + Sized { #[doc(hidden)] fn __as_raw_receiver(self) -> *mut Object; - /// Sends a message to self with the given selector and arguments. + /// Sends a message to the receiver with the given selector and arguments. /// /// The correct version of `objc_msgSend` will be chosen based on the /// return type. For more information, see the section on "Sending @@ -373,7 +374,8 @@ unsafe fn encountered_error(err: *mut E) -> Id { // Note that we implement MessageReceiver for unsized types as well, this is // to support `extern type`s in the future, not because we want to allow DSTs. -unsafe impl MessageReceiver for *const T { +impl private::Sealed for *const T {} +unsafe impl MessageReceiver for *const T { type __Inner = T; #[inline] @@ -382,7 +384,8 @@ unsafe impl MessageReceiver for *const T { } } -unsafe impl MessageReceiver for *mut T { +impl private::Sealed for *mut T {} +unsafe impl MessageReceiver for *mut T { type __Inner = T; #[inline] @@ -391,7 +394,8 @@ unsafe impl MessageReceiver for *mut T { } } -unsafe impl MessageReceiver for NonNull { +impl private::Sealed for NonNull {} +unsafe impl MessageReceiver for NonNull { type __Inner = T; #[inline] @@ -400,7 +404,8 @@ unsafe impl MessageReceiver for NonNull { } } -unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a T { +impl<'a, T: ?Sized + Message> private::Sealed for &'a T {} +unsafe impl<'a, T: ?Sized + Message> MessageReceiver for &'a T { type __Inner = T; #[inline] @@ -410,7 +415,10 @@ unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a T { } } -unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a mut T { +// TODO: Use `T: IsMutable + Root` here once we can handle `init` methods +// better in `declare_class!`. +impl<'a, T: ?Sized + Message> private::Sealed for &'a mut T {} +unsafe impl<'a, T: ?Sized + Message> MessageReceiver for &'a mut T { type __Inner = T; #[inline] @@ -420,7 +428,8 @@ unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a mut T { } } -unsafe impl<'a, T: Message + ?Sized, O: Ownership> MessageReceiver for &'a Id { +impl<'a, T: ?Sized + Message> private::Sealed for &'a Id {} +unsafe impl<'a, T: ?Sized + Message> MessageReceiver for &'a Id { type __Inner = T; #[inline] @@ -429,7 +438,8 @@ unsafe impl<'a, T: Message + ?Sized, O: Ownership> MessageReceiver for &'a Id MessageReceiver for &'a mut Id { +impl<'a, T: ?Sized + IsMutable> private::Sealed for &'a mut Id {} +unsafe impl<'a, T: ?Sized + IsMutable> MessageReceiver for &'a mut Id { type __Inner = T; #[inline] @@ -438,7 +448,8 @@ unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a mut Id { } } -unsafe impl MessageReceiver for ManuallyDrop> { +impl private::Sealed for ManuallyDrop> {} +unsafe impl MessageReceiver for ManuallyDrop> { type __Inner = T; #[inline] @@ -447,6 +458,7 @@ unsafe impl MessageReceiver for ManuallyDrop< } } +impl private::Sealed for *const Class {} unsafe impl MessageReceiver for *const Class { type __Inner = Class; @@ -456,6 +468,7 @@ unsafe impl MessageReceiver for *const Class { } } +impl<'a> private::Sealed for &'a Class {} unsafe impl<'a> MessageReceiver for &'a Class { type __Inner = Class; @@ -649,12 +662,24 @@ message_args_impl!( #[cfg(test)] mod tests { use super::*; - use crate::rc::{Id, Owned}; + use crate::mutability; + use crate::rc::Id; + use crate::runtime::NSObject; use crate::test_utils; - use crate::{msg_send, msg_send_id}; + use crate::{declare_class, msg_send, msg_send_id}; + + declare_class!( + struct MutableObject; + + unsafe impl ClassType for MutableObject { + type Super = NSObject; + type Mutability = mutability::Mutable; + const NAME: &'static str = "TestMutableObject"; + } + ); #[allow(unused)] - fn test_different_receivers(mut obj: Id) { + fn test_different_receivers(mut obj: Id) { unsafe { let x = &mut obj; let _: () = msg_send![x, mutable1]; @@ -662,10 +687,10 @@ mod tests { let _: () = msg_send![&mut *obj, mutable1]; let _: () = msg_send![&mut *obj, mutable2]; #[allow(clippy::needless_borrow)] - let obj: NonNull = (&mut *obj).into(); + let obj: NonNull = (&mut *obj).into(); let _: () = msg_send![obj, mutable1]; let _: () = msg_send![obj, mutable2]; - let obj: *mut Object = obj.as_ptr(); + let obj: *mut MutableObject = obj.as_ptr(); let _: () = msg_send![obj, mutable1]; let _: () = msg_send![obj, mutable2]; } diff --git a/crates/objc2/src/mutability.rs b/crates/objc2/src/mutability.rs new file mode 100644 index 000000000..686fc0b59 --- /dev/null +++ b/crates/objc2/src/mutability.rs @@ -0,0 +1,341 @@ +//! # Marker types for class mutability. +//! +//! Every class must indicate which kind of mutability its instances use: +//! - Is the instance mutable or immutable? +//! - Does it use interior mutability (mutable behind `&self`, like +//! [`UnsafeCell`])? +//! - Does it access global statics in such a way that the type is only safe +//! to use from the main thread? +//! +//! The answer to these facts influence the final capabilities the type has, +//! as encoded in [the traits in this module](#traits). +//! +//! Concretely, you set [`ClassType::Mutability`] to [one of the types in this +//! module](#structs) to indicate the properties of class you're dealing with +//! (can be done inside [`extern_class!`] and [`declare_class!`]). +//! +//! Note that precious little of Objective-C follows Rust's usual shared xor +//! unique ownership model, most often objects assume interior mutability. +//! +//! [`UnsafeCell`]: core::cell::UnsafeCell +//! [`ClassType::Mutability`]: crate::ClassType::Mutability +//! [`extern_class!`]: crate::extern_class +//! [`declare_class!`]: crate::declare_class +//! +//! +//! # SemVer +//! +//! It is considered a major change to change the [`ClassType::Mutability`] of +//! an object, though it can be done as a minor change in some cases to fix a +//! bug. +use core::marker::PhantomData; + +use crate::ClassType; + +/// Helper to make the structs uninhabited, without that being a public fact. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +enum Never {} + +/// Marker type for root classes. +/// +/// This is used for `icrate::Foundation::NSObject` and +/// `icrate::Foundation::NSProxy`, which are the two fundamental types that +/// all others inherit from. +/// +/// Functionality that is provided with this: +/// - [`IsIdCloneable`] -> [`Id::clone`][crate::rc::Id#impl-Clone-for-Id]. +/// - [`IsAllocableAnyThread`] -> [`ClassType::alloc`]. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct Root { + inner: Never, +} + +/// Marker type for immutable classes. +/// +/// Note that immutable objects are often both [`Send`] and [`Sync`], though +/// such implementations must be provided manually. +/// +/// Functionality that is provided with this: +/// - [`IsRetainable`] -> [`ClassType::retain`]. +/// - [`IsIdCloneable`] -> [`Id::clone`][crate::rc::Id#impl-Clone-for-Id]. +/// - [`IsAllocableAnyThread`] -> [`ClassType::alloc`]. +/// - You are allowed to hand out pointers / references to an instance's +/// internal data, since you know such data will never be mutated. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct Immutable { + inner: Never, +} + +/// Marker type for mutable classes. +/// +/// Note that mutable objects are often both [`Send`] and [`Sync`], though +/// such implementations must be provided manually (and are usually only safe +/// if all mutation happens behind `&mut self`). +/// +/// Functionality that is provided with this: +/// - [`IsAllocableAnyThread`] -> [`ClassType::alloc`]. +/// - [`IsMutable`] -> [`impl DerefMut for Id`][crate::rc::Id#impl-DerefMut-for-Id]. +/// - You are allowed to hand out pointers / references to an instance's +/// internal data, since you know such data will never be mutated without +/// the borrowchecker catching it. +/// +/// +/// # Safety notice +/// +/// - (Safe) methods that mutate the object (without synchronization) are +/// required to use `&mut self`. +/// - The `retain` selector is not generally safe to use on classes `T` that +/// specify this, since `Id` allows having `&mut T` references, which +/// Rust assume are unique. +/// - As a special case of that, `-[NSCopying copy]` and +/// `-[NSMutableCopying mutableCopy]`, if implemented, must return a new +/// instance (e.g. they cannot just `retain` the instance). +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct Mutable { + inner: Never, +} + +/// Marker type for immutable classes that have a mutable counterpart. +/// +/// This is effectively the same as [`Immutable`], except for the fact that +/// classes that specify this does not implement [`IsRetainable`], meaning +/// that [`ClassType::retain`] does not work (see that for details on why). +/// +/// The mutable counterpart must be specified as the type parameter `MS` to +/// allow `NSCopying` and `NSMutableCopying` to return the correct type. +/// +/// +/// # Example +/// +/// ```ignore +/// unsafe impl ClassType for NSString { +/// type Super = NSObject; +/// type Mutability = ImmutableWithMutableSubclass; +/// // ... +/// } +/// +/// unsafe impl ClassType for NSMutableString { +/// type Super = NSString; +/// type Mutability = MutableWithImmutableSubclass; +/// // ... +/// } +/// ``` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct ImmutableWithMutableSubclass { + inner: Never, + mutable_subclass: PhantomData, +} + +/// Marker type for mutable classes that have a immutable counterpart. +/// +/// This is effectively the same as [`Mutable`], except that the immutable +/// counterpart being be specified as the type parameter `IS` to allow +/// `NSCopying` and `NSMutableCopying` to return the correct type. +/// +/// +/// # Example +/// +/// ```ignore +/// unsafe impl ClassType for NSData { +/// type Super = NSObject; +/// type Mutability = ImmutableWithMutableSubclass; +/// // ... +/// } +/// +/// unsafe impl ClassType for NSMutableData { +/// type Super = NSData; +/// type Mutability = MutableWithImmutableSubclass; +/// // ... +/// } +/// ``` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct MutableWithImmutableSuperclass { + inner: Never, + immutable_superclass: PhantomData, +} + +/// Marker type for classes that use interior mutability. +/// +/// This is usually not `Send + Sync`, unless the class is guaranteed to use +/// thread-safe operations. +/// +/// Functionality that is provided with this: +/// - [`IsRetainable`] -> [`ClassType::retain`]. +/// - [`IsIdCloneable`] -> [`Id::clone`][crate::rc::Id#impl-Clone-for-Id]. +/// - [`IsAllocableAnyThread`] -> [`ClassType::alloc`]. +/// +/// +/// # Safety notice +/// +/// When declaring classes, it is recommended that you wrap your instance +/// variables in [`Cell`], [`RefCell`], atomics or other similar interior +/// mutability abstractions to allow mutating your instance variables through +/// `&self`. +/// +/// Declared classes that use this cannot take `&mut self`, except in +/// initializers. +/// +/// [`Cell`]: core::cell::Cell +/// [`RefCell`]: core::cell::RefCell +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct InteriorMutable { + inner: Never, +} + +/// Marker type for classes that are only safe to use from the main thread. +/// +/// This is effectively the same as [`InteriorMutable`], except that classes +/// that specify this are only allowed to be used from the main thread, and +/// hence are not [`IsAllocableAnyThread`]. +/// +/// This is commonly used in GUI code like `AppKit` and `UIKit`, e.g. +/// `UIWindow` is only usable from the application's main thread. +// +// While Xcode's Main Thread Checker doesn't report `alloc` and `dealloc` as +// unsafe from other threads, things like `NSView` and `NSWindow` still do a +// non-trivial amount of stuff on `dealloc`, even if the object is freshly +// `alloc`'d - so let's disallow that to be sure. +// +// This also has the nice property that `Allocated` is guaranteed to be +// allowed to initialize on the current thread. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct MainThreadOnly { + inner: Never, +} + +mod private { + use super::*; + + pub trait Sealed {} + impl Sealed for Root {} + impl Sealed for Immutable {} + impl Sealed for Mutable {} + impl Sealed for ImmutableWithMutableSubclass {} + impl Sealed for MutableWithImmutableSuperclass {} + impl Sealed for InteriorMutable {} + impl Sealed for MainThreadOnly {} + + pub trait MutabilityIsIdCloneable: Mutability {} + impl MutabilityIsIdCloneable for Root {} + impl MutabilityIsIdCloneable for Immutable {} + impl MutabilityIsIdCloneable for ImmutableWithMutableSubclass {} + impl MutabilityIsIdCloneable for InteriorMutable {} + impl MutabilityIsIdCloneable for MainThreadOnly {} + + pub trait MutabilityIsRetainable: MutabilityIsIdCloneable {} + impl MutabilityIsRetainable for Immutable {} + impl MutabilityIsRetainable for InteriorMutable {} + impl MutabilityIsRetainable for MainThreadOnly {} + + pub trait MutabilityIsAllocableAnyThread: Mutability {} + impl MutabilityIsAllocableAnyThread for Root {} + impl MutabilityIsAllocableAnyThread for Immutable {} + impl MutabilityIsAllocableAnyThread for Mutable {} + impl MutabilityIsAllocableAnyThread for ImmutableWithMutableSubclass {} + impl MutabilityIsAllocableAnyThread for MutableWithImmutableSuperclass {} + impl MutabilityIsAllocableAnyThread for InteriorMutable {} + + pub trait MutabilityIsMutable: Mutability {} + impl MutabilityIsMutable for Mutable {} + impl MutabilityIsMutable for MutableWithImmutableSuperclass {} + + // TODO: Trait for objects whose `hash` is guaranteed to never change, + // which allows it to be used as a key in `NSDictionary`. +} + +/// Marker trait for the different types of mutability a class can have. +/// +/// This is a sealed trait, and should not need to be implemented. Open an +/// issue if you know a use-case where this restrition should be lifted! +pub trait Mutability: private::Sealed + Sized {} +impl Mutability for Root {} +impl Mutability for Immutable {} +impl Mutability for Mutable {} +impl Mutability for ImmutableWithMutableSubclass {} +impl Mutability for MutableWithImmutableSuperclass {} +impl Mutability for InteriorMutable {} +impl Mutability for MainThreadOnly {} + +/// Marker trait for classes where [`Id::clone`][clone-id] is safe. +/// +/// This is implemented for classes whose [`ClassType::Mutability`] is one of: +/// - [`Root`]. +/// - [`Immutable`]. +/// - [`ImmutableWithMutableSubclass`]. +/// - [`InteriorMutable`]. +/// - [`MainThreadOnly`]. +/// +/// [clone-id]: crate::rc::Id#impl-Clone-for-Id +pub trait IsIdCloneable: ClassType {} +impl IsIdCloneable for T where T::Mutability: private::MutabilityIsIdCloneable +{} + +/// Marker trait for classes where the [`retain`] selector is always safe. +/// +/// This is implemented for classes whose [`ClassType::Mutability`] is one of: +/// - [`Immutable`]. +/// - [`InteriorMutable`]. +/// - [`MainThreadOnly`]. +/// +/// [`retain`]: ClassType::retain +pub trait IsRetainable: IsIdCloneable {} +impl IsRetainable for T where T::Mutability: private::MutabilityIsRetainable {} + +/// Marker trait for classes that can be allocated from any thread. +/// +/// This is implemented for classes whose [`ClassType::Mutability`] is one of: +/// - [`Root`]. +/// - [`Immutable`]. +/// - [`Mutable`]. +/// - [`ImmutableWithMutableSubclass`]. +/// - [`MutableWithImmutableSuperclass`]. +/// - [`InteriorMutable`]. +pub trait IsAllocableAnyThread: ClassType {} +impl IsAllocableAnyThread for T where + T::Mutability: private::MutabilityIsAllocableAnyThread +{ +} + +/// Marker trait for classes that are only mutable through `&mut`. +/// +/// This is implemented for classes whose [`ClassType::Mutability`] is one of: +/// - [`Mutable`] +/// - [`MutableWithImmutableSuperclass`] +/// +/// Notably, [`InteriorMutable`] does not implement this (though it is +/// technically mutable), since it is allowed to mutate through shared +/// references. +pub trait IsMutable: ClassType {} +impl IsMutable for T where T::Mutability: private::MutabilityIsMutable {} + +#[cfg(test)] +mod tests { + use super::*; + + use core::fmt; + use core::hash; + + #[test] + fn generic_traits() { + fn assert_traits() + where + T: Sync + Send, + T: Clone + Copy + PartialEq + Eq + PartialOrd + Ord + hash::Hash + fmt::Debug, + { + } + + assert_traits::(); + assert_traits::(); + assert_traits::(); + assert_traits::>(); + assert_traits::>(); + assert_traits::(); + assert_traits::(); + + #[allow(unused)] + fn test_mutability_implies_sized() { + fn assert_sized() {} + assert_sized::(); + } + } +} diff --git a/crates/objc2/src/rc/allocated.rs b/crates/objc2/src/rc/allocated.rs index 2aff582aa..4ad13276b 100644 --- a/crates/objc2/src/rc/allocated.rs +++ b/crates/objc2/src/rc/allocated.rs @@ -13,21 +13,16 @@ use crate::Message; /// `*mut T` is: /// - To allow releasing allocated objects, e.g. in the face of panics. /// - To safely know the object is valid (albeit uninitialized). -/// -/// Note that there is no way to specify ownership of allocated types, since -/// whether or not the allocated object is [`Owned`] or [`Shared`] is really -/// something to be determined _after_ you know which initializer it has been -/// called with. -/// -/// For example, `+[NSMutableString alloc]` is allowed to return a non-unique -/// object as an optimization, and then only figure out afterwards whether it -/// needs to allocate, or if it can store an `NSString` internally. -/// Similarly, while e.g. `+[NSData alloc]` may return a unique object, -/// calling `-[NSData init]` afterwards could easily just return a shared -/// empty `NSData` instance. -/// -/// [`Shared`]: super::Shared -/// [`Owned`]: super::Owned +// +// Note that there is no way to get something mutable out of `Allocated` +// unless you're defining the object. +// +// For example, `+[NSMutableString alloc]` is allowed to return a non-unique +// object as an optimization, and then only figure out afterwards whether it +// needs to allocate, or if it can store an `NSString` internally. +// Similarly, while e.g. `+[NSData alloc]` may return a unique object, +// calling `-[NSData init]` afterwards could easily just return a shared +// empty `NSData` instance. #[repr(transparent)] #[derive(Debug)] pub struct Allocated { @@ -49,7 +44,7 @@ pub struct Allocated { // Explicitly don't implement `Deref`, `Message` nor `RefEncode`! -impl Allocated { +impl Allocated { /// # Safety /// /// The caller must ensure the given object has +1 retain count, and that diff --git a/crates/objc2/src/rc/autorelease.rs b/crates/objc2/src/rc/autorelease.rs index df033afbb..016f0da41 100644 --- a/crates/objc2/src/rc/autorelease.rs +++ b/crates/objc2/src/rc/autorelease.rs @@ -516,36 +516,27 @@ where f(AutoreleasePool::new(None)) } -#[cfg(all(test, feature = "unstable-autoreleasesafe"))] +#[cfg(test)] mod tests { use core::marker::Unpin; use core::mem; use core::panic::{RefUnwindSafe, UnwindSafe}; + use static_assertions::{assert_impl_all, assert_not_impl_any}; + use super::{AutoreleasePool, AutoreleaseSafe}; use crate::runtime::Object; - fn requires_autoreleasesafe() {} - - #[test] - fn test_autoreleasesafe() { - requires_autoreleasesafe::(); - requires_autoreleasesafe::<*mut Object>(); - requires_autoreleasesafe::<&mut Object>(); - } - - #[test] - fn unwindsafe() { - fn assert_unwindsafe() {} - - assert_unwindsafe::>(); - } - #[test] - fn unpin() { - fn assert_unpin() {} + fn auto_traits() { + assert_impl_all!(AutoreleasePool<'static>: Unpin, UnwindSafe, RefUnwindSafe); + assert_not_impl_any!(AutoreleasePool<'static>: Send, Sync); - assert_unwindsafe::>(); + assert_impl_all!(usize: AutoreleaseSafe); + assert_impl_all!(*mut Object: AutoreleaseSafe); + assert_impl_all!(&mut Object: AutoreleaseSafe); + #[cfg(feature = "unstable-autoreleasesafe")] + assert_not_impl_any!(AutoreleasePool<'static>: AutoreleaseSafe); } #[allow(unused)] diff --git a/crates/objc2/src/rc/id.rs b/crates/objc2/src/rc/id.rs index 25c1ef1b5..a51b9c993 100644 --- a/crates/objc2/src/rc/id.rs +++ b/crates/objc2/src/rc/id.rs @@ -6,107 +6,113 @@ use core::panic::{RefUnwindSafe, UnwindSafe}; use core::ptr::{self, NonNull}; use super::AutoreleasePool; -use super::{Owned, Ownership, Shared}; -use crate::ffi; -use crate::{ClassType, Message}; +use crate::mutability::{IsIdCloneable, IsMutable}; +use crate::{ffi, ClassType, Message}; -/// An pointer for Objective-C reference counted objects. +/// A reference counted pointer type for Objective-C objects. /// /// [`Id`] strongly references or "retains" the given object `T`, and -/// "releases" it again when dropped, thereby ensuring it will be deallocated -/// at the right time. +/// decrements the retain count or "releases" it again when dropped, thereby +/// ensuring it will be deallocated at the right time. /// -/// An [`Id`] can either be [`Owned`] or [`Shared`], represented with the `O` -/// type parameter. The default is [`Shared`]. +/// The type `T` inside `Id` can be anything that implements [`Message`]. /// -/// If owned, it is guaranteed that there are no other references to the -/// object, and the [`Id`] can therefore be mutably dereferenced. +/// `T`'s [`ClassType`] implementation (if any) determines whether it is +/// mutable, and by extension whether `Id` is mutable. /// -/// If shared, however, it can only be immutably dereferenced because there -/// may be other references to the object, since a shared [`Id`] can be cloned -/// to provide exactly that. +/// This can usually be gotten from one of the methods in `icrate`, but can be +/// created manually with the [`msg_send_id!`] macro (or even more manually, +/// with the [`Id::new`], [`Id::retain`] or [`Id::retain_autoreleased`] +/// methods). /// -/// An [`Id`] can be safely converted to a [`Id`] using -/// [`Id::into_shared`] or `From`/`Into`. The opposite is not safely possible, -/// but the unsafe option [`Id::from_shared`] is provided. -/// -/// `Option>` is guaranteed to have the same size as a pointer to the -/// object. +/// [`msg_send_id!`]: crate::msg_send_id /// /// /// # Comparison to `std` types /// -/// `Id` can be thought of as the Objective-C equivalent of [`Box`] -/// from the standard library: It is a unique pointer to some allocated -/// object, and that means you're allowed to get a mutable reference to it. +/// `Id` can be thought of as kind of a weird combination of [`Arc`] and +/// [`Box`]: /// -/// Likewise, `Id` is the Objective-C equivalent of [`Arc`]: It is -/// a reference-counting pointer that, when cloned, increases the reference -/// count. +/// If `T` implements [`IsMutable`] (like it does on `NSMutableString` and +/// `NSMutableArray<_>`), `Id` acts like `Box`, and allows mutable / +/// unique access to the type. +/// +/// Otherwise, which is the most common case, `Id` acts like `Arc`, and +/// allows cloning by bumping the reference count. /// -/// [`Box`]: alloc::boxed::Box /// [`Arc`]: alloc::sync::Arc +/// [`Box`]: alloc::boxed::Box +/// /// -/// # Caveats +/// # Memory layout /// -/// If the inner type implements [`Drop`], that implementation will not be -/// called, since there is no way to ensure that the Objective-C runtime will -/// do so. If you need to run some code when the object is destroyed, -/// implement the `dealloc` method instead. +/// This is guaranteed to have the same size and alignment as a pointer to the +/// object, `*const T`. /// -/// This allows `?Sized` types `T`, but the intention is to only support when -/// `T` is an `extern type` (yet unstable). +/// Additionally, it participates in the null-pointer optimization, that is, +/// `Option>` is guaranteed to have the same size as `Id`. /// -/// # Examples /// -/// ```no_run -/// use objc2::msg_send_id; -/// use objc2::runtime::{Class, Object}; -/// use objc2::rc::{Id, Owned, Shared, WeakId}; /// -/// let cls = Class::get("NSObject").unwrap(); -/// let obj: Id = unsafe { msg_send_id![cls, new] }; -/// // obj will be released when it goes out of scope +/// # Example /// -/// // share the object so we can clone it -/// let obj: Id<_, Shared> = obj.into(); -/// let another_ref = obj.clone(); -/// // dropping our other reference will decrement the retain count -/// drop(another_ref); +/// Various usage of `Id` on an immutable object. /// -/// let weak = WeakId::new(&obj); -/// assert!(weak.load().is_some()); -/// // After the object is deallocated, our weak pointer returns none -/// drop(obj); -/// assert!(weak.load().is_none()); /// ``` +/// # #[cfg(not_available)] +/// use icrate::Foundation::{NSObject, NSString}; +/// # use objc2::runtime::NSObject; +/// use objc2::rc::Id; +/// use objc2::{ClassType, msg_send_id}; +/// # +/// # objc2::extern_class!( +/// # pub struct NSString; +/// # +/// # unsafe impl ClassType for NSString { +/// # type Super = NSObject; +/// # // This is wrong, but let's do it for the example +/// # type Mutability = objc2::mutability::Immutable; +/// # } +/// # ); /// -/// ```no_run -/// # use objc2::{class, msg_send_id}; -/// # use objc2::runtime::Object; -/// # use objc2::rc::{Id, Owned, Shared}; -/// # type T = Object; -/// let mut owned: Id; -/// # owned = unsafe { msg_send_id![class!(NSObject), new] }; -/// let mut_ref: &mut T = &mut *owned; -/// // Do something with `&mut T` here +/// // Use `msg_send_id!` to create an `Id` with correct memory management +/// // +/// // SAFETY: The types are correct, and it is safe to call the `new` +/// // selector on `NSString`. +/// let string: Id = unsafe { msg_send_id![NSString::class(), new] }; +/// // Or simply: +/// // let string = NSString::new(); /// -/// let shared: Id = owned.into(); -/// let cloned: Id = shared.clone(); -/// // Do something with `&T` here +/// // Methods on `NSString` is usable via. `Deref` +/// #[cfg(not_available)] +/// assert_eq!(string.len(), 0); +/// +/// // Bump the reference count of the object (possible because the object is +/// // immutable, would not be possible for `NSMutableString`). +/// let another_ref: Id = string.clone(); +/// +/// // Convert one of the references to a reference to `NSObject` instead +/// let obj: Id = Id::into_super(string); +/// +/// // And use the `Debug` impl from that +/// assert_eq!(format!("{obj:?}"), ""); +/// +/// // Finally, the `Id`s go out of scope, the reference counts are decreased, +/// // and the string will deallocate /// ``` #[repr(transparent)] -// TODO: Figure out if `Message` bound on `T` would be better here? +#[doc(alias = "id")] +#[doc(alias = "Retained")] +#[doc(alias = "StrongPtr")] // TODO: Add `ptr::Thin` bound on `T` to allow for only extern types -// TODO: Consider changing the name of Id -> Retain -pub struct Id { +pub struct Id { /// A pointer to the contained object. The pointer is always retained. /// /// It is important that this is `NonNull`, since we want to dereference /// it later, and be able to use the null-pointer optimization. /// /// Additionally, covariance is correct because we're either the unique - /// owner of `T` (O = Owned), or `T` is immutable (O = Shared). + /// owner of `T`, or `T` is immutable. ptr: NonNull, /// Necessary for dropck even though we never actually run T's destructor, /// because it might have a `dealloc` that assumes that contained @@ -114,8 +120,6 @@ pub struct Id { /// /// See item: PhantomData, - /// To prevent warnings about unused type parameters. - own: PhantomData, /// Marks the type as !UnwindSafe. Later on we'll re-enable this. /// /// See for why this is @@ -123,73 +127,61 @@ pub struct Id { notunwindsafe: PhantomData<&'static mut ()>, } -impl Id { +impl Id { #[inline] pub(crate) unsafe fn new_nonnull(ptr: NonNull) -> Self { Self { ptr, item: PhantomData, - own: PhantomData, notunwindsafe: PhantomData, } } } -impl Id { - /// Constructs an [`Id`] to an object that already has +1 retain count. +impl Id { + /// Construct an [`Id`] from a pointer that already has +1 retain count. + /// + /// Returns `None` if the pointer was NULL. /// /// This is useful when you have a retain count that has been handed off /// from somewhere else, usually Objective-C methods like `init`, `alloc`, /// `new`, `copy`, or methods with the `ns_returns_retained` attribute. /// - /// Since most of the above methods create new objects, and you therefore - /// hold unique access to the object, you would often set the ownership to - /// be [`Owned`]. - /// - /// But some immutable objects (like `NSString`) don't always return - /// unique references, so in those case you would use [`Shared`]. - /// - /// Returns `None` if the pointer was null. - /// /// /// # Safety /// - /// The caller must ensure the given object has +1 retain count, and that - /// the object pointer otherwise follows the same safety requirements as - /// in [`Id::retain`]. + /// You must uphold the same requirements as described in [`Id::retain`]. + /// + /// Additionally, you must ensure the given object pointer has +1 retain + /// count. /// /// /// # Example /// - /// ```no_run - /// # use objc2::{class, msg_send, msg_send_id}; - /// # use objc2::runtime::{Class, Object}; - /// # use objc2::rc::{Id, Owned}; - /// let cls: &Class; - /// # let cls = class!(NSObject); - /// let obj: &mut Object = unsafe { msg_send![cls, alloc] }; - /// let obj: Id = unsafe { Id::new(msg_send![obj, init]).unwrap() }; - /// // Or utilizing `msg_send_id`: - /// let obj = unsafe { msg_send_id![cls, alloc] }; - /// let obj: Id = unsafe { msg_send_id![obj, init] }; - /// // Or in this case simply just: - /// let obj: Id = unsafe { msg_send_id![cls, new] }; + /// Comparing different ways of creating a new `NSObject`. + /// /// ``` + /// use objc2::rc::Id; + /// use objc2::runtime::NSObject; + /// use objc2::{msg_send, msg_send_id, ClassType}; /// - /// ```no_run - /// # use objc2::{class, msg_send_id}; - /// # use objc2::runtime::Object; - /// # use objc2::rc::{Id, Shared}; - /// # type NSString = Object; - /// let cls = class!(NSString); - /// // NSString is immutable, so don't create an owned reference to it - /// let obj: Id = unsafe { msg_send_id![cls, new] }; + /// // Manually using `msg_send!` and `Id::new` + /// let obj: *mut NSObject = unsafe { msg_send![NSObject::class(), alloc] }; + /// let obj: *mut NSObject = unsafe { msg_send![obj, init] }; + /// // SAFETY: `-[NSObject init]` returns +1 retain count + /// let obj: Id = unsafe { Id::new(obj).unwrap() }; + /// + /// // Or with `msg_send_id!` + /// let obj: Id = unsafe { msg_send_id![NSObject::alloc(), init] }; + /// + /// // Or using the `NSObject::new` method + /// let obj = NSObject::new(); /// ``` #[inline] // Note: We don't take a reference as a parameter since it would be too // easy to accidentally create two aliasing mutable references. - pub unsafe fn new(ptr: *mut T) -> Option> { - // Should optimize down to nothing. + pub unsafe fn new(ptr: *mut T) -> Option { + // Should optimize down to a noop. // SAFETY: Upheld by the caller NonNull::new(ptr).map(|ptr| unsafe { Id::new_nonnull(ptr) }) } @@ -202,17 +194,10 @@ impl Id { /// /// This is an associated method, and must be called as `Id::as_ptr(obj)`. #[inline] - pub fn as_ptr(this: &Id) -> *const T { + pub fn as_ptr(this: &Self) -> *const T { this.ptr.as_ptr() } - #[inline] - pub(crate) fn consume_as_ptr(this: ManuallyDrop) -> *mut T { - this.ptr.as_ptr() - } -} - -impl Id { /// Returns a raw mutable pointer to the object. /// /// The pointer is valid for at least as long as the `Id` is held. @@ -222,13 +207,21 @@ impl Id { /// This is an associated method, and must be called as /// `Id::as_mut_ptr(obj)`. #[inline] - pub fn as_mut_ptr(this: &mut Id) -> *mut T { + pub fn as_mut_ptr(this: &mut Self) -> *mut T + where + T: IsMutable, + { + this.ptr.as_ptr() + } + + #[inline] + pub(crate) fn consume_as_ptr(this: ManuallyDrop) -> *mut T { this.ptr.as_ptr() } } // TODO: Add ?Sized bound -impl Id { +impl Id { /// Convert the type of the given object to another. /// /// This is equivalent to a `cast` between two pointers. @@ -257,8 +250,14 @@ impl Id { /// /// Additionally, you must ensure that any safety invariants that the new /// type has are upheld. + /// + /// Note that it is not in general safe to cast e.g. `Id` to + /// `Id`, even if you've checked at runtime that the + /// object is an instance of `NSMutableString`! This is because + /// `Id` assumes the string is unique, whereas it may + /// have been cloned while being an `Id`. #[inline] - pub unsafe fn cast(this: Self) -> Id { + pub unsafe fn cast(this: Self) -> Id { let ptr = ManuallyDrop::new(this).ptr.cast(); // SAFETY: The object is forgotten, so we have +1 retain count. // @@ -266,51 +265,38 @@ impl Id { unsafe { Id::new_nonnull(ptr) } } - /// Retains the given object pointer. + /// Retain the pointer and construct an [`Id`] from it. + /// + /// Returns `None` if the pointer was NULL. /// /// This is useful when you have been given a pointer to an object from /// some API, and you would like to ensure that the object stays around - /// so that you can work with it. - /// - /// If said API is a normal Objective-C method, you probably want to use - /// [`Id::retain_autoreleased`] instead. + /// while you work on it. /// - /// This is rarely used to construct owned [`Id`]s, see [`Id::new`] for - /// that. + /// For normal Objective-C methods, you may want to use + /// [`Id::retain_autoreleased`] instead, as that is usually more + /// performant. /// - /// Returns `None` if the pointer was null. + /// See [`ClassType::retain`] for a safe alternative. /// /// /// # Safety /// - /// The caller must ensure that the ownership is correct; that is, there - /// must be no [`Owned`] pointers or mutable references to the same - /// object, and when creating owned [`Id`]s, there must be no other - /// pointers or references to the object. + /// If the object is mutable, the caller must ensure that there are no + /// other pointers or references to the object, such that the returned + /// `Id` pointer is unique. /// /// Additionally, the pointer must be valid as a reference (aligned, /// dereferencable and initialized, see the [`std::ptr`] module for more - /// information). + /// information) or NULL. /// - /// Finally, if you do not know the concrete type of `T`, it may not be - /// `'static`, and hence you must ensure that the data that `T` references - /// lives for as long as `T`. + /// Finally, you must ensure that any data that `T` may reference lives + /// for at least as long as `T`. /// /// [`std::ptr`]: core::ptr - // - // This would be illegal: - // ```no_run - // let owned: Id; - // // Lifetime information is discarded - // let retained: Id = unsafe { Id::retain(&*owned) }; - // // Which means we can still mutate `Owned`: - // let x: &mut T = &mut *owned; - // // While we have an immutable reference - // let y: &T = &*retained; - // ``` #[doc(alias = "objc_retain")] #[inline] - pub unsafe fn retain(ptr: *mut T) -> Option> { + pub unsafe fn retain(ptr: *mut T) -> Option> { // SAFETY: The caller upholds that the pointer is valid let res: *mut T = unsafe { ffi::objc_retain(ptr.cast()) }.cast(); debug_assert_eq!(res, ptr, "objc_retain did not return the same pointer"); @@ -328,7 +314,7 @@ impl Id { /// yielding increased speed and reducing memory pressure. /// /// Note: This relies heavily on being inlined right after [`msg_send!`], - /// be careful not accidentally require instructions between these. + /// be careful to not accidentally require instructions between these. /// /// [mmRules]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html /// [`msg_send!`]: crate::msg_send @@ -339,7 +325,7 @@ impl Id { /// Same as [`Id::retain`]. #[doc(alias = "objc_retainAutoreleasedReturnValue")] #[inline] - pub unsafe fn retain_autoreleased(ptr: *mut T) -> Option> { + pub unsafe fn retain_autoreleased(ptr: *mut T) -> Option> { // Add magic nop instruction to participate in the fast autorelease // scheme. // @@ -407,7 +393,7 @@ impl Id { }; } - // SAFETY: Same as `retain`, this is just an optimization. + // SAFETY: Same as `Id::retain`, this is just an optimization. let res: *mut T = unsafe { ffi::objc_retainAutoreleasedReturnValue(ptr.cast()) }.cast(); // Ideally, we'd be able to specify that the above call should never @@ -436,28 +422,33 @@ impl Id { res, ptr, "objc_retainAutoreleasedReturnValue did not return the same pointer" ); + + // SAFETY: Same as `Id::retain`. unsafe { Self::new(res) } } #[inline] pub(super) fn autorelease_inner(this: Self) -> *mut T { let ptr = ManuallyDrop::new(this).ptr.as_ptr(); - // SAFETY: The `ptr` is guaranteed to be valid and have at least one - // retain count. - // And because of the ManuallyDrop, we don't call the Drop - // implementation, so the object won't also be released there. + // SAFETY: + // - The `ptr` is guaranteed to be valid and have at least one + // retain count. + // - Because of the ManuallyDrop, we don't call the Drop + // implementation, so the object won't also be released there. let res: *mut T = unsafe { ffi::objc_autorelease(ptr.cast()) }.cast(); debug_assert_eq!(res, ptr, "objc_autorelease did not return the same pointer"); res } - /// Autoreleases the shared [`Id`], returning an aliased reference bound - /// to the pool. + /// Autoreleases the [`Id`], returning a reference bound to the pool. /// /// The object is not immediately released, but will be when the innermost /// / current autorelease pool (given as a parameter) is drained. /// /// See [`Id::autorelease_mut`] for the mutable alternative. + /// + /// This is an associated method, and must be called as + /// `Id::autorelease(obj, pool)`. #[doc(alias = "objc_autorelease")] #[must_use = "If you don't intend to use the object any more, just drop it as usual"] #[inline] @@ -468,6 +459,32 @@ impl Id { unsafe { pool.ptr_as_ref(ptr) } } + /// Autoreleases the [`Id`], returning a mutable reference bound to the + /// pool. + /// + /// The object is not immediately released, but will be when the innermost + /// / current autorelease pool (given as a parameter) is drained. + /// + /// See [`Id::autorelease`] for the immutable alternative. + /// + /// This is an associated method, and must be called as + /// `Id::autorelease_mut(obj, pool)`. + #[doc(alias = "objc_autorelease")] + #[must_use = "If you don't intend to use the object any more, just drop it as usual"] + #[inline] + #[allow(clippy::needless_lifetimes)] + pub fn autorelease_mut<'p>(this: Self, pool: AutoreleasePool<'p>) -> &'p mut T + where + T: IsMutable, + { + let ptr = Self::autorelease_inner(this); + // SAFETY: + // - The pointer is valid as a reference. + // - The object is safe as mutable because of the `T: IsMutable` + // bound + the consumption of unique access to the `Id`. + unsafe { pool.ptr_as_mut(ptr) } + } + #[inline] pub(crate) fn autorelease_return_option(this: Option) -> *mut T { let ptr: *mut T = this @@ -493,33 +510,37 @@ impl Id { /// properly follow [Cocoa's Memory Management Policy][mmRules]. /// /// To that end, you could use [`Id::autorelease`], but that would require - /// you to have an [`AutoreleasePool`] object at hand, which you clearly + /// you to have an [`AutoreleasePool`] object at hand, which you often /// won't have in such cases. This function doesn't require a `pool` /// object (but as a downside returns a pointer instead of a reference). /// - /// This is also more efficient than a normal `autorelease`, it makes a - /// best effort attempt to hand off ownership of the retain count to a - /// subsequent call to `objc_retainAutoreleasedReturnValue` / - /// [`Id::retain_autoreleased`] in the enclosing call frame. Note: This - /// optimization relies heavily on this function being tail called, so be - /// careful to call this function at the end of your method. + /// This is also more efficient than a normal `autorelease`, since it + /// makes a best effort attempt to hand off ownership of the retain count + /// to a subsequent call to `objc_retainAutoreleasedReturnValue` / + /// [`Id::retain_autoreleased`] in the enclosing call frame. + /// + /// This optimization relies heavily on this function being tail called, + /// so make sure you only call this function at the end of your method. /// /// [declare]: crate::declare /// [mmRules]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html /// /// - /// # Examples + /// # Example + /// + /// Returning an `Id` from a declared method (note: the [`declare_class!`] + /// macro supports doing this for you automatically). /// /// ``` /// use objc2::{class, msg_send_id, sel}; /// use objc2::declare::ClassBuilder; - /// use objc2::rc::{Id, Owned}; + /// use objc2::rc::Id; /// use objc2::runtime::{Class, Object, Sel}; /// /// let mut builder = ClassBuilder::new("ExampleObject", class!(NSObject)).unwrap(); /// /// extern "C" fn get(cls: &Class, _cmd: Sel) -> *mut Object { - /// let obj: Id = unsafe { msg_send_id![cls, new] }; + /// let obj: Id = unsafe { msg_send_id![cls, new] }; /// Id::autorelease_return(obj) /// } /// @@ -532,6 +553,8 @@ impl Id { /// /// let cls = builder.register(); /// ``` + /// + /// [`declare_class!`]: crate::declare_class #[doc(alias = "objc_autoreleaseReturnValue")] #[must_use = "If you don't intend to use the object any more, just drop it as usual"] #[inline] @@ -540,96 +563,24 @@ impl Id { } } -// TODO: Consider something like this -// #[cfg(block)] -// impl Id { -// #[doc(alias = "objc_retainBlock")] -// pub unsafe fn retain_block(block: *mut T) -> Option { -// todo!() -// } -// } - -// TODO: Add ?Sized bound -impl Id { - /// Autoreleases the owned [`Id`], returning a mutable reference bound to - /// the pool. - /// - /// The object is not immediately released, but will be when the innermost - /// / current autorelease pool (given as a parameter) is drained. - /// - /// See [`Id::autorelease`] for the immutable alternative. - #[doc(alias = "objc_autorelease")] - #[must_use = "If you don't intend to use the object any more, just drop it as usual"] - #[inline] - #[allow(clippy::needless_lifetimes)] - pub fn autorelease_mut<'p>(this: Self, pool: AutoreleasePool<'p>) -> &'p mut T { - let ptr = Self::autorelease_inner(this); - // SAFETY: The pointer is valid as a reference, and we've consumed - // the unique access to the `Id` so mutability is safe. - unsafe { pool.ptr_as_mut(ptr) } - } - - /// Promote a shared [`Id`] to an owned one, allowing it to be mutated. - /// - /// - /// # Safety - /// - /// The caller must ensure that there are no other pointers (including - /// [`WeakId`][`super::WeakId`] pointers) to the same object. - /// - /// This also means that the given [`Id`] should have a retain count of - /// exactly 1 (except when autoreleases are involved). - /// - /// In general, this is wildly unsafe, do see if you can find a different - /// solution! - #[inline] - pub unsafe fn from_shared(obj: Id) -> Self { - // Note: We can't debug_assert retainCount because of autoreleases - let ptr = ManuallyDrop::new(obj).ptr; - // SAFETY: The pointer is valid - // Ownership rules are upheld by the caller - unsafe { >::new_nonnull(ptr) } - } - - /// Convert an owned to a shared [`Id`], allowing it to be cloned. - /// - /// This is also implemented as a `From` conversion, but this name is more - /// explicit, which may be useful in some cases. - #[inline] - pub fn into_shared(obj: Self) -> Id { - let ptr = ManuallyDrop::new(obj).ptr; - // SAFETY: The pointer is valid, and ownership is simply decreased - unsafe { >::new_nonnull(ptr) } - } -} - -impl Id +impl Id where T::Super: 'static, { /// Convert the object into its superclass. #[inline] - pub fn into_super(this: Self) -> Id { + pub fn into_super(this: Self) -> Id { // SAFETY: // - The casted-to type is a superclass of the type. - // - Both types are `'static` (this could maybe be relaxed a bit, but - // let's just be on the safe side)! + // - Both types are `'static`, so no lifetime information is lost + // (this could maybe be relaxed a bit, but let's just be on the safe + // side for now). unsafe { Self::cast::(this) } } } -impl From> for Id { - /// Convert an owned to a shared [`Id`], allowing it to be cloned. - /// - /// Same as [`Id::into_shared`]. - #[inline] - fn from(obj: Id) -> Self { - Id::into_shared(obj) - } -} - // TODO: Add ?Sized bound -impl Clone for Id { +impl Clone for Id { /// Makes a clone of the shared object. /// /// This increases the object's reference count. @@ -637,10 +588,18 @@ impl Clone for Id { #[doc(alias = "retain")] #[inline] fn clone(&self) -> Self { - // SAFETY: The pointer is valid + // SAFETY: + // - The object is known to not be mutable due to the `IsIdCloneable` + // bound. Additionally, since the object is already an `Id`, types + // like `NSObject` and `NSString` that have a mutable subclass is + // also allowed (since even if the object is originally an + // `Id`, by converting it into `Id` or + // `Id` that fact is wholly forgotten, and the object + // cannot ever be mutated again). + // - The pointer is valid. let obj = unsafe { Id::retain(self.ptr.as_ptr()) }; // SAFETY: `objc_retain` always returns the same object pointer, and - // the pointer is guaranteed non-null by Id. + // the pointer is guaranteed non-null. unsafe { obj.unwrap_unchecked() } } } @@ -652,101 +611,220 @@ impl Clone for Id { /// borrowed data. /// /// [dropck_eyepatch]: https://doc.rust-lang.org/nightly/nomicon/dropck.html#an-escape-hatch -impl Drop for Id { +impl Drop for Id { /// Releases the retained object. /// - /// The contained object's destructor (if it has one) is never run! + /// The contained object's destructor (`Drop` impl, if it has one) is + /// never run - override the `dealloc` method instead (which + /// `declare_class!` does for you). #[doc(alias = "objc_release")] #[doc(alias = "release")] #[inline] fn drop(&mut self) { - // We could technically run the destructor for `T` when `O = Owned`, - // and when `O = Shared` with (retainCount == 1), but that would be - // confusing and inconsistent since we cannot guarantee that it's run. + // We could technically run the destructor for `T` when it is mutable, + // but that would be confusing and inconsistent since we cannot really + // guarantee that it is run if the `Id` is passed to Objective-C. // SAFETY: The `ptr` is guaranteed to be valid and have at least one - // retain count + // retain count. unsafe { ffi::objc_release(self.ptr.as_ptr().cast()) }; } } -// https://doc.rust-lang.org/nomicon/arc-mutex/arc-base.html#send-and-sync -/// The `Send` implementation requires `T: Sync` because `Id` give -/// access to `&T`. -/// -/// Additiontally, it requires `T: Send` because if `T: !Send`, you could -/// clone a `Id`, send it to another thread, and drop the clone -/// last, making `dealloc` get called on the other thread, and violate -/// `T: !Send`. -unsafe impl Send for Id {} - -/// The `Sync` implementation requires `T: Sync` because `&Id` give -/// access to `&T`. -/// -/// Additiontally, it requires `T: Send`, because if `T: !Send`, you could -/// clone a `&Id` from another thread, and drop the clone last, -/// making `dealloc` get called on the other thread, and violate `T: !Send`. -unsafe impl Sync for Id {} - -/// `Id` are `Send` if `T` is `Send` because they give the same -/// access as having a T directly. -unsafe impl Send for Id {} - -/// `Id` are `Sync` if `T` is `Sync` because they give the same -/// access as having a `T` directly. -unsafe impl Sync for Id {} - -impl Deref for Id { +impl Deref for Id { type Target = T; /// Obtain an immutable reference to the object. // Box doesn't inline, but that's because it's a compiler built-in #[inline] fn deref(&self) -> &T { - // SAFETY: The pointer's validity is verified when the type is created + // SAFETY: The pointer's validity is verified when the type is + // created. unsafe { self.ptr.as_ref() } } } -impl DerefMut for Id { +impl DerefMut for Id { /// Obtain a mutable reference to the object. #[inline] fn deref_mut(&mut self) -> &mut T { - // SAFETY: The pointer's validity is verified when the type is created - // Additionally, the owned `Id` is the unique owner of the object, so - // mutability is safe. + // SAFETY: The pointer's validity is verified when the type is + // created, and `Id` is the unique owner of the object because of the + // `IsMutable` bound, so mutability is safe. unsafe { self.ptr.as_mut() } } } -impl fmt::Pointer for Id { +impl fmt::Pointer for Id { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Pointer::fmt(&self.ptr.as_ptr(), f) } } +mod private { + use crate::runtime::Object; + use core::panic::{RefUnwindSafe, UnwindSafe}; + + pub struct UnknownStorage(*const T, Object); + + pub struct ArcLikeStorage(*const T); + // SAFETY: Same as `Arc` + unsafe impl Send for ArcLikeStorage {} + // SAFETY: Same as `Arc` + unsafe impl Sync for ArcLikeStorage {} + impl RefUnwindSafe for ArcLikeStorage {} + impl UnwindSafe for ArcLikeStorage {} + impl Unpin for ArcLikeStorage {} + + pub struct BoxLikeStorage(T); + + use crate::mutability; + + #[doc(hidden)] + pub trait IdSendSyncHelper: mutability::Mutability { + type EquivalentType: ?Sized; + } + + impl IdSendSyncHelper for mutability::Root { + // To give us freedom in the future (no `Root` types implement any + // auto traits anyhow). + type EquivalentType = UnknownStorage; + } + + impl IdSendSyncHelper for mutability::Immutable { + type EquivalentType = ArcLikeStorage; + } + + impl IdSendSyncHelper for mutability::Mutable { + type EquivalentType = BoxLikeStorage; + } + + impl IdSendSyncHelper for mutability::ImmutableWithMutableSubclass { + type EquivalentType = ArcLikeStorage; + } + + impl IdSendSyncHelper for mutability::MutableWithImmutableSuperclass { + type EquivalentType = BoxLikeStorage; + } + + impl IdSendSyncHelper for mutability::InteriorMutable { + type EquivalentType = ArcLikeStorage; + } + + impl IdSendSyncHelper for mutability::MainThreadOnly { + type EquivalentType = ArcLikeStorage; + } +} + +// https://doc.rust-lang.org/nomicon/arc-mutex/arc-base.html#send-and-sync + +/// `Id` is always `Send` if `T` is `Send + Sync`. +/// +/// Additionally, for mutable types, `T` doesn't have to be `Sync` (only +/// requires `T: Send`), since it has unique access to the object. +// +// SAFETY: +// - `T: Send` is required because otherwise you could move the object to +// another thread and let `dealloc` get called there. +// - If `T` is not mutable, `T: Sync` is required because otherwise you could +// clone `&Id`, send it to another thread, and drop the clone last, +// making `dealloc` get called on the other thread. +unsafe impl Send for Id +where + T::Mutability: private::IdSendSyncHelper, + >::EquivalentType: Send, +{ +} + +/// `Id` is always `Sync` if `T` is `Send + Sync`. +/// +/// Additionally, for mutable types, `T` doesn't have to be `Send` (only +/// requires `T: Sync`), since it has unique access to the object. +// +// SAFETY: +// - `T: Sync` is required because `&Id` give access to `&T`. +// - If `T` is not mutable, `T: Send` is required because otherwise you could +// clone `&Id` from another thread, and drop the clone last, making +// `dealloc` get called on the other thread. +unsafe impl Sync for Id +where + T::Mutability: private::IdSendSyncHelper, + >::EquivalentType: Sync, +{ +} + // This is valid without `T: Unpin` because we don't implement any projection. // // See https://doc.rust-lang.org/1.54.0/src/alloc/boxed.rs.html#1652-1675 // and the `Arc` implementation. -impl Unpin for Id {} +impl Unpin for Id {} -impl RefUnwindSafe for Id {} +impl RefUnwindSafe for Id {} -// Same as `Arc`. -impl UnwindSafe for Id {} - -// Same as `Box`. -impl UnwindSafe for Id {} +// TODO: Relax this bound +impl UnwindSafe for Id {} #[cfg(test)] mod tests { use core::mem::size_of; + use static_assertions::{assert_impl_all, assert_not_impl_any}; + use super::*; - use crate::msg_send; + use crate::mutability::{Immutable, Mutable}; use crate::rc::{__RcTestObject, __ThreadTestData, autoreleasepool}; use crate::runtime::{NSObject, Object}; + use crate::{declare_class, msg_send}; + + #[test] + fn auto_traits() { + macro_rules! helper { + ($name:ident, $mutability:ty) => { + declare_class!( + struct $name; + + unsafe impl ClassType for $name { + type Super = NSObject; + type Mutability = $mutability; + const NAME: &'static str = concat!(stringify!($name), "Test"); + } + ); + }; + } + + helper!(ImmutableObject, Immutable); + helper!(ImmutableSendObject, Immutable); + unsafe impl Send for ImmutableSendObject {} + helper!(ImmutableSyncObject, Immutable); + unsafe impl Sync for ImmutableSyncObject {} + helper!(ImmutableSendSyncObject, Immutable); + unsafe impl Send for ImmutableSendSyncObject {} + unsafe impl Sync for ImmutableSendSyncObject {} + + helper!(MutableObject, Mutable); + helper!(MutableSendObject, Mutable); + unsafe impl Send for MutableSendObject {} + helper!(MutableSyncObject, Mutable); + unsafe impl Sync for MutableSyncObject {} + helper!(MutableSendSyncObject, Mutable); + unsafe impl Send for MutableSendSyncObject {} + unsafe impl Sync for MutableSendSyncObject {} + + assert_impl_all!(Id: Unpin); + assert_not_impl_any!(Id: Send, Sync, UnwindSafe, RefUnwindSafe); + + assert_not_impl_any!(Id: Send, Sync); + assert_not_impl_any!(Id: Send, Sync); + assert_not_impl_any!(Id: Send, Sync); + assert_impl_all!(Id: Send, Sync); + + assert_not_impl_any!(Id: Send, Sync); + assert_not_impl_any!(Id: Sync); + assert_impl_all!(Id: Send); + assert_not_impl_any!(Id: Send); + assert_impl_all!(Id: Sync); + assert_impl_all!(Id: Send, Sync); + } #[track_caller] fn assert_retain_count(obj: &Object, expected: usize) { @@ -771,7 +849,7 @@ mod tests { #[test] fn test_autorelease() { - let obj: Id<_, Shared> = __RcTestObject::new().into(); + let obj = __RcTestObject::new(); let cloned = obj.clone(); let mut expected = __ThreadTestData::current(); @@ -797,11 +875,10 @@ mod tests { #[test] fn test_clone() { - let obj: Id<_, Owned> = __RcTestObject::new(); + let obj = __RcTestObject::new(); assert_retain_count(&obj, 1); let mut expected = __ThreadTestData::current(); - let obj: Id<_, Shared> = obj.into(); expected.assert_current(); assert_retain_count(&obj, 1); @@ -824,26 +901,26 @@ mod tests { #[test] fn test_retain_autoreleased_works_as_retain() { - let obj: Id<_, Shared> = __RcTestObject::new().into(); + let obj = __RcTestObject::new(); let mut expected = __ThreadTestData::current(); let ptr = Id::as_ptr(&obj) as *mut __RcTestObject; - let _obj2: Id<_, Shared> = unsafe { Id::retain_autoreleased(ptr) }.unwrap(); + let _obj2 = unsafe { Id::retain_autoreleased(ptr) }.unwrap(); expected.retain += 1; expected.assert_current(); } #[test] fn test_cast() { - let obj: Id<__RcTestObject, _> = __RcTestObject::new(); + let obj: Id<__RcTestObject> = __RcTestObject::new(); let expected = __ThreadTestData::current(); // SAFETY: Any object can be cast to `Object` - let obj: Id = unsafe { Id::cast(obj) }; + let obj: Id = unsafe { Id::cast(obj) }; expected.assert_current(); // SAFETY: The object was originally `__RcTestObject` - let _obj: Id<__RcTestObject, _> = unsafe { Id::cast(obj) }; + let _obj: Id<__RcTestObject> = unsafe { Id::cast(obj) }; expected.assert_current(); } @@ -853,11 +930,9 @@ mod tests { p: PhantomData<&'a str>, } - /// Test that `Id` is covariant over `T`. + /// Test that `Id` is covariant over `T`. #[allow(unused)] - fn assert_id_variance<'a, 'b, O: Ownership>( - obj: &'a Id, O>, - ) -> &'a Id, O> { + fn assert_id_variance<'b>(obj: Id>) -> Id> { obj } @@ -865,9 +940,7 @@ mod tests { fn test_size_of() { let ptr_size = size_of::<&NSObject>(); - assert_eq!(size_of::>(), ptr_size); - assert_eq!(size_of::>(), ptr_size); - assert_eq!(size_of::>>(), ptr_size); - assert_eq!(size_of::>>(), ptr_size); + assert_eq!(size_of::>(), ptr_size); + assert_eq!(size_of::>>(), ptr_size); } } diff --git a/crates/objc2/src/rc/id_forwarding_impls.rs b/crates/objc2/src/rc/id_forwarding_impls.rs index f08484251..2829a2926 100644 --- a/crates/objc2/src/rc/id_forwarding_impls.rs +++ b/crates/objc2/src/rc/id_forwarding_impls.rs @@ -20,9 +20,10 @@ use core::task::{Context, Poll}; use std::error::Error; use std::io; -use super::{Id, Owned, Ownership}; +use super::Id; +use crate::mutability::IsMutable; -impl PartialEq for Id { +impl PartialEq for Id { #[inline] fn eq(&self, other: &Self) -> bool { (**self).eq(&**other) @@ -35,9 +36,9 @@ impl PartialEq for Id { } } -impl Eq for Id {} +impl Eq for Id {} -impl PartialOrd for Id { +impl PartialOrd for Id { #[inline] fn partial_cmp(&self, other: &Self) -> Option { (**self).partial_cmp(&**other) @@ -60,20 +61,20 @@ impl PartialOrd for Id { } } -impl Ord for Id { +impl Ord for Id { #[inline] fn cmp(&self, other: &Self) -> Ordering { (**self).cmp(&**other) } } -impl hash::Hash for Id { +impl hash::Hash for Id { fn hash(&self, state: &mut H) { (**self).hash(state) } } -impl hash::Hasher for Id { +impl hash::Hasher for Id { fn finish(&self) -> u64 { (**self).finish() } @@ -118,19 +119,19 @@ impl hash::Hasher for Id { } } -impl fmt::Display for Id { +impl fmt::Display for Id { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } -impl fmt::Debug for Id { +impl fmt::Debug for Id { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } -impl Iterator for Id { +impl Iterator for Id { type Item = I::Item; fn next(&mut self) -> Option { (**self).next() @@ -143,7 +144,7 @@ impl Iterator for Id { } } -impl DoubleEndedIterator for Id { +impl DoubleEndedIterator for Id { fn next_back(&mut self) -> Option { (**self).next_back() } @@ -152,16 +153,16 @@ impl DoubleEndedIterator for Id { } } -impl ExactSizeIterator for Id { +impl ExactSizeIterator for Id { fn len(&self) -> usize { (**self).len() } } -impl FusedIterator for Id {} +impl FusedIterator for Id {} // TODO: Consider this impl -// impl<'a, T, O: Ownership> IntoIterator for &'a Id +// impl<'a, T> IntoIterator for &'a Id // where // &'a T: IntoIterator, // { @@ -173,37 +174,37 @@ impl FusedIterator for Id {} // } // } -impl borrow::Borrow for Id { +impl borrow::Borrow for Id { fn borrow(&self) -> &T { Deref::deref(self) } } -impl borrow::BorrowMut for Id { +impl borrow::BorrowMut for Id { fn borrow_mut(&mut self) -> &mut T { DerefMut::deref_mut(self) } } -impl AsRef for Id { +impl AsRef for Id { fn as_ref(&self) -> &T { Deref::deref(self) } } -impl AsMut for Id { +impl AsMut for Id { fn as_mut(&mut self) -> &mut T { DerefMut::deref_mut(self) } } -impl Error for Id { +impl Error for Id { fn source(&self) -> Option<&(dyn Error + 'static)> { (**self).source() } } -impl io::Read for Id { +impl io::Read for Id { #[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result { (**self).read(buf) @@ -230,7 +231,7 @@ impl io::Read for Id { } } -impl io::Write for Id { +impl io::Write for Id { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { (**self).write(buf) @@ -257,7 +258,7 @@ impl io::Write for Id { } } -impl io::Seek for Id { +impl io::Seek for Id { #[inline] fn seek(&mut self, pos: io::SeekFrom) -> io::Result { (**self).seek(pos) @@ -269,7 +270,7 @@ impl io::Seek for Id { } } -impl io::BufRead for Id { +impl io::BufRead for Id { #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { (**self).fill_buf() @@ -291,7 +292,7 @@ impl io::BufRead for Id { } } -impl Future for Id { +impl Future for Id { type Output = T::Output; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/crates/objc2/src/rc/id_traits.rs b/crates/objc2/src/rc/id_traits.rs index bbcc9abdc..7afb9f150 100644 --- a/crates/objc2/src/rc/id_traits.rs +++ b/crates/objc2/src/rc/id_traits.rs @@ -1,70 +1,19 @@ //! Helper traits for Id. -use super::{Id, Owned, Ownership}; -use crate::Message; +use super::Id; +use crate::mutability::IsAllocableAnyThread; -/// Helper trait for functionality on slices containing [`Id`]s. -pub trait SliceId { - /// The type of the items in the slice. - type Item: ?Sized; - - /// Convert a slice of [`Id`]s into a slice of references. - fn as_slice_ref(&self) -> &[&Self::Item]; - - /// Convert a mutable slice of [`Id`]s into a mutable slice of references. - fn as_slice_mut(&mut self) -> &mut [&Self::Item]; -} - -/// Helper trait for functionality on slices containing owned [`Id`]s. -pub trait SliceIdMut: SliceId { - /// Convert a mutable slice of mutable [`Id`]s into a mutable slice of - /// mutable references. - fn as_mut_slice_mut(&mut self) -> &mut [&mut Self::Item]; -} - -impl SliceId for [Id] { - type Item = T; - - fn as_slice_ref(&self) -> &[&T] { - let ptr = self as *const Self as *const [&T]; - // SAFETY: Id and &T have the same memory layout. Further safety - // follows from `Deref` impl. - unsafe { ptr.as_ref().unwrap_unchecked() } - } - - fn as_slice_mut(&mut self) -> &mut [&T] { - let ptr = self as *mut Self as *mut [&T]; - // SAFETY: Id and &T have the same memory layout. Further safety - // follows from `Deref` impl. - unsafe { ptr.as_mut().unwrap_unchecked() } - } -} - -impl SliceIdMut for [Id] { - fn as_mut_slice_mut(&mut self) -> &mut [&mut T] { - let ptr = self as *mut Self as *mut [&mut T]; - // SAFETY: Id and &mut T have the same memory layout, and the - // `Id` is `Owned` so we're allowed to hand out mutable references. - // Further safety follows from `DerefMut` impl. - unsafe { ptr.as_mut().unwrap_unchecked() } - } -} - -/// Helper trait to implement [`Default`] on types whoose default value is an -/// [`Id`]. +/// Helper trait to implement [`Default`] on [`Id`]. // TODO: Maybe make this `unsafe` and provide a default implementation? -pub trait DefaultId { - /// Indicates whether the default value is mutable or immutable. - type Ownership: Ownership; - +pub trait DefaultId: IsAllocableAnyThread { /// The default [`Id`] for a type. /// /// On most objects the implementation would just be sending a message to /// the `new` selector. - fn default_id() -> Id; + fn default_id() -> Id; } -impl Default for Id { +impl Default for Id { #[inline] fn default() -> Self { T::default_id() diff --git a/crates/objc2/src/rc/mod.rs b/crates/objc2/src/rc/mod.rs index 688f19a59..c5d651c1e 100644 --- a/crates/objc2/src/rc/mod.rs +++ b/crates/objc2/src/rc/mod.rs @@ -1,15 +1,11 @@ -//! Utilities for reference counting Objective-C objects. +//! # Reference counting utilities. //! -//! These utilities in this module provide ARC-like semantics for working with -//! Objective-C's reference counted objects. +//! The types in this module provide roughly the same benefits as ARC does to +//! Objective-C. //! -//! A smart pointer [`Id`] is provided to ensure that Objective-C objects are -//! retained and released when created and dropped, respectively. -//! -//! To enforce aliasing rules, an `Id` can be either owned or shared; if it is -//! owned, meaning the `Id` is the only reference to the object, it can be -//! mutably dereferenced. An owned `Id` can be converted to a shared `Id`, -//! which can be cloned to allow multiple references. +//! Most importantly, a smart pointer [`Id`] is provided to ensure that +//! objects are correctly retained and released when created and dropped, +//! respectively. This ties in strongly with the [`msg_send_id!`] macro. //! //! Weak references may be created using the [`WeakId`] struct; these will not //! retain the object, but one can attempt to load them and obtain an `Id`, or @@ -22,6 +18,7 @@ //! It can also be useful to [enable Malloc Debugging][mem-debug] if you're trying //! to figure out if/where your application has memory errors and leaks. //! +//! [`msg_send_id!`]: crate::msg_send_id //! [clang-arc]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html //! [mem-mgmt]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html //! [cf]: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/CFMemoryMgmt.html @@ -30,27 +27,23 @@ //! //! ## Example //! -#![cfg_attr(feature = "apple", doc = "```")] -#![cfg_attr(not(feature = "apple"), doc = "```no_run")] -//! use objc2::{class, msg_send_id}; +//! ``` //! use objc2::rc::{autoreleasepool, Id, WeakId}; -//! use objc2::runtime::Object; +//! use objc2::runtime::NSObject; //! //! // Id will release the object when dropped -//! let obj: Id = unsafe { -//! msg_send_id![class!(NSObject), new] -//! }; +//! let obj: Id = NSObject::new(); //! //! // Cloning retains the object an additional time //! let cloned = obj.clone(); //! autoreleasepool(|pool| { -//! // Autorelease consumes the Id, but won't -//! // actually release until the end of an autoreleasepool -//! let obj_ref: &Object = Id::autorelease(cloned, pool); +//! // Autorelease consumes the Id, but won't actually +//! // release it until the end of the autoreleasepool +//! let obj_ref: &NSObject = Id::autorelease(cloned, pool); //! }); //! //! // Weak references won't retain the object -//! let weak = WeakId::new(&obj); +//! let weak = WeakId::from_id(&obj); //! drop(obj); //! assert!(weak.load().is_none()); //! ``` @@ -60,7 +53,6 @@ mod autorelease; mod id; mod id_forwarding_impls; mod id_traits; -mod ownership; mod test_object; mod weak_id; mod writeback; @@ -70,7 +62,6 @@ pub use self::autorelease::{ autoreleasepool, autoreleasepool_leaking, AutoreleasePool, AutoreleaseSafe, }; pub use self::id::Id; -pub use self::id_traits::{DefaultId, SliceId, SliceIdMut}; -pub use self::ownership::{Owned, Ownership, Shared}; +pub use self::id_traits::DefaultId; pub use self::test_object::{__RcTestObject, __ThreadTestData}; pub use self::weak_id::WeakId; diff --git a/crates/objc2/src/rc/ownership.rs b/crates/objc2/src/rc/ownership.rs deleted file mode 100644 index da13a9c35..000000000 --- a/crates/objc2/src/rc/ownership.rs +++ /dev/null @@ -1,82 +0,0 @@ -use core::fmt::Debug; -use core::hash::Hash; -use core::panic::{RefUnwindSafe, UnwindSafe}; - -use super::AutoreleaseSafe; - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -enum Never {} - -/// A type used to mark that a struct owns the object(s) it contains, -/// so it has the sole references to them. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct Owned { - inner: Never, -} - -/// A type used to mark that the object(s) a struct contains are shared, -/// so there may be other references to them. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct Shared { - inner: Never, -} - -mod private { - pub trait Sealed {} - - impl Sealed for super::Owned {} - impl Sealed for super::Shared {} -} - -/// A type that marks what type of ownership a struct has over the object(s) -/// it contains; specifically, either [`Owned`] or [`Shared`]. -/// -/// This trait is sealed and not meant to be implemented outside of the this -/// crate. -pub trait Ownership: - private::Sealed - // Special - + 'static - + Sized - // Auto-traits - + Send - + Sync - + Unpin - + UnwindSafe - + RefUnwindSafe - // Derived - + Clone - + Copy - + PartialEq - + Eq - + PartialOrd - + Ord - + Hash - + Debug - // Custom - + AutoreleaseSafe -{ -} - -impl Ownership for Owned {} -impl Ownership for Shared {} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_generic_ownership_traits() { - fn assert_partialeq() {} - - assert_partialeq::(); - assert_partialeq::(); - - fn test_ownership_implies_partialeq() { - assert_partialeq::(); - } - - test_ownership_implies_partialeq::(); - test_ownership_implies_partialeq::(); - } -} diff --git a/crates/objc2/src/rc/test_object.rs b/crates/objc2/src/rc/test_object.rs index e88115ca2..ac726a991 100644 --- a/crates/objc2/src/rc/test_object.rs +++ b/crates/objc2/src/rc/test_object.rs @@ -1,7 +1,8 @@ use core::cell::RefCell; use core::ptr; -use super::{Allocated, Id, Owned}; +use super::{Allocated, Id}; +use crate::mutability::Immutable; use crate::runtime::{NSObject, NSZone}; use crate::{declare_class, msg_send, msg_send_id, ClassType}; @@ -57,22 +58,23 @@ declare_class!( unsafe impl ClassType for __RcTestObject { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "__RcTestObject"; } unsafe impl __RcTestObject { #[method_id(newReturningNull)] - fn new_returning_null() -> Option> { + fn new_returning_null() -> Option> { None } #[method_id(newMethodOnInstance)] - fn new_method_on_instance(&self) -> Id { + fn new_method_on_instance(&self) -> Id { Self::new() } #[method_id(newMethodOnInstanceNull)] - fn new_method_on_instance_null(&self) -> Option> { + fn new_method_on_instance_null(&self) -> Option> { None } @@ -97,13 +99,13 @@ declare_class!( } #[method(init)] - fn init(this: &mut Self) -> *mut Self { + unsafe fn init(this: *mut Self) -> *mut Self { TEST_DATA.with(|data| data.borrow_mut().init += 1); unsafe { msg_send![super(this), init] } } #[method_id(initReturningNull)] - fn init_returning_null(_this: Allocated) -> Option> { + fn init_returning_null(_this: Allocated) -> Option> { None } @@ -137,29 +139,29 @@ declare_class!( } #[method_id(copyWithZone:)] - fn copy_with_zone(&self, _zone: *const NSZone) -> Id { + fn copy_with_zone(&self, _zone: *const NSZone) -> Id { TEST_DATA.with(|data| data.borrow_mut().copy += 1); Self::new() } #[method_id(mutableCopyWithZone:)] - fn mutable_copy_with_zone(&self, _zone: *const NSZone) -> Id { + fn mutable_copy_with_zone(&self, _zone: *const NSZone) -> Id { TEST_DATA.with(|data| data.borrow_mut().mutable_copy += 1); Self::new() } #[method_id(copyReturningNull)] - fn copy_returning_null(_this: &Self) -> Option> { + fn copy_returning_null(_this: &Self) -> Option> { None } #[method_id(methodReturningNull)] - fn method_returning_null(self: &Self) -> Option> { + fn method_returning_null(self: &Self) -> Option> { None } #[method_id(aMethod:)] - fn a_method(&self, param: bool) -> Option> { + fn a_method(&self, param: bool) -> Option> { param.then(Self::new) } @@ -195,7 +197,7 @@ declare_class!( fn class_error_id( should_error: bool, err: Option<&mut *mut __RcTestObject>, - ) -> Option> { + ) -> Option> { if should_error { if let Some(err) = err { *err = Id::autorelease_inner(__RcTestObject::new()); @@ -211,7 +213,7 @@ declare_class!( self: &Self, should_error: bool, err: Option<&mut *mut __RcTestObject>, - ) -> Option> { + ) -> Option> { if should_error { if let Some(err) = err { *err = Id::autorelease_inner(__RcTestObject::new()); @@ -226,7 +228,7 @@ declare_class!( fn new_error( should_error: bool, err: Option<&mut *mut __RcTestObject>, - ) -> Option> { + ) -> Option> { if should_error { if let Some(err) = err { *err = Id::autorelease_inner(__RcTestObject::new()); @@ -254,7 +256,7 @@ declare_class!( this: Allocated, should_error: bool, err: Option<&mut *mut __RcTestObject>, - ) -> Option> { + ) -> Option> { if should_error { if let Some(err) = err { *err = Id::autorelease_inner(__RcTestObject::new()); @@ -285,7 +287,7 @@ unsafe impl Sync for __RcTestObject {} impl __RcTestObject { #[doc(hidden)] - pub fn new() -> Id { + pub fn new() -> Id { // Use msg_send! - msg_send_id! is tested elsewhere! unsafe { Id::new(msg_send![Self::class(), new]) }.unwrap() } @@ -298,13 +300,14 @@ declare_class!( unsafe impl ClassType for RcTestObjectSubclass { #[inherits(NSObject)] type Super = __RcTestObject; + type Mutability = Immutable; const NAME: &'static str = "RcTestObjectSubclass"; } ); #[cfg_attr(not(test), allow(unused))] impl RcTestObjectSubclass { - fn new() -> Id { + fn new() -> Id { unsafe { msg_send_id![Self::class(), new] } } } @@ -392,7 +395,7 @@ mod tests { ($expected:expr, $if_autorelease_not_skipped:expr, $sel:ident, $($obj:tt)*) => { // Succeeds let res = autoreleasepool(|_pool| { - let res: Result, Id<__RcTestObject>> = unsafe { + let res: Result, Id<__RcTestObject>> = unsafe { msg_send_id![$($obj)*, $sel: false, error: _] }; let res = res.expect("not ok"); @@ -413,7 +416,7 @@ mod tests { // Errors let res = autoreleasepool(|_pool| { - let res: Result, Id<__RcTestObject>> = unsafe { + let res: Result, Id<__RcTestObject>> = unsafe { msg_send_id![$($obj)*, $sel: true, error: _] }; $expected.alloc += 1; @@ -503,13 +506,12 @@ mod tests { expected.init += 1; expected.assert_current(); - let res: Option> = unsafe { msg_send_id![&obj, aMethod: false] }; + let res: Option> = unsafe { msg_send_id![&obj, aMethod: false] }; assert!(res.is_none()); expected.assert_current(); let _res = autoreleasepool(|_pool| { - let res: Option> = - unsafe { msg_send_id![&obj, aMethod: true] }; + let res: Option> = unsafe { msg_send_id![&obj, aMethod: true] }; assert!(res.is_some()); expected.alloc += 1; expected.init += 1; diff --git a/crates/objc2/src/rc/weak_id.rs b/crates/objc2/src/rc/weak_id.rs index 13efd1e1f..a0fa35b58 100644 --- a/crates/objc2/src/rc/weak_id.rs +++ b/crates/objc2/src/rc/weak_id.rs @@ -5,15 +5,28 @@ use core::marker::PhantomData; use core::ptr; use std::panic::{RefUnwindSafe, UnwindSafe}; -use super::{Id, Shared}; -use crate::ffi; -use crate::Message; +use super::Id; +use crate::mutability::{IsIdCloneable, IsRetainable}; +use crate::{ffi, Message}; -/// A pointer type for a weak reference to an Objective-C reference counted -/// object. +/// A weak pointer to an Objective-C reference counted object. /// -/// Allows breaking reference cycles and safely checking whether the object -/// has been deallocated. +/// The object is allowed to be deallocated while the weak pointer is alive, +/// though the backing allocation for the object can only be released once all +/// weak pointers are gone. +/// +/// Useful for breaking reference cycles and safely checking whether an +/// object has been deallocated. +/// +/// +/// # Comparison to `std` types +/// +/// This is the Objective-C equivalent of [`sync::Weak`] from the standard +/// library, and hence is only usable on types where `Id` acts like +/// [`sync::Arc`], a.k.a. on non-mutable types. +/// +/// [`sync::Weak`]: std::sync::Weak +/// [`sync::Arc`]: std::sync::Arc #[repr(transparent)] pub struct WeakId { /// We give the runtime the address to this box, so that it can modify it @@ -30,22 +43,38 @@ pub struct WeakId { /// TODO: Investigate if we can avoid some allocations using `Pin`. inner: Box>, /// WeakId inherits variance, dropck and various marker traits from - /// `Id` because it can be loaded as a shared Id. - item: PhantomData>, + /// `Id`. + item: PhantomData>, } impl WeakId { - /// Construct a new [`WeakId`] referencing the given shared [`Id`]. + /// Construct a new weak pointer that references the given object. #[doc(alias = "objc_initWeak")] #[inline] - pub fn new(obj: &Id) -> Self { - // Note that taking `&Id` would not be safe since that would - // allow loading an `Id` later on. + pub fn new(obj: &T) -> Self + where + T: IsRetainable, + { + // SAFETY: `obj` is retainable + unsafe { Self::new_inner(obj) } + } - // SAFETY: `obj` is valid + /// Construct a new weak pointer that references the given [`Id`]. + /// + /// You should prefer [`WeakId::new`] whenever the object is retainable. + #[doc(alias = "objc_initWeak")] + #[inline] + pub fn from_id(obj: &Id) -> Self + where + T: IsIdCloneable, + { + // SAFETY: `obj` is cloneable, and is known to have come from `Id`. unsafe { Self::new_inner(Id::as_ptr(obj)) } } + /// Raw constructor. + /// + /// /// # Safety /// /// The object must be valid or null. @@ -59,25 +88,26 @@ impl WeakId { } } - /// Load a shared (and retained) [`Id`] if the object still exists. + /// Load the object into an [`Id`] if it still exists. /// - /// Returns [`None`] if the object has been deallocated or was created - /// with [`Default::default`]. + /// Returns [`None`] if the object has been deallocated, or the `WeakId` + /// was created with [`Default::default`]. #[doc(alias = "retain")] #[doc(alias = "objc_loadWeak")] #[doc(alias = "objc_loadWeakRetained")] #[inline] - pub fn load(&self) -> Option> { + pub fn load(&self) -> Option> { let ptr = self.inner.get(); let obj = unsafe { ffi::objc_loadWeakRetained(ptr) }.cast(); + // SAFETY: The object has +1 retain count unsafe { Id::new(obj) } } - // TODO: Add `autorelease(&self) -> Option<&T>` using `objc_loadWeak`? + // TODO: Add `autorelease(&self, pool) -> Option<&T>` using `objc_loadWeak`? } impl Drop for WeakId { - /// Drops the `WeakId` pointer. + /// Destroys the weak pointer. #[doc(alias = "objc_destroyWeak")] #[inline] fn drop(&mut self) { @@ -86,8 +116,8 @@ impl Drop for WeakId { } // TODO: Add ?Sized -impl Clone for WeakId { - /// Makes a clone of the `WeakId` that points to the same object. +impl Clone for WeakId { + /// Make a clone of the weak pointer that points to the same object. #[doc(alias = "objc_copyWeak")] fn clone(&self) -> Self { let ptr = Box::new(UnsafeCell::new(ptr::null_mut())); @@ -100,8 +130,8 @@ impl Clone for WeakId { } // TODO: Add ?Sized -impl Default for WeakId { - /// Constructs a new `WeakId` that doesn't reference any object. +impl Default for WeakId { + /// Constructs a new weak pointer that doesn't reference any object. /// /// Calling [`Self::load`] on the return value always gives [`None`]. #[inline] @@ -111,39 +141,48 @@ impl Default for WeakId { } } -/// This implementation follows the same reasoning as `Id`. -unsafe impl Sync for WeakId {} - -/// This implementation follows the same reasoning as `Id`. -unsafe impl Send for WeakId {} - -// Unsure about the Debug bound on T, see std::sync::Weak -impl fmt::Debug for WeakId { +impl fmt::Debug for WeakId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Note: We intentionally don't try to debug-print the value, since + // that could lead to cycles. See: + // https://github.com/rust-lang/rust/pull/90291 write!(f, "(WeakId)") } } -// Underneath this is just a `Box` -impl Unpin for WeakId {} +// Same as `std::sync::Weak`. +unsafe impl Sync for WeakId {} + +// Same as `std::sync::Weak`. +unsafe impl Send for WeakId {} + +// Same as `std::sync::Weak`. +impl Unpin for WeakId {} -// Same as `Id`. -impl RefUnwindSafe for WeakId {} +// Same as `std::sync::Weak`. +impl RefUnwindSafe for WeakId {} -// Same as `Id`. -impl UnwindSafe for WeakId {} +// Same as `std::sync::Weak`. +impl UnwindSafe for WeakId {} -impl From> for WeakId { +impl From<&T> for WeakId { #[inline] - fn from(obj: Id) -> Self { - WeakId::new(&obj) + fn from(obj: &T) -> Self { + WeakId::new(obj) } } -impl TryFrom> for Id { - type Error = (); - fn try_from(weak: WeakId) -> Result { - weak.load().ok_or(()) +impl From<&Id> for WeakId { + #[inline] + fn from(obj: &Id) -> Self { + WeakId::from_id(obj) + } +} + +impl From> for WeakId { + #[inline] + fn from(obj: Id) -> Self { + WeakId::from_id(&obj) } } @@ -157,10 +196,10 @@ mod tests { #[test] fn test_weak() { - let obj: Id<_, Shared> = __RcTestObject::new().into(); + let obj = __RcTestObject::new(); let mut expected = __ThreadTestData::current(); - let weak = WeakId::new(&obj); + let weak = WeakId::from(&obj); expected.assert_current(); let strong = weak.load().unwrap(); @@ -186,10 +225,10 @@ mod tests { #[test] fn test_weak_clone() { - let obj: Id<_, Shared> = __RcTestObject::new().into(); + let obj: Id<_> = __RcTestObject::new(); let mut expected = __ThreadTestData::current(); - let weak = WeakId::new(&obj); + let weak = WeakId::from(&obj); expected.assert_current(); let weak2 = weak.clone(); @@ -216,7 +255,7 @@ mod tests { #[test] fn test_weak_default() { - let weak: WeakId = WeakId::default(); + let weak: WeakId<__RcTestObject> = WeakId::default(); assert!(weak.load().is_none()); drop(weak); } diff --git a/crates/objc2/src/rc/writeback.rs b/crates/objc2/src/rc/writeback.rs index 4af176626..887f45090 100644 --- a/crates/objc2/src/rc/writeback.rs +++ b/crates/objc2/src/rc/writeback.rs @@ -11,12 +11,12 @@ use core::mem::ManuallyDrop; use core::ptr::NonNull; use crate::encode::__unstable::EncodeConvertArgument; -use crate::rc::{Id, Ownership}; +use crate::rc::Id; use crate::Message; // Note the `'static` bound here - this may not be necessary, but I'm unsure // of the exact requirements, so we better just keep it for now. -impl EncodeConvertArgument for &mut Id { +impl EncodeConvertArgument for &mut Id { // We use `*mut T` as the inner value instead of `NonNull`, since we // want to do debug checking that the value hasn't unexpectedly been // overwritten to contain NULL (which is clear UB, but the user might have @@ -35,12 +35,12 @@ impl EncodeConvertArgument for &mut Id #[inline] fn __from_declared_param(_inner: Self::__Inner) -> Self { - todo!("`&mut Id<_, _>` is not supported in `declare_class!` yet") + todo!("`&mut Id<_>` is not supported in `declare_class!` yet") } #[inline] fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage) { - let ptr: NonNull> = NonNull::from(self); + let ptr: NonNull> = NonNull::from(self); // `Id` is `#[repr(transparent)]` over `NonNull`. let ptr: NonNull> = ptr.cast(); @@ -83,20 +83,20 @@ impl EncodeConvertArgument for &mut Id // } // ``` // - // Note that using `Id` is perfectly sound, since we may - // only intermittently have a retain count of 2 to the value, but - // after the function returns we're guaranteed to be back to 1. + // Note that using a mutable `Id` is perfectly sound, since while + // we may intermittently have a retain count of 2 to the value, after + // the function returns we're guaranteed to be back to 1. // SAFETY: Caller ensures that the pointer is either left as-is, or is // safe to retain at this point. - let new: Option> = unsafe { Id::retain(*ptr.as_ptr()) }; + let new: Option> = unsafe { Id::retain(*ptr.as_ptr()) }; // We ignore the result of `retain`, since it always returns the same // value as was given (and it would just be unnecessary work to write // that value back into `ptr` again). let _new = ManuallyDrop::new(new); #[cfg(debug_assertions)] if _new.is_none() { - panic!("found that NULL was written to `&mut Id<_, _>`, which is UB! You should handle this with `&mut Option>` instead"); + panic!("found that NULL was written to `&mut Id<_>`, which is UB! You should handle this with `&mut Option>` instead"); } // SAFETY: The old pointer was valid when it was constructed. @@ -105,24 +105,24 @@ impl EncodeConvertArgument for &mut Id // retain count on the old pointer; so either we have +1 from that, or // the message send didn't modify the pointer and we instead have +1 // retain count from the `retain` above. - let _: Id = unsafe { Id::new_nonnull(old) }; + let _: Id = unsafe { Id::new_nonnull(old) }; } } -impl EncodeConvertArgument for &mut Option> { +impl EncodeConvertArgument for &mut Option> { type __Inner = NonNull<*mut T>; type __StoredBeforeMessage = (Self::__Inner, *mut T); #[inline] fn __from_declared_param(_inner: Self::__Inner) -> Self { - todo!("`&mut Option>` is not supported in `declare_class!` yet") + todo!("`&mut Option>` is not supported in `declare_class!` yet") } #[inline] fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage) { - let ptr: NonNull>> = NonNull::from(self); - // `Option>` has the same memory layout as `*mut T`. + let ptr: NonNull>> = NonNull::from(self); + // `Option>` has the same memory layout as `*mut T`. let ptr: NonNull<*mut T> = ptr.cast(); // SAFETY: Same as for `&mut Id` let old: *mut T = unsafe { *ptr.as_ptr() }; @@ -133,7 +133,7 @@ impl EncodeConvertArgument for &mut Option> = unsafe { Id::retain(*ptr.as_ptr()) }; + let new: Option> = unsafe { Id::retain(*ptr.as_ptr()) }; let _ = ManuallyDrop::new(new); // SAFETY: Same as for `&mut Id` @@ -147,7 +147,7 @@ impl EncodeConvertArgument for &mut Option> = unsafe { Id::new(old) }; + let _: Option> = unsafe { Id::new(old) }; } } @@ -156,14 +156,14 @@ impl EncodeConvertArgument for &mut Option EncodeConvertArgument for Option<&mut Id> { +impl EncodeConvertArgument for Option<&mut Id> { type __Inner = Option>; type __StoredBeforeMessage = Option<(NonNull<*mut T>, NonNull)>; #[inline] fn __from_declared_param(_inner: Self::__Inner) -> Self { - todo!("`Option<&mut Id<_, _>>` is not supported in `declare_class!` yet") + todo!("`Option<&mut Id<_>>` is not supported in `declare_class!` yet") } #[inline] @@ -180,19 +180,19 @@ impl EncodeConvertArgument for Option<&mut I unsafe fn __process_after_message_send(stored: Self::__StoredBeforeMessage) { if let Some(stored) = stored { // SAFETY: Checked by caller - unsafe { <&mut Id>::__process_after_message_send(stored) }; + unsafe { <&mut Id>::__process_after_message_send(stored) }; } } } -impl EncodeConvertArgument for Option<&mut Option>> { +impl EncodeConvertArgument for Option<&mut Option>> { type __Inner = Option>; type __StoredBeforeMessage = Option<(NonNull<*mut T>, *mut T)>; #[inline] fn __from_declared_param(_inner: Self::__Inner) -> Self { - todo!("`Option<&mut Option>>` is not supported in `declare_class!` yet") + todo!("`Option<&mut Option>>` is not supported in `declare_class!` yet") } #[inline] @@ -209,7 +209,7 @@ impl EncodeConvertArgument for Option<&mut O unsafe fn __process_after_message_send(stored: Self::__StoredBeforeMessage) { if let Some(stored) = stored { // SAFETY: Checked by caller - unsafe { <&mut Option>>::__process_after_message_send(stored) }; + unsafe { <&mut Option>>::__process_after_message_send(stored) }; } } } @@ -217,14 +217,14 @@ impl EncodeConvertArgument for Option<&mut O #[cfg(test)] mod tests { use super::*; - use crate::rc::{Owned, __RcTestObject, __ThreadTestData, autoreleasepool}; + use crate::rc::{__RcTestObject, __ThreadTestData, autoreleasepool}; use crate::{msg_send, msg_send_id, ClassType}; #[test] fn test_bool_error() { let mut expected = __ThreadTestData::current(); - fn bool_error(should_error: bool, error: Option<&mut Option>>) { + fn bool_error(should_error: bool, error: Option<&mut Option>>) { let cls = __RcTestObject::class(); let did_succeed: bool = unsafe { msg_send![cls, boolAndShouldError: should_error, error: error] }; @@ -238,7 +238,7 @@ mod tests { fn helper( expected: &mut __ThreadTestData, should_error: bool, - mut error: Option>, + mut error: Option>, ) { std::dbg!(should_error, &error); autoreleasepool(|_| { @@ -291,10 +291,10 @@ mod tests { ), ignore = "invokes UB which is only caught with debug_assertions" )] - #[should_panic = "found that NULL was written to `&mut Id<_, _>`, which is UB! You should handle this with `&mut Option>` instead"] + #[should_panic = "found that NULL was written to `&mut Id<_>`, which is UB! You should handle this with `&mut Option>` instead"] fn test_debug_check_ub() { let cls = __RcTestObject::class(); - let mut param: Id<_, _> = __RcTestObject::new(); + let mut param: Id<_> = __RcTestObject::new(); let _: () = unsafe { msg_send![cls, outParamNull: &mut param] }; } @@ -306,13 +306,13 @@ mod tests { let mut expected = __ThreadTestData::current(); let cls = __RcTestObject::class(); - let mut err: Id<__RcTestObject, Owned> = __RcTestObject::new(); + let mut err: Id<__RcTestObject> = __RcTestObject::new(); expected.alloc += 1; expected.init += 1; expected.assert_current(); autoreleasepool(|_| { - let obj: Option> = + let obj: Option> = unsafe { msg_send_id![cls, idAndShouldError: false, error: &mut err] }; expected.alloc += 1; expected.init += 1; diff --git a/crates/objc2/src/runtime/__nsstring.rs b/crates/objc2/src/runtime/__nsstring.rs index 58b230d52..6fe142be8 100644 --- a/crates/objc2/src/runtime/__nsstring.rs +++ b/crates/objc2/src/runtime/__nsstring.rs @@ -7,6 +7,8 @@ use crate::msg_send; use crate::rc::AutoreleasePool; use crate::runtime::NSObject; +// Note: While this is not public, it is still a breaking change to modify, +// since `icrate` relies on it. #[cfg(feature = "apple")] pub const UTF8_ENCODING: usize = 4; #[cfg(feature = "gnustep-1-7")] @@ -17,6 +19,9 @@ pub const UTF8_ENCODING: i32 = 4; /// # Safety /// /// The object must be an instance of `NSString`. +// +// Note: While this is not public, it is still a breaking change to modify, +// since `icrate` relies on it. pub unsafe fn nsstring_len(obj: &NSObject) -> NSUInteger { unsafe { msg_send![obj, lengthOfBytesUsingEncoding: UTF8_ENCODING] } } @@ -26,6 +31,9 @@ pub unsafe fn nsstring_len(obj: &NSObject) -> NSUInteger { /// # Safety /// /// The object must be an instance of `NSString`. +// +// Note: While this is not public, it is still a breaking change to modify, +// since `icrate` relies on it. pub unsafe fn nsstring_to_str<'r, 's: 'r, 'p: 'r>( obj: &'s NSObject, pool: AutoreleasePool<'p>, diff --git a/crates/objc2/src/runtime/mod.rs b/crates/objc2/src/runtime/mod.rs index 51967ad56..4a33fbb24 100644 --- a/crates/objc2/src/runtime/mod.rs +++ b/crates/objc2/src/runtime/mod.rs @@ -1,12 +1,11 @@ -//! A Rust interface for the functionality of the Objective-C runtime. +//! # High-level runtime bindings. //! -//! For more information on foreign functions, see Apple's documentation: -//! +//! This module contains safe(r) bindings to common parts of the Objective-C +//! runtime. See the [`ffi`][crate::ffi] module for details on the raw +//! bindings. #[cfg(feature = "malloc")] use alloc::vec::Vec; -#[cfg(doc)] -use core::cell::UnsafeCell; use core::fmt; use core::hash; use core::panic::{RefUnwindSafe, UnwindSafe}; @@ -19,10 +18,13 @@ use std::os::raw::c_char; #[cfg(feature = "malloc")] use std::os::raw::c_uint; +// Note: While this is not public, it is still a breaking change to remove, +// since `icrate` relies on it. #[doc(hidden)] pub mod __nsstring; mod bool; mod method_encoding_iter; +mod nscopying; mod nsobject; mod nsproxy; mod nszone; @@ -31,15 +33,20 @@ mod protocol_object; pub(crate) use self::method_encoding_iter::{EncodingParseError, MethodEncodingIter}; use crate::encode::__unstable::{EncodeArguments, EncodeConvertReturn, EncodeReturn}; use crate::encode::{Encode, Encoding, OptionEncode, RefEncode}; -use crate::ffi; use crate::verify::{verify_method_signature, Inner}; +use crate::{ffi, Message}; -pub use self::bool::Bool; -pub use self::nsobject::{NSObject, NSObjectProtocol}; -// Note: While this is not public, it is still a breaking change to remove, -// since `icrate` relies on it. +// Note: While these are not public, they are still a breaking change to +// remove, since `icrate` relies on them. +#[doc(hidden)] +pub use self::nscopying::{ + Copyhelper as __Copyhelper, NSCopying as __NSCopying, NSMutableCopying as __NSMutableCopying, +}; #[doc(hidden)] pub use self::nsproxy::NSProxy as __NSProxy; + +pub use self::bool::Bool; +pub use self::nsobject::{NSObject, NSObjectProtocol}; pub use self::nszone::NSZone; pub use self::protocol_object::{ImplementedBy, ProtocolObject}; pub use crate::verify::VerificationError; @@ -792,11 +799,11 @@ pub(crate) fn ivar_offset(cls: &Class, name: &str, expected: &Encoding) -> isize /// An Objective-C object. /// -/// This is slightly different from `NSObject` in that it may represent an +/// This is slightly different from [`NSObject`] in that it may represent an /// instance of an _arbitary_ Objective-C class (e.g. it does not have to be /// a subclass of `NSObject`). /// -/// `Id` is equivalent to Objective-C's `id`. +/// `Id` is equivalent to Objective-C's `id`. /// /// This contains [`UnsafeCell`], and is similar to that in that one can /// safely access and perform interior mutability on this (both via. @@ -809,6 +816,7 @@ pub(crate) fn ivar_offset(cls: &Class, name: &str, expected: &Encoding) -> isize /// not `Send`, it has to be deallocated on the same thread that it was /// created. `NSLock` is not `Send` either. /// +/// [`UnsafeCell`]: core::cell::UnsafeCell /// [`msg_send!`]: crate::msg_send #[doc(alias = "id")] #[repr(C)] @@ -818,6 +826,11 @@ unsafe impl RefEncode for Object { const ENCODING_REF: Encoding = Encoding::Object; } +// SAFETY: This is technically slightly wrong, not all objects implement the +// standard memory management methods. But not having this impl would be too +// restrictive, so we'll live with it. +unsafe impl Message for Object {} + impl Object { pub(crate) fn as_ptr(&self) -> *const ffi::objc_object { let ptr: *const Self = self; @@ -866,6 +879,7 @@ impl Object { /// Library implementors are strongly encouraged to expose a safe /// interface to the ivar. /// + /// [`UnsafeCell::get`]: core::cell::UnsafeCell::get /// [`ClassBuilder::add_ivar`]: crate::declare::ClassBuilder::add_ivar /// /// diff --git a/crates/objc2/src/runtime/nscopying.rs b/crates/objc2/src/runtime/nscopying.rs new file mode 100644 index 000000000..f63177b49 --- /dev/null +++ b/crates/objc2/src/runtime/nscopying.rs @@ -0,0 +1,157 @@ +use crate::mutability::{ + Immutable, ImmutableWithMutableSubclass, InteriorMutable, MainThreadOnly, Mutable, + MutableWithImmutableSuperclass, Root, +}; +use crate::rc::Id; +use crate::{msg_send_id, ClassType, ProtocolType}; + +/// Helper trait for [`NSCopying`] and [`NSMutableCopying`]. +/// +/// This is needed since those two can't have associated types themselves (at +/// least not if they want to be usable as `ProtocolObject`), +/// and because the return type of those differ if the class has a mutable or +/// an immutable counterpart (as is the case for `NSString` and +/// `NSMutableString`). +pub trait Copyhelper { + /// The output type of [`NSCopying`] for the given `T`. + type CopyOutput: ?Sized + ClassType; + /// The output type of [`NSMutableCopying`] for the given `T`. + type MutableCopyOutput: ?Sized + ClassType; + + // TODO: Use this to autogenerate `ToOwned` impls + #[doc(hidden)] + fn __do_copy(t: &T) -> Id; +} +impl> Copyhelper for Root { + type CopyOutput = T; + type MutableCopyOutput = T; + + #[inline] + fn __do_copy(t: &T) -> Id { + t.copy() + } +} +impl> Copyhelper for Immutable { + type CopyOutput = T; + type MutableCopyOutput = T; + + #[inline] + fn __do_copy(t: &T) -> Id { + t.retain() + } +} +impl> Copyhelper for Mutable { + type CopyOutput = T; + type MutableCopyOutput = T; + + #[inline] + fn __do_copy(t: &T) -> Id { + t.copy() + } +} +impl Copyhelper for ImmutableWithMutableSubclass +where + T: NSCopying + ClassType>, + S: ClassType>, +{ + type CopyOutput = T; + type MutableCopyOutput = S; + + #[inline] + fn __do_copy(t: &T) -> Id { + t.copy() + } +} +impl Copyhelper for MutableWithImmutableSuperclass +where + T: NSMutableCopying + ClassType>, + S: ClassType>, +{ + type CopyOutput = S; + type MutableCopyOutput = T; + + #[inline] + fn __do_copy(t: &T) -> Id { + t.mutableCopy() + } +} +impl> Copyhelper for InteriorMutable { + type CopyOutput = T; + type MutableCopyOutput = T; + + #[inline] + fn __do_copy(t: &T) -> Id { + t.retain() + } +} +impl> Copyhelper for MainThreadOnly { + type CopyOutput = T; + type MutableCopyOutput = T; + + #[inline] + fn __do_copy(t: &T) -> Id { + t.retain() + } +} + +/// A protocol to provide functional copies of objects. +/// +/// This is similar to Rust's [`Clone`] trait, along with sharing a few +/// similarities to the [`std::borrow::ToOwned`] trait with regards to the +/// output type. +/// +/// See [Apple's documentation][apple-doc] for details. +/// +/// [apple-doc]: https://developer.apple.com/documentation/foundation/nscopying +#[allow(clippy::missing_safety_doc)] // Same as all other traits +pub unsafe trait NSCopying { + /// Returns a new instance that's a copy of the receiver. + /// + /// The output type is usually `Self`, but e.g. `NSMutableString` returns + /// `NSString`. + fn copy(&self) -> Id<>::CopyOutput> + where + Self: Sized + ClassType, + Self::Mutability: Copyhelper, + { + unsafe { msg_send_id![self, copy] } + } +} + +crate::__inner_extern_protocol!( + () + (NSCopying) + (dyn NSCopying) + ("NSCopying") +); + +/// A protocol to provide mutable copies of objects. +/// +/// Only classes that have an “immutable vs. mutable” distinction should adopt +/// this protocol. +/// +/// See [Apple's documentation][apple-doc] for details. +/// +/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsmutablecopying +#[allow(clippy::missing_safety_doc)] // Same as all other traits +pub unsafe trait NSMutableCopying { + /// Returns a new instance that's a mutable copy of the receiver. + /// + /// The output type is the mutable counterpart of the object. E.g. both + /// `NSString` and `NSMutableString` return `NSMutableString`. + #[allow(non_snake_case)] + fn mutableCopy(&self) -> Id<>::MutableCopyOutput> + where + Self: Sized + ClassType, + Self::Mutability: Copyhelper, + { + unsafe { msg_send_id![self, mutableCopy] } + } +} + +crate::__inner_extern_protocol!( + () + (NSMutableCopying) + (dyn NSMutableCopying) + ("NSMutableCopying") +); diff --git a/crates/objc2/src/runtime/nsobject.rs b/crates/objc2/src/runtime/nsobject.rs index ba21340d8..64117cee6 100644 --- a/crates/objc2/src/runtime/nsobject.rs +++ b/crates/objc2/src/runtime/nsobject.rs @@ -1,9 +1,11 @@ use core::fmt; use core::hash; -use crate::rc::{DefaultId, Id, Owned}; +use crate::mutability::Root; +use crate::rc::{DefaultId, Id}; use crate::runtime::{Class, ImplementedBy, Object, Protocol, ProtocolObject}; -use crate::{extern_methods, msg_send, msg_send_id, ClassType, Message, ProtocolType}; +use crate::{extern_methods, msg_send, msg_send_id, Message}; +use crate::{ClassType, ProtocolType}; crate::__emit_struct! { ( @@ -30,11 +32,20 @@ crate::__emit_struct! { crate::__extern_class_impl_traits! { unsafe impl () for NSObject { INHERITS = [Object]; + + fn as_super(&self) { + &self.__inner + } + + fn as_super_mut(&mut self) { + &mut self.__inner + } } } unsafe impl ClassType for NSObject { type Super = Object; + type Mutability = Root; const NAME: &'static str = "NSObject"; #[inline] @@ -146,12 +157,14 @@ pub unsafe trait NSObjectProtocol { // not be modified. } -unsafe impl NSObjectProtocol for NSObject {} +crate::__inner_extern_protocol!( + () + (NSObjectProtocol) + (dyn NSObjectProtocol) + ("NSCopying") +); -unsafe impl NSObjectProtocol for ProtocolObject where - T: ?Sized + ProtocolType + NSObjectProtocol -{ -} +unsafe impl NSObjectProtocol for NSObject {} unsafe impl ProtocolType for NSObject { const NAME: &'static str = "NSObject"; @@ -174,7 +187,7 @@ extern_methods!( unsafe impl NSObject { /// Create a new empty `NSObject`. #[method_id(new)] - pub fn new() -> Id; + pub fn new() -> Id; } ); @@ -220,10 +233,8 @@ impl fmt::Debug for NSObject { } impl DefaultId for NSObject { - type Ownership = Owned; - #[inline] - fn default_id() -> Id { + fn default_id() -> Id { Self::new() } } @@ -233,11 +244,39 @@ mod tests { use super::*; use alloc::format; + use crate::extern_class; + use crate::mutability::Mutable; use crate::rc::__RcTestObject; + extern_class!( + #[derive(Debug, PartialEq, Eq, Hash)] + struct NSObjectMutable; + + unsafe impl ClassType for NSObjectMutable { + type Super = NSObject; + type Mutability = Mutable; + const NAME: &'static str = "NSObject"; + } + ); + + impl NSObjectMutable { + fn new() -> Id { + unsafe { Id::cast(NSObject::new()) } + } + } + #[test] fn test_deref() { - let mut obj: Id = NSObject::new(); + let obj: Id = NSObject::new(); + let _: &NSObject = &obj; + let _: &Object = &obj; + } + + #[test] + fn test_deref_mut() { + let mut obj: Id = NSObjectMutable::new(); + let _: &NSObjectMutable = &obj; + let _: &mut NSObjectMutable = &mut obj; let _: &NSObject = &obj; let _: &mut NSObject = &mut obj; let _: &Object = &obj; @@ -251,13 +290,20 @@ mod tests { fn impls_as_ref + Borrow + ?Sized, U: ?Sized>(_: &T) {} fn impls_as_mut + BorrowMut + ?Sized, U: ?Sized>(_: &mut T) {} - let mut obj = NSObject::new(); - impls_as_ref::, NSObject>(&obj); - impls_as_mut::, NSObject>(&mut obj); + let mut obj = NSObjectMutable::new(); + impls_as_ref::, NSObjectMutable>(&obj); + impls_as_mut::, NSObjectMutable>(&mut obj); + impls_as_ref::(&obj); + impls_as_mut::(&mut obj); impls_as_ref::(&obj); impls_as_mut::(&mut obj); impls_as_ref::(&obj); impls_as_mut::(&mut obj); + + let obj = NSObject::new(); + impls_as_ref::, NSObject>(&obj); + impls_as_ref::(&obj); + impls_as_ref::(&obj); } #[test] diff --git a/crates/objc2/src/runtime/nsproxy.rs b/crates/objc2/src/runtime/nsproxy.rs index 07fe033d5..658d0d6a1 100644 --- a/crates/objc2/src/runtime/nsproxy.rs +++ b/crates/objc2/src/runtime/nsproxy.rs @@ -1,6 +1,7 @@ use core::fmt; use core::hash; +use crate::mutability::Root; use crate::runtime::{Class, NSObject, NSObjectProtocol, Object, ProtocolObject}; use crate::ClassType; @@ -23,11 +24,20 @@ crate::__emit_struct! { crate::__extern_class_impl_traits! { unsafe impl () for NSProxy { INHERITS = [Object]; + + fn as_super(&self) { + &self.__inner + } + + fn as_super_mut(&mut self) { + &mut self.__inner + } } } unsafe impl ClassType for NSProxy { type Super = Object; + type Mutability = Root; const NAME: &'static str = "NSProxy"; #[inline] diff --git a/crates/objc2/src/runtime/protocol_object.rs b/crates/objc2/src/runtime/protocol_object.rs index 8e55da9b8..eb9aa50c1 100644 --- a/crates/objc2/src/runtime/protocol_object.rs +++ b/crates/objc2/src/runtime/protocol_object.rs @@ -4,7 +4,7 @@ use core::marker::PhantomData; use core::ptr::NonNull; use crate::encode::{Encoding, RefEncode}; -use crate::rc::{autoreleasepool_leaking, Id, Ownership}; +use crate::rc::{autoreleasepool_leaking, Id}; use crate::runtime::__nsstring::nsstring_to_str; use crate::runtime::{NSObjectProtocol, Object}; use crate::{Message, ProtocolType}; @@ -18,7 +18,7 @@ use crate::{Message, ProtocolType}; /// of the [`extern_protocol!`] macro. /// /// [`extern_protocol!`]: crate::extern_protocol -pub unsafe trait ImplementedBy { +pub unsafe trait ImplementedBy { #[doc(hidden)] const __INNER: (); } @@ -39,20 +39,21 @@ pub unsafe trait ImplementedBy { /// [protocol-type-checking]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html#//apple_ref/doc/uid/TP30001163-CH15-TPXREF151 /// /// -/// # Examples +/// # Example /// /// Convert an object `MyObject` that implements the a protocol `MyProtocol` /// into a [`ProtocolObject`] for working with the protocol in a type-erased /// way. /// -/// ```rust,ignore +/// ``` /// use objc2::runtime::ProtocolObject; /// use objc2::rc::Id; +/// # use objc2::runtime::NSObject as MyObject; +/// # use objc2::runtime::NSObjectProtocol as MyProtocol; /// -/// let obj: Id; -/// # obj = unimplemented!(); -/// let obj: &ProtocolObject = ProtocolObject::from_ref(&*obj); -/// let obj: Id> = ProtocolObject::from_id(obj); +/// let obj: Id = MyObject::new(); +/// let proto: &ProtocolObject = ProtocolObject::from_ref(&*obj); +/// let proto: Id> = ProtocolObject::from_id(obj); /// ``` #[doc(alias = "id")] #[repr(C)] @@ -104,7 +105,7 @@ impl ProtocolObject

{ /// Get a type-erased object from a type implementing a protocol. #[inline] - pub fn from_id(obj: Id) -> Id + pub fn from_id(obj: Id) -> Id where P: ImplementedBy + 'static, T: 'static, @@ -192,8 +193,10 @@ mod tests { use alloc::format; use core::mem::ManuallyDrop; + use static_assertions::{assert_impl_all, assert_not_impl_any}; + use super::*; - use crate::rc::Owned; + use crate::mutability::Mutable; use crate::runtime::{NSObject, NSObjectProtocol}; use crate::{declare_class, extern_methods, extern_protocol, ClassType}; @@ -251,6 +254,7 @@ mod tests { unsafe impl ClassType for DummyClass { type Super = NSObject; + type Mutability = Mutable; const NAME: &'static str = "ProtocolTestsDummyClass"; } ); @@ -258,7 +262,7 @@ mod tests { extern_methods!( unsafe impl DummyClass { #[method_id(new)] - fn new() -> Id; + fn new() -> Id; } ); @@ -269,45 +273,46 @@ mod tests { // unsafe impl FooFooBar for DummyClass {} #[test] - /// The out-commented ones here are tested in `test-ui/ui/protocol.rs` fn impl_traits() { - fn impl_nsobject() {} - fn impl_foo() {} - fn impl_bar() {} - fn impl_foobar() {} - fn impl_foofoobar() {} - - impl_nsobject::(); - impl_nsobject::>(); - // impl_nsobject::>(); - impl_nsobject::>(); - impl_nsobject::>(); - impl_nsobject::>(); - impl_nsobject::(); - - impl_foo::>(); - // impl_foo::>(); - impl_foo::>(); - impl_foo::>(); - impl_foo::(); - - // impl_bar::>(); - impl_bar::>(); - impl_bar::>(); - impl_bar::>(); - impl_bar::(); - - // impl_foobar::>(); - // impl_foobar::>(); - impl_foobar::>(); - impl_foobar::>(); - impl_foobar::(); - - // impl_foofoobar::>(); - // impl_foofoobar::>(); - // impl_foofoobar::>(); - impl_foofoobar::>(); - // impl_foofoobar::(); + assert_impl_all!(NSObject: NSObjectProtocol); + assert_impl_all!(ProtocolObject: NSObjectProtocol); + assert_not_impl_any!(ProtocolObject: NSObjectProtocol); + assert_impl_all!(ProtocolObject: NSObjectProtocol); + assert_impl_all!(ProtocolObject: NSObjectProtocol); + assert_impl_all!(ProtocolObject: NSObjectProtocol); + assert_impl_all!(DummyClass: NSObjectProtocol); + + assert_not_impl_any!(NSObject: Foo); + assert_not_impl_any!(ProtocolObject: Foo); + assert_impl_all!(ProtocolObject: Foo); + assert_not_impl_any!(ProtocolObject: Foo); + assert_impl_all!(ProtocolObject: Foo); + assert_impl_all!(ProtocolObject: Foo); + assert_impl_all!(DummyClass: Foo); + + assert_not_impl_any!(NSObject: Bar); + assert_not_impl_any!(ProtocolObject: Bar); + assert_not_impl_any!(ProtocolObject: Bar); + assert_impl_all!(ProtocolObject: Bar); + assert_impl_all!(ProtocolObject: Bar); + assert_impl_all!(ProtocolObject: Bar); + assert_impl_all!(DummyClass: Bar); + + assert_not_impl_any!(NSObject: FooBar); + assert_not_impl_any!(ProtocolObject: FooBar); + assert_not_impl_any!(ProtocolObject: FooBar); + assert_not_impl_any!(ProtocolObject: FooBar); + assert_impl_all!(ProtocolObject: FooBar); + assert_impl_all!(ProtocolObject: FooBar); + assert_impl_all!(DummyClass: FooBar); + + assert_not_impl_any!(NSObject: FooFooBar); + assert_not_impl_any!(ProtocolObject: FooFooBar); + assert_not_impl_any!(ProtocolObject: FooFooBar); + assert_not_impl_any!(ProtocolObject: FooFooBar); + assert_not_impl_any!(ProtocolObject: FooFooBar); + assert_impl_all!(ProtocolObject: FooFooBar); + assert_not_impl_any!(DummyClass: FooFooBar); } #[test] @@ -330,7 +335,7 @@ mod tests { let _nsobject: &ProtocolObject = ProtocolObject::from_ref(nsobject); let _foobar: &mut ProtocolObject = ProtocolObject::from_mut(&mut *obj); - let _foobar: Id, _> = ProtocolObject::from_id(obj); + let _foobar: Id> = ProtocolObject::from_id(obj); } #[test] @@ -347,7 +352,10 @@ mod tests { assert_eq!( format!("{obj:?}"), - format!("DummyClass {{ __inner: {:?} }}", ManuallyDrop::new(foobar)), + format!( + "DummyClass {{ __superclass: {:?} }}", + ManuallyDrop::new(foobar) + ), ); assert_eq!(obj == obj2, foobar == foobar2); diff --git a/crates/objc2/tests/declare_class_self.rs b/crates/objc2/tests/declare_class_self.rs index 0104d843d..7f4b39d39 100644 --- a/crates/objc2/tests/declare_class_self.rs +++ b/crates/objc2/tests/declare_class_self.rs @@ -4,7 +4,7 @@ //! types of such a function)! use objc2::rc::{Allocated, Id}; use objc2::runtime::NSObject; -use objc2::{declare_class, ClassType}; +use objc2::{declare_class, mutability, ClassType}; trait GetSameType { type SameType: ?Sized; @@ -25,6 +25,8 @@ declare_class!( unsafe impl ClassType for MyTestObject { type Super = NSObject; + type Mutability = mutability::Mutable; + const NAME: &'static str = "MyTestObject"; } diff --git a/crates/objc2/tests/no_prelude.rs b/crates/objc2/tests/no_prelude.rs index f7b1b7fa2..827468929 100644 --- a/crates/objc2/tests/no_prelude.rs +++ b/crates/objc2/tests/no_prelude.rs @@ -93,6 +93,7 @@ new_objc2::declare_class!( unsafe impl ClassType for CustomObject { type Super = new_objc2::runtime::NSObject; + type Mutability = new_objc2::mutability::Immutable; const NAME: &'static str = "CustomObject"; } @@ -101,7 +102,7 @@ new_objc2::declare_class!( fn _a() {} #[method_id(b)] - fn _b() -> new_objc2::rc::Id { + fn _b() -> new_objc2::rc::Id { ::core::unimplemented!() } } @@ -127,6 +128,7 @@ new_objc2::extern_class!( unsafe impl ClassType for NSObject2 { type Super = new_objc2::runtime::NSObject; + type Mutability = new_objc2::mutability::Immutable; const NAME: &'static str = "NSObject"; } ); @@ -161,11 +163,10 @@ pub fn test_msg_send(obj: &CustomObject) { } pub fn test_msg_send_id(obj: &new_objc2::runtime::Object) { - let _: new_objc2::rc::Id = + let _: new_objc2::rc::Id = unsafe { new_objc2::msg_send_id![obj, a] }; - let _: new_objc2::__macro_helpers::Option< - new_objc2::rc::Id, - > = unsafe { new_objc2::msg_send_id![obj, a] }; - let _: new_objc2::rc::Id = + let _: new_objc2::__macro_helpers::Option> = + unsafe { new_objc2::msg_send_id![obj, a] }; + let _: new_objc2::rc::Id = unsafe { new_objc2::msg_send_id![obj, a: obj, b: obj] }; } diff --git a/crates/objc2/tests/track_caller.rs b/crates/objc2/tests/track_caller.rs index aacb20f71..8d1d2d1ba 100644 --- a/crates/objc2/tests/track_caller.rs +++ b/crates/objc2/tests/track_caller.rs @@ -9,7 +9,7 @@ use std::sync::Mutex; use objc2::encode::Encode; use objc2::rc::{Allocated, Id, __RcTestObject}; use objc2::runtime::{NSObject, Object}; -use objc2::{class, declare_class, msg_send, msg_send_id, ClassType}; +use objc2::{class, declare_class, msg_send, msg_send_id, mutability, ClassType}; static EXPECTED_MESSAGE: Mutex = Mutex::new(String::new()); static EXPECTED_LINE: Mutex = Mutex::new(0); @@ -190,6 +190,7 @@ declare_class!( unsafe impl ClassType for PanickingClass { type Super = NSObject; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "PanickingClass"; } diff --git a/crates/objc2/tests/use_macros.rs b/crates/objc2/tests/use_macros.rs index 46d2ca732..32d149ecf 100644 --- a/crates/objc2/tests/use_macros.rs +++ b/crates/objc2/tests/use_macros.rs @@ -1,3 +1,4 @@ +use objc2::mutability::Immutable; use objc2::runtime::{Class, NSObject, Object}; use objc2::{class, declare_class, msg_send, sel, ClassType}; @@ -6,6 +7,7 @@ declare_class!( unsafe impl ClassType for MyObject { type Super = NSObject; + type Mutability = Immutable; const NAME: &'static str = "MyObject"; } ); diff --git a/crates/test-assembly/crates/test_extern_protocol/expected/apple-aarch64.s b/crates/test-assembly/crates/test_extern_protocol/expected/apple-aarch64.s index 3749b4a97..1c8a4e268 100644 --- a/crates/test-assembly/crates/test_extern_protocol/expected/apple-aarch64.s +++ b/crates/test-assembly/crates/test_extern_protocol/expected/apple-aarch64.s @@ -14,9 +14,9 @@ Lloh1: .p2align 2 _dyn_call: Lloh2: - adrp x8, L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6@PAGE + adrp x8, L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69@PAGE Lloh3: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6@PAGEOFF] + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69@PAGEOFF] b _objc_msgSend .loh AdrpLdr Lloh2, Lloh3 @@ -28,9 +28,9 @@ _dyn_consume: add x29, sp, #16 mov x19, x0 Lloh4: - adrp x8, L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6@PAGE + adrp x8, L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69@PAGE Lloh5: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6@PAGEOFF] + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69@PAGEOFF] bl _objc_msgSend mov x0, x19 ldp x29, x30, [sp, #16] @@ -43,20 +43,20 @@ l_anon.[ID].0: .ascii "MyProtocol" .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_acbdb619e79b01b6 + .globl L_OBJC_IMAGE_INFO_d7a070d5c55b8e69 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_acbdb619e79b01b6: +L_OBJC_IMAGE_INFO_d7a070d5c55b8e69: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_acbdb619e79b01b6 -L_OBJC_METH_VAR_NAME_acbdb619e79b01b6: + .globl L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69 +L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69: .asciz "aMethod" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6 + .globl L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6: - .quad L_OBJC_METH_VAR_NAME_acbdb619e79b01b6 +L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69: + .quad L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69 .subsections_via_symbols diff --git a/crates/test-assembly/crates/test_extern_protocol/expected/apple-armv7.s b/crates/test-assembly/crates/test_extern_protocol/expected/apple-armv7.s index 4987dac26..91ac95f02 100644 --- a/crates/test-assembly/crates/test_extern_protocol/expected/apple-armv7.s +++ b/crates/test-assembly/crates/test_extern_protocol/expected/apple-armv7.s @@ -15,8 +15,8 @@ LPC0_0: .p2align 2 .code 32 _dyn_call: - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6-(LPC1_0+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6-(LPC1_0+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69-(LPC1_0+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69-(LPC1_0+8)) LPC1_0: ldr r1, [pc, r1] b _objc_msgSend @@ -27,9 +27,9 @@ LPC1_0: _dyn_consume: push {r4, r7, lr} add r7, sp, #4 - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6-(LPC2_0+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69-(LPC2_0+8)) mov r4, r0 - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6-(LPC2_0+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69-(LPC2_0+8)) LPC2_0: ldr r1, [pc, r1] bl _objc_msgSend @@ -42,20 +42,20 @@ l_anon.[ID].0: .ascii "MyProtocol" .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_acbdb619e79b01b6 + .globl L_OBJC_IMAGE_INFO_d7a070d5c55b8e69 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_acbdb619e79b01b6: +L_OBJC_IMAGE_INFO_d7a070d5c55b8e69: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_acbdb619e79b01b6 -L_OBJC_METH_VAR_NAME_acbdb619e79b01b6: + .globl L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69 +L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69: .asciz "aMethod" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6 + .globl L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6: - .long L_OBJC_METH_VAR_NAME_acbdb619e79b01b6 +L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69: + .long L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69 .subsections_via_symbols diff --git a/crates/test-assembly/crates/test_extern_protocol/expected/apple-armv7s.s b/crates/test-assembly/crates/test_extern_protocol/expected/apple-armv7s.s index c9c66bb9d..1c15631f3 100644 --- a/crates/test-assembly/crates/test_extern_protocol/expected/apple-armv7s.s +++ b/crates/test-assembly/crates/test_extern_protocol/expected/apple-armv7s.s @@ -20,8 +20,8 @@ LPC0_0: _dyn_call: push {r7, lr} mov r7, sp - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6-(LPC1_0+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6-(LPC1_0+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69-(LPC1_0+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69-(LPC1_0+8)) LPC1_0: ldr r1, [pc, r1] bl _objc_msgSend @@ -33,9 +33,9 @@ LPC1_0: _dyn_consume: push {r4, r7, lr} add r7, sp, #4 - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6-(LPC2_0+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69-(LPC2_0+8)) mov r4, r0 - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6-(LPC2_0+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69-(LPC2_0+8)) LPC2_0: ldr r1, [pc, r1] bl _objc_msgSend @@ -48,20 +48,20 @@ l_anon.[ID].0: .ascii "MyProtocol" .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_acbdb619e79b01b6 + .globl L_OBJC_IMAGE_INFO_d7a070d5c55b8e69 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_acbdb619e79b01b6: +L_OBJC_IMAGE_INFO_d7a070d5c55b8e69: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_acbdb619e79b01b6 -L_OBJC_METH_VAR_NAME_acbdb619e79b01b6: + .globl L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69 +L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69: .asciz "aMethod" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6 + .globl L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6: - .long L_OBJC_METH_VAR_NAME_acbdb619e79b01b6 +L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69: + .long L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69 .subsections_via_symbols diff --git a/crates/test-assembly/crates/test_extern_protocol/expected/apple-old-x86.s b/crates/test-assembly/crates/test_extern_protocol/expected/apple-old-x86.s index c1e9a8bcb..38fac2cb5 100644 --- a/crates/test-assembly/crates/test_extern_protocol/expected/apple-old-x86.s +++ b/crates/test-assembly/crates/test_extern_protocol/expected/apple-old-x86.s @@ -28,7 +28,7 @@ _dyn_call: L1$pb: pop eax sub esp, 8 - push dword ptr [eax + L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6-L1$pb] + push dword ptr [eax + L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69-L1$pb] push dword ptr [ebp + 8] call _objc_msgSend add esp, 24 @@ -45,7 +45,7 @@ _dyn_consume: L2$pb: pop eax sub esp, 8 - push dword ptr [eax + L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6-L2$pb] + push dword ptr [eax + L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69-L2$pb] push dword ptr [ebp + 8] call _objc_msgSend add esp, 24 @@ -57,20 +57,20 @@ l_anon.[ID].0: .ascii "MyProtocol" .section __OBJC,__image_info - .globl L_OBJC_IMAGE_INFO_acbdb619e79b01b6 + .globl L_OBJC_IMAGE_INFO_d7a070d5c55b8e69 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_acbdb619e79b01b6: +L_OBJC_IMAGE_INFO_d7a070d5c55b8e69: .asciz "\000\000\000\000@\000\000" .section __TEXT,__cstring,cstring_literals - .globl L_OBJC_METH_VAR_NAME_acbdb619e79b01b6 -L_OBJC_METH_VAR_NAME_acbdb619e79b01b6: + .globl L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69 +L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69: .asciz "aMethod" .section __OBJC,__message_refs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6 + .globl L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6: - .long L_OBJC_METH_VAR_NAME_acbdb619e79b01b6 +L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69: + .long L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69 .subsections_via_symbols diff --git a/crates/test-assembly/crates/test_extern_protocol/expected/apple-x86.s b/crates/test-assembly/crates/test_extern_protocol/expected/apple-x86.s index 0fff7aae9..6443ae695 100644 --- a/crates/test-assembly/crates/test_extern_protocol/expected/apple-x86.s +++ b/crates/test-assembly/crates/test_extern_protocol/expected/apple-x86.s @@ -28,7 +28,7 @@ _dyn_call: L1$pb: pop eax sub esp, 8 - push dword ptr [eax + L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6-L1$pb] + push dword ptr [eax + L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69-L1$pb] push dword ptr [ebp + 8] call _objc_msgSend add esp, 24 @@ -45,7 +45,7 @@ _dyn_consume: L2$pb: pop eax sub esp, 8 - push dword ptr [eax + L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6-L2$pb] + push dword ptr [eax + L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69-L2$pb] push dword ptr [ebp + 8] call _objc_msgSend add esp, 24 @@ -57,20 +57,20 @@ l_anon.[ID].0: .ascii "MyProtocol" .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_acbdb619e79b01b6 + .globl L_OBJC_IMAGE_INFO_d7a070d5c55b8e69 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_acbdb619e79b01b6: +L_OBJC_IMAGE_INFO_d7a070d5c55b8e69: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_acbdb619e79b01b6 -L_OBJC_METH_VAR_NAME_acbdb619e79b01b6: + .globl L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69 +L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69: .asciz "aMethod" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6 + .globl L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6: - .long L_OBJC_METH_VAR_NAME_acbdb619e79b01b6 +L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69: + .long L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69 .subsections_via_symbols diff --git a/crates/test-assembly/crates/test_extern_protocol/expected/apple-x86_64.s b/crates/test-assembly/crates/test_extern_protocol/expected/apple-x86_64.s index 771861ce0..b5119b5ca 100644 --- a/crates/test-assembly/crates/test_extern_protocol/expected/apple-x86_64.s +++ b/crates/test-assembly/crates/test_extern_protocol/expected/apple-x86_64.s @@ -15,7 +15,7 @@ _get_protocol: _dyn_call: push rbp mov rbp, rsp - mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6] + mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69] pop rbp jmp _objc_msgSend @@ -27,7 +27,7 @@ _dyn_consume: push rbx push rax mov rbx, rdi - mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6] + mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69] call _objc_msgSend mov rdi, rbx add rsp, 8 @@ -40,20 +40,20 @@ l_anon.[ID].0: .ascii "MyProtocol" .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_acbdb619e79b01b6 + .globl L_OBJC_IMAGE_INFO_d7a070d5c55b8e69 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_acbdb619e79b01b6: +L_OBJC_IMAGE_INFO_d7a070d5c55b8e69: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_acbdb619e79b01b6 -L_OBJC_METH_VAR_NAME_acbdb619e79b01b6: + .globl L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69 +L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69: .asciz "aMethod" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6 + .globl L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_acbdb619e79b01b6: - .quad L_OBJC_METH_VAR_NAME_acbdb619e79b01b6 +L_OBJC_SELECTOR_REFERENCES_d7a070d5c55b8e69: + .quad L_OBJC_METH_VAR_NAME_d7a070d5c55b8e69 .subsections_via_symbols diff --git a/crates/test-assembly/crates/test_extern_protocol/lib.rs b/crates/test-assembly/crates/test_extern_protocol/lib.rs index 21768ffc8..77602db9f 100644 --- a/crates/test-assembly/crates/test_extern_protocol/lib.rs +++ b/crates/test-assembly/crates/test_extern_protocol/lib.rs @@ -7,6 +7,7 @@ use objc2::runtime::{Protocol, ProtocolObject}; use objc2::{extern_protocol, ProtocolType}; extern_protocol!( + #[allow(clippy::missing_safety_doc)] unsafe trait MyProtocol { #[allow(non_snake_case)] #[method(aMethod)] diff --git a/crates/test-assembly/crates/test_out_parameters/lib.rs b/crates/test-assembly/crates/test_out_parameters/lib.rs index c2cf19cd2..088b39c70 100644 --- a/crates/test-assembly/crates/test_out_parameters/lib.rs +++ b/crates/test-assembly/crates/test_out_parameters/lib.rs @@ -1,29 +1,25 @@ //! Test that out parameters are handled correctly. -use objc2::rc::{Id, Owned}; +use objc2::rc::Id; use objc2::runtime::{Object, Sel}; use objc2::MessageReceiver; #[no_mangle] -unsafe fn nonnull_nonnull(obj: &Object, sel: Sel, param: &mut Id) -> usize { +unsafe fn nonnull_nonnull(obj: &Object, sel: Sel, param: &mut Id) -> usize { MessageReceiver::send_message(obj, sel, (param,)) } #[no_mangle] -unsafe fn null_nonnull(obj: &Object, sel: Sel, param: Option<&mut Id>) -> usize { +unsafe fn null_nonnull(obj: &Object, sel: Sel, param: Option<&mut Id>) -> usize { MessageReceiver::send_message(obj, sel, (param,)) } #[no_mangle] -unsafe fn nonnull_null(obj: &Object, sel: Sel, param: &mut Option>) -> usize { +unsafe fn nonnull_null(obj: &Object, sel: Sel, param: &mut Option>) -> usize { MessageReceiver::send_message(obj, sel, (param,)) } #[no_mangle] -unsafe fn null_null( - obj: &Object, - sel: Sel, - param: Option<&mut Option>>, -) -> usize { +unsafe fn null_null(obj: &Object, sel: Sel, param: Option<&mut Option>>) -> usize { MessageReceiver::send_message(obj, sel, (param,)) } @@ -31,8 +27,8 @@ unsafe fn null_null( unsafe fn two_nonnull_nonnull( obj: &Object, sel: Sel, - param1: &mut Id, - param2: &mut Id, + param1: &mut Id, + param2: &mut Id, ) -> usize { MessageReceiver::send_message(obj, sel, (param1, param2)) } @@ -51,7 +47,7 @@ unsafe fn call_with_none2(obj: &Object, sel: Sel) -> usize { null_null(obj, sel, None) } -type Res = (usize, Option>); +type Res = (usize, Option>); // These should only need a `retain` #[no_mangle] @@ -69,18 +65,18 @@ unsafe fn call_with_none4(obj: &Object, sel: Sel) -> Res { // These should need `retain/release`, but not have any branches #[no_mangle] -unsafe fn call_with_some1(obj: &Object, sel: Sel, mut param: Id) -> Res { +unsafe fn call_with_some1(obj: &Object, sel: Sel, mut param: Id) -> Res { let res = null_nonnull(obj, sel, Some(&mut param)); (res, Some(param)) } #[no_mangle] -unsafe fn call_with_some2(obj: &Object, sel: Sel, param: Id) -> Res { +unsafe fn call_with_some2(obj: &Object, sel: Sel, param: Id) -> Res { let mut param = Some(param); let res = nonnull_null(obj, sel, &mut param); (res, param) } #[no_mangle] -unsafe fn call_with_some3(obj: &Object, sel: Sel, param: Id) -> Res { +unsafe fn call_with_some3(obj: &Object, sel: Sel, param: Id) -> Res { let mut param = Some(param); let res = null_null(obj, sel, Some(&mut param)); (res, param) diff --git a/crates/test-ui/Cargo.toml b/crates/test-ui/Cargo.toml index e5dc19596..a6aeef2d5 100644 --- a/crates/test-ui/Cargo.toml +++ b/crates/test-ui/Cargo.toml @@ -18,6 +18,7 @@ default = [ "icrate/Foundation_NSThread", "icrate/Foundation_NSError", "icrate/Foundation_NSArray", + "icrate/Foundation_NSMutableArray", "icrate/Foundation_NSValue", ] std = ["block2/std", "objc2/std", "icrate/std"] diff --git a/crates/test-ui/ui/declare_add_bad_method.rs b/crates/test-ui/ui/declare_add_bad_method.rs index 258a9d938..9c26f2500 100644 --- a/crates/test-ui/ui/declare_add_bad_method.rs +++ b/crates/test-ui/ui/declare_add_bad_method.rs @@ -1,6 +1,6 @@ use objc2::declare::ClassBuilder; use objc2::rc::{Allocated, Id}; -use objc2::runtime::{Sel, NSObject}; +use objc2::runtime::{NSObject, Sel}; use objc2::{sel, ClassType}; fn main() { diff --git a/crates/test-ui/ui/declare_class_classtype_imported.rs b/crates/test-ui/ui/declare_class_classtype_imported.rs index 6c7acc19f..7ab550cd2 100644 --- a/crates/test-ui/ui/declare_class_classtype_imported.rs +++ b/crates/test-ui/ui/declare_class_classtype_imported.rs @@ -1,12 +1,13 @@ -use objc2::declare_class; -#[allow(unused_imports)] +#![allow(unused_imports)] use objc2::runtime::NSObject; +use objc2::{declare_class, mutability}; declare_class!( struct CustomObject; unsafe impl objc2::ClassType for CustomObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "CustomObject"; } ); diff --git a/crates/test-ui/ui/declare_class_invalid_receiver.rs b/crates/test-ui/ui/declare_class_invalid_receiver.rs index e6c781363..b870458f6 100644 --- a/crates/test-ui/ui/declare_class_invalid_receiver.rs +++ b/crates/test-ui/ui/declare_class_invalid_receiver.rs @@ -1,12 +1,13 @@ -use objc2::{declare_class, ClassType}; use objc2::rc::{Allocated, Id}; use objc2::runtime::NSObject; +use objc2::{declare_class, mutability, ClassType}; declare_class!( struct CustomObject; unsafe impl ClassType for CustomObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "CustomObject"; } diff --git a/crates/test-ui/ui/declare_class_invalid_receiver.stderr b/crates/test-ui/ui/declare_class_invalid_receiver.stderr index 76af609fe..458d2e4c7 100644 --- a/crates/test-ui/ui/declare_class_invalid_receiver.stderr +++ b/crates/test-ui/ui/declare_class_invalid_receiver.stderr @@ -203,9 +203,9 @@ error[E0277]: the trait bound `Box: MessageReceiver` is not satisf | |_^ the trait `MessageReceiver` is not implemented for `Box` | = help: the following other types implement trait `MessageReceiver`: - &'a Id + &'a Id &'a T - &'a mut Id + &'a mut Id &'a mut T &'a objc2::runtime::Class *const T @@ -228,8 +228,8 @@ error[E0277]: the trait bound `Id: MessageReceiver` is not satisfi | |_^ the trait `MessageReceiver` is not implemented for `Id` | = help: the following other types implement trait `MessageReceiver`: - &'a Id - &'a mut Id + &'a Id + &'a mut Id = note: required for `RetainSemantics<5>` to implement `MessageRecieveId, Id>` = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -246,9 +246,9 @@ error[E0277]: the trait bound `CustomObject: MessageReceiver` is not satisfied | |_^ the trait `MessageReceiver` is not implemented for `CustomObject` | = help: the following other types implement trait `MessageReceiver`: - &'a Id + &'a Id &'a T - &'a mut Id + &'a mut Id &'a mut T &'a objc2::runtime::Class *const T @@ -271,9 +271,9 @@ error[E0277]: the trait bound `Allocated: MessageReceiver` is not | |_^ the trait `MessageReceiver` is not implemented for `Allocated` | = help: the following other types implement trait `MessageReceiver`: - &'a Id + &'a Id &'a T - &'a mut Id + &'a mut Id &'a mut T &'a objc2::runtime::Class *const T diff --git a/crates/test-ui/ui/declare_class_invalid_syntax.rs b/crates/test-ui/ui/declare_class_invalid_syntax.rs index 87d6229cb..dc18511e6 100644 --- a/crates/test-ui/ui/declare_class_invalid_syntax.rs +++ b/crates/test-ui/ui/declare_class_invalid_syntax.rs @@ -3,13 +3,14 @@ use std::marker::PhantomData; use objc2::declare::IvarEncode; use objc2::rc::Id; use objc2::runtime::NSObject; -use objc2::{declare_class, ClassType}; +use objc2::{declare_class, mutability, ClassType}; declare_class!( struct CustomObject; unsafe impl ClassType for CustomObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "CustomObject"; } @@ -87,6 +88,7 @@ declare_class!( unsafe impl ClassType for MissingName { type Super = NSObject; + type Mutability = mutability::InteriorMutable; } ); @@ -97,6 +99,7 @@ declare_class!( unsafe impl ClassType for InvalidField { type Super = NSObject; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "InvalidField"; } ); @@ -108,6 +111,7 @@ declare_class!( unsafe impl ClassType for UnnecessaryIvarModule { type Super = NSObject; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "UnnecessaryIvarModule"; } ); @@ -121,6 +125,7 @@ declare_class!( unsafe impl ClassType for UnnecessaryIvarModuleWithFields { type Super = NSObject; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "UnnecessaryIvarModuleWithFields"; } ); @@ -132,8 +137,17 @@ declare_class!( unsafe impl ClassType for MissingIvarModule { type Super = NSObject; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "MissingIvarModule"; } ); +declare_class!( + struct MissingMutability; + + unsafe impl ClassType for MissingMutability { + type Super = NSObject; + } +); + fn main() {} diff --git a/crates/test-ui/ui/declare_class_invalid_syntax.stderr b/crates/test-ui/ui/declare_class_invalid_syntax.stderr index 642a63985..f90383b25 100644 --- a/crates/test-ui/ui/declare_class_invalid_syntax.stderr +++ b/crates/test-ui/ui/declare_class_invalid_syntax.stderr @@ -214,7 +214,7 @@ error: no rules expected the token `}` note: while trying to match `const` --> $WORKSPACE/crates/objc2/src/macros/declare_class.rs | - | const NAME: &'static str = $name_const:literal; + | const NAME: &'static str = $name_const:expr; | ^^^^^ error: invalid type i32 in field field. Type must be either `PhantomData`, `IvarDrop`, `IvarBool` or `IvarEncode`. @@ -271,6 +271,18 @@ error: must specify an ivar module when the type has ivars | = note: this error originates in the macro `$crate::__parse_fields` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) +error: no rules expected the token `}` + --> ui/declare_class_invalid_syntax.rs + | + | } + | ^ no rules expected this token in macro call + | +note: while trying to match `type` + --> $WORKSPACE/crates/objc2/src/macros/declare_class.rs + | + | type Mutability = $mutability:ty; + | ^^^^ + error[E0599]: no function or associated item named `test_pattern` found for struct `CustomObject` in the current scope --> ui/declare_class_invalid_syntax.rs | diff --git a/crates/test-ui/ui/declare_class_invalid_type.rs b/crates/test-ui/ui/declare_class_invalid_type.rs index 95ec10e8f..a34b00e1b 100644 --- a/crates/test-ui/ui/declare_class_invalid_type.rs +++ b/crates/test-ui/ui/declare_class_invalid_type.rs @@ -1,12 +1,13 @@ -use objc2::{declare_class, ClassType}; use objc2::rc::Id; use objc2::runtime::NSObject; +use objc2::{declare_class, mutability, ClassType}; declare_class!( struct CustomObject; unsafe impl ClassType for CustomObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "CustomObject"; } diff --git a/crates/test-ui/ui/declare_class_invalid_type2.rs b/crates/test-ui/ui/declare_class_invalid_type2.rs index 908b1b312..18dcf5da3 100644 --- a/crates/test-ui/ui/declare_class_invalid_type2.rs +++ b/crates/test-ui/ui/declare_class_invalid_type2.rs @@ -1,12 +1,13 @@ use objc2::rc::{Allocated, Id}; -use objc2::{declare_class, ClassType}; use objc2::runtime::NSObject; +use objc2::{declare_class, mutability, ClassType}; declare_class!( struct CustomObject; unsafe impl ClassType for CustomObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "CustomObject"; } diff --git a/crates/test-ui/ui/declare_class_invalid_type2.stderr b/crates/test-ui/ui/declare_class_invalid_type2.stderr index a0fe66ee9..a9e81705c 100644 --- a/crates/test-ui/ui/declare_class_invalid_type2.stderr +++ b/crates/test-ui/ui/declare_class_invalid_type2.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving ` as MaybeUnwrap>::Input == Id` +error[E0271]: type mismatch resolving ` as MaybeUnwrap>::Input == Id` --> ui/declare_class_invalid_type2.rs | | / declare_class!( @@ -8,10 +8,10 @@ error[E0271]: type mismatch resolving ` as MaybeUnwrap>::Input == I ... | | | } | | ); - | |_^ expected `Id`, found `Id` + | |_^ expected `Id`, found `Id` | - = note: expected struct `Id` - found struct `Id` + = note: expected struct `Id` + found struct `Id` = note: required for `RetainSemantics<3>` to implement `MessageRecieveId, Id>` = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -28,7 +28,7 @@ error[E0277]: the trait bound `i32: MaybeOptionId` is not satisfied | |_^ the trait `MaybeOptionId` is not implemented for `i32` | = help: the following other types implement trait `MaybeOptionId`: - Id - Option> + Id + Option> = note: required for `RetainSemantics<5>` to implement `MessageRecieveId<&CustomObject, i32>` = note: this error originates in the macro `$crate::__declare_class_method_out_inner` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/declare_class_invalid_type3.rs b/crates/test-ui/ui/declare_class_invalid_type3.rs index 643cb9002..99480de84 100644 --- a/crates/test-ui/ui/declare_class_invalid_type3.rs +++ b/crates/test-ui/ui/declare_class_invalid_type3.rs @@ -1,6 +1,6 @@ use objc2::declare::IvarEncode; use objc2::runtime::NSObject; -use objc2::{declare_class, ClassType}; +use objc2::{declare_class, mutability, ClassType}; declare_class!( struct CustomObject { @@ -11,6 +11,7 @@ declare_class!( unsafe impl ClassType for CustomObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "CustomObject"; } ); diff --git a/crates/test-ui/ui/declare_class_mut_self_not_mutable.rs b/crates/test-ui/ui/declare_class_mut_self_not_mutable.rs new file mode 100644 index 000000000..d2fc4217d --- /dev/null +++ b/crates/test-ui/ui/declare_class_mut_self_not_mutable.rs @@ -0,0 +1,32 @@ +use objc2::rc::Id; +use objc2::runtime::NSObject; +use objc2::{declare_class, mutability, ClassType}; + +declare_class!( + struct CustomObject; + + unsafe impl ClassType for CustomObject { + type Super = NSObject; + type Mutability = mutability::InteriorMutable; + const NAME: &'static str = "CustomObject"; + } + + unsafe impl CustomObject { + #[method(initTest)] + fn initTest(&mut self) -> &mut Self { + unimplemented!() + } + + #[method(testMethod)] + fn test_method(&mut self) { + unimplemented!() + } + + #[method_id(testMethodId)] + fn test_method_id(&mut self) -> Id { + unimplemented!() + } + } +); + +fn main() {} diff --git a/crates/test-ui/ui/declare_class_mut_self_not_mutable.stderr b/crates/test-ui/ui/declare_class_mut_self_not_mutable.stderr new file mode 100644 index 000000000..6b45aa55a --- /dev/null +++ b/crates/test-ui/ui/declare_class_mut_self_not_mutable.stderr @@ -0,0 +1,80 @@ +error[E0277]: the trait bound `InteriorMutable: mutability::private::MutabilityIsMutable` is not satisfied + --> ui/declare_class_mut_self_not_mutable.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | | ^ + | | | + | |_the trait `mutability::private::MutabilityIsMutable` is not implemented for `InteriorMutable` + | required by a bound introduced by this call + | + = help: the following other types implement trait `mutability::private::MutabilityIsMutable`: + Mutable + MutableWithImmutableSuperclass + = note: required for `CustomObject` to implement `IsMutable` + = note: required for `extern "C" fn(&mut CustomObject, objc2::runtime::Sel) -> &mut CustomObject` to implement `MethodImplementation` +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare/mod.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + = note: this error originates in the macro `$crate::__declare_class_register_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `InteriorMutable: mutability::private::MutabilityIsMutable` is not satisfied + --> ui/declare_class_mut_self_not_mutable.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | | ^ + | | | + | |_the trait `mutability::private::MutabilityIsMutable` is not implemented for `InteriorMutable` + | required by a bound introduced by this call + | + = help: the following other types implement trait `mutability::private::MutabilityIsMutable`: + Mutable + MutableWithImmutableSuperclass + = note: required for `CustomObject` to implement `IsMutable` + = note: required for `extern "C" fn(&mut CustomObject, objc2::runtime::Sel)` to implement `MethodImplementation` +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare/mod.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + = note: this error originates in the macro `$crate::__declare_class_register_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `InteriorMutable: mutability::private::MutabilityIsMutable` is not satisfied + --> ui/declare_class_mut_self_not_mutable.rs + | + | / declare_class!( + | | struct CustomObject; + | | + | | unsafe impl ClassType for CustomObject { +... | + | | } + | | ); + | | ^ + | | | + | |_the trait `mutability::private::MutabilityIsMutable` is not implemented for `InteriorMutable` + | required by a bound introduced by this call + | + = help: the following other types implement trait `mutability::private::MutabilityIsMutable`: + Mutable + MutableWithImmutableSuperclass + = note: required for `CustomObject` to implement `IsMutable` + = note: required for `extern "C" fn(&mut CustomObject, objc2::runtime::Sel) -> __IdReturnValue` to implement `MethodImplementation` +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/crates/objc2/src/declare/mod.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + = note: this error originates in the macro `$crate::__declare_class_register_out` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/extern_class_feature_flag.rs b/crates/test-ui/ui/extern_class_feature_flag.rs index a279b4553..e9cc8d1ad 100644 --- a/crates/test-ui/ui/extern_class_feature_flag.rs +++ b/crates/test-ui/ui/extern_class_feature_flag.rs @@ -1,5 +1,5 @@ -use objc2::{extern_class, ClassType}; use objc2::runtime::NSObject; +use objc2::{extern_class, mutability, ClassType}; extern_class!( #[cfg(not(feature = "mytest"))] @@ -8,6 +8,7 @@ extern_class!( #[cfg(not(feature = "mytest"))] unsafe impl ClassType for MyTestEnabled { type Super = NSObject; + type Mutability = mutability::InteriorMutable; } ); @@ -18,6 +19,7 @@ extern_class!( #[cfg(feature = "mytest")] unsafe impl ClassType for MyTestDisabled { type Super = NSObject; + type Mutability = mutability::InteriorMutable; } ); diff --git a/crates/test-ui/ui/extern_class_not_zst.rs b/crates/test-ui/ui/extern_class_not_zst.rs index f6d264901..37d68a242 100644 --- a/crates/test-ui/ui/extern_class_not_zst.rs +++ b/crates/test-ui/ui/extern_class_not_zst.rs @@ -1,5 +1,5 @@ -use objc2::{extern_class, ClassType}; use objc2::runtime::NSObject; +use objc2::{extern_class, mutability, ClassType}; extern_class!( pub struct NSNumber { @@ -8,6 +8,7 @@ extern_class!( unsafe impl ClassType for NSNumber { type Super = NSObject; + type Mutability = mutability::InteriorMutable; } ); diff --git a/crates/test-ui/ui/extern_class_root.rs b/crates/test-ui/ui/extern_class_root.rs new file mode 100644 index 000000000..4b329fedd --- /dev/null +++ b/crates/test-ui/ui/extern_class_root.rs @@ -0,0 +1,39 @@ +use core::ops::{Deref, DerefMut}; + +use objc2::encode::{Encoding, RefEncode}; +use objc2::runtime::Object; +use objc2::{extern_class, mutability, ClassType, Message}; + +#[repr(transparent)] +struct MyObject(Object); + +unsafe impl RefEncode for MyObject { + const ENCODING_REF: Encoding = Encoding::Object; +} + +unsafe impl Message for MyObject {} + +impl Deref for MyObject { + type Target = Object; + + fn deref(&self) -> &Object { + &self.0 + } +} + +impl DerefMut for MyObject { + fn deref_mut(&mut self) -> &mut Object { + &mut self.0 + } +} + +extern_class!( + pub struct MyRootClass; + + unsafe impl ClassType for MyRootClass { + type Super = MyObject; + type Mutability = mutability::InteriorMutable; + } +); + +fn main() {} diff --git a/crates/test-ui/ui/extern_class_root.stderr b/crates/test-ui/ui/extern_class_root.stderr new file mode 100644 index 000000000..08d99b345 --- /dev/null +++ b/crates/test-ui/ui/extern_class_root.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `MyObject: ClassType` is not satisfied + --> ui/extern_class_root.rs + | + | / extern_class!( + | | pub struct MyRootClass; + | | + | | unsafe impl ClassType for MyRootClass { +... | + | | } + | | ); + | |_^ the trait `ClassType` is not implemented for `MyObject` + | + = help: the following other types implement trait `ClassType`: + MyRootClass + NSObject + __NSProxy + __RcTestObject + = note: this error originates in the macro `$crate::__inner_extern_class` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/extern_class_subclass_object.rs b/crates/test-ui/ui/extern_class_subclass_object.rs new file mode 100644 index 000000000..74f3f8d3d --- /dev/null +++ b/crates/test-ui/ui/extern_class_subclass_object.rs @@ -0,0 +1,13 @@ +use objc2::runtime::Object; +use objc2::{extern_class, mutability, ClassType}; + +extern_class!( + pub struct MyRootClass; + + unsafe impl ClassType for MyRootClass { + type Super = Object; + type Mutability = mutability::InteriorMutable; + } +); + +fn main() {} diff --git a/crates/test-ui/ui/extern_class_subclass_object.stderr b/crates/test-ui/ui/extern_class_subclass_object.stderr new file mode 100644 index 000000000..80c06386c --- /dev/null +++ b/crates/test-ui/ui/extern_class_subclass_object.stderr @@ -0,0 +1,67 @@ +error[E0119]: conflicting implementations of trait `AsRef` for type `MyRootClass` + --> ui/extern_class_subclass_object.rs + | + | / extern_class!( + | | pub struct MyRootClass; + | | + | | unsafe impl ClassType for MyRootClass { +... | + | | } + | | ); + | | ^ + | | | + | |_first implementation here + | conflicting implementation for `MyRootClass` + | + = note: this error originates in the macro `$crate::__impl_as_ref_borrow` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `AsMut` for type `MyRootClass` + --> ui/extern_class_subclass_object.rs + | + | / extern_class!( + | | pub struct MyRootClass; + | | + | | unsafe impl ClassType for MyRootClass { +... | + | | } + | | ); + | | ^ + | | | + | |_first implementation here + | conflicting implementation for `MyRootClass` + | + = note: this error originates in the macro `$crate::__impl_as_ref_borrow` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `Borrow` for type `MyRootClass` + --> ui/extern_class_subclass_object.rs + | + | / extern_class!( + | | pub struct MyRootClass; + | | + | | unsafe impl ClassType for MyRootClass { +... | + | | } + | | ); + | | ^ + | | | + | |_first implementation here + | conflicting implementation for `MyRootClass` + | + = note: this error originates in the macro `$crate::__impl_as_ref_borrow` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `BorrowMut` for type `MyRootClass` + --> ui/extern_class_subclass_object.rs + | + | / extern_class!( + | | pub struct MyRootClass; + | | + | | unsafe impl ClassType for MyRootClass { +... | + | | } + | | ); + | | ^ + | | | + | |_first implementation here + | conflicting implementation for `MyRootClass` + | + = note: this error originates in the macro `$crate::__impl_as_ref_borrow` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/extern_class_wrong_mutability.rs b/crates/test-ui/ui/extern_class_wrong_mutability.rs new file mode 100644 index 000000000..51ad94d8f --- /dev/null +++ b/crates/test-ui/ui/extern_class_wrong_mutability.rs @@ -0,0 +1,58 @@ +use objc2::runtime::NSObject; +use objc2::{extern_class, mutability, ClassType}; + +extern_class!( + pub struct MyMainThreadClass; + + unsafe impl ClassType for MyMainThreadClass { + type Super = NSObject; + type Mutability = mutability::MainThreadOnly; + } +); + +extern_class!( + pub struct MyAnyThreadClass; + + unsafe impl ClassType for MyAnyThreadClass { + type Super = MyMainThreadClass; + type Mutability = mutability::InteriorMutable; + } +); + +extern_class!( + pub struct MyImmutableClass1; + + unsafe impl ClassType for MyImmutableClass1 { + type Super = NSObject; + type Mutability = mutability::ImmutableWithMutableSubclass; + } +); + +extern_class!( + pub struct MyMutableClass1; + + unsafe impl ClassType for MyMutableClass1 { + type Super = MyImmutableClass1; + type Mutability = mutability::MutableWithImmutableSuperclass; + } +); + +extern_class!( + pub struct MyImmutableClass2; + + unsafe impl ClassType for MyImmutableClass2 { + type Super = NSObject; + type Mutability = mutability::ImmutableWithMutableSubclass; + } +); + +extern_class!( + pub struct MyMutableClass2; + + unsafe impl ClassType for MyMutableClass2 { + type Super = MyImmutableClass2; + type Mutability = mutability::MutableWithImmutableSuperclass; + } +); + +fn main() {} diff --git a/crates/test-ui/ui/extern_class_wrong_mutability.stderr b/crates/test-ui/ui/extern_class_wrong_mutability.stderr new file mode 100644 index 000000000..7636783fa --- /dev/null +++ b/crates/test-ui/ui/extern_class_wrong_mutability.stderr @@ -0,0 +1,107 @@ +error[E0277]: the trait bound `MainThreadOnly: ValidSubclassMutability` is not satisfied + --> ui/extern_class_wrong_mutability.rs + | + | / extern_class!( + | | pub struct MyAnyThreadClass; + | | + | | unsafe impl ClassType for MyAnyThreadClass { +... | + | | } + | | ); + | |_^ the trait `ValidSubclassMutability` is not implemented for `MainThreadOnly` + | + = help: the trait `ValidSubclassMutability` is implemented for `MainThreadOnly` +note: required by a bound in `assert_mutability_matches_superclass_mutability` + --> $WORKSPACE/crates/objc2/src/__macro_helpers/declare_class.rs + | + | ::Mutability: ValidSubclassMutability, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_mutability_matches_superclass_mutability` + = note: this error originates in the macro `$crate::__inner_extern_class` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0271]: type mismatch resolving `::Mutability == ImmutableWithMutableSubclass` + --> ui/extern_class_wrong_mutability.rs + | + | / extern_class!( + | | pub struct MyImmutableClass1; + | | + | | unsafe impl ClassType for MyImmutableClass1 { +... | + | | } + | | ); + | |_^ expected `ImmutableWithMutableSubclass`, found `Root` + | + = note: expected struct `ImmutableWithMutableSubclass` + found struct `Root` + = note: required for `Root` to implement `ValidSubclassMutability>` +note: required by a bound in `assert_mutability_matches_superclass_mutability` + --> $WORKSPACE/crates/objc2/src/__macro_helpers/declare_class.rs + | + | ::Mutability: ValidSubclassMutability, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_mutability_matches_superclass_mutability` + = note: this error originates in the macro `$crate::__inner_extern_class` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0271]: type mismatch resolving `::Mutability == ImmutableWithMutableSubclass` + --> ui/extern_class_wrong_mutability.rs + | + | / extern_class!( + | | pub struct MyMutableClass1; + | | + | | unsafe impl ClassType for MyMutableClass1 { +... | + | | } + | | ); + | |_^ expected `ImmutableWithMutableSubclass`, found `Root` + | + = note: expected struct `ImmutableWithMutableSubclass` + found struct `Root` + = note: required for `ImmutableWithMutableSubclass` to implement `ValidSubclassMutability>` +note: required by a bound in `assert_mutability_matches_superclass_mutability` + --> $WORKSPACE/crates/objc2/src/__macro_helpers/declare_class.rs + | + | ::Mutability: ValidSubclassMutability, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_mutability_matches_superclass_mutability` + = note: this error originates in the macro `$crate::__inner_extern_class` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0271]: type mismatch resolving `::Mutability == MutableWithImmutableSuperclass<_>` + --> ui/extern_class_wrong_mutability.rs + | + | / extern_class!( + | | pub struct MyImmutableClass2; + | | + | | unsafe impl ClassType for MyImmutableClass2 { +... | + | | } + | | ); + | |_^ expected `MutableWithImmutableSuperclass<_>`, found `Root` + | + = note: expected struct `MutableWithImmutableSuperclass<_>` + found struct `Root` + = note: required for `Root` to implement `ValidSubclassMutability>` +note: required by a bound in `assert_mutability_matches_superclass_mutability` + --> $WORKSPACE/crates/objc2/src/__macro_helpers/declare_class.rs + | + | ::Mutability: ValidSubclassMutability, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_mutability_matches_superclass_mutability` + = note: this error originates in the macro `$crate::__inner_extern_class` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0271]: type mismatch resolving `::Mutability == MutableWithImmutableSuperclass` + --> ui/extern_class_wrong_mutability.rs + | + | / extern_class!( + | | pub struct MyMutableClass2; + | | + | | unsafe impl ClassType for MyMutableClass2 { +... | + | | } + | | ); + | |_^ expected `MutableWithImmutableSuperclass`, found `Root` + | + = note: expected struct `MutableWithImmutableSuperclass` + found struct `Root` + = note: required for `ImmutableWithMutableSubclass` to implement `ValidSubclassMutability>` +note: required by a bound in `assert_mutability_matches_superclass_mutability` + --> $WORKSPACE/crates/objc2/src/__macro_helpers/declare_class.rs + | + | ::Mutability: ValidSubclassMutability, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_mutability_matches_superclass_mutability` + = note: this error originates in the macro `$crate::__inner_extern_class` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/extern_methods_bad_selector.rs b/crates/test-ui/ui/extern_methods_bad_selector.rs index 43aed3cb2..8b68edd09 100644 --- a/crates/test-ui/ui/extern_methods_bad_selector.rs +++ b/crates/test-ui/ui/extern_methods_bad_selector.rs @@ -1,11 +1,12 @@ -use objc2::{extern_class, extern_methods, ClassType}; use objc2::runtime::NSObject; +use objc2::{extern_class, extern_methods, mutability, ClassType}; extern_class!( pub struct MyObject; unsafe impl ClassType for MyObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; } ); diff --git a/crates/test-ui/ui/extern_methods_feature_flag.rs b/crates/test-ui/ui/extern_methods_feature_flag.rs index fced540f0..04ba32ef0 100644 --- a/crates/test-ui/ui/extern_methods_feature_flag.rs +++ b/crates/test-ui/ui/extern_methods_feature_flag.rs @@ -1,11 +1,12 @@ -use objc2::{extern_class, extern_methods, ClassType}; use objc2::runtime::NSObject; +use objc2::{extern_class, extern_methods, mutability, ClassType}; extern_class!( pub struct MyTest; unsafe impl ClassType for MyTest { type Super = NSObject; + type Mutability = mutability::InteriorMutable; } ); diff --git a/crates/test-ui/ui/extern_methods_feature_flag.stderr b/crates/test-ui/ui/extern_methods_feature_flag.stderr index b189a3297..023bd7d23 100644 --- a/crates/test-ui/ui/extern_methods_feature_flag.stderr +++ b/crates/test-ui/ui/extern_methods_feature_flag.stderr @@ -5,7 +5,7 @@ error[E0599]: no function or associated item named `disabled` found for struct ` | | pub struct MyTest; | | | | unsafe impl ClassType for MyTest { - | | type Super = NSObject; +... | | | } | | ); | |_- function or associated item `disabled` not found for this struct @@ -20,7 +20,7 @@ error[E0599]: no function or associated item named `disabled_inner1` found for s | | pub struct MyTest; | | | | unsafe impl ClassType for MyTest { - | | type Super = NSObject; +... | | | } | | ); | |_- function or associated item `disabled_inner1` not found for this struct @@ -38,7 +38,7 @@ error[E0599]: no function or associated item named `disabled_inner2` found for s | | pub struct MyTest; | | | | unsafe impl ClassType for MyTest { - | | type Super = NSObject; +... | | | } | | ); | |_- function or associated item `disabled_inner2` not found for this struct diff --git a/crates/test-ui/ui/extern_methods_invalid_type.rs b/crates/test-ui/ui/extern_methods_invalid_type.rs index cb45498d5..a6554d87f 100644 --- a/crates/test-ui/ui/extern_methods_invalid_type.rs +++ b/crates/test-ui/ui/extern_methods_invalid_type.rs @@ -1,12 +1,13 @@ -use objc2::{extern_class, extern_methods, ClassType}; use objc2::rc::Id; use objc2::runtime::NSObject; +use objc2::{extern_class, extern_methods, mutability, ClassType}; extern_class!( pub struct MyObject; unsafe impl ClassType for MyObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; } ); diff --git a/crates/test-ui/ui/extern_methods_invalid_type.stderr b/crates/test-ui/ui/extern_methods_invalid_type.stderr index 244895420..088f11a0e 100644 --- a/crates/test-ui/ui/extern_methods_invalid_type.stderr +++ b/crates/test-ui/ui/extern_methods_invalid_type.stderr @@ -41,9 +41,9 @@ error[E0277]: the trait bound `i32: MaybeUnwrap` is not satisfied | = help: the following other types implement trait `MaybeUnwrap`: Allocated - Id + Id Option> - Option> + Option> note: required by a bound in `send_message_id` --> $WORKSPACE/crates/objc2/src/__macro_helpers/mod.rs | @@ -117,9 +117,9 @@ error[E0277]: the trait bound `Result, Id>: MaybeUnwrap` | = help: the following other types implement trait `MaybeUnwrap`: Allocated - Id + Id Option> - Option> + Option> note: required by a bound in `send_message_id` --> $WORKSPACE/crates/objc2/src/__macro_helpers/mod.rs | diff --git a/crates/test-ui/ui/extern_methods_missing_method.rs b/crates/test-ui/ui/extern_methods_missing_method.rs index f9dca4008..608fac0da 100644 --- a/crates/test-ui/ui/extern_methods_missing_method.rs +++ b/crates/test-ui/ui/extern_methods_missing_method.rs @@ -1,11 +1,12 @@ -use objc2::{extern_class, extern_methods, ClassType}; use objc2::runtime::NSObject; +use objc2::{extern_class, extern_methods, mutability, ClassType}; extern_class!( pub struct MyObject; unsafe impl ClassType for MyObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; } ); diff --git a/crates/test-ui/ui/extern_methods_selector_twice.rs b/crates/test-ui/ui/extern_methods_selector_twice.rs index 4b3d61162..86cefef05 100644 --- a/crates/test-ui/ui/extern_methods_selector_twice.rs +++ b/crates/test-ui/ui/extern_methods_selector_twice.rs @@ -1,11 +1,12 @@ -use objc2::{extern_class, extern_methods, ClassType}; use objc2::runtime::NSObject; +use objc2::{extern_class, extern_methods, mutability, ClassType}; extern_class!( pub struct MyObject; unsafe impl ClassType for MyObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; } ); @@ -33,7 +34,6 @@ extern_methods!( } ); - extern_methods!( unsafe impl MyObject { #[method_id(d)] diff --git a/crates/test-ui/ui/extern_methods_variadic.rs b/crates/test-ui/ui/extern_methods_variadic.rs index c4963af3c..d1080a00d 100644 --- a/crates/test-ui/ui/extern_methods_variadic.rs +++ b/crates/test-ui/ui/extern_methods_variadic.rs @@ -1,12 +1,13 @@ -use objc2::{extern_class, extern_methods, ClassType}; use objc2::rc::Id; use objc2::runtime::NSObject; +use objc2::{extern_class, extern_methods, mutability, ClassType}; extern_class!( pub struct MyObject; unsafe impl ClassType for MyObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; } ); diff --git a/crates/test-ui/ui/extern_methods_wrong_arguments.rs b/crates/test-ui/ui/extern_methods_wrong_arguments.rs index 94d4d1e99..5e8eafd3b 100644 --- a/crates/test-ui/ui/extern_methods_wrong_arguments.rs +++ b/crates/test-ui/ui/extern_methods_wrong_arguments.rs @@ -1,11 +1,12 @@ -use objc2::{extern_class, extern_methods, ClassType}; use objc2::runtime::NSObject; +use objc2::{extern_class, extern_methods, mutability, ClassType}; extern_class!( pub struct MyObject; unsafe impl ClassType for MyObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; } ); diff --git a/crates/test-ui/ui/extern_methods_wrong_arguments_error.rs b/crates/test-ui/ui/extern_methods_wrong_arguments_error.rs index 8e8776857..ece4d5bd0 100644 --- a/crates/test-ui/ui/extern_methods_wrong_arguments_error.rs +++ b/crates/test-ui/ui/extern_methods_wrong_arguments_error.rs @@ -1,12 +1,13 @@ -use objc2::{extern_class, extern_methods, ClassType}; use objc2::rc::Id; use objc2::runtime::NSObject; +use objc2::{extern_class, extern_methods, mutability, ClassType}; extern_class!( pub struct MyObject; unsafe impl ClassType for MyObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; } ); diff --git a/crates/test-ui/ui/fn_ptr_reference_encode.rs b/crates/test-ui/ui/fn_ptr_reference_encode.rs index f4ef673a1..47c8e62a6 100644 --- a/crates/test-ui/ui/fn_ptr_reference_encode.rs +++ b/crates/test-ui/ui/fn_ptr_reference_encode.rs @@ -2,7 +2,7 @@ //! higher-ranked over lifetimes. //! //! Ideally, they should be, but they can't be right now. -use objc2::Encode; +use objc2::encode::Encode; extern "C" fn my_fn(_x: &i32) {} diff --git a/crates/test-ui/ui/fn_ptr_reference_method.rs b/crates/test-ui/ui/fn_ptr_reference_method.rs index 051f52633..73a82b2e1 100644 --- a/crates/test-ui/ui/fn_ptr_reference_method.rs +++ b/crates/test-ui/ui/fn_ptr_reference_method.rs @@ -5,9 +5,9 @@ //! //! (`_` can be used to work around this, by letting the compiler choose an //! appropriate lifetime '0 that the trait is implemented for). -use objc2::{class, sel}; use objc2::declare::ClassBuilder; use objc2::runtime::{Object, Sel}; +use objc2::{class, sel}; extern "C" fn my_fn(_this: &Object, _cmd: Sel, _x: &Object) {} diff --git a/crates/test-ui/ui/fn_ptr_reference_method2.rs b/crates/test-ui/ui/fn_ptr_reference_method2.rs index 3f1e00f68..bae53d029 100644 --- a/crates/test-ui/ui/fn_ptr_reference_method2.rs +++ b/crates/test-ui/ui/fn_ptr_reference_method2.rs @@ -1,8 +1,8 @@ //! Extra test for `fn_ptr_reference_method` //! (They fail at different compilation passes). -use objc2::{class, sel}; use objc2::declare::ClassBuilder; use objc2::runtime::{Object, Sel}; +use objc2::{class, sel}; extern "C" fn my_fn(_this: &Object, _cmd: Sel, _x: &Object) {} diff --git a/crates/test-ui/ui/invalid_option_encode_impl.rs b/crates/test-ui/ui/invalid_option_encode_impl.rs index 6d3b1a51e..8e5bbaf68 100644 --- a/crates/test-ui/ui/invalid_option_encode_impl.rs +++ b/crates/test-ui/ui/invalid_option_encode_impl.rs @@ -1,5 +1,5 @@ //! Ensure that implementing `OptionEncode` wrongly results in an error -use objc2::encode::{OptionEncode, Encode, RefEncode, Encoding}; +use objc2::encode::{Encode, Encoding, OptionEncode, RefEncode}; #[repr(transparent)] struct MyType(usize); diff --git a/crates/test-ui/ui/main_thread_only_not_allocable.rs b/crates/test-ui/ui/main_thread_only_not_allocable.rs new file mode 100644 index 000000000..2b67f131a --- /dev/null +++ b/crates/test-ui/ui/main_thread_only_not_allocable.rs @@ -0,0 +1,16 @@ +use objc2::runtime::NSObject; +use objc2::{declare_class, mutability, ClassType}; + +declare_class!( + struct MyMainThreadOnlyClass; + + unsafe impl ClassType for MyMainThreadOnlyClass { + type Super = NSObject; + type Mutability = mutability::MainThreadOnly; + const NAME: &'static str = "MyMainThreadOnlyClass"; + } +); + +fn main() { + let _ = MyMainThreadOnlyClass::alloc(); +} diff --git a/crates/test-ui/ui/main_thread_only_not_allocable.stderr b/crates/test-ui/ui/main_thread_only_not_allocable.stderr new file mode 100644 index 000000000..44a3ef4cb --- /dev/null +++ b/crates/test-ui/ui/main_thread_only_not_allocable.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `MainThreadOnly: mutability::private::MutabilityIsAllocableAnyThread` is not satisfied + --> ui/main_thread_only_not_allocable.rs + | + | let _ = MyMainThreadOnlyClass::alloc(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `mutability::private::MutabilityIsAllocableAnyThread` is not implemented for `MainThreadOnly` + | + = help: the following other types implement trait `mutability::private::MutabilityIsAllocableAnyThread`: + Immutable + ImmutableWithMutableSubclass + InteriorMutable + Mutable + MutableWithImmutableSuperclass + Root + = note: required for `MyMainThreadOnlyClass` to implement `IsAllocableAnyThread` +note: required by a bound in `objc2::ClassType::alloc` + --> $WORKSPACE/crates/objc2/src/class_type.rs + | + | Self: IsAllocableAnyThread, + | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassType::alloc` diff --git a/crates/test-ui/ui/msg_send_id_alloc_init_different.rs b/crates/test-ui/ui/msg_send_id_alloc_init_different.rs index 9e2572f10..0167514c0 100644 --- a/crates/test-ui/ui/msg_send_id_alloc_init_different.rs +++ b/crates/test-ui/ui/msg_send_id_alloc_init_different.rs @@ -1,7 +1,7 @@ //! Ensure that `init` returns the same type as given from `alloc`. -use objc2::{class, msg_send_id}; use objc2::rc::{Allocated, Id}; -use objc2::runtime::{Object, NSObject}; +use objc2::runtime::{NSObject, Object}; +use objc2::{class, msg_send_id}; fn main() { let cls = class!(NSObject); diff --git a/crates/test-ui/ui/msg_send_id_alloc_init_different.stderr b/crates/test-ui/ui/msg_send_id_alloc_init_different.stderr index bb2c68f9b..f3c135b10 100644 --- a/crates/test-ui/ui/msg_send_id_alloc_init_different.stderr +++ b/crates/test-ui/ui/msg_send_id_alloc_init_different.stderr @@ -1,11 +1,11 @@ -error[E0271]: type mismatch resolving ` as MaybeUnwrap>::Input == Id` +error[E0271]: type mismatch resolving ` as MaybeUnwrap>::Input == Id` --> ui/msg_send_id_alloc_init_different.rs | | let _: Id = unsafe { msg_send_id![obj, init] }; - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Id`, found `Id` + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Id`, found `Id` | - = note: expected struct `Id` - found struct `Id` + = note: expected struct `Id` + found struct `Id` note: required by a bound in `send_message_id` --> $WORKSPACE/crates/objc2/src/__macro_helpers/mod.rs | diff --git a/crates/test-ui/ui/msg_send_id_invalid_method.rs b/crates/test-ui/ui/msg_send_id_invalid_method.rs index c8c42e9ea..2b0e4187c 100644 --- a/crates/test-ui/ui/msg_send_id_invalid_method.rs +++ b/crates/test-ui/ui/msg_send_id_invalid_method.rs @@ -12,10 +12,4 @@ fn main() { unsafe { msg_send_id![object, release] }; unsafe { msg_send_id![object, autorelease] }; unsafe { msg_send_id![object, dealloc] }; - unsafe { - msg_send_id![ - object, - retain, - ] - }; } diff --git a/crates/test-ui/ui/msg_send_id_invalid_method.stderr b/crates/test-ui/ui/msg_send_id_invalid_method.stderr index 45180bafe..cdb4b6597 100644 --- a/crates/test-ui/ui/msg_send_id_invalid_method.stderr +++ b/crates/test-ui/ui/msg_send_id_invalid_method.stderr @@ -29,14 +29,3 @@ error: msg_send_id![obj, dealloc] is not supported. Drop an `Id` instead | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `$crate::__msg_send_id_helper` which comes from the expansion of the macro `msg_send_id` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: msg_send_id![obj, retain] is not supported. Use `Id::retain` instead - --> ui/msg_send_id_invalid_method.rs - | - | / msg_send_id![ - | | object, - | | retain, - | | ] - | |_________^ - | - = note: this error originates in the macro `$crate::__msg_send_id_helper` which comes from the expansion of the macro `msg_send_id` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/msg_send_id_invalid_receiver.stderr b/crates/test-ui/ui/msg_send_id_invalid_receiver.stderr index 31f3e7e98..20b1ab92b 100644 --- a/crates/test-ui/ui/msg_send_id_invalid_receiver.stderr +++ b/crates/test-ui/ui/msg_send_id_invalid_receiver.stderr @@ -93,9 +93,9 @@ error[E0277]: the trait bound `Id: MessageReceiver` is n | required by a bound introduced by this call | = help: the following other types implement trait `MessageReceiver`: - &'a Id - &'a mut Id - = note: required for `RetainSemantics<1>` to implement `MsgSendId, Id<_, _>>` + &'a Id + &'a mut Id + = note: required for `RetainSemantics<1>` to implement `MsgSendId, Id<_>>` error[E0277]: the trait bound `Id: MessageReceiver` is not satisfied --> ui/msg_send_id_invalid_receiver.rs @@ -107,6 +107,6 @@ error[E0277]: the trait bound `Id: MessageReceiver` is n | required by a bound introduced by this call | = help: the following other types implement trait `MessageReceiver`: - &'a Id - &'a mut Id - = note: required for `RetainSemantics<4>` to implement `MsgSendId, Id<_, _>>` + &'a Id + &'a mut Id + = note: required for `RetainSemantics<4>` to implement `MsgSendId, Id<_>>` diff --git a/crates/test-ui/ui/msg_send_id_invalid_return.rs b/crates/test-ui/ui/msg_send_id_invalid_return.rs index 26c90b7cb..e39aa9dee 100644 --- a/crates/test-ui/ui/msg_send_id_invalid_return.rs +++ b/crates/test-ui/ui/msg_send_id_invalid_return.rs @@ -1,7 +1,7 @@ //! Test compiler output with invalid msg_send_id return values. use objc2::msg_send_id; use objc2::rc::{Allocated, Id}; -use objc2::runtime::{Class, Object, NSObject}; +use objc2::runtime::{Class, NSObject, Object}; fn main() { let cls: &Class; diff --git a/crates/test-ui/ui/msg_send_id_invalid_return.stderr b/crates/test-ui/ui/msg_send_id_invalid_return.stderr index 80076e7d8..92b2d9c5b 100644 --- a/crates/test-ui/ui/msg_send_id_invalid_return.stderr +++ b/crates/test-ui/ui/msg_send_id_invalid_return.stderr @@ -6,9 +6,9 @@ error[E0277]: the trait bound `&objc2::runtime::Object: MaybeUnwrap` is not sati | = help: the following other types implement trait `MaybeUnwrap`: Allocated - Id + Id Option> - Option> + Option> note: required by a bound in `send_message_id` --> $WORKSPACE/crates/objc2/src/__macro_helpers/mod.rs | @@ -60,9 +60,9 @@ error[E0277]: the trait bound `&objc2::runtime::Object: MaybeUnwrap` is not sati | = help: the following other types implement trait `MaybeUnwrap`: Allocated - Id + Id Option> - Option> + Option> note: required by a bound in `send_message_id` --> $WORKSPACE/crates/objc2/src/__macro_helpers/mod.rs | @@ -126,9 +126,9 @@ error[E0277]: the trait bound `&objc2::runtime::Object: MaybeUnwrap` is not sati | = help: the following other types implement trait `MaybeUnwrap`: Allocated - Id + Id Option> - Option> + Option> note: required by a bound in `send_message_id` --> $WORKSPACE/crates/objc2/src/__macro_helpers/mod.rs | @@ -136,14 +136,14 @@ note: required by a bound in `send_message_id` | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `MsgSendId::send_message_id` = note: this error originates in the macro `msg_send_id` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0271]: type mismatch resolving ` as MaybeUnwrap>::Input == Id` +error[E0271]: type mismatch resolving ` as MaybeUnwrap>::Input == Id` --> ui/msg_send_id_invalid_return.rs | | let _: Id = unsafe { msg_send_id![obj, init] }; - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Id`, found `Id` + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Id`, found `Id` | - = note: expected struct `Id` - found struct `Id` + = note: expected struct `Id` + found struct `Id` note: required by a bound in `send_message_id` --> $WORKSPACE/crates/objc2/src/__macro_helpers/mod.rs | @@ -151,14 +151,14 @@ note: required by a bound in `send_message_id` | ^^^^^^^^^ required by this bound in `MsgSendId::send_message_id` = note: this error originates in the macro `msg_send_id` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0271]: type mismatch resolving ` as MaybeUnwrap>::Input == Id` +error[E0271]: type mismatch resolving ` as MaybeUnwrap>::Input == Id` --> ui/msg_send_id_invalid_return.rs | | let _: Id = unsafe { msg_send_id![obj, init] }; - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Id`, found `Id` + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Id`, found `Id` | - = note: expected struct `Id` - found struct `Id` + = note: expected struct `Id` + found struct `Id` note: required by a bound in `send_message_id` --> $WORKSPACE/crates/objc2/src/__macro_helpers/mod.rs | @@ -174,9 +174,9 @@ error[E0277]: the trait bound `&objc2::runtime::Object: MaybeUnwrap` is not sati | = help: the following other types implement trait `MaybeUnwrap`: Allocated - Id + Id Option> - Option> + Option> note: required by a bound in `send_message_id` --> $WORKSPACE/crates/objc2/src/__macro_helpers/mod.rs | @@ -192,9 +192,9 @@ error[E0277]: the trait bound `&objc2::runtime::Object: MaybeUnwrap` is not sati | = help: the following other types implement trait `MaybeUnwrap`: Allocated - Id + Id Option> - Option> + Option> note: required by a bound in `send_message_id` --> $WORKSPACE/crates/objc2/src/__macro_helpers/mod.rs | @@ -210,7 +210,7 @@ error[E0277]: the trait bound `Option<&objc2::runtime::Object>: MaybeUnwrap` is | = help: the following other types implement trait `MaybeUnwrap`: Option> - Option> + Option> note: required by a bound in `send_message_id` --> $WORKSPACE/crates/objc2/src/__macro_helpers/mod.rs | diff --git a/crates/test-ui/ui/msg_send_invalid_error.rs b/crates/test-ui/ui/msg_send_invalid_error.rs index d56c448ca..a352ee7af 100644 --- a/crates/test-ui/ui/msg_send_invalid_error.rs +++ b/crates/test-ui/ui/msg_send_invalid_error.rs @@ -1,7 +1,7 @@ //! Test that msg_send! error handling works correctly. -use objc2::{ClassType, msg_send, msg_send_id}; -use objc2::rc::Id; use icrate::Foundation::NSString; +use objc2::rc::Id; +use objc2::{msg_send, msg_send_id, ClassType}; fn main() { let obj: &NSString; diff --git a/crates/test-ui/ui/msg_send_invalid_error.stderr b/crates/test-ui/ui/msg_send_invalid_error.stderr index 2a3d3ecf7..7955ba58d 100644 --- a/crates/test-ui/ui/msg_send_invalid_error.stderr +++ b/crates/test-ui/ui/msg_send_invalid_error.stderr @@ -41,13 +41,13 @@ error[E0277]: the trait bound `i32: Message` is not satisfied | = help: the following other types implement trait `Message`: Exception + Foundation::generated::__NSString::NSMutableString NSAppleEventDescriptor - NSArray - NSDictionary - NSEnumerator + NSArray + NSDictionary + NSEnumerator NSError - NSHashTable - NSMapTable + NSHashTable and $N others note: required by a bound in `__send_message_error` --> $WORKSPACE/crates/objc2/src/message/mod.rs @@ -90,8 +90,8 @@ error[E0308]: mismatched types --> ui/msg_send_invalid_error.rs | | let _: () = unsafe { msg_send_id![obj, i: _] }; - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `Result, Id<_>>` + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `Result, Id<_>>` | = note: expected unit type `()` - found enum `Result, Id<_>>` + found enum `Result, Id<_>>` = note: this error originates in the macro `$crate::__msg_send_id_helper` which comes from the expansion of the macro `msg_send_id` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/msg_send_mutable.rs b/crates/test-ui/ui/msg_send_mutable.rs index a1a431089..17aa581b3 100644 --- a/crates/test-ui/ui/msg_send_mutable.rs +++ b/crates/test-ui/ui/msg_send_mutable.rs @@ -1,8 +1,8 @@ //! Test that `msg_send!` consumes their arguments, including the receiver. //! //! Ideally, it shouldn't be so, but that's how it works currently. -use objc2::{msg_send, class}; use objc2::runtime::Object; +use objc2::{class, msg_send}; fn main() { let cls = class!(NSObject); diff --git a/crates/test-ui/ui/msg_send_no_return_type.rs b/crates/test-ui/ui/msg_send_no_return_type.rs index 6fcf50218..bf2eb967a 100644 --- a/crates/test-ui/ui/msg_send_no_return_type.rs +++ b/crates/test-ui/ui/msg_send_no_return_type.rs @@ -3,8 +3,6 @@ use objc2::{class, msg_send}; fn main() { - unsafe { - let cls = class!(NSObject); - msg_send![cls, new]; - } + let cls = class!(NSObject); + unsafe { msg_send![cls, new] }; } diff --git a/crates/test-ui/ui/msg_send_no_return_type.stderr b/crates/test-ui/ui/msg_send_no_return_type.stderr index ba70bb7b4..418ff12a8 100644 --- a/crates/test-ui/ui/msg_send_no_return_type.stderr +++ b/crates/test-ui/ui/msg_send_no_return_type.stderr @@ -1,7 +1,7 @@ error[E0282]: type annotations needed --> ui/msg_send_no_return_type.rs | - | msg_send![cls, new]; - | ^^^^^^^^^^^^^^^^^^^ cannot infer type + | unsafe { msg_send![cls, new] }; + | ^^^^^^^^^^^^^^^^^^^ cannot infer type | = note: this error originates in the macro `$crate::__msg_send_helper` which comes from the expansion of the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/msg_send_only_message.stderr b/crates/test-ui/ui/msg_send_only_message.stderr index 8050359c4..b024fcd28 100644 --- a/crates/test-ui/ui/msg_send_only_message.stderr +++ b/crates/test-ui/ui/msg_send_only_message.stderr @@ -8,9 +8,9 @@ error[E0277]: the trait bound `{integer}: MessageReceiver` is not satisfied | required by a bound introduced by this call | = help: the following other types implement trait `MessageReceiver`: - &'a Id + &'a Id &'a T - &'a mut Id + &'a mut Id &'a mut T &'a objc2::runtime::Class *const T diff --git a/crates/test-ui/ui/msg_send_super_not_classtype.rs b/crates/test-ui/ui/msg_send_super_not_classtype.rs index 3705290d1..0940436ae 100644 --- a/crates/test-ui/ui/msg_send_super_not_classtype.rs +++ b/crates/test-ui/ui/msg_send_super_not_classtype.rs @@ -1,6 +1,6 @@ //! Invalid receiver to msg_send![super(obj), ...], missing ClassType impl. use objc2::msg_send; -use objc2::runtime::{Object, NSObject}; +use objc2::runtime::{NSObject, Object}; fn main() { let obj: &Object; diff --git a/crates/test-ui/ui/msg_send_underspecified_error.rs b/crates/test-ui/ui/msg_send_underspecified_error.rs index adf54f3a2..0d4ce0f6b 100644 --- a/crates/test-ui/ui/msg_send_underspecified_error.rs +++ b/crates/test-ui/ui/msg_send_underspecified_error.rs @@ -1,7 +1,7 @@ //! Test underspecified msg_send! errors. -use objc2::{msg_send, msg_send_id}; -use objc2::rc::Id; use icrate::Foundation::NSString; +use objc2::rc::Id; +use objc2::{msg_send, msg_send_id}; fn main() { let obj: &NSString; diff --git a/crates/test-ui/ui/msg_send_underspecified_error2.rs b/crates/test-ui/ui/msg_send_underspecified_error2.rs index 948ad4a96..b669b77b0 100644 --- a/crates/test-ui/ui/msg_send_underspecified_error2.rs +++ b/crates/test-ui/ui/msg_send_underspecified_error2.rs @@ -1,7 +1,7 @@ //! Test underspecified msg_send! errors. +use icrate::Foundation::{NSError, NSString}; use objc2::msg_send_id; use objc2::rc::Id; -use icrate::Foundation::{NSError, NSString}; fn main() { let obj: &NSString; diff --git a/crates/test-ui/ui/mutability_traits_unimplementable.rs b/crates/test-ui/ui/mutability_traits_unimplementable.rs new file mode 100644 index 000000000..8192b1091 --- /dev/null +++ b/crates/test-ui/ui/mutability_traits_unimplementable.rs @@ -0,0 +1,19 @@ +//! Check that the `mutability` traits are not implementable manually. +//! +//! Since they are not `unsafe`, it would be a soundness hole if you could. +use objc2::runtime::NSObject; +use objc2::{declare_class, mutability, ClassType}; + +declare_class!( + struct CustomObject; + + unsafe impl ClassType for CustomObject { + type Super = NSObject; + type Mutability = mutability::InteriorMutable; + const NAME: &'static str = "CustomObject"; + } +); + +impl mutability::IsMutable for CustomObject {} + +fn main() {} diff --git a/crates/test-ui/ui/mutability_traits_unimplementable.stderr b/crates/test-ui/ui/mutability_traits_unimplementable.stderr new file mode 100644 index 000000000..447783d75 --- /dev/null +++ b/crates/test-ui/ui/mutability_traits_unimplementable.stderr @@ -0,0 +1,9 @@ +error[E0119]: conflicting implementations of trait `IsMutable` for type `CustomObject` + --> ui/mutability_traits_unimplementable.rs + | + | impl mutability::IsMutable for CustomObject {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: conflicting implementation in crate `objc2`: + - impl IsMutable for T + where T: ClassType, ::Mutability: mutability::private::MutabilityIsMutable, T: ?Sized; diff --git a/crates/test-ui/ui/mutable_id_not_clone_not_retain.rs b/crates/test-ui/ui/mutable_id_not_clone_not_retain.rs new file mode 100644 index 000000000..b08dc1e1a --- /dev/null +++ b/crates/test-ui/ui/mutable_id_not_clone_not_retain.rs @@ -0,0 +1,19 @@ +use objc2::rc::Id; +use objc2::runtime::NSObject; +use objc2::{extern_class, mutability, msg_send_id, ClassType}; + +extern_class!( + struct NSMutableObject; + + unsafe impl ClassType for NSMutableObject { + type Super = NSObject; + type Mutability = mutability::Mutable; + const NAME: &'static str = "NSObject"; + } +); + +fn main() { + let obj: Id = unsafe { msg_send_id![NSMutableObject::class(), new] }; + let _ = obj.clone(); + let _ = obj.retain(); +} diff --git a/crates/test-ui/ui/mutable_id_not_clone_not_retain.stderr b/crates/test-ui/ui/mutable_id_not_clone_not_retain.stderr new file mode 100644 index 000000000..17d28f066 --- /dev/null +++ b/crates/test-ui/ui/mutable_id_not_clone_not_retain.stderr @@ -0,0 +1,45 @@ +error[E0599]: the method `clone` exists for struct `Id`, but its trait bounds were not satisfied + --> ui/mutable_id_not_clone_not_retain.rs + | + | / extern_class!( + | | struct NSMutableObject; + | | + | | unsafe impl ClassType for NSMutableObject { +... | + | | } + | | ); + | |_- doesn't satisfy `NSMutableObject: IsIdCloneable` +... + | let _ = obj.clone(); + | ^^^^^ method cannot be called on `Id` due to unsatisfied trait bounds + | + ::: $WORKSPACE/crates/objc2/src/rc/id.rs + | + | pub struct Id { + | ------------------------ doesn't satisfy `Id: Clone` + | + = note: the following trait bounds were not satisfied: + `NSMutableObject: IsIdCloneable` + which is required by `Id: Clone` +note: the trait `IsIdCloneable` must be implemented + --> $WORKSPACE/crates/objc2/src/mutability.rs + | + | pub trait IsIdCloneable: ClassType {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `Mutable: mutability::private::MutabilityIsRetainable` is not satisfied + --> ui/mutable_id_not_clone_not_retain.rs + | + | let _ = obj.retain(); + | ^^^^^^ the trait `mutability::private::MutabilityIsRetainable` is not implemented for `Mutable` + | + = help: the following other types implement trait `mutability::private::MutabilityIsRetainable`: + Immutable + InteriorMutable + MainThreadOnly + = note: required for `NSMutableObject` to implement `IsRetainable` +note: required by a bound in `retain` + --> $WORKSPACE/crates/objc2/src/class_type.rs + | + | Self: IsRetainable, + | ^^^^^^^^^^^^ required by this bound in `ClassType::retain` diff --git a/crates/test-ui/ui/not_encode.rs b/crates/test-ui/ui/not_encode.rs index a8dfe6894..c99b3bb57 100644 --- a/crates/test-ui/ui/not_encode.rs +++ b/crates/test-ui/ui/not_encode.rs @@ -2,9 +2,9 @@ use core::cell::{Cell, UnsafeCell}; use core::ffi::c_void; +use block2::Block; use objc2::encode::Encode; use objc2::runtime::Sel; -use block2::Block; fn is_encode() {} diff --git a/crates/test-ui/ui/not_writeback.rs b/crates/test-ui/ui/not_writeback.rs index d9bc04dae..2a3b00bd8 100644 --- a/crates/test-ui/ui/not_writeback.rs +++ b/crates/test-ui/ui/not_writeback.rs @@ -1,19 +1,19 @@ -use objc2::rc::{Id, Owned}; -use objc2::runtime::NSObject; use objc2::msg_send; +use objc2::rc::Id; +use objc2::runtime::NSObject; fn main() { let obj = &NSObject::new(); let _: &mut Id = unsafe { msg_send![obj, a] }; - let param: Id = NSObject::new(); + let param: Id = NSObject::new(); let _: () = unsafe { msg_send![obj, a: param] }; - let param: Id = NSObject::new(); + let param: Id = NSObject::new(); let _: () = unsafe { msg_send![obj, a: ¶m] }; - let param: Id = NSObject::new(); + let param: Id = NSObject::new(); let _: () = unsafe { msg_send![obj, a: Some(¶m)] }; let param: *mut Id = std::ptr::null_mut(); @@ -21,6 +21,6 @@ fn main() { let mut param = NSObject::new(); let mut param = &mut param; - let param: &mut &mut Id = &mut param; + let param: &mut &mut Id = &mut param; let _: () = unsafe { msg_send![obj, a: param] }; } diff --git a/crates/test-ui/ui/not_writeback.stderr b/crates/test-ui/ui/not_writeback.stderr index 9707ec04e..ed6cdae07 100644 --- a/crates/test-ui/ui/not_writeback.stderr +++ b/crates/test-ui/ui/not_writeback.stderr @@ -24,13 +24,13 @@ note: required by a bound in `send_message` | ^^^^^^^^^^^^^^^^^^^ required by this bound in `MessageReceiver::send_message` = note: this error originates in the macro `$crate::__msg_send_helper` which comes from the expansion of the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `Id: Encode` is not satisfied +error[E0277]: the trait bound `Id: Encode` is not satisfied --> ui/not_writeback.rs | | let _: () = unsafe { msg_send![obj, a: param] }; | ^^^^^^^^^^^^^^^^^^^^^^^^ | | - | the trait `Encode` is not implemented for `Id` + | the trait `Encode` is not implemented for `Id` | required by a bound introduced by this call | = help: the following other types implement trait `Encode`: @@ -43,8 +43,8 @@ error[E0277]: the trait bound `Id: Encode` is not sa AtomicI16 AtomicI32 and $N others - = note: required for `Id` to implement `EncodeConvertArgument` - = note: required for `(Id,)` to implement `MessageArguments` + = note: required for `Id` to implement `EncodeConvertArgument` + = note: required for `(Id,)` to implement `MessageArguments` note: required by a bound in `send_message` --> $WORKSPACE/crates/objc2/src/message/mod.rs | @@ -52,13 +52,13 @@ note: required by a bound in `send_message` | ^^^^^^^^^^^^^^^^ required by this bound in `MessageReceiver::send_message` = note: this error originates in the macro `$crate::__msg_send_helper` which comes from the expansion of the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `Id: RefEncode` is not satisfied +error[E0277]: the trait bound `Id: RefEncode` is not satisfied --> ui/not_writeback.rs | | let _: () = unsafe { msg_send![obj, a: ¶m] }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | the trait `RefEncode` is not implemented for `Id` + | the trait `RefEncode` is not implemented for `Id` | required by a bound introduced by this call | = help: the following other types implement trait `RefEncode`: @@ -71,9 +71,9 @@ error[E0277]: the trait bound `Id: RefEncode` is not AtomicI16 AtomicI32 and $N others - = note: required for `&Id` to implement `Encode` - = note: required for `&Id` to implement `EncodeConvertArgument` - = note: required for `(&Id,)` to implement `MessageArguments` + = note: required for `&Id` to implement `Encode` + = note: required for `&Id` to implement `EncodeConvertArgument` + = note: required for `(&Id,)` to implement `MessageArguments` note: required by a bound in `send_message` --> $WORKSPACE/crates/objc2/src/message/mod.rs | @@ -81,13 +81,13 @@ note: required by a bound in `send_message` | ^^^^^^^^^^^^^^^^ required by this bound in `MessageReceiver::send_message` = note: this error originates in the macro `$crate::__msg_send_helper` which comes from the expansion of the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `Id: RefEncode` is not satisfied +error[E0277]: the trait bound `Id: RefEncode` is not satisfied --> ui/not_writeback.rs | | let _: () = unsafe { msg_send![obj, a: Some(¶m)] }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | the trait `RefEncode` is not implemented for `Id` + | the trait `RefEncode` is not implemented for `Id` | required by a bound introduced by this call | = help: the following other types implement trait `RefEncode`: @@ -100,11 +100,11 @@ error[E0277]: the trait bound `Id: RefEncode` is not AtomicI16 AtomicI32 and $N others - = note: required for `&Id` to implement `Encode` + = note: required for `&Id` to implement `Encode` = note: 1 redundant requirement hidden - = note: required for `Option<&Id>` to implement `Encode` - = note: required for `Option<&Id>` to implement `EncodeConvertArgument` - = note: required for `(Option<&Id>,)` to implement `MessageArguments` + = note: required for `Option<&Id>` to implement `Encode` + = note: required for `Option<&Id>` to implement `EncodeConvertArgument` + = note: required for `(Option<&Id>,)` to implement `MessageArguments` note: required by a bound in `send_message` --> $WORKSPACE/crates/objc2/src/message/mod.rs | @@ -141,13 +141,13 @@ note: required by a bound in `send_message` | ^^^^^^^^^^^^^^^^ required by this bound in `MessageReceiver::send_message` = note: this error originates in the macro `$crate::__msg_send_helper` which comes from the expansion of the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `Id: RefEncode` is not satisfied +error[E0277]: the trait bound `Id: RefEncode` is not satisfied --> ui/not_writeback.rs | | let _: () = unsafe { msg_send![obj, a: param] }; | ^^^^^^^^^^^^^^^^^^^^^^^^ | | - | the trait `RefEncode` is not implemented for `Id` + | the trait `RefEncode` is not implemented for `Id` | required by a bound introduced by this call | = help: the following other types implement trait `RefEncode`: @@ -160,10 +160,10 @@ error[E0277]: the trait bound `Id: RefEncode` is not AtomicI16 AtomicI32 and $N others - = note: required for `&mut Id` to implement `RefEncode` - = note: required for `&mut &mut Id` to implement `Encode` - = note: required for `&mut &mut Id` to implement `EncodeConvertArgument` - = note: required for `(&mut &mut Id,)` to implement `MessageArguments` + = note: required for `&mut Id` to implement `RefEncode` + = note: required for `&mut &mut Id` to implement `Encode` + = note: required for `&mut &mut Id` to implement `EncodeConvertArgument` + = note: required for `(&mut &mut Id,)` to implement `MessageArguments` note: required by a bound in `send_message` --> $WORKSPACE/crates/objc2/src/message/mod.rs | diff --git a/crates/test-ui/ui/ns_string_output_not_const.rs b/crates/test-ui/ui/ns_string_output_not_const.rs index 597ee36b6..9ad16b88b 100644 --- a/crates/test-ui/ui/ns_string_output_not_const.rs +++ b/crates/test-ui/ui/ns_string_output_not_const.rs @@ -1,5 +1,5 @@ -use icrate::Foundation::NSString; use icrate::ns_string; +use icrate::Foundation::NSString; fn main() { static STRING: &NSString = ns_string!("abc"); diff --git a/crates/test-ui/ui/nsarray_bound_not_send_sync.rs b/crates/test-ui/ui/nsarray_bound_not_send_sync.rs index 21712012d..d7ef28fb9 100644 --- a/crates/test-ui/ui/nsarray_bound_not_send_sync.rs +++ b/crates/test-ui/ui/nsarray_bound_not_send_sync.rs @@ -1,10 +1,11 @@ -use icrate::Foundation::NSArray; -use objc2::runtime::Object; +use icrate::Foundation::{NSArray, NSMutableArray, NSObject}; fn needs_sync() {} fn needs_send() {} fn main() { - needs_sync::>(); - needs_send::>(); + needs_sync::>(); + needs_send::>(); + needs_sync::>(); + needs_send::>(); } diff --git a/crates/test-ui/ui/nsarray_bound_not_send_sync.stderr b/crates/test-ui/ui/nsarray_bound_not_send_sync.stderr index 504509051..86878c9b3 100644 --- a/crates/test-ui/ui/nsarray_bound_not_send_sync.stderr +++ b/crates/test-ui/ui/nsarray_bound_not_send_sync.stderr @@ -1,32 +1,90 @@ +error[E0277]: `*const NSObject` cannot be shared between threads safely + --> ui/nsarray_bound_not_send_sync.rs + | + | needs_sync::>(); + | ^^^^^^^^^^^^^^^^^ `*const NSObject` cannot be shared between threads safely + | + = help: within `icrate::objc2::rc::id::private::UnknownStorage`, the trait `Sync` is not implemented for `*const NSObject` + = note: required because it appears within the type `UnknownStorage` + = note: required for `Id` to implement `Sync` + = note: required because it appears within the type `PhantomData>` + = note: required because it appears within the type `NSArray` +note: required by a bound in `needs_sync` + --> ui/nsarray_bound_not_send_sync.rs + | + | fn needs_sync() {} + | ^^^^ required by this bound in `needs_sync` + error[E0277]: `UnsafeCell, PhantomPinned)>>` cannot be shared between threads safely --> ui/nsarray_bound_not_send_sync.rs | - | needs_sync::>(); - | ^^^^^^^^^^^^^^^ `UnsafeCell, PhantomPinned)>>` cannot be shared between threads safely + | needs_sync::>(); + | ^^^^^^^^^^^^^^^^^ `UnsafeCell, PhantomPinned)>>` cannot be shared between threads safely | - = help: within `objc2::runtime::Object`, the trait `Sync` is not implemented for `UnsafeCell, PhantomPinned)>>` + = help: within `NSObject`, the trait `Sync` is not implemented for `UnsafeCell, PhantomPinned)>>` = note: required because it appears within the type `objc_object` = note: required because it appears within the type `Object` - = note: required for `NSArray` to implement `Sync` + = note: required because it appears within the type `NSObject` + = note: required for `Id` to implement `Sync` + = note: required because it appears within the type `PhantomData>` + = note: required because it appears within the type `NSArray` note: required by a bound in `needs_sync` --> ui/nsarray_bound_not_send_sync.rs | | fn needs_sync() {} | ^^^^ required by this bound in `needs_sync` +error[E0277]: `*const NSObject` cannot be sent between threads safely + --> ui/nsarray_bound_not_send_sync.rs + | + | needs_send::>(); + | ^^^^^^^^^^^^^^^^^ `*const NSObject` cannot be sent between threads safely + | + = help: within `icrate::objc2::rc::id::private::UnknownStorage`, the trait `Send` is not implemented for `*const NSObject` + = note: required because it appears within the type `UnknownStorage` + = note: required for `Id` to implement `Send` + = note: required because it appears within the type `PhantomData>` + = note: required because it appears within the type `NSArray` +note: required by a bound in `needs_send` + --> ui/nsarray_bound_not_send_sync.rs + | + | fn needs_send() {} + | ^^^^ required by this bound in `needs_send` + error[E0277]: `*const UnsafeCell<()>` cannot be sent between threads safely --> ui/nsarray_bound_not_send_sync.rs | - | needs_sync::>(); - | ^^^^^^^^^^^^^^^ `*const UnsafeCell<()>` cannot be sent between threads safely + | needs_send::>(); + | ^^^^^^^^^^^^^^^^^ `*const UnsafeCell<()>` cannot be sent between threads safely | - = help: within `objc2::runtime::Object`, the trait `Send` is not implemented for `*const UnsafeCell<()>` + = help: within `NSObject`, the trait `Send` is not implemented for `*const UnsafeCell<()>` = note: required because it appears within the type `(*const UnsafeCell<()>, PhantomPinned)` = note: required because it appears within the type `PhantomData<(*const UnsafeCell<()>, PhantomPinned)>` = note: required because it appears within the type `UnsafeCell, PhantomPinned)>>` = note: required because it appears within the type `objc_object` = note: required because it appears within the type `Object` - = note: required for `NSArray` to implement `Sync` + = note: required because it appears within the type `NSObject` + = note: required for `Id` to implement `Send` + = note: required because it appears within the type `PhantomData>` + = note: required because it appears within the type `NSArray` +note: required by a bound in `needs_send` + --> ui/nsarray_bound_not_send_sync.rs + | + | fn needs_send() {} + | ^^^^ required by this bound in `needs_send` + +error[E0277]: `*const NSObject` cannot be shared between threads safely + --> ui/nsarray_bound_not_send_sync.rs + | + | needs_sync::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ `*const NSObject` cannot be shared between threads safely + | + = help: within `icrate::objc2::rc::id::private::UnknownStorage`, the trait `Sync` is not implemented for `*const NSObject` + = note: required because it appears within the type `UnknownStorage` + = note: required for `Id` to implement `Sync` + = note: required because it appears within the type `PhantomData>` + = note: required because it appears within the type `NSArray` + = note: required because it appears within the type `NSMutableArray` note: required by a bound in `needs_sync` --> ui/nsarray_bound_not_send_sync.rs | @@ -36,13 +94,35 @@ note: required by a bound in `needs_sync` error[E0277]: `UnsafeCell, PhantomPinned)>>` cannot be shared between threads safely --> ui/nsarray_bound_not_send_sync.rs | - | needs_send::>(); - | ^^^^^^^^^^^^^^^ `UnsafeCell, PhantomPinned)>>` cannot be shared between threads safely + | needs_sync::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell, PhantomPinned)>>` cannot be shared between threads safely | - = help: within `objc2::runtime::Object`, the trait `Sync` is not implemented for `UnsafeCell, PhantomPinned)>>` + = help: within `NSObject`, the trait `Sync` is not implemented for `UnsafeCell, PhantomPinned)>>` = note: required because it appears within the type `objc_object` = note: required because it appears within the type `Object` - = note: required for `NSArray` to implement `Send` + = note: required because it appears within the type `NSObject` + = note: required for `Id` to implement `Sync` + = note: required because it appears within the type `PhantomData>` + = note: required because it appears within the type `NSArray` + = note: required because it appears within the type `NSMutableArray` +note: required by a bound in `needs_sync` + --> ui/nsarray_bound_not_send_sync.rs + | + | fn needs_sync() {} + | ^^^^ required by this bound in `needs_sync` + +error[E0277]: `*const NSObject` cannot be sent between threads safely + --> ui/nsarray_bound_not_send_sync.rs + | + | needs_send::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ `*const NSObject` cannot be sent between threads safely + | + = help: within `icrate::objc2::rc::id::private::UnknownStorage`, the trait `Send` is not implemented for `*const NSObject` + = note: required because it appears within the type `UnknownStorage` + = note: required for `Id` to implement `Send` + = note: required because it appears within the type `PhantomData>` + = note: required because it appears within the type `NSArray` + = note: required because it appears within the type `NSMutableArray` note: required by a bound in `needs_send` --> ui/nsarray_bound_not_send_sync.rs | @@ -52,16 +132,20 @@ note: required by a bound in `needs_send` error[E0277]: `*const UnsafeCell<()>` cannot be sent between threads safely --> ui/nsarray_bound_not_send_sync.rs | - | needs_send::>(); - | ^^^^^^^^^^^^^^^ `*const UnsafeCell<()>` cannot be sent between threads safely + | needs_send::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ `*const UnsafeCell<()>` cannot be sent between threads safely | - = help: within `objc2::runtime::Object`, the trait `Send` is not implemented for `*const UnsafeCell<()>` + = help: within `NSObject`, the trait `Send` is not implemented for `*const UnsafeCell<()>` = note: required because it appears within the type `(*const UnsafeCell<()>, PhantomPinned)` = note: required because it appears within the type `PhantomData<(*const UnsafeCell<()>, PhantomPinned)>` = note: required because it appears within the type `UnsafeCell, PhantomPinned)>>` = note: required because it appears within the type `objc_object` = note: required because it appears within the type `Object` - = note: required for `NSArray` to implement `Send` + = note: required because it appears within the type `NSObject` + = note: required for `Id` to implement `Send` + = note: required because it appears within the type `PhantomData>` + = note: required because it appears within the type `NSArray` + = note: required because it appears within the type `NSMutableArray` note: required by a bound in `needs_send` --> ui/nsarray_bound_not_send_sync.rs | diff --git a/crates/test-ui/ui/nsobject_not_derefmut.rs b/crates/test-ui/ui/nsobject_not_derefmut.rs new file mode 100644 index 000000000..272fc3e61 --- /dev/null +++ b/crates/test-ui/ui/nsobject_not_derefmut.rs @@ -0,0 +1,7 @@ +use objc2::runtime::NSObject; + +fn main() { + #[allow(unused_mut)] + let mut obj = NSObject::new(); + let _: &mut NSObject = &mut *obj; +} diff --git a/crates/test-ui/ui/nsobject_not_derefmut.stderr b/crates/test-ui/ui/nsobject_not_derefmut.stderr new file mode 100644 index 000000000..f08e3b277 --- /dev/null +++ b/crates/test-ui/ui/nsobject_not_derefmut.stderr @@ -0,0 +1,7 @@ +error[E0596]: cannot borrow data in dereference of `Id` as mutable + --> ui/nsobject_not_derefmut.rs + | + | let _: &mut NSObject = &mut *obj; + | ^^^^^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Id` diff --git a/crates/test-ui/ui/nsobject_not_mutable.rs b/crates/test-ui/ui/nsobject_not_mutable.rs new file mode 100644 index 000000000..7c1247fab --- /dev/null +++ b/crates/test-ui/ui/nsobject_not_mutable.rs @@ -0,0 +1,10 @@ +use objc2::rc::{autoreleasepool, Id}; +use objc2::runtime::NSObject; + +fn main() { + let mut obj = NSObject::new(); + let mut_ptr = Id::as_mut_ptr(&mut obj); + autoreleasepool(|pool| { + let mut_ref = Id::autorelease_mut(obj, pool); + }); +} diff --git a/crates/test-ui/ui/nsobject_not_mutable.stderr b/crates/test-ui/ui/nsobject_not_mutable.stderr new file mode 100644 index 000000000..fcdf3fdb1 --- /dev/null +++ b/crates/test-ui/ui/nsobject_not_mutable.stderr @@ -0,0 +1,35 @@ +error[E0277]: the trait bound `Root: mutability::private::MutabilityIsMutable` is not satisfied + --> ui/nsobject_not_mutable.rs + | + | let mut_ptr = Id::as_mut_ptr(&mut obj); + | -------------- ^^^^^^^^ the trait `mutability::private::MutabilityIsMutable` is not implemented for `Root` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `mutability::private::MutabilityIsMutable`: + Mutable + MutableWithImmutableSuperclass + = note: required for `NSObject` to implement `IsMutable` +note: required by a bound in `Id::::as_mut_ptr` + --> $WORKSPACE/crates/objc2/src/rc/id.rs + | + | T: IsMutable, + | ^^^^^^^^^ required by this bound in `Id::::as_mut_ptr` + +error[E0277]: the trait bound `Root: mutability::private::MutabilityIsMutable` is not satisfied + --> ui/nsobject_not_mutable.rs + | + | let mut_ref = Id::autorelease_mut(obj, pool); + | ------------------- ^^^ the trait `mutability::private::MutabilityIsMutable` is not implemented for `Root` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `mutability::private::MutabilityIsMutable`: + Mutable + MutableWithImmutableSuperclass + = note: required for `NSObject` to implement `IsMutable` +note: required by a bound in `Id::::autorelease_mut` + --> $WORKSPACE/crates/objc2/src/rc/id.rs + | + | T: IsMutable, + | ^^^^^^^^^ required by this bound in `Id::::autorelease_mut` diff --git a/crates/test-ui/ui/nsstring_as_str_use_after_release.rs b/crates/test-ui/ui/nsstring_as_str_use_after_release.rs index 42432d1f4..ca1af26fa 100644 --- a/crates/test-ui/ui/nsstring_as_str_use_after_release.rs +++ b/crates/test-ui/ui/nsstring_as_str_use_after_release.rs @@ -1,5 +1,4 @@ //! Test that the lifetime of `NSString::as_str` is bound to the string. - use icrate::Foundation::NSString; use objc2::rc::autoreleasepool; diff --git a/crates/test-ui/ui/nsstring_as_str_use_outside_pool.rs b/crates/test-ui/ui/nsstring_as_str_use_outside_pool.rs index 73de0d680..e9f6a1afe 100644 --- a/crates/test-ui/ui/nsstring_as_str_use_outside_pool.rs +++ b/crates/test-ui/ui/nsstring_as_str_use_outside_pool.rs @@ -1,5 +1,4 @@ //! Test that the lifetime of `NSString::as_str` is bound to the pool. - use icrate::Foundation::NSString; use objc2::rc::autoreleasepool; diff --git a/crates/test-ui/ui/object_not_send_sync.rs b/crates/test-ui/ui/object_not_send_sync.rs index d145be695..bf004c3d4 100644 --- a/crates/test-ui/ui/object_not_send_sync.rs +++ b/crates/test-ui/ui/object_not_send_sync.rs @@ -3,9 +3,8 @@ //! //! Also test that `NSValue` is not Send nor Sync, because its contained value //! might not be. - use icrate::Foundation::NSValue; -use objc2::runtime::{Object, NSObject}; +use objc2::runtime::{NSObject, Object}; fn needs_sync() {} fn needs_send() {} diff --git a/crates/test-ui/ui/protocol.rs b/crates/test-ui/ui/protocol.rs deleted file mode 100644 index 984a2d439..000000000 --- a/crates/test-ui/ui/protocol.rs +++ /dev/null @@ -1,83 +0,0 @@ -//! The negative cases of protocol.rs `impl_traits` -use objc2::{ClassType, declare_class, extern_protocol, ProtocolType}; -use objc2::runtime::{NSObjectProtocol, NSObject, ProtocolObject}; - -extern_protocol!( - unsafe trait Foo {} - - unsafe impl ProtocolType for dyn Foo {} -); - -extern_protocol!( - unsafe trait Bar: NSObjectProtocol {} - - unsafe impl ProtocolType for dyn Bar {} -); - -extern_protocol!( - unsafe trait FooBar: Foo + Bar {} - - unsafe impl ProtocolType for dyn FooBar {} -); - -extern_protocol!( - unsafe trait FooFooBar: Foo + FooBar {} - - unsafe impl ProtocolType for dyn FooFooBar {} -); - -declare_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - struct DummyClass; - - unsafe impl ClassType for DummyClass { - type Super = NSObject; - const NAME: &'static str = "DummyClass"; - } -); - -unsafe impl NSObjectProtocol for DummyClass {} -unsafe impl Foo for DummyClass {} -unsafe impl Bar for DummyClass {} -unsafe impl FooBar for DummyClass {} -// unsafe impl FooFooBar for DummyClass {} - -fn main() { - fn impl_nsobject() {} - fn impl_foo() {} - fn impl_bar() {} - fn impl_foobar() {} - fn impl_foofoobar() {} - - impl_nsobject::(); - impl_nsobject::>(); - impl_nsobject::>(); - impl_nsobject::>(); - impl_nsobject::>(); - impl_nsobject::>(); - impl_nsobject::(); - - impl_foo::>(); - impl_foo::>(); - impl_foo::>(); - impl_foo::>(); - impl_foo::(); - - impl_bar::>(); - impl_bar::>(); - impl_bar::>(); - impl_bar::>(); - impl_bar::(); - - impl_foobar::>(); - impl_foobar::>(); - impl_foobar::>(); - impl_foobar::>(); - impl_foobar::(); - - impl_foofoobar::>(); - impl_foofoobar::>(); - impl_foofoobar::>(); - impl_foofoobar::>(); - impl_foofoobar::(); -} diff --git a/crates/test-ui/ui/protocol.stderr b/crates/test-ui/ui/protocol.stderr deleted file mode 100644 index 8504c9a80..000000000 --- a/crates/test-ui/ui/protocol.stderr +++ /dev/null @@ -1,199 +0,0 @@ -error[E0277]: the trait bound `dyn Foo: NSObjectProtocol` is not satisfied - --> ui/protocol.rs - | - | impl_nsobject::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `NSObjectProtocol` is not implemented for `dyn Foo` - | - = help: the following other types implement trait `NSObjectProtocol`: - DummyClass - NSObject - ProtocolObject - __NSProxy - = note: required for `ProtocolObject` to implement `NSObjectProtocol` -note: required by a bound in `impl_nsobject` - --> ui/protocol.rs - | - | fn impl_nsobject() {} - | ^^^^^^^^^^^^^^^^ required by this bound in `impl_nsobject` - -error[E0277]: the trait bound `dyn Bar: Foo` is not satisfied - --> ui/protocol.rs - | - | impl_foo::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `dyn Bar` - | - = help: the following other types implement trait `Foo`: - DummyClass - ProtocolObject -note: required for `ProtocolObject` to implement `Foo` - --> ui/protocol.rs - | - | / extern_protocol!( - | | unsafe trait Foo {} - | | - | | unsafe impl ProtocolType for dyn Foo {} - | | ); - | |_^ unsatisfied trait bound introduced here -note: required by a bound in `impl_foo` - --> ui/protocol.rs - | - | fn impl_foo() {} - | ^^^ required by this bound in `impl_foo` - = note: this error originates in the macro `extern_protocol` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `dyn Foo: Bar` is not satisfied - --> ui/protocol.rs - | - | impl_bar::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `dyn Foo` - | - = help: the following other types implement trait `Bar`: - DummyClass - ProtocolObject -note: required for `ProtocolObject` to implement `Bar` - --> ui/protocol.rs - | - | / extern_protocol!( - | | unsafe trait Bar: NSObjectProtocol {} - | | - | | unsafe impl ProtocolType for dyn Bar {} - | | ); - | |_^ unsatisfied trait bound introduced here -note: required by a bound in `impl_bar` - --> ui/protocol.rs - | - | fn impl_bar() {} - | ^^^ required by this bound in `impl_bar` - = note: this error originates in the macro `extern_protocol` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `dyn Foo: FooBar` is not satisfied - --> ui/protocol.rs - | - | impl_foobar::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `FooBar` is not implemented for `dyn Foo` - | - = help: the following other types implement trait `FooBar`: - DummyClass - ProtocolObject -note: required for `ProtocolObject` to implement `FooBar` - --> ui/protocol.rs - | - | / extern_protocol!( - | | unsafe trait FooBar: Foo + Bar {} - | | - | | unsafe impl ProtocolType for dyn FooBar {} - | | ); - | |_^ unsatisfied trait bound introduced here -note: required by a bound in `impl_foobar` - --> ui/protocol.rs - | - | fn impl_foobar() {} - | ^^^^^^ required by this bound in `impl_foobar` - = note: this error originates in the macro `extern_protocol` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `dyn Bar: FooBar` is not satisfied - --> ui/protocol.rs - | - | impl_foobar::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `FooBar` is not implemented for `dyn Bar` - | - = help: the following other types implement trait `FooBar`: - DummyClass - ProtocolObject -note: required for `ProtocolObject` to implement `FooBar` - --> ui/protocol.rs - | - | / extern_protocol!( - | | unsafe trait FooBar: Foo + Bar {} - | | - | | unsafe impl ProtocolType for dyn FooBar {} - | | ); - | |_^ unsatisfied trait bound introduced here -note: required by a bound in `impl_foobar` - --> ui/protocol.rs - | - | fn impl_foobar() {} - | ^^^^^^ required by this bound in `impl_foobar` - = note: this error originates in the macro `extern_protocol` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `dyn Foo: FooFooBar` is not satisfied - --> ui/protocol.rs - | - | impl_foofoobar::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `FooFooBar` is not implemented for `dyn Foo` - | - = help: the trait `FooFooBar` is implemented for `ProtocolObject` -note: required for `ProtocolObject` to implement `FooFooBar` - --> ui/protocol.rs - | - | / extern_protocol!( - | | unsafe trait FooFooBar: Foo + FooBar {} - | | - | | unsafe impl ProtocolType for dyn FooFooBar {} - | | ); - | |_^ unsatisfied trait bound introduced here -note: required by a bound in `impl_foofoobar` - --> ui/protocol.rs - | - | fn impl_foofoobar() {} - | ^^^^^^^^^ required by this bound in `impl_foofoobar` - = note: this error originates in the macro `extern_protocol` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `dyn Bar: FooFooBar` is not satisfied - --> ui/protocol.rs - | - | impl_foofoobar::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `FooFooBar` is not implemented for `dyn Bar` - | - = help: the trait `FooFooBar` is implemented for `ProtocolObject` -note: required for `ProtocolObject` to implement `FooFooBar` - --> ui/protocol.rs - | - | / extern_protocol!( - | | unsafe trait FooFooBar: Foo + FooBar {} - | | - | | unsafe impl ProtocolType for dyn FooFooBar {} - | | ); - | |_^ unsatisfied trait bound introduced here -note: required by a bound in `impl_foofoobar` - --> ui/protocol.rs - | - | fn impl_foofoobar() {} - | ^^^^^^^^^ required by this bound in `impl_foofoobar` - = note: this error originates in the macro `extern_protocol` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `dyn FooBar: FooFooBar` is not satisfied - --> ui/protocol.rs - | - | impl_foofoobar::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FooFooBar` is not implemented for `dyn FooBar` - | - = help: the trait `FooFooBar` is implemented for `ProtocolObject` -note: required for `ProtocolObject` to implement `FooFooBar` - --> ui/protocol.rs - | - | / extern_protocol!( - | | unsafe trait FooFooBar: Foo + FooBar {} - | | - | | unsafe impl ProtocolType for dyn FooFooBar {} - | | ); - | |_^ unsatisfied trait bound introduced here -note: required by a bound in `impl_foofoobar` - --> ui/protocol.rs - | - | fn impl_foofoobar() {} - | ^^^^^^^^^ required by this bound in `impl_foofoobar` - = note: this error originates in the macro `extern_protocol` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `DummyClass: FooFooBar` is not satisfied - --> ui/protocol.rs - | - | impl_foofoobar::(); - | ^^^^^^^^^^ the trait `FooFooBar` is not implemented for `DummyClass` - | - = help: the trait `FooFooBar` is implemented for `ProtocolObject` -note: required by a bound in `impl_foofoobar` - --> ui/protocol.rs - | - | fn impl_foofoobar() {} - | ^^^^^^^^^ required by this bound in `impl_foofoobar` diff --git a/crates/test-ui/ui/wrong_optional.rs b/crates/test-ui/ui/wrong_optional.rs index a766ae352..78203d187 100644 --- a/crates/test-ui/ui/wrong_optional.rs +++ b/crates/test-ui/ui/wrong_optional.rs @@ -1,12 +1,13 @@ -use objc2::{extern_class, extern_methods, declare_class, ClassType}; -use objc2::runtime::NSObject; use objc2::rc::Id; +use objc2::runtime::NSObject; +use objc2::{declare_class, extern_class, extern_methods, mutability, ClassType}; extern_class!( pub struct MyObject; unsafe impl ClassType for MyObject { type Super = NSObject; + type Mutability = mutability::InteriorMutable; } ); @@ -33,6 +34,7 @@ declare_class!( unsafe impl ClassType for CustomObject1 { type Super = NSObject; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "CustomObject1"; } @@ -49,6 +51,7 @@ declare_class!( unsafe impl ClassType for CustomObject2 { type Super = NSObject; + type Mutability = mutability::InteriorMutable; const NAME: &'static str = "CustomObject2"; } diff --git a/crates/tests/src/test_object.rs b/crates/tests/src/test_object.rs index 6db85c189..f7fd95295 100644 --- a/crates/tests/src/test_object.rs +++ b/crates/tests/src/test_object.rs @@ -1,14 +1,17 @@ +#![allow(clippy::missing_safety_doc)] use core::mem::{size_of, ManuallyDrop}; use std::os::raw::c_int; #[cfg(feature = "Foundation_all")] use icrate::Foundation::NSNumber; use objc2::encode::{Encoding, RefEncode}; -use objc2::rc::{autoreleasepool, AutoreleasePool, Id, Owned}; +use objc2::rc::{autoreleasepool, AutoreleasePool, Id}; use objc2::runtime::{Bool, Class, NSObject, NSObjectProtocol, Object, Protocol, ProtocolObject}; #[cfg(feature = "malloc")] use objc2::sel; -use objc2::{class, extern_protocol, msg_send, msg_send_id, ClassType, Message, ProtocolType}; +use objc2::{ + class, extern_protocol, msg_send, msg_send_id, mutability, ClassType, Message, ProtocolType, +}; extern_protocol!( unsafe trait MyTestProtocol: NSObjectProtocol { @@ -78,14 +81,15 @@ struct MyTestObject { inner: NSObject, } -unsafe impl Message for MyTestObject {} - unsafe impl RefEncode for MyTestObject { const ENCODING_REF: Encoding = Encoding::Object; } +unsafe impl Message for MyTestObject {} + unsafe impl ClassType for MyTestObject { type Super = NSObject; + type Mutability = mutability::Mutable; const NAME: &'static str = "MyTestObject"; fn class() -> &'static Class { @@ -105,18 +109,19 @@ unsafe impl NSObjectProtocol for MyTestObject {} unsafe impl MyTestProtocol for MyTestObject {} impl MyTestObject { - fn new() -> Id { + fn new() -> Id { let cls = Self::class(); unsafe { msg_send_id![cls, new] } } + #[allow(clippy::needless_lifetimes)] fn new_autoreleased<'p>(pool: AutoreleasePool<'p>) -> &'p Self { let cls = Self::class(); let ptr: *const Self = unsafe { msg_send![cls, getAutoreleasedInstance] }; unsafe { pool.ptr_as_ref(ptr) } } - fn new_autoreleased_retained() -> Id { + fn new_autoreleased_retained() -> Id { let cls = Self::class(); unsafe { msg_send_id![cls, getAutoreleasedInstance] } } @@ -289,12 +294,12 @@ fn test_object() { assert!(obj.var3().is_null()); assert!(obj.var3_ivar().is_null()); - let obj2 = Id::as_mut_ptr(&mut *ManuallyDrop::new(NSObject::new())).cast::(); + let obj2 = Id::as_ptr(&*ManuallyDrop::new(NSObject::new())) as _; obj.set_var3(obj2); assert_eq!(obj.var3(), obj2); assert_eq!(*obj.var3_ivar(), obj2); - let obj3 = Id::as_mut_ptr(&mut *ManuallyDrop::new(NSObject::new())).cast::(); + let obj3 = Id::as_ptr(&*ManuallyDrop::new(NSObject::new())) as _; *obj.var3_ivar_mut() = obj3; assert_ne!(obj.var3(), obj2); assert_ne!(*obj.var3_ivar(), obj2); @@ -305,7 +310,7 @@ fn test_object() { #[test] fn test_protocol() { let obj = MyTestObject::new(); - let proto: Id, _> = ProtocolObject::from_id(obj); + let proto: Id> = ProtocolObject::from_id(obj); assert_eq!(proto.a(), 1); assert_eq!(MyTestObject::b(), 2); #[cfg(feature = "Foundation_all")]