bevy_reflect: Reflect remote types (#6042)

# Objective

The goal with this PR is to allow the use of types that don't implement
`Reflect` within the reflection API.

Rust's [orphan
rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type)
prevents implementing a trait on an external type when neither type nor
trait are owned by the implementor. This means that if a crate,
`cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with
reflection. What this means is that we have to ignore it most of the
time:

```rust
#[derive(Reflect)]
struct SomeStruct {
  #[reflect(ignore)]
  data: cool_rust_lib::Foo
}
```

Obviously, it's impossible to implement `Reflect` on `Foo`. But does it
*have* to be?

Most of reflection doesn't deal with concrete types— it's almost all
using `dyn Reflect`. And being very metadata-driven, it should
theoretically be possible. I mean,
[`serde`](https://serde.rs/remote-derive.html) does it.

## Solution

> Special thanks to @danielhenrymantilla for their help reviewing this
PR and offering wisdom wrt safety.

Taking a page out of `serde`'s book, this PR adds the ability to easily
use "remote types" with reflection. In this context, a "remote type" is
the external type for which we have no ability to implement `Reflect`.

This adds the `#[reflect_remote(...)]` attribute macro, which is used to
generate "remote type wrappers". All you have to do is define the
wrapper exactly the same as the remote type's definition:

```rust
// Pretend this is our external crate
mod cool_rust_lib {
  #[derive(Default)]
  struct Foo {
    pub value: String
  }
}

#[reflect_remote(cool_rust_lib::Foo)]
struct FooWrapper {
  pub value: String
}
```

> **Note:** All fields in the external type *must* be public. This could
be addressed with a separate getter/setter attribute either in this PR
or in another one.

The macro takes this user-defined item and transforms it into a newtype
wrapper around the external type, marking it as `#[repr(transparent)]`.
The fields/variants defined by the user are simply used to build out the
reflection impls.

Additionally, it generates an implementation of the new trait,
`ReflectRemote`, which helps prevent accidental misuses of this API.

Therefore, the output generated by the macro would look something like:

```rust
#[repr(transparent)]
struct FooWrapper(pub cool_rust_lib::Foo);

impl ReflectRemote for FooWrapper {
  type Remote = cool_rust_lib::Foo;

  // transmutation methods...
}

// reflection impls...
// these will acknowledge and make use of the `value` field
```

Internally, the reflection API will pass around the `FooWrapper` and
[transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it
where necessary. All we have to do is then tell `Reflect` to do that. So
rather than ignoring the field, we tell `Reflect` to use our wrapper
using the `#[reflect(remote = ...)]` field attribute:

```rust
#[derive(Reflect)]
struct SomeStruct {
  #[reflect(remote = FooWrapper)]
  data: cool_rust_lib::Foo
}
```

#### Other Macros & Type Data

Because this macro consumes the defined item and generates a new one, we
can't just put our macros anywhere. All macros that should be passed to
the generated struct need to come *below* this macro. For example, to
derive `Default` and register its associated type data:

```rust
//  GOOD
#[reflect_remote(cool_rust_lib::Foo)]
#[derive(Default)]
#[reflect(Default)]
struct FooWrapper {
  pub value: String
}

//  BAD
#[derive(Default)]
#[reflect_remote(cool_rust_lib::Foo)]
#[reflect(Default)]
struct FooWrapper {
  pub value: String
}
```

#### Generics

Generics are forwarded to the generated struct as well. They should also
be defined in the same order:

```rust
#[reflect_remote(RemoteGeneric<'a, T1, T2>)]
struct GenericWrapper<'a, T1, T2> {
  pub foo: &'a T1,
  pub bar: &'a T2,
}
```

> Naming does *not* need to match the original definition's. Only order
matters here.

> Also note that the code above is just a demonstration and doesn't
actually compile since we'd need to enforce certain bounds (e.g. `T1:
Reflect`, `'a: 'static`, etc.)

#### Nesting

And, yes, you can nest remote types:

```rust
#[reflect_remote(RemoteOuter)]
struct OuterWrapper {
  #[reflect(remote = InnerWrapper)]
  pub inner: RemoteInner
}

#[reflect_remote(RemoteInner)]
struct InnerWrapper(usize);
```

#### Assertions

This macro will also generate some compile-time assertions to ensure
that the correct types are used. It's important we catch this early so
users don't have to wait for something to panic. And it also helps keep
our `unsafe` a little safer.

For example, a wrapper definition that does not match its corresponding
remote type will result in an error:

```rust
mod external_crate {
  pub struct TheirStruct(pub u32);
}

#[reflect_remote(external_crate::TheirStruct)]
struct MyStruct(pub String); // ERROR: expected type `u32` but found `String`
```

<details>
<summary>Generated Assertion</summary>

```rust
const _: () = {
  #[allow(non_snake_case)]
  #[allow(unused_variables)]
  #[allow(unused_assignments)]
  #[allow(unreachable_patterns)]
  #[allow(clippy::multiple_bound_locations)]
  fn assert_wrapper_definition_matches_remote_type(
    mut __remote__: external_crate::TheirStruct,
  ) {
    __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap();
  }
};
```

</details>

Additionally, using the incorrect type in a `#[reflect(remote = ...)]`
attribute should result in an error:

```rust
mod external_crate {
  pub struct TheirFoo(pub u32);
  pub struct TheirBar(pub i32);
}

#[reflect_remote(external_crate::TheirFoo)]
struct MyFoo(pub u32);

#[reflect_remote(external_crate::TheirBar)]
struct MyBar(pub i32);

#[derive(Reflect)]
struct MyStruct {
  #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar`
  foo: external_crate::TheirFoo
}
```

<details>
<summary>Generated Assertion</summary>

```rust
const _: () = {
    struct RemoteFieldAssertions;
    impl RemoteFieldAssertions {
        #[allow(non_snake_case)]
        #[allow(clippy::multiple_bound_locations)]
        fn assert__foo__is_valid_remote() {
            let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> {
              None
            })().unwrap();
        }
    }
};
```

</details>

### Discussion

There are a couple points that I think still need discussion or
validation.

- [x] 1. `Any` shenanigans

~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd
have to first downcast to the wrapper then extract the inner type. This
PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that
addresses this by making all the `Reflect::*any` methods return the
inner type rather than the wrapper type. This allows us to downcast
directly to our remote type.~~

~~However, I'm not sure if this is something we want to do. For
unknowing users, it could be confusing and seemingly inconsistent. Is it
worth keeping? Or should this behavior be removed?~~

I think this should be fine. The remote wrapper is an implementation
detail and users should not need to downcast to the wrapper type. Feel
free to let me know if there are other opinions on this though!

- [x] 2. Implementing `Deref/DerefMut` and `From`

~~We don't currently do this, but should we implement other traits on
the generated transparent struct? We could implement `Deref`/`DerefMut`
to easily access the inner type. And we could implement `From` for
easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As
mentioned in the comments, we probably don't need to do this. Again, the
remote wrapper is an implementation detail, and should generally not be
used directly.
     
- [x] 3. ~~Should we define a getter/setter field attribute in this PR
as well or leave it for a future one?~~ I think this should be saved for
a future PR

- [ ] 4. Any foreseeable issues with this implementation?

#### Alternatives

One alternative to defining our own `ReflectRemote` would be to use
[bytemuck's
`TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html)
(as suggested by @danielhenrymantilla).

This is definitely a viable option, as `ReflectRemote` is pretty much
the same thing as `TransparentWrapper`. However, the cost would be
bringing in a new crate— though, it is already in use in a few other
sub-crates like bevy_render.

I think we're okay just defining `ReflectRemote` ourselves, but we can
go the bytemuck route if we'd prefer offloading that work to another
crate.

---

## Changelog

* Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect`
to be used on remote types
* Added `ReflectRemote` trait for ensuring proper remote wrapper usage
This commit is contained in:
Gino Valente 2024-08-12 12:12:53 -07:00 committed by GitHub
parent aab1f8e435
commit 6183b56b5d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 2169 additions and 120 deletions

View file

@ -20,3 +20,7 @@ harness = false
[[test]]
name = "func"
harness = false
[[test]]
name = "remote"
harness = false

View file

@ -0,0 +1,24 @@
use bevy_reflect::Reflect;
mod external_crate {
pub struct TheirFoo {
pub value: u32,
}
}
#[repr(transparent)]
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct MyFoo(#[reflect(ignore)] pub external_crate::TheirFoo);
#[derive(Reflect)]
//~^ ERROR: the trait bound `MyFoo: ReflectRemote` is not satisfied
#[reflect(from_reflect = false)]
struct MyStruct {
// Reason: `MyFoo` does not implement `ReflectRemote` (from `#[reflect_remote]` attribute)
#[reflect(remote = MyFoo)]
//~^ ERROR: the trait bound `MyFoo: ReflectRemote` is not satisfied
foo: external_crate::TheirFoo,
}
fn main() {}

View file

@ -0,0 +1,21 @@
//@check-pass
use bevy_reflect::{reflect_remote, Reflect};
mod external_crate {
pub struct TheirFoo {
pub value: u32,
}
}
#[reflect_remote(external_crate::TheirFoo)]
struct MyFoo {
pub value: u32,
}
#[derive(Reflect)]
struct MyStruct {
#[reflect(remote = MyFoo)]
foo: external_crate::TheirFoo,
}
fn main() {}

View file

@ -0,0 +1,63 @@
mod structs {
use bevy_reflect::reflect_remote;
mod external_crate {
pub struct TheirStruct {
pub value: u32,
}
}
#[reflect_remote(external_crate::TheirStruct)]
//~^ ERROR: `?` operator has incompatible types
struct MyStruct {
// Reason: Should be `u32`
pub value: bool,
//~^ ERROR: mismatched types
}
}
mod tuple_structs {
use bevy_reflect::reflect_remote;
mod external_crate {
pub struct TheirStruct(pub u32);
}
#[reflect_remote(external_crate::TheirStruct)]
//~^ ERROR: `?` operator has incompatible types
struct MyStruct(
// Reason: Should be `u32`
pub bool,
//~^ ERROR: mismatched types
);
}
mod enums {
use bevy_reflect::reflect_remote;
mod external_crate {
pub enum TheirStruct {
Unit,
Tuple(u32),
Struct { value: usize },
}
}
#[reflect_remote(external_crate::TheirStruct)]
//~^ ERROR: variant `enums::external_crate::TheirStruct::Unit` does not have a field named `0`
//~| ERROR: variant `enums::external_crate::TheirStruct::Unit` has no field named `0`
//~| ERROR: `?` operator has incompatible types
//~| ERROR: `?` operator has incompatible types
enum MyStruct {
// Reason: Should be unit variant
Unit(i32),
// Reason: Should be `u32`
Tuple(bool),
//~^ ERROR: mismatched types
// Reason: Should be `usize`
Struct { value: String },
//~^ ERROR: mismatched types
}
}
fn main() {}

View file

@ -0,0 +1,48 @@
//@check-pass
mod structs {
use bevy_reflect::reflect_remote;
mod external_crate {
pub struct TheirStruct {
pub value: u32,
}
}
#[reflect_remote(external_crate::TheirStruct)]
struct MyStruct {
pub value: u32,
}
}
mod tuple_structs {
use bevy_reflect::reflect_remote;
mod external_crate {
pub struct TheirStruct(pub u32);
}
#[reflect_remote(external_crate::TheirStruct)]
struct MyStruct(pub u32);
}
mod enums {
use bevy_reflect::reflect_remote;
mod external_crate {
pub enum TheirStruct {
Unit,
Tuple(u32),
Struct { value: usize },
}
}
#[reflect_remote(external_crate::TheirStruct)]
enum MyStruct {
Unit,
Tuple(u32),
Struct { value: usize },
}
}
fn main() {}

