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