@@ -189,6 +189,43 @@ struct TraceField {
189
189
/// to be traced.
190
190
#[ darling( default ) ]
191
191
unsafe_skip_trace : bool ,
192
+ /// Avoid cycles in the computation of 'Trace::NEEDS_TRACE'.
193
+ ///
194
+ /// This overrides the default value of the 'Trace::NEEDS_TRACE'.
195
+ /// It may be necessary to avoid cycles in const evaluation.
196
+ /// For example,
197
+ /// ```no_run
198
+ /// # use zerogc_derive::Trace;
199
+ /// #[derive(Trace)]
200
+ /// struct FooIndirect(Foo);
201
+ /// #[derive(Trace)]
202
+ /// struct Foo {
203
+ /// #[zerogc(avoid_const_cycle)]
204
+ /// foo: Option<Box<FooIndirect>>
205
+ /// }
206
+ /// ```
207
+ /// the default generated value of 'Trace::NEEDS_TRACE'
208
+ /// would be equal to 'const NEEDS_TRACE = Option<Box<FooIndirect>>::NEEDS_TRACE'.
209
+ /// This would cause an infinite cycle, leading to a compiler error.
210
+ ///
211
+ /// NOTE: The macro has builtin cycle-protection for `Box<Foo>`.
212
+ /// It's only the existence of 'FooIndirect' that causes a problem.
213
+ ///
214
+ /// For example, the following works fine without an explicit attribute:
215
+ /// ```no_run
216
+ /// # use zerogc_derive::Trace;
217
+ /// #[derive(Trace)]
218
+ /// // NOTE: No explicit attribute needed ;)
219
+ /// struct Foo {
220
+ /// foo: Box<Foo>
221
+ /// }
222
+ /// ```
223
+ ///
224
+ /// If this is false, it overrides and disables the automatic cycle detection.
225
+ ///
226
+ /// Both options are completely safe.
227
+ #[ darling( default ) ]
228
+ avoid_const_cycle : Option < bool > ,
192
229
#[ darling( default , rename = "serde" ) ]
193
230
serde_opts : Option < SerdeFieldOpts > ,
194
231
#[ darling( forward_attrs( serde) ) ]
@@ -906,17 +943,32 @@ impl TraceDeriveInput {
906
943
} else {
907
944
quote ! ( false )
908
945
} ;
909
- let traced_field_types = self . determine_field_types ( false ) ;
910
- let all_field_types = self . determine_field_types ( true ) ;
911
- let needs_trace = traced_field_types. iter ( ) . map ( |ty| quote_spanned ! ( ty. span( ) => <#ty as zerogc:: Trace >:: NEEDS_TRACE ) ) ;
946
+ let all_fields = self . all_fields ( ) ;
947
+ let needs_trace = all_fields. iter ( )
948
+ . filter ( |field| !field. unsafe_skip_trace )
949
+ . map ( |field| {
950
+ let avoid_cycle = field. avoid_const_cycle . unwrap_or_else ( || {
951
+ detect_cycle ( & field. ty , target_type. clone ( ) )
952
+ } ) ;
953
+ let ty = & field. ty ;
954
+ if avoid_cycle {
955
+ quote ! ( true )
956
+ } else {
957
+ quote_spanned ! ( ty. span( ) => <#ty as zerogc:: Trace >:: NEEDS_TRACE )
958
+ }
959
+ } ) ;
912
960
let needs_drop = if self . is_copy {
913
961
vec ! [ quote!( false ) ]
914
962
} else {
915
- all_field_types. iter ( ) . map ( |ty| {
916
- if traced_field_types. contains ( ty) {
917
- quote_spanned ! ( ty. span( ) => <#ty as zerogc:: Trace >:: NEEDS_DROP )
918
- } else {
963
+ all_fields. iter ( ) . map ( |field| {
964
+ let avoid_cycle = field. avoid_const_cycle . unwrap_or_else ( || {
965
+ detect_cycle ( & field. ty , target_type. clone ( ) )
966
+ } ) ;
967
+ let ty = & field. ty ;
968
+ if field. unsafe_skip_trace || avoid_cycle {
919
969
quote_spanned ! ( ty. span( ) => core:: mem:: needs_drop:: <#ty>( ) )
970
+ } else {
971
+ quote_spanned ! ( ty. span( ) => <#ty as zerogc:: Trace >:: NEEDS_DROP )
920
972
}
921
973
} ) . collect :: < Vec < _ > > ( )
922
974
} ;
@@ -1132,6 +1184,26 @@ impl TraceDeriveInput {
1132
1184
}
1133
1185
}
1134
1186
1187
+ fn detect_cycle ( target : & syn:: Type , potential_cycle : impl Into < syn:: Path > ) -> bool {
1188
+ struct CycleDetector {
1189
+ potential_cycle : Path ,
1190
+ found : bool
1191
+ }
1192
+ impl < ' ast > syn:: visit:: Visit < ' ast > for CycleDetector {
1193
+ fn visit_path ( & mut self , path : & ' ast Path ) {
1194
+ if * path == self . potential_cycle {
1195
+ self . found = true ;
1196
+ }
1197
+ syn:: visit:: visit_path ( self , path) ;
1198
+ }
1199
+ }
1200
+ let mut visitor = CycleDetector {
1201
+ potential_cycle : potential_cycle. into ( ) ,
1202
+ found : false
1203
+ } ;
1204
+ syn:: visit:: visit_type ( & mut visitor, target) ;
1205
+ visitor. found
1206
+ }
1135
1207
pub enum FieldAccess {
1136
1208
None ,
1137
1209
SelfMember {
0 commit comments