1
+ #![ feature( backtrace) ]
1
2
extern crate proc_macro;
2
3
3
4
use quote:: { quote, quote_spanned} ;
4
5
use syn:: {
5
- parse_macro_input, parenthesized, parse_quote, DeriveInput , Data ,
6
- Error , Generics , GenericParam , TypeParamBound , Fields , Member ,
7
- Index , Type , GenericArgument , Attribute , PathArguments ,
6
+ parse_macro_input, parenthesized, parse_quote, DeriveInput ,
7
+ Data , Error , Generics , GenericParam , TypeParamBound , Fields ,
8
+ Member , Index , Type , GenericArgument , Attribute , PathArguments ,
9
+ Meta , NestedMeta , TypeParam , WherePredicate , PredicateType ,
10
+ Token
8
11
} ;
9
12
use proc_macro2:: { Ident , TokenStream , Span } ;
10
13
use syn:: spanned:: Spanned ;
11
14
use syn:: parse:: { ParseStream , Parse } ;
15
+ use std:: collections:: HashSet ;
16
+ use syn:: export:: fmt:: Display ;
17
+ use std:: io:: Write ;
12
18
13
19
struct MutableFieldOpts {
14
20
public : bool
@@ -91,6 +97,7 @@ impl Parse for GcFieldAttrs {
91
97
92
98
struct GcTypeAttrs {
93
99
is_copy : bool ,
100
+ ignore_params : HashSet < Ident >
94
101
}
95
102
impl GcTypeAttrs {
96
103
pub fn find ( attrs : & [ Attribute ] ) -> Result < Self , Error > {
@@ -107,6 +114,7 @@ impl Default for GcTypeAttrs {
107
114
fn default ( ) -> Self {
108
115
GcTypeAttrs {
109
116
is_copy : false ,
117
+ ignore_params : HashSet :: new ( )
110
118
}
111
119
}
112
120
}
@@ -116,14 +124,67 @@ impl Parse for GcTypeAttrs {
116
124
parenthesized ! ( input in raw_input) ;
117
125
let mut result = GcTypeAttrs :: default ( ) ;
118
126
while !input. is_empty ( ) {
119
- let flag_name = input. parse :: < Ident > ( ) ?;
120
- if flag_name == "copy" {
127
+ let meta = input. parse :: < Meta > ( ) ?;
128
+ if meta. path ( ) . is_ident ( "copy" ) {
129
+ if !matches ! ( meta, Meta :: Path ( _) ) {
130
+ return Err ( Error :: new (
131
+ meta. span ( ) ,
132
+ "Malformed attribute for #[zerogc(copy)]"
133
+ ) )
134
+ }
135
+ if result. is_copy {
136
+ return Err ( Error :: new (
137
+ meta. span ( ) ,
138
+ "Duplicate flags: #[zerogc(copy)]"
139
+ ) )
140
+ }
121
141
result. is_copy = true ;
142
+ } else if meta. path ( ) . is_ident ( "ignore_params" ) {
143
+ if !result. ignore_params . is_empty ( ) {
144
+ return Err ( Error :: new (
145
+ meta. span ( ) ,
146
+ "Duplicate flags: #[zerogc(ignore_params)]"
147
+ ) )
148
+ }
149
+ let list = match meta {
150
+ Meta :: List ( ref list) if list. nested . is_empty ( ) => {
151
+ return Err ( Error :: new (
152
+ list. span ( ) ,
153
+ "Empty list for #[zerogc(ignore_parameters)]"
154
+ ) )
155
+ }
156
+ Meta :: List ( list) => list,
157
+ _ => return Err ( Error :: new (
158
+ meta. span ( ) ,
159
+ "Expected a list attribute for #[zerogc(ignore_params)]"
160
+ ) )
161
+ } ;
162
+ for nested in list. nested {
163
+ match nested {
164
+ NestedMeta :: Meta ( Meta :: Path ( ref p) ) if
165
+ p. get_ident ( ) . is_some ( ) => {
166
+ let ident = p. get_ident ( ) . unwrap ( ) ;
167
+ if !result. ignore_params . insert ( ident. clone ( ) ) {
168
+ return Err ( Error :: new (
169
+ ident. span ( ) ,
170
+ "Duplicate parameter to ignore"
171
+ ) ) ;
172
+ }
173
+ }
174
+ _ => return Err ( Error :: new (
175
+ nested. span ( ) ,
176
+ "Invalid list value for #[zerogc(ignore_param)]"
177
+ ) )
178
+ }
179
+ }
122
180
} else {
123
181
return Err ( Error :: new (
124
- input . span ( ) , "Unknown type flag"
182
+ meta . span ( ) , "Unknown type flag"
125
183
) )
126
184
}
185
+ if input. peek ( Token ! [ , ] ) {
186
+ input. parse :: < Token ! [ , ] > ( ) ?;
187
+ }
127
188
}
128
189
Ok ( result)
129
190
}
@@ -132,20 +193,30 @@ impl Parse for GcTypeAttrs {
132
193
#[ proc_macro_derive( Trace , attributes( zerogc) ) ]
133
194
pub fn derive_trace ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
134
195
let input = parse_macro_input ! ( input as DeriveInput ) ;
135
- let trace_impl = impl_trace ( & input)
196
+ let attrs = match GcTypeAttrs :: find ( & * input. attrs ) {
197
+ Ok ( attrs) => attrs,
198
+ Err ( e) => return e. to_compile_error ( ) . into ( )
199
+ } ;
200
+ let trace_impl = impl_trace ( & input, & attrs)
136
201
. unwrap_or_else ( |e| e. to_compile_error ( ) ) ;
137
202
let brand_impl = impl_brand ( & input)
138
203
. unwrap_or_else ( |e| e. to_compile_error ( ) ) ;
139
- let gc_safe_impl = impl_gc_safe ( & input)
204
+ let gc_safe_impl = impl_gc_safe ( & input, & attrs )
140
205
. unwrap_or_else ( |e| e. to_compile_error ( ) ) ;
141
206
let extra_impls = impl_extras ( & input)
142
207
. unwrap_or_else ( |e| e. to_compile_error ( ) ) ;
143
- From :: from ( quote ! {
208
+ let t = From :: from ( quote ! {
144
209
#trace_impl
145
210
#brand_impl
146
211
#gc_safe_impl
147
212
#extra_impls
148
- } )
213
+ } ) ;
214
+ debug_derive (
215
+ "derive(Trace)" ,
216
+ & format_args ! ( "#[derive(Trace) for {}" , input. ident) ,
217
+ & t
218
+ ) ;
219
+ t
149
220
}
150
221
151
222
fn trace_fields ( fields : & Fields , access_ref : & mut dyn FnMut ( Member ) -> TokenStream ) -> TokenStream {
@@ -275,13 +346,22 @@ fn impl_brand(target: &DeriveInput) -> Result<TokenStream, Error> {
275
346
let name = & target. ident ;
276
347
let mut generics: Generics = target. generics . clone ( ) ;
277
348
let mut rewritten_params = Vec :: new ( ) ;
349
+ let mut rewritten_restrictions = Vec :: new ( ) ;
278
350
for param in & mut generics. params {
279
351
let rewritten_param: GenericArgument ;
280
352
match param {
281
353
GenericParam :: Type ( ref mut type_param) => {
354
+ let original_bounds = type_param. bounds . iter ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
282
355
type_param. bounds . push ( parse_quote ! ( :: zerogc:: GcBrand <' new_gc, S >) ) ;
283
356
let param_name = & type_param. ident ;
284
- rewritten_param = parse_quote ! ( <#param_name as :: zerogc:: GcBrand <' new_gc, S >:: Branded ) ;
357
+ let rewritten_type: Type = parse_quote ! ( <#param_name as :: zerogc:: GcBrand <' new_gc, S >>:: Branded ) ;
358
+ rewritten_restrictions. push ( WherePredicate :: Type ( PredicateType {
359
+ lifetimes : None ,
360
+ bounded_ty : rewritten_type. clone ( ) ,
361
+ colon_token : Default :: default ( ) ,
362
+ bounds : original_bounds. into_iter ( ) . collect ( )
363
+ } ) ) ;
364
+ rewritten_param = GenericArgument :: Type ( rewritten_type) ;
285
365
} ,
286
366
GenericParam :: Lifetime ( ref l) => {
287
367
/*
@@ -307,20 +387,22 @@ fn impl_brand(target: &DeriveInput) -> Result<TokenStream, Error> {
307
387
let mut impl_generics = generics. clone ( ) ;
308
388
impl_generics. params . push ( GenericParam :: Lifetime ( parse_quote ! ( ' new_gc) ) ) ;
309
389
impl_generics. params . push ( GenericParam :: Type ( parse_quote ! ( S : :: zerogc:: CollectorId ) ) ) ;
310
- let ( _, ty_generics, where_clause) = generics. split_for_impl ( ) ;
311
- let ( impl_generics, _, _) = impl_generics. split_for_impl ( ) ;
390
+ impl_generics. make_where_clause ( ) . predicates . extend ( rewritten_restrictions) ;
391
+ let ( _, ty_generics, _) = generics. split_for_impl ( ) ;
392
+ let ( impl_generics, _, where_clause) = impl_generics. split_for_impl ( ) ;
312
393
Ok ( quote ! {
313
394
unsafe impl #impl_generics :: zerogc:: GcBrand <' new_gc, S >
314
395
for #name #ty_generics #where_clause {
315
396
type Branded = #name:: <#( #rewritten_params) , * >;
316
397
}
317
398
} )
318
399
}
319
- fn impl_trace ( target : & DeriveInput ) -> Result < TokenStream , Error > {
400
+ fn impl_trace ( target : & DeriveInput , attrs : & GcTypeAttrs ) -> Result < TokenStream , Error > {
320
401
let name = & target. ident ;
321
- let generics = add_trait_bounds (
322
- & target. generics , parse_quote ! ( zerogc:: Trace )
323
- ) ;
402
+ let generics = add_trait_bounds_except (
403
+ & target. generics , parse_quote ! ( zerogc:: Trace ) ,
404
+ & attrs. ignore_params
405
+ ) ?;
324
406
let ( impl_generics, ty_generics, where_clause) = generics. split_for_impl ( ) ;
325
407
let field_types: Vec < & Type > ;
326
408
let trace_impl: TokenStream ;
@@ -399,12 +481,12 @@ fn impl_trace(target: &DeriveInput) -> Result<TokenStream, Error> {
399
481
}
400
482
} )
401
483
}
402
- fn impl_gc_safe ( target : & DeriveInput ) -> Result < TokenStream , Error > {
484
+ fn impl_gc_safe ( target : & DeriveInput , attrs : & GcTypeAttrs ) -> Result < TokenStream , Error > {
403
485
let name = & target. ident ;
404
- let generics = add_trait_bounds (
405
- & target. generics , parse_quote ! ( zerogc:: GcSafe )
406
- ) ;
407
- let attrs = GcTypeAttrs :: find ( & * target . attrs ) ?;
486
+ let generics = add_trait_bounds_except (
487
+ & target. generics , parse_quote ! ( zerogc:: GcSafe ) ,
488
+ & attrs . ignore_params
489
+ ) ?;
408
490
let ( impl_generics, ty_generics, where_clause) = generics. split_for_impl ( ) ;
409
491
let field_types: Vec < & Type > = match target. data {
410
492
Data :: Struct ( ref data) => {
@@ -468,12 +550,108 @@ fn impl_gc_safe(target: &DeriveInput) -> Result<TokenStream, Error> {
468
550
} )
469
551
}
470
552
471
- fn add_trait_bounds ( generics : & Generics , bound : TypeParamBound ) -> Generics {
553
+ fn add_trait_bounds_except (
554
+ generics : & Generics , bound : TypeParamBound ,
555
+ ignored_params : & HashSet < Ident >
556
+ ) -> Result < Generics , Error > {
557
+ let mut actually_ignored_args = HashSet :: < Ident > :: new ( ) ;
558
+ let generics = add_trait_bounds (
559
+ & generics, bound,
560
+ & mut |param : & TypeParam | {
561
+ if ignored_params. contains ( & param. ident ) {
562
+ actually_ignored_args. insert ( param. ident . clone ( ) ) ;
563
+ true
564
+ } else {
565
+ false
566
+ }
567
+ }
568
+ ) ;
569
+ if actually_ignored_args != * ignored_params {
570
+ let missing = ignored_params - & actually_ignored_args;
571
+ assert ! ( !missing. is_empty( ) ) ;
572
+ let mut combined_error: Option < Error > = None ;
573
+ for missing in missing {
574
+ let error = Error :: new (
575
+ missing. span ( ) ,
576
+ "Unknown parameter" ,
577
+ ) ;
578
+ match combined_error {
579
+ Some ( ref mut combined_error) => {
580
+ combined_error. combine ( error) ;
581
+ } ,
582
+ None => {
583
+ combined_error = Some ( error) ;
584
+ }
585
+ }
586
+ }
587
+ return Err ( combined_error. unwrap ( ) ) ;
588
+ }
589
+ Ok ( generics)
590
+ }
591
+
592
+ fn add_trait_bounds (
593
+ generics : & Generics , bound : TypeParamBound ,
594
+ should_ignore : & mut dyn FnMut ( & TypeParam ) -> bool
595
+ ) -> Generics {
472
596
let mut result: Generics = ( * generics) . clone ( ) ;
473
- for param in & mut result. params {
597
+ ' paramLoop : for param in & mut result. params {
474
598
if let GenericParam :: Type ( ref mut type_param) = * param {
599
+ if should_ignore ( type_param) {
600
+ continue ' paramLoop;
601
+ }
475
602
type_param. bounds . push ( bound. clone ( ) ) ;
476
603
}
477
604
}
478
605
result
479
606
}
607
+
608
+ fn debug_derive ( key : & str , message : & dyn Display , value : & dyn Display ) {
609
+ match :: std:: env:: var_os ( "DEBUG_DERIVE" ) {
610
+ Some ( var) if var == "*" ||
611
+ var. to_string_lossy ( ) . contains ( key) => {
612
+ // Enable this debug
613
+ } ,
614
+ _ => return ,
615
+ }
616
+ eprintln ! ( "{}:" , message) ;
617
+ use std:: process:: { Command , Stdio } ;
618
+ let original_input = format ! ( "{}" , value) ;
619
+ let cmd_res = Command :: new ( "rustfmt" )
620
+ . stdin ( Stdio :: piped ( ) )
621
+ . stdout ( Stdio :: piped ( ) )
622
+ . stderr ( Stdio :: piped ( ) )
623
+ . spawn ( )
624
+ . and_then ( |mut child| {
625
+ let mut stdin = child. stdin . take ( ) . unwrap ( ) ;
626
+ stdin. write_all ( original_input. as_bytes ( ) ) ?;
627
+ drop ( stdin) ;
628
+ child. wait_with_output ( )
629
+ } ) ;
630
+ match cmd_res {
631
+ Ok ( output) if output. status . success ( ) => {
632
+ let formatted = String :: from_utf8 ( output. stdout ) . unwrap ( ) ;
633
+ for line in formatted. lines ( ) {
634
+ eprintln ! ( " {}" , line) ;
635
+ }
636
+ } ,
637
+ // Fallthrough on failure
638
+ Ok ( output) => {
639
+ eprintln ! ( "Rustfmt error [code={}]:" , output. status. code( ) . map_or_else(
640
+ || String :: from( "?" ) ,
641
+ |i| format!( "{}" , i)
642
+ ) ) ;
643
+ let err_msg = String :: from_utf8 ( output. stderr ) . unwrap ( ) ;
644
+ for line in err_msg. lines ( ) {
645
+ eprintln ! ( " {}" , line) ;
646
+ }
647
+ eprintln ! ( "Original input: [[[[" ) ;
648
+ for line in original_input. lines ( ) {
649
+ eprintln ! ( "{}" , line) ;
650
+ }
651
+ eprintln ! ( "]]]]" ) ;
652
+ }
653
+ Err ( e) => {
654
+ eprintln ! ( "Failed to run rustfmt: {}" , e)
655
+ }
656
+ }
657
+ }
0 commit comments