2
2
3
3
use super :: * ;
4
4
5
+ use std:: cell:: RefCell ;
5
6
use std:: marker:: PhantomData ;
6
7
use std:: sync:: atomic:: AtomicU32 ;
7
8
@@ -189,61 +190,61 @@ struct Bridge<'a> {
189
190
impl < ' a > !Send for Bridge < ' a > { }
190
191
impl < ' a > !Sync for Bridge < ' a > { }
191
192
192
- enum BridgeState < ' a > {
193
- /// No server is currently connected to this client.
194
- NotConnected ,
193
+ #[ allow( unsafe_code) ]
194
+ mod state {
195
+ use super :: Bridge ;
196
+ use std:: cell:: { Cell , RefCell } ;
197
+ use std:: ptr;
195
198
196
- /// A server is connected and available for requests.
197
- Connected ( Bridge < ' a > ) ,
198
-
199
- /// Access to the bridge is being exclusively acquired
200
- /// (e.g., during `BridgeState::with`).
201
- InUse ,
202
- }
199
+ thread_local ! {
200
+ static BRIDGE_STATE : Cell <* const ( ) > = const { Cell :: new( ptr:: null( ) ) } ;
201
+ }
203
202
204
- enum BridgeStateL { }
203
+ pub ( super ) fn set < ' bridge , R > ( state : & RefCell < Bridge < ' bridge > > , f : impl FnOnce ( ) -> R ) -> R {
204
+ struct RestoreOnDrop ( * const ( ) ) ;
205
+ impl Drop for RestoreOnDrop {
206
+ fn drop ( & mut self ) {
207
+ BRIDGE_STATE . set ( self . 0 ) ;
208
+ }
209
+ }
205
210
206
- impl < ' a > scoped_cell :: ApplyL < ' a > for BridgeStateL {
207
- type Out = BridgeState < ' a > ;
208
- }
211
+ let inner = ptr :: from_ref ( state ) . cast ( ) ;
212
+ let outer = BRIDGE_STATE . replace ( inner ) ;
213
+ let _restore = RestoreOnDrop ( outer ) ;
209
214
210
- thread_local ! {
211
- static BRIDGE_STATE : scoped_cell:: ScopedCell <BridgeStateL > =
212
- const { scoped_cell:: ScopedCell :: new( BridgeState :: NotConnected ) } ;
213
- }
215
+ f ( )
216
+ }
214
217
215
- impl BridgeState < ' _ > {
216
- /// Take exclusive control of the thread-local
217
- /// `BridgeState`, and pass it to `f`, mutably.
218
- /// The state will be restored after `f` exits, even
219
- /// by panic, including modifications made to it by `f`.
220
- ///
221
- /// N.B., while `f` is running, the thread-local state
222
- /// is `BridgeState::InUse`.
223
- fn with < R > ( f : impl FnOnce ( & mut BridgeState < ' _ > ) -> R ) -> R {
224
- BRIDGE_STATE . with ( |state| state. replace ( BridgeState :: InUse , f) )
218
+ pub ( super ) fn with < R > (
219
+ f : impl for < ' bridge > FnOnce ( Option < & RefCell < Bridge < ' bridge > > > ) -> R ,
220
+ ) -> R {
221
+ let state = BRIDGE_STATE . get ( ) ;
222
+ // SAFETY: the only place where the pointer is set is in `set`. It puts
223
+ // back the previous value after the inner call has returned, so we know
224
+ // that as long as the pointer is not null, it came from a reference to
225
+ // a `RefCell<Bridge>` that outlasts the call to this function. Since `f`
226
+ // works the same for any lifetime of the bridge, including the actual
227
+ // one, we can lie here and say that the lifetime is `'static` without
228
+ // anyone noticing.
229
+ let bridge = unsafe { state. cast :: < RefCell < Bridge < ' static > > > ( ) . as_ref ( ) } ;
230
+ f ( bridge)
225
231
}
226
232
}
227
233
228
234
impl Bridge < ' _ > {
229
235
fn with < R > ( f : impl FnOnce ( & mut Bridge < ' _ > ) -> R ) -> R {
230
- BridgeState :: with ( |state| match state {
231
- BridgeState :: NotConnected => {
232
- panic ! ( "procedural macro API is used outside of a procedural macro" ) ;
233
- }
234
- BridgeState :: InUse => {
235
- panic ! ( "procedural macro API is used while it's already in use" ) ;
236
- }
237
- BridgeState :: Connected ( bridge) => f ( bridge) ,
236
+ state:: with ( |state| {
237
+ let bridge = state. expect ( "procedural macro API is used outside of a procedural macro" ) ;
238
+ let mut bridge = bridge
239
+ . try_borrow_mut ( )
240
+ . expect ( "procedural macro API is used while it's already in use" ) ;
241
+ f ( & mut bridge)
238
242
} )
239
243
}
240
244
}
241
245
242
246
pub ( crate ) fn is_available ( ) -> bool {
243
- BridgeState :: with ( |state| match state {
244
- BridgeState :: Connected ( _) | BridgeState :: InUse => true ,
245
- BridgeState :: NotConnected => false ,
246
- } )
247
+ state:: with ( |s| s. is_some ( ) )
247
248
}
248
249
249
250
/// A client-side RPC entry-point, which may be using a different `proc_macro`
@@ -282,11 +283,7 @@ fn maybe_install_panic_hook(force_show_panics: bool) {
282
283
HIDE_PANICS_DURING_EXPANSION . call_once ( || {
283
284
let prev = panic:: take_hook ( ) ;
284
285
panic:: set_hook ( Box :: new ( move |info| {
285
- let show = BridgeState :: with ( |state| match state {
286
- BridgeState :: NotConnected => true ,
287
- BridgeState :: Connected ( _) | BridgeState :: InUse => force_show_panics,
288
- } ) ;
289
- if show {
286
+ if force_show_panics || !is_available ( ) {
290
287
prev ( info)
291
288
}
292
289
} ) ) ;
@@ -312,29 +309,24 @@ fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
312
309
let ( globals, input) = <( ExpnGlobals < Span > , A ) >:: decode ( reader, & mut ( ) ) ;
313
310
314
311
// Put the buffer we used for input back in the `Bridge` for requests.
315
- let new_state =
316
- BridgeState :: Connected ( Bridge { cached_buffer : buf. take ( ) , dispatch, globals } ) ;
317
-
318
- BRIDGE_STATE . with ( |state| {
319
- state. set ( new_state, || {
320
- let output = f ( input) ;
321
-
322
- // Take the `cached_buffer` back out, for the output value.
323
- buf = Bridge :: with ( |bridge| bridge. cached_buffer . take ( ) ) ;
324
-
325
- // HACK(eddyb) Separate encoding a success value (`Ok(output)`)
326
- // from encoding a panic (`Err(e: PanicMessage)`) to avoid
327
- // having handles outside the `bridge.enter(|| ...)` scope, and
328
- // to catch panics that could happen while encoding the success.
329
- //
330
- // Note that panics should be impossible beyond this point, but
331
- // this is defensively trying to avoid any accidental panicking
332
- // reaching the `extern "C"` (which should `abort` but might not
333
- // at the moment, so this is also potentially preventing UB).
334
- buf. clear ( ) ;
335
- Ok :: < _ , ( ) > ( output) . encode ( & mut buf, & mut ( ) ) ;
336
- } )
337
- } )
312
+ let state = RefCell :: new ( Bridge { cached_buffer : buf. take ( ) , dispatch, globals } ) ;
313
+
314
+ let output = state:: set ( & state, || f ( input) ) ;
315
+
316
+ // Take the `cached_buffer` back out, for the output value.
317
+ buf = RefCell :: into_inner ( state) . cached_buffer ;
318
+
319
+ // HACK(eddyb) Separate encoding a success value (`Ok(output)`)
320
+ // from encoding a panic (`Err(e: PanicMessage)`) to avoid
321
+ // having handles outside the `bridge.enter(|| ...)` scope, and
322
+ // to catch panics that could happen while encoding the success.
323
+ //
324
+ // Note that panics should be impossible beyond this point, but
325
+ // this is defensively trying to avoid any accidental panicking
326
+ // reaching the `extern "C"` (which should `abort` but might not
327
+ // at the moment, so this is also potentially preventing UB).
328
+ buf. clear ( ) ;
329
+ Ok :: < _ , ( ) > ( output) . encode ( & mut buf, & mut ( ) ) ;
338
330
} ) )
339
331
. map_err ( PanicMessage :: from)
340
332
. unwrap_or_else ( |e| {
0 commit comments