5
5
extern crate proc_macro;
6
6
7
7
use quote:: { quote, quote_spanned} ;
8
- use syn:: { parse_macro_input, parenthesized, parse_quote, DeriveInput , Data , Error , Generics , GenericParam , TypeParamBound , Fields , Member , Index , Type , GenericArgument , Attribute , PathArguments , Meta , TypeParam , WherePredicate , PredicateType , Token , Lifetime , NestedMeta , Lit } ;
8
+ use syn:: { parse_macro_input, parenthesized, parse_quote, DeriveInput , Data , Error , Generics , GenericParam , TypeParamBound , Fields , Member , Index , Type , GenericArgument , Attribute , PathArguments , Meta , TypeParam , WherePredicate , PredicateType , Token , Lifetime , NestedMeta , Lit , Field } ;
9
9
use proc_macro2:: { Ident , TokenStream , Span } ;
10
10
use syn:: spanned:: Spanned ;
11
11
use syn:: parse:: { ParseStream , Parse } ;
@@ -65,23 +65,35 @@ impl Parse for MutableFieldOpts {
65
65
}
66
66
67
67
struct GcFieldAttrs {
68
- mutable : Option < MutableFieldOpts >
68
+ mutable : Option < MutableFieldOpts > ,
69
+ unsafe_skip_trace : bool
69
70
}
70
71
impl GcFieldAttrs {
71
72
pub fn find ( attrs : & [ Attribute ] ) -> Result < Self , Error > {
72
- attrs. iter ( ) . find_map ( |attr| {
73
+ let mut iter = attrs. iter ( ) . filter_map ( |attr| {
73
74
if attr. path . is_ident ( "zerogc" ) {
74
- Some ( syn:: parse2 :: < GcFieldAttrs > ( attr. tokens . clone ( ) ) )
75
+ Some ( ( attr , syn:: parse2 :: < GcFieldAttrs > ( attr. tokens . clone ( ) ) ) )
75
76
} else {
76
77
None
77
78
}
78
- } ) . unwrap_or_else ( || Ok ( GcFieldAttrs :: default ( ) ) )
79
+ } ) ;
80
+ match ( iter. next ( ) , iter. next ( ) ) {
81
+ ( Some ( ( _, parsed) ) , None ) => {
82
+ parsed
83
+ } ,
84
+ ( Some ( _) , Some ( ( dup_attr, _) ) ) => {
85
+ Err ( Error :: new ( dup_attr. span ( ) , "Duplicate #[zerogc] attr" ) )
86
+ } ,
87
+ ( None , None ) => Ok ( GcFieldAttrs :: default ( ) ) ,
88
+ ( None , Some ( _) ) => unreachable ! ( )
89
+ }
79
90
}
80
91
}
81
92
impl Default for GcFieldAttrs {
82
93
fn default ( ) -> Self {
83
94
GcFieldAttrs {
84
- mutable : None
95
+ mutable : None ,
96
+ unsafe_skip_trace : false
85
97
}
86
98
}
87
99
}
@@ -91,8 +103,9 @@ impl Parse for GcFieldAttrs {
91
103
parenthesized ! ( input in raw_input) ;
92
104
let mut result = GcFieldAttrs :: default ( ) ;
93
105
while !input. is_empty ( ) {
94
- let flag_name = input. parse :: < Ident > ( ) ?;
106
+ let flag_name = input. fork ( ) . parse :: < Ident > ( ) ?;
95
107
if flag_name == "mutable" {
108
+ input. parse :: < Ident > ( ) . unwrap ( ) ; // Actually advance
96
109
if input. peek ( syn:: token:: Paren ) {
97
110
let mut_opts;
98
111
parenthesized ! ( mut_opts in input) ;
@@ -105,9 +118,27 @@ impl Parse for GcFieldAttrs {
105
118
} else {
106
119
result. mutable = Some ( MutableFieldOpts :: default ( ) ) ;
107
120
}
121
+ continue
122
+ }
123
+ let meta = input. parse :: < Meta > ( ) ?;
124
+ if meta. path ( ) . is_ident ( "unsafe_skip_trace" ) {
125
+ if !matches ! ( meta, Meta :: Path ( _) ) {
126
+ return Err ( Error :: new (
127
+ meta. span ( ) ,
128
+ "Malformed attribute for #[zerogc(unsafe_skip_trace)]"
129
+ ) )
130
+ }
131
+ if result. unsafe_skip_trace {
132
+ return Err ( Error :: new (
133
+ meta. span ( ) ,
134
+ "Duplicate flags: #[zerogc(unsafe_skip_trace)]"
135
+ ) )
136
+ }
137
+ result. unsafe_skip_trace = true ;
108
138
} else {
109
139
return Err ( Error :: new (
110
- input. span ( ) , "Unknown field flag"
140
+ meta. path ( ) . span ( ) ,
141
+ "Unknown field flag"
111
142
) )
112
143
}
113
144
if input. peek ( Token ! [ , ] ) {
@@ -122,6 +153,10 @@ struct GcTypeInfo {
122
153
config : TypeAttrs ,
123
154
}
124
155
impl GcTypeInfo {
156
+ fn is_ignored_param ( & self , param : & TypeParam ) -> bool {
157
+ self . config . ignore_params . contains ( & param. ident ) ||
158
+ Some ( & param. ident ) == self . config . collector_id . as_ref ( )
159
+ }
125
160
fn parse ( input : & DeriveInput ) -> Result < GcTypeInfo , Error > {
126
161
let config = TypeAttrs :: find ( & * input. attrs ) ?;
127
162
if config. ignored_lifetimes . contains ( & config. gc_lifetime ( ) ) {
@@ -142,7 +177,9 @@ struct TypeAttrs {
142
177
ignored_lifetimes : HashSet < Lifetime > ,
143
178
/// Unsafely assume the type is safe to [drop]
144
179
/// from a GC, as consistent with the requirements of [GcSafe]
145
- unsafe_drop_safe : bool
180
+ ///
181
+ /// This 'skips' the generation of a dummy drop
182
+ unsafe_skip_drop : bool
146
183
}
147
184
impl TypeAttrs {
148
185
fn gc_lifetime ( & self ) -> Lifetime {
@@ -152,13 +189,23 @@ impl TypeAttrs {
152
189
}
153
190
}
154
191
pub fn find ( attrs : & [ Attribute ] ) -> Result < Self , Error > {
155
- attrs. iter ( ) . find_map ( |attr| {
192
+ let mut iter = attrs. iter ( ) . filter_map ( |attr| {
156
193
if attr. path . is_ident ( "zerogc" ) {
157
- Some ( syn:: parse2 :: < TypeAttrs > ( attr. tokens . clone ( ) ) )
194
+ Some ( ( attr , syn:: parse2 :: < TypeAttrs > ( attr. tokens . clone ( ) ) ) )
158
195
} else {
159
196
None
160
197
}
161
- } ) . unwrap_or_else ( || Ok ( TypeAttrs :: default ( ) ) )
198
+ } ) ;
199
+ match ( iter. next ( ) , iter. next ( ) ) {
200
+ ( Some ( _) , Some ( ( dup_attr, _) ) ) => {
201
+ return Err ( Error :: new ( dup_attr. path . span ( ) , "Duplicate #[zerogc] attribute" ) )
202
+ } ,
203
+ ( Some ( ( _, parsed) ) , None ) => {
204
+ parsed
205
+ }
206
+ ( None , None ) => Ok ( TypeAttrs :: default ( ) ) ,
207
+ ( None , Some ( _) ) => unreachable ! ( )
208
+ }
162
209
}
163
210
}
164
211
impl Default for TypeAttrs {
@@ -170,6 +217,7 @@ impl Default for TypeAttrs {
170
217
collector_id : None ,
171
218
ignore_params : Default :: default ( ) ,
172
219
ignored_lifetimes : Default :: default ( ) ,
220
+ unsafe_skip_drop : false
173
221
}
174
222
}
175
223
}
@@ -208,20 +256,20 @@ impl Parse for TypeAttrs {
208
256
) )
209
257
}
210
258
result. is_copy = true ;
211
- } else if meta. path ( ) . is_ident ( "unsafe_drop_safe " ) {
259
+ } else if meta. path ( ) . is_ident ( "unsafe_skip_drop " ) {
212
260
if !matches ! ( meta, Meta :: Path ( _) ) {
213
261
return Err ( Error :: new (
214
262
meta. span ( ) ,
215
- "Malformed attribute for #[zerogc(unsafe_drop_safe )]"
263
+ "Malformed attribute for #[zerogc(unsafe_skip_drop )]"
216
264
) )
217
265
}
218
- if result. unsafe_drop_safe {
266
+ if result. unsafe_skip_drop {
219
267
return Err ( Error :: new (
220
268
meta. span ( ) ,
221
- "Duplicate flags: #[zerogc(unsafe_drop_safe )]"
269
+ "Duplicate flags: #[zerogc(unsafe_skip_drop )]"
222
270
) )
223
271
}
224
- result. unsafe_drop_safe = true ;
272
+ result. unsafe_skip_drop = true ;
225
273
} else if meta. path ( ) . is_ident ( "nop_trace" ) {
226
274
if !matches ! ( meta, Meta :: Path ( _) ) {
227
275
return Err ( Error :: new (
@@ -444,7 +492,7 @@ fn impl_derive_trace(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
444
492
} )
445
493
}
446
494
447
- fn trace_fields ( fields : & Fields , access_ref : & mut dyn FnMut ( Member ) -> TokenStream ) -> TokenStream {
495
+ fn trace_fields ( fields : & Fields , access_ref : & mut dyn FnMut ( Member ) -> TokenStream ) -> Result < TokenStream , Error > {
448
496
let zerogc_crate = zerogc_crate ( ) ;
449
497
// TODO: Detect if we're a unit struct and implement `NullTrace`
450
498
let mut result = Vec :: new ( ) ;
@@ -453,9 +501,13 @@ fn trace_fields(fields: &Fields, access_ref: &mut dyn FnMut(Member) -> TokenStre
453
501
Some ( ref ident) => Member :: Named ( ident. clone ( ) ) ,
454
502
None => Member :: Unnamed ( Index :: from ( index) )
455
503
} ) ;
504
+ let attrs = GcFieldAttrs :: find ( & field. attrs ) ?;
505
+ if attrs. unsafe_skip_trace {
506
+ continue // Skip
507
+ }
456
508
result. push ( quote ! ( #zerogc_crate:: Trace :: visit( #val, & mut * visitor) ?) ) ;
457
509
}
458
- quote ! ( #( #result; ) * )
510
+ Ok ( quote ! ( #( #result; ) * ) )
459
511
}
460
512
461
513
/// Implement extra methods
@@ -641,10 +693,15 @@ fn impl_erase(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
641
693
let rewritten_param: GenericArgument ;
642
694
match param {
643
695
GenericParam :: Type ( ref mut type_param) => {
696
+ let param_name = & type_param. ident ;
697
+ if info. is_ignored_param ( & * type_param) {
698
+ let param_name = & type_param. ident ;
699
+ rewritten_params. push ( parse_quote ! ( #param_name) ) ;
700
+ continue
701
+ }
644
702
let original_bounds = type_param. bounds . iter ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
645
703
type_param. bounds . push ( parse_quote ! ( #zerogc_crate:: GcErase <' min, #collector_id>) ) ;
646
704
type_param. bounds . push ( parse_quote ! ( ' min) ) ;
647
- let param_name = & type_param. ident ;
648
705
let rewritten_type: Type = parse_quote ! ( <#param_name as #zerogc_crate:: GcErase <' min, #collector_id>>:: Erased ) ;
649
706
rewritten_restrictions. push ( WherePredicate :: Type ( PredicateType {
650
707
lifetimes : None ,
@@ -785,9 +842,13 @@ fn impl_rebrand(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream,
785
842
let rewritten_param: GenericArgument ;
786
843
match param {
787
844
GenericParam :: Type ( ref mut type_param) => {
845
+ let param_name = & type_param. ident ;
846
+ if info. is_ignored_param ( & * type_param) {
847
+ rewritten_params. push ( parse_quote ! ( #param_name) ) ;
848
+ continue
849
+ }
788
850
let original_bounds = type_param. bounds . iter ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
789
851
type_param. bounds . push ( parse_quote ! ( #zerogc_crate:: GcRebrand <' new_gc, #collector_id>) ) ;
790
- let param_name = & type_param. ident ;
791
852
let rewritten_type: Type = parse_quote ! ( <#param_name as #zerogc_crate:: GcRebrand <' new_gc, #collector_id>>:: Branded ) ;
792
853
rewritten_restrictions. push ( WherePredicate :: Type ( PredicateType {
793
854
lifetimes : None ,
@@ -864,19 +925,19 @@ fn impl_trace(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
864
925
& info. config . ignore_params , None
865
926
) ?;
866
927
let ( impl_generics, ty_generics, where_clause) = generics. split_for_impl ( ) ;
867
- let field_types : Vec < & Type > ;
928
+ let fields : Vec < & Field > ;
868
929
let trace_impl: TokenStream ;
869
930
match target. data {
870
931
Data :: Struct ( ref data) => {
871
932
trace_impl = trace_fields (
872
933
& data. fields ,
873
934
& mut |member| quote ! ( & mut self . #member)
874
- ) ;
875
- field_types = data. fields . iter ( ) . map ( |f| & f . ty ) . collect ( ) ;
935
+ ) ? ;
936
+ fields = data. fields . iter ( ) . collect ( ) ;
876
937
} ,
877
938
Data :: Enum ( ref data) => {
878
- field_types = data. variants . iter ( )
879
- . flat_map ( |var| var. fields . iter ( ) . map ( |f| & f . ty ) )
939
+ fields = data. variants . iter ( )
940
+ . flat_map ( |var| var. fields . iter ( ) )
880
941
. collect ( ) ;
881
942
let mut match_arms = Vec :: new ( ) ;
882
943
for variant in & data. variants {
@@ -895,7 +956,7 @@ fn impl_trace(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
895
956
} ;
896
957
quote ! ( #ident)
897
958
}
898
- ) ;
959
+ ) ? ;
899
960
let pattern = match variant. fields {
900
961
Fields :: Named ( ref fields) => {
901
962
let names = fields. named . iter ( )
@@ -925,9 +986,20 @@ fn impl_trace(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
925
986
) ) ;
926
987
} ,
927
988
}
989
+ let fields = fields. into_iter ( )
990
+ . map ( |field| Ok ( ( field, GcFieldAttrs :: find ( & field. attrs ) ?) ) )
991
+ . collect :: < Result < Vec < _ > , Error > > ( ) ?;
992
+ let fields_need_trace = fields. iter ( )
993
+ . filter_map ( |( field, attrs) | {
994
+ if attrs. unsafe_skip_trace {
995
+ return None
996
+ }
997
+ let field_type = & field. ty ;
998
+ Some ( quote_spanned ! ( field_type. span( ) => <#field_type as #zerogc_crate:: Trace >:: NEEDS_TRACE ) )
999
+ } ) ;
928
1000
Ok ( quote ! {
929
1001
unsafe impl #impl_generics #zerogc_crate:: Trace for #name #ty_generics #where_clause {
930
- const NEEDS_TRACE : bool = false #( || <#field_types as #zerogc_crate :: Trace > :: NEEDS_TRACE ) * ;
1002
+ const NEEDS_TRACE : bool = false #( || #fields_need_trace ) * ;
931
1003
932
1004
/*
933
1005
* The inline annotation adds this function's MIR to the metadata.
@@ -957,15 +1029,13 @@ fn impl_gc_safe(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream,
957
1029
} )
958
1030
) ?;
959
1031
let ( impl_generics, ty_generics, where_clause) = generics. split_for_impl ( ) ;
960
- let field_types : Vec < & Type > = match target. data {
1032
+ let target_fields : Vec < & Field > = match target. data {
961
1033
Data :: Struct ( ref data) => {
962
- data. fields . iter ( )
963
- . map ( |f| & f. ty )
964
- . collect ( )
1034
+ data. fields . iter ( ) . collect ( )
965
1035
} ,
966
1036
Data :: Enum ( ref data) => {
967
1037
data. variants . iter ( )
968
- . flat_map ( |v| v. fields . iter ( ) . map ( |f| & f . ty ) )
1038
+ . flat_map ( |v| v. fields . iter ( ) )
969
1039
. collect ( )
970
1040
} ,
971
1041
Data :: Union ( _) => {
@@ -974,12 +1044,21 @@ fn impl_gc_safe(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream,
974
1044
) )
975
1045
} ,
976
1046
} ;
1047
+ let target_fields = target_fields. into_iter ( )
1048
+ . map ( |field| Ok ( ( field, GcFieldAttrs :: find ( & field. attrs ) ?) ) )
1049
+ . collect :: < Result < Vec < _ > , Error > > ( ) ?;
977
1050
let does_need_drop = if info. config . is_copy {
978
1051
// If we're proven to be a copy type we don't need to be dropped
979
1052
quote ! ( false )
980
1053
} else {
1054
+ let drop_assertions = target_fields. iter ( ) . filter_map ( |( field, attrs) | {
1055
+ if attrs. unsafe_skip_trace { return None }
1056
+ let span = field. ty . span ( ) ;
1057
+ let field_type = & field. ty ;
1058
+ Some ( quote_spanned ! ( span => <#field_type as #zerogc_crate:: GcSafe >:: NEEDS_DROP ) )
1059
+ } ) ;
981
1060
// We need to be dropped if any of our fields need to be dropped
982
- quote ! ( false #( || <#field_types as #zerogc_crate :: GcSafe > :: NEEDS_DROP ) * )
1061
+ quote ! ( false #( || #drop_assertions ) * )
983
1062
} ;
984
1063
let fake_drop_impl = if info. config . is_copy {
985
1064
/*
@@ -990,7 +1069,9 @@ fn impl_gc_safe(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream,
990
1069
* in the first place.
991
1070
*/
992
1071
quote ! ( )
993
- } else if !info. config . unsafe_drop_safe {
1072
+ } else if info. config . unsafe_skip_drop {
1073
+ quote ! ( ) // Skip generating drop at user's request
1074
+ } else {
994
1075
quote ! ( impl #impl_generics Drop for #name #ty_generics #where_clause {
995
1076
#[ inline]
996
1077
fn drop( & mut self ) {
@@ -1004,7 +1085,13 @@ fn impl_gc_safe(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream,
1004
1085
let verify_gc_safe = if info. config . is_copy {
1005
1086
quote ! ( #zerogc_crate:: assert_copy:: <Self >( ) )
1006
1087
} else {
1007
- quote ! ( #( <#field_types as #zerogc_crate:: GcSafe >:: assert_gc_safe( ) ; ) * )
1088
+ let field_assertions = target_fields. iter ( ) . filter_map ( |( field, attrs) | {
1089
+ if attrs. unsafe_skip_trace { return None }
1090
+ let span = field. ty . span ( ) ;
1091
+ let field_type = & field. ty ;
1092
+ Some ( quote_spanned ! ( span => <#field_type as #zerogc_crate:: GcSafe >:: assert_gc_safe( ) ) )
1093
+ } ) ;
1094
+ quote ! ( #( #field_assertions; ) * )
1008
1095
} ;
1009
1096
Ok ( quote ! {
1010
1097
unsafe impl #impl_generics #zerogc_crate:: GcSafe
@@ -1028,14 +1115,14 @@ fn impl_nop_trace(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream
1028
1115
& info. config . ignore_params , None
1029
1116
) ?;
1030
1117
let ( impl_generics, ty_generics, where_clause) = generics. split_for_impl ( ) ;
1031
- let field_types : Vec < & Type > ;
1118
+ let target_fields : Vec < & Field > ;
1032
1119
match target. data {
1033
1120
Data :: Struct ( ref data) => {
1034
- field_types = data. fields . iter ( ) . map ( |f| & f . ty ) . collect ( ) ;
1121
+ target_fields = data. fields . iter ( ) . collect ( ) ;
1035
1122
} ,
1036
1123
Data :: Enum ( ref data) => {
1037
- field_types = data. variants . iter ( )
1038
- . flat_map ( |var| var. fields . iter ( ) . map ( |f| & f . ty ) )
1124
+ target_fields = data. variants . iter ( )
1125
+ . flat_map ( |var| var. fields . iter ( ) )
1039
1126
. collect ( ) ;
1040
1127
} ,
1041
1128
Data :: Union ( _) => {
@@ -1046,17 +1133,22 @@ fn impl_nop_trace(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream
1046
1133
} ,
1047
1134
}
1048
1135
// TODO: We should have some sort of const-assertion for this....
1049
- let trace_assertions = field_types. iter ( )
1050
- . map ( |& t| {
1136
+ let trace_assertions = target_fields. iter ( )
1137
+ . map ( |& field| {
1138
+ let field_attrs = GcFieldAttrs :: find ( & field. attrs ) ?;
1139
+ let t = & field. ty ;
1140
+ if field_attrs. unsafe_skip_trace {
1141
+ return Ok ( quote ! ( ) ) ;
1142
+ }
1051
1143
let ty_span = t. span ( ) ;
1052
- quote_spanned ! { ty_span =>
1144
+ Ok ( quote_spanned ! { ty_span =>
1053
1145
assert!(
1054
1146
!<#t as #zerogc_crate:: Trace >:: NEEDS_TRACE ,
1055
1147
"Can't #[derive(NullTrace) with {}" ,
1056
1148
stringify!( #t)
1057
1149
) ;
1058
- }
1059
- } ) . collect :: < Vec < _ > > ( ) ;
1150
+ } )
1151
+ } ) . collect :: < Result < Vec < _ > , Error > > ( ) ? ;
1060
1152
Ok ( quote ! {
1061
1153
unsafe impl #impl_generics #zerogc_crate:: Trace for #name #ty_generics #where_clause {
1062
1154
const NEEDS_TRACE : bool = false ;
0 commit comments