#![allow(dead_code, unused_imports)] use core::panic::{RefUnwindSafe, UnwindSafe}; use static_assertions::assert_not_impl_any; use objc2::define_class; use objc2::rc::Retained; use objc2::runtime::{AnyObject, NSObject}; // We expect most Foundation types to be UnwindSafe and RefUnwindSafe, // since they follow Rust's usual mutability rules (&T = immutable). // // A _lot_ of Objective-C code out there would be subtly broken if e.g. // `NSString` wasn't exception safe! As an example: `-[NSArray objectAtIndex:]` // can throw, but it is still perfectly valid to access the array after that! // // I'm pretty sure that even mutable classes like `NSMutableString` is still // exception safe, since preconditions are checked before the mutation is // executed, and more "internal" errors like Out-Of-Memory either crash the // application, or return / throw an error value. // // Also note that this is still only a speed bump, not actually part of any // unsafe contract; we can't really protect against it if something is not // exception safe, since `UnwindSafe` is a safe trait. fn assert_unwindsafe() {} fn assert_auto_traits() { assert_unwindsafe::(); } define_class!( #[unsafe(super(NSObject))] struct SendSyncObject; ); #[test] fn test_generic_auto_traits() { // assert_unwindsafe::>(); // assert_unwindsafe::>>(); // assert_unwindsafe::>(); // assert_unwindsafe::>>(); // assert_unwindsafe::>(); // assert_unwindsafe::>>(); // TODO: Unpin? #[cfg(feature = "NSArray")] assert_not_impl_any!(crate::NSArray: Unpin); #[cfg(feature = "NSArray")] assert_not_impl_any!(crate::NSMutableArray: Unpin); #[cfg(feature = "NSDictionary")] assert_not_impl_any!(crate::NSDictionary: Unpin); // Collections are not Send + Sync, since they are interior mutable, i.e. // mutable from `&self`. #[cfg(feature = "NSArray")] assert_not_impl_any!(crate::NSArray: Send, Sync); #[cfg(feature = "NSArray")] assert_not_impl_any!(crate::NSMutableArray: Send, Sync); #[cfg(feature = "NSDictionary")] assert_not_impl_any!(crate::NSDictionary: Send, Sync); // TODO: Make these UnwindSafe? #[cfg(feature = "NSProcessInfo")] { use crate::NSProcessInfo; #[cfg(feature = "NSDictionary")] assert_not_impl_any!(crate::NSDictionary: UnwindSafe, RefUnwindSafe); #[cfg(feature = "NSSet")] assert_not_impl_any!(crate::NSSet: UnwindSafe, RefUnwindSafe); #[cfg(feature = "NSSet")] assert_not_impl_any!(Retained>: UnwindSafe, RefUnwindSafe); #[cfg(feature = "NSArray")] assert_not_impl_any!(crate::NSMutableArray: UnwindSafe, RefUnwindSafe); #[cfg(feature = "NSDictionary")] assert_not_impl_any!(crate::NSMutableDictionary: UnwindSafe, RefUnwindSafe); #[cfg(feature = "NSSet")] assert_not_impl_any!(crate::NSMutableSet: UnwindSafe, RefUnwindSafe); } } #[test] fn send_sync_unwindsafe() { #[cfg(feature = "NSAttributedString")] assert_unwindsafe::(); #[cfg(feature = "NSObjCRuntime")] assert_auto_traits::(); #[cfg(feature = "NSData")] assert_unwindsafe::(); // TODO: Figure out if Send + Sync is safe? // assert_auto_traits::>(); // assert_auto_traits::>>(); #[cfg(feature = "NSError")] assert_auto_traits::(); #[cfg(feature = "NSException")] assert_auto_traits::(); #[cfg(all(feature = "NSGeometry", feature = "objc2-core-foundation"))] assert_auto_traits::(); #[cfg(all(feature = "NSGeometry", feature = "objc2-core-foundation"))] assert_auto_traits::(); #[cfg(all(feature = "NSGeometry", feature = "objc2-core-foundation"))] assert_auto_traits::(); #[cfg(feature = "NSAttributedString")] assert_unwindsafe::(); #[cfg(feature = "NSData")] assert_unwindsafe::(); #[cfg(feature = "NSString")] assert_unwindsafe::(); #[cfg(feature = "NSValue")] assert_auto_traits::(); // assert_auto_traits::(); // Intentional #[cfg(feature = "NSProcessInfo")] assert_auto_traits::(); #[cfg(feature = "NSRange")] assert_auto_traits::(); #[cfg(feature = "NSString")] assert_unwindsafe::(); #[cfg(feature = "NSThread")] assert_auto_traits::(); #[cfg(feature = "NSUUID")] assert_auto_traits::(); // assert_auto_traits::(); // Intentional #[cfg(feature = "NSZone")] assert_unwindsafe::(); // Intentional }