static_init_macro/
lib.rs

1// Copyright 2021 Olivier Kannengieser
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8///! Macros for static_init crate.
9extern 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, &section, &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    //Todo priority bellow(65535-100) should be unsafe
68    //or increase the number to be above 100
69    //
70    //idem put Room for lesser lazy initialized later
71    if cfg!(elf) {
72        // on linux the standard library args are initilized
73        // at .init_array.00099. => priority 65436
74        Ok(format!(".init_array.{:05}", 65535 - priority))
75    } else if cfg!(mach_o) {
76        //on mach it is not clear of ObjC runtime is initialized
77        //before or after constructors that are here
78        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        // on windows init maybe be called at .CRT$XCU
88        // so lets initialization takes place after
89        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        // destructors not used by standard library
98        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        // destructors not used by standard library
110        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, &section, &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        /*was try_init_once attribute used*/
361        {
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        /*was tolerate_leak attribute used*/
368        {
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    //if func.sig.unsafety.is_none() {
404    //    quote_spanned! {sp=>compile_error!("Constructors and destructors must be unsafe functions as \
405    //    they may access uninitialized memory regions")}
406    //} else {
407    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    //}
415}
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    //TODO: dropped static must be initialized by unsafe code because
430    //if initialization panic this will cause UB TBC.
431    //
432    //TODO: for lazy statics with leak tolerance => only usefull for thread locals.
433
434    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            //is_const_droped = true;
618            //parse_quote! {
619            //    ::static_init::lazy::ConstLockedLazyDroped::<#stat_typ,#stat_generator_name>
620            //}
621        } 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) if options.drop == DropMode::Drop => {
711        //    let attr: Attribute = parse_quote_spanned!(Span::mixed_site()=>
712        //    #[::static_init::constructor(#priority)]);
713        //    Some(quote_spanned! {sp=>
714        //            extern "C" fn __static_init_dropper() {
715        //                unsafe{#typ::drop(#stat_ref)}
716        //            }
717        //            #attr
718        //            extern "C" fn __static_init_initializer() {
719        //                ::static_init::raw_static::__set_init_prio(#priority as i32);
720        //                let __static_init_expr_result = #expr;
721        //                unsafe {#typ::set_to(#stat_ref,__static_init_expr_result);
722        //                ::libc::atexit(__static_init_dropper)};
723        //                ::static_init::raw_static::__set_init_prio(i32::MIN);
724        //            }
725        //    })
726        //}
727        //InitMode::Dynamic(priority) if options.drop == DropMode::Finalize => {
728        //    let attr: Attribute = parse_quote_spanned!(Span::mixed_site()=>#[::static_init::constructor(#priority)]);
729        //    Some(quote_spanned! {sp=>
730        //            extern "C" fn __static_init_dropper() {
731        //                unsafe{::static_init::Finaly::finalize(**#stat_ref)}
732        //            }
733        //            #attr
734        //            extern "C" fn __static_init_initializer() {
735        //                ::static_init::raw_static::__set_init_prio(#priority as i32);
736        //                let __static_init_expr_result = #expr;
737        //                unsafe {#typ::set_to(#stat_ref,__static_init_expr_result);
738        //                ::libc::atexit(__static_init_dropper)};
739        //                ::static_init::raw_static::__set_init_prio(i32::MIN);
740        //            }
741        //    })
742        //}
743        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}