From a39aadb96580eeb82466e6fd1185522f16ec5313 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 10 Sep 2023 18:36:02 +0200 Subject: [PATCH 1/8] Fix trait implementations on ItemIdentifier --- crates/header-translator/src/id.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/crates/header-translator/src/id.rs b/crates/header-translator/src/id.rs index 3a36d92d8..20c755e96 100644 --- a/crates/header-translator/src/id.rs +++ b/crates/header-translator/src/id.rs @@ -1,4 +1,6 @@ +use core::cmp::Ordering; use core::fmt; +use core::hash; use clang::Entity; @@ -20,7 +22,7 @@ impl ToOptionString for Option { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Debug, Clone)] pub struct ItemIdentifier { /// Names in Objective-C are global, so this is always enough to uniquely /// identify the item. @@ -31,6 +33,32 @@ pub struct ItemIdentifier { pub file_name: Option, } +impl PartialEq for ItemIdentifier { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} + +impl Eq for ItemIdentifier {} + +impl hash::Hash for ItemIdentifier { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +impl PartialOrd for ItemIdentifier { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ItemIdentifier { + fn cmp(&self, other: &Self) -> Ordering { + self.name.cmp(&other.name) + } +} + impl ItemIdentifier { pub fn from_raw(name: N, library: String) -> Self { Self { From f9a0f2b8239cb82a21fcb6808249f24923c4d7e8 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 11 Sep 2023 13:44:46 +0200 Subject: [PATCH 2/8] Don't include MainThreadMarker in methods that do not need it --- crates/header-translator/src/cache.rs | 71 ++++++++++++++------------- crates/icrate/src/generated | 2 +- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/crates/header-translator/src/cache.rs b/crates/header-translator/src/cache.rs index e3adf4c1c..b536cab70 100644 --- a/crates/header-translator/src/cache.rs +++ b/crates/header-translator/src/cache.rs @@ -105,45 +105,46 @@ impl<'a> Cache<'a> { } }); - match (method.is_class, self.mainthreadonly_classes.contains(id)) { - // MainThreadOnly class with static method - (true, true) => { - // Assume the method needs main thread - result_type_contains_mainthreadonly = true; - } - // Class with static method - (true, false) => { - // Continue with the normal check - } - // MainThreadOnly class with non-static method - (false, true) => { - // Method is already required to run on main - // thread, so no need to add MainThreadMarker - continue; - } - // Class with non-static method - (false, false) => { - // Continue with the normal check - } + let mut any_argument_contains_mainthreadonly: bool = false; + for (_, argument) in method.arguments.iter() { + // Important: We only visit the top-level types, to not + // include optional arguments like `Option<&NSView>` or + // `&NSArray`. + argument.visit_toplevel_types(&mut |id| { + if self.mainthreadonly_classes.contains(id) { + any_argument_contains_mainthreadonly = true; + } + }); } - if result_type_contains_mainthreadonly { - let mut any_argument_contains_mainthreadonly: bool = false; - for (_, argument) in method.arguments.iter() { - // Important: We only visit the top-level types, to not - // include e.g. `Option<&NSView>` or `&NSArray`. - argument.visit_toplevel_types(&mut |id| { - if self.mainthreadonly_classes.contains(id) { - any_argument_contains_mainthreadonly = true; - } - }); + if self.mainthreadonly_classes.contains(id) { + if method.is_class { + // Assume the method needs main thread if it's + // declared on a main thread only class. + result_type_contains_mainthreadonly = true; + } else { + // Method takes `&self` or `&mut self`, or is + // an initialization method, all of which + // already require the main thread. + // + // Note: Initialization methods can be passed + // `None`, but in that case the return will + // always be NULL. + any_argument_contains_mainthreadonly = true; } + } - // Apply main thread only, unless a (required) - // argument was main thread only. - if !any_argument_contains_mainthreadonly { - method.mainthreadonly = true; - } + if any_argument_contains_mainthreadonly { + // MainThreadMarker can be retrieved from + // `MainThreadMarker::from` inside these methods, + // and hence passing it is redundant. + method.mainthreadonly = false; + } else if result_type_contains_mainthreadonly { + method.mainthreadonly = true; + } else { + // If neither, then we respect any annotation + // the method may have had before + // method.mainthreadonly = method.mainthreadonly; } } } diff --git a/crates/icrate/src/generated b/crates/icrate/src/generated index 542165e89..486881e1b 160000 --- a/crates/icrate/src/generated +++ b/crates/icrate/src/generated @@ -1 +1 @@ -Subproject commit 542165e89f8677df7554c0788cbb9a5021ac31c8 +Subproject commit 486881e1bc7849db110cc6adb9b693462516b254 From 19d3df30bcd100a9003b133a52fa117c27862756 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 10 Sep 2023 22:12:19 +0200 Subject: [PATCH 3/8] Refactor how mutability traits are implemented --- crates/objc2/src/mutability.rs | 247 +++++++++--------- .../declare_class_mut_self_not_mutable.stderr | 18 +- .../ui/main_thread_only_not_allocable.stderr | 6 +- .../ui/mainthreadmarker_from_nsobject.rs | 6 + .../ui/mainthreadmarker_from_nsobject.stderr | 11 + .../mutability_traits_unimplementable.stderr | 2 +- .../ui/mutable_id_not_clone_not_retain.stderr | 6 +- crates/test-ui/ui/nsobject_not_mutable.stderr | 12 +- crates/test-ui/ui/nsset_from_nsobject.stderr | 6 +- 9 files changed, 172 insertions(+), 142 deletions(-) create mode 100644 crates/test-ui/ui/mainthreadmarker_from_nsobject.rs create mode 100644 crates/test-ui/ui/mainthreadmarker_from_nsobject.stderr diff --git a/crates/objc2/src/mutability.rs b/crates/objc2/src/mutability.rs index 799189084..2e621fd0f 100644 --- a/crates/objc2/src/mutability.rs +++ b/crates/objc2/src/mutability.rs @@ -34,6 +34,39 @@ use core::marker::PhantomData; use crate::ClassType; +mod private_mutability { + pub trait Sealed {} +} + +/// 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! +// +// Note: `Sized` is intentionally added to make the trait not object safe. +pub trait Mutability: private_mutability::Sealed + Sized {} + +impl private_mutability::Sealed for Root {} +impl Mutability for Root {} + +impl private_mutability::Sealed for Immutable {} +impl Mutability for Immutable {} + +impl private_mutability::Sealed for Mutable {} +impl Mutability for Mutable {} + +impl private_mutability::Sealed for ImmutableWithMutableSubclass {} +impl Mutability for ImmutableWithMutableSubclass {} + +impl private_mutability::Sealed for MutableWithImmutableSuperclass {} +impl Mutability for MutableWithImmutableSuperclass {} + +impl private_mutability::Sealed for InteriorMutable {} +impl Mutability for InteriorMutable {} + +impl private_mutability::Sealed for MainThreadOnly {} +impl Mutability for MainThreadOnly {} + /// Helper to make the structs uninhabited, without that being a public fact. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] enum Never {} @@ -226,110 +259,6 @@ 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 {} - - pub trait MutabilityIsMainThreadOnly: Mutability {} - impl MutabilityIsMainThreadOnly for MainThreadOnly {} - - pub trait MutabilityHashIsStable: Mutability {} - impl MutabilityHashIsStable for Immutable {} - impl MutabilityHashIsStable for Mutable {} - impl MutabilityHashIsStable for ImmutableWithMutableSubclass {} - impl MutabilityHashIsStable for MutableWithImmutableSuperclass {} - - pub trait MutabilityCounterpartOrSelf: Mutability { - type Immutable: ?Sized + ClassType; - type Mutable: ?Sized + ClassType; - } - impl> MutabilityCounterpartOrSelf for Root { - type Immutable = T; - type Mutable = T; - } - impl> MutabilityCounterpartOrSelf for Immutable { - type Immutable = T; - type Mutable = T; - } - impl> MutabilityCounterpartOrSelf for Mutable { - type Immutable = T; - type Mutable = T; - } - impl MutabilityCounterpartOrSelf for ImmutableWithMutableSubclass - where - T: ClassType>, - S: ClassType>, - { - type Immutable = T; - type Mutable = S; - } - impl MutabilityCounterpartOrSelf for MutableWithImmutableSuperclass - where - T: ClassType>, - S: ClassType>, - { - type Immutable = S; - type Mutable = T; - } - impl> MutabilityCounterpartOrSelf - for InteriorMutable - { - type Immutable = T; - type Mutable = T; - } - impl> MutabilityCounterpartOrSelf for MainThreadOnly { - type Immutable = T; - type Mutable = T; - } -} - -/// 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! -// -// Note: `Sized` is intentionally added to make the trait not object safe. -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`] is safe. /// /// Since the `Foundation` collection types (`NSArray`, @@ -346,8 +275,15 @@ impl Mutability for MainThreadOnly {} /// [`Id`]: crate::rc::Id /// [`Id::clone`]: crate::rc::Id#impl-Clone-for-Id pub trait IsIdCloneable: ClassType {} -impl IsIdCloneable for T where T::Mutability: private::MutabilityIsIdCloneable -{} + +trait MutabilityIsIdCloneable: Mutability {} +impl MutabilityIsIdCloneable for Root {} +impl MutabilityIsIdCloneable for Immutable {} +impl MutabilityIsIdCloneable for ImmutableWithMutableSubclass {} +impl MutabilityIsIdCloneable for InteriorMutable {} +impl MutabilityIsIdCloneable for MainThreadOnly {} + +impl IsIdCloneable for T where T::Mutability: MutabilityIsIdCloneable {} /// Marker trait for classes where the `retain` selector is always safe. /// @@ -362,7 +298,13 @@ impl IsIdCloneable for T where T::Mutability: private::Mu /// /// [`Id::clone`]: crate::rc::Id#impl-Clone-for-Id pub trait IsRetainable: IsIdCloneable {} -impl IsRetainable for T where T::Mutability: private::MutabilityIsRetainable {} + +trait MutabilityIsRetainable: MutabilityIsIdCloneable {} +impl MutabilityIsRetainable for Immutable {} +impl MutabilityIsRetainable for InteriorMutable {} +impl MutabilityIsRetainable for MainThreadOnly {} + +impl IsRetainable for T where T::Mutability: MutabilityIsRetainable {} /// Marker trait for classes that can be allocated from any thread. /// @@ -374,8 +316,17 @@ impl IsRetainable for T where T::Mutability: private::Mut /// - [`MutableWithImmutableSuperclass`]. /// - [`InteriorMutable`]. pub trait IsAllocableAnyThread: ClassType {} + +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 {} + impl IsAllocableAnyThread for T where - T::Mutability: private::MutabilityIsAllocableAnyThread + T::Mutability: MutabilityIsAllocableAnyThread { } @@ -389,7 +340,12 @@ impl IsAllocableAnyThread for T where /// technically mutable), since it is allowed to mutate through shared /// references. pub trait IsMutable: ClassType {} -impl IsMutable for T where T::Mutability: private::MutabilityIsMutable {} + +trait MutabilityIsMutable: Mutability {} +impl MutabilityIsMutable for Mutable {} +impl MutabilityIsMutable for MutableWithImmutableSuperclass {} + +impl IsMutable for T where T::Mutability: MutabilityIsMutable {} /// Marker trait for classes that are only available on the main thread. /// @@ -402,10 +358,11 @@ impl IsMutable for T where T::Mutability: private::Mutabi // // Note: MainThreadMarker::from relies on this. pub trait IsMainThreadOnly: ClassType {} -impl IsMainThreadOnly for T where - T::Mutability: private::MutabilityIsMainThreadOnly -{ -} + +trait MutabilityIsMainThreadOnly: Mutability {} +impl MutabilityIsMainThreadOnly for MainThreadOnly {} + +impl IsMainThreadOnly for T where T::Mutability: MutabilityIsMainThreadOnly {} /// Marker trait for classes whose `hash` and `isEqual:` methods are stable. /// @@ -425,7 +382,14 @@ impl IsMainThreadOnly for T where // // TODO: Exclude generic types like `NSArray` from this! pub trait HasStableHash: ClassType {} -impl HasStableHash for T where T::Mutability: private::MutabilityHashIsStable {} + +trait MutabilityHashIsStable: Mutability {} +impl MutabilityHashIsStable for Immutable {} +impl MutabilityHashIsStable for Mutable {} +impl MutabilityHashIsStable for ImmutableWithMutableSubclass {} +impl MutabilityHashIsStable for MutableWithImmutableSuperclass {} + +impl HasStableHash for T where T::Mutability: MutabilityHashIsStable {} /// Retrieve the immutable/mutable counterpart class, and fall back to `Self` /// if not applicable. @@ -449,12 +413,61 @@ pub trait CounterpartOrSelf: ClassType { /// `NSMutableString` has itself (`NSMutableString`). type Mutable: ?Sized + ClassType; } + +mod private_counterpart { + use super::*; + + pub trait MutabilityCounterpartOrSelf: Mutability { + type Immutable: ?Sized + ClassType; + type Mutable: ?Sized + ClassType; + } + impl> MutabilityCounterpartOrSelf for Root { + type Immutable = T; + type Mutable = T; + } + impl> MutabilityCounterpartOrSelf for Immutable { + type Immutable = T; + type Mutable = T; + } + impl> MutabilityCounterpartOrSelf for Mutable { + type Immutable = T; + type Mutable = T; + } + impl MutabilityCounterpartOrSelf for ImmutableWithMutableSubclass + where + T: ClassType>, + MS: ClassType>, + { + type Immutable = T; + type Mutable = MS; + } + impl MutabilityCounterpartOrSelf for MutableWithImmutableSuperclass + where + T: ClassType>, + IS: ClassType>, + { + type Immutable = IS; + type Mutable = T; + } + impl> MutabilityCounterpartOrSelf + for InteriorMutable + { + type Immutable = T; + type Mutable = T; + } + impl> MutabilityCounterpartOrSelf for MainThreadOnly { + type Immutable = T; + type Mutable = T; + } +} + impl CounterpartOrSelf for T where - T::Mutability: private::MutabilityCounterpartOrSelf, + T::Mutability: private_counterpart::MutabilityCounterpartOrSelf, { - type Immutable = >::Immutable; - type Mutable = >::Mutable; + type Immutable = + >::Immutable; + type Mutable = >::Mutable; } #[cfg(test)] 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 index ccdbe1f14..cd6376252 100644 --- a/crates/test-ui/ui/declare_class_mut_self_not_mutable.stderr +++ b/crates/test-ui/ui/declare_class_mut_self_not_mutable.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `InteriorMutable: mutability::private::MutabilityIsMutable` is not satisfied +error[E0277]: the trait bound `InteriorMutable: mutability::MutabilityIsMutable` is not satisfied --> ui/declare_class_mut_self_not_mutable.rs | | / declare_class!( @@ -10,10 +10,10 @@ error[E0277]: the trait bound `InteriorMutable: mutability::private::MutabilityI | | ); | | ^ | | | - | |_the trait `mutability::private::MutabilityIsMutable` is not implemented for `InteriorMutable` + | |_the trait `mutability::MutabilityIsMutable` is not implemented for `InteriorMutable` | required by a bound introduced by this call | - = help: the following other types implement trait `mutability::private::MutabilityIsMutable`: + = help: the following other types implement trait `mutability::MutabilityIsMutable`: Mutable MutableWithImmutableSuperclass = note: required for `CustomObject` to implement `IsMutable` @@ -28,7 +28,7 @@ note: required by a bound in `ClassBuilder::add_method` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 +error[E0277]: the trait bound `InteriorMutable: mutability::MutabilityIsMutable` is not satisfied --> ui/declare_class_mut_self_not_mutable.rs | | / declare_class!( @@ -40,10 +40,10 @@ error[E0277]: the trait bound `InteriorMutable: mutability::private::MutabilityI | | ); | | ^ | | | - | |_the trait `mutability::private::MutabilityIsMutable` is not implemented for `InteriorMutable` + | |_the trait `mutability::MutabilityIsMutable` is not implemented for `InteriorMutable` | required by a bound introduced by this call | - = help: the following other types implement trait `mutability::private::MutabilityIsMutable`: + = help: the following other types implement trait `mutability::MutabilityIsMutable`: Mutable MutableWithImmutableSuperclass = note: required for `CustomObject` to implement `IsMutable` @@ -58,7 +58,7 @@ note: required by a bound in `ClassBuilder::add_method` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 +error[E0277]: the trait bound `InteriorMutable: mutability::MutabilityIsMutable` is not satisfied --> ui/declare_class_mut_self_not_mutable.rs | | / declare_class!( @@ -70,10 +70,10 @@ error[E0277]: the trait bound `InteriorMutable: mutability::private::MutabilityI | | ); | | ^ | | | - | |_the trait `mutability::private::MutabilityIsMutable` is not implemented for `InteriorMutable` + | |_the trait `mutability::MutabilityIsMutable` is not implemented for `InteriorMutable` | required by a bound introduced by this call | - = help: the following other types implement trait `mutability::private::MutabilityIsMutable`: + = help: the following other types implement trait `mutability::MutabilityIsMutable`: Mutable MutableWithImmutableSuperclass = note: required for `CustomObject` to implement `IsMutable` diff --git a/crates/test-ui/ui/main_thread_only_not_allocable.stderr b/crates/test-ui/ui/main_thread_only_not_allocable.stderr index b1ac3d47c..8dfb1633d 100644 --- a/crates/test-ui/ui/main_thread_only_not_allocable.stderr +++ b/crates/test-ui/ui/main_thread_only_not_allocable.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `MainThreadOnly: mutability::private::MutabilityIsAllocableAnyThread` is not satisfied +error[E0277]: the trait bound `MainThreadOnly: mutability::MutabilityIsAllocableAnyThread` is not satisfied --> ui/main_thread_only_not_allocable.rs | | let _ = MyMainThreadOnlyClass::alloc(); - | ^^^^^^^^^^^^^^^^^^^^^ the trait `mutability::private::MutabilityIsAllocableAnyThread` is not implemented for `MainThreadOnly` + | ^^^^^^^^^^^^^^^^^^^^^ the trait `mutability::MutabilityIsAllocableAnyThread` is not implemented for `MainThreadOnly` | - = help: the following other types implement trait `mutability::private::MutabilityIsAllocableAnyThread`: + = help: the following other types implement trait `mutability::MutabilityIsAllocableAnyThread`: Root Immutable Mutable diff --git a/crates/test-ui/ui/mainthreadmarker_from_nsobject.rs b/crates/test-ui/ui/mainthreadmarker_from_nsobject.rs new file mode 100644 index 000000000..1ce912cd3 --- /dev/null +++ b/crates/test-ui/ui/mainthreadmarker_from_nsobject.rs @@ -0,0 +1,6 @@ +use icrate::Foundation::{MainThreadMarker, NSObject}; + +fn main() { + let obj = NSObject::new(); + let mtm = MainThreadMarker::from(&*obj); +} diff --git a/crates/test-ui/ui/mainthreadmarker_from_nsobject.stderr b/crates/test-ui/ui/mainthreadmarker_from_nsobject.stderr new file mode 100644 index 000000000..3794e3996 --- /dev/null +++ b/crates/test-ui/ui/mainthreadmarker_from_nsobject.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `Root: mutability::MutabilityIsMainThreadOnly` is not satisfied + --> ui/mainthreadmarker_from_nsobject.rs + | + | let mtm = MainThreadMarker::from(&*obj); + | ---------------------- ^^^^^ the trait `mutability::MutabilityIsMainThreadOnly` is not implemented for `Root` + | | + | required by a bound introduced by this call + | + = help: the trait `mutability::MutabilityIsMainThreadOnly` is implemented for `MainThreadOnly` + = note: required for `NSObject` to implement `IsMainThreadOnly` + = note: required for `MainThreadMarker` to implement `From<&NSObject>` diff --git a/crates/test-ui/ui/mutability_traits_unimplementable.stderr b/crates/test-ui/ui/mutability_traits_unimplementable.stderr index 447783d75..a64dc8752 100644 --- a/crates/test-ui/ui/mutability_traits_unimplementable.stderr +++ b/crates/test-ui/ui/mutability_traits_unimplementable.stderr @@ -6,4 +6,4 @@ error[E0119]: conflicting implementations of trait `IsMutable` for type `CustomO | = note: conflicting implementation in crate `objc2`: - impl IsMutable for T - where T: ClassType, ::Mutability: mutability::private::MutabilityIsMutable, T: ?Sized; + where T: ClassType, ::Mutability: mutability::MutabilityIsMutable, T: ?Sized; 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 index f1e64dd34..0ab547688 100644 --- a/crates/test-ui/ui/mutable_id_not_clone_not_retain.stderr +++ b/crates/test-ui/ui/mutable_id_not_clone_not_retain.stderr @@ -27,13 +27,13 @@ note: the trait `IsIdCloneable` must be implemented | pub trait IsIdCloneable: ClassType {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `Mutable: mutability::private::MutabilityIsRetainable` is not satisfied +error[E0277]: the trait bound `Mutable: mutability::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` + | ^^^^^^ the trait `mutability::MutabilityIsRetainable` is not implemented for `Mutable` | - = help: the following other types implement trait `mutability::private::MutabilityIsRetainable`: + = help: the following other types implement trait `mutability::MutabilityIsRetainable`: Immutable InteriorMutable MainThreadOnly diff --git a/crates/test-ui/ui/nsobject_not_mutable.stderr b/crates/test-ui/ui/nsobject_not_mutable.stderr index 61bda4403..a8882fecb 100644 --- a/crates/test-ui/ui/nsobject_not_mutable.stderr +++ b/crates/test-ui/ui/nsobject_not_mutable.stderr @@ -1,12 +1,12 @@ -error[E0277]: the trait bound `Root: mutability::private::MutabilityIsMutable` is not satisfied +error[E0277]: the trait bound `Root: mutability::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` + | -------------- ^^^^^^^^ the trait `mutability::MutabilityIsMutable` is not implemented for `Root` | | | required by a bound introduced by this call | - = help: the following other types implement trait `mutability::private::MutabilityIsMutable`: + = help: the following other types implement trait `mutability::MutabilityIsMutable`: Mutable MutableWithImmutableSuperclass = note: required for `NSObject` to implement `IsMutable` @@ -19,15 +19,15 @@ note: required by a bound in `Id::::as_mut_ptr` | T: IsMutable, | ^^^^^^^^^ required by this bound in `Id::::as_mut_ptr` -error[E0277]: the trait bound `Root: mutability::private::MutabilityIsMutable` is not satisfied +error[E0277]: the trait bound `Root: mutability::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` + | ------------------- ^^^ the trait `mutability::MutabilityIsMutable` is not implemented for `Root` | | | required by a bound introduced by this call | - = help: the following other types implement trait `mutability::private::MutabilityIsMutable`: + = help: the following other types implement trait `mutability::MutabilityIsMutable`: Mutable MutableWithImmutableSuperclass = note: required for `NSObject` to implement `IsMutable` diff --git a/crates/test-ui/ui/nsset_from_nsobject.stderr b/crates/test-ui/ui/nsset_from_nsobject.stderr index 3fb60164a..2a770840e 100644 --- a/crates/test-ui/ui/nsset_from_nsobject.stderr +++ b/crates/test-ui/ui/nsset_from_nsobject.stderr @@ -1,12 +1,12 @@ -error[E0277]: the trait bound `Root: mutability::private::MutabilityHashIsStable` is not satisfied +error[E0277]: the trait bound `Root: mutability::MutabilityHashIsStable` is not satisfied --> ui/nsset_from_nsobject.rs | | let _ = NSSet::from_vec(vec![NSObject::new()]); - | --------------- ^^^^^^^^^^^^^^^^^^^^^ the trait `mutability::private::MutabilityHashIsStable` is not implemented for `Root` + | --------------- ^^^^^^^^^^^^^^^^^^^^^ the trait `mutability::MutabilityHashIsStable` is not implemented for `Root` | | | required by a bound introduced by this call | - = help: the following other types implement trait `mutability::private::MutabilityHashIsStable`: + = help: the following other types implement trait `mutability::MutabilityHashIsStable`: Immutable Mutable ImmutableWithMutableSubclass From 9351cf4b91ded77507532eb7e2f1e84c464ee094 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 11 Sep 2023 04:59:59 +0200 Subject: [PATCH 4/8] Add UI test for creation of generic types --- crates/test-ui/ui/nsarray_not_message.rs | 8 + crates/test-ui/ui/nsarray_not_message.stderr | 178 +++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 crates/test-ui/ui/nsarray_not_message.rs create mode 100644 crates/test-ui/ui/nsarray_not_message.stderr diff --git a/crates/test-ui/ui/nsarray_not_message.rs b/crates/test-ui/ui/nsarray_not_message.rs new file mode 100644 index 000000000..55c600220 --- /dev/null +++ b/crates/test-ui/ui/nsarray_not_message.rs @@ -0,0 +1,8 @@ +//! Test output of creating `NSArray` from a non-`Message` type. +use icrate::Foundation::{NSArray, NSObject}; +use objc2::rc::Id; + +fn main() { + let _: Id> = NSArray::new(); + let _: Id>> = NSArray::from_slice(&[&NSObject::new()]); +} diff --git a/crates/test-ui/ui/nsarray_not_message.stderr b/crates/test-ui/ui/nsarray_not_message.stderr new file mode 100644 index 000000000..117a5b735 --- /dev/null +++ b/crates/test-ui/ui/nsarray_not_message.stderr @@ -0,0 +1,178 @@ +error[E0277]: the trait bound `i32: Message` is not satisfied + --> ui/nsarray_not_message.rs + | + | let _: Id> = NSArray::new(); + | ^^^^^^^^^^^^^^^^ the trait `Message` is not implemented for `i32` + | + = help: the following other types implement trait `Message`: + Exception + ProtocolObject

+ AnyObject + NSArray + NSMutableArray + NSDictionary + NSMutableDictionary + NSSet + and $N others +note: required by a bound in `NSArray` + --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs + | + | / __inner_extern_class!( + | | #[derive(PartialEq, Eq, Hash)] + | | #[cfg(feature = "Foundation_NSArray")] + | | pub struct NSArray { + | | ------- required by a bound in this struct +... | + | | } + | | ); + | |_^ required by this bound in `NSArray` + = note: this error originates in the macro `__inner_extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Id: Message` is not satisfied + --> ui/nsarray_not_message.rs + | + | let _: Id>> = NSArray::from_slice(&[&NSObject::new()]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Message` is not implemented for `Id` + | + = help: the following other types implement trait `Message`: + Exception + ProtocolObject

+ AnyObject + NSArray + NSMutableArray + NSDictionary + NSMutableDictionary + NSSet + and $N others +note: required by a bound in `NSArray` + --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs + | + | / __inner_extern_class!( + | | #[derive(PartialEq, Eq, Hash)] + | | #[cfg(feature = "Foundation_NSArray")] + | | pub struct NSArray { + | | ------- required by a bound in this struct +... | + | | } + | | ); + | |_^ required by this bound in `NSArray` + = note: this error originates in the macro `__inner_extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `i32: Message` is not satisfied + --> ui/nsarray_not_message.rs + | + | let _: Id> = NSArray::new(); + | ^^^^^^^^^^^^ the trait `Message` is not implemented for `i32` + | + = help: the following other types implement trait `Message`: + Exception + ProtocolObject

+ AnyObject + NSArray + NSMutableArray + NSDictionary + NSMutableDictionary + NSSet + and $N others +note: required by a bound in `Foundation::__NSArray::>::new` + --> $WORKSPACE/crates/icrate/src/generated/Foundation/NSArray.rs + | + | / extern_methods!( + | | /// Methods declared on superclass `NSObject` + | | #[cfg(feature = "Foundation_NSArray")] + | | unsafe impl NSArray { + | | #[method_id(@__retain_semantics New new)] + | | pub fn new() -> Id; + | | --- required by a bound in this associated function + | | } + | | ); + | |_^ required by this bound in `Foundation::__NSArray::>::new` + = note: this error originates in the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `i32: Message` is not satisfied + --> ui/nsarray_not_message.rs + | + | let _: Id> = NSArray::new(); + | ^^^^^^^ the trait `Message` is not implemented for `i32` + | + = help: the following other types implement trait `Message`: + Exception + ProtocolObject

+ AnyObject + NSArray + NSMutableArray + NSDictionary + NSMutableDictionary + NSSet + and $N others +note: required by a bound in `NSArray` + --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs + | + | / __inner_extern_class!( + | | #[derive(PartialEq, Eq, Hash)] + | | #[cfg(feature = "Foundation_NSArray")] + | | pub struct NSArray { + | | ------- required by a bound in this struct +... | + | | } + | | ); + | |_^ required by this bound in `NSArray` + = note: this error originates in the macro `__inner_extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Id: ClassType` is not satisfied + --> ui/nsarray_not_message.rs + | + | let _: Id>> = NSArray::from_slice(&[&NSObject::new()]); + | ------------------- ^^^^^^^^^^^^^^^^^^^ the trait `ClassType` is not implemented for `Id` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `ClassType`: + NSArray + NSMutableArray + NSDictionary + NSMutableDictionary + NSSet + NSMutableSet + NSEnumerator + NSAppleEventDescriptor + and $N others + = note: required for `Id` to implement `IsRetainable` +note: required by a bound in `icrate::Foundation::array::>::from_slice` + --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../additions/Foundation/array.rs + | + | pub fn from_slice(slice: &[&T]) -> Id + | ---------- required by a bound in this associated function + | where + | T: IsRetainable, + | ^^^^^^^^^^^^ required by this bound in `icrate::Foundation::array::>::from_slice` + +error[E0277]: the trait bound `Id: Message` is not satisfied + --> ui/nsarray_not_message.rs + | + | let _: Id>> = NSArray::from_slice(&[&NSObject::new()]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Message` is not implemented for `Id` + | + = help: the following other types implement trait `Message`: + Exception + ProtocolObject

+ AnyObject + NSArray + NSMutableArray + NSDictionary + NSMutableDictionary + NSSet + and $N others +note: required by a bound in `NSArray` + --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs + | + | / __inner_extern_class!( + | | #[derive(PartialEq, Eq, Hash)] + | | #[cfg(feature = "Foundation_NSArray")] + | | pub struct NSArray { + | | ------- required by a bound in this struct +... | + | | } + | | ); + | |_^ required by this bound in `NSArray` + = note: this error originates in the macro `__inner_extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) From 83b4a730d01fc1c67afc9d55659253ff362b88b0 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 11 Sep 2023 06:30:00 +0200 Subject: [PATCH 5/8] Make generic types not strictly require `T: Message` --- crates/header-translator/src/stmt.rs | 26 ++-- crates/icrate/CHANGELOG.md | 2 + crates/icrate/src/fixes/AppKit/mod.rs | 4 +- crates/icrate/src/fixes/Foundation/debug.rs | 8 ++ .../icrate/src/fixes/Foundation/generics.rs | 44 ++++--- crates/icrate/src/generated | 2 +- crates/objc2/src/macros/extern_class.rs | 10 +- .../ui/nsarray_bound_not_send_sync.stderr | 24 ++-- crates/test-ui/ui/nsarray_not_message.stderr | 120 ------------------ 9 files changed, 67 insertions(+), 173 deletions(-) diff --git a/crates/header-translator/src/stmt.rs b/crates/header-translator/src/stmt.rs index a1ac445e7..58ea349da 100644 --- a/crates/header-translator/src/stmt.rs +++ b/crates/header-translator/src/stmt.rs @@ -1381,7 +1381,7 @@ impl fmt::Display for Stmt { if !generics.is_empty() { write!(f, "<")?; for generic in generics { - write!(f, "{generic}: Message = AnyObject, ")?; + write!(f, "{generic}: ?Sized = AnyObject, ")?; } write!(f, ">")?; }; @@ -1411,7 +1411,7 @@ impl fmt::Display for Stmt { writeln!( f, " unsafe impl{} ClassType for {}{} {{", - GenericParamsHelper(generics, "Message"), + GenericParamsHelper(generics, "?Sized + Message"), id.name, GenericTyHelper(generics), )?; @@ -1494,6 +1494,7 @@ impl fmt::Display for Stmt { if let Some(feature) = cls.feature() { writeln!(f, " #[cfg(feature = \"{feature}\")]")?; } + // TODO: Add ?Sized here once `extern_methods!` supports it. writeln!( f, " unsafe impl{} {}{} {{", @@ -1545,6 +1546,7 @@ impl fmt::Display for Stmt { writeln!(f, ");")?; if let Some(method) = methods.iter().find(|method| method.usable_in_default_id()) { + writeln!(f)?; if let Some(feature) = cls.feature() { // Assume new methods require no extra features writeln!(f, " #[cfg(feature = \"{feature}\")]")?; @@ -1574,11 +1576,11 @@ impl fmt::Display for Stmt { // 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), + (_, _) if protocol.is_nsobject() => ("?Sized", None), // Encoding and decoding requires that the inner types // are codable as well. - ("Foundation", "NSCoding") => ("Message + NSCoding", None), - ("Foundation", "NSSecureCoding") => ("Message + NSSecureCoding", None), + ("Foundation", "NSCoding") => ("?Sized + NSCoding", None), + ("Foundation", "NSSecureCoding") => ("?Sized + NSSecureCoding", None), // Copying collections is done as a shallow copy: // // @@ -1588,24 +1590,24 @@ impl fmt::Display for Stmt { // // 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), + ("Foundation", "NSCopying") => ("?Sized + IsIdCloneable", None), + ("Foundation", "NSMutableCopying") => ("?Sized + IsIdCloneable", None), // TODO: Do we need further tweaks to this? - ("Foundation", "NSFastEnumeration") => ("Message", None), + ("Foundation", "NSFastEnumeration") => ("?Sized", None), // AppKit fixes. TODO: Should we add more bounds here? - ("AppKit", "NSCollectionViewDataSource") => ("Message", None), - ("AppKit", "NSTableViewDataSource") => ("Message", None), + ("AppKit", "NSCollectionViewDataSource") => ("?Sized", None), + ("AppKit", "NSTableViewDataSource") => ("?Sized", None), _ => { error!( ?protocol, ?cls, "unknown where bound for generic protocol impl" ); - ("Message", None) + ("?Sized", None) } } } else { - ("Message", None) + ("InvalidGenericBound", None) }; if let Some(feature) = cls.feature() { diff --git a/crates/icrate/CHANGELOG.md b/crates/icrate/CHANGELOG.md index 162306d76..ce992dc9d 100644 --- a/crates/icrate/CHANGELOG.md +++ b/crates/icrate/CHANGELOG.md @@ -67,6 +67,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). `NSSet` creation methods, fixing a long-standing soundess issue. * Fixed the protocol names of `NSAccessibilityElementProtocol`, `NSTextAttachmentCellProtocol` and `NSFileProviderItemProtocol`. +* **BREAKING**: Generic types no longer strictly require `Message` (although + most of their trait implementations still require that). ## icrate 0.0.4 - 2023-07-31 diff --git a/crates/icrate/src/fixes/AppKit/mod.rs b/crates/icrate/src/fixes/AppKit/mod.rs index 44975eff4..2d4d23259 100644 --- a/crates/icrate/src/fixes/AppKit/mod.rs +++ b/crates/icrate/src/fixes/AppKit/mod.rs @@ -43,14 +43,14 @@ extern_class!( __inner_extern_class!( #[cfg(feature = "AppKit_NSLayoutAnchor")] #[derive(Debug, PartialEq, Eq, Hash)] - pub struct NSLayoutAnchor { + 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; diff --git a/crates/icrate/src/fixes/Foundation/debug.rs b/crates/icrate/src/fixes/Foundation/debug.rs index 9066ddb9e..380cfd6e1 100644 --- a/crates/icrate/src/fixes/Foundation/debug.rs +++ b/crates/icrate/src/fixes/Foundation/debug.rs @@ -53,6 +53,14 @@ impl fmt::Debug } } +#[cfg(feature = "Foundation_NSCountedSet")] +impl fmt::Debug for Foundation::NSCountedSet { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + #[cfg(feature = "Foundation_NSMutableSet")] impl fmt::Debug for Foundation::NSMutableSet { #[inline] diff --git a/crates/icrate/src/fixes/Foundation/generics.rs b/crates/icrate/src/fixes/Foundation/generics.rs index 725d7ad51..0399ac9f8 100644 --- a/crates/icrate/src/fixes/Foundation/generics.rs +++ b/crates/icrate/src/fixes/Foundation/generics.rs @@ -18,7 +18,7 @@ impl UnwindSafe for UnsafeIgnoreAutoTraits {} __inner_extern_class!( #[derive(PartialEq, Eq, Hash)] #[cfg(feature = "Foundation_NSArray")] - pub struct NSArray { + pub struct NSArray { // SAFETY: Auto traits specified below. __superclass: UnsafeIgnoreAutoTraits, /// `NSArray` and `NSMutableArray` have `Id`-like storage. @@ -84,7 +84,7 @@ __inner_extern_class!( } #[cfg(feature = "Foundation_NSArray")] - unsafe impl ClassType for NSArray { + unsafe impl ClassType for NSArray { type Super = NSObject; type Mutability = ImmutableWithMutableSubclass>; @@ -101,13 +101,13 @@ __inner_extern_class!( __inner_extern_class!( #[derive(PartialEq, Eq, Hash)] #[cfg(feature = "Foundation_NSArray")] - pub struct NSMutableArray { + pub struct NSMutableArray { // Inherit auto traits from superclass. __superclass: NSArray, } #[cfg(feature = "Foundation_NSArray")] - unsafe impl ClassType for NSMutableArray { + unsafe impl ClassType for NSMutableArray { #[inherits(NSObject)] type Super = NSArray; type Mutability = MutableWithImmutableSuperclass>; @@ -125,7 +125,7 @@ __inner_extern_class!( __inner_extern_class!( #[derive(PartialEq, Eq, Hash)] #[cfg(feature = "Foundation_NSDictionary")] - pub struct NSDictionary { + pub struct NSDictionary { // SAFETY: Auto traits specified below. __superclass: UnsafeIgnoreAutoTraits, // Same as if the dictionary was implemented with: @@ -134,7 +134,9 @@ __inner_extern_class!( } #[cfg(feature = "Foundation_NSDictionary")] - unsafe impl ClassType for NSDictionary { + unsafe impl ClassType + for NSDictionary + { type Super = NSObject; type Mutability = ImmutableWithMutableSubclass>; @@ -151,13 +153,13 @@ __inner_extern_class!( __inner_extern_class!( #[derive(PartialEq, Eq, Hash)] #[cfg(feature = "Foundation_NSDictionary")] - pub struct NSMutableDictionary { + pub struct NSMutableDictionary { // Inherit auto traits from superclass. __superclass: NSDictionary, } #[cfg(feature = "Foundation_NSDictionary")] - unsafe impl ClassType + unsafe impl ClassType for NSMutableDictionary { #[inherits(NSObject)] @@ -177,7 +179,7 @@ __inner_extern_class!( __inner_extern_class!( #[derive(PartialEq, Eq, Hash)] #[cfg(feature = "Foundation_NSSet")] - pub struct NSSet { + pub struct NSSet { // SAFETY: Auto traits specified below. __superclass: UnsafeIgnoreAutoTraits, // Same as if the set was implemented as `NSArray`. @@ -185,7 +187,7 @@ __inner_extern_class!( } #[cfg(feature = "Foundation_NSSet")] - unsafe impl ClassType for NSSet { + unsafe impl ClassType for NSSet { type Super = NSObject; type Mutability = ImmutableWithMutableSubclass>; @@ -202,13 +204,13 @@ __inner_extern_class!( __inner_extern_class!( #[derive(PartialEq, Eq, Hash)] #[cfg(feature = "Foundation_NSSet")] - pub struct NSMutableSet { + pub struct NSMutableSet { // Inherit auto traits from superclass. __superclass: NSSet, } #[cfg(feature = "Foundation_NSSet")] - unsafe impl ClassType for NSMutableSet { + unsafe impl ClassType for NSMutableSet { #[inherits(NSObject)] type Super = NSSet; type Mutability = MutableWithImmutableSuperclass>; @@ -224,15 +226,15 @@ __inner_extern_class!( ); __inner_extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] + #[derive(PartialEq, Eq, Hash)] #[cfg(feature = "Foundation_NSCountedSet")] - pub struct NSCountedSet { + pub struct NSCountedSet { // Inherit auto traits from superclass. __superclass: NSMutableSet, } #[cfg(feature = "Foundation_NSCountedSet")] - unsafe impl ClassType for NSCountedSet { + unsafe impl ClassType for NSCountedSet { #[inherits(NSSet, NSObject)] type Super = NSMutableSet; type Mutability = Mutable; @@ -250,7 +252,7 @@ __inner_extern_class!( __inner_extern_class!( #[derive(PartialEq, Eq, Hash)] #[cfg(feature = "Foundation_NSOrderedSet")] - pub struct NSOrderedSet { + pub struct NSOrderedSet { // SAFETY: Auto traits specified below. __superclass: UnsafeIgnoreAutoTraits, // Same as if the set was implemented with `NSArray`. @@ -258,7 +260,7 @@ __inner_extern_class!( } #[cfg(feature = "Foundation_NSOrderedSet")] - unsafe impl ClassType for NSOrderedSet { + unsafe impl ClassType for NSOrderedSet { type Super = NSObject; type Mutability = ImmutableWithMutableSubclass>; @@ -275,13 +277,13 @@ __inner_extern_class!( __inner_extern_class!( #[derive(PartialEq, Eq, Hash)] #[cfg(feature = "Foundation_NSOrderedSet")] - pub struct NSMutableOrderedSet { + pub struct NSMutableOrderedSet { // Inherit auto traits from superclass. __superclass: NSOrderedSet, } #[cfg(feature = "Foundation_NSOrderedSet")] - unsafe impl ClassType for NSMutableOrderedSet { + unsafe impl ClassType for NSMutableOrderedSet { #[inherits(NSObject)] type Super = NSOrderedSet; type Mutability = MutableWithImmutableSuperclass>; @@ -299,7 +301,7 @@ __inner_extern_class!( __inner_extern_class!( #[derive(Debug, PartialEq, Eq, Hash)] #[cfg(feature = "Foundation_NSEnumerator")] - pub struct NSEnumerator { + pub struct NSEnumerator { // SAFETY: Auto traits specified below. __superclass: UnsafeIgnoreAutoTraits, // Enumerators are basically the same as if we were storing @@ -315,7 +317,7 @@ __inner_extern_class!( } #[cfg(feature = "Foundation_NSEnumerator")] - unsafe impl ClassType for NSEnumerator { + unsafe impl ClassType for NSEnumerator { type Super = NSObject; type Mutability = Mutable; diff --git a/crates/icrate/src/generated b/crates/icrate/src/generated index 486881e1b..cf4125408 160000 --- a/crates/icrate/src/generated +++ b/crates/icrate/src/generated @@ -1 +1 @@ -Subproject commit 486881e1bc7849db110cc6adb9b693462516b254 +Subproject commit cf4125408d57e55376449b5b35d9b78f6b4e4a72 diff --git a/crates/objc2/src/macros/extern_class.rs b/crates/objc2/src/macros/extern_class.rs index 90a95856a..d41a26b85 100644 --- a/crates/objc2/src/macros/extern_class.rs +++ b/crates/objc2/src/macros/extern_class.rs @@ -321,13 +321,13 @@ macro_rules! __impl_as_ref_borrow { macro_rules! __inner_extern_class { ( $(#[$m:meta])* - $v:vis struct $name:ident<$($t_struct:ident $(: $b_struct:ident $(= $default:ty)?)?),* $(,)?> { + $v:vis struct $name:ident<$($t_struct:ident $(: $(?$b_sized_struct:ident)? $($b_struct:ident)? $(= $default:ty)?)?),* $(,)?> { $superclass_field:ident: $superclass_field_ty:ty, $($fields:tt)* } $(#[$impl_m:meta])* - unsafe impl<$($t_for:ident $(: $b_for:ident)?),* $(,)?> ClassType for $for:ty { + unsafe impl<$($t_for:ident $(: $(?$b_sized_for:ident +)? $b_for:ident)?),* $(,)?> ClassType for $for:ty { $(#[inherits($($inheritance_rest:ty),+ $(,)?)])? type Super = $superclass:ty; type Mutability = $mutability:ty; @@ -341,7 +341,7 @@ macro_rules! __inner_extern_class { $crate::__emit_struct! { ($(#[$m])*) ($v) - ($name<$($t_struct $(: $b_struct $(= $default)?)?),*>) + ($name<$($t_struct $(: $(?$b_sized_struct)? $($b_struct)? $(= $default)?)?),*>) ( $superclass_field: $superclass_field_ty, $($fields)* @@ -350,7 +350,7 @@ macro_rules! __inner_extern_class { $crate::__extern_class_impl_traits! { $(#[$impl_m])* - unsafe impl ($($t_for $(: $b_for)?),*) for $for { + unsafe impl ($($t_for $(: $(?$b_sized_for +)? $b_for)?),*) for $for { INHERITS = [$superclass, $($($inheritance_rest,)+)? $crate::runtime::AnyObject]; fn as_super(&$as_super_self) $as_super @@ -359,7 +359,7 @@ macro_rules! __inner_extern_class { } $(#[$impl_m])* - unsafe impl<$($t_for $(: $b_for)?),*> ClassType for $for { + unsafe impl<$($t_for $(: $(?$b_sized_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)?); 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 3f9edc8ca..f3fb24c2e 100644 --- a/crates/test-ui/ui/nsarray_bound_not_send_sync.stderr +++ b/crates/test-ui/ui/nsarray_bound_not_send_sync.stderr @@ -25,7 +25,7 @@ note: required because it appears within the type `PhantomData>` note: required because it appears within the type `NSArray` --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs | - | pub struct NSArray { + | pub struct NSArray { | ^^^^^^^ note: required by a bound in `needs_sync` --> ui/nsarray_bound_not_send_sync.rs @@ -65,7 +65,7 @@ note: required because it appears within the type `PhantomData>` note: required because it appears within the type `NSArray` --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs | - | pub struct NSArray { + | pub struct NSArray { | ^^^^^^^ note: required by a bound in `needs_sync` --> ui/nsarray_bound_not_send_sync.rs @@ -100,7 +100,7 @@ note: required because it appears within the type `PhantomData>` note: required because it appears within the type `NSArray` --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs | - | pub struct NSArray { + | pub struct NSArray { | ^^^^^^^ note: required by a bound in `needs_send` --> ui/nsarray_bound_not_send_sync.rs @@ -151,7 +151,7 @@ note: required because it appears within the type `PhantomData>` note: required because it appears within the type `NSArray` --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs | - | pub struct NSArray { + | pub struct NSArray { | ^^^^^^^ note: required by a bound in `needs_send` --> ui/nsarray_bound_not_send_sync.rs @@ -186,12 +186,12 @@ note: required because it appears within the type `PhantomData>` note: required because it appears within the type `NSArray` --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs | - | pub struct NSArray { + | pub struct NSArray { | ^^^^^^^ note: required because it appears within the type `NSMutableArray` --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs | - | pub struct NSMutableArray { + | pub struct NSMutableArray { | ^^^^^^^^^^^^^^ note: required by a bound in `needs_sync` --> ui/nsarray_bound_not_send_sync.rs @@ -231,12 +231,12 @@ note: required because it appears within the type `PhantomData>` note: required because it appears within the type `NSArray` --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs | - | pub struct NSArray { + | pub struct NSArray { | ^^^^^^^ note: required because it appears within the type `NSMutableArray` --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs | - | pub struct NSMutableArray { + | pub struct NSMutableArray { | ^^^^^^^^^^^^^^ note: required by a bound in `needs_sync` --> ui/nsarray_bound_not_send_sync.rs @@ -271,12 +271,12 @@ note: required because it appears within the type `PhantomData>` note: required because it appears within the type `NSArray` --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs | - | pub struct NSArray { + | pub struct NSArray { | ^^^^^^^ note: required because it appears within the type `NSMutableArray` --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs | - | pub struct NSMutableArray { + | pub struct NSMutableArray { | ^^^^^^^^^^^^^^ note: required by a bound in `needs_send` --> ui/nsarray_bound_not_send_sync.rs @@ -327,12 +327,12 @@ note: required because it appears within the type `PhantomData>` note: required because it appears within the type `NSArray` --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs | - | pub struct NSArray { + | pub struct NSArray { | ^^^^^^^ note: required because it appears within the type `NSMutableArray` --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs | - | pub struct NSMutableArray { + | pub struct NSMutableArray { | ^^^^^^^^^^^^^^ note: required by a bound in `needs_send` --> ui/nsarray_bound_not_send_sync.rs diff --git a/crates/test-ui/ui/nsarray_not_message.stderr b/crates/test-ui/ui/nsarray_not_message.stderr index 117a5b735..f4a9f4ca4 100644 --- a/crates/test-ui/ui/nsarray_not_message.stderr +++ b/crates/test-ui/ui/nsarray_not_message.stderr @@ -1,63 +1,3 @@ -error[E0277]: the trait bound `i32: Message` is not satisfied - --> ui/nsarray_not_message.rs - | - | let _: Id> = NSArray::new(); - | ^^^^^^^^^^^^^^^^ the trait `Message` is not implemented for `i32` - | - = help: the following other types implement trait `Message`: - Exception - ProtocolObject

- AnyObject - NSArray - NSMutableArray - NSDictionary - NSMutableDictionary - NSSet - and $N others -note: required by a bound in `NSArray` - --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs - | - | / __inner_extern_class!( - | | #[derive(PartialEq, Eq, Hash)] - | | #[cfg(feature = "Foundation_NSArray")] - | | pub struct NSArray { - | | ------- required by a bound in this struct -... | - | | } - | | ); - | |_^ required by this bound in `NSArray` - = note: this error originates in the macro `__inner_extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `Id: Message` is not satisfied - --> ui/nsarray_not_message.rs - | - | let _: Id>> = NSArray::from_slice(&[&NSObject::new()]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Message` is not implemented for `Id` - | - = help: the following other types implement trait `Message`: - Exception - ProtocolObject

- AnyObject - NSArray - NSMutableArray - NSDictionary - NSMutableDictionary - NSSet - and $N others -note: required by a bound in `NSArray` - --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs - | - | / __inner_extern_class!( - | | #[derive(PartialEq, Eq, Hash)] - | | #[cfg(feature = "Foundation_NSArray")] - | | pub struct NSArray { - | | ------- required by a bound in this struct -... | - | | } - | | ); - | |_^ required by this bound in `NSArray` - = note: this error originates in the macro `__inner_extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0277]: the trait bound `i32: Message` is not satisfied --> ui/nsarray_not_message.rs | @@ -89,36 +29,6 @@ note: required by a bound in `Foundation::__NSArray::>: | |_^ required by this bound in `Foundation::__NSArray::>::new` = note: this error originates in the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `i32: Message` is not satisfied - --> ui/nsarray_not_message.rs - | - | let _: Id> = NSArray::new(); - | ^^^^^^^ the trait `Message` is not implemented for `i32` - | - = help: the following other types implement trait `Message`: - Exception - ProtocolObject

- AnyObject - NSArray - NSMutableArray - NSDictionary - NSMutableDictionary - NSSet - and $N others -note: required by a bound in `NSArray` - --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs - | - | / __inner_extern_class!( - | | #[derive(PartialEq, Eq, Hash)] - | | #[cfg(feature = "Foundation_NSArray")] - | | pub struct NSArray { - | | ------- required by a bound in this struct -... | - | | } - | | ); - | |_^ required by this bound in `NSArray` - = note: this error originates in the macro `__inner_extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0277]: the trait bound `Id: ClassType` is not satisfied --> ui/nsarray_not_message.rs | @@ -146,33 +56,3 @@ note: required by a bound in `icrate::Foundation::array::>::from | where | T: IsRetainable, | ^^^^^^^^^^^^ required by this bound in `icrate::Foundation::array::>::from_slice` - -error[E0277]: the trait bound `Id: Message` is not satisfied - --> ui/nsarray_not_message.rs - | - | let _: Id>> = NSArray::from_slice(&[&NSObject::new()]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Message` is not implemented for `Id` - | - = help: the following other types implement trait `Message`: - Exception - ProtocolObject

- AnyObject - NSArray - NSMutableArray - NSDictionary - NSMutableDictionary - NSSet - and $N others -note: required by a bound in `NSArray` - --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../fixes/Foundation/generics.rs - | - | / __inner_extern_class!( - | | #[derive(PartialEq, Eq, Hash)] - | | #[cfg(feature = "Foundation_NSArray")] - | | pub struct NSArray { - | | ------- required by a bound in this struct -... | - | | } - | | ); - | |_^ required by this bound in `NSArray` - = note: this error originates in the macro `__inner_extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) From c6f851c1d71f48c23c3cda7beb491e918dd9dbc7 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 11 Sep 2023 16:38:36 +0200 Subject: [PATCH 6/8] Implement mutability marker traits for ProtocolObject as well --- .../icrate/src/additions/Foundation/array.rs | 20 +-- .../src/additions/Foundation/dictionary.rs | 12 +- .../src/additions/Foundation/enumerator.rs | 2 - .../icrate/src/additions/Foundation/iter.rs | 16 +- crates/icrate/src/additions/Foundation/set.rs | 12 +- .../icrate/src/additions/Foundation/util.rs | 2 +- crates/icrate/src/fixes/Foundation/copy.rs | 8 +- crates/icrate/tests/array.rs | 25 ++- crates/icrate/tests/copying.rs | 18 ++ crates/objc2/CHANGELOG.md | 7 + crates/objc2/src/declare/mod.rs | 10 +- crates/objc2/src/message/mod.rs | 4 +- crates/objc2/src/mutability.rs | 159 +++++++++++++++--- crates/objc2/src/rc/id.rs | 8 +- crates/objc2/src/rc/id_traits.rs | 5 +- crates/objc2/src/rc/weak_id.rs | 20 +-- .../ui/mutability_traits_unimplementable.rs | 6 +- .../mutability_traits_unimplementable.stderr | 4 +- .../ui/mutability_traits_unimplementable2.rs | 8 + .../mutability_traits_unimplementable2.stderr | 17 ++ .../ui/mutable_id_not_clone_not_retain.stderr | 4 +- 21 files changed, 273 insertions(+), 94 deletions(-) create mode 100644 crates/icrate/tests/copying.rs create mode 100644 crates/test-ui/ui/mutability_traits_unimplementable2.rs create mode 100644 crates/test-ui/ui/mutability_traits_unimplementable2.stderr diff --git a/crates/icrate/src/additions/Foundation/array.rs b/crates/icrate/src/additions/Foundation/array.rs index 6866222a6..f9cf94136 100644 --- a/crates/icrate/src/additions/Foundation/array.rs +++ b/crates/icrate/src/additions/Foundation/array.rs @@ -391,7 +391,7 @@ __impl_iter! { pub struct IterMut<'a, T: Message>(iter::IterMut<'a, NSArray>); __impl_iter! { - impl<'a, T: IsMutable> Iterator for IterMut<'a, T> { ... } + impl<'a, T: Message + IsMutable> Iterator for IterMut<'a, T> { ... } } /// An iterator that retains the items of a `NSArray`. @@ -399,7 +399,7 @@ __impl_iter! { pub struct IterRetained<'a, T: Message>(iter::IterRetained<'a, NSArray>); __impl_iter! { - impl<'a, T: IsIdCloneable> Iterator> for IterRetained<'a, T> { ... } + impl<'a, T: Message + IsIdCloneable> Iterator> for IterRetained<'a, T> { ... } } /// A consuming iterator over the items of a `NSArray`. @@ -420,16 +420,16 @@ __impl_into_iter! { type IntoIter = Iter<'_, T>; } - impl IntoIterator for &mut NSArray { + impl IntoIterator for &mut NSArray { type IntoIter = IterMut<'_, T>; } #[cfg(feature = "Foundation_NSMutableArray")] - impl IntoIterator for &mut NSMutableArray { + impl IntoIterator for &mut NSMutableArray { type IntoIter = IterMut<'_, T>; } - impl IntoIterator for Id> { + impl IntoIterator for Id> { type IntoIter = IntoIter; } @@ -456,14 +456,14 @@ impl Index for NSMutableArray { } } -impl IndexMut for NSArray { +impl IndexMut for NSArray { fn index_mut(&mut self, index: usize) -> &mut T { self.get_mut(index).unwrap() } } #[cfg(feature = "Foundation_NSMutableArray")] -impl IndexMut for NSMutableArray { +impl IndexMut for NSMutableArray { fn index_mut(&mut self, index: usize) -> &mut T { self.get_mut(index).unwrap() } @@ -484,7 +484,7 @@ impl Extend> for NSMutableArray { } #[cfg(feature = "Foundation_NSMutableArray")] -impl<'a, T: IsRetainable> Extend<&'a T> for NSMutableArray { +impl<'a, T: Message + IsRetainable> Extend<&'a T> for NSMutableArray { fn extend>(&mut self, iter: I) { // SAFETY: Because of the `T: IsRetainable` bound, it is safe for the // array to retain the object here. @@ -493,7 +493,7 @@ impl<'a, T: IsRetainable> Extend<&'a T> for NSMutableArray { } } -impl<'a, T: IsRetainable + 'a> IdFromIterator<&'a T> for NSArray { +impl<'a, T: Message + IsRetainable + 'a> IdFromIterator<&'a T> for NSArray { fn id_from_iter>(iter: I) -> Id { let vec = Vec::from_iter(iter); Self::from_slice(&vec) @@ -508,7 +508,7 @@ impl IdFromIterator> for NSArray { } #[cfg(feature = "Foundation_NSMutableArray")] -impl<'a, T: IsRetainable + 'a> IdFromIterator<&'a T> for NSMutableArray { +impl<'a, T: Message + IsRetainable + 'a> IdFromIterator<&'a T> for NSMutableArray { fn id_from_iter>(iter: I) -> Id { let vec = Vec::from_iter(iter); Self::from_slice(&vec) diff --git a/crates/icrate/src/additions/Foundation/dictionary.rs b/crates/icrate/src/additions/Foundation/dictionary.rs index f2fef8d08..2feac65af 100644 --- a/crates/icrate/src/additions/Foundation/dictionary.rs +++ b/crates/icrate/src/additions/Foundation/dictionary.rs @@ -516,7 +516,7 @@ mod iter_helpers { ); __impl_iter! { - impl<'a, K: IsIdCloneable, V: Message> Iterator> for KeysRetained<'a, K, V> { ... } + impl<'a, K: Message + IsIdCloneable, V: Message> Iterator> for KeysRetained<'a, K, V> { ... } } /// An iterator over the values of a `NSDictionary`. @@ -536,7 +536,7 @@ mod iter_helpers { ); __impl_iter! { - impl<'a, K: Message, V: IsMutable> Iterator for ValuesMut<'a, K, V> { ... } + impl<'a, K: Message, V: Message + IsMutable> Iterator for ValuesMut<'a, K, V> { ... } } /// A iterator that retains the values of a `NSDictionary`. @@ -546,7 +546,7 @@ mod iter_helpers { ); __impl_iter! { - impl<'a, K: Message, V: IsIdCloneable> Iterator> for ValuesRetained<'a, K, V> { ... } + impl<'a, K: Message, V: Message + IsIdCloneable> Iterator> for ValuesRetained<'a, K, V> { ... } } /// A consuming iterator over the values of a `NSDictionary`. @@ -580,14 +580,16 @@ impl<'a, K: Message + Eq + Hash, V: Message> Index<&'a K> for NSMutableDictionar } } -impl<'a, K: Message + Eq + Hash, V: IsMutable> IndexMut<&'a K> for NSDictionary { +impl<'a, K: Message + Eq + Hash, V: Message + IsMutable> IndexMut<&'a K> for NSDictionary { fn index_mut<'s>(&'s mut self, index: &'a K) -> &'s mut V { self.get_mut(index).unwrap() } } #[cfg(feature = "Foundation_NSMutableDictionary")] -impl<'a, K: Message + Eq + Hash, V: IsMutable> IndexMut<&'a K> for NSMutableDictionary { +impl<'a, K: Message + Eq + Hash, V: Message + IsMutable> IndexMut<&'a K> + for NSMutableDictionary +{ fn index_mut<'s>(&'s mut self, index: &'a K) -> &'s mut V { self.get_mut(index).unwrap() } diff --git a/crates/icrate/src/additions/Foundation/enumerator.rs b/crates/icrate/src/additions/Foundation/enumerator.rs index cec199dd9..6a3f09399 100644 --- a/crates/icrate/src/additions/Foundation/enumerator.rs +++ b/crates/icrate/src/additions/Foundation/enumerator.rs @@ -1,7 +1,5 @@ //! Utilities for the `NSEnumerator` class. #![cfg(feature = "Foundation_NSEnumerator")] -use objc2::mutability::{IsIdCloneable, IsMutable}; - use super::iter; use crate::common::*; use crate::Foundation::NSEnumerator; diff --git a/crates/icrate/src/additions/Foundation/iter.rs b/crates/icrate/src/additions/Foundation/iter.rs index 51fa429e1..59b317be0 100644 --- a/crates/icrate/src/additions/Foundation/iter.rs +++ b/crates/icrate/src/additions/Foundation/iter.rs @@ -428,7 +428,7 @@ impl IntoIter { } } - pub(crate) fn new_mutable>(collection: Id) -> Self + pub(crate) fn new_mutable + IsMutable>(collection: Id) -> Self where C: IsIdCloneable, { @@ -648,7 +648,7 @@ where } } - pub(crate) unsafe fn new_mutable>( + pub(crate) unsafe fn new_mutable + IsMutable>( collection: Id, enumerator: Id, ) -> Self @@ -696,9 +696,9 @@ where #[doc(hidden)] macro_rules! __impl_iter { ( - impl<$($lifetime:lifetime, )? $t1:ident: $bound1:ident $(, $t2:ident: $bound2:ident)?> Iterator for $for:ty { ... } + impl<$($lifetime:lifetime, )? $t1:ident: $bound1:ident $(+ $bound1_b:ident)? $(, $t2:ident: $bound2:ident $(+ $bound2_b:ident)?)?> Iterator for $for:ty { ... } ) => { - impl<$($lifetime, )? $t1: $bound1 $(, $t2: $bound2)?> Iterator for $for { + impl<$($lifetime, )? $t1: $bound1 $(+ $bound1_b)? $(, $t2: $bound2 $(+ $bound2_b)?)?> Iterator for $for { type Item = $item; #[inline] @@ -743,14 +743,14 @@ macro_rules! __impl_into_iter { }; ( $(#[$m:meta])* - impl IntoIterator for &mut $ty:ident { + impl IntoIterator for &mut $ty:ident { type IntoIter = $iter_mut:ident<'_, T>; } $($rest:tt)* ) => { $(#[$m])* - impl<'a, T: IsMutable> IntoIterator for &'a mut $ty { + impl<'a, T: Message + IsMutable> IntoIterator for &'a mut $ty { type Item = &'a mut T; type IntoIter = $iter_mut<'a, T>; @@ -766,14 +766,14 @@ macro_rules! __impl_into_iter { }; ( $(#[$m:meta])* - impl IntoIterator for Id<$ty:ident> { + impl IntoIterator for Id<$ty:ident> { type IntoIter = $into_iter:ident; } $($rest:tt)* ) => { $(#[$m])* - impl objc2::rc::IdIntoIterator for $ty { + impl objc2::rc::IdIntoIterator for $ty { type Item = Id; type IntoIter = $into_iter; diff --git a/crates/icrate/src/additions/Foundation/set.rs b/crates/icrate/src/additions/Foundation/set.rs index 0d338b8fb..39eed720a 100644 --- a/crates/icrate/src/additions/Foundation/set.rs +++ b/crates/icrate/src/additions/Foundation/set.rs @@ -501,7 +501,7 @@ __impl_iter! { pub struct IterRetained<'a, T: Message>(iter::IterRetained<'a, NSSet>); __impl_iter! { - impl<'a, T: IsIdCloneable> Iterator> for IterRetained<'a, T> { ... } + impl<'a, T: Message + IsIdCloneable> Iterator> for IterRetained<'a, T> { ... } } /// A consuming iterator over the items of a `NSSet`. @@ -522,7 +522,7 @@ __impl_into_iter! { type IntoIter = Iter<'_, T>; } - impl IntoIterator for Id> { + impl IntoIterator for Id> { type IntoIter = IntoIter; } @@ -549,7 +549,7 @@ impl Extend> for NSMutableSet { } #[cfg(feature = "Foundation_NSMutableSet")] -impl<'a, T: IsRetainable + Eq + Hash + HasStableHash> Extend<&'a T> for NSMutableSet { +impl<'a, T: Message + Eq + Hash + HasStableHash + IsRetainable> Extend<&'a T> for NSMutableSet { fn extend>(&mut self, iter: I) { iter.into_iter().for_each(move |item| { self.insert(item); @@ -557,7 +557,9 @@ impl<'a, T: IsRetainable + Eq + Hash + HasStableHash> Extend<&'a T> for NSMutabl } } -impl<'a, T: IsRetainable + Eq + Hash + HasStableHash + 'a> IdFromIterator<&'a T> for NSSet { +impl<'a, T: Message + Eq + Hash + HasStableHash + IsRetainable + 'a> IdFromIterator<&'a T> + for NSSet +{ fn id_from_iter>(iter: I) -> Id { let vec = Vec::from_iter(iter); Self::from_slice(&vec) @@ -572,7 +574,7 @@ impl IdFromIterator> for NSSet } #[cfg(feature = "Foundation_NSMutableSet")] -impl<'a, T: IsRetainable + Eq + Hash + HasStableHash + 'a> IdFromIterator<&'a T> +impl<'a, T: Message + Eq + Hash + HasStableHash + IsRetainable + 'a> IdFromIterator<&'a T> for NSMutableSet { fn id_from_iter>(iter: I) -> Id { diff --git a/crates/icrate/src/additions/Foundation/util.rs b/crates/icrate/src/additions/Foundation/util.rs index 181dcabc1..e49456b14 100644 --- a/crates/icrate/src/additions/Foundation/util.rs +++ b/crates/icrate/src/additions/Foundation/util.rs @@ -68,7 +68,7 @@ pub(crate) fn id_ptr_cast_const(objects: *const Id) -> *mut NonNul #[inline] pub(crate) unsafe fn collection_retain_id(obj: &T) -> Id where - T: IsIdCloneable, + T: Message + IsIdCloneable, { // SAFETY: We're allowed to access `&Id` from `&self` in collections, // and since `T: IsIdCloneable`, we can convert that to `Id`. diff --git a/crates/icrate/src/fixes/Foundation/copy.rs b/crates/icrate/src/fixes/Foundation/copy.rs index 4f4f03b28..a0eed6d09 100644 --- a/crates/icrate/src/fixes/Foundation/copy.rs +++ b/crates/icrate/src/fixes/Foundation/copy.rs @@ -4,7 +4,7 @@ use crate::common::*; use crate::Foundation::{self, NSCopying, NSMutableCopying}; #[cfg(feature = "Foundation_NSArray")] -impl ToOwned for Foundation::NSArray { +impl ToOwned for Foundation::NSArray { type Owned = Id; fn to_owned(&self) -> Self::Owned { self.copy() @@ -12,7 +12,7 @@ impl ToOwned for Foundation::NSArray { } #[cfg(feature = "Foundation_NSMutableArray")] -impl ToOwned for Foundation::NSMutableArray { +impl ToOwned for Foundation::NSMutableArray { type Owned = Id; fn to_owned(&self) -> Self::Owned { self.mutableCopy() @@ -44,7 +44,7 @@ impl ToOwned for Foundation::NSException { } #[cfg(feature = "Foundation_NSSet")] -impl ToOwned for Foundation::NSSet { +impl ToOwned for Foundation::NSSet { type Owned = Id; fn to_owned(&self) -> Self::Owned { self.copy() @@ -52,7 +52,7 @@ impl ToOwned for Foundation::NSSet { } #[cfg(feature = "Foundation_NSMutableSet")] -impl ToOwned for Foundation::NSMutableSet { +impl ToOwned for Foundation::NSMutableSet { type Owned = Id; fn to_owned(&self) -> Self::Owned { self.mutableCopy() diff --git a/crates/icrate/tests/array.rs b/crates/icrate/tests/array.rs index 68e0ee019..b02cf0229 100644 --- a/crates/icrate/tests/array.rs +++ b/crates/icrate/tests/array.rs @@ -1,8 +1,10 @@ #![cfg(feature = "Foundation_NSArray")] #![cfg(feature = "Foundation_NSNumber")] use icrate::Foundation::{NSArray, NSNumber, NSObject}; -use objc2::rc::Id; -use objc2::rc::{__RcTestObject, __ThreadTestData}; +use objc2::mutability::IsRetainable; +use objc2::rc::{Id, __RcTestObject, __ThreadTestData}; +use objc2::runtime::ProtocolObject; +use objc2::{extern_protocol, ProtocolType}; fn sample_array(len: usize) -> Id> { let mut vec = Vec::with_capacity(len); @@ -262,3 +264,22 @@ fn test_generic_ownership_traits() { assert_partialeq::>(); } + +#[test] +fn test_trait_retainable() { + extern_protocol!( + #[allow(clippy::missing_safety_doc)] + unsafe trait TestProtocol: IsRetainable {} + + unsafe impl ProtocolType for dyn TestProtocol { + const NAME: &'static str = "NSObject"; + } + ); + + unsafe impl TestProtocol for NSNumber {} + + let obj: Id> = ProtocolObject::from_id(NSNumber::new_i32(42)); + let _ = NSArray::from_slice(&[&*obj, &*obj]); + let _ = NSArray::from_id_slice(&[obj.clone(), obj.clone()]); + let _ = NSArray::from_vec(vec![obj.clone(), obj.clone()]); +} diff --git a/crates/icrate/tests/copying.rs b/crates/icrate/tests/copying.rs new file mode 100644 index 000000000..5bd8c4161 --- /dev/null +++ b/crates/icrate/tests/copying.rs @@ -0,0 +1,18 @@ +#![cfg(feature = "Foundation")] +#![cfg(feature = "Foundation_NSString")] +use icrate::Foundation::{NSCopying, NSMutableCopying, NSString}; +use objc2::{rc::Id, runtime::ProtocolObject}; + +#[test] +fn copy() { + let obj = NSString::new(); + let protocol_object: &ProtocolObject = ProtocolObject::from_ref(&*obj); + let _: Id> = protocol_object.copy(); +} + +#[test] +fn copy_mutable() { + let obj = NSString::new(); + let protocol_object: &ProtocolObject = ProtocolObject::from_ref(&*obj); + let _: Id> = protocol_object.mutableCopy(); +} diff --git a/crates/objc2/CHANGELOG.md b/crates/objc2/CHANGELOG.md index 2ed8ef40e..30b4e28db 100644 --- a/crates/objc2/CHANGELOG.md +++ b/crates/objc2/CHANGELOG.md @@ -18,6 +18,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed * **BREAKING**: `AnyClass::verify_sel` now take more well-defined types `EncodeArguments` and `EncodeReturn`. +* **BREAKING**: Changed how the `mutability` traits work; these no longer have + `ClassType` as a super trait, allowing them to work for `ProtocolObject` as + well. + + This effectively means you can now `copy` a `ProtocolObject`. +* **BREAKING**: Allow implementing `DefaultId` for any type, not just those + who are `IsAllocableAnyThread`. ### Deprecated * Soft deprecated using `msg_send!` without a comma between arguments (i.e. diff --git a/crates/objc2/src/declare/mod.rs b/crates/objc2/src/declare/mod.rs index 52a7f3777..2accc2688 100644 --- a/crates/objc2/src/declare/mod.rs +++ b/crates/objc2/src/declare/mod.rs @@ -167,17 +167,17 @@ pub trait MethodImplementation: private::Sealed + Sized { } macro_rules! method_decl_impl { - (@<$($l:lifetime),*> T: $t_bound:ident, $r:ident, $f:ty, $($t:ident),*) => { + (@<$($l:lifetime),*> T: $t_bound:ident $(+ $t_bound2:ident)?, $r:ident, $f:ty, $($t:ident),*) => { impl<$($l,)* T, $r, $($t),*> private::Sealed for $f where - T: ?Sized + $t_bound, + T: ?Sized + $t_bound $(+ $t_bound2)?, $r: EncodeReturn, $($t: EncodeArgument,)* {} impl<$($l,)* T, $r, $($t),*> MethodImplementation for $f where - T: ?Sized + $t_bound, + T: ?Sized + $t_bound $(+ $t_bound2)?, $r: EncodeReturn, $($t: EncodeArgument,)* { @@ -244,11 +244,11 @@ macro_rules! method_decl_impl { }; (# $abi:literal; $($t:ident),*) => { 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!(@<'a> T: Message + 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> T: Message + IsMutable, R, unsafe extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*); method_decl_impl!(@<'a> AnyObject, R, extern $abi fn(&'a mut AnyObject, Sel $(, $t)*) -> R, $($t),*); method_decl_impl!(@<'a> AnyObject, R, unsafe extern $abi fn(&'a mut AnyObject, Sel $(, $t)*) -> R, $($t),*); diff --git a/crates/objc2/src/message/mod.rs b/crates/objc2/src/message/mod.rs index 4e797c330..e2dbdf13b 100644 --- a/crates/objc2/src/message/mod.rs +++ b/crates/objc2/src/message/mod.rs @@ -448,8 +448,8 @@ unsafe impl<'a, T: ?Sized + Message> MessageReceiver for &'a Id { } } -impl<'a, T: ?Sized + IsMutable> private::Sealed for &'a mut Id {} -unsafe impl<'a, T: ?Sized + IsMutable> MessageReceiver for &'a mut Id { +impl<'a, T: ?Sized + Message + IsMutable> private::Sealed for &'a mut Id {} +unsafe impl<'a, T: ?Sized + Message + IsMutable> MessageReceiver for &'a mut Id { type __Inner = T; #[inline] diff --git a/crates/objc2/src/mutability.rs b/crates/objc2/src/mutability.rs index 2e621fd0f..22a3cdb45 100644 --- a/crates/objc2/src/mutability.rs +++ b/crates/objc2/src/mutability.rs @@ -32,7 +32,8 @@ //! bug. use core::marker::PhantomData; -use crate::ClassType; +use crate::runtime::ProtocolObject; +use crate::{ClassType, Message, ProtocolType}; mod private_mutability { pub trait Sealed {} @@ -259,6 +260,13 @@ pub struct MainThreadOnly { inner: Never, } +mod private_traits { + pub trait Sealed {} +} + +impl private_traits::Sealed for T {} +impl private_traits::Sealed for ProtocolObject

{} + /// Marker trait for classes where [`Id::clone`] is safe. /// /// Since the `Foundation` collection types (`NSArray`, @@ -274,7 +282,13 @@ pub struct MainThreadOnly { /// /// [`Id`]: crate::rc::Id /// [`Id::clone`]: crate::rc::Id#impl-Clone-for-Id -pub trait IsIdCloneable: ClassType {} +/// +/// +/// # Safety +/// +/// 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 unsafe trait IsIdCloneable: private_traits::Sealed {} trait MutabilityIsIdCloneable: Mutability {} impl MutabilityIsIdCloneable for Root {} @@ -283,7 +297,8 @@ impl MutabilityIsIdCloneable for ImmutableWithMutableSubclass {} impl MutabilityIsIdCloneable for InteriorMutable {} impl MutabilityIsIdCloneable for MainThreadOnly {} -impl IsIdCloneable for T where T::Mutability: MutabilityIsIdCloneable {} +unsafe impl IsIdCloneable for T where T::Mutability: MutabilityIsIdCloneable {} +unsafe impl IsIdCloneable for ProtocolObject

{} /// Marker trait for classes where the `retain` selector is always safe. /// @@ -296,15 +311,25 @@ impl IsIdCloneable for T where T::Mutability: MutabilityI /// - [`InteriorMutable`]. /// - [`MainThreadOnly`]. /// +/// This trait inherits [`IsIdCloneable`], so if a function is bound by this, +/// functionality given with that trait is available. +/// /// [`Id::clone`]: crate::rc::Id#impl-Clone-for-Id -pub trait IsRetainable: IsIdCloneable {} +/// +/// +/// # Safety +/// +/// 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 unsafe trait IsRetainable: IsIdCloneable {} trait MutabilityIsRetainable: MutabilityIsIdCloneable {} impl MutabilityIsRetainable for Immutable {} impl MutabilityIsRetainable for InteriorMutable {} impl MutabilityIsRetainable for MainThreadOnly {} -impl IsRetainable for T where T::Mutability: MutabilityIsRetainable {} +unsafe impl IsRetainable for T where T::Mutability: MutabilityIsRetainable {} +unsafe impl IsRetainable for ProtocolObject

{} /// Marker trait for classes that can be allocated from any thread. /// @@ -315,7 +340,13 @@ impl IsRetainable for T where T::Mutability: MutabilityIs /// - [`ImmutableWithMutableSubclass`]. /// - [`MutableWithImmutableSuperclass`]. /// - [`InteriorMutable`]. -pub trait IsAllocableAnyThread: ClassType {} +/// +/// +/// # Safety +/// +/// 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 unsafe trait IsAllocableAnyThread: private_traits::Sealed {} trait MutabilityIsAllocableAnyThread: Mutability {} impl MutabilityIsAllocableAnyThread for Root {} @@ -325,10 +356,14 @@ impl MutabilityIsAllocableAnyThread for ImmutableWithMutableSubclass impl MutabilityIsAllocableAnyThread for MutableWithImmutableSuperclass {} impl MutabilityIsAllocableAnyThread for InteriorMutable {} -impl IsAllocableAnyThread for T where +unsafe impl IsAllocableAnyThread for T where T::Mutability: MutabilityIsAllocableAnyThread { } +unsafe impl IsAllocableAnyThread + for ProtocolObject

+{ +} /// Marker trait for classes that are only mutable through `&mut`. /// @@ -339,13 +374,20 @@ impl IsAllocableAnyThread for T where /// Notably, [`InteriorMutable`] does not implement this (though it is /// technically mutable), since it is allowed to mutate through shared /// references. -pub trait IsMutable: ClassType {} +/// +/// +/// # Safety +/// +/// 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 unsafe trait IsMutable: private_traits::Sealed {} trait MutabilityIsMutable: Mutability {} impl MutabilityIsMutable for Mutable {} impl MutabilityIsMutable for MutableWithImmutableSuperclass {} -impl IsMutable for T where T::Mutability: MutabilityIsMutable {} +unsafe impl IsMutable for T where T::Mutability: MutabilityIsMutable {} +unsafe impl IsMutable for ProtocolObject

{} /// Marker trait for classes that are only available on the main thread. /// @@ -354,15 +396,23 @@ impl IsMutable for T where T::Mutability: MutabilityIsMut /// /// Since `MainThreadOnly` types must be `!Send` and `!Sync`, if you hold a /// type that implements this trait, then you're guaranteed to be on the main -/// thread. -// -// Note: MainThreadMarker::from relies on this. -pub trait IsMainThreadOnly: ClassType {} +/// thread (and can get a `MainThreadMarker` using `MainThreadMarker::from`). +/// +/// +/// # Safety +/// +/// 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 unsafe trait IsMainThreadOnly: private_traits::Sealed {} trait MutabilityIsMainThreadOnly: Mutability {} impl MutabilityIsMainThreadOnly for MainThreadOnly {} -impl IsMainThreadOnly for T where T::Mutability: MutabilityIsMainThreadOnly {} +unsafe impl IsMainThreadOnly for T where + T::Mutability: MutabilityIsMainThreadOnly +{ +} +unsafe impl IsMainThreadOnly for ProtocolObject

{} /// Marker trait for classes whose `hash` and `isEqual:` methods are stable. /// @@ -379,9 +429,15 @@ impl IsMainThreadOnly for T where T::Mutability: Mutabili /// and `isEqual:` methods are required to not use external sources like /// thread locals or randomness to determine their result, we can guarantee /// that the hash is stable for these types. +/// +/// +/// # Safety +/// +/// 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! // // TODO: Exclude generic types like `NSArray` from this! -pub trait HasStableHash: ClassType {} +pub unsafe trait HasStableHash: private_traits::Sealed {} trait MutabilityHashIsStable: Mutability {} impl MutabilityHashIsStable for Immutable {} @@ -389,37 +445,57 @@ impl MutabilityHashIsStable for Mutable {} impl MutabilityHashIsStable for ImmutableWithMutableSubclass {} impl MutabilityHashIsStable for MutableWithImmutableSuperclass {} -impl HasStableHash for T where T::Mutability: MutabilityHashIsStable {} +unsafe impl HasStableHash for T where T::Mutability: MutabilityHashIsStable {} +unsafe impl HasStableHash for ProtocolObject

{} /// Retrieve the immutable/mutable counterpart class, and fall back to `Self` /// if not applicable. /// -/// This is mostly used for describing the return type of `NSCopying` and +/// This is used for describing the return type of `NSCopying` and /// `NSMutableCopying`, since due to Rust trait limitations, those two can't -/// have associated types themselves (at least not since we want to use them -/// in `ProtocolObject`). -pub trait CounterpartOrSelf: ClassType { +/// have associated types themselves (since we want to use them in +/// `ProtocolObject`). +/// +/// +/// # Usage notes +/// +/// You may not rely on this being implemented entirely correctly for protocol +/// objects, since we have less type-information available there. +/// +/// In particular, the immutable counterpart of a mutable object converted to +/// `ProtocolObject` may not itself implement the protocol, and +/// invalidly assuming it does is unsound. +/// +/// All of this is to say: Do not use this trait in isolation, either require +/// `NSCopying` or `ClassType` along with it. +/// +/// +/// # Safety +/// +/// 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 unsafe trait CounterpartOrSelf: private_traits::Sealed { /// The immutable counterpart of the type, or `Self` if the type has no /// immutable counterpart. /// /// The implementation for `NSString` has itself (`NSString`) here, while /// `NSMutableString` instead has `NSString`. - type Immutable: ?Sized + ClassType; + type Immutable: ?Sized + Message; /// The mutable counterpart of the type, or `Self` if the type has no /// mutable counterpart. /// /// The implementation for `NSString` has `NSMutableString` here, while /// `NSMutableString` has itself (`NSMutableString`). - type Mutable: ?Sized + ClassType; + type Mutable: ?Sized + Message; } mod private_counterpart { use super::*; pub trait MutabilityCounterpartOrSelf: Mutability { - type Immutable: ?Sized + ClassType; - type Mutable: ?Sized + ClassType; + type Immutable: ?Sized + Message; + type Mutable: ?Sized + Message; } impl> MutabilityCounterpartOrSelf for Root { type Immutable = T; @@ -461,7 +537,7 @@ mod private_counterpart { } } -impl CounterpartOrSelf for T +unsafe impl CounterpartOrSelf for T where T::Mutability: private_counterpart::MutabilityCounterpartOrSelf, { @@ -470,6 +546,27 @@ where type Mutable = >::Mutable; } +unsafe impl CounterpartOrSelf for ProtocolObject

{ + // SAFETY: The only place where this would differ from `Self` is for + // classes with `MutableWithImmutableSuperclass`. + // + // Superclasses are not in general required to implement the same traits + // as their subclasses, but we're not dealing with normal classes, we're + // dealing with with immutable/mutable class counterparts! + // + // We could probably get away with requiring that mutable classes + // only implement the same protocols as their immutable counterparts, but + // for now we relax the requirements of `CounterpartOrSelf`. + type Immutable = Self; + // SAFETY: The only place where this would differ from `Self` is for + // classes with `ImmutableWithMutableSubclass`. + // + // But subclasses are required to always implement the same traits as + // their superclasses, so a mutable subclass is required to implement the + // same traits too. + type Mutable = Self; +} + #[cfg(test)] mod tests { use crate::runtime::NSObject; @@ -515,4 +612,16 @@ mod tests { TypeId::of::<::Mutable>(), ); } + + #[allow(unused)] + fn object_safe( + _: &dyn IsIdCloneable, + _: &dyn IsRetainable, + _: &dyn IsAllocableAnyThread, + _: &dyn IsMutable, + _: &dyn IsMainThreadOnly, + _: &dyn HasStableHash, + _: &dyn CounterpartOrSelf, + ) { + } } diff --git a/crates/objc2/src/rc/id.rs b/crates/objc2/src/rc/id.rs index a428982a6..c35af57c2 100644 --- a/crates/objc2/src/rc/id.rs +++ b/crates/objc2/src/rc/id.rs @@ -621,7 +621,7 @@ where } // 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. @@ -815,12 +815,12 @@ where // // 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 {} // TODO: Relax this bound -impl UnwindSafe for Id {} +impl UnwindSafe for Id {} #[cfg(test)] mod tests { diff --git a/crates/objc2/src/rc/id_traits.rs b/crates/objc2/src/rc/id_traits.rs index 38db37acc..4ab267273 100644 --- a/crates/objc2/src/rc/id_traits.rs +++ b/crates/objc2/src/rc/id_traits.rs @@ -1,11 +1,10 @@ //! Helper traits for Id. use super::Id; -use crate::mutability::{IsAllocableAnyThread, IsMutable}; +use crate::mutability::IsMutable; /// Helper trait to implement [`Default`] on [`Id`]. -// TODO: Maybe make this `unsafe` and provide a default implementation? -pub trait DefaultId: IsAllocableAnyThread { +pub trait DefaultId { /// The default [`Id`] for a type. /// /// On most objects the implementation would be sending a message to the diff --git a/crates/objc2/src/rc/weak_id.rs b/crates/objc2/src/rc/weak_id.rs index ead36e0bd..9f13e1211 100644 --- a/crates/objc2/src/rc/weak_id.rs +++ b/crates/objc2/src/rc/weak_id.rs @@ -116,7 +116,7 @@ impl Drop for WeakId { } // TODO: Add ?Sized -impl Clone for WeakId { +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 { @@ -130,7 +130,7 @@ impl Clone for WeakId { } // TODO: Add ?Sized -impl Default for WeakId { +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`]. @@ -151,35 +151,35 @@ impl fmt::Debug for WeakId { } // Same as `std::sync::Weak`. -unsafe impl Sync for WeakId {} +unsafe impl Sync for WeakId {} // Same as `std::sync::Weak`. -unsafe impl Send for WeakId {} +unsafe impl Send for WeakId {} // Same as `std::sync::Weak`. -impl Unpin for WeakId {} +impl Unpin for WeakId {} // Same as `std::sync::Weak`. -impl RefUnwindSafe for WeakId {} +impl RefUnwindSafe for WeakId {} // Same as `std::sync::Weak`. -impl UnwindSafe for WeakId {} +impl UnwindSafe for WeakId {} -impl From<&T> for WeakId { +impl From<&T> for WeakId { #[inline] fn from(obj: &T) -> Self { WeakId::new(obj) } } -impl From<&Id> for WeakId { +impl From<&Id> for WeakId { #[inline] fn from(obj: &Id) -> Self { WeakId::from_id(obj) } } -impl From> for WeakId { +impl From> for WeakId { #[inline] fn from(obj: Id) -> Self { WeakId::from_id(&obj) diff --git a/crates/test-ui/ui/mutability_traits_unimplementable.rs b/crates/test-ui/ui/mutability_traits_unimplementable.rs index 8192b1091..96fe63505 100644 --- a/crates/test-ui/ui/mutability_traits_unimplementable.rs +++ b/crates/test-ui/ui/mutability_traits_unimplementable.rs @@ -1,6 +1,4 @@ -//! Check that the `mutability` traits are not implementable manually. -//! -//! Since they are not `unsafe`, it would be a soundness hole if you could. +//! Check that `mutability` traits are not implementable manually. use objc2::runtime::NSObject; use objc2::{declare_class, mutability, ClassType}; @@ -14,6 +12,6 @@ declare_class!( } ); -impl mutability::IsMutable for CustomObject {} +unsafe 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 index a64dc8752..8f857db41 100644 --- a/crates/test-ui/ui/mutability_traits_unimplementable.stderr +++ b/crates/test-ui/ui/mutability_traits_unimplementable.stderr @@ -1,8 +1,8 @@ error[E0119]: conflicting implementations of trait `IsMutable` for type `CustomObject` --> ui/mutability_traits_unimplementable.rs | - | impl mutability::IsMutable for CustomObject {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | unsafe impl mutability::IsMutable for CustomObject {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `objc2`: - impl IsMutable for T diff --git a/crates/test-ui/ui/mutability_traits_unimplementable2.rs b/crates/test-ui/ui/mutability_traits_unimplementable2.rs new file mode 100644 index 000000000..d75828407 --- /dev/null +++ b/crates/test-ui/ui/mutability_traits_unimplementable2.rs @@ -0,0 +1,8 @@ +//! Check that `mutability` traits are not implementable manually. +use objc2::mutability; + +struct CustomStruct; + +unsafe impl mutability::IsMutable for CustomStruct {} + +fn main() {} diff --git a/crates/test-ui/ui/mutability_traits_unimplementable2.stderr b/crates/test-ui/ui/mutability_traits_unimplementable2.stderr new file mode 100644 index 000000000..a73104fe5 --- /dev/null +++ b/crates/test-ui/ui/mutability_traits_unimplementable2.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `CustomStruct: ClassType` is not satisfied + --> ui/mutability_traits_unimplementable2.rs + | + | unsafe impl mutability::IsMutable for CustomStruct {} + | ^^^^^^^^^^^^ the trait `ClassType` is not implemented for `CustomStruct` + | + = help: the following other types implement trait `ClassType`: + __RcTestObject + NSObject + __NSProxy + = note: required for `CustomStruct` to implement `mutability::private_traits::Sealed` +note: required by a bound in `IsMutable` + --> $WORKSPACE/crates/objc2/src/mutability.rs + | + | pub unsafe trait IsMutable: private_traits::Sealed {} + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `IsMutable` + = note: `IsMutable` is a "sealed trait", because to implement it you also need to implement `objc2::mutability::private_traits::Sealed`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it 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 index 0ab547688..0a3019975 100644 --- a/crates/test-ui/ui/mutable_id_not_clone_not_retain.stderr +++ b/crates/test-ui/ui/mutable_id_not_clone_not_retain.stderr @@ -24,8 +24,8 @@ error[E0599]: the method `clone` exists for struct `Id`, but it note: the trait `IsIdCloneable` must be implemented --> $WORKSPACE/crates/objc2/src/mutability.rs | - | pub trait IsIdCloneable: ClassType {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | pub unsafe trait IsIdCloneable: private_traits::Sealed {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `Mutable: mutability::MutabilityIsRetainable` is not satisfied --> ui/mutable_id_not_clone_not_retain.rs From 9b30f3b7cd6193a8a7a4702a7042064d9f22c5ea Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 11 Sep 2023 06:20:57 +0200 Subject: [PATCH 7/8] Emit main thread requirements on protocols --- crates/header-translator/src/cache.rs | 47 ++++++++++----- crates/header-translator/src/config.rs | 3 + crates/header-translator/src/data/AppKit.rs | 11 ++++ .../header-translator/src/data/Automator.rs | 1 + crates/header-translator/src/data/OSAKit.rs | 1 + crates/header-translator/src/method.rs | 3 +- crates/header-translator/src/stmt.rs | 57 ++++++++++++++++++- .../header-translator/translation-config.toml | 16 +++++- crates/icrate/CHANGELOG.md | 8 ++- crates/icrate/src/common.rs | 4 +- crates/icrate/src/generated | 2 +- crates/test-ui/Cargo.toml | 3 + ...clare_class_delegate_not_mainthreadonly.rs | 43 ++++++++++++++ ...e_class_delegate_not_mainthreadonly.stderr | 21 +++++++ .../ui/implement_protocol_missing_super.rs | 19 +++++++ .../implement_protocol_missing_super.stderr | 29 ++++++++++ .../test-ui/ui/msg_send_invalid_error.stderr | 10 ++-- crates/test-ui/ui/nsarray_not_message.stderr | 26 ++++----- 18 files changed, 261 insertions(+), 43 deletions(-) create mode 100644 crates/test-ui/ui/declare_class_delegate_not_mainthreadonly.rs create mode 100644 crates/test-ui/ui/declare_class_delegate_not_mainthreadonly.stderr create mode 100644 crates/test-ui/ui/implement_protocol_missing_super.rs create mode 100644 crates/test-ui/ui/implement_protocol_missing_super.stderr diff --git a/crates/header-translator/src/cache.rs b/crates/header-translator/src/cache.rs index b536cab70..24c9a9b50 100644 --- a/crates/header-translator/src/cache.rs +++ b/crates/header-translator/src/cache.rs @@ -13,23 +13,44 @@ use crate::Mutability; #[derive(Debug, PartialEq, Clone)] pub struct Cache<'a> { config: &'a Config, - mainthreadonly_classes: BTreeSet, + mainthreadonly_items: BTreeSet, } impl<'a> Cache<'a> { pub fn new(output: &Output, config: &'a Config) -> Self { - let mut mainthreadonly_classes = BTreeSet::new(); + let mut mainthreadonly_items = BTreeSet::new(); for library in output.libraries.values() { for file in library.files.values() { for stmt in file.stmts.iter() { - if let Stmt::ClassDecl { - id, - mutability: Mutability::MainThreadOnly, - .. - } = stmt - { - mainthreadonly_classes.insert(id.clone()); + match stmt { + Stmt::ClassDecl { + id, + mutability: Mutability::MainThreadOnly, + .. + } => { + mainthreadonly_items.insert(id.clone()); + } + Stmt::ProtocolDecl { + id, + required_mainthreadonly: true, + .. + } => { + mainthreadonly_items.insert(id.clone()); + } + Stmt::ProtocolDecl { + id, + required_mainthreadonly: false, + protocols, + .. + } => { + for protocol in protocols { + if mainthreadonly_items.contains(protocol) { + let _ = mainthreadonly_items.insert(id.clone()); + } + } + } + _ => {} } } } @@ -37,7 +58,7 @@ impl<'a> Cache<'a> { Self { config, - mainthreadonly_classes, + mainthreadonly_items, } } @@ -100,7 +121,7 @@ impl<'a> Cache<'a> { for method in methods.iter_mut() { let mut result_type_contains_mainthreadonly: bool = false; method.result_type.visit_required_types(&mut |id| { - if self.mainthreadonly_classes.contains(id) { + if self.mainthreadonly_items.contains(id) { result_type_contains_mainthreadonly = true; } }); @@ -111,13 +132,13 @@ impl<'a> Cache<'a> { // include optional arguments like `Option<&NSView>` or // `&NSArray`. argument.visit_toplevel_types(&mut |id| { - if self.mainthreadonly_classes.contains(id) { + if self.mainthreadonly_items.contains(id) { any_argument_contains_mainthreadonly = true; } }); } - if self.mainthreadonly_classes.contains(id) { + if self.mainthreadonly_items.contains(id) { if method.is_class { // Assume the method needs main thread if it's // declared on a main thread only class. diff --git a/crates/header-translator/src/config.rs b/crates/header-translator/src/config.rs index 8a82abaf0..703da63de 100644 --- a/crates/header-translator/src/config.rs +++ b/crates/header-translator/src/config.rs @@ -146,6 +146,9 @@ pub struct ProtocolData { #[serde(default)] pub skipped: bool, #[serde(default)] + #[serde(rename = "requires-mainthreadonly")] + pub requires_mainthreadonly: Option, + #[serde(default)] pub methods: HashMap, } diff --git a/crates/header-translator/src/data/AppKit.rs b/crates/header-translator/src/data/AppKit.rs index 57bc56ac4..6f023800e 100644 --- a/crates/header-translator/src/data/AppKit.rs +++ b/crates/header-translator/src/data/AppKit.rs @@ -30,6 +30,13 @@ data! { // `run` cannot be safe, the user must ensure there is no re-entrancy. } + class NSController: MainThreadOnly {} + class NSObjectController: MainThreadOnly {} + class NSArrayController: MainThreadOnly {} + class NSDictionaryController: MainThreadOnly {} + class NSTreeController: MainThreadOnly {} + class NSUserDefaultsController: MainThreadOnly {} + // Documentation says: // > Color objects are immutable and thread-safe // @@ -38,6 +45,8 @@ data! { unsafe -clear; } + class NSColorPicker: MainThreadOnly {} + class NSControl { unsafe -isEnabled; unsafe -setEnabled; @@ -81,6 +90,8 @@ data! { } + class NSFontManager: MainThreadOnly {} + // Documented Thread-Unsafe, but: // > One thread can create an NSImage object, draw to the image buffer, // > and pass it off to the main thread for drawing. The underlying image diff --git a/crates/header-translator/src/data/Automator.rs b/crates/header-translator/src/data/Automator.rs index 89a871f29..9902f69f3 100644 --- a/crates/header-translator/src/data/Automator.rs +++ b/crates/header-translator/src/data/Automator.rs @@ -1,2 +1,3 @@ data! { + class AMWorkflowController: MainThreadOnly {} } diff --git a/crates/header-translator/src/data/OSAKit.rs b/crates/header-translator/src/data/OSAKit.rs index 89a871f29..5391fdea3 100644 --- a/crates/header-translator/src/data/OSAKit.rs +++ b/crates/header-translator/src/data/OSAKit.rs @@ -1,2 +1,3 @@ data! { + class OSAScriptController: MainThreadOnly {} } diff --git a/crates/header-translator/src/method.rs b/crates/header-translator/src/method.rs index 4e1987531..7a3d97b8e 100644 --- a/crates/header-translator/src/method.rs +++ b/crates/header-translator/src/method.rs @@ -700,8 +700,7 @@ impl fmt::Display for Method { let param = handle_reserved(&crate::to_snake_case(param)); write!(f, "{param}: {arg_ty}, ")?; } - // FIXME: Skipping main thread only on protocols for now - if self.mainthreadonly && !self.is_protocol { + if self.mainthreadonly { write!(f, "mtm: MainThreadMarker")?; } write!(f, ")")?; diff --git a/crates/header-translator/src/stmt.rs b/crates/header-translator/src/stmt.rs index 58ea349da..0413fa514 100644 --- a/crates/header-translator/src/stmt.rs +++ b/crates/header-translator/src/stmt.rs @@ -698,7 +698,13 @@ impl Stmt { verify_objc_decl(entity, context); let generics = parse_class_generics(entity, context); - let protocols = parse_direct_protocols(entity, context); + let mut protocols = parse_direct_protocols(entity, context); + + let skipped_protocols = data + .map(|data| data.skipped_protocols.clone()) + .unwrap_or_default(); + protocols.retain(|protocol| !skipped_protocols.contains(&protocol.name)); + let (methods, designated_initializers) = parse_methods( entity, |name| ClassData::get_method_data(data, name), @@ -825,7 +831,7 @@ impl Stmt { context, ); - let (sendable, mainthreadonly) = parse_attributes(entity, context); + let (sendable, mut mainthreadonly) = parse_attributes(entity, context); if !designated_initializers.is_empty() { warn!( @@ -834,6 +840,43 @@ impl Stmt { ) } + // Set the protocol as main thread only if all methods are + // main thread only. + // + // This is done to make the UI nicer when the user tries to + // implement such traits. + // + // Note: This is a deviation from the headers, but I don't + // see a way for this to be unsound? As an example, let's say + // there is some Objective-C code that assumes it can create + // an object which is not `MainThreadOnly`, and then sets it + // as the application delegate. + // + // Rust code that later retrieves the delegate would assume + // that the object is `MainThreadOnly`, and could use this + // information to create `MainThreadMarker`; but they can + // _already_ do that, since the only way to retrieve the + // delegate in the first place would be through + // `NSApplication`! + if !methods.is_empty() && methods.iter().all(|method| method.mainthreadonly) { + mainthreadonly = true; + } + + // Overwrite with config preference + if let Some(data) = data + .map(|data| data.requires_mainthreadonly) + .unwrap_or_default() + { + if mainthreadonly == data { + warn!( + mainthreadonly, + data, + "set requires-mainthreadonly to the same value that it already has" + ); + } + mainthreadonly = data; + } + vec![Self::ProtocolDecl { id, actual_name, @@ -1630,7 +1673,7 @@ impl fmt::Display for Stmt { protocols, methods, required_sendable: _, - required_mainthreadonly: _, + required_mainthreadonly, } => { writeln!(f, "extern_protocol!(")?; write!(f, "{availability}")?; @@ -1661,6 +1704,14 @@ impl fmt::Display for Stmt { // } // write!(f, "Send + Sync")?; // } + if *required_mainthreadonly { + if protocols.is_empty() { + write!(f, ": ")?; + } else { + write!(f, "+ ")?; + } + write!(f, "IsMainThreadOnly")?; + } writeln!(f, " {{")?; for method in methods { diff --git a/crates/header-translator/translation-config.toml b/crates/header-translator/translation-config.toml index f586ef040..71388f212 100644 --- a/crates/header-translator/translation-config.toml +++ b/crates/header-translator/translation-config.toml @@ -852,6 +852,20 @@ skipped = true [class.NSCollectionViewDiffableDataSource.methods.initWithCollectionView_itemProvider] skipped = true +# Requires `MainThreadOnly`, which I'm not sure is a good idea here? +[class.NSCollectionViewDiffableDataSource] +skipped-protocols = ["NSCollectionViewDataSource"] +[class.NSManagedObjectContext] +skipped-protocols = ["NSEditor", "NSEditorRegistration"] + +# Most methods on these require MainThreadMarker anyhow +[protocol.NSDraggingInfo] +requires-mainthreadonly = true +[protocol.NSBrowserDelegate] +requires-mainthreadonly = true +[protocol.NSSplitViewDelegate] +requires-mainthreadonly = true + # Both protocols and classes [protocol.NSTextAttachmentCell] renamed = "NSTextAttachmentCellProtocol" @@ -1614,7 +1628,7 @@ skipped-protocols = ["NSCopying", "NSMutableCopying"] skipped-protocols = ["NSCopying", "NSMutableCopying"] # Uses `NS_SWIFT_UI_ACTOR` on a static, which is hard to support. -# +# # Will have to be a method that takes `MainThreadMarker`. [static.NSApp] skipped = true diff --git a/crates/icrate/CHANGELOG.md b/crates/icrate/CHANGELOG.md index ce992dc9d..ca1d11a9c 100644 --- a/crates/icrate/CHANGELOG.md +++ b/crates/icrate/CHANGELOG.md @@ -31,9 +31,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). `MTLAccelerationStructureCommandEncoder` now take a nullable scratch buffer: - `refitAccelerationStructure_descriptor_destination_scratchBuffer_scratchBufferOffset` - `refitAccelerationStructure_descriptor_destination_scratchBuffer_scratchBufferOffset_options` -* **BREAKING**: Marked UI-related types as `MainThreadOnly`. This means that - they can now only be constructed on the main thread, meaning you have to - aquire a `MainThreadMarker` first. +* **BREAKING**: Marked UI-related classes as `MainThreadOnly`, and UI-related + protocols as `IsMainThreadOnly`. + + This means that they can now only be constructed, retrieved and used on the + main thread, meaning you usually have to aquire a `MainThreadMarker` first. ```rust // Before diff --git a/crates/icrate/src/common.rs b/crates/icrate/src/common.rs index b6ba9c38f..641f0fd61 100644 --- a/crates/icrate/src/common.rs +++ b/crates/icrate/src/common.rs @@ -14,8 +14,8 @@ pub(crate) use std::os::raw::{ pub(crate) use objc2::ffi::{NSInteger, NSIntegerMax, NSUInteger, NSUIntegerMax, IMP}; #[cfg(feature = "objective-c")] pub(crate) use objc2::mutability::{ - Immutable, ImmutableWithMutableSubclass, InteriorMutable, IsIdCloneable, MainThreadOnly, - Mutable, MutableWithImmutableSuperclass, + Immutable, ImmutableWithMutableSubclass, InteriorMutable, IsIdCloneable, IsMainThreadOnly, + MainThreadOnly, Mutable, MutableWithImmutableSuperclass, }; #[cfg(feature = "objective-c")] pub(crate) use objc2::rc::{Allocated, DefaultId, Id}; diff --git a/crates/icrate/src/generated b/crates/icrate/src/generated index cf4125408..6f3a2cf19 160000 --- a/crates/icrate/src/generated +++ b/crates/icrate/src/generated @@ -1 +1 @@ -Subproject commit cf4125408d57e55376449b5b35d9b78f6b4e4a72 +Subproject commit 6f3a2cf19589d89bd9c6bbe7e99186a6a45640b6 diff --git a/crates/test-ui/Cargo.toml b/crates/test-ui/Cargo.toml index a61d07a3e..2fbdba4ec 100644 --- a/crates/test-ui/Cargo.toml +++ b/crates/test-ui/Cargo.toml @@ -16,12 +16,15 @@ default = [ "icrate/Foundation", "icrate/Foundation_NSString", "icrate/Foundation_NSMutableString", + "icrate/Foundation_NSNotification", "icrate/Foundation_NSThread", "icrate/Foundation_NSError", "icrate/Foundation_NSArray", "icrate/Foundation_NSMutableArray", "icrate/Foundation_NSValue", "icrate/Foundation_NSSet", + "icrate/AppKit", + "icrate/AppKit_NSApplication", "objc2/unstable-msg-send-always-comma", ] std = ["block2/std", "objc2/std", "icrate/std"] diff --git a/crates/test-ui/ui/declare_class_delegate_not_mainthreadonly.rs b/crates/test-ui/ui/declare_class_delegate_not_mainthreadonly.rs new file mode 100644 index 000000000..af10f877b --- /dev/null +++ b/crates/test-ui/ui/declare_class_delegate_not_mainthreadonly.rs @@ -0,0 +1,43 @@ +//! Test that implementing `NSApplicationDelegate` and similar requires +//! a `MainThreadOnly` class. +use icrate::AppKit::{NSApplication, NSApplicationDelegate}; +use icrate::Foundation::{MainThreadMarker, NSNotification, NSObject, NSObjectProtocol}; +use objc2::rc::Id; +use objc2::runtime::ProtocolObject; +use objc2::{declare_class, extern_methods, mutability, ClassType}; + +declare_class!( + struct CustomObject; + + unsafe impl ClassType for CustomObject { + type Super = NSObject; + type Mutability = mutability::InteriorMutable; // Not `MainThreadOnly` + const NAME: &'static str = "CustomObject"; + } + + unsafe impl NSObjectProtocol for CustomObject {} + + unsafe impl NSApplicationDelegate for CustomObject { + #[method(applicationDidFinishLaunching:)] + unsafe fn application_did_finish_launching(&self, _notification: &NSNotification) { + // Unclear for the user how to get a main thread marker if `self` is not `MainThreadOnly` + let _mtm = MainThreadMarker::new().unwrap(); + } + } +); + +extern_methods!( + unsafe impl CustomObject { + #[method_id(new)] + fn new(mtm: MainThreadMarker) -> Id; + } +); + +fn main() { + let mtm = MainThreadMarker::new().unwrap(); + let app = NSApplication::sharedApplication(mtm); + + let delegate = CustomObject::new(mtm); + let delegate = ProtocolObject::from_ref(&*delegate); + app.setDelegate(Some(delegate)); +} diff --git a/crates/test-ui/ui/declare_class_delegate_not_mainthreadonly.stderr b/crates/test-ui/ui/declare_class_delegate_not_mainthreadonly.stderr new file mode 100644 index 000000000..127a469d4 --- /dev/null +++ b/crates/test-ui/ui/declare_class_delegate_not_mainthreadonly.stderr @@ -0,0 +1,21 @@ +error[E0277]: the trait bound `InteriorMutable: mutability::MutabilityIsMainThreadOnly` is not satisfied + --> ui/declare_class_delegate_not_mainthreadonly.rs + | + | unsafe impl NSApplicationDelegate for CustomObject { + | ^^^^^^^^^^^^ the trait `mutability::MutabilityIsMainThreadOnly` is not implemented for `InteriorMutable` + | + = help: the trait `mutability::MutabilityIsMainThreadOnly` is implemented for `MainThreadOnly` + = note: required for `CustomObject` to implement `IsMainThreadOnly` +note: required by a bound in `NSApplicationDelegate` + --> $WORKSPACE/crates/icrate/src/generated/AppKit/NSApplication.rs + | + | / extern_protocol!( + | | pub unsafe trait NSApplicationDelegate: NSObjectProtocol + IsMainThreadOnly { + | | --------------------- required by a bound in this trait + | | #[cfg(feature = "AppKit_NSApplication")] + | | #[optional] +... | + | | unsafe impl ProtocolType for dyn NSApplicationDelegate {} + | | ); + | |_^ required by this bound in `NSApplicationDelegate` + = note: this error originates in the macro `extern_protocol` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/implement_protocol_missing_super.rs b/crates/test-ui/ui/implement_protocol_missing_super.rs new file mode 100644 index 000000000..7e2fd36ad --- /dev/null +++ b/crates/test-ui/ui/implement_protocol_missing_super.rs @@ -0,0 +1,19 @@ +//! Test that implementing traits like `NSApplicationDelegate` requires super +//! protocols like `NSObjectProtocol` to also be implemented. +use icrate::AppKit::NSApplicationDelegate; +use icrate::Foundation::NSObject; +use objc2::{declare_class, mutability, ClassType}; + +declare_class!( + struct CustomObject; + + unsafe impl ClassType for CustomObject { + type Super = NSObject; + type Mutability = mutability::MainThreadOnly; + const NAME: &'static str = "CustomObject"; + } + + unsafe impl NSApplicationDelegate for CustomObject {} +); + +fn main() {} diff --git a/crates/test-ui/ui/implement_protocol_missing_super.stderr b/crates/test-ui/ui/implement_protocol_missing_super.stderr new file mode 100644 index 000000000..b45a0c989 --- /dev/null +++ b/crates/test-ui/ui/implement_protocol_missing_super.stderr @@ -0,0 +1,29 @@ +error[E0277]: the trait bound `CustomObject: NSObjectProtocol` is not satisfied + --> ui/implement_protocol_missing_super.rs + | + | unsafe impl NSApplicationDelegate for CustomObject {} + | ^^^^^^^^^^^^ the trait `NSObjectProtocol` is not implemented for `CustomObject` + | + = help: the following other types implement trait `NSObjectProtocol`: + ProtocolObject + NSObject + __NSProxy + NSApplication + NSCollectionView + NSCollectionLayoutSection + NSCollectionLayoutGroupCustomItem + NSControl + and $N others +note: required by a bound in `NSApplicationDelegate` + --> $WORKSPACE/crates/icrate/src/generated/AppKit/NSApplication.rs + | + | / extern_protocol!( + | | pub unsafe trait NSApplicationDelegate: NSObjectProtocol + IsMainThreadOnly { + | | --------------------- required by a bound in this trait + | | #[cfg(feature = "AppKit_NSApplication")] + | | #[optional] +... | + | | unsafe impl ProtocolType for dyn NSApplicationDelegate {} + | | ); + | |_^ required by this bound in `NSApplicationDelegate` + = note: this error originates in the macro `extern_protocol` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/msg_send_invalid_error.stderr b/crates/test-ui/ui/msg_send_invalid_error.stderr index 00ee6e294..ffd9cd0ca 100644 --- a/crates/test-ui/ui/msg_send_invalid_error.stderr +++ b/crates/test-ui/ui/msg_send_invalid_error.stderr @@ -38,11 +38,11 @@ error[E0277]: the trait bound `i32: Message` is not satisfied Exception ProtocolObject

AnyObject - NSArray - NSMutableArray - NSDictionary - NSMutableDictionary - NSSet + __RcTestObject + NSObject + __NSProxy + NSApplication + NSCollectionView and $N others note: required by a bound in `__send_message_error` --> $WORKSPACE/crates/objc2/src/message/mod.rs diff --git a/crates/test-ui/ui/nsarray_not_message.stderr b/crates/test-ui/ui/nsarray_not_message.stderr index f4a9f4ca4..8da89e478 100644 --- a/crates/test-ui/ui/nsarray_not_message.stderr +++ b/crates/test-ui/ui/nsarray_not_message.stderr @@ -8,11 +8,11 @@ error[E0277]: the trait bound `i32: Message` is not satisfied Exception ProtocolObject

AnyObject - NSArray - NSMutableArray - NSDictionary - NSMutableDictionary - NSSet + __RcTestObject + NSObject + __NSProxy + NSApplication + NSCollectionView and $N others note: required by a bound in `Foundation::__NSArray::>::new` --> $WORKSPACE/crates/icrate/src/generated/Foundation/NSArray.rs @@ -38,14 +38,14 @@ 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`: - NSArray - NSMutableArray - NSDictionary - NSMutableDictionary - NSSet - NSMutableSet - NSEnumerator - NSAppleEventDescriptor + __RcTestObject + NSObject + __NSProxy + NSApplication + NSCollectionView + NSCollectionLayoutSection + NSCollectionLayoutGroupCustomItem + NSControl and $N others = note: required for `Id` to implement `IsRetainable` note: required by a bound in `icrate::Foundation::array::>::from_slice` From fe33f6ce35690acd30022b551a78fb74faf68d66 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 11 Sep 2023 16:48:23 +0200 Subject: [PATCH 8/8] Add test to ensure that documenting auto traits of Id works --- crates/objc2/src/rc/id.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/objc2/src/rc/id.rs b/crates/objc2/src/rc/id.rs index c35af57c2..988288016 100644 --- a/crates/objc2/src/rc/id.rs +++ b/crates/objc2/src/rc/id.rs @@ -761,8 +761,7 @@ mod private { } /// Helper struct for avoiding a gnarly ICE in `rustdoc` when generating - /// documentation for `icrate` iterator helpers (in particular, it fails - /// in generating auto trait implementations). + /// documentation for auto traits for `Id` where `T: !ClassType`. /// /// See related issues: /// - @@ -822,6 +821,12 @@ impl RefUnwindSafe for Id {} // TODO: Relax this bound impl UnwindSafe for Id {} +#[cfg(doc)] +#[allow(unused)] +struct TestDocIdWithNonClassType { + id: Id, +} + #[cfg(test)] mod tests { use core::mem::size_of;