Skip to content
Merged
Show file tree
Hide file tree
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
4 changes: 2 additions & 2 deletions library/proc_macro/src/bridge/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,15 @@ macro_rules! define_client_side {
}
}
}
with_api!(self, define_client_side);
with_api!(define_client_side, TokenStream, Span, Symbol);

struct Bridge<'a> {
/// Reusable buffer (only `clear`-ed, never shrunk), primarily
/// used for making requests.
cached_buffer: Buffer,

/// Server-side function that the client uses to make requests.
dispatch: closure::Closure<'a, Buffer, Buffer>,
dispatch: closure::Closure<'a>,

/// Provided globals for this macro expansion.
globals: ExpnGlobals<Span>,
Expand Down
20 changes: 11 additions & 9 deletions library/proc_macro/src/bridge/closure.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`.
//! Closure type (equivalent to `&mut dyn FnMut(Buffer) -> Buffer`) that's `repr(C)`.

use std::marker::PhantomData;

use super::Buffer;

#[repr(C)]
pub(super) struct Closure<'a, A, R> {
call: unsafe extern "C" fn(*mut Env, A) -> R,
pub(super) struct Closure<'a> {
call: extern "C" fn(*mut Env, Buffer) -> Buffer,
env: *mut Env,
// Prevent Send and Sync impls.
//
Expand All @@ -14,17 +16,17 @@ pub(super) struct Closure<'a, A, R> {

struct Env;

impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> {
impl<'a, F: FnMut(Buffer) -> Buffer> From<&'a mut F> for Closure<'a> {
fn from(f: &'a mut F) -> Self {
unsafe extern "C" fn call<A, R, F: FnMut(A) -> R>(env: *mut Env, arg: A) -> R {
extern "C" fn call<F: FnMut(Buffer) -> Buffer>(env: *mut Env, arg: Buffer) -> Buffer {
unsafe { (*(env as *mut _ as *mut F))(arg) }
}
Closure { call: call::<A, R, F>, env: f as *mut _ as *mut Env, _marker: PhantomData }
Closure { call: call::<F>, env: f as *mut _ as *mut Env, _marker: PhantomData }
}
}

impl<'a, A, R> Closure<'a, A, R> {
pub(super) fn call(&mut self, arg: A) -> R {
unsafe { (self.call)(self.env, arg) }
impl<'a> Closure<'a> {
pub(super) fn call(&mut self, arg: Buffer) -> Buffer {
(self.call)(self.env, arg)
}
}
110 changes: 54 additions & 56 deletions library/proc_macro/src/bridge/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,71 +18,67 @@ use crate::{Delimiter, Level};
/// 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_macro)` expands to:
/// `with_api!(my_macro, MyTokenStream, MySpan, MySymbol)` expands to:
/// ```rust,ignore (pseudo-code)
/// my_macro! {
/// fn lit_character(ch: char) -> MySelf::Literal;
/// fn lit_span(lit: &MySelf::Literal) -> MySelf::Span;
/// fn lit_set_span(lit: &mut MySelf::Literal, span: MySelf::Span);
/// fn ts_clone(stream: &MyTokenStream) -> MyTokenStream;
/// fn span_debug(span: &MySpan) -> String;
/// // ...
/// }
/// ```
///
/// The first argument serves to customize the argument/return types,
/// to enable several different usecases:
///
/// 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`.
/// The second (`TokenStream`), third (`Span`) and fourth (`Symbol`)
/// argument serve to customize the argument/return types that need
/// special handling, to enable several different representations of
/// these types.
macro_rules! with_api {
($S:ident, $m:ident) => {
($m:ident, $TokenStream: path, $Span: path, $Symbol: path) => {
$m! {
fn injected_env_var(var: &str) -> Option<String>;
fn track_env_var(var: &str, value: Option<&str>);
fn track_path(path: &str);
fn literal_from_str(s: &str) -> Result<Literal<$S::Span, $S::Symbol>, ()>;
fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>);

fn ts_drop(stream: $S::TokenStream);
fn ts_clone(stream: &$S::TokenStream) -> $S::TokenStream;
fn ts_is_empty(stream: &$S::TokenStream) -> bool;
fn ts_expand_expr(stream: &$S::TokenStream) -> Result<$S::TokenStream, ()>;
fn ts_from_str(src: &str) -> $S::TokenStream;
fn ts_to_string(stream: &$S::TokenStream) -> String;
fn literal_from_str(s: &str) -> Result<Literal<$Span, $Symbol>, ()>;
fn emit_diagnostic(diagnostic: Diagnostic<$Span>);

fn ts_drop(stream: $TokenStream);
fn ts_clone(stream: &$TokenStream) -> $TokenStream;
fn ts_is_empty(stream: &$TokenStream) -> bool;
fn ts_expand_expr(stream: &$TokenStream) -> Result<$TokenStream, ()>;
fn ts_from_str(src: &str) -> $TokenStream;
fn ts_to_string(stream: &$TokenStream) -> String;
fn ts_from_token_tree(
tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>,
) -> $S::TokenStream;
tree: TokenTree<$TokenStream, $Span, $Symbol>,
) -> $TokenStream;
fn ts_concat_trees(
base: Option<$S::TokenStream>,
trees: Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol>>,
) -> $S::TokenStream;
base: Option<$TokenStream>,
trees: Vec<TokenTree<$TokenStream, $Span, $Symbol>>,
) -> $TokenStream;
fn ts_concat_streams(
base: Option<$S::TokenStream>,
streams: Vec<$S::TokenStream>,
) -> $S::TokenStream;
base: Option<$TokenStream>,
streams: Vec<$TokenStream>,
) -> $TokenStream;
fn ts_into_trees(
stream: $S::TokenStream
) -> Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol>>;

