From john.r.rose at oracle.com Sat Aug 1 02:47:16 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 31 Jul 2020 19:47:16 -0700 Subject: no good default issue In-Reply-To: <1439bf36-bf30-a561-9d70-8074f12dbbe5@oracle.com> References: <1439bf36-bf30-a561-9d70-8074f12dbbe5@oracle.com> Message-ID: On Jul 31, 2020, at 12:41 PM, Brian Goetz wrote: > > As far as I can tell what you're suggesting, it is that, when we detect a field is not initialized, we initialize it for you with some sort of default. But that brings us back to the main problem: what if the class _has no good default_? With what do we initialize it? I think he was going back to the old idea of an opt-in default value, which is then ?stamped? all over arrays and fields. A very natural notation for this would be a no-arg constructor. In this world, the vdefault bytecode would be privileged (usable only inside the same capsule, to bootstrap value creation). Where we have public uses of vdefault today, we would instead have an API point, a call to the no-arg constructor factory method. The no-arg constructor would (presumably) be run just once, the first time needed, and the value stored somewhere. The JVM would want to special-case this somehow. Perhaps the API point would surface as a well-known public-static-final? The rest of the VM would do some tricks Dan is suggesting, to ensure that non-private uses of the type would always refer to the public default value. I think it would still be desirable for the class itself to work with ?dangerous? all-zero instances (after all, it?s the class?s business to define exactly how dangerous they are), so that for example array creation inside the class might be faster than array creation outside the class. Personally, I view such tactics as possible but expensive, and would like to try to get by without JVM support for them, to start with. The JVM engineering teams are already overworked for Valhalla. ? John From brian.goetz at oracle.com Sat Aug 1 14:41:11 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 1 Aug 2020 10:41:11 -0400 Subject: no good default issue In-Reply-To: References: <1439bf36-bf30-a561-9d70-8074f12dbbe5@oracle.com> Message-ID: <19153034-0947-158b-aebe-48abcb471960@oracle.com> > Personally, I view such tactics as possible but expensive, > and would like to try to get by without JVM support for > them, to start with. The JVM engineering teams are already > overworked for Valhalla. > And, more than that.? The technique of "ask a no-arg ctor" (or some other stateless source) only works for the classes in Bucket 2, which, as Dan and Kevin have pointed out, are actually the smaller and far less interesting group than Bucket 3, where there is simply no good default, and anything returned by the no-arg ctor would just be a different flavor of ad-hoc null. One example of such a class is: ??? inline record Name(String first, String last) { } What would be the default?? Two empty strings?? Strings containing ""?? Are those really better than (null, null)? Kevin has argued that Date equally belongs in this bucket.? Sure, we can interpret 1970 as the default date, but in the situations where someone uses an uninitialized date, what percentage of the time are they satisfied with 1970 as the result?? I don't think it's going out on a limb to say "not that often."? And if the date is used as an input to, say, an interest calculation, that's pretty bad. The lesson of the most recent discussion is that "let the user pick the default" is not really better, and often worse, than "let zero be the default."? Really, this is not about defaults, but about detecting use of uninitialized data. Refs don't have this problem, because when you try to do anything nontrivial with a null (other than move it around or compare it to another ref), the VM tells you clearly that you tried to use uninitialized data. By the NNN Directive, winning looks like, to the extent we do anything here, aligning the behavior of dereferencing an uninitialized NGD value with that of dereferencing a null. From forax at univ-mlv.fr Sun Aug 2 12:02:35 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 2 Aug 2020 14:02:35 +0200 (CEST) Subject: no good default issue In-Reply-To: <19153034-0947-158b-aebe-48abcb471960@oracle.com> References: <1439bf36-bf30-a561-9d70-8074f12dbbe5@oracle.com> <19153034-0947-158b-aebe-48abcb471960@oracle.com> Message-ID: <1231130254.554524.1596369755510.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "John Rose" > Cc: "valhalla-spec-experts" > Envoy?: Samedi 1 Ao?t 2020 16:41:11 > Objet: Re: no good default issue >> Personally, I view such tactics as possible but expensive, >> and would like to try to get by without JVM support for >> them, to start with. The JVM engineering teams are already >> overworked for Valhalla. >> > > And, more than that.? The technique of "ask a no-arg ctor" (or some > other stateless source) only works for the classes in Bucket 2, which, > as Dan and Kevin have pointed out, are actually the smaller and far less > interesting group than Bucket 3, where there is simply no good default, > and anything returned by the no-arg ctor would just be a different > flavor of ad-hoc null. > > One example of such a class is: > > ??? inline record Name(String first, String last) { } > > What would be the default?? Two empty strings?? Strings containing > ""?? Are those really better than (null, null)? > > Kevin has argued that Date equally belongs in this bucket.? Sure, we can > interpret 1970 as the default date, but in the situations where someone > uses an uninitialized date, what percentage of the time are they > satisfied with 1970 as the result?? I don't think it's going out on a > limb to say "not that often."? And if the date is used as an input to, > say, an interest calculation, that's pretty bad. > > The lesson of the most recent discussion is that "let the user pick the > default" is not really better, and often worse, than "let zero be the > default."? Really, this is not about defaults, but about detecting use > of uninitialized data. It depends if throwing an exception in the default constructor is allowed or not ? i.e. if an exception is a valid state for a default value, like an exception is a valid value when evaluating a condy/ldc. > > Refs don't have this problem, because when you try to do anything > nontrivial with a null (other than move it around or compare it to > another ref), the VM tells you clearly that you tried to use > uninitialized data. > > By the NNN Directive, winning looks like, to the extent we do anything > here, aligning the behavior of dereferencing an uninitialized NGD value > with that of dereferencing a null. R?mi From peter.levart at gmail.com Sun Aug 2 16:08:51 2020 From: peter.levart at gmail.com (Peter Levart) Date: Sun, 2 Aug 2020 18:08:51 +0200 Subject: Revisiting default values In-Reply-To: References: <3D227EB1-4D86-4F97-BFCB-A5949C63A717@oracle.com> <614D2739-4756-4D61-BF45-BA36490AACB3@oracle.com> Message-ID: <7d9a30a8-5dd5-b950-2603-f2b2e1b59983@gmail.com> Hi, On 7/28/20 9:06 PM, Brian Goetz wrote: > I think what we need for Bucket 3 (which I think we agree is more > important than Bucket 2) is to (optionally, only for NGD inline > classes) restore parity with reference types by ensuring that the > receiver of a method invocation is never seen to be the default > value.? (We already do this for reference types; we NPE before the > dispatch would succeed.)?? And the strategies we've been kicking > around have ranged from "try to prevent the default from showing up in > the heap" to "detect when the default shows at various times." > > If the important point in time is method dispatch, then we can > probably simplify to: > > ?- Let some classes mark themselves as NGD (no good default) > ?- At the point of invocation of an NGD instance method, check the > receiver against the default, throw NPE if it is > ?- Optionally, try to optimize this check by identifying (manually or > automatically) a pivot field > > Note that even an unoptimized check is probably pretty fast already: > "are all the bits zero."? But we can probably often optimize down to a > single-word comparison to zero. > > Note too that we can implement this check in either generated bytecode > or in the VM; the semantics are the same, the latter is more secure. I can understand that automatic runtime prevention of invoking instance methods with default (all zero) object is important for fail-fast behavior. It is almost like invoking methods with identity typed parameters where null values are not valid parameters. We use Objects.requireNonNull() to check for such parameters at the beginning of such methods. So NGD classes could be designed such that they encapsulate all fields and explicitly check for absence of all-zero "this" value at the beginning of methods. People want to simplify such tedious repetitive coding so they make frameworks that turn @NonNull annotations on method parameters into non-null checks at the top of the method. I can imagine a javac plugin could insert checks in all (non-private only?) instance methods when an inline class is marked with @NGD for example. Or this could be baked into Java language. In either case I think it is a matter of the inline class bytecode and not the code doing invocation (the call site). So it is safe by itself. Or am I missing something? Regards, Peter From daniel.smith at oracle.com Mon Aug 3 20:21:14 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 3 Aug 2020 14:21:14 -0600 Subject: Revisiting default values In-Reply-To: <7d9a30a8-5dd5-b950-2603-f2b2e1b59983@gmail.com> References: <3D227EB1-4D86-4F97-BFCB-A5949C63A717@oracle.com> <614D2739-4756-4D61-BF45-BA36490AACB3@oracle.com> <7d9a30a8-5dd5-b950-2603-f2b2e1b59983@gmail.com> Message-ID: <613A220A-E279-4348-BAB7-832974574A5C@oracle.com> > On Aug 2, 2020, at 10:08 AM, Peter Levart wrote: > > In either case I think it is a matter of the inline class bytecode and not the code doing invocation (the call site). So it is safe by itself. Or am I missing something? You're describing Option F. Yes, we can have javac generate checks in the bytecode of inline class method bodies. Some awkwardness remains whenever default methods or Object methods are invoked. It would be difficult and expensive to implement any checks in these method bodies; and while bridge methods generated in the inline class's class file help, they don't guard against new methods declared after compilation (the motivating use case for the default methods feature). So we're left with one of: - Permit superclass/superinterface code to run, only throwing (or at least only guaranteeing a throw) when one of the declared instance methods of the class are invoked; or - Option G: implement the checks in the JVM, where we can see the entire set of inherited member methods as the inline class is loaded/linked From peter.levart at gmail.com Wed Aug 5 08:05:06 2020 From: peter.levart at gmail.com (Peter Levart) Date: Wed, 5 Aug 2020 10:05:06 +0200 Subject: Revisiting default values In-Reply-To: <613A220A-E279-4348-BAB7-832974574A5C@oracle.com> References: <3D227EB1-4D86-4F97-BFCB-A5949C63A717@oracle.com> <614D2739-4756-4D61-BF45-BA36490AACB3@oracle.com> <7d9a30a8-5dd5-b950-2603-f2b2e1b59983@gmail.com> <613A220A-E279-4348-BAB7-832974574A5C@oracle.com> Message-ID: On 8/3/20 10:21 PM, Dan Smith wrote: >> On Aug 2, 2020, at 10:08 AM, Peter Levart wrote: >> >> In either case I think it is a matter of the inline class bytecode and not the code doing invocation (the call site). So it is safe by itself. Or am I missing something? > You're describing Option F. Yes, we can have javac generate checks in the bytecode of inline class method bodies. > > Some awkwardness remains whenever default methods or Object methods are invoked. It would be difficult and expensive to implement any checks in these method bodies; and while bridge methods generated in the inline class's class file help, they don't guard against new methods declared after compilation (the motivating use case for the default methods feature). So we're left with one of: > > - Permit superclass/superinterface code to run, only throwing (or at least only guaranteeing a throw) when one of the declared instance methods of the class are invoked; or That would not be so bad, I think. Why? The Object methods that require access to instance state (equals, hashCode) would be implemented by inline class and would contain the checks. Other Object methods are mostly not allowed for inline classes anyway. So this leaves us with default methods of interfaces implemented by inline class. These are of two kinds: either they are just functions that don't deal with the instance state at all and would be better off as static methods anyway, or they deal with instance state in which case they must invoke at least one of inline class declared methods and the checks will be triggered. So I would say that anything important that accesses instance state is guarded by instrumentation of inline class methods. But what about accessing fields directly? Even if fields are encapsulated (private), they can be accessed by code of the inline class itself or a nestmate when an instance of the inline class is not "this". In that case, I think the same strategy as for null checking of identity types is in order: an equivalent of Objects.requireNonNull for inline types. Forgetting to issue those checks manually can have a surprising effect though. > > - Option G: implement the checks in the JVM, where we can see the entire set of inherited member methods as the inline class is loaded/linked > Yeah, this could also work for field accesses then. A hybrid of call-site (or field-access-site) checks and checks embedded in the instance methods is also possible. Javac could emit a check before accessing a field from code where embedded check is not performed (where instance is not "this") and before invoking default interface method (as determined by static type). This would then cover all places and still be secure since for security, the checks embedded in the instance methods can not be bypassed. Peter From peter.levart at gmail.com Wed Aug 5 08:11:45 2020 From: peter.levart at gmail.com (Peter Levart) Date: Wed, 5 Aug 2020 10:11:45 +0200 Subject: Revisiting default values In-Reply-To: References: <3D227EB1-4D86-4F97-BFCB-A5949C63A717@oracle.com> <614D2739-4756-4D61-BF45-BA36490AACB3@oracle.com> <7d9a30a8-5dd5-b950-2603-f2b2e1b59983@gmail.com> <613A220A-E279-4348-BAB7-832974574A5C@oracle.com> Message-ID: <81270900-468c-f80f-dea4-2c4acb77619d@gmail.com> On 8/5/20 10:05 AM, Peter Levart wrote: > Javac could emit a check before accessing a field from code where > embedded check is not performed (where instance is not "this") and > before invoking default interface method (as determined by static type) ...hm, javac could not do that, right? It does not know if the instance is of inline class at that time and of which inline class. This is only possible during runtime... But at least for field accesses this could work. Peter From daniel.smith at oracle.com Wed Aug 5 22:54:46 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 5 Aug 2020 16:54:46 -0600 Subject: Revisiting default values In-Reply-To: <81270900-468c-f80f-dea4-2c4acb77619d@gmail.com> References: <3D227EB1-4D86-4F97-BFCB-A5949C63A717@oracle.com> <614D2739-4756-4D61-BF45-BA36490AACB3@oracle.com> <7d9a30a8-5dd5-b950-2603-f2b2e1b59983@gmail.com> <613A220A-E279-4348-BAB7-832974574A5C@oracle.com> <81270900-468c-f80f-dea4-2c4acb77619d@gmail.com> Message-ID: <664CD940-901A-4F5B-B498-A231689346AA@oracle.com> > On Aug 5, 2020, at 2:11 AM, Peter Levart wrote: > > > On 8/5/20 10:05 AM, Peter Levart wrote: >> Javac could emit a check before accessing a field from code where embedded check is not performed (where instance is not "this") and before invoking default interface method (as determined by static type) > > ...hm, javac could not do that, right? It does not know if the instance is of inline class at that time and of which inline class. This is only possible during runtime... > > But at least for field accesses this could work. Correct. In theory, for method invocations, javac could do something reflective for *every* invocation involving Object or an interface. Something like: Runnable obj = ...; if (obj.getClass().isInline() && obj.getClass().defaultValue() == obj) throw new InvalidDefaultInstanceException(); obj.run(); But of course that's a huge burden on every Java program in the world, without regard to whether they expect to encounter Bucket #3 inline classes. It's a similar story for Option H?compiler checks on array reads from Object[], Runnable[], etc. From daniel.smith at oracle.com Tue Aug 11 20:22:56 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Tue, 11 Aug 2020 14:22:56 -0600 Subject: EG meeting *canceled*, 2020-08-12 Message-ID: <3A566E16-82DC-4FFC-91CA-B91781AC4D6A@oracle.com> There has been no significant new activity on the mailing list, so let's cancel tomorrow's Zoom meeting. If you have new topics for discussion, please send an email and we'll get to it next time (Aug 26). From daniel.smith at oracle.com Fri Aug 14 00:00:22 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 13 Aug 2020 18:00:22 -0600 Subject: JEP draft: Identity Warnings for Inline Class Candidates In-Reply-To: References: Message-ID: <2131ABD6-85AE-42FB-8693-8BAEAFD58C20@oracle.com> Updated. I've come around to the idea that what we really want is an annotation, and that the annotation we really want already exists as an informal constraint in the form of "value-based class". So now I'm proposing that we introduce a @ValueBased annotation, apply it to some classes, and key our warnings off of that. https://bugs.openjdk.java.net/browse/JDK-8249100 > On Jul 8, 2020, at 4:08 PM, Dan Smith wrote: > > Here's an initial JEP draft for the "Identity Warnings for Inline Class Candidates" feature, which I'm hoping we can target to 16. > > https://bugs.openjdk.java.net/browse/JDK-8249100 > > Feedback is welcome. From daniel.smith at oracle.com Fri Aug 14 00:06:14 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 13 Aug 2020 18:06:14 -0600 Subject: JEP draft: Identity Warnings for Inline Class Candidates In-Reply-To: <6bbf0263-4310-d720-0c92-33365deddf2a@oracle.com> References: <6bbf0263-4310-d720-0c92-33365deddf2a@oracle.com> Message-ID: <6B06133D-F035-4DFE-90F8-6A6373DF9A3D@oracle.com> > On Jul 20, 2020, at 9:38 AM, Brian Goetz wrote: > > In the motivation, I would add examples like: > > void foo(Object o) { > synchronized(o) { ... } > } > > void foo(T t) { /* same */ } > > to make it clear what we're talking about. The above code is valid today -- though may be semantically questionable, and doubly so when `o` is an `Integer`. Tomorrow, when someone passes an inline object, it will IMSE. These are the places where we want to detect when people are using questionable identities as locks. I added an example. But are you proposing this as a *compile-time* warning? What I had in mind is compiler warnings we can prove that an object is value-based, and runtime warnings when we can't prove it statically. I think compile-time warnings on every Object synchronization are going to generate a lot of noise. > I would also remind users what the _benefits_ of migrating, say, `Duration` to inline classes, to head off the inevitable "why are you guys always changing stuff that makes more work for me" objections. I say "there are significant benefits" in the "alternatives" section, but don't bother to explain what those are. :-) I think maybe "those are the rules of a @ValueBased class, sorry" is more persuasive now that I've framed it in that way ("your beef is with @ValueBased, not this JEP"). But I suppose the bottom line is that there will be new warnings due to this JEP, and programmers will come here looking for answers. I'll think about what to say. From brian.goetz at oracle.com Fri Aug 14 14:32:28 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 14 Aug 2020 10:32:28 -0400 Subject: JEP draft: Identity Warnings for Inline Class Candidates In-Reply-To: <6B06133D-F035-4DFE-90F8-6A6373DF9A3D@oracle.com> References: <6bbf0263-4310-d720-0c92-33365deddf2a@oracle.com> <6B06133D-F035-4DFE-90F8-6A6373DF9A3D@oracle.com> Message-ID: <978EADC1-39B1-486F-B2F6-0B1A681D7123@oracle.com> No, such a warning would be too noisy, as you suggest. My example was meant to help educate readers that locutions like this are why we _need_ runtime checking, because this is the common case by which Integer gets locked on. > On Aug 13, 2020, at 8:06 PM, Dan Smith wrote: > > >> On Jul 20, 2020, at 9:38 AM, Brian Goetz wrote: >> >> In the motivation, I would add examples like: >> >> void foo(Object o) { >> synchronized(o) { ... } >> } >> >> void foo(T t) { /* same */ } >> >> to make it clear what we're talking about. The above code is valid today -- though may be semantically questionable, and doubly so when `o` is an `Integer`. Tomorrow, when someone passes an inline object, it will IMSE. These are the places where we want to detect when people are using questionable identities as locks. > > I added an example. But are you proposing this as a *compile-time* warning? What I had in mind is compiler warnings we can prove that an object is value-based, and runtime warnings when we can't prove it statically. I think compile-time warnings on every Object synchronization are going to generate a lot of noise. > >> I would also remind users what the _benefits_ of migrating, say, `Duration` to inline classes, to head off the inevitable "why are you guys always changing stuff that makes more work for me" objections. > > I say "there are significant benefits" in the "alternatives" section, but don't bother to explain what those are. :-) I think maybe "those are the rules of a @ValueBased class, sorry" is more persuasive now that I've framed it in that way ("your beef is with @ValueBased, not this JEP"). But I suppose the bottom line is that there will be new warnings due to this JEP, and programmers will come here looking for answers. I'll think about what to say. > From daniel.smith at oracle.com Fri Aug 14 17:49:43 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 14 Aug 2020 11:49:43 -0600 Subject: JEP draft: Identity Warnings for Inline Class Candidates In-Reply-To: <6B06133D-F035-4DFE-90F8-6A6373DF9A3D@oracle.com> References: <6bbf0263-4310-d720-0c92-33365deddf2a@oracle.com> <6B06133D-F035-4DFE-90F8-6A6373DF9A3D@oracle.com> Message-ID: <2653FFED-E108-4C50-BC9B-DEAEBCCB3DBB@oracle.com> > On Aug 13, 2020, at 6:06 PM, Dan Smith wrote: > >> I would also remind users what the _benefits_ of migrating, say, `Duration` to inline classes, to head off the inevitable "why are you guys always changing stuff that makes more work for me" objections. > > I say "there are significant benefits" in the "alternatives" section, but don't bother to explain what those are. :-) I think maybe "those are the rules of a @ValueBased class, sorry" is more persuasive now that I've framed it in that way ("your beef is with @ValueBased, not this JEP"). But I suppose the bottom line is that there will be new warnings due to this JEP, and programmers will come here looking for answers. I'll think about what to say. Updated with a couple sentences about the benefits of eventually migrating to be inline. From daniel.smith at oracle.com Mon Aug 17 23:44:57 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 17 Aug 2020 17:44:57 -0600 Subject: IdentityObject & abstract superclasses Message-ID: There's an interesting interaction between IdentityObject and abstract superclasses of inline classes that might be worth leaning into. --- The "status quo" (inasmuch as one exists): An inline class can extend a class if it, and all of its supers, 1) are abstract, 2) declare no instance fields, and 3) have "empty" methods. These properties represent a new kind of abstract class?call it a "light" abstract class. Changing a "light" abstract class to be "heavy" is a binary incompatible change. Separately, we have the IdentityObject interface, which is implicitly attached to all non-inline concrete classes. An abstract class might also be able to implement IdentityObject explicitly, and doing so would also disqualify it from being an inline class super. A struggle in this story is getting programmers to care about whether their classes are "heavy" or "light", since even though it's an important property, it's easy to overlook (there's no syntax for it, and in many cases, there are no immediate effects). --- Alternative story: An inline class must not extend IdentityObject through any of its superclasses. (That's it.) A non-inline class implicitly implements IdentityObject if it 1) is concrete, 2) declares an instance field, or 3) has a non-empty method. Abstract classes can also explicitly implement IdentityObject. Changing a class so that it implements IdentityObject is a binary incompatible change. Now we have a highly-visible concept (IdentityObject) that programmers should generally be aware of anyway, and they should readily understand a difference between abstract classes that implement IdentityObject and those that don't. --- I think I like the alternative story. It feels simpler. One reason to avoid it is if we think there's potentially value in a "light" abstract class concept that is independent of IdentityObject. For example, maybe some other feature could build on the idea of a superclass that requires no initialization, without tying that to the topic of object identity. I'm having trouble envisioning a use case, though. Another reason to avoid it is if we want IdentityObject to be limited to concrete classes?no explicit implementing it allowed. If the alternative story is the one we want, it implies that the "empty " JVM feature should be part of Inline Classes, not a separate thing we deliver earlier?because it's directly tied to IdentityObject. From brian.goetz at oracle.com Tue Aug 18 00:48:16 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 17 Aug 2020 20:48:16 -0400 Subject: IdentityObject & abstract superclasses In-Reply-To: References: Message-ID: <93d56149-c5af-1a71-9dcd-fc484cce936c@oracle.com> I agree this seems a nicer story.? Having fields or construction logic is as reasonable a trigger as being concrete in terms of stapling on the identity-required constraint. On 8/17/2020 7:44 PM, Dan Smith wrote: > There's an interesting interaction between IdentityObject and abstract superclasses of inline classes that might be worth leaning into. > > --- > > The "status quo" (inasmuch as one exists): > > An inline class can extend a class if it, and all of its supers, 1) are abstract, 2) declare no instance fields, and 3) have "empty" methods. These properties represent a new kind of abstract class?call it a "light" abstract class. Changing a "light" abstract class to be "heavy" is a binary incompatible change. > > Separately, we have the IdentityObject interface, which is implicitly attached to all non-inline concrete classes. An abstract class might also be able to implement IdentityObject explicitly, and doing so would also disqualify it from being an inline class super. > > A struggle in this story is getting programmers to care about whether their classes are "heavy" or "light", since even though it's an important property, it's easy to overlook (there's no syntax for it, and in many cases, there are no immediate effects). > > --- > > Alternative story: > > An inline class must not extend IdentityObject through any of its superclasses. (That's it.) > > A non-inline class implicitly implements IdentityObject if it 1) is concrete, 2) declares an instance field, or 3) has a non-empty method. Abstract classes can also explicitly implement IdentityObject. > > Changing a class so that it implements IdentityObject is a binary incompatible change. > > Now we have a highly-visible concept (IdentityObject) that programmers should generally be aware of anyway, and they should readily understand a difference between abstract classes that implement IdentityObject and those that don't. > > --- > > I think I like the alternative story. It feels simpler. > > One reason to avoid it is if we think there's potentially value in a "light" abstract class concept that is independent of IdentityObject. For example, maybe some other feature could build on the idea of a superclass that requires no initialization, without tying that to the topic of object identity. I'm having trouble envisioning a use case, though. Another reason to avoid it is if we want IdentityObject to be limited to concrete classes?no explicit implementing it allowed. > > If the alternative story is the one we want, it implies that the "empty " JVM feature should be part of Inline Classes, not a separate thing we deliver earlier?because it's directly tied to IdentityObject. > From daniel.smith at oracle.com Thu Aug 20 22:07:00 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 20 Aug 2020 16:07:00 -0600 Subject: IdentityObject & abstract superclasses In-Reply-To: References: Message-ID: <2B1E2C32-C23B-40FD-937B-ED8B09C0258C@oracle.com> > On Aug 17, 2020, at 5:44 PM, Dan Smith wrote: > > Alternative story: > > An inline class must not extend IdentityObject through any of its superclasses. (That's it.) > > A non-inline class implicitly implements IdentityObject if it 1) is concrete, 2) declares an instance field, or 3) has a non-empty method. Abstract classes can also explicitly implement IdentityObject. > > Changing a class so that it implements IdentityObject is a binary incompatible change. > > Now we have a highly-visible concept (IdentityObject) that programmers should generally be aware of anyway, and they should readily understand a difference between abstract classes that implement IdentityObject and those that don't. Refining some of these details. I spent some time looking at abstract classes in the JDK to figure out what the best strategy is for deciding which implement IdentityObject. Something I want to be careful about: adding or removing 'IdentityObject' is a binary incompatible change. (Adding will break inline subclasses, removing will break clients depending on subtyping?although, FWIW, the verifier doesn't check superinterface types.) So we don't want to make it too easy to accidentally change whether a class implements IdentityObject. --- First, the low-hanging fruit: concrete non-inline classes (except Object) always implement IdentityObject. An abstract class indirectly implements IdentityObject when it extends a concrete class. In java.base, there are 479 abstract classes, and 455 that don't extend a concrete class. In java.desktop (and dependencies other than java.base; built on macOS), there are 528 abstract classes, and 491 that don't extend a concrete class. In jdk.compiler (and dependencies other than java.base), there are 479 abstract classes, and 459 that don't extend a concrete class. --- Second: instance fields. An inline class can't have a superclass with instance fields, which makes sense because there's no mechanism for mutating superclass instance fields, during instance initialization or later in an instance method. (It would be possible to design a factory-based initialization process, compartmentalized across multiple superclasses, but we haven't done so and don't plan to.) Inner classes have an implicit "enclosing instance" field. java.base: 455 candidates 125 extend another class with instance fields 17 more are inner classes 164 more declare their own instance fields = 149 remaining java.desktop: 491 candidates 118 extend another class with instance fields 16 more are inner classes 173 more declare their own instance fields = 184 remaining jdk.compiler: 459 candidates 210 extend another class with instance fields 21 more are inner classes 124 more declare their own instance fields = 104 remaining Most abstract classes?at least ? in these code bases?declare or inherit instance fields. I think it makes sense that an abstract class that declares instance fields, or an inner abstract class, automatically implements IdentityObject. We can infer this both at compile time and class load time. There's a little risk here that refactorings involving private fields could cause surprises. Traditionally, factoring something out into a private field (maybe for performance?) is an implementation detail. But I think we can get programmers to buy into the mindset that declaring a field is an implicit opt-out from inline subclasses. It's something new that requires some attention as you're programming. --- Third: constructors. There is no delegated instance initialization process for inline classes, so a superclass of an inline class must not have any instance initialization logic. Just where we draw the line on "any instance initialization logic" is unclear. java.base: 149 candidates 9 with a non-empty constructor (perform SecurityManager checks) 16 with a non-empty super constructor 1 with an empty constructor with a parameter (java.security.cert.CertStoreSpi) 1 with multiple constructors, all empty (java.security.SecureRandomSpi) 6 with an empty constructor with restricted access* 58 with an unrestricted empty no-arg constructor 58 with no constructor java.desktop: 184 candidates 2 with a non-empty constructor 2 with an empty constructor with restricted access* 46 with an unrestricted empty no-arg constructor 134 with no constructor jdk.compiler: 104 candidates 4 with a non-empty constructor 1 with an empty constructor with restricted access* 20 with an unrestricted empty no-arg constructor 79 with no constructor (*Restricted access means a package-access or private constructor in a public or protected class, and a private constructor in a package-access class.) As a baseline, all the "no constructor" cases should be inline superclass candidates, so should not implement IdentityObject. However, it's quite common to redundantly declare an empty constructor, e.g. for documentation purposes. (In fact, javac just added an optional lint warning supporting a coding convention in which constructors should always be explicit?see JDK-8071961.) Those classes probably shouldn't be IdentityObjects, either. Restricted access doesn't necessarily need to prevent inline subclasses, and doesn't have much to do with identity. We could leave it to the compiler to check super constructor access. But how to enforce that at run time? (There is no 'invokespecial' in the bytecode....) Constructor parameters are a borderline signal that you expect something to happen on instance initialization, even if you don't actually do anything with the parameter right now. If you have *multiple* constructors, maybe we only care about the no-args one. And then there are constructors with something more than 'super()' in their body. (Instance initializers, too, although I found no examples of them in any of the abstract classes I looked at.) These classes clearly need to implement IdentityObject in order to guarantee the traditional superclass initialization behavior. An approach I like, though it may be too disruptive: it's a compile-time error to declare a non-empty constructor in an abstract class unless that class implements IdentityObject (explicitly, indirectly, or by virtue of having fields). That would trigger ~10 compiler errors in java.base, 2 in java.desktop, and 4 in jdk.compiler. The advantage is that there's no ambiguity or surprises?for an abstract class without fields, you either opt in to IdentityObject explicitly, or you implicitly assert that you don't intend to, and then the compiler catches any problems with that assertion. Alternatively, we can once again infer 'implements IdentityObject' if a constructor has parameters or a body consisting of anything other than 'super()'. It's fairly easy to change the implicit 'implements' by doing something like adding a 'println' statement?but in practice, those sorts of changes may not be all that likely. In the JVM: - We've discussed a flag on methods to indicate that they are "empty". It seems a shame to do that when the compiler can also generate 'implements IdentityObject'. But this is a "wrong default" situation?abstract classes need to be presumed IdentityObjects unless they explicitly choose not to be. (I worry, e.g., about bytecode generators that didn't get the memo and continue to assume checks in their methods will always be executed.) So I think we'll need a rule that says you implicitly implement IdentityObject at class load time unless you have a special "empty" method. - Encoding "empty": current proposal is that an empty is ACC_ABSTRACT and has no Code attribute. We could revisit if we like, maybe with a new flag, or a class-level attribute, but this still seems pretty reasonable. - How to handle access control? I do think it's worth supporting limited access somehow (totally reasonable to allow inline subclasses in the current package, but reject subclasses elsewhere). I don't love any of the options, but maybe the best approach is to add a preparation- or initialization-time check that the superclass has a "()V" method with appropriate access? Or maybe we could have some kind of class-level attribute that controls access for subclassing (this is sort of like sealing)? Hmm, the more I think about this, the less enthusiastic I am about encoding important class properties in an "" method that's never going to be referenced... --- Finally: synchronized methods. These strike me as a pretty subtle implementation detail. But they're also *very* closely tied to the semantics of IdentityObject. Among our remaining candidate classes, they're pretty rare: java.base: 123 candidates 7 with synchronized methods = 116 can safely allow inline subclasses (24% of all abstracts) java.desktop: 182 candidates 7 with synchronized methods = 175 can safely allow inline subclasses (33% of all abstracts) jdk.compiler: 100 candidates 0 with synchronized methods = 100 can safely allow inline subclasses (21% of all abstracts) So synchronized methods on these field-free, initialization-free classes are pretty rare, but not unheard of. (Examples: java.net.URLStreamHandler, java.io.InputStream, java.lang.invoke.MethodHandleImpl.) Again, I think we can go one of two ways: - It's a compiler error to declare a 'synchronized' method if you don't already implement IdentityObject. or - Classes with synchronized methods implicitly implement IdentityObject. The first strategy would produce 7 new compiler errors in java.base and 7 more in java.desktop... but fixing those errors might prompt some reflection about the tradeoff between synchronizing on 'this' and allowing inline subclasses. The second strategy is frictionless to adopt, but may lead to surprises when code is refactored (note that 'synchronized' isn't documented by javadoc). In the JVM: I'm much less concerned about surprising inline subclasses in this case. The bytecode generator can explicitly implement IdentityObject if they want, and if they didn't/forgot/weren't aware of it, invoking the method on an inline class instance will throw an exception, just as it would if they'd used 'monitorenter' in the method body instead. From brian.goetz at oracle.com Thu Aug 20 22:25:51 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 20 Aug 2020 18:25:51 -0400 Subject: IdentityObject & abstract superclasses In-Reply-To: <2B1E2C32-C23B-40FD-937B-ED8B09C0258C@oracle.com> References: <2B1E2C32-C23B-40FD-937B-ED8B09C0258C@oracle.com> Message-ID: <126ce107-1aa4-5e75-d133-8502c5bd665f@oracle.com> One degree of freedom that you've omitted, which might help you wiggle out of some of the uncomfortable corners, is warning-now-error-later. Your "I like, but might be disruptive" approach is to error on the cases where it is not super clear.? But by starting with a warning, errors like the ~10 in java.base would have some time to explicitly say `implements IdObject` without it seeming like a world-breaking change. On 8/20/2020 6:07 PM, Dan Smith wrote: >> On Aug 17, 2020, at 5:44 PM, Dan Smith wrote: >> >> Alternative story: >> >> An inline class must not extend IdentityObject through any of its superclasses. (That's it.) >> >> A non-inline class implicitly implements IdentityObject if it 1) is concrete, 2) declares an instance field, or 3) has a non-empty method. Abstract classes can also explicitly implement IdentityObject. >> >> Changing a class so that it implements IdentityObject is a binary incompatible change. >> >> Now we have a highly-visible concept (IdentityObject) that programmers should generally be aware of anyway, and they should readily understand a difference between abstract classes that implement IdentityObject and those that don't. > Refining some of these details. I spent some time looking at abstract classes in the JDK to figure out what the best strategy is for deciding which implement IdentityObject. > > Something I want to be careful about: adding or removing 'IdentityObject' is a binary incompatible change. (Adding will break inline subclasses, removing will break clients depending on subtyping?although, FWIW, the verifier doesn't check superinterface types.) So we don't want to make it too easy to accidentally change whether a class implements IdentityObject. > > --- > > First, the low-hanging fruit: concrete non-inline classes (except Object) always implement IdentityObject. An abstract class indirectly implements IdentityObject when it extends a concrete class. > > In java.base, there are 479 abstract classes, and 455 that don't extend a concrete class. > > In java.desktop (and dependencies other than java.base; built on macOS), there are 528 abstract classes, and 491 that don't extend a concrete class. > > In jdk.compiler (and dependencies other than java.base), there are 479 abstract classes, and 459 that don't extend a concrete class. > > --- > > Second: instance fields. An inline class can't have a superclass with instance fields, which makes sense because there's no mechanism for mutating superclass instance fields, during instance initialization or later in an instance method. (It would be possible to design a factory-based initialization process, compartmentalized across multiple superclasses, but we haven't done so and don't plan to.) > > Inner classes have an implicit "enclosing instance" field. > > java.base: > 455 candidates > 125 extend another class with instance fields > 17 more are inner classes > 164 more declare their own instance fields > = 149 remaining > > java.desktop: > 491 candidates > 118 extend another class with instance fields > 16 more are inner classes > 173 more declare their own instance fields > = 184 remaining > > jdk.compiler: > 459 candidates > 210 extend another class with instance fields > 21 more are inner classes > 124 more declare their own instance fields > = 104 remaining > > Most abstract classes?at least ? in these code bases?declare or inherit instance fields. > > I think it makes sense that an abstract class that declares instance fields, or an inner abstract class, automatically implements IdentityObject. We can infer this both at compile time and class load time. > > There's a little risk here that refactorings involving private fields could cause surprises. Traditionally, factoring something out into a private field (maybe for performance?) is an implementation detail. But I think we can get programmers to buy into the mindset that declaring a field is an implicit opt-out from inline subclasses. It's something new that requires some attention as you're programming. > > --- > > Third: constructors. There is no delegated instance initialization process for inline classes, so a superclass of an inline class must not have any instance initialization logic. Just where we draw the line on "any instance initialization logic" is unclear. > > java.base: > 149 candidates > 9 with a non-empty constructor (perform SecurityManager checks) > 16 with a non-empty super constructor > 1 with an empty constructor with a parameter (java.security.cert.CertStoreSpi) > 1 with multiple constructors, all empty (java.security.SecureRandomSpi) > 6 with an empty constructor with restricted access* > 58 with an unrestricted empty no-arg constructor > 58 with no constructor > > java.desktop: > 184 candidates > 2 with a non-empty constructor > 2 with an empty constructor with restricted access* > 46 with an unrestricted empty no-arg constructor > 134 with no constructor > > jdk.compiler: > 104 candidates > 4 with a non-empty constructor > 1 with an empty constructor with restricted access* > 20 with an unrestricted empty no-arg constructor > 79 with no constructor > > (*Restricted access means a package-access or private constructor in a public or protected class, and a private constructor in a package-access class.) > > As a baseline, all the "no constructor" cases should be inline superclass candidates, so should not implement IdentityObject. > > However, it's quite common to redundantly declare an empty constructor, e.g. for documentation purposes. (In fact, javac just added an optional lint warning supporting a coding convention in which constructors should always be explicit?see JDK-8071961.) Those classes probably shouldn't be IdentityObjects, either. > > Restricted access doesn't necessarily need to prevent inline subclasses, and doesn't have much to do with identity. We could leave it to the compiler to check super constructor access. But how to enforce that at run time? (There is no 'invokespecial' in the bytecode....) > > Constructor parameters are a borderline signal that you expect something to happen on instance initialization, even if you don't actually do anything with the parameter right now. If you have *multiple* constructors, maybe we only care about the no-args one. > > And then there are constructors with something more than 'super()' in their body. (Instance initializers, too, although I found no examples of them in any of the abstract classes I looked at.) These classes clearly need to implement IdentityObject in order to guarantee the traditional superclass initialization behavior. > > An approach I like, though it may be too disruptive: it's a compile-time error to declare a non-empty constructor in an abstract class unless that class implements IdentityObject (explicitly, indirectly, or by virtue of having fields). That would trigger ~10 compiler errors in java.base, 2 in java.desktop, and 4 in jdk.compiler. The advantage is that there's no ambiguity or surprises?for an abstract class without fields, you either opt in to IdentityObject explicitly, or you implicitly assert that you don't intend to, and then the compiler catches any problems with that assertion. > > Alternatively, we can once again infer 'implements IdentityObject' if a constructor has parameters or a body consisting of anything other than 'super()'. It's fairly easy to change the implicit 'implements' by doing something like adding a 'println' statement?but in practice, those sorts of changes may not be all that likely. > > In the JVM: > > - We've discussed a flag on methods to indicate that they are "empty". It seems a shame to do that when the compiler can also generate 'implements IdentityObject'. But this is a "wrong default" situation?abstract classes need to be presumed IdentityObjects unless they explicitly choose not to be. (I worry, e.g., about bytecode generators that didn't get the memo and continue to assume checks in their methods will always be executed.) So I think we'll need a rule that says you implicitly implement IdentityObject at class load time unless you have a special "empty" method. > > - Encoding "empty": current proposal is that an empty is ACC_ABSTRACT and has no Code attribute. We could revisit if we like, maybe with a new flag, or a class-level attribute, but this still seems pretty reasonable. > > - How to handle access control? I do think it's worth supporting limited access somehow (totally reasonable to allow inline subclasses in the current package, but reject subclasses elsewhere). I don't love any of the options, but maybe the best approach is to add a preparation- or initialization-time check that the superclass has a "()V" method with appropriate access? Or maybe we could have some kind of class-level attribute that controls access for subclassing (this is sort of like sealing)? > > Hmm, the more I think about this, the less enthusiastic I am about encoding important class properties in an "" method that's never going to be referenced... > > --- > > Finally: synchronized methods. These strike me as a pretty subtle implementation detail. But they're also *very* closely tied to the semantics of IdentityObject. Among our remaining candidate classes, they're pretty rare: > > java.base: > 123 candidates > 7 with synchronized methods > = 116 can safely allow inline subclasses (24% of all abstracts) > > java.desktop: > 182 candidates > 7 with synchronized methods > = 175 can safely allow inline subclasses (33% of all abstracts) > > jdk.compiler: > 100 candidates > 0 with synchronized methods > = 100 can safely allow inline subclasses (21% of all abstracts) > > So synchronized methods on these field-free, initialization-free classes are pretty rare, but not unheard of. (Examples: java.net.URLStreamHandler, java.io.InputStream, java.lang.invoke.MethodHandleImpl.) > > Again, I think we can go one of two ways: > > - It's a compiler error to declare a 'synchronized' method if you don't already implement IdentityObject. > > or > > - Classes with synchronized methods implicitly implement IdentityObject. > > The first strategy would produce 7 new compiler errors in java.base and 7 more in java.desktop... but fixing those errors might prompt some reflection about the tradeoff between synchronizing on 'this' and allowing inline subclasses. The second strategy is frictionless to adopt, but may lead to surprises when code is refactored (note that 'synchronized' isn't documented by javadoc). > > In the JVM: I'm much less concerned about surprising inline subclasses in this case. The bytecode generator can explicitly implement IdentityObject if they want, and if they didn't/forgot/weren't aware of it, invoking the method on an inline class instance will throw an exception, just as it would if they'd used 'monitorenter' in the method body instead. > From daniel.smith at oracle.com Thu Aug 20 23:07:34 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 20 Aug 2020 17:07:34 -0600 Subject: IdentityObject & abstract superclasses In-Reply-To: <126ce107-1aa4-5e75-d133-8502c5bd665f@oracle.com> References: <2B1E2C32-C23B-40FD-937B-ED8B09C0258C@oracle.com> <126ce107-1aa4-5e75-d133-8502c5bd665f@oracle.com> Message-ID: <8B06647D-5131-4F14-A06E-9099EEC43B15@oracle.com> > On Aug 20, 2020, at 4:25 PM, Brian Goetz wrote: > > One degree of freedom that you've omitted, which might help you wiggle out of some of the uncomfortable corners, is warning-now-error-later. > > Your "I like, but might be disruptive" approach is to error on the cases where it is not super clear. But by starting with a warning, errors like the ~10 in java.base would have some time to explicitly say `implements IdObject` without it seeming like a world-breaking change. True. We're stuck waiting for IdentityObject before those warnings can be implemented, so we have to design for a world without any preparatory warnings first, but a few iterations down the road we could dial down inference and dial up error checks. From brian.goetz at oracle.com Sun Aug 23 16:26:09 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 23 Aug 2020 12:26:09 -0400 Subject: Fwd: Re: IdentityObject & abstract superclasses In-Reply-To: References: Message-ID: From the -comments list. -------- Forwarded Message -------- Subject: Re: IdentityObject & abstract superclasses Date: Fri, 21 Aug 2020 03:12:49 +0200 From: TheMrMilchmann To: valhalla-spec-comments at openjdk.java.net Hi, to me, the current issue of how to "declare" inline-friendliness in a backward-compatible manner seems to be somewhat similar to the introduction of lambdas to the language. Since I don't recall seeing this on the mailing list before, I wondered if you have considered a similar approach. Note that this is strictly about how to express inline-friendliness of abstract classes and not about the rules that make an abstract class inline-friendly. Let's consider an annotation - I will call it "@__InlineFriendly" for the sake of this example - that requires the compiler to generate error messages unless 1) the annotated type is an abstract class, and 2) the annotated type satisfies the requirements to be extensible by inline classes. Just like it is done for @FunctionalInterface, the compiler would still allow abstract classes that satisfy the requirements to be extended by @__InlineFriendly abstract classes and inline classes. Probably with an (optional?) compiler warning. In my opinion, a nice benefit of this approach is that opting into inline classes (and thus more strict rules for binary-compatibility) becomes an explicit decision compared to opting into requiring identity by extending IdentityObject. Since it aligns more closely with the current defaults (all abstract classes implicitly require identity since there is no such thing as no-identity yet), this also seems to be a more natural transition. E.g. Consider a library with an abstract class A that satisfies the requirements for inline classes but A is used for locking. (Unless I'm mistaking) the current conses seems to be that there will be a point when Java X (with IdentityObject) is released, where locking on A would become a warning unless A is updated to extend IdentityObject. Turning this warning into an error (even way down the road) might be tricky since there is still a chance that older libraries and frameworks contain a class like A. When taking the @__InlineFriendly approach, such a breaking error would not be necessary. However the possibility that users extend such an object A in an inline class remains. Although the risk of that happening accidentally would be greatly reduced by appropriate compile-time warnings. A couple of additional open issues and questions: 1. Should it be possible to annotate interfaces with @__InlineFriendly? This would yield no additional safety as interfaces will always be inline-friendly, but it might be confusing to disallow it. 2. This approach assumes that "abstract classes always require an identity" is a good default. While this is consistent with how it has been, it might not be the best approach going forward. (Though I have to admit that, as a Java user, I prefer the idea of not changing the default binary-compatibility rules of abstract classes.) Thanks for hearing me out. I hope I could contribute something of value to the discussion. :) Regards Leon Linhart On Tue, Aug 18, 2020 at 1:46 AM Dan Smith wrote: > There's an interesting interaction between IdentityObject and abstract > superclasses of inline classes that might be worth leaning into. > > --- > > The "status quo" (inasmuch as one exists): > > An inline class can extend a class if it, and all of its supers, 1) are > abstract, 2) declare no instance fields, and 3) have "empty" > methods. These properties represent a new kind of abstract class?call it a > "light" abstract class. Changing a "light" abstract class to be "heavy" is > a binary incompatible change. > > Separately, we have the IdentityObject interface, which is implicitly > attached to all non-inline concrete classes. An abstract class might also > be able to implement IdentityObject explicitly, and doing so would also > disqualify it from being an inline class super. > > A struggle in this story is getting programmers to care about whether > their classes are "heavy" or "light", since even though it's an important > property, it's easy to overlook (there's no syntax for it, and in many > cases, there are no immediate effects). > > --- > > Alternative story: > > An inline class must not extend IdentityObject through any of its > superclasses. (That's it.) > > A non-inline class implicitly implements IdentityObject if it 1) is > concrete, 2) declares an instance field, or 3) has a non-empty > method. Abstract classes can also explicitly implement IdentityObject. > > Changing a class so that it implements IdentityObject is a binary > incompatible change. > > Now we have a highly-visible concept (IdentityObject) that programmers > should generally be aware of anyway, and they should readily understand a > difference between abstract classes that implement IdentityObject and those > that don't. > > --- > > I think I like the alternative story. It feels simpler. > > One reason to avoid it is if we think there's potentially value in a > "light" abstract class concept that is independent of IdentityObject. For > example, maybe some other feature could build on the idea of a superclass > that requires no initialization, without tying that to the topic of object > identity. I'm having trouble envisioning a use case, though. Another reason > to avoid it is if we want IdentityObject to be limited to concrete > classes?no explicit implementing it allowed. > > If the alternative story is the one we want, it implies that the "empty > " JVM feature should be part of Inline Classes, not a separate thing > we deliver earlier?because it's directly tied to IdentityObject. > > From daniel.smith at oracle.com Wed Aug 26 00:03:17 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Tue, 25 Aug 2020 18:03:17 -0600 Subject: EG meeting, 2020-08-26 Message-ID: <91D7DA84-1E31-46BA-A624-69595B80F441@oracle.com> The next EG Zoom meeting is tomorrow, 4pm UTC (9am PDT, 12pm EDT). Recent threads to discuss: - "IdentityObject & abstract superclasses": I talked about using IdentityObject as the property that determines whether a class can be extended by an inline class. JVMs need to automatically apply IdentityObject to certain abstract classes. - "JEP draft: Identity Warnings for Inline Class Candidates": I updated the JEP to propose a new '@ValueBased' standard annotation. JEP: https://bugs.openjdk.java.net/browse/JDK-8249100 From forax at univ-mlv.fr Wed Aug 26 15:13:33 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 26 Aug 2020 15:13:33 +0000 Subject: EG meeting, 2020-08-26 In-Reply-To: <91D7DA84-1E31-46BA-A624-69595B80F441@oracle.com> References: <91D7DA84-1E31-46BA-A624-69595B80F441@oracle.com> Message-ID: <3DD896F5-729C-4D1B-9F10-D5D778A25409@univ-mlv.fr> I will still be on the road :) following the JEP draft, I wonder if all functional interfaces (at least the ones tagged with @FunctionalInterface) should also be considered as inline candidates, so synchronizing on them should report a warning too. the other question is how to declare your own ref class, i.e. how java.lang.int declares that Java.lang.Integer is its ref class ? Does extending a sealed abstract class enough ? regards, remi On August 26, 2020 12:03:17 AM UTC, Dan Smith wrote: >The next EG Zoom meeting is tomorrow, 4pm UTC (9am PDT, 12pm EDT). > >Recent threads to discuss: > >- "IdentityObject & abstract superclasses": I talked about using >IdentityObject as the property that determines whether a class can be >extended by an inline class. JVMs need to automatically apply >IdentityObject to certain abstract classes. > >- "JEP draft: Identity Warnings for Inline Class Candidates": I updated >the JEP to propose a new '@ValueBased' standard annotation. >JEP: https://bugs.openjdk.java.net/browse/JDK-8249100 -- Envoy? de mon appareil Android avec Courriel K-9 Mail. Veuillez excuser ma bri?vet?. From peter.levart at gmail.com Wed Aug 26 21:01:09 2020 From: peter.levart at gmail.com (Peter Levart) Date: Wed, 26 Aug 2020 23:01:09 +0200 Subject: IdentityObject & abstract superclasses In-Reply-To: References: Message-ID: Hi, Perhaps I missed some previous discussion about this, but I can't quite understand why a decision whether an abstract class is "light" or "heavy" and consequently doesn't implement IdentityObject or does implement, has to be implicit. Couldn't it be explicit like it is for concrete classes? Couldn't the language allow saying: public abstract inline class SuperInline { ??? // no instance fields allowed, no constructors allowed } vs.: public abstract class SuperIdentity { ??? // everything allowed (but nothing required), implicitly implements IdentityObject } A concrete or abstract inline class could only extend abstract inline class. A concrete or abstract non-inline class could only extend concrete or abstract non-inline class. With such explicitness about the intent (inline vs. non-inline) there would be less unintentional surprises. What do you think? Regards, Peter On 8/18/20 1:44 AM, Dan Smith wrote: > There's an interesting interaction between IdentityObject and abstract superclasses of inline classes that might be worth leaning into. > > --- > > The "status quo" (inasmuch as one exists): > > An inline class can extend a class if it, and all of its supers, 1) are abstract, 2) declare no instance fields, and 3) have "empty" methods. These properties represent a new kind of abstract class?call it a "light" abstract class. Changing a "light" abstract class to be "heavy" is a binary incompatible change. > > Separately, we have the IdentityObject interface, which is implicitly attached to all non-inline concrete classes. An abstract class might also be able to implement IdentityObject explicitly, and doing so would also disqualify it from being an inline class super. > > A struggle in this story is getting programmers to care about whether their classes are "heavy" or "light", since even though it's an important property, it's easy to overlook (there's no syntax for it, and in many cases, there are no immediate effects). > > --- > > Alternative story: > > An inline class must not extend IdentityObject through any of its superclasses. (That's it.) > > A non-inline class implicitly implements IdentityObject if it 1) is concrete, 2) declares an instance field, or 3) has a non-empty method. Abstract classes can also explicitly implement IdentityObject. > > Changing a class so that it implements IdentityObject is a binary incompatible change. > > Now we have a highly-visible concept (IdentityObject) that programmers should generally be aware of anyway, and they should readily understand a difference between abstract classes that implement IdentityObject and those that don't. > > --- > > I think I like the alternative story. It feels simpler. > > One reason to avoid it is if we think there's potentially value in a "light" abstract class concept that is independent of IdentityObject. For example, maybe some other feature could build on the idea of a superclass that requires no initialization, without tying that to the topic of object identity. I'm having trouble envisioning a use case, though. Another reason to avoid it is if we want IdentityObject to be limited to concrete classes?no explicit implementing it allowed. > > If the alternative story is the one we want, it implies that the "empty " JVM feature should be part of Inline Classes, not a separate thing we deliver earlier?because it's directly tied to IdentityObject. > From peter.levart at gmail.com Wed Aug 26 21:36:10 2020 From: peter.levart at gmail.com (Peter Levart) Date: Wed, 26 Aug 2020 23:36:10 +0200 Subject: IdentityObject & abstract superclasses In-Reply-To: References: Message-ID: I apologize for not reading the previous discussion about "Superclasses of inline classes" (as of 19th of dec. 2019). I now see the intent was to not use a special class modifier to express the feature of light abstract class. The intent was also to allow extending "light" abstract classes with inline as well as non-inline classes. In that case, a modifier like "inline" would be a strange way to express such thing. Regards, Peter On 8/26/20 11:01 PM, Peter Levart wrote: > Hi, > > > Perhaps I missed some previous discussion about this, but I can't > quite understand why a decision whether an abstract class is "light" > or "heavy" and consequently doesn't implement IdentityObject or does > implement, has to be implicit. Couldn't it be explicit like it is for > concrete classes? Couldn't the language allow saying: > > > public abstract inline class SuperInline { > > ??? // no instance fields allowed, no constructors allowed > > } > > > vs.: > > > public abstract class SuperIdentity { > > ??? // everything allowed (but nothing required), implicitly > implements IdentityObject > > } > > > A concrete or abstract inline class could only extend abstract inline > class. > > A concrete or abstract non-inline class could only extend concrete or > abstract non-inline class. > > > With such explicitness about the intent (inline vs. non-inline) there > would be less unintentional surprises. > > > What do you think? > > > Regards, Peter > > > On 8/18/20 1:44 AM, Dan Smith wrote: >> There's an interesting interaction between IdentityObject and >> abstract superclasses of inline classes that might be worth leaning >> into. >> >> --- >> >> The "status quo" (inasmuch as one exists): >> >> An inline class can extend a class if it, and all of its supers, 1) >> are abstract, 2) declare no instance fields, and 3) have "empty" >> methods. These properties represent a new kind of abstract >> class?call it a "light" abstract class. Changing a "light" abstract >> class to be "heavy" is a binary incompatible change. >> >> Separately, we have the IdentityObject interface, which is implicitly >> attached to all non-inline concrete classes. An abstract class might >> also be able to implement IdentityObject explicitly, and doing so >> would also disqualify it from being an inline class super. >> >> A struggle in this story is getting programmers to care about whether >> their classes are "heavy" or "light", since even though it's an >> important property, it's easy to overlook (there's no syntax for it, >> and in many cases, there are no immediate effects). >> >> --- >> >> Alternative story: >> >> An inline class must not extend IdentityObject through any of its >> superclasses. (That's it.) >> >> A non-inline class implicitly implements IdentityObject if it 1) is >> concrete, 2) declares an instance field, or 3) has a non-empty >> method. Abstract classes can also explicitly implement IdentityObject. >> >> Changing a class so that it implements IdentityObject is a binary >> incompatible change. >> >> Now we have a highly-visible concept (IdentityObject) that >> programmers should generally be aware of anyway, and they should >> readily understand a difference between abstract classes that >> implement IdentityObject and those that don't. >> >> --- >> >> I think I like the alternative story. It feels simpler. >> >> One reason to avoid it is if we think there's potentially value in a >> "light" abstract class concept that is independent of IdentityObject. >> For example, maybe some other feature could build on the idea of a >> superclass that requires no initialization, without tying that to the >> topic of object identity. I'm having trouble envisioning a use case, >> though. Another reason to avoid it is if we want IdentityObject to be >> limited to concrete classes?no explicit implementing it allowed. >> >> If the alternative story is the one we want, it implies that the >> "empty " JVM feature should be part of Inline Classes, not a >> separate thing we deliver earlier?because it's directly tied to >> IdentityObject. >> From daniel.smith at oracle.com Thu Aug 27 00:09:45 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 26 Aug 2020 18:09:45 -0600 Subject: IdentityObject & abstract superclasses In-Reply-To: References: Message-ID: > On Aug 26, 2020, at 3:36 PM, Peter Levart wrote: > > I apologize for not reading the previous discussion about "Superclasses of inline classes" (as of 19th of dec. 2019). I now see the intent was to not use a special class modifier to express the feature of light abstract class. > > The intent was also to allow extending "light" abstract classes with inline as well as non-inline classes. In that case, a modifier like "inline" would be a strange way to express such thing. Yeah, you got it. (No need to apologize for missing an 8-month old thread!) Given the numbers I cited, the motivation for doing this implicitly is that about 25% of existing abstract classes should be totally fine to extend with an inline class. If those classes have to explicitly opt in, most never will, limiting the usefulness of inline classes. (Similarly, imagine how it would go if you could only implement interfaces that explicitly permit inline subclasses.)