@@ -174,6 +174,13 @@ macro_rules! define_client_side {
174174}
175175with_api ! ( self , self , define_client_side) ;
176176
177+ enum BridgeState < ' a , ' bridge > {
178+ NotConnected ,
179+ Connected ( & ' a mut Bridge < ' bridge > ) ,
180+ InUse ,
181+ }
182+
183+ #[ repr( align( 2 ) ) ]
177184struct Bridge < ' a > {
178185 /// Reusable buffer (only `clear`-ed, never shrunk), primarily
179186 /// used for making requests.
@@ -189,45 +196,59 @@ struct Bridge<'a> {
189196impl < ' a > !Send for Bridge < ' a > { }
190197impl < ' a > !Sync for Bridge < ' a > { }
191198
192- enum BridgeState < ' a > {
193- /// No server is currently connected to this client.
194- NotConnected ,
199+ #[ allow( unsafe_code) ]
200+ mod state {
201+ use super :: { Bridge , BridgeState } ;
202+ use std:: cell:: Cell ;
203+ use std:: ptr;
195204
196- /// A server is connected and available for requests.
197- Connected ( Bridge < ' a > ) ,
205+ struct RestoreOnDrop ( * mut ( ) ) ;
198206
199- /// Access to the bridge is being exclusively acquired
200- /// (e.g., during `BridgeState::with`).
201- InUse ,
202- }
207+ impl Drop for RestoreOnDrop {
208+ fn drop ( & mut self ) {
209+ BRIDGE_STATE . set ( self . 0 ) ;
210+ }
211+ }
203212
204- enum BridgeStateL { }
213+ thread_local ! {
214+ static BRIDGE_STATE : Cell <* mut ( ) > = const { Cell :: new( ptr:: null_mut( ) ) } ;
215+ }
205216
206- impl < ' a > scoped_cell:: ApplyL < ' a > for BridgeStateL {
207- type Out = BridgeState < ' a > ;
208- }
217+ pub ( super ) fn connect < ' bridge , R > ( bridge : & mut Bridge < ' bridge > , f : impl FnOnce ( ) -> R ) -> R {
218+ let inner = ptr:: from_mut ( bridge) . cast ( ) ;
219+ let outer = BRIDGE_STATE . replace ( inner) ;
220+ let _restore = RestoreOnDrop ( outer) ;
209221
210- thread_local ! {
211- static BRIDGE_STATE : scoped_cell:: ScopedCell <BridgeStateL > =
212- const { scoped_cell:: ScopedCell :: new( BridgeState :: NotConnected ) } ;
213- }
222+ f ( )
223+ }
214224
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) )
225+ pub ( super ) fn with < R > ( f : impl for < ' bridge > FnOnce ( BridgeState < ' _ , ' bridge > ) -> R ) -> R {
226+ // As `Bridge` has an alignment of 2, this cannot be a valid pointer.
227+ // Use it to indicate that the bridge is in use.
228+ let state = BRIDGE_STATE . replace ( ptr:: without_provenance_mut ( 1 ) ) ;
229+ let _restore = RestoreOnDrop ( state) ;
230+ let bridge = if state. is_null ( ) {
231+ BridgeState :: NotConnected
232+ } else if state. addr ( ) == 1 {
233+ BridgeState :: InUse
234+ } else {
235+ // SAFETY: the only place where the pointer is set is in `connect`.
236+ // It puts back the previous value after the inner call has returned,
237+ // so we know that as long as the pointer is valid, it came from a
238+ // mutable reference to a `Bridge` that outlasts the call to this
239+ // function. Since `f` must work for any lifetime of the bridge,
240+ // including the actual one, we can lie here and say that the
241+ // lifetime is `'static` without anyone noticing.
242+ BridgeState :: Connected ( unsafe { & mut * ( state as * mut Bridge < ' static > ) } )
243+ } ;
244+
245+ f ( bridge)
225246 }
226247}
227248
228249impl Bridge < ' _ > {
229250 fn with < R > ( f : impl FnOnce ( & mut Bridge < ' _ > ) -> R ) -> R {
230- BridgeState :: with ( |state| match state {
251+ state :: with ( |state| match state {
231252 BridgeState :: NotConnected => {
232253 panic ! ( "procedural macro API is used outside of a procedural macro" ) ;
233254 }
@@ -240,10 +261,7 @@ impl Bridge<'_> {
240261}
241262
242263pub ( crate ) fn is_available ( ) -> bool {
243- BridgeState :: with ( |state| match state {
244- BridgeState :: Connected ( _) | BridgeState :: InUse => true ,
245- BridgeState :: NotConnected => false ,
246- } )
264+ state:: with ( |s| matches ! ( s, BridgeState :: Connected ( _) | BridgeState :: InUse ) )
247265}
248266
249267/// A client-side RPC entry-point, which may be using a different `proc_macro`
@@ -282,11 +300,7 @@ fn maybe_install_panic_hook(force_show_panics: bool) {
282300 HIDE_PANICS_DURING_EXPANSION . call_once ( || {
283301 let prev = panic:: take_hook ( ) ;
284302 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 {
303+ if force_show_panics || !is_available ( ) {
290304 prev ( info)
291305 }
292306 } ) ) ;
@@ -312,29 +326,24 @@ fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
312326 let ( globals, input) = <( ExpnGlobals < Span > , A ) >:: decode ( reader, & mut ( ) ) ;
313327
314328 // 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- } )
329+ let mut bridge = Bridge { cached_buffer : buf. take ( ) , dispatch, globals } ;
330+
331+ let output = state:: connect ( & mut bridge, || f ( input) ) ;
332+
333+ // Take the `cached_buffer` back out, for the output value.
334+ buf = bridge. cached_buffer ;
335+
336+ // HACK(eddyb) Separate encoding a success value (`Ok(output)`)
337+ // from encoding a panic (`Err(e: PanicMessage)`) to avoid
338+ // having handles outside the `bridge.enter(|| ...)` scope, and
339+ // to catch panics that could happen while encoding the success.
340+ //
341+ // Note that panics should be impossible beyond this point, but
342+ // this is defensively trying to avoid any accidental panicking
343+ // reaching the `extern "C"` (which should `abort` but might not
344+ // at the moment, so this is also potentially preventing UB).
345+ buf. clear ( ) ;
346+ Ok :: < _ , ( ) > ( output) . encode ( & mut buf, & mut ( ) ) ;
338347 } ) )
339348 . map_err ( PanicMessage :: from)
340349 . unwrap_or_else ( |e| {
0 commit comments