Skip to content
Open
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: 0 additions & 4 deletions compiler/rustc_expand/src/proc_macro_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,10 +556,6 @@ impl server::Server for Rustc<'_, '_> {
diag.emit();
}

fn ts_drop(&mut self, stream: Self::TokenStream) {
drop(stream);
}
Copy link
Copy Markdown
Member

@bjorn3 bjorn3 Jun 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this cause all tokenstreams to be unnecessarily retained on the server side until the end of the proc macro invocation even if the client drops it earlier?

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the motivation is missing for this change now - what did this drop do, what happens now when it's removed, why it's good.


fn ts_clone(&mut self, stream: &Self::TokenStream) -> Self::TokenStream {
stream.clone()
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1047,11 +1047,11 @@ impl CrateMetadata {

fn load_proc_macro<'tcx>(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> SyntaxExtension {
let (name, kind, helper_attrs) = match *self.raw_proc_macro(tcx, id) {
ProcMacro::CustomDerive { trait_name, attributes, client } => {
ProcMacro::CustomDerive { name, attributes, client } => {
let helper_attrs =
attributes.iter().cloned().map(Symbol::intern).collect::<Vec<_>>();
(
trait_name,
name,
SyntaxExtensionKind::Derive(Arc::new(DeriveProcMacro { client })),
helper_attrs,
)
Expand Down
99 changes: 24 additions & 75 deletions library/proc_macro/src/bridge/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
//! Buffer management for same-process client<->server communication.

use std::io::{self, Write};
use std::mem::{self, ManuallyDrop};
use std::ops::{Deref, DerefMut};
use std::slice;
use std::alloc::{self, Layout};
use std::ops::Deref;
use std::ptr::null_mut;
use std::{mem, slice};

#[repr(C)]
pub struct Buffer {
data: *mut u8,
len: usize,
capacity: usize,
reserve: extern "C" fn(Buffer, usize) -> Buffer,
drop: extern "C" fn(Buffer),
Copy link
Copy Markdown
Contributor

@petrochenkov petrochenkov Jun 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it better to directly manage Buffer memory without using Vec?

View changes since the review

}

unsafe impl Sync for Buffer {}
Expand All @@ -20,7 +18,7 @@ unsafe impl Send for Buffer {}
impl Default for Buffer {
#[inline]
fn default() -> Self {
Self::from(vec![])
Self { data: null_mut(), len: 0, capacity: 0 }
}
}

Expand All @@ -32,13 +30,6 @@ impl Deref for Buffer {
}
}

impl DerefMut for Buffer {
#[inline]
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.data, self.len) }
}
}

impl Buffer {
#[inline]
pub(super) fn new() -> Self {
Expand All @@ -55,27 +46,10 @@ impl Buffer {
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.
Copy link
Copy Markdown
Contributor

@petrochenkov petrochenkov Jun 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason to remove this optimization?
It would probably be better to benchmark it separately in any case.

View changes since the review

#[inline]
pub(super) fn extend_from_array<const N: usize>(&mut self, xs: &[u8; N]) {
if xs.len() > (self.capacity - self.len) {
let b = self.take();
*self = (b.reserve)(b, xs.len());
}
unsafe {
xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len());
self.len += xs.len();
}
}

#[inline]
pub(super) fn extend_from_slice(&mut self, xs: &[u8]) {
if xs.len() > (self.capacity - self.len) {
let b = self.take();
*self = (b.reserve)(b, xs.len());
self.reserve(xs.len());
}
unsafe {
xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len());
Expand All @@ -89,67 +63,42 @@ impl Buffer {
// 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);
self.reserve(1);
}
unsafe {
*self.data.add(self.len) = v;
self.len += 1;
}
}
}

impl Write for Buffer {
#[inline]
fn write(&mut self, xs: &[u8]) -> io::Result<usize> {
self.extend_from_slice(xs);
Ok(xs.len())
}

#[inline]
fn write_all(&mut self, xs: &[u8]) -> io::Result<()> {
self.extend_from_slice(xs);
Ok(())
fn layout(&self) -> Layout {
Layout::array::<u8>(self.capacity).unwrap()
}

#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
fn reserve(&mut self, amount: usize) {
debug_assert!(amount > 0);
self.capacity += amount;
assert!(self.capacity < isize::MAX as usize);
let layout = self.layout();
self.data = if self.data.is_null() {
unsafe { alloc::alloc(layout) }
} else {
unsafe { alloc::realloc(self.data, layout, self.capacity) }
};
if self.data.is_null() {
alloc::handle_alloc_error(layout);
}
}
}

impl Drop for Buffer {
#[inline]
fn drop(&mut self) {
let b = self.take();
(b.drop)(b);
}
}

impl From<Vec<u8>> for Buffer {
fn from(v: Vec<u8>) -> Self {
let mut v = ManuallyDrop::new(v);
let (data, len, capacity) = (v.as_mut_ptr(), v.len(), v.capacity());

// This utility function is nested in here because it can *only*
// be safely called on `Buffer`s created by *this* `proc_macro`.
fn to_vec(b: Buffer) -> Vec<u8> {
if !self.data.is_null() {
unsafe {
let b = ManuallyDrop::new(b);
Vec::from_raw_parts(b.data, b.len, b.capacity)
alloc::dealloc(self.data, self.layout());
}
}

extern "C" fn reserve(b: Buffer, additional: usize) -> Buffer {
let mut v = to_vec(b);
v.reserve(additional);
Buffer::from(v)
}

extern "C" fn drop(b: Buffer) {
mem::drop(to_vec(b));
}

Buffer { data, len, capacity, reserve, drop }
}
}
21 changes: 7 additions & 14 deletions library/proc_macro/src/bridge/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,6 @@ pub(crate) struct TokenStream {
impl !Send for TokenStream {}
impl !Sync for TokenStream {}

// Forward `Drop::drop` to the inherent `drop` method.
impl Drop for TokenStream {
fn drop(&mut self) {
Methods::ts_drop(TokenStream { handle: self.handle });
}
}

impl<S> Encode<S> for TokenStream {
fn encode(self, w: &mut Buffer, s: &mut S) {
mem::ManuallyDrop::new(self).handle.encode(w, s);
Expand Down Expand Up @@ -283,7 +276,7 @@ impl Client<crate::TokenStream, crate::TokenStream> {
pub const fn expand1(f: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy) -> Self {
Client {
run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| {
run_client(bridge, |input| f(input))
run_client(bridge, f)
}),
_marker: PhantomData,
}
Expand All @@ -307,7 +300,7 @@ impl Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream> {
#[derive(Copy, Clone)]
pub enum ProcMacro {
CustomDerive {
trait_name: &'static str,
name: &'static str,
attributes: &'static [&'static str],
client: Client<crate::TokenStream, crate::TokenStream>,
},
Expand All @@ -326,18 +319,18 @@ pub enum ProcMacro {
impl ProcMacro {
pub fn name(&self) -> &'static str {
match self {
ProcMacro::CustomDerive { trait_name, .. } => trait_name,
ProcMacro::Attr { name, .. } => name,
ProcMacro::Bang { name, .. } => name,
ProcMacro::CustomDerive { name, .. }
| ProcMacro::Attr { name, .. }
| ProcMacro::Bang { name, .. } => name,
}
}

pub const fn custom_derive(
trait_name: &'static str,
name: &'static str,
attributes: &'static [&'static str],
expand: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy,
) -> Self {
ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
ProcMacro::CustomDerive { name, attributes, client: Client::expand1(expand) }
}

pub const fn attr(
Expand Down
1 change: 0 additions & 1 deletion library/proc_macro/src/bridge/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ macro_rules! with_api {
fn literal_from_str(s: &str) -> Result<Literal<$Span, $Symbol>, String>;
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, ()>;
Expand Down
5 changes: 2 additions & 3 deletions library/proc_macro/src/bridge/rpc.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Serialization for client-server communication.

use std::any::Any;
use std::io::Write;
use std::num::NonZero;

use super::buffer::Buffer;
Expand All @@ -25,7 +24,7 @@ macro_rules! rpc_encode_decode {
let mut bytes = [0; $size];
bytes[..N].copy_from_slice(&self.to_le_bytes());

w.extend_from_array(&bytes);
w.extend_from_slice(&bytes);
}
}

Expand Down Expand Up @@ -168,7 +167,7 @@ impl<S> Encode<S> for &str {
fn encode(self, w: &mut Buffer, s: &mut S) {
let bytes = self.as_bytes();
bytes.len().encode(w, s);
w.write_all(bytes).unwrap();
w.extend_from_slice(bytes);
}
}

Expand Down
14 changes: 6 additions & 8 deletions library/proc_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
#![warn(unreachable_pub)]
#![deny(unsafe_op_in_unsafe_fn)]

#[unstable(feature = "proc_macro_internals", issue = "27812")]
#[unstable(feature = "proc_macro_internals", issue = "none")]
#[doc(hidden)]
pub mod bridge;

Expand Down Expand Up @@ -165,7 +165,7 @@ impl TokenStream {
/// Checks if this `TokenStream` is empty.
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
pub fn is_empty(&self) -> bool {
self.0.as_ref().map(|h| BridgeMethods::ts_is_empty(h)).unwrap_or(true)
self.0.as_ref().map(BridgeMethods::ts_is_empty).unwrap_or(true)
}

/// Parses this `TokenStream` as an expression and attempts to expand any
Expand Down Expand Up @@ -444,9 +444,7 @@ pub mod token_stream {
type IntoIter = IntoIter;

fn into_iter(self) -> IntoIter {
IntoIter(
self.0.map(|v| BridgeMethods::ts_into_trees(v)).unwrap_or_default().into_iter(),
)
IntoIter(self.0.map(BridgeMethods::ts_into_trees).unwrap_or_default().into_iter())
}
}
}
Expand All @@ -464,7 +462,7 @@ pub macro quote($($t:tt)*) {
/* compiler built-in */
}

#[unstable(feature = "proc_macro_internals", issue = "27812")]
#[unstable(feature = "proc_macro_internals", issue = "none")]
#[doc(hidden)]
mod quote;

Expand Down Expand Up @@ -624,14 +622,14 @@ impl Span {

// Used by the implementation of `Span::quote`
#[doc(hidden)]
#[unstable(feature = "proc_macro_internals", issue = "27812")]
#[unstable(feature = "proc_macro_internals", issue = "none")]
pub fn save_span(&self) -> usize {
BridgeMethods::span_save_span(self.0)
}

// Used by the implementation of `Span::quote`
#[doc(hidden)]
#[unstable(feature = "proc_macro_internals", issue = "27812")]
#[unstable(feature = "proc_macro_internals", issue = "none")]
pub fn recover_proc_macro_span(id: usize) -> Span {
Span(BridgeMethods::span_recover_proc_macro_span(id))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ impl ProcMacros {

for proc_macro in &self.0 {
match proc_macro {
bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
if *trait_name == macro_name =>
bridge::client::ProcMacro::CustomDerive { name, client, .. }
if *name == macro_name =>
{
let res = client.run(
&bridge::server::SAME_THREAD,
Expand Down Expand Up @@ -65,8 +65,8 @@ impl ProcMacros {

pub(crate) fn list_macros(&self) -> impl Iterator<Item = (&str, ProcMacroKind)> {
self.0.iter().map(|proc_macro| match *proc_macro {
bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
(trait_name, ProcMacroKind::CustomDerive)
bridge::client::ProcMacro::CustomDerive { name, .. } => {
(name, ProcMacroKind::CustomDerive)
}
bridge::client::ProcMacro::Bang { name, .. } => (name, ProcMacroKind::Bang),
bridge::client::ProcMacro::Attr { name, .. } => (name, ProcMacroKind::Attr),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ impl server::Server for RaSpanServer<'_> {
// FIXME handle diagnostic
}

fn ts_drop(&mut self, stream: Self::TokenStream) {
drop(stream);
}

fn ts_clone(&mut self, stream: &Self::TokenStream) -> Self::TokenStream {
stream.clone()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,6 @@ impl server::Server for SpanIdServer<'_> {

fn emit_diagnostic(&mut self, _: Diagnostic<Self::Span>) {}

fn ts_drop(&mut self, stream: Self::TokenStream) {
drop(stream);
}

fn ts_clone(&mut self, stream: &Self::TokenStream) -> Self::TokenStream {
stream.clone()
}
Expand Down
Loading