> From: "Brian Goetz" <brian.go...@oracle.com> > To: "Dan Heidinga" <heidi...@redhat.com> > Cc: "Remi Forax" <fo...@univ-mlv.fr>, "John Rose" <john.r.r...@oracle.com>, > "valhalla-spec-experts" <valhalla-spec-experts@openjdk.java.net> > Sent: Tuesday, January 25, 2022 3:57:17 PM > Subject: Re: VM model and aconst_init
> The motivation for this comes from erasure. Even if we specialize, we need to > be > able to use specialized generics in an erased context, because there will be > erased clients. If we have > class Foo<T> { > T m(); > T[] arr(); > } > we can use also sorts of bridging/casting tricks to make Foo<int> work with > the > erasure of T to Object, but we’ve got much less latitude when we want to treat > an int[] as an Object[]. Since arrays are mutable / identity objects, we can’t > copy them even if we wanted to; we need an identity-preserving way to say > “this > int[] array, as if it were an Object[]”. Rejigging array covariance to work > over the “extends” relation rather than the “subtype” relation seems the least > damaging way to get there. Agree for T[], but don't we have the same issue with T.ref, a L-type can be specialized by a Q-type ? class Foo<T> { T.ref field; } class Bar extends Foo<QComplex;> { void m() { field = Qcomplex;.new(); } } Rémi >> On Jan 25, 2022, at 9:40 AM, Dan Heidinga < [ mailto:heidi...@redhat.com | >> heidi...@redhat.com ] > wrote: >>> Can you mix and match both modes in the same method? >>> Probably, since the interpreter doesn’t care about >>> multi-bytecode patterns. Dunno if this causes a testing >>> problem, and if so how to fix it. I think it’s probably >>> OK, especially if we require the two-way checkcast >>> (Q-Foo not a subtype of L-Foo in the verifier) so that >>> each mode stays “in its own lane”. >>> More explicitly, this is a set of use cases for using >>> Q-types in C_Class entries in the constant pool to switch >>> to Q-mode for bytecodes that refer to classes, including >>> withfield and aconst_init. >>> Let's talk a bit about having the L world and the Q world completely >>> disjoint at >>> least from the bytecode verifier POV. >>> It means that we need some checkcasts to move in both direction, from a >>> Q-type >>> to a L-type and vice-versa. >>> But at the same time, an array of L-type is a subtype of an array of Q-type >>> ? >>> The result to a very uncommon/unconventional type system, and i'm not a big >>> fan >>> of surprises in that area. >> I've been puzzling over this as well and echo your discomfort with it, >> mostly on the array side. I haven't been able to identify what the >> sharp edge here is though other than that it feels surprising. >> After playing with bytecode sequences, the part I'm not clear on is >> whether I can store an LFoo; into a [QFoo; directly, or do I need a >> checkcast QFoo; before the aastore? If I need the checkcast (and I >> think I do), then I'm starting to come around to the view that the >> array side isn't actually any different from what we'd do for any >> other subclass relationship. The checkcast for Q->L is still odd, but >> less concerning as it deals with new value semantics rather than >> changing the array covariance? >> For reference, the bytecodes sequences I've been looking at are the >> following: >> Convert with checkcast: >> -------------------------------- >> aload_1 >> checkcast QFoo; // or LFoo; >> and >> Convert with Q->L array store/load: >> --------------------------------------- >> anewarray //LFoo >> astore_2 >> aload_2 >> iconst_0 >> invokestatic QFoo.<new>QFoo; // or any other way to get a Q >> aastore >> aload_2 >> iconst_0 >> aaload // use as an LFoo >> Convert with L->Q array store/load: >> --------------------------------------- >> anewarray //QFoo >> astore_2 >> aload_2 >> iconst_0 >> invokestatic X.getFoo:()LFoo; >> // Is a checkcast needed here first to downcast? I think so >> aastore >> aload_2 >> iconst_0 >> aaload // use as an QFoo >> --Dan >>> Furthermore, i believe that subtyping is a key to avoid multiple bytecode >>> verification of the generics code. >>> By example, with the TypeRestriction attribute [1], the restriction has to >>> be >>> subtype of the declared type/descriptor. >>> Rémi >>> [1] [ >>> https://cr.openjdk.java.net/~jrose/values/parametric-vm.html#type-restricted-methods-and-fields-and-the-typerestriction-attribute >>> | >>> https://cr.openjdk.java.net/~jrose/values/parametric-vm.html#type-restricted-methods-and-fields-and-the-typerestriction-attribute >>> ]