From 227e1bbf34ec25bba330c27d7ab7ddec5b540764 Mon Sep 17 00:00:00 2001 From: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Date: Mon, 28 Apr 2025 12:26:53 -0700 Subject: [PATCH] bevy_reflect: Re-reflect `hashbrown` types (#18944) # Objective Fixes #18943 ## Solution Reintroduces support for `hashbrown`'s `HashMap` and `HashSet` types. These were inadvertently removed when `bevy_platform` newtyped the `hashbrown` types. Since we removed our `hashbrown` dependency, I gated these impls behind a `hashbrown` feature. Not entirely sure if this is necessary since we enabled it for `bevy_reflect` through `bevy_platform` anyways. (Complex features still confuse me a bit so let me know if I can just remove it!) I also went ahead and preemptively implemented `TypePath` for `PassHash` while I was here. ## Testing You can test that it works by adding the following to a Bevy example based on this PR (you'll also need to include `hashbrown` of course): ```rust #[derive(Reflect)] struct Foo(hashbrown::HashMap); ``` Then check it compiles with: ``` cargo check --example hello_world --no-default-features --features=bevy_reflect/hashbrown ``` --- crates/bevy_reflect/Cargo.toml | 4 +++ crates/bevy_reflect/src/impls/std.rs | 37 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index d2297ab716..f37865f9a3 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -33,6 +33,9 @@ debug_stack = ["std"] ## Adds reflection support to `glam` types. glam = ["dep:glam"] +## Adds reflection support to `hashbrown` types. +hashbrown = ["dep:hashbrown"] + ## Adds reflection support to `petgraph` types. petgraph = ["dep:petgraph", "std"] @@ -87,6 +90,7 @@ bevy_platform = { path = "../bevy_platform", version = "0.16.0", default-feature # used by bevy-utils, but it also needs reflect impls foldhash = { version = "0.1.3", default-features = false } +hashbrown = { version = "0.15.1", optional = true, default-features = false } # other erased-serde = { version = "0.4", default-features = false, features = [ diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 350527f910..6a752d1877 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1001,6 +1001,19 @@ crate::func::macros::impl_function_traits!(::bevy_platform::collections::HashMap > ); +#[cfg(feature = "hashbrown")] +impl_reflect_for_hashmap!(hashbrown::hash_map::HashMap); +#[cfg(feature = "hashbrown")] +impl_type_path!(::hashbrown::hash_map::HashMap); +#[cfg(all(feature = "functions", feature = "hashbrown"))] +crate::func::macros::impl_function_traits!(::hashbrown::hash_map::HashMap; + < + K: FromReflect + MaybeTyped + TypePath + GetTypeRegistration + Eq + Hash, + V: FromReflect + MaybeTyped + TypePath + GetTypeRegistration, + S: TypePath + BuildHasher + Default + Send + Sync + > +); + macro_rules! impl_reflect_for_hashset { ($ty:path) => { impl Set for $ty @@ -1208,6 +1221,7 @@ macro_rules! impl_reflect_for_hashset { impl_type_path!(::bevy_platform::hash::NoOpHash); impl_type_path!(::bevy_platform::hash::FixedHasher); +impl_type_path!(::bevy_platform::hash::PassHash); impl_reflect_opaque!(::core::net::SocketAddr( Clone, Debug, @@ -1239,6 +1253,18 @@ crate::func::macros::impl_function_traits!(::bevy_platform::collections::HashSet > ); +#[cfg(feature = "hashbrown")] +impl_reflect_for_hashset!(::hashbrown::hash_set::HashSet); +#[cfg(feature = "hashbrown")] +impl_type_path!(::hashbrown::hash_set::HashSet); +#[cfg(all(feature = "functions", feature = "hashbrown"))] +crate::func::macros::impl_function_traits!(::hashbrown::hash_set::HashSet; + < + V: Hash + Eq + FromReflect + TypePath + GetTypeRegistration, + S: TypePath + BuildHasher + Default + Send + Sync + > +); + impl Map for ::alloc::collections::BTreeMap where K: FromReflect + MaybeTyped + TypePath + GetTypeRegistration + Eq + Ord, @@ -2848,4 +2874,15 @@ mod tests { let output = <&'static str as FromReflect>::from_reflect(&expected).unwrap(); assert_eq!(expected, output); } + + #[test] + fn should_reflect_hashmaps() { + assert_impl_all!(std::collections::HashMap: Reflect); + assert_impl_all!(bevy_platform::collections::HashMap: Reflect); + + // We specify `foldhash::fast::RandomState` directly here since without the `default-hasher` + // feature, hashbrown uses an empty enum to force users to specify their own + #[cfg(feature = "hashbrown")] + assert_impl_all!(hashbrown::HashMap: Reflect); + } }