I supposed a case, where there is such a QOM (QEMU Object Model)
structure relationship:
* DummyState / DummyClass: defined in Rust side, and registered the
TypeInfo by `Object` macro.
- So its class_init will be called by C QOM code.
* DummyChildState / DummyChildClass: defined in Rust side as the
child-object of DummyState, and registered the TypeInfo by `Object`
macro. And suppose it can inherit the trait of DummyClass -
ClassInitImpl<DummyClass> (but I found a gap here, as detailed later;
I expect it should be able to inherit normally).
- So its class_init will be called by C QOM code. In C code call chain,
its parent's class_init should be called by C before its own
class_init.
- However, note that according to the Rust class initialization call
chain, it should also call the parent's class_init within its own
class_init.
- :( the parent's class_init gets called twice.
No, I don't think so. You have the same thing already with PL011State/PL011Luminary.
There, you have
* object_class_init
* device_class_init
* sysbus_device_class_init
* <PL011State as ClassInitImpl<PL011Class>>::class_init
* <PL011State as ClassInitImpl<SysBusDeviceClass>>::class_init
* <PL011State as ClassInitImpl<DeviceClass>>::class_init
* <PL011State as ClassInitImpl<ObjectClass>>::class_init
* <PL011Luminary as ClassInitImpl<PL011Class>>::class_init
* <PL011Luminary as ClassInitImpl<SysBusDeviceClass>>::class_init
* <PL011Luminary as ClassInitImpl<DeviceClass>>::class_init
* <PL011Luminary as ClassInitImpl<ObjectClass>>::class_init
But note that these calls are all different and indeed the last three are empty (all vtable entries are None). This is like a C class_init implementation that does not set any of sdc, dc or oc.
Moving on to another topic, about the gap (or question :-)) where a
child class inherits the ClassInitImpl trait from the parent, please see
my test case example below: Doing something similar to SysBusDevice and
DeviceState using a generic T outside of the QOM library would violate
the orphan rule.
Ugh, you're right. Maybe ClassInitImpl should just be merged into ObjectImpl etc. as a default method implementation. I will check.
> > But, when there is deeper class inheritance, it seems impossible to
> > prevent class_init from being called both by the C side's QOM code and by
> > this kind of recursive case on the Rust side.
> >
>
> Note that here you have two parameters: what class is being filled (the
> argument C of ClassInitImpl<C>) *and* what type is being initialized
> (that's Self).
>
> The "recursion" is only on the argument C, and matches the way C code
> implements class_init.
For Rust side, PL011Class' class_init calls SysBusDeviceClass' class_init,
and SysBusDeviceClass will also call DeviceClass' class_init. So this is
also recursion, right?
No, Self is not PL011Class. Self is PL011State (or PL011Luminary/ and it always remains the same. What changes is *what part* of the class is overwritten, but the order of calls from qom/object.c follows the same logic in both C and Rust.
> Maybe the confusion is because I implemented class_init twice instead of
> using a separate trait "PL011Impl"?
Ah, yes! But I think the Rust call chain should not use class_init anymore
but should use a different method. This way, the original class_init would
only serve the C QOM. A separate trait might break the inheritance
relationship similar to ClassInitImpl.
Do you still think that this is the case? I will look into how to avoid the problem with the orphan rule, but otherwise I think things are fine.
Paolo