@@ -81,73 +81,144 @@ impl Drop for AutoreleasePool {
81
81
}
82
82
}
83
83
84
- // TODO:
85
- // #![feature(negative_impls)]
86
- // #![feature(auto_traits)]
87
- // /// A trait for the sole purpose of ensuring we can't pass an `&AutoreleasePool`
88
- // /// through to the closure inside `autoreleasepool`
89
- // pub unsafe auto trait AutoreleaseSafe {}
90
- // // TODO: Unsure how negative impls work exactly
91
- // unsafe impl !AutoreleaseSafe for AutoreleasePool {}
92
- // unsafe impl !AutoreleaseSafe for &AutoreleasePool {}
93
- // unsafe impl !AutoreleaseSafe for &mut AutoreleasePool {}
84
+ /// A trait for the sole purpose of ensuring we can't pass an `&AutoreleasePool`
85
+ /// through to the closure inside `autoreleasepool`
86
+ #[ cfg( feature = "unstable_autoreleasesafe" ) ]
87
+ pub unsafe auto trait AutoreleaseSafe { }
88
+ // TODO: Unsure how negative impls work exactly
89
+ #[ cfg( feature = "unstable_autoreleasesafe" ) ]
90
+ impl !AutoreleaseSafe for AutoreleasePool { }
94
91
95
- /// Execute `f` in the context of a new autorelease pool. The pool is drained
96
- /// after the execution of `f` completes.
97
- ///
98
- /// This corresponds to `@autoreleasepool` blocks in Objective-C and Swift.
99
- ///
100
- /// The pool is passed as a reference to the enclosing function to give it a
101
- /// lifetime parameter that autoreleased objects can refer to.
102
- ///
103
- /// # Examples
104
- ///
105
- /// ```rust
106
- /// use objc::{class, msg_send};
107
- /// use objc::rc::{autoreleasepool, AutoreleasePool};
108
- /// use objc::runtime::Object;
109
- ///
110
- /// fn needs_lifetime_from_pool<'p>(_pool: &'p AutoreleasePool) -> &'p mut Object {
111
- /// let obj: *mut Object = unsafe { msg_send![class!(NSObject), new] };
112
- /// let obj: *mut Object = unsafe { msg_send![obj, autorelease] };
113
- /// // SAFETY: Lifetime bounded by the pool
114
- /// unsafe { &mut *obj }
115
- /// }
116
- ///
117
- /// autoreleasepool(|pool| {
118
- /// let obj = needs_lifetime_from_pool(pool);
119
- /// // Use `obj`
120
- /// });
121
- ///
122
- /// // `obj` is deallocated when the pool ends
123
- /// ```
124
- ///
125
- /// ```rust,compile_fail
126
- /// # use objc::{class, msg_send};
127
- /// # use objc::rc::{autoreleasepool, AutoreleasePool};
128
- /// # use objc::runtime::Object;
129
- /// #
130
- /// # fn needs_lifetime_from_pool<'p>(_pool: &'p AutoreleasePool) -> &'p mut Object {
131
- /// # let obj: *mut Object = unsafe { msg_send![class!(NSObject), new] };
132
- /// # let obj: *mut Object = unsafe { msg_send![obj, autorelease] };
133
- /// # unsafe { &mut *obj }
134
- /// # }
135
- /// #
136
- /// // Fails to compile because `obj` does not live long enough for us to
137
- /// // safely take it out of the pool.
138
- ///
139
- /// let obj = autoreleasepool(|pool| {
140
- /// let obj = needs_lifetime_from_pool(pool);
141
- /// // Use `obj`
142
- /// obj
143
- /// });
144
- /// ```
145
- ///
146
- /// TODO: More examples.
147
- pub fn autoreleasepool < T , F > ( f : F ) -> T
148
- where
149
- for < ' p > F : FnOnce ( & ' p AutoreleasePool ) -> T , // + AutoreleaseSafe,
150
- {
151
- let pool = unsafe { AutoreleasePool :: new ( ) } ;
152
- f ( & pool)
92
+ #[ cfg( feature = "unstable_autoreleasesafe" ) ]
93
+ macro_rules! fn_autoreleasepool {
94
+ { $( #[ $fn_meta: meta] ) * $v: vis fn $fn: ident( $f: ident) $b: block} => {
95
+ $( #[ $fn_meta] ) *
96
+ $v fn $fn<T , F >( $f: F ) -> T
97
+ where
98
+ for <' p> F : FnOnce ( & ' p AutoreleasePool ) -> T + AutoreleaseSafe ,
99
+ {
100
+ $b
101
+ }
102
+ }
103
+ }
104
+
105
+ #[ cfg( not( feature = "unstable_autoreleasesafe" ) ) ]
106
+ macro_rules! fn_autoreleasepool {
107
+ { $( #[ $fn_meta: meta] ) * $v: vis fn $fn: ident( $f: ident) $b: block} => {
108
+ $( #[ $fn_meta] ) *
109
+ $v fn $fn<T , F >( $f: F ) -> T
110
+ where
111
+ for <' p> F : FnOnce ( & ' p AutoreleasePool ) -> T ,
112
+ {
113
+ $b
114
+ }
115
+ }
116
+ }
117
+
118
+ fn_autoreleasepool ! (
119
+ /// Execute `f` in the context of a new autorelease pool. The pool is
120
+ /// drained after the execution of `f` completes.
121
+ ///
122
+ /// This corresponds to `@autoreleasepool` blocks in Objective-C and
123
+ /// Swift.
124
+ ///
125
+ /// The pool is passed as a reference to the enclosing function to give it
126
+ /// a lifetime parameter that autoreleased objects can refer to.
127
+ ///
128
+ /// The given reference must not be used in an inner `autoreleasepool`,
129
+ /// doing so will be a compile error in a future release. You can test
130
+ /// this guarantee with the `unstable_autoreleasesafe` crate feature on
131
+ /// nightly Rust.
132
+ ///
133
+ /// So using `autoreleasepool` is unsound right now because of this
134
+ /// specific problem.
135
+ ///
136
+ /// # Examples
137
+ ///
138
+ /// Basic usage:
139
+ ///
140
+ /// ```rust
141
+ /// use objc::{class, msg_send};
142
+ /// use objc::rc::{autoreleasepool, AutoreleasePool};
143
+ /// use objc::runtime::Object;
144
+ ///
145
+ /// fn needs_lifetime_from_pool<'p>(_pool: &'p AutoreleasePool) -> &'p mut Object {
146
+ /// let obj: *mut Object = unsafe { msg_send![class!(NSObject), new] };
147
+ /// let obj: *mut Object = unsafe { msg_send![obj, autorelease] };
148
+ /// // SAFETY: Lifetime bounded by the pool
149
+ /// unsafe { &mut *obj }
150
+ /// }
151
+ ///
152
+ /// autoreleasepool(|pool| {
153
+ /// // Create `obj` and autorelease it to the pool
154
+ /// let obj = needs_lifetime_from_pool(pool);
155
+ /// // ... use `obj` here
156
+ /// // `obj` is deallocated when the pool ends
157
+ /// });
158
+ /// ```
159
+ ///
160
+ /// Fails to compile because `obj` does not live long enough for us to
161
+ /// safely take it out of the pool:
162
+ ///
163
+ /// ```rust,compile_fail
164
+ /// # use objc::{class, msg_send};
165
+ /// # use objc::rc::{autoreleasepool, AutoreleasePool};
166
+ /// # use objc::runtime::Object;
167
+ /// #
168
+ /// # fn needs_lifetime_from_pool<'p>(_pool: &'p AutoreleasePool) -> &'p mut Object {
169
+ /// # let obj: *mut Object = unsafe { msg_send![class!(NSObject), new] };
170
+ /// # let obj: *mut Object = unsafe { msg_send![obj, autorelease] };
171
+ /// # unsafe { &mut *obj }
172
+ /// # }
173
+ /// #
174
+ /// let obj = autoreleasepool(|pool| {
175
+ /// let obj = needs_lifetime_from_pool(pool);
176
+ /// // Use `obj`
177
+ /// obj
178
+ /// });
179
+ /// ```
180
+ ///
181
+ /// Incorrect usage which causes undefined behaviour:
182
+ ///
183
+ #[ cfg_attr( feature = "unstable_autoreleasesafe" , doc = "```rust,compile_fail" ) ]
184
+ #[ cfg_attr( not( feature = "unstable_autoreleasesafe" ) , doc = "```rust" ) ]
185
+ /// # use objc::{class, msg_send};
186
+ /// # use objc::rc::{autoreleasepool, AutoreleasePool};
187
+ /// # use objc::runtime::Object;
188
+ /// #
189
+ /// # fn needs_lifetime_from_pool<'p>(_pool: &'p AutoreleasePool) -> &'p mut Object {
190
+ /// # let obj: *mut Object = unsafe { msg_send![class!(NSObject), new] };
191
+ /// # let obj: *mut Object = unsafe { msg_send![obj, autorelease] };
192
+ /// # unsafe { &mut *obj }
193
+ /// # }
194
+ /// #
195
+ /// autoreleasepool(|outer_pool| {
196
+ /// let obj = autoreleasepool(|inner_pool| {
197
+ /// let obj = needs_lifetime_from_pool(outer_pool);
198
+ /// obj
199
+ /// });
200
+ /// // `obj` can wrongly be used here because it's lifetime was
201
+ /// // assigned to the outer pool, even though it was released by the
202
+ /// // inner pool already.
203
+ /// });
204
+ /// ```
205
+ pub fn autoreleasepool( f) {
206
+ let pool = unsafe { AutoreleasePool :: new( ) } ;
207
+ f( & pool)
208
+ }
209
+ ) ;
210
+
211
+ #[ cfg( all( test, feature = "unstable_autoreleasesafe" ) ) ]
212
+ mod tests {
213
+ use super :: AutoreleaseSafe ;
214
+ use crate :: runtime:: Object ;
215
+
216
+ fn requires_autoreleasesafe < T : AutoreleaseSafe > ( ) { }
217
+
218
+ #[ test]
219
+ fn test_autoreleasesafe ( ) {
220
+ requires_autoreleasesafe :: < usize > ( ) ;
221
+ requires_autoreleasesafe :: < * mut Object > ( ) ;
222
+ requires_autoreleasesafe :: < & mut Object > ( ) ;
223
+ }
153
224
}
0 commit comments