@@ -81,73 +81,144 @@ 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+ // TODO: Unsure how negative impls work exactly
89+ #[ cfg( feature = "unstable_autoreleasesafe" ) ]
90+ impl !AutoreleaseSafe for AutoreleasePool { }
9491
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+ }
153224}
0 commit comments