Respectfully, I think you're throwing the baby out with the bathwater.
I read your story, and I can certainly empathize.
But just because someone has made a tangled web using inheritance doesn't mean inheritance itself is to blame. Show me someone doing something stupid with inheritance and I can demonstrate the same stupidity with composition.
I mean, base classes should not be operating on derived class objects outside of the base class interface, like ever. That's just poorly architected code no matter which way you slice it. But just like Dijkstra railing against goto, there is a time and a place for (measured, intelligent & appropriate) use.
Even the Linux kernel uses subclassing extensively. Sometimes Struct B really is a Struct A, just with some extra bits. You shouldn't have to duplicate code or nest structures to attain those ergonomics.
Anything can lead to a rube-goldberg mess if not handled with some common sense.
I believe the problem is structural to all traditional class-based inheritance models.
The sort of situation I described is almost impossible with trait/typeclass-based polymorphism. You have to go very far out of the idiom with a weird mix of required and provided methods to achieve it, and I have never seen anyone do this in practice. The idiomatic way is for whatever consumes the trait to pass in a context type. There is a clear separation between the call-forward and the callback interfaces. In Rust, & and &mut mean that there's an upper limit to how tangled the interface can get.
I'm fine with inheritance if there's rigorous enforcement of one-directional calls in place (which makes it roughly equivalent to trait-based composition). The Liskov stuff is a terrible distraction from this far more serious issue. Does anyone do this?
> You shouldn't have to duplicate code or nest structures to attain those ergonomics.
What's wrong with nesting structures? I like nesting structures.