@@ -16,8 +16,8 @@ use crate::meta::ClassName;
1616use crate :: obj:: { cap, DynGd , Gd , GodotClass } ;
1717use crate :: private:: { ClassPlugin , PluginItem } ;
1818use crate :: registry:: callbacks;
19- use crate :: registry:: plugin:: { ErasedDynifyFn , ErasedRegisterFn , InherentImpl } ;
20- use crate :: { classes , godot_error, godot_warn, sys} ;
19+ use crate :: registry:: plugin:: { DynTraitImpl , ErasedRegisterFn , ITraitImpl , InherentImpl , Struct } ;
20+ use crate :: { godot_error, godot_warn, sys} ;
2121use sys:: { interface_fn, out, Global , GlobalGuard , GlobalLockError } ;
2222
2323/// Returns a lock to a global map of loaded classes, by initialization level.
@@ -45,9 +45,8 @@ fn global_loaded_classes_by_name() -> GlobalGuard<'static, HashMap<ClassName, Cl
4545 lock_or_panic ( & LOADED_CLASSES_BY_NAME , "loaded classes (by name)" )
4646}
4747
48- fn global_dyn_traits_by_typeid (
49- ) -> GlobalGuard < ' static , HashMap < any:: TypeId , Vec < DynToClassRelation > > > {
50- static DYN_TRAITS_BY_TYPEID : Global < HashMap < any:: TypeId , Vec < DynToClassRelation > > > =
48+ fn global_dyn_traits_by_typeid ( ) -> GlobalGuard < ' static , HashMap < any:: TypeId , Vec < DynTraitImpl > > > {
49+ static DYN_TRAITS_BY_TYPEID : Global < HashMap < any:: TypeId , Vec < DynTraitImpl > > > =
5150 Global :: default ( ) ;
5251
5352 lock_or_panic ( & DYN_TRAITS_BY_TYPEID , "dyn traits" )
@@ -68,12 +67,6 @@ pub struct LoadedClass {
6867// Currently empty, but should already work for per-class queries.
6968pub struct ClassMetadata { }
7069
71- /// Represents a `dyn Trait` implemented (and registered) for a class.
72- pub struct DynToClassRelation {
73- implementing_class_name : ClassName ,
74- erased_dynify_fn : ErasedDynifyFn ,
75- }
76-
7770// ----------------------------------------------------------------------------------------------------------------------------------------------
7871
7972// This works as long as fields are called the same. May still need individual #[cfg]s for newer fields.
@@ -110,7 +103,7 @@ struct ClassRegistrationInfo {
110103 is_editor_plugin : bool ,
111104
112105 /// One entry for each `dyn Trait` implemented (and registered) for this class.
113- dynify_fns_by_trait : HashMap < any:: TypeId , ErasedDynifyFn > ,
106+ dynify_fns_by_trait : HashMap < any:: TypeId , DynTraitImpl > ,
114107
115108 /// Used to ensure that each component is only filled once.
116109 component_already_filled : [ bool ; 4 ] ,
@@ -251,14 +244,11 @@ fn register_classes_and_dyn_traits(
251244 let metadata = ClassMetadata { } ;
252245
253246 // Transpose Class->Trait relations to Trait->Class relations.
254- for ( trait_type_id, dynify_fn ) in info. dynify_fns_by_trait . drain ( ) {
247+ for ( trait_type_id, dyn_trait_impl ) in info. dynify_fns_by_trait . drain ( ) {
255248 dyn_traits_by_typeid
256249 . entry ( trait_type_id)
257250 . or_default ( )
258- . push ( DynToClassRelation {
259- implementing_class_name : class_name,
260- erased_dynify_fn : dynify_fn,
261- } ) ;
251+ . push ( dyn_trait_impl) ;
262252 }
263253
264254 loaded_classes_by_level
@@ -301,15 +291,16 @@ pub fn auto_register_rpcs<T: GodotClass>(object: &mut T) {
301291 }
302292}
303293
304- /// Tries to upgrade a polymorphic `Gd<T>` to `DynGd<T, D>`, where the `T` -> `D` relation is only present via derived objects.
294+ /// Tries to convert a `Gd<T>` to a `DynGd<T, D>` for some class `T` and trait object `D`, where the trait may only be implemented for
295+ /// some subclass of `T`.
305296///
306- /// This works without direct `T: AsDyn<D>` because it considers `object`'s dynamic type `Td : Inherits<T >`.
297+ /// This works even when `T` doesn't implement ` AsDyn<D>`, as long as the dynamic class of `object` implements `AsDyn<D >`.
307298///
308- /// Only direct relations are considered, i.e. the `Td: AsDyn<D>` must be fulfilled (and registered). If any intermediate base class of `Td`
309- /// implements the trait `D `, this will not consider it. Base-derived conversions are theoretically possible, but need quite a bit of extra
310- /// machinery.
299+ /// This only looks for an ` AsDyn<D>` implementation in the dynamic class; the conversion will fail if the dynamic class doesn't
300+ /// implement `AsDyn<D> `, even if there exists some superclass that does implement `AsDyn<D>`. This restriction could in theory be
301+ /// lifted, but would need quite a bit of extra machinery to work .
311302pub ( crate ) fn try_dynify_object < T : GodotClass , D : ?Sized + ' static > (
312- object : Gd < T > ,
303+ mut object : Gd < T > ,
313304) -> Result < DynGd < T , D > , ConvertError > {
314305 let typeid = any:: TypeId :: of :: < D > ( ) ;
315306 let trait_name = sys:: short_type_name :: < D > ( ) ;
@@ -322,28 +313,16 @@ pub(crate) fn try_dynify_object<T: GodotClass, D: ?Sized + 'static>(
322313
323314 // TODO maybe use 2nd hashmap instead of linear search.
324315 // (probably not pair of typeid/classname, as that wouldn't allow the above check).
325- let dynamic_class = object. dynamic_class_string ( ) ;
326-
327316 for relation in relations {
328- if dynamic_class == relation. implementing_class_name . to_string_name ( ) {
329- let erased = ( relation. erased_dynify_fn ) ( object. upcast_object ( ) ) ;
330-
331- // Must succeed, or was registered wrong.
332- let dyn_gd_object = erased. boxed . downcast :: < DynGd < classes:: Object , D > > ( ) ;
333-
334- // SAFETY: the relation ensures that the **unified** (for storage) pointer was of type `DynGd<classes::Object, D>`.
335- let dyn_gd_object = unsafe { dyn_gd_object. unwrap_unchecked ( ) } ;
336-
337- // SAFETY: the relation ensures that the **original** pointer was of type `DynGd<T, D>`.
338- let dyn_gd_t = unsafe { dyn_gd_object. cast_unchecked :: < T > ( ) } ;
339-
340- return Ok ( dyn_gd_t) ;
317+ match relation. get_dyn_gd ( object) {
318+ Ok ( dyn_gd) => return Ok ( dyn_gd) ,
319+ Err ( obj) => object = obj,
341320 }
342321 }
343322
344323 let error = FromGodotError :: UnimplementedDynTrait {
345324 trait_name,
346- class_name : dynamic_class . to_string ( ) ,
325+ class_name : object . dynamic_class_string ( ) . to_string ( ) ,
347326 } ;
348327
349328 Err ( error. into_error ( object) )
@@ -376,8 +355,8 @@ where
376355 trait_name = sys:: short_type_name:: <D >( )
377356 ) ;
378357
379- GString :: from ( join_with ( relations. iter ( ) , ", " , |relation | {
380- relation . implementing_class_name . to_cow_str ( )
358+ GString :: from ( join_with ( relations. iter ( ) , ", " , |dyn_trait | {
359+ dyn_trait . class_name ( ) . to_cow_str ( )
381360 } ) )
382361}
383362
@@ -388,7 +367,7 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
388367 // out!("| reg (before): {c:?}");
389368 // out!("| comp: {component:?}");
390369 match item {
391- PluginItem :: Struct {
370+ PluginItem :: Struct ( Struct {
392371 base_class_name,
393372 generated_create_fn,
394373 generated_recreate_fn,
@@ -401,7 +380,7 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
401380 is_instantiable,
402381 #[ cfg( all( since_api = "4 . 3 ", feature = "register-docs") ) ]
403382 docs : _,
404- } => {
383+ } ) => {
405384 c. parent_class_name = Some ( base_class_name) ;
406385 c. default_virtual_fn = default_get_virtual_fn;
407386 c. register_properties_fn = Some ( register_properties_fn) ;
@@ -458,7 +437,7 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
458437 c. register_methods_constants_fn = Some ( register_methods_constants_fn) ;
459438 }
460439
461- PluginItem :: ITraitImpl {
440+ PluginItem :: ITraitImpl ( ITraitImpl {
462441 user_register_fn,
463442 user_create_fn,
464443 user_recreate_fn,
@@ -473,7 +452,7 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
473452 user_property_get_revert_fn,
474453 #[ cfg( all( since_api = "4 . 3 ", feature = "register-docs") ) ]
475454 virtual_method_docs : _,
476- } => {
455+ } ) => {
477456 c. user_register_fn = user_register_fn;
478457
479458 // The following unwraps of fill_into() shouldn't panic, since rustc will error if there are
@@ -497,20 +476,17 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
497476 c. godot_params . free_property_list_func = user_free_property_list_fn;
498477 c. godot_params . property_can_revert_func = user_property_can_revert_fn;
499478 c. godot_params . property_get_revert_func = user_property_get_revert_fn;
500- c. user_virtual_fn = Some ( get_virtual_fn) ;
479+ c. user_virtual_fn = get_virtual_fn;
501480 }
502- PluginItem :: DynTraitImpl {
503- dyn_trait_typeid,
504- erased_dynify_fn,
505- } => {
506- let prev = c
507- . dynify_fns_by_trait
508- . insert ( dyn_trait_typeid, erased_dynify_fn) ;
481+ PluginItem :: DynTraitImpl ( dyn_trait_impl) => {
482+ let type_id = dyn_trait_impl. dyn_trait_typeid ( ) ;
483+
484+ let prev = c. dynify_fns_by_trait . insert ( type_id, dyn_trait_impl) ;
509485
510486 assert ! (
511487 prev. is_none( ) ,
512488 "Duplicate registration of {:?} for class {}" ,
513- dyn_trait_typeid ,
489+ type_id ,
514490 c. class_name
515491 ) ;
516492 }
0 commit comments