From daniel.smith at oracle.com Wed Feb 9 03:04:43 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 9 Feb 2022 03:04:43 +0000 Subject: EG meeting, 2022-02-09 Message-ID: <72A65E33-9B81-4051-B5FD-4E0EA31B7E82@oracle.com> EG Zoom meeting tomorrow at 5pm UTC (9am PDT, 12pm EDT). Some mail discussions we can follow up on: "VM model and aconst_init": we had a side conversation about potentially requiring explicit conversions from Q to L "Interface pollution and JVM invariants": Dan pointed out that the interfaces IdentityObject and ValueObject are not strongly enforced at runtime, because the verifier doesn't check interface types "SoV-3: constructor questions": Dan asked about validation for and methods. Answer: JVM doesn't care about methods in abstract classes, the rules about methods still uncertain. "SoV-2: weak references": Dan raised concerns about the weak reference strategy described in SoV. "The interfaces IdentityObject and ValueObject must die !": Self-explanatory :-). We retraced how we got here and talked about alternatives. "JEP update: Classes for the Basic Primitives": I revised JEP 402 to catch up to changes to the model elsewhere "Terminology bikeshedding summary": I listed areas where we've had some concern about the terminology currently being used --- To provide some structure, I think we can put the topics in three categories: Known unfinished issues: - Factory method interpretation/restrictions - WeakReference handling Revisiting/questioning the current design: - Explicit Q->L conversions - IdentityObject and ValueObject interfaces - Terminology Probably resolved, but reviewing: - Abstract supers and construction signals/mechanisms - Declarations for basic primitives From forax at univ-mlv.fr Wed Feb 9 17:00:30 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 9 Feb 2022 18:00:30 +0100 (CET) Subject: SoV-2: weak references In-Reply-To: References: Message-ID: <1382016402.1129351.1644426030926.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Dan Heidinga" > To: "Brian Goetz" > Cc: "valhalla-spec-experts" > Sent: Wednesday, February 9, 2022 5:50:29 PM > Subject: Re: SoV-2: weak references > One option is to look at what we can do to help users prepare for IAE > when using value-based classes as keys to WHM. Can we take an > approach similar to JEP 390 [1] and provide JFR events that flag uses > of value-based classes as keys? It's not perfect but similar to JEP > 390, it does help users to know if they need to do something to > prepare for this. I wonder if there is not a difference between a weak reference of a value class and a weak reference on a primitive class. A weak reference on a primitive class makes no sense, a weak reference on a value class may make sense because a value class is handled by a pointer. > > --Dan R?mi > > [1] http://openjdk.java.net/jeps/390 > > On Thu, Jan 20, 2022 at 1:54 PM Dan Heidinga wrote: >> >> > >> > It certainly seems that all the choices are bad. >> > >> > The "obvious" choice is to simply say that WeakReference makes no sense, >> > in that it sidesteps the hard semantics questions. >> >> It's an uncomfortable answer but it seems to provide the most >> defensible (and thus understandable) semantics for users. Values don't >> have an explicit lifetime and thus there is no way to tell when "this" >> copy of a value goes out of scope and can be collected. The object >> references (if any) held by the value are not a good proxy for its >> lifecycle - they can have either shorter or much longer life spans - >> and will make reasoning about when a WeakReference can be >> collected difficult for experts, let alone most users. >> >> > My fear is that this will cause new failures, where existing libraries that toss >> > objects into WHMs to cache derived results, will start to experience new >> > failures when the value types show up in the heap (after all, >> > WeakReference::new takes Object.) >> >> This may be a case where the WeakReference constructor needs to be >> updated to take an IdentityObject and the old constructor marked as >> @Deprecated? Which doesn't solve the immediate problem but helps >> justify adding a "fail-fast" check to all WeakReference constructors >> so that they throw an IllegalArgumentException if the referent isn't >> an IdentityObject. >> >> This won't avoid failures but it does make it very clear what went >> wrong rather than introducing "strange", hard to diagnose failures. >> >> > And we'll have to have something to tell those users, because they declared a >> > WeakHashMap, and someone created a value subtype of User -- >> > which seems entirely valid. >> > >> > It is possible we could do something special with WHM, since it is layered atop >> > WR, but that still begs the question -- what? >> >> Starting from the conclusion that WeakReference is a >> meaningless entity, what are the options here? >> >> 1) Make it impossible to use a Value as a key in a WeakHashMap. >> ::put(key, value) & ::pulAll(Map m) will throw if a key is a Value >> object. ::containsKey(Object) will always be false if the Object is a >> ValueObject. This makes WeakHashMap unusable with Values. The >> semantics are clear but all existing uses of WeakHashMap will need to >> be adapted to defensively check for Values and do something (tbd) to >> avoid the exceptions. >> >> 2) Use strong references for Value keys in WeakHashMap. >> Treat each Value object used as a key in WeakHashMap as a strong >> reference. This means Value keys will never be removed and will keep >> their corresponding map value alive forever (or until explicitly >> removed). While this will allow WeakHashMaps to continue to be used >> as Maps for Values, it will break the contract for WHM and may >> introduce memory leaks into otherwise correct programs. >> >> 3) Pick some other object to act as the reference when using a Value >> key in a WHM. >> This is basically the solution we rejected for WeakReference >> and all the same problems apply here. It may allow existing code to >> "keep working" when it first deals with Values but introduces strange >> failure cases and difficult to reason about rules. It avoids >> exceptions but leaves the code doing the wrong thing with no way to >> tell. >> >> Anyone see another option here? >> > > --Dan From brian.goetz at oracle.com Wed Feb 9 18:02:26 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 9 Feb 2022 13:02:26 -0500 Subject: [External] : Re: SoV-2: weak references In-Reply-To: References: Message-ID: <26003625-1705-e52d-cd3d-ec0d3041bef3@oracle.com> I have come around to a similar conclusion.? None of the models for WR(value) are really all that justifiable, other than "throws".? But we dislike that because so much code uses WHM that we are worried about this code all of a sudden failing.? But we are trying to fix that by distorting WR, rather than enhancing WHM. WHM has its own set of constraints; it is the closest thing to a cache in the JDK (with a very rigid eviction policy), and frequently gets used as such.? But implicit in the use of a cache is something that only the code that uses WHM knows: are we caching because we're trying to avoid redundant computation, or are we caching because we *cannot* recompute the mapping? The current eviction strategy is consistent with both -- for identities.? But once we get past identity objects, I can very well imagine some clients wanting to aggressively release mappings from value keys (to save memory), and some wanting to never release mappings from value keys (because they need a unique target).? Which suggests that creating a WHM requires a user-specifiable policy; I can think of cases where each of the following policies are sensible (note that they all avoid getting into the details of invalidation): ?- THROW -- throw when someone uses a value key.? This would probably be the default. ?- KEEP -- keep value key mappings forever.? This is useful when we are computing, say, histograms of "how often have we seen key X". ?- DISCARD -- clear value key mappings immediately. ?- SOFT -- Keep value key mappings around for as long as there is not excessive heap pressure. For policy SOFT, we would likely implement by wrapping a SoftReference around an *entire* side map of key -> value mappings for value keys. Then we'd provide some new factories for WHM to select one of these policies. On 2/9/2022 11:50 AM, Dan Heidinga wrote: > One option is to look at what we can do to help users prepare for IAE > when using value-based classes as keys to WHM. Can we take an > approach similar to JEP 390 [1] and provide JFR events that flag uses > of value-based classes as keys? It's not perfect but similar to JEP > 390, it does help users to know if they need to do something to > prepare for this. > > --Dan > > [1]http://openjdk.java.net/jeps/390 > > On Thu, Jan 20, 2022 at 1:54 PM Dan Heidinga wrote: >>> It certainly seems that all the choices are bad. >>> >>> The "obvious" choice is to simply say that WeakReference makes no sense, in that it sidesteps the hard semantics questions. >> It's an uncomfortable answer but it seems to provide the most >> defensible (and thus understandable) semantics for users. Values don't >> have an explicit lifetime and thus there is no way to tell when "this" >> copy of a value goes out of scope and can be collected. The object >> references (if any) held by the value are not a good proxy for its >> lifecycle - they can have either shorter or much longer life spans - >> and will make reasoning about when a WeakReference can be >> collected difficult for experts, let alone most users. >> >>> My fear is that this will cause new failures, where existing libraries that toss objects into WHMs to cache derived results, will start to experience new failures when the value types show up in the heap (after all, WeakReference::new takes Object.) >> This may be a case where the WeakReference constructor needs to be >> updated to take an IdentityObject and the old constructor marked as >> @Deprecated? Which doesn't solve the immediate problem but helps >> justify adding a "fail-fast" check to all WeakReference constructors >> so that they throw an IllegalArgumentException if the referent isn't >> an IdentityObject. >> >> This won't avoid failures but it does make it very clear what went >> wrong rather than introducing "strange", hard to diagnose failures. >> >>> And we'll have to have something to tell those users, because they declared a WeakHashMap, and someone created a value subtype of User -- which seems entirely valid. >>> >>> It is possible we could do something special with WHM, since it is layered atop WR, but that still begs the question -- what? >> Starting from the conclusion that WeakReference is a >> meaningless entity, what are the options here? >> >> 1) Make it impossible to use a Value as a key in a WeakHashMap. >> ::put(key, value) & ::pulAll(Map m) will throw if a key is a Value >> object. ::containsKey(Object) will always be false if the Object is a >> ValueObject. This makes WeakHashMap unusable with Values. The >> semantics are clear but all existing uses of WeakHashMap will need to >> be adapted to defensively check for Values and do something (tbd) to >> avoid the exceptions. >> >> 2) Use strong references for Value keys in WeakHashMap. >> Treat each Value object used as a key in WeakHashMap as a strong >> reference. This means Value keys will never be removed and will keep >> their corresponding map value alive forever (or until explicitly >> removed). While this will allow WeakHashMaps to continue to be used >> as Maps for Values, it will break the contract for WHM and may >> introduce memory leaks into otherwise correct programs. >> >> 3) Pick some other object to act as the reference when using a Value >> key in a WHM. >> This is basically the solution we rejected for WeakReference >> and all the same problems apply here. It may allow existing code to >> "keep working" when it first deals with Values but introduces strange >> failure cases and difficult to reason about rules. It avoids >> exceptions but leaves the code doing the wrong thing with no way to >> tell. >> >> Anyone see another option here? >> >> --Dan From kevinb at google.com Wed Feb 9 18:23:35 2022 From: kevinb at google.com (Kevin Bourrillion) Date: Wed, 9 Feb 2022 10:23:35 -0800 Subject: [External] : Re: SoV-2: weak references In-Reply-To: <26003625-1705-e52d-cd3d-ec0d3041bef3@oracle.com> References: <26003625-1705-e52d-cd3d-ec0d3041bef3@oracle.com> Message-ID: This is not based on a very deep understanding, sorry: * WeakReference, 1-arg constructor: When given a bucket 2-or-3 instance, it seems maybe fine to just treat as strong reference? User is saying "just hold this loosely, don't keep it alive on my account" and doesn't care what happens after that. * WeakReference, 2-arg constructor: this already seems to depend on identity, so bucket 2 or 3 is more clearly problematic now. That this type wanted to become a value at all suggests the code might *already* be a bit broken. Seems worth an error or warning just for trying to do this at all. The behavior (if the warning is ignored) only has to be something "simple" and reasonable, since what it's being asked to do is arguably nonsensical. * For WeakHashMap use cases, I would expect that any attempts to actually design something "good" that does what users really "want" would end up at some different library entirely. This is something that Caffeine probably does well (disclosure, I worked on the thing that became that). On Wed, Feb 9, 2022 at 10:02 AM Brian Goetz wrote: > I have come around to a similar conclusion. None of the models for > WR(value) are really all that justifiable, other than "throws". But we > dislike that because so much code uses WHM that we are worried about this > code all of a sudden failing. But we are trying to fix that by distorting > WR, rather than enhancing WHM. > > WHM has its own set of constraints; it is the closest thing to a cache in > the JDK (with a very rigid eviction policy), and frequently gets used as > such. But implicit in the use of a cache is something that only the code > that uses WHM knows: are we caching because we're trying to avoid redundant > computation, or are we caching because we *cannot* recompute the mapping? > The current eviction strategy is consistent with both -- for identities. > But once we get past identity objects, I can very well imagine some clients > wanting to aggressively release mappings from value keys (to save memory), > and some wanting to never release mappings from value keys (because they > need a unique target). Which suggests that creating a WHM requires a > user-specifiable policy; I can think of cases where each of the following > policies are sensible (note that they all avoid getting into the details of > invalidation): > > - THROW -- throw when someone uses a value key. This would probably be > the default. > - KEEP -- keep value key mappings forever. This is useful when we are > computing, say, histograms of "how often have we seen key X". > - DISCARD -- clear value key mappings immediately. > - SOFT -- Keep value key mappings around for as long as there is not > excessive heap pressure. > > For policy SOFT, we would likely implement by wrapping a SoftReference > around an *entire* side map of key -> value mappings for value keys. > > Then we'd provide some new factories for WHM to select one of these > policies. > > On 2/9/2022 11:50 AM, Dan Heidinga wrote: > > One option is to look at what we can do to help users prepare for IAE > when using value-based classes as keys to WHM. Can we take an > approach similar to JEP 390 [1] and provide JFR events that flag uses > of value-based classes as keys? It's not perfect but similar to JEP > 390, it does help users to know if they need to do something to > prepare for this. > > --Dan > > [1] http://openjdk.java.net/jeps/390 > > On Thu, Jan 20, 2022 at 1:54 PM Dan Heidinga wrote: > > It certainly seems that all the choices are bad. > > The "obvious" choice is to simply say that WeakReference makes no sense, in that it sidesteps the hard semantics questions. > > It's an uncomfortable answer but it seems to provide the most > defensible (and thus understandable) semantics for users. Values don't > have an explicit lifetime and thus there is no way to tell when "this" > copy of a value goes out of scope and can be collected. The object > references (if any) held by the value are not a good proxy for its > lifecycle - they can have either shorter or much longer life spans - > and will make reasoning about when a WeakReference can be > collected difficult for experts, let alone most users. > > > My fear is that this will cause new failures, where existing libraries that toss objects into WHMs to cache derived results, will start to experience new failures when the value types show up in the heap (after all, WeakReference::new takes Object.) > > This may be a case where the WeakReference constructor needs to be > updated to take an IdentityObject and the old constructor marked as > @Deprecated? Which doesn't solve the immediate problem but helps > justify adding a "fail-fast" check to all WeakReference constructors > so that they throw an IllegalArgumentException if the referent isn't > an IdentityObject. > > This won't avoid failures but it does make it very clear what went > wrong rather than introducing "strange", hard to diagnose failures. > > > And we'll have to have something to tell those users, because they declared a WeakHashMap, and someone created a value subtype of User -- which seems entirely valid. > > It is possible we could do something special with WHM, since it is layered atop WR, but that still begs the question -- what? > > Starting from the conclusion that WeakReference is a > meaningless entity, what are the options here? > > 1) Make it impossible to use a Value as a key in a WeakHashMap. > ::put(key, value) & ::pulAll(Map m) will throw if a key is a Value > object. ::containsKey(Object) will always be false if the Object is a > ValueObject. This makes WeakHashMap unusable with Values. The > semantics are clear but all existing uses of WeakHashMap will need to > be adapted to defensively check for Values and do something (tbd) to > avoid the exceptions. > > 2) Use strong references for Value keys in WeakHashMap. > Treat each Value object used as a key in WeakHashMap as a strong > reference. This means Value keys will never be removed and will keep > their corresponding map value alive forever (or until explicitly > removed). While this will allow WeakHashMaps to continue to be used > as Maps for Values, it will break the contract for WHM and may > introduce memory leaks into otherwise correct programs. > > 3) Pick some other object to act as the reference when using a Value > key in a WHM. > This is basically the solution we rejected for WeakReference > and all the same problems apply here. It may allow existing code to > "keep working" when it first deals with Values but introduces strange > failure cases and difficult to reason about rules. It avoids > exceptions but leaves the code doing the wrong thing with no way to > tell. > > Anyone see another option here? > > --Dan > > > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From john.r.rose at oracle.com Wed Feb 9 18:32:07 2022 From: john.r.rose at oracle.com (John Rose) Date: Wed, 09 Feb 2022 10:32:07 -0800 Subject: EG meeting, 2022-02-09 [SoV-3: constructor questions] In-Reply-To: <72A65E33-9B81-4051-B5FD-4E0EA31B7E82@oracle.com> References: <72A65E33-9B81-4051-B5FD-4E0EA31B7E82@oracle.com> Message-ID: <905831BF-195D-46AA-80D1-6E6577FDFBA3@oracle.com> On 8 Feb 2022, at 19:04, Dan Smith wrote: > "SoV-3: constructor questions": Dan asked about validation for > and methods. Answer: JVM doesn't care about methods in > abstract classes, the rules about methods still uncertain. On the question of JVM validation of `` methods, I?m in favor of as few rules as possible, ideally treating `` as just another name. It?s super-power is not in its restrictions but in its conventionality: It?s the obvious choice for constructor factory methods. But it is not necessarily limited to that use. Maximal limitation would be that a `` method can only occur as the translation of a value-class constructor. Any evidence in the classfile that it was not such a translation would be grounds for failing a validation check. We?d make as many such rules as we can think of. Arguments against: - Having a special method identifier in the JVMs without other restrictions would be a new thing, and hence suspicious. - Limiting the use of `` as much as possible makes it clear, to higher layers of the code (javac and reflection) what is going on in the class file, as a ?reflection? of the source file. - Reflection of an irregular (non-source-conforming) `` method has to be messy. (Is it really a constructor? Or is it just a method named ``?) Arguments in favor: - It is a new thing in the JVM for any descriptor to be constrained to mention the same name as is the name of the constant pool item referred to by `ClassFile.this_class` item (JVMS 4.1). (It is suspicious.) - A maximal limitation would break hidden classes. (They must sometimes return a supertype from their factories, since the HC is not always name-able in a descriptor. HCs only work because the previous point.) - A limitation might preclude a perhaps-desirable future translation strategy that used `` factories uniformly to translate `new` source code expressions (identity or value objects, uniformly). - A limitation could remove a natural translation strategy for ?canonical factory methods? in non-concrete types. This is a hypothetical language feature for Java or some other language. (E.g., `new List(a,b,c)` instead of `List.of(a,b,c)`, removing the need of the user to remember whether the word was `of` or `make` or `build` or some other designer choice.) - Most any limitation would preclude ad hoc use of `` factories by translation strategies of other languages, such as Scala and Clojure, which surely have their own uses of JVM object life cycles. We want to be friendly to non-Java languages. Compromise positions: - Require a `` method to be `ACC_STATIC` but allow for any purpose (i.e., any access and any descriptor). - Require a `` method to return either the class named by `this_class` or some super type (TBD how *this* should be checked). I would prefer the first compromise: It?s `static` but otherwise the JVM asks no questions. Regarding reflection, I think it would be OK to surface all of the `` methods (of whatever signature) on the `getConstructors` list, even if they return ?something odd?. Alternatively, to prevent a sharp edge we could have a new list `Class::getFactories`, and *copy* (not move) entries from that list onto `getConstructors` exactly when the return type matches the enclosing class. That is a more natural move for reflection (which operates on runtime types) than for class file structuring (which is more static). The reason I prefer to require `static` marking is that it would prevent the funny name from appearing on the list of regular methods, via reflection. From frederic.parain at oracle.com Wed Feb 9 21:50:01 2022 From: frederic.parain at oracle.com (Frederic Parain) Date: Wed, 9 Feb 2022 16:50:01 -0500 Subject: Abstract class with fields implementing ValueObject Message-ID: <4b486a05-6206-30f3-a1a9-5cff64e627b4@oracle.com> There's a weird case that seems to be allowed by the Value Objects JVMS draft: An abstract class can declare non-static fields, which means it won't have the ACC_PERMITS_VALUE flag set, but also declare that it implements the ValueObject interface. The combination looks just wrong, because no class can subclass such class: - identity classes are not allowed because of the presence of the ValueObject interface - value classes are not allowed because of the absence of ACC_PERMITS_VALUE I've looked for a rule that would prohibit such combination in the JVMS draft but couldn't find one. Did I miss something? Fred From brian.goetz at oracle.com Wed Feb 9 21:57:40 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 9 Feb 2022 16:57:40 -0500 Subject: [External] : Re: SoV-2: weak references In-Reply-To: References: <26003625-1705-e52d-cd3d-ec0d3041bef3@oracle.com> Message-ID: <93ced1c5-241c-d7b9-afd5-a85d29f98de9@oracle.com> On 2/9/2022 1:23 PM, Kevin Bourrillion wrote: > This is not based on a very deep understanding, sorry: > > * WeakReference, 1-arg constructor: When given a bucket 2-or-3 > instance, it seems maybe fine to just treat as strong reference? User > is saying "just hold this loosely, don't keep it alive on my account" > and doesn't care what happens after that. This is *sometimes* what the user wants.? And one could argue that for a "pure" value (values all the way down), this is the right answer.? But suppose a value holds an identity: ??? value class ListHolder { ??????? List list = new ArrayList<>(1_000_000); ??? } If the user does ??? ListHolder lh = new ListHolder<>(); ??? WR wr = new WR(lh); there are conditions under which lh.list becomes unreachable -- but retaining the WR forever means it is never GCed.? Some users will find this surprising, and say "but I used a WR to avoid memory leaks."? So there's an argument that WR is really just about feeding reachability information back from the GC to the application, and the GC only talks about reachability for identities. Picking a one-size-fits-all policy like "always clear / never clear" means that WR will do the right thing sometimes, and the wrong thing other times. > * For WeakHashMap use cases, I would expect that any attempts to > actually design something "good" that does what users really "want" > would end up at some different library entirely. This is something > that Caffeine > > probably does well (disclosure, I worked on the thing that became that). Yes, probably.? We're worried about existing applications that are built around WHM, and trying to give them something reasonable that will keep them going, since many of these are legacy applications with limited room for rearchitecting.? So we're asking "what's the least we have to do", and the simple set of policies I outlined for WHM is a candidate. From john.r.rose at oracle.com Wed Feb 9 23:02:53 2022 From: john.r.rose at oracle.com (John Rose) Date: Wed, 09 Feb 2022 15:02:53 -0800 Subject: Abstract class with fields implementing ValueObject In-Reply-To: <4b486a05-6206-30f3-a1a9-5cff64e627b4@oracle.com> References: <4b486a05-6206-30f3-a1a9-5cff64e627b4@oracle.com> Message-ID: That could be one of very many edge conditions in the JVMS that are not diagnosed directly by a validation, but that will eventually cause an error when the broken classfile is further used. I don?t think there needs to be a special rule for this. We don?t try to comprehensively diagnose all ?impossible-to-use? classfiles. On 9 Feb 2022, at 13:50, Frederic Parain wrote: > There's a weird case that seems to be allowed by the Value Objects JVMS draft: > > An abstract class can declare non-static fields, which means it won't > have the ACC_PERMITS_VALUE flag set, but also declare that it implements > the ValueObject interface. > > The combination looks just wrong, because no class can subclass such class: > - identity classes are not allowed because of the presence of > the ValueObject interface > - value classes are not allowed because of the absence of > ACC_PERMITS_VALUE > > I've looked for a rule that would prohibit such combination in the > JVMS draft but couldn't find one. > > Did I miss something? > > Fred From kevinb at google.com Thu Feb 10 02:18:50 2022 From: kevinb at google.com (Kevin Bourrillion) Date: Wed, 9 Feb 2022 18:18:50 -0800 Subject: JEP update: Classes for the Basic Primitives In-Reply-To: References: Message-ID: Some much-delayed feedback/questions.... * Could the doc make a clearer distinction (throughout) between which facts about int/Integer are happening because we expect *all* bucket-3 classes to work that way, vs. which are special one-off tweaks for the 8 predefined types? * I'm curious whether it would be *possible* to make `int` no longer a keyword, just a special kind of type alias that normal people don't get to declare. I'm *not* claiming that'd be worth actually doing; just wanting to understand what forces would act to prevent it, as part of understanding everything that makes the 8 builtin types "irredeemably special". * I assume the reason "the JVM type Qjava/lang/Double cannot be encoded with a Class object" is because the distinction between it and D is intentional implementation detail. * Since I think/hope it is *not* true that `int` will be a subtype of `Integer`, it's not 100% clear whether the phrase "array covariance" in the doc is referring to the (desirable) property that `int[] <: Integer[]`. I think it is. * I said in the meeting that "I don't think anyone cares" what kind of exception gets thrown if trying to store null in an `Integer[]` which is secretly an `int[]`. Well, I'm sure that's not at all true. :-) Sorry. * Re: "a basic primitive class may declare an instance field of its own primitive" -- does it really need that field, or can we nuke it and just s/value/this/g throughout the file? If that could work, it would be so much less confusing -- nothing circular! The only magic would be "where are the actual bits??" but you don't see the bits of an object header in `Object.java` either and the number of people this bothers is a very round number. ~~ Since we might really be on the verge of being through with "boxing" for good, here, have an appropriate musical closing! * "Boxing's been good to me, Howard Now I'm told, you're growing old The whole time you knew, in a couple of years I'd be through Has boxing been good to you?"* https://youtu.be/UtIp6uP13kk?t=30 On Wed, Jan 12, 2022 at 4:22 PM Dan Smith wrote: > I'm made some revisions to JEP 402 to better track with the revised JEP > 401?in particular backing off of "everything is an object". > > There were some short-lived changes where I more aggressively pursued the > idea that the class was named 'int', but I've backed off of that, too. Now > taking the more conservative approach that there is a primitive class named > 'Integer', but by special rule its primitive type is expressed 'int', and > the class name refers to the reference type. > > I realize that much of this could potentially change?reflection story, > terminology, etc. But consider this the "plan of record" for now. > > https://bugs.openjdk.java.net/browse/JDK-8259731 -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From forax at univ-mlv.fr Thu Feb 10 09:02:47 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 10 Feb 2022 10:02:47 +0100 (CET) Subject: EG meeting, 2022-02-09 [SoV-3: constructor questions] In-Reply-To: <905831BF-195D-46AA-80D1-6E6577FDFBA3@oracle.com> References: <72A65E33-9B81-4051-B5FD-4E0EA31B7E82@oracle.com> <905831BF-195D-46AA-80D1-6E6577FDFBA3@oracle.com> Message-ID: <1049573879.1403422.1644483767549.JavaMail.zimbra@u-pem.fr> > From: "John Rose" > To: "daniel smith" > Cc: "valhalla-spec-experts" > Sent: Wednesday, February 9, 2022 7:32:07 PM > Subject: Re: EG meeting, 2022-02-09 [SoV-3: constructor questions] > On 8 Feb 2022, at 19:04, Dan Smith wrote: >> "SoV-3: constructor questions": Dan asked about validation for and >> methods. Answer: JVM doesn't care about methods in abstract classes, the >> rules about methods still uncertain. > On the question of JVM validation of methods, I?m in favor of as few rules > as possible, ideally treating as just another name. It?s super-power is > not in its restrictions but in its conventionality: It?s the obvious choice for > constructor factory methods. But it is not necessarily limited to that use. > Maximal limitation would be that a method can only occur as the > translation of a value-class constructor. Any evidence in the classfile that it > was not such a translation would be grounds for failing a validation check. > We?d make as many such rules as we can think of. > Arguments against: > * Having a special method identifier in the JVMs without other restrictions > would be a new thing, and hence suspicious. > * Limiting the use of as much as possible makes it clear, to higher layers > of the code (javac and reflection) what is going on in the class file, as a > ?reflection? of the source file. > * Reflection of an irregular (non-source-conforming) method has to be > messy. (Is it really a constructor? Or is it just a method named ?) > Arguments in favor: > * It is a new thing in the JVM for any descriptor to be constrained to mention > the same name as is the name of the constant pool item referred to by > ClassFile.this_class item (JVMS 4.1). (It is suspicious.) > * A maximal limitation would break hidden classes. (They must sometimes return a > supertype from their factories, since the HC is not always name-able in a > descriptor. HCs only work because the previous point.) > * A limitation might preclude a perhaps-desirable future translation strategy > that used factories uniformly to translate new source code expressions > (identity or value objects, uniformly). > * A limitation could remove a natural translation strategy for ?canonical > factory methods? in non-concrete types. This is a hypothetical language feature > for Java or some other language. (E.g., new List(a,b,c) instead of > List.of(a,b,c) , removing the need of the user to remember whether the word was > of or make or build or some other designer choice.) > * Most any limitation would preclude ad hoc use of factories by > translation strategies of other languages, such as Scala and Clojure, which > surely have their own uses of JVM object life cycles. We want to be friendly to > non-Java languages. > Compromise positions: > * Require a method to be ACC_STATIC but allow for any purpose (i.e., any > access and any descriptor). > * Require a method to return either the class named by this_class or some > super type (TBD how this should be checked). > I would prefer the first compromise: It?s static but otherwise the JVM asks no > questions. > Regarding reflection, I think it would be OK to surface all of the methods > (of whatever signature) on the getConstructors list, even if they return > ?something odd?. Alternatively, to prevent a sharp edge we could have a new > list Class::getFactories , and copy (not move) entries from that list onto > getConstructors exactly when the return type matches the enclosing class. That > is a more natural move for reflection (which operates on runtime types) than > for class file structuring (which is more static). > The reason I prefer to require static marking is that it would prevent the funny > name from appearing on the list of regular methods, via reflection. I agree with static being the only requirement for the VM side. For reflection,i prefer the first alternative, surface the method whatever the return type given that the return type is a declared type, not a runtime class. Dynamically typed languages like Groovy or JRuby may want to declare that the return type of is java.lang.Object (it's the default type if no type is provided in Groovy), if reflection filter out those methods, it will be bad for the interropt between Java and those dynamically typed languages. R?mi From forax at univ-mlv.fr Thu Feb 10 09:03:56 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 10 Feb 2022 10:03:56 +0100 (CET) Subject: Abstract class with fields implementing ValueObject In-Reply-To: References: <4b486a05-6206-30f3-a1a9-5cff64e627b4@oracle.com> Message-ID: <644073693.1404371.1644483836898.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "John Rose" > To: "Frederic Parain" > Cc: "valhalla-spec-experts" > Sent: Thursday, February 10, 2022 12:02:53 AM > Subject: Re: Abstract class with fields implementing ValueObject > That could be one of very many edge conditions in the JVMS that are not > diagnosed directly by a validation, but that will eventually cause an error > when the broken classfile is further used. > > I don?t think there needs to be a special rule for this. We don?t try to > comprehensively diagnose all ?impossible-to-use? classfiles. or better we ditch ValueObject and IdentityObject ... R?mi > > On 9 Feb 2022, at 13:50, Frederic Parain wrote: > >> There's a weird case that seems to be allowed by the Value Objects JVMS draft: >> >> An abstract class can declare non-static fields, which means it won't >> have the ACC_PERMITS_VALUE flag set, but also declare that it implements >> the ValueObject interface. >> >> The combination looks just wrong, because no class can subclass such class: >> - identity classes are not allowed because of the presence of >> the ValueObject interface >> - value classes are not allowed because of the absence of >> ACC_PERMITS_VALUE >> >> I've looked for a rule that would prohibit such combination in the >> JVMS draft but couldn't find one. >> >> Did I miss something? >> > > Fred From brian.goetz at oracle.com Thu Feb 10 12:24:50 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 10 Feb 2022 07:24:50 -0500 Subject: JEP update: Classes for the Basic Primitives In-Reply-To: References: Message-ID: <604d22a7-dc66-0f70-4fd7-7334815d671d@oracle.com> > * Could the doc make a clearer distinction (throughout) between which > facts about int/Integer are happening because we expect *all*?bucket-3 > classes to work that way, vs. which are special one-off tweaks for the > 8 predefined types? A "how are int/Integer special" section would indeed be useful.? And I think we haven't finished enumerating what goes in it; in the discussions over inference, for example, several folks have been heard mumbling "maybe we're trying to push the int/Integer analogy too far." > * I'm curious whether it would be /possible/ to make `int` no longer a > keyword, just a special kind of type alias that?normal people?don't > get to declare. I'm /not/?claiming that'd be worth actually doing; > just wanting to understand what forces would act to prevent it, as > part of understanding everything that makes the 8 builtin types > "irredeemably special". In a previous world, `int` could have been an alias for `Integer.val`.? But we (happily) have banished .val from the world. I think this is a useful exercise anyway, though; we have stumbled over type-vs-class in a lot of places already with respect to B3, and int has similar problems; most uses of `int` are types, but there's also `int.class`, which means ... something.? What exactly? And how does that differ from Point.class / Point.ref.class / Point.class.noNotThatClassTheOtherClass() ? > * I assume the reason "the JVM type Qjava/lang/Double cannot be > encoded with a Class object" is because the distinction between it and > D is intentional implementation detail. Worse :(?? The J and D carriers use two slots, so even if we wanted to abstract over the primitive carriers, this is kind of a dead end.? The java/lang/Double class allows us to encode List with a class that carries a double but fits in one slot. > * Since I think/hope it is /not/?true that `int` will be a subtype of > `Integer`, it's not 100% clear whether the phrase "array covariance" > in the doc is referring to the (desirable) property that `int[] <: > Integer[]`. I think it is. int will not be a subtype of Integer, nor will Point be a subtype of Point.ref.? (It was this way in a previous iteration; the VM still believes the latter, but we're probably going to disabuse it of same.)? Traditional array covariance is tied to subtyping: ?? ?? T <: U ??? ----------? T-Cov ??? T[] <: U[] But, we're going to modify this rule somewhat.? We define a relation between types, called "extends". ??? A extends B = A <: B??????? if A is a reference type ? ?? ???????????? A.ref <: B??? otherwise (We could write this more concisely as "A extends B == A.ref <: B", since the .ref operator is idempotent.)? We use "extends" for things like bounds conformance; if we have Foo, we treat Foo as being in bounds. So our modified covariance is: ?? ?? T extends U ??? --------------? T-Cov-Ext ?? ?? T[] <: U[] which gives us int[] <: Integer[], even though we don't have int <: Integer. This is no mere flourish; without it, erased generics don't work: ??? class Box { ??????? T value(); ??????? T[] asArray(); ??? } This erases to returning Object[], and if we want to specialize Box, asArray() should return int[], but to be compatible with erasure, we need int[] <: Object[]. > * I said in the meeting that "I don't think anyone cares" what kind of > exception gets thrown if trying to store null in an `Integer[]` which > is secretly an `int[]`. Well, I'm sure that's not at all true. :-) Sorry. You're lucky they didn't show you their scars. > * Re: "a basic primitive class may declare an instance field of its > own primitive" -- does it really need that field, or can we nuke it > and just s/value/this/g throughout the file? If that could work, it > would be so much less confusing -- nothing circular! The only magic > would be "where are the actual bits??" but you don't see the bits of > an object header in `Object.java` either and the number of people this > bothers is a very round number. Seems plausible :) From kevinb at google.com Fri Feb 11 22:46:39 2022 From: kevinb at google.com (Kevin Bourrillion) Date: Fri, 11 Feb 2022 14:46:39 -0800 Subject: JEP update: Classes for the Basic Primitives In-Reply-To: <604d22a7-dc66-0f70-4fd7-7334815d671d@oracle.com> References: <604d22a7-dc66-0f70-4fd7-7334815d671d@oracle.com> Message-ID: On Thu, Feb 10, 2022 at 4:25 AM Brian Goetz wrote: I think this is a useful exercise anyway, though; we have stumbled over > type-vs-class in a lot of places already with respect to B3, and int has > similar problems; most uses of `int` are types, but there's also > `int.class`, which means ... something. What exactly? > We *would* normally get 2 types called `Integer` and `Integer.ref`, but INSERT MAGIC HERE we make them come out as `int` and `Integer` instead for compatibility. So `int.class` is just what *would* have been `Integer.class` normally. The reason every other appearance of `int` is a type is that the other forms of "class usage, not type usage" listed on page 179 of JLS v17 ... all just don't happen to really apply. (Actually it seems like that list will have to be broken up now, huh?) > And how does that differ from Point.class / Point.ref.class / > Point.class.noNotThatClassTheOtherClass() ? > I realize you're not necessarily asking that question in this thread, but I want to walk through how I would respond to it anyway. First here is what I already take to be true now: The `java.lang.Class` type doesn't precisely represent a class, but an erased type (let's forget about `void.class`). The relationship from erased type to class is "1 to 0..1", so for any `j.l.Class` instance that does have a related class, that instance can go ahead and represent the class too (e.g. iterate over its methods). But the 8 "basic" primitive types don't have a class, and array types partly pretend to be classes, but this is okay. For starters this seems friendly to the arrival of Point. The relationship becomes "1..2 to 0..1", which could break some program assumptions, but it *basically* keeps working the same. I assume that Point.ref.class will have a modified name in some way, and some boolean method will address the distinction between the two, but they'll otherwise be identical. They'll even be tied together; e.g. use reflection to modify a field in one, it's magically changed on the other too. The mental model here is "one class, two Classes" and while that's a bit unfortunate, again, Class already doesn't really mean the same thing as class as it is. * I assume the reason "the JVM type Qjava/lang/Double cannot be encoded > with a Class object" is because the distinction between it and D is > intentional implementation detail. > > Worse :( The J and D carriers use two slots, so even if we wanted to > abstract over the primitive carriers, this is kind of a dead end. The > java/lang/Double class allows us to encode List with a class that > carries a double but fits in one slot. > But... to be clear... it *is *also intentional implementation detail, right? In the programming model a user can't care about the distinction? -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From brian.goetz at oracle.com Fri Feb 11 23:22:21 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 11 Feb 2022 18:22:21 -0500 Subject: [External] : Re: JEP update: Classes for the Basic Primitives In-Reply-To: References: <604d22a7-dc66-0f70-4fd7-7334815d671d@oracle.com> Message-ID: > > > ? And how does that differ from Point.class / Point.ref.class / > Point.class.noNotThatClassTheOtherClass() ? > > > I realize you're not necessarily asking that question in this thread, > but I want to walk through?how I would respond?to it anyway. > > First here is what I already take to be true now: > > The `java.lang.Class` type doesn't precisely represent a class, but an > erased type (let's forget about `void.class`). The relationship?from > erased type to class is "1 to 0..1", so for any `j.l.Class` instance > that does have a related class, that instance can go ahead and > represent the class too (e.g. iterate over its methods). But the 8 > "basic" primitive types don't have a class, and array types partly > pretend to be classes, but this is okay. I think this is giving Class more credit than it might deserve, but otherwise these arguments seem basically reasonable.? Class started out as 1:1 with .class files, but over time we realized "what about arrays", "what about primitives", etc.? It has been given too many jobs to do, so it defies having a clear role, but we can try to avoid making it too much worse. > >> * I assume the reason "the JVM type Qjava/lang/Double cannot be >> encoded with a Class object" is because the distinction between >> it and D is intentional implementation detail. > Worse :(?? The J and D carriers use two slots, so even if we > wanted to abstract over the primitive carriers, this is kind of a > dead end.? The java/lang/Double class allows us to encode > List with a class that carries a double but fits in one slot. > > > But... to be clear... it /is /also intentional implementation detail, > right? In the programming model a user can't care about the distinction? > Correct.? The existence of the D carrier is an implementation detail of the VM; if doubles were translated as LFakeDouble; the only thing the user would notice is that things got slower and heaps got bigger. From daniel.smith at oracle.com Sat Feb 12 00:24:24 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Sat, 12 Feb 2022 00:24:24 +0000 Subject: EG meeting, 2022-02-09 [SoV-3: constructor questions] In-Reply-To: <905831BF-195D-46AA-80D1-6E6577FDFBA3@oracle.com> References: <72A65E33-9B81-4051-B5FD-4E0EA31B7E82@oracle.com> <905831BF-195D-46AA-80D1-6E6577FDFBA3@oracle.com> Message-ID: I need to do more work and have something concrete to propose before engaging too deeply in this discussion, but: > On Feb 9, 2022, at 11:32 AM, John Rose wrote: > > Regarding reflection, I think it would be OK to surface all of the methods (of whatever signature) on the getConstructors list, even if they return ?something odd?. The thing about Constructors (and class instance creation expressions, for that matter, along with a lot of other -based tooling that this new feature might want to transparently slide into) is that they have a type determined by the class to which they belong. There's no 'getReturnType' in the Constructor API. From daniel.smith at oracle.com Sat Feb 12 00:49:11 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Sat, 12 Feb 2022 00:49:11 +0000 Subject: Abstract class with fields implementing ValueObject In-Reply-To: <4b486a05-6206-30f3-a1a9-5cff64e627b4@oracle.com> References: <4b486a05-6206-30f3-a1a9-5cff64e627b4@oracle.com> Message-ID: <796ECFC9-BF22-4D01-8249-B4B31E61477D@oracle.com> > On Feb 9, 2022, at 2:50 PM, Frederic Parain wrote: > > There's a weird case that seems to be allowed by the Value Objects JVMS draft: > > An abstract class can declare non-static fields, which means it won't > have the ACC_PERMITS_VALUE flag set, but also declare that it implements > the ValueObject interface. > > The combination looks just wrong, because no class can subclass such class: > - identity classes are not allowed because of the presence of > the ValueObject interface > - value classes are not allowed because of the absence of > ACC_PERMITS_VALUE > > I've looked for a rule that would prohibit such combination in the > JVMS draft but couldn't find one. > > Did I miss something? If it doesn't have ACC_PERMITS_VALUE set and it declares an method, the class implicitly implements IdentityObject (see 5.3.5). And then there's an immediate error, because of the IdentityObject/ValueObject clash. If it doesn't have ACC_PERMITS_VALUE set and it *doesn't* declare an method, it's impossible to instantiate. Then there's a technical question of whether an error occurs, but it's not really an interesting use case for programmers (and javac would never generate this). (Relevant discussion on this corner case from the spec changes draft: An abstract class implements IdentityObject if it declares an instance initialization method and does not have its ACC_PERMITS_VALUE flag set; and implements ValueObject if the opposite is true (ACC_PERMITS_VALUE, no instance initialization method). Instance initialization methods and ACC_PERMITS_VALUE represent two channels for subclass instance creation, and this analysis determines whether only one channel is "open". Alternatively, we could ignore instance initialization methods and rely entirely on ACC_PERMITS_VALUE. In practice, abstract classes written in the Java programming language always have instance initialization methods, so the difference in behavior is only relevant to classes produced via other languages or tools.) From srikanth.adayapalam at oracle.com Sun Feb 13 05:16:59 2022 From: srikanth.adayapalam at oracle.com (Srikanth Adayapalam) Date: Sun, 13 Feb 2022 05:16:59 +0000 Subject: Abstract class with fields implementing ValueObject In-Reply-To: <796ECFC9-BF22-4D01-8249-B4B31E61477D@oracle.com> References: <4b486a05-6206-30f3-a1a9-5cff64e627b4@oracle.com> <796ECFC9-BF22-4D01-8249-B4B31E61477D@oracle.com> Message-ID: I understand Frederic is asking about whether the spec? inadvertently allows something it should not - Here anyway is javac behavior: Given: abstract class A implements ValueObject { int x; } on compile: X.java:1: error: The type A attempts to implement the mutually incompatible interfaces ValueObject and IdentityObject abstract class A implements ValueObject { ^ 1 error Srikanth ________________________________ From: valhalla-spec-observers on behalf of Dan Smith Sent: 12 February 2022 06:19 To: Frederic Parain Cc: valhalla-spec-experts Subject: Re: Abstract class with fields implementing ValueObject > On Feb 9, 2022, at 2:50 PM, Frederic Parain wrote: > > There's a weird case that seems to be allowed by the Value Objects JVMS draft: > > An abstract class can declare non-static fields, which means it won't > have the ACC_PERMITS_VALUE flag set, but also declare that it implements > the ValueObject interface. > > The combination looks just wrong, because no class can subclass such class: > - identity classes are not allowed because of the presence of > the ValueObject interface > - value classes are not allowed because of the absence of > ACC_PERMITS_VALUE > > I've looked for a rule that would prohibit such combination in the > JVMS draft but couldn't find one. > > Did I miss something? If it doesn't have ACC_PERMITS_VALUE set and it declares an method, the class implicitly implements IdentityObject (see 5.3.5). And then there's an immediate error, because of the IdentityObject/ValueObject clash. If it doesn't have ACC_PERMITS_VALUE set and it *doesn't* declare an method, it's impossible to instantiate. Then there's a technical question of whether an error occurs, but it's not really an interesting use case for programmers (and javac would never generate this). (Relevant discussion on this corner case from the spec changes draft: An abstract class implements IdentityObject if it declares an instance initialization method and does not have its ACC_PERMITS_VALUE flag set; and implements ValueObject if the opposite is true (ACC_PERMITS_VALUE, no instance initialization method). Instance initialization methods and ACC_PERMITS_VALUE represent two channels for subclass instance creation, and this analysis determines whether only one channel is "open". Alternatively, we could ignore instance initialization methods and rely entirely on ACC_PERMITS_VALUE. In practice, abstract classes written in the Java programming language always have instance initialization methods, so the difference in behavior is only relevant to classes produced via other languages or tools.) From daniel.smith at oracle.com Sun Feb 13 18:05:29 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Sun, 13 Feb 2022 18:05:29 +0000 Subject: Abstract class with fields implementing ValueObject In-Reply-To: References: <4b486a05-6206-30f3-a1a9-5cff64e627b4@oracle.com> <796ECFC9-BF22-4D01-8249-B4B31E61477D@oracle.com> Message-ID: <09D66160-8A2C-46E3-A5E0-95F3A64E2500@oracle.com> > On Feb 12, 2022, at 10:16 PM, Srikanth Adayapalam wrote: > > I understand Frederic is asking about whether the spec? inadvertently allows something it should not - Here anyway is javac behavior: > > Given: > > abstract class A implements ValueObject { > int x; > } > > on compile: > X.java:1: error: The type A attempts to implement the mutually incompatible interfaces ValueObject and IdentityObject > abstract class A implements ValueObject { > ^ > 1 error Yep, this is expected and consistent: javac sees the field and infers the superinterface IdentityObject (per the language rules), then detects the conflict between interfaces. A slightly more interesting variation: declare a simple interface Foo; change to 'A implements Foo'. This compiles fine, inferring A implements IdentityObject. Then separately compile Foo so that it extends ValueObject. No compilation error, but the JVM should detect the IdentityObject/ValueObject conflict when A is loaded. To generate the kind of class files Fred asked about, you'd need to use something other than javac. From frederic.parain at oracle.com Mon Feb 14 14:23:11 2022 From: frederic.parain at oracle.com (Frederic Parain) Date: Mon, 14 Feb 2022 09:23:11 -0500 Subject: Abstract class with fields implementing ValueObject In-Reply-To: <09D66160-8A2C-46E3-A5E0-95F3A64E2500@oracle.com> References: <4b486a05-6206-30f3-a1a9-5cff64e627b4@oracle.com> <796ECFC9-BF22-4D01-8249-B4B31E61477D@oracle.com> <09D66160-8A2C-46E3-A5E0-95F3A64E2500@oracle.com> Message-ID: <4c591a7c-8bfd-df43-f0f5-839dca95ecb1@oracle.com> On 2/13/22 1:05 PM, Dan Smith wrote: >> On Feb 12, 2022, at 10:16 PM, Srikanth Adayapalam wrote: >> >> I understand Frederic is asking about whether the spec? inadvertently allows something it should not - Here anyway is javac behavior: >> >> Given: >> >> abstract class A implements ValueObject { >> int x; >> } >> >> on compile: >> X.java:1: error: The type A attempts to implement the mutually incompatible interfaces ValueObject and IdentityObject >> abstract class A implements ValueObject { >> ^ >> 1 error > > Yep, this is expected and consistent: javac sees the field and infers the superinterface IdentityObject (per the language rules), then detects the conflict between interfaces. > > A slightly more interesting variation: declare a simple interface Foo; change to 'A implements Foo'. This compiles fine, inferring A implements IdentityObject. Then separately compile Foo so that it extends ValueObject. No compilation error, but the JVM should detect the IdentityObject/ValueObject conflict when A is loaded. > > To generate the kind of class files Fred asked about, you'd need to use something other than javac. That's not really the point. The JVM cannot rely on what javac generates or not, because it has to deal with other class files generators. We have to agree on the behavior of the VM based on what is possible in the class file, because, at the end, this is what must be implemented. The fact that such a class file is useless to the user is almost secondary. We just need to know if the VM should accept such class file. As long as it doesn't break VM invariants, we are fine accepting it. Fred From daniel.smith at oracle.com Mon Feb 14 17:19:56 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 14 Feb 2022 17:19:56 +0000 Subject: Abstract class with fields implementing ValueObject In-Reply-To: <4c591a7c-8bfd-df43-f0f5-839dca95ecb1@oracle.com> References: <4b486a05-6206-30f3-a1a9-5cff64e627b4@oracle.com> <796ECFC9-BF22-4D01-8249-B4B31E61477D@oracle.com> <09D66160-8A2C-46E3-A5E0-95F3A64E2500@oracle.com> <4c591a7c-8bfd-df43-f0f5-839dca95ecb1@oracle.com> Message-ID: <6C97999A-0B18-428D-85F1-A19BB9B4A4DF@oracle.com> > On Feb 14, 2022, at 7:23 AM, Frederic Parain wrote: > > > On 2/13/22 1:05 PM, Dan Smith wrote: >>> On Feb 12, 2022, at 10:16 PM, Srikanth Adayapalam wrote: >>> >>> I understand Frederic is asking about whether the spec? inadvertently allows something it should not - Here anyway is javac behavior: >>> >>> Given: >>> >>> abstract class A implements ValueObject { >>> int x; >>> } >>> >>> on compile: >>> X.java:1: error: The type A attempts to implement the mutually incompatible interfaces ValueObject and IdentityObject >>> abstract class A implements ValueObject { >>> ^ >>> 1 error >> Yep, this is expected and consistent: javac sees the field and infers the superinterface IdentityObject (per the language rules), then detects the conflict between interfaces. >> A slightly more interesting variation: declare a simple interface Foo; change to 'A implements Foo'. This compiles fine, inferring A implements IdentityObject. Then separately compile Foo so that it extends ValueObject. No compilation error, but the JVM should detect the IdentityObject/ValueObject conflict when A is loaded. >> To generate the kind of class files Fred asked about, you'd need to use something other than javac. > > > That's not really the point. The JVM cannot rely on what javac generates > or not, because it has to deal with other class files generators. > We have to agree on the behavior of the VM based on what is possible in > the class file, because, at the end, this is what must be implemented. > > The fact that such a class file is useless to the user is almost > secondary. We just need to know if the VM should accept such class file. > As long as it doesn't break VM invariants, we are fine accepting it. Yes, I agree of course. This is an area of the proposed spec where there's an acknowledged uncertainty about what the rules should say, and we'll need to make a final choice and make sure implementations are conforming to that choice. To resolve the uncertainty, though, we've got to have a design conversation about user expectations, potential impact, etc., and in that context it's relevant how often the class files in question are likely to be produced, how useful the class files in question might be, and so on. To review where we're at on JVMS: 1) A class file with ACC_PERMITS_VALUE set may implement, or not, ValueObject (directly or indirectly) 2) A class file without ACC_PERMITS_VALUE set, but that declares an method, implicitly implements IdentityObject; if it also implements ValueObject (directly or indirectly), that's an error 3) For a class file without ACC_PERMITS_VALUE set, and without an method: 3a) Current spec draft says it doesn't implement anything implicitly, and so can implement whatever it wants explicitly 3b) An alternative spec approach would say it implicitly implements IdentityObject, and so it's an error to also implement ValueObject My points about "javac doesn't generate it" and "there's precedent for uninstantiable class files" are to argue that (3a) is not a bug. So we can leave it to other factors to decide whether (3a) or (3b) is the right approach. From daniel.smith at oracle.com Tue Feb 22 21:16:54 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Tue, 22 Feb 2022 21:16:54 +0000 Subject: Evolving instance creation Message-ID: <1DB20A74-B22C-4EB0-AB70-0949A51C365E@oracle.com> One of the longstanding properties of class instance creation expressions ('new Foo()') is that the instance being produced is unique?that is, not '==' to any previously-created instance. Value classes will disrupt this invariant, because it's possible to "create" an instance of a value class that already exists: new Point(1, 2) == new Point(1, 2) // always true A related, possibly-overlapping new Java feature idea (not concretely proposed, but something the language might want in the future) is the declaration of canonical factory methods in a class, which intentionally *don't* promise unique instances (for example, they might implement interning). These factories would be like constructors in that they wouldn't have a unique method name, but otherwise would behave like ad hoc static factory methods?take some arguments, use them to create/locate an appropriate instance, return it. I want to focus here on the usage of class instance creation expressions, and how to approach changes to their semantics. This involves balancing the needs of programmers who depend on the unique instance invariant with those who don't care and would prefer fewer knobs/less complexity. Here are three approaches that I could imagine pursuing: (1) Value classes are a special case for 'new Foo()' This is the plan of record: the unique instance invariant continues to hold for 'new Foo()' where Foo is an identity class, but if Foo is a value class, you might get an existing instance. In bytecode, the translation of 'new Foo()' depends on the kind of class (as determined at compile time). Identity class creation continues to be implemented via 'new Foo; dup; invokespecial Foo.()V'. Value class creation occurs via 'invokestatic Foo.()LFoo;' (method name bikeshedding tk). There is no compatibility between the two (e.g., if an identity class becomes a value class). In a way, it shouldn't be surprising that a value class doesn't guarantee unique instances, because uniqueness is closely tied to identity. So special-casing 'new Foo()' isn't that different from special-casing Object.equals'?in the absence of identity, we'll do something reasonable, but not quite the same. Factories don't enter into this story at all. If we end up having unnamed factories in the future, they will be declared and invoked with a separate syntax, and will be declarable both by identity classes and value classes. (Value class factories don't seem particularly compelling, but they could, say, be used to smooth migration, like 'Integer.valueOf'.) Biggest concerns: for now, it can be surprising that 'new' doesn't always give you a unique instance. In a future with factories, navigating between the 'new' syntax and the factory invocation syntax may be burdensome, with style wars about which approach is better. (2) 'new Foo()' as a general-purpose creation tool In this approach, 'new Foo()' is the use-site syntax for *both* factory and constructor invocation. Factories and constructors live in the same overload resolution "namespace", and all will be considered by the use site. In bytecode, the preferred translation of 'new Foo()' is 'invokestatic Foo.()LFoo;'. Note that this is the case for both value classes *and identity classes*. For compatibility, 'new/dup/' also needs to be supported for now; eventually, it might be deprecated. Refactoring between constructors and factories is generally compatible. Because this re-interpretation of 'new Foo()' supports factories, there is no unique instance invariant. At best, particular classes can document that they produce unique instances, and clients who need this behavior should ensure they're working with classes that promise it. (It's not as simple as looking for a *current* factory, because constructors can be refactored to factories.) For developers who don't care about unique instances, this is the simplest approach: whenever you want an instance of Foo, you say 'new Foo()'. Biggest concerns: we've demoted an ironclad semantic guarantee to an optional property of some classes. For those developers/use cases who care about the unique instance invariant, that may be difficult, especially because we're undoing a longstanding property rather than designing it this way from the beginning. (3) 'new Foo()' for unique instances and just 'Foo()' otherwise Here, the 'new' keyword is reserved for cases in which a unique instance is guaranteed. For value class creation, factory invocation, and constructor invocation when unique instances don't matter, a bare 'Foo()' call is used instead. 'new Point()' would be an error?this syntax doesn't work with value classes. In bytecode, 'new Foo()' always compiles to 'new/dup/', while plain 'Foo()' typically compiles to 'invokestatic Foo.()LFoo;' (method name bikeshedding tk). For compatibility, plain 'Foo()' would support 'new/dup/' invocations as well, if that's all the class provides. Refactoring between constructors and factories is generally compatible for plain 'Foo()' use sites, but not 'new Foo()' use sites. The plain 'Foo()' would become the preferred style for general-purpose usage, while 'new Foo()' would (eventually, after a long migration period) signal an interest in the unique instance guarantee. Java code written with the updated style is a little lighter on "ceremony". Biggest concerns: a somewhat arbitrary shift in coding style for all programmers to learn, which at a minimum must be adopted when working with value classes. --- What are your thoughts about the significance of the unique instance invariant? Is it important enough to design instance creation syntax around it? Do either (2) or (3) above sound like a better destination than the plan of record? From ali.ebrahimi1781 at gmail.com Wed Feb 23 10:17:17 2022 From: ali.ebrahimi1781 at gmail.com (Ali Ebrahimi) Date: Wed, 23 Feb 2022 13:47:17 +0330 Subject: Evolving instance creation In-Reply-To: <1DB20A74-B22C-4EB0-AB70-0949A51C365E@oracle.com> References: <1DB20A74-B22C-4EB0-AB70-0949A51C365E@oracle.com> Message-ID: Hi, One more option: (4) 'new Foo()' for unique instances and just 'Foo{}' otherwise var Point = Point{ x:10 , y:10} The syntax borrowed from Object Literal expressions in javafx1 language. On Wed, Feb 23, 2022 at 12:47 AM Dan Smith wrote: > One of the longstanding properties of class instance creation expressions > ('new Foo()') is that the instance being produced is unique?that is, not > '==' to any previously-created instance. > > Value classes will disrupt this invariant, because it's possible to > "create" an instance of a value class that already exists: > > new Point(1, 2) == new Point(1, 2) // always true > > A related, possibly-overlapping new Java feature idea (not concretely > proposed, but something the language might want in the future) is the > declaration of canonical factory methods in a class, which intentionally > *don't* promise unique instances (for example, they might implement > interning). These factories would be like constructors in that they > wouldn't have a unique method name, but otherwise would behave like ad hoc > static factory methods?take some arguments, use them to create/locate an > appropriate instance, return it. > > I want to focus here on the usage of class instance creation expressions, > and how to approach changes to their semantics. This involves balancing the > needs of programmers who depend on the unique instance invariant with those > who don't care and would prefer fewer knobs/less complexity. > > Here are three approaches that I could imagine pursuing: > > (1) Value classes are a special case for 'new Foo()' > > This is the plan of record: the unique instance invariant continues to > hold for 'new Foo()' where Foo is an identity class, but if Foo is a value > class, you might get an existing instance. > > In bytecode, the translation of 'new Foo()' depends on the kind of class > (as determined at compile time). Identity class creation continues to be > implemented via 'new Foo; dup; invokespecial Foo.()V'. Value class > creation occurs via 'invokestatic Foo.()LFoo;' (method name > bikeshedding tk). There is no compatibility between the two (e.g., if an > identity class becomes a value class). > > In a way, it shouldn't be surprising that a value class doesn't guarantee > unique instances, because uniqueness is closely tied to identity. So > special-casing 'new Foo()' isn't that different from special-casing > Object.equals'?in the absence of identity, we'll do something reasonable, > but not quite the same. > > Factories don't enter into this story at all. If we end up having unnamed > factories in the future, they will be declared and invoked with a separate > syntax, and will be declarable both by identity classes and value classes. > (Value class factories don't seem particularly compelling, but they could, > say, be used to smooth migration, like 'Integer.valueOf'.) > > Biggest concerns: for now, it can be surprising that 'new' doesn't always > give you a unique instance. In a future with factories, navigating between > the 'new' syntax and the factory invocation syntax may be burdensome, with > style wars about which approach is better. > > (2) 'new Foo()' as a general-purpose creation tool > > In this approach, 'new Foo()' is the use-site syntax for *both* factory > and constructor invocation. Factories and constructors live in the same > overload resolution "namespace", and all will be considered by the use site. > > In bytecode, the preferred translation of 'new Foo()' is 'invokestatic > Foo.()LFoo;'. Note that this is the case for both value classes *and > identity classes*. For compatibility, 'new/dup/' also needs to be > supported for now; eventually, it might be deprecated. Refactoring between > constructors and factories is generally compatible. > > Because this re-interpretation of 'new Foo()' supports factories, there is > no unique instance invariant. At best, particular classes can document that > they produce unique instances, and clients who need this behavior should > ensure they're working with classes that promise it. (It's not as simple as > looking for a *current* factory, because constructors can be refactored to > factories.) > > For developers who don't care about unique instances, this is the simplest > approach: whenever you want an instance of Foo, you say 'new Foo()'. > > Biggest concerns: we've demoted an ironclad semantic guarantee to an > optional property of some classes. For those developers/use cases who care > about the unique instance invariant, that may be difficult, especially > because we're undoing a longstanding property rather than designing it this > way from the beginning. > > (3) 'new Foo()' for unique instances and just 'Foo()' otherwise > > Here, the 'new' keyword is reserved for cases in which a unique instance > is guaranteed. For value class creation, factory invocation, and > constructor invocation when unique instances don't matter, a bare 'Foo()' > call is used instead. 'new Point()' would be an error?this syntax doesn't > work with value classes. > > In bytecode, 'new Foo()' always compiles to 'new/dup/', while plain > 'Foo()' typically compiles to 'invokestatic Foo.()LFoo;' (method name > bikeshedding tk). For compatibility, plain 'Foo()' would support > 'new/dup/' invocations as well, if that's all the class provides. > Refactoring between constructors and factories is generally compatible for > plain 'Foo()' use sites, but not 'new Foo()' use sites. > > The plain 'Foo()' would become the preferred style for general-purpose > usage, while 'new Foo()' would (eventually, after a long migration period) > signal an interest in the unique instance guarantee. Java code written with > the updated style is a little lighter on "ceremony". > > Biggest concerns: a somewhat arbitrary shift in coding style for all > programmers to learn, which at a minimum must be adopted when working with > value classes. > > --- > > What are your thoughts about the significance of the unique instance > invariant? Is it important enough to design instance creation syntax around > it? Do either (2) or (3) above sound like a better destination than the > plan of record? -- Best Regards, Ali Ebrahimi From daniel.smith at oracle.com Wed Feb 23 15:58:11 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 23 Feb 2022 15:58:11 +0000 Subject: EG meeting, 2022-02-23 Message-ID: <77DDED2E-EBD4-4C49-902E-9A438E645574@oracle.com> EG Zoom meeting today at 5pm UTC (9am PDT, 12pm EDT). Potential topics: "Evolving instance creation": I discussed considerations for how we evolve class instance creation expressions in the language "Abstract class with fields implementing ValueObject": discussion about a bytecode validation scenario Left over from last time: - IdentityObject and ValueObject interfaces - Terminology From daniel.smith at oracle.com Wed Feb 23 18:35:41 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 23 Feb 2022 18:35:41 +0000 Subject: Abstract class with fields implementing ValueObject In-Reply-To: <6C97999A-0B18-428D-85F1-A19BB9B4A4DF@oracle.com> References: <4b486a05-6206-30f3-a1a9-5cff64e627b4@oracle.com> <796ECFC9-BF22-4D01-8249-B4B31E61477D@oracle.com> <09D66160-8A2C-46E3-A5E0-95F3A64E2500@oracle.com> <4c591a7c-8bfd-df43-f0f5-839dca95ecb1@oracle.com> <6C97999A-0B18-428D-85F1-A19BB9B4A4DF@oracle.com> Message-ID: <3191AB6E-D720-4ACF-96AB-43AF8A4AD72E@oracle.com> Fred suggested that we enumerate the whole space here. So, some cases to consider: { ACC_PERMITS_VALUE, not } { has an declaration, not } { implements IdentityObject, not } { implements ValueObject, not } "implements" here refers to both direct and indirect superinterfaces. I'll focus on the first two, which affect the inference of superinterfaces. (1) ACC_PERMITS_VALUE, declaration This is a class that is able to support both identity and value subclasses. It implements no extra interfaces, but can restrict its subclasses via 'implements IdentityObject' or 'implements ValueObject'. (2) ACC_PERMITS_VALUE, no declaration The JVM infers that this class implements ValueObject, if it doesn't already. If it also implements IdentityObject, an error occurs at class load time. (Design alternative: we could ignore the declarations and treat this like case (1). In that case, the class could implement IdentityObject or be extended by identity classes without error (as long as it doesn't also implement ValueObject). But those identity subclasses couldn't declare verification-compatible methods, just like subclasses of abstract classes that have no methods today.) (3) no ACC_PERMITS_VALUE, declaration The JVM infers that this class implements IdentityObject, if it doesn't already. If it also implements ValueObject, an error occurs at class load time. (4) no ACC_PERMITS_VALUE, no declaration This is a class that effectively supports *no* subclasses. We don't infer any superinterfaces, but it can choose to implement IdentityObject or ValueObject. A value class that extends this class will fail to load. If the class doesn't implement ValueObject, an identity class that extends this class could load, but couldn't declare verification-compatible methods, just like subclasses of abstract classes that have no methods today. (Design alternative: we could ignore the declarations and treat this like case (3). In that case, it would be an error for the class to implement ValueObject, because it also implicitly implements IdentityObject.) --- Spelling this out makes me feel like treating the presence of methods as an inference signal may be overreaching and overcomplicating things. Today, declaring an method, or not, has no direct impact on anything, other than the side-effect that you can't write verification-compatible methods in your subclasses. I like the parallel between "permits identity (via )" and "permits value (via flag)", but flags and methods aren't really parallel constructs; in cases (2) and (4), we still "permit" identity subclasses, even if they're pretty useless. (And it doesn't help that javac doesn't give you any way to create these -free classes, so in practice they certainly don't have parallel prevalence.) Pursuing the "design alternative" strategies would essentially collapse this down to two cases: (1) ACC_PERMITS_VALUE, no superinterfaces inferred, but various checks performed (e.g., no instance fields); and (3) no ACC_PERMITS_VALUE, IdentityObject is inferred, error if there's also an explicit 'implements ValueObject'. How do we feel about that? From forax at univ-mlv.fr Thu Feb 24 15:47:28 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 24 Feb 2022 16:47:28 +0100 (CET) Subject: Evolving instance creation In-Reply-To: References: <1DB20A74-B22C-4EB0-AB70-0949A51C365E@oracle.com> Message-ID: <307737346.8016863.1645717648532.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Dan Heidinga" > To: "daniel smith" > Cc: "valhalla-spec-experts" > Sent: Thursday, February 24, 2022 4:39:52 PM > Subject: Re: Evolving instance creation > Repeating what I said in the EG meeting: > > * "new" carries the mental model of allocating space. For identity > objects, that's on the heap. For values, that may just be stack space > / registers. But it indicates that some kind of allocation / demand > for new storage has occurred. > > * It's important that "new" returns a unique instance. That invariant > has existed since Java's inception and we should be careful about > breaking it. In the case of values, two identical values can't be > differentiated so I think we're safe to say they are unique but > indistinguishable as no user program can differentiate them. Yes, it's more about == being different than "new" being different. "new" always creates a new instance but in case of value types, == does not allow us see if the instance are different or not. > > The rest of this is more of a language design question than a VM one. > The `Foo()` (without a new) is a good starting point for a canonical > factory model. The challenge will be in expressing the difference > between the factory method and the constructor as they need to be > distinct items in the source (different invariants, different return > values, etc) > > --Dan R?mi > > On Tue, Feb 22, 2022 at 4:17 PM Dan Smith wrote: >> >> One of the longstanding properties of class instance creation expressions ('new >> Foo()') is that the instance being produced is unique?that is, not '==' to any >> previously-created instance. >> >> Value classes will disrupt this invariant, because it's possible to "create" an >> instance of a value class that already exists: >> >> new Point(1, 2) == new Point(1, 2) // always true >> >> A related, possibly-overlapping new Java feature idea (not concretely proposed, >> but something the language might want in the future) is the declaration of >> canonical factory methods in a class, which intentionally *don't* promise >> unique instances (for example, they might implement interning). These factories >> would be like constructors in that they wouldn't have a unique method name, but >> otherwise would behave like ad hoc static factory methods?take some arguments, >> use them to create/locate an appropriate instance, return it. >> >> I want to focus here on the usage of class instance creation expressions, and >> how to approach changes to their semantics. This involves balancing the needs >> of programmers who depend on the unique instance invariant with those who don't >> care and would prefer fewer knobs/less complexity. >> >> Here are three approaches that I could imagine pursuing: >> >> (1) Value classes are a special case for 'new Foo()' >> >> This is the plan of record: the unique instance invariant continues to hold for >> 'new Foo()' where Foo is an identity class, but if Foo is a value class, you >> might get an existing instance. >> >> In bytecode, the translation of 'new Foo()' depends on the kind of class (as >> determined at compile time). Identity class creation continues to be >> implemented via 'new Foo; dup; invokespecial Foo.()V'. Value class >> creation occurs via 'invokestatic Foo.()LFoo;' (method name >> bikeshedding tk). There is no compatibility between the two (e.g., if an >> identity class becomes a value class). >> >> In a way, it shouldn't be surprising that a value class doesn't guarantee unique >> instances, because uniqueness is closely tied to identity. So special-casing >> 'new Foo()' isn't that different from special-casing Object.equals'?in the >> absence of identity, we'll do something reasonable, but not quite the same. >> >> Factories don't enter into this story at all. If we end up having unnamed >> factories in the future, they will be declared and invoked with a separate >> syntax, and will be declarable both by identity classes and value classes. >> (Value class factories don't seem particularly compelling, but they could, say, >> be used to smooth migration, like 'Integer.valueOf'.) >> >> Biggest concerns: for now, it can be surprising that 'new' doesn't always give >> you a unique instance. In a future with factories, navigating between the 'new' >> syntax and the factory invocation syntax may be burdensome, with style wars >> about which approach is better. >> >> (2) 'new Foo()' as a general-purpose creation tool >> >> In this approach, 'new Foo()' is the use-site syntax for *both* factory and >> constructor invocation. Factories and constructors live in the same overload >> resolution "namespace", and all will be considered by the use site. >> >> In bytecode, the preferred translation of 'new Foo()' is 'invokestatic >> Foo.()LFoo;'. Note that this is the case for both value classes *and >> identity classes*. For compatibility, 'new/dup/' also needs to be >> supported for now; eventually, it might be deprecated. Refactoring between >> constructors and factories is generally compatible. >> >> Because this re-interpretation of 'new Foo()' supports factories, there is no >> unique instance invariant. At best, particular classes can document that they >> produce unique instances, and clients who need this behavior should ensure >> they're working with classes that promise it. (It's not as simple as looking >> for a *current* factory, because constructors can be refactored to factories.) >> >> For developers who don't care about unique instances, this is the simplest >> approach: whenever you want an instance of Foo, you say 'new Foo()'. >> >> Biggest concerns: we've demoted an ironclad semantic guarantee to an optional >> property of some classes. For those developers/use cases who care about the >> unique instance invariant, that may be difficult, especially because we're >> undoing a longstanding property rather than designing it this way from the >> beginning. >> >> (3) 'new Foo()' for unique instances and just 'Foo()' otherwise >> >> Here, the 'new' keyword is reserved for cases in which a unique instance is >> guaranteed. For value class creation, factory invocation, and constructor >> invocation when unique instances don't matter, a bare 'Foo()' call is used >> instead. 'new Point()' would be an error?this syntax doesn't work with value >> classes. >> >> In bytecode, 'new Foo()' always compiles to 'new/dup/', while plain >> 'Foo()' typically compiles to 'invokestatic Foo.()LFoo;' (method name >> bikeshedding tk). For compatibility, plain 'Foo()' would support >> 'new/dup/' invocations as well, if that's all the class provides. >> Refactoring between constructors and factories is generally compatible for >> plain 'Foo()' use sites, but not 'new Foo()' use sites. >> >> The plain 'Foo()' would become the preferred style for general-purpose usage, >> while 'new Foo()' would (eventually, after a long migration period) signal an >> interest in the unique instance guarantee. Java code written with the updated >> style is a little lighter on "ceremony". >> >> Biggest concerns: a somewhat arbitrary shift in coding style for all programmers >> to learn, which at a minimum must be adopted when working with value classes. >> >> --- >> >> What are your thoughts about the significance of the unique instance invariant? >> Is it important enough to design instance creation syntax around it? Do either > > (2) or (3) above sound like a better destination than the plan of record? From daniel.smith at oracle.com Thu Feb 24 23:58:35 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 24 Feb 2022 23:58:35 +0000 Subject: Abstract class with fields implementing ValueObject In-Reply-To: References: <4b486a05-6206-30f3-a1a9-5cff64e627b4@oracle.com> <796ECFC9-BF22-4D01-8249-B4B31E61477D@oracle.com> <09D66160-8A2C-46E3-A5E0-95F3A64E2500@oracle.com> <4c591a7c-8bfd-df43-f0f5-839dca95ecb1@oracle.com> <6C97999A-0B18-428D-85F1-A19BB9B4A4DF@oracle.com> <3191AB6E-D720-4ACF-96AB-43AF8A4AD72E@oracle.com> Message-ID: TLDR: I'm convinced, let's revise our approach so that the JVM never infers interfaces for abstract classes. On Feb 24, 2022, at 8:57 AM, Dan Heidinga > wrote: Whether they can be instantiated is a decision better left to other parts of the spec (in this case, I believe verification will succeed and resolution of the `super()` call will fail). Right, my mistake. Verifier doesn't care what methods are declared in the superclass, but resolution of the invokespecial will fail. (3) no ACC_PERMITS_VALUE, declaration The JVM infers that this class implements IdentityObject, if it doesn't already. If it also implements ValueObject, an error occurs at class load time. I think this should be driven purely by the presence of the ACC_PERMITS_VALUE flag and the VM shouldn't be looking at the methods. Sounds like the consensus, agreed. The JVM shouldn't infer either IdentityObject or ValueObject for this abstract class - any inference decision should be delayed to the subclasses that extend this abstract class. My initial reaction was that, no, we really do want IdentityObject here, because it's useful to be able to assign an abstract class type to IdentityObject. But: for new classes, the compiler will have an opportunity to be explicit. It's mostly a question of how we handle legacy classes. And there, it would actually be bad to infer IdentityObject, when in most cases the class will get permits_value when it is recompiled. Probably best to avoid a scenario like: - Compile against legacy API, assign library.AbstractBanana to IdentityObject in your code - Upgrade to newer version of the API, assignment from library.AbstractBanana to IdentityObject is an error So, okay, let's say we limit JVM inference to concrete classes. And javac will infer/generate 'implements IdentityObject' if it decides an abstract class can't be permits_value. What about separate compilation? javac's behavior might be something like: 1) look for fields, 'synchronized', etc. in the class declaration, and if any are present, add 'implements IdentityObject' (if it's not already there); 2) if the superclass is permits_value and this class doesn't extend IdentityObject (directly or indirectly), set permits_value. (1) is a local decision, while (2) depends on multiple classes, so can be disrupted by separate compilation. But, thinking through the scenarios here... I'm pretty comfortable saying that an abstract class that is neither permits_value nor a subclass of IdentityObject is in an unstable state, and, like the legacy case, it's probably better if programmers *don't* write code assuming they can assign to IdentityObject. From daniel.smith at oracle.com Fri Feb 25 01:15:20 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 25 Feb 2022 01:15:20 +0000 Subject: Evolving instance creation In-Reply-To: <307737346.8016863.1645717648532.JavaMail.zimbra@u-pem.fr> References: <1DB20A74-B22C-4EB0-AB70-0949A51C365E@oracle.com> <307737346.8016863.1645717648532.JavaMail.zimbra@u-pem.fr> Message-ID: > On Feb 24, 2022, at 8:47 AM, Remi Forax wrote: > > ----- Original Message ----- >> From: "Dan Heidinga" >> To: "daniel smith" >> Cc: "valhalla-spec-experts" >> Sent: Thursday, February 24, 2022 4:39:52 PM >> Subject: Re: Evolving instance creation > >> Repeating what I said in the EG meeting: >> >> * "new" carries the mental model of allocating space. For identity >> objects, that's on the heap. For values, that may just be stack space >> / registers. But it indicates that some kind of allocation / demand >> for new storage has occurred. >> >> * It's important that "new" returns a unique instance. That invariant >> has existed since Java's inception and we should be careful about >> breaking it. In the case of values, two identical values can't be >> differentiated so I think we're safe to say they are unique but >> indistinguishable as no user program can differentiate them. > > Yes, it's more about == being different than "new" being different. > > "new" always creates a new instance but in case of value types, == does not allow us see if the instance are different or not. I'm not sure this is a good way to think value creation, though. It suggests that there still *is* an identity there (i.e., the new value has been newly allocated), you just can't see it. I'd rather have programmers think in these terms: when you instantiate a value class, you might get an object that already exists. Whether there are copies of that object at different memory locations or not is irrelevant?it's still *the same object*. From brian.goetz at oracle.com Fri Feb 25 01:34:55 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 24 Feb 2022 20:34:55 -0500 Subject: Evolving instance creation In-Reply-To: References: <1DB20A74-B22C-4EB0-AB70-0949A51C365E@oracle.com> <307737346.8016863.1645717648532.JavaMail.zimbra@u-pem.fr> Message-ID: <277d59cb-9977-071e-6076-61cfbed15c50@oracle.com> I find DanH's way of presenting it more natural (and makes perfect sense now that its been said that way): it *is* allocating something, just not in the heap.? It is requesting new storage for a new object, which might be in the heap, or the stack, or registers. And we might find that new object to be == to an old object, but we're still requesting that space for a new object be allocated. >> "new" always creates a new instance but in case of value types, == does not allow us see if the instance are different or not. > I'm not sure this is a good way to think value creation, though. It suggests that there still *is* an identity there (i.e., the new value has been newly allocated), you just can't see it. > > I'd rather have programmers think in these terms: when you instantiate a value class, you might get an object that already exists. Whether there are copies of that object at different memory locations or not is irrelevant?it's still *the same object*.