Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d2f1c95
macro
molikto Jan 9, 2026
a9ed553
wip
molikto Jan 9, 2026
8880f72
finish
molikto Jan 9, 2026
78358a1
fmt
molikto Jan 9, 2026
d754962
fmt
molikto Jan 9, 2026
84ffef4
default impls
molikto Jan 11, 2026
a55dad9
change doc
molikto Jan 11, 2026
cc43dc3
move test
molikto Jan 11, 2026
afe7fb7
more tests
molikto Jan 11, 2026
6724313
more tests
molikto Jan 11, 2026
f88aba3
fmt
molikto Jan 11, 2026
a7afe49
lint
molikto Jan 11, 2026
4cbe879
fix lint
molikto Jan 11, 2026
66e8acf
fmt
molikto Jan 11, 2026
f98023f
fix word
molikto Jan 11, 2026
d678370
fix lint
molikto Jan 11, 2026
e50b3b6
better named fields compare
molikto Jan 11, 2026
15630e5
reorder tests
molikto Jan 11, 2026
70897ff
cleanup
molikto Jan 11, 2026
90c1b25
more tests
molikto Jan 11, 2026
6a856d5
fmt
molikto Jan 11, 2026
7aacd5f
fix lint
molikto Jan 11, 2026
5c0b1e9
Update crates/bevy_reflect/src/impls/core/panic.rs
molikto Jan 14, 2026
ef38e57
Update crates/bevy_reflect/src/impls/alloc/borrow.rs
molikto Jan 14, 2026
6fcf1f3
Update crates/bevy_reflect/src/impls/std/path.rs
molikto Jan 14, 2026
208ac6d
Update crates/bevy_reflect/src/impls/std/path.rs
molikto Jan 14, 2026
33c84c1
Update crates/bevy_reflect/src/impls/core/primitives.rs
molikto Jan 14, 2026
464f975
Update crates/bevy_reflect/src/map.rs
molikto Jan 14, 2026
89530ff
Update crates/bevy_reflect/src/map.rs
molikto Jan 14, 2026
b2da5bf
Update crates/bevy_reflect/src/map.rs
molikto Jan 14, 2026
38c868f
Merge remote-tracking branch 'origin/main' into pr/reflect-partial-cmp
molikto Jan 14, 2026
b1f8274
Merge branch 'pr/reflect-partial-cmp' of github.com:molikto/bevy into…
molikto Jan 14, 2026
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
55 changes: 54 additions & 1 deletion crates/bevy_reflect/derive/src/container_attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod kw {
syn::custom_keyword!(type_path);
syn::custom_keyword!(Debug);
syn::custom_keyword!(PartialEq);
syn::custom_keyword!(PartialOrd);
syn::custom_keyword!(Hash);
syn::custom_keyword!(Clone);
syn::custom_keyword!(no_field_bounds);
Expand All @@ -33,6 +34,7 @@ mod kw {
// Received via attributes like `#[reflect(PartialEq, Hash, ...)]`
const DEBUG_ATTR: &str = "Debug";
const PARTIAL_EQ_ATTR: &str = "PartialEq";
const PARTIAL_ORD_ATTR: &str = "PartialOrd";
const HASH_ATTR: &str = "Hash";

// The traits listed below are not considered "special" (i.e. they use the `ReflectMyTrait` syntax)
Expand Down Expand Up @@ -180,6 +182,7 @@ pub(crate) struct ContainerAttributes {
clone: TraitImpl,
debug: TraitImpl,
hash: TraitImpl,
partial_ord: TraitImpl,
partial_eq: TraitImpl,
from_reflect_attrs: FromReflectAttrs,
type_path_attrs: TypePathAttrs,
Expand Down Expand Up @@ -248,6 +251,8 @@ impl ContainerAttributes {
self.parse_debug(input)
} else if lookahead.peek(kw::Hash) {
self.parse_hash(input)
} else if lookahead.peek(kw::PartialOrd) {
self.parse_partial_ord(input)
} else if lookahead.peek(kw::PartialEq) {
self.parse_partial_eq(input)
} else if lookahead.peek(Ident::peek_any) {
Expand All @@ -266,7 +271,7 @@ impl ContainerAttributes {

if input.peek(token::Paren) {
return Err(syn::Error::new(ident.span(), format!(
"only [{DEBUG_ATTR:?}, {PARTIAL_EQ_ATTR:?}, {HASH_ATTR:?}] may specify custom functions",
"only [{DEBUG_ATTR:?}, {PARTIAL_EQ_ATTR:?}, {PARTIAL_ORD_ATTR:?}, {HASH_ATTR:?}] may specify custom functions",
)));
}

Expand Down Expand Up @@ -342,6 +347,27 @@ impl ContainerAttributes {
Ok(())
}

/// Parse special `PartialOrd` registration.
///
/// Examples:
/// - `#[reflect(PartialOrd)]`
/// - `#[reflect(PartialOrd(custom_partial_cmp_fn))]`
fn parse_partial_ord(&mut self, input: ParseStream) -> syn::Result<()> {
let ident = input.parse::<kw::PartialOrd>()?;

if input.peek(token::Paren) {
let content;
parenthesized!(content in input);
let path = content.parse::<Path>()?;
self.partial_ord
.merge(TraitImpl::Custom(path, ident.span))?;
} else {
self.partial_ord = TraitImpl::Implemented(ident.span);
}

Ok(())
}

/// Parse special `Hash` registration.
///
/// Examples:
Expand Down Expand Up @@ -546,6 +572,33 @@ impl ContainerAttributes {
}
}

/// Returns the implementation of `PartialReflect::reflect_partial_cmp` as a `TokenStream`.
///
/// If `PartialOrd` was not registered, returns `None`.
pub fn get_partial_ord_impl(
&self,
bevy_reflect_path: &Path,
) -> Option<proc_macro2::TokenStream> {
match &self.partial_ord {
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
fn reflect_partial_cmp(&self, value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<::core::cmp::Ordering> {
let value = <dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<Self>(value);
if let #FQOption::Some(value) = value {
::core::cmp::PartialOrd::partial_cmp(self, value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not that well versed with reflection code, so this may be a stupid question, but:
Just took a closer look at this: this does not have to be wrapped in an FQOption right? I’m aware partial_cmp returns an Option already, but should that Option<Ordering> actually be wrapped in another outer layer FQOption? Same comment for the TraitImpl::Custom case for an Option around #impl_fn(self, value). I’m just asking this since the other functions i.e. partial_eq wrap the usual return type in Option

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you means should have type PartialReflect::reflect_partial_cmp(..) -> Option<Option<Ordering>>, instead of returning Option<Ordering>?

The outter Option seems means implemented or not. The inner Option means orderable or not. I feel in practice, merging them is ok and more convenient.

} else {
#FQOption::None
}
}
}),
&TraitImpl::Custom(ref impl_fn, span) => Some(quote_spanned! {span=>
fn reflect_partial_cmp(&self, value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<::core::cmp::Ordering> {
#impl_fn(self, value)
}
}),
TraitImpl::NotImplemented => None,
}
}

/// Returns the implementation of `PartialReflect::debug` as a `TokenStream`.
///
/// If `Debug` was not registered, returns `None`.
Expand Down
16 changes: 16 additions & 0 deletions crates/bevy_reflect/derive/src/impls/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub fn common_partial_reflect_methods(
meta: &ReflectMeta,
default_partial_eq_delegate: impl FnOnce() -> Option<proc_macro2::TokenStream>,
default_hash_delegate: impl FnOnce() -> Option<proc_macro2::TokenStream>,
default_partial_ord_delegate: impl FnOnce() -> Option<proc_macro2::TokenStream>,
) -> proc_macro2::TokenStream {
let bevy_reflect_path = meta.bevy_reflect_path();

Expand Down Expand Up @@ -113,6 +114,19 @@ pub fn common_partial_reflect_methods(
}
})
});
let partial_ord_fn = meta
.attrs()
.get_partial_ord_impl(bevy_reflect_path)
.or_else(move || {
let default_delegate = default_partial_ord_delegate();
default_delegate.map(|func| {
quote! {
fn reflect_partial_cmp(&self, value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<::core::cmp::Ordering> {
(#func)(self, value)
}
}
})
});

quote! {
#[inline]
Expand Down Expand Up @@ -151,6 +165,8 @@ pub fn common_partial_reflect_methods(

#partial_eq_fn

#partial_ord_fn

#debug_fn
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_reflect/derive/src/impls/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
reflect_enum.meta(),
|| Some(quote!(#bevy_reflect_path::enum_partial_eq)),
|| Some(quote!(#bevy_reflect_path::enum_hash)),
|| None,
);
let clone_fn = reflect_enum.get_clone_impl();

Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_reflect/derive/src/impls/opaque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub(crate) fn impl_opaque(meta: &ReflectMeta) -> proc_macro2::TokenStream {

let type_path_impl = impl_type_path(meta);
let full_reflect_impl = impl_full_reflect(&where_clause_options);
let common_methods = common_partial_reflect_methods(meta, || None, || None);
let common_methods = common_partial_reflect_methods(meta, || None, || None, || None);
let clone_fn = meta.attrs().get_clone_impl(bevy_reflect_path);

let apply_impl = if let Some(remote_ty) = meta.remote_ty() {
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_reflect/derive/src/impls/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS
reflect_struct.meta(),
|| Some(quote!(#bevy_reflect_path::struct_partial_eq)),
|| None,
|| None,
);
let clone_fn = reflect_struct.get_clone_impl();

Expand Down
1 change: 1 addition & 0 deletions crates/bevy_reflect/derive/src/impls/tuple_structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::
reflect_struct.meta(),
|| Some(quote!(#bevy_reflect_path::tuple_struct_partial_eq)),
|| None,
|| None,
);
let clone_fn = reflect_struct.get_clone_impl();

Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_reflect/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ fn match_reflect_impls(ast: DeriveInput, source: ReflectImplSource) -> TokenStre
/// A custom implementation may be provided using `#[reflect(PartialEq(my_partial_eq_func))]` where
/// `my_partial_eq_func` is the path to a function matching the signature:
/// `(&Self, value: &dyn #bevy_reflect_path::Reflect) -> bool`.
/// * `#[reflect(PartialOrd)]` will let the implementation of `PartialReflect::reflect_partial_cmp`
/// to rely on the type's [`PartialOrd`] implementation.
/// A custom implementation may be provided using `#[reflect(PartialOrd(my_partial_cmp_fn))]` where
/// `my_partial_cmp_fn` is the path to a function matching the signature:
/// `(&Self, value: &dyn #bevy_reflect_path::PartialReflect) -> Option<::core::cmp::Ordering>`.
/// * `#[reflect(Hash)]` will force the implementation of `Reflect::reflect_hash` to rely on
/// the type's [`Hash`] implementation.
/// A custom implementation may be provided using `#[reflect(Hash(my_hash_func))]` where
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_reflect/src/impls/alloc/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ impl_reflect_opaque!(::alloc::string::String(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize,
Default
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_reflect/src/impls/core/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ impl_reflect_opaque!(::core::net::SocketAddr(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize
));
12 changes: 12 additions & 0 deletions crates/bevy_reflect/src/impls/core/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ impl_reflect_opaque!(::core::num::NonZeroI128(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize
));
Expand All @@ -14,6 +15,7 @@ impl_reflect_opaque!(::core::num::NonZeroU128(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize
));
Expand All @@ -22,6 +24,7 @@ impl_reflect_opaque!(::core::num::NonZeroIsize(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize
));
Expand All @@ -30,6 +33,7 @@ impl_reflect_opaque!(::core::num::NonZeroUsize(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize
));
Expand All @@ -38,6 +42,7 @@ impl_reflect_opaque!(::core::num::NonZeroI64(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize
));
Expand All @@ -46,6 +51,7 @@ impl_reflect_opaque!(::core::num::NonZeroU64(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize
));
Expand All @@ -54,6 +60,7 @@ impl_reflect_opaque!(::core::num::NonZeroU32(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize
));
Expand All @@ -62,6 +69,7 @@ impl_reflect_opaque!(::core::num::NonZeroI32(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize
));
Expand All @@ -70,6 +78,7 @@ impl_reflect_opaque!(::core::num::NonZeroI16(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize
));
Expand All @@ -78,6 +87,7 @@ impl_reflect_opaque!(::core::num::NonZeroU16(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize
));
Expand All @@ -86,6 +96,7 @@ impl_reflect_opaque!(::core::num::NonZeroU8(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize
));
Expand All @@ -94,6 +105,7 @@ impl_reflect_opaque!(::core::num::NonZeroI8(
Debug,
Hash,
PartialEq,
PartialOrd,
Serialize,
Deserialize
));
Expand Down
Loading