]> git.proxmox.com Git - rustc.git/blobdiff - library/proc_macro/src/bridge/client.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / library / proc_macro / src / bridge / client.rs
index 9e9750eb8de4031dddb7facb7fb9ec35efa75b19..52a08cad9110fa18316478ad300f27788706ee5a 100644 (file)
@@ -2,6 +2,8 @@
 
 use super::*;
 
+use std::marker::PhantomData;
+
 macro_rules! define_handles {
     (
         'owned: $($oty:ident,)*
@@ -27,7 +29,6 @@ macro_rules! define_handles {
         }
 
         // FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
-        #[repr(C)]
         #[allow(non_snake_case)]
         pub(super) struct HandleStore<S: server::Types> {
             $($oty: handle::OwnedStore<S::$oty>,)*
@@ -44,21 +45,27 @@ macro_rules! define_handles {
         }
 
         $(
-            #[repr(C)]
-            pub(crate) struct $oty(handle::Handle);
-            impl !Send for $oty {}
-            impl !Sync for $oty {}
+            pub(crate) struct $oty {
+                handle: handle::Handle,
+                // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual
+                // way of doing this, but that requires unstable features.
+                // rust-analyzer uses this code and avoids unstable features.
+                _marker: PhantomData<*mut ()>,
+            }
 
             // Forward `Drop::drop` to the inherent `drop` method.
             impl Drop for $oty {
                 fn drop(&mut self) {
-                    $oty(self.0).drop();
+                    $oty {
+                        handle: self.handle,
+                        _marker: PhantomData,
+                    }.drop();
                 }
             }
 
             impl<S> Encode<S> for $oty {
                 fn encode(self, w: &mut Writer, s: &mut S) {
-                    let handle = self.0;
+                    let handle = self.handle;
                     mem::forget(self);
                     handle.encode(w, s);
                 }
@@ -74,7 +81,7 @@ macro_rules! define_handles {
 
             impl<S> Encode<S> for &$oty {
                 fn encode(self, w: &mut Writer, s: &mut S) {
-                    self.0.encode(w, s);
+                    self.handle.encode(w, s);
                 }
             }
 
@@ -88,7 +95,7 @@ macro_rules! define_handles {
 
             impl<S> Encode<S> for &mut $oty {
                 fn encode(self, w: &mut Writer, s: &mut S) {
-                    self.0.encode(w, s);
+                    self.handle.encode(w, s);
                 }
             }
 
@@ -113,21 +120,27 @@ macro_rules! define_handles {
 
             impl<S> DecodeMut<'_, '_, S> for $oty {
                 fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
-                    $oty(handle::Handle::decode(r, s))
+                    $oty {
+                        handle: handle::Handle::decode(r, s),
+                        _marker: PhantomData,
+                    }
                 }
             }
         )*
 
         $(
-            #[repr(C)]
             #[derive(Copy, Clone, PartialEq, Eq, Hash)]
-            pub(crate) struct $ity(handle::Handle);
-            impl !Send for $ity {}
-            impl !Sync for $ity {}
+            pub(crate) struct $ity {
+                handle: handle::Handle,
+                // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual
+                // way of doing this, but that requires unstable features.
+                // rust-analyzer uses this code and avoids unstable features.
+                _marker: PhantomData<*mut ()>,
+            }
 
             impl<S> Encode<S> for $ity {
                 fn encode(self, w: &mut Writer, s: &mut S) {
-                    self.0.encode(w, s);
+                    self.handle.encode(w, s);
                 }
             }
 
@@ -149,7 +162,10 @@ macro_rules! define_handles {
 
             impl<S> DecodeMut<'_, '_, S> for $ity {
                 fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
-                    $ity(handle::Handle::decode(r, s))
+                    $ity {
+                        handle: handle::Handle::decode(r, s),
+                        _marker: PhantomData,
+                    }
                 }
             }
         )*
@@ -159,24 +175,16 @@ define_handles! {
     'owned:
     FreeFunctions,
     TokenStream,
-    TokenStreamBuilder,
-    TokenStreamIter,
-    Group,
-    Literal,
     SourceFile,
-    MultiSpan,
-    Diagnostic,
 
     'interned:
-    Punct,
-    Ident,
     Span,
 }
 
 // FIXME(eddyb) generate these impls by pattern-matching on the
 // names of methods - also could use the presence of `fn drop`
 // to distinguish between 'owned and 'interned, above.
-// Alternatively, special 'modes" could be listed of types in with_api
+// Alternatively, special "modes" could be listed of types in with_api
 // instead of pattern matching on methods, here and in server decl.
 
 impl Clone for TokenStream {
@@ -185,40 +193,23 @@ impl Clone for TokenStream {
     }
 }
 
-impl Clone for TokenStreamIter {
+impl Clone for SourceFile {
     fn clone(&self) -> Self {
         self.clone()
     }
 }
 
-impl Clone for Group {
-    fn clone(&self) -> Self {
-        self.clone()
+impl Span {
+    pub(crate) fn def_site() -> Span {
+        Bridge::with(|bridge| bridge.globals.def_site)
     }
-}
 
-impl Clone for Literal {
-    fn clone(&self) -> Self {
-        self.clone()
+    pub(crate) fn call_site() -> Span {
+        Bridge::with(|bridge| bridge.globals.call_site)
     }
-}
 
-impl fmt::Debug for Literal {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Literal")
-            // format the kind without quotes, as in `kind: Float`
-            .field("kind", &format_args!("{}", &self.debug_kind()))
-            .field("symbol", &self.symbol())
-            // format `Some("...")` on one line even in {:#?} mode
-            .field("suffix", &format_args!("{:?}", &self.suffix()))
-            .field("span", &self.span())
-            .finish()
-    }
-}
-
-impl Clone for SourceFile {
-    fn clone(&self) -> Self {
-        self.clone()
+    pub(crate) fn mixed_site() -> Span {
+        Bridge::with(|bridge| bridge.globals.mixed_site)
     }
 }
 
@@ -228,24 +219,26 @@ impl fmt::Debug for Span {
     }
 }
 
+pub(crate) use super::symbol::Symbol;
+
 macro_rules! define_client_side {
     ($($name:ident {
-        $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
+        $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
     }),* $(,)?) => {
         $(impl $name {
-            $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* {
+            $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)? {
                 Bridge::with(|bridge| {
-                    let mut b = bridge.cached_buffer.take();
+                    let mut buf = bridge.cached_buffer.take();
 
-                    b.clear();
-                    api_tags::Method::$name(api_tags::$name::$method).encode(&mut b, &mut ());
-                    reverse_encode!(b; $($arg),*);
+                    buf.clear();
+                    api_tags::Method::$name(api_tags::$name::$method).encode(&mut buf, &mut ());
+                    reverse_encode!(buf; $($arg),*);
 
-                    b = bridge.dispatch.call(b);
+                    buf = bridge.dispatch.call(buf);
 
-                    let r = Result::<_, PanicMessage>::decode(&mut &b[..], &mut ());
+                    let r = Result::<_, PanicMessage>::decode(&mut &buf[..], &mut ());
 
-                    bridge.cached_buffer = b;
+                    bridge.cached_buffer = buf;
 
                     r.unwrap_or_else(|e| panic::resume_unwind(e.into()))
                 })
@@ -255,6 +248,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<Span>,
+}
+
+impl<'a> !Send for Bridge<'a> {}
+impl<'a> !Sync for Bridge<'a> {}
+
 enum BridgeState<'a> {
     /// No server is currently connected to this client.
     NotConnected,
@@ -297,33 +305,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<R>(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(|| {
-            panic::update_hook(move |prev, 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<R>(f: impl FnOnce(&mut Bridge<'_>) -> R) -> R {
         BridgeState::with(|state| match state {
             BridgeState::NotConnected => {
@@ -337,95 +318,140 @@ impl Bridge<'_> {
     }
 }
 
-/// A client-side "global object" (usually a function pointer),
-/// which may be using a different `proc_macro` from the one
-/// used by the server, but can be interacted with compatibly.
+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.
+///
+/// Note that the (phantom) `I` ("input") and `O` ("output") type parameters
+/// decorate the `Client<I, O>` with the RPC "interface" of the entry-point, but
+/// do not themselves participate in ABI, at all, only facilitate type-checking.
 ///
-/// N.B., `F` must have FFI-friendly memory layout (e.g., a pointer).
-/// The call ABI of function pointers used for `F` doesn't
-/// need to match between server and client, since it's only
-/// passed between them and (eventually) called by the client.
+/// E.g. `Client<TokenStream, TokenStream>` is the common proc macro interface,
+/// used for `#[proc_macro] fn foo(input: TokenStream) -> TokenStream`,
+/// indicating that the RPC input and output will be serialized token streams,
+/// and forcing the use of APIs that take/return `S::TokenStream`, server-side.
 #[repr(C)]
-#[derive(Copy, Clone)]
-pub struct Client<F> {
+pub struct Client<I, O> {
     // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of
     // 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<'_>, F) -> Buffer<u8>,
-    pub(super) f: F,
+
+    pub(super) run: extern "C" fn(BridgeConfig<'_>) -> Buffer,
+
+    pub(super) _marker: PhantomData<fn(I) -> O>,
+}
+
+impl<I, O> Copy for Client<I, O> {}
+impl<I, O> Clone for Client<I, O> {
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+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 std.
+    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<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
-    mut bridge: Bridge<'_>,
+    config: BridgeConfig<'_>,
     f: impl FnOnce(A) -> R,
-) -> Buffer<u8> {
-    // The initial `cached_buffer` contains the input.
-    let mut b = bridge.cached_buffer.take();
+) -> Buffer {
+    let BridgeConfig { input: mut buf, dispatch, force_show_panics, .. } = config;
 
     panic::catch_unwind(panic::AssertUnwindSafe(|| {
-        bridge.enter(|| {
-            let reader = &mut &b[..];
-            let input = A::decode(reader, &mut ());
-
-            // Put the `cached_buffer` back in the `Bridge`, for requests.
-            Bridge::with(|bridge| bridge.cached_buffer = b.take());
-
-            let output = f(input);
-
-            // Take the `cached_buffer` back out, for the output value.
-            b = 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).
-            b.clear();
-            Ok::<_, ()>(output).encode(&mut b, &mut ());
+        maybe_install_panic_hook(force_show_panics);
+
+        // Make sure the symbol store is empty before decoding inputs.
+        Symbol::invalidate_all();
+
+        let reader = &mut &buf[..];
+        let (globals, input) = <(ExpnGlobals<Span>, 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)
     .unwrap_or_else(|e| {
-        b.clear();
-        Err::<(), _>(e).encode(&mut b, &mut ());
+        buf.clear();
+        Err::<(), _>(e).encode(&mut buf, &mut ());
     });
-    b
+
+    // Now that a response has been serialized, invalidate all symbols
+    // registered with the interner.
+    Symbol::invalidate_all();
+    buf
 }
 
-impl Client<fn(crate::TokenStream) -> crate::TokenStream> {
-    #[rustc_allow_const_fn_unstable(const_fn)]
-    pub const fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self {
-        extern "C" fn run(
-            bridge: Bridge<'_>,
-            f: impl FnOnce(crate::TokenStream) -> crate::TokenStream,
-        ) -> Buffer<u8> {
-            run_client(bridge, |input| f(crate::TokenStream(input)).0)
+impl Client<crate::TokenStream, crate::TokenStream> {
+    pub const fn expand1(f: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy) -> Self {
+        Client {
+            get_handle_counters: HandleCounters::get,
+            run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| {
+                run_client(bridge, |input| f(crate::TokenStream(Some(input))).0)
+            }),
+            _marker: PhantomData,
         }
-        Client { get_handle_counters: HandleCounters::get, run, f }
     }
 }
 
-impl Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> {
-    #[rustc_allow_const_fn_unstable(const_fn)]
+impl Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream> {
     pub const fn expand2(
-        f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
+        f: impl Fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream + Copy,
     ) -> Self {
-        extern "C" fn run(
-            bridge: Bridge<'_>,
-            f: impl FnOnce(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
-        ) -> Buffer<u8> {
-            run_client(bridge, |(input, input2)| {
-                f(crate::TokenStream(input), crate::TokenStream(input2)).0
-            })
+        Client {
+            get_handle_counters: HandleCounters::get,
+            run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| {
+                run_client(bridge, |(input, input2)| {
+                    f(crate::TokenStream(Some(input)), crate::TokenStream(Some(input2))).0
+                })
+            }),
+            _marker: PhantomData,
         }
-        Client { get_handle_counters: HandleCounters::get, run, f }
     }
 }
 
@@ -435,17 +461,17 @@ pub enum ProcMacro {
     CustomDerive {
         trait_name: &'static str,
         attributes: &'static [&'static str],
-        client: Client<fn(crate::TokenStream) -> crate::TokenStream>,
+        client: Client<crate::TokenStream, crate::TokenStream>,
     },
 
     Attr {
         name: &'static str,
-        client: Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream>,
+        client: Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream>,
     },
 
     Bang {
         name: &'static str,
-        client: Client<fn(crate::TokenStream) -> crate::TokenStream>,
+        client: Client<crate::TokenStream, crate::TokenStream>,
     },
 }
 
@@ -458,27 +484,24 @@ impl ProcMacro {
         }
     }
 
-    #[rustc_allow_const_fn_unstable(const_fn)]
     pub const fn custom_derive(
         trait_name: &'static str,
         attributes: &'static [&'static str],
-        expand: fn(crate::TokenStream) -> crate::TokenStream,
+        expand: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy,
     ) -> Self {
         ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
     }
 
-    #[rustc_allow_const_fn_unstable(const_fn)]
     pub const fn attr(
         name: &'static str,
-        expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
+        expand: impl Fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream + Copy,
     ) -> Self {
         ProcMacro::Attr { name, client: Client::expand2(expand) }
     }
 
-    #[rustc_allow_const_fn_unstable(const_fn)]
     pub const fn bang(
         name: &'static str,
-        expand: fn(crate::TokenStream) -> crate::TokenStream,
+        expand: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy,
     ) -> Self {
         ProcMacro::Bang { name, client: Client::expand1(expand) }
     }