Skip to content

Commit 79661f9

Browse files
authored
Merge pull request #172 from madsmtm/allocated-struct
Add `rc::Allocated`
2 parents 98e111c + a925100 commit 79661f9

11 files changed

+160
-70
lines changed

objc2/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
3535
through `&Object`.
3636
* Added `VerificationError` as more specific return type from
3737
`Class::verify_sel`.
38+
* Added `rc::Allocated` struct which is used within `msg_send_id!`.
3839

3940
### Changed
4041
* **BREAKING**: `Sel` is now required to be non-null, which means that you

objc2/src/__macro_helpers.rs

+14-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::rc::{Id, Ownership};
1+
use crate::rc::{Allocated, Id, Ownership};
22
use crate::runtime::{Class, Sel};
33
use crate::{Message, MessageArguments, MessageReceiver};
44

@@ -68,8 +68,8 @@ impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, Id<T, O>>
6868
}
6969
}
7070

71-
// `alloc`, should mark the return value as "allocated, not initialized" somehow
72-
impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, Id<T, O>>
71+
// `alloc`
72+
impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, Id<Allocated<T>, O>>
7373
for RetainSemantics<false, true, false, false>
7474
{
7575
#[inline]
@@ -78,26 +78,26 @@ impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, Id<T, O>>
7878
cls: &Class,
7979
sel: Sel,
8080
args: A,
81-
) -> Option<Id<T, O>> {
81+
) -> Option<Id<Allocated<T>, O>> {
8282
// SAFETY: Checked by caller
8383
let obj = unsafe { MessageReceiver::send_message(cls, sel, args) };
8484
// SAFETY: The selector is `alloc`, so this has +1 retain count
85-
unsafe { Id::new(obj) }
85+
unsafe { Id::new_allocated(obj) }
8686
}
8787
}
8888