View file

@ -0,0 +1,19 @@
use bevy_reflect::{reflect_remote, std_traits::ReflectDefault};
mod external_crate {
#[derive(Debug, Default)]
pub struct TheirType {
pub value: String,
}
}
#[derive(Debug, Default)]
#[reflect_remote(external_crate::TheirType)]
#[reflect(Debug, Default)]
struct MyType {
pub value: String,
//~^ ERROR: no field `value` on type `&MyType`
//~| ERROR: struct `MyType` has no field named `value`
}
fn main() {}

View file

@ -0,0 +1,18 @@
//@check-pass
use bevy_reflect::{reflect_remote, std_traits::ReflectDefault};
mod external_crate {
#[derive(Debug, Default)]
pub struct TheirType {
pub value: String,
}
}
#[reflect_remote(external_crate::TheirType)]
#[derive(Debug, Default)]
#[reflect(Debug, Default)]
struct MyType {
pub value: String,
}
fn main() {}

View file

@ -0,0 +1,75 @@
mod external_crate {
pub struct TheirOuter<T> {
pub inner: TheirInner<T>,
}
pub struct TheirInner<T>(pub T);
}
mod missing_attribute {
use bevy_reflect::{FromReflect, GetTypeRegistration, reflect_remote};
#[reflect_remote(super::external_crate::TheirOuter<T>)]
struct MyOuter<T: FromReflect + GetTypeRegistration> {
// Reason: Missing `#[reflect(remote = ...)]` attribute
pub inner: super::external_crate::TheirInner<T>,
}
#[reflect_remote(super::external_crate::TheirInner<T>)]
struct MyInner<T>(pub T);
}
mod incorrect_inner_type {
use bevy_reflect::{FromReflect, GetTypeRegistration, reflect_remote};
#[reflect_remote(super::external_crate::TheirOuter<T>)]
//~^ ERROR: `TheirInner<T>` does not implement `PartialReflect` so cannot be introspected
//~| ERROR: `TheirInner<T>` does not implement `PartialReflect` so cannot be introspected
//~| ERROR: `TheirInner<T>` does not implement `PartialReflect` so cannot be introspected
//~| ERROR: `TheirInner<T>` can not be used as a dynamic type path
//~| ERROR: `?` operator has incompatible types
struct MyOuter<T: FromReflect + GetTypeRegistration> {
// Reason: Should not use `MyInner<T>` directly
pub inner: MyInner<T>,
//~^ ERROR: mismatched types
}
#[reflect_remote(super::external_crate::TheirInner<T>)]
struct MyInner<T>(pub T);
}
mod mismatched_remote_type {
use bevy_reflect::{FromReflect, GetTypeRegistration, reflect_remote};
#[reflect_remote(super::external_crate::TheirOuter<T>)]
//~^ ERROR: mismatched types
//~| ERROR: mismatched types
struct MyOuter<T: FromReflect + GetTypeRegistration> {
// Reason: Should be `MyInner<T>`
#[reflect(remote = MyOuter<T>)]
//~^ ERROR: mismatched types
pub inner: super::external_crate::TheirInner<T>,
}
#[reflect_remote(super::external_crate::TheirInner<T>)]
struct MyInner<T>(pub T);
}
mod mismatched_remote_generic {
use bevy_reflect::{FromReflect, GetTypeRegistration, reflect_remote};
#[reflect_remote(super::external_crate::TheirOuter<T>)]
//~^ ERROR: `?` operator has incompatible types
//~| ERROR: mismatched types
//~| ERROR: mismatched types
struct MyOuter<T: FromReflect + GetTypeRegistration> {
// Reason: `TheirOuter::inner` is not defined as `TheirInner<bool>`
#[reflect(remote = MyInner<bool>)]
pub inner: super::external_crate::TheirInner<bool>,
//~^ ERROR: mismatched types
}
#[reflect_remote(super::external_crate::TheirInner<T>)]
struct MyInner<T>(pub T);
}
fn main() {}

View file

@ -0,0 +1,20 @@
//@check-pass
use bevy_reflect::{FromReflect, GetTypeRegistration, reflect_remote, Reflect, Typed};
mod external_crate {
pub struct TheirOuter<T> {
pub inner: TheirInner<T>,
}
pub struct TheirInner<T>(pub T);
}
#[reflect_remote(external_crate::TheirOuter<T>)]
struct MyOuter<T: FromReflect + Typed + GetTypeRegistration> {
#[reflect(remote = MyInner<T>)]
pub inner: external_crate::TheirInner<T>,
}
#[reflect_remote(external_crate::TheirInner<T>)]
struct MyInner<T: Reflect>(pub T);
fn main() {}

View file

@ -0,0 +1,99 @@
//@no-rustfix
mod structs {
use bevy_reflect::{reflect_remote, Reflect};
mod external_crate {
pub struct TheirFoo {
pub value: u32,
}
pub struct TheirBar {
pub value: i32,
}
}
#[reflect_remote(external_crate::TheirFoo)]
struct MyFoo {
pub value: u32,
}
#[reflect_remote(external_crate::TheirBar)]
struct MyBar {
pub value: i32,
}
#[derive(Reflect)]
//~^ ERROR: mismatched types
//~| ERROR: mismatched types
struct MyStruct {
// Reason: Should use `MyFoo`
#[reflect(remote = MyBar)]
//~^ ERROR: mismatched types
foo: external_crate::TheirFoo,
}
}
mod tuple_structs {
use bevy_reflect::{reflect_remote, Reflect};
mod external_crate {
pub struct TheirFoo(pub u32);
pub struct TheirBar(pub i32);
}
#[reflect_remote(external_crate::TheirFoo)]
struct MyFoo(pub u32);
#[reflect_remote(external_crate::TheirBar)]
struct MyBar(pub i32);
#[derive(Reflect)]
//~^ ERROR: mismatched types
//~| ERROR: mismatched types
struct MyStruct(
// Reason: Should use `MyFoo`
#[reflect(remote = MyBar)] external_crate::TheirFoo,
//~^ ERROR: mismatched types
);
}
mod enums {
use bevy_reflect::{reflect_remote, Reflect};
mod external_crate {
pub enum TheirFoo {
Value(u32),
}
pub enum TheirBar {
Value(i32),
}
}
#[reflect_remote(external_crate::TheirFoo)]
enum MyFoo {
Value(u32),
}
#[reflect_remote(external_crate::TheirBar)]
//~^ ERROR: `?` operator has incompatible types
enum MyBar {
// Reason: Should use `i32`
Value(u32),
//~^ ERROR: mismatched types
}
#[derive(Reflect)]
//~^ ERROR: mismatched types
//~| ERROR: mismatched types
//~| ERROR: mismatched types
enum MyStruct {
Value(
// Reason: Should use `MyFoo`
#[reflect(remote = MyBar)] external_crate::TheirFoo,
//~^ ERROR: mismatched types
),
}
}
fn main() {}

View file

