From 24346416f36792b19d376d9d7538ccbae0c9bef4 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 29 Sep 2023 14:19:21 +0200 Subject: [PATCH 1/6] Add note about instance variable layouts being unused --- crates/objc2/src/declare/mod.rs | 4 ++++ crates/objc2/src/runtime/mod.rs | 32 +++++++++++++++++++------------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/crates/objc2/src/declare/mod.rs b/crates/objc2/src/declare/mod.rs index cad4f0ffe..490da7310 100644 --- a/crates/objc2/src/declare/mod.rs +++ b/crates/objc2/src/declare/mod.rs @@ -419,6 +419,10 @@ impl ClassBuilder { let c_name = CString::new(name).unwrap(); let encoding = CString::new(encoding.to_string()).unwrap(); + + // Note: The Objective-C runtime contains functionality to do stuff + // with "instance variable layouts", but we don't have to touch any of + // that, it was only used in the garbage-collecting runtime. let success = Bool::from_raw(unsafe { ffi::class_addIvar( self.as_mut_ptr(), diff --git a/crates/objc2/src/runtime/mod.rs b/crates/objc2/src/runtime/mod.rs index 0ad0df73b..da20c4e0d 100644 --- a/crates/objc2/src/runtime/mod.rs +++ b/crates/objc2/src/runtime/mod.rs @@ -713,17 +713,6 @@ impl AnyClass { } } - #[allow(unused)] - #[doc(alias = "class_getIvarLayout")] - fn instance_variable_layout(&self) -> Option<&[u8]> { - let layout: *const c_char = unsafe { ffi::class_getIvarLayout(self.as_ptr()).cast() }; - if layout.is_null() { - None - } else { - Some(unsafe { CStr::from_ptr(layout) }.to_bytes()) - } - } - #[allow(unused)] #[doc(alias = "class_getClassVariable")] fn class_variable(&self, name: &str) -> Option<&Ivar> { @@ -798,7 +787,6 @@ impl AnyClass { // fn properties(&self) -> Malloc<[&Property]>; // unsafe fn replace_method(&self, name: Sel, imp: Imp, types: &str) -> Imp; // unsafe fn replace_property(&self, name: &str, attributes: &[ffi::objc_property_attribute_t]); - // unsafe fn set_ivar_layout(&mut self, layout: &[u8]); // fn method_imp(&self, name: Sel) -> Imp; // + _stret // fn get_version(&self) -> u32; @@ -1285,7 +1273,7 @@ mod tests { use super::*; use crate::runtime::MessageReceiver; use crate::test_utils; - use crate::{msg_send, sel}; + use crate::{class, msg_send, sel}; #[test] fn test_selector() { @@ -1576,4 +1564,22 @@ mod tests { assert_eq!(size_of::(), 0); assert_eq!(size_of::(), 0); } + + fn get_ivar_layout(cls: &AnyClass) -> *const u8 { + let cls: *const AnyClass = cls; + unsafe { ffi::class_getIvarLayout(cls.cast()) } + } + + #[test] + #[cfg_attr( + feature = "gnustep-1-7", + ignore = "ivar layout is still used on GNUStep" + )] + fn test_layout_does_not_matter_any_longer() { + assert!(get_ivar_layout(class!(NSObject)).is_null()); + assert!(get_ivar_layout(class!(NSArray)).is_null()); + assert!(get_ivar_layout(class!(NSException)).is_null()); + assert!(get_ivar_layout(class!(NSNumber)).is_null()); + assert!(get_ivar_layout(class!(NSString)).is_null()); + } } From 68c1c2c068fcc4889db1c03ad1e10392ce386d01 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 29 Sep 2023 04:14:44 +0200 Subject: [PATCH 2/6] Make AnyObject.class return a static --- crates/objc2/CHANGELOG.md | 1 + crates/objc2/src/runtime/mod.rs | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/objc2/CHANGELOG.md b/crates/objc2/CHANGELOG.md index ce9d7b526..204f7bbfa 100644 --- a/crates/objc2/CHANGELOG.md +++ b/crates/objc2/CHANGELOG.md @@ -50,6 +50,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). `MethodImplementation` to `Return` and `Arguments`. * **BREAKING**: Make `rc::Allocated` allowed to be `NULL` internally, such that uses of `Option>` is now simply `Allocated`. +* `AnyObject::class` now returns a `'static` reference to the class. ### Deprecated * Soft deprecated using `msg_send!` without a comma between arguments (i.e. diff --git a/crates/objc2/src/runtime/mod.rs b/crates/objc2/src/runtime/mod.rs index da20c4e0d..a2219ed39 100644 --- a/crates/objc2/src/runtime/mod.rs +++ b/crates/objc2/src/runtime/mod.rs @@ -1052,9 +1052,11 @@ impl AnyObject { /// Dynamically find the class of this object. #[doc(alias = "object_getClass")] - pub fn class(&self) -> &AnyClass { + pub fn class(&self) -> &'static AnyClass { let ptr: *const AnyClass = unsafe { ffi::object_getClass(self.as_ptr()) }.cast(); - // SAFETY: The class is not NULL because the object is not NULL. + // SAFETY: The class is not NULL because the object is not NULL, and + // it is safe as `'static` since classes are static, and it could be + // retrieved via. `AnyClass::get(self.class().name())` anyhow. unsafe { ptr.as_ref().unwrap_unchecked() } } From 0870a264f0309e60cc6799a91618dc670003243c Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 29 Sep 2023 04:25:34 +0200 Subject: [PATCH 3/6] Improve runtime inlining slightly --- crates/objc2/src/runtime/mod.rs | 13 ++++++++++- .../expected/apple-aarch64.s | 22 +++++++++---------- .../test_declare_class/expected/apple-armv7.s | 22 +++++++++---------- .../expected/apple-armv7s.s | 22 +++++++++---------- .../expected/apple-old-x86.s | 22 +++++++++---------- .../test_declare_class/expected/apple-x86.s | 22 +++++++++---------- .../expected/apple-x86_64.s | 22 +++++++++---------- 7 files changed, 78 insertions(+), 67 deletions(-) diff --git a/crates/objc2/src/runtime/mod.rs b/crates/objc2/src/runtime/mod.rs index a2219ed39..f30f3b496 100644 --- a/crates/objc2/src/runtime/mod.rs +++ b/crates/objc2/src/runtime/mod.rs @@ -300,6 +300,7 @@ impl UnwindSafe for Ivar {} impl RefUnwindSafe for Ivar {} impl Ivar { + #[inline] pub(crate) fn as_ptr(&self) -> *const ffi::objc_ivar { let ptr: *const Self = self; ptr.cast() @@ -381,6 +382,7 @@ impl UnwindSafe for Method {} impl RefUnwindSafe for Method {} impl Method { + #[inline] pub(crate) fn as_ptr(&self) -> *const ffi::objc_method { let ptr: *const Self = self; ptr.cast() @@ -388,11 +390,13 @@ impl Method { // Note: We don't take `&mut` here, since the operations on methods work // atomically. + #[inline] pub(crate) fn as_mut_ptr(&self) -> *mut ffi::objc_method { self.as_ptr() as _ } /// Returns the name of self. + #[inline] #[doc(alias = "method_getName")] pub fn name(&self) -> Sel { unsafe { Sel::from_ptr(ffi::method_getName(self.as_ptr())).unwrap() } @@ -446,6 +450,7 @@ impl Method { } /// Returns the number of arguments accepted by self. + #[inline] #[doc(alias = "method_getNumberOfArguments")] pub fn arguments_count(&self) -> usize { unsafe { ffi::method_getNumberOfArguments(self.as_ptr()) as usize } @@ -483,7 +488,6 @@ impl Method { /// /// A common mistake would be expecting e.g. a pointer to not be null, /// where the null case was handled before. - #[inline] #[doc(alias = "method_setImplementation")] pub unsafe fn set_implementation(&self, imp: Imp) -> Imp { // SAFETY: The new impl is not NULL, and the rest is upheld by the @@ -574,6 +578,7 @@ impl RefUnwindSafe for AnyClass {} // Note that Unpin is not applicable. impl AnyClass { + #[inline] pub(crate) fn as_ptr(&self) -> *const ffi::objc_class { let ptr: *const Self = self; ptr.cast() @@ -603,6 +608,7 @@ impl AnyClass { } /// Returns the total number of registered classes. + #[inline] #[doc(alias = "objc_getClassList")] pub fn classes_count() -> usize { unsafe { ffi::objc_getClassList(ptr::null_mut(), 0) as usize } @@ -734,6 +740,7 @@ impl AnyClass { } /// Checks whether this class conforms to the specified protocol. + #[inline] #[doc(alias = "class_conformsToProtocol")] pub fn conforms_to(&self, proto: &AnyProtocol) -> bool { unsafe { @@ -861,6 +868,7 @@ impl RefUnwindSafe for AnyProtocol {} // Note that Unpin is not applicable. impl AnyProtocol { + #[inline] pub(crate) fn as_ptr(&self) -> *const ffi::objc_protocol { let ptr: *const Self = self; ptr.cast() @@ -901,6 +909,7 @@ impl AnyProtocol { } /// Checks whether this protocol conforms to the specified protocol. + #[inline] #[doc(alias = "protocol_conformsToProtocol")] pub fn conforms_to(&self, proto: &AnyProtocol) -> bool { unsafe { @@ -1045,12 +1054,14 @@ unsafe impl RefEncode for AnyObject { unsafe impl Message for AnyObject {} impl AnyObject { + #[inline] pub(crate) fn as_ptr(&self) -> *const ffi::objc_object { let ptr: *const Self = self; ptr.cast() } /// Dynamically find the class of this object. + #[inline] #[doc(alias = "object_getClass")] pub fn class(&self) -> &'static AnyClass { let ptr: *const AnyClass = unsafe { ffi::object_getClass(self.as_ptr()) }.cast(); diff --git a/crates/test-assembly/crates/test_declare_class/expected/apple-aarch64.s b/crates/test-assembly/crates/test_declare_class/expected/apple-aarch64.s index f544c0815..5650ba07e 100644 --- a/crates/test-assembly/crates/test_declare_class/expected/apple-aarch64.s +++ b/crates/test-assembly/crates/test_declare_class/expected/apple-aarch64.s @@ -383,7 +383,7 @@ _access_ivars: add x29, sp, #32 bl _get_obj mov x19, x0 - bl SYM(objc2::runtime::AnyObject::class::GENERATED_ID, 0) + bl _object_getClass Lloh88: adrp x1, l_anon.[ID].11@PAGE Lloh89: @@ -396,7 +396,7 @@ Lloh91: bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) ldrb w20, [x19, x0] mov x0, x19 - bl SYM(objc2::runtime::AnyObject::class::GENERATED_ID, 0) + bl _object_getClass Lloh92: adrp x1, l_anon.[ID].13@PAGE Lloh93: @@ -500,7 +500,7 @@ SYM( Date: Tue, 3 Oct 2023 00:41:41 +0200 Subject: [PATCH 4/6] Reduce usage of `__emit_struct` --- crates/objc2/src/macros/extern_class.rs | 13 +++---- crates/objc2/src/runtime/nsobject.rs | 35 ++++++++----------- crates/objc2/src/runtime/nsproxy.rs | 23 +++++------- .../ui/declare_class_invalid_receiver.stderr | 4 +-- crates/test-ui/ui/extern_class_root.stderr | 2 +- .../implement_protocol_missing_super.stderr | 2 +- .../ui/msg_send_id_invalid_return.stderr | 12 +++---- .../test-ui/ui/msg_send_invalid_error.stderr | 4 +-- .../ui/msg_send_super_not_classtype.stderr | 4 +-- .../mutability_traits_unimplementable2.stderr | 4 +-- .../ui/nsarray_bound_not_send_sync.stderr | 16 ++++----- crates/test-ui/ui/nsarray_not_message.stderr | 6 ++-- crates/test-ui/ui/object_not_send_sync.stderr | 16 ++++----- 13 files changed, 64 insertions(+), 77 deletions(-) diff --git a/crates/objc2/src/macros/extern_class.rs b/crates/objc2/src/macros/extern_class.rs index 8028024c9..1907a9d2e 100644 --- a/crates/objc2/src/macros/extern_class.rs +++ b/crates/objc2/src/macros/extern_class.rs @@ -338,14 +338,11 @@ macro_rules! __inner_extern_class { $(const NAME: &'static str = $name_const:expr;)? } ) => { - $crate::__emit_struct! { - ($(#[$m])*) - ($v) - ($name<$($t_struct $(: $(?$b_sized_struct)? $($b_struct)? $(= $default)?)?),*>) - ( - $superclass_field: $superclass_field_ty, - $($fields)* - ) + $(#[$m])* + #[repr(C)] + $v struct $name<$($t_struct $(: $(?$b_sized_struct)? $($b_struct)? $(= $default)?)?),*> { + $superclass_field: $superclass_field_ty, + $($fields)* } $crate::__extern_class_impl_traits! { diff --git a/crates/objc2/src/runtime/nsobject.rs b/crates/objc2/src/runtime/nsobject.rs index f188fb09d..3672a5c85 100644 --- a/crates/objc2/src/runtime/nsobject.rs +++ b/crates/objc2/src/runtime/nsobject.rs @@ -7,26 +7,21 @@ use crate::runtime::{AnyClass, AnyObject, ProtocolObject}; use crate::{extern_methods, msg_send, msg_send_id, Message}; use crate::{ClassType, ProtocolType}; -crate::__emit_struct! { - ( - /// The root class of most Objective-C class hierarchies. - /// - /// This represents the [`NSObject` class][cls]. The name "NSObject" also - /// refers to a protocol, see [`NSObjectProtocol`] for that. - /// - /// Since this class is only available with the `Foundation` framework, - /// `objc2` links to it for you. - /// - /// This is exported under `icrate::Foundation::NSObject`, you probably - /// want to use that path instead. - /// - /// [cls]: https://developer.apple.com/documentation/objectivec/nsobject?language=objc - ) - (pub) - (NSObject) - ( - __inner: AnyObject, - ) +/// The root class of most Objective-C class hierarchies. +/// +/// This represents the [`NSObject` class][cls]. The name "NSObject" also +/// refers to a protocol, see [`NSObjectProtocol`] for that. +/// +/// Since this class is only available with the `Foundation` framework, +/// `objc2` links to it for you. +/// +/// This is exported under `icrate::Foundation::NSObject`, you probably +/// want to use that path instead. +/// +/// [cls]: https://developer.apple.com/documentation/objectivec/nsobject?language=objc +#[repr(C)] +pub struct NSObject { + __inner: AnyObject, } crate::__extern_class_impl_traits! { diff --git a/crates/objc2/src/runtime/nsproxy.rs b/crates/objc2/src/runtime/nsproxy.rs index cfd87851d..7592dbd96 100644 --- a/crates/objc2/src/runtime/nsproxy.rs +++ b/crates/objc2/src/runtime/nsproxy.rs @@ -5,20 +5,15 @@ use crate::mutability::Root; use crate::runtime::{AnyClass, AnyObject, NSObjectProtocol, ProtocolObject}; use crate::ClassType; -crate::__emit_struct! { - ( - /// An abstract superclass defining an API for objects that act as - /// stand-ins for other objects or for objects that don’t exist yet. - /// - /// See [Apple's documentation][apple-doc] for more information. - /// - /// [apple-doc]: https://developer.apple.com/documentation/foundation/nsproxy?language=objc - ) - (pub) - (NSProxy) - ( - __inner: AnyObject, - ) +/// An abstract superclass defining an API for objects that act as +/// stand-ins for other objects or for objects that don’t exist yet. +/// +/// See [Apple's documentation][apple-doc] for more information. +/// +/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsproxy?language=objc +#[repr(C)] +pub struct NSProxy { + __inner: AnyObject, } crate::__extern_class_impl_traits! { diff --git a/crates/test-ui/ui/declare_class_invalid_receiver.stderr b/crates/test-ui/ui/declare_class_invalid_receiver.stderr index 42dcf27a6..b68f7a2d1 100644 --- a/crates/test-ui/ui/declare_class_invalid_receiver.stderr +++ b/crates/test-ui/ui/declare_class_invalid_receiver.stderr @@ -115,11 +115,11 @@ error[E0277]: the trait bound `AnyClass: Message` is not satisfied = help: the following other types implement trait `Message`: CustomObject Exception + NSObject + __NSProxy ProtocolObject

AnyObject __RcTestObject - NSObject - __NSProxy note: required by a bound in `ClassBuilder::add_method` --> $WORKSPACE/crates/objc2/src/declare/mod.rs | diff --git a/crates/test-ui/ui/extern_class_root.stderr b/crates/test-ui/ui/extern_class_root.stderr index 32669c5f2..08d99b345 100644 --- a/crates/test-ui/ui/extern_class_root.stderr +++ b/crates/test-ui/ui/extern_class_root.stderr @@ -12,7 +12,7 @@ error[E0277]: the trait bound `MyObject: ClassType` is not satisfied | = help: the following other types implement trait `ClassType`: MyRootClass - __RcTestObject 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/implement_protocol_missing_super.stderr b/crates/test-ui/ui/implement_protocol_missing_super.stderr index b45a0c989..eaa533bca 100644 --- a/crates/test-ui/ui/implement_protocol_missing_super.stderr +++ b/crates/test-ui/ui/implement_protocol_missing_super.stderr @@ -5,9 +5,9 @@ error[E0277]: the trait bound `CustomObject: NSObjectProtocol` is not satisfied | ^^^^^^^^^^^^ the trait `NSObjectProtocol` is not implemented for `CustomObject` | = help: the following other types implement trait `NSObjectProtocol`: - ProtocolObject NSObject __NSProxy + ProtocolObject NSApplication NSCollectionView NSCollectionLayoutSection 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 b182371c8..ea5e9995a 100644 --- a/crates/test-ui/ui/msg_send_id_invalid_return.stderr +++ b/crates/test-ui/ui/msg_send_id_invalid_return.stderr @@ -26,11 +26,11 @@ error[E0277]: the trait bound `AnyClass: Message` is not satisfied | = help: the following other types implement trait `Message`: Exception + NSObject + __NSProxy ProtocolObject

AnyObject __RcTestObject - NSObject - __NSProxy = note: required for `RetainSemantics<1>` to implement `MsgSendId<&AnyClass, Option>>` error[E0277]: the trait bound `AnyClass: Message` is not satisfied @@ -44,11 +44,11 @@ error[E0277]: the trait bound `AnyClass: Message` is not satisfied | = help: the following other types implement trait `Message`: Exception + NSObject + __NSProxy ProtocolObject

AnyObject __RcTestObject - NSObject - __NSProxy = note: required for `RetainSemantics<1>` to implement `MsgSendId<&AnyClass, Option>>` error[E0277]: the trait bound `&AnyObject: MaybeUnwrap` is not satisfied @@ -79,11 +79,11 @@ error[E0277]: the trait bound `AnyClass: Message` is not satisfied | = help: the following other types implement trait `Message`: Exception + NSObject + __NSProxy ProtocolObject

AnyObject __RcTestObject - NSObject - __NSProxy = note: required for `RetainSemantics<2>` to implement `MsgSendId<&AnyClass, Allocated>` error[E0271]: type mismatch resolving ` as MaybeUnwrap>::Input == Allocated<_>` diff --git a/crates/test-ui/ui/msg_send_invalid_error.stderr b/crates/test-ui/ui/msg_send_invalid_error.stderr index 4cbe3dc73..fb5bd2c07 100644 --- a/crates/test-ui/ui/msg_send_invalid_error.stderr +++ b/crates/test-ui/ui/msg_send_invalid_error.stderr @@ -36,11 +36,11 @@ error[E0277]: the trait bound `i32: Message` is not satisfied | = help: the following other types implement trait `Message`: Exception + NSObject + __NSProxy ProtocolObject

AnyObject __RcTestObject - NSObject - __NSProxy NSApplication NSCollectionView and $N others diff --git a/crates/test-ui/ui/msg_send_super_not_classtype.stderr b/crates/test-ui/ui/msg_send_super_not_classtype.stderr index 709d4f6fc..4ae011d4d 100644 --- a/crates/test-ui/ui/msg_send_super_not_classtype.stderr +++ b/crates/test-ui/ui/msg_send_super_not_classtype.stderr @@ -8,9 +8,9 @@ error[E0277]: the trait bound `AnyObject: ClassType` is not satisfied | required by a bound introduced by this call | = help: the following other types implement trait `ClassType`: - __RcTestObject NSObject __NSProxy + __RcTestObject note: required by a bound in `send_super_message_static` --> $WORKSPACE/crates/objc2/src/__macro_helpers/msg_send.rs | @@ -30,9 +30,9 @@ error[E0277]: the trait bound `AnyObject: ClassType` is not satisfied | required by a bound introduced by this call | = help: the following other types implement trait `ClassType`: - __RcTestObject NSObject __NSProxy + __RcTestObject note: required by a bound in `send_super_message_static` --> $WORKSPACE/crates/objc2/src/__macro_helpers/msg_send.rs | diff --git a/crates/test-ui/ui/mutability_traits_unimplementable2.stderr b/crates/test-ui/ui/mutability_traits_unimplementable2.stderr index 0972f5557..20fe4da0d 100644 --- a/crates/test-ui/ui/mutability_traits_unimplementable2.stderr +++ b/crates/test-ui/ui/mutability_traits_unimplementable2.stderr @@ -5,9 +5,9 @@ error[E0277]: the trait bound `CustomStruct: ClassType` is not satisfied | ^^^^^^^^^^^^ the trait `ClassType` is not implemented for `CustomStruct` | = help: the following other types implement trait `ClassType`: - __RcTestObject NSObject __NSProxy + __RcTestObject = note: required for `CustomStruct` to implement `mutability::private_traits::Sealed` note: required by a bound in `IsIdCloneable` --> $WORKSPACE/crates/objc2/src/mutability.rs @@ -23,9 +23,9 @@ error[E0277]: the trait bound `CustomStruct: ClassType` is not satisfied | ^^^^^^^^^^^^ the trait `ClassType` is not implemented for `CustomStruct` | = help: the following other types implement trait `ClassType`: - __RcTestObject NSObject __NSProxy + __RcTestObject = note: required for `CustomStruct` to implement `IsAllowedMutable` note: required by a bound in `IsMutable` --> $WORKSPACE/crates/objc2/src/mutability.rs 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 f3fb24c2e..7c77fbb01 100644 --- a/crates/test-ui/ui/nsarray_bound_not_send_sync.stderr +++ b/crates/test-ui/ui/nsarray_bound_not_send_sync.stderr @@ -54,8 +54,8 @@ note: required because it appears within the type `AnyObject` note: required because it appears within the type `NSObject` --> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs | - | (NSObject) - | ^^^^^^^^ + | pub struct NSObject { + | ^^^^^^^^ = note: required for `Id` to implement `Sync` note: required because it appears within the type `PhantomData>` --> $RUST/core/src/marker.rs @@ -140,8 +140,8 @@ note: required because it appears within the type `AnyObject` note: required because it appears within the type `NSObject` --> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs | - | (NSObject) - | ^^^^^^^^ + | pub struct NSObject { + | ^^^^^^^^ = note: required for `Id` to implement `Send` note: required because it appears within the type `PhantomData>` --> $RUST/core/src/marker.rs @@ -220,8 +220,8 @@ note: required because it appears within the type `AnyObject` note: required because it appears within the type `NSObject` --> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs | - | (NSObject) - | ^^^^^^^^ + | pub struct NSObject { + | ^^^^^^^^ = note: required for `Id` to implement `Sync` note: required because it appears within the type `PhantomData>` --> $RUST/core/src/marker.rs @@ -316,8 +316,8 @@ note: required because it appears within the type `AnyObject` note: required because it appears within the type `NSObject` --> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs | - | (NSObject) - | ^^^^^^^^ + | pub struct NSObject { + | ^^^^^^^^ = note: required for `Id` to implement `Send` note: required because it appears within the type `PhantomData>` --> $RUST/core/src/marker.rs diff --git a/crates/test-ui/ui/nsarray_not_message.stderr b/crates/test-ui/ui/nsarray_not_message.stderr index 8da89e478..01f4daa4b 100644 --- a/crates/test-ui/ui/nsarray_not_message.stderr +++ b/crates/test-ui/ui/nsarray_not_message.stderr @@ -6,11 +6,11 @@ error[E0277]: the trait bound `i32: Message` is not satisfied | = help: the following other types implement trait `Message`: Exception + NSObject + __NSProxy ProtocolObject

AnyObject __RcTestObject - NSObject - __NSProxy NSApplication NSCollectionView and $N others @@ -38,9 +38,9 @@ error[E0277]: the trait bound `Id: ClassType` is not satisfied | required by a bound introduced by this call | = help: the following other types implement trait `ClassType`: - __RcTestObject NSObject __NSProxy + __RcTestObject NSApplication NSCollectionView NSCollectionLayoutSection diff --git a/crates/test-ui/ui/object_not_send_sync.stderr b/crates/test-ui/ui/object_not_send_sync.stderr index cc55bc81f..8b83669f7 100644 --- a/crates/test-ui/ui/object_not_send_sync.stderr +++ b/crates/test-ui/ui/object_not_send_sync.stderr @@ -78,8 +78,8 @@ note: required because it appears within the type `AnyObject` note: required because it appears within the type `NSObject` --> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs | - | (NSObject) - | ^^^^^^^^ + | pub struct NSObject { + | ^^^^^^^^ note: required by a bound in `needs_sync` --> ui/object_not_send_sync.rs | @@ -118,8 +118,8 @@ note: required because it appears within the type `AnyObject` note: required because it appears within the type `NSObject` --> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs | - | (NSObject) - | ^^^^^^^^ + | pub struct NSObject { + | ^^^^^^^^ note: required by a bound in `needs_send` --> ui/object_not_send_sync.rs | @@ -147,8 +147,8 @@ note: required because it appears within the type `AnyObject` note: required because it appears within the type `NSObject` --> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs | - | (NSObject) - | ^^^^^^^^ + | pub struct NSObject { + | ^^^^^^^^ note: required because it appears within the type `NSValue` --> $WORKSPACE/crates/icrate/src/generated/Foundation/NSValue.rs | @@ -192,8 +192,8 @@ note: required because it appears within the type `AnyObject` note: required because it appears within the type `NSObject` --> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs | - | (NSObject) - | ^^^^^^^^ + | pub struct NSObject { + | ^^^^^^^^ note: required because it appears within the type `NSValue` --> $WORKSPACE/crates/icrate/src/generated/Foundation/NSValue.rs | From 9f761ff0d8e2462d85e38f04c2718105b3b9ce46 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 29 Sep 2023 17:40:07 +0200 Subject: [PATCH 5/6] Document why we do NULL receiver checks on GNUStep Initially introduced in f4f95a447b1077686ecd1e09f01c952ca8d9411e, but the documentation behind it was sparse --- crates/objc2/src/runtime/message_receiver.rs | 27 ++++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/crates/objc2/src/runtime/message_receiver.rs b/crates/objc2/src/runtime/message_receiver.rs index 7f5497394..f6c889bed 100644 --- a/crates/objc2/src/runtime/message_receiver.rs +++ b/crates/objc2/src/runtime/message_receiver.rs @@ -154,6 +154,15 @@ mod msg_send_primitive { args: A, ) -> R { let msg_send_fn = R::MSG_SEND; + // Note: Modern Objective-C compilers have a workaround to ensure that + // messages to `nil` with a struct return produces `mem::zeroed()`, + // see: + // + // + // We _could_ technically do something similar, but since we're + // disallowing messages to `nil` with `debug_assertions` enabled + // anyhow, and since Rust has a much stronger type-system that + // disallows NULL/nil in most cases, we won't bother supporting it. unsafe { A::__invoke(msg_send_fn, receiver, sel, args) } } @@ -206,13 +215,21 @@ mod msg_send_primitive { sel: Sel, args: A, ) -> R { - // If `receiver` is NULL, objc_msg_lookup will return a standard C-method - // taking two arguments, the receiver and the selector. Transmuting and - // calling such a function with multiple parameters is UB, so instead we - // return NULL directly. + // If `receiver` is NULL, objc_msg_lookup will return a standard + // C-method taking two arguments, the receiver and the selector. + // + // Transmuting and calling such a function with multiple parameters is + // safe as long as the return value is a primitive (and e.g. not a big + // struct or array). + // + // However, when the return value is a floating point value, the float + // will end up as some undefined value, usually NaN, which is + // incompatible with Apple's platforms. As such, we insert this extra + // NULL check here. if receiver.is_null() { // SAFETY: Caller guarantees that messages to NULL-receivers only - // return pointers, and a mem::zeroed pointer is just a NULL-pointer. + // return pointers or primitive values, and a mem::zeroed pointer + // / primitive is just a NULL-pointer or a zeroed primitive. return unsafe { mem::zeroed() }; } From 44e8bf85b5f57c5c762edc612a4baa3e421f7152 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 22 Sep 2023 17:19:06 +0200 Subject: [PATCH 6/6] Add a few more links to external documentation --- .github/workflows/ci.yml | 2 ++ crates/header-translator/src/rust_type.rs | 4 ++++ crates/objc-sys/build.rs | 1 + crates/objc-sys/src/lib.rs | 4 +++- crates/objc-sys/src/various.rs | 1 + crates/objc2/src/runtime/message_receiver.rs | 10 +++++++--- 6 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c0595d5b..018b4b7d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -584,6 +584,8 @@ jobs: ASMFLAGS: ${{ matrix.cflags }} LDFLAGS: ${{ matrix.cflags }} ARGS: --no-default-features --features=std,${{ matrix.runtime }} + # http://wiki.gnustep.org/index.php/Building_GNUstep_under_Debian_FreeBSD#installing_gnustep-make + RUNTIME_VERSION: gnustep-${{ matrix.libobjc2 }} steps: - uses: actions/checkout@v3 diff --git a/crates/header-translator/src/rust_type.rs b/crates/header-translator/src/rust_type.rs index a5243e0b7..11e20ab37 100644 --- a/crates/header-translator/src/rust_type.rs +++ b/crates/header-translator/src/rust_type.rs @@ -122,6 +122,10 @@ impl AttributeParser<'_, '_> { } } + /// We completely ignore `__kindof` in Rust as it is done in Swift, since + /// it only exists to allow legacy Objective-C code to continue compiling. + /// + /// See fn is_kindof(&mut self, position: ParsePosition) -> bool { self.strip("__kindof", position) } diff --git a/crates/objc-sys/build.rs b/crates/objc-sys/build.rs index a45244219..dfc99698d 100644 --- a/crates/objc-sys/build.rs +++ b/crates/objc-sys/build.rs @@ -171,6 +171,7 @@ fn main() { // The fragile runtime is expected on i686-apple-darwin, see: // https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/Driver/ToolChains/Darwin.h#L228-L231 // https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/Driver/ToolChains/Clang.cpp#L3639-L3640 + // https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtVersionsPlatforms.html (MacOS(version), "x86") => format!("macosx-fragile-{version}"), (MacOS(version), _) => format!("macosx-{version}"), (IOS(version), _) => format!("ios-{version}"), diff --git a/crates/objc-sys/src/lib.rs b/crates/objc-sys/src/lib.rs index 90b5217ff..68b0b1087 100644 --- a/crates/objc-sys/src/lib.rs +++ b/crates/objc-sys/src/lib.rs @@ -2,13 +2,15 @@ //! //! These bindings contain almost no documentation, so it is highly //! recommended to read the documentation of the original libraries: -//! - Apple's [official documentation][apple]. +//! - Apple's [documentation about the Objective-C runtime][runtime-guide]. +//! - Apple's [runtime reference][apple]. //! - Apple's `objc4` [source code][objc4], in particular `runtime.h`. //! - GNUStep's `libobjc2` [source code][libobjc2], in particular `runtime.h`. //! //! See also the [`README.md`](https://crates.io/crates/objc-sys) for more //! background information, and for how to configure the desired runtime. //! +//! [runtime-guide]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html //! [apple]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc //! [libobjc2]: https://github.com/gnustep/libobjc2/tree/v2.1/objc //! [objc4]: https://github.com/apple-oss-distributions/objc4 diff --git a/crates/objc-sys/src/various.rs b/crates/objc-sys/src/various.rs index fd9fdce5c..fcf51ce17 100644 --- a/crates/objc-sys/src/various.rs +++ b/crates/objc-sys/src/various.rs @@ -45,6 +45,7 @@ extern_c_unwind! { extern_c! { #[cfg(any(doc, not(objfw)))] pub fn imp_getBlock(imp: IMP) -> *mut objc_object; + // See also #[cfg(any(doc, not(objfw)))] pub fn imp_implementationWithBlock(block: *mut objc_object) -> IMP; #[cfg(any(doc, not(objfw)))] diff --git a/crates/objc2/src/runtime/message_receiver.rs b/crates/objc2/src/runtime/message_receiver.rs index f6c889bed..bc1dc6b4e 100644 --- a/crates/objc2/src/runtime/message_receiver.rs +++ b/crates/objc2/src/runtime/message_receiver.rs @@ -35,6 +35,10 @@ macro_rules! conditional_try { }}; } +// More information on how objc_msgSend works: +// +// +// #[cfg(feature = "apple")] mod msg_send_primitive { #[allow(unused_imports)] @@ -340,13 +344,13 @@ pub unsafe trait MessageReceiver: private::Sealed + Sized { /// 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 - /// Messages" in Apple's [documentation][runtime]. + /// return type. For more information, see [the Messaging section in + /// Apple's Objective-C Runtime Programming Guide][guide-messaging]. /// /// If the selector is known at compile-time, it is recommended to use the /// [`msg_send!`] macro rather than this method. /// - /// [runtime]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc + /// [guide-messaging]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtHowMessagingWorks.html /// /// /// # Safety