use proc_macro2::TokenStream;
use quote::{ToTokens, format_ident, quote};
use syn::{
Attribute, FnArg, Ident, Token, Type,
parse::{Parse, ParseStream},
parse_quote,
};
use crate::common::*;
use crate::token_discovery::*;
fn gen_token_assertion(
token_type_name: &Option<String>,
token_type: &Option<Type>,
suppress_const_test: bool,
) -> proc_macro2::TokenStream {
if suppress_const_test {
return quote! {};
}
if let (Some(name), Some(ty)) = (token_type_name, token_type)
&& let Some(expected_tag) = crate::generated::expected_tier_tag(name)
{
let assertion = format_ident!("__ARCHMAGE_ASSERT_TIER_{:08X}", expected_tag);
return quote! { let _: () = <#ty>::#assertion; };
}
quote! {}
}
#[derive(Default)]
pub(crate) struct ArcaneArgs {
suppress_const_test: bool,
inline_always: bool,
pub(crate) self_type: Option<Type>,
pub(crate) nested: bool,
pub(crate) import_intrinsics: bool,
pub(crate) import_magetypes: bool,
pub(crate) cfg_feature: Option<String>,
}
impl Parse for ArcaneArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut args = ArcaneArgs::default();
while !input.is_empty() {
let ident: Ident = input.parse()?;
match ident.to_string().as_str() {
"suppress_const_test" => args.suppress_const_test = true,
"inline_always" => args.inline_always = true,
"stub" => {
return Err(syn::Error::new(
ident.span(),
"`stub` has been removed. Use `incant!` for cross-arch dispatch \
instead — it cfg-gates each architecture automatically.\n\
\n\
Before: #[arcane(stub)] fn process(token: X64V3Token, ...) { ... }\n\
After: #[arcane] fn process_v3(token: X64V3Token, ...) { ... }\n\
\x20 fn dispatch(...) { incant!(process(...)) }",
));
}
"nested" => args.nested = true,
"import_intrinsics" => args.import_intrinsics = true,
"import_magetypes" => args.import_magetypes = true,
"cfg" => {
let content;
syn::parenthesized!(content in input);
let feat: Ident = content.parse()?;
args.cfg_feature = Some(feat.to_string());
}
"_self" => {
let _: Token![=] = input.parse()?;
args.self_type = Some(input.parse()?);
}
other => {
return Err(syn::Error::new(
ident.span(),
format!("unknown arcane argument: `{}`", other),
));
}
}
if input.peek(Token![,]) {
let _: Token![,] = input.parse()?;
}
}
if args.self_type.is_some() {
args.nested = true;
}
Ok(args)
}
}
pub(crate) enum SelfReceiver {
Owned,
Ref,
RefMut,
}
pub(crate) fn arcane_impl(
mut input_fn: LightFn,
macro_name: &str,
args: ArcaneArgs,
) -> TokenStream {
let has_self_receiver = input_fn
.sig
.inputs
.first()
.map(|arg| matches!(arg, FnArg::Receiver(_)))
.unwrap_or(false);
if has_self_receiver && args.nested && args.self_type.is_none() {
let msg = format!(
"{} with self receiver in nested mode requires `_self = Type` argument.\n\
Example: #[{}(nested, _self = MyType)]\n\
Use `_self` (not `self`) in the function body to refer to self.\n\
\n\
Alternatively, remove `nested` to use sibling expansion (default), \
which handles self/Self naturally.",
macro_name, macro_name
);
return syn::Error::new_spanned(&input_fn.sig, msg).to_compile_error();
}
let TokenParamInfo {
ident: mut _token_ident,
features,
target_arch,
token_type_name,
magetypes_namespace,
token_type,
tier_traits,
} = match find_token_param(&input_fn.sig).or_else(|| {
let self_ty = args.self_type.as_ref()?;
if !has_self_receiver {
return None;
}
let mut receiver_sig = input_fn.sig.clone();
receiver_sig.inputs.clear();
receiver_sig.inputs.push(syn::parse_quote!(_self: #self_ty));
find_token_param(&receiver_sig)
}) {
Some(result) => result,
None => {
if let Some(trait_name) = diagnose_featureless_token(&input_fn.sig) {
let msg = format!(
"`{trait_name}` cannot be used as a token bound in #[{macro_name}] \
because it doesn't specify any CPU features.\n\
\n\
#[{macro_name}] needs concrete features to generate #[target_feature]. \
Use a concrete token or a feature trait:\n\
\n\
Concrete tokens: X64V3Token, Desktop64, NeonToken, Arm64V2Token, ...\n\
Feature traits: impl HasX64V2, impl HasNeon, impl HasArm64V3, ..."
);
return syn::Error::new_spanned(&input_fn.sig, msg).to_compile_error();
}
let msg = format!(
"{} requires a token parameter. Supported forms:\n\
- Concrete: `token: X64V3Token`\n\
- impl Trait: `token: impl HasX64V2`\n\
- Generic: `fn foo<T: HasX64V2>(token: T, ...)`\n\
- With self: `#[{}(_self = Type)] fn method(&self, token: impl HasNeon, ...)`",
macro_name, macro_name
);
return syn::Error::new_spanned(&input_fn.sig, msg).to_compile_error();
}
};
#[cfg(not(feature = "avx512"))]
if args.import_intrinsics && features.iter().any(|f| f.starts_with("avx512")) {
let token_desc = token_type_name.as_deref().unwrap_or("an AVX-512 token");
let msg = format!(
"Using {token_desc} with `import_intrinsics` requires the `avx512` feature.\n\
\n\
Add to your Cargo.toml:\n\
\x20 archmage = {{ version = \"...\", features = [\"avx512\"] }}\n\
\n\
Without it, 512-bit safe memory ops (_mm512_loadu_ps etc.) are not available.\n\
If you only need value intrinsics (no memory ops), remove `import_intrinsics`."
);
return syn::Error::new_spanned(&input_fn.sig, msg).to_compile_error();
}
let body_imports = generate_imports(
target_arch,
magetypes_namespace,
args.import_intrinsics,
args.import_magetypes,
);
if !body_imports.is_empty() {
let original_body = &input_fn.body;
input_fn.body = quote! {
#body_imports
#original_body
};
}
if let Some(ref type_name) = token_type_name
&& let Some(tier_suffix) = crate::generated::canonical_token_to_tier_suffix(type_name)
&& let Some(tier) = crate::tiers::find_tier(tier_suffix)
{
let ctx = crate::rewrite::CallerContext {
tier_suffix: tier_suffix.to_string(),
target_arch: tier.target_arch,
token_ident: _token_ident.clone(),
has_token: true,
derive_token: false,
};
input_fn.body = crate::rewrite::rewrite_incant_in_body(input_fn.body, &ctx);
}
let features_csv = crate::token_discovery::features_csv(token_type_name.as_deref(), &features);
let target_feature_attrs: Vec<Attribute> =
vec![parse_quote!(#[target_feature(enable = #features_csv)])];
let mut pattern_rename_counter = 0u32;
let mut pattern_rebinds = Vec::new();
for arg in &mut input_fn.sig.inputs {
if let FnArg::Typed(pat_type) = arg {
let needs_rename = !matches!(pat_type.pat.as_ref(), syn::Pat::Ident(_));
if needs_rename {
let generated = format_ident!("__archmage_arg_{}", pattern_rename_counter);
pattern_rename_counter += 1;
let original_pat = pat_type.pat.clone();
let ty = &pat_type.ty;
pattern_rebinds.push(quote! { let #original_pat: #ty = #generated; });
*pat_type.pat = syn::Pat::Ident(syn::PatIdent {
attrs: vec![],
by_ref: None,
mutability: None,
ident: generated,
subpat: None,
});
}
}
}
if !pattern_rebinds.is_empty() {
let original_body = &input_fn.body;
input_fn.body = quote! {
#(#pattern_rebinds)*
#original_body
};
if let Some(info) = find_token_param(&input_fn.sig) {
_token_ident = info.ident;
}
}
let inline_attr: Attribute = if args.inline_always {
parse_quote!(#[inline(always)])
} else {
parse_quote!(#[inline])
};
if target_arch == Some("wasm32") {
return arcane_impl_wasm_safe(input_fn, &args, target_feature_attrs, inline_attr);
}
if args.nested {
arcane_impl_nested(
input_fn,
&args,
target_arch,
token_type_name,
token_type,
target_feature_attrs,
inline_attr,
_token_ident,
tier_traits,
)
} else {
arcane_impl_sibling(
input_fn,
&args,
target_arch,
token_type_name,
token_type,
target_feature_attrs,
inline_attr,
_token_ident,
tier_traits,
)
}
}
pub(crate) fn arcane_impl_wasm_safe(
input_fn: LightFn,
args: &ArcaneArgs,
target_feature_attrs: Vec<Attribute>,
inline_attr: Attribute,
) -> TokenStream {
let vis = &input_fn.vis;
let sig = &input_fn.sig;
let attrs = &input_fn.attrs;
let body = if args.self_type.is_some() {
let original_body = &input_fn.body;
quote! {
let _self = self;
#original_body
}
} else {
input_fn.body.clone()
};
let mut new_attrs = target_feature_attrs;
new_attrs.push(inline_attr);
for attr in filter_inline_attrs(attrs) {
new_attrs.push(attr.clone());
}
let expanded = quote! {
#[cfg(target_arch = "wasm32")]
#(#new_attrs)*
#vis #sig {
#body
}
};
expanded
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn arcane_impl_sibling(
input_fn: LightFn,
args: &ArcaneArgs,
target_arch: Option<&str>,
token_type_name: Option<String>,
token_type: Option<Type>,
target_feature_attrs: Vec<Attribute>,
inline_attr: Attribute,
_token_ident: Ident,
tier_traits: Vec<String>,
) -> TokenStream {
let vis = &input_fn.vis;
let sig = &input_fn.sig;
let fn_name = &sig.ident;
let generics = &sig.generics;
let where_clause = &generics.where_clause;
let inputs = &sig.inputs;
let output = &sig.output;
let body = &input_fn.body;
let attrs = filter_inline_attrs(&input_fn.attrs);
let lint_attrs = filter_lint_attrs(&input_fn.attrs);
let sibling_name = format_ident!("__arcane_{}", fn_name);
let has_self_receiver = inputs
.first()
.map(|arg| matches!(arg, FnArg::Receiver(_)))
.unwrap_or(false);
let sibling_sig_inputs = inputs;
let turbofish = build_turbofish(generics);
let sibling_call = if has_self_receiver {
let other_args: Vec<proc_macro2::TokenStream> = inputs
.iter()
.skip(1) .filter_map(|arg| {
if let FnArg::Typed(pat_type) = arg
&& let syn::Pat::Ident(pat_ident) = pat_type.pat.as_ref()
{
let ident = &pat_ident.ident;
Some(quote!(#ident))
} else {
None
}
})
.collect();
quote! { self.#sibling_name #turbofish(#(#other_args),*) }
} else {
let all_args: Vec<proc_macro2::TokenStream> = inputs
.iter()
.filter_map(|arg| {
if let FnArg::Typed(pat_type) = arg
&& let syn::Pat::Ident(pat_ident) = pat_type.pat.as_ref()
{
let ident = &pat_ident.ident;
Some(quote!(#ident))
} else {
None
}
})
.collect();
quote! { #sibling_name #turbofish(#(#all_args),*) }
};
let cfg_guard = gen_cfg_guard(target_arch, args.cfg_feature.as_deref());
if target_arch.is_some() {
let sibling_fn = quote! {
#cfg_guard
#[doc(hidden)]
#(#lint_attrs)*
#(#target_feature_attrs)*
#inline_attr
fn #sibling_name #generics (#sibling_sig_inputs) #output #where_clause {
#body
}
};
let token_assertion =
gen_token_assertion(&token_type_name, &token_type, args.suppress_const_test);
let tier_trait_assertion =
crate::common::gen_tier_trait_assertion(&tier_traits, &_token_ident);
let wrapper_fn = quote! {
#cfg_guard
#(#attrs)*
#[inline(always)]
#vis #sig {
#token_assertion
#tier_trait_assertion
unsafe { #sibling_call }
}
};
quote! {
#sibling_fn
#wrapper_fn
}
} else {
let sibling_fn = quote! {
#[doc(hidden)]
#(#lint_attrs)*
#(#target_feature_attrs)*
#inline_attr
fn #sibling_name #generics (#sibling_sig_inputs) #output #where_clause {
#body
}
};
let tier_trait_assertion =
crate::common::gen_tier_trait_assertion(&tier_traits, &_token_ident);
let wrapper_fn = quote! {
#(#attrs)*
#[inline(always)]
#vis #sig {
#tier_trait_assertion
unsafe { #sibling_call }
}
};
quote! {
#sibling_fn
#wrapper_fn
}
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn arcane_impl_nested(
input_fn: LightFn,
args: &ArcaneArgs,
target_arch: Option<&str>,
token_type_name: Option<String>,
token_type: Option<Type>,
target_feature_attrs: Vec<Attribute>,
inline_attr: Attribute,
_token_ident: Ident,
tier_traits: Vec<String>,
) -> TokenStream {
let vis = &input_fn.vis;
let sig = &input_fn.sig;
let fn_name = &sig.ident;
let generics = &sig.generics;
let where_clause = &generics.where_clause;
let inputs = &sig.inputs;
let output = &sig.output;
let body = &input_fn.body;
let attrs = filter_inline_attrs(&input_fn.attrs);
let lint_attrs = filter_lint_attrs(&input_fn.attrs);
let self_receiver_kind: Option<SelfReceiver> = inputs.first().and_then(|arg| match arg {
FnArg::Receiver(receiver) => {
match &receiver.kind {
syn::ReceiverKind::Reference(_, _, mutability) => {
if mutability.is_some() {
Some(SelfReceiver::RefMut)
} else {
Some(SelfReceiver::Ref)
}
}
_ => Some(SelfReceiver::Owned),
}
}
_ => None,
});
let inner_params: Vec<proc_macro2::TokenStream> = inputs
.iter()
.map(|arg| match arg {
FnArg::Receiver(_) => {
let self_ty = args.self_type.as_ref().unwrap();
match self_receiver_kind.as_ref().unwrap() {
SelfReceiver::Owned => quote!(_self: #self_ty),
SelfReceiver::Ref => quote!(_self: &#self_ty),
SelfReceiver::RefMut => quote!(_self: &mut #self_ty),
}
}
FnArg::Typed(pat_type) => {
if let Some(ref self_ty) = args.self_type {
replace_self_in_tokens(quote!(#pat_type), self_ty)
} else {
quote!(#pat_type)
}
}
})
.collect();
let inner_args: Vec<proc_macro2::TokenStream> = inputs
.iter()
.filter_map(|arg| match arg {
FnArg::Typed(pat_type) => {
if let syn::Pat::Ident(pat_ident) = pat_type.pat.as_ref() {
let ident = &pat_ident.ident;
Some(quote!(#ident))
} else {
None
}
}
FnArg::Receiver(_) => Some(quote!(self)), })
.collect();
let inner_fn_name = format_ident!("__simd_inner_{}", fn_name);
let turbofish = build_turbofish(generics);
let (inner_output, inner_body, inner_where_clause): (
proc_macro2::TokenStream,
proc_macro2::TokenStream,
proc_macro2::TokenStream,
) = if let Some(ref self_ty) = args.self_type {
let transformed_output = replace_self_in_tokens(output.to_token_stream(), self_ty);
let transformed_body = replace_self_in_tokens(body.clone(), self_ty);
let transformed_where = where_clause
.as_ref()
.map(|wc| replace_self_in_tokens(wc.to_token_stream(), self_ty))
.unwrap_or_default();
(transformed_output, transformed_body, transformed_where)
} else {
(
output.to_token_stream(),
body.clone(),
where_clause
.as_ref()
.map(|wc| wc.to_token_stream())
.unwrap_or_default(),
)
};
let cfg_guard = gen_cfg_guard(target_arch, args.cfg_feature.as_deref());
if target_arch.is_some() {
let token_assertion =
gen_token_assertion(&token_type_name, &token_type, args.suppress_const_test);
let tier_trait_assertion =
crate::common::gen_tier_trait_assertion(&tier_traits, &_token_ident);
quote! {
#cfg_guard
#(#attrs)*
#[inline(always)]
#vis #sig {
#(#target_feature_attrs)*
#inline_attr
#(#lint_attrs)*
fn #inner_fn_name #generics (#(#inner_params),*) #inner_output #inner_where_clause {
#inner_body
}
#token_assertion
#tier_trait_assertion
unsafe { #inner_fn_name #turbofish(#(#inner_args),*) }
}
}
} else {
let token_assertion =
gen_token_assertion(&token_type_name, &token_type, args.suppress_const_test);
let tier_trait_assertion =
crate::common::gen_tier_trait_assertion(&tier_traits, &_token_ident);
quote! {
#(#attrs)*
#[inline(always)]
#vis #sig {
#(#target_feature_attrs)*
#inline_attr
#(#lint_attrs)*
fn #inner_fn_name #generics (#(#inner_params),*) #inner_output #inner_where_clause {
#inner_body
}
#token_assertion
#tier_trait_assertion
unsafe { #inner_fn_name #turbofish(#(#inner_args),*) }
}
}
}
}
#[cfg(test)]
mod assertion_tests {
use super::*;
#[test]
fn concrete_checks_are_a_single_shared_lookup() {
let ty = Some(parse_quote!(renamed::X64V3Token));
let check = gen_token_assertion(&Some("X64V3Token".into()), &ty, false);
assert_eq!(
check.to_string(),
quote!(let _: () = <renamed::X64V3Token>::__ARCHMAGE_ASSERT_TIER_F38B284B;).to_string()
);
assert!(gen_token_assertion(&None, &None, false).is_empty());
assert!(gen_token_assertion(&Some("X64V3Token".into()), &ty, true).is_empty());
}
#[test]
fn no_shared_opt_in() {
assert!(syn::parse_str::<ArcaneArgs>("shared").is_err());
}
}