From forax at univ-mlv.fr Sun Sep 1 15:27:13 2019 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 1 Sep 2019 17:27:13 +0200 (CEST) Subject: Record classfile and runtime API Message-ID: <6151459.2236600.1567351633343.JavaMail.zimbra@u-pem.fr> Hi all, i think the current generated classfile bytecodes can be improved. For a record Point(int x, int y) { } The classfile contains: - a specific class attribute Record Record: int x; descriptor: I int y; descriptor: I which allows compiler separate compilation. - two bootstrap methods - one for the invodynamic calls used inside toString/equals/hashCode this bootstrap method takes 4 arguments #8 fr/umlv/record/Point #51 x;y #53 REF_getField fr/umlv/record/Point.x:I #54 REF_getField fr/umlv/record/Point.y:I - one for the constant dynamic used inside the pattern extractor/handler this bootstrap method takes 3 arguments #8 fr/umlv/record/Point #53 REF_getField fr/umlv/record/Point.x:I #54 REF_getField fr/umlv/record/Point.y:I so we have 3 different ways to represent exactly the same thing, a record Point is composed of two component int x and int y, i believe we can do better. The arguments of the bootstrap methods are redundant, you don't need to pass the record class because the Lookup object contains the record class (you have the API in Lookup to create an intermediary lookup from a Lookup and a class so no need to duplicate that information). For the first boostrap methods, the parameter containing "x;y" is due to the fact that a constant method handle is opaque by default so you can not get the corresponding field name. First, if you have the lookup that have created that object, you can get back the name with a "reveal". But i think here, the problem is that the arguments are the constant method handles and not the constant decriptor (ConstantDec). The java.lang.invoke API has currently the limitation that you can not use the ConstantDesc API to type values of the constant pool. How can we do better ? We only need the attribute Record, all other informations can be retrieved at runtime from the attribute Record. Now we have to talk about the reflection API, currently the method that reflects the attribute Record is Class.getRecordAccessors() that returns an array of Method corresponding to the accessors and there are many things wrong with this method. First, while returning the accessors may be a good idea, it should not be the sole API, we should have an API that reflects the data of the attribute Record itself. Then it returns Method so a live object that may have triggered the classloading of the types of each record component that may notexist at runtime, we have introduce the constable API, we should using it here. And the implementation has several issues, first it can throws a NoSuchMethodException which is a checked exception, then it uses getDeclaredMethod which returns the methods with the most precise return type which is not what you want here. Here is an example that show why it's buggy, le suppose i have an interface and a record interface I { default Object x() { return 0; } } record R(Object x) implements I { } now let say i change I without recompiling R interface I { default String x() { return 0; } } R.class.getRecordAccessors() will return the method x() that returns a String instead of the one that returns an Object. So first, let's have a method getRecordComponentFields() in java.lang.Class that returns an array of DirectMethodHandleDesc corresponding to method ref to the fields of a Record. It uses a constant desc, so no weird classloading will appear here. And it's better than an array of MethodHandle because a DirectMethodHandleDesc is not an opaque object. With that, we can unify the bootstrap methods to have only one bootstrap method with zero argument, the one that return a pattern/extractor can be implemented into another class but can be queried from the same bsm that the one that is needed for toString/equals/hashCode. BTW, why do we need an extractor for a record ? we should not need one because we can inside the algorithm that create the pattern tree have a if class.isRecord() ? (the same way the serailization is specilized for enums by example). On problem with this approach is that while it reduces the bytecode size, it will also resolve the constant method handles to the accessors several times but i think it's better to have this kind of cache inside java.lang.Class than to introduce a constant dynamic that will return a constant list of constant method handles instead. Introducing something in the constant pool for caching reason seems not a good idea if in the future we want to change the way it works. R?mi From forax at univ-mlv.fr Sun Sep 1 17:19:22 2019 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 1 Sep 2019 19:19:22 +0200 (CEST) Subject: Record classfile and runtime API In-Reply-To: <6151459.2236600.1567351633343.JavaMail.zimbra@u-pem.fr> References: <6151459.2236600.1567351633343.JavaMail.zimbra@u-pem.fr> Message-ID: <1545968277.2246078.1567358362030.JavaMail.zimbra@u-pem.fr> sorry wrong list. R?mi ----- Mail original ----- > De: "Remi Forax" > ?: "valhalla-spec-experts" > Envoy?: Dimanche 1 Septembre 2019 17:27:13 > Objet: Record classfile and runtime API > Hi all, > i think the current generated classfile bytecodes can be improved. > > For a record Point(int x, int y) { } > > The classfile contains: > - a specific class attribute Record > Record: > int x; > descriptor: I > int y; > descriptor: I > which allows compiler separate compilation. > - two bootstrap methods > - one for the invodynamic calls used inside toString/equals/hashCode > this bootstrap method takes 4 arguments > #8 fr/umlv/record/Point > #51 x;y > #53 REF_getField fr/umlv/record/Point.x:I > #54 REF_getField fr/umlv/record/Point.y:I > - one for the constant dynamic used inside the pattern extractor/handler > this bootstrap method takes 3 arguments > #8 fr/umlv/record/Point > #53 REF_getField fr/umlv/record/Point.x:I > #54 REF_getField fr/umlv/record/Point.y:I > > so we have 3 different ways to represent exactly the same thing, a record Point > is composed of two component int x and int y, i believe we can do better. > > The arguments of the bootstrap methods are redundant, you don't need to pass the > record class because the Lookup object contains the record class (you have the > API in Lookup to create an intermediary lookup from a Lookup and a class so no > need to duplicate that information). For the first boostrap methods, the > parameter containing "x;y" is due to the fact that a constant method handle is > opaque by default so you can not get the corresponding field name. First, if > you have the lookup that have created that object, you can get back the name > with a "reveal". But i think here, the problem is that the arguments are the > constant method handles and not the constant decriptor (ConstantDec). > > The java.lang.invoke API has currently the limitation that you can not use the > ConstantDesc API to type values of the constant pool. > > How can we do better ? > We only need the attribute Record, all other informations can be retrieved at > runtime from the attribute Record. > > Now we have to talk about the reflection API, currently the method that reflects > the attribute Record is Class.getRecordAccessors() that returns an array of > Method corresponding to the accessors and there are many things wrong with this > method. > First, while returning the accessors may be a good idea, it should not be the > sole API, we should have an API that reflects the data of the attribute Record > itself. Then it returns Method so a live object that may have triggered the > classloading of the types of each record component that may notexist at > runtime, we have introduce the constable API, we should using it here. And the > implementation has several issues, first it can throws a NoSuchMethodException > which is a checked exception, then it uses getDeclaredMethod which returns the > methods with the most precise return type which is not what you want here. > Here is an example that show why it's buggy, le suppose i have an interface and > a record > interface I { > default Object x() { return 0; } > } > record R(Object x) implements I { } > now let say i change I without recompiling R > interface I { > default String x() { return 0; } > } > R.class.getRecordAccessors() will return the method x() that returns a String > instead of the one that returns an Object. > > So first, let's have a method getRecordComponentFields() in java.lang.Class that > returns an array of DirectMethodHandleDesc corresponding to method ref to the > fields of a Record. It uses a constant desc, so no weird classloading will > appear here. And it's better than an array of MethodHandle because a > DirectMethodHandleDesc is not an opaque object. > > With that, we can unify the bootstrap methods to have only one bootstrap method > with zero argument, the one that return a pattern/extractor can be implemented > into another class but can be queried from the same bsm that the one that is > needed for toString/equals/hashCode. > > BTW, why do we need an extractor for a record ? we should not need one because > we can inside the algorithm that create the pattern tree have a if > class.isRecord() ? (the same way the serailization is specilized for enums by > example). > > On problem with this approach is that while it reduces the bytecode size, it > will also resolve the constant method handles to the accessors several times > but i think it's better to have this kind of cache inside java.lang.Class than > to introduce a constant dynamic that will return a constant list of constant > method handles instead. Introducing something in the constant pool for caching > reason seems not a good idea if in the future we want to change the way it > works. > > R?mi From eolivelli at gmail.com Sun Sep 1 18:28:24 2019 From: eolivelli at gmail.com (Enrico Olivelli) Date: Sun, 1 Sep 2019 20:28:24 +0200 Subject: Record classfile and runtime API In-Reply-To: <1545968277.2246078.1567358362030.JavaMail.zimbra@u-pem.fr> References: <6151459.2236600.1567351633343.JavaMail.zimbra@u-pem.fr> <1545968277.2246078.1567358362030.JavaMail.zimbra@u-pem.fr> Message-ID: Remi, May I ask which is the list for this discussion? IMHO this 'record' work overlaps partially with Valhalla work. Regards Enrico Il dom 1 set 2019, 19:21 Remi Forax ha scritto: > sorry wrong list. > > R?mi > > ----- Mail original ----- > > De: "Remi Forax" > > ?: "valhalla-spec-experts" > > Envoy?: Dimanche 1 Septembre 2019 17:27:13 > > Objet: Record classfile and runtime API > > > Hi all, > > i think the current generated classfile bytecodes can be improved. > > > > For a record Point(int x, int y) { } > > > > The classfile contains: > > - a specific class attribute Record > > Record: > > int x; > > descriptor: I > > int y; > > descriptor: I > > which allows compiler separate compilation. > > - two bootstrap methods > > - one for the invodynamic calls used inside toString/equals/hashCode > > this bootstrap method takes 4 arguments > > #8 fr/umlv/record/Point > > #51 x;y > > #53 REF_getField fr/umlv/record/Point.x:I > > #54 REF_getField fr/umlv/record/Point.y:I > > - one for the constant dynamic used inside the pattern > extractor/handler > > this bootstrap method takes 3 arguments > > #8 fr/umlv/record/Point > > #53 REF_getField fr/umlv/record/Point.x:I > > #54 REF_getField fr/umlv/record/Point.y:I > > > > so we have 3 different ways to represent exactly the same thing, a > record Point > > is composed of two component int x and int y, i believe we can do better. > > > > The arguments of the bootstrap methods are redundant, you don't need to > pass the > > record class because the Lookup object contains the record class (you > have the > > API in Lookup to create an intermediary lookup from a Lookup and a class > so no > > need to duplicate that information). For the first boostrap methods, the > > parameter containing "x;y" is due to the fact that a constant method > handle is > > opaque by default so you can not get the corresponding field name. > First, if > > you have the lookup that have created that object, you can get back the > name > > with a "reveal". But i think here, the problem is that the arguments are > the > > constant method handles and not the constant decriptor (ConstantDec). > > > > The java.lang.invoke API has currently the limitation that you can not > use the > > ConstantDesc API to type values of the constant pool. > > > > How can we do better ? > > We only need the attribute Record, all other informations can be > retrieved at > > runtime from the attribute Record. > > > > Now we have to talk about the reflection API, currently the method that > reflects > > the attribute Record is Class.getRecordAccessors() that returns an array > of > > Method corresponding to the accessors and there are many things wrong > with this > > method. > > First, while returning the accessors may be a good idea, it should not > be the > > sole API, we should have an API that reflects the data of the attribute > Record > > itself. Then it returns Method so a live object that may have triggered > the > > classloading of the types of each record component that may notexist at > > runtime, we have introduce the constable API, we should using it here. > And the > > implementation has several issues, first it can throws a > NoSuchMethodException > > which is a checked exception, then it uses getDeclaredMethod which > returns the > > methods with the most precise return type which is not what you want > here. > > Here is an example that show why it's buggy, le suppose i have an > interface and > > a record > > interface I { > > default Object x() { return 0; } > > } > > record R(Object x) implements I { } > > now let say i change I without recompiling R > > interface I { > > default String x() { return 0; } > > } > > R.class.getRecordAccessors() will return the method x() that returns a > String > > instead of the one that returns an Object. > > > > So first, let's have a method getRecordComponentFields() in > java.lang.Class that > > returns an array of DirectMethodHandleDesc corresponding to method ref > to the > > fields of a Record. It uses a constant desc, so no weird classloading > will > > appear here. And it's better than an array of MethodHandle because a > > DirectMethodHandleDesc is not an opaque object. > > > > With that, we can unify the bootstrap methods to have only one bootstrap > method > > with zero argument, the one that return a pattern/extractor can be > implemented > > into another class but can be queried from the same bsm that the one > that is > > needed for toString/equals/hashCode. > > > > BTW, why do we need an extractor for a record ? we should not need one > because > > we can inside the algorithm that create the pattern tree have a if > > class.isRecord() ? (the same way the serailization is specilized for > enums by > > example). > > > > On problem with this approach is that while it reduces the bytecode > size, it > > will also resolve the constant method handles to the accessors several > times > > but i think it's better to have this kind of cache inside > java.lang.Class than > > to introduce a constant dynamic that will return a constant list of > constant > > method handles instead. Introducing something in the constant pool for > caching > > reason seems not a good idea if in the future we want to change the way > it > > works. > > > > R?mi > From forax at univ-mlv.fr Sun Sep 1 18:51:11 2019 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 1 Sep 2019 20:51:11 +0200 (CEST) Subject: Record classfile and runtime API In-Reply-To: References: <6151459.2236600.1567351633343.JavaMail.zimbra@u-pem.fr> <1545968277.2246078.1567358362030.JavaMail.zimbra@u-pem.fr> Message-ID: <1964291640.2251362.1567363871561.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Enrico Olivelli" > ?: "Valhalla Expert Group Observers" > Envoy?: Dimanche 1 Septembre 2019 20:28:24 > Objet: Re: Record classfile and runtime API > Remi, > May I ask which is the list for this discussion? yes, https://mail.openjdk.java.net/mailman/listinfo/amber-spec-experts > > IMHO this 'record' work overlaps partially with Valhalla work. > > Regards > Enrico R?mi > > > Il dom 1 set 2019, 19:21 Remi Forax ha scritto: > >> sorry wrong list. >> >> R?mi >> >> ----- Mail original ----- >> > De: "Remi Forax" >> > ?: "valhalla-spec-experts" >> > Envoy?: Dimanche 1 Septembre 2019 17:27:13 >> > Objet: Record classfile and runtime API >> >> > Hi all, >> > i think the current generated classfile bytecodes can be improved. >> > >> > For a record Point(int x, int y) { } >> > >> > The classfile contains: >> > - a specific class attribute Record >> > Record: >> > int x; >> > descriptor: I >> > int y; >> > descriptor: I >> > which allows compiler separate compilation. >> > - two bootstrap methods >> > - one for the invodynamic calls used inside toString/equals/hashCode >> > this bootstrap method takes 4 arguments >> > #8 fr/umlv/record/Point >> > #51 x;y >> > #53 REF_getField fr/umlv/record/Point.x:I >> > #54 REF_getField fr/umlv/record/Point.y:I >> > - one for the constant dynamic used inside the pattern >> extractor/handler >> > this bootstrap method takes 3 arguments >> > #8 fr/umlv/record/Point >> > #53 REF_getField fr/umlv/record/Point.x:I >> > #54 REF_getField fr/umlv/record/Point.y:I >> > >> > so we have 3 different ways to represent exactly the same thing, a >> record Point >> > is composed of two component int x and int y, i believe we can do better. >> > >> > The arguments of the bootstrap methods are redundant, you don't need to >> pass the >> > record class because the Lookup object contains the record class (you >> have the >> > API in Lookup to create an intermediary lookup from a Lookup and a class >> so no >> > need to duplicate that information). For the first boostrap methods, the >> > parameter containing "x;y" is due to the fact that a constant method >> handle is >> > opaque by default so you can not get the corresponding field name. >> First, if >> > you have the lookup that have created that object, you can get back the >> name >> > with a "reveal". But i think here, the problem is that the arguments are >> the >> > constant method handles and not the constant decriptor (ConstantDec). >> > >> > The java.lang.invoke API has currently the limitation that you can not >> use the >> > ConstantDesc API to type values of the constant pool. >> > >> > How can we do better ? >> > We only need the attribute Record, all other informations can be >> retrieved at >> > runtime from the attribute Record. >> > >> > Now we have to talk about the reflection API, currently the method that >> reflects >> > the attribute Record is Class.getRecordAccessors() that returns an array >> of >> > Method corresponding to the accessors and there are many things wrong >> with this >> > method. >> > First, while returning the accessors may be a good idea, it should not >> be the >> > sole API, we should have an API that reflects the data of the attribute >> Record >> > itself. Then it returns Method so a live object that may have triggered >> the >> > classloading of the types of each record component that may notexist at >> > runtime, we have introduce the constable API, we should using it here. >> And the >> > implementation has several issues, first it can throws a >> NoSuchMethodException >> > which is a checked exception, then it uses getDeclaredMethod which >> returns the >> > methods with the most precise return type which is not what you want >> here. >> > Here is an example that show why it's buggy, le suppose i have an >> interface and >> > a record >> > interface I { >> > default Object x() { return 0; } >> > } >> > record R(Object x) implements I { } >> > now let say i change I without recompiling R >> > interface I { >> > default String x() { return 0; } >> > } >> > R.class.getRecordAccessors() will return the method x() that returns a >> String >> > instead of the one that returns an Object. >> > >> > So first, let's have a method getRecordComponentFields() in >> java.lang.Class that >> > returns an array of DirectMethodHandleDesc corresponding to method ref >> to the >> > fields of a Record. It uses a constant desc, so no weird classloading >> will >> > appear here. And it's better than an array of MethodHandle because a >> > DirectMethodHandleDesc is not an opaque object. >> > >> > With that, we can unify the bootstrap methods to have only one bootstrap >> method >> > with zero argument, the one that return a pattern/extractor can be >> implemented >> > into another class but can be queried from the same bsm that the one >> that is >> > needed for toString/equals/hashCode. >> > >> > BTW, why do we need an extractor for a record ? we should not need one >> because >> > we can inside the algorithm that create the pattern tree have a if >> > class.isRecord() ? (the same way the serailization is specilized for >> enums by >> > example). >> > >> > On problem with this approach is that while it reduces the bytecode >> size, it >> > will also resolve the constant method handles to the accessors several >> times >> > but i think it's better to have this kind of cache inside >> java.lang.Class than >> > to introduce a constant dynamic that will return a constant list of >> constant >> > method handles instead. Introducing something in the constant pool for >> caching >> > reason seems not a good idea if in the future we want to change the way >> it >> > works. >> > >> > R?mi From john.r.rose at oracle.com Wed Sep 25 20:33:59 2019 From: john.r.rose at oracle.com (John Rose) Date: Wed, 25 Sep 2019 13:33:59 -0700 Subject: Valhalla EG 20190925 In-Reply-To: References: Message-ID: On Sep 25, 2019, at 10:04 AM, David Simms wrote: > > - Remi: As a compiler strategy ? > - DanH: Allow user to hand write (e.g. pre-exisiting mig, i.e. Optional) > - Prefer compiler does the work > - Brian: sealing would provide trust to the VM > - John: sealed super-type that is empty (empty interface), a good way to go ? > - Brian: possible > - Brian: which method we go with, must be driven primarily by primitives, how to get to int. > - Something has got to give, fuzzing current type relationships. "Digusting Hacks", "Special Behavior". > - Eclair and other food analogies continue, but we need to figure out what we get for a given path first I think the consensus is that for new value types (new eclairs) the JVM doesn?t have to do much and the translation strategy just emits standard JVM stuff. Sealing will let the JVM do optimizations at the JIT level. In the verifier and interpreter, sealed interfaces are probably not going to have special treatment so there is a small pre-JIT performance debit, compared with analogously ?sealed? super-classes. (For superclasses, and not for interfaces, the verifier provides static type checking which the interpreter can exploit for a modest performance bump.) Old types migrated to value types are a different matter. Examples are ju.Optional and (maybe) jl.Integer. Consensus is that some kind of powerful low-level bridging mechanism from the JVM will go a long way toward making the new forms of these things compatible with old linkage requests (old client code). My hope is that bridges are the main contribution from the JVM. These two use cases correspond to two ends of an implementation spectrum for the eclair pattern. A thin-skinned eclair (Brian says paper-wrapped muffin) corresponds to an empty marker interface B and a value class V (the bulky muffin inside) that does all the work. The translation strategy emits linkage requests only against V. If the user writes method calls against B, they are lowered to calls on V (with an invisible cast, and possible NPE). The legitimacy of lowering calls on B to V methods is proven by the fact (which javac can observe) that B is sealed to V alone. So B can be physically empty. This is a nice thing for the JVM, since the JVM doesn?t like to see redundant inputs; they just cause useless binary compatibility hazards and expensive useless corner cases. The other end of the eclair spectrum would be a thick wrapper and tiny filling. This seens desirable when the box type has a previous career as a value base class like ju.Optional. Linkage requests from both legacy code and new code would apply to the box type B and not to the value type V. The value type V would be minimized down to some sort of record-like type. This is easiest to think about when V has a single field, as is the case with jl.Integer. In that case, Integer supply ad hoc box logic while the inner ?int? would be the value type. Note that ?int? has no methods; perhaps there is a way for B (the thick layer of box) to supply *all* methods, and V (the crunchy center) to supply *only* the state component. That also would be a sweet spot for the JVM, again because there is no duplication between the two classes B and V. I think Brian called this a ?cannoli?. If it were savory it would be a tamale or dim sum bun with a fluffy outside and small bite at the center. I observe a spectrum here because it seems reasonable to allow users to ?get their hands on? B in new code also, refactoring it from a blank interface to something more interesting. I can?t imagine why the user wouldn?t just put everything down on V in new code, but that?s just a temporary ?can?t imagine?, not a true ?would never happen?. Or maybe in a migration case the new crunchy kernel V has some natural new methods which interact with the old methods on B. If V is to be minimized by moving everything onto B, it follows that (as Brian and David observed) something has to give. Maybe B as an interface needs to get more power to define V?s state vector. Or maybe B needs to be a different kind of super. If B is an abstract super class that ?abstractly? defines one field (or several fields) of state, then the concrete V can be totally blank, assuming there is a way for a value class to inherit state components. (Spinning the constructor is hard here, but maybe we can appeal to records for that.) So V is a record type all of whose components are inherited from B but concretized in V record-wise. Another option (besides interface or abstract class) is that the super of V is a template B of which V is really an instance B. This is not parametric polymorphism, but it is possibly a legitimate use of very ad hoc template polymorphism. Another way to think about using templates is that there are three types: B<_>, B and B. The job of the legacy class would be shared by the first two, while the new stuff would be expanded out from the third type. The types would all be named B (and B.class is the Object.getClass, always). The species would carry the distinctions between the three types. In this case, we might have jl.Integer<_> as a template that carries the legacy protocol (in all its glory), while Integer is an identity object in the old style (so you can still say ?new Integer(5)?). Meanwhile the sibling subtype Integer is a true value type, the inside of the eclair. (The template parameters ?legacy? and ?value? might be booleans or enums, or even a pair of types. It doesn?t matter much. Note, however, that this is likely to be a use case for template arguments which are *not* types, just as C++ has many such use cases.) ? John From brian.goetz at oracle.com Mon Sep 30 20:12:14 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 30 Sep 2019 16:12:14 -0400 Subject: Fwd: Migrating the primitive boxes to values In-Reply-To: References: Message-ID: <44f6ac27-f9ca-0329-5121-d5b403131587@oracle.com> Received on the -comments list (some time ago). -------- Forwarded Message -------- Subject: Migrating the primitive boxes to values Date: Sat, 26 Jan 2019 00:07:53 -0800 From: Jack Ammo To: valhalla-spec-comments at openjdk.java.net From an outsider's perspective, i'd like to ask a perhaps naive question about the locking issue on values. Instead of just opting to break existing code as you seem to be worried about, why not just box the value before locking? Assuming that a boxing conversion to a full fledged Object is still kicking around as a fall back, this seems like the most familiar option to me as a developer: i can technically do the lock on an Integer but it is not advised and results are not guaranteed. I understand that everyone has worked hard to avoid the cost of boxing, but in this particular case it seems like a decent option that is the least difficult to wrap my mind around. Locking requires a full Object, so values (like an int) need to be boxed as such. From brian.goetz at oracle.com Mon Sep 30 20:11:08 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 30 Sep 2019 16:11:08 -0400 Subject: Fwd: The Spirit of acmp In-Reply-To: References: Message-ID: <3269302a-14fa-5594-2524-18a8fab4e1e2@oracle.com> This was received on the -comments list (some time ago). The gist of this comment is that this particular commenter is willing to trade completeness for speed, by trying to make a "best efforts" attempt to have ACMP compare values, but punt when it comes to recursion (an idea we've discussed before.)? He gets there by appealing to the distinction between "pure" and "polluted" values, which has also come up before in the discussion. -------- Forwarded Message -------- Subject: The Spirit of acmp Date: Wed, 10 Apr 2019 03:55:12 +0200 From: Patrick Plieschnegger To: valhalla-spec-comments at openjdk.java.net Hello, Acmp poses some interesting problems that could influence fundamental aspects of Java. No consensus seems to exist about acmp yet, but we know that there are different categories of ?value types? with unique needs. The point of this mail is to explore the consequences of making a case distinction between different categories of ?value types? and find solutions to general concerns. I tried to be concise, but I think such a delicate topic needs to be done accurately (please bear with me). Before making my point, I want to re-iterate on the critical aspects of "==": - It must have a defined behavior for "value types" since they can be cast to Object. - It must be fast. A recursive descent for tree or list like structures is not an option. - It _should_ perform a substitutability test for "some cases". In this context I think "some cases" actually refers to "data carriers" such as "Point". At least for numerics I think it is a hard sell not to have "complex1 == complex2" in a language. Nonetheless, the real problem of "==" comes from non data classes like "Cursor" or "ValueTreeNode" as they violate the performance constraint when checking for substitutability. Because of this violation the only easy way out is to always return "false". The question is, can we get away with having the latter while still keeping the former? For this we could make a case distinction. First, we got "composed primitive" types which really want "==" and compare continuous bits in memory. But this puts some constraints on field types. ?Composed primitives" can contain only primitives or other "composed primitives" (hence the name). Technically, RefObjects could be legal fields, but relying on object identity for value substitutability scares me. But more pressingly are "inline class" types which are more akin to objects without identity (where "==" breaks apart). They have no restrictions on their fields and always return ?false?... and I think the consequences are manageable. First, you can counteract this by not letting "==" compile, which will make many developers aware of the problem. Furthermore, the application of "==" is rather narrow in scope. You use it on numbers, in null checks and as optimization for comparisons (after all, it is just a subset of the logical .equals). As a side effect, making "==" return "false" puts more pressure on .equals (since it is often called when an identity check fails). Because of this pressure the .equals method of "inline classes" should implement the exhaustive evaluation that is so scary to use for "==". If you want fast substitutability, use Objects. Consequently, banning "==" from "inline classes" means that you cannot use "==" on generic types that include "inline classes" (just like you can't assign null). Would it be so bad to force users to rely on logical equality in this case? Since .equals is a superset of identity checks, bugs should only come up in questionable code. However, if identity checks turn out to be significant for performance for a large enough amount of cases we could introduce a new operator for logical equality (similar to Stephen's suggestion). Now, if the type parameter is a RefObject this operator could compile to an additional identity check before .equals is called. Similarly, this could give you an easy way to perform a null-check on an ?any T? type. It can correctly check nullable types for zeroes and returns false otherwise. This operator could help in the migration work of libraries to support "value types" and clean up some syntax. In general, an equality operator could guide the language (and its users) towards a more value-like path that is less dependent on identity (but still benefits from it when applicable). I can only imagine that an equality operator like .= or ?= would be adopted rather quick for migration purposes and in general. Going full cycle, the notion of a "value type" just might be too broad. Conceptually, "composed primitives" (true value types) are different to "inline classes" (technical value types). Maybe they should be treated as such. Here are some continuative thoughts which are out of scope: - 1: If "==" is allowed on "composed primitives" it could be interpreted as a form of operator overloading. Buildings upon this, a ?composed primitive? could implement a "numerical interface" which allows to use more operators (%, *, /, + or ?). Technically, value types and their generic specialization are an exciting premise for new math APIs. And given the constraints of "composed primitives" I think the room for misusing/abusing operator overloading is limited. - 2: I think the core problem of "value types with substitutability check" aligns itself with "record classes". Where "composed primitives" are data carriers of primitives, records are data carriers of all types. Perhaps "composed primitives" are really just "primitive records"? That's it. I hope there were some interesting bits. Patrick --- Diese E-Mail wurde von Avast Antivirus-Software auf Viren gepr?ft. https://www.avast.com/antivirus From brian.goetz at oracle.com Mon Sep 30 20:20:36 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 30 Sep 2019 16:20:36 -0400 Subject: Fwd: Number problems In-Reply-To: References: Message-ID: <8cae501f-35e5-8507-ca1e-b67352935965@oracle.com> Received on the -comments list. The points raised here are quite sensible, and we've been grappling with them in a peeking-down-the-road sort of way, but we're not quite ready to engage them directly yet.? Yes, the fact that Number is a class, rather than an interface causes certain kinds of problems, and we're going to have to contend with them somehow.? There are a variety of techniques on the table, but exactly how we do so should be addressed in the bigger context of what we want the type hierarchy to look like.? So, stay tuned. -------- Forwarded Message -------- Subject: Number problems Date: Sun, 29 Sep 2019 11:26:13 +0200 From: Anders Peterson To: valhalla-spec-comments at openjdk.java.net Hi, A ComplexNumber class that will be efficient to use is a common example of what Valhalla will enable. In order to be able to write math code that is common for any/all ?numbers? then the various implementations must have something in common - like a common interface. Inline classes cannot extends any other class - they cannot extend java.lang.Number. Can existing final classes that only extend Object be converted to inline classes? Even if that will be possible the classes Double, Integer, Long? cannot be converted to inline since they extend Number. I?m the author of a Java math (linear algebra) library, and very interested in the capabilities I believe Valhalla will bring. I plan to have a Valhalla-enabled version of my library as soon as it?s released, and I?m eager to make adaptations already now that will simply future changes. Are there any plans to create a new interface that (re)defines java.lang.Number? Then perhaps a second interface, that extends that first one, and further declares methods like add/subtract/multiply/divide/? Most (all) classes that today extend java.lang.Number should be able to implement this interface AND it can be used to create new numerical inline classes like ComplexNumber. In my library today there is such an interface but there are difficulties regarding how to interact and coexist with java.lang.Number and the various primitive types. Some of these challenges would go away if (something like) the interfaces I invented was defined in ?Java? itself. /Anders From john.r.rose at oracle.com Mon Sep 30 23:49:46 2019 From: john.r.rose at oracle.com (John Rose) Date: Mon, 30 Sep 2019 16:49:46 -0700 Subject: Migrating the primitive boxes to values In-Reply-To: <44f6ac27-f9ca-0329-5121-d5b403131587@oracle.com> References: <44f6ac27-f9ca-0329-5121-d5b403131587@oracle.com> Message-ID: <9EC2754E-B672-4675-A5DE-068A57529D20@oracle.com> We?ve been round and round on this one in various discussions, and I think our answer is probably documented somewhere, but here?s a quick summary: 1. We will never endow every value at creation with an object identity. (And an object identity is that thing which connects side-effecting writers to readers of their side effects. And monitor locking is a side effect.) 2. Nor will we allow a side-effecting channel that can somehow add an object identity to all occurrences of the ?same value?. That?s just sneaking in object identities again, to define ?same? in a way that allows side effects between occurrences of the ?same value?. Try to imagine assigning an object identity to all occurrences of some number ?42?, that somehow ?were copied from the same source?. It doesn?t work; 42 is 42, globally. Same thing with values. 3. Therefore, if we were to go through the motions of locking an inline value by assigning it an object identity, we would need to make a context-free choice of which identity to use. 4. We could build a fresh box on every monitor-enter event. The effect of that would be to have all such locks be uncontended. That?s a no-op. Making locking on values into a no-op is asking for bugs, so we won?t do that. (Example code from today: ?synchronized ((Object) 999999999) { }?.) 5. We could try to track ?the same value? through local code paths and somehow assign the same box. This is asking for even worse bugs, since it?s anybody?s guess which occurrences of ?the same? value will be assigned the same box. 6. We could rummage around the universe to see if anybody else has previously built a box for the *same value*, and use that box. (Example code from today: ?synchronized ((Object) 9) { }?.) That requires a large dictionary of all inline values ever (mis-)treated in this way, which is expensive. (Weak links make it slightly less costly.) Supplying such a table encourages its use; it?s an expense we wish to take on only if absolutely necessary, not just because someone says ?why don?t you??. 7. The safest thing is to break a minor compatibility to save a major one. We say, ?You can?t lock on an int, nor can you lock on an inline value.? The locking operations on Object become partial, throwing exceptions for some inputs. Having Object as a top-type is tremendously valuable, even at the expense of making some operations (those that require an object *identity*) into partial throwing operations. It?s the best trade-off that we know of (and we know a lot of them, intimately by now). HTH ? John On Sep 30, 2019, at 1:12 PM, Brian Goetz wrote: > > Received on the -comments list (some time ago). > > > > > -------- Forwarded Message -------- > Subject: Migrating the primitive boxes to values > Date: Sat, 26 Jan 2019 00:07:53 -0800 > From: Jack Ammo > To: valhalla-spec-comments at openjdk.java.net > > From an outsider's perspective, i'd like to ask a perhaps naive question > about the locking issue on values. Instead of just opting to break existing > code as you seem to be worried about, why not just box the value before > locking? Assuming that a boxing conversion to a full fledged Object is > still kicking around as a fall back, this seems like the most familiar > option to me as a developer: i can technically do the lock on an Integer > but it is not advised and results are not guaranteed. I understand that > everyone has worked hard to avoid the cost of boxing, but in this > particular case it seems like a decent option that is the least difficult > to wrap my mind around. Locking requires a full Object, so values (like an > int) need to be boxed as such.