px_linkme_impl/
declaration.rs

1use crate::{attr, linker, ty};
2use proc_macro2::{Span, TokenStream};
3use quote::quote;
4use syn::parse::{Parse, ParseStream, Result};
5use syn::{bracketed, Attribute, Error, Ident, Token, Type, Visibility};
6
7struct Declaration {
8    attrs: Vec<Attribute>,
9    vis: Visibility,
10    ident: Ident,
11    ty: Type,
12}
13
14impl Parse for Declaration {
15    fn parse(input: ParseStream) -> Result<Self> {
16        let attrs = input.call(Attribute::parse_outer)?;
17        let vis: Visibility = input.parse()?;
18        input.parse::<Token![static]>()?;
19        let mut_token: Option<Token![mut]> = input.parse()?;
20        if let Some(mut_token) = mut_token {
21            return Err(Error::new_spanned(
22                mut_token,
23                "static mut is not supported by distributed_slice",
24            ));
25        }
26        let ident: Ident = input.parse()?;
27        input.parse::<Token![:]>()?;
28        let ty: Type = input.parse()?;
29
30        let eq_token: Option<Token![=]> = input.parse()?;
31        if eq_token.is_some() {
32            let content;
33            bracketed!(content in input);
34            content.parse::<Token![..]>()?;
35        }
36
37        input.parse::<Token![;]>()?;
38
39        Ok(Declaration {
40            attrs,
41            vis,
42            ident,
43            ty,
44        })
45    }
46}
47
48pub fn expand(input: TokenStream) -> TokenStream {
49    let msg = "distributed_slice is not implemented for this platform";
50    let error = Error::new_spanned(&input, msg);
51    let unsupported_platform = error.to_compile_error();
52
53    let decl: Declaration = match syn::parse2(input) {
54        Ok(decl) => decl,
55        Err(err) => return err.to_compile_error(),
56    };
57
58    let mut attrs = decl.attrs;
59    let vis = decl.vis;
60    let ident = decl.ident;
61    let mut ty = decl.ty;
62    let name = ident.to_string();
63
64    let xname = format!("@@linkme/{name}");
65
66    let linkme_path = match attr::linkme_path(&mut attrs) {
67        Ok(path) => path,
68        Err(err) => return err.to_compile_error(),
69    };
70
71    ty::populate_static_lifetimes(&mut ty);
72
73    let used = if cfg!(feature = "used_linker") {
74        quote!(#[used(linker)])
75    } else {
76        quote!(#[used])
77    };
78
79    let linux_section = linker::linux::section(&ident);
80    let linux_section_start = linker::linux::section_start(&ident);
81    let linux_section_stop = linker::linux::section_stop(&ident);
82    let linux_dupcheck = linux_section.replacen("linkme", "linkm2", 1);
83    let linux_dupcheck_start = linux_section_start.replacen("linkme", "linkm2", 1);
84    let linux_dupcheck_stop = linux_section_stop.replacen("linkme", "linkm2", 1);
85
86    let macho_section = linker::macho::section(&ident);
87    let macho_section_start = linker::macho::section_start(&ident);
88    let macho_section_stop = linker::macho::section_stop(&ident);
89    let macho_dupcheck = macho_section.replacen("linkme", "linkm2", 1);
90    let macho_dupcheck_start = macho_section_start.replacen("linkme", "linkm2", 1);
91    let macho_dupcheck_stop = macho_section_stop.replacen("linkme", "linkm2", 1);
92
93    let windows_section = linker::windows::section(&ident);
94    let windows_section_start = linker::windows::section_start(&ident);
95    let windows_section_stop = linker::windows::section_stop(&ident);
96    let windows_dupcheck = windows_section.replacen("linkme", "linkm2", 1);
97    let windows_dupcheck_start = windows_section_start.replacen("linkme", "linkm2", 1);
98    let windows_dupcheck_stop = windows_section_stop.replacen("linkme", "linkm2", 1);
99
100    let illumos_section = linker::illumos::section(&ident);
101    let illumos_section_start = linker::illumos::section_start(&ident);
102    let illumos_section_stop = linker::illumos::section_stop(&ident);
103    let illumos_dupcheck = illumos_section.replacen("linkme", "linkm2", 1);
104    let illumos_dupcheck_start = illumos_section_start.replacen("linkme", "linkm2", 1);
105    let illumos_dupcheck_stop = illumos_section_stop.replacen("linkme", "linkm2", 1);
106
107    let bsd_section = linker::bsd::section(&ident);
108    let bsd_section_start = linker::bsd::section_start(&ident);
109    let bsd_section_stop = linker::bsd::section_stop(&ident);
110    let bsd_dupcheck = bsd_section.replacen("linkme", "linkm2", 1);
111    let bsd_dupcheck_start = bsd_section_start.replacen("linkme", "linkm2", 1);
112    let bsd_dupcheck_stop = bsd_section_stop.replacen("linkme", "linkm2", 1);
113
114    let call_site = Span::call_site();
115    let link_section_macro_str = format!("_linkme_macro_{}", ident);
116    let link_section_macro = Ident::new(&link_section_macro_str, call_site);
117
118    let unsafe_extern = if cfg!(no_unsafe_extern_blocks) {
119        None
120    } else {
121        Some(Token![unsafe](call_site))
122    };
123
124    let (unsafe_attr, link_section_attr, wasm_import_attr) =
125        if cfg!(no_unsafe_attributes) {
126            // #[cfg_attr(all(), link_section = ...)]
127            (
128                Ident::new("cfg_attr", call_site),
129                quote!(all(), link_section),
130                quote!(all(), wasm_import_module),
131            )
132        } else {
133            // #[unsafe(link_section = ...)]
134            (
135                Ident::new("unsafe", call_site),
136                quote!(link_section),
137                quote!(wasm_import_module),
138            )
139        };
140
141    quote! {
142        #(#attrs)*
143        #vis static #ident: #linkme_path::DistributedSlice<#ty> = {
144            #[cfg(target_family = "wasm")]
145            {
146                #[#unsafe_attr(#wasm_import_attr = #xname)]
147                #unsafe_extern extern "C"{
148                    fn _init(a: *mut <#ty as #linkme_path::__private::Slice>::Element) -> *mut <#ty as #linkme_path::__private::Slice>::Element;
149                    fn _len() -> usize;
150                }
151                static lazy: #linkme_path::__private::once_cell::sync::OnceCell<#linkme_path::__private::StaticPtr<<#ty as #linkme_path::__private::Slice>::Element>> = #linkme_path::__private::std::sync::OnceLock::new();
152                unsafe{
153                    #linkme_path::DistributedSlice::private_new(#name,_init,_len,&lazy)
154                }
155            }
156            #[cfg(not(target_family = "wasm"))]
157            {
158            #[cfg(any(
159                target_os = "none",
160                target_os = "linux",
161                target_os = "macos",
162                target_os = "ios",
163                target_os = "tvos",
164                target_os = "android",
165                target_os = "fuchsia",
166                target_os = "illumos",
167                target_os = "freebsd",
168                target_os = "openbsd",
169                target_os = "psp",
170            ))]
171            #unsafe_extern extern "Rust" {
172                #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), link_name = #linux_section_start)]
173                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_section_start)]
174                #[cfg_attr(target_os = "illumos", link_name = #illumos_section_start)]
175                #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), link_name = #bsd_section_start)]
176                static LINKME_START: <#ty as #linkme_path::__private::Slice>::Element;
177
178                #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), link_name = #linux_section_stop)]
179                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_section_stop)]
180                #[cfg_attr(target_os = "illumos", link_name = #illumos_section_stop)]
181                #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), link_name = #bsd_section_stop)]
182                static LINKME_STOP: <#ty as #linkme_path::__private::Slice>::Element;
183
184                #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), link_name = #linux_dupcheck_start)]
185                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_dupcheck_start)]
186                #[cfg_attr(target_os = "illumos", link_name = #illumos_dupcheck_start)]
187                #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), link_name = #bsd_dupcheck_start)]
188                static DUPCHECK_START: #linkme_path::__private::usize;
189
190                #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), link_name = #linux_dupcheck_stop)]
191                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_dupcheck_stop)]
192                #[cfg_attr(target_os = "illumos", link_name = #illumos_dupcheck_stop)]
193                #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), link_name = #bsd_dupcheck_stop)]
194                static DUPCHECK_STOP: #linkme_path::__private::usize;
195            }
196
197            #[cfg(any(target_os = "uefi", target_os = "windows"))]
198            #[#unsafe_attr(#link_section_attr = #windows_section_start)]
199            static LINKME_START: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];
200
201            #[cfg(any(target_os = "uefi", target_os = "windows"))]
202            #[#unsafe_attr(#link_section_attr = #windows_section_stop)]
203            static LINKME_STOP: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];
204
205            #[cfg(any(target_os = "uefi", target_os = "windows"))]
206            #[#unsafe_attr(#link_section_attr = #windows_dupcheck_start)]
207            static DUPCHECK_START: () = ();
208
209            #[cfg(any(target_os = "uefi", target_os = "windows"))]
210            #[#unsafe_attr(#link_section_attr = #windows_dupcheck_stop)]
211            static DUPCHECK_STOP: () = ();
212
213            #used
214            #[cfg(any(
215                target_os = "none",
216                target_os = "linux",
217                target_os = "android",
218                target_os = "fuchsia",
219                target_os = "illumos",
220                target_os = "freebsd",
221                target_os = "openbsd",
222                target_os = "psp",
223            ))]
224            #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), #unsafe_attr(#link_section_attr = #linux_section))]
225            #[cfg_attr(target_os = "illumos", #unsafe_attr(#link_section_attr = #illumos_section))]
226            #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), #unsafe_attr(#link_section_attr = #bsd_section))]
227            static mut LINKME_PLEASE: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];
228
229            #used
230            #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), #unsafe_attr(#link_section_attr = #linux_dupcheck))]
231            #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), #unsafe_attr(#link_section_attr = #macho_dupcheck))]
232            #[cfg_attr(any(target_os = "uefi", target_os = "windows"), #unsafe_attr(#link_section_attr = #windows_dupcheck))]
233            #[cfg_attr(target_os = "illumos", #unsafe_attr(#link_section_attr = #illumos_dupcheck))]
234            #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), #unsafe_attr(#link_section_attr = #bsd_dupcheck))]
235            static DUPCHECK: #linkme_path::__private::usize = 1;
236
237            #[cfg(not(any(
238                target_os = "none",
239                target_os = "linux",
240                target_os = "macos",
241                target_os = "ios",
242                target_os = "tvos",
243                target_os = "windows",
244                target_os = "uefi",
245                target_os = "android",
246                target_os = "fuchsia",
247                target_os = "illumos",
248                target_os = "freebsd",
249                target_os = "openbsd",
250                target_os = "psp",
251            )))]
252            #unsupported_platform
253
254            #linkme_path::__private::assert!(
255                #linkme_path::__private::mem::size_of::<<#ty as #linkme_path::__private::Slice>::Element>() > 0,
256            );
257
258            unsafe {
259                #linkme_path::DistributedSlice::private_new(
260                    #name,
261                    #linkme_path::__private::ptr::addr_of!(LINKME_START),
262                    #linkme_path::__private::ptr::addr_of!(LINKME_STOP),
263                    #linkme_path::__private::ptr::addr_of!(DUPCHECK_START),
264                    #linkme_path::__private::ptr::addr_of!(DUPCHECK_STOP),
265                )
266            }
267        }
268        };
269
270        #[doc(hidden)]
271        #[macro_export]
272        macro_rules! #link_section_macro {
273            (
274                #![linkme_macro = $macro:path]
275                #![linkme_sort_key = $key:tt]
276                $item:item
277            ) => {
278                $macro ! {
279                    #![linkme_linux_section = concat!(#linux_section, $key)]
280                    #![linkme_macho_section = concat!(#macho_section, $key)]
281                    #![linkme_windows_section = concat!(#windows_section, $key)]
282                    #![linkme_illumos_section = concat!(#illumos_section, $key)]
283                    #![linkme_bsd_section = concat!(#bsd_section, $key)]
284                    #![linkme_wasm_export = concat!(#xname,"/",$key)]
285                    $item
286                }
287            };
288            (
289                #![linkme_linux_section = $linux_section:expr]
290                #![linkme_macho_section = $macho_section:expr]
291                #![linkme_windows_section = $windows_section:expr]
292                #![linkme_illumos_section = $illumos_section:expr]
293                #![linkme_bsd_section = $bsd_section:expr]
294                #![linkme_wasm_export = $wasm_export:expr]
295                $item:item
296            ) => {
297                #used
298                #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), #unsafe_attr(#link_section_attr = $linux_section))]
299                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), #unsafe_attr(#link_section_attr = $macho_section))]
300                #[cfg_attr(any(target_os = "uefi", target_os = "windows"), #unsafe_attr(#link_section_attr = $windows_section))]
301                #[cfg_attr(target_os = "illumos", #unsafe_attr(#link_section_attr = $illumos_section))]
302                #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), #unsafe_attr(#link_section_attr = $bsd_section))]
303                #[cfg_attr(target_family = "wasm",#unsafe_attr(export_name = $wasm_export))]
304                $item
305            };
306            ($item:item) => {
307                #used
308                #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), #unsafe_attr(#link_section_attr = #linux_section))]
309                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), #unsafe_attr(#link_section_attr = #macho_section))]
310                #[cfg_attr(any(target_os = "uefi", target_os = "windows"), #unsafe_attr(#link_section_attr = #windows_section))]
311                #[cfg_attr(target_os = "illumos", #unsafe_attr(#link_section_attr = #illumos_section))]
312                #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), #unsafe_attr(#link_section_attr = #bsd_section))]
313                #[cfg_attr(target_family = "wasm",#unsafe_attr(export_name = #xname))]
314                $item
315            };
316        }
317
318        #[doc(hidden)]
319        #vis use #link_section_macro as #ident;
320    }
321}