Skip to content

feat: Proc macro multi apbi proof of concept #9550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 13, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions crates/proc_macro_srv/src/abis/abi_1_47/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//! Macro ABI for version 1.47 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 use rustc_server::TokenStream;

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()
}
}
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/client.rs>
//! augmented with removing unstable features
use super::super::TokenStream as CrateTokenStream;
use super::*;

macro_rules! define_handles {
@@ -401,26 +402,26 @@ fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
b
}

impl Client<fn(crate::TokenStream) -> crate::TokenStream> {
pub fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self {
impl Client<fn(CrateTokenStream) -> CrateTokenStream> {
pub fn expand1(f: fn(CrateTokenStream) -> CrateTokenStream) -> Self {
extern "C" fn run(
bridge: Bridge<'_>,
f: impl FnOnce(crate::TokenStream) -> crate::TokenStream,
f: impl FnOnce(CrateTokenStream) -> CrateTokenStream,
) -> Buffer<u8> {
run_client(bridge, |input| f(crate::TokenStream(input)).0)
run_client(bridge, |input| f(CrateTokenStream(input)).0)
}
Client { get_handle_counters: HandleCounters::get, run, f }
}
}

impl Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> {
pub fn expand2(f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream) -> Self {
impl Client<fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream> {
pub fn expand2(f: fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream) -> Self {
extern "C" fn run(
bridge: Bridge<'_>,
f: impl FnOnce(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
f: impl FnOnce(CrateTokenStream, CrateTokenStream) -> CrateTokenStream,
) -> Buffer<u8> {
run_client(bridge, |(input, input2)| {
f(crate::TokenStream(input), crate::TokenStream(input2)).0
f(CrateTokenStream(input), CrateTokenStream(input2)).0
})
}
Client { get_handle_counters: HandleCounters::get, run, f }
@@ -433,17 +434,17 @@ pub enum ProcMacro {
CustomDerive {
trait_name: &'static str,
attributes: &'static [&'static str],
client: Client<fn(crate::TokenStream) -> crate::TokenStream>,
client: Client<fn(CrateTokenStream) -> CrateTokenStream>,
},

Attr {
name: &'static str,
client: Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream>,
client: Client<fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream>,
},

Bang {
name: &'static str,
client: Client<fn(crate::TokenStream) -> crate::TokenStream>,
client: Client<fn(CrateTokenStream) -> CrateTokenStream>,
},
}

@@ -465,19 +466,19 @@ impl ProcMacro {
pub fn custom_derive(
trait_name: &'static str,
attributes: &'static [&'static str],
expand: fn(crate::TokenStream) -> crate::TokenStream,
expand: fn(CrateTokenStream) -> CrateTokenStream,
) -> Self {
ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
}

pub fn attr(
name: &'static str,
expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
expand: fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream,
) -> Self {
ProcMacro::Attr { name, client: Client::expand2(expand) }
}

pub fn bang(name: &'static str, expand: fn(crate::TokenStream) -> crate::TokenStream) -> Self {
pub fn bang(name: &'static str, expand: fn(CrateTokenStream) -> CrateTokenStream) -> Self {
ProcMacro::Bang { name, client: Client::expand1(expand) }
}
}
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
#![deny(unsafe_code)]

pub use crate::proc_macro::{Delimiter, Level, LineColumn, Spacing};
pub use super::{Delimiter, Level, LineColumn, Spacing};
use std::fmt;
use std::hash::Hash;
use std::marker;
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/server.rs>
//! augmented with removing unstable features
use super::super::TokenStream as ProcMacroTokenStream;
use super::*;

// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
@@ -308,7 +309,7 @@ fn run_server<
Result::decode(&mut &b[..], &mut dispatcher.handle_store)
}

impl client::Client<fn(crate::TokenStream) -> crate::TokenStream> {
impl client::Client<fn(ProcMacroTokenStream) -> ProcMacroTokenStream> {
pub fn run<S: Server>(
&self,
strategy: &impl ExecutionStrategy,
@@ -330,7 +331,7 @@ impl client::Client<fn(crate::TokenStream) -> crate::TokenStream> {
}
}

impl client::Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> {
impl client::Client<fn(ProcMacroTokenStream, ProcMacroTokenStream) -> ProcMacroTokenStream> {
pub fn run<S: Server>(
&self,
strategy: &impl ExecutionStrategy,
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/diagnostic.rs>
//! augmented with removing unstable features
use crate::proc_macro::Span;
use super::Span;

/// An enum representing a diagnostic level.
#[derive(Copy, Clone, Debug)]
@@ -146,15 +146,15 @@ impl Diagnostic {

/// Emit the diagnostic.
pub fn emit(self) {
fn to_internal(spans: Vec<Span>) -> crate::proc_macro::bridge::client::MultiSpan {
let mut multi_span = crate::proc_macro::bridge::client::MultiSpan::new();
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 = crate::proc_macro::bridge::client::Diagnostic::new(
let mut diag = super::bridge::client::Diagnostic::new(
self.level,
&self.message[..],
to_internal(self.spans),
Original file line number Diff line number Diff line change
@@ -133,7 +133,7 @@ impl Extend<TokenStream> for TokenStream {

/// Public implementation details for the `TokenStream` type, such as iterators.
pub mod token_stream {
use crate::proc_macro::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree};
use super::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree};

/// An iterator over `TokenStream`'s `TokenTree`s.
/// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups,
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
//!
//! FIXME: No span and source file information is implemented yet
use crate::proc_macro::bridge::{self, server};
use super::proc_macro::bridge::{self, server};

use std::collections::HashMap;
use std::hash::Hash;
@@ -97,9 +97,9 @@ impl Extend<TokenStream> for TokenStream {
}
}

type Level = crate::proc_macro::Level;
type LineColumn = crate::proc_macro::LineColumn;
type SourceFile = crate::proc_macro::SourceFile;
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.
@@ -734,8 +734,8 @@ impl server::MultiSpan for Rustc {

#[cfg(test)]
mod tests {
use super::super::proc_macro::bridge::server::Literal;
use super::*;
use crate::proc_macro::bridge::server::Literal;

#[test]
fn test_rustc_server_literals() {
104 changes: 104 additions & 0 deletions crates/proc_macro_srv/src/abis/abi_1_55/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//! Macro ABI for version 1.55 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()
}
}
160 changes: 160 additions & 0 deletions crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
//! lib-proc-macro Buffer management for same-process client<->server communication.
//!
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/buffer.rs>
//! augmented with removing unstable features
use std::io::{self, Write};
use std::mem;
use std::ops::{Deref, DerefMut};
use std::slice;

#[repr(C)]
struct Slice<'a, T> {
data: &'a [T; 0],
len: usize,
}

unsafe impl<'a, T: Sync> Sync for Slice<'a, T> {}
unsafe impl<'a, T: Sync> Send for Slice<'a, T> {}

impl<'a, T> Copy for Slice<'a, T> {}
impl<'a, T> Clone for Slice<'a, T> {
fn clone(&self) -> Self {
*self
}
}

impl<'a, T> From<&'a [T]> for Slice<'a, T> {
fn from(xs: &'a [T]) -> Self {
Slice { data: unsafe { &*(xs.as_ptr() as *const [T; 0]) }, len: xs.len() }
}
}

impl<'a, T> Deref for Slice<'a, T> {
type Target = [T];
fn deref(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.data.as_ptr(), self.len) }
}
}

#[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)
}

pub(super) fn extend_from_slice(&mut self, xs: &[T]) {
if xs.len() > self.capacity.wrapping_sub(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 }
}
}
484 changes: 484 additions & 0 deletions crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/client.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! lib-proc-macro Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`.
//!
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/closure.rs>
//! augmented with removing unstable features
#[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> !Sync for Closure<'a, A, R> {}
// impl<'a, A, R> !Send for Closure<'a, A, R> {}

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) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//! lib-proc-macro Server-side handles and storage for per-handle data.
//!
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/handle.rs>
//! augmented with removing unstable features
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]
}
}
428 changes: 428 additions & 0 deletions crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/mod.rs

Large diffs are not rendered by default.

311 changes: 311 additions & 0 deletions crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/rpc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
//! lib-proc-macro Serialization for client-server communication.
//!
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/rpc.rs>
//! augmented with removing unstable features
//!
//! 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.write_all(&self.to_le_bytes()).unwrap();
}
}

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.write_all(&[self]).unwrap();
}
}

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`.
#[derive(Debug)]
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,
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//! lib-proc-macro `Cell` variant for (scoped) existential lifetimes.
//!
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/scoped_cell.rs#L1>
//! augmented with removing unstable features
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())
}
}
352 changes: 352 additions & 0 deletions crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
//! lib-proc-macro server-side traits
//!
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/server.rs>
//! augmented with removing unstable features
use super::super::TokenStream as CrateTokenStream;
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(CrateTokenStream) -> CrateTokenStream> {
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(CrateTokenStream, CrateTokenStream) -> CrateTokenStream> {
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 changes: 167 additions & 0 deletions crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/diagnostic.rs
Original file line number Diff line number Diff line change
@@ -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();
}
}
964 changes: 964 additions & 0 deletions crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/mod.rs

Large diffs are not rendered by default.

836 changes: 836 additions & 0 deletions crates/proc_macro_srv/src/abis/abi_1_55/rustc_server.rs

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions crates/proc_macro_srv/src/abis/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//! Procedural macros are implemented by compiling the macro providing crate
//! to a dynamic library with a particular ABI which the compiler uses to expand
//! macros. Unfortunately this ABI is not specified and can change from version
//! to version of the compiler. To support this we copy the ABI from the rust
//! compiler into submodules of this module (e.g proc_macro_srv::abis::abi_1_47).
//!
//! All of these ABIs are subsumed in the `Abi` enum, which exposes a simple
//! interface the rest of rust analyzer can use to talk to the macro
//! provider.
//!
//! # Adding a new ABI
//!
//! To add a new ABI you'll need to copy the source of the target proc_macro
//! crate from the source tree of the Rust compiler into this directory tree.
//! Then you'll need to modify it
//! - Remove any feature! or other things which won't compile on stable
//! - change any absolute imports to relative imports within the ABI tree
//!
//! Then you'll need to add a branch to the `Abi` enum and an implementation of
//! `Abi::expand`, `Abi::list_macros` and `Abi::from_lib` for the new ABI. See
//! `proc_macro_srv/src/abis/abi_1_47/mod.rs` for an example. Finally you'll
//! need to update the conditionals in `Abi::from_lib` to return your new ABI
//! for the relevant versions of the rust compiler
//!
// pub(crate) so tests can use the TokenStream, more notes in test/utils.rs
pub(crate) mod abi_1_47;
mod abi_1_55;

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;
use libloading::Library;
use proc_macro_api::{ProcMacroKind, RustCInfo};

pub struct PanicMessage {
message: Option<String>,
}

impl PanicMessage {
pub fn as_str(&self) -> Option<String> {
self.message.clone()
}
}

pub(crate) enum Abi {
Abi1_47(Abi_1_47),
Abi1_55(Abi_1_55),
}

impl Abi {
/// Load a new ABI.
///
/// # Arguments
///
/// *`lib` - The dynamic library containing the macro implementations
/// *`symbol_name` - The symbol name the macros can be found attributes
/// *`info` - RustCInfo about the compiler that was used to compile the
/// macro crate. This is the information we use to figure out
/// which ABI to return
pub fn from_lib(
lib: &Library,
symbol_name: String,
info: RustCInfo,
) -> Result<Abi, LoadProcMacroDylibError> {
if info.version.0 != 1 {
Err(LoadProcMacroDylibError::UnsupportedABI)
} else if info.version.1 < 47 {
Err(LoadProcMacroDylibError::UnsupportedABI)
} else if info.version.1 < 54 {
let inner = unsafe { Abi_1_47::from_lib(lib, symbol_name) }?;
Ok(Abi::Abi1_47(inner))
} else {
let inner = unsafe { Abi_1_55::from_lib(lib, symbol_name) }?;
Ok(Abi::Abi1_55(inner))
}
}

pub fn expand(
&self,
macro_name: &str,
macro_body: &tt::Subtree,
attributes: Option<&tt::Subtree>,
) -> Result<tt::Subtree, PanicMessage> {
match self {
Self::Abi1_55(abi) => abi.expand(macro_name, macro_body, attributes),
Self::Abi1_47(abi) => abi.expand(macro_name, macro_body, attributes),
}
}

pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
match self {
Self::Abi1_47(abi) => abi.list_macros(),
Self::Abi1_55(abi) => abi.list_macros(),
}
}
}
117 changes: 44 additions & 73 deletions crates/proc_macro_srv/src/dylib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Handles dynamic library loading for proc macro
use std::{
fmt,
fs::File,
io,
path::{Path, PathBuf},
@@ -9,9 +10,9 @@ use std::{
use libloading::Library;
use memmap2::Mmap;
use object::Object;
use proc_macro_api::ProcMacroKind;
use proc_macro_api::{read_dylib_info, ProcMacroKind};

use crate::{proc_macro::bridge, rustc_server::TokenStream};
use super::abis::Abi;

const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";

@@ -74,26 +75,52 @@ fn load_library(file: &Path) -> Result<Library, libloading::Error> {
unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) }
}

#[derive(Debug)]
pub enum LoadProcMacroDylibError {
Io(io::Error),
LibLoading(libloading::Error),
UnsupportedABI,
}

impl fmt::Display for LoadProcMacroDylibError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => e.fmt(f),
Self::UnsupportedABI => write!(f, "unsupported ABI version"),
Self::LibLoading(e) => e.fmt(f),
}
}
}

impl From<io::Error> for LoadProcMacroDylibError {
fn from(e: io::Error) -> Self {
LoadProcMacroDylibError::Io(e)
}
}

impl From<libloading::Error> for LoadProcMacroDylibError {
fn from(e: libloading::Error) -> Self {
LoadProcMacroDylibError::LibLoading(e)
}
}

struct ProcMacroLibraryLibloading {
// Hold the dylib to prevent it from unloading
// Hold on to the library so it doesn't unload
_lib: Library,
exported_macros: Vec<bridge::client::ProcMacro>,
abi: Abi,
}

impl ProcMacroLibraryLibloading {
fn open(file: &Path) -> io::Result<Self> {
fn open(file: &Path) -> Result<Self, LoadProcMacroDylibError> {
let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| {
invalid_data_err(format!("Cannot find registrar symbol in file {}", file.display()))
})?;

let lib = load_library(file).map_err(invalid_data_err)?;
let exported_macros = {
let macros: libloading::Symbol<&&[bridge::client::ProcMacro]> =
unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?;
macros.to_vec()
};
let version_info = read_dylib_info(file)?;

Ok(ProcMacroLibraryLibloading { _lib: lib, exported_macros })
let lib = load_library(file).map_err(invalid_data_err)?;
let abi = Abi::from_lib(&lib, symbol_name, version_info)?;
Ok(ProcMacroLibraryLibloading { _lib: lib, abi })
}
}

@@ -102,7 +129,7 @@ pub struct Expander {
}

impl Expander {
pub fn new(lib: &Path) -> io::Result<Expander> {
pub fn new(lib: &Path) -> Result<Expander, LoadProcMacroDylibError> {
// Some libraries for dynamic loading require canonicalized path even when it is
// already absolute
let lib = lib.canonicalize()?;
@@ -119,69 +146,13 @@ impl Expander {
macro_name: &str,
macro_body: &tt::Subtree,
attributes: Option<&tt::Subtree>,
) -> Result<tt::Subtree, bridge::PanicMessage> {
let parsed_body = TokenStream::with_subtree(macro_body.clone());

let parsed_attributes = attributes
.map_or(crate::rustc_server::TokenStream::new(), |attr| {
TokenStream::with_subtree(attr.clone())
});

for proc_macro in &self.inner.exported_macros {
match proc_macro {
bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
if *trait_name == macro_name =>
{
let res = client.run(
&crate::proc_macro::bridge::server::SameThread,
crate::rustc_server::Rustc::default(),
parsed_body,
false,
);
return res.map(|it| it.into_subtree());
}
bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
let res = client.run(
&crate::proc_macro::bridge::server::SameThread,
crate::rustc_server::Rustc::default(),
parsed_body,
false,
);
return res.map(|it| it.into_subtree());
}
bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
let res = client.run(
&crate::proc_macro::bridge::server::SameThread,
crate::rustc_server::Rustc::default(),
parsed_attributes,
parsed_body,
false,
);
return res.map(|it| it.into_subtree());
}
_ => continue,
}
}

Err(bridge::PanicMessage::String("Nothing to expand".to_string()))
) -> Result<tt::Subtree, String> {
let result = self.inner.abi.expand(macro_name, macro_body, attributes);
result.map_err(|e| e.as_str().unwrap_or_else(|| "<unknown error>".to_string()))
}

pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
self.inner
.exported_macros
.iter()
.map(|proc_macro| match proc_macro {
bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
(trait_name.to_string(), ProcMacroKind::CustomDerive)
}
bridge::client::ProcMacro::Bang { name, .. } => {
(name.to_string(), ProcMacroKind::FuncLike)
}
bridge::client::ProcMacro::Attr { name, .. } => {
(name.to_string(), ProcMacroKind::Attr)
}
})
.collect()
self.inner.abi.list_macros()
}
}

