about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs330
-rw-r--r--library/proc_macro/src/bridge/client.rs20
-rw-r--r--library/proc_macro/src/bridge/mod.rs76
-rw-r--r--library/proc_macro/src/bridge/server.rs1
-rw-r--r--library/proc_macro/src/lib.rs138
5 files changed, 305 insertions, 260 deletions
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index ffd5e8763e3..0618c9aa084 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -14,9 +14,10 @@ use rustc_span::def_id::CrateNum;
 use rustc_span::symbol::{self, sym, Symbol};
 use rustc_span::{BytePos, FileName, Pos, SourceFile, Span};
 
-use pm::bridge::{server, DelimSpan, ExpnGlobals, Group, Ident, Punct, TokenTree};
+use pm::bridge::{
+    server, DelimSpan, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree,
+};
 use pm::{Delimiter, Level, LineColumn};
-use std::ascii;
 use std::ops::Bound;
 
 trait FromInternal<T> {
@@ -49,9 +50,40 @@ impl ToInternal<token::Delimiter> for Delimiter {
     }
 }
 
-impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)>
-    for Vec<TokenTree<TokenStream, Span, Symbol, Literal>>
-{
+impl FromInternal<token::LitKind> for LitKind {
+    fn from_internal(kind: token::LitKind) -> Self {
+        match kind {
+            token::Byte => LitKind::Byte,
+            token::Char => LitKind::Char,
+            token::Integer => LitKind::Integer,
+            token::Float => LitKind::Float,
+            token::Str => LitKind::Str,
+            token::StrRaw(n) => LitKind::StrRaw(n),
+            token::ByteStr => LitKind::ByteStr,
+            token::ByteStrRaw(n) => LitKind::ByteStrRaw(n),
+            token::Err => LitKind::Err,
+            token::Bool => unreachable!(),
+        }
+    }
+}
+
+impl ToInternal<token::LitKind> for LitKind {
+    fn to_internal(self) -> token::LitKind {
+        match self {
+            LitKind::Byte => token::Byte,
+            LitKind::Char => token::Char,
+            LitKind::Integer => token::Integer,
+            LitKind::Float => token::Float,
+            LitKind::Str => token::Str,
+            LitKind::StrRaw(n) => token::StrRaw(n),
+            LitKind::ByteStr => token::ByteStr,
+            LitKind::ByteStrRaw(n) => token::ByteStrRaw(n),
+            LitKind::Err => token::Err,
+        }
+    }
+}
+
+impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStream, Span, Symbol>> {
     fn from_internal((stream, rustc): (TokenStream, &mut Rustc<'_, '_>)) -> Self {
         use rustc_ast::token::*;
 
@@ -143,7 +175,14 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)>
                         TokenTree::Ident(Ident { sym: ident.name, is_raw: false, span }),
                     ]);
                 }
-                Literal(lit) => trees.push(TokenTree::Literal(self::Literal { lit, span })),
+                Literal(token::Lit { kind, symbol, suffix }) => {
+                    trees.push(TokenTree::Literal(self::Literal {
+                        kind: FromInternal::from_internal(kind),
+                        symbol,
+                        suffix,
+                        span,
+                    }));
+                }
                 DocComment(_, attr_style, data) => {
                     let mut escaped = String::new();
                     for ch in data.as_str().chars() {
@@ -199,9 +238,7 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)>
     }
 }
 
