@@ -41,18 +41,34 @@ fn dyn_gd_creation_bind() {
41
41
42
42
#[ itest]
43
43
fn dyn_gd_creation_deref ( ) {
44
- let node = foreign :: NodeHealth :: new_alloc ( ) ;
45
- let original_id = node . instance_id ( ) ;
44
+ let obj = Gd :: from_object ( RefcHealth { hp : 100 } ) ;
45
+ let original_id = obj . instance_id ( ) ;
46
46
47
- let mut node = node. into_dyn :: < dyn Health > ( ) ;
47
+ // Type can be safely inferred.
48
+ let mut obj = obj. into_dyn ( ) ;
48
49
49
- let dyn_id = node . instance_id ( ) ;
50
+ let dyn_id = obj . instance_id ( ) ;
50
51
assert_eq ! ( dyn_id, original_id) ;
51
52
52
- deal_20_damage ( & mut * node. dyn_bind_mut ( ) ) ;
53
- assert_eq ! ( node. dyn_bind( ) . get_hitpoints( ) , 80 ) ;
53
+ deal_20_damage ( & mut * obj. dyn_bind_mut ( ) ) ;
54
+ assert_eq ! ( obj. dyn_bind( ) . get_hitpoints( ) , 80 ) ;
55
+ }
54
56
55
- node. free ( ) ;
57
+ #[ itest]
58
+ fn dyn_gd_creation_deref_multiple_traits ( ) {
59
+ let obj = foreign:: NodeHealth :: new_alloc ( ) ;
60
+ let original_id = obj. instance_id ( ) ;
61
+
62
+ // `dyn Health` must be explicitly declared if multiple AsDyn<...> trait implementations exist.
63
+ let mut obj = obj. into_dyn :: < dyn Health > ( ) ;
64
+
65
+ let dyn_id = obj. instance_id ( ) ;
66
+ assert_eq ! ( dyn_id, original_id) ;
67
+
68
+ deal_20_damage ( & mut * obj. dyn_bind_mut ( ) ) ;
69
+ assert_eq ! ( obj. dyn_bind( ) . get_hitpoints( ) , 80 ) ;
70
+
71
+ obj. free ( ) ;
56
72
}
57
73
58
74
fn deal_20_damage ( h : & mut dyn Health ) {
@@ -104,13 +120,25 @@ fn dyn_gd_downcast() {
104
120
105
121
#[ itest]
106
122
fn dyn_gd_debug ( ) {
107
- let obj = Gd :: from_object ( RefcHealth { hp : 20 } ) . into_dyn ( ) ;
108
- let id = obj . instance_id ( ) ;
123
+ let node = foreign :: NodeHealth :: new_alloc ( ) ;
124
+ let id = node . instance_id ( ) ;
109
125
110
- let actual = format ! ( ".:{obj:?}:." ) ;
111
- let expected = format ! ( ".:DynGd {{ id: {id}, class: RefcHealth, trait: dyn Health }}:." ) ;
126
+ let node = node. into_dyn :: < dyn Health > ( ) ;
127
+
128
+ let actual = format ! ( ".:{node:?}:." ) ;
129
+ let expected = format ! ( ".:DynGd {{ id: {id}, class: NodeHealth, trait: dyn Health }}:." ) ;
112
130
113
131
assert_eq ! ( actual, expected) ;
132
+
133
+ let node = node
134
+ . into_gd ( )
135
+ . into_dyn :: < dyn InstanceIdProvider < Id = InstanceId > > ( ) ;
136
+ let actual = format ! ( ".:{node:?}:." ) ;
137
+ let expected = format ! ( ".:DynGd {{ id: {id}, class: NodeHealth, trait: dyn InstanceIdProvider<Id = InstanceId> }}:." ) ;
138
+
139
+ assert_eq ! ( actual, expected) ;
140
+
141
+ node. free ( ) ;
114
142
}
115
143
116
144
#[ itest]
@@ -249,42 +277,51 @@ fn dyn_gd_pass_to_godot_api() {
249
277
250
278
#[ itest]
251
279
fn dyn_gd_variant_conversions ( ) {
252
- let original = Gd :: from_object ( RefcHealth { hp : 11 } ) . into_dyn :: < dyn Health > ( ) ;
253
- let original_id = original. instance_id ( ) ;
254
- let refc = original. into_gd ( ) . upcast :: < RefCounted > ( ) ;
280
+ let node = foreign:: NodeHealth :: new_alloc ( ) ;
281
+ let original_id = node. instance_id ( ) ;
255
282
256
- let variant = refc . to_variant ( ) ;
283
+ let variant = node . to_variant ( ) ;
257
284
258
285
// Convert to different levels of DynGd:
259
286
260
- let back: DynGd < RefcHealth , dyn Health > = variant. to ( ) ;
261
- assert_eq ! ( back. bind( ) . get_hitpoints( ) , 11 ) ;
287
+ let back: DynGd < foreign :: NodeHealth , dyn Health > = variant. to ( ) ;
288
+ assert_eq ! ( back. bind( ) . get_hitpoints( ) , 100 ) ;
262
289
assert_eq ! ( back. instance_id( ) , original_id) ;
263
290
264
- let back: DynGd < RefCounted , dyn Health > = variant. to ( ) ;
265
- assert_eq ! ( back. dyn_bind( ) . get_hitpoints( ) , 11 ) ;
291
+ let back: DynGd < Node , dyn Health > = variant. to ( ) ;
292
+ assert_eq ! ( back. dyn_bind( ) . get_hitpoints( ) , 100 ) ;
266
293
assert_eq ! ( back. instance_id( ) , original_id) ;
267
294
268
295
let back: DynGd < Object , dyn Health > = variant. to ( ) ;
269
- assert_eq ! ( back. dyn_bind( ) . get_hitpoints( ) , 11 ) ;
296
+ assert_eq ! ( back. dyn_bind( ) . get_hitpoints( ) , 100 ) ;
270
297
assert_eq ! ( back. instance_id( ) , original_id) ;
271
298
272
- // Convert to different levels of Gd :
299
+ // Convert to different DynGd :
273
300
274
- let back: Gd < RefcHealth > = variant. to ( ) ;
275
- assert_eq ! ( back. bind( ) . get_hitpoints( ) , 11 ) ;
276
- assert_eq ! ( back. instance_id( ) , original_id) ;
301
+ let back: DynGd < foreign:: NodeHealth , dyn InstanceIdProvider < Id = InstanceId > > = variant. to ( ) ;
302
+ assert_eq ! ( back. dyn_bind( ) . get_id_dynamic( ) , original_id) ;
303
+
304
+ let back: DynGd < Node , dyn InstanceIdProvider < Id = InstanceId > > = variant. to ( ) ;
305
+ assert_eq ! ( back. dyn_bind( ) . get_id_dynamic( ) , original_id) ;
277
306
278
- let back: Gd < RefcHealth > = variant. to ( ) ;
307
+ let back: DynGd < Object , dyn InstanceIdProvider < Id = InstanceId > > = variant. to ( ) ;
308
+ assert_eq ! ( back. dyn_bind( ) . get_id_dynamic( ) , original_id) ;
309
+
310
+ // Convert to different levels of Gd:
311
+
312
+ let back: Gd < foreign:: NodeHealth > = variant. to ( ) ;
313
+ assert_eq ! ( back. bind( ) . get_hitpoints( ) , 100 ) ;
279
314
assert_eq ! ( back. instance_id( ) , original_id) ;
280
315
281
316
let back: Gd < Object > = variant. to ( ) ;
282
317
assert_eq ! ( back. instance_id( ) , original_id) ;
318
+
319
+ node. free ( ) ;
283
320
}
284
321
285
322
#[ itest]
286
323
fn dyn_gd_store_in_godot_array ( ) {
287
- let a = Gd :: from_object ( RefcHealth { hp : 33 } ) . into_dyn :: < dyn Health > ( ) ;
324
+ let a = Gd :: from_object ( RefcHealth { hp : 33 } ) . into_dyn ( ) ;
288
325
let b = foreign:: NodeHealth :: new_alloc ( ) . into_dyn ( ) ;
289
326
290
327
let array: Array < DynGd < Object , _ > > = array ! [ & a. upcast( ) , & b. upcast( ) ] ;
@@ -298,21 +335,29 @@ fn dyn_gd_store_in_godot_array() {
298
335
#[ itest]
299
336
fn dyn_gd_error_unregistered_trait ( ) {
300
337
trait UnrelatedTrait { }
338
+ let node = foreign:: NodeHealth :: new_alloc ( ) . into_dyn :: < dyn Health > ( ) ;
301
339
302
- let obj = Gd :: from_object ( RefcHealth { hp : 33 } ) . into_dyn :: < dyn Health > ( ) ;
340
+ let variant = node . to_variant ( ) ;
303
341
304
- let variant = obj. to_variant ( ) ;
305
- let back = variant. try_to :: < DynGd < RefcHealth , dyn UnrelatedTrait > > ( ) ;
342
+ let back = variant. try_to :: < DynGd < foreign:: NodeHealth , dyn UnrelatedTrait > > ( ) ;
343
+
344
+ // The conversion fails before a DynGd is created, so Display still operates on the Gd.
345
+ let node = node. into_gd ( ) ;
306
346
307
347
let err = back. expect_err ( "DynGd::try_to() should have failed" ) ;
308
- let expected_err = {
309
- // The conversion fails before a DynGd is created, so Display still operates on the Gd.
310
- let obj = obj. into_gd ( ) ;
348
+ let expected_err =
349
+ format ! ( "trait `dyn UnrelatedTrait` has not been registered with #[godot_dyn]: {node:?}" ) ;
311
350
312
- format ! ( "trait `dyn UnrelatedTrait` has not been registered with #[godot_dyn]: {obj:?}" )
313
- } ;
351
+ assert_eq ! ( err. to_string( ) , expected_err) ;
352
+
353
+ let back = variant. try_to :: < DynGd < foreign:: NodeHealth , dyn InstanceIdProvider < Id = i32 > > > ( ) ;
354
+
355
+ let err = back. expect_err ( "DynGd::try_to() should have failed" ) ;
356
+ let expected_err = format ! ( "trait `dyn InstanceIdProvider<Id = i32>` has not been registered with #[godot_dyn]: {node:?}" ) ;
314
357
315
358
assert_eq ! ( err. to_string( ) , expected_err) ;
359
+
360
+ node. free ( ) ;
316
361
}
317
362
318
363
#[ itest]
@@ -327,11 +372,23 @@ fn dyn_gd_error_unimplemented_trait() {
327
372
err. to_string( ) ,
328
373
format!( "none of the classes derived from `RefCounted` have been linked to trait `dyn Health` with #[godot_dyn]: {obj:?}" )
329
374
) ;
375
+
376
+ let node = foreign:: NodeHealth :: new_alloc ( ) ;
377
+ let variant = node. to_variant ( ) ;
378
+ let back = variant. try_to :: < DynGd < foreign:: NodeHealth , dyn InstanceIdProvider < Id = f32 > > > ( ) ;
379
+
380
+ let err = back. expect_err ( "DynGd::try_to() should have failed" ) ;
381
+ assert_eq ! (
382
+ err. to_string( ) ,
383
+ format!( "none of the classes derived from `NodeHealth` have been linked to trait `dyn InstanceIdProvider<Id = f32>` with #[godot_dyn]: {node:?}" )
384
+ ) ;
385
+
386
+ node. free ( ) ;
330
387
}
331
388
332
389
#[ itest]
333
390
fn dyn_gd_free_while_dyn_bound ( ) {
334
- let mut obj: DynGd < _ , dyn Health > = foreign:: NodeHealth :: new_alloc ( ) . into_dyn ( ) ;
391
+ let mut obj = foreign:: NodeHealth :: new_alloc ( ) . into_dyn :: < dyn Health > ( ) ;
335
392
336
393
{
337
394
let copy = obj. clone ( ) ;
@@ -359,7 +416,9 @@ fn dyn_gd_multiple_traits() {
359
416
let obj = foreign:: NodeHealth :: new_alloc ( ) ;
360
417
let original_id = obj. instance_id ( ) ;
361
418
362
- let obj = obj. into_dyn :: < dyn InstanceIdProvider > ( ) . upcast :: < Node > ( ) ;
419
+ let obj = obj
420
+ . into_dyn :: < dyn InstanceIdProvider < Id = InstanceId > > ( )
421
+ . upcast :: < Node > ( ) ;
363
422
let id = obj. dyn_bind ( ) . get_id_dynamic ( ) ;
364
423
assert_eq ! ( id, original_id) ;
365
424
@@ -434,14 +493,15 @@ impl Health for foreign::NodeHealth {
434
493
// ----------------------------------------------------------------------------------------------------------------------------------------------
435
494
// Check that one class can implement two or more traits.
436
495
437
- // Pointless trait, but tests access to object.
438
496
trait InstanceIdProvider {
439
- fn get_id_dynamic ( & self ) -> InstanceId ;
497
+ type Id ;
498
+ fn get_id_dynamic ( & self ) -> Self :: Id ;
440
499
}
441
500
442
501
#[ godot_dyn]
443
502
impl InstanceIdProvider for foreign:: NodeHealth {
444
- fn get_id_dynamic ( & self ) -> InstanceId {
503
+ type Id = InstanceId ;
504
+ fn get_id_dynamic ( & self ) -> Self :: Id {
445
505
self . base ( ) . instance_id ( )
446
506
}
447
507
}
@@ -455,5 +515,15 @@ struct RefcDynGdExporter {
455
515
#[ var]
456
516
first : Option < DynGd < Object , dyn Health > > ,
457
517
#[ export]
458
- second : Option < DynGd < foreign:: NodeHealth , dyn InstanceIdProvider > > ,
518
+ second : Option < DynGd < foreign:: NodeHealth , dyn InstanceIdProvider < Id = InstanceId > > > ,
519
+ }
520
+
521
+ // Implementation created only to register the DynGd `HealthWithAssociatedType<HealthType=f32>` trait.
522
+ // Pointless trait, but tests proper conversion.
523
+ #[ godot_dyn]
524
+ impl InstanceIdProvider for RefcDynGdExporter {
525
+ type Id = f32 ;
526
+ fn get_id_dynamic ( & self ) -> Self :: Id {
527
+ 42.0
528
+ }
459
529
}
0 commit comments