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