@ -0,0 +1,79 @@
//@check-pass
mod structs {
use bevy_reflect::{reflect_remote, Reflect};
mod external_crate {
pub struct TheirFoo {
pub value: u32,
}
pub struct TheirBar {
pub value: i32,
}
}
#[reflect_remote(external_crate::TheirFoo)]
struct MyFoo {
pub value: u32,
}
#[reflect_remote(external_crate::TheirBar)]
struct MyBar {
pub value: i32,
}
#[derive(Reflect)]
struct MyStruct {
#[reflect(remote = MyFoo)]
foo: external_crate::TheirFoo,
}
}
mod tuple_structs {
use bevy_reflect::{reflect_remote, Reflect};
mod external_crate {
pub struct TheirFoo(pub u32);
pub struct TheirBar(pub i32);
}
#[reflect_remote(external_crate::TheirFoo)]
struct MyFoo(pub u32);
#[reflect_remote(external_crate::TheirBar)]
struct MyBar(pub i32);
#[derive(Reflect)]
struct MyStruct(#[reflect(remote = MyFoo)] external_crate::TheirFoo);
}
mod enums {
use bevy_reflect::{reflect_remote, Reflect};
mod external_crate {
pub enum TheirFoo {
Value(u32),
}
pub enum TheirBar {
Value(i32),
}
}
#[reflect_remote(external_crate::TheirFoo)]
enum MyFoo {
Value(u32),
}
#[reflect_remote(external_crate::TheirBar)]
enum MyBar {
Value(i32),
}
#[derive(Reflect)]
enum MyStruct {
Value(#[reflect(remote = MyFoo)] external_crate::TheirFoo),
}
}
fn main() {}

View file

@ -0,0 +1,3 @@
fn main() -> compile_fail_utils::ui_test::Result<()> {
compile_fail_utils::test("reflect_remote", "tests/reflect_remote")
}

View file

@ -8,6 +8,7 @@ use crate::utility::{StringExpr, WhereClauseOptions};
use quote::{quote, ToTokens};
use syn::token::Comma;
use crate::remote::RemoteType;
use crate::serialization::SerializationDataDef;
use crate::{
utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME, TYPE_NAME_ATTRIBUTE_NAME,
@ -46,6 +47,8 @@ pub(crate) struct ReflectMeta<'a> {
attrs: ContainerAttributes,
/// The path to this type.
type_path: ReflectTypePath<'a>,
/// The optional remote type to use instead of the actual type.
remote_ty: Option<RemoteType<'a>>,
/// A cached instance of the path to the `bevy_reflect` crate.
bevy_reflect_path: Path,
/// The documentation for this type, if any
@ -146,8 +149,12 @@ enum ReflectMode {
/// How the macro was invoked.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) enum ReflectImplSource {
/// Using `impl_reflect!`.
ImplRemoteType,
/// Using `#[derive(...)]`.
DeriveLocalType,
/// Using `#[reflect_remote]`.
RemoteReflect,
}
/// Which trait the macro explicitly implements.
@ -173,7 +180,9 @@ impl fmt::Display for ReflectProvenance {
(S::DeriveLocalType, T::Reflect) => "`#[derive(Reflect)]`",
(S::DeriveLocalType, T::FromReflect) => "`#[derive(FromReflect)]`",
(S::DeriveLocalType, T::TypePath) => "`#[derive(TypePath)]`",
(S::ImplRemoteType, T::FromReflect | T::TypePath) => unreachable!(),
(S::RemoteReflect, T::Reflect) => "`#[reflect_remote]`",
(S::RemoteReflect, T::FromReflect | T::TypePath)
| (S::ImplRemoteType, T::FromReflect | T::TypePath) => unreachable!(),
};
f.write_str(str)
}
@ -343,13 +352,52 @@ impl<'a> ReflectDerive<'a> {
};
}
pub fn meta(&self) -> &ReflectMeta<'a> {
/// Set the remote type for this derived type.
///
/// # Panics
///
/// Panics when called on [`ReflectDerive::Value`].
pub fn set_remote(&mut self, remote_ty: Option<RemoteType<'a>>) {
match self {
ReflectDerive::Struct(data)
| ReflectDerive::TupleStruct(data)
| ReflectDerive::UnitStruct(data) => data.meta(),
ReflectDerive::Enum(data) => data.meta(),
ReflectDerive::Value(meta) => meta,
Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {
data.meta.remote_ty = remote_ty;
}
Self::Enum(data) => {
data.meta.remote_ty = remote_ty;
}
Self::Value(meta) => {
meta.remote_ty = remote_ty;
}
}
}
/// Get the remote type path, if any.
pub fn remote_ty(&self) -> Option<RemoteType> {
match self {
Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {
data.meta.remote_ty()
}
Self::Enum(data) => data.meta.remote_ty(),
Self::Value(meta) => meta.remote_ty(),
}
}
/// Get the [`ReflectMeta`] for this derived type.
pub fn meta(&self) -> &ReflectMeta {
match self {
Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => data.meta(),
Self::Enum(data) => data.meta(),
Self::Value(meta) => meta,
}
}
pub fn where_clause_options(&self) -> WhereClauseOptions {
match self {
Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {
data.where_clause_options()
}
Self::Enum(data) => data.where_clause_options(),
Self::Value(meta) => WhereClauseOptions::new(meta),
}
}
@ -424,6 +472,7 @@ impl<'a> ReflectMeta<'a> {
Self {
attrs,
type_path,
remote_ty: None,
bevy_reflect_path: utility::get_bevy_reflect_path(),
#[cfg(feature = "documentation")]
docs: Default::default(),
@ -457,6 +506,16 @@ impl<'a> ReflectMeta<'a> {
&self.type_path
}
/// Get the remote type path, if any.
pub fn remote_ty(&self) -> Option<RemoteType> {
self.remote_ty
}
/// Whether this reflected type represents a remote type or not.
pub fn is_remote_wrapper(&self) -> bool {
self.remote_ty.is_some()
}
/// The cached `bevy_reflect` path.
pub fn bevy_reflect_path(&self) -> &Path {
&self.bevy_reflect_path
@ -500,7 +559,7 @@ impl<'a> StructField<'a> {
}
};
let ty = &self.data.ty;
let ty = self.reflected_type();
let custom_attributes = self.attrs.custom_attributes.to_tokens(bevy_reflect_path);
#[allow(unused_mut)] // Needs mutability for the feature gate
@ -518,6 +577,19 @@ impl<'a> StructField<'a> {
info
}
/// Returns the reflected type of this field.
///
/// Normally this is just the field's defined type.
/// However, this can be adjusted to use a different type, like for representing remote types.
/// In those cases, the returned value is the remote wrapper type.
pub fn reflected_type(&self) -> &Type {
self.attrs.remote.as_ref().unwrap_or(&self.data.ty)
}
pub fn attrs(&self) -> &FieldAttributes {
&self.attrs
}
}
impl<'a> ReflectStruct<'a> {
@ -549,7 +621,7 @@ impl<'a> ReflectStruct<'a> {
/// Get a collection of types which are exposed to the reflection API
pub fn active_types(&self) -> Vec<Type> {
self.active_fields()
.map(|field| field.data.ty.clone())
.map(|field| field.reflected_type().clone())
.collect()
}
@ -631,8 +703,18 @@ impl<'a> ReflectEnum<'a> {
}
/// Returns the given ident as a qualified unit variant of this enum.
///
/// This takes into account the remote type, if any.
pub fn get_unit(&self, variant: &Ident) -> proc_macro2::TokenStream {
let name = self.meta.type_path();
let name = self
.meta
.remote_ty
.map(|path| match path.as_expr_path() {
Ok(path) => path.to_token_stream(),
Err(err) => err.into_compile_error(),
})
.unwrap_or_else(|| self.meta.type_path().to_token_stream());
quote! {
#name::#variant
}
@ -646,7 +728,7 @@ impl<'a> ReflectEnum<'a> {
/// Get a collection of types which are exposed to the reflection API
pub fn active_types(&self) -> Vec<Type> {
self.active_fields()
.map(|field| field.data.ty.clone())
.map(|field| field.reflected_type().clone())
.collect()
}
@ -670,7 +752,7 @@ impl<'a> ReflectEnum<'a> {
self.meta(),
where_clause_options,
None,
Some(self.active_fields().map(|field| &field.data.ty)),
Some(self.active_fields().map(StructField::reflected_type)),
)
}
@ -1121,6 +1203,28 @@ impl<'a> ReflectTypePath<'a> {
pub fn type_ident(&self) -> Option<StringExpr> {
self.get_ident().map(StringExpr::from)
}
/// Returns the true type regardless of whether a custom path is specified.
///
/// To get the custom path if there is one, use [`Self::get_path`].
///
/// For example, the type `Foo<T: Debug>` would return `Foo<T>`.
pub fn true_type(&self) -> proc_macro2::TokenStream {
match self {
Self::Primitive(ident) => quote!(#ident),
Self::Internal {
ident, generics, ..
} => {
let (_, ty_generics, _) = generics.split_for_impl();
quote!(#ident #ty_generics)
}
Self::External { path, generics, .. } => {
let (_, ty_generics, _) = generics.split_for_impl();
quote!(#path #ty_generics)
}
Self::Anonymous { qualified_type, .. } => qualified_type.to_token_stream(),
}
}
}
impl<'a> ToTokens for ReflectTypePath<'a> {

View file

@ -79,12 +79,14 @@ pub(crate) trait VariantBuilder: Sized {
/// * `this`: The identifier of the enum
/// * `field`: The field to access
fn on_active_field(&self, this: &Ident, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum().meta().bevy_reflect_path();
let field_accessor = self.access_field(this, field);
let alias = field.alias;
let field_ty = field.field.reflected_type();
let field_constructor = self.construct_field(field);
match &field.field.attrs.default {
let construction = match &field.field.attrs.default {
DefaultBehavior::Func(path) => quote! {
if let #FQOption::Some(#alias) = #field_accessor {
#field_constructor
@ -109,6 +111,14 @@ pub(crate) trait VariantBuilder: Sized {
#field_constructor
}}
}
};
if field.field.attrs().remote.is_some() {
quote! {
<#field_ty as #bevy_reflect_path::ReflectRemote>::into_remote(#construction)
}
} else {
construction
}
}
@ -200,7 +210,7 @@ impl<'a> VariantBuilder for FromReflectVariantBuilder<'a> {
fn construct_field(&self, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let field_ty = &field.field.data.ty;
let field_ty = field.field.reflected_type();
let alias = field.alias;
quote! {
@ -251,7 +261,7 @@ impl<'a> VariantBuilder for TryApplyVariantBuilder<'a> {
fn construct_field(&self, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let alias = field.alias;
let field_ty = &field.field.data.ty;
let field_ty = field.field.reflected_type();
quote! {
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#alias)

View file

@ -7,13 +7,15 @@
use crate::custom_attributes::CustomAttributes;
use crate::utility::terminated_parser;
use crate::REFLECT_ATTRIBUTE_NAME;
use quote::ToTokens;
use syn::parse::ParseStream;
use syn::{Attribute, LitStr, Meta, Token};
use syn::{Attribute, LitStr, Meta, Token, Type};
mod kw {
syn::custom_keyword!(ignore);
syn::custom_keyword!(skip_serializing);
syn::custom_keyword!(default);
syn::custom_keyword!(remote);
}
pub(crate) const IGNORE_SERIALIZATION_ATTR: &str = "skip_serializing";
@ -76,6 +78,8 @@ pub(crate) struct FieldAttributes {
pub default: DefaultBehavior,
/// Custom attributes created via `#[reflect(@...)]`.
pub custom_attributes: CustomAttributes,
/// For defining the remote wrapper type that should be used in place of the field for reflection logic.
pub remote: Option<Type>,
}
impl FieldAttributes {
@ -119,6 +123,8 @@ impl FieldAttributes {
self.parse_skip_serializing(input)
} else if lookahead.peek(kw::default) {
self.parse_default(input)
} else if lookahead.peek(kw::remote) {
self.parse_remote(input)
} else {
Err(lookahead.error())
}
@ -190,4 +196,41 @@ impl FieldAttributes {
fn parse_custom_attribute(&mut self, input: ParseStream) -> syn::Result<()> {
self.custom_attributes.parse_custom_attribute(input)
}
/// Parse `remote` attribute.
///
/// Examples:
/// - `#[reflect(remote = path::to::RemoteType)]`
fn parse_remote(&mut self, input: ParseStream) -> syn::Result<()> {
if let Some(remote) = self.remote.as_ref() {
return Err(input.error(format!(
"remote type already specified as {}",
remote.to_token_stream()
)));
}
input.parse::<kw::remote>()?;
input.parse::<Token![=]>()?;
self.remote = Some(input.parse()?);
Ok(())
}
/// Returns `Some(true)` if the field has a generic remote type.
///
/// If the remote type is not generic, returns `Some(false)`.
///
/// If the field does not have a remote type, returns `None`.
pub fn is_remote_generic(&self) -> Option<bool> {
if let Type::Path(type_path) = self.remote.as_ref()? {
type_path
.path
.segments
.last()
.map(|segment| !segment.arguments.is_empty())
} else {
Some(false)
}
}
}

View file

@ -52,6 +52,16 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
..
} = FromReflectVariantBuilder::new(reflect_enum).build(&ref_value);
let match_branches = if reflect_enum.meta().is_remote_wrapper() {
quote! {
#(#variant_names => #fqoption::Some(Self(#variant_constructors)),)*
}
} else {
quote! {
#(#variant_names => #fqoption::Some(#variant_constructors),)*
}
};
let (impl_generics, ty_generics, where_clause) = enum_path.generics().split_for_impl();
// Add FromReflect bound for each active field
@ -66,7 +76,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
#bevy_reflect_path::PartialReflect::reflect_ref(#ref_value)
{
match #bevy_reflect_path::Enum::variant_name(#ref_value) {
#(#variant_names => #fqoption::Some(#variant_constructors),)*
#match_branches
name => panic!("variant with name `{}` does not exist on enum `{}`", name, <Self as #bevy_reflect_path::TypePath>::type_path()),
}
} else {
@ -94,6 +104,7 @@ fn impl_struct_internal(
let fqoption = FQOption.into_token_stream();
let struct_path = reflect_struct.meta().type_path();
let remote_ty = reflect_struct.meta().remote_ty();
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
let ref_struct = Ident::new("__ref_struct", Span::call_site());
@ -107,28 +118,48 @@ fn impl_struct_internal(
get_active_fields(reflect_struct, &ref_struct, &ref_struct_type, is_tuple);
let is_defaultable = reflect_struct.meta().attrs().contains(REFLECT_DEFAULT);
// The constructed "Self" ident
let __this = Ident::new("__this", Span::call_site());
// The reflected type: either `Self` or a remote type
let (reflect_ty, constructor, retval) = if let Some(remote_ty) = remote_ty {
let constructor = match remote_ty.as_expr_path() {
Ok(path) => path,
Err(err) => return err.into_compile_error(),
};
let remote_ty = remote_ty.type_path();
(
quote!(#remote_ty),
quote!(#constructor),
quote!(Self(#__this)),
)
} else {
(quote!(Self), quote!(Self), quote!(#__this))
};
let constructor = if is_defaultable {
quote!(
let mut __this: Self = #FQDefault::default();
quote! {
let mut #__this = <#reflect_ty as #FQDefault>::default();
#(
if let #fqoption::Some(__field) = #active_values() {
// Iff field exists -> use its value
__this.#active_members = __field;
#__this.#active_members = __field;
}
)*
#FQOption::Some(__this)
)
#FQOption::Some(#retval)
}
} else {
let MemberValuePair(ignored_members, ignored_values) = get_ignored_fields(reflect_struct);
quote!(
#FQOption::Some(
Self {
#(#active_members: #active_values()?,)*
#(#ignored_members: #ignored_values,)*
}
)
)
quote! {
let #__this = #constructor {
#(#active_members: #active_values()?,)*
#(#ignored_members: #ignored_values,)*
};
#FQOption::Some(#retval)
}
};
let (impl_generics, ty_generics, where_clause) = reflect_struct
@ -201,34 +232,76 @@ fn get_active_fields(
field.reflection_index.expect("field should be active"),
is_tuple,
);
let ty = field.data.ty.clone();
let ty = field.reflected_type().clone();
let real_ty = &field.data.ty;
let get_field = quote! {
#bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor)
};
let into_remote = |value: proc_macro2::TokenStream| {
if field.attrs.is_remote_generic().unwrap_or_default() {
quote! {
#FQOption::Some(
// SAFETY: The remote type should always be a `#[repr(transparent)]` for the actual field type
unsafe {
::core::mem::transmute_copy::<#ty, #real_ty>(
&::core::mem::ManuallyDrop::new(#value?)
)
}
)
}
} else if field.attrs().remote.is_some() {
quote! {
#FQOption::Some(
// SAFETY: The remote type should always be a `#[repr(transparent)]` for the actual field type
unsafe {
::core::mem::transmute::<#ty, #real_ty>(#value?)
}
)
}
} else {
value
}
};
let value = match &field.attrs.default {
DefaultBehavior::Func(path) => quote! {
(||
if let #FQOption::Some(field) = #get_field {
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
} else {
#FQOption::Some(#path())
}
)
},
DefaultBehavior::Default => quote! {
(||
if let #FQOption::Some(field) = #get_field {
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
} else {
#FQOption::Some(#FQDefault::default())
}
)
},
DefaultBehavior::Required => quote! {
(|| <#ty as #bevy_reflect_path::FromReflect>::from_reflect(#get_field?))
},
DefaultBehavior::Func(path) => {
let value = into_remote(quote! {
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
});
quote! {
(||
if let #FQOption::Some(field) = #get_field {
#value
} else {
#FQOption::Some(#path())
}
)
}
}
DefaultBehavior::Default => {
let value = into_remote(quote! {
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
});
quote! {
(||
if let #FQOption::Some(field) = #get_field {
#value
} else {
#FQOption::Some(#FQDefault::default())
}
)
}
}
DefaultBehavior::Required => {
let value = into_remote(quote! {
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(#get_field?)
});
quote! {
(|| #value)
}
}
};
(member, value)

View file

@ -0,0 +1,14 @@
use crate::derive_data::ReflectDerive;
use crate::remote::generate_remote_assertions;
use quote::quote;
/// Generates an anonymous block containing compile-time assertions.
pub(crate) fn impl_assertions(derive_data: &ReflectDerive) -> proc_macro2::TokenStream {
let mut output = quote!();
if let Some(assertions) = generate_remote_assertions(derive_data) {
output.extend(assertions);
}
output
}

View file

@ -14,8 +14,25 @@ pub fn impl_full_reflect(
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
impl #impl_generics #bevy_reflect_path::Reflect for #type_path #ty_generics #where_reflect_clause {
let any_impls = if meta.is_remote_wrapper() {
quote! {
#[inline]
fn into_any(self: #FQBox<Self>) -> #FQBox<dyn #FQAny> {
#FQBox::new(self.0)
}
#[inline]
fn as_any(&self) -> &dyn #FQAny {
&self.0
}
#[inline]
fn as_any_mut(&mut self) -> &mut dyn #FQAny {
&mut self.0
}
}
} else {
quote! {
#[inline]
fn into_any(self: #FQBox<Self>) -> #FQBox<dyn #FQAny> {
self
@ -30,6 +47,12 @@ pub fn impl_full_reflect(
fn as_any_mut(&mut self) -> &mut dyn #FQAny {
self
}
}
};
quote! {
impl #impl_generics #bevy_reflect_path::Reflect for #type_path #ty_generics #where_reflect_clause {
#any_impls
#[inline]
fn into_reflect(self: #FQBox<Self>) -> #FQBox<dyn #bevy_reflect_path::Reflect> {

View file

@ -4,11 +4,31 @@ use crate::impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_
use bevy_macro_utils::fq_std::{FQBox, FQOption, FQResult};
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::Fields;
use syn::{Fields, Path};
pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream {
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
let enum_path = reflect_enum.meta().type_path();
let is_remote = reflect_enum.meta().is_remote_wrapper();
// For `match self` expressions where self is a reference
let match_this = if is_remote {
quote!(&self.0)
} else {
quote!(self)
};
// For `match self` expressions where self is a mutable reference
let match_this_mut = if is_remote {
quote!(&mut self.0)
} else {
quote!(self)
};
// For `*self` assignments
let deref_this = if is_remote {
quote!(self.0)
} else {
quote!(*self)
};
let ref_name = Ident::new("__name_param", Span::call_site());
let ref_index = Ident::new("__index_param", Span::call_site());
@ -18,7 +38,9 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
let EnumImpls {
enum_field,
enum_field_mut,
enum_field_at,
enum_field_at_mut,
enum_index_of,
enum_name_at,
enum_field_len,
@ -73,42 +95,42 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
impl #impl_generics #bevy_reflect_path::Enum for #enum_path #ty_generics #where_reflect_clause {
fn field(&self, #ref_name: &str) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match self {
match #match_this {
#(#enum_field,)*
_ => #FQOption::None,
}
}
fn field_at(&self, #ref_index: usize) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match self {
match #match_this {
#(#enum_field_at,)*
_ => #FQOption::None,
}
}
fn field_mut(&mut self, #ref_name: &str) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match self {
#(#enum_field,)*
match #match_this_mut {
#(#enum_field_mut,)*
_ => #FQOption::None,
}
}
fn field_at_mut(&mut self, #ref_index: usize) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match self {
#(#enum_field_at,)*
match #match_this_mut {
#(#enum_field_at_mut,)*
_ => #FQOption::None,
}
}
fn index_of(&self, #ref_name: &str) -> #FQOption<usize> {
match self {
match #match_this {
#(#enum_index_of,)*
_ => #FQOption::None,
}
}
fn name_at(&self, #ref_index: usize) -> #FQOption<&str> {
match self {
match #match_this {
#(#enum_name_at,)*
_ => #FQOption::None,
}
@ -120,7 +142,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
#[inline]
fn field_len(&self) -> usize {
match self {
match #match_this {
#(#enum_field_len,)*
_ => 0,
}
@ -128,7 +150,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
#[inline]
fn variant_name(&self) -> &str {
match self {
match #match_this {
#(#enum_variant_name,)*
_ => unreachable!(),
}
@ -136,7 +158,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
#[inline]
fn variant_index(&self) -> usize {
match self {
match #match_this {
#(#enum_variant_index,)*
_ => unreachable!(),
}
@ -144,7 +166,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
#[inline]
fn variant_type(&self) -> #bevy_reflect_path::VariantType {
match self {
match #match_this {
#(#enum_variant_type,)*
_ => unreachable!(),
}
@ -197,7 +219,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
// New variant -> perform a switch
match #bevy_reflect_path::Enum::variant_name(#ref_value) {
#(#variant_names => {
*self = #variant_constructors
#deref_this = #variant_constructors
})*
name => {
return #FQResult::Err(
@ -243,7 +265,9 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
struct EnumImpls {
enum_field: Vec<proc_macro2::TokenStream>,
enum_field_mut: Vec<proc_macro2::TokenStream>,
enum_field_at: Vec<proc_macro2::TokenStream>,
enum_field_at_mut: Vec<proc_macro2::TokenStream>,
enum_index_of: Vec<proc_macro2::TokenStream>,
enum_name_at: Vec<proc_macro2::TokenStream>,
enum_field_len: Vec<proc_macro2::TokenStream>,
@ -256,7 +280,9 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
let mut enum_field = Vec::new();
let mut enum_field_mut = Vec::new();
let mut enum_field_at = Vec::new();
let mut enum_field_at_mut = Vec::new();
let mut enum_index_of = Vec::new();
let mut enum_name_at = Vec::new();
let mut enum_field_len = Vec::new();
@ -304,6 +330,29 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden
field_len
}
/// Process the field value to account for remote types.
///
/// If the field is a remote type, then the value will be transmuted accordingly.
fn process_field_value(
ident: &Ident,
field: &StructField,
is_mutable: bool,
bevy_reflect_path: &Path,
) -> proc_macro2::TokenStream {
let method = if is_mutable {
quote!(as_wrapper_mut)
} else {
quote!(as_wrapper)
};
field
.attrs
.remote
.as_ref()
.map(|ty| quote!(<#ty as #bevy_reflect_path::ReflectRemote>::#method(#ident)))
.unwrap_or_else(|| quote!(#ident))
}
match &variant.fields {
EnumVariantFields::Unit => {
let field_len = process_fields(&[], |_| {});
@ -319,8 +368,16 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden
.expect("reflection index should exist for active field");
let declare_field = syn::Index::from(field.declaration_index);
let __value = Ident::new("__value", Span::call_site());
let value_ref = process_field_value(&__value, field, false, bevy_reflect_path);
let value_mut = process_field_value(&__value, field, true, bevy_reflect_path);
enum_field_at.push(quote! {
#unit { #declare_field : value, .. } if #ref_index == #reflection_index => #FQOption::Some(value)
#unit { #declare_field : #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_ref)
});
enum_field_at_mut.push(quote! {
#unit { #declare_field : #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_mut)
});
});
@ -336,11 +393,21 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden
.reflection_index
.expect("reflection index should exist for active field");
let __value = Ident::new("__value", Span::call_site());
let value_ref = process_field_value(&__value, field, false, bevy_reflect_path);
let value_mut = process_field_value(&__value, field, true, bevy_reflect_path);
enum_field.push(quote! {
#unit{ #field_ident, .. } if #ref_name == #field_name => #FQOption::Some(#field_ident)
#unit{ #field_ident: #__value, .. } if #ref_name == #field_name => #FQOption::Some(#value_ref)
});
enum_field_mut.push(quote! {
#unit{ #field_ident: #__value, .. } if #ref_name == #field_name => #FQOption::Some(#value_mut)
});
enum_field_at.push(quote! {
#unit{ #field_ident, .. } if #ref_index == #reflection_index => #FQOption::Some(#field_ident)
#unit{ #field_ident: #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_ref)
});
enum_field_at_mut.push(quote! {
#unit{ #field_ident: #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_mut)
});
enum_index_of.push(quote! {
#unit{ .. } if #ref_name == #field_name => #FQOption::Some(#reflection_index)
@ -359,7 +426,9 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden
EnumImpls {
enum_field,
enum_field_mut,
enum_field_at,
enum_field_at_mut,
enum_index_of,
enum_name_at,
enum_field_len,

View file

@ -1,3 +1,4 @@
mod assertions;
mod common;
mod enums;
#[cfg(feature = "functions")]
@ -7,6 +8,7 @@ mod tuple_structs;
mod typed;
mod values;
pub(crate) use assertions::impl_assertions;
pub(crate) use common::{common_partial_reflect_methods, impl_full_reflect};
pub(crate) use enums::impl_enum;
#[cfg(feature = "functions")]

View file

@ -1,5 +1,5 @@
use crate::impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed};
use crate::utility::ident_or_index;
use crate::struct_utility::FieldAccessors;
use crate::ReflectStruct;
use bevy_macro_utils::fq_std::{FQBox, FQDefault, FQOption, FQResult};
use quote::{quote, ToTokens};
@ -22,12 +22,14 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS
.unwrap_or_else(|| field.declaration_index.to_string())
})
.collect::<Vec<String>>();
let field_idents = reflect_struct
.active_fields()
.map(|field| ident_or_index(field.data.ident.as_ref(), field.declaration_index))
.collect::<Vec<_>>();
let field_count = field_idents.len();
let field_indices = (0..field_count).collect::<Vec<usize>>();
let FieldAccessors {
fields_ref,
fields_mut,
field_indices,
field_count,
..
} = FieldAccessors::new(reflect_struct);
let where_clause_options = reflect_struct.where_clause_options();
let typed_impl = impl_typed(
@ -74,28 +76,28 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS
impl #impl_generics #bevy_reflect_path::Struct for #struct_path #ty_generics #where_reflect_clause {
fn field(&self, name: &str) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match name {
#(#field_names => #fqoption::Some(&self.#field_idents),)*
#(#field_names => #fqoption::Some(#fields_ref),)*
_ => #FQOption::None,
}
}
fn field_mut(&mut self, name: &str) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match name {
#(#field_names => #fqoption::Some(&mut self.#field_idents),)*
#(#field_names => #fqoption::Some(#fields_mut),)*
_ => #FQOption::None,
}
}
fn field_at(&self, index: usize) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match index {
#(#field_indices => #fqoption::Some(&self.#field_idents),)*
#(#field_indices => #fqoption::Some(#fields_ref),)*
_ => #FQOption::None,
}
}
fn field_at_mut(&mut self, index: usize) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match index {
#(#field_indices => #fqoption::Some(&mut self.#field_idents),)*
#(#field_indices => #fqoption::Some(#fields_mut),)*
_ => #FQOption::None,
}
}
@ -118,7 +120,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS
fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct {
let mut dynamic: #bevy_reflect_path::DynamicStruct = #FQDefault::default();
dynamic.set_represented_type(#bevy_reflect_path::PartialReflect::get_represented_type_info(self));
#(dynamic.insert_boxed(#field_names, #bevy_reflect_path::PartialReflect::clone_value(&self.#field_idents));)*
#(dynamic.insert_boxed(#field_names, #bevy_reflect_path::PartialReflect::clone_value(#fields_ref));)*
dynamic
}
}

View file

@ -1,8 +1,8 @@
use crate::impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed};
use crate::struct_utility::FieldAccessors;
use crate::ReflectStruct;
use bevy_macro_utils::fq_std::{FQBox, FQDefault, FQOption, FQResult};
use quote::{quote, ToTokens};
use syn::{Index, Member};
/// Implements `TupleStruct`, `GetTypeRegistration`, and `Reflect` for the given derive data.
pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenStream {
@ -11,12 +11,13 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
let struct_path = reflect_struct.meta().type_path();
let field_idents = reflect_struct
.active_fields()
.map(|field| Member::Unnamed(Index::from(field.declaration_index)))
.collect::<Vec<_>>();
let field_count = field_idents.len();
let field_indices = (0..field_count).collect::<Vec<usize>>();
let FieldAccessors {
fields_ref,
fields_mut,
field_indices,
field_count,
..
} = FieldAccessors::new(reflect_struct);
let where_clause_options = reflect_struct.where_clause_options();
let get_type_registration_impl = reflect_struct.get_type_registration(&where_clause_options);
@ -63,14 +64,14 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::
impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_path #ty_generics #where_reflect_clause {
fn field(&self, index: usize) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match index {
#(#field_indices => #fqoption::Some(&self.#field_idents),)*
#(#field_indices => #fqoption::Some(#fields_ref),)*
_ => #FQOption::None,
}
}
fn field_mut(&mut self, index: usize) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match index {
#(#field_indices => #fqoption::Some(&mut self.#field_idents),)*
#(#field_indices => #fqoption::Some(#fields_mut),)*
_ => #FQOption::None,
}
}
@ -86,7 +87,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::
fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTupleStruct {
let mut dynamic: #bevy_reflect_path::DynamicTupleStruct = #FQDefault::default();
dynamic.set_represented_type(#bevy_reflect_path::PartialReflect::get_represented_type_info(self));
#(dynamic.insert_boxed(#bevy_reflect_path::PartialReflect::clone_value(&self.#field_idents));)*
#(dynamic.insert_boxed(#bevy_reflect_path::PartialReflect::clone_value(#fields_ref));)*
dynamic
}
}

View file

@ -31,6 +31,23 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream {
let full_reflect_impl = impl_full_reflect(meta, &where_clause_options);
let common_methods = common_partial_reflect_methods(meta, || None, || None);
let apply_impl = if let Some(remote_ty) = meta.remote_ty() {
let ty = remote_ty.type_path();
quote! {
if let #FQOption::Some(value) = <dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<#ty>(value) {
*self = Self(#FQClone::clone(value));
return #FQResult::Ok(());
}
}
} else {
quote! {
if let #FQOption::Some(value) = <dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<Self>(value) {
*self = #FQClone::clone(value);
return #FQResult::Ok(());
}
}
};
#[cfg(not(feature = "functions"))]
let function_impls = None::<proc_macro2::TokenStream>;
#[cfg(feature = "functions")]
@ -67,17 +84,14 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream {
&mut self,
value: &dyn #bevy_reflect_path::PartialReflect
) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
if let #FQOption::Some(value) = <dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<Self>(value) {
*self = #FQClone::clone(value);
} else {
return #FQResult::Err(
#bevy_reflect_path::ApplyError::MismatchedTypes {
from_type: ::core::convert::Into::into(#bevy_reflect_path::DynamicTypePath::reflect_type_path(value)),
to_type: ::core::convert::Into::into(<Self as #bevy_reflect_path::TypePath>::type_path()),
}
);
}
#FQResult::Ok(())
#apply_impl
#FQResult::Err(
#bevy_reflect_path::ApplyError::MismatchedTypes {
from_type: ::core::convert::Into::into(#bevy_reflect_path::DynamicTypePath::reflect_type_path(value)),
to_type: ::core::convert::Into::into(<Self as #bevy_reflect_path::TypePath>::type_path()),
}
)
}
#[inline]

View file

@ -27,7 +27,9 @@ mod from_reflect;
mod impls;
mod reflect_value;
mod registration;
mod remote;
mod serialization;
mod struct_utility;
mod trait_reflection;
mod type_path;
mod utility;
@ -62,6 +64,8 @@ fn match_reflect_impls(ast: DeriveInput, source: ReflectImplSource) -> TokenStre
Err(err) => return err.into_compile_error().into(),
};
let assertions = impls::impl_assertions(&derive_data);
let (reflect_impls, from_reflect_impl) = match derive_data {
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => (
impls::impl_struct(&struct_data),
@ -100,7 +104,10 @@ fn match_reflect_impls(ast: DeriveInput, source: ReflectImplSource) -> TokenStre
TokenStream::from(quote! {
const _: () = {
#reflect_impls
#from_reflect_impl
#assertions
};
})
}
@ -511,6 +518,91 @@ pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream {
trait_reflection::reflect_trait(&args, input)
}
/// Generates a wrapper type that can be used to "derive `Reflect`" for remote types.
///
/// This works by wrapping the remote type in a generated wrapper that has the `#[repr(transparent)]` attribute.
/// This allows the two types to be safely [transmuted] back-and-forth.
///
/// # Defining the Wrapper
///
/// Before defining the wrapper type, please note that it is _required_ that all fields of the remote type are public.
/// The generated code will, at times, need to access or mutate them,
/// and we do not currently have a way to assign getters/setters to each field
/// (but this may change in the future).
///
/// The wrapper definition should match the remote type 1-to-1.
/// This includes the naming and ordering of the fields and variants.
///
/// Generics and lifetimes do _not_ need to have the same names, however, they _do_ need to follow the same order.
/// Additionally, whether generics are inlined or placed in a where clause should not matter.
///
/// Lastly, all macros and doc-comments should be placed __below__ this attribute.
/// If they are placed above, they will not be properly passed to the generated wrapper type.
///
/// # Example
///
/// Given a remote type, `RemoteType`:
///
/// ```
/// #[derive(Default)]
/// struct RemoteType<T>
/// where
/// T: Default + Clone,
/// {
/// pub foo: T,
/// pub bar: usize
/// }
/// ```
///
/// We would define our wrapper type as such:
///
/// ```ignore
/// use external_crate::RemoteType;
///
/// #[reflect_remote(RemoteType<T>)]
/// #[derive(Default)]
/// pub struct WrapperType<T: Default + Clone> {
/// pub foo: T,
/// pub bar: usize
/// }
/// ```
///
/// Apart from all the reflection trait implementations, this generates something like the following:
///
/// ```ignore
/// use external_crate::RemoteType;
///
/// #[derive(Default)]
/// #[repr(transparent)]
/// pub struct Wrapper<T: Default + Clone>(RemoteType<T>);
/// ```
///
/// # Usage as a Field
///
/// You can tell `Reflect` to use a remote type's wrapper internally on fields of a struct or enum.
/// This allows the real type to be used as usual while `Reflect` handles everything internally.
/// To do this, add the `#[reflect(remote = path::to::MyType)]` attribute to your field:
///
/// ```ignore
/// #[derive(Reflect)]
/// struct SomeStruct {
/// #[reflect(remote = RemoteTypeWrapper)]
/// data: RemoteType
/// }
/// ```
///
/// ## Safety
///
/// When using the `#[reflect(remote = path::to::MyType)]` field attribute, be sure you are defining the correct wrapper type.
/// Internally, this field will be unsafely [transmuted], and is only sound if using a wrapper generated for the remote type.
/// This also means keeping your wrapper definitions up-to-date with the remote types.
///
/// [transmuted]: std::mem::transmute
#[proc_macro_attribute]
pub fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStream {
remote::reflect_remote(args, input)
}
/// A macro used to generate reflection trait implementations for the given type.
///
/// This is functionally the same as [deriving `Reflect`] using the `#[reflect_value]` container attribute.

View file

@ -0,0 +1,499 @@
use crate::derive_data::{ReflectImplSource, ReflectProvenance, ReflectTraitToImpl};
use crate::impls::impl_assertions;
use crate::utility::ident_or_index;
use crate::{
from_reflect, impls, ReflectDerive, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME,
};
use bevy_macro_utils::fq_std::FQOption;
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::{format_ident, quote, quote_spanned};
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::token::PathSep;
use syn::{
parse_macro_input, DeriveInput, ExprPath, Generics, Member, PathArguments, Type, TypePath,
};
/// Generates the remote wrapper type and implements all the necessary traits.
pub(crate) fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStream {
let remote_args = match syn::parse::<RemoteArgs>(args) {
Ok(path) => path,
Err(err) => return err.to_compile_error().into(),
};
let remote_ty = remote_args.remote_ty;
let ast = parse_macro_input!(input as DeriveInput);
let wrapper_definition = generate_remote_wrapper(&ast, &remote_ty);
let mut derive_data = match ReflectDerive::from_input(
&ast,
ReflectProvenance {
source: ReflectImplSource::RemoteReflect,
trait_: ReflectTraitToImpl::Reflect,
},
) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};
derive_data.set_remote(Some(RemoteType::new(&remote_ty)));
let assertions = impl_assertions(&derive_data);
let definition_assertions = generate_remote_definition_assertions(&derive_data);
let reflect_remote_impl = impl_reflect_remote(&derive_data, &remote_ty);
let (reflect_impls, from_reflect_impl) = match derive_data {
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => (
impls::impl_struct(&struct_data),
if struct_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_struct(&struct_data))
} else {
None
},
),
ReflectDerive::TupleStruct(struct_data) => (
impls::impl_tuple_struct(&struct_data),
if struct_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_tuple_struct(&struct_data))
} else {
None
},
),
ReflectDerive::Enum(enum_data) => (
impls::impl_enum(&enum_data),
if enum_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_enum(&enum_data))
} else {
None
},
),
ReflectDerive::Value(meta) => (
impls::impl_value(&meta),
if meta.from_reflect().should_auto_derive() {
Some(from_reflect::impl_value(&meta))
} else {
None
},
),
};
TokenStream::from(quote! {
#wrapper_definition
const _: () = {
#reflect_remote_impl
#reflect_impls
#from_reflect_impl
#definition_assertions
#assertions
};
})
}
/// Generates the remote wrapper type.
///
/// # Example
///
/// If the supplied remote type is `Bar<T>`, then the wrapper type— named `Foo<T>`— would look like:
///
/// ```
/// # struct Bar<T>(T);
///
/// #[repr(transparent)]
/// struct Foo<T>(Bar<T>);
/// ```
fn generate_remote_wrapper(input: &DeriveInput, remote_ty: &TypePath) -> proc_macro2::TokenStream {
let ident = &input.ident;
let vis = &input.vis;
let ty_generics = &input.generics;
let where_clause = &input.generics.where_clause;
let attrs = input.attrs.iter().filter(|attr| {
!attr.path().is_ident(REFLECT_ATTRIBUTE_NAME)
&& !attr.path().is_ident(REFLECT_VALUE_ATTRIBUTE_NAME)
});
quote! {
#(#attrs)*
#[repr(transparent)]
#[doc(hidden)]
#vis struct #ident #ty_generics (pub #remote_ty) #where_clause;
}
}
/// Generates the implementation of the `ReflectRemote` trait for the given derive data and remote type.
///
/// # Note to Developers
///
/// The `ReflectRemote` trait could likely be made with default method implementations.
/// However, this makes it really easy for a user to accidentally implement this trait in an unsafe way.
/// To prevent this, we instead generate the implementation through a macro using this function.
fn impl_reflect_remote(input: &ReflectDerive, remote_ty: &TypePath) -> proc_macro2::TokenStream {
let bevy_reflect_path = input.meta().bevy_reflect_path();
let type_path = input.meta().type_path();
let (impl_generics, ty_generics, where_clause) =
input.meta().type_path().generics().split_for_impl();
let where_reflect_clause = input
.where_clause_options()
.extend_where_clause(where_clause);
quote! {
// SAFE: The generated wrapper type is guaranteed to be valid and repr(transparent) over the remote type.
impl #impl_generics #bevy_reflect_path::ReflectRemote for #type_path #ty_generics #where_reflect_clause {
type Remote = #remote_ty;
fn as_remote(&self) -> &Self::Remote {
&self.0
}
fn as_remote_mut(&mut self) -> &mut Self::Remote {
&mut self.0
}
fn into_remote(self) -> Self::Remote
{
// SAFE: The wrapper type should be repr(transparent) over the remote type
unsafe {
// Unfortunately, we have to use `transmute_copy` to avoid a compiler error:
// ```
// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
// |
// | std::mem::transmute::<A, B>(a)
// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// |
// = note: source type: `A` (this type does not have a fixed size)
// = note: target type: `B` (this type does not have a fixed size)
// ```
::core::mem::transmute_copy::<Self, Self::Remote>(
// `ManuallyDrop` is used to prevent double-dropping `self`
&::core::mem::ManuallyDrop::new(self)
)
}
}
fn as_wrapper(remote: &Self::Remote) -> &Self {
// SAFE: The wrapper type should be repr(transparent) over the remote type
unsafe { ::core::mem::transmute::<&Self::Remote, &Self>(remote) }
}
fn as_wrapper_mut(remote: &mut Self::Remote) -> &mut Self {
// SAFE: The wrapper type should be repr(transparent) over the remote type
unsafe { ::core::mem::transmute::<&mut Self::Remote, &mut Self>(remote) }
}
fn into_wrapper(remote: Self::Remote) -> Self
{
// SAFE: The wrapper type should be repr(transparent) over the remote type
unsafe {
// Unfortunately, we have to use `transmute_copy` to avoid a compiler error:
// ```
// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
// |
// | std::mem::transmute::<A, B>(a)
// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// |
// = note: source type: `A` (this type does not have a fixed size)
// = note: target type: `B` (this type does not have a fixed size)
// ```
::core::mem::transmute_copy::<Self::Remote, Self>(
// `ManuallyDrop` is used to prevent double-dropping `self`
&::core::mem::ManuallyDrop::new(remote)
)
}
}
}
}
}
/// Generates compile-time assertions for remote fields.
///
/// This prevents types from using an invalid remote type.
/// this works by generating a struct, `RemoteFieldAssertions`, with methods that
/// will result in compile-time failure if types are mismatched.
/// The output of this function is best placed within an anonymous context to maintain hygiene.
///
/// # Example
///
/// The following would fail to compile due to an incorrect `#[reflect(remote = ...)]` value.
///
/// ```ignore
/// mod external_crate {
/// pub struct TheirFoo(pub u32);
/// pub struct TheirBar(pub i32);
/// }
///
/// #[reflect_remote(external_crate::TheirFoo)]
/// struct MyFoo(pub u32);
/// #[reflect_remote(external_crate::TheirBar)]
/// struct MyBar(pub i32);
///
/// #[derive(Reflect)]
/// struct MyStruct {
/// #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar`
/// foo: external_crate::TheirFoo
/// }
/// ```
pub(crate) fn generate_remote_assertions(
derive_data: &ReflectDerive,
) -> Option<proc_macro2::TokenStream> {
struct RemoteAssertionData<'a> {
ident: Member,
variant: Option<&'a Ident>,
ty: &'a Type,
generics: &'a Generics,
remote_ty: &'a Type,
}
let bevy_reflect_path = derive_data.meta().bevy_reflect_path();
let fields: Box<dyn Iterator<Item = RemoteAssertionData>> = match derive_data {
ReflectDerive::Struct(data)
| ReflectDerive::TupleStruct(data)
| ReflectDerive::UnitStruct(data) => Box::new(data.active_fields().filter_map(|field| {
field
.attrs
.remote
.as_ref()
.map(|remote_ty| RemoteAssertionData {
ident: ident_or_index(field.data.ident.as_ref(), field.declaration_index),
variant: None,
ty: &field.data.ty,
generics: data.meta().type_path().generics(),
remote_ty,
})
})),
ReflectDerive::Enum(data) => Box::new(data.variants().iter().flat_map(|variant| {
variant.active_fields().filter_map(|field| {
field
.attrs
.remote
.as_ref()
.map(|remote_ty| RemoteAssertionData {
ident: ident_or_index(field.data.ident.as_ref(), field.declaration_index),
variant: Some(&variant.data.ident),
ty: &field.data.ty,
generics: data.meta().type_path().generics(),
remote_ty,
})
})
})),
_ => return None,
};
let assertions = fields
.map(move |field| {
let ident = if let Some(variant) = field.variant {
format_ident!("{}__{}", variant, field.ident)
} else {
match field.ident {
Member::Named(ident) => ident,
Member::Unnamed(index) => format_ident!("field_{}", index),
}
};
let (impl_generics, _, where_clause) = field.generics.split_for_impl();
let where_reflect_clause = derive_data
.where_clause_options()
.extend_where_clause(where_clause);
let ty = &field.ty;
let remote_ty = field.remote_ty;
let assertion_ident = format_ident!("assert__{}__is_valid_remote", ident);
let span = create_assertion_span(remote_ty.span());
quote_spanned! {span=>
#[allow(non_snake_case)]
#[allow(clippy::multiple_bound_locations)]
fn #assertion_ident #impl_generics () #where_reflect_clause {
let _: <#remote_ty as #bevy_reflect_path::ReflectRemote>::Remote = (|| -> #FQOption<#ty> {
None
})().unwrap();
}
}
})
.collect::<proc_macro2::TokenStream>();
if assertions.is_empty() {
None
} else {
Some(quote! {
struct RemoteFieldAssertions;
impl RemoteFieldAssertions {
#assertions
}
})
}
}
/// Generates compile-time assertions that ensure a remote wrapper definition matches up with the
/// remote type it's wrapping.
///
/// Note: This currently results in "backwards" error messages like:
///
/// ```ignore
/// expected: <WRAPPER_FIELD_TYPE>
/// found: <REMOTE_FIELD_TYPE>
/// ```
///
/// Ideally it would be the other way around, but there's no easy way of doing this without
/// generating a copy of the struct/enum definition and using that as the base instead of the remote type.
fn generate_remote_definition_assertions(derive_data: &ReflectDerive) -> proc_macro2::TokenStream {
let meta = derive_data.meta();
let self_ident = format_ident!("__remote__");
let self_ty = derive_data.remote_ty().unwrap().type_path();
let self_expr_path = derive_data.remote_ty().unwrap().as_expr_path().unwrap();
let (impl_generics, _, where_clause) = meta.type_path().generics().split_for_impl();
let where_reflect_clause = derive_data
.where_clause_options()
.extend_where_clause(where_clause);
let assertions = match derive_data {
ReflectDerive::Struct(data)
| ReflectDerive::TupleStruct(data)
| ReflectDerive::UnitStruct(data) => {
let mut output = proc_macro2::TokenStream::new();
for field in data.fields() {
let field_member =
ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let field_ty = &field.data.ty;
let span = create_assertion_span(field_ty.span());
output.extend(quote_spanned! {span=>
#self_ident.#field_member = (|| -> #FQOption<#field_ty> {None})().unwrap();
});
}
output
}
ReflectDerive::Enum(data) => {
let variants = data.variants().iter().map(|variant| {
let ident = &variant.data.ident;
let mut output = proc_macro2::TokenStream::new();
if variant.fields().is_empty() {
return quote!(#self_expr_path::#ident => {});
}
for field in variant.fields() {
let field_member =
ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let field_ident = format_ident!("field_{}", field_member);
let field_ty = &field.data.ty;
let span = create_assertion_span(field_ty.span());
output.extend(quote_spanned! {span=>
#self_expr_path::#ident {#field_member: mut #field_ident, ..} => {
#field_ident = (|| -> #FQOption<#field_ty> {None})().unwrap();
}
});
}
output
});
quote! {
match #self_ident {
#(#variants)*
}
}
}
ReflectDerive::Value(_) => {
// No assertions needed since there are no fields to check
proc_macro2::TokenStream::new()
}
};
quote! {
const _: () = {
#[allow(non_snake_case)]
#[allow(unused_variables)]
#[allow(unused_assignments)]
#[allow(unreachable_patterns)]
#[allow(clippy::multiple_bound_locations)]
fn assert_wrapper_definition_matches_remote_type #impl_generics (mut #self_ident: #self_ty) #where_reflect_clause {
#assertions
}
};
}
}
/// Creates a span located around the given one, but resolves to the assertion's context.
///
/// This should allow the compiler to point back to the line and column in the user's code,
/// while still attributing the error to the macro.
fn create_assertion_span(span: Span) -> Span {
Span::call_site().located_at(span)
}
/// A reflected type's remote type.
///
/// This is a wrapper around [`TypePath`] that allows it to be paired with other remote-specific logic.
#[derive(Copy, Clone)]
pub(crate) struct RemoteType<'a> {
path: &'a TypePath,
}
impl<'a> RemoteType<'a> {
pub fn new(path: &'a TypePath) -> Self {
Self { path }
}
/// Returns the [type path](TypePath) of this remote type.
pub fn type_path(&self) -> &'a TypePath {
self.path
}
/// Attempts to convert the [type path](TypePath) of this remote type into an [expression path](ExprPath).
///
/// For example, this would convert `foo::Bar<T>` into `foo::Bar::<T>` to be used as part of an expression.
///
/// This will return an error for types that are parenthesized, such as in `Fn() -> Foo`.
pub fn as_expr_path(&self) -> Result<ExprPath, syn::Error> {
let mut expr_path = self.path.clone();
if let Some(segment) = expr_path.path.segments.last_mut() {
match &mut segment.arguments {
PathArguments::None => {}
PathArguments::AngleBracketed(arg) => {
arg.colon2_token = Some(PathSep::default());
}
PathArguments::Parenthesized(arg) => {
return Err(syn::Error::new(
arg.span(),
"cannot use parenthesized type as remote type",
))
}
}
}
Ok(ExprPath {
path: expr_path.path,
qself: expr_path.qself,
attrs: Vec::new(),
})
}
}
/// Metadata from the arguments defined in the `reflect_remote` attribute.
///
/// The syntax for the arguments is: `#[reflect_remote(REMOTE_TYPE_PATH)]`.
struct RemoteArgs {
remote_ty: TypePath,
}
impl Parse for RemoteArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
remote_ty: input.parse()?,
})
}
}

