1extern crate proc_macro;
10extern crate syn;
11use syn::spanned::Spanned;
12use syn::*;
13
14use core::result::Result;
15
16extern crate quote;
17use quote::quote_spanned;
18
19use proc_macro::TokenStream;
20
21extern crate proc_macro2;
22use proc_macro2::{Span, TokenStream as TokenStream2};
23
24macro_rules! ok_or_return {
25 ($e:expr) => {
26 match $e {
27 Ok(v) => v,
28 Err(t) => return t.into(),
29 }
30 };
31}
32
33#[proc_macro_attribute]
34pub fn constructor(args: TokenStream, input: TokenStream) -> TokenStream {
35 let priority = ok_or_return!(parse_priority(args));
36
37 let section = ok_or_return!(init_section(priority));
38
39 let func: ItemFn = parse_macro_input!(input);
40
41 let func_ptr_name = format!("__static_init_constructor_{}", func.sig.ident);
42
43 let func_type = get_init_func_sig(&func.sig);
44
45 gen_ctor_dtor(func, §ion, &func_ptr_name, func_type).into()
46}
47
48fn get_init_func_sig(sig: &Signature) -> TypeBareFn {
49 let sp = sig.span().resolved_at(Span::mixed_site());
50
51 if cfg!(target_env = "gnu") && cfg!(target_family = "unix") && !sig.inputs.is_empty() {
52 parse2(quote_spanned!(sp=>extern "C" fn(i32,*const*const u8, *const *const u8)))
53 .unwrap()
54 } else {
55 parse2(quote_spanned!(sp=>extern "C" fn())).unwrap()
56 }
57}
58
59fn const_dtor_no_support() -> TokenStream {
60 quote_spanned!(Span::mixed_site()=>compile_error!(
61 "program constructors/destructors not supported on this target"
62 ))
63 .into()
64}
65
66fn init_section(priority: u16) -> Result<String, TokenStream> {
67 if cfg!(elf) {
72 Ok(format!(".init_array.{:05}", 65535 - priority))
75 } else if cfg!(mach_o) {
76 if priority != 0 {
79 Err(quote_spanned!(Span::mixed_site()=>compile_error!(
80 "Constructor priority other than 0 not supported on this plateform."
81 ))
82 .into())
83 } else {
84 Ok("__DATA,__mod_init_func".to_string())
85 }
86 } else if cfg!(coff) {
87 Ok(format!(".CRT$XCU{:05}", 65535 - priority))
90 } else {
91 Err(const_dtor_no_support())
92 }
93}
94
95fn fini_section(priority: u16) -> Result<String, TokenStream> {
96 if cfg!(elf) {
97 Ok(format!(".fini_array.{:05}", 65535 - priority))
99 } else if cfg!(mach_o) {
100 if priority != 0 {
101 Err(quote_spanned!(Span::mixed_site()=>compile_error!(
102 "Constructor priority not supported on this plateform."
103 ))
104 .into())
105 } else {
106 Ok("__DATA,__mod_term_func".to_string())
107 }
108 } else if cfg!(coff) {
109 Ok(format!(".CRT$XPTZ{:05}", 65535 - priority))
111 } else {
112 Err(const_dtor_no_support())
113 }
114}
115
116#[proc_macro_attribute]
117pub fn destructor(args: TokenStream, input: TokenStream) -> TokenStream {
118 let priority = ok_or_return!(parse_priority(args));
119
120 let section = ok_or_return!(fini_section(priority));
121
122 let func: ItemFn = parse_macro_input!(input);
123
124 let func_ptr_name = format!("__static_init_destructor_{}", func.sig.ident);
125
126 let sp = func.sig.span();
127 let func_type = parse2(quote_spanned!(sp.span()=>extern "C" fn())).unwrap();
128
129 gen_ctor_dtor(func, §ion, &func_ptr_name, func_type).into()
130}
131
132
133#[proc_macro_attribute]
134pub fn dynamic(args: TokenStream, input: TokenStream) -> TokenStream {
135 let item: ItemStatic = parse_macro_input!(input);
136
137 let options = ok_or_return!(parse_dyn_options(parse_macro_input!(args)));
138
139 gen_dyn_init(item, options).into()
140}
141
142#[derive(Clone, Copy, Eq, PartialEq)]
143enum InitMode {
144 Const,
145 Lazy,
146 LesserLazy,
147 Dynamic(u16),
148}
149#[derive(Clone, Copy, Eq, PartialEq)]
150enum DropMode {
151 None,
152 Drop,
153 Finalize,
154 Dynamic(u16),
155}
156#[derive(Clone, Copy, Eq, PartialEq)]
157struct Tolerance {
158 init_fail: bool,
159 registration_fail: bool,
160}
161
162#[derive(Clone, Copy, Eq, PartialEq)]
163struct DynMode {
164 init: InitMode,
165 drop: DropMode,
166 tolerance: Tolerance,
167 priming: bool,
168}
169
170fn parse_priority(args: TokenStream) -> std::result::Result<u16, TokenStream2> {
171 if !args.is_empty() {
172 if let Ok(n) = syn::parse(args.clone()).map_err(|e| e.to_compile_error()) {
173 let n: Ident = n;
174 if n == "__lazy_init" {
175 return Ok(1);
176 } else if n == "__lazy_init_finished" {
177 return Ok(0);
178 }
179 }
180 let lit: Lit = syn::parse(args).map_err(|e| e.to_compile_error())?;
181 parse_priority_literal(&lit)
182 } else {
183 Ok(0)
184 }
185}
186
187macro_rules! generate_error{
188 ($span:expr => $($args:tt),*) => {
189 {
190 let __expand = [$(generate_error!(@expand $args)),*];
191 quote_spanned!($span.resolved_at(Span::mixed_site()) => ::core::compile_error!(::core::concat!(#(#__expand),*)))
192 }
193 };
194 ($($args:tt),*) => {{
195 let __expand = [$(generate_error!(@expand $args)),*];
196 quote_spanned!(Span::mixed_site()=>::core::compile_error!(::core::concat!(#(#__expand),*)))
197 }
198 };
199 (@expand $v:literal) => {
200 quote_spanned!(Span::mixed_site()=>$v)
201 };
202 (@expand $v:ident) => {
203 {
204 quote_spanned!(Span::mixed_site()=>::core::stringify!(#$v))
205 }
206 };
207
208}
209
210fn parse_priority_literal(lit: &Lit) -> Result<u16, TokenStream2> {
211 match lit {
212 Lit::Int(n) => n.base10_parse::<u16>().map_err(|e| e.to_compile_error()),
213 _ => Err(
214 generate_error!(lit.span()=>"Expected a priority in the range [0 ; 65535], found `",lit,"`."),
215 ),
216 }
217}
218
219fn parse_dyn_options(args: AttributeArgs) -> std::result::Result<DynMode, TokenStream2> {
220 let mut opt = DynMode {
221 init: InitMode::LesserLazy,
222 drop: DropMode::None,
223 tolerance: Tolerance {
224 init_fail: true,
225 registration_fail: false,
226 },
227 priming: false,
228 };
229
230 let mut init_set = false;
231 let mut drop_set = false;
232 macro_rules! check_no_init{
233 ($id: expr) => {
234 if init_set {
235 let __attr_arg = &$id;
236 return Err(generate_error!($id.span()=>"Initialization already specified `",__attr_arg,"`"));
237 } else {
238 init_set = true;
239 }
240 }
241 }
242 macro_rules! check_no_drop{
243 ($id: expr) => {
244 if drop_set {
245 let __attr_arg = &$id;
246 return Err(generate_error!($id.span()=>"Drop already specified `",__attr_arg,"`"));
247 } else {
248 drop_set = true;
249 }
250 }
251 }
252
253 macro_rules! unexpected_arg{
254 ($id: expr) => {{
255 let __unexpected = &$id;
256 Err(generate_error!($id.span()=>
257 "Unexpected attribute argument `",
258 __unexpected,
259 "`. Expected either `init[=<u16>]`, `drop[=<u16>]`, `lazy`, `lesser_lazy`, `drop_only=<u16>`, `prime`, `tolerate_leak` or `try_init_once`."
260 ))
261 }
262 }
263 }
264
265 for arg in args {
266 match arg {
267 NestedMeta::Meta(Meta::Path(id)) => {
268 let id = if let Some(id) = id.get_ident() {
269 id
270 } else {
271 return unexpected_arg!(id);
272 };
273 if id == "init" {
274 check_no_init!(id);
275 opt.init = InitMode::Dynamic(0);
276 } else if id == "drop" {
277 if !cfg!(constructor_destructor) {
278 return Err(generate_error!(id.span()=>"Static drop mode only supported on unixes and windows"))
279 }
280 check_no_drop!(id);
281 opt.drop = DropMode::Drop;
282 } else if id == "finalize" {
283 if !cfg!(constructor_destructor) {
284 return Err(generate_error!(id.span()=>"Static finalization mode only supported on unixes and windows"))
285 }
286 check_no_drop!(id);
287 opt.drop = DropMode::Finalize;
288 } else if id == "lazy" {
289 check_no_init!(id);
290 opt.init = InitMode::Lazy;
291 } else if id == "lesser_lazy" {
292 check_no_init!(id);
293 opt.init = InitMode::LesserLazy;
294 } else if id == "try_init_once" {
295 opt.tolerance.init_fail = false;
296 } else if id == "tolerate_leak" {
297 opt.tolerance.registration_fail = true;
298 } else if id == "prime" {
299 opt.priming = true;
300 } else {
301 return unexpected_arg!(id);
302 }
303 }
304 NestedMeta::Meta(Meta::NameValue(nv)) => {
305 let id = if let Some(id) = nv.path.get_ident() {
306 id
307 } else {
308 return unexpected_arg!(nv.path);
309 };
310 if id == "init" {
311 check_no_init!(id);
312 let priority = parse_priority_literal(&nv.lit)?;
313 opt.init = InitMode::Dynamic(priority);
314 } else if id == "drop" {
315 check_no_drop!(id);
316 let priority = parse_priority_literal(&nv.lit)?;
317 opt.drop = DropMode::Dynamic(priority);
318 } else if id == "drop_only" {
319 check_no_init!(id);
320 check_no_drop!(id);
321 let priority = parse_priority_literal(&nv.lit)?;
322 opt.init = InitMode::Const;
323 opt.drop = DropMode::Dynamic(priority);
324 } else {
325 return unexpected_arg!(id);
326 }
327 }
328 NestedMeta::Lit(lit) => {
329 check_no_init!(lit);
330 let priority = parse_priority_literal(&lit)?;
331 opt.init = InitMode::Dynamic(priority);
332 }
333 _ => {
334 return unexpected_arg!(arg);
335 }
336 }
337 }
338 if opt.init == InitMode::LesserLazy && !cfg!(constructor_destructor) {
339 opt.init = InitMode::Lazy
340 }
341 if opt.drop == DropMode::None && opt.tolerance.registration_fail {
342 return Err(generate_error!(
343 "Unusefull `tolerate_leak`: this static is not dropped, it will always leak. Add \
344 `drop` or `finalize` attribute argument if the intent is that this static is dropped."
345 ));
346 }
347 if opt.priming && ! (opt.init== InitMode::Lazy || opt.init == InitMode::LesserLazy) {
348 return Err(generate_error!(
349 "Only lazy statics can be primed"
350 ));
351 }
352 if (opt.init == InitMode::Lazy || opt.init == InitMode::LesserLazy)
353 && !(opt.drop == DropMode::None
354 || opt.drop == DropMode::Finalize
355 || opt.drop == DropMode::Drop)
356 {
357 Err(generate_error!("Drop mode not supported for lazy statics."))
358 } else if let InitMode::Dynamic(p) = opt.init {
359 if !opt.tolerance.init_fail
360 {
362 Err(generate_error!(
363 "Unusefull `try_init_once` attribute: raw statics initialization is attempted \
364 only once."
365 ))
366 } else if opt.tolerance.registration_fail
367 {
369 Err(generate_error!(
370 "Unusefull `tolerate_leak` attribute: raw statics are registered for drop at \
371 compile time."
372 ))
373 } else {
374 match opt.drop {
375 DropMode::Drop => {
376 opt.drop = DropMode::Dynamic(p);
377 Ok(opt)
378 }
379 DropMode::Finalize => Err(generate_error!(
380 "Drop mode finalize not supported for global dynamic statics."
381 )),
382 _ => Ok(opt),
383 }
384 }
385 } else {
386 Ok(opt)
387 }
388}
389
390fn gen_ctor_dtor(
391 func: ItemFn,
392 section: &str,
393 func_ptr_name: &str,
394 typ: TypeBareFn,
395) -> TokenStream2 {
396 let func_ptr_name = Ident::new(func_ptr_name, Span::call_site());
397
398 let section = LitStr::new(section, Span::call_site());
399
400 let func_name = &func.sig.ident;
401
402 let sp = func.sig.span().resolved_at(Span::mixed_site());
403 quote_spanned! {sp=>
408 #func
409 #[doc(hidden)]
410 #[link_section = #section]
411 #[used]
412 pub static #func_ptr_name: #typ = #func_name;
413 }
414 }
416
417fn has_thread_local(attrs: &[Attribute]) -> bool {
418 for attr in attrs {
419 for seg in &attr.path.segments {
420 if seg.ident == "thread_local" {
421 return true;
422 }
423 }
424 }
425 false
426}
427
428fn gen_dyn_init(mut stat: ItemStatic, options: DynMode) -> TokenStream2 {
429 let stat_name = &stat.ident;
435 let stat_vis = &stat.vis;
436
437 let stat_generator_name = format!("__StaticInitGeneratorFor_{}", stat_name);
438
439 let stat_generator_name = Ident::new(&stat_generator_name, Span::call_site());
440
441 let err = generate_error!(stat.expr.span()=>
442 "Expected an expression of the form `match INIT { PRIME => /*expr/*, DYN => /*expr*/}`"
443 );
444
445
446 let stat_typ = &*stat.ty;
447
448 let is_thread_local = has_thread_local(&stat.attrs);
449
450 if is_thread_local && !(options.init == InitMode::Lazy || options.init == InitMode::LesserLazy)
451 {
452 return generate_error!(
453 "Only statics with `#[dynamic(lazy)]` or `#[dynamic(lazy,drop)]` can also have \
454 `#[thread_local]` attribute"
455 );
456 }
457
458 let stat_ref: Expr =
459 if !(options.init == InitMode::Lazy || options.init == InitMode::LesserLazy) {
460 parse_quote! {
461 &mut #stat_name
462 }
463 } else {
464 parse_quote! {
465 &#stat_name
466 }
467 };
468
469 macro_rules! into_mutable {
470 () => {
471 stat.mutability = Some(token::Mut {
472 span: stat.ty.span(),
473 })
474 };
475 }
476 macro_rules! into_immutable {
477 () => {
478 stat.mutability = None
479 };
480 }
481
482 let typ: Type = if !(options.init == InitMode::Lazy || options.init == InitMode::LesserLazy) {
483 if stat.mutability.is_none() {
484 into_mutable!();
485 parse_quote! {
486 ::static_init::raw_static::ConstStatic::<#stat_typ>
487 }
488 } else {
489 parse_quote! {
490 ::static_init::raw_static::Static::<#stat_typ>
491 }
492 }
493 } else if is_thread_local && options.priming && options.drop == DropMode::None {
494 if stat.mutability.is_none() {
495 return generate_error!(stat.static_token.span()=>
496 "Primed statics are mutating (safe). Add the `mut` keyword."
497 );
498 } else {
499 into_immutable!();
500 parse_quote! {
501 ::static_init::lazy::UnSyncPrimedLockedLazy::<#stat_typ,#stat_generator_name>
502 }
503 }
504 } else if is_thread_local && options.priming {
505 if stat.mutability.is_none() {
506 return generate_error!(stat.static_token.span()=>
507 "Primed statics are mutating (safe). Add the `mut` keyword."
508 );
509 } else {
510 into_immutable!();
511 parse_quote! {
512 ::static_init::lazy::UnSyncPrimedLockedLazyDroped::<#stat_typ,#stat_generator_name>
513 }
514 }
515 } else if options.priming && options.init == InitMode::Lazy && options.drop == DropMode::None {
516 if stat.mutability.is_none() {
517 return generate_error!(stat.static_token.span()=>
518 "Primed statics are mutating (safe). Add the `mut` keyword."
519 );
520 } else {
521 into_immutable!();
522 parse_quote! {
523 ::static_init::lazy::PrimedLockedLazy::<#stat_typ,#stat_generator_name>
524 }
525 }
526 } else if options.priming && options.init == InitMode::LesserLazy && options.drop == DropMode::None {
527 if stat.mutability.is_none() {
528 return generate_error!(stat.static_token.span()=>
529 "Primed statics are mutating (safe). Add the `mut` keyword."
530 );
531 } else {
532 into_immutable!();
533 parse_quote! {
534 ::static_init::lazy::PrimedLesserLockedLazy::<#stat_typ,#stat_generator_name>
535 }
536 }
537 } else if options.priming && options.init == InitMode::Lazy{
538 if stat.mutability.is_none() {
539 return generate_error!(stat.static_token.span()=>
540 "Primed statics are mutating (safe). Add the `mut` keyword."
541 );
542 } else {
543 into_immutable!();
544 parse_quote! {
545 ::static_init::lazy::PrimedLockedLazyDroped::<#stat_typ,#stat_generator_name>
546 }
547 }
548 } else if options.priming && options.init == InitMode::LesserLazy{
549 if stat.mutability.is_none() {
550 return generate_error!(stat.static_token.span()=>
551 "Primed statics are mutating (safe). Add the `mut` keyword."
552 );
553 } else {
554 into_immutable!();
555 parse_quote! {
556 ::static_init::lazy::PrimedLesserLockedLazyDroped::<#stat_typ,#stat_generator_name>
557 }
558 }
559 } else if is_thread_local && options.drop == DropMode::Finalize {
560 if stat.mutability.is_none() {
561 parse_quote! {
562 ::static_init::lazy::UnSyncLazyFinalize::<#stat_typ,#stat_generator_name>
563 }
564 } else {
565 into_immutable!();
566 parse_quote! {
567 ::static_init::lazy::UnSyncLockedLazyFinalize::<#stat_typ,#stat_generator_name>
568 }
569 }
570 } else if is_thread_local && options.drop == DropMode::Drop {
571 if stat.mutability.is_none() {
572 parse_quote! {
573 ::static_init::lazy::UnSyncLazyDroped::<#stat_typ,#stat_generator_name>
574 }
575 } else {
576 into_immutable!();
577 parse_quote! {
578 ::static_init::lazy::UnSyncLockedLazyDroped::<#stat_typ,#stat_generator_name>
579 }
580 }
581 } else if is_thread_local {
582 if stat.mutability.is_none() {
583 parse_quote! {
584 ::static_init::lazy::UnSyncLazy::<#stat_typ,#stat_generator_name>
585 }
586 } else {
587 into_immutable!();
588 parse_quote! {
589 ::static_init::lazy::UnSyncLockedLazy::<#stat_typ,#stat_generator_name>
590 }
591 }
592 } else if options.drop == DropMode::Finalize && options.init == InitMode::LesserLazy {
593 if stat.mutability.is_none() {
594 parse_quote! {
595 ::static_init::lazy::LesserLazyFinalize::<#stat_typ,#stat_generator_name>
596 }
597 } else {
598 into_immutable!();
599 parse_quote! {
600 ::static_init::lazy::LesserLockedLazyFinalize::<#stat_typ,#stat_generator_name>
601 }
602 }
603 } else if options.drop == DropMode::Finalize && options.init == InitMode::Lazy {
604 if stat.mutability.is_none() {
605 parse_quote! {
606 ::static_init::lazy::LazyFinalize::<#stat_typ,#stat_generator_name>
607 }
608 } else {
609 into_immutable!();
610 parse_quote! {
611 ::static_init::lazy::LockedLazyFinalize::<#stat_typ,#stat_generator_name>
612 }
613 }
614 } else if options.drop == DropMode::Drop && options.init == InitMode::Lazy {
615 if stat.mutability.is_none() {
616 return generate_error!("Droped lazy must be mutable");
617 } else {
622 into_immutable!();
623 parse_quote! {
624 ::static_init::lazy::LockedLazyDroped::<#stat_typ,#stat_generator_name>
625 }
626 }
627 } else if options.drop == DropMode::Drop && options.init == InitMode::LesserLazy {
628 if stat.mutability.is_none() {
629 return generate_error!("Droped lazy must be mutable");
630 } else {
631 into_immutable!();
632 parse_quote! {
633 ::static_init::lazy::LesserLockedLazyDroped::<#stat_typ,#stat_generator_name>
634 }
635 }
636 } else if options.init == InitMode::LesserLazy {
637 if stat.mutability.is_none() {
638 parse_quote! {
639 ::static_init::lazy::LesserLazy::<#stat_typ,#stat_generator_name>
640 }
641 } else {
642 into_immutable!();
643 parse_quote! {
644 ::static_init::lazy::LesserLockedLazy::<#stat_typ,#stat_generator_name>
645 }
646 }
647 } else if stat.mutability.is_none() {
648 parse_quote! {
649 ::static_init::lazy::Lazy::<#stat_typ,#stat_generator_name>
650 }
651 } else {
652 into_immutable!();
653 parse_quote! {
654 ::static_init::lazy::LockedLazy::<#stat_typ,#stat_generator_name>
655 }
656 };
657
658 let (expr, prime_expr) = if !options.priming {
659 (&*stat.expr, None)
660 } else if let Expr::Match(mexp) = &*stat.expr {
661 if let Expr::Path(p) = &*mexp.expr {
662 if !p.path.segments.len() == 1 && p.path.segments.first().unwrap().ident == "INIT" {
663 return generate_error!(mexp.expr.span()=>
664 "Expected `INIT` because the static has `#[dynamic(prime)]` attribute."
665 );
666 }
667 } else {
668 return generate_error!(mexp.expr.span()=>
669 "Expected `INIT` because the static has `#[dynamic(prime)]` attribute."
670 );
671 }
672 if mexp.arms.len() != 2 {
673 return generate_error!(mexp.span()=>
674 "Expected two match arms as the static has `#[dynamic(prime)]` attribute."
675 );
676 }
677 let mut expr = None;
678 let mut prime_expr = None;
679 for arm in &mexp.arms {
680 let p = match &arm.pat {
681 Pat::Ident(p)
682 if p.by_ref.is_none() && p.mutability.is_none() && p.subpat.is_none() =>
683 {
684 p
685 }
686 x => {
687 return generate_error!(x.span()=>
688 "Expected either `DYN` or `PRIME` as the static has `#[dynamic(prime)]` attribute."
689 )
690 }
691 };
692 if p.ident == "PRIME" && prime_expr.is_none() {
693 prime_expr = Some(&*arm.body);
694 } else if p.ident == "DYN" && expr.is_none() {
695 expr = Some(&*arm.body);
696 } else {
697 return generate_error!(p.span()=>
698 "Repeated match expression `", p, "`. There must be one arm that matches `PRIME` and the other `DYN`."
699 );
700 }
701 }
702 (expr.unwrap(), prime_expr)
703 } else {
704 return err;
705 };
706
707 let sp = stat.expr.span().resolved_at(Span::mixed_site());
708
709 let initer = match options.init {
710 InitMode::Dynamic(priority) => {
744 let attr: Attribute = parse_quote!(#[::static_init::constructor(#priority)]);
745 Some(quote_spanned! {sp=>
746 #attr
747 extern "C" fn __static_init_initializer() {
748 ::static_init::raw_static::__set_init_prio(#priority as i32);
749 let __static_init_expr_result = #expr;
750 unsafe {#typ::set_to(#stat_ref,__static_init_expr_result)};
751 ::static_init::raw_static::__set_init_prio(i32::MIN);
752 }
753 })
754 }
755
756 InitMode::LesserLazy if !is_thread_local && cfg!(support_priority) => {
757 Some(quote_spanned! {sp=>
758 #[::static_init::constructor(__lazy_init)]
759 extern "C" fn __static_init_initializer() {
760 unsafe {#typ::init(#stat_ref)};
761 }
762 })
763 }
764
765 InitMode::Const | InitMode::Lazy | InitMode::LesserLazy => None,
766 };
767
768 let droper = if let DropMode::Dynamic(priority) = options.drop {
769 let attr: Attribute = parse_quote!(#[::static_init::destructor(#priority)]);
770 Some(quote_spanned! {sp=>
771 #attr
772 extern "C" fn __static_init_droper() {
773 unsafe {#typ::drop(#stat_ref)}
774 }
775 })
776 } else {
777 None
778 };
779
780 let statid = &stat.ident;
781
782 let init_priority: Expr = match options.init {
783 InitMode::Dynamic(n) => parse_quote!(::static_init::InitMode::ProgramConstructor(#n)),
784 InitMode::Lazy => parse_quote!(::static_init::InitMode::Lazy),
785 InitMode::LesserLazy => parse_quote!(::static_init::InitMode::LesserLazy),
786 InitMode::Const => parse_quote!(::static_init::InitMode::Const),
787 };
788
789 let drop_priority: Expr = match options.drop {
790 DropMode::Dynamic(n) => parse_quote!(::static_init::FinalyMode::ProgramDestructor(#n)),
791 DropMode::Finalize => parse_quote!(::static_init::FinalyMode::Finalize),
792 DropMode::Drop => parse_quote!(::static_init::FinalyMode::Drop),
793 DropMode::None => parse_quote!(::static_init::FinalyMode::None),
794 };
795
796 let static_info: Option<Expr> = if cfg!(debug_mode) {
797 Some(parse_quote!(
798 ::static_init::StaticInfo{
799 variable_name: ::core::stringify!(#statid),
800 file_name: ::core::file!(),
801 line: ::core::line!(),
802 column: ::core::column!(),
803 init_mode: #init_priority,
804 drop_mode: #drop_priority
805 }))
806 } else {
807 None
808 };
809
810 let init_fail_tol = options.tolerance.init_fail;
811 let reg_fail_tol = options.tolerance.registration_fail;
812
813 let lazy_generator = if matches!(options.init, InitMode::Lazy | InitMode::LesserLazy) {
814 Some(quote_spanned! {sp=>
815 #[allow(clippy::upper_case_acronyms)]
816 #stat_vis struct #stat_generator_name;
817 impl ::static_init::Generator<#stat_typ> for #stat_generator_name {
818 #[inline]
819 fn generate(&self) -> #stat_typ {
820 #expr
821 }
822 }
823 impl ::static_init::GeneratorTolerance for #stat_generator_name {
824 const INIT_FAILURE: bool = #init_fail_tol;
825 const FINAL_REGISTRATION_FAILURE: bool = #reg_fail_tol;
826 }
827 })
828 } else {
829 None
830 };
831
832 let const_init = match options.init {
833 InitMode::Dynamic(_) => {
834 quote_spanned! {sp=>{
835 #initer
836 #droper
837 unsafe{#typ::uninit(#static_info)}
838 }
839 }
840 }
841 InitMode::Lazy | InitMode::LesserLazy if options.priming && cfg!(debug_mode) => {
842 quote_spanned! {sp=> {
843 #initer
844
845 let _ = ();
846
847 unsafe{#typ::from_generator_with_info(#prime_expr,#stat_generator_name, #static_info)}
848 }
849 }
850 }
851 InitMode::Lazy | InitMode::LesserLazy if options.priming => {
852 quote_spanned! {sp=> {
853 #initer
854
855 let _ = ();
856
857 unsafe{#typ::from_generator(#prime_expr,#stat_generator_name)}
858 }
859 }
860 }
861 InitMode::Lazy | InitMode::LesserLazy if cfg!(debug_mode) => {
862 quote_spanned! {sp=> {
863 #initer
864
865 let _ = ();
866
867 unsafe{#typ::from_generator_with_info(#stat_generator_name, #static_info)}
868 }
869 }
870 }
871 InitMode::Lazy | InitMode::LesserLazy => {
872 quote_spanned! {sp=>{
873 #initer
874
875 let _ = ();
876
877 unsafe{#typ::from_generator(#stat_generator_name)}
878 }
879 }
880 }
881 InitMode::Const => {
882 quote_spanned! {sp=>{
883 #initer
884 #droper
885 #typ::from(#expr, #static_info)
886 }
887 }
888 }
889 };
890
891 *stat.expr = match parse(const_init.into()) {
892 Ok(exp) => exp,
893 Err(e) => return e.to_compile_error(),
894 };
895
896 *stat.ty = typ;
897
898 quote_spanned! {sp=>
899 #lazy_generator
900 #stat
901 }
902}