From bdb72022b00a9d2cfc699a93b27fa9d5ef948e26 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 15 Oct 2019 16:32:08 +0200 Subject: [PATCH] Require users of `msg_send!` to specify the return type This is a breaking change, to prevent misuses such as https://github.com/SSheldon/rust-objc/issues/62 --- CHANGELOG.md | 19 +++++++++++++++++++ Cargo.toml | 2 +- README.md | 10 +++++----- examples/example.rs | 8 ++++---- src/declare.rs | 8 +++----- src/lib.rs | 8 ++++---- src/macros.rs | 22 +++++++++++----------- src/message/mod.rs | 31 +++++++++++++++---------------- src/rc/mod.rs | 15 ++++++++------- src/runtime.rs | 4 ++-- src/test_utils.rs | 4 ++-- tests/use_macros.rs | 6 +++--- 12 files changed, 77 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73bdbb699..e1f751978 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +## 0.3.0 + +### Changed + +* Require users of `msg_send!` to specify the return type. + + Previously, the return type was inferred based on context, and + `let () = msg_send![…];` was documented as the correct use when there is no return value. + However `msg_send![…];` compiled fine and was equivalent as of Rust 1.38, + even though it happened to rely on an unspecified part of the language + (type inference fallback for diverging expressions) + + When Rust first tried to change fallback from `()` (the unit type) to `!` (the never type), + this cause such uses of `msg_send!` to have Undefined Behavior + beacause a function was returning `Result::Ok`, which should be impossible: + + + With this change, both `let () = msg_send![…];` and `msg_send![…];` become `msg_send![… => ()];`. + ## 0.2.6 ### Fixed diff --git a/Cargo.toml b/Cargo.toml index eeaeeff58..230d5c7a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "objc" -version = "0.2.6" +version = "0.3.0" authors = ["Steven Sheldon"] edition = "2018" diff --git a/README.md b/README.md index 55fba6484..2df6e0138 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,11 @@ Objective-C objects can be messaged using the `msg_send!` macro: ``` rust let cls = class!(NSObject); -let obj: *mut Object = msg_send![cls, new]; -let hash: usize = msg_send![obj, hash]; -let is_kind: BOOL = msg_send![obj, isKindOfClass:cls]; +let obj = msg_send![cls, new => *mut Object]; +let hash = msg_send![obj, hash => usize]; +let is_kind = msg_send![obj, isKindOfClass:cls => BOOL]; // Even void methods must have their return type annotated -let _: () = msg_send![obj, release]; +msg_send![obj, release => ()]; ``` ## Reference counting @@ -27,7 +27,7 @@ and safely fails if the object has been deallocated. ``` rust // StrongPtr will release the object when dropped let obj = unsafe { - StrongPtr::new(msg_send![class!(NSObject), new]) + StrongPtr::new(msg_send![class!(NSObject), new => *mut Object]) }; // Cloning retains the object an additional time diff --git a/examples/example.rs b/examples/example.rs index a3ae0adc6..13dff69c7 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -18,8 +18,8 @@ fn main() { // Allocate an instance let obj = unsafe { - let obj: *mut Object = msg_send![cls, alloc]; - let obj: *mut Object = msg_send![obj, init]; + let obj = msg_send![cls, alloc => *mut Object]; + let obj = msg_send![obj, init => *mut Object]; StrongPtr::new(obj) }; println!("NSObject address: {:p}", obj); @@ -38,8 +38,8 @@ fn main() { assert!(*hash_return == usize::ENCODING); // Invoke a method on the object - let hash: usize = unsafe { - msg_send![*obj, hash] + let hash = unsafe { + msg_send![*obj, hash => usize] }; println!("NSObject hash: {}", hash); } diff --git a/src/declare.rs b/src/declare.rs index 729a650aa..1ad430dfd 100644 --- a/src/declare.rs +++ b/src/declare.rs @@ -322,9 +322,8 @@ mod tests { // Registering the custom class is in test_utils let obj = test_utils::custom_object(); unsafe { - let _: () = msg_send![obj, setFoo:13u32]; - let result: u32 = msg_send![obj, foo]; - assert!(result == 13); + msg_send![obj, setFoo:13u32 => ()]; + assert!(msg_send![obj, foo => u32] == 13); } } @@ -332,8 +331,7 @@ mod tests { fn test_class_method() { let cls = test_utils::custom_class(); unsafe { - let result: u32 = msg_send![cls, classFoo]; - assert!(result == 7); + assert!(msg_send![cls, classFoo => u32] == 7); } } } diff --git a/src/lib.rs b/src/lib.rs index 5f2d4e9f1..dcb5caafd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,11 +11,11 @@ Objective-C objects can be messaged using the [`msg_send!`](macro.msg_send!.html # fn main() { # unsafe { let cls = class!(NSObject); -let obj: *mut Object = msg_send![cls, new]; -let hash: usize = msg_send![obj, hash]; -let is_kind: BOOL = msg_send![obj, isKindOfClass:cls]; +let obj = msg_send![cls, new => *mut Object]; +let hash = msg_send![obj, hash => usize]; +let is_kind = msg_send![obj, isKindOfClass:cls => BOOL]; // Even void methods must have their return type annotated -let _: () = msg_send![obj, release]; +msg_send![obj, release => ()]; # } # } ``` diff --git a/src/macros.rs b/src/macros.rs index 6813bdc39..05ff16ed7 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -69,39 +69,39 @@ Variadic arguments are not currently supported. # fn main() { # unsafe { let obj: *mut Object; -# let obj: *mut Object = 0 as *mut Object; -let description: *const Object = msg_send![obj, description]; -let _: () = msg_send![obj, setArg1:1 arg2:2]; +# let obj = 0 as *mut Object; +let description = msg_send![obj, description => *mut Object]; +msg_send![obj, setArg1:1 arg2:2 => ()]; # } # } ``` */ #[macro_export] macro_rules! msg_send { - (super($obj:expr, $superclass:expr), $name:ident) => ({ + (super($obj:expr, $superclass:expr), $name:ident => $ret:ty) => ({ let sel = $crate::sel!($name); - match $crate::__send_super_message(&*$obj, $superclass, sel, ()) { + match $crate::__send_super_message::<_, _, $ret>(&*$obj, $superclass, sel, ()) { Err(s) => panic!("{}", s), Ok(r) => r, } }); - (super($obj:expr, $superclass:expr), $($name:ident : $arg:expr)+) => ({ + (super($obj:expr, $superclass:expr), $($name:ident : $arg:expr)+ => $ret:ty) => ({ let sel = $crate::sel!($($name:)+); - match $crate::__send_super_message(&*$obj, $superclass, sel, ($($arg,)*)) { + match $crate::__send_super_message::<_, _, $ret>(&*$obj, $superclass, sel, ($($arg,)*)) { Err(s) => panic!("{}", s), Ok(r) => r, } }); - ($obj:expr, $name:ident) => ({ + ($obj:expr, $name:ident => $ret:ty) => ({ let sel = $crate::sel!($name); - match $crate::__send_message(&*$obj, sel, ()) { + match $crate::__send_message::<_, _, $ret>(&*$obj, sel, ()) { Err(s) => panic!("{}", s), Ok(r) => r, } }); - ($obj:expr, $($name:ident : $arg:expr)+) => ({ + ($obj:expr, $($name:ident : $arg:expr)+ => $ret:ty) => ({ let sel = $crate::sel!($($name:)+); - match $crate::__send_message(&*$obj, sel, ($($arg,)*)) { + match $crate::__send_message::<_, _, $ret>(&*$obj, sel, ($($arg,)*)) { Err(s) => panic!("{}", s), Ok(r) => r, } diff --git a/src/message/mod.rs b/src/message/mod.rs index b88190fd4..d6dd4e591 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -88,7 +88,7 @@ pub unsafe trait Message { # use objc::Message; # fn main() { let obj: &Object; - # obj = unsafe { msg_send![class!(NSObject), new] }; + # obj = unsafe { msg_send![class!(NSObject), new => &Object] }; let sel = sel!(isKindOfClass:); // Verify isKindOfClass: takes one Class and returns a BOOL let result = obj.verify_message::<(&Class,), BOOL>(sel); @@ -230,9 +230,9 @@ mod tests { #[test] fn test_send_message() { let obj = test_utils::custom_object(); - let result: u32 = unsafe { - let _: () = msg_send![obj, setFoo:4u32]; - msg_send![obj, foo] + let result = unsafe { + msg_send![obj, setFoo:4u32 => ()]; + msg_send![obj, foo => u32] }; assert!(result == 4); } @@ -240,8 +240,8 @@ mod tests { #[test] fn test_send_message_stret() { let obj = test_utils::custom_object(); - let result: test_utils::CustomStruct = unsafe { - msg_send![obj, customStruct] + let result = unsafe { + msg_send![obj, customStruct => test_utils::CustomStruct] }; let expected = test_utils::CustomStruct { a: 1, b:2, c: 3, d: 4 }; assert!(result == expected); @@ -251,18 +251,18 @@ mod tests { #[test] fn test_send_message_nil() { let nil: *mut Object = ::std::ptr::null_mut(); - let result: usize = unsafe { - msg_send![nil, hash] + let result = unsafe { + msg_send![nil, hash => usize] }; assert!(result == 0); - let result: *mut Object = unsafe { - msg_send![nil, description] + let result = unsafe { + msg_send![nil, description => *mut Object] }; assert!(result.is_null()); - let result: f64 = unsafe { - msg_send![nil, doubleValue] + let result = unsafe { + msg_send![nil, doubleValue => f64] }; assert!(result == 0.0); } @@ -272,13 +272,12 @@ mod tests { let obj = test_utils::custom_subclass_object(); let superclass = test_utils::custom_class(); unsafe { - let _: () = msg_send![obj, setFoo:4u32]; - let foo: u32 = msg_send![super(obj, superclass), foo]; + msg_send![obj, setFoo:4u32 => ()]; + let foo = msg_send![super(obj, superclass), foo => u32]; assert!(foo == 4); // The subclass is overriden to return foo + 2 - let foo: u32 = msg_send![obj, foo]; - assert!(foo == 6); + assert!(msg_send![obj, foo => u32] == 6); } } diff --git a/src/rc/mod.rs b/src/rc/mod.rs index c703f585d..791ebea6a 100644 --- a/src/rc/mod.rs +++ b/src/rc/mod.rs @@ -18,10 +18,11 @@ For more information on Objective-C's reference counting, see Apple's documentat ``` no_run # #[macro_use] extern crate objc; # use objc::rc::{autoreleasepool, StrongPtr}; +# use objc::runtime::Object; # fn main() { // StrongPtr will release the object when dropped let obj = unsafe { - StrongPtr::new(msg_send![class!(NSObject), new]) + StrongPtr::new(msg_send![class!(NSObject), new => *mut Object]) }; // Cloning retains the object an additional time @@ -58,11 +59,11 @@ mod tests { #[test] fn test_strong_clone() { fn retain_count(obj: *mut Object) -> usize { - unsafe { msg_send![obj, retainCount] } + unsafe { msg_send![obj, retainCount => usize] } } let obj = unsafe { - StrongPtr::new(msg_send![class!(NSObject), new]) + StrongPtr::new(msg_send![class!(NSObject), new => *mut Object]) }; assert!(retain_count(*obj) == 1); @@ -77,7 +78,7 @@ mod tests { #[test] fn test_weak() { let obj = unsafe { - StrongPtr::new(msg_send![class!(NSObject), new]) + StrongPtr::new(msg_send![class!(NSObject), new => *mut Object]) }; let weak = obj.weak(); @@ -92,7 +93,7 @@ mod tests { #[test] fn test_weak_copy() { let obj = unsafe { - StrongPtr::new(msg_send![class!(NSObject), new]) + StrongPtr::new(msg_send![class!(NSObject), new => *mut Object]) }; let weak = obj.weak(); @@ -104,11 +105,11 @@ mod tests { #[test] fn test_autorelease() { let obj = unsafe { - StrongPtr::new(msg_send![class!(NSObject), new]) + StrongPtr::new(msg_send![class!(NSObject), new => *mut Object]) }; fn retain_count(obj: *mut Object) -> usize { - unsafe { msg_send![obj, retainCount] } + unsafe { msg_send![obj, retainCount => usize] } } let cloned = obj.clone(); diff --git a/src/runtime.rs b/src/runtime.rs index dd8f3aa09..756686082 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -593,8 +593,8 @@ mod tests { #[test] fn test_protocol_method() { let class = test_utils::custom_class(); - let result: i32 = unsafe { - msg_send![class, addNumber:1 toNumber:2] + let result = unsafe { + msg_send![class, addNumber:1 toNumber:2 => i32] }; assert_eq!(result, 3); } diff --git a/src/test_utils.rs b/src/test_utils.rs index 0d417d0c4..ef6ca0a9c 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -157,8 +157,8 @@ pub fn custom_subclass() -> &'static Class { let mut decl = ClassDecl::new("CustomSubclassObject", superclass).unwrap(); extern fn custom_subclass_get_foo(this: &Object, _cmd: Sel) -> u32 { - let foo: u32 = unsafe { - msg_send![super(this, custom_class()), foo] + let foo = unsafe { + msg_send![super(this, custom_class()), foo => u32] }; foo + 2 } diff --git a/tests/use_macros.rs b/tests/use_macros.rs index 7ebe28fe2..93892bd1e 100644 --- a/tests/use_macros.rs +++ b/tests/use_macros.rs @@ -9,9 +9,9 @@ use objc::runtime::Object; fn use_class_and_msg_send() { unsafe { let cls = class!(NSObject); - let obj: *mut Object = msg_send![cls, new]; - let _hash: usize = msg_send![obj, hash]; - let _: () = msg_send![obj, release]; + let obj = msg_send![cls, new => *mut Object]; + let _hash = msg_send![obj, hash => usize]; + msg_send![obj, release => ()]; } }