15 changes: 3 additions & 12 deletions crates/proc_macro_srv/src/lib.rs
Original file line number Diff line number Diff line change
@@ -11,16 +11,10 @@
//! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)…
#![allow(unreachable_pub)]

#[allow(dead_code)]
#[doc(hidden)]
mod proc_macro;

#[doc(hidden)]
mod rustc_server;

mod dylib;

use proc_macro::bridge::client::TokenStream;
mod abis;

use proc_macro_api::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
use std::{
collections::{hash_map::Entry, HashMap},
@@ -55,10 +49,7 @@ impl ProcMacroSrv {

match result {
Ok(expansion) => Ok(ExpansionResult { expansion }),
Err(msg) => {
let msg = msg.as_str().unwrap_or("<unknown error>");
Err(format!("proc-macro panicked: {}", msg))
}
Err(msg) => Err(format!("proc-macro panicked: {}", msg)),
}
}

10 changes: 8 additions & 2 deletions crates/proc_macro_srv/src/tests/utils.rs
Original file line number Diff line number Diff line change
@@ -12,8 +12,14 @@ pub mod fixtures {
}
}

fn parse_string(code: &str) -> Option<crate::rustc_server::TokenStream> {
Some(crate::rustc_server::TokenStream::from_str(code).unwrap())
fn parse_string(code: &str) -> Option<crate::abis::abi_1_47::TokenStream> {
// This is a bit strange. We need to parse a string into a token stream into
// order to create a tt::SubTree from it in fixtures. `into_subtree` is
// implemented by all the ABIs we have so we arbitrarily choose one ABI to
// write a `parse_string` function for and use that. The tests don't really
// care which ABI we're using as the `into_subtree` function isn't part of
// the ABI and shouldn't change between ABI versions.
crate::abis::abi_1_47::TokenStream::from_str(code).ok()
}

pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect) {