bevy/crates/bevy_derive/src/lib.rs
Edgar Geier a830530be4
Replace all labels with interned labels (#7762)
# Objective

First of all, this PR took heavy inspiration from #7760 and #5715. It
intends to also fix #5569, but with a slightly different approach.


This also fixes #9335 by reexporting `DynEq`.

## Solution

The advantage of this API is that we can intern a value without
allocating for zero-sized-types and for enum variants that have no
fields. This PR does this automatically in the `SystemSet` and
`ScheduleLabel` derive macros for unit structs and fieldless enum
variants. So this should cover many internal and external use cases of
`SystemSet` and `ScheduleLabel`. In these optimal use cases, no memory
will be allocated.

- The interning returns a `Interned<dyn SystemSet>`, which is just a
wrapper around a `&'static dyn SystemSet`.
- `Hash` and `Eq` are implemented in terms of the pointer value of the
reference, similar to my first approach of anonymous system sets in
#7676.
- Therefore, `Interned<T>` does not implement `Borrow<T>`, only `Deref`.
- The debug output of `Interned<T>` is the same as the interned value.

Edit: 
- `AppLabel` is now also interned and the old
`derive_label`/`define_label` macros were replaced with the new
interning implementation.
- Anonymous set ids are reused for different `Schedule`s, reducing the
amount of leaked memory.

### Pros
- `InternedSystemSet` and `InternedScheduleLabel` behave very similar to
the current `BoxedSystemSet` and `BoxedScheduleLabel`, but can be copied
without an allocation.
- Many use cases don't allocate at all.
- Very fast lookups and comparisons when using `InternedSystemSet` and
`InternedScheduleLabel`.
- The `intern` module might be usable in other areas.
- `Interned{ScheduleLabel, SystemSet, AppLabel}` does implement
`{ScheduleLabel, SystemSet, AppLabel}`, increasing ergonomics.

### Cons
- Implementors of `SystemSet` and `ScheduleLabel` still need to
implement `Hash` and `Eq` (and `Clone`) for it to work.

## Changelog

### Added

- Added `intern` module to `bevy_utils`.
- Added reexports of `DynEq` to `bevy_ecs` and `bevy_app`.

### Changed

- Replaced `BoxedSystemSet` and `BoxedScheduleLabel` with
`InternedSystemSet` and `InternedScheduleLabel`.
- Replaced `impl AsRef<dyn ScheduleLabel>` with `impl ScheduleLabel`.
- Replaced `AppLabelId` with `InternedAppLabel`.
- Changed `AppLabel` to use `Debug` for error messages.
- Changed `AppLabel` to use interning.
- Changed `define_label`/`derive_label` to use interning. 
- Replaced `define_boxed_label`/`derive_boxed_label` with
`define_label`/`derive_label`.
- Changed anonymous set ids to be only unique inside a schedule, not
globally.
- Made interned label types implement their label trait. 

### Removed

- Removed `define_boxed_label` and `derive_boxed_label`. 

## Migration guide

- Replace `BoxedScheduleLabel` and `Box<dyn ScheduleLabel>` with
`InternedScheduleLabel` or `Interned<dyn ScheduleLabel>`.
- Replace `BoxedSystemSet` and `Box<dyn SystemSet>` with
`InternedSystemSet` or `Interned<dyn SystemSet>`.
- Replace `AppLabelId` with `InternedAppLabel` or `Interned<dyn
AppLabel>`.
- Types manually implementing `ScheduleLabel`, `AppLabel` or `SystemSet`
need to implement:
  - `dyn_hash` directly instead of implementing `DynHash`
  - `as_dyn_eq`
- Pass labels to `World::try_schedule_scope`, `World::schedule_scope`,
`World::try_run_schedule`. `World::run_schedule`, `Schedules::remove`,
`Schedules::remove_entry`, `Schedules::contains`, `Schedules::get` and
`Schedules::get_mut` by value instead of by reference.

---------

Co-authored-by: Joseph <21144246+JoJoJet@users.noreply.github.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-10-25 21:39:23 +00:00

214 lines
5.4 KiB
Rust

#![allow(clippy::type_complexity)]
extern crate proc_macro;
mod app_plugin;
mod bevy_main;
mod derefs;
mod enum_variant_meta;
use bevy_macro_utils::{derive_label, BevyManifest};
use proc_macro::TokenStream;
use quote::format_ident;
/// Generates a dynamic plugin entry point function for the given `Plugin` type.
#[proc_macro_derive(DynamicPlugin)]
pub fn derive_dynamic_plugin(input: TokenStream) -> TokenStream {
app_plugin::derive_dynamic_plugin(input)
}
/// Implements [`Deref`] for structs. This is especially useful when utilizing the [newtype] pattern.
///
/// For single-field structs, the implementation automatically uses that field.
/// For multi-field structs, you must specify which field to use with the `#[deref]` attribute.
///
/// If you need [`DerefMut`] as well, consider using the other [derive] macro alongside
/// this one.
///
/// # Example
///
/// ## Tuple Structs
///
/// Using a single-field struct:
///
/// ```
/// use bevy_derive::Deref;
///
/// #[derive(Deref)]
/// struct MyNewtype(String);
///
/// let foo = MyNewtype(String::from("Hello"));
/// assert_eq!("Hello", *foo);
/// ```
///
/// Using a multi-field struct:
///
/// ```
/// # use std::marker::PhantomData;
/// use bevy_derive::Deref;
///
/// #[derive(Deref)]
/// struct MyStruct<T>(#[deref] String, PhantomData<T>);
///
/// let foo = MyStruct(String::from("Hello"), PhantomData::<usize>);
/// assert_eq!("Hello", *foo);
/// ```
///
/// ## Named Structs
///
/// Using a single-field struct:
///
/// ```
/// use bevy_derive::{Deref, DerefMut};
///
/// #[derive(Deref, DerefMut)]
/// struct MyStruct {
/// value: String,
/// }
///
/// let foo = MyStruct {
/// value: String::from("Hello")
/// };
/// assert_eq!("Hello", *foo);
/// ```
///
/// Using a multi-field struct:
///
/// ```
/// # use std::marker::PhantomData;
/// use bevy_derive::{Deref, DerefMut};
///
/// #[derive(Deref, DerefMut)]
/// struct MyStruct<T> {
/// #[deref]
/// value: String,
/// _phantom: PhantomData<T>,
/// }
///
/// let foo = MyStruct {
/// value:String::from("Hello"),
/// _phantom:PhantomData::<usize>
/// };
/// assert_eq!("Hello", *foo);
/// ```
///
/// [`Deref`]: std::ops::Deref
/// [newtype]: https://doc.rust-lang.org/rust-by-example/generics/new_types.html
/// [`DerefMut`]: std::ops::DerefMut
/// [derive]: crate::derive_deref_mut
#[proc_macro_derive(Deref, attributes(deref))]
pub fn derive_deref(input: TokenStream) -> TokenStream {
derefs::derive_deref(input)
}
/// Implements [`DerefMut`] for structs. This is especially useful when utilizing the [newtype] pattern.
///
/// For single-field structs, the implementation automatically uses that field.
/// For multi-field structs, you must specify which field to use with the `#[deref]` attribute.
///
/// [`DerefMut`] requires a [`Deref`] implementation. You can implement it manually or use
/// Bevy's [derive] macro for convenience.
///
/// # Example
///
/// ## Tuple Structs
///
/// Using a single-field struct:
///
/// ```
/// use bevy_derive::{Deref, DerefMut};
///
/// #[derive(Deref, DerefMut)]
/// struct MyNewtype(String);
///
/// let mut foo = MyNewtype(String::from("Hello"));
/// foo.push_str(" World!");
/// assert_eq!("Hello World!", *foo);
/// ```
///
/// Using a multi-field struct:
///
/// ```
/// # use std::marker::PhantomData;
/// use bevy_derive::{Deref, DerefMut};
///
/// #[derive(Deref, DerefMut)]
/// struct MyStruct<T>(#[deref] String, PhantomData<T>);
///
/// let mut foo = MyStruct(String::from("Hello"), PhantomData::<usize>);
/// foo.push_str(" World!");
/// assert_eq!("Hello World!", *foo);
/// ```
///
/// ## Named Structs
///
/// Using a single-field struct:
///
/// ```
/// use bevy_derive::{Deref, DerefMut};
///
/// #[derive(Deref, DerefMut)]
/// struct MyStruct {
/// value: String,
/// }
///
/// let mut foo = MyStruct {
/// value: String::from("Hello")
/// };
/// foo.push_str(" World!");
/// assert_eq!("Hello World!", *foo);
/// ```
///
/// Using a multi-field struct:
///
/// ```
/// # use std::marker::PhantomData;
/// use bevy_derive::{Deref, DerefMut};
///
/// #[derive(Deref, DerefMut)]
/// struct MyStruct<T> {
/// #[deref]
/// value: String,
/// _phantom: PhantomData<T>,
/// }
///
/// let mut foo = MyStruct {
/// value:String::from("Hello"),
/// _phantom:PhantomData::<usize>
/// };
/// foo.push_str(" World!");
/// assert_eq!("Hello World!", *foo);
/// ```
///
/// [`DerefMut`]: std::ops::DerefMut
/// [newtype]: https://doc.rust-lang.org/rust-by-example/generics/new_types.html
/// [`Deref`]: std::ops::Deref
/// [derive]: crate::derive_deref
#[proc_macro_derive(DerefMut, attributes(deref))]
pub fn derive_deref_mut(input: TokenStream) -> TokenStream {
derefs::derive_deref_mut(input)
}
#[proc_macro_attribute]
pub fn bevy_main(attr: TokenStream, item: TokenStream) -> TokenStream {
bevy_main::bevy_main(attr, item)
}
#[proc_macro_derive(EnumVariantMeta)]
pub fn derive_enum_variant_meta(input: TokenStream) -> TokenStream {
enum_variant_meta::derive_enum_variant_meta(input)
}
/// Generates an impl of the `AppLabel` trait.
///
/// This does not work for unions.
#[proc_macro_derive(AppLabel)]
pub fn derive_app_label(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let mut trait_path = BevyManifest::default().get_path("bevy_app");
let mut dyn_eq_path = trait_path.clone();
trait_path.segments.push(format_ident!("AppLabel").into());
dyn_eq_path.segments.push(format_ident!("DynEq").into());
derive_label(input, "AppLabel", &trait_path, &dyn_eq_path)
}