fn span_debug(span: $S::Span) -> String;
fn span_parent(span: $S::Span) -> Option<$S::Span>;
fn span_source(span: $S::Span) -> $S::Span;
fn span_byte_range(span: $S::Span) -> Range<usize>;
fn span_start(span: $S::Span) -> $S::Span;
fn span_end(span: $S::Span) -> $S::Span;
fn span_line(span: $S::Span) -> usize;
fn span_column(span: $S::Span) -> usize;
fn span_file(span: $S::Span) -> String;
fn span_local_file(span: $S::Span) -> Option<String>;
fn span_join(span: $S::Span, other: $S::Span) -> Option<$S::Span>;
fn span_subspan(span: $S::Span, start: Bound<usize>, end: Bound<usize>) -> Option<$S::Span>;
fn span_resolved_at(span: $S::Span, at: $S::Span) -> $S::Span;
fn span_source_text(span: $S::Span) -> Option<String>;
fn span_save_span(span: $S::Span) -> usize;
fn span_recover_proc_macro_span(id: usize) -> $S::Span;

fn symbol_normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>;
stream: $TokenStream
) -> Vec<TokenTree<$TokenStream, $Span, $Symbol>>;

fn span_debug(span: $Span) -> String;
fn span_parent(span: $Span) -> Option<$Span>;
fn span_source(span: $Span) -> $Span;
fn span_byte_range(span: $Span) -> Range<usize>;
fn span_start(span: $Span) -> $Span;
fn span_end(span: $Span) -> $Span;
fn span_line(span: $Span) -> usize;
fn span_column(span: $Span) -> usize;
fn span_file(span: $Span) -> String;
fn span_local_file(span: $Span) -> Option<String>;
fn span_join(span: $Span, other: $Span) -> Option<$Span>;
fn span_subspan(span: $Span, start: Bound<usize>, end: Bound<usize>) -> Option<$Span>;
fn span_resolved_at(span: $Span, at: $Span) -> $Span;
fn span_source_text(span: $Span) -> Option<String>;
fn span_save_span(span: $Span) -> usize;
fn span_recover_proc_macro_span(id: usize) -> $Span;

fn symbol_normalize_and_validate_ident(string: &str) -> Result<$Symbol, ()>;
}
};
}
Expand Down Expand Up @@ -126,7 +122,7 @@ pub struct BridgeConfig<'a> {
input: Buffer,

/// Server-side function that the client uses to make requests.
dispatch: closure::Closure<'a, Buffer, Buffer>,
dispatch: closure::Closure<'a>,

