Skip to content

Commit 264224f

Browse files
committed
Support deriving NullTrace via #[zerogc(nop_trace)] attribute
This verifies that all the field types implement NullTrace, than proceeds to implement NullTrace on the parent struct. The error messages aren't the greatest :(
1 parent 257d6f0 commit 264224f

File tree

2 files changed

+107
-20
lines changed

2 files changed

+107
-20
lines changed

libs/derive/src/lib.rs

Lines changed: 93 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ impl Parse for GcFieldAttrs {
9797

9898
struct GcTypeAttrs {
9999
is_copy: bool,
100+
nop_trace: bool,
100101
ignore_params: HashSet<Ident>
101102
}
102103
impl GcTypeAttrs {
@@ -114,6 +115,7 @@ impl Default for GcTypeAttrs {
114115
fn default() -> Self {
115116
GcTypeAttrs {
116117
is_copy: false,
118+
nop_trace: false,
117119
ignore_params: HashSet::new()
118120
}
119121
}
@@ -139,6 +141,20 @@ impl Parse for GcTypeAttrs {
139141
))
140142
}
141143
result.is_copy = true;
144+
} else if meta.path().is_ident("nop_trace") {
145+
if !matches!(meta, Meta::Path(_)) {
146+
return Err(Error::new(
147+
meta.span(),
148+
"Malformed attribute for #[zerogc(nop_trace)]"
149+
))
150+
}
151+
if result.nop_trace {
152+
return Err(Error::new(
153+
meta.span(),
154+
"Duplicate flags: #[zerogc(nop_trace)]"
155+
))
156+
}
157+
result.nop_trace = true;
142158
} else if meta.path().is_ident("ignore_params") {
143159
if !result.ignore_params.is_empty() {
144160
return Err(Error::new(
@@ -193,30 +209,32 @@ impl Parse for GcTypeAttrs {
193209
#[proc_macro_derive(Trace, attributes(zerogc))]
194210
pub fn derive_trace(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
195211
let input = parse_macro_input!(input as DeriveInput);
196-
let attrs = match GcTypeAttrs::find(&*input.attrs) {
197-
Ok(attrs) => attrs,
198-
Err(e) => return e.to_compile_error().into()
212+
let res = From::from(impl_derive_trace(&input)
213+
.unwrap_or_else(|e| e.to_compile_error()));
214+
debug_derive(
215+
"derive(Trace)",
216+
&format_args!("#[derive(Trace) for {}", input.ident),
217+
&res
218+
);
219+
res
220+
}
221+
222+
fn impl_derive_trace(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
223+
let attrs = GcTypeAttrs::find(&*input.attrs)?;
224+
let trace_impl = if attrs.nop_trace {
225+
impl_nop_trace(&input, &attrs)?
226+
} else {
227+
impl_trace(&input, &attrs)?
199228
};
200-
let trace_impl = impl_trace(&input, &attrs)
201-
.unwrap_or_else(|e| e.to_compile_error());
202-
let brand_impl = impl_brand(&input)
203-
.unwrap_or_else(|e| e.to_compile_error());
204-
let gc_safe_impl = impl_gc_safe(&input, &attrs)
205-
.unwrap_or_else(|e| e.to_compile_error());
206-
let extra_impls = impl_extras(&input)
207-
.unwrap_or_else(|e| e.to_compile_error());
208-
let t = From::from(quote! {
229+
let brand_impl = impl_brand(&input)?;
230+
let gc_safe_impl = impl_gc_safe(&input, &attrs)?;
231+
let extra_impls = impl_extras(&input)?;
232+
Ok(quote! {
209233
#trace_impl
210234
#brand_impl
211235
#gc_safe_impl
212236
#extra_impls
213-
});
214-
debug_derive(
215-
"derive(Trace)",
216-
&format_args!("#[derive(Trace) for {}", input.ident),
217-
&t
218-
);
219-
t
237+
})
220238
}
221239

222240
fn trace_fields(fields: &Fields, access_ref: &mut dyn FnMut(Member) -> TokenStream) -> TokenStream {
@@ -550,6 +568,62 @@ fn impl_gc_safe(target: &DeriveInput, attrs: &GcTypeAttrs) -> Result<TokenStream
550568
})
551569
}
552570

571+
572+
fn impl_nop_trace(target: &DeriveInput, attrs: &GcTypeAttrs) -> Result<TokenStream, Error> {
573+
let name = &target.ident;
574+
let generics = add_trait_bounds_except(
575+
&target.generics, parse_quote!(zerogc::Trace),
576+
&attrs.ignore_params
577+
)?;
578+
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
579+
let field_types: Vec<&Type>;
580+
match target.data {
581+
Data::Struct(ref data) => {
582+
field_types = data.fields.iter().map(|f| &f.ty).collect();
583+
},
584+
Data::Enum(ref data) => {
585+
field_types = data.variants.iter()
586+
.flat_map(|var| var.fields.iter().map(|f| &f.ty))
587+
.collect();
588+
},
589+
Data::Union(_) => {
590+
return Err(Error::new(
591+
name.span(),
592+
"Unions can't #[derive(Trace)]"
593+
));
594+
},
595+
}
596+
let const_assertions = field_types.iter()
597+
.map(|&t| {
598+
let ty_span = t.span();
599+
quote_spanned! { ty_span =>
600+
#[allow(clippy::eq_op)]
601+
const _: [(); 0 - !{
602+
const ASSERT: bool = !<#t as Trace>::NEEDS_TRACE;
603+
ASSERT
604+
} as usize] = [];
605+
}
606+
}).collect::<Vec<_>>();
607+
Ok(quote! {
608+
#(#const_assertions)*
609+
unsafe impl #impl_generics ::zerogc::Trace for #name #ty_generics #where_clause {
610+
const NEEDS_TRACE: bool = false;
611+
612+
#[inline(always)] // NOP
613+
fn visit<V: ::zerogc::GcVisitor>(&mut self, #[allow(unused)] visitor: &mut V) -> Result<(), V::Err> {
614+
Ok(())
615+
}
616+
}
617+
unsafe impl #impl_generics ::zerogc::TraceImmutable for #name #ty_generics #where_clause {
618+
#[inline(always)] // NOP
619+
fn visit_immutable<V: ::zerogc::GcVisitor>(&self, #[allow(unused)] visitor: &mut V) -> Result<(), V::Err> {
620+
Ok(())
621+
}
622+
}
623+
unsafe impl #impl_generics ::zerogc::NullTrace for #name #ty_generics #where_clause {}
624+
})
625+
}
626+
553627
fn add_trait_bounds_except(
554628
generics: &Generics, bound: TypeParamBound,
555629
ignored_params: &HashSet<Ident>

libs/derive/tests/basic.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use zerogc::{Gc, CollectorId, Trace, GcSafe};
1+
use zerogc::{Gc, CollectorId, Trace, GcSafe, NullTrace};
22

33
use zerogc_derive::Trace;
44

@@ -19,6 +19,7 @@ pub struct BasicCopy<'gc, Id: CollectorId> {
1919
}
2020

2121
fn assert_copy<T: Copy>() {}
22+
fn assert_null_trace<T: NullTrace>() {}
2223
fn check_id<'gc, Id: CollectorId>() {
2324
assert_copy::<BasicCopy<'gc, Id>>();
2425
assert_copy::<Gc<'gc, BasicCopy<'gc, Id>, Id>>();
@@ -28,6 +29,16 @@ fn check_id<'gc, Id: CollectorId>() {
2829
assert!(<Basic<'gc, Id> as GcSafe>::NEEDS_DROP);
2930
}
3031

32+
#[derive(Trace)]
33+
#[zerogc(nop_trace)]
34+
#[allow(unused)]
35+
struct NopTrace {
36+
s: String,
37+
i: i32,
38+
wow: Box<NopTrace>
39+
}
40+
41+
3142
#[test]
3243
fn basic() {
3344
let _b = Basic::<dummy::DummyCollectorId> {
@@ -40,6 +51,8 @@ fn basic() {
4051
assert!(<Basic::<dummy::DummyCollectorId> as GcSafe>::NEEDS_DROP);
4152
assert!(!<BasicCopy::<dummy::DummyCollectorId> as GcSafe>::NEEDS_DROP);
4253
assert_copy::<BasicCopy::<dummy::DummyCollectorId>>();
54+
assert_null_trace::<NopTrace>();
55+
assert!(!<NopTrace as Trace>::NEEDS_TRACE);
4356

4457
check_id::<dummy::DummyCollectorId>();
4558
}

0 commit comments

Comments
 (0)