@@ -36,10 +36,11 @@ pub fn derive_type_layout(input: TokenStream) -> TokenStream {
36
36
let Attributes {
37
37
reprs,
38
38
extra_bounds,
39
- } = parse_attributes ( & input. attrs , & mut type_params) ;
39
+ ground,
40
+ } = parse_attributes ( & input. attrs , & mut type_params, & input. data ) ;
40
41
41
42
let layout = layout_of_type ( & ty_name, & ty_generics, & input. data , & reprs) ;
42
- let uninit = uninit_for_type ( & ty_name, & input. data ) ;
43
+ let uninit = uninit_for_type ( & ty_name, & input. data , & ground ) ;
43
44
44
45
let inner_types = extract_inner_types ( & input. data ) ;
45
46
@@ -95,15 +96,49 @@ pub fn derive_type_layout(input: TokenStream) -> TokenStream {
95
96
struct Attributes {
96
97
reprs : String ,
97
98
extra_bounds : Vec < syn:: WherePredicate > ,
99
+ ground : Vec < syn:: Ident > ,
98
100
}
99
101
100
102
#[ allow( clippy:: too_many_lines) ]
101
- fn parse_attributes ( attrs : & [ syn:: Attribute ] , type_params : & mut Vec < & syn:: Ident > ) -> Attributes {
103
+ fn parse_attributes (
104
+ attrs : & [ syn:: Attribute ] ,
105
+ type_params : & mut Vec < & syn:: Ident > ,
106
+ data : & syn:: Data ,
107
+ ) -> Attributes {
102
108
// Could parse based on https://github.com/rust-lang/rust/blob/d13e8dd41d44a73664943169d5b7fe39b22c449f/compiler/rustc_attr/src/builtin.rs#L772-L781 instead
103
109
let mut reprs = Vec :: new ( ) ;
104
110
105
111
let mut extra_bounds: Vec < syn:: WherePredicate > = Vec :: new ( ) ;
106
112
113
+ let mut ground = match data {
114
+ syn:: Data :: Struct ( _) => Vec :: new ( ) ,
115
+ syn:: Data :: Enum ( syn:: DataEnum { variants, .. } ) => {
116
+ let mut ground = Vec :: with_capacity ( variants. len ( ) ) ;
117
+
118
+ for variant in variants {
119
+ if matches ! ( variant. fields, syn:: Fields :: Unit ) {
120
+ ground. push ( variant. ident . clone ( ) ) ;
121
+ }
122
+ }
123
+
124
+ for variant in variants {
125
+ if !matches ! ( variant. fields, syn:: Fields :: Unit ) {
126
+ ground. push ( variant. ident . clone ( ) ) ;
127
+ }
128
+ }
129
+
130
+ ground
131
+ } ,
132
+ syn:: Data :: Union ( syn:: DataUnion {
133
+ fields : syn:: FieldsNamed { named : fields, .. } ,
134
+ ..
135
+ } ) => fields
136
+ . iter ( )
137
+ . map ( |field| field. ident . clone ( ) . unwrap ( ) )
138
+ . collect ( ) ,
139
+ } ;
140
+ let mut groundier = Vec :: with_capacity ( ground. len ( ) ) ;
141
+
107
142
for attr in attrs {
108
143
if attr. path . is_ident ( "repr" ) {
109
144
if let Ok ( syn:: Meta :: List ( syn:: MetaList { nested, .. } ) ) = attr. parse_meta ( ) {
@@ -161,10 +196,63 @@ fn parse_attributes(attrs: &[syn::Attribute], type_params: &mut Vec<&syn::Ident>
161
196
err
162
197
) ,
163
198
}
199
+ } else if path. is_ident ( "ground" ) {
200
+ match syn:: parse_str ( & s. value ( ) ) {
201
+ Ok ( g) => match data {
202
+ syn:: Data :: Struct ( _) => emit_error ! (
203
+ path. span( ) ,
204
+ "[const-type-layout]: Invalid #[layout(ground)] \
205
+ attribute: structs do not have a ground layout."
206
+ ) ,
207
+ syn:: Data :: Union ( _) | syn:: Data :: Enum ( _) => {
208
+ let g: syn:: Ident = g;
209
+
210
+ if let Some ( i) = ground. iter ( ) . position ( |e| e == & g) {
211
+ let g = ground. remove ( i) ;
212
+ groundier. push ( g) ;
213
+ } else if groundier. contains ( & g) {
214
+ emit_error ! (
215
+ path. span( ) ,
216
+ "[const-type-layout]: Duplicate #[layout(ground = \
217
+ \" {}\" )] attribute.",
218
+ g
219
+ ) ;
220
+ } else {
221
+ emit_error ! (
222
+ path. span( ) ,
223
+ "[const-type-layout]: Invalid #[layout(ground)] \
224
+ attribute: \" {}\" is not a {} in this {}.",
225
+ g,
226
+ match data {
227
+ syn:: Data :: Enum ( _) => "variant" ,
228
+ syn:: Data :: Struct ( _) | syn:: Data :: Union ( _) =>
229
+ "field" ,
230
+ } ,
231
+ match data {
232
+ syn:: Data :: Enum ( _) => "enum" ,
233
+ syn:: Data :: Struct ( _) | syn:: Data :: Union ( _) =>
234
+ "union" ,
235
+ } ,
236
+ ) ;
237
+ }
238
+ } ,
239
+ } ,
240
+ Err ( err) => emit_error ! (
241
+ s. span( ) ,
242
+ "[const-type-layout]: Invalid #[layout(ground = \" {}\" )] \
243
+ attribute: {}.",
244
+ match data {
245
+ syn:: Data :: Enum ( _) => "variant" ,
246
+ syn:: Data :: Struct ( _) | syn:: Data :: Union ( _) => "field" ,
247
+ } ,
248
+ err
249
+ ) ,
250
+ }
164
251
} else {
165
252
emit_error ! (
166
253
path. span( ) ,
167
- "[const-type-layout]: Unknown attribute, use `bound` or `free`."
254
+ "[const-type-layout]: Unknown attribute, use `bound`, `free`, or \
255
+ `ground`."
168
256
) ;
169
257
}
170
258
} else {
@@ -193,9 +281,12 @@ fn parse_attributes(attrs: &[syn::Attribute], type_params: &mut Vec<&syn::Ident>
193
281
. intersperse ( String :: from ( "," ) )
194
282
. collect :: < String > ( ) ;
195
283
284
+ groundier. extend ( ground) ;
285
+
196
286
Attributes {
197
287
reprs,
198
288
extra_bounds,
289
+ ground : groundier,
199
290
}
200
291
}
201
292
@@ -587,7 +678,11 @@ fn quote_discriminant(
587
678
}
588
679
589
680
#[ allow( clippy:: too_many_lines) ]
590
- fn uninit_for_type ( ty_name : & syn:: Ident , data : & syn:: Data ) -> proc_macro2:: TokenStream {
681
+ fn uninit_for_type (
682
+ ty_name : & syn:: Ident ,
683
+ data : & syn:: Data ,
684
+ ground : & [ syn:: Ident ] ,
685
+ ) -> proc_macro2:: TokenStream {
591
686
match data {
592
687
syn:: Data :: Struct ( data) => {
593
688
// Structs are uninhabited if any of their fields in uninhabited
@@ -656,7 +751,7 @@ fn uninit_for_type(ty_name: &syn::Ident, data: &syn::Data) -> proc_macro2::Token
656
751
// (2) tuple and struct variants are uninhabited
657
752
// if any of their fields are uninhabited
658
753
659
- let variant_initialisers = variants. iter ( ) . map ( |syn:: Variant {
754
+ let variant_initialisers = ground . iter ( ) . filter_map ( |g| variants. iter ( ) . find ( |v| & v . ident == g ) ) . map ( |syn:: Variant {
660
755
ident : variant_name,
661
756
fields : variant_fields,
662
757
..
@@ -732,8 +827,9 @@ fn uninit_for_type(ty_name: &syn::Ident, data: &syn::Data) -> proc_macro2::Token
732
827
} ) => {
733
828
// Unions are uninhabited if all fields are uninhabited
734
829
735
- let ( field_names, field_initialisers) = fields
830
+ let ( field_names, field_initialisers) = ground
736
831
. iter ( )
832
+ . filter_map ( |g| fields. iter ( ) . find ( |f| f. ident . as_ref ( ) == Some ( g) ) )
737
833
. map (
738
834
|syn:: Field {
739
835
ident : field_name,
0 commit comments