about summary refs log tree commit diff
diff options
context:
space:
mode:
authorThe 8472 <git@infinite-source.de>2023-07-02 22:43:39 +0200
committerThe 8472 <git@infinite-source.de>2023-07-03 13:29:15 +0200
commit14e57d7c134cc80878c10f2e2bb61e62cc6a2fdb (patch)
treeb970f018ef5091a430589e8c2a1c152252d4df18
parent6162f6f12339aa81fe16b8a64644ead497e411b2 (diff)
downloadrust-14e57d7c134cc80878c10f2e2bb61e62cc6a2fdb.tar.gz
rust-14e57d7c134cc80878c10f2e2bb61e62cc6a2fdb.zip
perform TokenStream replacement in-place when possible in expand_macro
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs21
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs3
2 files changed, 19 insertions, 5 deletions
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index db296aa44db..45c0586424a 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -25,7 +25,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use rustc_span::{Span, DUMMY_SP};
 use smallvec::{smallvec, SmallVec};
 
-use std::{fmt, iter};
+use std::{fmt, iter, mem};
 
 /// When the main Rust parser encounters a syntax-extension invocation, it
 /// parses the arguments to the invocation as a token tree. This is a very
@@ -410,8 +410,23 @@ impl TokenStream {
         t1.next().is_none() && t2.next().is_none()
     }
 
-    pub fn map_enumerated<F: FnMut(usize, &TokenTree) -> TokenTree>(self, mut f: F) -> TokenStream {
-        TokenStream(Lrc::new(self.0.iter().enumerate().map(|(i, tree)| f(i, tree)).collect()))
+    /// Applies the supplied function to each `TokenTree` and its index in `self`, returning a new `TokenStream`
+    ///
+    /// It is equivalent to `TokenStream::new(self.trees().cloned().enumerate().map(|(i, tt)| f(i, tt)).collect())`.
+    pub fn map_enumerated_owned(
+        mut self,
+        mut f: impl FnMut(usize, TokenTree) -> TokenTree,
+    ) -> TokenStream {
+        if let Some(inner) = Lrc::get_mut(&mut self.0) {
+            // optimization: perform the map in-place if self's reference count is 1
+            let owned = mem::take(inner);
+            *inner = owned.into_iter().enumerate().map(|(i, tree)| f(i, tree)).collect();
+            self
+        } else {
+            TokenStream(Lrc::new(
+                self.0.iter().enumerate().map(|(i, tree)| f(i, tree.clone())).collect(),
+            ))
+        }
     }
 
     /// Create a token stream containing a single token with alone spacing.
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 4a8a14994ff..42cc0a6b143 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -223,8 +223,7 @@ fn expand_macro<'cx>(
             // Replace all the tokens for the corresponding positions in the macro, to maintain
             // proper positions in error reporting, while maintaining the macro_backtrace.
             if tts.len() == rhs.tts.len() {
-                tts = tts.map_enumerated(|i, tt| {
-                    let mut tt = tt.clone();
+                tts = tts.map_enumerated_owned(|i, mut tt| {
                     let rhs_tt = &rhs.tts[i];
                     let ctxt = tt.span().ctxt();
                     match (&mut tt, rhs_tt) {