Skip to content

Commit e2c893b

Browse files
bors[bot]taiki-e
andauthored
Merge #60
60: core: Remove usage of mutable global state r=taiki-e a=taiki-e It may be a warning in the future. So, use the hash value of the input AST instead of PRNG. See rust-lang/rust#64398 (comment) and rust-lang/rust#63831. Co-authored-by: Taiki Endo <[email protected]>
2 parents bc71439 + 2754d49 commit e2c893b

File tree

2 files changed

+68
-37
lines changed

2 files changed

+68
-37
lines changed

core/src/auto_enum/context.rs

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
use std::{
2-
cell::Cell,
32
collections::hash_map::DefaultHasher,
4-
hash::Hasher,
3+
hash::{Hash, Hasher},
54
iter, mem,
6-
num::Wrapping,
7-
sync::atomic::{AtomicUsize, Ordering},
85
};
96

10-
use proc_macro2::TokenStream;
7+
use proc_macro2::{Delimiter, Spacing, TokenStream, TokenTree};
118
use quote::format_ident;
129
use syn::{
1310
parse::{Parse, ParseStream},
@@ -120,7 +117,7 @@ impl Context {
120117
markers.push(marker_string);
121118

122119
Ok(Self {
123-
builder: Builder::new(),
120+
builder: Builder::new(&span),
124121
args,
125122
marker,
126123
markers,
@@ -338,16 +335,16 @@ struct Builder {
338335
}
339336

340337
impl Builder {
341-
fn new() -> Self {
342-
Self { ident: format_ident!("___Enum{}", random()), variants: Vec::new() }
338+
fn new(input: &TokenStream) -> Self {
339+
Self { ident: format_ident!("__Enum{}", hash(input)), variants: Vec::new() }
343340
}
344341

345342
fn iter(&self) -> impl Iterator<Item = &Ident> + '_ {
346343
self.variants.iter()
347344
}
348345

349346
fn next_expr(&mut self, attrs: Vec<Attribute>, expr: Expr) -> Expr {
350-
let variant = format_ident!("___Variant{}", self.variants.len());
347+
let variant = format_ident!("__Variant{}", self.variants.len());
351348

352349
let path =
353350
path(iter::once(self.ident.clone().into()).chain(iter::once(variant.clone().into())));
@@ -373,37 +370,59 @@ impl Builder {
373370
}
374371

375372
// =================================================================================================
376-
// RNG
377-
378-
/// Pseudorandom number generator based on [xorshift*].
379-
///
380-
/// [xorshift*]: https://en.wikipedia.org/wiki/Xorshift#xorshift*
381-
fn random() -> u64 {
382-
thread_local! {
383-
static RNG: Cell<Wrapping<u64>> = Cell::new(Wrapping(prng_seed()));
384-
}
373+
// Hash
374+
375+
/// Returns the hash value of the input AST.
376+
fn hash(tokens: &TokenStream) -> u64 {
377+
let tokens = TokenStreamHelper(tokens);
378+
let mut hasher = DefaultHasher::new();
379+
tokens.hash(&mut hasher);
380+
hasher.finish()
381+
}
385382

386-
fn prng_seed() -> u64 {
387-
static COUNTER: AtomicUsize = AtomicUsize::new(0);
383+
// Based on https://github.com/dtolnay/syn/blob/1.0.5/src/tt.rs
388384

389-
// Any non-zero seed will do -- this uses the hash of a global counter.
390-
// Refs: https://github.com/rayon-rs/rayon/pull/571
391-
let mut seed = 0;
392-
while seed == 0 {
393-
let mut hasher = DefaultHasher::new();
394-
hasher.write_usize(COUNTER.fetch_add(1, Ordering::Relaxed));
395-
seed = hasher.finish();
385+
struct TokenTreeHelper<'a>(&'a TokenTree);
386+
387+
impl Hash for TokenTreeHelper<'_> {
388+
fn hash<H: Hasher>(&self, h: &mut H) {
389+
match self.0 {
390+
TokenTree::Group(g) => {
391+
0_u8.hash(h);
392+
match g.delimiter() {
393+
Delimiter::Parenthesis => 0_u8.hash(h),
394+
Delimiter::Brace => 1_u8.hash(h),
395+
Delimiter::Bracket => 2_u8.hash(h),
396+
Delimiter::None => 3_u8.hash(h),
397+
}
398+
399+
for tt in g.stream() {
400+
TokenTreeHelper(&tt).hash(h);
401+
}
402+
0xff_u8.hash(h); // terminator w/ a variant we don't normally hash
403+
}
404+
TokenTree::Punct(op) => {
405+
1_u8.hash(h);
406+
op.as_char().hash(h);
407+
match op.spacing() {
408+
Spacing::Alone => 0_u8.hash(h),
409+
Spacing::Joint => 1_u8.hash(h),
410+
}
411+
}
412+
TokenTree::Literal(lit) => (2_u8, lit.to_string()).hash(h),
413+
TokenTree::Ident(word) => (3_u8, word).hash(h),
396414
}
397-
seed
398415
}
416+
}
399417

400-
RNG.with(|rng| {
401-
let mut x = rng.get();
402-
debug_assert_ne!(x.0, 0);
403-
x ^= x >> 12;
404-
x ^= x << 25;
405-
x ^= x >> 27;
406-
rng.set(x);
407-
x.0.wrapping_mul(0x2545_f491_4f6c_dd1d)
408-
})
418+
struct TokenStreamHelper<'a>(&'a TokenStream);
419+
420+
impl Hash for TokenStreamHelper<'_> {
421+
fn hash<H: Hasher>(&self, state: &mut H) {
422+
let tokens = self.0.clone().into_iter().collect::<Vec<_>>();
423+
tokens.len().hash(state);
424+
for tt in tokens {
425+
TokenTreeHelper(&tt).hash(state);
426+
}
427+
}
409428
}

tests/auto_enum.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,18 @@ mod nightly {
759759
(if option { |x| x + 1 } else { |y| y - 1 }): _
760760
}
761761
assert_eq!(fn_traits2(true)(1), 2);
762+
763+
#[auto_enum(Iterator, Clone)]
764+
let _y = match 0 {
765+
0 => 2..8,
766+
_ => 2..=10,
767+
};
768+
769+
#[auto_enum(Iterator, Clone)]
770+
let _x = match 0 {
771+
0 => 2..8,
772+
_ => 2..=10,
773+
};
762774
}
763775

764776
#[test]

0 commit comments

Comments
 (0)