Skip to main content

dtor/
parse.rs

1#[macro_export]
2#[doc(hidden)]
3macro_rules! __dtor_parse {
4    ( $($input:tt)* ) => {
5        $crate::__perform!(
6            ($($input)*),
7            $crate::__chain[
8                $crate::__parse_item[$crate::__dtor_features],
9                $crate::__extract_unsafe,
10                $crate::__dtor_parse_impl,
11            ]
12        );
13    };
14}
15
16/// Parse a processed `dtor` item. This is intentionally verbose to avoid
17/// excessive nesting of macro calls in user code.
18#[macro_export]
19#[doc(hidden)]
20macro_rules! __dtor_parse_impl {
21    // Step 0: Check function shape and features
22
23    // fn
24    ( @entry next=$next:path[$next_args:tt], input=(
25        features = (
26            anonymous = $anonymous:tt : $anonymous_spec:ident,
27            crate_path = $crate_path:tt : $crate_path_spec:ident,
28            ctor_export_name_prefix = $ctor_export_name_prefix:tt : $ctor_export_name_prefix_spec:ident,
29            ctor_link_section = $ctor_link_section:tt : $ctor_link_section_spec:ident,
30            default_term_method = $default_term_method:tt : $default_term_method_spec:ident,
31            default_unload_method = $default_unload_method:tt : $default_unload_method_spec:ident,
32            export_name_prefix = $export_name_prefix:tt : $export_name_prefix_spec:ident,
33            link_section = $link_section:tt : $link_section_spec:ident,
34            method = $method:tt : $method_spec:ident,
35            proc_macro = $proc_macro:tt : $proc_macro_spec:ident,
36            std = $std:tt : $std_spec:ident,
37            r#unsafe = $no_warn_on_missing_unsafe:tt : $no_warn_on_missing_unsafe_spec:ident,
38            used_linker = $used_linker:tt : $used_linker_spec:ident,
39        ),
40        self = $self:tt,
41        meta = $meta:tt,
42        unsafe = ($($unsafe:tt)*),
43        item = ($vis:vis $(unsafe)? $( extern $abi:literal )? fn $name:ident () $( -> () )? {
44            $($body:tt)*
45        })
46    ) ) => {
47        $crate::__dtor_parse_impl!(@entry next=$next[$next_args], input=(
48            features = (
49                anonymous = $anonymous,
50                ctor_export_name_prefix = $ctor_export_name_prefix,
51                ctor_link_section = $ctor_link_section,
52                default_term_method = $default_term_method,
53                default_unload_method = $default_unload_method,
54                export_name_prefix = $export_name_prefix,
55                link_section = $link_section,
56                method = $method,
57                no_warn_on_missing_unsafe = $no_warn_on_missing_unsafe,
58                used_linker = $used_linker,
59            ),
60            meta = $meta,
61            unsafe = ($($unsafe)*),
62            item = ($vis $($unsafe)* $( extern $abi )? fn $name () {
63                $($body)*
64            })
65        ));
66    };
67
68    ( @entry next=$next:path[$next_args:tt], input=(
69        features = (
70            anonymous = $anonymous:tt : $anonymous_spec:ident,
71            $($rest:tt)*
72        ),
73        self = $self:tt,
74        meta = $meta:tt,
75        unsafe = $unsafe:tt,
76        item = ($item:item)
77    ) ) => {
78        compile_error!("Invalid dtor function. \
79            Expected a function with no args, \
80            return value, or type parameters.\n\
81            Valid forms are: [pub] [unsafe] [extern $abi] fn $name() { ... }");
82    };
83
84    ( @entry next=$next:path[$next_args:tt], input=(
85        features = (
86            anonymous = $anonymous:tt : $anonymous_spec:ident,
87            $($rest:tt)*
88        ),
89        self = $self:tt,
90        meta = $meta:tt,
91        unsafe = $unsafe:tt,
92        item = ($($item:tt)*)
93    ) ) => {
94        compile_error!(concat!("Invalid dtor, didn't get an item: ", stringify!($($item)*)));
95    };
96
97    // Step 1: Compute method
98
99    // Delegate term -> default_term_method
100    ( @entry next=$next:path[$next_args:tt], input=(
101        features = (
102            anonymous = $anonymous:tt,
103            ctor_export_name_prefix = $ctor_export_name_prefix:tt,
104            ctor_link_section = $ctor_link_section:tt,
105            default_term_method = $default_term_method:tt,
106            default_unload_method = $default_unload_method:tt,
107            export_name_prefix = $export_name_prefix:tt,
108            link_section = $link_section:tt,
109            method = term,
110            no_warn_on_missing_unsafe = $no_warn_on_missing_unsafe:tt,
111            used_linker = $used_linker:tt,
112        ),
113        meta = $meta:tt,
114        unsafe = $unsafe:tt,
115        item = $item:tt
116    ) ) => {
117        // method = term
118        $crate::__dtor_parse_impl(@entry next=$next[$next_args], input=(
119            features = (
120                anonymous = $anonymous,
121                method = ($default_term_method $ctor_export_name_prefix $ctor_link_section $export_name_prefix $link_section),
122                no_warn_on_missing_unsafe = $no_warn_on_missing_unsafe,
123                used_linker = $used_linker,
124            ),
125            meta = $meta,
126            unsafe = $unsafe,
127            item = $item
128        ));
129    };
130
131    // Delegate unload -> default_unload_method
132    ( @entry next=$next:path[$next_args:tt], input=(
133        features = (
134            anonymous = $anonymous:tt,
135            ctor_export_name_prefix = $ctor_export_name_prefix:tt,
136            ctor_link_section = $ctor_link_section:tt,
137            default_term_method = $default_term_method:tt,
138            default_unload_method = $default_unload_method:tt,
139            export_name_prefix = $export_name_prefix:tt,
140            link_section = $link_section:tt,
141            method = unload,
142            no_warn_on_missing_unsafe = $no_warn_on_missing_unsafe:tt,
143            used_linker = $used_linker:tt,
144        ),
145        meta = $meta:tt,
146        unsafe = $unsafe:tt,
147        item = $item:tt
148    ) ) => {
149        // method = unload
150        $crate::__dtor_parse_impl!(@entry next=$next[$next_args], input=(
151            features = (
152                anonymous = $anonymous,
153                method = ($default_unload_method $ctor_export_name_prefix $ctor_link_section $export_name_prefix $link_section),
154                no_warn_on_missing_unsafe = $no_warn_on_missing_unsafe,
155                used_linker = $used_linker,
156            ),
157            meta = $meta,
158            unsafe = $unsafe,
159            item = $item
160        ));
161    };
162
163    // Other methods, pass through
164    ( @entry next=$next:path[$next_args:tt], input=(
165        features = (
166            anonymous = $anonymous:tt,
167            ctor_export_name_prefix = $ctor_export_name_prefix:tt,
168            ctor_link_section = $ctor_link_section:tt,
169            default_term_method = $default_term_method:tt,
170            default_unload_method = $default_unload_method:tt,
171            export_name_prefix = $export_name_prefix:tt,
172            link_section = $link_section:tt,
173            method = $method:tt,
174            no_warn_on_missing_unsafe = $no_warn_on_missing_unsafe:tt,
175            used_linker = $used_linker:tt,
176        ),
177        meta = $meta:tt,
178        unsafe = $unsafe:tt,
179        item = $item:tt
180    ) ) => {
181        // method = other
182        $crate::__dtor_parse_impl!(@entry next=$next[$next_args], input=(
183            features = (
184                anonymous = $anonymous,
185                method = ($method $ctor_export_name_prefix $ctor_link_section $export_name_prefix $link_section),
186                no_warn_on_missing_unsafe = $no_warn_on_missing_unsafe,
187                used_linker = $used_linker,
188            ),
189            meta = $meta,
190            unsafe = $unsafe,
191            item = $item
192        ));
193    };
194
195    // Step 2: warn on missing unsafe
196
197    // warn iff no_warn_on_missing_unsafe is not present AND unsafe is not present
198    ( @entry next=$next:path[$next_args:tt], input=(
199        features = (
200            anonymous = $anonymous:tt,
201            method = $method:tt,
202            no_warn_on_missing_unsafe = (),
203            used_linker = $used_linker:tt,
204        ),
205        meta = $meta:tt,
206        unsafe = (),
207        item = $item:tt
208    ) ) => {
209        const _: () = {
210            #[deprecated="dtor deprecation note:\n\n\
211            Use of #[dtor] without `#[dtor(unsafe)]` or `unsafe fn` is deprecated. As code execution\n\
212            before main is unsupported by most Rust runtime functions, these functions must be marked\n\
213            `unsafe`."]
214            const fn dtor_without_unsafe_is_deprecated() {}
215            #[allow(unused)]
216            static UNSAFE_WARNING: () = {
217                dtor_without_unsafe_is_deprecated()
218            };
219        };
220
221        $crate::__dtor_parse_impl!(@entry next=$next[$next_args], input=(
222            features = (
223                anonymous = $anonymous,
224                method = $method,
225                used_linker = $used_linker,
226            ),
227            meta = $meta,
228            unsafe = (),
229            item = $item
230        ));
231    };
232
233    // If no_warn_on_missing_unsafe, ignore
234    ( @entry next=$next:path[$next_args:tt], input=(
235        features = (
236            anonymous = $anonymous:tt,
237            method = $method:tt,
238            no_warn_on_missing_unsafe = $no_warn_on_missing_unsafe:tt,
239            used_linker = $used_linker:tt,
240        ),
241        meta = $meta:tt,
242        unsafe = $unsafe:tt,
243        item = $item:tt
244    ) ) => {
245        $crate::__dtor_parse_impl!(@entry next=$next[$next_args], input=(
246            features = (
247                anonymous = $anonymous,
248                method = $method,
249                used_linker = $used_linker,
250            ),
251            meta = $meta,
252            unsafe = $unsafe,
253            item = $item
254        ));
255    };
256
257    // Step 3: Wrap anonymous if needed
258    ( @entry next=$next:path[$next_args:tt], input=(
259        features = (
260            anonymous = anonymous,
261            method = $method:tt,
262            used_linker = $used_linker:tt,
263        ),
264        meta = $meta:tt,
265        unsafe = $unsafe:tt,
266        item = $item:tt
267    ) ) => {
268        // Anonymous function
269        const _: () = {
270            $crate::__dtor_parse_impl!(@entry next=$next[$next_args], input=(
271                features = (
272                    method = $method,
273                    used_linker = $used_linker,
274                ),
275                meta = $meta,
276                unsafe = $unsafe,
277                item = $item
278            ));
279        };
280    };
281
282    ( @entry next=$next:path[$next_args:tt], input=(
283        features = (
284            anonymous = (),
285            method = $method:tt,
286            used_linker = $used_linker:tt,
287        ),
288        meta = $meta:tt,
289        unsafe = $unsafe:tt,
290        item = $item:tt
291    ) ) => {
292        $crate::__dtor_parse_impl!(@entry next=$next[$next_args], input=(
293            features = (
294                method = $method,
295                used_linker = $used_linker,
296            ),
297            meta = $meta,
298            unsafe = $unsafe,
299            item = $item
300        ));
301    };
302
303    // Step 4: Compute used_linker
304    ( @entry next=$next:path[$next_args:tt], input=(
305        features = (
306            method = $method:tt,
307            used_linker = (),
308        ),
309        meta = $meta:tt,
310        unsafe = $unsafe:tt,
311        item = $item:tt
312    ) ) => {
313        $crate::__dtor_parse_impl!(@entry next=$next[$next_args], input=(
314            features = (
315                method = $method,
316                used_linker_meta = (#[used]),
317            ),
318            meta = $meta,
319            unsafe = $unsafe,
320            item = $item
321        ));
322    };
323
324    ( @entry next=$next:path[$next_args:tt], input=(
325        features = (
326            method = $method:tt,
327            used_linker = used_linker,
328        ),
329        meta = $meta:tt,
330        unsafe = $unsafe:tt,
331        item = $item:tt
332    ) ) => {
333        $crate::__dtor_parse_impl!(@entry next=$next[$next_args], input=(
334            features = (
335                method = $method,
336                used_linker_meta = (#[used(linker)]),
337            ),
338            meta = $meta,
339            unsafe = $unsafe,
340            item = $item
341        ));
342    };
343
344    // Step 5: Delegate on method (at_module_exit, at_binary_exit, link_section)
345    ( @entry next=$next:path[$next_args:tt], input=(
346        features = (
347            method = ($method:tt $ctor_export_name_prefix:tt $ctor_link_section:tt $export_name_prefix:tt $link_section:tt),
348            used_linker_meta = (#$used_linker_meta:tt),
349        ),
350        meta = ($($meta:tt)*),
351        unsafe = ($($unsafe:tt)*),
352        item = ($vis:vis $(unsafe)? $( extern $abi:literal )? fn $name:ident $args:tt $( -> () )? {
353            $($body:tt)*
354        })
355    ) ) => {
356        $($meta)*
357        #[allow(dead_code)]
358        $vis $($unsafe)* $( extern $abi )? fn $name $args {
359            // The outer function may be attached to a struct, so we generate an
360            // inner function that is freestanding and call it from both places.
361            $($unsafe)* $( extern $abi )? fn __dtor_private_inner $args {
362                $($body)*
363            }
364
365            $crate::__dtor_parse_impl!(@dtor next=$next[$next_args], input=(
366                features = (
367                    ctor_export_name_prefix = $ctor_export_name_prefix,
368                    ctor_link_section = $ctor_link_section,
369                    export_name_prefix = $export_name_prefix,
370                    link_section = $link_section,
371                    method = $method,
372                    used_linker_meta = (#$used_linker_meta),
373                ),
374                item = ($($unsafe)* { __dtor_private_inner() })
375            ));
376
377            $($unsafe)* { __dtor_private_inner() }
378        }
379    };
380
381    // dtor
382
383    ( @dtor next=$next:path[$next_args:tt], input=(
384        features = (
385            ctor_export_name_prefix = $ctor_export_name_prefix:tt,
386            ctor_link_section = $ctor_link_section:tt,
387            export_name_prefix = (),
388            link_section = $link_section:tt,
389            method = linker,
390            used_linker_meta = (#$used_linker_meta:tt),
391        ),
392        item = ($($item:tt)*)
393    ) ) => {
394        const _: () = {
395            #[link_section = $link_section]
396            #$used_linker_meta
397            static __DTOR_PRIVATE_REF: extern "C" fn() = {
398                extern "C" fn __dtor_private() {
399                    $($item)*
400                }
401                __dtor_private
402            };
403        };
404    };
405
406    ( @dtor next=$next:path[$next_args:tt], input=(
407        features = (
408            ctor_export_name_prefix = $ctor_export_name_prefix:tt,
409            ctor_link_section = $ctor_link_section:tt,
410            export_name_prefix = $export_name_prefix:tt,
411            link_section = $link_section:tt,
412            method = linker,
413            used_linker_meta = (#$used_linker_meta:tt),
414        ),
415        item = ($($item:tt)*)
416    ) ) => {
417        const _: () = {
418            #[no_mangle]
419            #[export_name = concat!($export_name_prefix, "_",
420                env!("CARGO_PKG_NAME"),
421                "_",
422                module_path!(),
423                "_",
424                stringify!($name),
425                "_L",
426                line!(),
427                "C",
428                column!())]
429            extern "C" fn __dtor_private() {
430                $($item)*
431            }
432        };
433    };
434
435    ( @dtor next=$next:path[$next_args:tt], input=(
436        features = (
437            ctor_export_name_prefix = (),
438            ctor_link_section = $ctor_link_section:tt,
439            export_name_prefix = $export_name_prefix:tt,
440            link_section = $link_section:tt,
441            method = $method:ident,
442            used_linker_meta = (#$used_linker_meta:tt),
443        ),
444        item = ($($item:tt)*)
445    ) ) => {
446        const _: () = {
447            #[link_section = $ctor_link_section]
448            #$used_linker_meta
449            static __CTOR_PRIVATE_REF: unsafe extern "C" fn() = {
450                unsafe extern "C" fn __ctor_private() {
451                    #[cfg(all(target_family = "wasm", target_os = "unknown"))]
452                    {
453                        static DISARMED: ::core::sync::atomic::AtomicBool = ::core::sync::atomic::AtomicBool::new(false);
454                        if DISARMED.swap(true, ::core::sync::atomic::Ordering::Relaxed) {
455                            return;
456                        }
457                    }
458                    $crate::__support::$method(__dtor_private);
459                }
460                extern "C" fn __dtor_private() {
461                    $($item)*
462                }
463                __ctor_private
464            };
465        };
466    };
467
468    ( @dtor next=$next:path[$next_args:tt], input=(
469        features = (
470            ctor_export_name_prefix = $ctor_export_name_prefix:tt,
471            ctor_link_section = $ctor_link_section:tt,
472            export_name_prefix = $export_name_prefix:tt,
473            link_section = $link_section:tt,
474            method = $method:ident,
475            used_linker_meta = (#$used_linker_meta:tt),
476        ),
477        item = ($($item:tt)*)
478    ) ) => {
479        const _: () = {
480            #[no_mangle]
481            #[export_name = concat!($ctor_export_name_prefix, "_",
482                env!("CARGO_PKG_NAME"),
483                "_",
484                module_path!(),
485                "_",
486                stringify!($name),
487                "_L",
488                line!(),
489                "C",
490                column!())]
491            unsafe extern "C" fn __ctor_private() {
492                $crate::__support::$method(__dtor_private);
493            }
494
495            extern "C" fn __dtor_private() {
496                $($item)*
497            }
498        };
499    };
500
501    ( @entry next=$next:path[$next_args:tt], input=$input:tt ) => {
502        compile_error!(concat!("Invalid dtor input: ", stringify!($input)));
503    };
504}