+100; great summary > On Nov 2, 2021, at 2:18 PM, Brian Goetz <brian.go...@oracle.com> wrote: > > which means that the `L*` types might work out here. Stay tuned for more > details.
A footnote, FTR, about L*-descriptors, in case it doesn’t ring a bell. Brian is referring here to the thing we have talked about several years ago, of loosely coupling a side-record with an occurrence of L-Foo that means “link like L-Foo, but load and adapt like Q-Foo”. We went through some of these iterations even before we settled on Q-descriptors; they are back again, but in a far more tractable form we think. L* is not a new descriptor, it’s just an L (so it links to plain L’s) but some sort of star-like marking * (not really in the descriptor string but a side channel!) alerts the JVM to do extra loading and adapting. So, one current vision of this side-channel is a very limited early use of the “Type Restriction” mechanism, as mentioned in the Parametric VM proposal and elsewhere. The idea is that a type L*-Foo would be TR-ed to itself (Foo.class) and since TR’s use eager loading (of the content of the TR, not of the type it applies to) the effect would be similar to a Q-Foo, but it would still be spelled L-Foo. To avoid implementation burdens, the JVM would not accept any more “interesting” TRs, until we need to build them out for specialized generics. Or we’d just have a one-shot, purpose-built side channel which smells like an infant sibling to an eventual real T.R. feature. A T.R. that really restricts a type (instead of just asks the JVM to take a closer look a la Q-Foo) is a much deeper implementation challenge, since it creates possible failure points when restrictions are violated. An L* cannot violate itself since the value set is the same. This is why L* only works on the middle bucket. L*-Foo (using TRs or any other side-channel) is not a perfect substitute for Q-Foo, because the stars “rub off too easily” to ensure rigid correspondence between callers and callee. This means L*-based API linkage requires more speculation and runtime checking, compared to Q-based API linkage. Although it may seem odd, there are a number of practical reasons to use L* in the middle bucket but Q in the left bucket. The left bucket needs two descriptors, so L/Q. The middle bucket has just one class mirror, so either Q or else a mix of L and L*, and it needs some story for migration for a few of its citizens, so L* looks good again (linking with legacy L with a dynamic mixup). As Brian says, we may elect to use Q uniformly for the middle bucket, and handle the migration problem another way. It would be good if we could decide Q vs. L* for the middle bucket without co-solving the migration problem. Anyway, such smaller details are up in the air. The points in Brian’s message are the high-order bits, and the stuff I’ve shared here is a footnote. Please do give the high-order bits your best attention. It’s a really good write-up. — John