Skip to content

Commit bf2c667

Browse files
committed
Support tuple-structs for #[derive(StaticReflect)]
Also support tuple-fields for `field_offset!`
1 parent f00ca1b commit bf2c667

File tree

6 files changed

+179
-41
lines changed

6 files changed

+179
-41
lines changed

lib/derive-internals/src/fields.rs

Lines changed: 88 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use quote::quote;
1+
use quote::{quote, format_ident};
22
use syn::{parenthesized, Token, parse_quote, DeriveInput, Data, Generics, GenericParam, TypeParamBound, DataEnum, DataStruct, DataUnion, Type};
33
use proc_macro2::{TokenStream, Ident, Span};
44
use syn::parse::{self, Parse, ParseStream};
@@ -154,16 +154,24 @@ fn handle_type<'a, T: TypeHandler<'a>>(
154154
where_clause: TokenStream,
155155
extra_defs: &mut Vec<TokenStream>
156156
) -> Result<TokenStream, syn::Error> {
157-
let mut field_info: IndexMap<Ident, TokenStream> = IndexMap::new();
157+
let mut field_info: IndexMap<FieldName<'a>, TokenStream> = IndexMap::new();
158158
let mut field_associated_types = Vec::new();
159159
let mut field_defs = Vec::new();
160160
target.handle_fields(|field| {
161161
let field_name = field.name;
162162
let field_type = &field.static_type;
163163
field_info.insert(field_name.clone(), field.static_def.clone());
164-
field_associated_types.push(quote!(type #field_name = #field_type;));
164+
let associated_type_name = field_name.associated_type_name();
165+
field_associated_types.push(quote!(type #associated_type_name = #field_type;));
165166
let field_def_type = T::field_def_type(Some(quote!(#field_type)));
166-
field_defs.push(quote!(pub #field_name: #field_def_type,));
167+
match field_name {
168+
FieldName::Tuple { index: _ } => {
169+
field_defs.push(quote!(pub #field_def_type));
170+
}
171+
FieldName::Named { name } => {
172+
field_defs.push(quote!(pub #name: #field_def_type));
173+
}
174+
}
167175
})?;
168176
let field_info_struct_name = Ident::new(
169177
&format!("_FieldInfo{}", name),
@@ -173,43 +181,59 @@ fn handle_type<'a, T: TypeHandler<'a>>(
173181
&format!("_FieldTrait{}", name),
174182
name.span()
175183
);
176-
let field_names = field_info.keys().collect::<Vec<_>>();
177-
extra_defs.push(quote!(
178-
#[allow(missing_docs)]
179-
#[doc(hidden)]
180-
pub struct #field_info_struct_name {
181-
#(#field_defs)*
184+
let associated_type_names = field_info.keys().map(FieldName::associated_type_name);
185+
let field_info_struct_def = {
186+
let fields = quote!(#(#field_defs),*);
187+
let fields = if target.is_tuple_style() {
188+
// NOTE: I guess a tuple-struct needs a semicolon but a regular struct doesn't....
189+
quote!((#fields);)
190+
} else {
191+
quote!({ #fields })
192+
};
193+
quote! {
194+
#[allow(missing_docs)]
195+
#[doc(hidden)]
196+
pub struct #field_info_struct_name #fields
182197
}
198+
};
199+
extra_defs.push(quote!(
200+
#field_info_struct_def
183201
#[allow(non_camel_case_types)]
184202
#[doc(hidden)]
185203
trait #field_info_trait_name {
186-
#(type #field_names;)*
204+
#(type #associated_type_names;)*
187205
}
188206
#[allow(non_camel_case_types)]
189207
impl #impl_generics #field_info_trait_name for #name #ty_generics #where_clause {
190208
#(#field_associated_types)*
191209
}
192210
));
193211
let field_inits = field_info.iter()
194-
.map(|(name, def)| quote!(#name: #def,))
212+
.map(|(name, def)| match name {
213+
FieldName::Tuple { .. } => quote!(#def),
214+
FieldName::Named { name } => quote!(#name: #def)
215+
})
195216
.collect::<Vec<TokenStream>>();
217+
let field_inits = if target.is_tuple_style() {
218+
quote!((#(#field_inits,)*))
219+
} else {
220+
quote!({#(#field_inits,)*})
221+
};
196222
extra_defs.push(quote!(
197223
unsafe impl #impl_generics static_reflect::FieldReflect for #name #ty_generics #where_clause {
198224
type NamedFieldInfo = #field_info_struct_name;
199-
const NAMED_FIELD_INFO: Self::NamedFieldInfo = #field_info_struct_name {
200-
#(#field_inits)*
201-
};
225+
const NAMED_FIELD_INFO: Self::NamedFieldInfo = #field_info_struct_name #field_inits;
202226
}
203227
));
204-
let field_names = field_info.keys().collect::<Vec<_>>();
228+
let field_access = field_info.keys().map(|name| name.access()).collect::<Vec<_>>();
205229
let field_def_type_name = T::field_def_type(None);
206230
let type_def_type = T::type_def_type();
207231
let header = quote! {
208232
use static_reflect::{StaticReflect, FieldReflect};
209233
use static_reflect::types::TypeInfo;
210234
use #field_def_type_name;
211235
use #type_def_type;
212-
const _FIELDS: &'static [#field_def_type_name<'static>] = &[#(<#name as FieldReflect>::NAMED_FIELD_INFO.#field_names.erase()),*];
236+
const _FIELDS: &'static [#field_def_type_name<'static>] = &[#(<#name as FieldReflect>::NAMED_FIELD_INFO.#field_access.erase()),*];
213237
};
214238
let static_def = target.create_static_def(header);
215239
let into_type = T::def_into_type(quote!(_DEF));
@@ -232,14 +256,41 @@ fn enum_static_type(data: &DataEnum, name: &Ident) -> Result<TokenStream, syn::E
232256
}
233257
}
234258
trait TypeHandler<'a> {
259+
fn is_tuple_style(&self) -> bool;
235260
fn field_def_type(field_type: Option<TokenStream>) -> TokenStream;
236261
fn type_def_type() -> TokenStream;
237262
fn def_into_type(def_ref: TokenStream) -> TokenStream;
238263
fn handle_fields<F: FnMut(FieldInfo<'a>)>(&mut self, handler: F) -> syn::Result<()>;
239264
fn create_static_def(self, header: TokenStream) -> TokenStream;
240265
}
266+
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
267+
pub enum FieldName<'a> {
268+
Tuple {
269+
index: usize,
270+
},
271+
Named {
272+
name: &'a Ident
273+
}
274+
}
275+
impl FieldName<'_> {
276+
pub fn access(&self) -> TokenStream {
277+
match *self {
278+
FieldName::Tuple { index } => {
279+
let idx = syn::Index::from(index);
280+
quote!(#idx)
281+
},
282+
FieldName::Named { name } => quote!(#name),
283+
}
284+
}
285+
pub fn associated_type_name(&self) -> Ident {
286+
match *self {
287+
FieldName::Tuple { index } => format_ident!("_Tuple_{}", index),
288+
FieldName::Named { name } => name.clone()
289+
}
290+
}
291+
}
241292
struct FieldInfo<'a> {
242-
name: &'a Ident,
293+
name: FieldName<'a>,
243294
static_type: Type,
244295
static_def: TokenStream
245296
}
@@ -256,6 +307,9 @@ impl<'a> StructHandler<'a> {
256307
}
257308
}
258309
impl<'a> TypeHandler<'a> for StructHandler<'a> {
310+
fn is_tuple_style(&self) -> bool {
311+
matches!(self.data.fields, syn::Fields::Unnamed(_))
312+
}
259313

260314
fn field_def_type(field_type: Option<TokenStream>) -> TokenStream {
261315
match field_type {
@@ -274,15 +328,18 @@ impl<'a> TypeHandler<'a> for StructHandler<'a> {
274328

275329
fn handle_fields<F: FnMut(FieldInfo<'a>)>(&mut self, mut handler: F) -> syn::Result<()> {
276330
/*
277-
* NOTE: Layout algorithim for repr(C) given in reference
331+
* NOTE: Layout algorithm for repr(C) given in reference
278332
* https://doc.rust-lang.org/reference/type-layout.html#reprc-structs
279333
* We have to use recursion to compute offsets :(
280334
*/
281335
let mut current_offset = self.current_offset.clone();
282336
for (index, field) in self.data.fields.iter().enumerate() {
283337
let DeriveFieldOptions { opaque_array, assume_repr } =
284338
DeriveFieldOptions::parse_attrs(&field.attrs)?;
285-
let field_name = field.ident.as_ref().expect("Need named fields");
339+
let field_name = match field.ident {
340+
Some(ref name) => FieldName::Named { name },
341+
None => FieldName::Tuple { index }
342+
};
286343
let mut field_type = field.ty.clone();
287344
let original_type = field_type.clone();
288345
if opaque_array {
@@ -320,8 +377,12 @@ impl<'a> TypeHandler<'a> for StructHandler<'a> {
320377
let rem = old_offset % std::mem::align_of::<#original_type>();
321378
old_offset + (if rem == 0 { 0 } else { std::mem::align_of::<#original_type>() - rem })
322379
});
380+
let name_field_value = match field_name {
381+
FieldName::Tuple { .. } => quote!(None),
382+
FieldName::Named { name } => quote!(Some(stringify!(#name)))
383+
};
323384
let static_def = quote!(::static_reflect::types::FieldDef {
324-
name: stringify!(#field_name),
385+
name: #name_field_value,
325386
value_type: ::static_reflect::types::TypeId::<#field_type>::get(),
326387
offset: #current_offset,
327388
index: #index
@@ -378,6 +439,10 @@ struct UnionTypeHandler<'a> {
378439
name: &'a Ident
379440
}
380441
impl<'a> TypeHandler<'a> for UnionTypeHandler<'a> {
442+
fn is_tuple_style(&self) -> bool {
443+
false // unions can't have tuple-fields
444+
}
445+
381446
fn field_def_type(field_type: Option<TokenStream>) -> TokenStream {
382447
match field_type {
383448
None => quote!(static_reflect::types::UnionFieldDef),
@@ -395,7 +460,7 @@ impl<'a> TypeHandler<'a> for UnionTypeHandler<'a> {
395460

396461
fn handle_fields<F: FnMut(FieldInfo<'a>)>(&mut self, mut handler: F) -> syn::Result<()> {
397462
/*
398-
* NOTE: Layout algorithim for repr(C) given in reference
463+
* NOTE: Layout algorithm for repr(C) given in reference
399464
* https://doc.rust-lang.org/reference/type-layout.html#reprc-unions
400465
*
401466
* Unions are pretty simple since they're just glorified `mem::transmute`
@@ -420,7 +485,7 @@ impl<'a> TypeHandler<'a> for UnionTypeHandler<'a> {
420485
index: #index
421486
});
422487
handler(FieldInfo {
423-
name: field_name,
488+
name: FieldName::Named { name: field_name },
424489
static_type: field_type,
425490
static_def
426491
});

lib/derive/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ path = "../derive-internals"
2222
[dev-dependencies]
2323
# Testing
2424
static-reflect = { version = "0.1.0", path = "../.." }
25+
pretty_assertions = "0.7.2"

lib/derive/tests/simple.rs

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// RFC has been accepted
44
const_panic
55
)]
6+
use pretty_assertions::assert_eq;
67

78
use static_reflect::{field_offset, StaticReflect, FieldReflect};
89
use static_reflect::types::{TypeInfo, FieldDef, StructureDef, UnionDef, UnionFieldDef, TypeId};
@@ -96,56 +97,56 @@ fn test_struct_types() {
9697
});
9798
assert_eq!(Nested::TYPE_INFO, NESTED_TYPE);
9899
assert_eq!(Nested::NAMED_FIELD_INFO.cycle, FieldDef {
99-
name: "cycle",
100+
name: Some("cycle"),
100101
value_type: TypeId::<*mut SimpleStruct>::get(),
101102
offset: field_offset!(Nested, cycle),
102103
index: 0
103104
});
104105
assert_eq!(Nested::NAMED_FIELD_INFO.float, FieldDef {
105-
name: "float",
106+
name: Some("float"),
106107
value_type: TypeId::<f64>::get(),
107108
offset: field_offset!(Nested, float),
108109
index: 1
109110
});
110111
assert_eq!(Nested::NAMED_FIELD_INFO.number, FieldDef {
111-
name: "number",
112+
name: Some("number"),
112113
value_type: TypeId::<u64>::get(),
113114
offset: field_offset!(Nested, number),
114115
index: 2
115116
});
116117
let fields = vec![
117118
FieldDef {
118-
name: "text",
119+
name: Some("text"),
119120
value_type: TypeId::erased::<*mut String>(),
120121
offset: field_offset!(SimpleStruct, text),
121122
index: 0
122123
},
123124
FieldDef {
124-
name: "number",
125+
name: Some("number"),
125126
value_type: TypeId::erased::<u32>(),
126127
offset: field_offset!(SimpleStruct, number),
127128
index: 1
128129
},
129130
FieldDef {
130-
name: "float",
131+
name: Some("float"),
131132
value_type: TypeId::erased::<f64>(),
132133
offset: field_offset!(SimpleStruct, float),
133134
index: 2
134135
},
135136
FieldDef {
136-
name: "b",
137+
name: Some("b"),
137138
value_type: TypeId::erased::<bool>(),
138139
offset: field_offset!(SimpleStruct, b),
139140
index: 3
140141
},
141142
FieldDef {
142-
name: "unit",
143+
name: Some("unit"),
143144
value_type: TypeId::erased::<()>(),
144145
offset: field_offset!(SimpleStruct, unit),
145146
index: 4
146147
},
147148
FieldDef {
148-
name: "nested_struct",
149+
name: Some("nested_struct"),
149150
// NOTE: We already checked Nested::STATIC_TYPE
150151
value_type: TypeId::erased::<Nested>(),
151152
offset: field_offset!(SimpleStruct, nested_struct),
@@ -164,6 +165,49 @@ fn test_struct_types() {
164165
);
165166
}
166167

168+
169+
#[derive(StaticReflect)]
170+
#[repr(C)]
171+
struct SimpleTupleStruct(*mut String, f32, Nested);
172+
173+
#[test]
174+
fn test_tuple_struct() {
175+
let fields = vec![
176+
FieldDef {
177+
name: None,
178+
value_type: TypeId::erased::<*mut String>(),
179+
offset: field_offset!(SimpleTupleStruct, 0),
180+
index: 0
181+
},
182+
FieldDef {
183+
name: None,
184+
value_type: TypeId::erased::<f32>(),
185+
offset: field_offset!(SimpleTupleStruct, 1),
186+
index: 1
187+
},
188+
FieldDef {
189+
name: None,
190+
// NOTE: We already checked Nested::STATIC_TYPE
191+
value_type: TypeId::erased::<Nested>(),
192+
offset: field_offset!(SimpleTupleStruct, 2),
193+
index: 2
194+
},
195+
];
196+
assert_eq!(SimpleTupleStruct::NAMED_FIELD_INFO.0.erase(), fields[0]);
197+
assert_eq!(SimpleTupleStruct::NAMED_FIELD_INFO.1.erase(), fields[1]);
198+
assert_eq!(SimpleTupleStruct::NAMED_FIELD_INFO.2.erase(), fields[2]);
199+
let static_fields = leak_vec(fields);
200+
assert_eq!(
201+
SimpleTupleStruct::TYPE_INFO,
202+
TypeInfo::Structure(&StructureDef {
203+
name: "SimpleTupleStruct",
204+
fields: static_fields,
205+
size: size_of::<SimpleTupleStruct>(),
206+
alignment: align_of::<SimpleTupleStruct>(),
207+
})
208+
);
209+
}
210+
167211
#[derive(StaticReflect)]
168212
#[repr(C)]
169213
struct OpaqueArray {
@@ -186,13 +230,13 @@ fn test_options() {
186230
});
187231
assert_eq!(OPAQUE_ARRAY_TYPE, OpaqueArray::TYPE_INFO);
188232
assert_eq!(OpaqueArray::NAMED_FIELD_INFO.first, FieldDef {
189-
name: "first",
233+
name: Some("first"),
190234
value_type: TypeId::<i8>::get(), // It's actually a 'u8', but we assume_repr
191235
offset: field_offset!(OpaqueArray, first),
192236
index: 0
193237
});
194238
assert_eq!(OpaqueArray::NAMED_FIELD_INFO.array, FieldDef {
195-
name: "array",
239+
name: Some("array"),
196240
value_type: TypeId::<*mut String>::get(),
197241
offset: field_offset!(OpaqueArray, array),
198242
index: 1

0 commit comments

Comments
 (0)