Skip to content

Commit bca4a3b

Browse files
committed
proc_macro: integrate RefCell into state pointer
1 parent ac770f7 commit bca4a3b

File tree

1 file changed

+52
-35
lines changed

1 file changed

+52
-35
lines changed

library/proc_macro/src/bridge/client.rs

+52-35
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
33
use super::*;
44

5-
use std::cell::RefCell;
65
use std::marker::PhantomData;
76
use std::sync::atomic::AtomicU32;
87

@@ -175,6 +174,13 @@ macro_rules! define_client_side {
175174
}
176175
with_api!(self, self, define_client_side);
177176

177+
enum BridgeState<'a, 'bridge> {
178+
NotConnected,
179+
Connected(&'a mut Bridge<'bridge>),
180+
InUse,
181+
}
182+
183+
#[repr(align(2))]
178184
struct Bridge<'a> {
179185
/// Reusable buffer (only `clear`-ed, never shrunk), primarily
180186
/// used for making requests.
@@ -192,59 +198,70 @@ impl<'a> !Sync for Bridge<'a> {}
192198

193199
#[allow(unsafe_code)]
194200
mod state {
195-
use super::Bridge;
196-
use std::cell::{Cell, RefCell};
201+
use super::{Bridge, BridgeState};
202+
use std::cell::Cell;
197203
use std::ptr;
198204

199-
thread_local! {
200-
static BRIDGE_STATE: Cell<*const ()> = const { Cell::new(ptr::null()) };
201-
}
205+
struct RestoreOnDrop(*mut ());
202206

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-
}
207+
impl Drop for RestoreOnDrop {
208+
fn drop(&mut self) {
209+
BRIDGE_STATE.set(self.0);
209210
}
211+
}
210212

211-
let inner = ptr::from_ref(state).cast();
213+
thread_local! {
214+
static BRIDGE_STATE: Cell<*mut ()> = const { Cell::new(ptr::null_mut()) };
215+
}
216+
217+
pub(super) fn connect<'bridge, R>(bridge: &mut Bridge<'bridge>, f: impl FnOnce() -> R) -> R {
218+
let inner = ptr::from_mut(bridge).cast();
212219
let outer = BRIDGE_STATE.replace(inner);
213220
let _restore = RestoreOnDrop(outer);
214221

215222
f()
216223
}
217224

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() };
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+
230245
f(bridge)
231246
}
232247
}
233248

234249
impl Bridge<'_> {
235250
fn with<R>(f: impl FnOnce(&mut Bridge<'_>) -> R) -> R {
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)
251+
state::with(|state| match state {
252+
BridgeState::NotConnected => {
253+
panic!("procedural macro API is used outside of a procedural macro");
254+
}
255+
BridgeState::InUse => {
256+
panic!("procedural macro API is used while it's already in use");
257+
}
258+
BridgeState::Connected(bridge) => f(bridge),
242259
})
243260
}
244261
}
245262

246263
pub(crate) fn is_available() -> bool {
247-
state::with(|s| s.is_some())
264+
state::with(|s| matches!(s, BridgeState::Connected(_) | BridgeState::InUse))
248265
}
249266

250267
/// A client-side RPC entry-point, which may be using a different `proc_macro`
@@ -309,12 +326,12 @@ fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
309326
let (globals, input) = <(ExpnGlobals<Span>, A)>::decode(reader, &mut ());
310327

311328
// Put the buffer we used for input back in the `Bridge` for requests.
312-
let state = RefCell::new(Bridge { cached_buffer: buf.take(), dispatch, globals });
329+
let mut bridge = Bridge { cached_buffer: buf.take(), dispatch, globals };
313330

314-
let output = state::set(&state, || f(input));
331+
let output = state::connect(&mut bridge, || f(input));
315332

316333
// Take the `cached_buffer` back out, for the output value.
317-
buf = RefCell::into_inner(state).cached_buffer;
334+
buf = bridge.cached_buffer;
318335

319336
// HACK(eddyb) Separate encoding a success value (`Ok(output)`)
320337
// from encoding a panic (`Err(e: PanicMessage)`) to avoid

0 commit comments

Comments
 (0)