Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_reflect/derive/src/remote.rs
9418 views
1
use crate::{
2
derive_data::{ReflectImplSource, ReflectProvenance, ReflectTraitToImpl},
3
from_reflect, impls,
4
impls::impl_assertions,
5
ReflectDerive, REFLECT_ATTRIBUTE_NAME,
6
};
7
use bevy_macro_utils::as_member;
8
use bevy_macro_utils::fq_std::FQOption;
9
use proc_macro::TokenStream;
10
use proc_macro2::{Ident, Span};
11
use quote::{format_ident, quote, quote_spanned};
12
use syn::{
13
parse::{Parse, ParseStream},
14
parse_macro_input,
15
spanned::Spanned,
16
token::PathSep,
17
DeriveInput, ExprPath, Generics, Member, PathArguments, Type, TypePath,
18
};
19
20
/// Generates the remote wrapper type and implements all the necessary traits.
21
pub(crate) fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStream {
22
let remote_args = match syn::parse::<RemoteArgs>(args) {
23
Ok(path) => path,
24
Err(err) => return err.to_compile_error().into(),
25
};
26
27
let remote_ty = remote_args.remote_ty;
28
29
let ast = parse_macro_input!(input as DeriveInput);
30
let wrapper_definition = generate_remote_wrapper(&ast, &remote_ty);
31
32
let mut derive_data = match ReflectDerive::from_input(
33
&ast,
34
ReflectProvenance {
35
source: ReflectImplSource::RemoteReflect,
36
trait_: ReflectTraitToImpl::Reflect,
37
},
38
) {
39
Ok(data) => data,
40
Err(err) => return err.into_compile_error().into(),
41
};
42
43
derive_data.set_remote(Some(RemoteType::new(&remote_ty)));
44
45
let assertions = impl_assertions(&derive_data);
46
let definition_assertions = generate_remote_definition_assertions(&derive_data);
47
48
let reflect_remote_impl = impl_reflect_remote(&derive_data, &remote_ty);
49
50
let (reflect_impls, from_reflect_impl) = match derive_data {
51
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => (
52
impls::impl_struct(&struct_data),
53
if struct_data.meta().from_reflect().should_auto_derive() {
54
Some(from_reflect::impl_struct(&struct_data))
55
} else {
56
None
57
},
58
),
59
ReflectDerive::TupleStruct(struct_data) => (
60
impls::impl_tuple_struct(&struct_data),
61
if struct_data.meta().from_reflect().should_auto_derive() {
62
Some(from_reflect::impl_tuple_struct(&struct_data))
63
} else {
64
None
65
},
66
),
67
ReflectDerive::Enum(enum_data) => (
68
impls::impl_enum(&enum_data),
69
if enum_data.meta().from_reflect().should_auto_derive() {
70
Some(from_reflect::impl_enum(&enum_data))
71
} else {
72
None
73
},
74
),
75
ReflectDerive::Opaque(meta) => (
76
impls::impl_opaque(&meta),
77
if meta.from_reflect().should_auto_derive() {
78
Some(from_reflect::impl_opaque(&meta))
79
} else {
80
None
81
},
82
),
83
};
84
85
TokenStream::from(quote! {
86
#wrapper_definition
87
88
const _: () = {
89
#reflect_remote_impl
90
91
#reflect_impls
92
93
#from_reflect_impl
94
95
#definition_assertions
96
97
#assertions
98
};
99
})
100
}
101
102
/// Generates the remote wrapper type.
103
///
104
/// # Example
105
///
106
/// If the supplied remote type is `Bar<T>`, then the wrapper type— named `Foo<T>`— would look like:
107
///
108
/// ```
109
/// # struct Bar<T>(T);
110
///
111
/// #[repr(transparent)]
112
/// struct Foo<T>(Bar<T>);
113
/// ```
114
fn generate_remote_wrapper(input: &DeriveInput, remote_ty: &TypePath) -> proc_macro2::TokenStream {
115
let ident = &input.ident;
116
let vis = &input.vis;
117
let ty_generics = &input.generics;
118
let where_clause = &input.generics.where_clause;
119
let attrs = input
120
.attrs
121
.iter()
122
.filter(|attr| !attr.path().is_ident(REFLECT_ATTRIBUTE_NAME));
123
124
quote! {
125
#(#attrs)*
126
#[repr(transparent)]
127
#[doc(hidden)]
128
#vis struct #ident #ty_generics (pub #remote_ty) #where_clause;
129
}
130
}
131
132
/// Generates the implementation of the `ReflectRemote` trait for the given derive data and remote type.
133
///
134
/// # Note to Developers
135
///
136
/// The `ReflectRemote` trait could likely be made with default method implementations.
137
/// However, this makes it really easy for a user to accidentally implement this trait in an unsafe way.
138
/// To prevent this, we instead generate the implementation through a macro using this function.
139
fn impl_reflect_remote(input: &ReflectDerive, remote_ty: &TypePath) -> proc_macro2::TokenStream {
140
let bevy_reflect_path = input.meta().bevy_reflect_path();
141
142
let type_path = input.meta().type_path();
143
let (impl_generics, ty_generics, where_clause) =
144
input.meta().type_path().generics().split_for_impl();
145
146
let where_reflect_clause = input
147
.where_clause_options()
148
.extend_where_clause(where_clause);
149
150
quote! {
151
// SAFE: The generated wrapper type is guaranteed to be valid and repr(transparent) over the remote type.
152
impl #impl_generics #bevy_reflect_path::ReflectRemote for #type_path #ty_generics #where_reflect_clause {
153
type Remote = #remote_ty;
154
155
fn as_remote(&self) -> &Self::Remote {
156
&self.0
157
}
158
fn as_remote_mut(&mut self) -> &mut Self::Remote {
159
&mut self.0
160
}
161
fn into_remote(self) -> Self::Remote
162
{
163
// SAFE: The wrapper type should be repr(transparent) over the remote type
164
unsafe {
165
// Unfortunately, we have to use `transmute_copy` to avoid a compiler error:
166
// ```
167
// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
168
// |
169
// | core::mem::transmute::<A, B>(a)
170
// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
171
// |
172
// = note: source type: `A` (this type does not have a fixed size)
173
// = note: target type: `B` (this type does not have a fixed size)
174
// ```
175
::core::mem::transmute_copy::<Self, Self::Remote>(
176
// `ManuallyDrop` is used to prevent double-dropping `self`
177
&::core::mem::ManuallyDrop::new(self)
178
)
179
}
180
}
181
182
fn as_wrapper(remote: &Self::Remote) -> &Self {
183
// SAFE: The wrapper type should be repr(transparent) over the remote type
184
unsafe { ::core::mem::transmute::<&Self::Remote, &Self>(remote) }
185
}
186
fn as_wrapper_mut(remote: &mut Self::Remote) -> &mut Self {
187
// SAFE: The wrapper type should be repr(transparent) over the remote type
188
unsafe { ::core::mem::transmute::<&mut Self::Remote, &mut Self>(remote) }
189
}
190
fn into_wrapper(remote: Self::Remote) -> Self
191
{
192
// SAFE: The wrapper type should be repr(transparent) over the remote type
193
unsafe {
194
// Unfortunately, we have to use `transmute_copy` to avoid a compiler error:
195
// ```
196
// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
197
// |
198
// | core::mem::transmute::<A, B>(a)
199
// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
200
// |
201
// = note: source type: `A` (this type does not have a fixed size)
202
// = note: target type: `B` (this type does not have a fixed size)
203
// ```
204
::core::mem::transmute_copy::<Self::Remote, Self>(
205
// `ManuallyDrop` is used to prevent double-dropping `self`
206
&::core::mem::ManuallyDrop::new(remote)
207
)
208
}
209
}
210
}
211
}
212
}
213
214
/// Generates compile-time assertions for remote fields.
215
///
216
/// This prevents types from using an invalid remote type.
217
/// this works by generating a struct, `RemoteFieldAssertions`, with methods that
218
/// will result in compile-time failure if types are mismatched.
219
/// The output of this function is best placed within an anonymous context to maintain hygiene.
220
///
221
/// # Example
222
///
223
/// The following would fail to compile due to an incorrect `#[reflect(remote = ...)]` value.
224
///
225
/// ```ignore
226
/// mod external_crate {
227
/// pub struct TheirFoo(pub u32);
228
/// pub struct TheirBar(pub i32);
229
/// }
230
///
231
/// #[reflect_remote(external_crate::TheirFoo)]
232
/// struct MyFoo(pub u32);
233
/// #[reflect_remote(external_crate::TheirBar)]
234
/// struct MyBar(pub i32);
235
///
236
/// #[derive(Reflect)]
237
/// struct MyStruct {
238
/// #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar`
239
/// foo: external_crate::TheirFoo
240
/// }
241
/// ```
242
pub(crate) fn generate_remote_assertions(
243
derive_data: &ReflectDerive,
244
) -> Option<proc_macro2::TokenStream> {
245
struct RemoteAssertionData<'a> {
246
ident: Member,
247
variant: Option<&'a Ident>,
248
ty: &'a Type,
249
generics: &'a Generics,
250
remote_ty: &'a Type,
251
}
252
253
let bevy_reflect_path = derive_data.meta().bevy_reflect_path();
254
255
let fields: Box<dyn Iterator<Item = RemoteAssertionData>> = match derive_data {
256
ReflectDerive::Struct(data)
257
| ReflectDerive::TupleStruct(data)
258
| ReflectDerive::UnitStruct(data) => Box::new(data.active_fields().filter_map(|field| {
259
field
260
.attrs
261
.remote
262
.as_ref()
263
.map(|remote_ty| RemoteAssertionData {
264
ident: as_member(field.data.ident.as_ref(), field.declaration_index),
265
variant: None,
266
ty: &field.data.ty,
267
generics: data.meta().type_path().generics(),
268
remote_ty,
269
})
270
})),
271
ReflectDerive::Enum(data) => Box::new(data.variants().iter().flat_map(|variant| {
272
variant.active_fields().filter_map(|field| {
273
field
274
.attrs
275
.remote
276
.as_ref()
277
.map(|remote_ty| RemoteAssertionData {
278
ident: as_member(field.data.ident.as_ref(), field.declaration_index),
279
variant: Some(&variant.data.ident),
280
ty: &field.data.ty,
281
generics: data.meta().type_path().generics(),
282
remote_ty,
283
})
284
})
285
})),
286
287
_ => return None,
288
};
289
290
let assertions = fields
291
.map(move |field| {
292
let ident = if let Some(variant) = field.variant {
293
format_ident!("{}__{}", variant, field.ident)
294
} else {
295
match field.ident {
296
Member::Named(ident) => ident,
297
Member::Unnamed(index) => format_ident!("field_{}", index),
298
}
299
};
300
let (impl_generics, _, where_clause) = field.generics.split_for_impl();
301
302
let where_reflect_clause = derive_data
303
.where_clause_options()
304
.extend_where_clause(where_clause);
305
306
let ty = &field.ty;
307
let remote_ty = field.remote_ty;
308
let assertion_ident = format_ident!("assert__{}__is_valid_remote", ident);
309
310
let span = create_assertion_span(remote_ty.span());
311
312
quote_spanned! {span=>
313
#[allow(non_snake_case)]
314
#[allow(clippy::multiple_bound_locations)]
315
fn #assertion_ident #impl_generics () #where_reflect_clause {
316
let _: <#remote_ty as #bevy_reflect_path::ReflectRemote>::Remote = (|| -> #FQOption<#ty> {
317
None
318
})().unwrap();
319
}
320
}
321
})
322
.collect::<proc_macro2::TokenStream>();
323
324
if assertions.is_empty() {
325
None
326
} else {
327
Some(quote! {
328
struct RemoteFieldAssertions;
329
330
impl RemoteFieldAssertions {
331
#assertions
332
}
333
})
334
}
335
}
336
337
/// Generates compile-time assertions that ensure a remote wrapper definition matches up with the
338
/// remote type it's wrapping.
339
///
340
/// Note: This currently results in "backwards" error messages like:
341
///
342
/// ```ignore
343
/// expected: <WRAPPER_FIELD_TYPE>
344
/// found: <REMOTE_FIELD_TYPE>
345
/// ```
346
///
347
/// Ideally it would be the other way around, but there's no easy way of doing this without
348
/// generating a copy of the struct/enum definition and using that as the base instead of the remote type.
349
fn generate_remote_definition_assertions(derive_data: &ReflectDerive) -> proc_macro2::TokenStream {
350
let meta = derive_data.meta();
351
let self_ident = format_ident!("__remote__");
352
let self_ty = derive_data.remote_ty().unwrap().type_path();
353
let self_expr_path = derive_data.remote_ty().unwrap().as_expr_path().unwrap();
354
let (impl_generics, _, where_clause) = meta.type_path().generics().split_for_impl();
355
356
let where_reflect_clause = derive_data
357
.where_clause_options()
358
.extend_where_clause(where_clause);
359
360
let assertions = match derive_data {
361
ReflectDerive::Struct(data)
362
| ReflectDerive::TupleStruct(data)
363
| ReflectDerive::UnitStruct(data) => {
364
let mut output = proc_macro2::TokenStream::new();
365
366
for field in data.fields() {
367
let field_member = as_member(field.data.ident.as_ref(), field.declaration_index);
368
let field_ty = &field.data.ty;
369
let span = create_assertion_span(field_ty.span());
370
371
output.extend(quote_spanned! {span=>
372
#self_ident.#field_member = (|| -> #FQOption<#field_ty> {None})().unwrap();
373
});
374
}
375
376
output
377
}
378
ReflectDerive::Enum(data) => {
379
let variants = data.variants().iter().map(|variant| {
380
let ident = &variant.data.ident;
381
382
let mut output = proc_macro2::TokenStream::new();
383
384
if variant.fields().is_empty() {
385
return quote!(#self_expr_path::#ident => {});
386
}
387
388
for field in variant.fields() {
389
let field_member =
390
as_member(field.data.ident.as_ref(), field.declaration_index);
391
let field_ident = format_ident!("field_{}", field_member);
392
let field_ty = &field.data.ty;
393
let span = create_assertion_span(field_ty.span());
394
395
output.extend(quote_spanned! {span=>
396
#self_expr_path::#ident {#field_member: mut #field_ident, ..} => {
397
#field_ident = (|| -> #FQOption<#field_ty> {None})().unwrap();
398
}
399
});
400
}
401
402
output
403
});
404
405
quote! {
406
match #self_ident {
407
#(#variants)*
408
}
409
}
410
}
411
ReflectDerive::Opaque(_) => {
412
// No assertions needed since there are no fields to check
413
proc_macro2::TokenStream::new()
414
}
415
};
416
417
quote! {
418
const _: () = {
419
#[allow(non_snake_case)]
420
#[allow(unused_variables)]
421
#[allow(unused_assignments)]
422
#[allow(unreachable_patterns)]
423
#[allow(clippy::multiple_bound_locations)]
424
fn assert_wrapper_definition_matches_remote_type #impl_generics (mut #self_ident: #self_ty) #where_reflect_clause {
425
#assertions
426
}
427
};
428
}
429
}
430
431
/// Creates a span located around the given one, but resolves to the assertion's context.
432
///
433
/// This should allow the compiler to point back to the line and column in the user's code,
434
/// while still attributing the error to the macro.
435
fn create_assertion_span(span: Span) -> Span {
436
Span::call_site().located_at(span)
437
}
438
439
/// A reflected type's remote type.
440
///
441
/// This is a wrapper around [`TypePath`] that allows it to be paired with other remote-specific logic.
442
#[derive(Copy, Clone)]
443
pub(crate) struct RemoteType<'a> {
444
path: &'a TypePath,
445
}
446
447
impl<'a> RemoteType<'a> {
448
pub fn new(path: &'a TypePath) -> Self {
449
Self { path }
450
}
451
452
/// Returns the [type path](TypePath) of this remote type.
453
pub fn type_path(&self) -> &'a TypePath {
454
self.path
455
}
456
457
/// Attempts to convert the [type path](TypePath) of this remote type into an [expression path](ExprPath).
458
///
459
/// For example, this would convert `foo::Bar<T>` into `foo::Bar::<T>` to be used as part of an expression.
460
///
461
/// This will return an error for types that are parenthesized, such as in `Fn() -> Foo`.
462
pub fn as_expr_path(&self) -> Result<ExprPath, syn::Error> {
463
let mut expr_path = self.path.clone();
464
if let Some(segment) = expr_path.path.segments.last_mut() {
465
match &mut segment.arguments {
466
PathArguments::None => {}
467
PathArguments::AngleBracketed(arg) => {
468
arg.colon2_token = Some(PathSep::default());
469
}
470
PathArguments::Parenthesized(arg) => {
471
return Err(syn::Error::new(
472
arg.span(),
473
"cannot use parenthesized type as remote type",
474
))
475
}
476
}
477
}
478
479
Ok(ExprPath {
480
path: expr_path.path,
481
qself: expr_path.qself,
482
attrs: Vec::new(),
483
})
484
}
485
}
486
487
/// Metadata from the arguments defined in the `reflect_remote` attribute.
488
///
489
/// The syntax for the arguments is: `#[reflect_remote(REMOTE_TYPE_PATH)]`.
490
struct RemoteArgs {
491
remote_ty: TypePath,
492
}
493
494
impl Parse for RemoteArgs {
495
fn parse(input: ParseStream) -> syn::Result<Self> {
496
Ok(Self {
497
remote_ty: input.parse()?,
498
})
499
}
500
}
501
502