Skip to content

Commit b559042

Browse files
committed
Auto merge of #53304 - dtolnay:extend, r=dtolnay
TokenStream::extend Two new insta-stable impls in libproc_macro: ```rust impl Extend<TokenTree> for TokenStream impl Extend<TokenStream> for TokenStream ``` `proc_macro::TokenStream` already implements `FromIterator<TokenTree>` and `FromIterator<TokenStream>` so I elected to support the same input types for `Extend`. **This commit reduces compile time of Serde derives by 60% (takes less than half as long to compile)** as measured by building our test suite: ```console $ git clone https://github.com/serde-rs/serde $ cd serde/test_suite $ cargo check --tests --features proc-macro2/nightly $ rm -f ../target/debug/deps/libtest_*.rmeta $ time cargo check --tests --features proc-macro2/nightly Before: 20.8 seconds After: 8.6 seconds ``` r? @alexcrichton
2 parents 5050349 + 69b9c23 commit b559042

File tree

4 files changed

+279
-16
lines changed

4 files changed

+279
-16
lines changed

src/libproc_macro/lib.rs

+14
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,20 @@ impl iter::FromIterator<TokenStream> for TokenStream {
180180
}
181181
}
182182

183+
#[stable(feature = "token_stream_extend", since = "1.30.0")]
184+
impl Extend<TokenTree> for TokenStream {
185+
fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, trees: I) {
186+
self.extend(trees.into_iter().map(TokenStream::from));
187+
}
188+
}
189+
190+
#[stable(feature = "token_stream_extend", since = "1.30.0")]
191+
impl Extend<TokenStream> for TokenStream {
192+
fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) {
193+
self.0.extend(streams.into_iter().map(|stream| stream.0));
194+
}
195+
}
196+
183197
/// Public implementation details for the `TokenStream` type, such as iterators.
184198
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
185199
pub mod token_stream {

src/libsyntax/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ pub mod util {
130130

131131
mod rc_slice;
132132
pub use self::rc_slice::RcSlice;
133+
134+
mod rc_vec;
135+
pub use self::rc_vec::RcVec;
133136
}
134137

135138
pub mod json;

src/libsyntax/tokenstream.rs

+172-16
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use parse::Directory;
2929
use parse::token::{self, Token};
3030
use print::pprust;
3131
use serialize::{Decoder, Decodable, Encoder, Encodable};
32-
use util::RcSlice;
32+
use util::RcVec;
3333

3434
use std::borrow::Cow;
3535
use std::{fmt, iter, mem};
@@ -221,7 +221,7 @@ impl TokenStream {
221221
new_slice.extend_from_slice(parts.0);
222222
new_slice.push(comma);
223223
new_slice.extend_from_slice(parts.1);
224-
let slice = RcSlice::new(new_slice);
224+
let slice = RcVec::new(new_slice);
225225
return Some((TokenStream { kind: TokenStreamKind::Stream(slice) }, sp));
226226
}
227227
}
@@ -234,7 +234,7 @@ enum TokenStreamKind {
234234
Empty,
235235
Tree(TokenTree),
236236
JointTree(TokenTree),
237-
Stream(RcSlice<TokenStream>),
237+
Stream(RcVec<TokenStream>),
238238
}
239239

240240
impl From<TokenTree> for TokenStream {
@@ -255,6 +255,60 @@ impl<T: Into<TokenStream>> iter::FromIterator<T> for TokenStream {
255255
}
256256
}
257257

258+
impl Extend<TokenStream> for TokenStream {
259+
fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, iter: I) {
260+
let iter = iter.into_iter();
261+
let kind = mem::replace(&mut self.kind, TokenStreamKind::Empty);
262+
263+
// Vector of token streams originally in self.
264+
let tts: Vec<TokenStream> = match kind {
265+
TokenStreamKind::Empty => {
266+
let mut vec = Vec::new();
267+
vec.reserve(iter.size_hint().0);
268+
vec
269+
}
270+
TokenStreamKind::Tree(_) | TokenStreamKind::JointTree(_) => {
271+
let mut vec = Vec::new();
272+
vec.reserve(1 + iter.size_hint().0);
273+
vec.push(TokenStream { kind });
274+
vec
275+
}
276+
TokenStreamKind::Stream(rc_vec) => match RcVec::try_unwrap(rc_vec) {
277+
Ok(mut vec) => {
278+
// Extend in place using the existing capacity if possible.
279+
// This is the fast path for libraries like `quote` that
280+
// build a token stream.
281+
vec.reserve(iter.size_hint().0);
282+
vec
283+
}
284+
Err(rc_vec) => {
285+
// Self is shared so we need to copy and extend that.
286+
let mut vec = Vec::new();
287+
vec.reserve(rc_vec.len() + iter.size_hint().0);
288+
vec.extend_from_slice(&rc_vec);
289+
vec
290+
}
291+
}
292+
};
293+
294+
// Perform the extend, joining tokens as needed along the way.
295+
let mut builder = TokenStreamBuilder(tts);
296+
for stream in iter {
297+
builder.push(stream);
298+
}
299+
300+
// Build the resulting token stream. If it contains more than one token,
301+
// preserve capacity in the vector in anticipation of the caller
302+
// performing additional calls to extend.
303+
let mut tts = builder.0;
304+
*self = match tts.len() {
305+
0 => TokenStream::empty(),
306+
1 => tts.pop().unwrap(),
307+
_ => TokenStream::concat_rc_vec(RcVec::new_preserving_capacity(tts)),
308+
};
309+
}
310+
}
311+
258312
impl Eq for TokenStream {}
259313

