summaryrefslogtreecommitdiff
path: root/src/declaration.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/declaration.rs')
-rw-r--r--src/declaration.rs300
1 files changed, 300 insertions, 0 deletions
diff --git a/src/declaration.rs b/src/declaration.rs
new file mode 100644
index 0000000..7543bad
--- /dev/null
+++ b/src/declaration.rs
@@ -0,0 +1,300 @@
+use crate::{attr, linker};
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::parse::{Parse, ParseStream, Result};
+use syn::{
+ bracketed, Attribute, Error, GenericArgument, Ident, Lifetime, PathArguments, Token, Type,
+ Visibility,
+};
+
+struct Declaration {
+ attrs: Vec<Attribute>,
+ vis: Visibility,
+ ident: Ident,
+ ty: Type,
+}
+
+impl Parse for Declaration {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let attrs = input.call(Attribute::parse_outer)?;
+ let vis: Visibility = input.parse()?;
+ input.parse::<Token![static]>()?;
+ let mut_token: Option<Token![mut]> = input.parse()?;
+ if let Some(mut_token) = mut_token {
+ return Err(Error::new_spanned(
+ mut_token,
+ "static mut is not supported by distributed_slice",
+ ));
+ }
+ let ident: Ident = input.parse()?;
+ input.parse::<Token![:]>()?;
+ let ty: Type = input.parse()?;
+ input.parse::<Token![=]>()?;
+
+ let content;
+ bracketed!(content in input);
+ content.parse::<Token![..]>()?;
+
+ input.parse::<Token![;]>()?;
+
+ Ok(Declaration {
+ attrs,
+ vis,
+ ident,
+ ty,
+ })
+ }
+}
+
+pub fn expand(input: TokenStream) -> TokenStream {
+ let msg = "distributed_slice is not implemented for this platform";
+ let error = Error::new_spanned(&input, msg);
+ let unsupported_platform = error.to_compile_error();
+
+ let decl: Declaration = match syn::parse2(input) {
+ Ok(decl) => decl,
+ Err(err) => return err.to_compile_error(),
+ };
+
+ let mut attrs = decl.attrs;
+ let vis = decl.vis;
+ let ident = decl.ident;
+ let mut ty = decl.ty;
+ let name = ident.to_string();
+
+ let linkme_path = match attr::linkme_path(&mut attrs) {
+ Ok(path) => path,
+ Err(err) => return err.to_compile_error(),
+ };
+
+ populate_static_lifetimes(&mut ty);
+
+ let used = if cfg!(feature = "used_linker") {
+ quote!(#[used(linker)])
+ } else {
+ quote!(#[used])
+ };
+
+ let linux_section = linker::linux::section(&ident);
+ let linux_section_start = linker::linux::section_start(&ident);
+ let linux_section_stop = linker::linux::section_stop(&ident);
+ let linux_dupcheck = linux_section.replacen("linkme", "linkm2", 1);
+ let linux_dupcheck_start = linux_section_start.replacen("linkme", "linkm2", 1);
+ let linux_dupcheck_stop = linux_section_stop.replacen("linkme", "linkm2", 1);
+
+ let macho_section = linker::macho::section(&ident);
+ let macho_section_start = linker::macho::section_start(&ident);
+ let macho_section_stop = linker::macho::section_stop(&ident);
+ let macho_dupcheck = macho_section.replacen("linkme", "linkm2", 1);
+ let macho_dupcheck_start = macho_section_start.replacen("linkme", "linkm2", 1);
+ let macho_dupcheck_stop = macho_section_stop.replacen("linkme", "linkm2", 1);
+
+ let windows_section = linker::windows::section(&ident);
+ let windows_section_start = linker::windows::section_start(&ident);
+ let windows_section_stop = linker::windows::section_stop(&ident);
+ let windows_dupcheck = windows_section.replacen("linkme", "linkm2", 1);
+ let windows_dupcheck_start = windows_section_start.replacen("linkme", "linkm2", 1);
+ let windows_dupcheck_stop = windows_section_stop.replacen("linkme", "linkm2", 1);
+
+ let illumos_section = linker::illumos::section(&ident);
+ let illumos_section_start = linker::illumos::section_start(&ident);
+ let illumos_section_stop = linker::illumos::section_stop(&ident);
+ let illumos_dupcheck = illumos_section.replacen("linkme", "linkm2", 1);
+ let illumos_dupcheck_start = illumos_section_start.replacen("linkme", "linkm2", 1);
+ let illumos_dupcheck_stop = illumos_section_stop.replacen("linkme", "linkm2", 1);
+
+ let freebsd_section = linker::freebsd::section(&ident);
+ let freebsd_section_start = linker::freebsd::section_start(&ident);
+ let freebsd_section_stop = linker::freebsd::section_stop(&ident);
+ let freebsd_dupcheck = freebsd_section.replacen("linkme", "linkm2", 1);
+ let freebsd_dupcheck_start = freebsd_section_start.replacen("linkme", "linkm2", 1);
+ let freebsd_dupcheck_stop = freebsd_section_stop.replacen("linkme", "linkm2", 1);
+
+ let call_site = Span::call_site();
+ let link_section_macro_str = format!("_linkme_macro_{}", ident);
+ let link_section_macro = Ident::new(&link_section_macro_str, call_site);
+
+ quote! {
+ #(#attrs)*
+ #vis static #ident: #linkme_path::DistributedSlice<#ty> = {
+ #[cfg(any(
+ target_os = "none",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "tvos",
+ target_os = "illumos",
+ target_os = "freebsd",
+ ))]
+ extern "Rust" {
+ #[cfg_attr(any(target_os = "none", target_os = "linux"), link_name = #linux_section_start)]
+ #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_section_start)]
+ #[cfg_attr(target_os = "illumos", link_name = #illumos_section_start)]
+ #[cfg_attr(target_os = "freebsd", link_name = #freebsd_section_start)]
+ static LINKME_START: <#ty as #linkme_path::__private::Slice>::Element;
+
+ #[cfg_attr(any(target_os = "none", target_os = "linux"), link_name = #linux_section_stop)]
+ #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_section_stop)]
+ #[cfg_attr(target_os = "illumos", link_name = #illumos_section_stop)]
+ #[cfg_attr(target_os = "freebsd", link_name = #freebsd_section_stop)]
+ static LINKME_STOP: <#ty as #linkme_path::__private::Slice>::Element;
+
+ #[cfg_attr(any(target_os = "none", target_os = "linux"), link_name = #linux_dupcheck_start)]
+ #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_dupcheck_start)]
+ #[cfg_attr(target_os = "illumos", link_name = #illumos_dupcheck_start)]
+ #[cfg_attr(target_os = "freebsd", link_name = #freebsd_dupcheck_start)]
+ static DUPCHECK_START: #linkme_path::__private::usize;
+
+ #[cfg_attr(any(target_os = "none", target_os = "linux"), link_name = #linux_dupcheck_stop)]
+ #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_dupcheck_stop)]
+ #[cfg_attr(target_os = "illumos", link_name = #illumos_dupcheck_stop)]
+ #[cfg_attr(target_os = "freebsd", link_name = #freebsd_dupcheck_stop)]
+ static DUPCHECK_STOP: #linkme_path::__private::usize;
+ }
+
+ #[cfg(target_os = "windows")]
+ #[link_section = #windows_section_start]
+ static LINKME_START: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];
+
+ #[cfg(target_os = "windows")]
+ #[link_section = #windows_section_stop]
+ static LINKME_STOP: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];
+
+ #[cfg(target_os = "windows")]
+ #[link_section = #windows_dupcheck_start]
+ static DUPCHECK_START: () = ();
+
+ #[cfg(target_os = "windows")]
+ #[link_section = #windows_dupcheck_stop]
+ static DUPCHECK_STOP: () = ();
+
+ #used
+ #[cfg(any(target_os = "none", target_os = "linux", target_os = "illumos", target_os = "freebsd"))]
+ #[cfg_attr(any(target_os = "none", target_os = "linux"), link_section = #linux_section)]
+ #[cfg_attr(target_os = "illumos", link_section = #illumos_section)]
+ #[cfg_attr(target_os = "freebsd", link_section = #freebsd_section)]
+ static mut LINKME_PLEASE: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];
+
+ #used
+ #[cfg_attr(any(target_os = "none", target_os = "linux"), link_section = #linux_dupcheck)]
+ #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_section = #macho_dupcheck)]
+ #[cfg_attr(target_os = "windows", link_section = #windows_dupcheck)]
+ #[cfg_attr(target_os = "illumos", link_section = #illumos_dupcheck)]
+ #[cfg_attr(target_os = "freebsd", link_section = #freebsd_dupcheck)]
+ static DUPCHECK: #linkme_path::__private::usize = 1;
+
+ #[cfg(not(any(
+ target_os = "none",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "tvos",
+ target_os = "windows",
+ target_os = "illumos",
+ target_os = "freebsd",
+ )))]
+ #unsupported_platform
+
+ #linkme_path::__private::assert!(
+ #linkme_path::__private::mem::size_of::<<#ty as #linkme_path::__private::Slice>::Element>() > 0,
+ );
+
+ unsafe {
+ #linkme_path::DistributedSlice::private_new(
+ #name,
+ &LINKME_START,
+ &LINKME_STOP,
+ &DUPCHECK_START,
+ &DUPCHECK_STOP,
+ )
+ }
+ };
+
+ #[doc(hidden)]
+ #[macro_export]
+ macro_rules! #link_section_macro {
+ (
+ #![linkme_macro = $macro:path]
+ #![linkme_sort_key = $key:tt]
+ $item:item
+ ) => {
+ $macro ! {
+ #![linkme_linux_section = concat!(#linux_section, $key)]
+ #![linkme_macho_section = concat!(#macho_section, $key)]
+ #![linkme_windows_section = concat!(#windows_section, $key)]
+ #![linkme_illumos_section = concat!(#illumos_section, $key)]
+ #![linkme_freebsd_section = concat!(#freebsd_section, $key)]
+ $item
+ }
+ };
+ (
+ #![linkme_linux_section = $linux_section:expr]
+ #![linkme_macho_section = $macho_section:expr]
+ #![linkme_windows_section = $windows_section:expr]
+ #![linkme_illumos_section = $illumos_section:expr]
+ #![linkme_freebsd_section = $freebsd_section:expr]
+ $item:item
+ ) => {
+ #used
+ #[cfg_attr(any(target_os = "none", target_os = "linux"), link_section = $linux_section)]
+ #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_section = $macho_section)]
+ #[cfg_attr(target_os = "windows", link_section = $windows_section)]
+ #[cfg_attr(target_os = "illumos", link_section = $illumos_section)]
+ #[cfg_attr(target_os = "freebsd", link_section = $freebsd_section)]
+ $item
+ };
+ ($item:item) => {
+ #used
+ #[cfg_attr(any(target_os = "none", target_os = "linux"), link_section = #linux_section)]
+ #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_section = #macho_section)]
+ #[cfg_attr(target_os = "windows", link_section = #windows_section)]
+ #[cfg_attr(target_os = "illumos", link_section = #illumos_section)]
+ #[cfg_attr(target_os = "freebsd", link_section = #freebsd_section)]
+ $item
+ };
+ }
+
+ #[doc(hidden)]
+ #vis use #link_section_macro as #ident;
+ }
+}
+
+fn populate_static_lifetimes(ty: &mut Type) {
+ match ty {
+ Type::Array(ty) => populate_static_lifetimes(&mut ty.elem),
+ Type::Group(ty) => populate_static_lifetimes(&mut ty.elem),
+ Type::Paren(ty) => populate_static_lifetimes(&mut ty.elem),
+ Type::Path(ty) => {
+ if let Some(qself) = &mut ty.qself {
+ populate_static_lifetimes(&mut qself.ty);
+ }
+ for segment in &mut ty.path.segments {
+ if let PathArguments::AngleBracketed(segment) = &mut segment.arguments {
+ for arg in &mut segment.args {
+ if let GenericArgument::Type(arg) = arg {
+ populate_static_lifetimes(arg);
+ }
+ }
+ }
+ }
+ }
+ Type::Ptr(ty) => populate_static_lifetimes(&mut ty.elem),
+ Type::Reference(ty) => {
+ if ty.lifetime.is_none() {
+ ty.lifetime = Some(Lifetime::new("'static", ty.and_token.span));
+ }
+ populate_static_lifetimes(&mut ty.elem);
+ }
+ Type::Slice(ty) => populate_static_lifetimes(&mut ty.elem),
+ Type::Tuple(ty) => ty.elems.iter_mut().for_each(populate_static_lifetimes),
+ Type::ImplTrait(_)
+ | Type::Infer(_)
+ | Type::Macro(_)
+ | Type::Never(_)
+ | Type::TraitObject(_)
+ | Type::BareFn(_)
+ | Type::Verbatim(_) => {}
+ #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ _ => unimplemented!("unknown Type"),
+ }
+}