89-
// `init`, should mark the input value as "allocated, not initialized" somehow
90-
impl<T: ?Sized + Message, O: Ownership> MsgSendId<Option<Id<T, O>>, Id<T, O>>
89+
// `init`
90+
impl<T: ?Sized + Message, O: Ownership> MsgSendId<Option<Id<Allocated<T>, O>>, Id<T, O>>
9191
for RetainSemantics<false, false, true, false>
9292
{
9393
#[inline]
9494
#[track_caller]
9595
unsafe fn send_message_id<A: MessageArguments>(
96-
obj: Option<Id<T, O>>,
96+
obj: Option<Id<Allocated<T>, O>>,
9797
sel: Sel,
9898
args: A,
9999
) -> Option<Id<T, O>> {
100-
let ptr = Id::option_into_ptr(obj);
100+
let ptr = Id::option_into_ptr(obj.map(|obj| unsafe { Id::assume_init(obj) }));
101101
// SAFETY: `ptr` may be null here, but that's fine since the return
102102
// is `*mut T`, which is one of the few types where messages to nil is
103103
// allowed.
@@ -191,7 +191,7 @@ mod tests {
191191

192192
use core::ptr;
193193

194-
use crate::rc::{Owned, RcTestObject, Shared, ThreadTestData};
194+
use crate::rc::{Allocated, Owned, RcTestObject, Shared, ThreadTestData};
195195
use crate::runtime::Object;
196196
use crate::{Encoding, RefEncode};
197197

@@ -200,7 +200,7 @@ mod tests {
200200
let mut expected = ThreadTestData::current();
201201
let cls = RcTestObject::class();
202202

203-
let obj: Id<RcTestObject, Shared> = unsafe { msg_send_id![cls, alloc].unwrap() };
203+
let obj: Id<Allocated<RcTestObject>, Shared> = unsafe { msg_send_id![cls, alloc].unwrap() };
204204
expected.alloc += 1;
205205
expected.assert_current();
206206

@@ -230,7 +230,7 @@ mod tests {
230230
let cls = RcTestObject::class();
231231

232232
let zone: *const _NSZone = ptr::null();
233-
let _obj: Id<RcTestObject, Owned> =
233+
let _obj: Id<Allocated<RcTestObject>, Owned> =
234234
unsafe { msg_send_id![cls, allocWithZone: zone].unwrap() };
235235
// `+[NSObject alloc]` delegates to `+[NSObject allocWithZone:]`, but
236236
// `RcTestObject` only catches `alloc`.
@@ -243,14 +243,14 @@ mod tests {
243243
let mut expected = ThreadTestData::current();
244244
let cls = RcTestObject::class();
245245

246-
let obj: Option<Id<RcTestObject, Shared>> = unsafe { msg_send_id![cls, alloc] };
246+
let obj: Option<Id<Allocated<RcTestObject>, Shared>> = unsafe { msg_send_id![cls, alloc] };
247247
expected.alloc += 1;
248248
// Don't check allocation error
249249
let _obj: Id<RcTestObject, Shared> = unsafe { msg_send_id![obj, init].unwrap() };
250250
expected.init += 1;
251251
expected.assert_current();
252252

253-
let obj: Option<Id<RcTestObject, Shared>> = unsafe { msg_send_id![cls, alloc] };
253+
let obj: Option<Id<Allocated<RcTestObject>, Shared>> = unsafe { msg_send_id![cls, alloc] };
254254
expected.alloc += 1;
255255
// Check allocation error before init
256256
let obj = obj.unwrap();

objc2/src/macros.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -590,11 +590,12 @@ macro_rules! msg_send_bool {
590590
/// is a generic `Option<Id<T, O>>`.
591591
///
592592
/// - The `alloc` family: The receiver must be `&Class`, and the return type
593-
/// is a generic `Option<Id<T, O>>`. (This will change, see [#172]).
593+
/// is a generic `Option<Id<Allocated<T>, O>>`.
594594
///
595-
/// - The `init` family: The receiver must be `Option<Id<T, O>>` as returned
596-
/// from `alloc`. The receiver is consumed, and a the now-initialized
597-
/// `Option<Id<T, O>>` (with the same `T` and `O`) is returned.
595+
/// - The `init` family: The receiver must be `Option<Id<Allocated<T>, O>>`
596+
/// as returned from `alloc`. The receiver is consumed, and a the
597+
/// now-initialized `Option<Id<T, O>>` (with the same `T` and `O`) is
598+
/// returned.
598599
///
599600
/// - The `copy` family: The receiver may be anything that implements
600601
/// [`MessageReceiver`] and the return type is a generic `Option<Id<T, O>>`.
@@ -615,7 +616,6 @@ macro_rules! msg_send_bool {
615616
/// [`Id::retain`], [`Id::drop`] and [`Id::autorelease`] for that.
616617
///
617618
/// [sel-families]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families
618-
/// [#172]: https://github.com/madsmtm/objc2/pull/172
619619
/// [`MessageReceiver`]: crate::MessageReceiver
620620
/// [`Id::retain_autoreleased`]: crate::rc::Id::retain_autoreleased
621621
/// [arc-retainable]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#retainable-object-pointers-as-operands-and-arguments

objc2/src/rc/allocated.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// A marker type that can be used within [`Id`] to indicate that the object
2+
/// has been allocated but not initialized.
3+
///
4+
/// The reason we use `Option<Id<Allocated<T>, O>>` instead of just `*mut T`
5+
/// is:
6+
/// - To allow releasing allocated objects, e.g. in the face of panics.
7+
/// - To safely know the object is valid (albeit uninitialized).
8+
/// - To allow specifying ownership.
9+
///
10+
/// [`Id`]: crate::rc::Id
11+
#[repr(transparent)]
12+
#[derive(Debug)]
13+
pub struct Allocated<T: ?Sized>(T);
14+
15+
// Explicitly don't implement `Deref`, `Message` nor `RefEncode`!

objc2/src/rc/id.rs

+34-10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use core::ops::{Deref, DerefMut};
55
use core::panic::{RefUnwindSafe, UnwindSafe};
66
use core::ptr::NonNull;
77

8+
use super::Allocated;
89
use super::AutoreleasePool;
910
use super::{Owned, Ownership, Shared};
1011
use crate::ffi;
@@ -124,6 +125,39 @@ pub struct Id<T: ?Sized, O: Ownership> {
124125
notunwindsafe: PhantomData<&'static mut ()>,
125126
}
126127

128+
impl<T: ?Sized, O: Ownership> Id<T, O> {
129+
#[inline]
130+
unsafe fn new_nonnull(ptr: NonNull<T>) -> Self {
131+
Self {
132+
ptr,
133+
item: PhantomData,
134+
own: PhantomData,
135+
notunwindsafe: PhantomData,
136+
}
137+
}
138+
}
139+
140+
impl<T: Message + ?Sized, O: Ownership> Id<Allocated<T>, O> {
141+
#[inline]
142+
pub(crate) unsafe fn new_allocated(ptr: *mut T) -> Option<Self> {
143+
// SAFETY: Upheld by the caller
144+
NonNull::new(ptr as *mut Allocated<T>).map(|ptr| unsafe { Self::new_nonnull(ptr) })
145+
}
146+
147+
#[inline]
148+
pub(crate) unsafe fn assume_init(this: Self) -> Id<T, O> {
149+
let ptr = ManuallyDrop::new(this).ptr;
150+
151+
// NonNull::cast
152+
let ptr = ptr.as_ptr() as *mut T;
153+
let ptr = unsafe { NonNull::new_unchecked(ptr) };
154+
155+
// SAFETY: The pointer is valid.
156+
// Caller verifies that the object is allocated.
157+
unsafe { Id::new_nonnull(ptr) }
158+
}
159+
}
160+
127161
impl<T: Message + ?Sized, O: Ownership> Id<T, O> {
128162
/// Constructs an [`Id`] to an object that already has +1 retain count.
129163
///
@@ -181,16 +215,6 @@ impl<T: Message + ?Sized, O: Ownership> Id<T, O> {
181215
NonNull::new(ptr).map(|ptr| unsafe { Id::new_nonnull(ptr) })
182216
}
183217

184-
#[inline]
185-
unsafe fn new_nonnull(ptr: NonNull<T>) -> Id<T, O> {
186-
Self {
187-
ptr,
188-
item: PhantomData,
189-
own: PhantomData,
190-
notunwindsafe: PhantomData,
191-
}
192-
}
193-
194218
/// Returns a raw pointer to the object.
195219
///
196220
/// The pointer is valid for at least as long as the `Id` is held.

objc2/src/rc/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
//! assert!(weak.load().is_none());
5656
//! ```
5757
58+
mod allocated;
5859
mod autorelease;
5960
mod id;
6061
mod id_forwarding_impls;
@@ -65,6 +66,7 @@ mod weak_id;
6566
#[cfg(test)]
6667
mod test_object;
6768

69+
pub use self::allocated::Allocated;
6870
pub use self::autorelease::{autoreleasepool, AutoreleasePool, AutoreleaseSafe};
6971
pub use self::id::Id;
7072
pub use self::id_traits::{DefaultId, SliceId, SliceIdMut};

tests/assembly/test_msg_send_id/lib.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
//! Test assembly output of `msg_send_id!` internals.
22
use objc2::__macro_helpers::{MsgSendId, RetainSemantics};
3-
use objc2::rc::{Id, Shared};
3+
use objc2::rc::{Allocated, Id, Shared};
44
use objc2::runtime::{Class, Object, Sel};
55

66
#[no_mangle]
7-
unsafe fn handle_alloc(obj: &Class, sel: Sel) -> Option<Id<Object, Shared>> {
7+
unsafe fn handle_alloc(obj: &Class, sel: Sel) -> Option<Id<Allocated<Object>, Shared>> {
88
<RetainSemantics<false, true, false, false>>::send_message_id(obj, sel, ())
99
}
1010

1111
#[no_mangle]
12-
unsafe fn handle_init(obj: Option<Id<Object, Shared>>, sel: Sel) -> Option<Id<Object, Shared>> {
12+
unsafe fn handle_init(
13+
obj: Option<Id<Allocated<Object>, Shared>>,
14+
sel: Sel,
15+
) -> Option<Id<Object, Shared>> {
1316
<RetainSemantics<false, false, true, false>>::send_message_id(obj, sel, ())
1417
}
1518

@@ -21,7 +24,7 @@ unsafe fn handle_alloc_init(obj: &Class, sel1: Sel, sel2: Sel) -> Option<Id<Obje
2124

2225
#[no_mangle]
2326
unsafe fn handle_alloc_release(cls: &Class, sel: Sel) {
24-
let _obj: Id<Object, Shared> =
27+
let _obj: Id<Allocated<Object>, Shared> =
2528
<RetainSemantics<false, true, false, false>>::send_message_id(cls, sel, ())
2629
.unwrap_unchecked();
2730
}

tests/ui/msg_send_id_invalid_receiver.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
//! Test compiler output with invalid msg_send_id receivers.
22
use objc2::msg_send_id;
33
use objc2::runtime::{Class, Object};
4-
use objc2::rc::{Id, Shared};
4+
use objc2::rc::{Allocated, Id, Shared};
55

66
fn main() {
77
let obj: &Object;
88
let _: Id<Object, Shared> = unsafe { msg_send_id![obj, new].unwrap() };
9-
let _: Id<Object, Shared> = unsafe { msg_send_id![obj, alloc].unwrap() };
9+
let _: Id<Allocated<Object>, Shared> = unsafe { msg_send_id![obj, alloc].unwrap() };
1010
let _: Id<Object, Shared> = unsafe { msg_send_id![obj, init].unwrap() };
1111

1212
let cls: &Class;
1313
let _: Id<Object, Shared> = unsafe { msg_send_id![cls, init].unwrap() };
14+
let obj: Id<Object, Shared>;
15+
let _: Id<Object, Shared> = unsafe { msg_send_id![obj, init].unwrap() };
16+
let obj: Option<Id<Object, Shared>>;
17+
let _: Id<Object, Shared> = unsafe { msg_send_id![obj, init].unwrap() };
1418

1519
let obj: Id<Object, Shared>;
1620
let _: Id<Object, Shared> = unsafe { msg_send_id![obj, copy].unwrap() };

tests/ui/msg_send_id_invalid_receiver.stderr

+44-10
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ note: associated function defined here
1616
| ^^^^^^^^^^^^^^^
1717

1818
error[E0308]: mismatched types
19-
--> ui/msg_send_id_invalid_receiver.rs:9:55
19+
--> ui/msg_send_id_invalid_receiver.rs:9:66
2020
|
21-
9 | let _: Id<Object, Shared> = unsafe { msg_send_id![obj, alloc].unwrap() };
22-
| -------------^^^--------
23-
| | |
24-
| | expected struct `objc2::runtime::Class`, found struct `objc2::runtime::Object`
25-
| arguments to this function are incorrect
21+
9 | let _: Id<Allocated<Object>, Shared> = unsafe { msg_send_id![obj, alloc].unwrap() };
22+
| -------------^^^--------
23+
| | |
24+
| | expected struct `objc2::runtime::Class`, found struct `objc2::runtime::Object`
25+
| arguments to this function are incorrect
2626
|
2727
= note: expected reference `&objc2::runtime::Class`
2828
found reference `&objc2::runtime::Object`
@@ -41,7 +41,7 @@ error[E0308]: mismatched types
4141
| | expected enum `Option`, found `&objc2::runtime::Object`
4242
| arguments to this function are incorrect
4343
|
44-
= note: expected enum `Option<Id<_, _>>`
44+
= note: expected enum `Option<Id<Allocated<_>, _>>`
4545
found reference `&objc2::runtime::Object`
4646
note: associated function defined here
4747
--> $WORKSPACE/objc2/src/__macro_helpers.rs
@@ -58,18 +58,52 @@ error[E0308]: mismatched types
5858
| | expected enum `Option`, found `&objc2::runtime::Class`
5959
| arguments to this function are incorrect
6060
|
61-
= note: expected enum `Option<Id<_, _>>`
61+
= note: expected enum `Option<Id<Allocated<_>, _>>`
6262
found reference `&objc2::runtime::Class`
6363
note: associated function defined here
6464
--> $WORKSPACE/objc2/src/__macro_helpers.rs
6565
|
6666
| unsafe fn send_message_id<A: MessageArguments>(obj: T, sel: Sel, args: A) -> Option<U>;
6767
| ^^^^^^^^^^^^^^^
6868

69+
error[E0308]: mismatched types
70+
--> ui/msg_send_id_invalid_receiver.rs:15:55
71+
|
72+
15 | let _: Id<Object, Shared> = unsafe { msg_send_id![obj, init].unwrap() };
73+
| -------------^^^-------
74+
| | |
75+
| | expected enum `Option`, found struct `Id`
76+
| arguments to this function are incorrect
77+
|
78+
= note: expected enum `Option<Id<Allocated<_>, _>>`
79+
found struct `Id<objc2::runtime::Object, Shared>`
80+
note: associated function defined here
81+
--> $WORKSPACE/objc2/src/__macro_helpers.rs
82+
|
83+
| unsafe fn send_message_id<A: MessageArguments>(obj: T, sel: Sel, args: A) -> Option<U>;
84+
| ^^^^^^^^^^^^^^^
85+
86+
error[E0308]: mismatched types
87+
--> ui/msg_send_id_invalid_receiver.rs:17:55
88+
|
89+
17 | let _: Id<Object, Shared> = unsafe { msg_send_id![obj, init].unwrap() };
90+
| -------------^^^-------
91+
| | |
92+
| | expected struct `Allocated`, found struct `objc2::runtime::Object`
93+
| arguments to this function are incorrect
94+
|
95+
= note: expected enum `Option<Id<Allocated<_>, _>>`
96+
found enum `Option<Id<objc2::runtime::Object, Shared>>`
97+
note: associated function defined here
98+
--> $WORKSPACE/objc2/src/__macro_helpers.rs
99+
|
100+
| unsafe fn send_message_id<A: MessageArguments>(obj: T, sel: Sel, args: A) -> Option<U>;
101+
| ^^^^^^^^^^^^^^^
102+
69103
error[E0277]: the trait bound `Id<objc2::runtime::Object, Shared>: MessageReceiver` is not satisfied
70-
--> ui/msg_send_id_invalid_receiver.rs:16:42
104+
--> ui/msg_send_id_invalid_receiver.rs:20:42
71105
|
72-
16 | let _: Id<Object, Shared> = unsafe { msg_send_id![obj, copy].unwrap() };
106+
20 | let _: Id<Object, Shared> = unsafe { msg_send_id![obj, copy].unwrap() };
73107
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `MessageReceiver` is not implemented for `Id<objc2::runtime::Object, Shared>`
74108
|
75109
= help: the following other types implement trait `MessageReceiver`:

tests/ui/msg_send_id_invalid_return.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
//! Test compiler output with invalid msg_send_id receivers.
22
use objc2::msg_send_id;
33
use objc2::runtime::{Class, Object};
4-
use objc2::rc::{Id, Owned, Shared};
4+
use objc2::rc::{Allocated, Id, Owned, Shared};
55
use objc2_foundation::NSObject;
66

77
fn main() {
88
let cls: &Class;
99
let _: &Object = unsafe { msg_send_id![cls, new].unwrap() };
1010
let _: Id<Class, Shared> = unsafe { msg_send_id![cls, new].unwrap() };
1111
let _: &Object = unsafe { msg_send_id![cls, alloc].unwrap() };
12-
let _: Id<Class, Shared> = unsafe { msg_send_id![cls, alloc].unwrap() };
12+
let _: Id<Allocated<Class>, Shared> = unsafe { msg_send_id![cls, alloc].unwrap() };
13+
let _: Id<Object, Shared> = unsafe { msg_send_id![cls, alloc].unwrap() };
1314

14-
let obj: Option<Id<Object, Shared>>;
15+
let obj: Option<Id<Allocated<Object>, Shared>>;
1516
let _: &Object = unsafe { msg_send_id![obj, init].unwrap() };
16-
let obj: Option<Id<Object, Shared>>;
17+
let obj: Option<Id<Allocated<Object>, Shared>>;
1718
let _: Id<Class, Shared> = unsafe { msg_send_id![obj, init].unwrap() };
18-
let obj: Option<Id<Object, Shared>>;
19+
let obj: Option<Id<Allocated<Object>, Shared>>;
1920
let _: Id<NSObject, Shared> = unsafe { msg_send_id![obj, init].unwrap() };
20-
let obj: Option<Id<Object, Shared>>;
21+
let obj: Option<Id<Allocated<Object>, Shared>>;
2122
let _: Id<Object, Owned> = unsafe { msg_send_id![obj, init].unwrap() };
2223

2324
let obj: Id<Object, Shared>;

0 commit comments

Comments
 (0)