260314
impl PartialEq<TokenStream> for TokenStream {
@@ -287,11 +341,11 @@ impl TokenStream {
287341
match streams.len() {
288342
0 => TokenStream::empty(),
289343
1 => streams.pop().unwrap(),
290-
_ => TokenStream::concat_rc_slice(RcSlice::new(streams)),
344+
_ => TokenStream::concat_rc_vec(RcVec::new(streams)),
291345
}
292346
}
293347

294-
fn concat_rc_slice(streams: RcSlice<TokenStream>) -> TokenStream {
348+
fn concat_rc_vec(streams: RcVec<TokenStream>) -> TokenStream {
295349
TokenStream { kind: TokenStreamKind::Stream(streams) }
296350
}
297351

@@ -434,7 +488,7 @@ impl TokenStreamBuilder {
434488
match len {
435489
1 => {}
436490
2 => self.0.push(streams[0].clone().into()),
437-
_ => self.0.push(TokenStream::concat_rc_slice(streams.sub_slice(0 .. len - 1))),
491+
_ => self.0.push(TokenStream::concat_rc_vec(streams.sub_slice(0 .. len - 1))),
438492
}
439493
self.push_all_but_last_tree(&streams[len - 1])
440494
}
@@ -446,7 +500,7 @@ impl TokenStreamBuilder {
446500
match len {
447501
1 => {}
448502
2 => self.0.push(streams[1].clone().into()),
449-
_ => self.0.push(TokenStream::concat_rc_slice(streams.sub_slice(1 .. len))),
503+
_ => self.0.push(TokenStream::concat_rc_vec(streams.sub_slice(1 .. len))),
450504
}
451505
self.push_all_but_first_tree(&streams[0])
452506
}
@@ -466,13 +520,13 @@ enum CursorKind {
466520

467521
#[derive(Clone)]
468522
struct StreamCursor {
469-
stream: RcSlice<TokenStream>,
523+
stream: RcVec<TokenStream>,
470524
index: usize,
471-
stack: Vec<(RcSlice<TokenStream>, usize)>,
525+
stack: Vec<(RcVec<TokenStream>, usize)>,
472526
}
473527

474528
impl StreamCursor {
475-
fn new(stream: RcSlice<TokenStream>) -> Self {
529+
fn new(stream: RcVec<TokenStream>) -> Self {
476530
StreamCursor { stream: stream, index: 0, stack: Vec::new() }
477531
}
478532

@@ -495,7 +549,7 @@ impl StreamCursor {
495549
}
496550
}
497551

498-
fn insert(&mut self, stream: RcSlice<TokenStream>) {
552+
fn insert(&mut self, stream: RcVec<TokenStream>) {
499553
self.stack.push((mem::replace(&mut self.stream, stream),
500554
mem::replace(&mut self.index, 0)));
501555
}
@@ -557,7 +611,7 @@ impl Cursor {
557611
CursorKind::Empty => TokenStream::empty(),
558612
CursorKind::Tree(ref tree, _) => tree.clone().into(),
559613
CursorKind::JointTree(ref tree, _) => tree.clone().joint(),
560-
CursorKind::Stream(ref cursor) => TokenStream::concat_rc_slice({
614+
CursorKind::Stream(ref cursor) => TokenStream::concat_rc_vec({
561615
cursor.stack.get(0).cloned().map(|(stream, _)| stream)
562616
.unwrap_or(cursor.stream.clone())
563617
}),
@@ -607,22 +661,22 @@ impl Cursor {
607661
/// `ThinTokenStream` is smaller, but needs to allocate to represent a single `TokenTree`.
608662
/// We must use `ThinTokenStream` in `TokenTree::Delimited` to avoid infinite size due to recursion.
609663
#[derive(Debug, Clone)]
610-
pub struct ThinTokenStream(Option<RcSlice<TokenStream>>);
664+
pub struct ThinTokenStream(Option<RcVec<TokenStream>>);
611665

612666
impl From<TokenStream> for ThinTokenStream {
613667
fn from(stream: TokenStream) -> ThinTokenStream {
614668
ThinTokenStream(match stream.kind {
615669
TokenStreamKind::Empty => None,
616-
TokenStreamKind::Tree(tree) => Some(RcSlice::new(vec![tree.into()])),
617-
TokenStreamKind::JointTree(tree) => Some(RcSlice::new(vec![tree.joint()])),
670+
TokenStreamKind::Tree(tree) => Some(RcVec::new(vec![tree.into()])),
671+
TokenStreamKind::JointTree(tree) => Some(RcVec::new(vec![tree.joint()])),
618672
TokenStreamKind::Stream(stream) => Some(stream),
619673
})
620674
}
621675
}
622676

623677
impl From<ThinTokenStream> for TokenStream {
624678
fn from(stream: ThinTokenStream) -> TokenStream {
625-
stream.0.map(TokenStream::concat_rc_slice).unwrap_or_else(TokenStream::empty)
679+
stream.0.map(TokenStream::concat_rc_vec).unwrap_or_else(TokenStream::empty)
626680
}
627681
}
628682

@@ -773,4 +827,106 @@ mod tests {
773827
assert_eq!(stream.trees().count(), 1);
774828
}
775829

830+
#[test]
831+
fn test_extend_empty() {
832+
with_globals(|| {
833+
// Append a token onto an empty token stream.
834+
let mut stream = TokenStream::empty();
835+
stream.extend(vec![string_to_ts("t")]);
836+
837+
let expected = string_to_ts("t");
838+
assert!(stream.eq_unspanned(&expected));
839+
});
840+
}
841+
842+
#[test]
843+
fn test_extend_nothing() {
844+
with_globals(|| {
845+
// Append nothing onto a token stream containing one token.
846+
let mut stream = string_to_ts("t");
847+
stream.extend(vec![]);
848+
849+
let expected = string_to_ts("t");
850+
assert!(stream.eq_unspanned(&expected));
851+
});
852+
}
853+
854+
#[test]
855+
fn test_extend_single() {
856+
with_globals(|| {
857+
// Append a token onto token stream containing a single token.
858+
let mut stream = string_to_ts("t1");
859+
stream.extend(vec![string_to_ts("t2")]);
860+
861+
let expected = string_to_ts("t1 t2");
862+
assert!(stream.eq_unspanned(&expected));
863+
});
864+
}
865+
866+
#[test]
867+
fn test_extend_in_place() {
868+
with_globals(|| {
869+
// Append a token onto token stream containing a reference counted
870+
// vec of tokens. The token stream has a reference count of 1 so
871+
// this can happen in place.
872+
let mut stream = string_to_ts("t1 t2");
873+
stream.extend(vec![string_to_ts("t3")]);
874+
875+
let expected = string_to_ts("t1 t2 t3");
876+
assert!(stream.eq_unspanned(&expected));
877+
});
878+
}
879+
880+
#[test]
881+
fn test_extend_copy() {
882+
with_globals(|| {
883+
// Append a token onto token stream containing a reference counted
884+
// vec of tokens. The token stream is shared so the extend takes
885+
// place on a copy.
886+
let mut stream = string_to_ts("t1 t2");
887+
let _incref = stream.clone();
888+
stream.extend(vec![string_to_ts("t3")]);
889+
890+
let expected = string_to_ts("t1 t2 t3");
891+
assert!(stream.eq_unspanned(&expected));
892+
});
893+
}
894+
895+
#[test]
896+
fn test_extend_no_join() {
897+
with_globals(|| {
898+
let first = TokenTree::Token(DUMMY_SP, Token::Dot);
899+
let second = TokenTree::Token(DUMMY_SP, Token::Dot);
900+
901+
// Append a dot onto a token stream containing a dot, but do not
902+
// join them.
903+
let mut stream = TokenStream::from(first);
904+
stream.extend(vec![TokenStream::from(second)]);
905+
906+
let expected = string_to_ts(". .");
907+
assert!(stream.eq_unspanned(&expected));
908+
909+
let unexpected = string_to_ts("..");
910+
assert!(!stream.eq_unspanned(&unexpected));
911+
});
912+
}
913+
914+
#[test]
915+
fn test_extend_join() {
916+
with_globals(|| {
917+
let first = TokenTree::Token(DUMMY_SP, Token::Dot).joint();
918+
let second = TokenTree::Token(DUMMY_SP, Token::Dot);
919+
920+
// Append a dot onto a token stream containing a dot, forming a
921+
// dotdot.
922+
let mut stream = first;
923+
stream.extend(vec![TokenStream::from(second)]);
924+
925+
let expected = string_to_ts("..");
926+
assert!(stream.eq_unspanned(&expected));
927+
928+
let unexpected = string_to_ts(". .");
929+
assert!(!stream.eq_unspanned(&unexpected));
930+
});
931+
}
776932
}

0 commit comments

Comments
 (0)