diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 1e4193a5a16cc..6b9bc0ab54bf3 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -14,7 +14,7 @@ use rustc_span::def_id::CrateNum; use rustc_span::symbol::{self, kw, sym, Symbol}; use rustc_span::{BytePos, FileName, Pos, SourceFile, Span}; -use pm::bridge::{server, TokenTree}; +use pm::bridge::{server, ExpnGlobals, TokenTree}; use pm::{Delimiter, Level, LineColumn, Spacing}; use std::ops::Bound; use std::{ascii, panic}; @@ -370,7 +370,7 @@ impl<'a, 'b> Rustc<'a, 'b> { } fn lit(&mut self, kind: token::LitKind, symbol: Symbol, suffix: Option) -> Literal { - Literal { lit: token::Lit::new(kind, symbol, suffix), span: server::Span::call_site(self) } + Literal { lit: token::Lit::new(kind, symbol, suffix), span: self.call_site } } } @@ -547,7 +547,7 @@ impl server::Group for Rustc<'_, '_> { Group { delimiter, stream: stream.unwrap_or_default(), - span: DelimSpan::from_single(server::Span::call_site(self)), + span: DelimSpan::from_single(self.call_site), flatten: false, } } @@ -579,7 +579,7 @@ impl server::Group for Rustc<'_, '_> { impl server::Punct for Rustc<'_, '_> { fn new(&mut self, ch: char, spacing: Spacing) -> Self::Punct { - Punct::new(ch, spacing == Spacing::Joint, server::Span::call_site(self)) + Punct::new(ch, spacing == Spacing::Joint, self.call_site) } fn as_char(&mut self, punct: Self::Punct) -> char { @@ -829,18 +829,6 @@ impl server::Span for Rustc<'_, '_> { } } - fn def_site(&mut self) -> Self::Span { - self.def_site - } - - fn call_site(&mut self) -> Self::Span { - self.call_site - } - - fn mixed_site(&mut self) -> Self::Span { - self.mixed_site - } - fn source_file(&mut self, span: Self::Span) -> Self::SourceFile { self.sess().source_map().lookup_char_pos(span.lo()).file } @@ -926,3 +914,13 @@ impl server::Span for Rustc<'_, '_> { }) } } + +impl server::Server for Rustc<'_, '_> { + fn globals(&mut self) -> ExpnGlobals { + ExpnGlobals { + def_site: self.def_site, + call_site: self.call_site, + mixed_site: self.mixed_site, + } + } +} diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index d7cc700495b97..74b4a91662bb5 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -230,6 +230,20 @@ impl Clone for SourceFile { } } +impl Span { + pub(crate) fn def_site() -> Span { + Bridge::with(|bridge| bridge.globals.def_site) + } + + pub(crate) fn call_site() -> Span { + Bridge::with(|bridge| bridge.globals.call_site) + } + + pub(crate) fn mixed_site() -> Span { + Bridge::with(|bridge| bridge.globals.mixed_site) + } +} + impl fmt::Debug for Span { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.debug()) @@ -263,6 +277,21 @@ macro_rules! define_client_side { } with_api!(self, self, define_client_side); +struct Bridge<'a> { + /// Reusable buffer (only `clear`-ed, never shrunk), primarily + /// used for making requests. + cached_buffer: Buffer, + + /// Server-side function that the client uses to make requests. + dispatch: closure::Closure<'a, Buffer, Buffer>, + + /// Provided globals for this macro expansion. + globals: ExpnGlobals, +} + +impl<'a> !Send for Bridge<'a> {} +impl<'a> !Sync for Bridge<'a> {} + enum BridgeState<'a> { /// No server is currently connected to this client. NotConnected, @@ -305,34 +334,6 @@ impl BridgeState<'_> { } impl Bridge<'_> { - pub(crate) fn is_available() -> bool { - BridgeState::with(|state| match state { - BridgeState::Connected(_) | BridgeState::InUse => true, - BridgeState::NotConnected => false, - }) - } - - fn enter(self, f: impl FnOnce() -> R) -> R { - let force_show_panics = self.force_show_panics; - // Hide the default panic output within `proc_macro` expansions. - // NB. the server can't do this because it may use a different libstd. - static HIDE_PANICS_DURING_EXPANSION: Once = Once::new(); - HIDE_PANICS_DURING_EXPANSION.call_once(|| { - let prev = panic::take_hook(); - panic::set_hook(Box::new(move |info| { - let show = BridgeState::with(|state| match state { - BridgeState::NotConnected => true, - BridgeState::Connected(_) | BridgeState::InUse => force_show_panics, - }); - if show { - prev(info) - } - })); - }); - - BRIDGE_STATE.with(|state| state.set(BridgeState::Connected(self), f)) - } - fn with(f: impl FnOnce(&mut Bridge<'_>) -> R) -> R { BridgeState::with(|state| match state { BridgeState::NotConnected => { @@ -346,6 +347,13 @@ impl Bridge<'_> { } } +pub(crate) fn is_available() -> bool { + BridgeState::with(|state| match state { + BridgeState::Connected(_) | BridgeState::InUse => true, + BridgeState::NotConnected => false, + }) +} + /// A client-side RPC entry-point, which may be using a different `proc_macro` /// from the one used by the server, but can be invoked compatibly. /// @@ -363,7 +371,7 @@ pub struct Client { // a wrapper `fn` pointer, once `const fn` can reference `static`s. pub(super) get_handle_counters: extern "C" fn() -> &'static HandleCounters, - pub(super) run: extern "C" fn(Bridge<'_>) -> Buffer, + pub(super) run: extern "C" fn(BridgeConfig<'_>) -> Buffer, pub(super) _marker: PhantomData O>, } @@ -375,40 +383,62 @@ impl Clone for Client { } } +fn maybe_install_panic_hook(force_show_panics: bool) { + // Hide the default panic output within `proc_macro` expansions. + // NB. the server can't do this because it may use a different libstd. + static HIDE_PANICS_DURING_EXPANSION: Once = Once::new(); + HIDE_PANICS_DURING_EXPANSION.call_once(|| { + let prev = panic::take_hook(); + panic::set_hook(Box::new(move |info| { + let show = BridgeState::with(|state| match state { + BridgeState::NotConnected => true, + BridgeState::Connected(_) | BridgeState::InUse => force_show_panics, + }); + if show { + prev(info) + } + })); + }); +} + /// Client-side helper for handling client panics, entering the bridge, /// deserializing input and serializing output. // FIXME(eddyb) maybe replace `Bridge::enter` with this? fn run_client DecodeMut<'a, 's, ()>, R: Encode<()>>( - mut bridge: Bridge<'_>, + config: BridgeConfig<'_>, f: impl FnOnce(A) -> R, ) -> Buffer { - // The initial `cached_buffer` contains the input. - let mut buf = bridge.cached_buffer.take(); + let BridgeConfig { input: mut buf, dispatch, force_show_panics, .. } = config; panic::catch_unwind(panic::AssertUnwindSafe(|| { - bridge.enter(|| { - let reader = &mut &buf[..]; - let input = A::decode(reader, &mut ()); - - // Put the `cached_buffer` back in the `Bridge`, for requests. - Bridge::with(|bridge| bridge.cached_buffer = buf.take()); - - let output = f(input); - - // Take the `cached_buffer` back out, for the output value. - buf = Bridge::with(|bridge| bridge.cached_buffer.take()); - - // HACK(eddyb) Separate encoding a success value (`Ok(output)`) - // from encoding a panic (`Err(e: PanicMessage)`) to avoid - // having handles outside the `bridge.enter(|| ...)` scope, and - // to catch panics that could happen while encoding the success. - // - // Note that panics should be impossible beyond this point, but - // this is defensively trying to avoid any accidental panicking - // reaching the `extern "C"` (which should `abort` but might not - // at the moment, so this is also potentially preventing UB). - buf.clear(); - Ok::<_, ()>(output).encode(&mut buf, &mut ()); + maybe_install_panic_hook(force_show_panics); + + let reader = &mut &buf[..]; + let (globals, input) = <(ExpnGlobals, A)>::decode(reader, &mut ()); + + // Put the buffer we used for input back in the `Bridge` for requests. + let new_state = + BridgeState::Connected(Bridge { cached_buffer: buf.take(), dispatch, globals }); + + BRIDGE_STATE.with(|state| { + state.set(new_state, || { + let output = f(input); + + // Take the `cached_buffer` back out, for the output value. + buf = Bridge::with(|bridge| bridge.cached_buffer.take()); + + // HACK(eddyb) Separate encoding a success value (`Ok(output)`) + // from encoding a panic (`Err(e: PanicMessage)`) to avoid + // having handles outside the `bridge.enter(|| ...)` scope, and + // to catch panics that could happen while encoding the success. + // + // Note that panics should be impossible beyond this point, but + // this is defensively trying to avoid any accidental panicking + // reaching the `extern "C"` (which should `abort` but might not + // at the moment, so this is also potentially preventing UB). + buf.clear(); + Ok::<_, ()>(output).encode(&mut buf, &mut ()); + }) }) })) .map_err(PanicMessage::from) diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 4e931569ef633..3bdc9007cd2e9 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -151,9 +151,6 @@ macro_rules! with_api { }, Span { fn debug($self: $S::Span) -> String; - fn def_site() -> $S::Span; - fn call_site() -> $S::Span; - fn mixed_site() -> $S::Span; fn source_file($self: $S::Span) -> $S::SourceFile; fn parent($self: $S::Span) -> Option<$S::Span>; fn source($self: $S::Span) -> $S::Span; @@ -213,16 +210,15 @@ use buffer::Buffer; pub use rpc::PanicMessage; use rpc::{Decode, DecodeMut, Encode, Reader, Writer}; -/// An active connection between a server and a client. -/// The server creates the bridge (`Bridge::run_server` in `server.rs`), -/// then passes it to the client through the function pointer in the `run` -/// field of `client::Client`. The client holds its copy of the `Bridge` +/// Configuration for establishing an active connection between a server and a +/// client. The server creates the bridge config (`run_server` in `server.rs`), +/// then passes it to the client through the function pointer in the `run` field +/// of `client::Client`. The client constructs a local `Bridge` from the config /// in TLS during its execution (`Bridge::{enter, with}` in `client.rs`). #[repr(C)] -pub struct Bridge<'a> { - /// Reusable buffer (only `clear`-ed, never shrunk), primarily - /// used for making requests, but also for passing input to client. - cached_buffer: Buffer, +pub struct BridgeConfig<'a> { + /// Buffer used to pass initial input to the client. + input: Buffer, /// Server-side function that the client uses to make requests. dispatch: closure::Closure<'a, Buffer, Buffer>, @@ -379,6 +375,25 @@ rpc_encode_decode!( ); macro_rules! mark_compound { + (struct $name:ident <$($T:ident),+> { $($field:ident),* $(,)? }) => { + impl<$($T: Mark),+> Mark for $name <$($T),+> { + type Unmarked = $name <$($T::Unmarked),+>; + fn mark(unmarked: Self::Unmarked) -> Self { + $name { + $($field: Mark::mark(unmarked.$field)),* + } + } + } + + impl<$($T: Unmark),+> Unmark for $name <$($T),+> { + type Unmarked = $name <$($T::Unmarked),+>; + fn unmark(self) -> Self::Unmarked { + $name { + $($field: Unmark::unmark(self.$field)),* + } + } + } + }; (enum $name:ident <$($T:ident),+> { $($variant:ident $(($field:ident))?),* $(,)? }) => { impl<$($T: Mark),+> Mark for $name <$($T),+> { type Unmarked = $name <$($T::Unmarked),+>; @@ -449,3 +464,16 @@ compound_traits!( Literal(tt), } ); + +/// Globals provided alongside the initial inputs for a macro expansion. +/// Provides values such as spans which are used frequently to avoid RPC. +#[derive(Clone)] +pub struct ExpnGlobals { + pub def_site: S, + pub call_site: S, + pub mixed_site: S, +} + +compound_traits!( + struct ExpnGlobals { def_site, call_site, mixed_site } +); diff --git a/library/proc_macro/src/bridge/selfless_reify.rs b/library/proc_macro/src/bridge/selfless_reify.rs index 4ee4bb87c2bbd..907ad256e4b43 100644 --- a/library/proc_macro/src/bridge/selfless_reify.rs +++ b/library/proc_macro/src/bridge/selfless_reify.rs @@ -75,9 +75,10 @@ macro_rules! define_reify_functions { define_reify_functions! { fn _reify_to_extern_c_fn_unary for extern "C" fn(arg: A) -> R; - // HACK(eddyb) this abstraction is used with `for<'a> fn(Bridge<'a>) -> T` - // but that doesn't work with just `reify_to_extern_c_fn_unary` because of - // the `fn` pointer type being "higher-ranked" (i.e. the `for<'a>` binder). - // FIXME(eddyb) try to remove the lifetime from `Bridge`, that'd help. - fn reify_to_extern_c_fn_hrt_bridge for extern "C" fn(bridge: super::Bridge<'_>) -> R; + // HACK(eddyb) this abstraction is used with `for<'a> fn(BridgeConfig<'a>) + // -> T` but that doesn't work with just `reify_to_extern_c_fn_unary` + // because of the `fn` pointer type being "higher-ranked" (i.e. the + // `for<'a>` binder). + // FIXME(eddyb) try to remove the lifetime from `BridgeConfig`, that'd help. + fn reify_to_extern_c_fn_hrt_bridge for extern "C" fn(bridge: super::BridgeConfig<'_>) -> R; } diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 3672299f18f48..1b7657eab7034 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -38,14 +38,21 @@ macro_rules! declare_server_traits { $(associated_fn!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)* })* - pub trait Server: Types $(+ $name)* {} - impl Server for S {} + pub trait Server: Types $(+ $name)* { + fn globals(&mut self) -> ExpnGlobals; + } } } with_api!(Self, self_, declare_server_traits); pub(super) struct MarkedTypes(S); +impl Server for MarkedTypes { + fn globals(&mut self) -> ExpnGlobals { + <_>::mark(Server::globals(&mut self.0)) + } +} + macro_rules! define_mark_types_impls { ($($name:ident { $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* @@ -120,7 +127,7 @@ pub trait ExecutionStrategy { &self, dispatcher: &mut impl DispatcherTrait, input: Buffer, - run_client: extern "C" fn(Bridge<'_>) -> Buffer, + run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, force_show_panics: bool, ) -> Buffer; } @@ -132,13 +139,13 @@ impl ExecutionStrategy for SameThread { &self, dispatcher: &mut impl DispatcherTrait, input: Buffer, - run_client: extern "C" fn(Bridge<'_>) -> Buffer, + run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, force_show_panics: bool, ) -> Buffer { let mut dispatch = |buf| dispatcher.dispatch(buf); - run_client(Bridge { - cached_buffer: input, + run_client(BridgeConfig { + input, dispatch: (&mut dispatch).into(), force_show_panics, _marker: marker::PhantomData, @@ -156,7 +163,7 @@ impl ExecutionStrategy for CrossThread1 { &self, dispatcher: &mut impl DispatcherTrait, input: Buffer, - run_client: extern "C" fn(Bridge<'_>) -> Buffer, + run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, force_show_panics: bool, ) -> Buffer { use std::sync::mpsc::channel; @@ -170,8 +177,8 @@ impl ExecutionStrategy for CrossThread1 { res_rx.recv().unwrap() }; - run_client(Bridge { - cached_buffer: input, + run_client(BridgeConfig { + input, dispatch: (&mut dispatch).into(), force_show_panics, _marker: marker::PhantomData, @@ -193,7 +200,7 @@ impl ExecutionStrategy for CrossThread2 { &self, dispatcher: &mut impl DispatcherTrait, input: Buffer, - run_client: extern "C" fn(Bridge<'_>) -> Buffer, + run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, force_show_panics: bool, ) -> Buffer { use std::sync::{Arc, Mutex}; @@ -219,8 +226,8 @@ impl ExecutionStrategy for CrossThread2 { } }; - let r = run_client(Bridge { - cached_buffer: input, + let r = run_client(BridgeConfig { + input, dispatch: (&mut dispatch).into(), force_show_panics, _marker: marker::PhantomData, @@ -258,14 +265,16 @@ fn run_server< handle_counters: &'static client::HandleCounters, server: S, input: I, - run_client: extern "C" fn(Bridge<'_>) -> Buffer, + run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, force_show_panics: bool, ) -> Result { let mut dispatcher = Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) }; + let globals = dispatcher.server.globals(); + let mut buf = Buffer::new(); - input.encode(&mut buf, &mut dispatcher.handle_store); + (globals, input).encode(&mut buf, &mut dispatcher.handle_store); buf = strategy.run_bridge_and_client(&mut dispatcher, buf, run_client, force_show_panics); diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 5e1289ec79d30..771ee50e13861 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -60,7 +60,7 @@ use std::{error, fmt, iter}; /// inside of a procedural macro, false if invoked from any other binary. #[stable(feature = "proc_macro_is_available", since = "1.57.0")] pub fn is_available() -> bool { - bridge::Bridge::is_available() + bridge::client::is_available() } /// The main type provided by this crate, representing an abstract stream of