-impl ToInternal<TokenStream>
-    for (TokenTree<TokenStream, Span, Symbol, Literal>, &mut Rustc<'_, '_>)
-{
+impl ToInternal<TokenStream> for (TokenTree<TokenStream, Span, Symbol>, &mut Rustc<'_, '_>) {
     fn to_internal(self) -> TokenStream {
         use rustc_ast::token::*;
 
@@ -221,7 +258,9 @@ impl ToInternal<TokenStream>
                 return tokenstream::TokenTree::token(Ident(sym, is_raw), span).into();
             }
             TokenTree::Literal(self::Literal {
-                lit: token::Lit { kind: token::Integer, symbol, suffix },
+                kind: self::LitKind::Integer,
+                symbol,
+                suffix,
                 span,
             }) if symbol.as_str().starts_with('-') => {
                 let minus = BinOp(BinOpToken::Minus);
@@ -232,7 +271,9 @@ impl ToInternal<TokenStream>
                 return [a, b].into_iter().collect();
             }
             TokenTree::Literal(self::Literal {
-                lit: token::Lit { kind: token::Float, symbol, suffix },
+                kind: self::LitKind::Float,
+                symbol,
+                suffix,
                 span,
             }) if symbol.as_str().starts_with('-') => {
                 let minus = BinOp(BinOpToken::Minus);
@@ -242,8 +283,12 @@ impl ToInternal<TokenStream>
                 let b = tokenstream::TokenTree::token(float, span);
                 return [a, b].into_iter().collect();
             }
-            TokenTree::Literal(self::Literal { lit, span }) => {
-                return tokenstream::TokenTree::token(Literal(lit), span).into();
+            TokenTree::Literal(self::Literal { kind, symbol, suffix, span }) => {
+                return tokenstream::TokenTree::token(
+                    TokenKind::lit(kind.to_internal(), symbol, suffix),
+                    span,
+                )
+                .into();
             }
         };
 
@@ -292,13 +337,6 @@ impl ToInternal<rustc_errors::Level> for Level {
 
 pub struct FreeFunctions;
 
-// FIXME(eddyb) `Literal` should not expose internal `Debug` impls.
-#[derive(Clone, Debug)]
-pub struct Literal {
-    lit: token::Lit,
-    span: Span,
-}
-
 pub(crate) struct Rustc<'a, 'b> {
     ecx: &'a mut ExtCtxt<'b>,
     def_site: Span,
@@ -324,16 +362,11 @@ impl<'a, 'b> Rustc<'a, 'b> {
     fn sess(&self) -> &ParseSess {
         self.ecx.parse_sess()
     }
-
-    fn lit(&mut self, kind: token::LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Literal {
-        Literal { lit: token::Lit::new(kind, symbol, suffix), span: self.call_site }
-    }
 }
 
 impl server::Types for Rustc<'_, '_> {
     type FreeFunctions = FreeFunctions;
     type TokenStream = TokenStream;
-    type Literal = Literal;
     type SourceFile = Lrc<SourceFile>;
     type MultiSpan = Vec<Span>;
     type Diagnostic = Diagnostic;
@@ -352,6 +385,94 @@ impl server::FreeFunctions for Rustc<'_, '_> {
     fn track_path(&mut self, path: &str) {
         self.sess().file_depinfo.borrow_mut().insert(Symbol::intern(path));
     }
+
+    fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span, Self::Symbol>, ()> {
+        let name = FileName::proc_macro_source_code(s);
+        let mut parser = rustc_parse::new_parser_from_source_str(self.sess(), name, s.to_owned());
+
+        let first_span = parser.token.span.data();
+        let minus_present = parser.eat(&token::BinOp(token::Minus));
+
+        let lit_span = parser.token.span.data();
+        let token::Literal(mut lit) = parser.token.kind else {
+            return Err(());
+        };
+
+        // Check no comment or whitespace surrounding the (possibly negative)
+        // literal, or more tokens after it.
+        if (lit_span.hi.0 - first_span.lo.0) as usize != s.len() {
+            return Err(());
+        }
+
+        if minus_present {
+            // If minus is present, check no comment or whitespace in between it
+            // and the literal token.
+            if first_span.hi.0 != lit_span.lo.0 {
+                return Err(());
+            }
+
+            // Check literal is a kind we allow to be negated in a proc macro token.
+            match lit.kind {
+                token::LitKind::Bool
+                | token::LitKind::Byte
+                | token::LitKind::Char
+                | token::LitKind::Str
+                | token::LitKind::StrRaw(_)
+                | token::LitKind::ByteStr
+                | token::LitKind::ByteStrRaw(_)
+                | token::LitKind::Err => return Err(()),
+                token::LitKind::Integer | token::LitKind::Float => {}
+            }
+
+            // Synthesize a new symbol that includes the minus sign.
+            let symbol = Symbol::intern(&s[..1 + lit.symbol.as_str().len()]);
+            lit = token::Lit::new(lit.kind, symbol, lit.suffix);
+        }
+        let token::Lit { kind, symbol, suffix } = lit;
+        Ok(Literal {
+            kind: FromInternal::from_internal(kind),
+            symbol,
+            suffix,
+            span: self.call_site,
+        })
+    }
+
+    fn literal_subspan(
+        &mut self,
+        literal: Literal<Self::Span, Self::Symbol>,
+        start: Bound<usize>,
+        end: Bound<usize>,
+    ) -> Option<Self::Span> {
+        let span = literal.span;
+        let length = span.hi().to_usize() - span.lo().to_usize();
+
+        let start = match start {
+            Bound::Included(lo) => lo,
+            Bound::Excluded(lo) => lo.checked_add(1)?,
+            Bound::Unbounded => 0,
+        };
+
+        let end = match end {
+            Bound::Included(hi) => hi.checked_add(1)?,
+            Bound::Excluded(hi) => hi,
+            Bound::Unbounded => length,
+        };
+
+        // Bounds check the values, preventing addition overflow and OOB spans.
+        if start > u32::MAX as usize
+            || end > u32::MAX as usize
+            || (u32::MAX - start as u32) < span.lo().to_u32()
+            || (u32::MAX - end as u32) < span.lo().to_u32()
+            || start >= end
+            || end > length
+        {
+            return None;
+        }
+
+        let new_lo = span.lo() + BytePos::from_usize(start);
+        let new_hi = span.lo() + BytePos::from_usize(end);
+        Some(span.with_lo(new_lo).with_hi(new_hi))
+    }
 }
 
 impl server::TokenStream for Rustc<'_, '_> {
@@ -429,7 +550,7 @@ impl server::TokenStream for Rustc<'_, '_> {
 
     fn from_token_tree(
         &mut self,
-        tree: TokenTree<Self::TokenStream, Self::Span, Self::Symbol, Self::Literal>,
+        tree: TokenTree<Self::TokenStream, Self::Span, Self::Symbol>,
     ) -> Self::TokenStream {
         (tree, &mut *self).to_internal()
     }
@@ -437,7 +558,7 @@ impl server::TokenStream for Rustc<'_, '_> {
     fn concat_trees(
         &mut self,
         base: Option<Self::TokenStream>,
-        trees: Vec<TokenTree<Self::TokenStream, Self::Span, Self::Symbol, Self::Literal>>,
+        trees: Vec<TokenTree<Self::TokenStream, Self::Span, Self::Symbol>>,
     ) -> Self::TokenStream {
         let mut builder = tokenstream::TokenStreamBuilder::new();
         if let Some(base) = base {
@@ -467,164 +588,11 @@ impl server::TokenStream for Rustc<'_, '_> {
     fn into_trees(
         &mut self,
         stream: Self::TokenStream,
-    ) -> Vec<TokenTree<Self::TokenStream, Self::Span, Self::Symbol, Self::Literal>> {
+    ) -> Vec<TokenTree<Self::TokenStream, Self::Span, Self::Symbol>> {
         FromInternal::from_internal((stream, self))
     }
 }
 
-impl server::Literal for Rustc<'_, '_> {
-    fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> {
-        let name = FileName::proc_macro_source_code(s);
-        let mut parser = rustc_parse::new_parser_from_source_str(self.sess(), name, s.to_owned());
-
-        let first_span = parser.token.span.data();
-        let minus_present = parser.eat(&token::BinOp(token::Minus));
-
-        let lit_span = parser.token.span.data();
-        let token::Literal(mut lit) = parser.token.kind else {
-            return Err(());
-        };
-
-        // Check no comment or whitespace surrounding the (possibly negative)
-        // literal, or more tokens after it.
-        if (lit_span.hi.0 - first_span.lo.0) as usize != s.len() {
-            return Err(());
-        }
-
-        if minus_present {
-            // If minus is present, check no comment or whitespace in between it
-            // and the literal token.
-            if first_span.hi.0 != lit_span.lo.0 {
-                return Err(());
-            }
-
-            // Check literal is a kind we allow to be negated in a proc macro token.
-            match lit.kind {
-                token::LitKind::Bool
-                | token::LitKind::Byte
-                | token::LitKind::Char
-                | token::LitKind::Str
-                | token::LitKind::StrRaw(_)
-                | token::LitKind::ByteStr
-                | token::LitKind::ByteStrRaw(_)
-                | token::LitKind::Err => return Err(()),
-                token::LitKind::Integer | token::LitKind::Float => {}
-            }
-
-            // Synthesize a new symbol that includes the minus sign.
-            let symbol = Symbol::intern(&s[..1 + lit.symbol.as_str().len()]);
-            lit = token::Lit::new(lit.kind, symbol, lit.suffix);
-        }
-
-        Ok(Literal { lit, span: self.call_site })
-    }
-
-    fn to_string(&mut self, literal: &Self::Literal) -> String {
-        literal.lit.to_string()
-    }
-
-    fn debug_kind(&mut self, literal: &Self::Literal) -> String {
-        format!("{:?}", literal.lit.kind)
-    }
-
-    fn symbol(&mut self, literal: &Self::Literal) -> String {
-        literal.lit.symbol.to_string()
-    }
-
-    fn suffix(&mut self, literal: &Self::Literal) -> Option<String> {
-        literal.lit.suffix.as_ref().map(Symbol::to_string)
-    }
-
-    fn integer(&mut self, n: &str) -> Self::Literal {
-        self.lit(token::Integer, Symbol::intern(n), None)
-    }
-
-    fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal {
-        self.lit(token::Integer, Symbol::intern(n), Some(Symbol::intern(kind)))
-    }
-
-    fn float(&mut self, n: &str) -> Self::Literal {
-        self.lit(token::Float, Symbol::intern(n), None)
-    }
-
-    fn f32(&mut self, n: &str) -> Self::Literal {
-        self.lit(token::Float, Symbol::intern(n), Some(sym::f32))
-    }
-
-    fn f64(&mut self, n: &str) -> Self::Literal {
-        self.lit(token::Float, Symbol::intern(n), Some(sym::f64))
-    }
-
-    fn string(&mut self, string: &str) -> Self::Literal {
-        let quoted = format!("{:?}", string);
-        assert!(quoted.starts_with('"') && quoted.ends_with('"'));
-        let symbol = &quoted[1..quoted.len() - 1];
-        self.lit(token::Str, Symbol::intern(symbol), None)
-    }
-
-    fn character(&mut self, ch: char) -> Self::Literal {
-        let quoted = format!("{:?}", ch);
-        assert!(quoted.starts_with('\'') && quoted.ends_with('\''));
-        let symbol = &quoted[1..quoted.len() - 1];
-        self.lit(token::Char, Symbol::intern(symbol), None)
-    }
-
-    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>();
-        self.lit(token::ByteStr, Symbol::intern(&string), None)
-    }
-
-    fn span(&mut self, literal: &Self::Literal) -> Self::Span {
-        literal.span
-    }
-
-    fn set_span(&mut self, literal: &mut Self::Literal, span: Self::Span) {
-        literal.span = span;
-    }
-
-    fn subspan(
-        &mut self,
-        literal: &Self::Literal,
-        start: Bound<usize>,
-        end: Bound<usize>,
-    ) -> Option<Self::Span> {
-        let span = literal.span;
-        let length = span.hi().to_usize() - span.lo().to_usize();
-
-        let start = match start {
-            Bound::Included(lo) => lo,
-            Bound::Excluded(lo) => lo.checked_add(1)?,
-            Bound::Unbounded => 0,
-        };
-
-        let end = match end {
-            Bound::Included(hi) => hi.checked_add(1)?,
-            Bound::Excluded(hi) => hi,
-            Bound::Unbounded => length,
-        };
-
-        // Bounds check the values, preventing addition overflow and OOB spans.
-        if start > u32::MAX as usize
-            || end > u32::MAX as usize
-            || (u32::MAX - start as u32) < span.lo().to_u32()
-            || (u32::MAX - end as u32) < span.lo().to_u32()
-            || start >= end
-            || end > length
-        {
-            return None;
-        }
-
-        let new_lo = span.lo() + BytePos::from_usize(start);
-        let new_hi = span.lo() + BytePos::from_usize(end);
-        Some(span.with_lo(new_lo).with_hi(new_hi))
-    }
-}
-
 impl server::SourceFile for Rustc<'_, '_> {
     fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool {
         Lrc::ptr_eq(file1, file2)
diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs
index 6d48faf5413..1516f084ab8 100644
--- a/library/proc_macro/src/bridge/client.rs
+++ b/library/proc_macro/src/bridge/client.rs
@@ -175,7 +175,6 @@ define_handles! {
     'owned:
     FreeFunctions,
     TokenStream,
-    Literal,
     SourceFile,
     MultiSpan,
     Diagnostic,
@@ -196,25 +195,6 @@ impl Clone for TokenStream {
     }
 }
 
-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()
diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs
index 1015c0d7259..712b8c63797 100644
--- a/library/proc_macro/src/bridge/mod.rs
+++ b/library/proc_macro/src/bridge/mod.rs
@@ -56,6 +56,8 @@ macro_rules! with_api {
                 fn drop($self: $S::FreeFunctions);
                 fn track_env_var(var: &str, value: Option<&str>);
                 fn track_path(path: &str);
+                fn literal_from_str(s: &str) -> Result<Literal<$S::Span, $S::Symbol>, ()>;
+                fn literal_subspan(lit: Literal<$S::Span, $S::Symbol>, start: Bound<usize>, end: Bound<usize>) -> Option<$S::Span>;
             },
             TokenStream {
                 fn drop($self: $S::TokenStream);
@@ -65,11 +67,11 @@ macro_rules! with_api {
                 fn from_str(src: &str) -> $S::TokenStream;
                 fn to_string($self: &$S::TokenStream) -> String;
                 fn from_token_tree(
-                    tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol, $S::Literal>,
+                    tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>,
                 ) -> $S::TokenStream;
                 fn concat_trees(
                     base: Option<$S::TokenStream>,
-                    trees: Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol, $S::Literal>>,
+                    trees: Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol>>,
                 ) -> $S::TokenStream;
                 fn concat_streams(
                     base: Option<$S::TokenStream>,
@@ -77,31 +79,7 @@ macro_rules! with_api {
                 ) -> $S::TokenStream;
                 fn into_trees(
                     $self: $S::TokenStream
-                ) -> Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol, $S::Literal>>;
-            },
-            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>;
+                ) -> Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol>>;
             },
             SourceFile {
                 fn drop($self: $S::SourceFile);
@@ -332,6 +310,7 @@ mark_noop! {
     u8,
     usize,
     Delimiter,
+    LitKind,
     Level,
     LineColumn,
     Spacing,
@@ -361,6 +340,33 @@ rpc_encode_decode!(
     }
 );
 
+#[derive(Copy, Clone, Eq, PartialEq, Debug)]
+pub enum LitKind {
+    Byte,
+    Char,
+    Integer,
+    Float,
+    Str,
+    StrRaw(u8),
+    ByteStr,
+    ByteStrRaw(u8),
+    Err,
+}
+
+rpc_encode_decode!(
+    enum LitKind {
+        Byte,
+        Char,
+        Integer,
+        Float,
+        Str,
+        StrRaw(n),
+        ByteStr,
+        ByteStrRaw(n),
+        Err,
+    }
+);
+
 macro_rules! mark_compound {
     (struct $name:ident <$($T:ident),+> { $($field:ident),* $(,)? }) => {
         impl<$($T: Mark),+> Mark for $name <$($T),+> {
@@ -477,16 +483,26 @@ pub struct Ident<Span, Symbol> {
 
 compound_traits!(struct Ident<Span, Symbol> { sym, is_raw, span });
 
+#[derive(Clone, Eq, PartialEq)]
+pub struct Literal<Span, Symbol> {
+    pub kind: LitKind,
+    pub symbol: Symbol,
+    pub suffix: Option<Symbol>,
+    pub span: Span,
+}
+
+compound_traits!(struct Literal<Sp, Sy> { kind, symbol, suffix, span });
+
 #[derive(Clone)]
-pub enum TokenTree<TokenStream, Span, Symbol, Literal> {
+pub enum TokenTree<TokenStream, Span, Symbol> {
     Group(Group<TokenStream, Span>),
     Punct(Punct<Span>),
     Ident(Ident<Span, Symbol>),
-    Literal(Literal),
+    Literal(Literal<Span, Symbol>),
 }
 
 compound_traits!(
-    enum TokenTree<TokenStream, Span, Symbol, Literal> {
+    enum TokenTree<TokenStream, Span, Symbol> {
         Group(tt),
         Punct(tt),
         Ident(tt),
diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs
index 53668ecfead..d46e325951d 100644
--- a/library/proc_macro/src/bridge/server.rs
+++ b/library/proc_macro/src/bridge/server.rs
@@ -8,7 +8,6 @@ use super::client::HandleStore;
 pub trait Types {
     type FreeFunctions: 'static;
     type TokenStream: 'static + Clone;
-    type Literal: 'static + Clone;
     type SourceFile: 'static + Clone;
     type MultiSpan: 'static;
     type Diagnostic: 'static;
diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index 911deaef8c9..372bd3ac214 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -215,12 +215,7 @@ pub use quote::{quote, quote_span};
 
 fn tree_to_bridge_tree(
     tree: TokenTree,
-) -> bridge::TokenTree<
-    bridge::client::TokenStream,
-    bridge::client::Span,
-    bridge::client::Symbol,
-    bridge::client::Literal,
-> {
+) -> bridge::TokenTree<bridge::client::TokenStream, bridge::client::Span, bridge::client::Symbol> {
     match tree {
         TokenTree::Group(tt) => bridge::TokenTree::Group(tt.0),
         TokenTree::Punct(tt) => bridge::TokenTree::Punct(tt.0),
@@ -245,7 +240,6 @@ struct ConcatTreesHelper {
             bridge::client::TokenStream,
             bridge::client::Span,
             bridge::client::Symbol,
-            bridge::client::Literal,
         >,
     >,
 }
@@ -372,7 +366,6 @@ pub mod token_stream {
                 bridge::client::TokenStream,
                 bridge::client::Span,
                 bridge::client::Symbol,
-                bridge::client::Literal,
             >,
         >,
     );
@@ -1147,7 +1140,7 @@ impl fmt::Debug for Ident {
 /// Boolean literals like `true` and `false` do not belong here, they are `Ident`s.
 #[derive(Clone)]
 #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
-pub struct Literal(bridge::client::Literal);
+pub struct Literal(bridge::Literal<bridge::client::Span, bridge::client::Symbol>);
 
 macro_rules! suffixed_int_literals {
     ($($name:ident => $kind:ident,)*) => ($(
@@ -1164,7 +1157,12 @@ macro_rules! suffixed_int_literals {
         /// below.
         #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
         pub fn $name(n: $kind) -> Literal {
-            Literal(bridge::client::Literal::typed_integer(&n.to_string(), stringify!($kind)))
+            Literal(bridge::Literal {
+                kind: bridge::LitKind::Integer,
+                symbol: bridge::client::Symbol::new(&n.to_string()),
+                suffix: Some(bridge::client::Symbol::new(stringify!($kind))),
+                span: Span::call_site().0,
+            })
         }
     )*)
 }
@@ -1186,12 +1184,26 @@ macro_rules! unsuffixed_int_literals {
         /// below.
         #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
         pub fn $name(n: $kind) -> Literal {
-            Literal(bridge::client::Literal::integer(&n.to_string()))
+            Literal(bridge::Literal {
+                kind: bridge::LitKind::Integer,
+                symbol: bridge::client::Symbol::new(&n.to_string()),
+                suffix: None,
+                span: Span::call_site().0,
+            })
         }
     )*)
 }
 
 impl Literal {
+    fn new(kind: bridge::LitKind, value: &str, suffix: Option<&str>) -> Self {
+        Literal(bridge::Literal {
+            kind,
+            symbol: bridge::client::Symbol::new(value),
+            suffix: suffix.map(bridge::client::Symbol::new),
+            span: Span::call_site().0,
+        })
+    }
+
     suffixed_int_literals! {
         u8_suffixed => u8,
         u16_suffixed => u16,
@@ -1243,7 +1255,7 @@ impl Literal {
         if !repr.contains('.') {
             repr.push_str(".0");
         }
-        Literal(bridge::client::Literal::float(&repr))
+        Literal::new(bridge::LitKind::Float, &repr, None)
     }
 
     /// Creates a new suffixed floating-point literal.
@@ -1264,7 +1276,7 @@ impl Literal {
         if !n.is_finite() {
             panic!("Invalid float literal {n}");
         }
-        Literal(bridge::client::Literal::f32(&n.to_string()))
+        Literal::new(bridge::LitKind::Float, &n.to_string(), Some("f32"))
     }
 
     /// Creates a new unsuffixed floating-point literal.
@@ -1288,7 +1300,7 @@ impl Literal {
         if !repr.contains('.') {
             repr.push_str(".0");
         }
-        Literal(bridge::client::Literal::float(&repr))
+        Literal::new(bridge::LitKind::Float, &repr, None)
     }
 
     /// Creates a new suffixed floating-point literal.
@@ -1309,37 +1321,49 @@ impl Literal {
         if !n.is_finite() {
             panic!("Invalid float literal {n}");
         }
-        Literal(bridge::client::Literal::f64(&n.to_string()))
+        Literal::new(bridge::LitKind::Float, &n.to_string(), Some("f64"))
     }
 
     /// String literal.
     #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
     pub fn string(string: &str) -> Literal {
-        Literal(bridge::client::Literal::string(string))
+        let quoted = format!("{:?}", string);
+        assert!(quoted.starts_with('"') && quoted.ends_with('"'));
+        let symbol = &quoted[1..quoted.len() - 1];
+        Literal::new(bridge::LitKind::Str, symbol, None)
     }
 
     /// Character literal.
     #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
     pub fn character(ch: char) -> Literal {
-        Literal(bridge::client::Literal::character(ch))
+        let quoted = format!("{:?}", ch);
+        assert!(quoted.starts_with('\'') && quoted.ends_with('\''));
+        let symbol = &quoted[1..quoted.len() - 1];
+        Literal::new(bridge::LitKind::Char, symbol, None)
     }
 
     /// Byte string literal.
     #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
     pub fn byte_string(bytes: &[u8]) -> Literal {
-        Literal(bridge::client::Literal::byte_string(bytes))
+        let string = bytes
+            .iter()
+            .cloned()
+            .flat_map(std::ascii::escape_default)
+            .map(Into::<char>::into)
+            .collect::<String>();
+        Literal::new(bridge::LitKind::ByteStr, &string, None)
     }
 
     /// Returns the span encompassing this literal.
     #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
     pub fn span(&self) -> Span {
-        Span(self.0.span())
+        Span(self.0.span)
     }
 
     /// Configures the span associated for this literal.
     #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
     pub fn set_span(&mut self, span: Span) {
-        self.0.set_span(span.0);
+        self.0.span = span.0;
     }
 
     /// Returns a `Span` that is a subset of `self.span()` containing only the
@@ -1355,7 +1379,55 @@ impl Literal {
     // was 'c' or whether it was '\u{63}'.
     #[unstable(feature = "proc_macro_span", issue = "54725")]
     pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span> {
-        self.0.subspan(range.start_bound().cloned(), range.end_bound().cloned()).map(Span)
+        bridge::client::FreeFunctions::literal_subspan(
+            self.0.clone(),
+            range.start_bound().cloned(),
+            range.end_bound().cloned(),
+        )
+        .map(Span)
+    }
+
+    fn with_symbol_and_suffix<R>(&self, f: impl FnOnce(&str, &str) -> R) -> R {
+        self.0.symbol.with(|symbol| match self.0.suffix {
+            Some(suffix) => suffix.with(|suffix| f(symbol, suffix)),
+            None => f(symbol, ""),
+        })
+    }
+
+    /// Invokes the callback with a `&[&str]` consisting of each part of the
+    /// literal's representation. This is done to allow the `ToString` and
+    /// `Display` implementations to borrow references to symbol values, and
+    /// both be optimized to reduce overhead.
+    fn with_stringify_parts<R>(&self, f: impl FnOnce(&[&str]) -> R) -> R {
+        /// Returns a string containing exactly `num` '#' characters.
+        /// Uses a 256-character source string literal which is always safe to
+        /// index with a `u8` index.
+        fn get_hashes_str(num: u8) -> &'static str {
+            const HASHES: &str = "\
+            ################################################################\
+            ################################################################\
+            ################################################################\
+            ################################################################\
+            ";
+            const _: () = assert!(HASHES.len() == 256);
+            &HASHES[..num as usize]
+        }
+
+        self.with_symbol_and_suffix(|symbol, suffix| match self.0.kind {
+            bridge::LitKind::Byte => f(&["b'", symbol, "'", suffix]),
+            bridge::LitKind::Char => f(&["'", symbol, "'", suffix]),
+            bridge::LitKind::Str => f(&["\"", symbol, "\"", suffix]),
+            bridge::LitKind::StrRaw(n) => {
+                let hashes = get_hashes_str(n);
+                f(&["r", hashes, "\"", symbol, "\"", hashes, suffix])
+            }
+            bridge::LitKind::ByteStr => f(&["b\"", symbol, "\"", suffix]),
+            bridge::LitKind::ByteStrRaw(n) => {
+                let hashes = get_hashes_str(n);
+                f(&["br", hashes, "\"", symbol, "\"", hashes, suffix])
+            }
+            _ => f(&[symbol, suffix]),
+        })
     }
 }
 
@@ -1374,19 +1446,17 @@ impl FromStr for Literal {
     type Err = LexError;
 
     fn from_str(src: &str) -> Result<Self, LexError> {
-        match bridge::client::Literal::from_str(src) {
+        match bridge::client::FreeFunctions::literal_from_str(src) {
             Ok(literal) => Ok(Literal(literal)),
             Err(()) => Err(LexError),
         }
     }
 }
 
-// N.B., the bridge only provides `to_string`, implement `fmt::Display`
-// based on it (the reverse of the usual relationship between the two).
-#[stable(feature = "proc_macro_lib", since = "1.15.0")]
+#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
 impl ToString for Literal {
     fn to_string(&self) -> String {
-        self.0.to_string()
+        self.with_stringify_parts(|parts| parts.concat())
     }
 }
 
@@ -1395,14 +1465,26 @@ impl ToString for Literal {
 #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
 impl fmt::Display for Literal {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(&self.to_string())
+        self.with_stringify_parts(|parts| {
+            for part in parts {
+                fmt::Display::fmt(part, f)?;
+            }
+            Ok(())
+        })
     }
 }
 
 #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
 impl fmt::Debug for Literal {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.fmt(f)
+        f.debug_struct("Literal")
+            // format the kind on one line even in {:#?} mode
+            .field("kind", &format_args!("{:?}", &self.0.kind))
+            .field("symbol", &self.0.symbol)
+            // format `Some("...")` on one line even in {:#?} mode
+            .field("suffix", &format_args!("{:?}", &self.0.suffix))
+            .field("span", &self.0.span)
+            .finish()
     }
 }