aboutsummaryrefslogtreecommitdiff
path: root/macros/src/codegen.rs
diff options
context:
space:
mode:
authorGravatar bors[bot] <bors[bot]@users.noreply.github.com> 2019-02-15 23:39:28 +0000
committerGravatar bors[bot] <bors[bot]@users.noreply.github.com> 2019-02-15 23:39:28 +0000
commitc91b14bcd49f05ea40617dbd3166afa63234cb91 (patch)
tree1a7c65d4a68619966e4a133dd165de7a3ec64c6b /macros/src/codegen.rs
parentfdba26525c4a190d0275dd3b5f3a154fa189a799 (diff)
parente5e54ee8f1b7afca614f642ee064a7f00a1f8548 (diff)
downloadrtic-c91b14bcd49f05ea40617dbd3166afa63234cb91.tar.gz
rtic-c91b14bcd49f05ea40617dbd3166afa63234cb91.tar.zst
rtic-c91b14bcd49f05ea40617dbd3166afa63234cb91.zip
Merge #151
151: make builds reproducible r=japaric a=japaric This is a rebased and augmented version of #132. With this PR both dev and release builds that do not use the owned-singleton stuff become reproducible. (I haven't really bothered to make owned-singleton reproducible since [lifo] is way more ergonomic than [alloc-singleton] and will eventually make its way into heapless). [lifo]: https://github.com/japaric/lifo [alloc-singleton]: https://crates.io/crates/alloc-singleton Thanks @hugwijst for doing the bulk of the work! closes #132 Co-authored-by: Hugo van der Wijst <hvanderwijst@tesla.com> Co-authored-by: Hugo van der Wijst <hugo@wij.st> Co-authored-by: Jorge Aparicio <jorge@japaric.io>
Diffstat (limited to 'macros/src/codegen.rs')
-rw-r--r--macros/src/codegen.rs298
1 files changed, 173 insertions, 125 deletions
diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs
index af3def64..af09ad42 100644
--- a/macros/src/codegen.rs
+++ b/macros/src/codegen.rs
@@ -2,8 +2,7 @@
use proc_macro::TokenStream;
use std::{
- collections::HashMap,
- sync::atomic::{AtomicUsize, Ordering},
+ collections::{BTreeMap, HashMap},
time::{SystemTime, UNIX_EPOCH},
};
@@ -20,13 +19,13 @@ use crate::{
// NOTE to avoid polluting the user namespaces we map some identifiers to pseudo-hygienic names.
// In some instances we also use the pseudo-hygienic names for safety, for example the user should
// not modify the priority field of resources.
-type Aliases = HashMap<Ident, Ident>;
+type Aliases = BTreeMap<Ident, Ident>;
struct Context {
// Alias
#[cfg(feature = "timer-queue")]
baseline: Ident,
- dispatchers: HashMap<u8, Dispatcher>,
+ dispatchers: BTreeMap<u8, Dispatcher>,
// Alias (`fn`)
idle: Ident,
// Alias (`fn`)
@@ -41,9 +40,11 @@ struct Context {
schedule_enum: Ident,
// Task -> Alias (`fn`)
schedule_fn: Aliases,
- tasks: HashMap<Ident, Task>,
+ tasks: BTreeMap<Ident, Task>,
// Alias (`struct` / `static mut`)
timer_queue: Ident,
+ // Generator of Ident names or suffixes
+ ident_gen: IdentGenerator,
}
struct Dispatcher {
@@ -63,19 +64,22 @@ struct Task {
impl Default for Context {
fn default() -> Self {
+ let mut ident_gen = IdentGenerator::new();
+
Context {
#[cfg(feature = "timer-queue")]
- baseline: mk_ident(None),
- dispatchers: HashMap::new(),
- idle: mk_ident(Some("idle")),
- init: mk_ident(Some("init")),
- priority: mk_ident(None),
+ baseline: ident_gen.mk_ident(None, false),
+ dispatchers: BTreeMap::new(),
+ idle: ident_gen.mk_ident(Some("idle"), false),
+ init: ident_gen.mk_ident(Some("init"), false),
+ priority: ident_gen.mk_ident(None, false),
statics: Aliases::new(),
resources: HashMap::new(),
- schedule_enum: mk_ident(None),
+ schedule_enum: ident_gen.mk_ident(None, false),
schedule_fn: Aliases::new(),
- tasks: HashMap::new(),
- timer_queue: mk_ident(None),
+ tasks: BTreeMap::new(),
+ timer_queue: ident_gen.mk_ident(None, false),
+ ident_gen,
}
}
}
@@ -164,13 +168,13 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream {
() => quote!(),
};
- let timer_queue = timer_queue(&ctxt, app, analysis);
+ let timer_queue = timer_queue(&mut ctxt, app, analysis);
let pre_init = pre_init(&ctxt, &app, analysis);
let assertions = assertions(app, analysis);
- let main = mk_ident(None);
+ let main = ctxt.ident_gen.mk_ident(None, false);
quote!(
#resources
@@ -236,7 +240,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2:
pub static #mut_ #name: #ty = #expr;
));
- let alias = mk_ident(None);
+ let alias = ctxt.ident_gen.mk_ident(None, true); // XXX is randomness required?
if let Some(Ownership::Shared { ceiling }) = analysis.ownerships.get(name) {
items.push(mk_resource(
ctxt,
@@ -252,7 +256,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2:
ctxt.statics.insert(name.clone(), alias);
} else {
- let alias = mk_ident(None);
+ let alias = ctxt.ident_gen.mk_ident(None, false);
let symbol = format!("{}::{}", name, alias);
items.push(
@@ -360,7 +364,7 @@ fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> (proc_macro2::Tok
let (late_resources, late_resources_ident, ret) = if app.init.returns_late_resources {
// create `LateResources` struct in the root of the crate
- let ident = mk_ident(None);
+ let ident = ctxt.ident_gen.mk_ident(None, false);
let fields = app
.resources
@@ -405,7 +409,7 @@ fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> (proc_macro2::Tok
let baseline = &ctxt.baseline;
let baseline_let = match () {
#[cfg(feature = "timer-queue")]
- () => quote!(let #baseline = rtfm::Instant::artificial(0);),
+ () => quote!(let ref #baseline = rtfm::Instant::artificial(0);),
#[cfg(not(feature = "timer-queue"))]
() => quote!(),
@@ -415,7 +419,7 @@ fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> (proc_macro2::Tok
#[cfg(feature = "timer-queue")]
() => quote!(
#[allow(unused_variables)]
- let start = #baseline;
+ let start = *#baseline;
),
#[cfg(not(feature = "timer-queue"))]
() => quote!(),
@@ -430,21 +434,27 @@ fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> (proc_macro2::Tok
#module
+ // unsafe trampoline to deter end-users from calling this non-reentrant function
#(#attrs)*
- #unsafety fn #init(mut core: rtfm::Peripherals) #ret {
- #(#locals)*
+ unsafe fn #init(core: rtfm::Peripherals) #ret {
+ #[inline(always)]
+ #unsafety fn init(mut core: rtfm::Peripherals) #ret {
+ #(#locals)*
+
+ #baseline_let
- #baseline_let
+ #prelude
- #prelude
+ let mut device = unsafe { #device::Peripherals::steal() };
- let mut device = unsafe { #device::Peripherals::steal() };
+ #start_let
- #start_let
+ #(#stmts)*
- #(#stmts)*
+ #(#assigns)*
+ }
- #(#assigns)*
+ init(core)
}
),
has_late_resources,
@@ -563,7 +573,7 @@ fn module(
#[derive(Clone, Copy)]
pub struct Schedule<'a> {
#[doc(hidden)]
- pub #priority: &'a core::cell::Cell<u8>,
+ pub #priority: &'a rtfm::export::Priority,
}
));
}
@@ -582,7 +592,7 @@ fn module(
#[derive(Clone, Copy)]
pub struct Spawn<'a> {
#[doc(hidden)]
- pub #priority: &'a core::cell::Cell<u8>,
+ pub #priority: &'a rtfm::export::Priority,
}
));
} else {
@@ -591,8 +601,10 @@ fn module(
() => {
let baseline = &ctxt.baseline;
quote!(
+ // NOTE this field is visible so we use a shared reference to make it
+ // immutable
#[doc(hidden)]
- pub #baseline: rtfm::Instant,
+ pub #baseline: &'a rtfm::Instant,
)
}
#[cfg(not(feature = "timer-queue"))]
@@ -605,7 +617,7 @@ fn module(
pub struct Spawn<'a> {
#baseline_field
#[doc(hidden)]
- pub #priority: &'a core::cell::Cell<u8>,
+ pub #priority: &'a rtfm::export::Priority,
}
));
}
@@ -687,7 +699,7 @@ fn prelude(
let mut exprs = vec![];
// NOTE This field is just to avoid unused type parameter errors around `'a`
- defs.push(quote!(#[allow(dead_code)] #priority: &'a core::cell::Cell<u8>));
+ defs.push(quote!(#[allow(dead_code)] pub #priority: &'a rtfm::export::Priority));
exprs.push(parse_quote!(#priority));
let mut may_call_lock = false;
@@ -826,7 +838,8 @@ fn prelude(
#(#cfgs)*
pub #name: &'a #mut_ #name
));
- let alias = mk_ident(None);
+ // XXX is randomness required?
+ let alias = ctxt.ident_gen.mk_ident(None, true);
items.push(quote!(
#(#cfgs)*
let #mut_ #alias = unsafe {
@@ -843,7 +856,8 @@ fn prelude(
#(#cfgs)*
pub #name: rtfm::Exclusive<'a, #name>
));
- let alias = mk_ident(None);
+ // XXX is randomness required?
+ let alias = ctxt.ident_gen.mk_ident(None, true);
items.push(quote!(
#(#cfgs)*
let #mut_ #alias = unsafe {
@@ -910,7 +924,7 @@ fn prelude(
}
}
- let alias = mk_ident(None);
+ let alias = ctxt.ident_gen.mk_ident(None, false);
let unsafety = if needs_unsafe {
Some(quote!(unsafe))
} else {
@@ -971,7 +985,8 @@ fn prelude(
continue;
}
- ctxt.schedule_fn.insert(task.clone(), mk_ident(None));
+ ctxt.schedule_fn
+ .insert(task.clone(), ctxt.ident_gen.mk_ident(None, false));
}
items.push(quote!(
@@ -987,7 +1002,7 @@ fn prelude(
quote!()
} else {
quote!(
- let ref #priority = core::cell::Cell::new(#logical_prio);
+ let ref #priority = unsafe { rtfm::export::Priority::new(#logical_prio) };
#(#items)*
)
@@ -1031,13 +1046,19 @@ fn idle(
quote!(
#module
+ // unsafe trampoline to deter end-users from calling this non-reentrant function
#(#attrs)*
- #unsafety fn #idle() -> ! {
- #(#locals)*
+ unsafe fn #idle() -> ! {
+ #[inline(always)]
+ #unsafety fn idle() -> ! {
+ #(#locals)*
- #prelude
+ #prelude
- #(#stmts)*
+ #(#stmts)*
+ }
+
+ idle()
}
),
quote!(#idle()),
@@ -1083,7 +1104,7 @@ fn exceptions(ctxt: &mut Context, app: &App, analysis: &Analysis) -> Vec<proc_ma
let baseline = &ctxt.baseline;
let baseline_let = match () {
#[cfg(feature = "timer-queue")]
- () => quote!(let #baseline = rtfm::Instant::now();),
+ () => quote!(let ref #baseline = rtfm::Instant::now();),
#[cfg(not(feature = "timer-queue"))]
() => quote!(),
};
@@ -1092,7 +1113,7 @@ fn exceptions(ctxt: &mut Context, app: &App, analysis: &Analysis) -> Vec<proc_ma
#[cfg(feature = "timer-queue")]
() => quote!(
#[allow(unused_variables)]
- let start = #baseline;
+ let start = *#baseline;
),
#[cfg(not(feature = "timer-queue"))]
() => quote!(),
@@ -1100,26 +1121,31 @@ fn exceptions(ctxt: &mut Context, app: &App, analysis: &Analysis) -> Vec<proc_ma
let locals = mk_locals(&exception.statics, false);
let symbol = ident.to_string();
- let alias = mk_ident(None);
+ let alias = ctxt.ident_gen.mk_ident(None, false);
let unsafety = &exception.unsafety;
quote!(
#module
- #[doc(hidden)]
+ // unsafe trampoline to deter end-users from calling this non-reentrant function
#[export_name = #symbol]
#(#attrs)*
- #unsafety fn #alias() {
- #(#locals)*
+ unsafe fn #alias() {
+ #[inline(always)]
+ #unsafety fn exception() {
+ #(#locals)*
- #baseline_let
+ #baseline_let
- #prelude
+ #prelude
- #start_let
+ #start_let
- rtfm::export::run(move || {
- #(#stmts)*
- })
+ rtfm::export::run(move || {
+ #(#stmts)*
+ })
+ }
+
+ exception()
}
)
})
@@ -1163,7 +1189,7 @@ fn interrupts(
let baseline = &ctxt.baseline;
let baseline_let = match () {
#[cfg(feature = "timer-queue")]
- () => quote!(let #baseline = rtfm::Instant::now();),
+ () => quote!(let ref #baseline = rtfm::Instant::now();),
#[cfg(not(feature = "timer-queue"))]
() => quote!(),
};
@@ -1172,34 +1198,40 @@ fn interrupts(
#[cfg(feature = "timer-queue")]
() => quote!(
#[allow(unused_variables)]
- let start = #baseline;
+ let start = *#baseline;
),
#[cfg(not(feature = "timer-queue"))]
() => quote!(),
};
let locals = mk_locals(&interrupt.statics, false);
- let alias = mk_ident(None);
+ let alias = ctxt.ident_gen.mk_ident(None, false);
let symbol = ident.to_string();
let unsafety = &interrupt.unsafety;
scoped.push(quote!(
+ // unsafe trampoline to deter end-users from calling this non-reentrant function
#(#attrs)*
#[export_name = #symbol]
- #unsafety fn #alias() {
- // check that this interrupt exists
- let _ = #device::interrupt::#ident;
+ unsafe fn #alias() {
+ #[inline(always)]
+ #unsafety fn interrupt() {
+ // check that this interrupt exists
+ let _ = #device::interrupt::#ident;
- #(#locals)*
+ #(#locals)*
- #baseline_let
+ #baseline_let
+
+ #prelude
- #prelude
+ #start_let
- #start_let
+ rtfm::export::run(move || {
+ #(#stmts)*
+ })
+ }
- rtfm::export::run(move || {
- #(#stmts)*
- })
+ interrupt()
}
));
}
@@ -1213,10 +1245,10 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok
// first pass to generate buffers (statics and resources) and spawn aliases
for (name, task) in &app.tasks {
#[cfg(feature = "timer-queue")]
- let scheduleds_alias = mk_ident(None);
- let free_alias = mk_ident(None);
- let inputs_alias = mk_ident(None);
- let task_alias = mk_ident(Some(&name.to_string()));
+ let scheduleds_alias = ctxt.ident_gen.mk_ident(None, false);
+ let free_alias = ctxt.ident_gen.mk_ident(None, false);
+ let inputs_alias = ctxt.ident_gen.mk_ident(None, false);
+ let task_alias = ctxt.ident_gen.mk_ident(Some(&name.to_string()), false);
let inputs = &task.inputs;
@@ -1277,7 +1309,7 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok
alias: task_alias,
free_queue: free_alias,
inputs: inputs_alias,
- spawn_fn: mk_ident(None),
+ spawn_fn: ctxt.ident_gen.mk_ident(None, false),
#[cfg(feature = "timer-queue")]
scheduleds: scheduleds_alias,
@@ -1296,7 +1328,7 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok
#[cfg(feature = "timer-queue")]
() => {
let baseline = &ctxt.baseline;
- quote!(let scheduled = #baseline;)
+ quote!(let scheduled = *#baseline;)
}
#[cfg(not(feature = "timer-queue"))]
() => quote!(),
@@ -1325,26 +1357,33 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok
let attrs = &task.attrs;
let cfgs = &task.cfgs;
let task_alias = &ctxt.tasks[name].alias;
- let baseline_arg = match () {
+ let (baseline, baseline_arg) = match () {
#[cfg(feature = "timer-queue")]
() => {
let baseline = &ctxt.baseline;
- quote!(#baseline: rtfm::Instant,)
+ (quote!(#baseline,), quote!(#baseline: &rtfm::Instant,))
}
#[cfg(not(feature = "timer-queue"))]
- () => quote!(),
+ () => (quote!(), quote!()),
};
+ let pats = tuple_pat(inputs);
items.push(quote!(
+ // unsafe trampoline to deter end-users from calling this non-reentrant function
#(#attrs)*
#(#cfgs)*
- #unsafety fn #task_alias(#baseline_arg #(#inputs,)*) {
- #(#locals)*
+ unsafe fn #task_alias(#baseline_arg #(#inputs,)*) {
+ #[inline(always)]
+ #unsafety fn task(#baseline_arg #(#inputs,)*) {
+ #(#locals)*
+
+ #prelude
- #prelude
+ #scheduled_let
- #scheduled_let
+ #(#stmts)*
+ }
- #(#stmts)*
+ task(#baseline #pats)
}
));
}
@@ -1362,8 +1401,8 @@ fn dispatchers(
let device = &app.args.device;
for (level, dispatcher) in &analysis.dispatchers {
- let ready_alias = mk_ident(None);
- let enum_alias = mk_ident(None);
+ let ready_alias = ctxt.ident_gen.mk_ident(None, false);
+ let enum_alias = ctxt.ident_gen.mk_ident(None, false);
let capacity = mk_typenum_capacity(dispatcher.capacity, true);
let variants = dispatcher
@@ -1427,7 +1466,7 @@ fn dispatchers(
let baseline =
ptr::read(#scheduleds.get_ref().get_unchecked(usize::from(index)));
);
- call = quote!(#alias(baseline, #pats));
+ call = quote!(#alias(&baseline, #pats));
}
#[cfg(not(feature = "timer-queue"))]
() => {
@@ -1452,7 +1491,7 @@ fn dispatchers(
let attrs = &dispatcher.attrs;
let interrupt = &dispatcher.interrupt;
let symbol = interrupt.to_string();
- let alias = mk_ident(None);
+ let alias = ctxt.ident_gen.mk_ident(None, false);
dispatchers.push(quote!(
#(#attrs)*
#[export_name = #symbol]
@@ -1534,7 +1573,7 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt
#(#cfgs)*
unsafe fn #alias(
#baseline_arg
- #priority: &core::cell::Cell<u8>,
+ #priority: &rtfm::export::Priority,
#(#args,)*
) -> Result<(), #ty> {
use core::ptr;
@@ -1583,7 +1622,7 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt
if is_idle {
quote!(rtfm::Instant::now(),)
} else {
- quote!(self.#baseline,)
+ quote!(*self.#baseline,)
}
}
#[cfg(not(feature = "timer-queue"))]
@@ -1632,7 +1671,7 @@ fn schedule(ctxt: &Context, app: &App) -> proc_macro2::TokenStream {
#[inline(always)]
#(#cfgs)*
unsafe fn #alias(
- #priority: &core::cell::Cell<u8>,
+ #priority: &rtfm::export::Priority,
instant: rtfm::Instant,
#(#args,)*
) -> Result<(), #ty> {
@@ -1703,7 +1742,7 @@ fn schedule(ctxt: &Context, app: &App) -> proc_macro2::TokenStream {
quote!(#(#items)*)
}
-fn timer_queue(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream {
+fn timer_queue(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream {
let tasks = &analysis.timer_queue.tasks;
if tasks.is_empty() {
@@ -1778,14 +1817,14 @@ fn timer_queue(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::T
.collect::<Vec<_>>();
let logical_prio = analysis.timer_queue.priority;
- let alias = mk_ident(None);
+ let alias = ctxt.ident_gen.mk_ident(None, false);
items.push(quote!(
#[export_name = "SysTick"]
#[doc(hidden)]
unsafe fn #alias() {
use rtfm::Mutex;
- let ref #priority = core::cell::Cell::new(#logical_prio);
+ let ref #priority = rtfm::export::Priority::new(#logical_prio);
rtfm::export::run(|| {
rtfm::export::sys_tick(#tq { #priority }, |task, index| {
@@ -1929,7 +1968,7 @@ fn mk_resource(
#(#cfgs)*
pub struct #struct_<'a> {
#[doc(hidden)]
- pub #priority: &'a core::cell::Cell<u8>,
+ pub #priority: &'a rtfm::export::Priority,
}
));
@@ -1938,7 +1977,7 @@ fn mk_resource(
items.push(quote!(
#(#cfgs)*
struct #struct_<'a> {
- #priority: &'a core::cell::Cell<u8>,
+ #priority: &'a rtfm::export::Priority,
}
));
@@ -1989,52 +2028,61 @@ fn mk_typenum_capacity(capacity: u8, power_of_two: bool) -> proc_macro2::TokenSt
quote!(rtfm::export::consts::#ident)
}
-fn mk_ident(name: Option<&str>) -> Ident {
- static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
+struct IdentGenerator {
+ call_count: u32,
+ rng: rand::rngs::SmallRng,
+}
- let elapsed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
+impl IdentGenerator {
+ fn new() -> IdentGenerator {
+ let elapsed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
- let secs = elapsed.as_secs();
- let nanos = elapsed.subsec_nanos();
+ let secs = elapsed.as_secs();
+ let nanos = elapsed.subsec_nanos();
- let count = CALL_COUNT.fetch_add(1, Ordering::SeqCst) as u32;
- let mut seed: [u8; 16] = [0; 16];
+ let mut seed: [u8; 16] = [0; 16];
- for (i, v) in seed.iter_mut().take(8).enumerate() {
- *v = ((secs >> (i * 8)) & 0xFF) as u8
- }
+ for (i, v) in seed.iter_mut().take(8).enumerate() {
+ *v = ((secs >> (i * 8)) & 0xFF) as u8
+ }
- for (i, v) in seed.iter_mut().skip(8).take(4).enumerate() {
- *v = ((nanos >> (i * 8)) & 0xFF) as u8
- }
+ for (i, v) in seed.iter_mut().skip(8).take(4).enumerate() {
+ *v = ((nanos >> (i * 8)) & 0xFF) as u8
+ }
- for (i, v) in seed.iter_mut().skip(12).enumerate() {
- *v = ((count >> (i * 8)) & 0xFF) as u8
- }
+ let rng = rand::rngs::SmallRng::from_seed(seed);
- let n;
- let mut s = if let Some(name) = name {
- n = 4;
- format!("{}_", name)
- } else {
- n = 16;
- String::new()
- };
+ IdentGenerator { call_count: 0, rng }
+ }
- let mut rng = rand::rngs::SmallRng::from_seed(seed);
- for i in 0..n {
- if i == 0 || rng.gen() {
- s.push(('a' as u8 + rng.gen::<u8>() % 25) as char)
+ fn mk_ident(&mut self, name: Option<&str>, random: bool) -> Ident {
+ let s = if let Some(name) = name {
+ format!("{}_", name)
} else {
- s.push(('0' as u8 + rng.gen::<u8>() % 10) as char)
+ "__rtfm_internal_".to_string()
+ };
+
+ let mut s = format!("{}{}", s, self.call_count);
+ self.call_count += 1;
+
+ if random {
+ s.push('_');
+
+ for i in 0..4 {
+ if i == 0 || self.rng.gen() {
+ s.push(('a' as u8 + self.rng.gen::<u8>() % 25) as char)
+ } else {
+ s.push(('0' as u8 + self.rng.gen::<u8>() % 10) as char)
+ }
+ }
}
- }
- Ident::new(&s, Span::call_site())
+ Ident::new(&s, Span::call_site())
+ }
}
// `once = true` means that these locals will be called from a function that will run *once*
-fn mk_locals(locals: &HashMap<Ident, Static>, once: bool) -> proc_macro2::TokenStream {
+fn mk_locals(locals: &BTreeMap<Ident, Static>, once: bool) -> proc_macro2::TokenStream {
let lt = if once { Some(quote!('static)) } else { None };
let locals = locals