@@ -81,73 +81,143 @@ impl Drop for AutoreleasePool {
8181 }
8282}
8383
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 { }
9490
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+ }
153223}
0 commit comments