target_feature_dispatch/
lib.rs

1// SPDX-License-Identifier: MIT
2// SPDX-FileCopyrightText: Copyright (C) 2025 Tsukasa OI <floss_rust@irq.a4lg.com>.
3
4#![doc = include_str!("../docs/readme.md")]
5// no_std by default.
6#![no_std]
7// Unsafe code is not allowed.  Forbid it unless in the tests and documentation.
8#![deny(unsafe_code)]
9#![cfg_attr(not(any(test, doc)), forbid(unsafe_code))]
10// In the code maintenance mode, disallow all warnings.
11#![cfg_attr(feature = "maint-code", deny(warnings))]
12// Non-test code requires documents.
13#![cfg_attr(not(test), warn(missing_docs, clippy::missing_docs_in_private_items))]
14// Unless in the maintenance mode, allow unknown lints / old lint names.
15#![cfg_attr(
16    not(feature = "maint-lints"),
17    allow(unknown_lints, renamed_and_removed_lints)
18)]
19
20// Import std on documentation.
21#[cfg(doc)]
22extern crate std;
23
24#[doc = include_str!("../docs/target_feature_dispatch.md")]
25#[macro_export]
26macro_rules! target_feature_dispatch {
27    /*
28        Public Interface.
29
30        Dynamic dispatching: default-disabled (any())
31        Nightly features:    default-disabled (any())
32        Non-fallback paths:  default-enabled  (all())
33    */
34    ($(#[$($pseudo_meta: tt)+])* $(if $($arch: tt $(($arch_arg: tt))?)||+ { $($if: tt)* })else+ else { $($else: tt)* }) => {
35        $crate::target_feature_dispatch!(
36            @__tgtfeat_dispatch_parse_options
37            (any(), any(), all())
38            $(#[$($pseudo_meta)+])*
39            ($($else)*) $((($($arch$(($arch_arg))?)||+) ($($if)*)))+
40        )
41    };
42
43    /*
44        Parse options.
45    */
46    // Unconditional "dynamic".
47    (@__tgtfeat_dispatch_parse_options ($dyn: meta, $nightly: meta, $dispatch: meta) #[dynamic] $($rest: tt)+) => {
48        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_parse_options (all(), $nightly, $dispatch) $($rest)+)
49    };
50    // Unconditional "static".
51    (@__tgtfeat_dispatch_parse_options ($dyn: meta, $nightly: meta, $dispatch: meta) #[static] $($rest: tt)+) => {
52        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_parse_options (any(), $nightly, $dispatch) $($rest)+)
53    };
54    // Unconditional "unstable".
55    (@__tgtfeat_dispatch_parse_options ($dyn: meta, $nightly: meta, $dispatch: meta) #[unstable] $($rest: tt)+) => {
56        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_parse_options ($dyn, all(), $dispatch) $($rest)+)
57    };
58    // Unconditional "stable".
59    (@__tgtfeat_dispatch_parse_options ($dyn: meta, $nightly: meta, $dispatch: meta) #[stable] $($rest: tt)+) => {
60        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_parse_options ($dyn, any(), $dispatch) $($rest)+)
61    };
62    // Conditional "dynamic".
63    (@__tgtfeat_dispatch_parse_options ($dyn: meta, $nightly: meta, $dispatch: meta) #[cfg_attr($meta: meta, dynamic)] $($rest: tt)+) => {
64        $crate::target_feature_dispatch!(
65            @__tgtfeat_dispatch_parse_options
66            (any($dyn, $meta), $nightly, $dispatch)
67            $($rest)+
68        )
69    };
70    // Conditional "static".
71    (@__tgtfeat_dispatch_parse_options ($dyn: meta, $nightly: meta, $dispatch: meta) #[cfg_attr($meta: meta, static)] $($rest: tt)+) => {
72        $crate::target_feature_dispatch!(
73            @__tgtfeat_dispatch_parse_options
74            (all($dyn, not($meta)), $nightly, $dispatch)
75            $($rest)+
76        )
77    };
78    // Conditional "unstable".
79    (@__tgtfeat_dispatch_parse_options ($dyn: meta, $nightly: meta, $dispatch: meta) #[cfg_attr($meta: meta, unstable)] $($rest: tt)+) => {
80        $crate::target_feature_dispatch!(
81            @__tgtfeat_dispatch_parse_options
82            ($dyn, any($nightly, $meta), $dispatch)
83            $($rest)+
84        )
85    };
86    // Conditional "stable".
87    (@__tgtfeat_dispatch_parse_options ($dyn: meta, $nightly: meta, $dispatch: meta) #[cfg_attr($meta: meta, stable)] $($rest: tt)+) => {
88        $crate::target_feature_dispatch!(
89            @__tgtfeat_dispatch_parse_options
90            ($dyn, all($nightly, not($meta)), $dispatch)
91            $($rest)+
92        )
93    };
94    // Conditional non-fallback paths.
95    (@__tgtfeat_dispatch_parse_options ($dyn: meta, $nightly: meta, $dispatch: meta) #[cfg_non_fallback($meta: meta)] $($rest: tt)+) => {
96        $crate::target_feature_dispatch!(
97            @__tgtfeat_dispatch_parse_options
98            ($dyn, $nightly, all($dispatch, $meta))
99            $($rest)+
100        )
101    };
102    // Invalid pseudo-attribute.
103    (@__tgtfeat_dispatch_parse_options ($dyn: meta, $nightly: meta, $dispatch: meta) #[$($pmeta: tt)+] $($rest: tt)+) => {
104        compile_error!(concat!("invalid pseudo-attribute: ", stringify!(#[$($pmeta)+])));
105    };
106    // No more options (pass to the architecture-specific chain).
107    (@__tgtfeat_dispatch_parse_options ($dyn: meta, $nightly: meta, $dispatch: meta) ($($else: tt)*) $(($($ifs: tt)+))+) => {
108        {
109            #[cfg($dispatch)]
110            {
111                $crate::target_feature_dispatch!(
112                    @__tgtfeat_dispatch_arch_chain ($dyn, $nightly)
113                    ($($else)*) $(($($ifs)+))+
114                )
115            }
116            #[cfg(not($dispatch))]
117            {
118                $crate::target_feature_dispatch!(@__tgtfeat_dispatch_as_expr $($else)*)
119            }
120        }
121    };
122
123    /*
124        Architecture-specific `if`-`else` chain.
125        Note that families are also parsed in @__tgtfeat_dispatch_arch_chain_2.
126    */
127    // `if`: family("aarch64") → any(target_arch = "aarch64", target_arch = "arm64ec")
128    (@__tgtfeat_dispatch_arch_chain ($($opts: meta),*) ($($else: tt)*) ((family("aarch64")) ($($if: tt)*)) $($rest: tt)*) => {
129        {
130            #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))]
131            {
132                $crate::target_feature_dispatch!(
133                    @__tgtfeat_dispatch_arch_clause (family("aarch64")) ($($opts),*)
134                    ($($else)*) ($($if)*)
135                )
136            }
137            #[cfg(not(any(target_arch = "aarch64", target_arch = "arm64ec")))]
138            {
139                $crate::target_feature_dispatch!(
140                    @__tgtfeat_dispatch_arch_chain ($($opts),*)
141                    ($($else)*) $($rest)*
142                )
143            }
144        }
145    };
146    // `if`: family("riscv") → any(target_arch = "riscv32", target_arch = "riscv64")
147    (@__tgtfeat_dispatch_arch_chain ($($opts: meta),*) ($($else: tt)*) ((family("riscv")) ($($if: tt)*)) $($rest: tt)*) => {
148        {
149            #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
150            {
151                $crate::target_feature_dispatch!(
152                    @__tgtfeat_dispatch_arch_clause (family("riscv")) ($($opts),*)
153                    ($($else)*) ($($if)*)
154                )
155            }
156            #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
157            {
158                $crate::target_feature_dispatch!(
159                    @__tgtfeat_dispatch_arch_chain ($($opts),*)
160                    ($($else)*) $($rest)*
161                )
162            }
163        }
164    };
165    // `if`: family("x86") → any(target_arch = "x86", target_arch = "x86_64")
166    (@__tgtfeat_dispatch_arch_chain ($($opts: meta),*) ($($else: tt)*) ((family("x86")) ($($if: tt)*)) $($rest: tt)*) => {
167        {
168            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
169            {
170                $crate::target_feature_dispatch!(
171                    @__tgtfeat_dispatch_arch_clause (family("x86")) ($($opts),*)
172                    ($($else)*) ($($if)*)
173                )
174            }
175            #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
176            {
177                $crate::target_feature_dispatch!(
178                    @__tgtfeat_dispatch_arch_chain ($($opts),*)
179                    ($($else)*) $($rest)*
180                )
181            }
182        }
183    };
184    // `if`: class("arm") → any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm")
185    (@__tgtfeat_dispatch_arch_chain ($($opts: meta),*) ($($else: tt)*) ((class("arm")) ($($if: tt)*)) $($rest: tt)*) => {
186        {
187            #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm"))]
188            {
189                $crate::target_feature_dispatch!(
190                    @__tgtfeat_dispatch_arch_clause (class("arm")) ($($opts),*)
191                    ($($else)*) ($($if)*)
192                )
193            }
194            #[cfg(not(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm")))]
195            {
196                $crate::target_feature_dispatch!(
197                    @__tgtfeat_dispatch_arch_chain ($($opts),*)
198                    ($($else)*) $($rest)*
199                )
200            }
201        }
202    };
203    // `if`: class("mips") → any(target_arch = "mips", target_arch = "mips64", target_arch = "mips32r6", target_arch = "mips64r6")
204    (@__tgtfeat_dispatch_arch_chain ($($opts: meta),*) ($($else: tt)*) ((class("mips")) ($($if: tt)*)) $($rest: tt)*) => {
205        {
206            #[cfg(any(target_arch = "mips", target_arch = "mips64", target_arch = "mips32r6", target_arch = "mips64r6"))]
207            {
208                $crate::target_feature_dispatch!(
209                    @__tgtfeat_dispatch_arch_clause (class("mips")) ($($opts),*)
210                    ($($else)*) ($($if)*)
211                )
212            }
213            #[cfg(not(any(target_arch = "mips", target_arch = "mips64", target_arch = "mips32r6", target_arch = "mips64r6")))]
214            {
215                $crate::target_feature_dispatch!(
216                    @__tgtfeat_dispatch_arch_chain ($($opts),*)
217                    ($($else)*) $($rest)*
218                )
219            }
220        }
221    };
222    // `if`: class("mips-classic") → any(target_arch = "mips", target_arch = "mips64")
223    (@__tgtfeat_dispatch_arch_chain ($($opts: meta),*) ($($else: tt)*) ((class("mips-classic")) ($($if: tt)*)) $($rest: tt)*) => {
224        {
225            #[cfg(any(target_arch = "mips", target_arch = "mips64"))]
226            {
227                $crate::target_feature_dispatch!(
228                    @__tgtfeat_dispatch_arch_clause (class("mips-classic")) ($($opts),*)
229                    ($($else)*) ($($if)*)
230                )
231            }
232            #[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
233            {
234                $crate::target_feature_dispatch!(
235                    @__tgtfeat_dispatch_arch_chain ($($opts),*)
236                    ($($else)*) $($rest)*
237                )
238            }
239        }
240    };
241    // `if`: class("powerpc") → any(target_arch = "powerpc", target_arch = "powerpc64")
242    (@__tgtfeat_dispatch_arch_chain ($($opts: meta),*) ($($else: tt)*) ((class("powerpc")) ($($if: tt)*)) $($rest: tt)*) => {
243        {
244            #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
245            {
246                $crate::target_feature_dispatch!(
247                    @__tgtfeat_dispatch_arch_clause (class("powerpc")) ($($opts),*)
248                    ($($else)*) ($($if)*)
249                )
250            }
251            #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))]
252            {
253                $crate::target_feature_dispatch!(
254                    @__tgtfeat_dispatch_arch_chain ($($opts),*)
255                    ($($else)*) $($rest)*
256                )
257            }
258        }
259    };
260    // `if`: class("sparc") → any(target_arch = "sparc", target_arch = "sparc64")
261    (@__tgtfeat_dispatch_arch_chain ($($opts: meta),*) ($($else: tt)*) ((class("sparc")) ($($if: tt)*)) $($rest: tt)*) => {
262        {
263            #[cfg(any(target_arch = "sparc", target_arch = "sparc64"))]
264            {
265                $crate::target_feature_dispatch!(
266                    @__tgtfeat_dispatch_arch_clause (class("sparc")) ($($opts),*)
267                    ($($else)*) ($($if)*)
268                )
269            }
270            #[cfg(not(any(target_arch = "sparc", target_arch = "sparc64")))]
271            {
272                $crate::target_feature_dispatch!(
273                    @__tgtfeat_dispatch_arch_chain ($($opts),*)
274                    ($($else)*) $($rest)*
275                )
276            }
277        }
278    };
279    // `if`: class("wasm") → any(target_arch = "wasm32", target_arch = "wasm64")
280    (@__tgtfeat_dispatch_arch_chain ($($opts: meta),*) ($($else: tt)*) ((class("wasm")) ($($if: tt)*)) $($rest: tt)*) => {
281        {
282            #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
283            {
284                $crate::target_feature_dispatch!(
285                    @__tgtfeat_dispatch_arch_clause (class("wasm")) ($($opts),*)
286                    ($($else)*) ($($if)*)
287                )
288            }
289            #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
290            {
291                $crate::target_feature_dispatch!(
292                    @__tgtfeat_dispatch_arch_chain ($($opts),*)
293                    ($($else)*) $($rest)*
294                )
295            }
296        }
297    };
298    // `if`: Generic (others): pass to the final step below.
299    (@__tgtfeat_dispatch_arch_chain ($($opts: meta),*) ($($else: tt)*) (($($arch: tt $(($arch_arg: tt))?)||+) ($($if: tt)*)) $($rest: tt)*) => {
300        $crate::target_feature_dispatch!(
301            @__tgtfeat_dispatch_arch_chain_2 ($($opts),*) ($($else)*)
302            (() ($($arch$(($arch_arg))?)||+) ($($if)*)) $($rest)*
303        )
304    };
305    // `else`
306    (@__tgtfeat_dispatch_arch_chain ($($opts: meta),*) ($($else: tt)*)) => {
307        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_as_expr $($else)*)
308    };
309
310    /*
311        Architecture-specific `if`-`else` chain: `if` (final step)
312        Conversion to regular list of architectures.
313    */
314    // family("aarch64") → "aarch64" || "arm64ec"
315    (
316        @__tgtfeat_dispatch_arch_chain_2 ($($opts: meta),*) ($($else: tt)*)
317        (($($added: tt,)*) (family("aarch64") $(|| $($arch2: tt $(($arch2_arg: tt))?)||+)?) ($($if: tt)*)) $($rest: tt)*
318    ) => {
319        $crate::target_feature_dispatch!(
320            @__tgtfeat_dispatch_arch_chain_2 ($($opts),*) ($($else)*)
321            (($($added,)* "aarch64", "arm64ec",) ($($($arch2$(($arch2_arg))?)||+)?) ($($if)*))
322            $($rest)*
323        )
324    };
325    // family("riscv") → "riscv32" || "riscv64"
326    (
327        @__tgtfeat_dispatch_arch_chain_2 ($($opts: meta),*) ($($else: tt)*)
328        (($($added: tt,)*) (family("riscv") $(|| $($arch2: tt $(($arch2_arg: tt))?)||+)?) ($($if: tt)*)) $($rest: tt)*
329    ) => {
330        $crate::target_feature_dispatch!(
331            @__tgtfeat_dispatch_arch_chain_2 ($($opts),*) ($($else)*)
332            (($($added,)* "riscv32", "riscv64",) ($($($arch2$(($arch2_arg))?)||+)?) ($($if)*))
333            $($rest)*
334        )
335    };
336    // family("x86") → "x86" || "x86_64"
337    (
338        @__tgtfeat_dispatch_arch_chain_2 ($($opts: meta),*) ($($else: tt)*)
339        (($($added: tt,)*) (family("x86") $(|| $($arch2: tt $(($arch2_arg: tt))?)||+)?) ($($if: tt)*)) $($rest: tt)*
340    ) => {
341        $crate::target_feature_dispatch!(
342            @__tgtfeat_dispatch_arch_chain_2 ($($opts),*) ($($else)*)
343            (($($added,)* "x86", "x86_64",) ($($($arch2$(($arch2_arg))?)||+)?) ($($if)*))
344            $($rest)*
345        )
346    };
347    // class("arm") → "aarch64" || "arm64ec" || "arm"
348    (
349        @__tgtfeat_dispatch_arch_chain_2 ($($opts: meta),*) ($($else: tt)*)
350        (($($added: tt,)*) (class("arm") $(|| $($arch2: tt $(($arch2_arg: tt))?)||+)?) ($($if: tt)*)) $($rest: tt)*
351    ) => {
352        $crate::target_feature_dispatch!(
353            @__tgtfeat_dispatch_arch_chain_2 ($($opts),*) ($($else)*)
354            (($($added,)* "aarch64", "arm64ec", "arm",) ($($($arch2$(($arch2_arg))?)||+)?) ($($if)*))
355            $($rest)*
356        )
357    };
358    // class("mips") → "mips" || "mips64" || "mips32r6" || "mips64r6"
359    (
360        @__tgtfeat_dispatch_arch_chain_2 ($($opts: meta),*) ($($else: tt)*)
361        (($($added: tt,)*) (class("mips") $(|| $($arch2: tt $(($arch2_arg: tt))?)||+)?) ($($if: tt)*)) $($rest: tt)*
362    ) => {
363        $crate::target_feature_dispatch!(
364            @__tgtfeat_dispatch_arch_chain_2 ($($opts),*) ($($else)*)
365            (($($added,)* "mips", "mips64", "mips32r6", "mips64r6",) ($($($arch2$(($arch2_arg))?)||+)?) ($($if)*))
366            $($rest)*
367        )
368    };
369    // class("mips-classic") → "mips" || "mips64"
370    (
371        @__tgtfeat_dispatch_arch_chain_2 ($($opts: meta),*) ($($else: tt)*)
372        (($($added: tt,)*) (class("mips-classic") $(|| $($arch2: tt $(($arch2_arg: tt))?)||+)?) ($($if: tt)*)) $($rest: tt)*
373    ) => {
374        $crate::target_feature_dispatch!(
375            @__tgtfeat_dispatch_arch_chain_2 ($($opts),*) ($($else)*)
376            (($($added,)* "mips", "mips64",) ($($($arch2$(($arch2_arg))?)||+)?) ($($if)*))
377            $($rest)*
378        )
379    };
380    // class("powerpc") → "powerpc" || "powerpc64"
381    (
382        @__tgtfeat_dispatch_arch_chain_2 ($($opts: meta),*) ($($else: tt)*)
383        (($($added: tt,)*) (class("powerpc") $(|| $($arch2: tt $(($arch2_arg: tt))?)||+)?) ($($if: tt)*)) $($rest: tt)*
384    ) => {
385        $crate::target_feature_dispatch!(
386            @__tgtfeat_dispatch_arch_chain_2 ($($opts),*) ($($else)*)
387            (($($added,)* "powerpc", "powerpc64",) ($($($arch2$(($arch2_arg))?)||+)?) ($($if)*))
388            $($rest)*
389        )
390    };
391    // class("sparc") → "sparc" || "sparc64"
392    (
393        @__tgtfeat_dispatch_arch_chain_2 ($($opts: meta),*) ($($else: tt)*)
394        (($($added: tt,)*) (class("sparc") $(|| $($arch2: tt $(($arch2_arg: tt))?)||+)?) ($($if: tt)*)) $($rest: tt)*
395    ) => {
396        $crate::target_feature_dispatch!(
397            @__tgtfeat_dispatch_arch_chain_2 ($($opts),*) ($($else)*)
398            (($($added,)* "sparc", "sparc64",) ($($($arch2$(($arch2_arg))?)||+)?) ($($if)*))
399            $($rest)*
400        )
401    };
402    // class("wasm") → "wasm32" || "wasm64"
403    (
404        @__tgtfeat_dispatch_arch_chain_2 ($($opts: meta),*) ($($else: tt)*)
405        (($($added: tt,)*) (class("wasm") $(|| $($arch2: tt $(($arch2_arg: tt))?)||+)?) ($($if: tt)*)) $($rest: tt)*
406    ) => {
407        $crate::target_feature_dispatch!(
408            @__tgtfeat_dispatch_arch_chain_2 ($($opts),*) ($($else)*)
409            (($($added,)* "wasm32", "wasm64",) ($($($arch2$(($arch2_arg))?)||+)?) ($($if)*))
410            $($rest)*
411        )
412    };
413    // Others
414    (
415        @__tgtfeat_dispatch_arch_chain_2 ($($opts: meta),*) ($($else: tt)*)
416        (($($added: tt,)*) ($arch1: tt ($arch1_arg: tt) $(|| $($arch2: tt$(($arch2_arg: tt))?)||+)?) ($($if: tt)*)) $($rest: tt)*
417    ) => {
418        compile_error!(concat!("Invalid architecture specifier: ", stringify!($arch1($arch1_arg))));
419    };
420    (
421        @__tgtfeat_dispatch_arch_chain_2 ($($opts: meta),*) ($($else: tt)*)
422        (($($added: tt,)*) ($arch1: tt $(|| $($arch2: tt$(($arch2_arg: tt))?)||+)?) ($($if: tt)*)) $($rest: tt)*
423    ) => {
424        $crate::target_feature_dispatch!(
425            @__tgtfeat_dispatch_arch_chain_2 ($($opts),*) ($($else)*)
426            (($($added,)* $arch1,) ($($($arch2$(($arch2_arg))?)||+)?) ($($if)*))
427            $($rest)*
428        )
429    };
430    // Architectural branch conversion is completed.
431    (
432        @__tgtfeat_dispatch_arch_chain_2 ($($opts: meta),*) ($($else: tt)*)
433        (($($added: tt,)+) () ($($if: tt)*)) $($rest: tt)*
434    ) => {
435        {
436            #[cfg(any($(target_arch = $added),+))]
437            {
438                $crate::target_feature_dispatch!(
439                    @__tgtfeat_dispatch_arch_clause ($($added)||+) ($($opts),*)
440                    ($($else)*) ($($if)*)
441                )
442            }
443            #[cfg(not(any($(target_arch = $added),+)))]
444            {
445                $crate::target_feature_dispatch!(
446                    @__tgtfeat_dispatch_arch_chain ($($opts),*)
447                    ($($else)*) $($rest)*
448                )
449            }
450        }
451    };
452
453    /*
454        Architecture-specific clause (one of architectural `if`).
455
456        If the `else` clause is missing, fallback for static dispatching is of
457        the root and fallback for dynamic dispatching is none
458        (meaning, do the "last resort" static dispatching as a fallback).
459
460        If the `else` clause exists, it is always used as the final fallback
461        (for both static and dynamic dispatching methods).
462
463        If one of the following is specified:
464
465        1.  ( EXPR )
466        2.  { /* statements (results in an expression) */ }
467        3.  (empty)
468
469        This is an architecture-only dispatch.
470    */
471    // `if`-`else` chain without `else`.
472    (
473        @__tgtfeat_dispatch_arch_clause ($($arch: tt $(($arch_arg: tt))?)||+) ($($opts: meta),*) ($($else1: tt)*)
474        ($(if $($feat: tt)&&+ { $($if: tt)* })else+)
475    ) => {
476        $crate::target_feature_dispatch!(
477            @__tgtfeat_dispatch_feat_chain_entry
478            ($($arch$(($arch_arg))?)||+) ($($opts),*)
479            ($($else1)*) (@__tgtfeat_dispatch_no_fallback)
480            $((($($feat)&&+) ($($if)*)))+
481        )
482    };
483    // `if`-`else` chain with invalid `else`.
484    (
485        @__tgtfeat_dispatch_arch_clause ($($arch: tt $(($arch_arg: tt))?)||+) ($($opts: meta),*) ($($else1: tt)*)
486        ($(if $($feat: tt)&&+ { $($if: tt)* })else+ else { @__tgtfeat_dispatch_no_fallback })
487    ) => {
488        compile_error!("invalid feature-specific `else` clause");
489    };
490    // `if`-`else` chain with `else`.
491    (
492        @__tgtfeat_dispatch_arch_clause ($($arch: tt $(($arch_arg: tt))?)||+) ($($opts: meta),*) ($($else1: tt)*)
493        ($(if $($feat: tt)&&+ { $($if: tt)* })else+ else { $($else2: tt)* })
494    ) => {
495        $crate::target_feature_dispatch!(
496            @__tgtfeat_dispatch_feat_chain_entry
497            ($($arch$(($arch_arg))?)||+) ($($opts),*)
498            ($($else2)*) ($($else2)*)
499            $((($($feat)&&+) ($($if)*)))+
500        )
501    };
502    // Architecture-only dispatch: Single expression enclosed by parens.
503    (@__tgtfeat_dispatch_arch_clause ($($arch: tt $(($arch_arg: tt))?)||+) ($($opts: meta),*) ($($else1: tt)*) (($expr: expr))) => {
504        $expr
505    };
506    // Architecture-only dispatch: Single block results in an expression.
507    (@__tgtfeat_dispatch_arch_clause ($($arch: tt $(($arch_arg: tt))?)||+) ($($opts: meta),*) ($($else1: tt)*) ({$($tt: tt)*})) => {
508        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_as_expr $($tt)*)
509    };
510    // Architecture-only dispatch: Empty (handle as returning the unit value).
511    (@__tgtfeat_dispatch_arch_clause ($($arch: tt $(($arch_arg: tt))?)||+) ($($opts: meta),*) ($($else1: tt)*) ()) => {
512        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_as_expr ())
513    };
514    // Invalid architecture clause.
515    (@__tgtfeat_dispatch_arch_clause ($($arch: tt $(($arch_arg: tt))?)||+) ($($opts: meta),*) ($($else1: tt)*) ($($tt: tt)*)) => {
516        compile_error!("unsupported or invalid architecture clause");
517    };
518
519    /*
520        Feature-specific `if`-`else` chain (entrypoint).
521        Determine dispatching based on the architecture.
522
523        $else_sta is used on fallback static dispatch path.
524        $else_dyn is either fallback on dynamic dispatch path or
525        special keyword "@__tgtfeat_dispatch_no_fallback" meaning none.
526    */
527    // Arm / AArch64 (64-bit): AArch64 + Arm64EC
528    (@__tgtfeat_dispatch_feat_chain_entry (family("aarch64")) ($($opts: meta),*) $($rest: tt)+) => {
529        $crate::target_feature_dispatch!(
530            @__tgtfeat_dispatch_feat_chain_dispatch_dyn ($($opts),*)
531            (::std::arch::is_aarch64_feature_detected) $($rest)+
532        )
533    };
534    (@__tgtfeat_dispatch_feat_chain_entry ("aarch64") ($($opts: meta),*) $($rest: tt)+) => {
535        $crate::target_feature_dispatch!(
536            @__tgtfeat_dispatch_feat_chain_dispatch_dyn ($($opts),*)
537            (::std::arch::is_aarch64_feature_detected) $($rest)+
538        )
539    };
540    (@__tgtfeat_dispatch_feat_chain_entry ("arm64ec") ($($opts: meta),*) $($rest: tt)+) => {
541        $crate::target_feature_dispatch!(
542            @__tgtfeat_dispatch_feat_chain_dispatch_dyn ($($opts),*)
543            (::std::arch::is_aarch64_feature_detected) $($rest)+
544        )
545    };
546    // RISC-V (32-bit and 64-bit)
547    (@__tgtfeat_dispatch_feat_chain_entry (family("riscv")) ($($opts: meta),*) $($rest: tt)+) => {
548        $crate::target_feature_dispatch!(
549            @__tgtfeat_dispatch_feat_chain_dispatch_dyn ($($opts),*)
550            (::std::arch::is_riscv_feature_detected) $($rest)+
551        )
552    };
553    (@__tgtfeat_dispatch_feat_chain_entry ("riscv32") ($($opts: meta),*) $($rest: tt)+) => {
554        $crate::target_feature_dispatch!(
555            @__tgtfeat_dispatch_feat_chain_dispatch_dyn ($($opts),*)
556            (::std::arch::is_riscv_feature_detected) $($rest)+
557        )
558    };
559    (@__tgtfeat_dispatch_feat_chain_entry ("riscv64") ($($opts: meta),*) $($rest: tt)+) => {
560        $crate::target_feature_dispatch!(
561            @__tgtfeat_dispatch_feat_chain_dispatch_dyn ($($opts),*)
562            (::std::arch::is_riscv_feature_detected) $($rest)+
563        )
564    };
565    // x86 (32-bit and 64-bit)
566    (@__tgtfeat_dispatch_feat_chain_entry (family("x86")) ($($opts: meta),*) $($rest: tt)+) => {
567        $crate::target_feature_dispatch!(
568            @__tgtfeat_dispatch_feat_chain_dispatch_dyn ($($opts),*)
569            (::std::arch::is_x86_feature_detected) $($rest)+
570        )
571    };
572    (@__tgtfeat_dispatch_feat_chain_entry ("x86") ($($opts: meta),*) $($rest: tt)+) => {
573        $crate::target_feature_dispatch!(
574            @__tgtfeat_dispatch_feat_chain_dispatch_dyn ($($opts),*)
575            (::std::arch::is_x86_feature_detected) $($rest)+
576        )
577    };
578    (@__tgtfeat_dispatch_feat_chain_entry ("x86_64") ($($opts: meta),*) $($rest: tt)+) => {
579        $crate::target_feature_dispatch!(
580            @__tgtfeat_dispatch_feat_chain_dispatch_dyn ($($opts),*)
581            (::std::arch::is_x86_feature_detected) $($rest)+
582        )
583    };
584    // Arm (32-bit)
585    (@__tgtfeat_dispatch_feat_chain_entry ("arm") ($($opts: meta),*) $($rest: tt)+) => {
586        $crate::target_feature_dispatch!(
587            @__tgtfeat_dispatch_feat_chain_dispatch_dyn_nightly ($($opts),*)
588            (::std::arch::is_arm_feature_detected) $($rest)+
589        )
590    };
591    // LoongArch (64-bit)
592    (@__tgtfeat_dispatch_feat_chain_entry ("loongarch64") ($($opts: meta),*) $($rest: tt)+) => {
593        $crate::target_feature_dispatch!(
594            @__tgtfeat_dispatch_feat_chain_dispatch_dyn_nightly ($($opts),*)
595            (::std::arch::is_loongarch_feature_detected) $($rest)+
596        )
597    };
598    // MIPS (32-bit)
599    (@__tgtfeat_dispatch_feat_chain_entry ("mips") ($($opts: meta),*) $($rest: tt)+) => {
600        $crate::target_feature_dispatch!(
601            @__tgtfeat_dispatch_feat_chain_dispatch_dyn_nightly ($($opts),*)
602            (::std::arch::is_mips_feature_detected) $($rest)+
603        )
604    };
605    // MIPS (64-bit)
606    (@__tgtfeat_dispatch_feat_chain_entry ("mips64") ($($opts: meta),*) $($rest: tt)+) => {
607        $crate::target_feature_dispatch!(
608            @__tgtfeat_dispatch_feat_chain_dispatch_dyn_nightly ($($opts),*)
609            (::std::arch::is_mips64_feature_detected) $($rest)+
610        )
611    };
612    // PowerPC (32-bit)
613    (@__tgtfeat_dispatch_feat_chain_entry ("powerpc") ($($opts: meta),*) $($rest: tt)+) => {
614        $crate::target_feature_dispatch!(
615            @__tgtfeat_dispatch_feat_chain_dispatch_dyn_nightly ($($opts),*)
616            (::std::arch::is_powerpc_feature_detected) $($rest)+
617        )
618    };
619    // PowerPC (64-bit)
620    (@__tgtfeat_dispatch_feat_chain_entry ("powerpc64") ($($opts: meta),*) $($rest: tt)+) => {
621        $crate::target_feature_dispatch!(
622            @__tgtfeat_dispatch_feat_chain_dispatch_dyn_nightly ($($opts),*)
623            (::std::arch::is_powerpc64_feature_detected) $($rest)+
624        )
625    };
626    // s390x (z/Architecture starting with IBM zSeries; 64-bit)
627    (@__tgtfeat_dispatch_feat_chain_entry ("s390x") ($($opts: meta),*) $($rest: tt)+) => {
628        $crate::target_feature_dispatch!(
629            @__tgtfeat_dispatch_feat_chain_dispatch_dyn_nightly ($($opts),*)
630            (::std::arch::is_s390x_feature_detected) $($rest)+
631        )
632    };
633    // Arm (32-bit and 64-bit)
634    (@__tgtfeat_dispatch_feat_chain_entry (class("arm")) ($($opts: meta),*) $($rest: tt)+) => {
635        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_feat_chain_dispatch_static ($($opts),*) $($rest)+)
636    };
637    // MIPS (32-bit and 64-bit)
638    (@__tgtfeat_dispatch_feat_chain_entry (class("mips")) ($($opts: meta),*) $($rest: tt)+) => {
639        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_feat_chain_dispatch_static ($($opts),*) $($rest)+)
640    };
641    // MIPS (32-bit and 64-bit) - classic variant
642    (@__tgtfeat_dispatch_feat_chain_entry (class("mips-classic")) ($($opts: meta),*) $($rest: tt)+) => {
643        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_feat_chain_dispatch_static ($($opts),*) $($rest)+)
644    };
645    // PowerPC (32-bit and 64-bit)
646    (@__tgtfeat_dispatch_feat_chain_entry (class("powerpc")) ($($opts: meta),*) $($rest: tt)+) => {
647        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_feat_chain_dispatch_static ($($opts),*) $($rest)+)
648    };
649    // SPARC (32-bit and 64-bit)
650    (@__tgtfeat_dispatch_feat_chain_entry (class("sparc")) ($($opts: meta),*) $($rest: tt)+) => {
651        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_feat_chain_dispatch_static ($($opts),*) $($rest)+)
652    };
653    // WebAssembly (32-bit and 64-bit)
654    (@__tgtfeat_dispatch_feat_chain_entry (class("wasm")) ($($opts: meta),*) $($rest: tt)+) => {
655        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_feat_chain_dispatch_static ($($opts),*) $($rest)+)
656    };
657    // Others (use static dispatching only)
658    (@__tgtfeat_dispatch_feat_chain_entry ($($arch: tt $(($arch_arg: tt))?)||+) ($($opts: meta),*) $($rest: tt)+) => {
659        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_feat_chain_dispatch_static ($($opts),*) $($rest)+)
660    };
661
662    /*
663        Feature-specific `if`-`else` chain (with specific dispatching).
664    */
665    // Dynamic dispatching (if enabled).
666    (
667        @__tgtfeat_dispatch_feat_chain_dispatch_dyn ($dyn: meta, $nightly: meta) ($detect: path)
668        ($($else_sta: tt)*) ($($else_dyn: tt)*) $($rest: tt)+
669    ) => {
670        {
671            #[cfg($dyn)]
672            {
673                $crate::target_feature_dispatch!(
674                    @__tgtfeat_dispatch_feat_chain_dynamic
675                    ($detect)
676                    ($($else_sta)*) ($($else_dyn)*) $($rest)+
677                )
678            }
679            #[cfg(not($dyn))]
680            {
681                $crate::target_feature_dispatch!(@__tgtfeat_dispatch_feat_chain_static ($($else_sta)*) $($rest)+)
682            }
683        }
684    };
685    // Dynamic dispatching only on Nightly (and if enabled).
686    (
687        @__tgtfeat_dispatch_feat_chain_dispatch_dyn_nightly ($dyn: meta, $nightly: meta) ($detect: path)
688        ($($else_sta: tt)*) ($($else_dyn: tt)*) $($rest: tt)+
689    ) => {
690        {
691            #[cfg(all($dyn, $nightly))]
692            {
693                $crate::target_feature_dispatch!(
694                    @__tgtfeat_dispatch_feat_chain_dynamic
695                    ($detect)
696                    ($($else_sta)*) ($($else_dyn)*) $($rest)+
697                )
698            }
699            #[cfg(not(all($dyn, $nightly)))]
700            {
701                $crate::target_feature_dispatch!(@__tgtfeat_dispatch_feat_chain_static ($($else_sta)*) $($rest)+)
702            }
703        }
704    };
705    // Static (only) dispatching.
706    (
707        @__tgtfeat_dispatch_feat_chain_dispatch_static ($dyn: meta, $nightly: meta)
708        ($($else_sta: tt)*) ($($else_dyn: tt)*) $($rest: tt)+
709    ) => {
710        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_feat_chain_static ($($else_sta)*) $($rest)+)
711    };
712
713    /*
714        Feature-specific dynamic dispatching.
715    */
716    // No feature-specific fallback
717    // (use static dispatching with global fallback).
718    (
719        @__tgtfeat_dispatch_feat_chain_dynamic ($detect: path)
720        ($($else_sta: tt)*) (@__tgtfeat_dispatch_no_fallback) $((($($feat: tt)&&+) ($($if: tt)*)))+
721    ) => {
722        $(
723            if $({$detect!($feat)})&&+ {
724                $crate::target_feature_dispatch!(@__tgtfeat_dispatch_as_expr $($if)*)
725            }
726        )else+
727        else {
728            $crate::target_feature_dispatch!(
729                @__tgtfeat_dispatch_feat_chain_static
730                ($($else_sta)*) $((($($feat)&&+) ($($if)*)))+
731            )
732        }
733    };
734    // Architecture-specific fallback is specified
735    // (always use local fallback).
736    (
737        @__tgtfeat_dispatch_feat_chain_dynamic ($detect: path)
738        ($($else_sta: tt)*) ($($else_dyn: tt)*) $((($($feat: tt)&&+) ($($if: tt)*)))+
739    ) => {
740        $(
741            if $({$detect!($feat)})&&+ {
742                $crate::target_feature_dispatch!(@__tgtfeat_dispatch_as_expr $($if)*)
743            }
744        )else+
745        else {
746            $crate::target_feature_dispatch!(@__tgtfeat_dispatch_as_expr $($else_dyn)*)
747        }
748    };
749
750    /*
751        Feature-specific static dispatching.
752    */
753    // `if`
754    (@__tgtfeat_dispatch_feat_chain_static ($($else: tt)*) (($($feat: tt)&&+) ($($if: tt)*)) $($rest: tt)*) => {
755        {
756            #[cfg(all($(target_feature = $feat),+))]
757            {
758                $crate::target_feature_dispatch!(@__tgtfeat_dispatch_as_expr $($if)*)
759            }
760            #[cfg(not(all($(target_feature = $feat),+)))]
761            {
762                $crate::target_feature_dispatch!(
763                    @__tgtfeat_dispatch_feat_chain_static
764                    ($($else)*) $($rest)*
765                )
766            }
767        }
768    };
769    // `else`
770    (@__tgtfeat_dispatch_feat_chain_static ($($else: tt)*)) => {
771        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_as_expr $($else)*)
772    };
773
774    // Coerce tokens into an expression.
775    (@__tgtfeat_dispatch_as_expr $expr: expr) => { $expr };
776    // If empty, substitute with the unit value.
777    (@__tgtfeat_dispatch_as_expr) => { () };
778    // If the next coercion (STMTS → { STMTS }) did not make an expression,
779    // raise a compile error.
780    (@__tgtfeat_dispatch_as_expr {$($tt: tt)+}) => {
781        compile_error!(concat!("failed to parse { ", stringify!($($tt)+), "} as expression"));
782    };
783    // Coercion for series of statements (STMTS → { STMTS }).
784    (@__tgtfeat_dispatch_as_expr $($tt: tt)+) => {
785        $crate::target_feature_dispatch!(@__tgtfeat_dispatch_as_expr { $($tt)+ } )
786    };
787}