@@ -174,6 +174,13 @@ macro_rules! define_client_side {
174
174
}
175
175
with_api ! ( self , self , define_client_side) ;
176
176
177
+ enum BridgeState < ' a , ' bridge > {
178
+ NotConnected ,
179
+ Connected ( & ' a mut Bridge < ' bridge > ) ,
180
+ InUse ,
181
+ }
182
+
183
+ #[ repr( align( 2 ) ) ]
177
184
struct Bridge < ' a > {
178
185
/// Reusable buffer (only `clear`-ed, never shrunk), primarily
179
186
/// used for making requests.
@@ -189,45 +196,59 @@ struct Bridge<'a> {
189
196
impl < ' a > !Send for Bridge < ' a > { }
190
197
impl < ' a > !Sync for Bridge < ' a > { }
191
198
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;
195
204
196
- /// A server is connected and available for requests.
197
- Connected ( Bridge < ' a > ) ,
205
+ struct RestoreOnDrop ( * mut ( ) ) ;
198
206
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
+ }
203
212
204
- enum BridgeStateL { }
213
+ thread_local ! {
214
+ static BRIDGE_STATE : Cell <* mut ( ) > = const { Cell :: new( ptr:: null_mut( ) ) } ;
215
+ }
205
216
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) ;
209
221
210
- thread_local ! {
211
- static BRIDGE_STATE : scoped_cell:: ScopedCell <BridgeStateL > =
212
- const { scoped_cell:: ScopedCell :: new( BridgeState :: NotConnected ) } ;
213
- }
222
+ f ( )
223
+ }
214
224
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)
225
246
}
226
247
}
227
248
228
249
impl Bridge < ' _ > {
229
250
fn with < R > ( f : impl FnOnce ( & mut Bridge < ' _ > ) -> R ) -> R {
230
- BridgeState :: with ( |state| match state {
251
+ state :: with ( |state| match state {
231
252
BridgeState :: NotConnected => {
232
253
panic ! ( "procedural macro API is used outside of a procedural macro" ) ;
233
254
}
@@ -240,10 +261,7 @@ impl Bridge<'_> {
240
261
}
241
262
242
263
pub ( 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 ) )
247
265
}
248
266
249
267
/// 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) {
282
300
HIDE_PANICS_DURING_EXPANSION . call_once ( || {
283
301
let prev = panic:: take_hook ( ) ;
284
302
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 ( ) {
290
304
prev ( info)
291
305
}
292
306
} ) ) ;
@@ -312,29 +326,24 @@ fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
312
326
let ( globals, input) = <( ExpnGlobals < Span > , A ) >:: decode ( reader, & mut ( ) ) ;
313
327
314
328
// 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 ( ) ) ;
338
347
} ) )
339
348
. map_err ( PanicMessage :: from)
340
349
. unwrap_or_else ( |e| {
0 commit comments