From 8853bef6df826021f461dfd8974957295ba27d66 Mon Sep 17 00:00:00 2001 From: dis-da-moe Date: Thu, 16 Feb 2023 17:09:44 +0000 Subject: [PATCH] implement `TypeUuid` for primitives and fix multiple-parameter generics having the same `TypeUuid` (#6633) # Objective - Fixes #5432 - Fixes #6680 ## Solution - move code responsible for generating the `impl TypeUuid` from `type_uuid_derive` into a new function, `gen_impl_type_uuid`. - this allows the new proc macro, `impl_type_uuid`, to call the code for generation. - added struct `TypeUuidDef` and implemented `syn::Parse` to allow parsing of the input for the new macro. - finally, used the new macro `impl_type_uuid` to implement `TypeUuid` for the standard library (in `crates/bevy_reflect/src/type_uuid_impl.rs`). - fixes #6680 by doing a wrapping add of the param's index to its `TYPE_UUID` Co-authored-by: dis-da-moe <84386186+dis-da-moe@users.noreply.github.com> --- crates/bevy_ecs/macros/src/lib.rs | 74 +---------------- crates/bevy_ecs/src/bundle.rs | 2 +- crates/bevy_ecs/src/lib.rs | 2 +- crates/bevy_ecs/src/query/fetch.rs | 2 +- crates/bevy_ecs/src/query/filter.rs | 2 +- crates/bevy_ecs/src/schedule/config.rs | 2 +- .../src/system/exclusive_function_system.rs | 3 +- .../src/system/exclusive_system_param.rs | 2 +- crates/bevy_ecs/src/system/function_system.rs | 3 +- crates/bevy_ecs/src/system/system_param.rs | 4 +- .../bevy_reflect_derive/src/lib.rs | 9 +++ .../bevy_reflect_derive/src/type_uuid.rs | 71 ++++++++++++---- crates/bevy_reflect/src/lib.rs | 1 + crates/bevy_reflect/src/type_uuid.rs | 32 ++++++++ crates/bevy_reflect/src/type_uuid_impl.rs | 80 +++++++++++++++++++ crates/bevy_render/src/render_phase/draw.rs | 3 +- crates/bevy_utils/Cargo.toml | 1 + crates/bevy_utils/macros/Cargo.toml | 14 ++++ crates/bevy_utils/macros/src/lib.rs | 71 ++++++++++++++++ crates/bevy_utils/src/lib.rs | 1 + 20 files changed, 281 insertions(+), 98 deletions(-) create mode 100644 crates/bevy_reflect/src/type_uuid_impl.rs create mode 100644 crates/bevy_utils/macros/Cargo.toml create mode 100644 crates/bevy_utils/macros/src/lib.rs diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 7196e8c9d2..b4dde955dd 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -11,79 +11,11 @@ use proc_macro::TokenStream; use proc_macro2::Span; use quote::{format_ident, quote}; use syn::{ - parse::{Parse, ParseStream}, - parse_macro_input, parse_quote, - punctuated::Punctuated, - spanned::Spanned, - token::Comma, - ConstParam, DeriveInput, Field, GenericParam, Ident, Index, LitInt, Meta, MetaList, NestedMeta, - Result, Token, TypeParam, + parse::ParseStream, parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, + ConstParam, DeriveInput, Field, GenericParam, Ident, Index, Meta, MetaList, NestedMeta, Token, + TypeParam, }; -struct AllTuples { - macro_ident: Ident, - start: usize, - end: usize, - idents: Vec, -} - -impl Parse for AllTuples { - fn parse(input: ParseStream) -> Result { - let macro_ident = input.parse::()?; - input.parse::()?; - let start = input.parse::()?.base10_parse()?; - input.parse::()?; - let end = input.parse::()?.base10_parse()?; - input.parse::()?; - let mut idents = vec![input.parse::()?]; - while input.parse::().is_ok() { - idents.push(input.parse::()?); - } - - Ok(AllTuples { - macro_ident, - start, - end, - idents, - }) - } -} - -#[proc_macro] -pub fn all_tuples(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as AllTuples); - let len = input.end - input.start; - let mut ident_tuples = Vec::with_capacity(len); - for i in input.start..=input.end { - let idents = input - .idents - .iter() - .map(|ident| format_ident!("{}{}", ident, i)); - if input.idents.len() < 2 { - ident_tuples.push(quote! { - #(#idents)* - }); - } else { - ident_tuples.push(quote! { - (#(#idents),*) - }); - } - } - - let macro_ident = &input.macro_ident; - let invocations = (input.start..=input.end).map(|i| { - let ident_tuples = &ident_tuples[..i]; - quote! { - #macro_ident!(#(#ident_tuples),*); - } - }); - TokenStream::from(quote! { - #( - #invocations - )* - }) -} - enum BundleFieldKind { Component, Ignore, diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index a2d091e549..162fe07c0b 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -15,8 +15,8 @@ use crate::{ storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow}, TypeIdMap, }; -use bevy_ecs_macros::all_tuples; use bevy_ptr::OwningPtr; +use bevy_utils::all_tuples; use std::any::TypeId; /// The `Bundle` trait enables insertion and removal of [`Component`]s from an entity. diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 38125d0b56..8ed41658ea 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -52,7 +52,7 @@ pub mod prelude { }; } -pub use bevy_ecs_macros::all_tuples; +pub use bevy_utils::all_tuples; /// A specialized hashmap type with Key of `TypeId` type TypeIdMap = rustc_hash::FxHashMap; diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 5733ffd262..ee67a401af 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -7,9 +7,9 @@ use crate::{ storage::{ComponentSparseSet, Table, TableRow}, world::{Mut, Ref, World}, }; -use bevy_ecs_macros::all_tuples; pub use bevy_ecs_macros::WorldQuery; use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; +use bevy_utils::all_tuples; use std::{cell::UnsafeCell, marker::PhantomData}; /// Types that can be fetched from a [`World`] using a [`Query`]. diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index a9e831c2c9..9732d5c3c8 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -6,8 +6,8 @@ use crate::{ storage::{Column, ComponentSparseSet, Table, TableRow}, world::World, }; -use bevy_ecs_macros::all_tuples; use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; +use bevy_utils::all_tuples; use std::{cell::UnsafeCell, marker::PhantomData}; use super::ReadOnlyWorldQuery; diff --git a/crates/bevy_ecs/src/schedule/config.rs b/crates/bevy_ecs/src/schedule/config.rs index e942d9af11..db18ea632e 100644 --- a/crates/bevy_ecs/src/schedule/config.rs +++ b/crates/bevy_ecs/src/schedule/config.rs @@ -1,4 +1,4 @@ -use bevy_ecs_macros::all_tuples; +use bevy_utils::all_tuples; use crate::{ schedule::{ diff --git a/crates/bevy_ecs/src/system/exclusive_function_system.rs b/crates/bevy_ecs/src/system/exclusive_function_system.rs index daa3ae65d7..4ba4a15418 100644 --- a/crates/bevy_ecs/src/system/exclusive_function_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_function_system.rs @@ -9,7 +9,8 @@ use crate::{ }, world::{World, WorldId}, }; -use bevy_ecs_macros::all_tuples; + +use bevy_utils::all_tuples; use std::{any::TypeId, borrow::Cow, marker::PhantomData}; /// A function system that runs with exclusive [`World`] access. diff --git a/crates/bevy_ecs/src/system/exclusive_system_param.rs b/crates/bevy_ecs/src/system/exclusive_system_param.rs index 4d77049e31..9f7ccb5ae9 100644 --- a/crates/bevy_ecs/src/system/exclusive_system_param.rs +++ b/crates/bevy_ecs/src/system/exclusive_system_param.rs @@ -4,7 +4,7 @@ use crate::{ system::{Local, SystemMeta, SystemParam, SystemState}, world::World, }; -use bevy_ecs_macros::all_tuples; +use bevy_utils::all_tuples; use bevy_utils::synccell::SyncCell; pub trait ExclusiveSystemParam: Sized { diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index e17ec7a82f..07e801898a 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -7,7 +7,8 @@ use crate::{ system::{check_system_change_tick, ReadOnlySystemParam, System, SystemParam, SystemParamItem}, world::{World, WorldId}, }; -use bevy_ecs_macros::all_tuples; + +use bevy_utils::all_tuples; use std::{any::TypeId, borrow::Cow, marker::PhantomData}; use super::ReadOnlySystem; diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index df524f742c..437a726f44 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -11,11 +11,11 @@ use crate::{ system::{Query, SystemMeta}, world::{FromWorld, World}, }; +use bevy_ecs_macros::impl_param_set; pub use bevy_ecs_macros::Resource; pub use bevy_ecs_macros::SystemParam; -use bevy_ecs_macros::{all_tuples, impl_param_set}; use bevy_ptr::UnsafeCellDeref; -use bevy_utils::synccell::SyncCell; +use bevy_utils::{all_tuples, synccell::SyncCell}; use std::{ borrow::Cow, fmt::Debug, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index eb486d86ba..4d3c27caec 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -30,11 +30,13 @@ mod type_uuid; mod utility; use crate::derive_data::{ReflectDerive, ReflectMeta, ReflectStruct}; +use crate::type_uuid::gen_impl_type_uuid; use proc_macro::TokenStream; use quote::quote; use reflect_value::ReflectValueDef; use syn::spanned::Spanned; use syn::{parse_macro_input, DeriveInput}; +use type_uuid::TypeUuidDef; pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; @@ -185,3 +187,10 @@ pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { def.traits.unwrap_or_default(), )) } + +/// Derives `TypeUuid` for the given type. This is used internally to implement `TypeUuid` on foreign types, such as those in the std. This macro should be used in the format of `<[Generic Params]> [Type (Path)], [Uuid (String Literal)]`. +#[proc_macro] +pub fn impl_type_uuid(input: TokenStream) -> TokenStream { + let def = parse_macro_input!(input as TypeUuidDef); + gen_impl_type_uuid(def) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs b/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs index d030731ab9..925acb3969 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs @@ -2,25 +2,17 @@ extern crate proc_macro; use bevy_macro_utils::BevyManifest; use quote::quote; +use syn::parse::{Parse, ParseStream}; use syn::*; use uuid::Uuid; +/// Parses input from a derive of `TypeUuid`. pub(crate) fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // Construct a representation of Rust code as a syntax tree // that we can manipulate - let mut ast: DeriveInput = syn::parse(input).unwrap(); - let bevy_reflect_path: Path = BevyManifest::default().get_path("bevy_reflect"); - + let ast: DeriveInput = syn::parse(input).unwrap(); // Build the trait implementation - let name = &ast.ident; - - ast.generics.type_params_mut().for_each(|param| { - param - .bounds - .push(syn::parse_quote!(#bevy_reflect_path::TypeUuid)); - }); - - let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + let type_ident = ast.ident; let mut uuid = None; for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { @@ -50,24 +42,73 @@ pub(crate) fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::To let uuid = uuid.expect("No `#[uuid = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"` attribute found."); + gen_impl_type_uuid(TypeUuidDef { + type_ident, + generics: ast.generics, + uuid, + }) +} + +/// Generates an implementation of `TypeUuid`. If there any generics, the `TYPE_UUID` will be a composite of the generic types' `TYPE_UUID`. +pub(crate) fn gen_impl_type_uuid(def: TypeUuidDef) -> proc_macro::TokenStream { + let uuid = def.uuid; + let mut generics = def.generics; + let ty = def.type_ident; + + let bevy_reflect_path: Path = BevyManifest::default().get_path("bevy_reflect"); + + generics.type_params_mut().for_each(|param| { + param + .bounds + .push(syn::parse_quote!(#bevy_reflect_path::TypeUuid)); + }); + let bytes = uuid .as_bytes() .iter() .map(|byte| format!("{byte:#X}")) .map(|byte_str| syn::parse_str::(&byte_str).unwrap()); + let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); + let base = quote! { #bevy_reflect_path::Uuid::from_bytes([#( #bytes ),*]) }; - let type_uuid = ast.generics.type_params().fold(base, |acc, param| { + let type_uuid = generics.type_params().enumerate().fold(base, |acc, (index, param)| { let ident = ¶m.ident; + let param_uuid = quote!( + #bevy_reflect_path::Uuid::from_u128(<#ident as #bevy_reflect_path::TypeUuid>::TYPE_UUID.as_u128().wrapping_add(#index as u128)) + ); quote! { - #bevy_reflect_path::__macro_exports::generate_composite_uuid(#acc, <#ident as #bevy_reflect_path::TypeUuid>::TYPE_UUID) + #bevy_reflect_path::__macro_exports::generate_composite_uuid(#acc, #param_uuid) } }); let gen = quote! { - impl #impl_generics #bevy_reflect_path::TypeUuid for #name #type_generics #where_clause { + impl #impl_generics #bevy_reflect_path::TypeUuid for #ty #type_generics #where_clause { const TYPE_UUID: #bevy_reflect_path::Uuid = #type_uuid; } }; gen.into() } + +/// A struct containing the data required to generate an implementation of `TypeUuid`. This can be generated by either [`impl_type_uuid!`][crate::impl_type_uuid!] or [`type_uuid_derive`]. +pub(crate) struct TypeUuidDef { + pub type_ident: Ident, + pub generics: Generics, + pub uuid: Uuid, +} + +impl Parse for TypeUuidDef { + fn parse(input: ParseStream) -> Result { + let type_ident = input.parse::()?; + let generics = input.parse::()?; + input.parse::()?; + let uuid = input.parse::()?.value(); + let uuid = Uuid::parse_str(&uuid).map_err(|err| input.error(format!("{}", err)))?; + + Ok(Self { + type_ident, + generics, + uuid, + }) + } +} diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 2a80f501dd..c56debd983 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -13,6 +13,7 @@ mod tuple_struct; mod type_info; mod type_registry; mod type_uuid; +mod type_uuid_impl; mod impls { #[cfg(feature = "glam")] mod glam; diff --git a/crates/bevy_reflect/src/type_uuid.rs b/crates/bevy_reflect/src/type_uuid.rs index 5cfd46ab6a..79ccbb0217 100644 --- a/crates/bevy_reflect/src/type_uuid.rs +++ b/crates/bevy_reflect/src/type_uuid.rs @@ -122,4 +122,36 @@ mod test { assert_eq!(uuid_a, uuid_b); } + + #[test] + fn test_multiple_generic_uuid() { + #[derive(TypeUuid)] + #[uuid = "35c8a7d3-d4b3-4bd7-b847-1118dc78092f"] + struct TestGeneric { + _value_a: A, + _value_b: B, + } + assert_ne!( + TestGeneric::::TYPE_UUID, + TestGeneric::::TYPE_UUID + ); + } + + #[test] + fn test_primitive_generic_uuid() { + test_impl_type_uuid(&true); + test_impl_type_uuid(&Some(true)); + test_impl_type_uuid(&TestDeriveStruct:: { _value: true }); + + assert_ne!(Option::::TYPE_UUID, Option::::TYPE_UUID); + + assert_ne!(<[bool; 0]>::TYPE_UUID, <[bool; 1]>::TYPE_UUID); + assert_ne!(<[bool; 0]>::TYPE_UUID, <[f32; 0]>::TYPE_UUID); + + assert_ne!( + <(bool, bool)>::TYPE_UUID, + <(bool, bool, bool, bool)>::TYPE_UUID + ); + assert_ne!(<(bool, f32)>::TYPE_UUID, <(f32, bool)>::TYPE_UUID); + } } diff --git a/crates/bevy_reflect/src/type_uuid_impl.rs b/crates/bevy_reflect/src/type_uuid_impl.rs new file mode 100644 index 0000000000..3860117a6c --- /dev/null +++ b/crates/bevy_reflect/src/type_uuid_impl.rs @@ -0,0 +1,80 @@ +use crate::TypeUuid; +use crate::{self as bevy_reflect, __macro_exports::generate_composite_uuid}; +use bevy_reflect_derive::impl_type_uuid; +use bevy_utils::{all_tuples, Duration, HashMap, HashSet, Instant, Uuid}; +#[cfg(feature = "smallvec")] +use smallvec::SmallVec; +#[cfg(any(unix, windows))] +use std::ffi::OsString; +use std::{ + num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, + }, + ops::{RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, + path::PathBuf, +}; +impl TypeUuid for [T; N] { + const TYPE_UUID: Uuid = generate_composite_uuid( + Uuid::from_u128(0x18d33c78e63c47b9bbf8f095008ab693), + generate_composite_uuid(Uuid::from_u128(N as u128), T::TYPE_UUID), + ); +} +impl_type_uuid!(bool, "eb1ad0ee2dff473285bc54ebbdef682c"); +impl_type_uuid!(char, "45a4710278ba48f8b31f0d72ff7f9d46"); +impl_type_uuid!(u8, "fdf1a88a3e0543ca9f51ad5978ca519f"); +impl_type_uuid!(u16, "ddeb93f791074860aaac1540de254edc"); +impl_type_uuid!(u32, "fc565ea2367f405591e1c55f91cb60bd"); +impl_type_uuid!(u64, "6c74b6a983eb44b096a9169baa6af0a1"); +impl_type_uuid!(u128, "f837371a4f534b7381ed776d5056d0c1"); +impl_type_uuid!(usize, "0129e1d8cff041f9b23aa99c6e1006b8"); +impl_type_uuid!(i8, "af7a5411661e43b0b1631ea43a825fd2"); +impl_type_uuid!(i16, "68592d5de5be4a608603c6988edfdf9c"); +impl_type_uuid!(i32, "439ff07f96c94aa5a86352ded71e4730"); +impl_type_uuid!(i64, "7f9534793ad24ab2b9f05d8254f4204a"); +impl_type_uuid!(i128, "6e5009be5845460daf814e052cc9fcf0"); +impl_type_uuid!(isize, "d3d52630da45497faf86859051c79e7d"); +impl_type_uuid!(f32, "006607124a8148e1910c86f0c18c9015"); +impl_type_uuid!(f64, "a5bc32f5632b478c92a0939b821fff80"); +impl_type_uuid!(Result, "d5960af2e8a743dfb7427dd59b70df95"); +impl_type_uuid!(String, "c9f90d31b52d4bcd8b5c1d8b6fc1bcba"); +impl_type_uuid!(PathBuf, "aa79933abd1743698583a3acad3b8989"); +impl_type_uuid!(Vec, "ab98f5408b974475b643662247fb3886"); +impl_type_uuid!(HashMap,"f37bfad9ca8c4f6ea7448f1c39e05f98"); +impl_type_uuid!(Option, "8d5ba9a9031347078955fba01ff439f0"); +#[cfg(feature = "smallvec")] +impl_type_uuid!( + SmallVec, + "26fd5c1bed7144fbb8d1546c02ba255a" +); +impl_type_uuid!(HashSet, "5ebd2379ece44ef2b1478262962617a3"); +impl_type_uuid!(RangeInclusive, "79613b729ca9490881c7f47b24b22b60"); +impl_type_uuid!(RangeFrom, "1bd8c975f122486c9ed443e277964642"); +impl_type_uuid!(RangeTo, "7d938903749a4d198f496cb354929b9b"); +impl_type_uuid!(RangeToInclusive, "2fec56936206462fa5f35c99a62c5ed1"); +impl_type_uuid!(RangeFull, "227af17f65db448782a2f6980ceae25d"); +impl_type_uuid!(Duration, "cee5978c60f74a53b6848cb9c46a6e1c"); +impl_type_uuid!(Instant, "9b0194a1d31c44c1afd2f6fd80ab8dfb"); +impl_type_uuid!(NonZeroI128, "915a1e7fcaeb433982cebf58c2ac20e7"); +impl_type_uuid!(NonZeroU128, "286de521146042cda31dfbef8f3f6cdc"); +impl_type_uuid!(NonZeroIsize, "9318740a9fd14603b709b8fbc6fd2812"); +impl_type_uuid!(NonZeroUsize, "a26533ed16324189878263d5e7a294ce"); +impl_type_uuid!(NonZeroI64, "1aa38623127a42419cca4992e6fc3152"); +impl_type_uuid!(NonZeroU64, "46be65e669a2477d942e2ec39d0d2af7"); +impl_type_uuid!(NonZeroU32, "cf53a46d9efe4022967160cb61762c91"); +impl_type_uuid!(NonZeroI32, "a69fbd659bef4322b88b15ff3263f530"); +impl_type_uuid!(NonZeroI16, "8744c2ec8a10491fae40f8bafa58b30d"); +impl_type_uuid!(NonZeroU16, "c7b8b60780a6495bab4fda2bdfedabcc"); +impl_type_uuid!(NonZeroU8, "635ee104ef7947fb9d7f79dad47255a3"); +impl_type_uuid!(NonZeroI8, "2d3f1570b7f64779826d44da5c7ba069"); +#[cfg(any(unix, windows))] +impl_type_uuid!(OsString, "809e7b3c1ea240979ecd832f91eb842a"); +macro_rules! impl_tuple { + ( $($name: ident),* ) => { + const _: () = { + type Tuple< $($name),* > = ( $($name,)* ); + impl_type_uuid!(Tuple< $($name),* > , "35c8a7d3d4b34bd7b8471118dc78092f"); + }; + }; +} +all_tuples!(impl_tuple, 0, 12, A); diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index f839febb5a..9b370f2e38 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -1,13 +1,12 @@ use crate::render_phase::{PhaseItem, TrackedRenderPass}; use bevy_app::App; use bevy_ecs::{ - all_tuples, entity::Entity, query::{QueryState, ROQueryItem, ReadOnlyWorldQuery}, system::{ReadOnlySystemParam, Resource, SystemParam, SystemParamItem, SystemState}, world::World, }; -use bevy_utils::HashMap; +use bevy_utils::{all_tuples, HashMap}; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use std::{any::TypeId, fmt::Debug, hash::Hash}; diff --git a/crates/bevy_utils/Cargo.toml b/crates/bevy_utils/Cargo.toml index 07a7a7ae9f..c795abcfbf 100644 --- a/crates/bevy_utils/Cargo.toml +++ b/crates/bevy_utils/Cargo.toml @@ -17,6 +17,7 @@ tracing = { version = "0.1", default-features = false, features = ["std"] } instant = { version = "0.1", features = ["wasm-bindgen"] } uuid = { version = "1.1", features = ["v4", "serde"] } hashbrown = { version = "0.12", features = ["serde"] } +bevy_utils_proc_macros = {version = "0.9.0", path = "macros"} petgraph = "0.6" thiserror = "1.0" diff --git a/crates/bevy_utils/macros/Cargo.toml b/crates/bevy_utils/macros/Cargo.toml new file mode 100644 index 0000000000..3134f00265 --- /dev/null +++ b/crates/bevy_utils/macros/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "bevy_utils_proc_macros" +version = "0.9.0" +description = "Bevy Utils Proc Macros" +edition = "2021" +license = "MIT OR Apache-2.0" + +[lib] +proc-macro = true + +[dependencies] +syn = "1.0" +quote = "1.0" +proc-macro2 = "1.0" diff --git a/crates/bevy_utils/macros/src/lib.rs b/crates/bevy_utils/macros/src/lib.rs new file mode 100644 index 0000000000..5362f5c320 --- /dev/null +++ b/crates/bevy_utils/macros/src/lib.rs @@ -0,0 +1,71 @@ +use proc_macro::TokenStream; +use quote::{format_ident, quote}; +use syn::{ + parse::{Parse, ParseStream}, + parse_macro_input, + token::Comma, + Ident, LitInt, Result, +}; +struct AllTuples { + macro_ident: Ident, + start: usize, + end: usize, + idents: Vec, +} + +impl Parse for AllTuples { + fn parse(input: ParseStream) -> Result { + let macro_ident = input.parse::()?; + input.parse::()?; + let start = input.parse::()?.base10_parse()?; + input.parse::()?; + let end = input.parse::()?.base10_parse()?; + input.parse::()?; + let mut idents = vec![input.parse::()?]; + while input.parse::().is_ok() { + idents.push(input.parse::()?); + } + + Ok(AllTuples { + macro_ident, + start, + end, + idents, + }) + } +} + +#[proc_macro] +pub fn all_tuples(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as AllTuples); + let len = input.end - input.start; + let mut ident_tuples = Vec::with_capacity(len); + for i in input.start..=input.end { + let idents = input + .idents + .iter() + .map(|ident| format_ident!("{}{}", ident, i)); + if input.idents.len() < 2 { + ident_tuples.push(quote! { + #(#idents)* + }); + } else { + ident_tuples.push(quote! { + (#(#idents),*) + }); + } + } + + let macro_ident = &input.macro_ident; + let invocations = (input.start..=input.end).map(|i| { + let ident_tuples = &ident_tuples[..i]; + quote! { + #macro_ident!(#(#ident_tuples),*); + } + }); + TokenStream::from(quote! { + #( + #invocations + )* + }) +} diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index e8ce5bdd84..b39ba0f540 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -21,6 +21,7 @@ mod default; mod float_ord; pub use ahash::AHasher; +pub use bevy_utils_proc_macros::*; pub use default::default; pub use float_ord::*; pub use hashbrown;