From forax at univ-mlv.fr Thu Aug 2 14:39:21 2018 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 2 Aug 2018 16:39:21 +0200 (CEST) Subject: value based class vs nullable value type Message-ID: <2113881715.95619.1533220761304.JavaMail.zimbra@u-pem.fr> Hi all, just to write my current state of thinking somewhere, I see 3 ways to implement value based class which all have their pros and cons - the value based class bit, a value based class is tagged with ACC_VALUE_BASED, which means that they have no identity. It's not a value class so instances of a value based class are not flattened for fields and arrays but the JIT can flattened them and re-materialize them. - the interface trick, a nullable value type is like an interface that contains a value class. The idea is to change the semantics of invokevirtual to be able to call methods of an interface. This way we can swap the implementation of the value based classes to use interfaces instead. Like the previous proposal it means that because it's an interface there is no flattening for fields and array but the JIT is able to remove allocation on stack because the interface implementation is a value type. - nullable value type, as decribed before on this list, the developer that create a value based class indicate a field which if is zero encodes null. A nullable value type is flattened for fields and arrays. The main drawback of this approach is that non nullable value type pay the cost of the possibility to having nullable value type on operation like acmp. This model is also far more complex to implement because it introduce another kind of world, the N-world. To summarize, the first two proposals allow a value based class to be null with the cost of not having their values being flattened if not on stack the last proposal allow value based class to be fully flattened with the cost of making the VM more complex and slowing Object operations because it introduce a new world. Given that we want to support value based class only for retrofitting purpose, my money is on 2 actually :) R?mi From Daniel_Heidinga at ca.ibm.com Thu Aug 2 15:12:45 2018 From: Daniel_Heidinga at ca.ibm.com (Daniel Heidinga) Date: Thu, 2 Aug 2018 15:12:45 +0000 Subject: value based class vs nullable value type In-Reply-To: <2113881715.95619.1533220761304.JavaMail.zimbra@u-pem.fr> References: <2113881715.95619.1533220761304.JavaMail.zimbra@u-pem.fr> Message-ID: >Hi all, >just to write my current state of thinking somewhere, > >I see 3 ways to implement value based class which all have their pros >and cons Thanks for writing this up, Remi. I've responded with some initial questions (and concerns). Hopefully we'll get a chance to discuss further in person while many of us are here at jvmls / ocw. > >- the value based class bit, > a value based class is tagged with ACC_VALUE_BASED, which means >that they have no identity. It's not a value class so instances of a >value based class are not flattened for fields and arrays but the JIT >can flattened them and re-materialize them. This sounds similar to the heisenbox proposals where identity was accidental and could come and go, correct? Or are all identity-ful operations (acmp, synchronization, etc) on these tagged classes rejected? If this is the same as the heisenbox proposal, then the previous concerns about the about the user model when identity can come and go still apply. > >- the interface trick, > a nullable value type is like an interface that contains a value >class. The idea is to change the semantics of invokevirtual to be >able to call methods of an interface. Would this apply to all interfaces? Would invokevirtual now need to do both virtual and interface style invokes for all types of classes? >From a VM perspective, I'm not a fan of merging the semantics of invokevirtual and invokeinterface. These kinds of merges usually come with a performance cost which is especially evident at startup. > This way we can swap the >implementation of the value based classes to use interfaces instead. >Like the previous proposal it means that because it's an interface >there is no flattening for fields and array but the JIT is able to >remove allocation on stack because the interface implementation is a >value type. Others could implement the same interface as well so there would either need to be two paths or a fallback option when a non-valuetype receiver was passed. I'm hesitant about this approach given the extra costs it puts on invokevirtual. > >- nullable value type, > as decribed before on this list, the developer that create a value >based class indicate a field which if is zero encodes null. A >nullable value type is flattened for fields and arrays. The main >drawback of this approach is that non nullable value type pay the >cost of the possibility to having nullable value type on operation >like acmp. This model is also far more complex to implement because >it introduce another kind of world, the N-world. Modifying the descriptors comes at a high cost in terms of determining overriding, bridges, vtable packing, etc as discovered by the Q world prototypes. > >To summarize, the first two proposals allow a value based class to be >null with the cost of not having their values being flattened if not >on stack the last proposal allow value based class to be fully >flattened with the cost of making the VM more complex and slowing >Object operations because it introduce a new world. > >Given that we want to support value based class only for retrofitting >purpose, my money is on 2 actually :) > >R?mi > From forax at univ-mlv.fr Fri Aug 3 16:14:47 2018 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Fri, 3 Aug 2018 18:14:47 +0200 (CEST) Subject: value based class vs nullable value type In-Reply-To: References: <2113881715.95619.1533220761304.JavaMail.zimbra@u-pem.fr> Message-ID: <1382896252.145993.1533312887460.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Daniel Heidinga" > ?: "Remi Forax" > Cc: "valhalla-spec-experts" > Envoy?: Jeudi 2 Ao?t 2018 17:12:45 > Objet: Re: value based class vs nullable value type >>Hi all, >>just to write my current state of thinking somewhere, >> >>I see 3 ways to implement value based class which all have their pros >>and cons > > Thanks for writing this up, Remi. I've responded with some initial > questions (and concerns). Hopefully we'll get a chance to discuss > further in person while many of us are here at jvmls / ocw. > >> >>- the value based class bit, >> a value based class is tagged with ACC_VALUE_BASED, which means >>that they have no identity. It's not a value class so instances of a >>value based class are not flattened for fields and arrays but the JIT >>can flattened them and re-materialize them. > > This sounds similar to the heisenbox proposals where identity was accidental > and could come and go, correct? Or are all identity-ful operations > (acmp, synchronization, etc) on these tagged classes rejected? yes, correct. > > If this is the same as the heisenbox proposal, then the previous concerns > about the about the user model when identity can come and go still apply. The value based class semantics is currently defined by the javadoc [1] which contains this sentence "are freely substitutable when equal, meaning that interchanging any two instances x and y that are equal according to equals() in any computation or method invocation should produce no visible change in behavior". The heisenbox semantics is a technically a subset of the described semantics because a value based class doesn't allow to use any identity based operation, ==, synchronized() etc while the heisenbox semantics allow such operation but provides dubious result. In my opinion, javac should enforce the stricter value based class semantics while the VM should only implement the heisenbox semantics. I agree that the semantics may be weird if seen in isolation but we are introducing the value type semantics at the same time. As Mr Simms said, actually we are removing properties if you compare to a reference type, so the value type semantics or the value based class emantics, so users will need to cope with that. Also note that a VM is free to not implement the heisenbox model and consider all value based class as reference because the plain old class semantics is a valid mapping to the value based class semantics as we have actually. >> >>- the interface trick, >> a nullable value type is like an interface that contains a value >>class. The idea is to change the semantics of invokevirtual to be >>able to call methods of an interface. > > Would this apply to all interfaces? yes, i think we need a marker (a flag) in the interface because javac need to know if it's a value based class or not but the VM doesn't need to enforce that. > Would invokevirtual now need to do both virtual and interface style invokes for all types of classes? you mean all type of interfaces ? yes ! > > From a VM perspective, I'm not a fan of merging the semantics of > invokevirtual and invokeinterface. These kinds of merges usually > come with a performance cost which is especially evident at startup. yes and no. the separation between invokevirtual and invokeinterface (and their corresponding constant pool constant) is a kind of premature optimization, we have already invokeinterface that can act as invokevirtual, so in my opinion, it's more like aligning the two semantics than overloading the meaning of invokevirtual for no good reason. BTW, we have already agree on that :) by only providing Lookup.findVirtual to map either invokevirtual or invokeinterface, a method handle semantics is the semantics of the equivalent bytecode, here, we should align the semantics of invokevirtual to the semantics of findVirtual. And yes, it means that there is a small cost at startup time. > >> This way we can swap the >>implementation of the value based classes to use interfaces instead. >>Like the previous proposal it means that because it's an interface >>there is no flattening for fields and array but the JIT is able to >>remove allocation on stack because the interface implementation is a >>value type. > > Others could implement the same interface as well so there would either > need to be two paths or a fallback option when a non-valuetype receiver > was passed. You may need a CHA to know if the interface has more that one implementation or not, note that here having the interface marked with a bit saying ACC_VALUE_BASED (here the name is wrong!) helps to make the difference between a classical interface and a retrofitted one. On the plus side, it muddies the question of using an interface or a class when designing a library because you can move from one to another and still be binary compatible, this is by itself a huge win. > > I'm hesitant about this approach given the extra costs it puts on > invokevirtual. even for completeness ? > >> >>- nullable value type, >> as decribed before on this list, the developer that create a value >>based class indicate a field which if is zero encodes null. A >>nullable value type is flattened for fields and arrays. The main >>drawback of this approach is that non nullable value type pay the >>cost of the possibility to having nullable value type on operation >>like acmp. This model is also far more complex to implement because >>it introduce another kind of world, the N-world. > > Modifying the descriptors comes at a high cost in terms of determining > overriding, bridges, vtable packing, etc as discovered by the Q world > prototypes. yes, i was thinking of something like adding a boolean in the ValueTypes attribute, anyway, it comes it still have a great cost. > >> >>To summarize, the first two proposals allow a value based class to be >>null with the cost of not having their values being flattened if not >>on stack the last proposal allow value based class to be fully >>flattened with the cost of making the VM more complex and slowing >>Object operations because it introduce a new world. >> >>Given that we want to support value based class only for retrofitting >>purpose, my money is on 2 actually :) >> >>R?mi R?mi [1] https://docs.oracle.com/javase/10/docs/api/java/lang/doc-files/ValueBased.html From karen.kinnear at oracle.com Mon Aug 6 16:59:23 2018 From: karen.kinnear at oracle.com (Karen Kinnear) Date: Mon, 6 Aug 2018 12:59:23 -0400 Subject: Valhalla EG meeting August 15, 2018 Message-ID: Would anyone be willing to host the meeting August 15 and take minutes? I will be out of the country. thanks, Karen From karen.kinnear at oracle.com Sat Aug 11 01:31:13 2018 From: karen.kinnear at oracle.com (Karen Kinnear) Date: Fri, 10 Aug 2018 21:31:13 -0400 Subject: Valhalla EG meeting August 15, 2018 In-Reply-To: References: Message-ID: <6B143EC0-7401-44F4-B367-97042F863980@oracle.com> Hearing no offers, cancelling August 15th Valhalla EG meeting Thanks, Karen > On Aug 6, 2018, at 12:59 PM, Karen Kinnear wrote: > > Would anyone be willing to host the meeting August 15 and take minutes? > I will be out of the country. > > thanks, > Karen From forax at univ-mlv.fr Tue Aug 21 14:19:31 2018 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 21 Aug 2018 16:19:31 +0200 (CEST) Subject: Tuples Was: Small things Java lets you do, but not everywhere. In-Reply-To: <95e75ddd-6bcb-ca61-4ba2-76186cc06d8b@hispeed.ch> References: <2127848435.256643.1534859378088.JavaMail.zimbra@u-pem.fr> <95e75ddd-6bcb-ca61-4ba2-76186cc06d8b@hispeed.ch> Message-ID: <939663832.267808.1534861171757.JavaMail.zimbra@u-pem.fr> ok, got it. The current plan is to introduce tuples once we have value type, because unlike arrays, a value type can be stack allocated or even register allocated. This remember me an interresting question, should a tuple always be be named in Java ? Tuples are like function types, there are structural types, not nominal types. In Java, to keep function type nominal, we invent functional interface which is a way to define a function type but with a name, should we do the same thing with tuples, by example provide a new keyword tuple (that works like a value record). tuple MinMax(int min, int max); MinMax minmax(int value1, int value2) { if (value1 < value2) { return (value1, value2); } return (value2, value1); } in that case we can pattern match on tuples if the target type is available MinMax result = ... switch(result) { case (res1, res2) -> res1 + res2; case (0, 0) -> throw ...; } regards, R?mi ----- Mail original ----- > De: "Cyrill Brunner" > ?: "Amber Expert Group Observers" , "Remi Forax" , "jeremy a > barrow" > Envoy?: Mardi 21 Ao?t 2018 16:03:14 > Objet: Re: Small things Java lets you do, but not everywhere. >> - array literals >> we already have them no ? >> new int[] { 2, 3, 5}; >> if you mean supported in the bytecode, yes, we are working on that as a follow >> up of ConstantDynamic. >> >> > Seeing as they explicitly mentioned "I can imagine `return` statements > working really well with it, ...", I think they refer to the shorthand > instead. > i.e. That you can do this: > > int[] a = {2, 3}; > > But can't do any of these: > > int[] a; a = {2, 3}; > > int[] f() { return {2, 3}; } > > void g(int[] a) {} g({2, 3}); > > For all of these cases and possibly more, the shorthand literals are not > allowed and explicit type information has to be added. To their point > about expression switch, I think it's to allow this: > > break {2, 3}; > > But with moving closer to patterns at a later stage, I think this would > also be helpful > > switch(...) { > ? case {2, 3} -> ...; > ? default -> ...; > } > > Regards, > Cyrill Brunner From brian.goetz at oracle.com Sun Aug 26 21:35:11 2018 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 26 Aug 2018 17:35:11 -0400 Subject: Tuples Was: Small things Java lets you do, but not everywhere. In-Reply-To: <939663832.267808.1534861171757.JavaMail.zimbra@u-pem.fr> References: <2127848435.256643.1534859378088.JavaMail.zimbra@u-pem.fr> <95e75ddd-6bcb-ca61-4ba2-76186cc06d8b@hispeed.ch> <939663832.267808.1534861171757.JavaMail.zimbra@u-pem.fr> Message-ID: <1fb4f563-69ed-393f-6917-3ba94450df5a@oracle.com> > The current plan is to introduce tuples once we have value type, because unlike arrays, a value type can be stack allocated or even register allocated. No, there's no current plan to introduce (structural) tuples. However, records (data classes), can be thought of as "nominal tuples" -- perhaps that's what you meant?? We don't need value types to do these, though when we have value types, records can be values as well. > tuple MinMax(int min, int max); > > MinMax minmax(int value1, int value2) { > if (value1 < value2) { > return (value1, value2); > } > return (value2, value1); > } > > in that case we can pattern match on tuples if the target type is available > > MinMax result = ... > switch(result) { > case (res1, res2) -> res1 + res2; > case (0, 0) -> throw ...; > } You can do this without tuples, with feature plans that are already on the books: ??? record MinMax(int min, int max); ??? let MinMax(var min, var max) = minmax(..);? // unconditional pattern match ??? // min and max are now in scope From karen.kinnear at oracle.com Tue Aug 28 21:14:56 2018 From: karen.kinnear at oracle.com (Karen Kinnear) Date: Tue, 28 Aug 2018 17:14:56 -0400 Subject: EG help please with getting to LW1 re: Value Types Consistency Checking In-Reply-To: References: <58F4AAFF-77D4-4CB4-A296-6620BDE75CA7@oracle.com> Message-ID: <62DC39B7-56B7-4936-B30E-7FC4C0241915@oracle.com> Tobi, Thank you for the thoughtful proposal. Couple of questions/comments on the proposal > On Jul 6, 2018, at 11:38 AM, Tobi Ajila wrote: > > I would like to propose an alternative strategy, one that would effectively provide the same consistency checks outlined in http://cr.openjdk.java.net/~acorn/value-types-consistency-checking-details.pdf without requiring eager loading of value types (VT) in the return/args of methods during the preparation phase. This is born out of our experience with PackedObjects which took a very similar approach to the ValueTypes attribute. > > This proposal makes use of the existing ValueTypes attribute. In addition, it requires that each classloader keeps a registry of ?expected? VTs. > > Proposal > -------------------------- > The following steps occur in the loading phase: > > 1. Prior to loading the current class each type in the ValueTypes attribute is checked see if it is loaded. If it is, the type must be a VT or ICCE is thrown. Otherwise, the type is registered with the initiating classloaders expected VT registry (EVTR). > I have not walked through the implications of using an initiating loader to record EVTR. What confuses me here is that when defining a class, we look at the defining loader, so the class could successfully be defined, but there would be an ICCE when returning the type to the initiating loader so the loading/resolution would fail? > > 2. Pre-loading of ACC_FLATTENABLE instance fields follow the same rules as the ones specified in Karen's proposal. > > 3. Prior to loading the current class, if the current class is a VT it must be in the classloader's EVTR or ICCE is thrown. If the current class is not a VT and does appear in the EVTR, ICCE is thrown. > What happens if there is a call to define a class which is a VT but it has not yet been referenced by another class and is therefore not in any class loader?s EVTR? (we get a defineClass call with a byte[]). It won?t occur in an EVTR but we don?t want an ICCE at this point, right? Concern: Who pays the cost of a mistake? If we check during preparation of a local method, then the ICCE is thrown to the class declaring the method with the missing VT attribute. If we check lazily, then the ICCE is given to the class that we expected to be a VT or not which doesn?t match some class? expectations. I grant that if the expected class has already been loaded, the ICCE will go to the referring class. Is it the case then, that by adding the wrong class e.g. Foo to my ValueTypes attribute, or by accidentally leaving out Point from my ValueTypes attribute - then neither Foo nor Point will be able to load without getting an ICCE, and no one else can use them? > > In link phase prior to preparation: > - Same as Karen's static field rules, "static fields declared with the ACC_FLATTENABLE flag set have their type pre-loaded..." > > In preparation phase: > - Same as Karen's method overriding rules, "any method overriding needs to perform value type consistency checking between declarer of the overridden method ..." > > At resolution time: > - Same as Karen?s CONSTANT_Class, field and method resolution rules > --------------------------- > > The benefit of this approach is that as soon as anyone believes a class is a VT, it must be loaded as a VT or it fails. As a result, there is no inconsistency of loaded VTs. This proposal doesn't guard against cases where a class was not expecting a method return/arg type to be a VT but then later on turned out to be a VT when it was resolved. However, I think don?t think Karen?s proposal offered these guarantees either. > // Not sure if my assumptions work - my proposal is that for the case you describe: a class was not expecting a method return/arg to be a VT but then later on turned out to be a VT when it was resolved: *5 in my proposal - method invocation can work with NULL - but the method invocation can not work with an actual value type - that gets caught when the method tries to get its hands on the actual value type - I walked through my logic - that doesn?t count as a guarantee - love your double-checking > > > Some aspects of the implementation complexity that we have identified so far: > > a) At method resolution time, if there is a mismatch in value type consistency > > between the declaring class and the actual type in the signature, then there > > is also a mismatch in all classes that have overridden the current method. > > This is particularly painful with default methods in interfaces. > > With this proposal the only possible inconsistency here is: > Method has a return/arg type that is believed to not be a VT but turns out to be a VT. In this case any compiled code is doing pass by reference calling convention which works for both VT and non-VT types. > // And the generated method does not know it is dealing with a VT, so no other VT optimizations either. > > > b) We need to ensure that we catch all method resolution, through all of the > > alternate accessor paths, including MethodHandles, VarHandles, Reflection, JNI, > > so that we catch both the specification and implementation changes. > > All these cases are covered with the class loading consistency checks (EVTR). > Still sanity checking that at the implementation level - independent of which of the proposal variations we use. > > > c) Our favorite, invokespecial ACC_SUPER, needs special handling, since it > > performs selection which is not based on overriding, but based on virtually > > re-resolving. > > same as above > > > e) If we modify the specification to allow eager loading, and save errors > > to throw at method resolution, we need to work through the JVMS question > > of which errors would be saved (e.g. OOME, SOE might be thrown as part of > > the implementation vs. saving LinkageError), as well as designing a new > > implementation mechanism to repeat exceptions relative to signatures used > > for method resolution. > > This wouldn?t be required in this proposal > Thanks! > > > d) Pass by value calling convention optimizations depend on loading the > > actual type. Loading of the parameter types on first method resolution > > implies that if the caller is compiled, the caller method requires > > deoptimization/recompilation to pass arguments by value for future calls, > > which is a performance cost. > > Typically, a method is run a few times before it is compiled (perhaps I?m making implementation assumptions?). At this stage, the return/arg types are either loaded or they are always null. This seems to suggest that deoptimization/recompilation scenario would not be a common occurrence. > I believe that is an implementation assumption, which I initially shared. At least with -Xcomp, this is not the case. thanks, Karen > > > --Tobi > > > From: Karen Kinnear > > To: valhalla-spec-experts > > Date: 2018/06/26 10:32 AM > > Subject: EG help please with getting to LW1 re: Value Types > > Consistency Checking > > Sent by: "valhalla-spec-experts" > bounces at openjdk.java.net> > > > > Summary: Could we please allow eager loading for value types in the > > locally declared method signatures > > prior to preparation for LW1? > > > > Without that we seriously risk being able to offer an LW1 early > > access binary for the JVMLS. > > We believe it is more important to get this into people?s hands for > > experimentation and feedback than > > to delay the eager loading at this time. > > > > Details: > > At our EG discussion on June 20, 2018, we discussed the proposal for > > Value Types Consistency checking > > at http://cr.openjdk.java.net/~acorn/value-types-consistency- > > checking-details.pdf > > > > Part of the proposal for checking value type consistency relative to > > the actual type > > was for locally declared methods. The proposal was to check the > > value types in arguments/return type > > before preparation of the declaring class. > > > > During the meeting, there was a request to explore whether we could either: > > 1) delay checking the value type consistency until an attempt to > > resolve the method for invocation, or > > 2) write the JVMS is such as way as to allow eager loading, but only > > throw an error related to the eager loading at method resolution. > > > > My understanding is that the goals of this are two-fold: > > 1) if the method is never called, the rest of the code will continue to run > > 2) reduce eager loading > > > > We have started this investigation, and there is non-trivial > > complexity in implementing either of these approaches, > > and we would like more time to explore how to make this possible, > > after making LW1 available. > > > > Some aspects of the implementation complexity that we have identified so far: > > a) At method resolution time, if there is a mismatch in value type > > consistency between the declaring class and the actual > > type in the signature, then there is also a mismatch in all classes > > that have overridden the current method. This is particularly > > painful with default methods in interfaces. > > b) We need to ensure that we catch all method resolution, through > > all of the alternate accessor paths, including MethodHandles, VarHandles, > > Reflection, JNI, so that we catch both the specification and > > implementation changes. > > c) Our favorite, invokespecial ACC_SUPER, needs special handling, > > since it performs selection which is not based on overriding, but > > based on virtually re-resolving. > > d) Pass by value calling convention optimizations depend on loading > > the actual type. Loading of the parameter types on first method > > resolution implies that if the caller is compiled, the caller method > > requires deoptimization/recompilation to pass arguments by value for > > future calls, which is a performance cost. > > e) If we modify the specification to allow eager loading, and save > > errors to throw at method resolution, we need to work through the > > JVMS question of which errors would be saved (e.g. OOME, SOE might > > be thrown as part of the implementation vs. saving LinkageError), as > > well as designing a new implementation mechanism to repeat > > exceptions relative to signatures used for method resolution. > > > > thanks, > > Karen From forax at univ-mlv.fr Thu Aug 30 00:02:36 2018 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 30 Aug 2018 02:02:36 +0200 (CEST) Subject: Nullable ValueType, Generics and other vexations Message-ID: <1574715495.732770.1535587356125.JavaMail.zimbra@u-pem.fr> Hi all, just to formalize a little more my thinking about the interaction between (nullable) value types and erased generics. With LW1, the VM enforces that a value type can not be null, so each time there is a conversion between an Object to a value type, a nullcheck is inserted. This works great until you start to use already existing code that give a meaning to null, like j.u.Map.get is specified to return a V or null, so a code like this (with Complex a value type) Map map = ... Complex complex = map.get("a-key-that-does-not-exist"); throw a NPE before a user can even check if complex is null or not. >From Java the language point of view, a solution is to have a way to express that a value type can be nullable, by mandating a users to write Complex? complex = map.get("a-key-that-does-not-exist"); and teach javac how to do a null analysis (guaranteeing that you can not call a method on a Type? without a test of null first). The question is how to translate to bytecode something like Complex?. We have two choices, one is to teach the VM what Complex? is by adding a bit along with field/method descriptor the other is to erase Complex? like we erase T (to Object or an interface). I believe is that we should choose the latter solution - reifying the null information for the VM means solving the null problem of Java not only for value types but for every types, because if we come with a partial solution now, it will hamper our design choices if we want to extend it latter. And solving the nullablity problem is not one of the goal of valhalla, valhalla is hard enough, making it twice hard make no sense. - having nullable value types reified in the VM is not enough to allow the migration between a reference type to a value type, programs will still choke on identity, synchronized, etc. But it helps for value based classes, yes, but it's the tail wagging the dog. There are few value based classes defined in the JDK and we know from the whole module 'experience' that a contract defined in the javadoc and not enforced by javac or the VM means nothing. - erasure works because either a value type comes from a type parameter so it is already erased or it comes from a new code so it can be erased because if there is a signature clash due to erasure, it's in a new code so the user can figure out a way to workaround that. Note2: because of separate compilations the '?' information need to be available in a new attribute (or in an old one by extending it) but only the compiler and the reflection will consume it, not the VM. Note2: we can also restrict further the use of '?' by disallowing to use it in method parameter unless it's either on a type variable or in a method overriding another one that uses T?, but that a language issue. R?mi From brian.goetz at oracle.com Thu Aug 30 21:51:43 2018 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 30 Aug 2018 17:51:43 -0400 Subject: Nullable ValueType, Generics and other vexations In-Reply-To: <1574715495.732770.1535587356125.JavaMail.zimbra@u-pem.fr> References: <1574715495.732770.1535587356125.JavaMail.zimbra@u-pem.fr> Message-ID: <7be3bec3-402f-4fff-a28e-0b167266dd1c@oracle.com> The emotional types (T? and T!) are tempting here.? But they are also intrusive.? Once you let them in the house, they want to go everywhere.? And not just on the main floor (language), they want to roam the dungeons too (JVM).? I am a little wary of inviting them into the house just as a way of talking about nullability. An alternate approach, which I don't yet have a proposals for but am thinking about, is to denote these slightly differently, without putting nulls quote so prominently.? Which is, doubling down on an existing precedent: boxes.? Primitives have boxes, but they're crappy boxes -- they have accidental identity, they are spelled weirdly, they are ad-hoc.? Call them "prototype boxes" -- the hand made ones that you did before you could automate.? The machine-crafted, automated boxes could be sleeker and more uniform.? Then in LW10 you just generify over the box and you're good, since its nullable.? (For LW100, more help is needed.) There's a massive bikeshed (please let's not try to paint now) for how we would denote value-V separately from box-V.? But this might be a smaller can of worms than T? types. On 8/29/2018 8:02 PM, Remi Forax wrote: > Hi all, > just to formalize a little more my thinking about the interaction between (nullable) value types and erased generics. > > With LW1, the VM enforces that a value type can not be null, so each time there is a conversion between an Object to a value type, a nullcheck is inserted. > This works great until you start to use already existing code that give a meaning to null, like j.u.Map.get is specified to return a V or null, > so a code like this (with Complex a value type) > > Map map = ... > Complex complex = map.get("a-key-that-does-not-exist"); > > throw a NPE before a user can even check if complex is null or not. > > From Java the language point of view, a solution is to have a way to express that a value type can be nullable, by mandating a users to write > Complex? complex = map.get("a-key-that-does-not-exist"); > and teach javac how to do a null analysis (guaranteeing that you can not call a method on a Type? without a test of null first). > > The question is how to translate to bytecode something like Complex?. > We have two choices, one is to teach the VM what Complex? is by adding a bit along with field/method descriptor the other is to erase Complex? like we erase T (to Object or an interface). > > I believe is that we should choose the latter solution > - reifying the null information for the VM means solving the null problem of Java not only for value types but for every types, because if we come with a partial solution now, it will hamper our design choices if we want to extend it latter. And solving the nullablity problem is not one of the goal of valhalla, valhalla is hard enough, making it twice hard make no sense. > - having nullable value types reified in the VM is not enough to allow the migration between a reference type to a value type, programs will still choke on identity, synchronized, etc. But it helps for value based classes, yes, but it's the tail wagging the dog. There are few value based classes defined in the JDK and we know from the whole module 'experience' that a contract defined in the javadoc and not enforced by javac or the VM means nothing. > - erasure works because either a value type comes from a type parameter so it is already erased or it comes from a new code so it can be erased because if there is a signature clash due to erasure, it's in a new code so the user can figure out a way to workaround that. > > Note2: because of separate compilations the '?' information need to be available in a new attribute (or in an old one by extending it) but only the compiler and the reflection will consume it, not the VM. > > Note2: we can also restrict further the use of '?' by disallowing to use it in method parameter unless it's either on a type variable or in a method overriding another one that uses T?, but that a language issue. > > R?mi From forax at univ-mlv.fr Thu Aug 30 23:36:39 2018 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Fri, 31 Aug 2018 01:36:39 +0200 (CEST) Subject: Nullable ValueType, Generics and other vexations In-Reply-To: <7be3bec3-402f-4fff-a28e-0b167266dd1c@oracle.com> References: <1574715495.732770.1535587356125.JavaMail.zimbra@u-pem.fr> <7be3bec3-402f-4fff-a28e-0b167266dd1c@oracle.com> Message-ID: <74760462.1017901.1535672199822.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "Remi Forax" , "valhalla-spec-experts" > Envoy?: Jeudi 30 Ao?t 2018 23:51:43 > Objet: Re: Nullable ValueType, Generics and other vexations > The emotional types (T? and T!) are tempting here.? But they are also > intrusive.? Once you let them in the house, they want to go everywhere. > And not just on the main floor (language), they want to roam the > dungeons too (JVM).? I am a little wary of inviting them into the house > just as a way of talking about nullability. agree ! > > An alternate approach, which I don't yet have a proposals for but am > thinking about, is to denote these slightly differently, without putting > nulls quote so prominently.? Which is, doubling down on an existing > precedent: boxes.? Primitives have boxes, but they're crappy boxes -- > they have accidental identity, they are spelled weirdly, they are > ad-hoc.? Call them "prototype boxes" -- the hand made ones that you did > before you could automate.? The machine-crafted, automated boxes could > be sleeker and more uniform.? Then in LW10 you just generify over the > box and you're good, since its nullable.? (For LW100, more help is needed.) This kind of boxes are really close to a reified java/lang/Box container. class Box { private T value; T unwrap() { return value; } static Box wrap(T value) { return new Box(value); } } so in LW100, you will have trouble to explain the differences between V+Box and Box if V+Box is a magic construction from the VM. [...] Another idea, if we restrict ourselves to just a discussion about the surface language, you may not have to introduce T? or T+Box, because as i said at the end of my previous message you can force users to treat null locally and doesn't allow it to escape. So Complex? will be a type that exists internally for the compiler but that can not be written by a user. so Map map = ... Complex complex = map.get("a-key-that-does-not-exist"); will be rejected by the compiler because Complex is a value type so it doesn't accept null but var complex = map.get("a-key-that-does-not-exist"); is Ok and if the compiler has special code to handle Optional correctly, the code below will be valid var complexOrNull = map.get("a-key-that-does-not-exist"); Complex complex = Optional.ofNullable(complexOrNull).orElseThrow(); And the compiler will erase Complex? to Object (or an interface if Complex implements that interface). R?mi > > On 8/29/2018 8:02 PM, Remi Forax wrote: >> Hi all, >> just to formalize a little more my thinking about the interaction between >> (nullable) value types and erased generics. >> >> With LW1, the VM enforces that a value type can not be null, so each time there >> is a conversion between an Object to a value type, a nullcheck is inserted. >> This works great until you start to use already existing code that give a >> meaning to null, like j.u.Map.get is specified to return a V or null, >> so a code like this (with Complex a value type) >> >> Map map = ... >> Complex complex = map.get("a-key-that-does-not-exist"); >> >> throw a NPE before a user can even check if complex is null or not. >> >> From Java the language point of view, a solution is to have a way to express >> that a value type can be nullable, by mandating a users to write >> Complex? complex = map.get("a-key-that-does-not-exist"); >> and teach javac how to do a null analysis (guaranteeing that you can not call a >> method on a Type? without a test of null first). >> >> The question is how to translate to bytecode something like Complex?. >> We have two choices, one is to teach the VM what Complex? is by adding a bit >> along with field/method descriptor the other is to erase Complex? like we erase >> T (to Object or an interface). >> >> I believe is that we should choose the latter solution >> - reifying the null information for the VM means solving the null problem of >> Java not only for value types but for every types, because if we come with a >> partial solution now, it will hamper our design choices if we want to extend it >> latter. And solving the nullablity problem is not one of the goal of valhalla, >> valhalla is hard enough, making it twice hard make no sense. >> - having nullable value types reified in the VM is not enough to allow the >> migration between a reference type to a value type, programs will still choke >> on identity, synchronized, etc. But it helps for value based classes, yes, but >> it's the tail wagging the dog. There are few value based classes defined in the >> JDK and we know from the whole module 'experience' that a contract defined in >> the javadoc and not enforced by javac or the VM means nothing. >> - erasure works because either a value type comes from a type parameter so it is >> already erased or it comes from a new code so it can be erased because if there >> is a signature clash due to erasure, it's in a new code so the user can figure >> out a way to workaround that. >> >> Note2: because of separate compilations the '?' information need to be available >> in a new attribute (or in an old one by extending it) but only the compiler and >> the reflection will consume it, not the VM. >> >> Note2: we can also restrict further the use of '?' by disallowing to use it in >> method parameter unless it's either on a type variable or in a method >> overriding another one that uses T?, but that a language issue. >> > > R?mi From karen.kinnear at oracle.com Fri Aug 31 15:24:52 2018 From: karen.kinnear at oracle.com (Karen Kinnear) Date: Fri, 31 Aug 2018 11:24:52 -0400 Subject: Valhalla EG notes Aug 29, 2018 Message-ID: <261DCDD8-1BC3-40B9-BED5-7586FA7F962B@oracle.com> Attendees: Remi, Tobi, John, Dan Smith, Frederic, David Simms, Victor Rudometov, Karen AI: Karen: send out ways in which value types can become null AI: Dan: send out summaries/tradeoffs of nullability strategies corrections/clarifications welcome I. Value Type upcoming plans? Remi would like to see a release without waiting for reified generics. We are all in agreement about phasing: proposing: step 1: EA binaries that support erased generics - need agreement on how to handle nullability with nullable value types - to get feedback/validation on this approach step 2: Preview with language support with erased generics farther future: support for reified generics/generic specialization. II. Nullability - the key open design issue for our next step Assumptions we now all agree on: based in part from user feedback from LW1 - many thanks! 1. To support erased generics we need nullable value types 2. From a JVM perspective we need the default for value types to be the same as for Object, i.e. nullable, i.e. LPoint; in a vm descriptor is nullable if Point is a value type. Tradeoffs from the JVM level: backward compatibility with erased generics: which requires nullable value types JIT optimizations: need null-free value types There are additional language-level tradeoffs such as exposure to syntactic complexity. It is a language design choice what the nullability default is for value types at the source level. Karen clarified that for LW1: javac rejected nulls for any value types, the jvm implementation decoupled value type consistency from the nullability check and has runtime null checks. So we need to move beyond this mismatch. Remi: Is nullability of value types declaration-site or use-site? John: Qualitative difference between local type checking and multiple compilation units. e.g. if nullability can not be constrained locally, it may require a dynamic check. LW1 taught us: 1. fields and array elements: flattenable or not - decision needs to proceed regardless of the static knowledge of callers 2. Analogously, method calls, implementation artifacts such as vtables, need design constraint similar to heap storage, i.e. need to make decisions regardless of static knowledge of callers Generics breaks assumption that Value Types attribute can be used to assume that all value types are non-nullable. Consider concept of Constraints on Descriptors. We don?t want new descriptors with new rules. We want LDescriptors with a rider which only added constraints, no expansions LW1 showed us that runtime checks can cover many value type nullability checks. The ValueTypes attribute does not cover generics or value-based-class migration. Optimizations unlocked for value types (ed. note - that are not null) - flattenable - method calling convention Both are global contracts Need side channel information such as ValueTypes attribute or additional information on descriptor. We need finer-grained nullability information to reduce the requirement on cross-compilation consistency. ?find the primitive? exercise: How do we describe a reference to a value type as nullable or non-nullable To reduce friction with generics and erasure - changed idea - need to have the default for new types be nullable. There are also some wormholes in our value type consistency checking, e.g. MethodHandles and we need to reify a global contract. Propose that we reify a first class descriptor - which is more future proof than QTypes vision: Add constraints as riders to explicit types. This is extensible This provides a predictable requirement and can be also used by specialized generics, for language design and migration design. Example for specialized generics: List != List What if we continued to use List as an interface, and used a descriptor to add constraints See Type Operators JEP (ed. note: JDK-8204937 ) We might want to phase this in Frederic is also exploring a proposal to add suffices to type descriptors, e.g. for nullability Karen: Note that a key benefit of using descriptors is the granularity they offer, not at the class file level, that allows for JIT optimization and backward compatibility in the same file Remi: would prefer a side table Karen: need descriptor for field and method declaration and access note: array creation will need enhancing Frederic: anewarray today only has Class_info for the element, so needs extending One approach is to extend a small number of bytecodes to operate either on a Class_info or on a reference John: proposing extending Class_info to support descriptors Remi: type annotation on code e.g. want method attribute for bytecode John: channel null-rejecting/null-accepting in 1) descriptor vs. 2) ValueTypes attribute Descriptor is more localized note: no one wants to use the type annotations approach Descriptor approach: extensible mechanism Type with a tail: constraint only, restricts the value space, not extend Verifier can check value space restrictions, and may allow some restricted->unrestricted conversions Karen: let?s start with explicit conversions with exact matches for the verifier and evolve from there John: generics did that, in future the verifier could handle no-op conversions Remi: how would you express this in the java language? John: this is a vm mechanism, that is a language choice Remi: Would javac track nullability John: A source processor that did not understand type constraints could rely on dynamic checks Remi: concerns about leakage if stored in a local variable John: Var x = myMap.get() // result is unconstrained Dan: tentatively the type system may provide a way to express nullability John: already expresses unconstrained Remi: javac - objects are always nullable John: language level take - want javac to assume restrict nulls for value types unless evidence to the contrary, e.g. calls to erased generics that semantically return null or explicit source decorations - not clear what a local variable will denote - might want inverted defaults for value type nullability at the source level Remi: concern: erased generic returns null -> this could spread checkcast could prevent null from propagating John: Clojure/Scala - have their own descriptor constraints could decouple with indy on checkcast Remi: value type and nullability as separate concerns what about a reference type that is non-nullable from the vm point of view John: nonnullable and value type might be the 1st use. We need an extensible mechanism Remi: why not value type attribute John: could explore value type as a descriptor extension Remi: what if we solved this at the java level, if java wants a nullable VT, use erasure and checkcast? John: Dan is exploring possible models Getting user back pressure - don?t take away nulls Liked the trick of erasing to object lost the battle due to language requirements Remi: if erase to Object/Interfaces, this is not backward compatible Not forward compatible either with expectations of overloading in java language Have overloading today because we don?t have Object as root for primitives and value types What if we got a value from generics already erased and let java handle this? Karen: we need to handle both erased generics and value-based-class migration Remi: Create folks want value types, and want to retrofit existing classes - they don?t want to lose sync and mutability. ed. note: You can only pass-by-value if you have no identity and are immutable. Karen: Descriptor contraints extensibility could provide additional use cases for optimization, such as ?frozen? or just identity less. We chose value types as a sweet spot that solves a number of use cases and can be optimized. Additional discussion on overloading John: one goal is to migrate List to a reified generic. We could do dynamic checking and speculating, it is more performant to do reified generics The nullability contract of a container impacts the implementation of the value space Remi: If map.get() were to return a value type, we don?t see the null until we publish or invoke a method John: with the more complex descriptor model, getfield has fewer runtime checks Karen: goals is to have information for javac, verifier and runtime so that javac can have the same contract for nullability and NPEs that we have today for ClassCastExceptions - i.e. if your code compiles without warnings you will not get a runtime CCE should apply to NPEs (or ArrayStoreException) Remi: concern about heap pollution Guava has an unsafe cast to take a public static method()Object and return anything John: we should distinguish between heap pollution and the far riskier other levels of unsafe Remi: what if javac could handle erased generics without descriptors? And require a null check before a null escapes Karen: we would lose the value-based-class migration John: If Remi is describing: if we only had the ValueTypes attribute and one descriptor and the language folks handled any null erased to Object, then we have to check for wormholes, such as MethodHandles or other cases in which the ValueTypes attributes differ. Karen: I don?t see a language solution for value-based-class migration. Note also that there are multiple ways to get null, not just through bytecodes. Remi: What if we had a small jdk-only list of corner cases for migration? Karen: Dan has pointed out and I agree with him that many users are going to want the benefits of migrating to value types, and even in the JDK, there are already experiments beyond the small list of existing VBCs. If we have a way to avoid corner cases that would be a much more maintainable design. Remi: But if sync and == don?t work there is no way to migrate most classes. And if value-based-class properties are not enforced, there is no guarantee they are followed. Karen: recognize that - we will be offering a flag to check identity - one for javac lint and one for runtime. Karen: note also that for erased generics we have two null issues 1. returning null 2. passing array in and writing null goal is a javac warning before a runtime NPE Remi: Can we find a way to solve this at the language level, javac can give a warning but the vm doesn?t need to know Kotlin has 2 types: nullable and nonnullable with compiler enforcement Frederic: clarification Kotlin has references that are nullable or not, not types John: Had been exploring using erasure for the nullable value type case and the ValueType attribute implying null. Dan is exploring possible alternative translation strategies with different approaches to distinguish a non-nullable value type from an old erased value type Remi: concern is that if we solve nullability here for value types, we may miss something for the bigger nullability picture. .net got this wrong and requires too much complexity with little user benefit. Longer-term: want non-nullable Exploring how to solve value types and reified generics without involving the vm. John: would be happy to defer a nullability vm feature until reified generics or after Remi: wants longer-term non-nullability, e.g. for Strings, with vm optimizations .net added too much source level line noise for little benefit Karen: we need to handle nullability for value types for more than just erased generics. We have mentioned value-based-class migration. There are 8-9 different ways in which value types can become null at runtime. AI: Karen - send nullability cases Dan: Will send out a description of current options we are exploring to handle requirement for null-accepting and null-rejecting value types.