> This is the second turn of the crank (the first was "you can extend Object > only"), but as this conversation hinted, there may be a further turn where > abstract identity-agnostic classes can contribute to the layout and > initialization of a concrete final by-value class without pulling it into the > world of new-dup-init. The following is an exploration, not a proposal, but > might help find the next turn of the crank. The exposition is > translational-centric but that's not essential. > > An abstract class can contribute fields, initialization of those fields, and > behavior. We can transform: > > abstract class C extends ValueObject { // no identity children, please > T t; > > C(T t) { ... t = e ... } > } > > into > > interface C { > abstract <V extends C> protected V withT(V v, T t); > abstract protected T t(); > > static<V extends C> protected V init(V v, T t) { > ... v = withT(v, e) ... > return v; > } > } > > and a subclass > > b2-class V extends C { > U u; > > V(T t, U u) { super(t); u = e; } > } > > into > > b2-class V implements C { > T t; // pull down fields from super > U u; > > V(T t, U u) { > V this = initialvalue; > this = C.init(this, t); > this = this withfield[u] u; > } > } > > The point of this exercise is to observe that the two components of C that > are doing double-duty as both API points for clients of C and extension > points for subclasses of C -- the constructor and the layout -- can be given > new implementations for the by-value world, that is consistent with the > inheritance semantics the user expects. > > Again, not making a proposal here, as much as probing at the bounds of a new > object model. > > (I think this is similar to what you sketched in your next mail.)
I spent some time playing with this model and it seems pretty workable in a consistently compiled world, though with some pretty sharp edges. Outside such a world - where classes may be recompiled separately - it would be easy to break compatibility as the translation copies implementation details of the super into both field and constructor signatures. Changing C->C' in otherwise compatible ways such as adding a field, or changing the type of the field (depending on accessibility) would lead to many more ICCE cases then we see today and be a surprise to users. As an EG, we've consistently walked back the design anytime the translation strategy has become too clever, or resulted in classfile artifacts saying something different than the source, or changed the meaning of the source (e.g. Class::isInterface). This feels like we're starting to fall down the clever rabbit hole and reminds me of Paul Phillips "Scala war stories" JVMLS talk which highlighted the compatibility issues scala has faced due to their clever translation strategy. And some of this seems like ground we've already trod (and rejected). Brian, to quote one of your emails from Dec 2019 [1] on this topic: > (I remain unconvinced that instance fields in inline-friendly abstract > classes could possibly be in balance, cost-benefit-wise, and > super-unconvinced about inlines extending non-abstract classes.) I'm trying to understand what flipped the cost-benefit calculation here that makes it worthwhile to re-explore allowing values to inherit fields from abstract supers. --Dan [1] https://mail.openjdk.java.net/pipermail/valhalla-spec-experts/2019-December/001181.html