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 linkme_path = match attr::linkme_path(&mut attrs) {
65        Ok(path) => path,
66        Err(err) => return err.to_compile_error(),
67    };
68
69    ty::populate_static_lifetimes(&mut ty);
70
71    let used = if cfg!(feature = "used_linker") {
72        quote!(#[used(linker)])
73    } else {
74        quote!(#[used])
75    };
76
77    let linux_section = linker::linux::section(&ident);
78    let linux_section_start = linker::linux::section_start(&ident);
79    let linux_section_stop = linker::linux::section_stop(&ident);
80    let linux_dupcheck = linux_section.replacen("linkme", "linkm2", 1);
81    let linux_dupcheck_start = linux_section_start.replacen("linkme", "linkm2", 1);
82    let linux_dupcheck_stop = linux_section_stop.replacen("linkme", "linkm2", 1);
83
84    let macho_section = linker::macho::section(&ident);
85    let macho_section_start = linker::macho::section_start(&ident);
86    let macho_section_stop = linker::macho::section_stop(&ident);
87    let macho_dupcheck = macho_section.replacen("linkme", "linkm2", 1);
88    let macho_dupcheck_start = macho_section_start.replacen("linkme", "linkm2", 1);
89    let macho_dupcheck_stop = macho_section_stop.replacen("linkme", "linkm2", 1);
90
91    let windows_section = linker::windows::section(&ident);
92    let windows_section_start = linker::windows::section_start(&ident);
93    let windows_section_stop = linker::windows::section_stop(&ident);
94    let windows_dupcheck = windows_section.replacen("linkme", "linkm2", 1);
95    let windows_dupcheck_start = windows_section_start.replacen("linkme", "linkm2", 1);
96    let windows_dupcheck_stop = windows_section_stop.replacen("linkme", "linkm2", 1);
97
98    let illumos_section = linker::illumos::section(&ident);
99    let illumos_section_start = linker::illumos::section_start(&ident);
100    let illumos_section_stop = linker::illumos::section_stop(&ident);
101    let illumos_dupcheck = illumos_section.replacen("linkme", "linkm2", 1);
102    let illumos_dupcheck_start = illumos_section_start.replacen("linkme", "linkm2", 1);
103    let illumos_dupcheck_stop = illumos_section_stop.replacen("linkme", "linkm2", 1);
104
105    let bsd_section = linker::bsd::section(&ident);
106    let bsd_section_start = linker::bsd::section_start(&ident);
107    let bsd_section_stop = linker::bsd::section_stop(&ident);
108    let bsd_dupcheck = bsd_section.replacen("linkme", "linkm2", 1);
109    let bsd_dupcheck_start = bsd_section_start.replacen("linkme", "linkm2", 1);
110    let bsd_dupcheck_stop = bsd_section_stop.replacen("linkme", "linkm2", 1);
111
112    let call_site = Span::call_site();
113    let link_section_macro_str = format!("_linkme_macro_{}", ident);
114    let link_section_macro = Ident::new(&link_section_macro_str, call_site);
115
116    let unsafe_extern = if cfg!(no_unsafe_extern_blocks) {
117        None
118    } else {
119        Some(Token![unsafe](call_site))
120    };
121
122    let (unsafe_attr, link_section_attr) = if cfg!(no_unsafe_attributes) {
123        // #[cfg_attr(all(), link_section = ...)]
124        (
125            Ident::new("cfg_attr", call_site),
126            quote!(all(), link_section),
127        )
128    } else {
129        // #[unsafe(link_section = ...)]
130        (Ident::new("unsafe", call_site), quote!(link_section))
131    };
132
133    quote! {
134        #(#attrs)*
135        #vis static #ident: #linkme_path::DistributedSlice<#ty> = {
136            #[cfg(any(
137                target_os = "none",
138                target_os = "linux",
139                target_os = "macos",
140                target_os = "ios",
141                target_os = "tvos",
142                target_os = "android",
143                target_os = "fuchsia",
144                target_os = "illumos",
145                target_os = "freebsd",
146                target_os = "openbsd",
147                target_os = "psp",
148            ))]
149            #unsafe_extern extern "Rust" {
150                #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), link_name = #linux_section_start)]
151                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_section_start)]
152                #[cfg_attr(target_os = "illumos", link_name = #illumos_section_start)]
153                #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), link_name = #bsd_section_start)]
154                static LINKME_START: <#ty as #linkme_path::__private::Slice>::Element;
155
156                #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), link_name = #linux_section_stop)]
157                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_section_stop)]
158                #[cfg_attr(target_os = "illumos", link_name = #illumos_section_stop)]
159                #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), link_name = #bsd_section_stop)]
160                static LINKME_STOP: <#ty as #linkme_path::__private::Slice>::Element;
161
162                #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), link_name = #linux_dupcheck_start)]
163                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_dupcheck_start)]
164                #[cfg_attr(target_os = "illumos", link_name = #illumos_dupcheck_start)]
165                #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), link_name = #bsd_dupcheck_start)]
166                static DUPCHECK_START: #linkme_path::__private::usize;
167
168                #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), link_name = #linux_dupcheck_stop)]
169                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_dupcheck_stop)]
170                #[cfg_attr(target_os = "illumos", link_name = #illumos_dupcheck_stop)]
171                #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), link_name = #bsd_dupcheck_stop)]
172                static DUPCHECK_STOP: #linkme_path::__private::usize;
173            }
174
175            #[cfg(any(target_os = "uefi", target_os = "windows"))]
176            #[#unsafe_attr(#link_section_attr = #windows_section_start)]
177            static LINKME_START: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];
178
179            #[cfg(any(target_os = "uefi", target_os = "windows"))]
180            #[#unsafe_attr(#link_section_attr = #windows_section_stop)]
181            static LINKME_STOP: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];
182
183            #[cfg(any(target_os = "uefi", target_os = "windows"))]
184            #[#unsafe_attr(#link_section_attr = #windows_dupcheck_start)]
185            static DUPCHECK_START: () = ();
186
187            #[cfg(any(target_os = "uefi", target_os = "windows"))]
188            #[#unsafe_attr(#link_section_attr = #windows_dupcheck_stop)]
189            static DUPCHECK_STOP: () = ();
190
191            #used
192            #[cfg(any(
193                target_os = "none",
194                target_os = "linux",
195                target_os = "android",
196                target_os = "fuchsia",
197                target_os = "illumos",
198                target_os = "freebsd",
199                target_os = "openbsd",
200                target_os = "psp",
201            ))]
202            #[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))]
203            #[cfg_attr(target_os = "illumos", #unsafe_attr(#link_section_attr = #illumos_section))]
204            #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), #unsafe_attr(#link_section_attr = #bsd_section))]
205            static mut LINKME_PLEASE: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];
206
207            #used
208            #[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))]
209            #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), #unsafe_attr(#link_section_attr = #macho_dupcheck))]
210            #[cfg_attr(any(target_os = "uefi", target_os = "windows"), #unsafe_attr(#link_section_attr = #windows_dupcheck))]
211            #[cfg_attr(target_os = "illumos", #unsafe_attr(#link_section_attr = #illumos_dupcheck))]
212            #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), #unsafe_attr(#link_section_attr = #bsd_dupcheck))]
213            static DUPCHECK: #linkme_path::__private::usize = 1;
214
215            #[cfg(not(any(
216                target_os = "none",
217                target_os = "linux",
218                target_os = "macos",
219                target_os = "ios",
220                target_os = "tvos",
221                target_os = "windows",
222                target_os = "uefi",
223                target_os = "android",
224                target_os = "fuchsia",
225                target_os = "illumos",
226                target_os = "freebsd",
227                target_os = "openbsd",
228                target_os = "psp",
229            )))]
230            #unsupported_platform
231
232            #linkme_path::__private::assert!(
233                #linkme_path::__private::mem::size_of::<<#ty as #linkme_path::__private::Slice>::Element>() > 0,
234            );
235
236            unsafe {
237                #linkme_path::DistributedSlice::private_new(
238                    #name,
239                    #linkme_path::__private::ptr::addr_of!(LINKME_START),
240                    #linkme_path::__private::ptr::addr_of!(LINKME_STOP),
241                    #linkme_path::__private::ptr::addr_of!(DUPCHECK_START),
242                    #linkme_path::__private::ptr::addr_of!(DUPCHECK_STOP),
243                )
244            }
245        };
246
247        #[doc(hidden)]
248        #[macro_export]
249        macro_rules! #link_section_macro {
250            (
251                #![linkme_macro = $macro:path]
252                #![linkme_sort_key = $key:tt]
253                $item:item
254            ) => {
255                $macro ! {
256                    #![linkme_linux_section = concat!(#linux_section, $key)]
257                    #![linkme_macho_section = concat!(#macho_section, $key)]
258                    #![linkme_windows_section = concat!(#windows_section, $key)]
259                    #![linkme_illumos_section = concat!(#illumos_section, $key)]
260                    #![linkme_bsd_section = concat!(#bsd_section, $key)]
261                    $item
262                }
263            };
264            (
265                #![linkme_linux_section = $linux_section:expr]
266                #![linkme_macho_section = $macho_section:expr]
267                #![linkme_windows_section = $windows_section:expr]
268                #![linkme_illumos_section = $illumos_section:expr]
269                #![linkme_bsd_section = $bsd_section:expr]
270                $item:item
271            ) => {
272                #used
273                #[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))]
274                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), #unsafe_attr(#link_section_attr = $macho_section))]
275                #[cfg_attr(any(target_os = "uefi", target_os = "windows"), #unsafe_attr(#link_section_attr = $windows_section))]
276                #[cfg_attr(target_os = "illumos", #unsafe_attr(#link_section_attr = $illumos_section))]
277                #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), #unsafe_attr(#link_section_attr = $bsd_section))]
278                $item
279            };
280            ($item:item) => {
281                #used
282                #[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))]
283                #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), #unsafe_attr(#link_section_attr = #macho_section))]
284                #[cfg_attr(any(target_os = "uefi", target_os = "windows"), #unsafe_attr(#link_section_attr = #windows_section))]
285                #[cfg_attr(target_os = "illumos", #unsafe_attr(#link_section_attr = #illumos_section))]
286                #[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), #unsafe_attr(#link_section_attr = #bsd_section))]
287                $item
288            };
289        }
290
291        #[doc(hidden)]
292        #vis use #link_section_macro as #ident;
293    }
294}