Skip to content

Commit cbe61bc

Browse files
committed
improve type safety for generic hashed labels
1 parent bf842a4 commit cbe61bc

File tree

3 files changed

+23
-10
lines changed

3 files changed

+23
-10
lines changed

crates/bevy_ecs/examples/derive_label.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ fn main() {
4545
// created from a value of type `ComplexLabel`.
4646
unreachable!();
4747
}
48+
49+
// Generic heap-allocated labels;
50+
let id = ComplexerLabel(1_i128).as_label();
51+
assert_eq!(format!("{id:?}"), "ComplexerLabel(1)");
52+
assert!(id.downcast::<ComplexerLabel<usize>>().is_none());
53+
if let Some(label) = id.downcast::<ComplexerLabel<i128>>() {
54+
assert_eq!(label.0, 1);
55+
}
4856
}
4957

5058
#[derive(SystemLabel)]

crates/bevy_ecs/src/schedule/label.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::fmt::Debug;
1+
use std::{fmt::Debug, hash::Hash};
22

33
use bevy_utils::{define_label, StableHashMap};
44
use downcast_rs::{impl_downcast, Downcast};
@@ -32,14 +32,14 @@ define_label!(
3232
);
3333

3434
/// Data structure used to intern a set of labels for a given type.
35-
pub struct Labels<L>(RwLock<StableHashMap<u64, L>>);
35+
pub struct Labels<K: Hash + Eq + Copy, L>(RwLock<StableHashMap<K, L>>);
3636

3737
/// The type returned from [`Labels::get`](Labels#method.get).
3838
///
3939
/// Will hold a lock on the string interner for type `L`, until this value gets dropped.
4040
pub type LabelGuard<'a, L> = parking_lot::MappedRwLockReadGuard<'a, L>;
4141

42-
impl<L> Labels<L> {
42+
impl<K: Hash + Eq + Copy, L> Labels<K, L> {
4343
#[inline]
4444
pub const fn new() -> Self {
4545
Self(RwLock::new(StableHashMap::with_hasher(
@@ -48,7 +48,7 @@ impl<L> Labels<L> {
4848
}
4949

5050
/// Interns a value, if it was not already interned in this set.
51-
pub fn intern(&self, key: u64, f: impl FnOnce() -> L) {
51+
pub fn intern(&self, key: K, f: impl FnOnce() -> L) {
5252
use parking_lot::RwLockUpgradableReadGuard as Guard;
5353

5454
// Acquire an upgradeable read lock, since we might not have to do any writing.
@@ -70,11 +70,11 @@ impl<L> Labels<L> {
7070
/// optionally returning a value.
7171
///
7272
/// Returns `None` if there is no interned label with that key.
73-
pub fn scope<U>(&self, key: u64, f: impl FnOnce(&L) -> U) -> Option<U> {
73+
pub fn scope<U>(&self, key: K, f: impl FnOnce(&L) -> U) -> Option<U> {
7474
self.0.read().get(&key).map(f)
7575
}
7676

77-
pub fn get(&self, key: u64) -> Option<LabelGuard<L>> {
77+
pub fn get(&self, key: K) -> Option<LabelGuard<L>> {
7878
RwLockReadGuard::try_map(self.0.read(), |map| map.get(&key)).ok()
7979
}
8080
}

crates/bevy_macro_utils/src/lib.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -345,25 +345,29 @@ fn derive_hashed_label(
345345
path
346346
};
347347

348+
let boxed_ty = quote! { ::std::boxed::Box::<dyn #downcast_debug_path + ::std::marker::Send + ::std::marker::Sync> };
349+
348350
quote! {
349351
// NOTE: We need to box the interned objects in order to deal with generics.
350352
// Unfortunately, we cannot associate data with generic impls, so they all must be
351353
// stored together.
352354
// FIXME: Make this concrete for types without generics.
353355
static #interner_ident
354-
: #interner_type_path <::std::boxed::Box::<dyn #downcast_debug_path + ::std::marker::Send + ::std::marker::Sync>>
356+
: #interner_type_path <(u64, ::std::any::TypeId), #boxed_ty>
355357
= #interner_type_path::new();
356358

357359
impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
358360
#[inline]
359361
fn data(&self) -> u64 {
360362
let hash = #compute_hash_path(self);
361-
#interner_ident .intern(hash, || ::std::boxed::Box::new(::std::clone::Clone::clone(self)));
363+
let key = (hash, ::std::any::TypeId::of::<Self>());
364+
#interner_ident .intern(key, || ::std::boxed::Box::new(::std::clone::Clone::clone(self)));
362365
hash
363366
}
364367
fn fmt(hash: u64, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
368+
let key = (hash, ::std::any::TypeId::of::<Self>());
365369
#interner_ident
366-
.scope(hash, |val| ::std::fmt::Debug::fmt(val, f))
370+
.scope(key, |val| ::std::fmt::Debug::fmt(val, f))
367371
.ok_or(::std::fmt::Error)?
368372
}
369373
}
@@ -372,8 +376,9 @@ fn derive_hashed_label(
372376
type Output = #guard_type_path <'static, Self>;
373377
fn downcast_from(label: #id_path) -> Option<Self::Output> {
374378
let hash = <#id_path as #trait_path>::data(&label);
379+
let key = (hash, ::std::any::TypeId::of::<Self>());
375380
<#guard_type_path<_>>::try_map(
376-
#interner_ident .get(hash)?,
381+
#interner_ident .get(key)?,
377382
|val| <dyn #downcast_debug_path>::downcast_ref::<Self>(&**val),
378383
).ok()
379384
}

0 commit comments

Comments
 (0)