Merge #9806
9806: Add proc_macro crate for the 1.56 ABI r=lnicola a=alexjg I've copied the latest proc macro source from Rust nightly and modified it to compile on `stable`. This fixes #9795 . Almost everything here is uninteresting copy and paste, the interesting stuff is in `crates/proc_macro_srv/src/abis/mod.rs`. I've left the 1.55 ABI implementation in for now. We did discuss only supporting one nightly ABI so we may want to remove 1.55. That will break code which is pinned to older nightly releases but that seems acceptable to me, what do people think? Co-authored-by: Alex Good <alex@memoryandthought.me>
This commit is contained in:
commit
c5bde08f6e
crates/proc_macro_srv/src/abis
104
crates/proc_macro_srv/src/abis/abi_1_56/mod.rs
Normal file
104
crates/proc_macro_srv/src/abis/abi_1_56/mod.rs
Normal file
@ -0,0 +1,104 @@
|
||||
//! Macro ABI for version 1.56 of rustc
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[doc(hidden)]
|
||||
mod proc_macro;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[doc(hidden)]
|
||||
mod rustc_server;
|
||||
use libloading::Library;
|
||||
|
||||
use proc_macro_api::ProcMacroKind;
|
||||
|
||||
use super::PanicMessage;
|
||||
|
||||
pub(crate) struct Abi {
|
||||
exported_macros: Vec<proc_macro::bridge::client::ProcMacro>,
|
||||
}
|
||||
|
||||
impl From<proc_macro::bridge::PanicMessage> for PanicMessage {
|
||||
fn from(p: proc_macro::bridge::PanicMessage) -> Self {
|
||||
Self { message: p.as_str().map(|s| s.to_string()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Abi {
|
||||
pub unsafe fn from_lib(lib: &Library, symbol_name: String) -> Result<Abi, libloading::Error> {
|
||||
let macros: libloading::Symbol<&&[proc_macro::bridge::client::ProcMacro]> =
|
||||
lib.get(symbol_name.as_bytes())?;
|
||||
Ok(Self { exported_macros: macros.to_vec() })
|
||||
}
|
||||
|
||||
pub fn expand(
|
||||
&self,
|
||||
macro_name: &str,
|
||||
macro_body: &tt::Subtree,
|
||||
attributes: Option<&tt::Subtree>,
|
||||
) -> Result<tt::Subtree, PanicMessage> {
|
||||
let parsed_body = rustc_server::TokenStream::with_subtree(macro_body.clone());
|
||||
|
||||
let parsed_attributes = attributes.map_or(rustc_server::TokenStream::new(), |attr| {
|
||||
rustc_server::TokenStream::with_subtree(attr.clone())
|
||||
});
|
||||
|
||||
for proc_macro in &self.exported_macros {
|
||||
match proc_macro {
|
||||
proc_macro::bridge::client::ProcMacro::CustomDerive {
|
||||
trait_name, client, ..
|
||||
} if *trait_name == macro_name => {
|
||||
let res = client.run(
|
||||
&proc_macro::bridge::server::SameThread,
|
||||
rustc_server::Rustc::default(),
|
||||
parsed_body,
|
||||
false,
|
||||
);
|
||||
return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
|
||||
}
|
||||
proc_macro::bridge::client::ProcMacro::Bang { name, client }
|
||||
if *name == macro_name =>
|
||||
{
|
||||
let res = client.run(
|
||||
&proc_macro::bridge::server::SameThread,
|
||||
rustc_server::Rustc::default(),
|
||||
parsed_body,
|
||||
false,
|
||||
);
|
||||
return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
|
||||
}
|
||||
proc_macro::bridge::client::ProcMacro::Attr { name, client }
|
||||
if *name == macro_name =>
|
||||
{
|
||||
let res = client.run(
|
||||
&proc_macro::bridge::server::SameThread,
|
||||
rustc_server::Rustc::default(),
|
||||
parsed_attributes,
|
||||
parsed_body,
|
||||
false,
|
||||
);
|
||||
return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into())
|
||||
}
|
||||
|
||||
pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
|
||||
self.exported_macros
|
||||
.iter()
|
||||
.map(|proc_macro| match proc_macro {
|
||||
proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
|
||||
(trait_name.to_string(), ProcMacroKind::CustomDerive)
|
||||
}
|
||||
proc_macro::bridge::client::ProcMacro::Bang { name, .. } => {
|
||||
(name.to_string(), ProcMacroKind::FuncLike)
|
||||
}
|
||||
proc_macro::bridge::client::ProcMacro::Attr { name, .. } => {
|
||||
(name.to_string(), ProcMacroKind::Attr)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
//! 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<T: Copy> {
|
||||
data: *mut T,
|
||||
len: usize,
|
||||
capacity: usize,
|
||||
reserve: extern "C" fn(Buffer<T>, usize) -> Buffer<T>,
|
||||
drop: extern "C" fn(Buffer<T>),
|
||||
}
|
||||
|
||||
unsafe impl<T: Copy + Sync> Sync for Buffer<T> {}
|
||||
unsafe impl<T: Copy + Send> Send for Buffer<T> {}
|
||||
|
||||
impl<T: Copy> Default for Buffer<T> {
|
||||
fn default() -> Self {
|
||||
Self::from(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> Deref for Buffer<T> {
|
||||
type Target = [T];
|
||||
fn deref(&self) -> &[T] {
|
||||
unsafe { slice::from_raw_parts(self.data as *const T, self.len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> DerefMut for Buffer<T> {
|
||||
fn deref_mut(&mut self) -> &mut [T] {
|
||||
unsafe { slice::from_raw_parts_mut(self.data, self.len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> Buffer<T> {
|
||||
pub(super) fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub(super) fn clear(&mut self) {
|
||||
self.len = 0;
|
||||
}
|
||||
|
||||
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.
|
||||
pub(super) fn extend_from_array<const N: usize>(&mut self, xs: &[T; 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();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn extend_from_slice(&mut self, xs: &[T]) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn push(&mut self, v: T) {
|
||||
// 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<u8> {
|
||||
fn write(&mut self, xs: &[u8]) -> io::Result<usize> {
|
||||
self.extend_from_slice(xs);
|
||||
Ok(xs.len())
|
||||
}
|
||||
|
||||
fn write_all(&mut self, xs: &[u8]) -> io::Result<()> {
|
||||
self.extend_from_slice(xs);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> Drop for Buffer<T> {
|
||||
fn drop(&mut self) {
|
||||
let b = self.take();
|
||||
(b.drop)(b);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> From<Vec<T>> for Buffer<T> {
|
||||
fn from(mut v: Vec<T>) -> 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<T: Copy>(b: Buffer<T>) -> Vec<T> {
|
||||
unsafe {
|
||||
let Buffer { data, len, capacity, .. } = b;
|
||||
mem::forget(b);
|
||||
Vec::from_raw_parts(data, len, capacity)
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn reserve<T: Copy>(b: Buffer<T>, additional: usize) -> Buffer<T> {
|
||||
let mut v = to_vec(b);
|
||||
v.reserve(additional);
|
||||
Buffer::from(v)
|
||||
}
|
||||
|
||||
extern "C" fn drop<T: Copy>(b: Buffer<T>) {
|
||||
mem::drop(to_vec(b));
|
||||
}
|
||||
|
||||
Buffer { data, len, capacity, reserve, drop }
|
||||
}
|
||||
}
|
@ -0,0 +1,485 @@
|
||||
//! Client-side types.
|
||||
|
||||
use super::*;
|
||||
|
||||
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`.
|
||||
#[repr(C)]
|
||||
#[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),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
#[repr(C)]
|
||||
pub(crate) struct $oty(handle::Handle);
|
||||
|
||||
// Forward `Drop::drop` to the inherent `drop` method.
|
||||
impl Drop for $oty {
|
||||
fn drop(&mut self) {
|
||||
$oty(self.0).drop();
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Encode<S> for $oty {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
let handle = self.0;
|
||||
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.0.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.0.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::decode(r, s))
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
$(
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct $ity(handle::Handle);
|
||||
|
||||
impl<S> Encode<S> for $ity {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
self.0.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::decode(r, s))
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
define_handles! {
|
||||
'owned:
|
||||
FreeFunctions,
|
||||
TokenStream,
|
||||
TokenStreamBuilder,
|
||||
TokenStreamIter,
|
||||
Group,
|
||||
Literal,
|
||||
SourceFile,
|
||||
MultiSpan,
|
||||
Diagnostic,
|
||||
|
||||
'interned:
|
||||
Punct,
|
||||
Ident,
|
||||
Span,
|
||||
}
|
||||
|
||||
// FIXME(eddyb) generate these impls by pattern-matching on the
|
||||
// names of methods - also could use the presence of `fn drop`
|
||||
// to distinguish between 'owned and 'interned, above.
|
||||
// Alternatively, special 'modes" could be listed of types in with_api
|
||||
// instead of pattern matching on methods, here and in server decl.
|
||||
|
||||
impl Clone for TokenStream {
|
||||
fn clone(&self) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for TokenStreamIter {
|
||||
fn clone(&self) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Group {
|
||||
fn clone(&self) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Literal {
|
||||
fn clone(&self) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Literal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Literal")
|
||||
// format the kind without quotes, as in `kind: Float`
|
||||
.field("kind", &format_args!("{}", &self.debug_kind()))
|
||||
.field("symbol", &self.symbol())
|
||||
// format `Some("...")` on one line even in {:#?} mode
|
||||
.field("suffix", &format_args!("{:?}", &self.suffix()))
|
||||
.field("span", &self.span())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for SourceFile {
|
||||
fn clone(&self) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Span {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&self.debug())
|
||||
}
|
||||
}
|
||||
|
||||
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 b = bridge.cached_buffer.take();
|
||||
|
||||
b.clear();
|
||||
api_tags::Method::$name(api_tags::$name::$method).encode(&mut b, &mut ());
|
||||
reverse_encode!(b; $($arg),*);
|
||||
|
||||
b = bridge.dispatch.call(b);
|
||||
|
||||
let r = Result::<_, PanicMessage>::decode(&mut &b[..], &mut ());
|
||||
|
||||
bridge.cached_buffer = b;
|
||||
|
||||
r.unwrap_or_else(|e| panic::resume_unwind(e.into()))
|
||||
})
|
||||
})*
|
||||
})*
|
||||
}
|
||||
}
|
||||
with_api!(self, self, define_client_side);
|
||||
|
||||
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<'_> {
|
||||
pub(crate) fn is_available() -> bool {
|
||||
BridgeState::with(|state| match state {
|
||||
BridgeState::Connected(_) | BridgeState::InUse => true,
|
||||
BridgeState::NotConnected => false,
|
||||
})
|
||||
}
|
||||
|
||||
fn enter<R>(self, f: impl FnOnce() -> R) -> R {
|
||||
let force_show_panics = self.force_show_panics;
|
||||
// Hide the default panic output within `proc_macro` expansions.
|
||||
// NB. the server can't do this because it may use a different libstd.
|
||||
static HIDE_PANICS_DURING_EXPANSION: Once = Once::new();
|
||||
HIDE_PANICS_DURING_EXPANSION.call_once(|| {
|
||||
let prev = panic::take_hook();
|
||||
panic::set_hook(Box::new(move |info| {
|
||||
let show = BridgeState::with(|state| match state {
|
||||
BridgeState::NotConnected => true,
|
||||
BridgeState::Connected(_) | BridgeState::InUse => force_show_panics,
|
||||
});
|
||||
if show {
|
||||
prev(info)
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
BRIDGE_STATE.with(|state| state.set(BridgeState::Connected(self), f))
|
||||
}
|
||||
|
||||
fn with<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),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A client-side "global object" (usually a function pointer),
|
||||
/// which may be using a different `proc_macro` from the one
|
||||
/// used by the server, but can be interacted with compatibly.
|
||||
///
|
||||
/// N.B., `F` must have FFI-friendly memory layout (e.g., a pointer).
|
||||
/// The call ABI of function pointers used for `F` doesn't
|
||||
/// need to match between server and client, since it's only
|
||||
/// passed between them and (eventually) called by the client.
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Client<F> {
|
||||
// FIXME(eddyb) use a reference to the `static COUNTERS`, instead of
|
||||
// a wrapper `fn` pointer, once `const fn` can reference `static`s.
|
||||
pub(super) get_handle_counters: extern "C" fn() -> &'static HandleCounters,
|
||||
pub(super) run: extern "C" fn(Bridge<'_>, F) -> Buffer<u8>,
|
||||
pub(super) f: F,
|
||||
}
|
||||
|
||||
/// Client-side helper for handling client panics, entering the bridge,
|
||||
/// deserializing input and serializing output.
|
||||
// FIXME(eddyb) maybe replace `Bridge::enter` with this?
|
||||
fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
|
||||
mut bridge: Bridge<'_>,
|
||||
f: impl FnOnce(A) -> R,
|
||||
) -> Buffer<u8> {
|
||||
// The initial `cached_buffer` contains the input.
|
||||
let mut b = bridge.cached_buffer.take();
|
||||
|
||||
panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
bridge.enter(|| {
|
||||
let reader = &mut &b[..];
|
||||
let input = A::decode(reader, &mut ());
|
||||
|
||||
// Put the `cached_buffer` back in the `Bridge`, for requests.
|
||||
Bridge::with(|bridge| bridge.cached_buffer = b.take());
|
||||
|
||||
let output = f(input);
|
||||
|
||||
// Take the `cached_buffer` back out, for the output value.
|
||||
b = Bridge::with(|bridge| bridge.cached_buffer.take());
|
||||
|
||||
// HACK(eddyb) Separate encoding a success value (`Ok(output)`)
|
||||
// from encoding a panic (`Err(e: PanicMessage)`) to avoid
|
||||
// having handles outside the `bridge.enter(|| ...)` scope, and
|
||||
// to catch panics that could happen while encoding the success.
|
||||
//
|
||||
// Note that panics should be impossible beyond this point, but
|
||||
// this is defensively trying to avoid any accidental panicking
|
||||
// reaching the `extern "C"` (which should `abort` but might not
|
||||
// at the moment, so this is also potentially preventing UB).
|
||||
b.clear();
|
||||
Ok::<_, ()>(output).encode(&mut b, &mut ());
|
||||
})
|
||||
}))
|
||||
.map_err(PanicMessage::from)
|
||||
.unwrap_or_else(|e| {
|
||||
b.clear();
|
||||
Err::<(), _>(e).encode(&mut b, &mut ());
|
||||
});
|
||||
b
|
||||
}
|
||||
|
||||
impl Client<fn(super::super::TokenStream) -> super::super::TokenStream> {
|
||||
pub fn expand1(f: fn(super::super::TokenStream) -> super::super::TokenStream) -> Self {
|
||||
extern "C" fn run(
|
||||
bridge: Bridge<'_>,
|
||||
f: impl FnOnce(super::super::TokenStream) -> super::super::TokenStream,
|
||||
) -> Buffer<u8> {
|
||||
run_client(bridge, |input| f(super::super::TokenStream(input)).0)
|
||||
}
|
||||
Client { get_handle_counters: HandleCounters::get, run, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl Client<fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream> {
|
||||
pub fn expand2(
|
||||
f: fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream,
|
||||
) -> Self {
|
||||
extern "C" fn run(
|
||||
bridge: Bridge<'_>,
|
||||
f: impl FnOnce(
|
||||
super::super::TokenStream,
|
||||
super::super::TokenStream,
|
||||
) -> super::super::TokenStream,
|
||||
) -> Buffer<u8> {
|
||||
run_client(bridge, |(input, input2)| {
|
||||
f(super::super::TokenStream(input), super::super::TokenStream(input2)).0
|
||||
})
|
||||
}
|
||||
Client { get_handle_counters: HandleCounters::get, run, f }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ProcMacro {
|
||||
CustomDerive {
|
||||
trait_name: &'static str,
|
||||
attributes: &'static [&'static str],
|
||||
client: Client<fn(super::super::TokenStream) -> super::super::TokenStream>,
|
||||
},
|
||||
|
||||
Attr {
|
||||
name: &'static str,
|
||||
client: Client<
|
||||
fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream,
|
||||
>,
|
||||
},
|
||||
|
||||
Bang {
|
||||
name: &'static str,
|
||||
client: Client<fn(super::super::TokenStream) -> super::super::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 fn custom_derive(
|
||||
trait_name: &'static str,
|
||||
attributes: &'static [&'static str],
|
||||
expand: fn(super::super::TokenStream) -> super::super::TokenStream,
|
||||
) -> Self {
|
||||
ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
|
||||
}
|
||||
|
||||
pub fn attr(
|
||||
name: &'static str,
|
||||
expand: fn(
|
||||
super::super::TokenStream,
|
||||
super::super::TokenStream,
|
||||
) -> super::super::TokenStream,
|
||||
) -> Self {
|
||||
ProcMacro::Attr { name, client: Client::expand2(expand) }
|
||||
}
|
||||
|
||||
pub fn bang(
|
||||
name: &'static str,
|
||||
expand: fn(super::super::TokenStream) -> super::super::TokenStream,
|
||||
) -> Self {
|
||||
ProcMacro::Bang { name, client: Client::expand1(expand) }
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
//! Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`.
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Closure<'a, A, R> {
|
||||
call: unsafe extern "C" fn(&mut Env, A) -> R,
|
||||
env: &'a mut Env,
|
||||
}
|
||||
|
||||
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: unsafe { &mut *(f as *mut _ as *mut Env) } }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A, R> Closure<'a, A, R> {
|
||||
pub fn call(&mut self, arg: A) -> R {
|
||||
unsafe { (self.call)(self.env, arg) }
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
//! Server-side handles and storage for per-handle data.
|
||||
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::hash::Hash;
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
pub(super) type Handle = NonZeroU32;
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct InternedStore<T: 'static> {
|
||||
owned: OwnedStore<T>,
|
||||
interner: HashMap<T, Handle>,
|
||||
}
|
||||
|
||||
impl<T: Copy + Eq + Hash> InternedStore<T> {
|
||||
pub(super) fn new(counter: &'static AtomicUsize) -> Self {
|
||||
InternedStore { owned: OwnedStore::new(counter), interner: HashMap::new() }
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
}
|
426
crates/proc_macro_srv/src/abis/abi_1_56/proc_macro/bridge/mod.rs
Normal file
426
crates/proc_macro_srv/src/abis/abi_1_56/proc_macro/bridge/mod.rs
Normal file
@ -0,0 +1,426 @@
|
||||
//! 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)]
|
||||
|
||||
pub use super::{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);
|
||||
},
|
||||
TokenStream {
|
||||
fn drop($self: $S::TokenStream);
|
||||
fn clone($self: &$S::TokenStream) -> $S::TokenStream;
|
||||
fn new() -> $S::TokenStream;
|
||||
fn is_empty($self: &$S::TokenStream) -> bool;
|
||||
fn from_str(src: &str) -> $S::TokenStream;
|
||||
fn to_string($self: &$S::TokenStream) -> String;
|
||||
fn from_token_tree(
|
||||
tree: TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>,
|
||||
) -> $S::TokenStream;
|
||||
fn into_iter($self: $S::TokenStream) -> $S::TokenStreamIter;
|
||||
},
|
||||
TokenStreamBuilder {
|
||||
fn drop($self: $S::TokenStreamBuilder);
|
||||
fn new() -> $S::TokenStreamBuilder;
|
||||
fn push($self: &mut $S::TokenStreamBuilder, stream: $S::TokenStream);
|
||||
fn build($self: $S::TokenStreamBuilder) -> $S::TokenStream;
|
||||
},
|
||||
TokenStreamIter {
|
||||
fn drop($self: $S::TokenStreamIter);
|
||||
fn clone($self: &$S::TokenStreamIter) -> $S::TokenStreamIter;
|
||||
fn next(
|
||||
$self: &mut $S::TokenStreamIter,
|
||||
) -> Option<TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>>;
|
||||
},
|
||||
Group {
|
||||
fn drop($self: $S::Group);
|
||||
fn clone($self: &$S::Group) -> $S::Group;
|
||||
fn new(delimiter: Delimiter, stream: $S::TokenStream) -> $S::Group;
|
||||
fn delimiter($self: &$S::Group) -> Delimiter;
|
||||
fn stream($self: &$S::Group) -> $S::TokenStream;
|
||||
fn span($self: &$S::Group) -> $S::Span;
|
||||
fn span_open($self: &$S::Group) -> $S::Span;
|
||||
fn span_close($self: &$S::Group) -> $S::Span;
|
||||
fn set_span($self: &mut $S::Group, span: $S::Span);
|
||||
},
|
||||
Punct {
|
||||
fn new(ch: char, spacing: Spacing) -> $S::Punct;
|
||||
fn as_char($self: $S::Punct) -> char;
|
||||
fn spacing($self: $S::Punct) -> Spacing;
|
||||
fn span($self: $S::Punct) -> $S::Span;
|
||||
fn with_span($self: $S::Punct, span: $S::Span) -> $S::Punct;
|
||||
},
|
||||
Ident {
|
||||
fn new(string: &str, span: $S::Span, is_raw: bool) -> $S::Ident;
|
||||
fn span($self: $S::Ident) -> $S::Span;
|
||||
fn with_span($self: $S::Ident, span: $S::Span) -> $S::Ident;
|
||||
},
|
||||
Literal {
|
||||
fn drop($self: $S::Literal);
|
||||
fn clone($self: &$S::Literal) -> $S::Literal;
|
||||
fn from_str(s: &str) -> Result<$S::Literal, ()>;
|
||||
fn to_string($self: &$S::Literal) -> String;
|
||||
fn debug_kind($self: &$S::Literal) -> String;
|
||||
fn symbol($self: &$S::Literal) -> String;
|
||||
fn suffix($self: &$S::Literal) -> Option<String>;
|
||||
fn integer(n: &str) -> $S::Literal;
|
||||
fn typed_integer(n: &str, kind: &str) -> $S::Literal;
|
||||
fn float(n: &str) -> $S::Literal;
|
||||
fn f32(n: &str) -> $S::Literal;
|
||||
fn f64(n: &str) -> $S::Literal;
|
||||
fn string(string: &str) -> $S::Literal;
|
||||
fn character(ch: char) -> $S::Literal;
|
||||
fn byte_string(bytes: &[u8]) -> $S::Literal;
|
||||
fn span($self: &$S::Literal) -> $S::Span;
|
||||
fn set_span($self: &mut $S::Literal, span: $S::Span);
|
||||
fn subspan(
|
||||
$self: &$S::Literal,
|
||||
start: Bound<usize>,
|
||||
end: Bound<usize>,
|
||||
) -> Option<$S::Span>;
|
||||
},
|
||||
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 def_site() -> $S::Span;
|
||||
fn call_site() -> $S::Span;
|
||||
fn mixed_site() -> $S::Span;
|
||||
fn source_file($self: $S::Span) -> $S::SourceFile;
|
||||
fn parent($self: $S::Span) -> Option<$S::Span>;
|
||||
fn source($self: $S::Span) -> $S::Span;
|
||||
fn start($self: $S::Span) -> LineColumn;
|
||||
fn end($self: $S::Span) -> LineColumn;
|
||||
fn join($self: $S::Span, other: $S::Span) -> 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;
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// FIXME(eddyb) this calls `encode` for each argument, but in reverse,
|
||||
// to avoid borrow conflicts from borrows started by `&mut` arguments.
|
||||
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 buffer;
|
||||
#[forbid(unsafe_code)]
|
||||
pub mod client;
|
||||
#[allow(unsafe_code)]
|
||||
mod closure;
|
||||
#[forbid(unsafe_code)]
|
||||
mod handle;
|
||||
#[macro_use]
|
||||
#[forbid(unsafe_code)]
|
||||
mod rpc;
|
||||
#[allow(unsafe_code)]
|
||||
mod scoped_cell;
|
||||
#[forbid(unsafe_code)]
|
||||
pub mod server;
|
||||
|
||||
use buffer::Buffer;
|
||||
pub use rpc::PanicMessage;
|
||||
use rpc::{Decode, DecodeMut, Encode, Reader, Writer};
|
||||
|
||||
/// An active connection between a server and a client.
|
||||
/// The server creates the bridge (`Bridge::run_server` in `server.rs`),
|
||||
/// then passes it to the client through the function pointer in the `run`
|
||||
/// field of `client::Client`. The client holds its copy of the `Bridge`
|
||||
/// in TLS during its execution (`Bridge::{enter, with}` in `client.rs`).
|
||||
#[repr(C)]
|
||||
pub struct Bridge<'a> {
|
||||
/// Reusable buffer (only `clear`-ed, never shrunk), primarily
|
||||
/// used for making requests, but also for passing input to client.
|
||||
cached_buffer: Buffer<u8>,
|
||||
|
||||
/// Server-side function that the client uses to make requests.
|
||||
dispatch: closure::Closure<'a, Buffer<u8>, Buffer<u8>>,
|
||||
|
||||
/// If 'true', always invoke the default panic hook
|
||||
force_show_panics: bool,
|
||||
}
|
||||
|
||||
#[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 Option<T> {
|
||||
type Unmarked = Option<T::Unmarked>;
|
||||
fn mark(unmarked: Self::Unmarked) -> Self {
|
||||
unmarked.map(T::mark)
|
||||
}
|
||||
}
|
||||
impl<T: Unmark> Unmark for Option<T> {
|
||||
type Unmarked = Option<T::Unmarked>;
|
||||
fn unmark(self) -> Self::Unmarked {
|
||||
self.map(T::unmark)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Mark, E: Mark> Mark for Result<T, E> {
|
||||
type Unmarked = Result<T::Unmarked, E::Unmarked>;
|
||||
fn mark(unmarked: Self::Unmarked) -> Self {
|
||||
unmarked.map(T::mark).map_err(E::mark)
|
||||
}
|
||||
}
|
||||
impl<T: Unmark, E: Unmark> Unmark for Result<T, E> {
|
||||
type Unmarked = Result<T::Unmarked, E::Unmarked>;
|
||||
fn unmark(self) -> Self::Unmarked {
|
||||
self.map(T::unmark).map_err(E::unmark)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
usize,
|
||||
Delimiter,
|
||||
Level,
|
||||
LineColumn,
|
||||
Spacing,
|
||||
Bound<usize>,
|
||||
}
|
||||
|
||||
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(Clone)]
|
||||
pub enum TokenTree<G, P, I, L> {
|
||||
Group(G),
|
||||
Punct(P),
|
||||
Ident(I),
|
||||
Literal(L),
|
||||
}
|
||||
|
||||
impl<G: Mark, P: Mark, I: Mark, L: Mark> Mark for TokenTree<G, P, I, L> {
|
||||
type Unmarked = TokenTree<G::Unmarked, P::Unmarked, I::Unmarked, L::Unmarked>;
|
||||
fn mark(unmarked: Self::Unmarked) -> Self {
|
||||
match unmarked {
|
||||
TokenTree::Group(tt) => TokenTree::Group(G::mark(tt)),
|
||||
TokenTree::Punct(tt) => TokenTree::Punct(P::mark(tt)),
|
||||
TokenTree::Ident(tt) => TokenTree::Ident(I::mark(tt)),
|
||||
TokenTree::Literal(tt) => TokenTree::Literal(L::mark(tt)),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<G: Unmark, P: Unmark, I: Unmark, L: Unmark> Unmark for TokenTree<G, P, I, L> {
|
||||
type Unmarked = TokenTree<G::Unmarked, P::Unmarked, I::Unmarked, L::Unmarked>;
|
||||
fn unmark(self) -> Self::Unmarked {
|
||||
match self {
|
||||
TokenTree::Group(tt) => TokenTree::Group(tt.unmark()),
|
||||
TokenTree::Punct(tt) => TokenTree::Punct(tt.unmark()),
|
||||
TokenTree::Ident(tt) => TokenTree::Ident(tt.unmark()),
|
||||
TokenTree::Literal(tt) => TokenTree::Literal(tt.unmark()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpc_encode_decode!(
|
||||
enum TokenTree<G, P, I, L> {
|
||||
Group(tt),
|
||||
Punct(tt),
|
||||
Ident(tt),
|
||||
Literal(tt),
|
||||
}
|
||||
);
|
305
crates/proc_macro_srv/src/abis/abi_1_56/proc_macro/bridge/rpc.rs
Normal file
305
crates/proc_macro_srv/src/abis/abi_1_56/proc_macro/bridge/rpc.rs
Normal file
@ -0,0 +1,305 @@
|
||||
//! Serialization for client-server communication.
|
||||
|
||||
use std::any::Any;
|
||||
use std::char;
|
||||
use std::io::Write;
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::Bound;
|
||||
use std::str;
|
||||
|
||||
pub(super) type Writer = super::buffer::Buffer<u8>;
|
||||
|
||||
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 { $($field:ident),* $(,)? }) => {
|
||||
impl<S> Encode<S> for $name {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
$(self.$field.encode(w, s);)*
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> DecodeMut<'_, '_, S> for $name {
|
||||
fn decode(r: &mut Reader<'_>, 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))
|
||||
}
|
||||
}
|
||||
|
||||
rpc_encode_decode!(
|
||||
enum Bound<T> {
|
||||
Included(x),
|
||||
Excluded(x),
|
||||
Unbounded,
|
||||
}
|
||||
);
|
||||
|
||||
rpc_encode_decode!(
|
||||
enum Option<T> {
|
||||
None,
|
||||
Some(x),
|
||||
}
|
||||
);
|
||||
|
||||
rpc_encode_decode!(
|
||||
enum Result<T, E> {
|
||||
Ok(x),
|
||||
Err(e),
|
||||
}
|
||||
);
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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 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())
|
||||
}
|
||||
}
|
@ -0,0 +1,352 @@
|
||||
//! Server-side traits.
|
||||
|
||||
use super::*;
|
||||
|
||||
// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
|
||||
use super::client::HandleStore;
|
||||
|
||||
/// Declare an associated item of one of the traits below, optionally
|
||||
/// adjusting it (i.e., adding bounds to types and default bodies to methods).
|
||||
macro_rules! associated_item {
|
||||
(type FreeFunctions) =>
|
||||
(type FreeFunctions: 'static;);
|
||||
(type TokenStream) =>
|
||||
(type TokenStream: 'static + Clone;);
|
||||
(type TokenStreamBuilder) =>
|
||||
(type TokenStreamBuilder: 'static;);
|
||||
(type TokenStreamIter) =>
|
||||
(type TokenStreamIter: 'static + Clone;);
|
||||
(type Group) =>
|
||||
(type Group: 'static + Clone;);
|
||||
(type Punct) =>
|
||||
(type Punct: 'static + Copy + Eq + Hash;);
|
||||
(type Ident) =>
|
||||
(type Ident: 'static + Copy + Eq + Hash;);
|
||||
(type Literal) =>
|
||||
(type Literal: 'static + Clone;);
|
||||
(type SourceFile) =>
|
||||
(type SourceFile: 'static + Clone;);
|
||||
(type MultiSpan) =>
|
||||
(type MultiSpan: 'static;);
|
||||
(type Diagnostic) =>
|
||||
(type Diagnostic: 'static;);
|
||||
(type Span) =>
|
||||
(type Span: 'static + Copy + Eq + Hash;);
|
||||
(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 Types {
|
||||
$(associated_item!(type $name);)*
|
||||
}
|
||||
|
||||
$(pub trait $name: Types {
|
||||
$(associated_item!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)*
|
||||
})*
|
||||
|
||||
pub trait Server: Types $(+ $name)* {}
|
||||
impl<S: Types $(+ $name)*> Server for S {}
|
||||
}
|
||||
}
|
||||
with_api!(Self, self_, declare_server_traits);
|
||||
|
||||
pub(super) struct MarkedTypes<S: Types>(S);
|
||||
|
||||
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, b: Buffer<u8>) -> Buffer<u8>;
|
||||
}
|
||||
|
||||
impl<S: Server> DispatcherTrait for Dispatcher<MarkedTypes<S>> {
|
||||
$(type $name = <MarkedTypes<S> as Types>::$name;)*
|
||||
fn dispatch(&mut self, mut b: Buffer<u8>) -> Buffer<u8> {
|
||||
let Dispatcher { handle_store, server } = self;
|
||||
|
||||
let mut reader = &b[..];
|
||||
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)
|
||||
};
|
||||
|
||||
b.clear();
|
||||
r.encode(&mut b, handle_store);
|
||||
})*
|
||||
}),*
|
||||
}
|
||||
b
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
with_api!(Self, self_, define_dispatcher_impl);
|
||||
|
||||
pub trait ExecutionStrategy {
|
||||
fn run_bridge_and_client<D: Copy + Send + 'static>(
|
||||
&self,
|
||||
dispatcher: &mut impl DispatcherTrait,
|
||||
input: Buffer<u8>,
|
||||
run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
|
||||
client_data: D,
|
||||
force_show_panics: bool,
|
||||
) -> Buffer<u8>;
|
||||
}
|
||||
|
||||
pub struct SameThread;
|
||||
|
||||
impl ExecutionStrategy for SameThread {
|
||||
fn run_bridge_and_client<D: Copy + Send + 'static>(
|
||||
&self,
|
||||
dispatcher: &mut impl DispatcherTrait,
|
||||
input: Buffer<u8>,
|
||||
run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
|
||||
client_data: D,
|
||||
force_show_panics: bool,
|
||||
) -> Buffer<u8> {
|
||||
let mut dispatch = |b| dispatcher.dispatch(b);
|
||||
|
||||
run_client(
|
||||
Bridge { cached_buffer: input, dispatch: (&mut dispatch).into(), force_show_panics },
|
||||
client_data,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(eddyb) Two implementations are provided, the second one is a bit
|
||||
// faster but neither is anywhere near as fast as same-thread execution.
|
||||
|
||||
pub struct CrossThread1;
|
||||
|
||||
impl ExecutionStrategy for CrossThread1 {
|
||||
fn run_bridge_and_client<D: Copy + Send + 'static>(
|
||||
&self,
|
||||
dispatcher: &mut impl DispatcherTrait,
|
||||
input: Buffer<u8>,
|
||||
run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
|
||||
client_data: D,
|
||||
force_show_panics: bool,
|
||||
) -> Buffer<u8> {
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
let (req_tx, req_rx) = channel();
|
||||
let (res_tx, res_rx) = channel();
|
||||
|
||||
let join_handle = thread::spawn(move || {
|
||||
let mut dispatch = |b| {
|
||||
req_tx.send(b).unwrap();
|
||||
res_rx.recv().unwrap()
|
||||
};
|
||||
|
||||
run_client(
|
||||
Bridge {
|
||||
cached_buffer: input,
|
||||
dispatch: (&mut dispatch).into(),
|
||||
force_show_panics,
|
||||
},
|
||||
client_data,
|
||||
)
|
||||
});
|
||||
|
||||
for b in req_rx {
|
||||
res_tx.send(dispatcher.dispatch(b)).unwrap();
|
||||
}
|
||||
|
||||
join_handle.join().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CrossThread2;
|
||||
|
||||
impl ExecutionStrategy for CrossThread2 {
|
||||
fn run_bridge_and_client<D: Copy + Send + 'static>(
|
||||
&self,
|
||||
dispatcher: &mut impl DispatcherTrait,
|
||||
input: Buffer<u8>,
|
||||
run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
|
||||
client_data: D,
|
||||
force_show_panics: bool,
|
||||
) -> Buffer<u8> {
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
enum State<T> {
|
||||
Req(T),
|
||||
Res(T),
|
||||
}
|
||||
|
||||
let mut state = Arc::new(Mutex::new(State::Res(Buffer::new())));
|
||||
|
||||
let server_thread = thread::current();
|
||||
let state2 = state.clone();
|
||||
let join_handle = thread::spawn(move || {
|
||||
let mut dispatch = |b| {
|
||||
*state2.lock().unwrap() = State::Req(b);
|
||||
server_thread.unpark();
|
||||
loop {
|
||||
thread::park();
|
||||
if let State::Res(b) = &mut *state2.lock().unwrap() {
|
||||
break b.take();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let r = run_client(
|
||||
Bridge {
|
||||
cached_buffer: input,
|
||||
dispatch: (&mut dispatch).into(),
|
||||
force_show_panics,
|
||||
},
|
||||
client_data,
|
||||
);
|
||||
|
||||
// Wake up the server so it can exit the dispatch loop.
|
||||
drop(state2);
|
||||
server_thread.unpark();
|
||||
|
||||
r
|
||||
});
|
||||
|
||||
// Check whether `state2` was dropped, to know when to stop.
|
||||
while Arc::get_mut(&mut state).is_none() {
|
||||
thread::park();
|
||||
let mut b = match &mut *state.lock().unwrap() {
|
||||
State::Req(b) => b.take(),
|
||||
_ => continue,
|
||||
};
|
||||
b = dispatcher.dispatch(b.take());
|
||||
*state.lock().unwrap() = State::Res(b);
|
||||
join_handle.thread().unpark();
|
||||
}
|
||||
|
||||
join_handle.join().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn run_server<
|
||||
S: Server,
|
||||
I: Encode<HandleStore<MarkedTypes<S>>>,
|
||||
O: for<'a, 's> DecodeMut<'a, 's, HandleStore<MarkedTypes<S>>>,
|
||||
D: Copy + Send + 'static,
|
||||
>(
|
||||
strategy: &impl ExecutionStrategy,
|
||||
handle_counters: &'static client::HandleCounters,
|
||||
server: S,
|
||||
input: I,
|
||||
run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
|
||||
client_data: D,
|
||||
force_show_panics: bool,
|
||||
) -> Result<O, PanicMessage> {
|
||||
let mut dispatcher =
|
||||
Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) };
|
||||
|
||||
let mut b = Buffer::new();
|
||||
input.encode(&mut b, &mut dispatcher.handle_store);
|
||||
|
||||
b = strategy.run_bridge_and_client(
|
||||
&mut dispatcher,
|
||||
b,
|
||||
run_client,
|
||||
client_data,
|
||||
force_show_panics,
|
||||
);
|
||||
|
||||
Result::decode(&mut &b[..], &mut dispatcher.handle_store)
|
||||
}
|
||||
|
||||
impl client::Client<fn(super::super::TokenStream) -> super::super::TokenStream> {
|
||||
pub fn run<S: Server>(
|
||||
&self,
|
||||
strategy: &impl ExecutionStrategy,
|
||||
server: S,
|
||||
input: S::TokenStream,
|
||||
force_show_panics: bool,
|
||||
) -> Result<S::TokenStream, PanicMessage> {
|
||||
let client::Client { get_handle_counters, run, f } = *self;
|
||||
run_server(
|
||||
strategy,
|
||||
get_handle_counters(),
|
||||
server,
|
||||
<MarkedTypes<S> as Types>::TokenStream::mark(input),
|
||||
run,
|
||||
f,
|
||||
force_show_panics,
|
||||
)
|
||||
.map(<MarkedTypes<S> as Types>::TokenStream::unmark)
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
client::Client<
|
||||
fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream,
|
||||
>
|
||||
{
|
||||
pub fn run<S: Server>(
|
||||
&self,
|
||||
strategy: &impl ExecutionStrategy,
|
||||
server: S,
|
||||
input: S::TokenStream,
|
||||
input2: S::TokenStream,
|
||||
force_show_panics: bool,
|
||||
) -> Result<S::TokenStream, PanicMessage> {
|
||||
let client::Client { get_handle_counters, run, f } = *self;
|
||||
run_server(
|
||||
strategy,
|
||||
get_handle_counters(),
|
||||
server,
|
||||
(
|
||||
<MarkedTypes<S> as Types>::TokenStream::mark(input),
|
||||
<MarkedTypes<S> as Types>::TokenStream::mark(input2),
|
||||
),
|
||||
run,
|
||||
f,
|
||||
force_show_panics,
|
||||
)
|
||||
.map(<MarkedTypes<S> as Types>::TokenStream::unmark)
|
||||
}
|
||||
}
|
167
crates/proc_macro_srv/src/abis/abi_1_56/proc_macro/diagnostic.rs
Normal file
167
crates/proc_macro_srv/src/abis/abi_1_56/proc_macro/diagnostic.rs
Normal file
@ -0,0 +1,167 @@
|
||||
//! lib-proc-macro diagnostic
|
||||
//!
|
||||
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/diagnostic.rs>
|
||||
//! augmented with removing unstable features
|
||||
|
||||
use super::Span;
|
||||
|
||||
/// An enum representing a diagnostic level.
|
||||
#[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.
|
||||
pub trait MultiSpan {
|
||||
/// Converts `self` into a `Vec<Span>`.
|
||||
fn into_spans(self) -> Vec<Span>;
|
||||
}
|
||||
|
||||
impl MultiSpan for Span {
|
||||
fn into_spans(self) -> Vec<Span> {
|
||||
vec![self]
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiSpan for Vec<Span> {
|
||||
fn into_spans(self) -> Vec<Span> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
#[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) => {
|
||||
/// Adds a new child diagnostic message to `self` with the level
|
||||
/// identified by this method's name with 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
|
||||
}
|
||||
|
||||
/// Adds a new child diagnostic message to `self` with the level
|
||||
/// identified by this method's name with 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)]
|
||||
pub struct Children<'a>(std::slice::Iter<'a, Diagnostic>);
|
||||
|
||||
impl<'a> Iterator for Children<'a> {
|
||||
type Item = &'a Diagnostic;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl Diagnostic {
|
||||
/// Creates a new diagnostic with the given `level` and `message`.
|
||||
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`.
|
||||
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`.
|
||||
pub fn level(&self) -> Level {
|
||||
self.level
|
||||
}
|
||||
|
||||
/// Sets the level in `self` to `level`.
|
||||
pub fn set_level(&mut self, level: Level) {
|
||||
self.level = level;
|
||||
}
|
||||
|
||||
/// Returns the message in `self`.
|
||||
pub fn message(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
|
||||
/// Sets the message in `self` to `message`.
|
||||
pub fn set_message<T: Into<String>>(&mut self, message: T) {
|
||||
self.message = message.into();
|
||||
}
|
||||
|
||||
/// Returns the `Span`s in `self`.
|
||||
pub fn spans(&self) -> &[Span] {
|
||||
&self.spans
|
||||
}
|
||||
|
||||
/// Sets the `Span`s in `self` to `spans`.
|
||||
pub fn set_spans<S: MultiSpan>(&mut self, spans: S) {
|
||||
self.spans = spans.into_spans();
|
||||
}
|
||||
|
||||
/// Returns an iterator over the children diagnostics of `self`.
|
||||
pub fn children(&self) -> Children<'_> {
|
||||
Children(self.children.iter())
|
||||
}
|
||||
|
||||
/// Emit the diagnostic.
|
||||
pub fn emit(self) {
|
||||
fn to_internal(spans: Vec<Span>) -> super::bridge::client::MultiSpan {
|
||||
let mut multi_span = super::bridge::client::MultiSpan::new();
|
||||
for span in spans {
|
||||
multi_span.push(span.0);
|
||||
}
|
||||
multi_span
|
||||
}
|
||||
|
||||
let mut diag = super::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();
|
||||
}
|
||||
}
|
1013
crates/proc_macro_srv/src/abis/abi_1_56/proc_macro/mod.rs
Normal file
1013
crates/proc_macro_srv/src/abis/abi_1_56/proc_macro/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
140
crates/proc_macro_srv/src/abis/abi_1_56/proc_macro/quote.rs
Normal file
140
crates/proc_macro_srv/src/abis/abi_1_56/proc_macro/quote.rs
Normal file
@ -0,0 +1,140 @@
|
||||
//! # 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 super::{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) };
|
||||
(=) => { 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`.
|
||||
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.
|
||||
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)))))
|
||||
}
|
846
crates/proc_macro_srv/src/abis/abi_1_56/rustc_server.rs
Normal file
846
crates/proc_macro_srv/src/abis/abi_1_56/rustc_server.rs
Normal file
@ -0,0 +1,846 @@
|
||||
//! Rustc proc-macro server implementation with tt
|
||||
//!
|
||||
//! Based on idea from <https://github.com/fedochet/rust-proc-macro-expander>
|
||||
//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that
|
||||
//! we could provide any TokenStream implementation.
|
||||
//! The original idea from fedochet is using proc-macro2 as backend,
|
||||
//! we use tt instead for better integration with RA.
|
||||
//!
|
||||
//! FIXME: No span and source file information is implemented yet
|
||||
|
||||
use super::proc_macro::bridge::{self, server};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::Bound;
|
||||
use std::{ascii, vec::IntoIter};
|
||||
|
||||
type Group = tt::Subtree;
|
||||
type TokenTree = tt::TokenTree;
|
||||
type Punct = tt::Punct;
|
||||
type Spacing = tt::Spacing;
|
||||
type Literal = tt::Literal;
|
||||
type Span = tt::TokenId;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TokenStream {
|
||||
pub token_trees: Vec<TokenTree>,
|
||||
}
|
||||
|
||||
impl TokenStream {
|
||||
pub fn new() -> Self {
|
||||
TokenStream { token_trees: Default::default() }
|
||||
}
|
||||
|
||||
pub fn with_subtree(subtree: tt::Subtree) -> Self {
|
||||
if subtree.delimiter.is_some() {
|
||||
TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] }
|
||||
} else {
|
||||
TokenStream { token_trees: subtree.token_trees }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_subtree(self) -> tt::Subtree {
|
||||
tt::Subtree { delimiter: None, token_trees: self.token_trees }
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.token_trees.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a token stream containing a single token tree.
|
||||
impl From<TokenTree> for TokenStream {
|
||||
fn from(tree: TokenTree) -> TokenStream {
|
||||
TokenStream { token_trees: vec![tree] }
|
||||
}
|
||||
}
|
||||
|
||||
/// Collects a number of token trees into a single stream.
|
||||
impl FromIterator<TokenTree> for TokenStream {
|
||||
fn from_iter<I: IntoIterator<Item = TokenTree>>(trees: I) -> Self {
|
||||
trees.into_iter().map(TokenStream::from).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// A "flattening" operation on token streams, collects token trees
|
||||
/// from multiple token streams into a single stream.
|
||||
impl FromIterator<TokenStream> for TokenStream {
|
||||
fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self {
|
||||
let mut builder = TokenStreamBuilder::new();
|
||||
streams.into_iter().for_each(|stream| builder.push(stream));
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<TokenTree> for TokenStream {
|
||||
fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, trees: I) {
|
||||
self.extend(trees.into_iter().map(TokenStream::from));
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<TokenStream> for TokenStream {
|
||||
fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) {
|
||||
for item in streams {
|
||||
for tkn in item {
|
||||
match tkn {
|
||||
tt::TokenTree::Subtree(subtree) if subtree.delimiter.is_none() => {
|
||||
self.token_trees.extend(subtree.token_trees);
|
||||
}
|
||||
_ => {
|
||||
self.token_trees.push(tkn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Level = super::proc_macro::Level;
|
||||
type LineColumn = super::proc_macro::LineColumn;
|
||||
type SourceFile = super::proc_macro::SourceFile;
|
||||
|
||||
/// A structure representing a diagnostic message and associated children
|
||||
/// messages.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Diagnostic {
|
||||
level: Level,
|
||||
message: String,
|
||||
spans: Vec<Span>,
|
||||
children: Vec<Diagnostic>,
|
||||
}
|
||||
|
||||
impl Diagnostic {
|
||||
/// Creates a new diagnostic with the given `level` and `message`.
|
||||
pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
|
||||
Diagnostic { level, message: message.into(), spans: vec![], children: vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
// Rustc Server Ident has to be `Copyable`
|
||||
// We use a stub here for bypassing
|
||||
#[derive(Hash, Eq, PartialEq, Copy, Clone)]
|
||||
pub struct IdentId(u32);
|
||||
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
struct IdentData(tt::Ident);
|
||||
|
||||
#[derive(Default)]
|
||||
struct IdentInterner {
|
||||
idents: HashMap<IdentData, u32>,
|
||||
ident_data: Vec<IdentData>,
|
||||
}
|
||||
|
||||
impl IdentInterner {
|
||||
fn intern(&mut self, data: &IdentData) -> u32 {
|
||||
if let Some(index) = self.idents.get(data) {
|
||||
return *index;
|
||||
}
|
||||
|
||||
let index = self.idents.len() as u32;
|
||||
self.ident_data.push(data.clone());
|
||||
self.idents.insert(data.clone(), index);
|
||||
index
|
||||
}
|
||||
|
||||
fn get(&self, index: u32) -> &IdentData {
|
||||
&self.ident_data[index as usize]
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn get_mut(&mut self, index: u32) -> &mut IdentData {
|
||||
self.ident_data.get_mut(index as usize).expect("Should be consistent")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TokenStreamBuilder {
|
||||
acc: TokenStream,
|
||||
}
|
||||
|
||||
/// Public implementation details for the `TokenStream` type, such as iterators.
|
||||
pub mod token_stream {
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::{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.
|
||||
impl IntoIterator for TokenStream {
|
||||
type Item = TokenTree;
|
||||
type IntoIter = super::IntoIter<TokenTree>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.token_trees.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
type LexError = String;
|
||||
|
||||
/// 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.
|
||||
impl FromStr for TokenStream {
|
||||
type Err = LexError;
|
||||
|
||||
fn from_str(src: &str) -> Result<TokenStream, LexError> {
|
||||
let (subtree, _token_map) =
|
||||
mbe::parse_to_token_tree(src).ok_or("Failed to parse from mbe")?;
|
||||
|
||||
let subtree = subtree_replace_token_ids_with_unspecified(subtree);
|
||||
Ok(TokenStream::with_subtree(subtree))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for TokenStream {
|
||||
fn to_string(&self) -> String {
|
||||
return tokentrees_to_text(&self.token_trees[..]);
|
||||
|
||||
fn tokentrees_to_text(tkns: &[tt::TokenTree]) -> String {
|
||||
tkns.iter()
|
||||
.fold((String::new(), true), |(last, last_to_joint), tkn| {
|
||||
let s = [last, tokentree_to_text(tkn)].join(if last_to_joint {
|
||||
""
|
||||
} else {
|
||||
" "
|
||||
});
|
||||
let mut is_joint = false;
|
||||
if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn {
|
||||
if punct.spacing == tt::Spacing::Joint {
|
||||
is_joint = true;
|
||||
}
|
||||
}
|
||||
(s, is_joint)
|
||||
})
|
||||
.0
|
||||
}
|
||||
|
||||
fn tokentree_to_text(tkn: &tt::TokenTree) -> String {
|
||||
match tkn {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.text.clone().into(),
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(literal)) => literal.text.clone().into(),
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => format!("{}", punct.char),
|
||||
tt::TokenTree::Subtree(subtree) => {
|
||||
let content = tokentrees_to_text(&subtree.token_trees);
|
||||
let (open, close) = match subtree.delimiter.map(|it| it.kind) {
|
||||
None => ("", ""),
|
||||
Some(tt::DelimiterKind::Brace) => ("{", "}"),
|
||||
Some(tt::DelimiterKind::Parenthesis) => ("(", ")"),
|
||||
Some(tt::DelimiterKind::Bracket) => ("[", "]"),
|
||||
};
|
||||
format!("{}{}{}", open, content, close)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn subtree_replace_token_ids_with_unspecified(subtree: tt::Subtree) -> tt::Subtree {
|
||||
tt::Subtree {
|
||||
delimiter: subtree
|
||||
.delimiter
|
||||
.map(|d| tt::Delimiter { id: tt::TokenId::unspecified(), ..d }),
|
||||
token_trees: subtree
|
||||
.token_trees
|
||||
.into_iter()
|
||||
.map(token_tree_replace_token_ids_with_unspecified)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn token_tree_replace_token_ids_with_unspecified(tt: tt::TokenTree) -> tt::TokenTree {
|
||||
match tt {
|
||||
tt::TokenTree::Leaf(leaf) => {
|
||||
tt::TokenTree::Leaf(leaf_replace_token_ids_with_unspecified(leaf))
|
||||
}
|
||||
tt::TokenTree::Subtree(subtree) => {
|
||||
tt::TokenTree::Subtree(subtree_replace_token_ids_with_unspecified(subtree))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn leaf_replace_token_ids_with_unspecified(leaf: tt::Leaf) -> tt::Leaf {
|
||||
match leaf {
|
||||
tt::Leaf::Literal(lit) => {
|
||||
tt::Leaf::Literal(tt::Literal { id: tt::TokenId::unspecified(), ..lit })
|
||||
}
|
||||
tt::Leaf::Punct(punct) => {
|
||||
tt::Leaf::Punct(tt::Punct { id: tt::TokenId::unspecified(), ..punct })
|
||||
}
|
||||
tt::Leaf::Ident(ident) => {
|
||||
tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), ..ident })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStreamBuilder {
|
||||
fn new() -> TokenStreamBuilder {
|
||||
TokenStreamBuilder { acc: TokenStream::new() }
|
||||
}
|
||||
|
||||
fn push(&mut self, stream: TokenStream) {
|
||||
self.acc.extend(stream.into_iter())
|
||||
}
|
||||
|
||||
fn build(self) -> TokenStream {
|
||||
self.acc
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FreeFunctions;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TokenStreamIter {
|
||||
trees: IntoIter<TokenTree>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Rustc {
|
||||
ident_interner: IdentInterner,
|
||||
// FIXME: store span information here.
|
||||
}
|
||||
|
||||
impl server::Types for Rustc {
|
||||
type FreeFunctions = FreeFunctions;
|
||||
type TokenStream = TokenStream;
|
||||
type TokenStreamBuilder = TokenStreamBuilder;
|
||||
type TokenStreamIter = TokenStreamIter;
|
||||
type Group = Group;
|
||||
type Punct = Punct;
|
||||
type Ident = IdentId;
|
||||
type Literal = Literal;
|
||||
type SourceFile = SourceFile;
|
||||
type Diagnostic = Diagnostic;
|
||||
type Span = Span;
|
||||
type MultiSpan = Vec<Span>;
|
||||
}
|
||||
|
||||
impl server::FreeFunctions for Rustc {
|
||||
fn track_env_var(&mut self, _var: &str, _value: Option<&str>) {
|
||||
// FIXME: track env var accesses
|
||||
// https://github.com/rust-lang/rust/pull/71858
|
||||
}
|
||||
fn track_path(&mut self, _path: &str) {}
|
||||
}
|
||||
|
||||
impl server::TokenStream for Rustc {
|
||||
fn new(&mut self) -> Self::TokenStream {
|
||||
Self::TokenStream::new()
|
||||
}
|
||||
|
||||
fn is_empty(&mut self, stream: &Self::TokenStream) -> bool {
|
||||
stream.is_empty()
|
||||
}
|
||||
fn from_str(&mut self, src: &str) -> Self::TokenStream {
|
||||
use std::str::FromStr;
|
||||
|
||||
Self::TokenStream::from_str(src).expect("cannot parse string")
|
||||
}
|
||||
fn to_string(&mut self, stream: &Self::TokenStream) -> String {
|
||||
stream.to_string()
|
||||
}
|
||||
fn from_token_tree(
|
||||
&mut self,
|
||||
tree: bridge::TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>,
|
||||
) -> Self::TokenStream {
|
||||
match tree {
|
||||
bridge::TokenTree::Group(group) => {
|
||||
let tree = TokenTree::from(group);
|
||||
Self::TokenStream::from_iter(vec![tree])
|
||||
}
|
||||
|
||||
bridge::TokenTree::Ident(IdentId(index)) => {
|
||||
let IdentData(ident) = self.ident_interner.get(index).clone();
|
||||
let ident: tt::Ident = ident;
|
||||
let leaf = tt::Leaf::from(ident);
|
||||
let tree = TokenTree::from(leaf);
|
||||
Self::TokenStream::from_iter(vec![tree])
|
||||
}
|
||||
|
||||
bridge::TokenTree::Literal(literal) => {
|
||||
let leaf = tt::Leaf::from(literal);
|
||||
let tree = TokenTree::from(leaf);
|
||||
Self::TokenStream::from_iter(vec![tree])
|
||||
}
|
||||
|
||||
bridge::TokenTree::Punct(p) => {
|
||||
let leaf = tt::Leaf::from(p);
|
||||
let tree = TokenTree::from(leaf);
|
||||
Self::TokenStream::from_iter(vec![tree])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn into_iter(&mut self, stream: Self::TokenStream) -> Self::TokenStreamIter {
|
||||
let trees: Vec<TokenTree> = stream.into_iter().collect();
|
||||
TokenStreamIter { trees: trees.into_iter() }
|
||||
}
|
||||
}
|
||||
|
||||
impl server::TokenStreamBuilder for Rustc {
|
||||
fn new(&mut self) -> Self::TokenStreamBuilder {
|
||||
Self::TokenStreamBuilder::new()
|
||||
}
|
||||
fn push(&mut self, builder: &mut Self::TokenStreamBuilder, stream: Self::TokenStream) {
|
||||
builder.push(stream)
|
||||
}
|
||||
fn build(&mut self, builder: Self::TokenStreamBuilder) -> Self::TokenStream {
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl server::TokenStreamIter for Rustc {
|
||||
fn next(
|
||||
&mut self,
|
||||
iter: &mut Self::TokenStreamIter,
|
||||
) -> Option<bridge::TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>> {
|
||||
iter.trees.next().map(|tree| match tree {
|
||||
TokenTree::Subtree(group) => bridge::TokenTree::Group(group),
|
||||
TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
|
||||
bridge::TokenTree::Ident(IdentId(self.ident_interner.intern(&IdentData(ident))))
|
||||
}
|
||||
TokenTree::Leaf(tt::Leaf::Literal(literal)) => bridge::TokenTree::Literal(literal),
|
||||
TokenTree::Leaf(tt::Leaf::Punct(punct)) => bridge::TokenTree::Punct(punct),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn delim_to_internal(d: bridge::Delimiter) -> Option<tt::Delimiter> {
|
||||
let kind = match d {
|
||||
bridge::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis,
|
||||
bridge::Delimiter::Brace => tt::DelimiterKind::Brace,
|
||||
bridge::Delimiter::Bracket => tt::DelimiterKind::Bracket,
|
||||
bridge::Delimiter::None => return None,
|
||||
};
|
||||
Some(tt::Delimiter { id: tt::TokenId::unspecified(), kind })
|
||||
}
|
||||
|
||||
fn delim_to_external(d: Option<tt::Delimiter>) -> bridge::Delimiter {
|
||||
match d.map(|it| it.kind) {
|
||||
Some(tt::DelimiterKind::Parenthesis) => bridge::Delimiter::Parenthesis,
|
||||
Some(tt::DelimiterKind::Brace) => bridge::Delimiter::Brace,
|
||||
Some(tt::DelimiterKind::Bracket) => bridge::Delimiter::Bracket,
|
||||
None => bridge::Delimiter::None,
|
||||
}
|
||||
}
|
||||
|
||||
fn spacing_to_internal(spacing: bridge::Spacing) -> Spacing {
|
||||
match spacing {
|
||||
bridge::Spacing::Alone => Spacing::Alone,
|
||||
bridge::Spacing::Joint => Spacing::Joint,
|
||||
}
|
||||
}
|
||||
|
||||
fn spacing_to_external(spacing: Spacing) -> bridge::Spacing {
|
||||
match spacing {
|
||||
Spacing::Alone => bridge::Spacing::Alone,
|
||||
Spacing::Joint => bridge::Spacing::Joint,
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Group for Rustc {
|
||||
fn new(&mut self, delimiter: bridge::Delimiter, stream: Self::TokenStream) -> Self::Group {
|
||||
Self::Group { delimiter: delim_to_internal(delimiter), token_trees: stream.token_trees }
|
||||
}
|
||||
fn delimiter(&mut self, group: &Self::Group) -> bridge::Delimiter {
|
||||
delim_to_external(group.delimiter)
|
||||
}
|
||||
|
||||
// NOTE: Return value of do not include delimiter
|
||||
fn stream(&mut self, group: &Self::Group) -> Self::TokenStream {
|
||||
TokenStream { token_trees: group.token_trees.clone() }
|
||||
}
|
||||
|
||||
fn span(&mut self, group: &Self::Group) -> Self::Span {
|
||||
group.delimiter.map(|it| it.id).unwrap_or_else(tt::TokenId::unspecified)
|
||||
}
|
||||
|
||||
fn set_span(&mut self, _group: &mut Self::Group, _span: Self::Span) {
|
||||
// FIXME handle span
|
||||
}
|
||||
|
||||
fn span_open(&mut self, _group: &Self::Group) -> Self::Span {
|
||||
// FIXME handle span
|
||||
// MySpan(self.span_interner.intern(&MySpanData(group.span_open())))
|
||||
tt::TokenId::unspecified()
|
||||
}
|
||||
|
||||
fn span_close(&mut self, _group: &Self::Group) -> Self::Span {
|
||||
// FIXME handle span
|
||||
tt::TokenId::unspecified()
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Punct for Rustc {
|
||||
fn new(&mut self, ch: char, spacing: bridge::Spacing) -> Self::Punct {
|
||||
tt::Punct {
|
||||
char: ch,
|
||||
spacing: spacing_to_internal(spacing),
|
||||
id: tt::TokenId::unspecified(),
|
||||
}
|
||||
}
|
||||
fn as_char(&mut self, punct: Self::Punct) -> char {
|
||||
punct.char
|
||||
}
|
||||
fn spacing(&mut self, punct: Self::Punct) -> bridge::Spacing {
|
||||
spacing_to_external(punct.spacing)
|
||||
}
|
||||
fn span(&mut self, _punct: Self::Punct) -> Self::Span {
|
||||
// FIXME handle span
|
||||
tt::TokenId::unspecified()
|
||||
}
|
||||
fn with_span(&mut self, punct: Self::Punct, _span: Self::Span) -> Self::Punct {
|
||||
// FIXME handle span
|
||||
punct
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Ident for Rustc {
|
||||
fn new(&mut self, string: &str, _span: Self::Span, _is_raw: bool) -> Self::Ident {
|
||||
IdentId(
|
||||
self.ident_interner.intern(&IdentData(tt::Ident {
|
||||
text: string.into(),
|
||||
id: tt::TokenId::unspecified(),
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
fn span(&mut self, _ident: Self::Ident) -> Self::Span {
|
||||
// FIXME handle span
|
||||
tt::TokenId::unspecified()
|
||||
}
|
||||
fn with_span(&mut self, ident: Self::Ident, _span: Self::Span) -> Self::Ident {
|
||||
// FIXME handle span
|
||||
ident
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Literal for Rustc {
|
||||
fn debug_kind(&mut self, _literal: &Self::Literal) -> String {
|
||||
// r-a: debug_kind and suffix are unsupported; corresponding client code has been changed to not call these.
|
||||
// They must still be present to be ABI-compatible and work with upstream proc_macro.
|
||||
"".to_owned()
|
||||
}
|
||||
fn from_str(&mut self, _s: &str) -> Result<Self::Literal, ()> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn symbol(&mut self, literal: &Self::Literal) -> String {
|
||||
literal.text.to_string()
|
||||
}
|
||||
fn suffix(&mut self, _literal: &Self::Literal) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
fn to_string(&mut self, _literal: &Self::Literal) -> String {
|
||||
_literal.to_string()
|
||||
}
|
||||
|
||||
fn integer(&mut self, n: &str) -> Self::Literal {
|
||||
let n = if let Ok(n) = n.parse::<i128>() {
|
||||
n.to_string()
|
||||
} else {
|
||||
n.parse::<u128>().unwrap().to_string()
|
||||
};
|
||||
Literal { text: n.into(), id: tt::TokenId::unspecified() }
|
||||
}
|
||||
|
||||
fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal {
|
||||
macro_rules! def_suffixed_integer {
|
||||
($kind:ident, $($ty:ty),*) => {
|
||||
match $kind {
|
||||
$(
|
||||
stringify!($ty) => {
|
||||
let n: $ty = n.parse().unwrap();
|
||||
format!(concat!("{}", stringify!($ty)), n)
|
||||
}
|
||||
)*
|
||||
_ => unimplemented!("unknown args for typed_integer: n {}, kind {}", n, $kind),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let text = def_suffixed_integer! {kind, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize};
|
||||
|
||||
Literal { text: text.into(), id: tt::TokenId::unspecified() }
|
||||
}
|
||||
|
||||
fn float(&mut self, n: &str) -> Self::Literal {
|
||||
let n: f64 = n.parse().unwrap();
|
||||
let mut text = f64::to_string(&n);
|
||||
if !text.contains('.') {
|
||||
text += ".0"
|
||||
}
|
||||
Literal { text: text.into(), id: tt::TokenId::unspecified() }
|
||||
}
|
||||
|
||||
fn f32(&mut self, n: &str) -> Self::Literal {
|
||||
let n: f32 = n.parse().unwrap();
|
||||
let text = format!("{}f32", n);
|
||||
Literal { text: text.into(), id: tt::TokenId::unspecified() }
|
||||
}
|
||||
|
||||
fn f64(&mut self, n: &str) -> Self::Literal {
|
||||
let n: f64 = n.parse().unwrap();
|
||||
let text = format!("{}f64", n);
|
||||
Literal { text: text.into(), id: tt::TokenId::unspecified() }
|
||||
}
|
||||
|
||||
fn string(&mut self, string: &str) -> Self::Literal {
|
||||
let mut escaped = String::new();
|
||||
for ch in string.chars() {
|
||||
escaped.extend(ch.escape_debug());
|
||||
}
|
||||
Literal { text: format!("\"{}\"", escaped).into(), id: tt::TokenId::unspecified() }
|
||||
}
|
||||
|
||||
fn character(&mut self, ch: char) -> Self::Literal {
|
||||
Literal { text: format!("'{}'", ch).into(), id: tt::TokenId::unspecified() }
|
||||
}
|
||||
|
||||
fn byte_string(&mut self, bytes: &[u8]) -> Self::Literal {
|
||||
let string = bytes
|
||||
.iter()
|
||||
.cloned()
|
||||
.flat_map(ascii::escape_default)
|
||||
.map(Into::<char>::into)
|
||||
.collect::<String>();
|
||||
|
||||
Literal { text: format!("b\"{}\"", string).into(), id: tt::TokenId::unspecified() }
|
||||
}
|
||||
|
||||
fn span(&mut self, literal: &Self::Literal) -> Self::Span {
|
||||
literal.id
|
||||
}
|
||||
|
||||
fn set_span(&mut self, _literal: &mut Self::Literal, _span: Self::Span) {
|
||||
// FIXME handle span
|
||||
}
|
||||
|
||||
fn subspan(
|
||||
&mut self,
|
||||
_literal: &Self::Literal,
|
||||
_start: Bound<usize>,
|
||||
_end: Bound<usize>,
|
||||
) -> Option<Self::Span> {
|
||||
// FIXME handle span
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl server::SourceFile for Rustc {
|
||||
fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool {
|
||||
file1.eq(file2)
|
||||
}
|
||||
fn path(&mut self, file: &Self::SourceFile) -> String {
|
||||
String::from(
|
||||
file.path().to_str().expect("non-UTF8 file path in `proc_macro::SourceFile::path`"),
|
||||
)
|
||||
}
|
||||
fn is_real(&mut self, file: &Self::SourceFile) -> bool {
|
||||
file.is_real()
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Diagnostic for Rustc {
|
||||
fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic {
|
||||
let mut diag = Diagnostic::new(level, msg);
|
||||
diag.spans = spans;
|
||||
diag
|
||||
}
|
||||
|
||||
fn sub(
|
||||
&mut self,
|
||||
_diag: &mut Self::Diagnostic,
|
||||
_level: Level,
|
||||
_msg: &str,
|
||||
_spans: Self::MultiSpan,
|
||||
) {
|
||||
// FIXME handle diagnostic
|
||||
//
|
||||
}
|
||||
|
||||
fn emit(&mut self, _diag: Self::Diagnostic) {
|
||||
// FIXME handle diagnostic
|
||||
// diag.emit()
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Span for Rustc {
|
||||
fn debug(&mut self, span: Self::Span) -> String {
|
||||
format!("{:?}", span.0)
|
||||
}
|
||||
fn def_site(&mut self) -> Self::Span {
|
||||
// MySpan(self.span_interner.intern(&MySpanData(Span::def_site())))
|
||||
// FIXME handle span
|
||||
tt::TokenId::unspecified()
|
||||
}
|
||||
fn call_site(&mut self) -> Self::Span {
|
||||
// MySpan(self.span_interner.intern(&MySpanData(Span::call_site())))
|
||||
// FIXME handle span
|
||||
tt::TokenId::unspecified()
|
||||
}
|
||||
fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile {
|
||||
// let MySpanData(span) = self.span_interner.get(span.0);
|
||||
unimplemented!()
|
||||
}
|
||||
fn save_span(&mut self, _span: Self::Span) -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span {
|
||||
unimplemented!()
|
||||
}
|
||||
/// Recent feature, not yet in the proc_macro
|
||||
///
|
||||
/// See PR:
|
||||
/// https://github.com/rust-lang/rust/pull/55780
|
||||
fn source_text(&mut self, _span: Self::Span) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
fn parent(&mut self, _span: Self::Span) -> Option<Self::Span> {
|
||||
// FIXME handle span
|
||||
None
|
||||
}
|
||||
fn source(&mut self, span: Self::Span) -> Self::Span {
|
||||
// FIXME handle span
|
||||
span
|
||||
}
|
||||
fn start(&mut self, _span: Self::Span) -> LineColumn {
|
||||
// FIXME handle span
|
||||
LineColumn { line: 0, column: 0 }
|
||||
}
|
||||
fn end(&mut self, _span: Self::Span) -> LineColumn {
|
||||
// FIXME handle span
|
||||
LineColumn { line: 0, column: 0 }
|
||||
}
|
||||
fn join(&mut self, _first: Self::Span, _second: Self::Span) -> Option<Self::Span> {
|
||||
None
|
||||
}
|
||||
fn resolved_at(&mut self, _span: Self::Span, _at: Self::Span) -> Self::Span {
|
||||
// FIXME handle span
|
||||
tt::TokenId::unspecified()
|
||||
}
|
||||
|
||||
fn mixed_site(&mut self) -> Self::Span {
|
||||
// FIXME handle span
|
||||
tt::TokenId::unspecified()
|
||||
}
|
||||
}
|
||||
|
||||
impl server::MultiSpan for Rustc {
|
||||
fn new(&mut self) -> Self::MultiSpan {
|
||||
// FIXME handle span
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn push(&mut self, other: &mut Self::MultiSpan, span: Self::Span) {
|
||||
//TODP
|
||||
other.push(span)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::proc_macro::bridge::server::Literal;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_rustc_server_literals() {
|
||||
let mut srv = Rustc { ident_interner: IdentInterner::default() };
|
||||
assert_eq!(srv.integer("1234").text, "1234");
|
||||
|
||||
assert_eq!(srv.typed_integer("12", "u8").text, "12u8");
|
||||
assert_eq!(srv.typed_integer("255", "u16").text, "255u16");
|
||||
assert_eq!(srv.typed_integer("1234", "u32").text, "1234u32");
|
||||
assert_eq!(srv.typed_integer("15846685", "u64").text, "15846685u64");
|
||||
assert_eq!(srv.typed_integer("15846685258", "u128").text, "15846685258u128");
|
||||
assert_eq!(srv.typed_integer("156788984", "usize").text, "156788984usize");
|
||||
assert_eq!(srv.typed_integer("127", "i8").text, "127i8");
|
||||
assert_eq!(srv.typed_integer("255", "i16").text, "255i16");
|
||||
assert_eq!(srv.typed_integer("1234", "i32").text, "1234i32");
|
||||
assert_eq!(srv.typed_integer("15846685", "i64").text, "15846685i64");
|
||||
assert_eq!(srv.typed_integer("15846685258", "i128").text, "15846685258i128");
|
||||
assert_eq!(srv.float("0").text, "0.0");
|
||||
assert_eq!(srv.float("15684.5867").text, "15684.5867");
|
||||
assert_eq!(srv.f32("15684.58").text, "15684.58f32");
|
||||
assert_eq!(srv.f64("15684.58").text, "15684.58f64");
|
||||
|
||||
assert_eq!(srv.string("hello_world").text, "\"hello_world\"");
|
||||
assert_eq!(srv.character('c').text, "'c'");
|
||||
assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\"");
|
||||
|
||||
// u128::max
|
||||
assert_eq!(
|
||||
srv.integer("340282366920938463463374607431768211455").text,
|
||||
"340282366920938463463374607431768211455"
|
||||
);
|
||||
// i128::min
|
||||
assert_eq!(
|
||||
srv.integer("-170141183460469231731687303715884105728").text,
|
||||
"-170141183460469231731687303715884105728"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rustc_server_to_string() {
|
||||
let s = TokenStream {
|
||||
token_trees: vec![
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
|
||||
text: "struct".into(),
|
||||
id: tt::TokenId::unspecified(),
|
||||
})),
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
|
||||
text: "T".into(),
|
||||
id: tt::TokenId::unspecified(),
|
||||
})),
|
||||
tt::TokenTree::Subtree(tt::Subtree {
|
||||
delimiter: Some(tt::Delimiter {
|
||||
id: tt::TokenId::unspecified(),
|
||||
kind: tt::DelimiterKind::Brace,
|
||||
}),
|
||||
token_trees: vec![],
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
assert_eq!(s.to_string(), "struct T {}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rustc_server_from_str() {
|
||||
use std::str::FromStr;
|
||||
let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree {
|
||||
delimiter: Some(tt::Delimiter {
|
||||
id: tt::TokenId::unspecified(),
|
||||
kind: tt::DelimiterKind::Parenthesis,
|
||||
}),
|
||||
token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
|
||||
text: "a".into(),
|
||||
id: tt::TokenId::unspecified(),
|
||||
}))],
|
||||
});
|
||||
|
||||
let t1 = TokenStream::from_str("(a)").unwrap();
|
||||
assert_eq!(t1.token_trees.len(), 1);
|
||||
assert_eq!(t1.token_trees[0], subtree_paren_a);
|
||||
|
||||
let t2 = TokenStream::from_str("(a);").unwrap();
|
||||
assert_eq!(t2.token_trees.len(), 2);
|
||||
assert_eq!(t2.token_trees[0], subtree_paren_a);
|
||||
|
||||
let underscore = TokenStream::from_str("_").unwrap();
|
||||
assert_eq!(
|
||||
underscore.token_trees[0],
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
|
||||
text: "_".into(),
|
||||
id: tt::TokenId::unspecified(),
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
@ -26,10 +26,12 @@
|
||||
// pub(crate) so tests can use the TokenStream, more notes in test/utils.rs
|
||||
pub(crate) mod abi_1_47;
|
||||
mod abi_1_55;
|
||||
mod abi_1_56;
|
||||
|
||||
use super::dylib::LoadProcMacroDylibError;
|
||||
pub(crate) use abi_1_47::Abi as Abi_1_47;
|
||||
pub(crate) use abi_1_55::Abi as Abi_1_55;
|
||||
pub(crate) use abi_1_56::Abi as Abi_1_56;
|
||||
use libloading::Library;
|
||||
use proc_macro_api::{ProcMacroKind, RustCInfo};
|
||||
|
||||
@ -46,6 +48,7 @@ impl PanicMessage {
|
||||
pub(crate) enum Abi {
|
||||
Abi1_47(Abi_1_47),
|
||||
Abi1_55(Abi_1_55),
|
||||
Abi1_56(Abi_1_56),
|
||||
}
|
||||
|
||||
impl Abi {
|
||||
@ -70,9 +73,12 @@ impl Abi {
|
||||
} else if info.version.1 < 54 {
|
||||
let inner = unsafe { Abi_1_47::from_lib(lib, symbol_name) }?;
|
||||
Ok(Abi::Abi1_47(inner))
|
||||
} else {
|
||||
} else if info.version.1 < 56 {
|
||||
let inner = unsafe { Abi_1_55::from_lib(lib, symbol_name) }?;
|
||||
Ok(Abi::Abi1_55(inner))
|
||||
} else {
|
||||
let inner = unsafe { Abi_1_56::from_lib(lib, symbol_name) }?;
|
||||
Ok(Abi::Abi1_56(inner))
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,6 +91,7 @@ impl Abi {
|
||||
match self {
|
||||
Self::Abi1_55(abi) => abi.expand(macro_name, macro_body, attributes),
|
||||
Self::Abi1_47(abi) => abi.expand(macro_name, macro_body, attributes),
|
||||
Self::Abi1_56(abi) => abi.expand(macro_name, macro_body, attributes),
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,6 +99,7 @@ impl Abi {
|
||||
match self {
|
||||
Self::Abi1_47(abi) => abi.list_macros(),
|
||||
Self::Abi1_55(abi) => abi.list_macros(),
|
||||
Self::Abi1_56(abi) => abi.list_macros(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user