bevy_reflect: Fix TypePath string concatenation (#18609)

# Objective

Fixes #18606

When a type implements `Add` for `String`, the compiler can get confused
when attempting to add a `&String` to a `String`.

Unfortunately, this seems to be [expected
behavior](https://github.com/rust-lang/rust/issues/77143#issuecomment-698369286)
which causes problems for generic types since the current `TypePath`
derive generates code that appends strings in this manner.

## Solution

Explicitly use the `Add<&str>` implementation in the `TypePath` derive
macro.

## Testing

You can test locally by running:

```
cargo check -p bevy_reflect --tests
```
This commit is contained in:
Gino Valente 2025-03-29 14:01:53 -07:00 committed by GitHub
parent 58b8f554a1
commit f4716034fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 36 additions and 1 deletions

View File

@ -80,7 +80,7 @@ impl StringExpr {
let owned = self.into_owned();
let borrowed = other.into_borrowed();
Self::Owned(quote! {
#owned + #borrowed
::core::ops::Add::<&str>::add(#owned, #borrowed)
})
}
}

View File

@ -988,6 +988,41 @@ mod tests {
assert_eq!(values, vec![1]);
}
/// This test ensures that we are able to reflect generic types with one or more type parameters.
///
/// When there is an `Add` implementation for `String`, the compiler isn't able to infer the correct
/// type to deref to.
/// If we don't append the strings in the `TypePath` derive correctly (i.e. explicitly specifying the type),
/// we'll get a compilation error saying that "`&String` cannot be added to `String`".
///
/// So this test just ensures that we do do that correctly.
///
/// This problem is a known issue and is unexpectedly expected behavior:
/// - <https://github.com/rust-lang/rust/issues/77143>
/// - <https://github.com/bodil/smartstring/issues/7>
/// - <https://github.com/pola-rs/polars/issues/14666>
#[test]
fn should_reflect_generic() {
struct FakeString {}
// This implementation confuses the compiler when trying to add a `&String` to a `String`
impl core::ops::Add<FakeString> for String {
type Output = Self;
fn add(self, _rhs: FakeString) -> Self::Output {
unreachable!()
}
}
#[derive(Reflect)]
struct Foo<A>(A);
#[derive(Reflect)]
struct Bar<A, B>(A, B);
#[derive(Reflect)]
struct Baz<A, B, C>(A, B, C);
}
#[test]
fn should_reflect_clone() {
// Struct