View file

@ -0,0 +1,80 @@
use crate::derive_data::StructField;
use crate::{utility, ReflectStruct};
use quote::quote;
/// A helper struct for creating remote-aware field accessors.
///
/// These are "remote-aware" because when a field is a remote field, it uses a [`transmute`] internally
/// to access the field.
///
/// [`transmute`]: core::mem::transmute
pub(crate) struct FieldAccessors {
/// The referenced field accessors, such as `&self.foo`.
pub fields_ref: Vec<proc_macro2::TokenStream>,
/// The mutably referenced field accessors, such as `&mut self.foo`.
pub fields_mut: Vec<proc_macro2::TokenStream>,
/// The ordered set of field indices (basically just the range of [0, `field_count`).
pub field_indices: Vec<usize>,
/// The number of fields in the reflected struct.
pub field_count: usize,
}
impl FieldAccessors {
pub fn new(reflect_struct: &ReflectStruct) -> Self {
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
let fields_ref = Self::get_fields(reflect_struct, |field, accessor| {
match &field.attrs.remote {
Some(wrapper_ty) => {
quote! {
<#wrapper_ty as #bevy_reflect_path::ReflectRemote>::as_wrapper(&#accessor)
}
}
None => quote!(& #accessor),
}
});
let fields_mut = Self::get_fields(reflect_struct, |field, accessor| {
match &field.attrs.remote {
Some(wrapper_ty) => {
quote! {
<#wrapper_ty as #bevy_reflect_path::ReflectRemote>::as_wrapper_mut(&mut #accessor)
}
}
None => quote!(&mut #accessor),
}
});
let field_count = fields_ref.len();
let field_indices = (0..field_count).collect();
Self {
fields_ref,
fields_mut,
field_indices,
field_count,
}
}
fn get_fields<F>(
reflect_struct: &ReflectStruct,
mut wrapper_fn: F,
) -> Vec<proc_macro2::TokenStream>
where
F: FnMut(&StructField, proc_macro2::TokenStream) -> proc_macro2::TokenStream,
{
let is_remote = reflect_struct.meta().is_remote_wrapper();
reflect_struct
.active_fields()
.map(|field| {
let member =
utility::ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let accessor = if is_remote {
quote!(self.0.#member)
} else {
quote!(self.#member)
};
wrapper_fn(field, accessor)
})
.collect::<Vec<_>>()
}
}

View file

@ -154,17 +154,18 @@ impl<'a, 'b> WhereClauseOptions<'a, 'b> {
&self,
where_clause: Option<&WhereClause>,
) -> proc_macro2::TokenStream {
let type_path = self.meta.type_path();
let (_, ty_generics, _) = self.meta.type_path().generics().split_for_impl();
// We would normally just use `Self`, but that won't work for generating things like assertion functions
// and trait impls for a type's reference (e.g. `impl FromArg for &MyType`)
let this = self.meta.type_path().true_type();
let required_bounds = self.required_bounds();
// Maintain existing where clause, if any.
let mut generic_where_clause = if let Some(where_clause) = where_clause {
let predicates = where_clause.predicates.iter();
quote! {where #type_path #ty_generics: #required_bounds, #(#predicates,)*}
quote! {where #this: #required_bounds, #(#predicates,)*}
} else {
quote!(where #type_path #ty_generics: #required_bounds,)
quote!(where #this: #required_bounds,)
};
// Add additional reflection trait bounds

View file

@ -534,6 +534,7 @@ mod list;
mod map;
mod path;
mod reflect;
mod remote;
mod set;
mod struct_trait;
mod tuple;
@ -584,6 +585,7 @@ pub use list::*;
pub use map::*;
pub use path::*;
pub use reflect::*;
pub use remote::*;
pub use set::*;
pub use struct_trait::*;
pub use tuple::*;
@ -2479,6 +2481,467 @@ bevy_reflect::tests::Test {
assert_impl_all!(Enum: Reflect);
}
#[test]
fn should_reflect_remote_type() {
mod external_crate {
#[derive(Debug, Default)]
pub struct TheirType {
pub value: String,
}
}
// === Remote Wrapper === //
#[reflect_remote(external_crate::TheirType)]
#[derive(Debug, Default)]
#[reflect(Debug, Default)]
struct MyType {
pub value: String,
}
let mut patch = DynamicStruct::default();
patch.set_represented_type(Some(MyType::type_info()));
patch.insert("value", "Goodbye".to_string());
let mut data = MyType(external_crate::TheirType {
value: "Hello".to_string(),
});
assert_eq!("Hello", data.0.value);
data.apply(&patch);
assert_eq!("Goodbye", data.0.value);
// === Struct Container === //
#[derive(Reflect, Debug)]
#[reflect(from_reflect = false)]
struct ContainerStruct {
#[reflect(remote = MyType)]
their_type: external_crate::TheirType,
}
let mut patch = DynamicStruct::default();
patch.set_represented_type(Some(ContainerStruct::type_info()));
patch.insert(
"their_type",
MyType(external_crate::TheirType {
value: "Goodbye".to_string(),
}),
);
let mut data = ContainerStruct {
their_type: external_crate::TheirType {
value: "Hello".to_string(),
},
};
assert_eq!("Hello", data.their_type.value);
data.apply(&patch);
assert_eq!("Goodbye", data.their_type.value);
// === Tuple Struct Container === //
#[derive(Reflect, Debug)]
struct ContainerTupleStruct(#[reflect(remote = MyType)] external_crate::TheirType);
let mut patch = DynamicTupleStruct::default();
patch.set_represented_type(Some(ContainerTupleStruct::type_info()));
patch.insert(MyType(external_crate::TheirType {
value: "Goodbye".to_string(),
}));
let mut data = ContainerTupleStruct(external_crate::TheirType {
value: "Hello".to_string(),
});
assert_eq!("Hello", data.0.value);
data.apply(&patch);
assert_eq!("Goodbye", data.0.value);
}
#[test]
fn should_reflect_remote_value_type() {
mod external_crate {
#[derive(Clone, Debug, Default)]
pub struct TheirType {
pub value: String,
}
}
// === Remote Wrapper === //
#[reflect_remote(external_crate::TheirType)]
#[derive(Clone, Debug, Default)]
#[reflect_value(Debug, Default)]
struct MyType {
pub value: String,
}
let mut data = MyType(external_crate::TheirType {
value: "Hello".to_string(),
});
let patch = MyType(external_crate::TheirType {
value: "Goodbye".to_string(),
});
assert_eq!("Hello", data.0.value);
data.apply(&patch);
assert_eq!("Goodbye", data.0.value);
// === Struct Container === //
#[derive(Reflect, Debug)]
#[reflect(from_reflect = false)]
struct ContainerStruct {
#[reflect(remote = MyType)]
their_type: external_crate::TheirType,
}
let mut patch = DynamicStruct::default();
patch.set_represented_type(Some(ContainerStruct::type_info()));
patch.insert(
"their_type",
MyType(external_crate::TheirType {
value: "Goodbye".to_string(),
}),
);
let mut data = ContainerStruct {
their_type: external_crate::TheirType {
value: "Hello".to_string(),
},
};
assert_eq!("Hello", data.their_type.value);
data.apply(&patch);
assert_eq!("Goodbye", data.their_type.value);
// === Tuple Struct Container === //
#[derive(Reflect, Debug)]
struct ContainerTupleStruct(#[reflect(remote = MyType)] external_crate::TheirType);
let mut patch = DynamicTupleStruct::default();
patch.set_represented_type(Some(ContainerTupleStruct::type_info()));
patch.insert(MyType(external_crate::TheirType {
value: "Goodbye".to_string(),
}));
let mut data = ContainerTupleStruct(external_crate::TheirType {
value: "Hello".to_string(),
});
assert_eq!("Hello", data.0.value);
data.apply(&patch);
assert_eq!("Goodbye", data.0.value);
}
#[test]
fn should_reflect_remote_type_from_module() {
mod wrapper {
use super::*;
// We have to place this module internally to this one to get around the following error:
// ```
// error[E0433]: failed to resolve: use of undeclared crate or module `external_crate`
// ```
pub mod external_crate {
pub struct TheirType {
pub value: String,
}
}
#[reflect_remote(external_crate::TheirType)]
pub struct MyType {
pub value: String,
}
}
#[derive(Reflect)]
struct ContainerStruct {
#[reflect(remote = wrapper::MyType)]
their_type: wrapper::external_crate::TheirType,
}
}
#[test]
fn should_reflect_remote_enum() {
mod external_crate {
#[derive(Debug, PartialEq, Eq)]
pub enum TheirType {
Unit,
Tuple(usize),
Struct { value: String },
}
}
// === Remote Wrapper === //
#[reflect_remote(external_crate::TheirType)]
#[derive(Debug)]
#[reflect(Debug)]
enum MyType {
Unit,
Tuple(usize),
Struct { value: String },
}
let mut patch = DynamicEnum::from(MyType(external_crate::TheirType::Tuple(123)));
let mut data = MyType(external_crate::TheirType::Unit);
assert_eq!(external_crate::TheirType::Unit, data.0);
data.apply(&patch);
assert_eq!(external_crate::TheirType::Tuple(123), data.0);
patch = DynamicEnum::from(MyType(external_crate::TheirType::Struct {
value: "Hello world!".to_string(),
}));
data.apply(&patch);
assert_eq!(
external_crate::TheirType::Struct {
value: "Hello world!".to_string()
},
data.0
);
// === Enum Container === //
#[derive(Reflect, Debug, PartialEq)]
enum ContainerEnum {
Foo,
Bar {
#[reflect(remote = MyType)]
their_type: external_crate::TheirType,
},
}
let patch = DynamicEnum::from(ContainerEnum::Bar {
their_type: external_crate::TheirType::Tuple(123),
});
let mut data = ContainerEnum::Foo;
assert_eq!(ContainerEnum::Foo, data);
data.apply(&patch);
assert_eq!(
ContainerEnum::Bar {
their_type: external_crate::TheirType::Tuple(123)
},
data
);
}
#[test]
fn should_reflect_nested_remote_type() {
mod external_crate {
pub struct TheirOuter<T> {
pub a: TheirInner<T>,
pub b: TheirInner<bool>,
}
pub struct TheirInner<T>(pub T);
}
#[reflect_remote(external_crate::TheirOuter<T>)]
struct MyOuter<T: FromReflect + Typed + GetTypeRegistration> {
#[reflect(remote = MyInner<T>)]
pub a: external_crate::TheirInner<T>,
#[reflect(remote = MyInner<bool>)]
pub b: external_crate::TheirInner<bool>,
}
#[reflect_remote(external_crate::TheirInner<T>)]
struct MyInner<T: FromReflect>(pub T);
let mut patch = DynamicStruct::default();
patch.set_represented_type(Some(MyOuter::<i32>::type_info()));
patch.insert("a", MyInner(external_crate::TheirInner(321_i32)));
patch.insert("b", MyInner(external_crate::TheirInner(true)));
let mut data = MyOuter(external_crate::TheirOuter {
a: external_crate::TheirInner(123_i32),
b: external_crate::TheirInner(false),
});
assert_eq!(123, data.0.a.0);
assert!(!data.0.b.0);
data.apply(&patch);
assert_eq!(321, data.0.a.0);
assert!(data.0.b.0);
}
#[test]
fn should_reflect_nested_remote_enum() {
mod external_crate {
use std::fmt::Debug;
#[derive(Debug)]
pub enum TheirOuter<T: Debug> {
Unit,
Tuple(TheirInner<T>),
Struct { value: TheirInner<T> },
}
#[derive(Debug)]
pub enum TheirInner<T: Debug> {
Unit,
Tuple(T),
Struct { value: T },
}
}
#[reflect_remote(external_crate::TheirOuter<T>)]
#[derive(Debug)]
enum MyOuter<T: FromReflect + Typed + Debug + GetTypeRegistration> {
Unit,
Tuple(#[reflect(remote = MyInner<T>)] external_crate::TheirInner<T>),
Struct {
#[reflect(remote = MyInner<T>)]
value: external_crate::TheirInner<T>,
},
}
#[reflect_remote(external_crate::TheirInner<T>)]
#[derive(Debug)]
enum MyInner<T: FromReflect + Debug> {
Unit,
Tuple(T),
Struct { value: T },
}
let mut patch = DynamicEnum::default();
let mut value = DynamicStruct::default();
value.insert("value", MyInner(external_crate::TheirInner::Tuple(123)));
patch.set_variant("Struct", value);
let mut data = MyOuter(external_crate::TheirOuter::<i32>::Unit);
assert!(matches!(
data,
MyOuter(external_crate::TheirOuter::<i32>::Unit)
));
data.apply(&patch);
assert!(matches!(
data,
MyOuter(external_crate::TheirOuter::Struct {
value: external_crate::TheirInner::Tuple(123)
})
));
}
#[test]
fn should_take_remote_type() {
mod external_crate {
#[derive(Debug, Default, PartialEq, Eq)]
pub struct TheirType {
pub value: String,
}
}
// === Remote Wrapper === //
#[reflect_remote(external_crate::TheirType)]
#[derive(Debug, Default)]
#[reflect(Debug, Default)]
struct MyType {
pub value: String,
}
let input: Box<dyn Reflect> = Box::new(MyType(external_crate::TheirType {
value: "Hello".to_string(),
}));
let output: external_crate::TheirType = input
.take()
.expect("should downcast to `external_crate::TheirType`");
assert_eq!(
external_crate::TheirType {
value: "Hello".to_string(),
},
output
);
}
#[test]
fn should_try_take_remote_type() {
mod external_crate {
#[derive(Debug, Default, PartialEq, Eq)]
pub struct TheirType {
pub value: String,
}
}
// === Remote Wrapper === //
#[reflect_remote(external_crate::TheirType)]
#[derive(Debug, Default)]
#[reflect(Debug, Default)]
struct MyType {
pub value: String,
}
let input: Box<dyn PartialReflect> = Box::new(MyType(external_crate::TheirType {
value: "Hello".to_string(),
}));
let output: external_crate::TheirType = input
.try_take()
.expect("should downcast to `external_crate::TheirType`");
assert_eq!(
external_crate::TheirType {
value: "Hello".to_string(),
},
output,
);
}
#[test]
fn should_take_nested_remote_type() {
mod external_crate {
#[derive(PartialEq, Eq, Debug)]
pub struct TheirOuter<T> {
pub inner: TheirInner<T>,
}
#[derive(PartialEq, Eq, Debug)]
pub struct TheirInner<T>(pub T);
}
#[reflect_remote(external_crate::TheirOuter<T>)]
struct MyOuter<T: FromReflect + Typed + GetTypeRegistration> {
#[reflect(remote = MyInner<T>)]
pub inner: external_crate::TheirInner<T>,
}
#[reflect_remote(external_crate::TheirInner<T>)]
struct MyInner<T: FromReflect>(pub T);
let input: Box<dyn Reflect> = Box::new(MyOuter(external_crate::TheirOuter {
inner: external_crate::TheirInner(123),
}));
let output: external_crate::TheirOuter<i32> = input
.take()
.expect("should downcast to `external_crate::TheirOuter`");
assert_eq!(
external_crate::TheirOuter {
inner: external_crate::TheirInner(123),
},
output
);
}
#[test]
fn should_reflect_external_crate_type() {
// This test relies on the external type not implementing `Reflect`,
// so let's just double-check that it does not
assert_not_impl_all!(std::collections::Bound<i32>: Reflect);
#[reflect_remote(std::collections::Bound<T>)]
enum MyBound<T> {
Included(T),
Excluded(T),
Unbounded,
}
#[derive(Reflect)]
struct MyType {
#[reflect(remote = MyBound<String>)]
bound: std::collections::Bound<String>,
}
}
#[cfg(feature = "glam")]
mod glam {
use super::*;

View file

@ -410,12 +410,18 @@ where
)]
pub trait Reflect: PartialReflect + Any {
/// Returns the value as a [`Box<dyn Any>`][std::any::Any].
///
/// For remote wrapper types, this will return the remote type instead.
fn into_any(self: Box<Self>) -> Box<dyn Any>;
/// Returns the value as a [`&dyn Any`][std::any::Any].
///
/// For remote wrapper types, this will return the remote type instead.
fn as_any(&self) -> &dyn Any;
/// Returns the value as a [`&mut dyn Any`][std::any::Any].
///
/// For remote wrapper types, this will return the remote type instead.
fn as_any_mut(&mut self) -> &mut dyn Any;
/// Casts this type to a boxed, fully-reflected value.
@ -450,7 +456,9 @@ impl dyn PartialReflect {
///
/// If the underlying value does not implement [`Reflect`]
/// or is not of type `T`, returns `Err(self)`.
pub fn try_downcast<T: Reflect>(
///
/// For remote types, `T` should be the type itself rather than the wrapper type.
pub fn try_downcast<T: Any>(
self: Box<dyn PartialReflect>,
) -> Result<Box<T>, Box<dyn PartialReflect>> {
self.try_into_reflect()?
@ -462,9 +470,9 @@ impl dyn PartialReflect {
///
/// If the underlying value does not implement [`Reflect`]
/// or is not of type `T`, returns `Err(self)`.
pub fn try_take<T: Reflect>(
self: Box<dyn PartialReflect>,
) -> Result<T, Box<dyn PartialReflect>> {
///
/// For remote types, `T` should be the type itself rather than the wrapper type.
pub fn try_take<T: Any>(self: Box<dyn PartialReflect>) -> Result<T, Box<dyn PartialReflect>> {
self.try_downcast().map(|value| *value)
}
@ -472,7 +480,9 @@ impl dyn PartialReflect {
///
/// If the underlying value does not implement [`Reflect`]
/// or is not of type `T`, returns [`None`].
pub fn try_downcast_ref<T: Reflect>(&self) -> Option<&T> {
///
/// For remote types, `T` should be the type itself rather than the wrapper type.
pub fn try_downcast_ref<T: Any>(&self) -> Option<&T> {
self.try_as_reflect()?.downcast_ref()
}
@ -480,7 +490,9 @@ impl dyn PartialReflect {
///
/// If the underlying value does not implement [`Reflect`]
/// or is not of type `T`, returns [`None`].
pub fn try_downcast_mut<T: Reflect>(&mut self) -> Option<&mut T> {
///
/// For remote types, `T` should be the type itself rather than the wrapper type.
pub fn try_downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
self.try_as_reflect_mut()?.downcast_mut()
}
}
@ -508,7 +520,9 @@ impl dyn Reflect {
/// Downcasts the value to type `T`, consuming the trait object.
///
/// If the underlying value is not of type `T`, returns `Err(self)`.
pub fn downcast<T: Reflect>(self: Box<dyn Reflect>) -> Result<Box<T>, Box<dyn Reflect>> {
///
/// For remote types, `T` should be the type itself rather than the wrapper type.
pub fn downcast<T: Any>(self: Box<dyn Reflect>) -> Result<Box<T>, Box<dyn Reflect>> {
if self.is::<T>() {
Ok(self.into_any().downcast().unwrap())
} else {
@ -519,7 +533,9 @@ impl dyn Reflect {
/// Downcasts the value to type `T`, unboxing and consuming the trait object.
///
/// If the underlying value is not of type `T`, returns `Err(self)`.
pub fn take<T: Reflect>(self: Box<dyn Reflect>) -> Result<T, Box<dyn Reflect>> {
///
/// For remote types, `T` should be the type itself rather than the wrapper type.
pub fn take<T: Any>(self: Box<dyn Reflect>) -> Result<T, Box<dyn Reflect>> {
self.downcast::<T>().map(|value| *value)
}
@ -532,25 +548,31 @@ impl dyn Reflect {
/// to determine what type they represent. Represented types cannot be downcasted
/// to, but you can use [`FromReflect`] to create a value of the represented type from them.
///
/// For remote types, `T` should be the type itself rather than the wrapper type.
///
/// [`FromReflect`]: crate::FromReflect
#[inline]
pub fn is<T: Reflect>(&self) -> bool {
self.type_id() == TypeId::of::<T>()
pub fn is<T: Any>(&self) -> bool {
self.as_any().type_id() == TypeId::of::<T>()
}
/// Downcasts the value to type `T` by reference.
///
/// If the underlying value is not of type `T`, returns `None`.
///
/// For remote types, `T` should be the type itself rather than the wrapper type.
#[inline]
pub fn downcast_ref<T: Reflect>(&self) -> Option<&T> {
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
self.as_any().downcast_ref::<T>()
}
/// Downcasts the value to type `T` by mutable reference.
///
/// If the underlying value is not of type `T`, returns `None`.
///
/// For remote types, `T` should be the type itself rather than the wrapper type.
#[inline]
pub fn downcast_mut<T: Reflect>(&mut self) -> Option<&mut T> {
pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
self.as_any_mut().downcast_mut::<T>()
}
}

View file

@ -0,0 +1,64 @@
use crate::Reflect;
/// Marks a type as a [reflectable] wrapper for a remote type.
///
/// This allows types from external libraries (remote types) to be included in reflection.
///
/// # Safety
///
/// It is highly recommended to avoid implementing this trait manually and instead use the
/// [`#[reflect_remote]`](crate::reflect_remote) attribute macro.
/// This is because the trait tends to rely on [`transmute`], which is [very unsafe].
///
/// The macro will ensure that the following safety requirements are met:
/// - `Self` is a single-field tuple struct (i.e. a newtype) containing the remote type.
/// - `Self` is `#[repr(transparent)]` over the remote type.
///
/// Additionally, the macro will automatically generate [`Reflect`] and [`FromReflect`] implementations,
/// along with compile-time assertions to validate that the safety requirements have been met.
///
/// # Example
///
/// ```
/// use bevy_reflect_derive::{reflect_remote, Reflect};
///
/// mod some_lib {
/// pub struct TheirType {
/// pub value: u32
/// }
/// }
///
/// #[reflect_remote(some_lib::TheirType)]
/// struct MyType {
/// pub value: u32
/// }
///
/// #[derive(Reflect)]
/// struct MyStruct {
/// #[reflect(remote = MyType)]
/// data: some_lib::TheirType,
/// }
/// ```
///
/// [reflectable]: Reflect
/// [`transmute`]: core::mem::transmute
/// [very unsafe]: https://doc.rust-lang.org/1.71.0/nomicon/transmutes.html
/// [`FromReflect`]: crate::FromReflect
pub trait ReflectRemote: Reflect {
/// The remote type this type represents via reflection.
type Remote;
/// Converts a reference of this wrapper to a reference of its remote type.
fn as_remote(&self) -> &Self::Remote;
/// Converts a mutable reference of this wrapper to a mutable reference of its remote type.
fn as_remote_mut(&mut self) -> &mut Self::Remote;
/// Converts this wrapper into its remote type.
fn into_remote(self) -> Self::Remote;
/// Converts a reference of the remote type to a reference of this wrapper.
fn as_wrapper(remote: &Self::Remote) -> &Self;
/// Converts a mutable reference of the remote type to a mutable reference of this wrapper.
fn as_wrapper_mut(remote: &mut Self::Remote) -> &mut Self;
/// Converts the remote type into this wrapper.
fn into_wrapper(remote: Self::Remote) -> Self;
}