Path: blob/main/crates/bevy_render/macros/src/specializer.rs
9368 views
use bevy_macro_utils::{1fq_std::{FQDefault, FQResult},2get_struct_fields, require_named,3};4use proc_macro::TokenStream;5use proc_macro2::Span;6use quote::{format_ident, quote};7use syn::{8parse::{Parse, ParseStream},9parse_macro_input, parse_quote,10punctuated::Punctuated,11spanned::Spanned,12DeriveInput, Expr, Field, Ident, Index, Member, Meta, MetaList, Pat, Path, Token, Type,13WherePredicate,14};1516const SPECIALIZE_ATTR_IDENT: &str = "specialize";17const SPECIALIZE_ALL_IDENT: &str = "all";1819const KEY_ATTR_IDENT: &str = "key";20const KEY_DEFAULT_IDENT: &str = "default";2122enum SpecializeImplTargets {23All,24Specific(Vec<Path>),25}2627impl Parse for SpecializeImplTargets {28fn parse(input: ParseStream) -> syn::Result<Self> {29let paths = input.parse_terminated(Path::parse, Token![,])?;30if paths31.first()32.is_some_and(|p| p.is_ident(SPECIALIZE_ALL_IDENT))33{34Ok(SpecializeImplTargets::All)35} else {36Ok(SpecializeImplTargets::Specific(paths.into_iter().collect()))37}38}39}4041#[derive(Clone)]42enum Key {43Whole,44Default,45Index(Index),46Custom(Expr),47}4849impl Key {50fn expr(&self) -> Expr {51match self {52Key::Whole => parse_quote!(key),53Key::Default => parse_quote!(#FQDefault::default()),54Key::Index(index) => {55let member = Member::Unnamed(index.clone());56parse_quote!(key.#member)57}58Key::Custom(expr) => expr.clone(),59}60}61}6263const KEY_ERROR_MSG: &str = "Invalid key override. Must be either `default` or a valid Rust expression of the correct key type";6465impl Parse for Key {66fn parse(input: ParseStream) -> syn::Result<Self> {67if let Ok(ident) = input.parse::<Ident>() {68if ident == KEY_DEFAULT_IDENT {69Ok(Key::Default)70} else {71Err(syn::Error::new_spanned(ident, KEY_ERROR_MSG))72}73} else {74input.parse::<Expr>().map(Key::Custom).map_err(|mut err| {75err.extend(syn::Error::new(err.span(), KEY_ERROR_MSG));76err77})78}79}80}8182#[derive(Clone)]83struct FieldInfo {84ty: Type,85member: Member,86key: Key,87}8889impl FieldInfo {90fn key_ty(&self, specialize_path: &Path, target_path: &Path) -> Option<Type> {91let ty = &self.ty;92matches!(self.key, Key::Whole | Key::Index(_))93.then_some(parse_quote!(<#ty as #specialize_path::Specializer<#target_path>>::Key))94}9596fn key_ident(&self, ident: Ident) -> Option<Ident> {97matches!(self.key, Key::Whole | Key::Index(_)).then_some(ident)98}99100fn specialize_expr(&self, specialize_path: &Path, target_path: &Path) -> Expr {101let FieldInfo {102ty, member, key, ..103} = &self;104let key_expr = key.expr();105parse_quote!(<#ty as #specialize_path::Specializer<#target_path>>::specialize(&self.#member, #key_expr, descriptor))106}107108fn specialize_predicate(&self, specialize_path: &Path, target_path: &Path) -> WherePredicate {109let ty = &self.ty;110if matches!(&self.key, Key::Default) {111parse_quote!(#ty: #specialize_path::Specializer<#target_path, Key: #FQDefault>)112} else {113parse_quote!(#ty: #specialize_path::Specializer<#target_path>)114}115}116}117118fn get_field_info(119fields: &Punctuated<Field, Token![,]>,120targets: &SpecializeImplTargets,121) -> syn::Result<Vec<FieldInfo>> {122let mut field_info: Vec<FieldInfo> = Vec::new();123let mut used_count = 0;124let mut single_index = 0;125for (index, field) in fields.iter().enumerate() {126let field_ty = field.ty.clone();127let field_member = field.ident.clone().map_or(128Member::Unnamed(Index {129index: index as u32,130span: field.span(),131}),132Member::Named,133);134let key_index = Index {135index: used_count,136span: field.span(),137};138139let mut use_key_field = true;140let mut key = Key::Index(key_index);141for attr in &field.attrs {142match &attr.meta {143Meta::List(MetaList { path, tokens, .. }) if path.is_ident(&KEY_ATTR_IDENT) => {144let owned_tokens = tokens.clone().into();145let Ok(parsed_key) = syn::parse::<Key>(owned_tokens) else {146return Err(syn::Error::new(147attr.span(),148"Invalid key override attribute",149));150};151key = parsed_key;152if matches!(153(&key, &targets),154(Key::Custom(_), SpecializeImplTargets::All)155) {156return Err(syn::Error::new(157attr.span(),158"#[key(default)] is the only key override type allowed with #[specialize(all)]",159));160}161use_key_field = false;162}163_ => {}164}165}166167if use_key_field {168used_count += 1;169single_index = index;170}171172field_info.push(FieldInfo {173ty: field_ty,174member: field_member,175key,176});177}178179if used_count == 1 {180field_info[single_index].key = Key::Whole;181}182183Ok(field_info)184}185186fn get_specialize_targets(187ast: &DeriveInput,188derive_name: &str,189) -> syn::Result<SpecializeImplTargets> {190let specialize_attr = ast.attrs.iter().find_map(|attr| {191if attr.path().is_ident(SPECIALIZE_ATTR_IDENT)192&& let Meta::List(meta_list) = &attr.meta193{194return Some(meta_list);195}196None197});198let Some(specialize_meta_list) = specialize_attr else {199return Err(syn::Error::new(200Span::call_site(),201format!("#[derive({derive_name})] must be accompanied by #[specialize(..targets)].\n Example usages: #[specialize(RenderPipeline)], #[specialize(all)]")202));203};204syn::parse::<SpecializeImplTargets>(specialize_meta_list.tokens.clone().into())205}206207macro_rules! guard {208($expr: expr) => {209match $expr {210Ok(__val) => __val,211Err(err) => return err.to_compile_error().into(),212}213};214}215216pub fn impl_specializer(input: TokenStream) -> TokenStream {217let bevy_render_path: Path = crate::bevy_render_path();218let specialize_path = {219let mut path = bevy_render_path.clone();220path.segments.push(format_ident!("render_resource").into());221path222};223224let ecs_path = crate::bevy_ecs_path();225226let ast = parse_macro_input!(input as DeriveInput);227let targets = guard!(get_specialize_targets(&ast, "Specializer"));228let fields = guard!(get_struct_fields(&ast.data, "Specializer"));229let fields = guard!(require_named(fields));230let field_info = guard!(get_field_info(fields, &targets));231232let key_idents: Vec<Option<Ident>> = field_info233.iter()234.enumerate()235.map(|(i, field_info)| field_info.key_ident(format_ident!("key{i}")))236.collect();237let key_tuple_idents: Vec<Ident> = key_idents.iter().flatten().cloned().collect();238let ignore_pat: Pat = parse_quote!(_);239let key_patterns: Vec<Pat> = key_idents240.iter()241.map(|key_ident| match key_ident {242Some(key_ident) => parse_quote!(#key_ident),243None => ignore_pat.clone(),244})245.collect();246247match targets {248SpecializeImplTargets::All => impl_specialize_all(249&specialize_path,250&ecs_path,251&ast,252&field_info,253&key_patterns,254&key_tuple_idents,255),256SpecializeImplTargets::Specific(targets) => targets257.iter()258.map(|target| {259impl_specialize_specific(260&specialize_path,261&ecs_path,262&ast,263&field_info,264target,265&key_patterns,266&key_tuple_idents,267)268})269.collect(),270}271}272273fn impl_specialize_all(274specialize_path: &Path,275ecs_path: &Path,276ast: &DeriveInput,277field_info: &[FieldInfo],278key_patterns: &[Pat],279key_tuple_idents: &[Ident],280) -> TokenStream {281let target_path = Path::from(format_ident!("T"));282let key_elems: Vec<Type> = field_info283.iter()284.filter_map(|field_info| field_info.key_ty(specialize_path, &target_path))285.collect();286let specialize_exprs: Vec<Expr> = field_info287.iter()288.map(|field_info| field_info.specialize_expr(specialize_path, &target_path))289.collect();290291let struct_name = &ast.ident;292let mut generics = ast.generics.clone();293generics.params.insert(2940,295parse_quote!(#target_path: #specialize_path::Specializable),296);297298if !field_info.is_empty() {299let where_clause = generics.make_where_clause();300for field in field_info {301where_clause302.predicates303.push(field.specialize_predicate(specialize_path, &target_path));304}305}306307let (_, type_generics, _) = ast.generics.split_for_impl();308let (impl_generics, _, where_clause) = &generics.split_for_impl();309310TokenStream::from(quote! {311impl #impl_generics #specialize_path::Specializer<#target_path> for #struct_name #type_generics #where_clause {312type Key = (#(#key_elems),*);313314fn specialize(315&self,316key: Self::Key,317descriptor: &mut <#target_path as #specialize_path::Specializable>::Descriptor318) -> #FQResult<#specialize_path::Canonical<Self::Key>, #ecs_path::error::BevyError> {319#(let #key_patterns = #specialize_exprs?;)*320#FQResult::Ok((#(#key_tuple_idents),*))321}322}323})324}325326fn impl_specialize_specific(327specialize_path: &Path,328ecs_path: &Path,329ast: &DeriveInput,330field_info: &[FieldInfo],331target_path: &Path,332key_patterns: &[Pat],333key_tuple_idents: &[Ident],334) -> TokenStream {335let key_elems: Vec<Type> = field_info336.iter()337.filter_map(|field_info| field_info.key_ty(specialize_path, target_path))338.collect();339let specialize_exprs: Vec<Expr> = field_info340.iter()341.map(|field_info| field_info.specialize_expr(specialize_path, target_path))342.collect();343344let struct_name = &ast.ident;345let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();346347TokenStream::from(quote! {348impl #impl_generics #specialize_path::Specializer<#target_path> for #struct_name #type_generics #where_clause {349type Key = (#(#key_elems),*);350351fn specialize(352&self,353key: Self::Key,354descriptor: &mut <#target_path as #specialize_path::Specializable>::Descriptor355) -> #FQResult<#specialize_path::Canonical<Self::Key>, #ecs_path::error::BevyError> {356#(let #key_patterns = #specialize_exprs?;)*357#FQResult::Ok((#(#key_tuple_idents),*))358}359}360})361}362363pub fn impl_specializer_key(input: TokenStream) -> TokenStream {364let bevy_render_path: Path = crate::bevy_render_path();365let specialize_path = {366let mut path = bevy_render_path.clone();367path.segments.push(format_ident!("render_resource").into());368path369};370371let ast = parse_macro_input!(input as DeriveInput);372let ident = ast.ident;373TokenStream::from(quote!(374impl #specialize_path::SpecializerKey for #ident {375const IS_CANONICAL: bool = true;376type Canonical = Self;377}378))379}380381382