Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - implement TypeUuid for primitives and fix multiple-parameter generics having the same TypeUuid #6633

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 3 additions & 71 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Ident>,
}

impl Parse for AllTuples {
fn parse(input: ParseStream) -> Result<Self> {
let macro_ident = input.parse::<Ident>()?;
input.parse::<Comma>()?;
let start = input.parse::<LitInt>()?.base10_parse()?;
input.parse::<Comma>()?;
let end = input.parse::<LitInt>()?.base10_parse()?;
input.parse::<Comma>()?;
let mut idents = vec![input.parse::<Ident>()?];
while input.parse::<Comma>().is_ok() {
idents.push(input.parse::<Ident>()?);
}

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,
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,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.
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<V> = rustc_hash::FxHashMap<TypeId, V>;
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/query/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`].
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/query/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/schedule/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use bevy_ecs_macros::all_tuples;
use bevy_utils::all_tuples;

use crate::{
schedule::{
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_ecs/src/system/exclusive_function_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/system/exclusive_system_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_ecs/src/system/function_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/system/system_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions crates/bevy_reflect/bevy_reflect_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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)
}
71 changes: 56 additions & 15 deletions crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down Expand Up @@ -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::<LitInt>(&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 = &param.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<Self> {
let type_ident = input.parse::<Ident>()?;
let generics = input.parse::<Generics>()?;
input.parse::<Token![,]>()?;
let uuid = input.parse::<LitStr>()?.value();
let uuid = Uuid::parse_str(&uuid).map_err(|err| input.error(format!("{}", err)))?;

Ok(Self {
type_ident,
generics,
uuid,
})
}
}
1 change: 1 addition & 0 deletions crates/bevy_reflect/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
32 changes: 32 additions & 0 deletions crates/bevy_reflect/src/type_uuid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<A, B> {
_value_a: A,
_value_b: B,
}
assert_ne!(
TestGeneric::<f32, bool>::TYPE_UUID,
TestGeneric::<bool, f32>::TYPE_UUID
);
}

#[test]
fn test_primitive_generic_uuid() {
test_impl_type_uuid(&true);
test_impl_type_uuid(&Some(true));
test_impl_type_uuid(&TestDeriveStruct::<bool> { _value: true });

assert_ne!(Option::<bool>::TYPE_UUID, Option::<f32>::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);
}
}
Loading