From daniel.smith at oracle.com Wed Jan 5 23:37:09 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 5 Jan 2022 23:37:09 +0000 Subject: Updated State of Valhalla documents In-Reply-To: <313972565.1229854.1640289483686.JavaMail.zimbra@u-pem.fr> References: <09EED588-7A6E-4BB8-8DCA-08C29F4E3D73@oracle.com> <1471584940.1211017.1640284516318.JavaMail.zimbra@u-pem.fr> <112398AD-D910-4AF4-8644-844A07DE6539@oracle.com> <394037657.1216754.1640287568639.JavaMail.zimbra@u-pem.fr> <056021F6-4BDF-43C8-A3DC-C9D2B064FC59@oracle.com> <313972565.1229854.1640289483686.JavaMail.zimbra@u-pem.fr> Message-ID: > On Dec 23, 2021, at 12:58 PM, forax at univ-mlv.fr wrote: > > But for Java, i would argue that the model is more > we have either reference objects or primitives, for reference objects you have those with identity and those without identity, > hence "primitive" being a top-level kind while "value" (or a better term) being a modifier. I don't want to get too in the weeds on syntax (even though, yes, it does help convey the underlying model!). The change you propose is, indeed, a possibility that is still on the table. But: it's really important to understand that, in the proposed model, primitive values, identity objects, and value objects *all* belong to classes. That's where they get their members, via the normal rules about class membership. A primitive value is an instance of a class, even though it is not an object. Whether we use the syntax 'c l a s s' in the primitive declaration, the thing being declared is a class. Just like the thing being declared with 'e n u m' is a class. From forax at univ-mlv.fr Thu Jan 6 00:14:36 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 6 Jan 2022 01:14:36 +0100 (CET) Subject: Updated State of Valhalla documents In-Reply-To: References: <09EED588-7A6E-4BB8-8DCA-08C29F4E3D73@oracle.com> <1471584940.1211017.1640284516318.JavaMail.zimbra@u-pem.fr> <112398AD-D910-4AF4-8644-844A07DE6539@oracle.com> <394037657.1216754.1640287568639.JavaMail.zimbra@u-pem.fr> <056021F6-4BDF-43C8-A3DC-C9D2B064FC59@oracle.com> <313972565.1229854.1640289483686.JavaMail.zimbra@u-pem.fr> Message-ID: <24868508.6004197.1641428076934.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "daniel smith" > To: "Remi Forax" > Cc: "valhalla-spec-experts" > Sent: Thursday, January 6, 2022 12:37:09 AM > Subject: Re: Updated State of Valhalla documents >> On Dec 23, 2021, at 12:58 PM, forax at univ-mlv.fr wrote: >> >> But for Java, i would argue that the model is more >> we have either reference objects or primitives, for reference objects you have >> those with identity and those without identity, >> hence "primitive" being a top-level kind while "value" (or a better term) being >> a modifier. > > I don't want to get too in the weeds on syntax (even though, yes, it does help > convey the underlying model!). The change you propose is, indeed, a possibility > that is still on the table. > > But: it's really important to understand that, in the proposed model, primitive > values, identity objects, and value objects *all* belong to classes. That's > where they get their members, via the normal rules about class membership. A > primitive value is an instance of a class, even though it is not an object. > > Whether we use the syntax 'c l a s s' in the primitive declaration, the thing > being declared is a class. Just like the thing being declared with 'e n u m' is > a class. yes, for the VM, a lambda, a record or an enum are all classes, even if in the syntax the keyword "class" is not used. A primitive (B3) does not provide proper encapsulation unlike a classical Java class (the one spelt "class" in the language), because - the default value (the fields fill with zeroes) is always a valid value - a primitive is tearable I'm trying to find ways to convey that semantics through the syntax, because most users, at least at the beginning, derive the semantics from the syntax, - Not using "primitive class" but only "primitive" is a possible step. - Forcing to have a default constructor automatically generated with the same visiblity as the class that can not be overridden (apart to add javadoc) by the user is another possible step. As another example, Anderson Vasconcelos Pires proposed on the dev mailing list to not use "new" to create a primitive object, i believe because a primitive object does not require a heap allocation, so it may make sense to not use "new" the same way we do not use "new" in front of a lambda. But it's a counter example, because we know that if primitive can be created without new people will abuse of it and declare primitive classes where they should not, just to avoid 3 keystrokes. regards, R?mi From daniel.smith at oracle.com Thu Jan 6 00:44:37 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 6 Jan 2022 00:44:37 +0000 Subject: Updated State of Valhalla documents In-Reply-To: <24868508.6004197.1641428076934.JavaMail.zimbra@u-pem.fr> References: <09EED588-7A6E-4BB8-8DCA-08C29F4E3D73@oracle.com> <1471584940.1211017.1640284516318.JavaMail.zimbra@u-pem.fr> <112398AD-D910-4AF4-8644-844A07DE6539@oracle.com> <394037657.1216754.1640287568639.JavaMail.zimbra@u-pem.fr> <056021F6-4BDF-43C8-A3DC-C9D2B064FC59@oracle.com> <313972565.1229854.1640289483686.JavaMail.zimbra@u-pem.fr> <24868508.6004197.1641428076934.JavaMail.zimbra@u-pem.fr> Message-ID: <4A7F0325-CE74-4BC8-BD5C-15C59B8C9637@oracle.com> > On Jan 5, 2022, at 5:14 PM, forax at univ-mlv.fr wrote: > >> But: it's really important to understand that, in the proposed model, primitive >> values, identity objects, and value objects *all* belong to classes. > > yes, > for the VM, a lambda, a record or an enum are all classes, even if in the syntax the keyword "class" is not used. Not talking about the VM. I'm talking about the language model. > A primitive (B3) does not provide proper encapsulation unlike a classical Java class (the one spelt "class" in the language), You should say "object" here, not "class". Primitive values have classes, even though they are not objects. From john.r.rose at oracle.com Thu Jan 6 03:53:28 2022 From: john.r.rose at oracle.com (John Rose) Date: Thu, 6 Jan 2022 03:53:28 +0000 Subject: Updated State of Valhalla documents In-Reply-To: <4A7F0325-CE74-4BC8-BD5C-15C59B8C9637@oracle.com> References: <09EED588-7A6E-4BB8-8DCA-08C29F4E3D73@oracle.com> <1471584940.1211017.1640284516318.JavaMail.zimbra@u-pem.fr> <112398AD-D910-4AF4-8644-844A07DE6539@oracle.com> <394037657.1216754.1640287568639.JavaMail.zimbra@u-pem.fr> <056021F6-4BDF-43C8-A3DC-C9D2B064FC59@oracle.com> <313972565.1229854.1640289483686.JavaMail.zimbra@u-pem.fr> <24868508.6004197.1641428076934.JavaMail.zimbra@u-pem.fr> <4A7F0325-CE74-4BC8-BD5C-15C59B8C9637@oracle.com> Message-ID: <3C817B4C-E943-46B9-9C3C-FE0D4D8861CA@oracle.com> > On Jan 5, 2022, at 4:45 PM, Dan Smith wrote: > > Not talking about the VM. I'm talking about the language model. > >> A primitive (B3) does not provide proper encapsulation unlike a classical Java class (the one spelt "class" in the language), > > You should say "object" here, not "class". Primitive values have classes, even though they are not objects. Yes. And what?s more, Remi?s point about encapsulation is weak, because we can (possibly) assume that every author of a primitive class has checked those boxes off, saying that all-zero default is a valid value and tearing is acceptable. There are plenty of Java B1 classes today that are designed with such weaknesses. Class abstractions come in various strengths as selected by each class?s author. Selecting primitive for a class forces the author to gives up some abstraction but keeps most abstraction decisions intact. Having the required hardwired null-arg constructor syntactixally present is an interesting idea to ensure that the author has explicitly ?checked the box? about the default value. Not sure it?s worth it though. From forax at univ-mlv.fr Thu Jan 6 18:40:02 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 6 Jan 2022 19:40:02 +0100 (CET) Subject: Updated State of Valhalla documents In-Reply-To: <3C817B4C-E943-46B9-9C3C-FE0D4D8861CA@oracle.com> References: <09EED588-7A6E-4BB8-8DCA-08C29F4E3D73@oracle.com> <394037657.1216754.1640287568639.JavaMail.zimbra@u-pem.fr> <056021F6-4BDF-43C8-A3DC-C9D2B064FC59@oracle.com> <313972565.1229854.1640289483686.JavaMail.zimbra@u-pem.fr> <24868508.6004197.1641428076934.JavaMail.zimbra@u-pem.fr> <4A7F0325-CE74-4BC8-BD5C-15C59B8C9637@oracle.com> <3C817B4C-E943-46B9-9C3C-FE0D4D8861CA@oracle.com> Message-ID: <2013584012.6666840.1641494402447.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "John Rose" > To: "daniel smith" > Cc: "Remi Forax" , "valhalla-spec-experts" > Sent: Thursday, January 6, 2022 4:53:28 AM > Subject: Re: Updated State of Valhalla documents >> On Jan 5, 2022, at 4:45 PM, Dan Smith wrote: >> >> Not talking about the VM. I'm talking about the language model. >> >>> A primitive (B3) does not provide proper encapsulation unlike a classical Java >>> class (the one spelt "class" in the language), >> >> You should say "object" here, not "class". Primitive values have classes, even >> though they are not objects. > > Yes. And what?s more, Remi?s point about encapsulation is weak, because we can > (possibly) assume that every author of a primitive class has checked those > boxes off, saying that all-zero default is a valid value and tearing is > acceptable. There are plenty of Java B1 classes today that are designed with > such weaknesses. Class abstractions come in various strengths as selected by > each class?s author. Selecting primitive for a class forces the author to gives > up some abstraction but keeps most abstraction decisions intact. Being a primitive weaken the encapsulation, but your are right that it's not a all or nothing thing. > > Having the required hardwired null-arg constructor syntactically present is an > interesting idea to ensure that the author has explicitly ?checked the box? > about the default value. Not sure it?s worth it though. If we want to check the other box, tearing, at least partially, i suppose that creating a VarHandle on a non volatile primitive class should be rejected too. R?mi From forax at univ-mlv.fr Thu Jan 6 18:50:54 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 6 Jan 2022 19:50:54 +0100 (CET) Subject: Why do we need .ref class for primtive class ? Message-ID: <1025656701.6673192.1641495054893.JavaMail.zimbra@u-pem.fr> It just occurs to me that while ACC_VALUE is a bit that change the runtime semantics, something the VM should take care of, ACC_PRIMITIVE is not a bit that change the runtime semantics, only the javac translation strategy, javac emits Q-types instead of L-type + the Preload attribute. If value classes and primitive classes are equivalent at runtime, why do we need to generate the .ref interface/abstract class ? We can use L-type instead. R?mi From forax at univ-mlv.fr Thu Jan 6 19:24:37 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 6 Jan 2022 20:24:37 +0100 (CET) Subject: Why do we need .ref class for primtive class ? In-Reply-To: <1025656701.6673192.1641495054893.JavaMail.zimbra@u-pem.fr> References: <1025656701.6673192.1641495054893.JavaMail.zimbra@u-pem.fr> Message-ID: <127499830.6678892.1641497077746.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Remi Forax" > To: "valhalla-spec-experts" > Sent: Thursday, January 6, 2022 7:50:54 PM > Subject: Why do we need .ref class for primtive class ? > It just occurs to me that while ACC_VALUE is a bit that change the runtime > semantics, > something the VM should take care of, ACC_PRIMITIVE is not a bit that change the > runtime semantics, only the javac translation strategy, > javac emits Q-types instead of L-type + the Preload attribute. > > If value classes and primitive classes are equivalent at runtime, why do we need > to generate the .ref interface/abstract class ? > We can use L-type instead. Just to be clear, it's not about removing Complex.ref from the language but to translate it to LComplex; R?mi From brian.goetz at oracle.com Thu Jan 6 20:12:21 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 6 Jan 2022 15:12:21 -0500 Subject: Why do we need .ref class for primtive class ? In-Reply-To: <1025656701.6673192.1641495054893.JavaMail.zimbra@u-pem.fr> References: <1025656701.6673192.1641495054893.JavaMail.zimbra@u-pem.fr> Message-ID: Because the Q is what permits the tearing. On 1/6/2022 1:50 PM, Remi Forax wrote: > It just occurs to me that while ACC_VALUE is a bit that change the runtime semantics, > something the VM should take care of, ACC_PRIMITIVE is not a bit that change the > runtime semantics, only the javac translation strategy, > javac emits Q-types instead of L-type + the Preload attribute. > > If value classes and primitive classes are equivalent at runtime, why do we need to generate the .ref interface/abstract class ? > We can use L-type instead. > > R?mi From forax at univ-mlv.fr Thu Jan 6 22:08:03 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 6 Jan 2022 23:08:03 +0100 (CET) Subject: Why do we need .ref class for primtive class ? In-Reply-To: References: <1025656701.6673192.1641495054893.JavaMail.zimbra@u-pem.fr> Message-ID: <387808400.6729306.1641506883754.JavaMail.zimbra@u-pem.fr> Let me re-phrase my question because i've asked the wrong question. My question is not why do we need .ref in the language but why .ref can not be represented by a L-type in the bytecode instead of a full reified interface/abstract class. Asking for a .ref is something similar to a boxing, but using checkcast to convert from a QComplex; to a LComplex; One reason may be that this is not a subtyping relationship but a boxing relationship so it may not work when well we will have fully reified generics. R?mi > From: "Brian Goetz" > To: "Remi Forax" , "valhalla-spec-experts" > > Sent: Thursday, January 6, 2022 9:12:21 PM > Subject: Re: Why do we need .ref class for primtive class ? > Because the Q is what permits the tearing. > On 1/6/2022 1:50 PM, Remi Forax wrote: >> It just occurs to me that while ACC_VALUE is a bit that change the runtime >> semantics, >> something the VM should take care of, ACC_PRIMITIVE is not a bit that change the >> runtime semantics, only the javac translation strategy, >> javac emits Q-types instead of L-type + the Preload attribute. >> If value classes and primitive classes are equivalent at runtime, why do we need >> to generate the .ref interface/abstract class ? >> We can use L-type instead. >> R?mi From brian.goetz at oracle.com Thu Jan 6 22:18:30 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 6 Jan 2022 17:18:30 -0500 Subject: [External] : Re: Why do we need .ref class for primtive class ? In-Reply-To: <387808400.6729306.1641506883754.JavaMail.zimbra@u-pem.fr> References: <1025656701.6673192.1641495054893.JavaMail.zimbra@u-pem.fr> <387808400.6729306.1641506883754.JavaMail.zimbra@u-pem.fr> Message-ID: For a B3 class, .ref *is* indicated by an L type: ??? void m(Point p) { }????? // (QPoint;)V ??? void m(Point.ref p) { }? // (LPoint;)V On 1/6/2022 5:08 PM, forax at univ-mlv.fr wrote: > Let me re-phrase my question because i've asked the wrong question. > > My question is not why do we need .ref in the language but why .ref > can not be represented by a L-type in the bytecode instead of a full > reified interface/abstract class. > > Asking for a .ref is something similar to a boxing, but using > checkcast to convert from a QComplex; to a LComplex; > > One reason may be that this is not a subtyping relationship but a > boxing relationship so it may not work when well we will have fully > reified generics. > > R?mi > > ------------------------------------------------------------------------ > > *From: *"Brian Goetz" > *To: *"Remi Forax" , "valhalla-spec-experts" > > *Sent: *Thursday, January 6, 2022 9:12:21 PM > *Subject: *Re: Why do we need .ref class for primtive class ? > > Because the Q is what permits the tearing. > > On 1/6/2022 1:50 PM, Remi Forax wrote: > > It just occurs to me that while ACC_VALUE is a bit that change the runtime semantics, > something the VM should take care of, ACC_PRIMITIVE is not a bit that change the > runtime semantics, only the javac translation strategy, > javac emits Q-types instead of L-type + the Preload attribute. > > If value classes and primitive classes are equivalent at runtime, why do we need to generate the .ref interface/abstract class ? > We can use L-type instead. > > R?mi > > > From forax at univ-mlv.fr Wed Jan 12 11:45:18 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 12 Jan 2022 12:45:18 +0100 (CET) Subject: L-type, Q-type and class mirrors Message-ID: <1303352024.10598834.1641987918801.JavaMail.zimbra@u-pem.fr> Hi all, i want to propose a way to reconcile builtin primitive types and primitive classes in the B1/B2/B3 world. Let's take a detour, and try to answer to the question, how do we do reflection on method with Q-types ? Given that reflection is using the class java.lang.Class, it means that we need a class that represents a L-type and a class that represent a Q-type. The class that represents a Q-type does not have to be a 'real' class, the same way int.class (Integer.TYPE) is not a real class, it's a mirror class that represent 'I' in the method signature, same with void.class (Void.TYPE) represent 'V'. So the class that represents a Q-type is a mirror class synthesized by the VM at runtime. Given that it is synthesized, i believe it should be only accessible using a method of java.lang.Class, by example Complex.class.mirrorType() // the name "mirror" is perhaps not the best Once we agree about that, we can push a little further and see how we can have only one unified kind of primitive class. If we want to be able to have an ArrayList of int, we need either to see int as a Q-type or Integer as a Q-type. The later is more attractive because it means that at runtime we can see a ArrayList as an ArrayList so call a method of an existing API using ArrayList with the more compact ArrayList. So java.lang.Integer is a primitive class. But it's not a true primitive class because - in a method descriptor because of backward compatibility, it has to be a Ljava/lang/Integer; - inside the '<' '>' of a generics, we need a way to express a Q-type. I propose to introduce a new kind of primitive class, the builtin primitive class, that - use L-type in signature - are eagerly loaded by the VM (this is already the case) thus does not need to appear in the attribute Preload. - this is more controversial, inside a generics, int is translated by the compiler to Qjava/lang/Integer; + usual boxing/unboxing operations so ArrayList is a specialized generics using the class miror corresponding to Qjava/lang/Integer; as argument. In practice, only the wrapper types, Byte, Short, Integer, etc, can be declared as builtin primitive (by example, the compiler can restrict the use of builtin to java.base or java.lang). R?mi From brian.goetz at oracle.com Wed Jan 12 12:27:07 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 12 Jan 2022 07:27:07 -0500 Subject: L-type, Q-type and class mirrors In-Reply-To: <1303352024.10598834.1641987918801.JavaMail.zimbra@u-pem.fr> References: <1303352024.10598834.1641987918801.JavaMail.zimbra@u-pem.fr> Message-ID: <3d3a4d14-14ed-ce41-1c77-11f642e6dcc4@oracle.com> > Let's take a detour, and try to answer to the question, how do we do reflection on method with Q-types ? > Given that reflection is using the class java.lang.Class, it means that we need a class that represents a L-type and a class that represent a Q-type. Correct.? In addition to all sorts of VM reasons for this, users need a way of distinguishing between ??? m(Point.ref p) and ??? m(Point p) with reflection, MH.Lookup, etc. > The class that represents a Q-type does not have to be a 'real' class, the same way int.class (Integer.TYPE) is not a real class, > it's a mirror class that represent 'I' in the method signature, same with void.class (Void.TYPE) represent 'V'. Correct.? We've been calling these "secondary mirrors".? Note that with extended primitives, it is possible (though not required) that the secondary mirror reflect almost everything that the primary does -- methods, fields, supertypes, etc.? Alternately, they could be more like int.class, which is pretty limited. Unfortunately, though, we run into a user expectation problem: users will reasonable expect that if they reflect over ??? m(Point p) they will get Point.class back.? Since the user views "Point" as the primary type (Point.ref is a supporting player), they'll expect Point.class to be the "important" mirror.? This asymmetry with the current state of affairs is a challenge. > So the class that represents a Q-type is a mirror class synthesized by the VM at runtime. > Given that it is synthesized, i believe it should be only accessible using a method of java.lang.Class, > by example > Complex.class.mirrorType() // the name "mirror" is perhaps not the best Stepping back, what you're saying is to expose only Point.class, and make it harder to get at the "other" mirror, such as by relegating it to a method like Class::otherMirror.? This is a reasonable move, but it does run smack into the above problem -- that if there's one mirror, users will expect Point.class to reflect the Point type they are most familiar with, which is QPoint. > I propose to introduce a new kind of primitive class, the builtin primitive class, that Whether we call it that, or summon it by magic, we are stuck with some form of this anyway, because of the asymmetries you've observed; the existing int/Integer pairs have subtle differences from declared Point/Point.ref pairs.? There will have to be some special pleading for built-in/legacy/basic primitives. I think where you're going is some flavor of this: ??? special-legacy-backwards value class Integer ??????? with-legacy-primitive int { ... } where we declare the Integer/int pair, but do so through Integer, but declare it to be backwards from the other primitives, where there are only eight of these allowed. From forax at univ-mlv.fr Wed Jan 12 12:31:17 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 12 Jan 2022 13:31:17 +0100 (CET) Subject: VM model and aconst_init Message-ID: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> I've some troubles to wrap my head around those two sentences """ aconst_init is the analogue of new for value objects; it leaves a reference to the initial value for a value class on the stack. This initial value is guaranteed to not be equal to null. The sole operand of this bytecode is a reference to a CONSTANT_Class item giving the internal binary name of the value class (not its Q descriptor). """ and """ Both withfield and aconst_init return a Q type if and only if their class is a primitive class. """ The second is ambiguous because it's not clear if aconst_init can return a L-type. I suppose it can not but this is not clear at all. If this is the case, what is the use case for withfield taking a L-type as parameter ?? regards, R?mi From brian.goetz at oracle.com Wed Jan 12 12:45:34 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 12 Jan 2022 07:45:34 -0500 Subject: VM model and aconst_init In-Reply-To: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> Message-ID: <271fec68-0102-4460-8ba4-fd1ff8c66994@oracle.com> Both value and primitive classes use the aconst_init / withfield initialization protocol.? The former is an L-type (QOptional is illegal); the latter uses Q types for initialization. Value classes ?-> are L types ?-> which are references ?-> references can be null ?-> VM manages where the nullity bits are so for value classes, there has to be some way of starting the ball rolling with an uninitialized, but non-null, value.? We can then modify the fields (but not the nullity) with `withfield`. On 1/12/2022 7:31 AM, Remi Forax wrote: > I've some troubles to wrap my head around those two sentences > > """ > aconst_init is the analogue of new for value objects; it leaves a reference to the initial value for a value class on the stack. This initial value is guaranteed to not be equal to null. The sole operand of this bytecode is a reference to a CONSTANT_Class item giving the internal binary name of the value class (not its Q descriptor). > """ > > and > """ > Both withfield and aconst_init return a Q type if and only if their class is a primitive class. > """ > > The second is ambiguous because it's not clear if aconst_init can return a L-type. I suppose it can not but this is not clear at all. > > If this is the case, what is the use case for withfield taking a L-type as parameter ?? > > regards, > R?mi From forax at univ-mlv.fr Wed Jan 12 13:10:35 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Wed, 12 Jan 2022 14:10:35 +0100 (CET) Subject: L-type, Q-type and class mirrors In-Reply-To: <3d3a4d14-14ed-ce41-1c77-11f642e6dcc4@oracle.com> References: <1303352024.10598834.1641987918801.JavaMail.zimbra@u-pem.fr> <3d3a4d14-14ed-ce41-1c77-11f642e6dcc4@oracle.com> Message-ID: <1680706175.10653631.1641993035945.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Brian Goetz" > To: "Remi Forax" , "valhalla-spec-experts" > Sent: Wednesday, January 12, 2022 1:27:07 PM > Subject: Re: L-type, Q-type and class mirrors >> Let's take a detour, and try to answer to the question, how do we do reflection >> on method with Q-types ? >> Given that reflection is using the class java.lang.Class, it means that we need >> a class that represents a L-type and a class that represent a Q-type. > > Correct.? In addition to all sorts of VM reasons for this, users need a > way of distinguishing between > > ??? m(Point.ref p) > and > ??? m(Point p) > > with reflection, MH.Lookup, etc. > >> The class that represents a Q-type does not have to be a 'real' class, the same >> way int.class (Integer.TYPE) is not a real class, >> it's a mirror class that represent 'I' in the method signature, same with >> void.class (Void.TYPE) represent 'V'. > > Correct.? We've been calling these "secondary mirrors".? Note that with > extended primitives, it is possible (though not required) that the > secondary mirror reflect almost everything that the primary does -- > methods, fields, supertypes, etc.? Alternately, they could be more like > int.class, which is pretty limited. > > Unfortunately, though, we run into a user expectation problem: users > will reasonable expect that if they reflect over > > ??? m(Point p) > > they will get Point.class back.? Since the user views "Point" as the > primary type (Point.ref is a supporting player), they'll expect > Point.class to be the "important" mirror.? This asymmetry with the > current state of affairs is a challenge. There are two asymmetries, and we need to pick one. Either we allow Point.ref.class and in that case, you are right that EnclosingClass.class.getMethod("m").getParameterType(0) as to return a class that represent a QPoint;. Or we consider that Point.ref is a type, so Point.ref.class does not exist the same way List.class or List.class do not exist, in that case, Point.class.mirror() does not to have fields, methods, etc attached, the same way int.class has no fields/methods. In the latter case, it's also important that the secondary mirror has an invalid name, a name like "Point\mirror" or whatever that shows that it's a synthetic class so like Class.forName("int") does not work, Class.forName(Point.class.mirror()) should not work too. > >> So the class that represents a Q-type is a mirror class synthesized by the VM at >> runtime. >> Given that it is synthesized, i believe it should be only accessible using a >> method of java.lang.Class, >> by example >> Complex.class.mirrorType() // the name "mirror" is perhaps not the best > > Stepping back, what you're saying is to expose only Point.class, and > make it harder to get at the "other" mirror, such as by relegating it to > a method like Class::otherMirror.? This is a reasonable move, but it > does run smack into the above problem -- that if there's one mirror, > users will expect Point.class to reflect the Point type they are most > familiar with, which is QPoint. see above > >> I propose to introduce a new kind of primitive class, the builtin primitive >> class, that > > Whether we call it that, or summon it by magic, we are stuck with some > form of this anyway, because of the asymmetries you've observed; the > existing int/Integer pairs have subtle differences from declared > Point/Point.ref pairs.? There will have to be some special pleading for > built-in/legacy/basic primitives. > > I think where you're going is some flavor of this: > > ??? special-legacy-backwards value class Integer > ??????? with-legacy-primitive int { ... } > > where we declare the Integer/int pair, but do so through Integer, but > declare it to be backwards from the other primitives, where there are > only eight of these allowed. yes, but i think it should be a primitive class not a value class. R?mi From forax at univ-mlv.fr Wed Jan 12 13:14:12 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Wed, 12 Jan 2022 14:14:12 +0100 (CET) Subject: VM model and aconst_init In-Reply-To: <271fec68-0102-4460-8ba4-fd1ff8c66994@oracle.com> References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> <271fec68-0102-4460-8ba4-fd1ff8c66994@oracle.com> Message-ID: <1555310797.10655508.1641993252455.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "Remi Forax" , "valhalla-spec-experts" > > Sent: Wednesday, January 12, 2022 1:45:34 PM > Subject: Re: VM model and aconst_init > Both value and primitive classes use the aconst_init / withfield initialization > protocol. The former is an L-type (QOptional is illegal); the latter uses Q > types for initialization. > Value classes > -> are L types > -> which are references > -> references can be null > -> VM manages where the nullity bits are > so for value classes, there has to be some way of starting the ball rolling with > an uninitialized, but non-null, value. We can then modify the fields (but not > the nullity) with `withfield`. Ok, but in that case how the verifiers know if aconst_init generate a Q-type or a L-type given that aconst_init takes a CONSTANT_CLASS and not a descriptor as parameter. R?mi > On 1/12/2022 7:31 AM, Remi Forax wrote: >> I've some troubles to wrap my head around those two sentences >> """ >> aconst_init is the analogue of new for value objects; it leaves a reference to >> the initial value for a value class on the stack. This initial value is >> guaranteed to not be equal to null. The sole operand of this bytecode is a >> reference to a CONSTANT_Class item giving the internal binary name of the value >> class (not its Q descriptor). >> """ >> and >> """ >> Both withfield and aconst_init return a Q type if and only if their class is a >> primitive class. >> """ >> The second is ambiguous because it's not clear if aconst_init can return a >> L-type. I suppose it can not but this is not clear at all. >> If this is the case, what is the use case for withfield taking a L-type as >> parameter ?? >> regards, >> R?mi From brian.goetz at oracle.com Wed Jan 12 13:30:00 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 12 Jan 2022 08:30:00 -0500 Subject: [External] : Re: VM model and aconst_init In-Reply-To: <1555310797.10655508.1641993252455.JavaMail.zimbra@u-pem.fr> References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> <271fec68-0102-4460-8ba4-fd1ff8c66994@oracle.com> <1555310797.10655508.1641993252455.JavaMail.zimbra@u-pem.fr> Message-ID: <81fd637d-5538-2ea5-c674-8681ddf097a6@oracle.com> The operand of C_Class is a weird beast.? It can be an internal name (com/foo/Bar), but it can also be a *descriptor* for an array type.? Valhalla extends it to allow Q descriptors as well (but not L descriptors -- there should be one way to say C_Class[String].) On 1/12/2022 8:14 AM, forax at univ-mlv.fr wrote: > Ok, but in that case how the verifiers know if aconst_init generate a > Q-type or a L-type given that aconst_init takes a CONSTANT_CLASS and > not a descriptor as parameter. From daniel.smith at oracle.com Wed Jan 12 16:08:14 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 12 Jan 2022 16:08:14 +0000 Subject: EG meeting, 2022-01-12 Message-ID: EG Zoom meeting today at 5pm UTC (9am PDT, 12pm EDT). We've had a flurry of activity in the last month. The list is below; probably best for those involved to decide whether they feel like the topic is settled, or deserves further discussion. "JEP update: Primitive Classes": revisions to JEP 401 to complement the new Value Objects JEP "Enhancing java.lang.constant for Valhalla": discussing whether/how the Preload attribute is manifested in this API "We have to talk about 'primitive'": terminology discussion regarding the word "primitive" "JEP update: Value Objects": followup discussion about inference/checking/usage of the IdentityObject & ValueObject interfaces "Updated State of Valhalla documents": revisions to the big-picture-oriented State of Valhalla documents to reflect recent design changes "Why do we need .ref class for primitive class?": clearing up how '.ref' types are encoded in bytecode "L-type, Q-type and class mirrors": discussing how '.ref' types are modeled by reflection "VM model and aconst_init": discussion how 'aconst_init' interacts with 'null' and verification types From kevinb at google.com Wed Jan 12 16:14:49 2022 From: kevinb at google.com (Kevin Bourrillion) Date: Wed, 12 Jan 2022 08:14:49 -0800 Subject: EG meeting, 2022-01-12 In-Reply-To: References: Message-ID: Can't attend today, but.... On Wed, Jan 12, 2022 at 8:08 AM Dan Smith wrote: "We have to talk about 'primitive'": terminology discussion regarding the > word "primitive" > I'm planning to continue this conversation on-thread, so no worries for now. -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From forax at univ-mlv.fr Wed Jan 12 16:45:04 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Wed, 12 Jan 2022 17:45:04 +0100 (CET) Subject: [External] : Re: VM model and aconst_init In-Reply-To: <81fd637d-5538-2ea5-c674-8681ddf097a6@oracle.com> References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> <271fec68-0102-4460-8ba4-fd1ff8c66994@oracle.com> <1555310797.10655508.1641993252455.JavaMail.zimbra@u-pem.fr> <81fd637d-5538-2ea5-c674-8681ddf097a6@oracle.com> Message-ID: <1823500600.10802162.1642005904641.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "Remi Forax" > Cc: "valhalla-spec-experts" > Sent: Wednesday, January 12, 2022 2:30:00 PM > Subject: Re: [External] : Re: VM model and aconst_init > The operand of C_Class is a weird beast. It can be an internal name > (com/foo/Bar), but it can also be a *descriptor* for an array type. Valhalla > extends it to allow Q descriptors as well (but not L descriptors -- there > should be one way to say C_Class[String].) Your explanation maks sense but it's not was this sentence says """ The sole operand of this bytecode is a reference to a CONSTANT_Class item giving the internal binary name of the value class (not its Q descriptor) """ R?mi > On 1/12/2022 8:14 AM, [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] wrote: >> Ok, but in that case how the verifiers know if aconst_init generate a Q-type or >> a L-type given that aconst_init takes a CONSTANT_CLASS and not a descriptor as >> parameter. From daniel.smith at oracle.com Wed Jan 12 23:26:32 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 12 Jan 2022 23:26:32 +0000 Subject: Terminology bikeshedding summary Message-ID: <1F2A06FF-582A-44A6-BD01-BA52673DB716@oracle.com> EG meeting touched on a few different renaming ideas to consider. Wanted to put those all down in one place. Personally, I don't have strong feelings about any of these. I think the status quo is reasonable, and these changes would probably be fine, too (modulo coming up with the right word in some cases). Some of this faces an inherent conflict between different views/models/users, and we won't be able to please everybody; the choice is about which of those views/models/users we want to emphasize. #1: value class vs. [something else] class I don't recall a concrete proposal, but this reflects some discomfort with the idea that "value" is being used differently than when we talk about "the value of a variable" or, from some other languages, a "value type". #2: primitive class vs. bare value class vs. [something else] value class primitive type vs. bare value type vs. [something else] value type primitive value vs. bare value vs. [something else] value Three objections to "primitive": - Asking developers to loosen their notion of "primitive" to include user-defined, composite types may be too much of a conceptual shift/abuse of terminology - The original 8 primitives still have a number of properties that make them special, perhaps deserving a convenient, one-word name (on the other hand, *not* giving them a distinct name helpfully communicates that these differences should not be considered significant...) - It's useful to highlight that these things are still a kind of value class, in some sense reducing the conceptual overhead (there are identity classes and value classes, everything else is secondary) #3: primitive value vs. object We're trying to make a distinction between primitive values being "class instances" and calling them "objects", but for many developers, especially beginners, that sounds like meaningless pedantry. We might be over-rotating on the subtle differences that make these entities distinct, rather than acknowledging that, with their fields and methods, they will be commonly understood to be a kind of object. From john.r.rose at oracle.com Thu Jan 13 00:05:19 2022 From: john.r.rose at oracle.com (John Rose) Date: Thu, 13 Jan 2022 00:05:19 +0000 Subject: Terminology bikeshedding summary In-Reply-To: <1F2A06FF-582A-44A6-BD01-BA52673DB716@oracle.com> References: <1F2A06FF-582A-44A6-BD01-BA52673DB716@oracle.com> Message-ID: #3 is more like: primitive value vs. bare object Or primitive value vs. [something else] object Where object is split not only into value object and identity objects, but bare (value) objects are another split. You get bare vs heap just as you get value vs identity as cleavage planes in the universe of objects. Also: Legacy primitives would not be objects in any case. But we can mock up classes to wrap and/or emulate them and even declare in the user model that these very special classes in some sense ?are? the primitives. > On Jan 12, 2022, at 3:27 PM, Dan Smith wrote: > > #3: > > primitive value vs. object From daniel.smith at oracle.com Thu Jan 13 00:21:55 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 13 Jan 2022 00:21:55 +0000 Subject: JEP update: Classes for the Basic Primitives Message-ID: I'm made some revisions to JEP 402 to better track with the revised JEP 401?in particular backing off of "everything is an object". There were some short-lived changes where I more aggressively pursued the idea that the class was named 'int', but I've backed off of that, too. Now taking the more conservative approach that there is a primitive class named 'Integer', but by special rule its primitive type is expressed 'int', and the class name refers to the reference type. I realize that much of this could potentially change?reflection story, terminology, etc. But consider this the "plan of record" for now. https://bugs.openjdk.java.net/browse/JDK-8259731 From john.r.rose at oracle.com Wed Jan 19 08:40:15 2022 From: john.r.rose at oracle.com (John Rose) Date: Wed, 19 Jan 2022 00:40:15 -0800 Subject: VM model and aconst_init In-Reply-To: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> Message-ID: <1BB6397E-2103-4AF3-A857-02CF2FA0B453@oracle.com> I think (based on our most recent conversations) that `aconst_init` can return a Q-type for B3 types and an L-type for B2 types. And likewise for the input and output of `withfield`. The net result is that both bytecodes need to be permissive about L and Q types, because B2 and B3 translation strategies require distinct parallel use cases. This is probably not clear in the docs, but I think it makes sense. 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`. On 12 Jan 2022, at 4:31, Remi Forax wrote: > I've some troubles to wrap my head around those two sentences > > """ > aconst_init is the analogue of new for value objects; it leaves a > reference to the initial value for a value class on the stack. This > initial value is guaranteed to not be equal to null. The sole operand > of this bytecode is a reference to a CONSTANT_Class item giving the > internal binary name of the value class (not its Q descriptor). > """ > > and > """ > Both withfield and aconst_init return a Q type if and only if their > class is a primitive class. > """ > > The second is ambiguous because it's not clear if aconst_init can > return a L-type. I suppose it can not but this is not clear at all. > > If this is the case, what is the use case for withfield taking a > L-type as parameter ?? > > regards, > R?mi From john.r.rose at oracle.com Wed Jan 19 08:46:21 2022 From: john.r.rose at oracle.com (John Rose) Date: Wed, 19 Jan 2022 00:46:21 -0800 Subject: VM model and aconst_init In-Reply-To: <1555310797.10655508.1641993252455.JavaMail.zimbra@u-pem.fr> References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> <271fec68-0102-4460-8ba4-fd1ff8c66994@oracle.com> <1555310797.10655508.1641993252455.JavaMail.zimbra@u-pem.fr> Message-ID: <40C9D97F-49E9-4A5A-97E3-BDCFDB8CE3D7@oracle.com> On 12 Jan 2022, at 5:14, forax at univ-mlv.fr wrote: > Ok, but in that case how the verifiers know if aconst_init generate a > Q-type or a L-type given that aconst_init takes a CONSTANT_CLASS and > not a descriptor as parameter. In the terms of my previous message, the `CONSTANT_Class` item in the CP must be ?modal?, refer to either an L-type or Q-type. That?s true whether it is the direct operand of `aconst_init` or a sub-operand of `withfield`. All of this (about pervasive use of bimodal `C_Class` items) is hard to avoid. I would have preferred to say that `C_Class` items are always for L-types, but that runs afoul of other requirements, notably that Point.class be the mirror for Q-Point not L-Point if Point is a B3-capable class. From john.r.rose at oracle.com Wed Jan 19 08:47:26 2022 From: john.r.rose at oracle.com (John Rose) Date: Wed, 19 Jan 2022 00:47:26 -0800 Subject: [External] : Re: VM model and aconst_init In-Reply-To: <1823500600.10802162.1642005904641.JavaMail.zimbra@u-pem.fr> References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> <271fec68-0102-4460-8ba4-fd1ff8c66994@oracle.com> <1555310797.10655508.1641993252455.JavaMail.zimbra@u-pem.fr> <81fd637d-5538-2ea5-c674-8681ddf097a6@oracle.com> <1823500600.10802162.1642005904641.JavaMail.zimbra@u-pem.fr> Message-ID: <01754CF4-6757-4BDD-99B3-E82A5FCD4C62@oracle.com> On 12 Jan 2022, at 8:45, forax at univ-mlv.fr wrote: >> From: "Brian Goetz" >> To: "Remi Forax" >> Cc: "valhalla-spec-experts" >> Sent: Wednesday, January 12, 2022 2:30:00 PM >> Subject: Re: [External] : Re: VM model and aconst_init > >> The operand of C_Class is a weird beast. It can be an internal name >> (com/foo/Bar), but it can also be a *descriptor* for an array type. Valhalla >> extends it to allow Q descriptors as well (but not L descriptors -- >> there >> should be one way to say C_Class[String].) > Your explanation maks sense but it's not was this sentence says > """ > The sole operand of this bytecode is a reference to a CONSTANT_Class item giving the internal binary name of the value class (not its Q descriptor) > """ > That was? aspirational. From brian.goetz at oracle.com Wed Jan 19 23:57:25 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 19 Jan 2022 18:57:25 -0500 Subject: SoV-2: weak references In-Reply-To: References: Message-ID: It certainly seems that all the choices are bad. The "obvious" choice is to simply say that WeakReference makes no sense, in that it sidesteps the hard semantics questions.? My fear is that this will cause new failures, where existing libraries that toss objects into WHMs to cache derived results, will start to experience new failures when the value types show up in the heap (after all, WeakReference::new takes Object.)? And we'll have to have something to tell those users, because they declared a WeakHashMap, and someone created a value subtype of User -- which seems entirely valid. It is possible we could do something special with WHM, since it is layered atop WR, but that still begs the question -- what? On 1/19/2022 4:28 PM, Dan Heidinga wrote: > In the Dec 2021 update to State of Valhalla, part 2: The language > model, the section on "Identity-sensitive operations" states this > about weak references: > >> Weak references to value objects that contain no references to identity objects should never be >> cleared; weak references to value objects that contain references to identity objects should be >> cleared when those objects are no longer strongly reachable. > I'm concerned that this approach makes using weak references correctly > for values very difficult, especially in the face of scalarization. > > Scalarization allows separating values into their individual > components and flowing them separately through the code. This can - > with some caveats for decompilation and maybe other features - result > in very different lifetimes for each separate component, with some > object references going out of scope "earlier" than expected. The > source code will of course show the *entire* value being carried along > even though only a single component may survive post-scalarization > which means developers will see very different behaviour between the > interpreter (which doesn't scalarize) and jitted code - making it very > easy to write tests that give them the wrong impression. > > Reference::reachabilityFence was added in Java 9 to help users deal > with premature finalization and has gotten little attention from most > users. The proposed semantics for WeakReferences on value objects will > drag the reachabilityFence method into the light and expose users to a > problem they'd rather not have. Just as we avoided exposing most > users to tearing of their (bucket 2) value objects, we shouldn't > expose them to having to reason about complicated reachability rules. > > Which is to say, Reference objects and value objects need to be > incompatible to give users what they expect. Anything else is loading > a gun and pointing it at their feet. > > Here's an example of why the suggested rules for weak references using > the identity objects may bring reachability issues to the forefront > (where we don't want them): > > public value class ResourcePair { > static long nativePtr; > static IdentityObject companion; > > ResourcePair(long ptr, IdentityObject o) { > nativePtr = ptr; > companion = o; > } > } > > class PtrRef extends WeakReference { > long nativePtr; > > PtrRef(ResourcePair p, ReferenceQueue q) { > super(p, q); > nativePtr = p.nativePtr; > } > > void free() { /* do something to nativePtr */ } > } > > If the `companion` object of the ResourcePair value goes out of scope > before `nativePtr` is done being used - the issue of reachabilityFence > - the ReferenceQueue processor could call free() before the > `nativePtr` is done being used. Scalarization will make this more > common and even very common if we're getting the benefits we expect > from it. > > --Dan > From wrprice at gmail.com Thu Jan 20 22:47:05 2022 From: wrprice at gmail.com (William Price) Date: Thu, 20 Jan 2022 16:47:05 -0600 Subject: Terminology bikeshedding summary Message-ID: Hello all, I've been observing the Valhalla-spec-experts list via the archives for quite a while and enjoying the progress. I am eagerly awaiting the new JEPs and meanwhile thinking to myself how I would mentor other developers on how and when to (not) use these features once released into the language. With the recent thread on bikeshedding your B1/B2/B3 categories, some thoughts popped into my head and I'd like to be so bold as to share them from the perspective of a (power) user and someone acting in a mentor role. > #1. value class vs. [something else] class Value types will have no object identity, and that's important to understand from the perspective of monitors and how the JVM treats such types in memory, but I don't think most developers synchronize on their objects. The effects on `==` vs `equals(Object)` behaviors are probably the most important for "lay" and beginning Java devs to get right. So in my head I read the above as, "value equivalence class" vs "intrinsic identity class", with a focus on the behavior of the `==` operator. Of course, that's too verbose as far as syntax goes. I think "value class" is clear and concise for source code. > #2. > primitive class vs. bare value class vs. [something else] value class > primitive type vs. bare value type vs. [something else] value type > primitive value vs. bare value vs. [something else] value I'll add my voice behind those saying leave "primitive" alone as much as possible. The definition of the word matches w/ the specialized behavior supported by the underlying VM. The set of primitive types remains the base case of all data structures enabled by the type system. Long-time users, at least those who are less receptive to change, are going to have enough on their plate w/ the introduction of "value class". The use of the word "bare" isn't sitting well with me, either; probably because it will require a verbose definition to have any meaning to most users. I don't think developers are going to think of their values as "clothed" or not. ;-) The closest use of "bare" in computation that I can think of is "bare metal" referring to hardware and the lack of virtualization (or abstraction!). I can see an argument that flattened layouts are closer to the "bare" metal, but that's a VM/hardware centric view. To your average developer, Valhalla is introducing quite a bit more complexity to the VM abstraction between the source code and the metal. (That's assuming the developer cares to understand how the VM works its magic in the first place.) >From the above two paragraphs, I'm squarely in camp "[something else]" and I'll get to that something in a moment. > #3: > primitive value vs. object > > We're trying to make a distinction between primitive values being "class instances" and > calling them "objects", but for many developers, especially beginners, that sounds like > meaningless pedantry. +1. Let "objects are instances of a class" remain true and apply to all of these new types. One only has to look through the Java Tutorials section on Creating Objects[1] to see that the developer's syntax when working with proposed value- and [something else] classes will largely mirror the existing experience with classic objects -- all the way from `new`, constructor-call syntax, and `instanceof`. What Valhalla is proposing to change is that [something else], which had previously been limited to primitives, are now objects (instances) too, but not all objects have identity. If that interpretation is correct, then "everything is an object" isn't too hard to explain. > #3 is more like: > > primitive value vs. bare object > Or > primitive value vs. [something else] object > > Where object is split not only into value object and identity objects, but bare (value) objects > are another split. You get bare vs heap just as you get value vs identity as cleavage planes > in the universe of objects. > > Also: > > Legacy primitives would not be objects in any case. But we can mock up classes to wrap > and/or emulate them and even declare in the user model that these very special classes in > some sense ?are? the primitives. This is what caused a *click* in my head and prompted this e-mail. It reminded me of how, after Java 5 introduced `enum` types, I don't recall myself or any colleagues ever referring to "enum classes" outside of those dealing directly with reflection or bytecode manipulation. They're simply "enums". Despite the initial shock that one is defined in source as a `[] enum` absent the `class` or `interface` keyword, we all now understand that there's a class behind it, just as we previously understood that there was a `java.lang.Class` behind a proper `interface` type. Similarly, the primitive-value/bare/[something else] type WILL be understood to be both an object, and a value-type, and have a class representation at runtime/reflection. In the documentation, of course, it will need to be heavily emphasized that [something else] is not just flattenable but is also a value type. However, I don't feel that these attributes require heavy emphasis in the formal name. I favor "public [something]" over "public [something [value]] class", just as Java 5 did for enum. I've been working professionally in Java land for a long time, but when I look at the attributes of primitive/bare/[something else], as in JEP 401, what I see is a struct. Not the struct from Java's roots in C/C++ but the struct[2] from C#, which could be considered a closer comparison these days. Though the Valhalla proposal is not 1:1 with C#, the latter's (readonly) structs have methods, support inlining outside of heap, and many similar characteristics. This similarity may bring with it familiarity to developers coming from .NET ecosystems as a tangential benefit. For developers unfamiliar with C#, "struct" is still existing terminology that -- without any further definition -- should evoke thoughts of data structures and/or in-memory representation. Given this is a thread on bikeshedding, my humble opinion for syntax related to these new Valhalla classes/types is as follows: class Foo // identity-based L-type value class Bar // value-based L-type struct Qat // flattened value Q-type In recognition that structs in other languages do not implicitly impose `final` on their fields, and in the hope that perhaps direct inlined mutation might be allowed by future Java versions, I would also suggest that javac require an *explicit* `final` modifier on fields declared within a `struct` rather than rely on the implicit behavior of general value classes. Yes, it's more verbose, but that's countered by advice to users that they should use `struct` judiciously (due to tearing, etc.) and not go overboard with use -- in other words, the impact felt from that extra boilerplate should be rare. Of course, if you're certain you'll never-ever allow such fields to be non-final, then my above argument is moot. But I have had real-life cases where I think I would benefit from encapsulating and inlining struct-like classes within another class, avoiding the indirection, but supporting in-place mutation of individual struct fields. Implicitly final fields do not allow you to change your mind on this point without breaking source backwards compatibility in a way that might not be noticed upon a subsequent recompile. I'll stick my neck out farther and bring in this snippet from a different thread on the list. Brian, you wrote in response[3] to Remi: > I think where you're going is some flavor of this: > > special-legacy-backwards value class Integer > with-legacy-primitive int { ... } I may have missed the need for [special-legacy-backwards] separate from [with-legacy-primitive]. Given that [with-legacy-primitive] would be reserved for the JDK alone and not user-defined classes, I thought of several existing keywords that could be used in that place without *too* much head-scratching. But one in particular stood out for its potential simplicity: public struct Integer extends Number implements int, Comparable { private final int value; ^^^^ /* ... */ } - `int` and the other Language Keywords for primitives are not interfaces, so this is the special JDK-reserved behavior and the only "smell" that jumps out to me - `implements int`, read as English, says exactly what you mean for readers to understand - no new keywords or reserved identifiers required - The "backwards" behavior (Integer.class == int.ref?) is only applied to these special cases - Does not use the defined type (Integer) as a member field (non-circular) That's more than enough for now. Thank you for reading this far, and I'm eagerly anticipating the fruits of both Valhalla and Loom. [1]: https://docs.oracle.com/javase/tutorial/java/javaOO/objectcreation.html [2]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct [3]: https://mail.openjdk.java.net/pipermail/valhalla-spec-experts/2022-January/001764.html -- William Price From forax at univ-mlv.fr Tue Jan 25 10:50:10 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 25 Jan 2022 11:50:10 +0100 (CET) Subject: VM model and aconst_init In-Reply-To: <1BB6397E-2103-4AF3-A857-02CF2FA0B453@oracle.com> References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> <1BB6397E-2103-4AF3-A857-02CF2FA0B453@oracle.com> Message-ID: <1577708639.3794959.1643107810290.JavaMail.zimbra@u-pem.fr> > From: "John Rose" > To: "Remi Forax" > Cc: "valhalla-spec-experts" > Sent: Wednesday, January 19, 2022 9:40:15 AM > Subject: Re: VM model and aconst_init > I think (based on our most recent conversations) > that aconst_init can return a Q-type for B3 types > and an L-type for B2 types. And likewise for the input > and output of withfield . Yes ! > The net result is that both bytecodes need to be > permissive about L and Q types, because B2 and B3 > translation strategies require distinct parallel use > cases. > This is probably not clear in the docs, but I think > it makes sense. > 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. 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 From brian.goetz at oracle.com Tue Jan 25 14:57:17 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 25 Jan 2022 14:57:17 +0000 Subject: VM model and aconst_init In-Reply-To: References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> <1BB6397E-2103-4AF3-A857-02CF2FA0B453@oracle.com> <1577708639.3794959.1643107810290.JavaMail.zimbra@u-pem.fr> Message-ID: <3CFB7ED1-2114-40A1-9EE1-81DC5D3647FF@oracle.com> 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 m(); T[] arr(); } we can use also sorts of bridging/casting tricks to make Foo 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. On Jan 25, 2022, at 9:40 AM, Dan Heidinga > 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.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 From forax at univ-mlv.fr Tue Jan 25 15:36:16 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 25 Jan 2022 16:36:16 +0100 (CET) Subject: VM model and aconst_init In-Reply-To: <3CFB7ED1-2114-40A1-9EE1-81DC5D3647FF@oracle.com> References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> <1BB6397E-2103-4AF3-A857-02CF2FA0B453@oracle.com> <1577708639.3794959.1643107810290.JavaMail.zimbra@u-pem.fr> <3CFB7ED1-2114-40A1-9EE1-81DC5D3647FF@oracle.com> Message-ID: <1534054066.4049476.1643124976954.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "Dan Heidinga" > Cc: "Remi Forax" , "John Rose" , > "valhalla-spec-experts" > 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 m(); > T[] arr(); > } > we can use also sorts of bridging/casting tricks to make Foo 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.ref field; } class Bar extends Foo { void m() { field = Qcomplex;.new(); } } R?mi >> On Jan 25, 2022, at 9:40 AM, Dan Heidinga < [ mailto:heidinga at redhat.com | >> heidinga at 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.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 >>> ] From forax at univ-mlv.fr Tue Jan 25 21:39:25 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 25 Jan 2022 22:39:25 +0100 (CET) Subject: The interfaces IdentityObject and ValueObject must die ! Message-ID: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> I think we should revisit the idea of having the interfaces IdentityObject/ValueObject. They serve two purposes 1/ documentation: explain the difference between an identity class and a value class 2/ type restriction: can be used as type or bound of type parameter for algorithms that only works with identity class Sadly, our design as evolved but not those interfaces, they do not work well as type restriction, because the type is lost once an interface/j.l.Object is used and the cost of their introduction is higher than previously though. 1/ documentation - Those interface split the possible types in two groups but the spec split the types in 3, B1/B2/B3, thus they are not aligned anymore with the new design. - they will be noise in the future, for Valhalla, the separation between identity object and value object may be important but from a POV of someone learning/discovering the language it's a corner case (like primitives are). This is even more true with the introduction of B2, you can use B2 for a long time without knowing what a value type is. So having such interfaces front and center is too much. 2/ as type - Being a value class or a primitive class is a runtime behavior not a compile time behavior, so representing them with special types (a type is a compile time construct) will always be an approximation. As a consequence, you can have an identity class (resp value class) typed with a type which is not a subtype of IdentityObject (resp ValueObject). This discrepancy is hard to grasp for beginners (in fact not only for beginners) and make IdentityObject/ValueObject useless because if a method takes an IdentityObject as parameter but the type is an interface, users will have to cast it into an IdentityObject which defeat the purpose of having such interface. (This is the reason why ObjectOutputStream.writeObject() takes an Object as parameter and not Serializable) And the cost of introduction is high - they are not source backward compatible class A {} class B {} var list = List.of(new A(), new B()); List list2 = list; - they are not binary backward compatible new Object().getClass() != Object.class - at least IdentityObject needs to be injected at runtime, which by side effect requires patching several VM components: reflection/j.l.invoke, JVMTI, etc making the VM spec more complex that it should be. You may not agree with some of my points, nevertheless, the current design is not aligned with the B1/B2/B3 world, have a high cost and with little benefit so i think it's time to remove those interfaces from the design. regards, R?mi From daniel.smith at oracle.com Wed Jan 26 05:20:07 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 26 Jan 2022 05:20:07 +0000 Subject: The interfaces IdentityObject and ValueObject must die ! In-Reply-To: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> References: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> Message-ID: <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> > On Jan 25, 2022, at 2:39 PM, Remi Forax wrote: > > I think we should revisit the idea of having the interfaces IdentityObject/ValueObject. > > They serve two purposes > 1/ documentation: explain the difference between an identity class and a value class And, in particular, describe the differences in runtime behavior. > 2/ type restriction: can be used as type or bound of type parameter for algorithms that only works with identity class > > Sadly, our design as evolved but not those interfaces, they do not work well as type restriction, because > the type is lost once an interface/j.l.Object is used and the cost of their introduction is higher than previously though. Not sure there's a problem here. For example, if it's important to constrain type parameters to be identity classes, but there's already/also a need for an interface bound, nothing wrong with saying: If the *use site* may have lost track of the types it would need to satisfy the bound, then, sure, better to relax the bound. So, not useful in some use cases, useful in others. Other purposes for these interfaces: 3/ dynamic tagging: support a runtime test to distinguish between value objects and identity objects 4/ subclass restriction: allow authors of abstract classes and interfaces to restrict implementations to only value classes or identity classes 5/ identity-dependent abstract classes: implicitly identifying, as part of an abstract class's API, that the class requires/assumes identity subclasses > 1/ documentation > > - Those interface split the possible types in two groups > but the spec split the types in 3, B1/B2/B3, thus they are not aligned anymore with the new design. The identity class vs. value class distinction affects runtime semantics of class instances. Whether the class is declared as a value class or a primitive class, the instances get ValueObject semantics. The primitive type vs. reference type distinction is a property of *variables* (and other type uses); the runtime semantics of class instances don't change between the two. Being a primitive class is statically interesting, because it means you can use it as a primitive type, but at runtime it's not really important. In other words: I don't see a use case for distinguishing between primitive and value classes with different interfaces. > - they will be noise in the future, for Valhalla, the separation between identity object and value object > may be important but from a POV of someone learning/discovering the language it's a corner case > (like primitives are). This is even more true with the introduction of B2, you can use B2 for a long time without knowing what a value type is. So having such interfaces front and center is too much. I think it's notable that you get two different equality semantics?something you really ought to be aware of when working with a class. But it's a subjective call about how prominent that information should be. > 2/ as type > > - Being a value class or a primitive class is a runtime behavior not a compile time behavior, > so representing them with special types (a type is a compile time construct) will always be an approximation. > As a consequence, you can have an identity class (resp value class) typed with a type which is not a subtype > of IdentityObject (resp ValueObject). > > This discrepancy is hard to grasp for beginners (in fact not only for beginners) and make IdentityObject/ValueObject > useless because if a method takes an IdentityObject as parameter but the type is an interface, users will have > to cast it into an IdentityObject which defeat the purpose of having such interface. > > (This is the reason why ObjectOutputStream.writeObject() takes an Object as parameter and not Serializable) It is sometimes possible/useful to statically identify this runtime property. At other times, it's not. That's not an argument for never representing the property with static types. And even if you never pay attention to the static type, it turns out that the dynamic tagging and inheritance capabilities of interfaces are useful features for explaining/validating runtime behavior. > And the cost of introduction is high > > - they are not source backward compatible > class A {} > class B {} > var list = List.of(new A(), new B()); > List list2 = list; How about List? Yes, it's possible to disrupt inference, and touching *every class in existence* has the potential to be really disruptive. But we should validate that with some real-world code. > - they are not binary backward compatible > new Object().getClass() != Object.class This has nothing to do with the interfaces. This is based on the more general property that every class instance must belong to a class that is either an identity class or a value class. Interfaces are just the way we've chosen to encode that property. Abandoning the property entirely would be a bigger deal. > - at least IdentityObject needs to be injected at runtime, which by side effect requires patching several > VM components: reflection/j.l.invoke, JVMTI, etc making the VM spec more complex that it should be. Yes, there are some nice things about re-using an existing class feature rather than inventing a new one (we could have a new "class mode" or something); but it's a disadvantage that we then have to disrupt existing properties/behaviors. Summarizing, yes, there are some areas of concern or caution. But this remains the best way we've identified so far to achieve a lot of different goals. From daniel.smith at oracle.com Wed Jan 26 05:23:55 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 26 Jan 2022 05:23:55 +0000 Subject: EG meeting *canceled*, 2022-01-26 Message-ID: I've got a conflict tomorrow; let's cancel the EG meeting. There are a few ongoing email discussions, in particular about the State of Valhalla documents. If it's still useful, we can pick up those discussions next time. From forax at univ-mlv.fr Wed Jan 26 09:18:54 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Wed, 26 Jan 2022 10:18:54 +0100 (CET) Subject: The interfaces IdentityObject and ValueObject must die ! In-Reply-To: <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> References: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> Message-ID: <547491109.4463966.1643188734104.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "daniel smith" > To: "Remi Forax" > Cc: "valhalla-spec-experts" > Sent: Wednesday, January 26, 2022 6:20:07 AM > Subject: Re: The interfaces IdentityObject and ValueObject must die ! >> On Jan 25, 2022, at 2:39 PM, Remi Forax wrote: >> >> I think we should revisit the idea of having the interfaces >> IdentityObject/ValueObject. >> >> They serve two purposes >> 1/ documentation: explain the difference between an identity class and a value >> class > > And, in particular, describe the differences in runtime behavior. parts of the difference in runtime behavior, you still have the issue where to document the difference between a value class and a primitive class. > >> 2/ type restriction: can be used as type or bound of type parameter for >> algorithms that only works with identity class >> >> Sadly, our design as evolved but not those interfaces, they do not work well as >> type restriction, because >> the type is lost once an interface/j.l.Object is used and the cost of their >> introduction is higher than previously though. > > > Not sure there's a problem here. For example, if it's important to constrain > type parameters to be identity classes, but there's already/also a need for an > interface bound, nothing wrong with saying: > > > > If the *use site* may have lost track of the types it would need to satisfy the > bound, then, sure, better to relax the bound. So, not useful in some use cases, > useful in others. > > Other purposes for these interfaces: > > 3/ dynamic tagging: support a runtime test to distinguish between value objects > and identity objects aClass.isIdentityClass()/isValueClass()/isPrimitiveClass() does the same > > 4/ subclass restriction: allow authors of abstract classes and interfaces to > restrict implementations to only value classes or identity classes > > 5/ identity-dependent abstract classes: implicitly identifying, as part of an > abstract class's API, that the class requires/assumes identity subclasses both these examples are sub cases of using type restriction, so they are really under 2/. > >> 1/ documentation >> >> - Those interface split the possible types in two groups >> but the spec split the types in 3, B1/B2/B3, thus they are not aligned anymore >> with the new design. > > The identity class vs. value class distinction affects runtime semantics of > class instances. Whether the class is declared as a value class or a primitive > class, the instances get ValueObject semantics. > > The primitive type vs. reference type distinction is a property of *variables* > (and other type uses); the runtime semantics of class instances don't change > between the two. Being a primitive class is statically interesting, because it > means you can use it as a primitive type, but at runtime it's not really > important. > > In other words: I don't see a use case for distinguishing between primitive and > value classes with different interfaces. Primitive classes does not allow nulls and are tearable, following your logic, there should be a subclass of ValueObject named PrimitiveObject that reflects that semantics. This is especially useful when you have an array of PrimitiveObject, you know that a storing null in an array of PrimitiveObject will always generate a NPE at runtime and that you may have to use either the volatile semantics or a lock when you read/write values from/to the array of PrimitiveObject. For examples, public void m(PrimitiveObject[] array, int index) { array[index] = null; // can be a compile time error } public void swap(PrimitiveObject[] array, int i, int j) { // non tearable swap synchronized(lock) { var tmp = array[i]; array[i] = array[j]; array[j] = tmp; } } public class NullableArray ( // specialized generics private final boolean[] nullables; // use a side array to represent nulls private final T[] array; ... } Given that the model B1/B2/B3 has separated the runtime behaviors of the value class into 2 sub categories, I don't understand why the semantics of ==, synchronized() and weak reference of B2 is more important than the nullability and tearability of B3. > >> - they will be noise in the future, for Valhalla, the separation between >> identity object and value object >> may be important but from a POV of someone learning/discovering the language >> it's a corner case >> (like primitives are). This is even more true with the introduction of B2, you >> can use B2 for a long time without knowing what a value type is. So having such >> interfaces front and center is too much. > > I think it's notable that you get two different equality semantics?something you > really ought to be aware of when working with a class. But it's a subjective > call about how prominent that information should be. Most people don't call == on Object nor use synchronized on an object they do not control. > >> 2/ as type >> >> - Being a value class or a primitive class is a runtime behavior not a compile >> time behavior, >> so representing them with special types (a type is a compile time construct) >> will always be an approximation. >> As a consequence, you can have an identity class (resp value class) typed with a >> type which is not a subtype >> of IdentityObject (resp ValueObject). >> >> This discrepancy is hard to grasp for beginners (in fact not only for beginners) >> and make IdentityObject/ValueObject >> useless because if a method takes an IdentityObject as parameter but the type is >> an interface, users will have >> to cast it into an IdentityObject which defeat the purpose of having such >> interface. >> >> (This is the reason why ObjectOutputStream.writeObject() takes an Object as >> parameter and not Serializable) > > It is sometimes possible/useful to statically identify this runtime property. At > other times, it's not. That's not an argument for never representing the > property with static types. > > And even if you never pay attention to the static type, it turns out that the > dynamic tagging and inheritance capabilities of interfaces are useful features > for explaining/validating runtime behavior. Inheritance capabilities of interfaces does not work for Object and also as a side effect creates impossible types (i've forgotten that one). An impossible type, it's a type that can be declared but no class will ever match. Examples of impossible types, at declaration site interface I extends ValueObject {} interface J extends IdentityObject {} void foo() { } > >> And the cost of introduction is high >> >> - they are not source backward compatible >> class A {} >> class B {} >> var list = List.of(new A(), new B()); >> List list2 = list; > > How about List? > > Yes, it's possible to disrupt inference, and touching *every class in existence* > has the potential to be really disruptive. But we should validate that with > some real-world code. All we need is the inference to compute the lowest upper bound of two or more types and no explicit type for the result. So at least all codes that using a method that takes a T... (Arrays.asList(), List.of(), Stream.of()) that either stores the result in a var or acts as a monad (think stream.map()/flatMap() etc) will produce a different type. > >> - they are not binary backward compatible >> new Object().getClass() != Object.class > > This has nothing to do with the interfaces. This is based on the more general > property that every class instance must belong to a class that is either an > identity class or a value class. Interfaces are just the way we've chosen to > encode that property. Abandoning the property entirely would be a bigger deal. If we do not use interfaces, the runtime class of java.lang.Object can be Object, being an identity class or not is a just a bit in the reified class, not a compile time property, there is contamination by inheritance. > >> - at least IdentityObject needs to be injected at runtime, which by side effect >> requires patching several >> VM components: reflection/j.l.invoke, JVMTI, etc making the VM spec more complex >> that it should be. > > Yes, there are some nice things about re-using an existing class feature rather > than inventing a new one (we could have a new "class mode" or something); but > it's a disadvantage that we then have to disrupt existing properties/behaviors. > > Summarizing, yes, there are some areas of concern or caution. But this remains > the best way we've identified so far to achieve a lot of different goals. I fully disagree, there is no goal among documentation, type restriction, dynamic checking, where using two interfaces ValueObject and IdentityObject fully fullfill its duty *and* it's wildly not backward compatible thus requires heroic tweaks (dynamic interface injection, bytecode rewriting of new Object, reflection type filtering, type hiding during inference etc). For me, it's like opening the door of your house to an elephant because it has a nice hat and saying you will fix that with scotch-tape each time it touches something. R?mi From daniel.smith at oracle.com Wed Jan 26 15:42:30 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 26 Jan 2022 15:42:30 +0000 Subject: The interfaces IdentityObject and ValueObject must die ! In-Reply-To: <547491109.4463966.1643188734104.JavaMail.zimbra@u-pem.fr> References: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> <547491109.4463966.1643188734104.JavaMail.zimbra@u-pem.fr> Message-ID: <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> On Jan 26, 2022, at 2:18 AM, forax at univ-mlv.fr wrote: In other words: I don't see a use case for distinguishing between primitive and value classes with different interfaces. Primitive classes does not allow nulls and are tearable, following your logic, there should be a subclass of ValueObject named PrimitiveObject that reflects that semantics. But this isn't a property of the *class*, it's a property of the *type*, as used at a particular use site. If you want to know whether an array is flattened, the class of the component can't tell you. This is especially useful when you have an array of PrimitiveObject, you know that a storing null in an array of PrimitiveObject will always generate a NPE at runtime and that you may have to use either the volatile semantics or a lock when you read/write values from/to the array of PrimitiveObject. For examples, public void m(PrimitiveObject[] array, int index) { array[index] = null; // can be a compile time error } If we said class Point implements PrimitiveObject then it would be the case that Point.ref[] <: PrimitiveObject[] and so PrimitiveObject[] wouldn't mean what you want it to mean. We could make a special rule that says primitive types are subtypes of a special interface, even though their class does not implement that interface. But that doesn't really work, either?primitive types are monomorphic. If you've got a variable with an interface type, you've got a reference. We could also make a special rule that says arrays of primitive types implement an interface PrimitiveArray. More generally, we've considered enhancements to arrays where there are different implementations provided by different classes. That seems plausible, but it's orthogonal to the IdentityObject/ValueObject feature. Meanwhile, I'd suggest writing the method like this, using universal generics: public void m(T[] array, int index) { array[index] = null; // null warning } An impossible type, it's a type that can be declared but no class will ever match. Examples of impossible types, at declaration site interface I extends ValueObject {} interface J extends IdentityObject {} void foo() { } It would definitely be illegal to declare a class that extends I and J. Our rules about well-formedness for bounds have always been sketchy, but potentially that would be a malformed type variable. Abandoning the property entirely would be a bigger deal. If we do not use interfaces, the runtime class of java.lang.Object can be Object, being an identity class or not is a just a bit in the reified class, not a compile time property, there is contamination by inheritance. Object can't be an identity class, at compile time or run time, because some subclasses of Object are value classes. What you'd need is a property of individual *objects*, not represented at all with the class. Theoretically possible, but like I said, a pretty big disruption to our current model. For me, it's like opening the door of your house to an elephant because it has a nice hat and saying you will fix that with scotch-tape each time it touches something. Ha. This sounds like maybe there's a French idiom involved, but anyway we should try to get John to add this to his repertoire of analogies. From daniel.smith at oracle.com Wed Jan 26 22:15:12 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 26 Jan 2022 22:15:12 +0000 Subject: SoV-3: constructor questions In-Reply-To: References: Message-ID: <1AAC2834-ADEE-42EC-AE9F-E72B85AE0378@oracle.com> > On Jan 26, 2022, at 2:18 PM, Dan Heidinga wrote: > > After re-reading the State of Valhalla part 3 again [1], I have a > couple of questions on constructor handling: > > 1) The rules for handling ACC_PERMITS_VALUE are different between > SoV-2 and SoV-3 in that the language imposes constraints the VM > doesn't check. Is this deliberate? > > SoV-2 says: >> The classes they can extend are restricted: Object or abstract classes with no fields, empty no-arg constructor bodies, no other constructors, no instance initializers, no synchronized methods, and whose superclasses all meet this same set of conditions. (Number is an example of such a class.) > > while SoV-3 says: >> Perhaps surprisingly, the JVM does not perform the following checks for value-superclass candidates, although the source language compiler will typically do so: >> >> It should have declared an empty no-argument constructor. (Or if it didn?t, then the author has somehow consented to having all of the constructors being skipped by the unnamed factory methods of value subclasses.) > > "Perhaps surprisingly" is right as I'm surprised =) and not sure I > follow why the VM wouldn't enforce the restriction. Is it to avoid > having to specify the attributes of that constructor? We can come up with a rule to specify, in the Java language, what an "empty constructor" looks like. (Nothing syntactically in the body, for example.) It's harder for the JVM to specify what an "empty method" looks like. It must at least have an 'invokespecial' of its superclass's . Might do some stacking of arguments. Etc. JVMs don't want to be in the business of recognizing code patterns. So the model for the JVM is: you can declare methods if you want to support identity subclass creation, and you can declare ACC_PERMITS_VALUE if you want to support value subclass creation. Independent channels, no need for them to interact. > Which leads me to the next concern: how javac will compile the "empty > no-arg constructor bodies" required by SoV-2? Or is the answer we > don't care because the VM won't check anyway? The Java language will produce class files for qualifying abstract classes with: - ACC_PERMITS_VALUE set - The same methods it would have produced in previous versions (involving a super invokespecial call) For a non-qualifying abstract class, you'll get - ACC_PERMITS_VALUE *not* set - The same methods it would have produced in previous versions (potentially involving arbitrary user code) And, yes, the JVM doesn't care. Other patterns are possible in legal class files (but javac won't produce them). > 2) What is the rationale behind the return type restrictions on methods? > >> A method must return the type of its declaring class, or a supertype. > .... >> While methods must always be static and must return a type consistent with their class, they can (unlike methods) be declared in any class file, as far as the JVM is concerned. > > If I'm reading this correctly, to enforce the first quote we'll need a > verifier check to ensure that the declared return type of the > method is consistent with the current class or a supertype. But > Object is a common supertype, as is ValueObject, so I'm not sure what > we're gaining with this restriction as any type is a valid candidate > for return from a method as anything can be a subclass of > Object. Treatment of methods is still unresolved, so this (and the JEP) is just describing one possible approach. I tried to reach a conclusion on this a few months ago on this list, but we ended in an unresolved place. I'll try again... Anyway, in this incarnation: the rule is that the return type must be a type that includes instances of the current class. So, in class Point, QPoint is okay, LObject is okay, but LString is not. > We get a better restriction from the `aconst_init` and `withfield` > bytecodes which "can only be executed within the nest of the class > that declares the value class being initialized or modified". Do we > actually need the restriction on the method or should it be > considered non-normative (aka a best practice)? I think there are certainly use cases for class instantiation outside of a method named '' (even if javac won't generate them), and wouldn't want to limit those instructions to methods named ''. It gives '' more power than I think we intend?it's supposed to be a convenient place to put stuff, not a mandatory feature of instance creation. From forax at univ-mlv.fr Wed Jan 26 23:36:09 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 27 Jan 2022 00:36:09 +0100 (CET) Subject: The interfaces IdentityObject and ValueObject must die ! In-Reply-To: <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> References: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> <547491109.4463966.1643188734104.JavaMail.zimbra@u-pem.fr> <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> Message-ID: <484356026.4998060.1643240169282.JavaMail.zimbra@u-pem.fr> > From: "daniel smith" > To: "Remi Forax" > Cc: "valhalla-spec-experts" > Sent: Wednesday, January 26, 2022 4:42:30 PM > Subject: Re: The interfaces IdentityObject and ValueObject must die ! >> On Jan 26, 2022, at 2:18 AM, [ mailto:forax at univ-mlv.fr | >> forax at univ-mlv.fr ] wrote: >>> In other words: I don't see a use case for distinguishing between primitive and >>> value classes with different interfaces. >> Primitive classes does not allow nulls and are tearable, following your logic, >> there should be a subclass of ValueObject named PrimitiveObject that reflects >> that semantics. > But this isn't a property of the *class*, it's a property of the *type*, as used > at a particular use site. If you want to know whether an array is flattened, > the class of the component can't tell you. The semantics B1/B2/B3 is a property of the class, an instance of a value class stored in an Object is still an instance of a value class, same for primtive classes. Being flattenable or not, is an orthogonal concern. Being flattenable for a field or a local variable/parameter it's a property of the descriptor, but for arrays it's a property of the array class because arrays are covariant in Java. For arrays, a code like this should work Object[] o = new QComplex;[3]; o[1] = QComplex;.new(...); >> This is especially useful when you have an array of PrimitiveObject, you know >> that a storing null in an array of PrimitiveObject will always generate a NPE >> at runtime and that you may have to use either the volatile semantics or a lock >> when you read/write values from/to the array of PrimitiveObject. >> For examples, >> public void m(PrimitiveObject[] array, int index) { >> array[index] = null; // can be a compile time error >> } > If we said > primitive class Point implements PrimitiveObject > then it would be the case that > Point.ref[] <: PrimitiveObject[] > and so PrimitiveObject[] wouldn't mean what you want it to mean. > We could make a special rule that says primitive types are subtypes of a special > interface, even though their class does not implement that interface. But that > doesn't really work, either?primitive types are monomorphic. If you've got a > variable with an interface type, you've got a reference. I don't understand why you want Point.ref/LPoint; to be a PrimitiveObject and not a ValueObject. We have QPoint;[] <: LPoint;[] so LPoint; implements ValueObject and QPoint; implements PrimitiveObject. Anyway, it shows that using interfaces to represent properties of a class (behavior of ==, synchronized, nullability, tearability) is far from obvious. > Meanwhile, I'd suggest writing the method like this, using universal generics: > public void m(T[] array, int index) { > array[index] = null; // null warning > } If you are Okay with code that can raise a NPE, why are you not Okay with code that can raise an IllegalMonitorStateException ? Or said differently why ValueObject and not PrimitiveObject. I'm not advocating for having 3 interfaces, because i think of the ratio cost/benefit of such interfaces is poor. But having 2 interfaces is definitively weird since we have updated the model to B1/B2/B3. >> An impossible type, it's a type that can be declared but no class will ever >> match. >> Examples of impossible types, at declaration site >> interface I extends ValueObject {} >> interface J extends IdentityObject {} >> void foo() { } > It would definitely be illegal to declare a class that extends I and J. Our > rules about well-formedness for bounds have always been sketchy, but > potentially that would be a malformed type variable. another cost of introducing those interfaces, the JLS has to be updated to take care of the invalid bounds >>> Abandoning the property entirely would be a bigger deal. >> If we do not use interfaces, the runtime class of java.lang.Object can be >> Object, being an identity class or not is a just a bit in the reified class, >> not a compile time property, there is contamination by inheritance. > Object can't be an identity class, at compile time or run time, because some > subclasses of Object are value classes. Object the type is not an identity class, but Object the class (the Object in "new Object()") is an identity class. > What you'd need is a property of individual *objects*, not represented at all > with the class. Theoretically possible, but like I said, a pretty big > disruption to our current model. nope, we need to decouple the notion of type from the notion of being an identity/value/primitive class because it's a property of the class not a property of the type. >> For me, it's like opening the door of your house to an elephant because it has a >> nice hat and saying you will fix that with scotch-tape each time it touches >> something. > Ha. This sounds like maybe there's a French idiom involved, but anyway we should > try to get John to add this to his repertoire of analogies. "Like a bull in a china shop" is "comme un ?l?phant dans un magazin de porcelaine" in French. R?mi From john.r.rose at oracle.com Wed Jan 26 23:46:19 2022 From: john.r.rose at oracle.com (John Rose) Date: Wed, 26 Jan 2022 15:46:19 -0800 Subject: The interfaces IdentityObject and ValueObject must die ! In-Reply-To: <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> References: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> <547491109.4463966.1643188734104.JavaMail.zimbra@u-pem.fr> <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> Message-ID: <5F5BA014-D5CB-45D6-9D77-6BAA04E702AA@oracle.com> On 26 Jan 2022, at 7:42, Dan Smith wrote: > For me, it's like opening the door of your house to an elephant because it has a nice hat and saying you will fix that with scotch-tape each time it touches something. > > Ha. This sounds like maybe there's a French idiom involved, but anyway we should try to get John to add this to his repertoire of analogies. Is this elephant also being followed around a crowd of blind men? Too bad they can?t see its nice hat. See also: A bull walked into a china shop. The owner said, ?nice hat!? From john.r.rose at oracle.com Wed Jan 26 23:47:46 2022 From: john.r.rose at oracle.com (John Rose) Date: Wed, 26 Jan 2022 15:47:46 -0800 Subject: The interfaces IdentityObject and ValueObject must die ! In-Reply-To: <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> References: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> <547491109.4463966.1643188734104.JavaMail.zimbra@u-pem.fr> <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> Message-ID: <1AD66DD3-C569-4E3D-9401-53A6B0E913D3@oracle.com> On 26 Jan 2022, at 7:42, Dan Smith wrote: > On Jan 26, 2022, at 2:18 AM, > forax at univ-mlv.fr wrote: > > In other words: I don't see a use case for distinguishing between > primitive and > value classes with different interfaces. > > Primitive classes does not allow nulls and are tearable, following > your logic, there should be a subclass of ValueObject named > PrimitiveObject that reflects that semantics. > > But this isn't a property of the *class*, it's a property of the > *type*, as used at a particular use site. If you want to know whether > an array is flattened, the class of the component can't tell you. > > This is especially useful when you have an array of PrimitiveObject, > you know that a storing null in an array of PrimitiveObject will > always generate a NPE at runtime and that you may have to use either > the volatile semantics or a lock when you read/write values from/to > the array of PrimitiveObject. > > For examples, > public void m(PrimitiveObject[] array, int index) { > array[index] = null; // can be a compile time error > } > > If we said > > class Point implements PrimitiveObject > > then it would be the case that > > Point.ref[] <: PrimitiveObject[] > > and so PrimitiveObject[] wouldn't mean what you want it to mean. > > We could make a special rule that says primitive types are subtypes of > a special interface, even though their class does not implement that > interface. But that doesn't really work, either?primitive types are > monomorphic. If you've got a variable with an interface type, you've > got a reference. > > We could also make a special rule that says arrays of primitive types > implement an interface PrimitiveArray. More generally, we've > considered enhancements to arrays where there are different > implementations provided by different classes. That seems plausible, > but it's orthogonal to the IdentityObject/ValueObject feature. > > Meanwhile, I'd suggest writing the method like this, using universal > generics: > > public void m(T[] array, int index) { > array[index] = null; // null warning > } > > An impossible type, it's a type that can be declared but no class will > ever match. > > Examples of impossible types, at declaration site > interface I extends ValueObject {} > interface J extends IdentityObject {} > void foo() { } > > It would definitely be illegal to declare a class that extends I and > J. Our rules about well-formedness for bounds have always been > sketchy, but potentially that would be a malformed type variable. > > Abandoning the property entirely would be a bigger deal. > > If we do not use interfaces, the runtime class of java.lang.Object can > be Object, being an identity class or not is a just a bit in the > reified class, not a compile time property, there is contamination by > inheritance. > > Object can't be an identity class, at compile time or run time, > because some subclasses of Object are value classes. > > What you'd need is a property of individual *objects*, not represented > at all with the class. Theoretically possible, but like I said, a > pretty big disruption to our current model. > > For me, it's like opening the door of your house to an elephant > because it has a nice hat and saying you will fix that with > scotch-tape each time it touches something. > > Ha. This sounds like maybe there's a French idiom involved, but anyway > we should try to get John to add this to his repertoire of analogies. From john.r.rose at oracle.com Wed Jan 26 23:55:14 2022 From: john.r.rose at oracle.com (John Rose) Date: Wed, 26 Jan 2022 15:55:14 -0800 Subject: The interfaces IdentityObject and ValueObject must die ! In-Reply-To: <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> References: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> <547491109.4463966.1643188734104.JavaMail.zimbra@u-pem.fr> <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> Message-ID: <5079388C-93D2-42EA-BA99-F8312ABF26B9@oracle.com> [Sorry disregard last content-free message. Still getting used to a new mail client.] On 26 Jan 2022, at 7:42, Dan Smith wrote: > If we do not use interfaces, the runtime class of java.lang.Object can > be Object, being an identity class or not is a just a bit in the > reified class, not a compile time property, there is contamination by > inheritance. > > Object can't be an identity class, at compile time or run time, > because some subclasses of Object are value classes. That?s true, but stripping away the marker interfaces removes (one part of) the contract that a class, as a whole, must always accurately report whether *all its instances* have one or the other property, of having identity or having no identity. As I said in an earlier meeting, there are sometimes reasons to give *the same class* both value instances and identity instances. Yes this muddies the user model but it also helps us with compatibility moves that are required, however much they muddy the user mode. (Actually all exact instances of `Object` will be identity objects. I?m thinking of `Integer` which might want mostly values but some identity objects for backward compatibility. Maybe.) Independently of that, for the specific case of `Object`, having a query function `Class.instanceKind`, which returns ?NONE? for abstracts else ?VALUE? or ?IDENTITY?, would encode the same information we are looking at with those marker interfaces. But the contract for a method is *more flexible* than the contract of a marker interface. In particular, `instanceKind` is not required to report the same thing for T and U when T<:U but marker interfaces are forced to be consistent across T<:U. I think this is an advantage, precisely because it has more flexible structure, for the method rather than the marker interface. If the marker interfaces also have little use as textual types (e.g., for bounds and method parameters) then I agree with Remi. Ditch ?em. From daniel.smith at oracle.com Thu Jan 27 00:36:30 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 27 Jan 2022 00:36:30 +0000 Subject: The interfaces IdentityObject and ValueObject must die ! In-Reply-To: <484356026.4998060.1643240169282.JavaMail.zimbra@u-pem.fr> References: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> <547491109.4463966.1643188734104.JavaMail.zimbra@u-pem.fr> <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> <484356026.4998060.1643240169282.JavaMail.zimbra@u-pem.fr> Message-ID: On Jan 26, 2022, at 4:36 PM, forax at univ-mlv.fr wrote: But this isn't a property of the *class*, it's a property of the *type*, as used at a particular use site. If you want to know whether an array is flattened, the class of the component can't tell you. The semantics B1/B2/B3 is a property of the class, an instance of a value class stored in an Object is still an instance of a value class, same for primtive classes. Being flattenable or not, is an orthogonal concern. Being flattenable for a field or a local variable/parameter it's a property of the descriptor, but for arrays it's a property of the array class because arrays are covariant in Java. For arrays, a code like this should work Object[] o = new QComplex;[3]; o[1] = QComplex;.new(...); My point is that if you're holding an Object, there is nothing interesting about instances of primitive classes to distinguish them from instances of value classes. They are both value objects, with value object semantics. Now, if you're holding an Object[] and you want to know whether the array is flat or not, that could be a useful property to detect. But a query like the following won't tell you: PrimitiveObject.class.isAssignableFrom(arr.getClass().getComponentType()); Instead, the question you want to ask is something like: arr.getClass().getComponentType().isPrimitive(); You're claiming that the IdentityObject vs. ValueObject and value class vs. primitive class distinctions should be treated the same, but what I'm illustrating here is that they are different kinds of properties. The first is a property of a class (or of all instances of a class), the second of a type (or a container with a type). I don't understand why you want Point.ref/LPoint; to be a PrimitiveObject and not a ValueObject. We have QPoint;[] <: LPoint;[] so LPoint; implements ValueObject and QPoint; implements PrimitiveObject. Classes implement interfaces. Types have subtyping relationships *derived from* classes. The mechanism of encoding a property with an interface thus involves attaching that interface to a particular class. So if you want QPoint <: PrimitiveObject, it must be the case that *class* Point implements PrimitiveObject. And then it will be the case also that LPoint <: PrimitiveObject. (I don't want any of this. What I want is for there to be just one interface, ValueObject, that indicates identity-free semantics for a class/object, and I want class Point to implement that interface.) Meanwhile, I'd suggest writing the method like this, using universal generics: public void m(T[] array, int index) { array[index] = null; // null warning } If you are Okay with code that can raise a NPE, why are you not Okay with code that can raise an IllegalMonitorStateException ? Or said differently why ValueObject and not PrimitiveObject. You've lost me here. I showed an example that uses universal generics. The type variable T ranges over both primitive and reference types, so may not be nullable. It is improper to write a null into a T[]. As a concession to compatibility, we want to treat this as a warning rather than an error, because plenty of code like this exists today. Similarly, there may be code that ranges over both value objects and identity objects. An easy way to do this is with a variable of type Object. It is improper to synchronize on an Object that may be a value object. Again, as a concession to compatibility, we probably want to treat this as a warning. public void m(Object obj) { synchronized (obj) { // warning ... } } These are both situations where being able to detect properties of the input could be useful to avoid runtime errors, but that doesn't mean the mechanism should be the same for both. (And I explained above why testing for PrimitiveObject[] wouldn't do what you want it to do.) Object can't be an identity class, at compile time or run time, because some subclasses of Object are value classes. Object the type is not an identity class, but Object the class (the Object in "new Object()") is an identity class. You're envisioning a different model for types and classes than the one we have. An instance of a class is also an instance of (and carries the properties of) its superclasses. Value objects are instances of the class Object. I can imagine a design in which we say that instances of Object may be either identity or value objects, but direct instances of the class are always identity objects. But this is not how we've handled the property anywhere else, and it breaks some invariants. We've gotten where we are because it seemed less disruptive to introduce a subclass of Object that can behave like a normal identity class. (I think we can explore this more, though, and note that I didn't say anything about interfaces above. It's orthogonal.) From daniel.smith at oracle.com Thu Jan 27 00:55:10 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 27 Jan 2022 00:55:10 +0000 Subject: The interfaces IdentityObject and ValueObject must die ! In-Reply-To: <5079388C-93D2-42EA-BA99-F8312ABF26B9@oracle.com> References: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> <547491109.4463966.1643188734104.JavaMail.zimbra@u-pem.fr> <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> <5079388C-93D2-42EA-BA99-F8312ABF26B9@oracle.com> Message-ID: On Jan 26, 2022, at 4:55 PM, John Rose > wrote: Independently of that, for the specific case of Object, having a query function Class.instanceKind, which returns ?NONE? for abstracts else ?VALUE? or ?IDENTITY?, would encode the same information we are looking at with those marker interfaces. Right, so you're envisioning a move in which, rather than 'obj instanceof ValueObject', the dynamic test is 'obj.getClass().instanceKind() == VALUE'. For dynamic testing (my #3), sure, these are equivalent. But the contract for a method is more flexible than the contract of a marker interface. In particular, instanceKind is not required to report the same thing for T and U when T<:U but marker interfaces are forced to be consistent across T<:U. I think this is an advantage, precisely because it has more flexible structure, for the method rather than the marker interface. I would expect that 'cls.instanceKind() == IDENTITY' has the exact same semantics as 'IdentityObject.class.isAssignableFrom(cls)': if a class claims to be an identity class, then all its instances (direct and via subclassing) are identity objects. I'm not seeing a sensible alternative. How does this behave in the odd world in which direct instances of Object are identity objects? 'Object.class.instanceKind()' must return NONE, just as Object.class must not implement either IdentityObject or ValueObject. Given one of these oddball objects, 'obj.getClass().instanceKind()' will, naturally, return NONE. Which is surprising, breaking the expectation that there are only two possible results of this expression. Just as these objects would break the expectation that every object is either 'instanceof IdentityObject' or 'instanceof ValueObject'. I keep saying this: how we handle the 'new Object()' problem doesn't seem to me to have any impact on how we encode "I'm an identity class". It's not a discussion that belongs in this email thread. If the marker interfaces also have little use as textual types (e.g., for bounds and method parameters) then I agree with Remi. Ditch ?em. I outlined many ways in which we're making use of these interfaces. Static types is just one. Getting rid of them isn't as easy as "ditch 'em", it would involve redesigning for all of those use cases (plus any I forgot), and coming up with something that is compellingly better. (This is an invitation, for anyone interested in proposing something specific...) From john.r.rose at oracle.com Thu Jan 27 01:14:04 2022 From: john.r.rose at oracle.com (John Rose) Date: Wed, 26 Jan 2022 17:14:04 -0800 Subject: The interfaces IdentityObject and ValueObject must die ! In-Reply-To: References: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> <547491109.4463966.1643188734104.JavaMail.zimbra@u-pem.fr> <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> <5079388C-93D2-42EA-BA99-F8312ABF26B9@oracle.com> Message-ID: On 26 Jan 2022, at 16:55, Dan Smith wrote: >> On Jan 26, 2022, at 4:55 PM, John Rose >> wrote: >> >> Independently of that, for the specific case of Object >> >> , having a query function Class.instanceKind >> >> , which returns ?NONE? for abstracts else ?VALUE? or >> ?IDENTITY?, would encode the same information we are looking at >> with those marker interfaces. > > Right, so you're envisioning a move in which, rather than 'obj > instanceof ValueObject', the dynamic test is > 'obj.getClass().instanceKind() == VALUE'. > > For dynamic testing (my #3), sure, these are equivalent. > >> But the contract for a method is more flexible than the contract of a >> marker interface. >> >> In particular, instanceKind >> >> is not required to report the same thing for T and U when T<:U but >> marker interfaces are forced to be consistent across T<:U. I think >> this is an advantage, precisely because it has more flexible >> structure, for the method rather than the marker interface. > > I would expect that 'cls.instanceKind() == IDENTITY' has the exact > same semantics as 'IdentityObject.class.isAssignableFrom(cls)': if a > class claims to be an identity class, then all its instances (direct > and via subclassing) are identity objects. I'm not seeing a sensible > alternative. > > How does this behave in the odd world in which direct instances of > Object are identity objects? > > 'Object.class.instanceKind()' must return NONE, just as Object.class > must not implement either IdentityObject or ValueObject. That last ?must? is necessarily true, but the second-to-last ?must? is not necessarily true. That?s my point here. A. I am aiming for `new Object().getClass() == Object.class`. B. But it is also true that `new Object()` is an identity object (no caps yet). C. *If* the way to ask whether an object `x` is an identity object is a type test (`x instanceof IdentityObject`), *then* given step A. and B. `Object` must implement the marker `IdentityObject`. (Trouble is brewing now.) D. Meanwhile, some random non-identity value class `Point` ultimately subtypes `Object`. E. But (and here?s *exactly* where the exquisite regularity of inheritance patterns fails to meet our requirements) this means that `Point` must inherit the super type `IdentityObject` because of step C. F. Thus, inheritance of a reflective marker would force the query `new Point() instanceof IdentityObject` to be true. It?s an impasse! We can break the impasse at step C by *not* using marker interfaces *because they inherit*. The reflective query I?m proposing, to a class C, applies *only to concrete instances of that precise class C* and not to instances of C?s subtypes. A different condition is the limitation that *all subtypes* of C must be values, or must be identities. Asking about all subtypes is a slightly different question. Using a marker interface conflates those two questions. > Given one of these oddball objects, 'obj.getClass().instanceKind()' > will, naturally, return NONE. Which is surprising, breaking the > expectation that there are only two possible results of this > expression. Just as these objects would break the expectation that > every object is either 'instanceof IdentityObject' or 'instanceof > ValueObject'. > > I keep saying this: how we handle the 'new Object()' problem doesn't > seem to me to have any impact on how we encode "I'm an identity > class". It's not a discussion that belongs in this email thread. > >> If the marker interfaces also have little use as textual types (e.g., >> for bounds and method parameters) then I agree with Remi. Ditch >> ?em. > > I outlined many ways in which we're making use of these interfaces. > Static types is just one. Getting rid of them isn't as easy as "ditch > 'em", it would involve redesigning for all of those use cases (plus > any I forgot), and coming up with something that is compellingly > better. (This is an invitation, for anyone interested in proposing > something specific...) That?s fair; you are right that the whole list needs to be covered. My point about ?ditch 'em? is simply that if all of the use cases of marker interfaces involve reflective queries (rather than formal arguments that textually name the interfaces) then we can use the above query methods instead. And I don?t think I?m too far off, given that `Serializable` and `Cloneable` are *never* (or 0.001%) used textually to declare formal variables; they always show up as `Serializable.class` or `x instanceof Serializable` or `extends Cloneable`. I think one of your use cases that isn?t covered well by the reflective query I suggested is declaring a type that extends `IO` or `VO`, thus constraining all concrete types that extend that type. We could keep this as a reduced portfolio for *explicit* `IO` and `VO` but still divert the *query* to a method. So the marker interfaces are *one way* to force the query to return a specific kind other than `NONE` (or `BOTH`). From john.r.rose at oracle.com Thu Jan 27 01:18:03 2022 From: john.r.rose at oracle.com (John Rose) Date: Wed, 26 Jan 2022 17:18:03 -0800 Subject: The interfaces IdentityObject and ValueObject must die ! In-Reply-To: References: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> <547491109.4463966.1643188734104.JavaMail.zimbra@u-pem.fr> <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> <484356026.4998060.1643240169282.JavaMail.zimbra@u-pem.fr> Message-ID: <9DC15706-CED3-4265-90DA-A9F6C1CD0380@oracle.com> On 26 Jan 2022, at 16:36, Dan Smith wrote: > An instance of a class is also an instance of (and carries the > properties of) its superclasses. Value objects are instances of the > class Object. > > I can imagine a design in which we say that instances of Object may be > either identity or value objects, but direct instances of the class > are always identity objects. But this is not how we've handled the > property anywhere else, and it breaks some invariants. We've gotten > where we are because it seemed less disruptive to introduce a subclass > of Object that can behave like a normal identity class. And yet there is also a second way a class can be an instance; it can be *exactly an instance of C*, when `x.getClass()==C.class`. That?s the condition which can be teased apart here, if we allow ourselves to use something other than marker interfaces. But marker interfaces (as I said) are committed to ignoring the ?exactly an instance? condition, because they inherit. From forax at univ-mlv.fr Thu Jan 27 07:24:54 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 27 Jan 2022 08:24:54 +0100 (CET) Subject: The interfaces IdentityObject and ValueObject must die ! In-Reply-To: <9DC15706-CED3-4265-90DA-A9F6C1CD0380@oracle.com> References: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> <547491109.4463966.1643188734104.JavaMail.zimbra@u-pem.fr> <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> <484356026.4998060.1643240169282.JavaMail.zimbra@u-pem.fr> <9DC15706-CED3-4265-90DA-A9F6C1CD0380@oracle.com> Message-ID: <744464774.5070394.1643268294887.JavaMail.zimbra@u-pem.fr> > From: "John Rose" > To: "daniel smith" > Cc: "Remi Forax" , "valhalla-spec-experts" > > Sent: Thursday, January 27, 2022 2:18:03 AM > Subject: Re: The interfaces IdentityObject and ValueObject must die ! > On 26 Jan 2022, at 16:36, Dan Smith wrote: >> An instance of a class is also an instance of (and carries the properties of) >> its superclasses. Value objects are instances of the class Object. >> I can imagine a design in which we say that instances of Object may be either >> identity or value objects, but direct instances of the class are always >> identity objects. But this is not how we've handled the property anywhere else, >> and it breaks some invariants. We've gotten where we are because it seemed less >> disruptive to introduce a subclass of Object that can behave like a normal >> identity class. > And yet there is also a second way a class can be an instance; it can be exactly > an instance of C , when x.getClass()==C.class . That?s the condition which can > be teased apart here, if we allow ourselves to use something other than marker > interfaces. But marker interfaces (as I said) are committed to ignoring the > ?exactly an instance? condition, because they inherit. YES ! being a value class (primitive class) is a runtime property, not something attached to a type and this is a separate concern from the flattenability which is either type hint (Q-type, L-type + Preload) or a type hint + a runtime check in case of arrays. R?mi From forax at univ-mlv.fr Thu Jan 27 15:26:14 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 27 Jan 2022 16:26:14 +0100 (CET) Subject: SoV-3: constructor questions In-Reply-To: References: <1AAC2834-ADEE-42EC-AE9F-E72B85AE0378@oracle.com> Message-ID: <1053095331.5488956.1643297174924.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Dan Heidinga" > To: "daniel smith" > Cc: "valhalla-spec-experts" > Sent: Thursday, January 27, 2022 4:09:58 PM > Subject: Re: SoV-3: constructor questions > (Resending as I forgot to CC the list - Sorry for the duplicate email Dan) > >> We can come up with a rule to specify, in the Java language, what an "empty >> constructor" looks like. (Nothing syntactically in the body, for example.) >> >> It's harder for the JVM to specify what an "empty method" looks like. It >> must at least have an 'invokespecial' of its superclass's . Might do some >> stacking of arguments. Etc. JVMs don't want to be in the business of >> recognizing code patterns. > > Ok. This is due to a class having a single no-args `()V` method > that has to do double duty - supply the super-call for identity > subclasses and be ignored by value subclasses. Seems reasonable. > >> > 2) What is the rationale behind the return type restrictions on methods? > .... >> Treatment of methods is still unresolved, so this (and the JEP) is just >> describing one possible approach. I tried to reach a conclusion on this a few >> months ago on this list, but we ended in an unresolved place. I'll try again... >> >> Anyway, in this incarnation: the rule is that the return type must be a type >> that includes instances of the current class. So, in class Point, QPoint is >> okay, LObject is okay, but LString is not. > > I don't understand the point of this restriction. Since > Ljava/lang/Object; is acceptable (and has to be), I can use a `` > method to return *any* class but the caller will need to downcast to > use it. > > class QPoint { > static Object () { return new String("string factory"); } > static void user() { > String s = (String) QPoint.:()Ljava/lang/Object; > } > } > We've made the bytecode patterns ugly by requiring a checkcast - and > maybe slightly harder to write a large set of factory methods due to > needing different descriptors for each one - and gained what? Javac > may not generate this kind of code but it still seems odd to restrict > it. The reason John gave for allowing a method to return a super type is for lambda proxies. A lambda proxies is a hidden value class, i.e. a value class loaded by lookup.defineHiddenClass(), given that a hidden class as no real name, the idea is to use Object or perhaps the functional interface as return type. If the functional interface is used, there will be no weird cast in the bytecode. [...] > > --Dan R?mi > > On Wed, Jan 26, 2022 at 5:15 PM Dan Smith wrote: >> >> > On Jan 26, 2022, at 2:18 PM, Dan Heidinga wrote: >> > >> > After re-reading the State of Valhalla part 3 again [1], I have a >> > couple of questions on constructor handling: >> > >> > 1) The rules for handling ACC_PERMITS_VALUE are different between >> > SoV-2 and SoV-3 in that the language imposes constraints the VM >> > doesn't check. Is this deliberate? >> > >> > SoV-2 says: >> >> The classes they can extend are restricted: Object or abstract classes with no >> >> fields, empty no-arg constructor bodies, no other constructors, no instance >> >> initializers, no synchronized methods, and whose superclasses all meet this >> >> same set of conditions. (Number is an example of such a class.) >> > >> > while SoV-3 says: >> >> Perhaps surprisingly, the JVM does not perform the following checks for >> >> value-superclass candidates, although the source language compiler will >> >> typically do so: >> >> >> >> It should have declared an empty no-argument constructor. (Or if it didn?t, then >> >> the author has somehow consented to having all of the constructors being >> >> skipped by the unnamed factory methods of value subclasses.) >> > >> > "Perhaps surprisingly" is right as I'm surprised =) and not sure I >> > follow why the VM wouldn't enforce the restriction. Is it to avoid >> > having to specify the attributes of that constructor? >> >> We can come up with a rule to specify, in the Java language, what an "empty >> constructor" looks like. (Nothing syntactically in the body, for example.) >> >> It's harder for the JVM to specify what an "empty method" looks like. It >> must at least have an 'invokespecial' of its superclass's . Might do some >> stacking of arguments. Etc. JVMs don't want to be in the business of >> recognizing code patterns. >> >> So the model for the JVM is: you can declare methods if you want to >> support identity subclass creation, and you can declare ACC_PERMITS_VALUE if >> you want to support value subclass creation. Independent channels, no need for >> them to interact. >> >> > Which leads me to the next concern: how javac will compile the "empty >> > no-arg constructor bodies" required by SoV-2? Or is the answer we >> > don't care because the VM won't check anyway? >> >> The Java language will produce class files for qualifying abstract classes with: >> - ACC_PERMITS_VALUE set >> - The same methods it would have produced in previous versions (involving >> a super invokespecial call) >> >> For a non-qualifying abstract class, you'll get >> - ACC_PERMITS_VALUE *not* set >> - The same methods it would have produced in previous versions >> (potentially involving arbitrary user code) >> >> And, yes, the JVM doesn't care. Other patterns are possible in legal class files >> (but javac won't produce them). >> >> > 2) What is the rationale behind the return type restrictions on methods? >> > >> >> A method must return the type of its declaring class, or a supertype. >> > .... >> >> While methods must always be static and must return a type consistent with >> >> their class, they can (unlike methods) be declared in any class file, as >> >> far as the JVM is concerned. >> > >> > If I'm reading this correctly, to enforce the first quote we'll need a >> > verifier check to ensure that the declared return type of the >> > method is consistent with the current class or a supertype. But >> > Object is a common supertype, as is ValueObject, so I'm not sure what >> > we're gaining with this restriction as any type is a valid candidate >> > for return from a method as anything can be a subclass of >> > Object. >> >> Treatment of methods is still unresolved, so this (and the JEP) is just >> describing one possible approach. I tried to reach a conclusion on this a few >> months ago on this list, but we ended in an unresolved place. I'll try again... >> >> Anyway, in this incarnation: the rule is that the return type must be a type >> that includes instances of the current class. So, in class Point, QPoint is >> okay, LObject is okay, but LString is not. >> >> > We get a better restriction from the `aconst_init` and `withfield` >> > bytecodes which "can only be executed within the nest of the class >> > that declares the value class being initialized or modified". Do we >> > actually need the restriction on the method or should it be >> > considered non-normative (aka a best practice)? >> >> I think there are certainly use cases for class instantiation outside of a >> method named '' (even if javac won't generate them), and wouldn't want to >> limit those instructions to methods named ''. It gives '' more power >> than I think we intend?it's supposed to be a convenient place to put stuff, not >> a mandatory feature of instance creation. From forax at univ-mlv.fr Thu Jan 27 20:12:49 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 27 Jan 2022 21:12:49 +0100 (CET) Subject: SoV-3: constructor questions In-Reply-To: References: <1AAC2834-ADEE-42EC-AE9F-E72B85AE0378@oracle.com> <1053095331.5488956.1643297174924.JavaMail.zimbra@u-pem.fr> Message-ID: <684596209.5633616.1643314369582.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Dan Heidinga" > To: "Remi Forax" > Cc: "daniel smith" , "valhalla-spec-experts" > Sent: Thursday, January 27, 2022 4:41:14 PM > Subject: Re: SoV-3: constructor questions >> >> The reason John gave for allowing a method to return a super type is for >> lambda proxies. >> >> A lambda proxies is a hidden value class, i.e. a value class loaded by >> lookup.defineHiddenClass(), >> given that a hidden class as no real name, the idea is to use Object or perhaps >> the functional interface as return type. >> If the functional interface is used, there will be no weird cast in the >> bytecode. >> > > Right. I'm suggesting we remove any restrictions on the return type > for `` methods as we've identified cases (like the hidden value > class) where restrictions are problematic and haven't clearly > identified the benefits from these restrictions. It feels like we're > trying to specify a "best practice" while leaving an escape hatch > cause we know we need it. Seems like it would be better to not add > the restriction in the first place. yes, i agree on that. > > --Dan R?mi From john.r.rose at oracle.com Thu Jan 27 21:00:20 2022 From: john.r.rose at oracle.com (John Rose) Date: Thu, 27 Jan 2022 13:00:20 -0800 Subject: [External] : Re: VM model and aconst_init In-Reply-To: <1577708639.3794959.1643107810290.JavaMail.zimbra@u-pem.fr> References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> <1BB6397E-2103-4AF3-A857-02CF2FA0B453@oracle.com> <1577708639.3794959.1643107810290.JavaMail.zimbra@u-pem.fr> Message-ID: <459A0059-7BE6-4925-8B69-78DB7C6C89C8@oracle.com> On 25 Jan 2022, at 2:50, forax at univ-mlv.fr wrote: > Let's talk a bit about having the L world and the Q world completely > disjoint at least from the bytecode verifier POV. Yes please; indeed, that is my strong recommendation. Doing this (`Q>: It means that we need some checkcasts to move in both direction, from > a Q-type to a L-type and vice-versa. Correct. Checkcast is the mode-change operator (both directions). No need to create a new bytecode for either direction. > But at the same time, an array of L-type is a subtype of an array of > Q-type ? That depends on what you mean by ?subtype?. The JVM has multiple type systems. In the verifier we might have a choice of allowing `Q[]<:L[]` or not. It?s better not. That puts no limitations on what the `aaload` and `aastore` instructions do. They just dynamically sense whether the array is flattened or not and DTRT. That has nothing to do with verifier types. In fact, it?s a separated job of the runtime type system. For the runtime type system, we will say that arrays are covariant across an *extends* which is an augmentation of the *subtype* system. So for the runtime `T[]<:U[]` iff `T extends U`, so `Q[]<:L[]` (for the same class in both modes). But in the verifier `Q[]>: The result to a very uncommon/unconventional type system, and i'm not > a big fan of surprises in that area. You are looking for a conventional type system involving arrays? Then surely Java is not where you should be looking! Arrays are always going to be an embarrassment, but it?s going to be more embarrassing overall if we let the whole design pivot around the task of minimizing array embarrassments. > Furthermore, i believe that subtyping is a key to avoid multiple > bytecode verification of the generics code. I recommend a far simpler technique: Just don?t. Don?t multiple-verify, and and don?t do anything that would *need* such extra verification (with different types) to give the same answer (as the first verification). Also, don?t think of a JVM-loaded set of bytecodes as every having more than one operational meaning. (I don?t understand this obsession, not just with you, of imagining generic bytecodes as statically ?recopied? or ?recapitulated? or ?stamped out? for each generic instance. I have an uncomfortable feeling I?m missing something important about that. But I have to say, that?s not how the JVM will ever work. The JIT is in charge of managing copies of code paths, and the verifier should not be extended in any way to support multiple passes or multiple meanings or multiple copies. Maybe the idea about retyping bytecodes is a hold-over from the Model 3 experiments, where prototypes were built that elaborated, at some sort of load time, multiple copies of originally generic bytecodes, for distinct instances. That was a quagmire in several ways.) So, let?s not treat generic bytecodes as anything other than singly-typed by a one-time pass of the verifier. Any attempt to reverify later from a different POV on just repeats earlier trips into the quagmire. Now, the *source code* can be *imagined* to be re-typed in generic instances; that what generic code *means*. But don?t confuse the *implementation* of generics from the *semantic model*. The implementation will (in our plan of record) use something the verifier knows nothing about: Dynamic type restrictions (or something equivalent) which manage the projections and embeddings of specific instance types into the generic bound type. These *do not have to be* (indeed *must not be*, see below) identified as verifier types. They are associations of pairs of JVM runtime types. > By example, with the TypeRestriction attribute [1], the restriction > has to be subtype of the declared type/descriptor. No, just no. (Not your fault; I suppose the TR docs are in a confusing state.) There?s no reason to make that restriction (linking TRs to the verifier type system), other than the futile obsession I called out above. And if you do you make yourself sorry when you remember about the verifier?s I/J/F/D types. And for many other reasons, such as instance types which are inaccessible to the generic code. And the simple fact that bytecodes are not really generic-capable, directly, without serious (and futile) redesign. Making bytecodes directly generic (in the sense of contextually re-type-able) is never going to be as simple as using a TR mechanism which occurs *apart from the verifier*. (Plus, I don?t trust the verifier to take on new jobs. In its existing job it has been an ever-flowing fountain of security bugs. It made a hash of jsr/ret, and continually messed up instance creation paths. I won?t re-hire it for any new jobs. It?s good for matching descriptors in symbolic references, and maybe for helping the *interpreter* make a minor optimization to the `getfield` and `putfield` bytecodes, but that?s about it. The v-table index caching for `invokevirtual` looks cool but is effectively a no-op. The JIT makes all of the above irrelevant to performance, once the JIT kicks in. So the verifier gives an incremental push to startup performance, or interpreter-only performance if anyone still cares. Plus it gives us extra ?security? except when it doesn?t. OK, I?ll stop with the verifier now.) A ?type system? for TRs is pretty meaningless, from the JVM POV. In the JVM, a TR (despite its name) is most fundamentally an operational specification of how to project (G->I) or embed (I->G) instance types into generic types. It?s a function pair. It can connect `I=int` to `G=Object`, which the JVM *knows* are disjoint types. A TR is very useful for implementing source-level type relations that the JVM doesn?t know directly about, such as `int extends Object` (`int<|Object` maybe?). So talking about subtypes in TRs is nonsense as far as I can see, unless in the special sense that ?we use TRs in a translation strategy, to help extend the JVM?s limited subtyping notions to *our language?s* more expansive subtyping notions?. If TRs were a language-level construct, then, yeah, maybe we say a TR has some constraints about language-level subtypes. But it?s a JVM-only construct that plugs in where the JVM?s type system doesn?t work well enough. (Should we rename TR to PE, ?Project-Embedding Pair?? I like TR, but maybe only because I?m used to the term. As a JVM-level concept, it doesn?t need to get a highly polished name. The audience for it is limited.) From daniel.smith at oracle.com Thu Jan 27 23:59:27 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 27 Jan 2022 23:59:27 +0000 Subject: SoV-3: constructor questions In-Reply-To: References: <1AAC2834-ADEE-42EC-AE9F-E72B85AE0378@oracle.com> Message-ID: > On Jan 27, 2022, at 8:09 AM, Dan Heidinga wrote: > >>> 2) What is the rationale behind the return type restrictions on methods? > .... >> Treatment of methods is still unresolved, so this (and the JEP) is just describing one possible approach. I tried to reach a conclusion on this a few months ago on this list, but we ended in an unresolved place. I'll try again... >> >> Anyway, in this incarnation: the rule is that the return type must be a type that includes instances of the current class. So, in class Point, QPoint is okay, LObject is okay, but LString is not. > > I don't understand the point of this restriction. Since > Ljava/lang/Object; is acceptable (and has to be), I can use a `` > method to return *any* class but the caller will need to downcast to > use it. I think the reason we might have some sort of restriction is if we intend for a language or reflection API to be able to rely on these methods having some consistent properties (imagine them being surfaced with java.lang.reflect.Constructor, for example). So think of the restriction as a placeholder ("we may have some sort of restriction on the return type, TBD"). We still need to do some work to figure out the precise requirements, if any. From daniel.smith at oracle.com Fri Jan 28 00:46:32 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 28 Jan 2022 00:46:32 +0000 Subject: The interfaces IdentityObject and ValueObject must die ! In-Reply-To: References: <1984046756.4249884.1643146765320.JavaMail.zimbra@u-pem.fr> <94716CB6-B117-42DE-91F5-77B33485C514@oracle.com> <547491109.4463966.1643188734104.JavaMail.zimbra@u-pem.fr> <059B1AC6-683E-424E-8CCB-E29AB0BCBB6B@oracle.com> <5079388C-93D2-42EA-BA99-F8312ABF26B9@oracle.com> Message-ID: <794F6E7D-1CC5-4594-B6D8-42ED5498886F@oracle.com> On Jan 26, 2022, at 6:14 PM, John Rose > wrote: On 26 Jan 2022, at 16:55, Dan Smith wrote: On Jan 26, 2022, at 4:55 PM, John Rose > wrote: Independently of that, for the specific case of Object , having a query function Class.instanceKind , which returns ?NONE? for abstracts else ?VALUE? or ?IDENTITY?, would encode the same information we are looking at with those marker interfaces. Right, so you're envisioning a move in which, rather than 'obj instanceof ValueObject', the dynamic test is 'obj.getClass().instanceKind() == VALUE'. ... 'Object.class.instanceKind()' must return NONE, just as Object.class must not implement either IdentityObject or ValueObject. That last ?must? is necessarily true, but the second-to-last ?must? is not necessarily true. That?s my point here. Okay, I understand?it's possible for library code to do whatever arbitrary things it wants, while 'instanceof' has specific, fixed behavior. But... I don't really see how clients of this method would be comfortable with 'Object.class.instanceKind()' and 'Runnable.class.instanceKind()' returning different things. They've both got to be 'NONE', it seems to me. What does 'NONE' mean, if not "instances of this Class (and its subclasses) can be both value classes and identity classes"? (I guess we could have two methods, one of which is called 'directInstanceKind'. But how likely would users be to use the right one, depending on which question they were trying to ask? And wouldn't those users smart enough to ask the right question be okay just testing for Object.class as a special case?) From daniel.smith at oracle.com Fri Jan 28 18:00:09 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 28 Jan 2022 18:00:09 +0000 Subject: VM model and aconst_init In-Reply-To: <459A0059-7BE6-4925-8B69-78DB7C6C89C8@oracle.com> References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> <1BB6397E-2103-4AF3-A857-02CF2FA0B453@oracle.com> <1577708639.3794959.1643107810290.JavaMail.zimbra@u-pem.fr> <459A0059-7BE6-4925-8B69-78DB7C6C89C8@oracle.com> Message-ID: I'll chime in that I am coming around to disjoint Q/L, and here are a couple of thoughts on how to reconcile that with VM generics. On Jan 27, 2022, at 2:00 PM, John Rose > wrote: Furthermore, i believe that subtyping is a key to avoid multiple bytecode verification of the generics code. I recommend a far simpler technique: Just don?t. Don?t multiple-verify, and and don?t do anything that would need such extra verification (with different types) to give the same answer (as the first verification). Also, don?t think of a JVM-loaded set of bytecodes as every having more than one operational meaning. (I don?t understand this obsession, not just with you, of imagining generic bytecodes as statically ?recopied? or ?recapitulated? or ?stamped out? for each generic instance. I think it's an important aspect of parametric polymorphism that properties that are true in the generic code continue to be true for specific instantiations. (It's complicated, I'm not making a formal rule here, but there's an intuition there that I think is right.) So the key for me is: if the generic code says you're going to aload a Ljava/lang/Object and then store it with putfield, it should be true that, *modulo optimizations*, all instantiations will load an Object and store it in an Object field. That doesn't mean we're literally verifying it for each instantiation, but it's how I expect code authors to understand what they are saying. By example, with the TypeRestriction attribute [1], the restriction has to be subtype of the declared type/descriptor. No, just no. (Not your fault; I suppose the TR docs are in a confusing state.) There?s no reason to make that restriction (linking TRs to the verifier type system), other than the futile obsession I called out above. And if you do you make yourself sorry when you remember about the verifier?s I/J/F/D types. And for many other reasons, such as instance types which are inaccessible to the generic code. And the simple fact that bytecodes are not really generic-capable, directly, without serious (and futile) redesign. Making bytecodes directly generic (in the sense of contextually re-type-able) is never going to be as simple as using a TR mechanism which occurs apart from the verifier. A ?type system? for TRs is pretty meaningless, from the JVM POV. In the JVM, a TR (despite its name) is most fundamentally an operational specification of how to project (G->I) or embed (I->G) instance types into generic types. It?s a function pair. It can connect I=int to G=Object, which the JVM knows are disjoint types. I do think it's important that type restrictions are polymorphic: if there's a type restriction on my above Object, it should *both* be true that the value is an Object, and the value has whatever property the type restriction claims. A type restriction can't make the value no longer an Object. But I think we can hold on to this property and still support disjoint Q/L. How? By not allowing type restrictions to literally claim that values have Q types. Instead, they claim that a value with some L type is *freely convertible to* a particular Q type. (This may be the same thing as John saying the type restriction involves projections and embeddings, although I'm not sure I would make it the type restriction's responsibility to encapsulate those conversions.) So, for example: a type restriction that we might spell as 'QPoint' (and maybe that notation is a mistake) is an assertion that a particular L-typed variable always stores non-null objects for which 'instanceof Point' is true. *But they're still objects*, as far as the abstract JVM is concerned. Then the JVM implementation is free to recognize that it can use the same encoding it uses for the actual type 'QPoint' to store things in the variable. There are a couple places where reality intrudes on this simple model: - The initial value of a field/array with a type restriction is determined by that type restriction (because, e.g., 'null' can't satisfy the 'QPoint' restriction) - Type restrictions may introduce tearing risks, which would have to be explained by specifying the possibility that a JVM implementation may use type restrictions to optimize storage of value object instances of primitive classes, encoding them as primitive values I'm left feeling somewhat uneasy that we end up with a world in which directly-typed code has Q types, while specialized generic code has as its "types"?two different ways to explain the exact same thing, in some sense duplicating efforts?but I think we can live with that. On the other hand, it's a nice win that the language runtime model is more closely aligned with the JVM's runtime model (where value objects and primitive values are two distinct things). From daniel.smith at oracle.com Fri Jan 28 23:54:23 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 28 Jan 2022 23:54:23 +0000 Subject: Interface pollution and JVM invariants In-Reply-To: References: Message-ID: <7B514652-96EE-434C-9710-D3EABE9EA15C@oracle.com> On Jan 28, 2022, at 2:28 PM, Dan Heidinga > wrote: public WeakReference(T o) { if (o.getClass().isValue()) throw IAE; referent = o; } That kind of check is easy to miss (or assume isn't required) based on a straightforward reading of the source code. I like the IO/VO interfaces as they let us put the constraints "must be identity (or not)" in the type system but having them as interfaces means the guarantees aren't strictly enforced by the runtime. A useful observation, thanks! We should be careful not to fall into the trap of thinking 'IdentityObject' as a type guarantees that you're not operating on a value object, where such a guarantee would be important. Even if you recognize the problem, it can be hard to address it in source. This won't catch it (replace "Runnable" with whatever interface you care about): void test(Runnable r) { this.r = (Runnable) r; // javac ignores "redundant" cast } This will: void test(Runnable r) { if (r instanceof Runnable) { this.r = r; } else { throw new ClassCastException(); } } From brian.goetz at oracle.com Sat Jan 29 17:40:13 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 29 Jan 2022 12:40:13 -0500 Subject: VM model and aconst_init In-Reply-To: References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> <1BB6397E-2103-4AF3-A857-02CF2FA0B453@oracle.com> <1577708639.3794959.1643107810290.JavaMail.zimbra@u-pem.fr> <459A0059-7BE6-4925-8B69-78DB7C6C89C8@oracle.com> Message-ID: I think we're kind of stuck with a model like this, whether we admit it or not, because we have to allow for pervasive erasure and be able to fall back to looser linkage conventions.? SO the type restriction was always optimistic; we might say "Object restricted to QPoint", but someone could show up and say "what's a restriction?" and then we have to respond "I said Object, sir". On 1/28/2022 1:00 PM, Dan Smith wrote: > > But I think we can hold on to this property and still support disjoint > Q/L. How? By not allowing type restrictions to literally claim that > values have Q types. Instead, they claim that a value with some L type > is *freely convertible to* a particular Q type. (This may be the same > thing as John saying the type restriction involves projections and > embeddings, although I'm not sure I would make it the type > restriction's responsibility to encapsulate those conversions.) > > So, for example: a type restriction that we might spell as 'QPoint' > (and maybe that notation is a mistake) is an assertion that a > particular L-typed variable always stores non-null objects for which > 'instanceof Point' is true. *But they're still objects*, as far as the > abstract JVM is concerned. Then the JVM implementation is free to > recognize that it can use the same encoding it uses for the actual > type 'QPoint' to store things in the variable. > > There are a couple places where reality intrudes on this simple model: > - The initial value of a field/array with a type restriction is > determined by that type restriction (because, e.g., 'null' can't > satisfy the 'QPoint' restriction) > - Type restrictions may introduce tearing risks, which would have to > be explained by specifying the possibility that a JVM implementation > may use type restrictions to optimize storage of value object > instances of primitive classes, encoding them as primitive values > > I'm left feeling somewhat uneasy that we end up with a world in which > directly-typed code has Q types, while specialized generic code has > as its "types"?two different > ways to explain the exact same thing, in some sense duplicating > efforts?but I think we can live with that. On the other hand, it's a > nice win that the language runtime model is more closely aligned with > the JVM's runtime model (where value objects and primitive values are > two distinct things). From forax at univ-mlv.fr Mon Jan 31 18:04:26 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 31 Jan 2022 19:04:26 +0100 (CET) Subject: [External] : Re: VM model and aconst_init In-Reply-To: <459A0059-7BE6-4925-8B69-78DB7C6C89C8@oracle.com> References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> <1BB6397E-2103-4AF3-A857-02CF2FA0B453@oracle.com> <1577708639.3794959.1643107810290.JavaMail.zimbra@u-pem.fr> <459A0059-7BE6-4925-8B69-78DB7C6C89C8@oracle.com> Message-ID: <1970854028.7573029.1643652266525.JavaMail.zimbra@u-pem.fr> > From: "John Rose" > To: "Remi Forax" > Cc: "valhalla-spec-experts" > Sent: Thursday, January 27, 2022 10:00:20 PM > Subject: Re: [External] : Re: VM model and aconst_init > On 25 Jan 2022, at 2:50, [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] wrote: >> Let's talk a bit about having the L world and the Q world completely disjoint at >> least from the bytecode verifier POV. > Yes please; indeed, that is my strong recommendation. Doing this ( Q>: perhaps, the ?squinting cat operator??) allows the interpreter (as well as > verifier) to concentrate one ?mode? at a time, for values on stack. This in > turn unlocks implementation possibilities which are not as practical when Q<:L > (our previous plan of record). > But keeping Q and L disjoint is a relative notion. Clearly they have to > interoperate somewhere . And I believe that ?somewhere?, where Q?s can become > L?s and vice versa, is in the dynamic behavior of bytecodes, not in the type > system (in the verifier) which relates multiple bytecodes. >> It means that we need some checkcasts to move in both direction, from a Q-type >> to a L-type and vice-versa. > Correct. Checkcast is the mode-change operator (both directions). No need to > create a new bytecode for either direction. >> But at the same time, an array of L-type is a subtype of an array of Q-type ? > That depends on what you mean by ?subtype?. The JVM has multiple type systems. > In the verifier we might have a choice of allowing Q[]<:L[] or not. It?s better > not. That puts no limitations on what the aaload and aastore instructions do. > They just dynamically sense whether the array is flattened or not and DTRT. > That has nothing to do with verifier types. In fact, it?s a separated job of > the runtime type system. > For the runtime type system, we will say that arrays are covariant across an > extends which is an augmentation of the subtype system. So for the runtime > T[]<:U[] iff T extends U , so Q[]<:L[] (for the same class in both modes). But > in the verifier Q[]>: given Java?s history with arrays and the weakness of the verifier. It did not occur to me that the verifier type system and the runtime type system can be so divorced. Currently, the verifier just does not do some verifications (related to interfaces) that are done later at runtime. But you propose to do the opposite, something that is not legal for the verifier but legal at runtime. I don't say that this is not possible to do that, but this idea has a huge cost in term of engineering because it requires to think in terms of two different mental models, so every discrepancies can be a source of bug. Also, what does it means for java.lang.invoke which tries to follow the bytecode semantics but at runtime ? [...] >> Furthermore, i believe that subtyping is a key to avoid multiple bytecode >> verification of the generics code. > I recommend a far simpler technique: Just don?t. Don?t multiple-verify, and and > don?t do anything that would need such extra verification (with different > types) to give the same answer (as the first verification). Also, don?t think > of a JVM-loaded set of bytecodes as every having more than one operational > meaning. I agree, no multiple-verify, but like the VM has to do some runtime checks akin when there is a checkcast with an interface, the VM has to do something when there is a TypeRestriction. [...] > But don?t confuse the implementation of generics from the semantic model . The > implementation will (in our plan of record) use something the verifier knows > nothing about: Dynamic type restrictions (or something equivalent) which manage > the projections and embeddings of specific instance types into the generic > bound type. These do not have to be (indeed must not be , see below) identified > as verifier types. They are associations of pairs of JVM runtime types. >> By example, with the TypeRestriction attribute [1], the restriction has to be >> subtype of the declared type/descriptor. > No, just no. (Not your fault; I suppose the TR docs are in a confusing state.) > There?s no reason to make that restriction (linking TRs to the verifier type > system), other than the futile obsession I called out above. And if you do you > make yourself sorry when you remember about the verifier?s I/J/F/D types. And > for many other reasons, such as instance types which are inaccessible to the > generic code. And the simple fact that bytecodes are not really > generic-capable, directly, without serious (and futile) redesign. > Making bytecodes directly generic (in the sense of contextually re-type-able) is > never going to be as simple as using a TR mechanism which occurs apart from the > verifier . Yes, > (Plus, I don?t trust the verifier to take on new jobs. In its existing job it > has been an ever-flowing fountain of security bugs. It made a hash of jsr/ret, > and continually messed up instance creation paths. I won?t re-hire it for any > new jobs. It?s good for matching descriptors in symbolic references, and maybe > for helping the interpreter make a minor optimization to the getfield and > putfield bytecodes, but that?s about it. The v-table index caching for > invokevirtual looks cool but is effectively a no-op. The JIT makes all of the > above irrelevant to performance, once the JIT kicks in. So the verifier gives > an incremental push to startup performance, or interpreter-only performance if > anyone still cares. Plus it gives us extra ?security? except when it doesn?t. > OK, I?ll stop with the verifier now.) It also gives meaningful (mostly) error messages when my students try to generate bytecodes :) > A ?type system? for TRs is pretty meaningless, from the JVM POV. In the JVM, a > TR (despite its name) is most fundamentally an operational specification of how > to project (G->I) or embed (I->G) instance types into generic types. It?s a > function pair. It can connect I=int to G=Object , which the JVM knows are > disjoint types. A TR is very useful for implementing source-level type > relations that the JVM doesn?t know directly about, such as int extends Object > ( int<|Object maybe?). So talking about subtypes in TRs is nonsense as far as I > can see, unless in the special sense that ?we use TRs in a translation > strategy, to help extend the JVM?s limited subtyping notions to our language?s > more expansive subtyping notions?. If TRs were a language-level construct, > then, yeah, maybe we say a TR has some constraints about language-level > subtypes. But it?s a JVM-only construct that plugs in where the JVM?s type > system doesn?t work well enough. First, i don't think you need int extends Object, because you can have Qjava/lang/Integer; which already extends Object, ArrayList being another spelling of ArrayList which is great because it's a subtype (at runtme) of ArrayList. TR is not a silver bullet, TR has only two paths, one using the declared type and one using the restricted type, when there is inheritance, unlike bridges that can generate several paths (at the cost of several methods/entry points), with TR you are stuck with two, the fully optimized and the un-optimized, anything in between will go to the un-optimized path. Furthermore, any codes with a wildcard will go through the un-optimized path. So the runtime checks associated with TRs as to be fast. Hence the idea to restrict them to subtyping checks. > (Should we rename TR to PE, ?Project-Embedding Pair?? I like TR, but maybe only > because I?m used to the term. As a JVM-level concept, it doesn?t need to get a > highly polished name. The audience for it is limited.) R?mi From forax at univ-mlv.fr Mon Jan 31 18:06:23 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 31 Jan 2022 19:06:23 +0100 (CET) Subject: VM model and aconst_init In-Reply-To: References: <2016676454.10625433.1641990677691.JavaMail.zimbra@u-pem.fr> <1BB6397E-2103-4AF3-A857-02CF2FA0B453@oracle.com> <1577708639.3794959.1643107810290.JavaMail.zimbra@u-pem.fr> <459A0059-7BE6-4925-8B69-78DB7C6C89C8@oracle.com> Message-ID: <142411170.7573436.1643652383285.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "daniel smith" , "John Rose" > > Cc: "Remi Forax" , "valhalla-spec-experts" > > Sent: Saturday, January 29, 2022 6:40:13 PM > Subject: Re: VM model and aconst_init > I think we're kind of stuck with a model like this, whether we admit it or not, > because we have to allow for pervasive erasure and be able to fall back to > looser linkage conventions. SO the type restriction was always optimistic; we > might say "Object restricted to QPoint", but someone could show up and say > "what's a restriction?" and then we have to respond "I said Object, sir". yes, because of raw types/erasure or wildcard types ! R?mi > On 1/28/2022 1:00 PM, Dan Smith wrote: >> But I think we can hold on to this property and still support disjoint Q/L. How? >> By not allowing type restrictions to literally claim that values have Q types. >> Instead, they claim that a value with some L type is *freely convertible to* a >> particular Q type. (This may be the same thing as John saying the type >> restriction involves projections and embeddings, although I'm not sure I would >> make it the type restriction's responsibility to encapsulate those >> conversions.) >> So, for example: a type restriction that we might spell as 'QPoint' (and maybe >> that notation is a mistake) is an assertion that a particular L-typed variable >> always stores non-null objects for which 'instanceof Point' is true. *But >> they're still objects*, as far as the abstract JVM is concerned. Then the JVM >> implementation is free to recognize that it can use the same encoding it uses >> for the actual type 'QPoint' to store things in the variable. >> There are a couple places where reality intrudes on this simple model: >> - The initial value of a field/array with a type restriction is determined by >> that type restriction (because, e.g., 'null' can't satisfy the 'QPoint' >> restriction) >> - Type restrictions may introduce tearing risks, which would have to be >> explained by specifying the possibility that a JVM implementation may use type >> restrictions to optimize storage of value object instances of primitive >> classes, encoding them as primitive values >> I'm left feeling somewhat uneasy that we end up with a world in which >> directly-typed code has Q types, while specialized generic code has > instances of primitive classes> as its "types"?two different ways to explain >> the exact same thing, in some sense duplicating efforts?but I think we can live >> with that. On the other hand, it's a nice win that the language runtime model >> is more closely aligned with the JVM's runtime model (where value objects and >> primitive values are two distinct things).