Skip to content

Commit 7fa8586

Browse files
committed
[derive] Add #[zerogc(unsafe_skip_drop)] to skip dummy drop impl
We no longer generate auto-drops for NullTrace types either. If there's nothing to trace, then there's nothing that needs to worry about drop safety ;) - Generate an error in the event of multiple `#[zerogc]` attrs
1 parent 010be99 commit 7fa8586

File tree

2 files changed

+159
-46
lines changed

2 files changed

+159
-46
lines changed

libs/derive/src/lib.rs

Lines changed: 137 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
extern crate proc_macro;
66

77
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};
99
use proc_macro2::{Ident, TokenStream, Span};
1010
use syn::spanned::Spanned;
1111
use syn::parse::{ParseStream, Parse};
@@ -65,23 +65,35 @@ impl Parse for MutableFieldOpts {
6565
}
6666

6767
struct GcFieldAttrs {
68-
mutable: Option<MutableFieldOpts>
68+
mutable: Option<MutableFieldOpts>,
69+
unsafe_skip_trace: bool
6970
}
7071
impl GcFieldAttrs {
7172
pub fn find(attrs: &[Attribute]) -> Result<Self, Error> {
72-
attrs.iter().find_map(|attr| {
73+
let mut iter = attrs.iter().filter_map(|attr| {
7374
if attr.path.is_ident("zerogc") {
74-
Some(syn::parse2::<GcFieldAttrs>(attr.tokens.clone()))
75+
Some((attr, syn::parse2::<GcFieldAttrs>(attr.tokens.clone())))
7576
} else {
7677
None
7778
}
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+
}
7990
}
8091
}
8192
impl Default for GcFieldAttrs {
8293
fn default() -> Self {
8394
GcFieldAttrs {
84-
mutable: None
95+
mutable: None,
96+
unsafe_skip_trace: false
8597
}
8698
}
8799
}
@@ -91,8 +103,9 @@ impl Parse for GcFieldAttrs {
91103
parenthesized!(input in raw_input);
92104
let mut result = GcFieldAttrs::default();
93105
while !input.is_empty() {
94-
let flag_name = input.parse::<Ident>()?;
106+
let flag_name = input.fork().parse::<Ident>()?;
95107
if flag_name == "mutable" {
108+
input.parse::<Ident>().unwrap(); // Actually advance
96109
if input.peek(syn::token::Paren) {
97110
let mut_opts;
98111
parenthesized!(mut_opts in input);
@@ -105,9 +118,27 @@ impl Parse for GcFieldAttrs {
105118
} else {
106119
result.mutable = Some(MutableFieldOpts::default());
107120
}
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;
108138
} else {
109139
return Err(Error::new(
110-
input.span(), "Unknown field flag"
140+
meta.path().span(),
141+
"Unknown field flag"
111142
))
112143
}
113144
if input.peek(Token![,]) {
@@ -122,6 +153,10 @@ struct GcTypeInfo {
122153
config: TypeAttrs,
123154
}
124155
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+
}
125160
fn parse(input: &DeriveInput) -> Result<GcTypeInfo, Error> {
126161
let config = TypeAttrs::find(&*input.attrs)?;
127162
if config.ignored_lifetimes.contains(&config.gc_lifetime()) {
@@ -142,7 +177,9 @@ struct TypeAttrs {
142177
ignored_lifetimes: HashSet<Lifetime>,
143178
/// Unsafely assume the type is safe to [drop]
144179
/// 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
146183
}
147184
impl TypeAttrs {
148185
fn gc_lifetime(&self) -> Lifetime {
@@ -152,13 +189,23 @@ impl TypeAttrs {
152189
}
153190
}
154191
pub fn find(attrs: &[Attribute]) -> Result<Self, Error> {
155-
attrs.iter().find_map(|attr| {
192+
let mut iter = attrs.iter().filter_map(|attr| {
156193
if attr.path.is_ident("zerogc") {
157-
Some(syn::parse2::<TypeAttrs>(attr.tokens.clone()))
194+
Some((attr, syn::parse2::<TypeAttrs>(attr.tokens.clone())))
158195
} else {
159196
None
160197
}
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+
}
162209
}
163210
}
164211
impl Default for TypeAttrs {
@@ -170,6 +217,7 @@ impl Default for TypeAttrs {
170217
collector_id: None,
171218
ignore_params: Default::default(),
172219
ignored_lifetimes: Default::default(),
220+
unsafe_skip_drop: false
173221
}
174222
}
175223
}
@@ -208,20 +256,20 @@ impl Parse for TypeAttrs {
208256
))
209257
}
210258
result.is_copy = true;
211-
} else if meta.path().is_ident("unsafe_drop_safe") {
259+
} else if meta.path().is_ident("unsafe_skip_drop") {
212260
if !matches!(meta, Meta::Path(_)) {
213261
return Err(Error::new(
214262
meta.span(),
215-
"Malformed attribute for #[zerogc(unsafe_drop_safe)]"
263+
"Malformed attribute for #[zerogc(unsafe_skip_drop)]"
216264
))
217265
}
218-
if result.unsafe_drop_safe {
266+
if result.unsafe_skip_drop {
219267
return Err(Error::new(
220268
meta.span(),
221-
"Duplicate flags: #[zerogc(unsafe_drop_safe)]"
269+
"Duplicate flags: #[zerogc(unsafe_skip_drop)]"
222270
))
223271
}
224-
result.unsafe_drop_safe = true;
272+
result.unsafe_skip_drop = true;
225273
} else if meta.path().is_ident("nop_trace") {
226274
if !matches!(meta, Meta::Path(_)) {
227275
return Err(Error::new(
@@ -444,7 +492,7 @@ fn impl_derive_trace(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
444492
})
445493
}
446494

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> {
448496
let zerogc_crate = zerogc_crate();
449497
// TODO: Detect if we're a unit struct and implement `NullTrace`
450498
let mut result = Vec::new();
@@ -453,9 +501,13 @@ fn trace_fields(fields: &Fields, access_ref: &mut dyn FnMut(Member) -> TokenStre
453501
Some(ref ident) => Member::Named(ident.clone()),
454502
None => Member::Unnamed(Index::from(index))
455503
});
504+
let attrs = GcFieldAttrs::find(&field.attrs)?;
505+
if attrs.unsafe_skip_trace {
506+
continue // Skip
507+
}
456508
result.push(quote!(#zerogc_crate::Trace::visit(#val, &mut *visitor)?));
457509
}
458-
quote!(#(#result;)*)
510+
Ok(quote!(#(#result;)*))
459511
}
460512

461513
/// Implement extra methods
@@ -641,10 +693,15 @@ fn impl_erase(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
641693
let rewritten_param: GenericArgument;
642694
match param {
643695
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+
}
644702
let original_bounds = type_param.bounds.iter().cloned().collect::<Vec<_>>();
645703
type_param.bounds.push(parse_quote!(#zerogc_crate::GcErase<'min, #collector_id>));
646704
type_param.bounds.push(parse_quote!('min));
647-
let param_name = &type_param.ident;
648705
let rewritten_type: Type = parse_quote!(<#param_name as #zerogc_crate::GcErase<'min, #collector_id>>::Erased);
649706
rewritten_restrictions.push(WherePredicate::Type(PredicateType {
650707
lifetimes: None,
@@ -785,9 +842,13 @@ fn impl_rebrand(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream,
785842
let rewritten_param: GenericArgument;
786843
match param {
787844
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+
}
788850
let original_bounds = type_param.bounds.iter().cloned().collect::<Vec<_>>();
789851
type_param.bounds.push(parse_quote!(#zerogc_crate::GcRebrand<'new_gc, #collector_id>));
790-
let param_name = &type_param.ident;
791852
let rewritten_type: Type = parse_quote!(<#param_name as #zerogc_crate::GcRebrand<'new_gc, #collector_id>>::Branded);
792853
rewritten_restrictions.push(WherePredicate::Type(PredicateType {
793854
lifetimes: None,
@@ -864,19 +925,19 @@ fn impl_trace(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
864925
&info.config.ignore_params, None
865926
)?;
866927
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
867-
let field_types: Vec<&Type>;
928+
let fields: Vec<&Field>;
868929
let trace_impl: TokenStream;
869930
match target.data {
870931
Data::Struct(ref data) => {
871932
trace_impl = trace_fields(
872933
&data.fields,
873934
&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();
876937
},
877938
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())
880941
.collect();
881942
let mut match_arms = Vec::new();
882943
for variant in &data.variants {
@@ -895,7 +956,7 @@ fn impl_trace(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
895956
};
896957
quote!(#ident)
897958
}
898-
);
959+
)?;
899960
let pattern = match variant.fields {
900961
Fields::Named(ref fields) => {
901962
let names = fields.named.iter()
@@ -925,9 +986,20 @@ fn impl_trace(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
925986
));
926987
},
927988
}
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+
});
9281000
Ok(quote! {
9291001
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)*;
9311003

9321004
/*
9331005
* 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,
9571029
})
9581030
)?;
9591031
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 {
9611033
Data::Struct(ref data) => {
962-
data.fields.iter()
963-
.map(|f| &f.ty)
964-
.collect()
1034+
data.fields.iter().collect()
9651035
},
9661036
Data::Enum(ref data) => {
9671037
data.variants.iter()
968-
.flat_map(|v| v.fields.iter().map(|f| &f.ty))
1038+
.flat_map(|v| v.fields.iter())
9691039
.collect()
9701040
},
9711041
Data::Union(_) => {
@@ -974,12 +1044,21 @@ fn impl_gc_safe(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream,
9741044
))
9751045
},
9761046
};
1047+
let target_fields = target_fields.into_iter()
1048+
.map(|field| Ok((field, GcFieldAttrs::find(&field.attrs)?)))
1049+
.collect::<Result<Vec<_>, Error>>()?;
9771050
let does_need_drop = if info.config.is_copy {
9781051
// If we're proven to be a copy type we don't need to be dropped
9791052
quote!(false)
9801053
} 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+
});
9811060
// 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)*)
9831062
};
9841063
let fake_drop_impl = if info.config.is_copy {
9851064
/*
@@ -990,7 +1069,9 @@ fn impl_gc_safe(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream,
9901069
* in the first place.
9911070
*/
9921071
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 {
9941075
quote!(impl #impl_generics Drop for #name #ty_generics #where_clause {
9951076
#[inline]
9961077
fn drop(&mut self) {
@@ -1004,7 +1085,13 @@ fn impl_gc_safe(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream,
10041085
let verify_gc_safe = if info.config.is_copy {
10051086
quote!(#zerogc_crate::assert_copy::<Self>())
10061087
} 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;)*)
10081095
};
10091096
Ok(quote! {
10101097
unsafe impl #impl_generics #zerogc_crate::GcSafe
@@ -1028,14 +1115,14 @@ fn impl_nop_trace(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream
10281115
&info.config.ignore_params, None
10291116
)?;
10301117
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
1031-
let field_types: Vec<&Type>;
1118+
let target_fields: Vec<&Field>;
10321119
match target.data {
10331120
Data::Struct(ref data) => {
1034-
field_types = data.fields.iter().map(|f| &f.ty).collect();
1121+
target_fields = data.fields.iter().collect();
10351122
},
10361123
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())
10391126
.collect();
10401127
},
10411128
Data::Union(_) => {
@@ -1046,17 +1133,22 @@ fn impl_nop_trace(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream
10461133
},
10471134
}
10481135
// 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+
}
10511143
let ty_span = t.span();
1052-
quote_spanned! { ty_span =>
1144+
Ok(quote_spanned! { ty_span =>
10531145
assert!(
10541146
!<#t as #zerogc_crate::Trace>::NEEDS_TRACE,
10551147
"Can't #[derive(NullTrace) with {}",
10561148
stringify!(#t)
10571149
);
1058-
}
1059-
}).collect::<Vec<_>>();
1150+
})
1151+
}).collect::<Result<Vec<_>, Error>>()?;
10601152
Ok(quote! {
10611153
unsafe impl #impl_generics #zerogc_crate::Trace for #name #ty_generics #where_clause {
10621154
const NEEDS_TRACE: bool = false;

0 commit comments

Comments
 (0)