Skip to content

Commit aee5449

Browse files
committed
Make #[zerogc(nop_trace)] skip an automatic Drop implementation
NullTrace types are implicitly drop safe. No tracing needed implies there are no reachable GC references. Therefore there is nothing to fear about finalizers resurrecting things
1 parent 7fa8586 commit aee5449

File tree

2 files changed

+37
-2
lines changed

2 files changed

+37
-2
lines changed

libs/derive/src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ impl TypeAttrs {
202202
},
203203
(Some((_, parsed)), None) => {
204204
parsed
205-
}
205+
},
206206
(None, None) => Ok(TypeAttrs::default()),
207207
(None, Some(_)) => unreachable!()
208208
}
@@ -1071,6 +1071,14 @@ fn impl_gc_safe(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream,
10711071
quote!()
10721072
} else if info.config.unsafe_skip_drop {
10731073
quote!() // Skip generating drop at user's request
1074+
} else if info.config.nop_trace {
1075+
/*
1076+
* A NullTrace type is implicitly drop safe.
1077+
*
1078+
* No tracing needed implies there are no reachable GC references.
1079+
* Therefore there is nothing to fear about finalizers resurrecting things
1080+
*/
1081+
quote!()
10741082
} else {
10751083
quote!(impl #impl_generics Drop for #name #ty_generics #where_clause {
10761084
#[inline]

libs/derive/tests/basic.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,22 @@ pub enum BasicEnum<'gc, Id: CollectorId> {
4848
)
4949
}
5050

51+
/// A testing type that has no destructor
52+
///
53+
/// It doesn't implement copy, but shouldn't need
54+
/// to be dropped.
55+
///
56+
/// We shouldn't need to annotate with `unsafe_skip_drop`.
57+
/// The dummy destructor shouldn't be generated because of `#[zerogc(nop_trace)]`
58+
#[derive(Trace)]
59+
#[zerogc(nop_trace)]
60+
#[allow(unused)]
61+
struct NoDestructorNullTrace {
62+
val: i32,
63+
s: &'static str,
64+
f: f32
65+
}
66+
5167
fn assert_copy<T: Copy>() {}
5268
fn assert_null_trace<T: NullTrace>() {}
5369
fn check_id<'gc, Id: CollectorId>() {
@@ -110,11 +126,22 @@ fn basic<'gc>() {
110126
// We explicitly skipped the only trace field
111127
assert!(!<UnsafeSkipped<'gc> as Trace>::NEEDS_TRACE);
112128
/*
113-
* We (unsafely) claimed drop-safety, so we shouldn't generate a destructor
129+
* We (unsafely) claimed drop-safety (w/ `unsafe_skip_drop`),
130+
* so we shouldn't generate a destructor
114131
*
115132
* GcSafe::NEEDS_DROP should already be false (since we have no Drop fields),
116133
* however in this case `std::mem::needs_drop` is false since we have no dummy drop impl.
117134
*/
118135
assert!(!<UnsafeSkipped<'gc> as GcSafe>::NEEDS_DROP);
119136
assert!(!std::mem::needs_drop::<UnsafeSkipped<'gc>>());
137+
138+
/*
139+
* Ensure that `NoDestructorNullTrace` doesn't need to be dropped
140+
*
141+
* The `nop_trace` automatically implies `unsafe_skip_drop` (safely)
142+
*/
143+
assert_null_trace::<NoDestructorNullTrace>();
144+
assert!(!<NoDestructorNullTrace as GcSafe>::NEEDS_DROP);
145+
assert!(!std::mem::needs_drop::<NoDestructorNullTrace>());
146+
120147
}

0 commit comments

Comments
 (0)