Skip to content

Commit 5529dc3

Browse files
committed
Let c_void be a valid encoding in more places
1 parent ce6100c commit 5529dc3

File tree

3 files changed

+67
-5
lines changed

3 files changed

+67
-5
lines changed

crates/objc2/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
88

99
### Added
1010
* Allow using `MainThreadMarker` in `extern_methods!`.
11+
* Added the feature flag `"relax-void-encoding"`, which when enabled, allows
12+
using `*mut c_void` in a few places where you would otherwise have to
13+
specify the encoding precisely.
1114

1215
### Changed
1316
* Renamed `runtime` types:

crates/objc2/Cargo.toml

+7
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ catch-all = ["exception"]
3535
# Enable all verification steps when debug assertions are enabled.
3636
verify = ["malloc"]
3737

38+
# Allow `*const c_void` and `*mut c_void` to be used as arguments and return
39+
# types where other pointers were expected.
40+
#
41+
# This may be useful for CoreFoundation types, or for migrating code from objc
42+
# to objc2.
43+
relax-void-encoding = []
44+
3845
# Expose features that require linking to `libc::free`.
3946
#
4047
# This is not enabled by default because most users won't need it, and it

crates/objc2/src/verify.rs

+57-5
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,22 @@ impl fmt::Display for VerificationError {
7171

7272
impl Error for VerificationError {}
7373

74+
/// Relaxed version of `Encoding::equivalent_to_box` that allows
75+
/// `*mut c_void` and `*const c_void` to be used in place of other pointers.
76+
///
77+
/// Note: This is a top-level comparison; `*mut *mut c_void` or structures
78+
/// containing `*mut c_void` are not allowed differently than usual.
79+
fn relaxed_equivalent_to_box(encoding: &Encoding, expected: &EncodingBox) -> bool {
80+
if cfg!(feature = "relax-void-encoding")
81+
&& matches!(encoding, Encoding::Pointer(&Encoding::Void))
82+
&& matches!(expected, EncodingBox::Pointer(_))
83+
{
84+
true
85+
} else {
86+
encoding.equivalent_to_box(expected)
87+
}
88+
}
89+
7490
pub(crate) fn verify_method_signature(
7591
method: &Method,
7692
args: &[Encoding],
@@ -80,7 +96,7 @@ pub(crate) fn verify_method_signature(
8096

8197
// TODO: Verify stack layout
8298
let (expected, _stack_layout) = iter.extract_return()?;
83-
if !ret.equivalent_to_box(&expected) {
99+
if !relaxed_equivalent_to_box(ret, &expected) {
84100
return Err(Inner::MismatchedReturn(expected, ret.clone()).into());
85101
}
86102

@@ -93,7 +109,7 @@ pub(crate) fn verify_method_signature(
93109
if let Some(res) = iter.next() {
94110
// TODO: Verify stack layout
95111
let (expected, _stack_layout) = res?;
96-
if !actual.equivalent_to_box(&expected) {
112+
if !relaxed_equivalent_to_box(actual, &expected) {
97113
return Err(Inner::MismatchedArgument(i, expected, actual.clone()).into());
98114
}
99115
} else {
@@ -118,9 +134,10 @@ pub(crate) fn verify_method_signature(
118134
mod tests {
119135
use super::*;
120136
use crate::runtime::Sel;
121-
use crate::sel;
122137
use crate::test_utils;
138+
use crate::{msg_send, sel};
123139
use alloc::string::ToString;
140+
use core::ffi::c_void;
124141
use core::panic::{RefUnwindSafe, UnwindSafe};
125142

126143
#[test]
@@ -179,20 +196,55 @@ mod tests {
179196
#[should_panic = "invalid message send to -[CustomObject foo]: expected return to have type code 'I', but found 'i'"]
180197
fn test_send_message_verified() {
181198
let obj = test_utils::custom_object();
182-
let _: i32 = unsafe { crate::msg_send![&obj, foo] };
199+
let _: i32 = unsafe { msg_send![&obj, foo] };
183200
}
184201

185202
#[test]
186203
#[cfg(debug_assertions)]
187204
#[should_panic = "invalid message send to +[CustomObject abcDef]: method not found"]
188205
fn test_send_message_verified_to_class() {
189206
let cls = test_utils::custom_class();
190-
let _: i32 = unsafe { crate::msg_send![cls, abcDef] };
207+
let _: i32 = unsafe { msg_send![cls, abcDef] };
191208
}
192209

193210
#[test]
194211
fn test_marker_traits() {
195212
fn assert_marker_traits<T: Send + Sync + UnwindSafe + RefUnwindSafe + Unpin>() {}
196213
assert_marker_traits::<VerificationError>();
197214
}
215+
216+
#[test]
217+
fn test_get_reference() {
218+
let mut obj = test_utils::custom_object();
219+
let _: () = unsafe { msg_send![&mut obj, setFoo: 42u32] };
220+
221+
let res: &u32 = unsafe { msg_send![&obj, fooReference] };
222+
assert_eq!(*res, 42);
223+
let res: *const u32 = unsafe { msg_send![&obj, fooReference] };
224+
assert_eq!(unsafe { *res }, 42);
225+
let res: *mut u32 = unsafe { msg_send![&obj, fooReference] };
226+
assert_eq!(unsafe { *res }, 42);
227+
}
228+
229+
#[test]
230+
#[cfg_attr(
231+
all(debug_assertions, not(feature = "relax-void-encoding")),
232+
should_panic = "invalid message send to -[CustomObject fooReference]: expected return to have type code '^I', but found '^v'"
233+
)]
234+
fn test_get_reference_void() {
235+
let mut obj = test_utils::custom_object();
236+
let _: () = unsafe { msg_send![&mut obj, setFoo: 42u32] };
237+
238+
let res: *mut c_void = unsafe { msg_send![&obj, fooReference] };
239+
let res: *mut u32 = res.cast();
240+
assert_eq!(unsafe { *res }, 42);
241+
}
242+
243+
#[test]
244+
#[cfg(debug_assertions)]
245+
#[should_panic = "invalid message send to -[CustomObject foo]: expected return to have type code 'I', but found '^v'"]
246+
fn test_get_integer_void() {
247+
let obj = test_utils::custom_object();
248+
let _: *mut c_void = unsafe { msg_send![&obj, foo] };
249+
}
198250
}

0 commit comments

Comments
 (0)