diff options
Diffstat (limited to 'library/proc_macro')
-rw-r--r-- | library/proc_macro/Cargo.toml | 11 | ||||
-rw-r--r-- | library/proc_macro/src/bridge/arena.rs | 113 | ||||
-rw-r--r-- | library/proc_macro/src/bridge/buffer.rs | 156 | ||||
-rw-r--r-- | library/proc_macro/src/bridge/client.rs | 510 | ||||
-rw-r--r-- | library/proc_macro/src/bridge/closure.rs | 32 | ||||
-rw-r--r-- | library/proc_macro/src/bridge/fxhash.rs | 117 | ||||
-rw-r--r-- | library/proc_macro/src/bridge/handle.rs | 75 | ||||
-rw-r--r-- | library/proc_macro/src/bridge/mod.rs | 524 | ||||
-rw-r--r-- | library/proc_macro/src/bridge/rpc.rs | 304 | ||||
-rw-r--r-- | library/proc_macro/src/bridge/scoped_cell.rs | 81 | ||||
-rw-r--r-- | library/proc_macro/src/bridge/selfless_reify.rs | 84 | ||||
-rw-r--r-- | library/proc_macro/src/bridge/server.rs | 339 | ||||
-rw-r--r-- | library/proc_macro/src/bridge/symbol.rs | 205 | ||||
-rw-r--r-- | library/proc_macro/src/diagnostic.rs | 182 | ||||
-rw-r--r-- | library/proc_macro/src/lib.rs | 1526 | ||||
-rw-r--r-- | library/proc_macro/src/quote.rs | 141 |
16 files changed, 4400 insertions, 0 deletions
diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml new file mode 100644 index 000000000..e54a50aa1 --- /dev/null +++ b/library/proc_macro/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "proc_macro" +version = "0.0.0" +edition = "2021" + +[dependencies] +std = { path = "../std" } +# Workaround: when documenting this crate rustdoc will try to load crate named +# `core` when resolving doc links. Without this line a different `core` will be +# loaded from sysroot causing duplicate lang items and other similar errors. +core = { path = "../core" } diff --git a/library/proc_macro/src/bridge/arena.rs b/library/proc_macro/src/bridge/arena.rs new file mode 100644 index 000000000..fa72d2816 --- /dev/null +++ b/library/proc_macro/src/bridge/arena.rs @@ -0,0 +1,113 @@ +//! A minimal arena allocator inspired by `rustc_arena::DroplessArena`. +//! +//! This is unfortunately a minimal re-implementation rather than a dependency +//! as it is difficult to depend on crates from within `proc_macro`, due to it +//! being built at the same time as `std`. + +use std::cell::{Cell, RefCell}; +use std::cmp; +use std::mem::MaybeUninit; +use std::ops::Range; +use std::ptr; +use std::slice; +use std::str; + +// The arenas start with PAGE-sized chunks, and then each new chunk is twice as +// big as its predecessor, up until we reach HUGE_PAGE-sized chunks, whereupon +// we stop growing. This scales well, from arenas that are barely used up to +// arenas that are used for 100s of MiBs. Note also that the chosen sizes match +// the usual sizes of pages and huge pages on Linux. +const PAGE: usize = 4096; +const HUGE_PAGE: usize = 2 * 1024 * 1024; + +/// A minimal arena allocator inspired by `rustc_arena::DroplessArena`. +/// +/// This is unfortunately a complete re-implementation rather than a dependency +/// as it is difficult to depend on crates from within `proc_macro`, due to it +/// being built at the same time as `std`. +/// +/// This arena doesn't have support for allocating anything other than byte +/// slices, as that is all that is necessary. +pub(crate) struct Arena { + start: Cell<*mut MaybeUninit<u8>>, + end: Cell<*mut MaybeUninit<u8>>, + chunks: RefCell<Vec<Box<[MaybeUninit<u8>]>>>, +} + +impl Arena { + pub(crate) fn new() -> Self { + Arena { + start: Cell::new(ptr::null_mut()), + end: Cell::new(ptr::null_mut()), + chunks: RefCell::new(Vec::new()), + } + } + + /// Add a new chunk with at least `additional` free bytes. + #[inline(never)] + #[cold] + fn grow(&self, additional: usize) { + let mut chunks = self.chunks.borrow_mut(); + let mut new_cap; + if let Some(last_chunk) = chunks.last_mut() { + // If the previous chunk's len is less than HUGE_PAGE + // bytes, then this chunk will be least double the previous + // chunk's size. + new_cap = last_chunk.len().min(HUGE_PAGE / 2); + new_cap *= 2; + } else { + new_cap = PAGE; + } + // Also ensure that this chunk can fit `additional`. + new_cap = cmp::max(additional, new_cap); + + let mut chunk = Box::new_uninit_slice(new_cap); + let Range { start, end } = chunk.as_mut_ptr_range(); + self.start.set(start); + self.end.set(end); + chunks.push(chunk); + } + + /// Allocates a byte slice with specified size from the current memory + /// chunk. Returns `None` if there is no free space left to satisfy the + /// request. + fn alloc_raw_without_grow(&self, bytes: usize) -> Option<&mut [MaybeUninit<u8>]> { + let start = self.start.get().addr(); + let old_end = self.end.get(); + let end = old_end.addr(); + + let new_end = end.checked_sub(bytes)?; + if start <= new_end { + let new_end = old_end.with_addr(new_end); + self.end.set(new_end); + // SAFETY: `bytes` bytes starting at `new_end` were just reserved. + Some(unsafe { slice::from_raw_parts_mut(new_end, bytes) }) + } else { + None + } + } + + fn alloc_raw(&self, bytes: usize) -> &mut [MaybeUninit<u8>] { + if bytes == 0 { + return &mut []; + } + + loop { + if let Some(a) = self.alloc_raw_without_grow(bytes) { + break a; + } + // No free space left. Allocate a new chunk to satisfy the request. + // On failure the grow will panic or abort. + self.grow(bytes); + } + } + + pub(crate) fn alloc_str<'a>(&'a self, string: &str) -> &'a mut str { + let alloc = self.alloc_raw(string.len()); + let bytes = MaybeUninit::write_slice(alloc, string.as_bytes()); + + // SAFETY: we convert from `&str` to `&[u8]`, clone it into the arena, + // and immediately convert the clone back to `&str`. + unsafe { str::from_utf8_unchecked_mut(bytes) } + } +} diff --git a/library/proc_macro/src/bridge/buffer.rs b/library/proc_macro/src/bridge/buffer.rs new file mode 100644 index 000000000..48030f8d8 --- /dev/null +++ b/library/proc_macro/src/bridge/buffer.rs @@ -0,0 +1,156 @@ +//! Buffer management for same-process client<->server communication. + +use std::io::{self, Write}; +use std::mem; +use std::ops::{Deref, DerefMut}; +use std::slice; + +#[repr(C)] +pub struct Buffer { + data: *mut u8, + len: usize, + capacity: usize, + reserve: extern "C" fn(Buffer, usize) -> Buffer, + drop: extern "C" fn(Buffer), +} + +unsafe impl Sync for Buffer {} +unsafe impl Send for Buffer {} + +impl Default for Buffer { + #[inline] + fn default() -> Self { + Self::from(vec![]) + } +} + +impl Deref for Buffer { + type Target = [u8]; + #[inline] + fn deref(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.data as *const u8, self.len) } + } +} + +impl DerefMut for Buffer { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.data, self.len) } + } +} + +impl Buffer { + #[inline] + pub(super) fn new() -> Self { + Self::default() + } + + #[inline] + pub(super) fn clear(&mut self) { + self.len = 0; + } + + #[inline] + pub(super) fn take(&mut self) -> Self { + mem::take(self) + } + + // We have the array method separate from extending from a slice. This is + // because in the case of small arrays, codegen can be more efficient + // (avoiding a memmove call). With extend_from_slice, LLVM at least + // currently is not able to make that optimization. + #[inline] + pub(super) fn extend_from_array<const N: usize>(&mut self, xs: &[u8; N]) { + if xs.len() > (self.capacity - self.len) { + let b = self.take(); + *self = (b.reserve)(b, xs.len()); + } + unsafe { + xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len()); + self.len += xs.len(); + } + } + + #[inline] + pub(super) fn extend_from_slice(&mut self, xs: &[u8]) { + if xs.len() > (self.capacity - self.len) { + let b = self.take(); + *self = (b.reserve)(b, xs.len()); + } + unsafe { + xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len()); + self.len += xs.len(); + } + } + + #[inline] + pub(super) fn push(&mut self, v: u8) { + // The code here is taken from Vec::push, and we know that reserve() + // will panic if we're exceeding isize::MAX bytes and so there's no need + // to check for overflow. + if self.len == self.capacity { + let b = self.take(); + *self = (b.reserve)(b, 1); + } + unsafe { + *self.data.add(self.len) = v; + self.len += 1; + } + } +} + +impl Write for Buffer { + #[inline] + fn write(&mut self, xs: &[u8]) -> io::Result<usize> { + self.extend_from_slice(xs); + Ok(xs.len()) + } + + #[inline] + fn write_all(&mut self, xs: &[u8]) -> io::Result<()> { + self.extend_from_slice(xs); + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Drop for Buffer { + #[inline] + fn drop(&mut self) { + let b = self.take(); + (b.drop)(b); + } +} + +impl From<Vec<u8>> for Buffer { + fn from(mut v: Vec<u8>) -> Self { + let (data, len, capacity) = (v.as_mut_ptr(), v.len(), v.capacity()); + mem::forget(v); + + // This utility function is nested in here because it can *only* + // be safely called on `Buffer`s created by *this* `proc_macro`. + fn to_vec(b: Buffer) -> Vec<u8> { + unsafe { + let Buffer { data, len, capacity, .. } = b; + mem::forget(b); + Vec::from_raw_parts(data, len, capacity) + } + } + + extern "C" fn reserve(b: Buffer, additional: usize) -> Buffer { + let mut v = to_vec(b); + v.reserve(additional); + Buffer::from(v) + } + + extern "C" fn drop(b: Buffer) { + mem::drop(to_vec(b)); + } + + Buffer { data, len, capacity, reserve, drop } + } +} diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs new file mode 100644 index 000000000..1516f084a --- /dev/null +++ b/library/proc_macro/src/bridge/client.rs @@ -0,0 +1,510 @@ +//! Client-side types. + +use super::*; + +use std::marker::PhantomData; + +macro_rules! define_handles { + ( + 'owned: $($oty:ident,)* + 'interned: $($ity:ident,)* + ) => { + #[repr(C)] + #[allow(non_snake_case)] + pub struct HandleCounters { + $($oty: AtomicUsize,)* + $($ity: AtomicUsize,)* + } + + impl HandleCounters { + // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of + // a wrapper `fn` pointer, once `const fn` can reference `static`s. + extern "C" fn get() -> &'static Self { + static COUNTERS: HandleCounters = HandleCounters { + $($oty: AtomicUsize::new(1),)* + $($ity: AtomicUsize::new(1),)* + }; + &COUNTERS + } + } + + // FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`. + #[allow(non_snake_case)] + pub(super) struct HandleStore<S: server::Types> { + $($oty: handle::OwnedStore<S::$oty>,)* + $($ity: handle::InternedStore<S::$ity>,)* + } + + impl<S: server::Types> HandleStore<S> { + pub(super) fn new(handle_counters: &'static HandleCounters) -> Self { + HandleStore { + $($oty: handle::OwnedStore::new(&handle_counters.$oty),)* + $($ity: handle::InternedStore::new(&handle_counters.$ity),)* + } + } + } + + $( + 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 { + handle: self.handle, + _marker: PhantomData, + }.drop(); + } + } + + impl<S> Encode<S> for $oty { + fn encode(self, w: &mut Writer, s: &mut S) { + let handle = self.handle; + mem::forget(self); + handle.encode(w, s); + } + } + + impl<S: server::Types> DecodeMut<'_, '_, HandleStore<server::MarkedTypes<S>>> + for Marked<S::$oty, $oty> + { + fn decode(r: &mut Reader<'_>, s: &mut HandleStore<server::MarkedTypes<S>>) -> Self { + s.$oty.take(handle::Handle::decode(r, &mut ())) + } + } + + impl<S> Encode<S> for &$oty { + fn encode(self, w: &mut Writer, s: &mut S) { + self.handle.encode(w, s); + } + } + + impl<'s, S: server::Types> Decode<'_, 's, HandleStore<server::MarkedTypes<S>>> + for &'s Marked<S::$oty, $oty> + { + fn decode(r: &mut Reader<'_>, s: &'s HandleStore<server::MarkedTypes<S>>) -> Self { + &s.$oty[handle::Handle::decode(r, &mut ())] + } + } + + impl<S> Encode<S> for &mut $oty { + fn encode(self, w: &mut Writer, s: &mut S) { + self.handle.encode(w, s); + } + } + + impl<'s, S: server::Types> DecodeMut<'_, 's, HandleStore<server::MarkedTypes<S>>> + for &'s mut Marked<S::$oty, $oty> + { + fn decode( + r: &mut Reader<'_>, + s: &'s mut HandleStore<server::MarkedTypes<S>> + ) -> Self { + &mut s.$oty[handle::Handle::decode(r, &mut ())] + } + } + + impl<S: server::Types> Encode<HandleStore<server::MarkedTypes<S>>> + for Marked<S::$oty, $oty> + { + fn encode(self, w: &mut Writer, s: &mut HandleStore<server::MarkedTypes<S>>) { + s.$oty.alloc(self).encode(w, s); + } + } + + impl<S> DecodeMut<'_, '_, S> for $oty { + fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + $oty { + handle: handle::Handle::decode(r, s), + _marker: PhantomData, + } + } + } + )* + + $( + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + 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.handle.encode(w, s); + } + } + + impl<S: server::Types> DecodeMut<'_, '_, HandleStore<server::MarkedTypes<S>>> + for Marked<S::$ity, $ity> + { + fn decode(r: &mut Reader<'_>, s: &mut HandleStore<server::MarkedTypes<S>>) -> Self { + s.$ity.copy(handle::Handle::decode(r, &mut ())) + } + } + + impl<S: server::Types> Encode<HandleStore<server::MarkedTypes<S>>> + for Marked<S::$ity, $ity> + { + fn encode(self, w: &mut Writer, s: &mut HandleStore<server::MarkedTypes<S>>) { + s.$ity.alloc(self).encode(w, s); + } + } + + impl<S> DecodeMut<'_, '_, S> for $ity { + fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + $ity { + handle: handle::Handle::decode(r, s), + _marker: PhantomData, + } + } + } + )* + } +} +define_handles! { + 'owned: + FreeFunctions, + TokenStream, + SourceFile, + MultiSpan, + Diagnostic, + + 'interned: + 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 +// instead of pattern matching on methods, here and in server decl. + +impl Clone for TokenStream { + fn clone(&self) -> Self { + self.clone() + } +} + +impl Clone for SourceFile { + fn clone(&self) -> Self { + self.clone() + } +} + +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()) + } +} + +pub(crate) use super::symbol::Symbol; + +macro_rules! define_client_side { + ($($name:ident { + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + }),* $(,)?) => { + $(impl $name { + $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* { + Bridge::with(|bridge| { + let mut buf = bridge.cached_buffer.take(); + + buf.clear(); + api_tags::Method::$name(api_tags::$name::$method).encode(&mut buf, &mut ()); + reverse_encode!(buf; $($arg),*); + + buf = bridge.dispatch.call(buf); + + let r = Result::<_, PanicMessage>::decode(&mut &buf[..], &mut ()); + + bridge.cached_buffer = buf; + + r.unwrap_or_else(|e| panic::resume_unwind(e.into())) + }) + })* + })* + } +} +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, + + /// A server is connected and available for requests. + Connected(Bridge<'a>), + + /// Access to the bridge is being exclusively acquired + /// (e.g., during `BridgeState::with`). + InUse, +} + +enum BridgeStateL {} + +impl<'a> scoped_cell::ApplyL<'a> for BridgeStateL { + type Out = BridgeState<'a>; +} + +thread_local! { + static BRIDGE_STATE: scoped_cell::ScopedCell<BridgeStateL> = + scoped_cell::ScopedCell::new(BridgeState::NotConnected); +} + +impl BridgeState<'_> { + /// Take exclusive control of the thread-local + /// `BridgeState`, and pass it to `f`, mutably. + /// The state will be restored after `f` exits, even + /// by panic, including modifications made to it by `f`. + /// + /// N.B., while `f` is running, the thread-local state + /// is `BridgeState::InUse`. + fn with<R>(f: impl FnOnce(&mut BridgeState<'_>) -> R) -> R { + BRIDGE_STATE.with(|state| { + state.replace(BridgeState::InUse, |mut state| { + // FIXME(#52812) pass `f` directly to `replace` when `RefMutL` is gone + f(&mut *state) + }) + }) + } +} + +impl Bridge<'_> { + fn with<R>(f: impl FnOnce(&mut Bridge<'_>) -> R) -> R { + BridgeState::with(|state| match state { + BridgeState::NotConnected => { + panic!("procedural macro API is used outside of a procedural macro"); + } + BridgeState::InUse => { + panic!("procedural macro API is used while it's already in use"); + } + BridgeState::Connected(bridge) => f(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. +/// +/// 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. +/// +/// 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)] +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(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 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<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>( + config: BridgeConfig<'_>, + f: impl FnOnce(A) -> R, +) -> Buffer { + let BridgeConfig { input: mut buf, dispatch, force_show_panics, .. } = config; + + panic::catch_unwind(panic::AssertUnwindSafe(|| { + 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| { + buf.clear(); + Err::<(), _>(e).encode(&mut buf, &mut ()); + }); + + // Now that a response has been serialized, invalidate all symbols + // registered with the interner. + Symbol::invalidate_all(); + buf +} + +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, + } + } +} + +impl Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream> { + pub const fn expand2( + f: impl Fn(crate::TokenStream, 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, input2)| { + f(crate::TokenStream(Some(input)), crate::TokenStream(Some(input2))).0 + }) + }), + _marker: PhantomData, + } + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum ProcMacro { + CustomDerive { + trait_name: &'static str, + attributes: &'static [&'static str], + client: Client<crate::TokenStream, crate::TokenStream>, + }, + + Attr { + name: &'static str, + client: Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream>, + }, + + Bang { + name: &'static str, + client: Client<crate::TokenStream, crate::TokenStream>, + }, +} + +impl ProcMacro { + pub fn name(&self) -> &'static str { + match self { + ProcMacro::CustomDerive { trait_name, .. } => trait_name, + ProcMacro::Attr { name, .. } => name, + ProcMacro::Bang { name, .. } => name, + } + } + + pub const fn custom_derive( + trait_name: &'static str, + attributes: &'static [&'static str], + expand: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy, + ) -> Self { + ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) } + } + + pub const fn attr( + name: &'static str, + expand: impl Fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream + Copy, + ) -> Self { + ProcMacro::Attr { name, client: Client::expand2(expand) } + } + + pub const fn bang( + name: &'static str, + expand: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy, + ) -> Self { + ProcMacro::Bang { name, client: Client::expand1(expand) } + } +} diff --git a/library/proc_macro/src/bridge/closure.rs b/library/proc_macro/src/bridge/closure.rs new file mode 100644 index 000000000..d371ae3ce --- /dev/null +++ b/library/proc_macro/src/bridge/closure.rs @@ -0,0 +1,32 @@ +//! Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`. + +use std::marker::PhantomData; + +#[repr(C)] +pub struct Closure<'a, A, R> { + call: unsafe extern "C" fn(*mut Env, A) -> R, + env: *mut Env, + // 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. + // + // The `'a` lifetime parameter represents the lifetime of `Env`. + _marker: PhantomData<*mut &'a mut ()>, +} + +struct Env; + +impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> { + fn from(f: &'a mut F) -> Self { + unsafe extern "C" fn call<A, R, F: FnMut(A) -> R>(env: *mut Env, arg: A) -> R { + (*(env as *mut _ as *mut F))(arg) + } + Closure { call: call::<A, R, F>, env: f as *mut _ as *mut Env, _marker: PhantomData } + } +} + +impl<'a, A, R> Closure<'a, A, R> { + pub fn call(&mut self, arg: A) -> R { + unsafe { (self.call)(self.env, arg) } + } +} diff --git a/library/proc_macro/src/bridge/fxhash.rs b/library/proc_macro/src/bridge/fxhash.rs new file mode 100644 index 000000000..4b1e412e2 --- /dev/null +++ b/library/proc_macro/src/bridge/fxhash.rs @@ -0,0 +1,117 @@ +//! This is a copy of the `rustc_hash` crate, adapted to work as a module. +//! +//! If in the future it becomes more reasonable to add dependencies to +//! `proc_macro`, this module should be removed and replaced with a dependency +//! on the `rustc_hash` crate. + +use std::collections::HashMap; +use std::convert::TryInto; +use std::default::Default; +use std::hash::BuildHasherDefault; +use std::hash::Hasher; +use std::mem::size_of; +use std::ops::BitXor; + +/// Type alias for a hashmap using the `fx` hash algorithm. +pub type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>; + +/// A speedy hash algorithm for use within rustc. The hashmap in liballoc +/// by default uses SipHash which isn't quite as speedy as we want. In the +/// compiler we're not really worried about DOS attempts, so we use a fast +/// non-cryptographic hash. +/// +/// This is the same as the algorithm used by Firefox -- which is a homespun +/// one not based on any widely-known algorithm -- though modified to produce +/// 64-bit hash values instead of 32-bit hash values. It consistently +/// out-performs an FNV-based hash within rustc itself -- the collision rate is +/// similar or slightly worse than FNV, but the speed of the hash function +/// itself is much higher because it works on up to 8 bytes at a time. +pub struct FxHasher { + hash: usize, +} + +#[cfg(target_pointer_width = "32")] +const K: usize = 0x9e3779b9; +#[cfg(target_pointer_width = "64")] +const K: usize = 0x517cc1b727220a95; + +impl Default for FxHasher { + #[inline] + fn default() -> FxHasher { + FxHasher { hash: 0 } + } +} + +impl FxHasher { + #[inline] + fn add_to_hash(&mut self, i: usize) { + self.hash = self.hash.rotate_left(5).bitxor(i).wrapping_mul(K); + } +} + +impl Hasher for FxHasher { + #[inline] + fn write(&mut self, mut bytes: &[u8]) { + #[cfg(target_pointer_width = "32")] + let read_usize = |bytes: &[u8]| u32::from_ne_bytes(bytes[..4].try_into().unwrap()); + #[cfg(target_pointer_width = "64")] + let read_usize = |bytes: &[u8]| u64::from_ne_bytes(bytes[..8].try_into().unwrap()); + + let mut hash = FxHasher { hash: self.hash }; + assert!(size_of::<usize>() <= 8); + while bytes.len() >= size_of::<usize>() { + hash.add_to_hash(read_usize(bytes) as usize); + bytes = &bytes[size_of::<usize>()..]; + } + if (size_of::<usize>() > 4) && (bytes.len() >= 4) { + hash.add_to_hash(u32::from_ne_bytes(bytes[..4].try_into().unwrap()) as usize); + bytes = &bytes[4..]; + } + if (size_of::<usize>() > 2) && bytes.len() >= 2 { + hash.add_to_hash(u16::from_ne_bytes(bytes[..2].try_into().unwrap()) as usize); + bytes = &bytes[2..]; + } + if (size_of::<usize>() > 1) && bytes.len() >= 1 { + hash.add_to_hash(bytes[0] as usize); + } + self.hash = hash.hash; + } + + #[inline] + fn write_u8(&mut self, i: u8) { + self.add_to_hash(i as usize); + } + + #[inline] + fn write_u16(&mut self, i: u16) { + self.add_to_hash(i as usize); + } + + #[inline] + fn write_u32(&mut self, i: u32) { + self.add_to_hash(i as usize); + } + + #[cfg(target_pointer_width = "32")] + #[inline] + fn write_u64(&mut self, i: u64) { + self.add_to_hash(i as usize); + self.add_to_hash((i >> 32) as usize); + } + + #[cfg(target_pointer_width = "64")] + #[inline] + fn write_u64(&mut self, i: u64) { + self.add_to_hash(i as usize); + } + + #[inline] + fn write_usize(&mut self, i: usize) { + self.add_to_hash(i); + } + + #[inline] + fn finish(&self) -> u64 { + self.hash as u64 + } +} diff --git a/library/proc_macro/src/bridge/handle.rs b/library/proc_macro/src/bridge/handle.rs new file mode 100644 index 000000000..00954107b --- /dev/null +++ b/library/proc_macro/src/bridge/handle.rs @@ -0,0 +1,75 @@ +//! Server-side handles and storage for per-handle data. + +use std::collections::BTreeMap; +use std::hash::Hash; +use std::num::NonZeroU32; +use std::ops::{Index, IndexMut}; +use std::sync::atomic::{AtomicUsize, Ordering}; + +use super::fxhash::FxHashMap; + +pub(super) type Handle = NonZeroU32; + +/// A store that associates values of type `T` with numeric handles. A value can +/// be looked up using its handle. +pub(super) struct OwnedStore<T: 'static> { + counter: &'static AtomicUsize, + data: BTreeMap<Handle, T>, +} + +impl<T> OwnedStore<T> { + pub(super) fn new(counter: &'static AtomicUsize) -> Self { + // Ensure the handle counter isn't 0, which would panic later, + // when `NonZeroU32::new` (aka `Handle::new`) is called in `alloc`. + assert_ne!(counter.load(Ordering::SeqCst), 0); + + OwnedStore { counter, data: BTreeMap::new() } + } +} + +impl<T> OwnedStore<T> { + pub(super) fn alloc(&mut self, x: T) -> Handle { + let counter = self.counter.fetch_add(1, Ordering::SeqCst); + let handle = Handle::new(counter as u32).expect("`proc_macro` handle counter overflowed"); + assert!(self.data.insert(handle, x).is_none()); + handle + } + + pub(super) fn take(&mut self, h: Handle) -> T { + self.data.remove(&h).expect("use-after-free in `proc_macro` handle") + } +} + +impl<T> Index<Handle> for OwnedStore<T> { + type Output = T; + fn index(&self, h: Handle) -> &T { + self.data.get(&h).expect("use-after-free in `proc_macro` handle") + } +} + +impl<T> IndexMut<Handle> for OwnedStore<T> { + fn index_mut(&mut self, h: Handle) -> &mut T { + self.data.get_mut(&h).expect("use-after-free in `proc_macro` handle") + } +} + +/// Like `OwnedStore`, but avoids storing any value more than once. +pub(super) struct InternedStore<T: 'static> { + owned: OwnedStore<T>, + interner: FxHashMap<T, Handle>, +} + +impl<T: Copy + Eq + Hash> InternedStore<T> { + pub(super) fn new(counter: &'static AtomicUsize) -> Self { + InternedStore { owned: OwnedStore::new(counter), interner: FxHashMap::default() } + } + + pub(super) fn alloc(&mut self, x: T) -> Handle { + let owned = &mut self.owned; + *self.interner.entry(x).or_insert_with(|| owned.alloc(x)) + } + + pub(super) fn copy(&mut self, h: Handle) -> T { + self.owned[h] + } +} diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs new file mode 100644 index 000000000..5cde966bf --- /dev/null +++ b/library/proc_macro/src/bridge/mod.rs @@ -0,0 +1,524 @@ +//! Internal interface for communicating between a `proc_macro` client +//! (a proc macro crate) and a `proc_macro` server (a compiler front-end). +//! +//! Serialization (with C ABI buffers) and unique integer handles are employed +//! to allow safely interfacing between two copies of `proc_macro` built +//! (from the same source) by different compilers with potentially mismatching +//! Rust ABIs (e.g., stage0/bin/rustc vs stage1/bin/rustc during bootstrap). + +#![deny(unsafe_code)] + +use crate::{Delimiter, Level, LineColumn, Spacing}; +use std::fmt; +use std::hash::Hash; +use std::marker; +use std::mem; +use std::ops::Bound; +use std::panic; +use std::sync::atomic::AtomicUsize; +use std::sync::Once; +use std::thread; + +/// Higher-order macro describing the server RPC API, allowing automatic +/// generation of type-safe Rust APIs, both client-side and server-side. +/// +/// `with_api!(MySelf, my_self, my_macro)` expands to: +/// ```rust,ignore (pseudo-code) +/// my_macro! { +/// // ... +/// Literal { +/// // ... +/// fn character(ch: char) -> MySelf::Literal; +/// // ... +/// fn span(my_self: &MySelf::Literal) -> MySelf::Span; +/// fn set_span(my_self: &mut MySelf::Literal, span: MySelf::Span); +/// }, +/// // ... +/// } +/// ``` +/// +/// The first two arguments serve to customize the arguments names +/// and argument/return types, to enable several different usecases: +/// +/// If `my_self` is just `self`, then each `fn` signature can be used +/// as-is for a method. If it's anything else (`self_` in practice), +/// then the signatures don't have a special `self` argument, and +/// can, therefore, have a different one introduced. +/// +/// If `MySelf` is just `Self`, then the types are only valid inside +/// a trait or a trait impl, where the trait has associated types +/// for each of the API types. If non-associated types are desired, +/// a module name (`self` in practice) can be used instead of `Self`. +macro_rules! with_api { + ($S:ident, $self:ident, $m:ident) => { + $m! { + FreeFunctions { + fn drop($self: $S::FreeFunctions); + fn track_env_var(var: &str, value: Option<&str>); + fn track_path(path: &str); + fn literal_from_str(s: &str) -> Result<Literal<$S::Span, $S::Symbol>, ()>; + }, + TokenStream { + fn drop($self: $S::TokenStream); + fn clone($self: &$S::TokenStream) -> $S::TokenStream; + fn is_empty($self: &$S::TokenStream) -> bool; + fn expand_expr($self: &$S::TokenStream) -> Result<$S::TokenStream, ()>; + fn from_str(src: &str) -> $S::TokenStream; + fn to_string($self: &$S::TokenStream) -> String; + fn from_token_tree( + tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>, + ) -> $S::TokenStream; + fn concat_trees( + base: Option<$S::TokenStream>, + trees: Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol>>, + ) -> $S::TokenStream; + fn concat_streams( + base: Option<$S::TokenStream>, + streams: Vec<$S::TokenStream>, + ) -> $S::TokenStream; + fn into_trees( + $self: $S::TokenStream + ) -> Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol>>; + }, + SourceFile { + fn drop($self: $S::SourceFile); + fn clone($self: &$S::SourceFile) -> $S::SourceFile; + fn eq($self: &$S::SourceFile, other: &$S::SourceFile) -> bool; + fn path($self: &$S::SourceFile) -> String; + fn is_real($self: &$S::SourceFile) -> bool; + }, + MultiSpan { + fn drop($self: $S::MultiSpan); + fn new() -> $S::MultiSpan; + fn push($self: &mut $S::MultiSpan, span: $S::Span); + }, + Diagnostic { + fn drop($self: $S::Diagnostic); + fn new(level: Level, msg: &str, span: $S::MultiSpan) -> $S::Diagnostic; + fn sub( + $self: &mut $S::Diagnostic, + level: Level, + msg: &str, + span: $S::MultiSpan, + ); + fn emit($self: $S::Diagnostic); + }, + Span { + fn debug($self: $S::Span) -> String; + fn source_file($self: $S::Span) -> $S::SourceFile; + fn parent($self: $S::Span) -> Option<$S::Span>; + fn source($self: $S::Span) -> $S::Span; + fn start($self: $S::Span) -> LineColumn; + fn end($self: $S::Span) -> LineColumn; + fn before($self: $S::Span) -> $S::Span; + fn after($self: $S::Span) -> $S::Span; + fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>; + fn subspan($self: $S::Span, start: Bound<usize>, end: Bound<usize>) -> Option<$S::Span>; + fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span; + fn source_text($self: $S::Span) -> Option<String>; + fn save_span($self: $S::Span) -> usize; + fn recover_proc_macro_span(id: usize) -> $S::Span; + }, + Symbol { + fn normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>; + }, + } + }; +} + +// FIXME(eddyb) this calls `encode` for each argument, but in reverse, +// to match the ordering in `reverse_decode`. +macro_rules! reverse_encode { + ($writer:ident;) => {}; + ($writer:ident; $first:ident $(, $rest:ident)*) => { + reverse_encode!($writer; $($rest),*); + $first.encode(&mut $writer, &mut ()); + } +} + +// FIXME(eddyb) this calls `decode` for each argument, but in reverse, +// to avoid borrow conflicts from borrows started by `&mut` arguments. +macro_rules! reverse_decode { + ($reader:ident, $s:ident;) => {}; + ($reader:ident, $s:ident; $first:ident: $first_ty:ty $(, $rest:ident: $rest_ty:ty)*) => { + reverse_decode!($reader, $s; $($rest: $rest_ty),*); + let $first = <$first_ty>::decode(&mut $reader, $s); + } +} + +#[allow(unsafe_code)] +mod arena; +#[allow(unsafe_code)] +mod buffer; +#[forbid(unsafe_code)] +pub mod client; +#[allow(unsafe_code)] +mod closure; +#[forbid(unsafe_code)] +mod fxhash; +#[forbid(unsafe_code)] +mod handle; +#[macro_use] +#[forbid(unsafe_code)] +mod rpc; +#[allow(unsafe_code)] +mod scoped_cell; +#[allow(unsafe_code)] +mod selfless_reify; +#[forbid(unsafe_code)] +pub mod server; +#[allow(unsafe_code)] +mod symbol; + +use buffer::Buffer; +pub use rpc::PanicMessage; +use rpc::{Decode, DecodeMut, Encode, Reader, Writer}; + +/// 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 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>, + + /// If 'true', always invoke the default panic hook + force_show_panics: bool, + + // 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: marker::PhantomData<*mut ()>, +} + +#[forbid(unsafe_code)] +#[allow(non_camel_case_types)] +mod api_tags { + use super::rpc::{DecodeMut, Encode, Reader, Writer}; + + macro_rules! declare_tags { + ($($name:ident { + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + }),* $(,)?) => { + $( + pub(super) enum $name { + $($method),* + } + rpc_encode_decode!(enum $name { $($method),* }); + )* + + pub(super) enum Method { + $($name($name)),* + } + rpc_encode_decode!(enum Method { $($name(m)),* }); + } + } + with_api!(self, self, declare_tags); +} + +/// Helper to wrap associated types to allow trait impl dispatch. +/// That is, normally a pair of impls for `T::Foo` and `T::Bar` +/// can overlap, but if the impls are, instead, on types like +/// `Marked<T::Foo, Foo>` and `Marked<T::Bar, Bar>`, they can't. +trait Mark { + type Unmarked; + fn mark(unmarked: Self::Unmarked) -> Self; +} + +/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details). +trait Unmark { + type Unmarked; + fn unmark(self) -> Self::Unmarked; +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +struct Marked<T, M> { + value: T, + _marker: marker::PhantomData<M>, +} + +impl<T, M> Mark for Marked<T, M> { + type Unmarked = T; + fn mark(unmarked: Self::Unmarked) -> Self { + Marked { value: unmarked, _marker: marker::PhantomData } + } +} +impl<T, M> Unmark for Marked<T, M> { + type Unmarked = T; + fn unmark(self) -> Self::Unmarked { + self.value + } +} +impl<'a, T, M> Unmark for &'a Marked<T, M> { + type Unmarked = &'a T; + fn unmark(self) -> Self::Unmarked { + &self.value + } +} +impl<'a, T, M> Unmark for &'a mut Marked<T, M> { + type Unmarked = &'a mut T; + fn unmark(self) -> Self::Unmarked { + &mut self.value + } +} + +impl<T: Mark> Mark for Vec<T> { + type Unmarked = Vec<T::Unmarked>; + fn mark(unmarked: Self::Unmarked) -> Self { + // Should be a no-op due to std's in-place collect optimizations. + unmarked.into_iter().map(T::mark).collect() + } +} +impl<T: Unmark> Unmark for Vec<T> { + type Unmarked = Vec<T::Unmarked>; + fn unmark(self) -> Self::Unmarked { + // Should be a no-op due to std's in-place collect optimizations. + self.into_iter().map(T::unmark).collect() + } +} + +macro_rules! mark_noop { + ($($ty:ty),* $(,)?) => { + $( + impl Mark for $ty { + type Unmarked = Self; + fn mark(unmarked: Self::Unmarked) -> Self { + unmarked + } + } + impl Unmark for $ty { + type Unmarked = Self; + fn unmark(self) -> Self::Unmarked { + self + } + } + )* + } +} +mark_noop! { + (), + bool, + char, + &'_ [u8], + &'_ str, + String, + u8, + usize, + Delimiter, + LitKind, + Level, + LineColumn, + Spacing, +} + +rpc_encode_decode!( + enum Delimiter { + Parenthesis, + Brace, + Bracket, + None, + } +); +rpc_encode_decode!( + enum Level { + Error, + Warning, + Note, + Help, + } +); +rpc_encode_decode!(struct LineColumn { line, column }); +rpc_encode_decode!( + enum Spacing { + Alone, + Joint, + } +); + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum LitKind { + Byte, + Char, + Integer, + Float, + Str, + StrRaw(u8), + ByteStr, + ByteStrRaw(u8), + Err, +} + +rpc_encode_decode!( + enum LitKind { + Byte, + Char, + Integer, + Float, + Str, + StrRaw(n), + ByteStr, + ByteStrRaw(n), + Err, + } +); + +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),+>; + fn mark(unmarked: Self::Unmarked) -> Self { + match unmarked { + $($name::$variant $(($field))? => { + $name::$variant $((Mark::mark($field)))? + })* + } + } + } + + impl<$($T: Unmark),+> Unmark for $name <$($T),+> { + type Unmarked = $name <$($T::Unmarked),+>; + fn unmark(self) -> Self::Unmarked { + match self { + $($name::$variant $(($field))? => { + $name::$variant $((Unmark::unmark($field)))? + })* + } + } + } + } +} + +macro_rules! compound_traits { + ($($t:tt)*) => { + rpc_encode_decode!($($t)*); + mark_compound!($($t)*); + }; +} + +compound_traits!( + enum Bound<T> { + Included(x), + Excluded(x), + Unbounded, + } +); + +compound_traits!( + enum Option<T> { + Some(t), + None, + } +); + +compound_traits!( + enum Result<T, E> { + Ok(t), + Err(e), + } +); + +#[derive(Copy, Clone)] +pub struct DelimSpan<Span> { + pub open: Span, + pub close: Span, + pub entire: Span, +} + +impl<Span: Copy> DelimSpan<Span> { + pub fn from_single(span: Span) -> Self { + DelimSpan { open: span, close: span, entire: span } + } +} + +compound_traits!(struct DelimSpan<Span> { open, close, entire }); + +#[derive(Clone)] +pub struct Group<TokenStream, Span> { + pub delimiter: Delimiter, + pub stream: Option<TokenStream>, + pub span: DelimSpan<Span>, +} + +compound_traits!(struct Group<TokenStream, Span> { delimiter, stream, span }); + +#[derive(Clone)] +pub struct Punct<Span> { + pub ch: u8, + pub joint: bool, + pub span: Span, +} + +compound_traits!(struct Punct<Span> { ch, joint, span }); + +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct Ident<Span, Symbol> { + pub sym: Symbol, + pub is_raw: bool, + pub span: Span, +} + +compound_traits!(struct Ident<Span, Symbol> { sym, is_raw, span }); + +#[derive(Clone, Eq, PartialEq)] +pub struct Literal<Span, Symbol> { + pub kind: LitKind, + pub symbol: Symbol, + pub suffix: Option<Symbol>, + pub span: Span, +} + +compound_traits!(struct Literal<Sp, Sy> { kind, symbol, suffix, span }); + +#[derive(Clone)] +pub enum TokenTree<TokenStream, Span, Symbol> { + Group(Group<TokenStream, Span>), + Punct(Punct<Span>), + Ident(Ident<Span, Symbol>), + Literal(Literal<Span, Symbol>), +} + +compound_traits!( + enum TokenTree<TokenStream, Span, Symbol> { + Group(tt), + Punct(tt), + Ident(tt), + 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<Span> { + pub def_site: Span, + pub call_site: Span, + pub mixed_site: Span, +} + +compound_traits!( + struct ExpnGlobals<Span> { def_site, call_site, mixed_site } +); diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs new file mode 100644 index 000000000..e9d7a46c0 --- /dev/null +++ b/library/proc_macro/src/bridge/rpc.rs @@ -0,0 +1,304 @@ +//! Serialization for client-server communication. + +use std::any::Any; +use std::char; +use std::io::Write; +use std::num::NonZeroU32; +use std::str; + +pub(super) type Writer = super::buffer::Buffer; + +pub(super) trait Encode<S>: Sized { + fn encode(self, w: &mut Writer, s: &mut S); +} + +pub(super) type Reader<'a> = &'a [u8]; + +pub(super) trait Decode<'a, 's, S>: Sized { + fn decode(r: &mut Reader<'a>, s: &'s S) -> Self; +} + +pub(super) trait DecodeMut<'a, 's, S>: Sized { + fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self; +} + +macro_rules! rpc_encode_decode { + (le $ty:ty) => { + impl<S> Encode<S> for $ty { + fn encode(self, w: &mut Writer, _: &mut S) { + w.extend_from_array(&self.to_le_bytes()); + } + } + + impl<S> DecodeMut<'_, '_, S> for $ty { + fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { + const N: usize = ::std::mem::size_of::<$ty>(); + + let mut bytes = [0; N]; + bytes.copy_from_slice(&r[..N]); + *r = &r[N..]; + + Self::from_le_bytes(bytes) + } + } + }; + (struct $name:ident $(<$($T:ident),+>)? { $($field:ident),* $(,)? }) => { + impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? { + fn encode(self, w: &mut Writer, s: &mut S) { + $(self.$field.encode(w, s);)* + } + } + + impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S> + for $name $(<$($T),+>)? + { + fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + $name { + $($field: DecodeMut::decode(r, s)),* + } + } + } + }; + (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => { + impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? { + fn encode(self, w: &mut Writer, s: &mut S) { + // HACK(eddyb): `Tag` enum duplicated between the + // two impls as there's no other place to stash it. + #[allow(non_upper_case_globals)] + mod tag { + #[repr(u8)] enum Tag { $($variant),* } + + $(pub const $variant: u8 = Tag::$variant as u8;)* + } + + match self { + $($name::$variant $(($field))* => { + tag::$variant.encode(w, s); + $($field.encode(w, s);)* + })* + } + } + } + + impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S> + for $name $(<$($T),+>)? + { + fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + // HACK(eddyb): `Tag` enum duplicated between the + // two impls as there's no other place to stash it. + #[allow(non_upper_case_globals)] + mod tag { + #[repr(u8)] enum Tag { $($variant),* } + + $(pub const $variant: u8 = Tag::$variant as u8;)* + } + + match u8::decode(r, s) { + $(tag::$variant => { + $(let $field = DecodeMut::decode(r, s);)* + $name::$variant $(($field))* + })* + _ => unreachable!(), + } + } + } + } +} + +impl<S> Encode<S> for () { + fn encode(self, _: &mut Writer, _: &mut S) {} +} + +impl<S> DecodeMut<'_, '_, S> for () { + fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {} +} + +impl<S> Encode<S> for u8 { + fn encode(self, w: &mut Writer, _: &mut S) { + w.push(self); + } +} + +impl<S> DecodeMut<'_, '_, S> for u8 { + fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { + let x = r[0]; + *r = &r[1..]; + x + } +} + +rpc_encode_decode!(le u32); +rpc_encode_decode!(le usize); + +impl<S> Encode<S> for bool { + fn encode(self, w: &mut Writer, s: &mut S) { + (self as u8).encode(w, s); + } +} + +impl<S> DecodeMut<'_, '_, S> for bool { + fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + match u8::decode(r, s) { + 0 => false, + 1 => true, + _ => unreachable!(), + } + } +} + +impl<S> Encode<S> for char { + fn encode(self, w: &mut Writer, s: &mut S) { + (self as u32).encode(w, s); + } +} + +impl<S> DecodeMut<'_, '_, S> for char { + fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + char::from_u32(u32::decode(r, s)).unwrap() + } +} + +impl<S> Encode<S> for NonZeroU32 { + fn encode(self, w: &mut Writer, s: &mut S) { + self.get().encode(w, s); + } +} + +impl<S> DecodeMut<'_, '_, S> for NonZeroU32 { + fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + Self::new(u32::decode(r, s)).unwrap() + } +} + +impl<S, A: Encode<S>, B: Encode<S>> Encode<S> for (A, B) { + fn encode(self, w: &mut Writer, s: &mut S) { + self.0.encode(w, s); + self.1.encode(w, s); + } +} + +impl<'a, S, A: for<'s> DecodeMut<'a, 's, S>, B: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> + for (A, B) +{ + fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + (DecodeMut::decode(r, s), DecodeMut::decode(r, s)) + } +} + +impl<S> Encode<S> for &[u8] { + fn encode(self, w: &mut Writer, s: &mut S) { + self.len().encode(w, s); + w.write_all(self).unwrap(); + } +} + +impl<'a, S> DecodeMut<'a, '_, S> for &'a [u8] { + fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + let len = usize::decode(r, s); + let xs = &r[..len]; + *r = &r[len..]; + xs + } +} + +impl<S> Encode<S> for &str { + fn encode(self, w: &mut Writer, s: &mut S) { + self.as_bytes().encode(w, s); + } +} + +impl<'a, S> DecodeMut<'a, '_, S> for &'a str { + fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + str::from_utf8(<&[u8]>::decode(r, s)).unwrap() + } +} + +impl<S> Encode<S> for String { + fn encode(self, w: &mut Writer, s: &mut S) { + self[..].encode(w, s); + } +} + +impl<S> DecodeMut<'_, '_, S> for String { + fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + <&str>::decode(r, s).to_string() + } +} + +impl<S, T: Encode<S>> Encode<S> for Vec<T> { + fn encode(self, w: &mut Writer, s: &mut S) { + self.len().encode(w, s); + for x in self { + x.encode(w, s); + } + } +} + +impl<'a, S, T: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> for Vec<T> { + fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + let len = usize::decode(r, s); + let mut vec = Vec::with_capacity(len); + for _ in 0..len { + vec.push(T::decode(r, s)); + } + vec + } +} + +/// Simplified version of panic payloads, ignoring +/// types other than `&'static str` and `String`. +pub enum PanicMessage { + StaticStr(&'static str), + String(String), + Unknown, +} + +impl From<Box<dyn Any + Send>> for PanicMessage { + fn from(payload: Box<dyn Any + Send + 'static>) -> Self { + if let Some(s) = payload.downcast_ref::<&'static str>() { + return PanicMessage::StaticStr(s); + } + if let Ok(s) = payload.downcast::<String>() { + return PanicMessage::String(*s); + } + PanicMessage::Unknown + } +} + +impl Into<Box<dyn Any + Send>> for PanicMessage { + fn into(self) -> Box<dyn Any + Send> { + match self { + PanicMessage::StaticStr(s) => Box::new(s), + PanicMessage::String(s) => Box::new(s), + PanicMessage::Unknown => { + struct UnknownPanicMessage; + Box::new(UnknownPanicMessage) + } + } + } +} + +impl PanicMessage { + pub fn as_str(&self) -> Option<&str> { + match self { + PanicMessage::StaticStr(s) => Some(s), + PanicMessage::String(s) => Some(s), + PanicMessage::Unknown => None, + } + } +} + +impl<S> Encode<S> for PanicMessage { + fn encode(self, w: &mut Writer, s: &mut S) { + self.as_str().encode(w, s); + } +} + +impl<S> DecodeMut<'_, '_, S> for PanicMessage { + fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + match Option::<String>::decode(r, s) { + Some(s) => PanicMessage::String(s), + None => PanicMessage::Unknown, + } + } +} diff --git a/library/proc_macro/src/bridge/scoped_cell.rs b/library/proc_macro/src/bridge/scoped_cell.rs new file mode 100644 index 000000000..2cde1f65a --- /dev/null +++ b/library/proc_macro/src/bridge/scoped_cell.rs @@ -0,0 +1,81 @@ +//! `Cell` variant for (scoped) existential lifetimes. + +use std::cell::Cell; +use std::mem; +use std::ops::{Deref, DerefMut}; + +/// Type lambda application, with a lifetime. +#[allow(unused_lifetimes)] +pub trait ApplyL<'a> { + type Out; +} + +/// Type lambda taking a lifetime, i.e., `Lifetime -> Type`. +pub trait LambdaL: for<'a> ApplyL<'a> {} + +impl<T: for<'a> ApplyL<'a>> LambdaL for T {} + +// HACK(eddyb) work around projection limitations with a newtype +// FIXME(#52812) replace with `&'a mut <T as ApplyL<'b>>::Out` +pub struct RefMutL<'a, 'b, T: LambdaL>(&'a mut <T as ApplyL<'b>>::Out); + +impl<'a, 'b, T: LambdaL> Deref for RefMutL<'a, 'b, T> { + type Target = <T as ApplyL<'b>>::Out; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a, 'b, T: LambdaL> DerefMut for RefMutL<'a, 'b, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + +pub struct ScopedCell<T: LambdaL>(Cell<<T as ApplyL<'static>>::Out>); + +impl<T: LambdaL> ScopedCell<T> { + pub const fn new(value: <T as ApplyL<'static>>::Out) -> Self { + ScopedCell(Cell::new(value)) + } + + /// Sets the value in `self` to `replacement` while + /// running `f`, which gets the old value, mutably. + /// The old value will be restored after `f` exits, even + /// by panic, including modifications made to it by `f`. + pub fn replace<'a, R>( + &self, + replacement: <T as ApplyL<'a>>::Out, + f: impl for<'b, 'c> FnOnce(RefMutL<'b, 'c, T>) -> R, + ) -> R { + /// Wrapper that ensures that the cell always gets filled + /// (with the original state, optionally changed by `f`), + /// even if `f` had panicked. + struct PutBackOnDrop<'a, T: LambdaL> { + cell: &'a ScopedCell<T>, + value: Option<<T as ApplyL<'static>>::Out>, + } + + impl<'a, T: LambdaL> Drop for PutBackOnDrop<'a, T> { + fn drop(&mut self) { + self.cell.0.set(self.value.take().unwrap()); + } + } + + let mut put_back_on_drop = PutBackOnDrop { + cell: self, + value: Some(self.0.replace(unsafe { + let erased = mem::transmute_copy(&replacement); + mem::forget(replacement); + erased + })), + }; + + f(RefMutL(put_back_on_drop.value.as_mut().unwrap())) + } + + /// Sets the value in `self` to `value` while running `f`. + pub fn set<R>(&self, value: <T as ApplyL<'_>>::Out, f: impl FnOnce() -> R) -> R { + self.replace(value, |_| f()) + } +} diff --git a/library/proc_macro/src/bridge/selfless_reify.rs b/library/proc_macro/src/bridge/selfless_reify.rs new file mode 100644 index 000000000..907ad256e --- /dev/null +++ b/library/proc_macro/src/bridge/selfless_reify.rs @@ -0,0 +1,84 @@ +//! Abstraction for creating `fn` pointers from any callable that *effectively* +//! has the equivalent of implementing `Default`, even if the compiler neither +//! provides `Default` nor allows reifying closures (i.e. creating `fn` pointers) +//! other than those with absolutely no captures. +//! +//! More specifically, for a closure-like type to be "effectively `Default`": +//! * it must be a ZST (zero-sized type): no information contained within, so +//! that `Default`'s return value (if it were implemented) is unambiguous +//! * it must be `Copy`: no captured "unique ZST tokens" or any other similar +//! types that would make duplicating values at will unsound +//! * combined with the ZST requirement, this confers a kind of "telecopy" +//! ability: similar to `Copy`, but without keeping the value around, and +//! instead "reconstructing" it (a noop given it's a ZST) when needed +//! * it must be *provably* inhabited: no captured uninhabited types or any +//! other types that cannot be constructed by the user of this abstraction +//! * the proof is a value of the closure-like type itself, in a sense the +//! "seed" for the "telecopy" process made possible by ZST + `Copy` +//! * this requirement is the only reason an abstraction limited to a specific +//! usecase is required: ZST + `Copy` can be checked with *at worst* a panic +//! at the "attempted `::default()` call" time, but that doesn't guarantee +//! that the value can be soundly created, and attempting to use the typical +//! "proof ZST token" approach leads yet again to having a ZST + `Copy` type +//! that is not proof of anything without a value (i.e. isomorphic to a +//! newtype of the type it's trying to prove the inhabitation of) +//! +//! A more flexible (and safer) solution to the general problem could exist once +//! `const`-generic parameters can have type parameters in their types: +//! +//! ```rust,ignore (needs future const-generics) +//! extern "C" fn ffi_wrapper< +//! A, R, +//! F: Fn(A) -> R, +//! const f: F, // <-- this `const`-generic is not yet allowed +//! >(arg: A) -> R { +//! f(arg) +//! } +//! ``` + +use std::mem; + +// FIXME(eddyb) this could be `trait` impls except for the `const fn` requirement. +macro_rules! define_reify_functions { + ($( + fn $name:ident $(<$($param:ident),*>)? + for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty; + )+) => { + $(pub const fn $name< + $($($param,)*)? + F: Fn($($arg_ty),*) -> $ret_ty + Copy + >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { + // FIXME(eddyb) describe the `F` type (e.g. via `type_name::<F>`) once panic + // formatting becomes possible in `const fn`. + assert!(mem::size_of::<F>() == 0, "selfless_reify: closure must be zero-sized"); + + $(extern $abi)? fn wrapper< + $($($param,)*)? + F: Fn($($arg_ty),*) -> $ret_ty + Copy + >($($arg: $arg_ty),*) -> $ret_ty { + let f = unsafe { + // SAFETY: `F` satisfies all criteria for "out of thin air" + // reconstructability (see module-level doc comment). + mem::MaybeUninit::<F>::uninit().assume_init() + }; + f($($arg),*) + } + let _f_proof = f; + wrapper::< + $($($param,)*)? + F + > + })+ + } +} + +define_reify_functions! { + fn _reify_to_extern_c_fn_unary<A, R> for extern "C" fn(arg: A) -> 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<R> 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 new file mode 100644 index 000000000..e068ec60b --- /dev/null +++ b/library/proc_macro/src/bridge/server.rs @@ -0,0 +1,339 @@ +//! Server-side traits. + +use super::*; + +use std::marker::PhantomData; + +// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`. +use super::client::HandleStore; + +pub trait Types { + type FreeFunctions: 'static; + type TokenStream: 'static + Clone; + type SourceFile: 'static + Clone; + type MultiSpan: 'static; + type Diagnostic: 'static; + type Span: 'static + Copy + Eq + Hash; + type Symbol: 'static; +} + +/// Declare an associated fn of one of the traits below, adding necessary +/// default bodies. +macro_rules! associated_fn { + (fn drop(&mut self, $arg:ident: $arg_ty:ty)) => + (fn drop(&mut self, $arg: $arg_ty) { mem::drop($arg) }); + + (fn clone(&mut self, $arg:ident: $arg_ty:ty) -> $ret_ty:ty) => + (fn clone(&mut self, $arg: $arg_ty) -> $ret_ty { $arg.clone() }); + + ($($item:tt)*) => ($($item)*;) +} + +macro_rules! declare_server_traits { + ($($name:ident { + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* + }),* $(,)?) => { + $(pub trait $name: Types { + $(associated_fn!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)* + })* + + pub trait Server: Types $(+ $name)* { + fn globals(&mut self) -> ExpnGlobals<Self::Span>; + + /// Intern a symbol received from RPC + fn intern_symbol(ident: &str) -> Self::Symbol; + + /// Recover the string value of a symbol, and invoke a callback with it. + fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)); + } + } +} +with_api!(Self, self_, declare_server_traits); + +pub(super) struct MarkedTypes<S: Types>(S); + +impl<S: Server> Server for MarkedTypes<S> { + fn globals(&mut self) -> ExpnGlobals<Self::Span> { + <_>::mark(Server::globals(&mut self.0)) + } + fn intern_symbol(ident: &str) -> Self::Symbol { + <_>::mark(S::intern_symbol(ident)) + } + fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { + S::with_symbol_string(symbol.unmark(), f) + } +} + +macro_rules! define_mark_types_impls { + ($($name:ident { + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* + }),* $(,)?) => { + impl<S: Types> Types for MarkedTypes<S> { + $(type $name = Marked<S::$name, client::$name>;)* + } + + $(impl<S: $name> $name for MarkedTypes<S> { + $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)? { + <_>::mark($name::$method(&mut self.0, $($arg.unmark()),*)) + })* + })* + } +} +with_api!(Self, self_, define_mark_types_impls); + +struct Dispatcher<S: Types> { + handle_store: HandleStore<S>, + server: S, +} + +macro_rules! define_dispatcher_impl { + ($($name:ident { + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* + }),* $(,)?) => { + // FIXME(eddyb) `pub` only for `ExecutionStrategy` below. + pub trait DispatcherTrait { + // HACK(eddyb) these are here to allow `Self::$name` to work below. + $(type $name;)* + + fn dispatch(&mut self, buf: Buffer) -> Buffer; + } + + impl<S: Server> DispatcherTrait for Dispatcher<MarkedTypes<S>> { + $(type $name = <MarkedTypes<S> as Types>::$name;)* + + fn dispatch(&mut self, mut buf: Buffer) -> Buffer { + let Dispatcher { handle_store, server } = self; + + let mut reader = &buf[..]; + match api_tags::Method::decode(&mut reader, &mut ()) { + $(api_tags::Method::$name(m) => match m { + $(api_tags::$name::$method => { + let mut call_method = || { + reverse_decode!(reader, handle_store; $($arg: $arg_ty),*); + $name::$method(server, $($arg),*) + }; + // HACK(eddyb) don't use `panic::catch_unwind` in a panic. + // If client and server happen to use the same `libstd`, + // `catch_unwind` asserts that the panic counter was 0, + // even when the closure passed to it didn't panic. + let r = if thread::panicking() { + Ok(call_method()) + } else { + panic::catch_unwind(panic::AssertUnwindSafe(call_method)) + .map_err(PanicMessage::from) + }; + + buf.clear(); + r.encode(&mut buf, handle_store); + })* + }),* + } + buf + } + } + } +} +with_api!(Self, self_, define_dispatcher_impl); + +pub trait ExecutionStrategy { + fn run_bridge_and_client( + &self, + dispatcher: &mut impl DispatcherTrait, + input: Buffer, + run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, + force_show_panics: bool, + ) -> Buffer; +} + +pub struct MaybeCrossThread<P> { + cross_thread: bool, + marker: PhantomData<P>, +} + +impl<P> MaybeCrossThread<P> { + pub const fn new(cross_thread: bool) -> Self { + MaybeCrossThread { cross_thread, marker: PhantomData } + } +} + +impl<P> ExecutionStrategy for MaybeCrossThread<P> +where + P: MessagePipe<Buffer> + Send + 'static, +{ + fn run_bridge_and_client( + &self, + dispatcher: &mut impl DispatcherTrait, + input: Buffer, + run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, + force_show_panics: bool, + ) -> Buffer { + if self.cross_thread { + <CrossThread<P>>::new().run_bridge_and_client( + dispatcher, + input, + run_client, + force_show_panics, + ) + } else { + SameThread.run_bridge_and_client(dispatcher, input, run_client, force_show_panics) + } + } +} + +pub struct SameThread; + +impl ExecutionStrategy for SameThread { + fn run_bridge_and_client( + &self, + dispatcher: &mut impl DispatcherTrait, + input: Buffer, + run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, + force_show_panics: bool, + ) -> Buffer { + let mut dispatch = |buf| dispatcher.dispatch(buf); + + run_client(BridgeConfig { + input, + dispatch: (&mut dispatch).into(), + force_show_panics, + _marker: marker::PhantomData, + }) + } +} + +pub struct CrossThread<P>(PhantomData<P>); + +impl<P> CrossThread<P> { + pub const fn new() -> Self { + CrossThread(PhantomData) + } +} + +impl<P> ExecutionStrategy for CrossThread<P> +where + P: MessagePipe<Buffer> + Send + 'static, +{ + fn run_bridge_and_client( + &self, + dispatcher: &mut impl DispatcherTrait, + input: Buffer, + run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, + force_show_panics: bool, + ) -> Buffer { + let (mut server, mut client) = P::new(); + + let join_handle = thread::spawn(move || { + let mut dispatch = |b: Buffer| -> Buffer { + client.send(b); + client.recv().expect("server died while client waiting for reply") + }; + + run_client(BridgeConfig { + input, + dispatch: (&mut dispatch).into(), + force_show_panics, + _marker: marker::PhantomData, + }) + }); + + while let Some(b) = server.recv() { + server.send(dispatcher.dispatch(b)); + } + + join_handle.join().unwrap() + } +} + +/// A message pipe used for communicating between server and client threads. +pub trait MessagePipe<T>: Sized { + /// Create a new pair of endpoints for the message pipe. + fn new() -> (Self, Self); + + /// Send a message to the other endpoint of this pipe. + fn send(&mut self, value: T); + + /// Receive a message from the other endpoint of this pipe. + /// + /// Returns `None` if the other end of the pipe has been destroyed, and no + /// message was received. + fn recv(&mut self) -> Option<T>; +} + +fn run_server< + S: Server, + I: Encode<HandleStore<MarkedTypes<S>>>, + O: for<'a, 's> DecodeMut<'a, 's, HandleStore<MarkedTypes<S>>>, +>( + strategy: &impl ExecutionStrategy, + handle_counters: &'static client::HandleCounters, + server: S, + input: I, + run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, + force_show_panics: bool, +) -> Result<O, PanicMessage> { + let mut dispatcher = + Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) }; + + let globals = dispatcher.server.globals(); + + let mut buf = Buffer::new(); + (globals, input).encode(&mut buf, &mut dispatcher.handle_store); + + buf = strategy.run_bridge_and_client(&mut dispatcher, buf, run_client, force_show_panics); + + Result::decode(&mut &buf[..], &mut dispatcher.handle_store) +} + +impl client::Client<crate::TokenStream, crate::TokenStream> { + pub fn run<S>( + &self, + strategy: &impl ExecutionStrategy, + server: S, + input: S::TokenStream, + force_show_panics: bool, + ) -> Result<S::TokenStream, PanicMessage> + where + S: Server, + S::TokenStream: Default, + { + let client::Client { get_handle_counters, run, _marker } = *self; + run_server( + strategy, + get_handle_counters(), + server, + <MarkedTypes<S> as Types>::TokenStream::mark(input), + run, + force_show_panics, + ) + .map(|s| <Option<<MarkedTypes<S> as Types>::TokenStream>>::unmark(s).unwrap_or_default()) + } +} + +impl client::Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream> { + pub fn run<S>( + &self, + strategy: &impl ExecutionStrategy, + server: S, + input: S::TokenStream, + input2: S::TokenStream, + force_show_panics: bool, + ) -> Result<S::TokenStream, PanicMessage> + where + S: Server, + S::TokenStream: Default, + { + let client::Client { get_handle_counters, run, _marker } = *self; + run_server( + strategy, + get_handle_counters(), + server, + ( + <MarkedTypes<S> as Types>::TokenStream::mark(input), + <MarkedTypes<S> as Types>::TokenStream::mark(input2), + ), + run, + force_show_panics, + ) + .map(|s| <Option<<MarkedTypes<S> as Types>::TokenStream>>::unmark(s).unwrap_or_default()) + } +} diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs new file mode 100644 index 000000000..930c11145 --- /dev/null +++ b/library/proc_macro/src/bridge/symbol.rs @@ -0,0 +1,205 @@ +//! Client-side interner used for symbols. +//! +//! This is roughly based on the symbol interner from `rustc_span` and the +//! DroplessArena from `rustc_arena`. It is unfortunately a complete +//! copy/re-implementation rather than a dependency as it is difficult to depend +//! on crates from within `proc_macro`, due to it being built at the same time +//! as `std`. +//! +//! If at some point in the future it becomes easier to add dependencies to +//! proc_macro, this module should probably be removed or simplified. + +use std::cell::RefCell; +use std::num::NonZeroU32; +use std::str; + +use super::*; + +/// Handle for a symbol string stored within the Interner. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Symbol(NonZeroU32); + +impl !Send for Symbol {} +impl !Sync for Symbol {} + +impl Symbol { + /// Intern a new `Symbol` + pub(crate) fn new(string: &str) -> Self { + INTERNER.with_borrow_mut(|i| i.intern(string)) + } + + /// Create a new `Symbol` for an identifier. + /// + /// Validates and normalizes before converting it to a symbol. + pub(crate) fn new_ident(string: &str, is_raw: bool) -> Self { + // Fast-path: check if this is a valid ASCII identifier + if Self::is_valid_ascii_ident(string.as_bytes()) { + if is_raw && !Self::can_be_raw(string) { + panic!("`{}` cannot be a raw identifier", string); + } + return Self::new(string); + } + + // Slow-path: If the string is already ASCII we're done, otherwise ask + // our server to do this for us over RPC. + // We don't need to check for identifiers which can't be raw here, + // because all of them are ASCII. + if string.is_ascii() { + Err(()) + } else { + client::Symbol::normalize_and_validate_ident(string) + } + .unwrap_or_else(|_| panic!("`{:?}` is not a valid identifier", string)) + } + + /// Run a callback with the symbol's string value. + pub(crate) fn with<R>(self, f: impl FnOnce(&str) -> R) -> R { + INTERNER.with_borrow(|i| f(i.get(self))) + } + + /// Clear out the thread-local symbol interner, making all previously + /// created symbols invalid such that `with` will panic when called on them. + pub(crate) fn invalidate_all() { + INTERNER.with_borrow_mut(|i| i.clear()); + } + + /// Check if the ident is a valid ASCII identifier. + /// + /// This is a short-circuit which is cheap to implement within the + /// proc-macro client to avoid RPC when creating simple idents, but may + /// return `false` for a valid identifier if it contains non-ASCII + /// characters. + fn is_valid_ascii_ident(bytes: &[u8]) -> bool { + matches!(bytes.first(), Some(b'_' | b'a'..=b'z' | b'A'..=b'Z')) + && bytes[1..] + .iter() + .all(|b| matches!(b, b'_' | b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9')) + } + + // Mimics the behaviour of `Symbol::can_be_raw` from `rustc_span` + fn can_be_raw(string: &str) -> bool { + match string { + "_" | "super" | "self" | "Self" | "crate" => false, + _ => true, + } + } +} + +impl fmt::Debug for Symbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.with(|s| fmt::Debug::fmt(s, f)) + } +} + +impl ToString for Symbol { + fn to_string(&self) -> String { + self.with(|s| s.to_owned()) + } +} + +impl fmt::Display for Symbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.with(|s| fmt::Display::fmt(s, f)) + } +} + +impl<S> Encode<S> for Symbol { + fn encode(self, w: &mut Writer, s: &mut S) { + self.with(|sym| sym.encode(w, s)) + } +} + +impl<S: server::Server> DecodeMut<'_, '_, client::HandleStore<server::MarkedTypes<S>>> + for Marked<S::Symbol, Symbol> +{ + fn decode(r: &mut Reader<'_>, s: &mut client::HandleStore<server::MarkedTypes<S>>) -> Self { + Mark::mark(S::intern_symbol(<&str>::decode(r, s))) + } +} + +impl<S: server::Server> Encode<client::HandleStore<server::MarkedTypes<S>>> + for Marked<S::Symbol, Symbol> +{ + fn encode(self, w: &mut Writer, s: &mut client::HandleStore<server::MarkedTypes<S>>) { + S::with_symbol_string(&self.unmark(), |sym| sym.encode(w, s)) + } +} + +impl<S> DecodeMut<'_, '_, S> for Symbol { + fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + Symbol::new(<&str>::decode(r, s)) + } +} + +thread_local! { + static INTERNER: RefCell<Interner> = RefCell::new(Interner { + arena: arena::Arena::new(), + names: fxhash::FxHashMap::default(), + strings: Vec::new(), + // Start with a base of 1 to make sure that `NonZeroU32` works. + sym_base: NonZeroU32::new(1).unwrap(), + }); +} + +/// Basic interner for a `Symbol`, inspired by the one in `rustc_span`. +struct Interner { + arena: arena::Arena, + // SAFETY: These `'static` lifetimes are actually references to data owned + // by the Arena. This is safe, as we never return them as static references + // from `Interner`. + names: fxhash::FxHashMap<&'static str, Symbol>, + strings: Vec<&'static str>, + // The offset to apply to symbol names stored in the interner. This is used + // to ensure that symbol names are not re-used after the interner is + // cleared. + sym_base: NonZeroU32, +} + +impl Interner { + fn intern(&mut self, string: &str) -> Symbol { + if let Some(&name) = self.names.get(string) { + return name; + } + + let name = Symbol( + self.sym_base + .checked_add(self.strings.len() as u32) + .expect("`proc_macro` symbol name overflow"), + ); + + let string: &str = self.arena.alloc_str(string); + + // SAFETY: we can extend the arena allocation to `'static` because we + // only access these while the arena is still alive. + let string: &'static str = unsafe { &*(string as *const str) }; + self.strings.push(string); + self.names.insert(string, name); + name + } + + /// Read a symbol's value from the store while it is held. + fn get(&self, symbol: Symbol) -> &str { + // NOTE: Subtract out the offset which was added to make the symbol + // nonzero and prevent symbol name re-use. + let name = symbol + .0 + .get() + .checked_sub(self.sym_base.get()) + .expect("use-after-free of `proc_macro` symbol"); + self.strings[name as usize] + } + + /// Clear all symbols from the store, invalidating them such that `get` will + /// panic if they are accessed in the future. + fn clear(&mut self) { + // NOTE: Be careful not to panic here, as we may be called on the client + // when a `catch_unwind` isn't installed. + self.sym_base = self.sym_base.saturating_add(self.strings.len() as u32); + self.names.clear(); + self.strings.clear(); + + // SAFETY: This is cleared after the names and strings tables are + // cleared out, so no references into the arena should remain. + self.arena = arena::Arena::new(); + } +} diff --git a/library/proc_macro/src/diagnostic.rs b/library/proc_macro/src/diagnostic.rs new file mode 100644 index 000000000..6e46dc036 --- /dev/null +++ b/library/proc_macro/src/diagnostic.rs @@ -0,0 +1,182 @@ +use crate::Span; + +/// An enum representing a diagnostic level. +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +#[derive(Copy, Clone, Debug)] +#[non_exhaustive] +pub enum Level { + /// An error. + Error, + /// A warning. + Warning, + /// A note. + Note, + /// A help message. + Help, +} + +/// Trait implemented by types that can be converted into a set of `Span`s. +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +pub trait MultiSpan { + /// Converts `self` into a `Vec<Span>`. + fn into_spans(self) -> Vec<Span>; +} + +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +impl MultiSpan for Span { + fn into_spans(self) -> Vec<Span> { + vec![self] + } +} + +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +impl MultiSpan for Vec<Span> { + fn into_spans(self) -> Vec<Span> { + self + } +} + +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +impl<'a> MultiSpan for &'a [Span] { + fn into_spans(self) -> Vec<Span> { + self.to_vec() + } +} + +/// A structure representing a diagnostic message and associated children +/// messages. +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +#[derive(Clone, Debug)] +pub struct Diagnostic { + level: Level, + message: String, + spans: Vec<Span>, + children: Vec<Diagnostic>, +} + +macro_rules! diagnostic_child_methods { + ($spanned:ident, $regular:ident, $level:expr) => { + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + #[doc = concat!("Adds a new child diagnostics message to `self` with the [`", + stringify!($level), "`] level, and the given `spans` and `message`.")] + pub fn $spanned<S, T>(mut self, spans: S, message: T) -> Diagnostic + where + S: MultiSpan, + T: Into<String>, + { + self.children.push(Diagnostic::spanned(spans, $level, message)); + self + } + + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + #[doc = concat!("Adds a new child diagnostic message to `self` with the [`", + stringify!($level), "`] level, and the given `message`.")] + pub fn $regular<T: Into<String>>(mut self, message: T) -> Diagnostic { + self.children.push(Diagnostic::new($level, message)); + self + } + }; +} + +/// Iterator over the children diagnostics of a `Diagnostic`. +#[derive(Debug, Clone)] +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +pub struct Children<'a>(std::slice::Iter<'a, Diagnostic>); + +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +impl<'a> Iterator for Children<'a> { + type Item = &'a Diagnostic; + + fn next(&mut self) -> Option<Self::Item> { + self.0.next() + } +} + +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +impl Diagnostic { + /// Creates a new diagnostic with the given `level` and `message`. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic { + Diagnostic { level, message: message.into(), spans: vec![], children: vec![] } + } + + /// Creates a new diagnostic with the given `level` and `message` pointing to + /// the given set of `spans`. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn spanned<S, T>(spans: S, level: Level, message: T) -> Diagnostic + where + S: MultiSpan, + T: Into<String>, + { + Diagnostic { level, message: message.into(), spans: spans.into_spans(), children: vec![] } + } + + diagnostic_child_methods!(span_error, error, Level::Error); + diagnostic_child_methods!(span_warning, warning, Level::Warning); + diagnostic_child_methods!(span_note, note, Level::Note); + diagnostic_child_methods!(span_help, help, Level::Help); + + /// Returns the diagnostic `level` for `self`. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn level(&self) -> Level { + self.level + } + + /// Sets the level in `self` to `level`. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn set_level(&mut self, level: Level) { + self.level = level; + } + + /// Returns the message in `self`. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn message(&self) -> &str { + &self.message + } + + /// Sets the message in `self` to `message`. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn set_message<T: Into<String>>(&mut self, message: T) { + self.message = message.into(); + } + + /// Returns the `Span`s in `self`. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn spans(&self) -> &[Span] { + &self.spans + } + + /// Sets the `Span`s in `self` to `spans`. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn set_spans<S: MultiSpan>(&mut self, spans: S) { + self.spans = spans.into_spans(); + } + + /// Returns an iterator over the children diagnostics of `self`. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn children(&self) -> Children<'_> { + Children(self.children.iter()) + } + + /// Emit the diagnostic. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn emit(self) { + fn to_internal(spans: Vec<Span>) -> crate::bridge::client::MultiSpan { + let mut multi_span = crate::bridge::client::MultiSpan::new(); + for span in spans { + multi_span.push(span.0); + } + multi_span + } + + let mut diag = crate::bridge::client::Diagnostic::new( + self.level, + &self.message[..], + to_internal(self.spans), + ); + for c in self.children { + diag.sub(c.level, &c.message[..], to_internal(c.spans)); + } + diag.emit(); + } +} diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs new file mode 100644 index 000000000..8e478cd7b --- /dev/null +++ b/library/proc_macro/src/lib.rs @@ -0,0 +1,1526 @@ +//! A support library for macro authors when defining new macros. +//! +//! This library, provided by the standard distribution, provides the types +//! consumed in the interfaces of procedurally defined macro definitions such as +//! function-like macros `#[proc_macro]`, macro attributes `#[proc_macro_attribute]` and +//! custom derive attributes`#[proc_macro_derive]`. +//! +//! See [the book] for more. +//! +//! [the book]: ../book/ch19-06-macros.html#procedural-macros-for-generating-code-from-attributes + +#![stable(feature = "proc_macro_lib", since = "1.15.0")] +#![deny(missing_docs)] +#![doc( + html_playground_url = "https://play.rust-lang.org/", + issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", + test(no_crate_inject, attr(deny(warnings))), + test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) +)] +// This library is copied into rust-analyzer to allow loading rustc compiled proc macros. +// Please avoid unstable features where possible to minimize the amount of changes necessary +// to make it compile with rust-analyzer on stable. +#![feature(rustc_allow_const_fn_unstable)] +#![feature(staged_api)] +#![feature(allow_internal_unstable)] +#![feature(decl_macro)] +#![feature(local_key_cell_methods)] +#![feature(maybe_uninit_write_slice)] +#![feature(negative_impls)] +#![feature(new_uninit)] +#![feature(restricted_std)] +#![feature(rustc_attrs)] +#![feature(min_specialization)] +#![feature(strict_provenance)] +#![recursion_limit = "256"] + +#[unstable(feature = "proc_macro_internals", issue = "27812")] +#[doc(hidden)] +pub mod bridge; + +mod diagnostic; + +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +pub use diagnostic::{Diagnostic, Level, MultiSpan}; + +use std::cmp::Ordering; +use std::ops::RangeBounds; +use std::path::PathBuf; +use std::str::FromStr; +use std::{error, fmt, iter}; + +/// Determines whether proc_macro has been made accessible to the currently +/// running program. +/// +/// The proc_macro crate is only intended for use inside the implementation of +/// procedural macros. All the functions in this crate panic if invoked from +/// outside of a procedural macro, such as from a build script or unit test or +/// ordinary Rust binary. +/// +/// With consideration for Rust libraries that are designed to support both +/// macro and non-macro use cases, `proc_macro::is_available()` provides a +/// non-panicking way to detect whether the infrastructure required to use the +/// API of proc_macro is presently available. Returns true if invoked from +/// 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::client::is_available() +} + +/// The main type provided by this crate, representing an abstract stream of +/// tokens, or, more specifically, a sequence of token trees. +/// The type provide interfaces for iterating over those token trees and, conversely, +/// collecting a number of token trees into one stream. +/// +/// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]` +/// and `#[proc_macro_derive]` definitions. +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +#[derive(Clone)] +pub struct TokenStream(Option<bridge::client::TokenStream>); + +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl !Send for TokenStream {} +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl !Sync for TokenStream {} + +/// Error returned from `TokenStream::from_str`. +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +#[non_exhaustive] +#[derive(Debug)] +pub struct LexError; + +#[stable(feature = "proc_macro_lexerror_impls", since = "1.44.0")] +impl fmt::Display for LexError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cannot parse string into token stream") + } +} + +#[stable(feature = "proc_macro_lexerror_impls", since = "1.44.0")] +impl error::Error for LexError {} + +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl !Send for LexError {} +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl !Sync for LexError {} + +/// Error returned from `TokenStream::expand_expr`. +#[unstable(feature = "proc_macro_expand", issue = "90765")] +#[non_exhaustive] +#[derive(Debug)] +pub struct ExpandError; + +#[unstable(feature = "proc_macro_expand", issue = "90765")] +impl fmt::Display for ExpandError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("macro expansion failed") + } +} + +#[unstable(feature = "proc_macro_expand", issue = "90765")] +impl error::Error for ExpandError {} + +#[unstable(feature = "proc_macro_expand", issue = "90765")] +impl !Send for ExpandError {} + +#[unstable(feature = "proc_macro_expand", issue = "90765")] +impl !Sync for ExpandError {} + +impl TokenStream { + /// Returns an empty `TokenStream` containing no token trees. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn new() -> TokenStream { + TokenStream(None) + } + + /// Checks if this `TokenStream` is empty. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn is_empty(&self) -> bool { + self.0.as_ref().map(|h| h.is_empty()).unwrap_or(true) + } + + /// Parses this `TokenStream` as an expression and attempts to expand any + /// macros within it. Returns the expanded `TokenStream`. + /// + /// Currently only expressions expanding to literals will succeed, although + /// this may be relaxed in the future. + /// + /// NOTE: In error conditions, `expand_expr` may leave macros unexpanded, + /// report an error, failing compilation, and/or return an `Err(..)`. The + /// specific behavior for any error condition, and what conditions are + /// considered errors, is unspecified and may change in the future. + #[unstable(feature = "proc_macro_expand", issue = "90765")] + pub fn expand_expr(&self) -> Result<TokenStream, ExpandError> { + let stream = self.0.as_ref().ok_or(ExpandError)?; + match bridge::client::TokenStream::expand_expr(stream) { + Ok(stream) => Ok(TokenStream(Some(stream))), + Err(_) => Err(ExpandError), + } + } +} + +/// Attempts to break the string into tokens and parse those tokens into a token stream. +/// May fail for a number of reasons, for example, if the string contains unbalanced delimiters +/// or characters not existing in the language. +/// All tokens in the parsed stream get `Span::call_site()` spans. +/// +/// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to +/// change these errors into `LexError`s later. +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl FromStr for TokenStream { + type Err = LexError; + + fn from_str(src: &str) -> Result<TokenStream, LexError> { + Ok(TokenStream(Some(bridge::client::TokenStream::from_str(src)))) + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl ToString for TokenStream { + fn to_string(&self) -> String { + self.0.as_ref().map(|t| t.to_string()).unwrap_or_default() + } +} + +/// Prints the token stream as a string that is supposed to be losslessly convertible back +/// into the same token stream (modulo spans), except for possibly `TokenTree::Group`s +/// with `Delimiter::None` delimiters and negative numeric literals. +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl fmt::Display for TokenStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_string()) + } +} + +/// Prints token in a form convenient for debugging. +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl fmt::Debug for TokenStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("TokenStream ")?; + f.debug_list().entries(self.clone()).finish() + } +} + +#[stable(feature = "proc_macro_token_stream_default", since = "1.45.0")] +impl Default for TokenStream { + fn default() -> Self { + TokenStream::new() + } +} + +#[unstable(feature = "proc_macro_quote", issue = "54722")] +pub use quote::{quote, quote_span}; + +fn tree_to_bridge_tree( + tree: TokenTree, +) -> bridge::TokenTree<bridge::client::TokenStream, bridge::client::Span, bridge::client::Symbol> { + match tree { + TokenTree::Group(tt) => bridge::TokenTree::Group(tt.0), + TokenTree::Punct(tt) => bridge::TokenTree::Punct(tt.0), + TokenTree::Ident(tt) => bridge::TokenTree::Ident(tt.0), + TokenTree::Literal(tt) => bridge::TokenTree::Literal(tt.0), + } +} + +/// Creates a token stream containing a single token tree. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl From<TokenTree> for TokenStream { + fn from(tree: TokenTree) -> TokenStream { + TokenStream(Some(bridge::client::TokenStream::from_token_tree(tree_to_bridge_tree(tree)))) + } +} + +/// Non-generic helper for implementing `FromIterator<TokenTree>` and +/// `Extend<TokenTree>` with less monomorphization in calling crates. +struct ConcatTreesHelper { + trees: Vec< + bridge::TokenTree< + bridge::client::TokenStream, + bridge::client::Span, + bridge::client::Symbol, + >, + >, +} + +impl ConcatTreesHelper { + fn new(capacity: usize) -> Self { + ConcatTreesHelper { trees: Vec::with_capacity(capacity) } + } + + fn push(&mut self, tree: TokenTree) { + self.trees.push(tree_to_bridge_tree(tree)); + } + + fn build(self) -> TokenStream { + if self.trees.is_empty() { + TokenStream(None) + } else { + TokenStream(Some(bridge::client::TokenStream::concat_trees(None, self.trees))) + } + } + + fn append_to(self, stream: &mut TokenStream) { + if self.trees.is_empty() { + return; + } + stream.0 = Some(bridge::client::TokenStream::concat_trees(stream.0.take(), self.trees)) + } +} + +/// Non-generic helper for implementing `FromIterator<TokenStream>` and +/// `Extend<TokenStream>` with less monomorphization in calling crates. +struct ConcatStreamsHelper { + streams: Vec<bridge::client::TokenStream>, +} + +impl ConcatStreamsHelper { + fn new(capacity: usize) -> Self { + ConcatStreamsHelper { streams: Vec::with_capacity(capacity) } + } + + fn push(&mut self, stream: TokenStream) { + if let Some(stream) = stream.0 { + self.streams.push(stream); + } + } + + fn build(mut self) -> TokenStream { + if self.streams.len() <= 1 { + TokenStream(self.streams.pop()) + } else { + TokenStream(Some(bridge::client::TokenStream::concat_streams(None, self.streams))) + } + } + + fn append_to(mut self, stream: &mut TokenStream) { + if self.streams.is_empty() { + return; + } + let base = stream.0.take(); + if base.is_none() && self.streams.len() == 1 { + stream.0 = self.streams.pop(); + } else { + stream.0 = Some(bridge::client::TokenStream::concat_streams(base, self.streams)); + } + } +} + +/// Collects a number of token trees into a single stream. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl iter::FromIterator<TokenTree> for TokenStream { + fn from_iter<I: IntoIterator<Item = TokenTree>>(trees: I) -> Self { + let iter = trees.into_iter(); + let mut builder = ConcatTreesHelper::new(iter.size_hint().0); + iter.for_each(|tree| builder.push(tree)); + builder.build() + } +} + +/// A "flattening" operation on token streams, collects token trees +/// from multiple token streams into a single stream. +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl iter::FromIterator<TokenStream> for TokenStream { + fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self { + let iter = streams.into_iter(); + let mut builder = ConcatStreamsHelper::new(iter.size_hint().0); + iter.for_each(|stream| builder.push(stream)); + builder.build() + } +} + +#[stable(feature = "token_stream_extend", since = "1.30.0")] +impl Extend<TokenTree> for TokenStream { + fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, trees: I) { + let iter = trees.into_iter(); + let mut builder = ConcatTreesHelper::new(iter.size_hint().0); + iter.for_each(|tree| builder.push(tree)); + builder.append_to(self); + } +} + +#[stable(feature = "token_stream_extend", since = "1.30.0")] +impl Extend<TokenStream> for TokenStream { + fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) { + let iter = streams.into_iter(); + let mut builder = ConcatStreamsHelper::new(iter.size_hint().0); + iter.for_each(|stream| builder.push(stream)); + builder.append_to(self); + } +} + +/// Public implementation details for the `TokenStream` type, such as iterators. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +pub mod token_stream { + use crate::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree}; + + /// An iterator over `TokenStream`'s `TokenTree`s. + /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, + /// and returns whole groups as token trees. + #[derive(Clone)] + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub struct IntoIter( + std::vec::IntoIter< + bridge::TokenTree< + bridge::client::TokenStream, + bridge::client::Span, + bridge::client::Symbol, + >, + >, + ); + + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + impl Iterator for IntoIter { + type Item = TokenTree; + + fn next(&mut self) -> Option<TokenTree> { + self.0.next().map(|tree| match tree { + bridge::TokenTree::Group(tt) => TokenTree::Group(Group(tt)), + bridge::TokenTree::Punct(tt) => TokenTree::Punct(Punct(tt)), + bridge::TokenTree::Ident(tt) => TokenTree::Ident(Ident(tt)), + bridge::TokenTree::Literal(tt) => TokenTree::Literal(Literal(tt)), + }) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.0.size_hint() + } + + fn count(self) -> usize { + self.0.count() + } + } + + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + impl IntoIterator for TokenStream { + type Item = TokenTree; + type IntoIter = IntoIter; + + fn into_iter(self) -> IntoIter { + IntoIter(self.0.map(|v| v.into_trees()).unwrap_or_default().into_iter()) + } + } +} + +/// `quote!(..)` accepts arbitrary tokens and expands into a `TokenStream` describing the input. +/// For example, `quote!(a + b)` will produce an expression, that, when evaluated, constructs +/// the `TokenStream` `[Ident("a"), Punct('+', Alone), Ident("b")]`. +/// +/// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term. +/// To quote `$` itself, use `$$`. +#[unstable(feature = "proc_macro_quote", issue = "54722")] +#[allow_internal_unstable(proc_macro_def_site, proc_macro_internals)] +#[rustc_builtin_macro] +pub macro quote($($t:tt)*) { + /* compiler built-in */ +} + +#[unstable(feature = "proc_macro_internals", issue = "27812")] +#[doc(hidden)] +mod quote; + +/// A region of source code, along with macro expansion information. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +#[derive(Copy, Clone)] +pub struct Span(bridge::client::Span); + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Send for Span {} +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Sync for Span {} + +macro_rules! diagnostic_method { + ($name:ident, $level:expr) => { + /// Creates a new `Diagnostic` with the given `message` at the span + /// `self`. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn $name<T: Into<String>>(self, message: T) -> Diagnostic { + Diagnostic::spanned(self, $level, message) + } + }; +} + +impl Span { + /// A span that resolves at the macro definition site. + #[unstable(feature = "proc_macro_def_site", issue = "54724")] + pub fn def_site() -> Span { + Span(bridge::client::Span::def_site()) + } + + /// The span of the invocation of the current procedural macro. + /// Identifiers created with this span will be resolved as if they were written + /// directly at the macro call location (call-site hygiene) and other code + /// at the macro call site will be able to refer to them as well. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn call_site() -> Span { + Span(bridge::client::Span::call_site()) + } + + /// A span that represents `macro_rules` hygiene, and sometimes resolves at the macro + /// definition site (local variables, labels, `$crate`) and sometimes at the macro + /// call site (everything else). + /// The span location is taken from the call-site. + #[stable(feature = "proc_macro_mixed_site", since = "1.45.0")] + pub fn mixed_site() -> Span { + Span(bridge::client::Span::mixed_site()) + } + + /// The original source file into which this span points. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn source_file(&self) -> SourceFile { + SourceFile(self.0.source_file()) + } + + /// The `Span` for the tokens in the previous macro expansion from which + /// `self` was generated from, if any. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn parent(&self) -> Option<Span> { + self.0.parent().map(Span) + } + + /// The span for the origin source code that `self` was generated from. If + /// this `Span` wasn't generated from other macro expansions then the return + /// value is the same as `*self`. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn source(&self) -> Span { + Span(self.0.source()) + } + + /// Gets the starting line/column in the source file for this span. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn start(&self) -> LineColumn { + self.0.start().add_1_to_column() + } + + /// Gets the ending line/column in the source file for this span. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn end(&self) -> LineColumn { + self.0.end().add_1_to_column() + } + + /// Creates an empty span pointing to directly before this span. + #[unstable(feature = "proc_macro_span_shrink", issue = "87552")] + pub fn before(&self) -> Span { + Span(self.0.before()) + } + + /// Creates an empty span pointing to directly after this span. + #[unstable(feature = "proc_macro_span_shrink", issue = "87552")] + pub fn after(&self) -> Span { + Span(self.0.after()) + } + + /// Creates a new span encompassing `self` and `other`. + /// + /// Returns `None` if `self` and `other` are from different files. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn join(&self, other: Span) -> Option<Span> { + self.0.join(other.0).map(Span) + } + + /// Creates a new span with the same line/column information as `self` but + /// that resolves symbols as though it were at `other`. + #[stable(feature = "proc_macro_span_resolved_at", since = "1.45.0")] + pub fn resolved_at(&self, other: Span) -> Span { + Span(self.0.resolved_at(other.0)) + } + + /// Creates a new span with the same name resolution behavior as `self` but + /// with the line/column information of `other`. + #[stable(feature = "proc_macro_span_located_at", since = "1.45.0")] + pub fn located_at(&self, other: Span) -> Span { + other.resolved_at(*self) + } + + /// Compares to spans to see if they're equal. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn eq(&self, other: &Span) -> bool { + self.0 == other.0 + } + + /// Returns the source text behind a span. This preserves the original source + /// code, including spaces and comments. It only returns a result if the span + /// corresponds to real source code. + /// + /// Note: The observable result of a macro should only rely on the tokens and + /// not on this source text. The result of this function is a best effort to + /// be used for diagnostics only. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn source_text(&self) -> Option<String> { + self.0.source_text() + } + + // Used by the implementation of `Span::quote` + #[doc(hidden)] + #[unstable(feature = "proc_macro_internals", issue = "27812")] + pub fn save_span(&self) -> usize { + self.0.save_span() + } + + // Used by the implementation of `Span::quote` + #[doc(hidden)] + #[unstable(feature = "proc_macro_internals", issue = "27812")] + pub fn recover_proc_macro_span(id: usize) -> Span { + Span(bridge::client::Span::recover_proc_macro_span(id)) + } + + diagnostic_method!(error, Level::Error); + diagnostic_method!(warning, Level::Warning); + diagnostic_method!(note, Level::Note); + diagnostic_method!(help, Level::Help); +} + +/// Prints a span in a form convenient for debugging. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Debug for Span { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +/// A line-column pair representing the start or end of a `Span`. +#[unstable(feature = "proc_macro_span", issue = "54725")] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct LineColumn { + /// The 1-indexed line in the source file on which the span starts or ends (inclusive). + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub line: usize, + /// The 1-indexed column (number of bytes in UTF-8 encoding) in the source + /// file on which the span starts or ends (inclusive). + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub column: usize, +} + +impl LineColumn { + fn add_1_to_column(self) -> Self { + LineColumn { line: self.line, column: self.column + 1 } + } +} + +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl !Send for LineColumn {} +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl !Sync for LineColumn {} + +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl Ord for LineColumn { + fn cmp(&self, other: &Self) -> Ordering { + self.line.cmp(&other.line).then(self.column.cmp(&other.column)) + } +} + +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl PartialOrd for LineColumn { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +/// The source file of a given `Span`. +#[unstable(feature = "proc_macro_span", issue = "54725")] +#[derive(Clone)] +pub struct SourceFile(bridge::client::SourceFile); + +impl SourceFile { + /// Gets the path to this source file. + /// + /// ### Note + /// If the code span associated with this `SourceFile` was generated by an external macro, this + /// macro, this might not be an actual path on the filesystem. Use [`is_real`] to check. + /// + /// Also note that even if `is_real` returns `true`, if `--remap-path-prefix` was passed on + /// the command line, the path as given might not actually be valid. + /// + /// [`is_real`]: Self::is_real + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn path(&self) -> PathBuf { + PathBuf::from(self.0.path()) + } + + /// Returns `true` if this source file is a real source file, and not generated by an external + /// macro's expansion. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn is_real(&self) -> bool { + // This is a hack until intercrate spans are implemented and we can have real source files + // for spans generated in external macros. + // https://github.com/rust-lang/rust/pull/43604#issuecomment-333334368 + self.0.is_real() + } +} + +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl fmt::Debug for SourceFile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SourceFile") + .field("path", &self.path()) + .field("is_real", &self.is_real()) + .finish() + } +} + +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl PartialEq for SourceFile { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl Eq for SourceFile {} + +/// A single token or a delimited sequence of token trees (e.g., `[1, (), ..]`). +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +#[derive(Clone)] +pub enum TokenTree { + /// A token stream surrounded by bracket delimiters. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Group(#[stable(feature = "proc_macro_lib2", since = "1.29.0")] Group), + /// An identifier. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Ident(#[stable(feature = "proc_macro_lib2", since = "1.29.0")] Ident), + /// A single punctuation character (`+`, `,`, `$`, etc.). + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Punct(#[stable(feature = "proc_macro_lib2", since = "1.29.0")] Punct), + /// A literal character (`'a'`), string (`"hello"`), number (`2.3`), etc. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Literal(#[stable(feature = "proc_macro_lib2", since = "1.29.0")] Literal), +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Send for TokenTree {} +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Sync for TokenTree {} + +impl TokenTree { + /// Returns the span of this tree, delegating to the `span` method of + /// the contained token or a delimited stream. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn span(&self) -> Span { + match *self { + TokenTree::Group(ref t) => t.span(), + TokenTree::Ident(ref t) => t.span(), + TokenTree::Punct(ref t) => t.span(), + TokenTree::Literal(ref t) => t.span(), + } + } + + /// Configures the span for *only this token*. + /// + /// Note that if this token is a `Group` then this method will not configure + /// the span of each of the internal tokens, this will simply delegate to + /// the `set_span` method of each variant. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn set_span(&mut self, span: Span) { + match *self { + TokenTree::Group(ref mut t) => t.set_span(span), + TokenTree::Ident(ref mut t) => t.set_span(span), + TokenTree::Punct(ref mut t) => t.set_span(span), + TokenTree::Literal(ref mut t) => t.set_span(span), + } + } +} + +/// Prints token tree in a form convenient for debugging. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Debug for TokenTree { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Each of these has the name in the struct type in the derived debug, + // so don't bother with an extra layer of indirection + match *self { + TokenTree::Group(ref tt) => tt.fmt(f), + TokenTree::Ident(ref tt) => tt.fmt(f), + TokenTree::Punct(ref tt) => tt.fmt(f), + TokenTree::Literal(ref tt) => tt.fmt(f), + } + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl From<Group> for TokenTree { + fn from(g: Group) -> TokenTree { + TokenTree::Group(g) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl From<Ident> for TokenTree { + fn from(g: Ident) -> TokenTree { + TokenTree::Ident(g) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl From<Punct> for TokenTree { + fn from(g: Punct) -> TokenTree { + TokenTree::Punct(g) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl From<Literal> for TokenTree { + fn from(g: Literal) -> TokenTree { + TokenTree::Literal(g) + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl ToString for TokenTree { + fn to_string(&self) -> String { + match *self { + TokenTree::Group(ref t) => t.to_string(), + TokenTree::Ident(ref t) => t.to_string(), + TokenTree::Punct(ref t) => t.to_string(), + TokenTree::Literal(ref t) => t.to_string(), + } + } +} + +/// Prints the token tree as a string that is supposed to be losslessly convertible back +/// into the same token tree (modulo spans), except for possibly `TokenTree::Group`s +/// with `Delimiter::None` delimiters and negative numeric literals. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Display for TokenTree { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_string()) + } +} + +/// A delimited token stream. +/// +/// A `Group` internally contains a `TokenStream` which is surrounded by `Delimiter`s. +#[derive(Clone)] +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +pub struct Group(bridge::Group<bridge::client::TokenStream, bridge::client::Span>); + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Send for Group {} +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Sync for Group {} + +/// Describes how a sequence of token trees is delimited. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +pub enum Delimiter { + /// `( ... )` + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Parenthesis, + /// `{ ... }` + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Brace, + /// `[ ... ]` + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Bracket, + /// `Ø ... Ø` + /// An invisible delimiter, that may, for example, appear around tokens coming from a + /// "macro variable" `$var`. It is important to preserve operator priorities in cases like + /// `$var * 3` where `$var` is `1 + 2`. + /// Invisible delimiters might not survive roundtrip of a token stream through a string. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + None, +} + +impl Group { + /// Creates a new `Group` with the given delimiter and token stream. + /// + /// This constructor will set the span for this group to + /// `Span::call_site()`. To change the span you can use the `set_span` + /// method below. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn new(delimiter: Delimiter, stream: TokenStream) -> Group { + Group(bridge::Group { + delimiter, + stream: stream.0, + span: bridge::DelimSpan::from_single(Span::call_site().0), + }) + } + + /// Returns the delimiter of this `Group` + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn delimiter(&self) -> Delimiter { + self.0.delimiter + } + + /// Returns the `TokenStream` of tokens that are delimited in this `Group`. + /// + /// Note that the returned token stream does not include the delimiter + /// returned above. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn stream(&self) -> TokenStream { + TokenStream(self.0.stream.clone()) + } + + /// Returns the span for the delimiters of this token stream, spanning the + /// entire `Group`. + /// + /// ```text + /// pub fn span(&self) -> Span { + /// ^^^^^^^ + /// ``` + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn span(&self) -> Span { + Span(self.0.span.entire) + } + + /// Returns the span pointing to the opening delimiter of this group. + /// + /// ```text + /// pub fn span_open(&self) -> Span { + /// ^ + /// ``` + #[stable(feature = "proc_macro_group_span", since = "1.55.0")] + pub fn span_open(&self) -> Span { + Span(self.0.span.open) + } + + /// Returns the span pointing to the closing delimiter of this group. + /// + /// ```text + /// pub fn span_close(&self) -> Span { + /// ^ + /// ``` + #[stable(feature = "proc_macro_group_span", since = "1.55.0")] + pub fn span_close(&self) -> Span { + Span(self.0.span.close) + } + + /// Configures the span for this `Group`'s delimiters, but not its internal + /// tokens. + /// + /// This method will **not** set the span of all the internal tokens spanned + /// by this group, but rather it will only set the span of the delimiter + /// tokens at the level of the `Group`. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn set_span(&mut self, span: Span) { + self.0.span = bridge::DelimSpan::from_single(span.0); + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl ToString for Group { + fn to_string(&self) -> String { + TokenStream::from(TokenTree::from(self.clone())).to_string() + } +} + +/// Prints the group as a string that should be losslessly convertible back +/// into the same group (modulo spans), except for possibly `TokenTree::Group`s +/// with `Delimiter::None` delimiters. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Display for Group { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_string()) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Debug for Group { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Group") + .field("delimiter", &self.delimiter()) + .field("stream", &self.stream()) + .field("span", &self.span()) + .finish() + } +} + +/// A `Punct` is a single punctuation character such as `+`, `-` or `#`. +/// +/// Multi-character operators like `+=` are represented as two instances of `Punct` with different +/// forms of `Spacing` returned. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +#[derive(Clone)] +pub struct Punct(bridge::Punct<bridge::client::Span>); + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Send for Punct {} +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Sync for Punct {} + +/// Describes whether a `Punct` is followed immediately by another `Punct` ([`Spacing::Joint`]) or +/// by a different token or whitespace ([`Spacing::Alone`]). +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +pub enum Spacing { + /// A `Punct` is not immediately followed by another `Punct`. + /// E.g. `+` is `Alone` in `+ =`, `+ident` and `+()`. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Alone, + /// A `Punct` is immediately followed by another `Punct`. + /// E.g. `+` is `Joint` in `+=` and `++`. + /// + /// Additionally, single quote `'` can join with identifiers to form lifetimes: `'ident`. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Joint, +} + +impl Punct { + /// Creates a new `Punct` from the given character and spacing. + /// The `ch` argument must be a valid punctuation character permitted by the language, + /// otherwise the function will panic. + /// + /// The returned `Punct` will have the default span of `Span::call_site()` + /// which can be further configured with the `set_span` method below. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn new(ch: char, spacing: Spacing) -> Punct { + const LEGAL_CHARS: &[char] = &[ + '=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^', '&', '|', '@', '.', ',', ';', + ':', '#', '$', '?', '\'', + ]; + if !LEGAL_CHARS.contains(&ch) { + panic!("unsupported character `{:?}`", ch); + } + Punct(bridge::Punct { + ch: ch as u8, + joint: spacing == Spacing::Joint, + span: Span::call_site().0, + }) + } + + /// Returns the value of this punctuation character as `char`. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn as_char(&self) -> char { + self.0.ch as char + } + + /// Returns the spacing of this punctuation character, indicating whether it's immediately + /// followed by another `Punct` in the token stream, so they can potentially be combined into + /// a multi-character operator (`Joint`), or it's followed by some other token or whitespace + /// (`Alone`) so the operator has certainly ended. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn spacing(&self) -> Spacing { + if self.0.joint { Spacing::Joint } else { Spacing::Alone } + } + + /// Returns the span for this punctuation character. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn span(&self) -> Span { + Span(self.0.span) + } + + /// Configure the span for this punctuation character. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn set_span(&mut self, span: Span) { + self.0.span = span.0; + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl ToString for Punct { + fn to_string(&self) -> String { + self.as_char().to_string() + } +} + +/// Prints the punctuation character as a string that should be losslessly convertible +/// back into the same character. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Display for Punct { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_char()) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Debug for Punct { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Punct") + .field("ch", &self.as_char()) + .field("spacing", &self.spacing()) + .field("span", &self.span()) + .finish() + } +} + +#[stable(feature = "proc_macro_punct_eq", since = "1.50.0")] +impl PartialEq<char> for Punct { + fn eq(&self, rhs: &char) -> bool { + self.as_char() == *rhs + } +} + +#[stable(feature = "proc_macro_punct_eq_flipped", since = "1.52.0")] +impl PartialEq<Punct> for char { + fn eq(&self, rhs: &Punct) -> bool { + *self == rhs.as_char() + } +} + +/// An identifier (`ident`). +#[derive(Clone)] +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +pub struct Ident(bridge::Ident<bridge::client::Span, bridge::client::Symbol>); + +impl Ident { + /// Creates a new `Ident` with the given `string` as well as the specified + /// `span`. + /// The `string` argument must be a valid identifier permitted by the + /// language (including keywords, e.g. `self` or `fn`). Otherwise, the function will panic. + /// + /// Note that `span`, currently in rustc, configures the hygiene information + /// for this identifier. + /// + /// As of this time `Span::call_site()` explicitly opts-in to "call-site" hygiene + /// meaning that identifiers created with this span will be resolved as if they were written + /// directly at the location of the macro call, and other code at the macro call site will be + /// able to refer to them as well. + /// + /// Later spans like `Span::def_site()` will allow to opt-in to "definition-site" hygiene + /// meaning that identifiers created with this span will be resolved at the location of the + /// macro definition and other code at the macro call site will not be able to refer to them. + /// + /// Due to the current importance of hygiene this constructor, unlike other + /// tokens, requires a `Span` to be specified at construction. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn new(string: &str, span: Span) -> Ident { + Ident(bridge::Ident { + sym: bridge::client::Symbol::new_ident(string, false), + is_raw: false, + span: span.0, + }) + } + + /// Same as `Ident::new`, but creates a raw identifier (`r#ident`). + /// The `string` argument be a valid identifier permitted by the language + /// (including keywords, e.g. `fn`). Keywords which are usable in path segments + /// (e.g. `self`, `super`) are not supported, and will cause a panic. + #[stable(feature = "proc_macro_raw_ident", since = "1.47.0")] + pub fn new_raw(string: &str, span: Span) -> Ident { + Ident(bridge::Ident { + sym: bridge::client::Symbol::new_ident(string, true), + is_raw: true, + span: span.0, + }) + } + + /// Returns the span of this `Ident`, encompassing the entire string returned + /// by [`to_string`](ToString::to_string). + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn span(&self) -> Span { + Span(self.0.span) + } + + /// Configures the span of this `Ident`, possibly changing its hygiene context. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn set_span(&mut self, span: Span) { + self.0.span = span.0; + } +} + +/// Converts the identifier to a string that should be losslessly convertible +/// back into the same identifier. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl ToString for Ident { + fn to_string(&self) -> String { + self.0.sym.with(|sym| if self.0.is_raw { ["r#", sym].concat() } else { sym.to_owned() }) + } +} + +/// Prints the identifier as a string that should be losslessly convertible back +/// into the same identifier. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Display for Ident { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.0.is_raw { + f.write_str("r#")?; + } + fmt::Display::fmt(&self.0.sym, f) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Debug for Ident { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Ident") + .field("ident", &self.to_string()) + .field("span", &self.span()) + .finish() + } +} + +/// A literal string (`"hello"`), byte string (`b"hello"`), +/// character (`'a'`), byte character (`b'a'`), an integer or floating point number +/// with or without a suffix (`1`, `1u8`, `2.3`, `2.3f32`). +/// Boolean literals like `true` and `false` do not belong here, they are `Ident`s. +#[derive(Clone)] +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +pub struct Literal(bridge::Literal<bridge::client::Span, bridge::client::Symbol>); + +macro_rules! suffixed_int_literals { + ($($name:ident => $kind:ident,)*) => ($( + /// Creates a new suffixed integer literal with the specified value. + /// + /// This function will create an integer like `1u32` where the integer + /// value specified is the first part of the token and the integral is + /// also suffixed at the end. + /// Literals created from negative numbers might not survive round-trips through + /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). + /// + /// Literals created through this method have the `Span::call_site()` + /// span by default, which can be configured with the `set_span` method + /// below. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn $name(n: $kind) -> Literal { + Literal(bridge::Literal { + kind: bridge::LitKind::Integer, + symbol: bridge::client::Symbol::new(&n.to_string()), + suffix: Some(bridge::client::Symbol::new(stringify!($kind))), + span: Span::call_site().0, + }) + } + )*) +} + +macro_rules! unsuffixed_int_literals { + ($($name:ident => $kind:ident,)*) => ($( + /// Creates a new unsuffixed integer literal with the specified value. + /// + /// This function will create an integer like `1` where the integer + /// value specified is the first part of the token. No suffix is + /// specified on this token, meaning that invocations like + /// `Literal::i8_unsuffixed(1)` are equivalent to + /// `Literal::u32_unsuffixed(1)`. + /// Literals created from negative numbers might not survive rountrips through + /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). + /// + /// Literals created through this method have the `Span::call_site()` + /// span by default, which can be configured with the `set_span` method + /// below. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn $name(n: $kind) -> Literal { + Literal(bridge::Literal { + kind: bridge::LitKind::Integer, + symbol: bridge::client::Symbol::new(&n.to_string()), + suffix: None, + span: Span::call_site().0, + }) + } + )*) +} + +impl Literal { + fn new(kind: bridge::LitKind, value: &str, suffix: Option<&str>) -> Self { + Literal(bridge::Literal { + kind, + symbol: bridge::client::Symbol::new(value), + suffix: suffix.map(bridge::client::Symbol::new), + span: Span::call_site().0, + }) + } + + suffixed_int_literals! { + u8_suffixed => u8, + u16_suffixed => u16, + u32_suffixed => u32, + u64_suffixed => u64, + u128_suffixed => u128, + usize_suffixed => usize, + i8_suffixed => i8, + i16_suffixed => i16, + i32_suffixed => i32, + i64_suffixed => i64, + i128_suffixed => i128, + isize_suffixed => isize, + } + + unsuffixed_int_literals! { + u8_unsuffixed => u8, + u16_unsuffixed => u16, + u32_unsuffixed => u32, + u64_unsuffixed => u64, + u128_unsuffixed => u128, + usize_unsuffixed => usize, + i8_unsuffixed => i8, + i16_unsuffixed => i16, + i32_unsuffixed => i32, + i64_unsuffixed => i64, + i128_unsuffixed => i128, + isize_unsuffixed => isize, + } + + /// Creates a new unsuffixed floating-point literal. + /// + /// This constructor is similar to those like `Literal::i8_unsuffixed` where + /// the float's value is emitted directly into the token but no suffix is + /// used, so it may be inferred to be a `f64` later in the compiler. + /// Literals created from negative numbers might not survive rountrips through + /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). + /// + /// # Panics + /// + /// This function requires that the specified float is finite, for + /// example if it is infinity or NaN this function will panic. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn f32_unsuffixed(n: f32) -> Literal { + if !n.is_finite() { + panic!("Invalid float literal {n}"); + } + let mut repr = n.to_string(); + if !repr.contains('.') { + repr.push_str(".0"); + } + Literal::new(bridge::LitKind::Float, &repr, None) + } + + /// Creates a new suffixed floating-point literal. + /// + /// This constructor will create a literal like `1.0f32` where the value + /// specified is the preceding part of the token and `f32` is the suffix of + /// the token. This token will always be inferred to be an `f32` in the + /// compiler. + /// Literals created from negative numbers might not survive rountrips through + /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). + /// + /// # Panics + /// + /// This function requires that the specified float is finite, for + /// example if it is infinity or NaN this function will panic. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn f32_suffixed(n: f32) -> Literal { + if !n.is_finite() { + panic!("Invalid float literal {n}"); + } + Literal::new(bridge::LitKind::Float, &n.to_string(), Some("f32")) + } + + /// Creates a new unsuffixed floating-point literal. + /// + /// This constructor is similar to those like `Literal::i8_unsuffixed` where + /// the float's value is emitted directly into the token but no suffix is + /// used, so it may be inferred to be a `f64` later in the compiler. + /// Literals created from negative numbers might not survive rountrips through + /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). + /// + /// # Panics + /// + /// This function requires that the specified float is finite, for + /// example if it is infinity or NaN this function will panic. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn f64_unsuffixed(n: f64) -> Literal { + if !n.is_finite() { + panic!("Invalid float literal {n}"); + } + let mut repr = n.to_string(); + if !repr.contains('.') { + repr.push_str(".0"); + } + Literal::new(bridge::LitKind::Float, &repr, None) + } + + /// Creates a new suffixed floating-point literal. + /// + /// This constructor will create a literal like `1.0f64` where the value + /// specified is the preceding part of the token and `f64` is the suffix of + /// the token. This token will always be inferred to be an `f64` in the + /// compiler. + /// Literals created from negative numbers might not survive rountrips through + /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). + /// + /// # Panics + /// + /// This function requires that the specified float is finite, for + /// example if it is infinity or NaN this function will panic. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn f64_suffixed(n: f64) -> Literal { + if !n.is_finite() { + panic!("Invalid float literal {n}"); + } + Literal::new(bridge::LitKind::Float, &n.to_string(), Some("f64")) + } + + /// String literal. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn string(string: &str) -> Literal { + let quoted = format!("{:?}", string); + assert!(quoted.starts_with('"') && quoted.ends_with('"')); + let symbol = "ed[1..quoted.len() - 1]; + Literal::new(bridge::LitKind::Str, symbol, None) + } + + /// Character literal. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn character(ch: char) -> Literal { + let quoted = format!("{:?}", ch); + assert!(quoted.starts_with('\'') && quoted.ends_with('\'')); + let symbol = "ed[1..quoted.len() - 1]; + Literal::new(bridge::LitKind::Char, symbol, None) + } + + /// Byte string literal. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn byte_string(bytes: &[u8]) -> Literal { + let string = bytes + .iter() + .cloned() + .flat_map(std::ascii::escape_default) + .map(Into::<char>::into) + .collect::<String>(); + Literal::new(bridge::LitKind::ByteStr, &string, None) + } + + /// Returns the span encompassing this literal. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn span(&self) -> Span { + Span(self.0.span) + } + + /// Configures the span associated for this literal. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn set_span(&mut self, span: Span) { + self.0.span = span.0; + } + + /// Returns a `Span` that is a subset of `self.span()` containing only the + /// source bytes in range `range`. Returns `None` if the would-be trimmed + /// span is outside the bounds of `self`. + // FIXME(SergioBenitez): check that the byte range starts and ends at a + // UTF-8 boundary of the source. otherwise, it's likely that a panic will + // occur elsewhere when the source text is printed. + // FIXME(SergioBenitez): there is no way for the user to know what + // `self.span()` actually maps to, so this method can currently only be + // called blindly. For example, `to_string()` for the character 'c' returns + // "'\u{63}'"; there is no way for the user to know whether the source text + // was 'c' or whether it was '\u{63}'. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span> { + self.0.span.subspan(range.start_bound().cloned(), range.end_bound().cloned()).map(Span) + } + + fn with_symbol_and_suffix<R>(&self, f: impl FnOnce(&str, &str) -> R) -> R { + self.0.symbol.with(|symbol| match self.0.suffix { + Some(suffix) => suffix.with(|suffix| f(symbol, suffix)), + None => f(symbol, ""), + }) + } + + /// Invokes the callback with a `&[&str]` consisting of each part of the + /// literal's representation. This is done to allow the `ToString` and + /// `Display` implementations to borrow references to symbol values, and + /// both be optimized to reduce overhead. + fn with_stringify_parts<R>(&self, f: impl FnOnce(&[&str]) -> R) -> R { + /// Returns a string containing exactly `num` '#' characters. + /// Uses a 256-character source string literal which is always safe to + /// index with a `u8` index. + fn get_hashes_str(num: u8) -> &'static str { + const HASHES: &str = "\ + ################################################################\ + ################################################################\ + ################################################################\ + ################################################################\ + "; + const _: () = assert!(HASHES.len() == 256); + &HASHES[..num as usize] + } + + self.with_symbol_and_suffix(|symbol, suffix| match self.0.kind { + bridge::LitKind::Byte => f(&["b'", symbol, "'", suffix]), + bridge::LitKind::Char => f(&["'", symbol, "'", suffix]), + bridge::LitKind::Str => f(&["\"", symbol, "\"", suffix]), + bridge::LitKind::StrRaw(n) => { + let hashes = get_hashes_str(n); + f(&["r", hashes, "\"", symbol, "\"", hashes, suffix]) + } + bridge::LitKind::ByteStr => f(&["b\"", symbol, "\"", suffix]), + bridge::LitKind::ByteStrRaw(n) => { + let hashes = get_hashes_str(n); + f(&["br", hashes, "\"", symbol, "\"", hashes, suffix]) + } + _ => f(&[symbol, suffix]), + }) + } +} + +/// Parse a single literal from its stringified representation. +/// +/// In order to parse successfully, the input string must not contain anything +/// but the literal token. Specifically, it must not contain whitespace or +/// comments in addition to the literal. +/// +/// The resulting literal token will have a `Span::call_site()` span. +/// +/// NOTE: some errors may cause panics instead of returning `LexError`. We +/// reserve the right to change these errors into `LexError`s later. +#[stable(feature = "proc_macro_literal_parse", since = "1.54.0")] +impl FromStr for Literal { + type Err = LexError; + + fn from_str(src: &str) -> Result<Self, LexError> { + match bridge::client::FreeFunctions::literal_from_str(src) { + Ok(literal) => Ok(Literal(literal)), + Err(()) => Err(LexError), + } + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl ToString for Literal { + fn to_string(&self) -> String { + self.with_stringify_parts(|parts| parts.concat()) + } +} + +/// Prints the literal as a string that should be losslessly convertible +/// back into the same literal (except for possible rounding for floating point literals). +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Display for Literal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.with_stringify_parts(|parts| { + for part in parts { + fmt::Display::fmt(part, f)?; + } + Ok(()) + }) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Debug for Literal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Literal") + // format the kind on one line even in {:#?} mode + .field("kind", &format_args!("{:?}", &self.0.kind)) + .field("symbol", &self.0.symbol) + // format `Some("...")` on one line even in {:#?} mode + .field("suffix", &format_args!("{:?}", &self.0.suffix)) + .field("span", &self.0.span) + .finish() + } +} + +/// Tracked access to environment variables. +#[unstable(feature = "proc_macro_tracked_env", issue = "99515")] +pub mod tracked_env { + use std::env::{self, VarError}; + use std::ffi::OsStr; + + /// Retrieve an environment variable and add it to build dependency info. + /// Build system executing the compiler will know that the variable was accessed during + /// compilation, and will be able to rerun the build when the value of that variable changes. + /// Besides the dependency tracking this function should be equivalent to `env::var` from the + /// standard library, except that the argument must be UTF-8. + #[unstable(feature = "proc_macro_tracked_env", issue = "99515")] + pub fn var<K: AsRef<OsStr> + AsRef<str>>(key: K) -> Result<String, VarError> { + let key: &str = key.as_ref(); + let value = env::var(key); + crate::bridge::client::FreeFunctions::track_env_var(key, value.as_deref().ok()); + value + } +} + +/// Tracked access to additional files. +#[unstable(feature = "track_path", issue = "99515")] +pub mod tracked_path { + + /// Track a file explicitly. + /// + /// Commonly used for tracking asset preprocessing. + #[unstable(feature = "track_path", issue = "99515")] + pub fn path<P: AsRef<str>>(path: P) { + let path: &str = path.as_ref(); + crate::bridge::client::FreeFunctions::track_path(path); + } +} diff --git a/library/proc_macro/src/quote.rs b/library/proc_macro/src/quote.rs new file mode 100644 index 000000000..04fa696d5 --- /dev/null +++ b/library/proc_macro/src/quote.rs @@ -0,0 +1,141 @@ +//! # Quasiquoter +//! This file contains the implementation internals of the quasiquoter provided by `quote!`. + +//! This quasiquoter uses macros 2.0 hygiene to reliably access +//! items from `proc_macro`, to build a `proc_macro::TokenStream`. + +use crate::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; + +macro_rules! quote_tt { + (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) }; + ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) }; + ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) }; + (,) => { Punct::new(',', Spacing::Alone) }; + (.) => { Punct::new('.', Spacing::Alone) }; + (;) => { Punct::new(';', Spacing::Alone) }; + (!) => { Punct::new('!', Spacing::Alone) }; + (<) => { Punct::new('<', Spacing::Alone) }; + (>) => { Punct::new('>', Spacing::Alone) }; + (&) => { Punct::new('&', Spacing::Alone) }; + (=) => { Punct::new('=', Spacing::Alone) }; + ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) }; +} + +macro_rules! quote_ts { + ((@ $($t:tt)*)) => { $($t)* }; + (::) => { + [ + TokenTree::from(Punct::new(':', Spacing::Joint)), + TokenTree::from(Punct::new(':', Spacing::Alone)), + ].iter() + .cloned() + .map(|mut x| { + x.set_span(Span::def_site()); + x + }) + .collect::<TokenStream>() + }; + ($t:tt) => { TokenTree::from(quote_tt!($t)) }; +} + +/// Simpler version of the real `quote!` macro, implemented solely +/// through `macro_rules`, for bootstrapping the real implementation +/// (see the `quote` function), which does not have access to the +/// real `quote!` macro due to the `proc_macro` crate not being +/// able to depend on itself. +/// +/// Note: supported tokens are a subset of the real `quote!`, but +/// unquoting is different: instead of `$x`, this uses `(@ expr)`. +macro_rules! quote { + () => { TokenStream::new() }; + ($($t:tt)*) => { + [ + $(TokenStream::from(quote_ts!($t)),)* + ].iter().cloned().collect::<TokenStream>() + }; +} + +/// Quote a `TokenStream` into a `TokenStream`. +/// This is the actual implementation of the `quote!()` proc macro. +/// +/// It is loaded by the compiler in `register_builtin_macros`. +#[unstable(feature = "proc_macro_quote", issue = "54722")] +pub fn quote(stream: TokenStream) -> TokenStream { + if stream.is_empty() { + return quote!(crate::TokenStream::new()); + } + let proc_macro_crate = quote!(crate); + let mut after_dollar = false; + let tokens = stream + .into_iter() + .filter_map(|tree| { + if after_dollar { + after_dollar = false; + match tree { + TokenTree::Ident(_) => { + return Some(quote!(Into::<crate::TokenStream>::into( + Clone::clone(&(@ tree))),)); + } + TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} + _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), + } + } else if let TokenTree::Punct(ref tt) = tree { + if tt.as_char() == '$' { + after_dollar = true; + return None; + } + } + + Some(quote!(crate::TokenStream::from((@ match tree { + TokenTree::Punct(tt) => quote!(crate::TokenTree::Punct(crate::Punct::new( + (@ TokenTree::from(Literal::character(tt.as_char()))), + (@ match tt.spacing() { + Spacing::Alone => quote!(crate::Spacing::Alone), + Spacing::Joint => quote!(crate::Spacing::Joint), + }), + ))), + TokenTree::Group(tt) => quote!(crate::TokenTree::Group(crate::Group::new( + (@ match tt.delimiter() { + Delimiter::Parenthesis => quote!(crate::Delimiter::Parenthesis), + Delimiter::Brace => quote!(crate::Delimiter::Brace), + Delimiter::Bracket => quote!(crate::Delimiter::Bracket), + Delimiter::None => quote!(crate::Delimiter::None), + }), + (@ quote(tt.stream())), + ))), + TokenTree::Ident(tt) => quote!(crate::TokenTree::Ident(crate::Ident::new( + (@ TokenTree::from(Literal::string(&tt.to_string()))), + (@ quote_span(proc_macro_crate.clone(), tt.span())), + ))), + TokenTree::Literal(tt) => quote!(crate::TokenTree::Literal({ + let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string()))) + .parse::<crate::TokenStream>() + .unwrap() + .into_iter(); + if let (Some(crate::TokenTree::Literal(mut lit)), None) = + (iter.next(), iter.next()) + { + lit.set_span((@ quote_span(proc_macro_crate.clone(), tt.span()))); + lit + } else { + unreachable!() + } + })) + })),)) + }) + .collect::<TokenStream>(); + + if after_dollar { + panic!("unexpected trailing `$` in `quote!`"); + } + + quote!([(@ tokens)].iter().cloned().collect::<crate::TokenStream>()) +} + +/// Quote a `Span` into a `TokenStream`. +/// This is needed to implement a custom quoter. +#[unstable(feature = "proc_macro_quote", issue = "54722")] +pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream { + let id = span.save_span(); + quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id))))) +} |