Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require users of msg_send! to specify the return type #83

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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<!, SomeError>::Ok`, which should be impossible:
<https://github.com/SSheldon/rust-objc/issues/62>

With this change, both `let () = msg_send![…];` and `msg_send![…];` become `msg_send![… => ()];`.

## 0.2.6

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "objc"
version = "0.2.6"
version = "0.3.0"
authors = ["Steven Sheldon"]
edition = "2018"

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
8 changes: 4 additions & 4 deletions examples/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
}
8 changes: 3 additions & 5 deletions src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,18 +322,16 @@ 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);
}
}

#[test]
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);
}
}
}
8 changes: 4 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => ()];
# }
# }
```
Expand Down
22 changes: 11 additions & 11 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
31 changes: 15 additions & 16 deletions src/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -230,18 +230,18 @@ 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);
}

#[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);
Expand All @@ -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);
}
Expand All @@ -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);
}
}

Expand Down
15 changes: 8 additions & 7 deletions src/rc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand All @@ -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();

Expand All @@ -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();

Expand All @@ -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();

Expand Down
4 changes: 2 additions & 2 deletions src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
4 changes: 2 additions & 2 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
6 changes: 3 additions & 3 deletions tests/use_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => ()];
}
}

Expand Down