22
33use super :: * ;
44
5+ use std:: cell:: RefCell ;
56use std:: marker:: PhantomData ;
67use std:: sync:: atomic:: AtomicU32 ;
78
@@ -189,61 +190,61 @@ struct Bridge<'a> {
189190impl < ' a > !Send for Bridge < ' a > { }
190191impl < ' a > !Sync for Bridge < ' a > { }
191192
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;
195198
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+ }
203202
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+ }
205210
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 ) ;
209214
210- thread_local ! {
211- static BRIDGE_STATE : scoped_cell:: ScopedCell <BridgeStateL > =
212- const { scoped_cell:: ScopedCell :: new( BridgeState :: NotConnected ) } ;
213- }
215+ f ( )
216+ }
214217
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)
225231 }
226232}
227233
228234impl Bridge < ' _ > {
229235 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)
238242 } )
239243 }
240244}
241245
242246pub ( 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 ( ) )
247248}
248249
249250/// 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) {
282283 HIDE_PANICS_DURING_EXPANSION . call_once ( || {
283284 let prev = panic:: take_hook ( ) ;
284285 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 ( ) {
290287 prev ( info)
291288 }
292289 } ) ) ;
@@ -312,29 +309,24 @@ fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
312309 let ( globals, input) = <( ExpnGlobals < Span > , A ) >:: decode ( reader, & mut ( ) ) ;
313310
314311 // 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 ( ) ) ;
338330 } ) )
339331 . map_err ( PanicMessage :: from)
340332 . unwrap_or_else ( |e| {
0 commit comments