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