/// If 'true', always invoke the default panic hook
force_show_panics: bool,
Expand All @@ -146,7 +142,7 @@ macro_rules! declare_tags {
rpc_encode_decode!(enum ApiTags { $($method),* });
}
}
with_api!(self, declare_tags);
with_api!(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`
Expand All @@ -173,7 +169,7 @@ impl<T, M> Mark for Marked<T, M> {
self.value
}
}
impl<'a, T, M> Mark for &'a Marked<T, M> {
impl<'a, T> Mark for &'a Marked<T, client::TokenStream> {
type Unmarked = &'a T;
fn mark(_: Self::Unmarked) -> Self {
unreachable!()
Expand Down Expand Up @@ -220,6 +216,8 @@ mark_noop! {
Delimiter,
LitKind,
Level,
Bound<usize>,
Range<usize>,
}

rpc_encode_decode!(
Expand Down Expand Up @@ -318,7 +316,7 @@ macro_rules! compound_traits {
};
}

compound_traits!(
rpc_encode_decode!(
enum Bound<T> {
Included(x),
Excluded(x),
Expand Down Expand Up @@ -390,7 +388,7 @@ pub struct Literal<Span, Symbol> {
pub span: Span,
}

compound_traits!(struct Literal<Sp, Sy> { kind, symbol, suffix, span });
compound_traits!(struct Literal<Span, Symbol> { kind, symbol, suffix, span });

#[derive(Clone)]
pub enum TokenTree<TokenStream, Span, Symbol> {
Expand Down Expand Up @@ -434,6 +432,6 @@ compound_traits!(
struct ExpnGlobals<Span> { def_site, call_site, mixed_site }
);

compound_traits!(
rpc_encode_decode!(
struct Range<T> { start, end }
);
60 changes: 26 additions & 34 deletions library/proc_macro/src/bridge/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,45 +52,37 @@ macro_rules! rpc_encode_decode {
}
};
(enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => {
impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? {
fn encode(self, w: &mut Buffer, s: &mut S) {
// HACK(eddyb): `Tag` enum duplicated between the
// two impls as there's no other place to stash it.
#[allow(non_camel_case_types)]
#[repr(u8)]
enum Tag { $($variant),* }

match self {
$($name::$variant $(($field))* => {
(Tag::$variant as u8).encode(w, s);
$($field.encode(w, s);)*
})*
#[allow(non_upper_case_globals, non_camel_case_types)]
const _: () = {
#[repr(u8)] enum Tag { $($variant),* }

$(const $variant: u8 = Tag::$variant as u8;)*

impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? {
fn encode(self, w: &mut Buffer, s: &mut S) {
match self {
$($name::$variant $(($field))* => {
$variant.encode(w, s);
$($field.encode(w, s);)*
})*
}
}
}
}

impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S>
for $name $(<$($T),+>)?
{
fn decode(r: &mut &'a [u8], 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, non_camel_case_types)]
mod tag {
#[repr(u8)] enum Tag { $($variant),* }

$(pub(crate) const $variant: u8 = Tag::$variant as u8;)*
}

match u8::decode(r, s) {
$(tag::$variant => {
$(let $field = Decode::decode(r, s);)*
$name::$variant $(($field))*
})*
_ => unreachable!(),
impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S>
for $name $(<$($T),+>)?
{
fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
match u8::decode(r, s) {
$($variant => {
$(let $field = Decode::decode(r, s);)*
$name::$variant $(($field))*
})*
_ => unreachable!(),
}
}
}
}
};
}
}

Expand Down
64 changes: 22 additions & 42 deletions library/proc_macro/src/bridge/selfless_reify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,47 +38,27 @@

use std::mem;

// FIXME(eddyb) this could be `trait` impls except for the `const fn` requirement.
macro_rules! define_reify_functions {
($(
fn $name:ident $(<$($param:ident),*>)?
for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty;
)+) => {
$(pub(super) const fn $name<
$($($param,)*)?
F: Fn($($arg_ty),*) -> $ret_ty + Copy
>(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty {
// FIXME(eddyb) describe the `F` type (e.g. via `type_name::<F>`) once panic
// formatting becomes possible in `const fn`.
const { assert!(size_of::<F>() == 0, "selfless_reify: closure must be zero-sized"); }

$(extern $abi)? fn wrapper<
$($($param,)*)?
F: Fn($($arg_ty),*) -> $ret_ty + Copy
>($($arg: $arg_ty),*) -> $ret_ty {
let f = unsafe {
// SAFETY: `F` satisfies all criteria for "out of thin air"
// reconstructability (see module-level doc comment).
mem::MaybeUninit::<F>::uninit().assume_init()
};
f($($arg),*)
}
let _f_proof = f;
wrapper::<
$($($param,)*)?
F
>
})+
pub(super) const fn reify_to_extern_c_fn_hrt_bridge<
R,
F: Fn(super::BridgeConfig<'_>) -> R + Copy,
>(
f: F,
) -> extern "C" fn(super::BridgeConfig<'_>) -> R {
// FIXME(eddyb) describe the `F` type (e.g. via `type_name::<F>`) once panic
// formatting becomes possible in `const fn`.
const {
assert!(size_of::<F>() == 0, "selfless_reify: closure must be zero-sized");
}
}

define_reify_functions! {
fn _reify_to_extern_c_fn_unary<A, R> for extern "C" fn(arg: A) -> R;

// HACK(eddyb) this abstraction is used with `for<'a> fn(BridgeConfig<'a>)
// -> T` but that doesn't work with just `reify_to_extern_c_fn_unary`
// because of the `fn` pointer type being "higher-ranked" (i.e. the
// `for<'a>` binder).
// FIXME(eddyb) try to remove the lifetime from `BridgeConfig`, that'd help.
fn reify_to_extern_c_fn_hrt_bridge<R> for extern "C" fn(bridge: super::BridgeConfig<'_>) -> R;
extern "C" fn wrapper<R, F: Fn(super::BridgeConfig<'_>) -> R + Copy>(
bridge: super::BridgeConfig<'_>,
) -> R {
let f = unsafe {
// SAFETY: `F` satisfies all criteria for "out of thin air"
// reconstructability (see module-level doc comment).
mem::conjure_zst::<F>()
};
f(bridge)
}
let _f_proof = f;
wrapper::<R, F>
}
Loading
Loading