@@ -188,6 +188,36 @@ namespace
188188 static inline MyCopyableClass* instance_ = nullptr ;
189189 };
190190
191+ // Abstract class with virtual destructor - deletion through base pointer is safe
192+ class AbstractVirtualDtor
193+ {
194+ public:
195+ static inline int destructorCalls = 0 ;
196+
197+ static void reset ()
198+ {
199+ destructorCalls = 0 ;
200+ }
201+
202+ virtual ~AbstractVirtualDtor ()
203+ {
204+ destructorCalls++;
205+ }
206+
207+ virtual int compute () const = 0;
208+ };
209+
210+ class ConcreteChild : public AbstractVirtualDtor
211+ {
212+ public:
213+ int compute () const override { return 42 ; }
214+ };
215+
216+ AbstractVirtualDtor* createChild ()
217+ {
218+ return new ConcreteChild ();
219+ }
220+
191221 class OwnerBox ;
192222
193223 class AliasBox
@@ -327,6 +357,14 @@ SETUP(Ownership)
327357 .define_constructor (Constructor<SharedFactory>())
328358 .define_method (" create_owned" , &SharedFactory::createOwned, Return ().takeOwnership ())
329359 .define_method (" get_borrowed" , &SharedFactory::getBorrowed);
360+
361+ define_class<AbstractVirtualDtor>(" AbstractVirtualDtor" )
362+ .define_method (" compute" , &AbstractVirtualDtor::compute);
363+
364+ define_class<ConcreteChild, AbstractVirtualDtor>(" ConcreteChild" );
365+
366+ define_module (" AbstractVirtualDtorTest" )
367+ .define_module_function (" create_child" , &createChild, Return ().takeOwnership ());
330368}
331369
332370TEARDOWN (Ownership)
@@ -541,3 +579,23 @@ TESTCASE(MultipleOwnerReferences)
541579 result = m.module_eval (code);
542580 ASSERT_EQUAL (Qfalse, result.value ());
543581}
582+
583+ TESTCASE (AbstractVirtualDestructor)
584+ {
585+ AbstractVirtualDtor::reset ();
586+
587+ Module m = define_module (" TestingModule" );
588+
589+ std::string code = R"( child = AbstractVirtualDtorTest.create_child
590+ child.compute)" ;
591+
592+ Object result = m.module_eval (code);
593+ ASSERT_EQUAL (detail::To_Ruby<int >().convert (42 ), result.value ());
594+
595+ // Force GC to clean up the object - destructor should be called
596+ // since AbstractVirtualDtor has a virtual destructor
597+ rb_gc_start ();
598+ rb_gc_start ();
599+
600+ ASSERT_EQUAL (1 , AbstractVirtualDtor::destructorCalls);
601+ }
0 commit comments