Skip to content

Commit

Permalink
Fix reflect_remote not working with generics
Browse files Browse the repository at this point in the history
  • Loading branch information
MrGVSV committed Dec 21, 2022
1 parent d1f0286 commit 59f68b9
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 28 deletions.
20 changes: 11 additions & 9 deletions crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ use crate::utility::members_to_serialization_denylist;
use bit_set::BitSet;
use quote::{quote, ToTokens};

use crate::remote::RemoteType;
use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{
Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Type, TypePath, Variant,
};
use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Type, Variant};

pub(crate) enum ReflectDerive<'a> {
Struct(ReflectStruct<'a>),
Expand Down Expand Up @@ -63,7 +62,7 @@ pub(crate) struct ReflectStruct<'a> {
meta: ReflectMeta<'a>,
serialization_denylist: BitSet<u32>,
fields: Vec<StructField<'a>>,
remote_ty: Option<&'a TypePath>,
remote_ty: Option<RemoteType<'a>>,
}

/// Enum data used by derive macros for `Reflect` and `FromReflect`.
Expand All @@ -82,7 +81,7 @@ pub(crate) struct ReflectStruct<'a> {
pub(crate) struct ReflectEnum<'a> {
meta: ReflectMeta<'a>,
variants: Vec<EnumVariant<'a>>,
remote_ty: Option<&'a TypePath>,
remote_ty: Option<RemoteType<'a>>,
}

/// Represents a field on a struct or tuple struct.
Expand Down Expand Up @@ -237,7 +236,7 @@ impl<'a> ReflectDerive<'a> {
/// # Panics
///
/// Panics when called on [`ReflectDerive::Value`].
pub fn set_remote(&mut self, remote_ty: Option<&'a TypePath>) {
pub fn set_remote(&mut self, remote_ty: Option<RemoteType<'a>>) {
match self {
Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {
data.remote_ty = remote_ty;
Expand Down Expand Up @@ -383,7 +382,7 @@ impl<'a> ReflectStruct<'a> {

#[allow(dead_code)]
/// Get the remote type path, if any.
pub fn remote_ty(&self) -> Option<&'a TypePath> {
pub fn remote_ty(&self) -> Option<RemoteType<'a>> {
self.remote_ty
}

Expand Down Expand Up @@ -453,7 +452,7 @@ impl<'a> ReflectEnum<'a> {

#[allow(dead_code)]
/// Get the remote type path, if any.
pub fn remote_ty(&self) -> Option<&'a TypePath> {
pub fn remote_ty(&self) -> Option<RemoteType<'a>> {
self.remote_ty
}

Expand All @@ -463,7 +462,10 @@ impl<'a> ReflectEnum<'a> {
pub fn get_unit(&self, variant: &Ident) -> proc_macro2::TokenStream {
let name = self
.remote_ty
.map(|path| path.to_token_stream())
.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_name.to_token_stream());

quote! {
Expand Down
26 changes: 19 additions & 7 deletions crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,25 @@ pub(crate) fn get_variant_constructors(
let accessor = quote!(#field_accessor .expect(#missing_field_err_message));

if let Some(field_ty) = &field.attrs.remote {
quote! {
unsafe {
// SAFE: The wrapper type should be repr(transparent) over the remote type
::std::mem::transmute(
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#ref_value #accessor)
#unwrapper
)

let param = quote! {
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#ref_value #accessor)
#unwrapper
};

if field.attrs.is_remote_generic().unwrap_or_default() {
quote!{
unsafe {
// SAFE: The wrapper type should be repr(transparent) over the remote type
::core::mem::transmute_copy(&#param)
}
}
} else {
quote! {
unsafe {
// SAFE: The wrapper type should be repr(transparent) over the remote type
::core::mem::transmute(#param)
}
}
}
} else {
Expand Down
14 changes: 14 additions & 0 deletions crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ pub(crate) struct ReflectFieldAttr {
pub remote: Option<Type>,
}

impl ReflectFieldAttr {
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)
}
}
}

/// Controls how the default value is determined for a field.
#[derive(Default)]
pub(crate) enum DefaultBehavior {
Expand Down
44 changes: 38 additions & 6 deletions crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,20 @@ fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> Token
let __this = Ident::new("__this", Span::call_site());

// The reflected type: either `Self` or a remote type
let (reflect_ty, retval) = if let Some(remote_ty) = reflect_struct.remote_ty() {
(quote!(#remote_ty), quote!(Self(#__this)))
let (reflect_ty, constructor, retval) = if let Some(remote_ty) = reflect_struct.remote_ty() {
let constructor = match remote_ty.as_expr_path() {
Ok(path) => path,
Err(err) => return err.into_compile_error().into(),
};
let remote_ty = remote_ty.type_path();

(
quote!(#remote_ty),
quote!(#constructor),
quote!(Self(#__this)),
)
} else {
(quote!(Self), quote!(#__this))
(quote!(Self), quote!(Self), quote!(#__this))
};

let constructor = if reflect_struct.meta().traits().contains(REFLECT_DEFAULT) {
Expand All @@ -128,7 +138,7 @@ fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> Token
get_ignored_fields(reflect_struct, is_tuple);

quote! {
let #__this = #reflect_ty {
let #__this = #constructor {
#(#active_members: #active_values()?,)*
#(#ignored_members: #ignored_values,)*
};
Expand Down Expand Up @@ -204,13 +214,14 @@ fn get_active_fields(
.map(|field| {
let member = get_ident(field.data, field.index, is_tuple);
let accessor = get_field_accessor(field.data, field.index, is_tuple);
let ty = field.data.ty.clone();
let ty = field.reflected_type();
let real_ty = &field.data.ty;

let get_field = quote! {
#bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor)
};

let value = match &field.attrs.default {
let mut value = match &field.attrs.default {
DefaultBehavior::Func(path) => quote! {
(||
if let #FQOption::Some(field) = #get_field {
Expand All @@ -234,6 +245,27 @@ fn get_active_fields(
},
};

if field.attrs.remote.is_some() {
value = if field.attrs.is_remote_generic().unwrap_or_default() {
quote! {
(|| Some(
unsafe {::core::mem::transmute_copy::<_, #real_ty>(&#value()?)}
))
}
} else {
quote! {
(|| Some(
unsafe {::core::mem::transmute::<_, #real_ty>(#value()?)}
))
}
};
value = quote! {
(|| Some(
unsafe {::core::mem::transmute_copy::<_, #real_ty>(&#value()?)}
))
}
}

(member, value)
})
.unzip(),
Expand Down
52 changes: 50 additions & 2 deletions crates/bevy_reflect/bevy_reflect_derive/src/remote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use proc_macro::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::{parse_macro_input, DeriveInput, Token, TypePath};
use syn::{parse_macro_input, DeriveInput, ExprPath, PathArguments, Token, TypePath};

/// Generates the remote wrapper type and implements all the necessary traits.
pub(crate) fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStream {
Expand All @@ -23,7 +23,7 @@ pub(crate) fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStre
Err(err) => return err.into_compile_error().into(),
};

derive_data.set_remote(Some(&remote_ty));
derive_data.set_remote(Some(RemoteType::new(&remote_ty)));

let from_reflect_impl = if is_from_reflect {
Some(from_reflect_remote(&derive_data))
Expand Down Expand Up @@ -96,6 +96,54 @@ fn generate_remote_wrapper(input: &DeriveInput, remote_ty: &TypePath) -> proc_ma
}
}

/// 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(syn::token::Colon2::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)]`
Expand Down
Loading

0 comments on commit 59f68b9

Please sign in to comment.