From daniel.smith at oracle.com Wed Jun 2 14:36:58 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 2 Jun 2021 14:36:58 +0000 Subject: EG meeting *canceled*, 2021-06-02 Message-ID: <050CD364-1E6D-4786-AE23-E4FA94E5F708@oracle.com> Nothing new this time, so we'll skip the meeting today. From daniel.smith at oracle.com Wed Jun 2 14:50:21 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 2 Jun 2021 14:50:21 +0000 Subject: Making Object abstract In-Reply-To: <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> References: <580432CB-6575-4C4E-92B5-83CAB6C73467@oracle.com> <65df60bb-100a-25f8-1145-b7091518f0fb@oracle.com> <87B00510-0F0C-4961-B2EA-6816AEF810ED@oracle.com> <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> Message-ID: > On May 8, 2021, at 1:20 PM, Brian Goetz wrote: > > I agree that we can introduce the new API point immediately. The 17 window hasn't even closed yet! But we'd have to get a move on. But realistically, we can expect it to be several years before we are comfortable erroring on the `new Object()` constructor. Update: we spent a little time discussing "what does this API look like?", and settled on: - A new static factory method, java.lang.IdentityObject.newIdentity() - A possibly-private implementation class, java.lang.Identity or jdk.internal.Identity - javac treats 'new Object()' as equivalent to 'IdentityObject.newIdentity()', with a warning - the JVM executes 'new java/lang/Object' by creating an instance of 'java/lang/Identity' The advantage of putting the factory in an interface is that static interface methods aren't inherited. (If we were to put it in Object, it would pollute the method namespace of all classes.) Of course these details can evolve, but as it stands, there's not a good path to introducing the API before we introduce primitive objects?IdentityObject has no reason to exist without primitive objects. So efforts to get people to migrate to something new will need to wait until then. (As discussed earlier in the thread, that's fine?we're inevitably going to have a period of time when 'new Object()' does something weird.) From brian.goetz at oracle.com Wed Jun 2 14:57:52 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 2 Jun 2021 10:57:52 -0400 Subject: Making Object abstract In-Reply-To: References: <580432CB-6575-4C4E-92B5-83CAB6C73467@oracle.com> <65df60bb-100a-25f8-1145-b7091518f0fb@oracle.com> <87B00510-0F0C-4961-B2EA-6816AEF810ED@oracle.com> <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> Message-ID: <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> A minor bikeshed comment: We're asking users to change their `new Object()` to `IdentityObject.newIdentity()`, and they may ask "why do I have to say 'Identity' twice"?? (And by ask, I mean grumble, because we're already asking them to change their working code.) After a few minutes of thought, I think it might be a better fit to put this at Objects::newIdentity.? The methods in Objects are conveniences that users could write themselves, which this basically is -- there's nothing special about this method, other than having a preferred alternative to `new Object()` which users will understand.? So parking this where the Object conveniences go seems slightly lower friction. On 6/2/2021 10:50 AM, Dan Smith wrote: >> On May 8, 2021, at 1:20 PM, Brian Goetz wrote: >> >> I agree that we can introduce the new API point immediately. The 17 window hasn't even closed yet! But we'd have to get a move on. But realistically, we can expect it to be several years before we are comfortable erroring on the `new Object()` constructor. > Update: we spent a little time discussing "what does this API look like?", and settled on: > > - A new static factory method, java.lang.IdentityObject.newIdentity() > - A possibly-private implementation class, java.lang.Identity or jdk.internal.Identity > - javac treats 'new Object()' as equivalent to 'IdentityObject.newIdentity()', with a warning > - the JVM executes 'new java/lang/Object' by creating an instance of 'java/lang/Identity' > > The advantage of putting the factory in an interface is that static interface methods aren't inherited. (If we were to put it in Object, it would pollute the method namespace of all classes.) > > Of course these details can evolve, but as it stands, there's not a good path to introducing the API before we introduce primitive objects?IdentityObject has no reason to exist without primitive objects. So efforts to get people to migrate to something new will need to wait until then. (As discussed earlier in the thread, that's fine?we're inevitably going to have a period of time when 'new Object()' does something weird.) From daniel.smith at oracle.com Thu Jun 3 17:02:48 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 3 Jun 2021 17:02:48 +0000 Subject: Making Object abstract In-Reply-To: <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> References: <580432CB-6575-4C4E-92B5-83CAB6C73467@oracle.com> <65df60bb-100a-25f8-1145-b7091518f0fb@oracle.com> <87B00510-0F0C-4961-B2EA-6816AEF810ED@oracle.com> <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> Message-ID: > On Jun 2, 2021, at 8:57 AM, Brian Goetz wrote: > > A minor bikeshed comment: We're asking users to change their `new Object()` to `IdentityObject.newIdentity()`, and they may ask "why do I have to say 'Identity' twice"? (And by ask, I mean grumble, because we're already asking them to change their working code.) Possibly addressed with a different name. (Maybe something like 'make'? Do we have a consistent standard for the equivalent of an 'of' method, but with no parameters?) > > After a few minutes of thought, I think it might be a better fit to put this at Objects::newIdentity. The methods in Objects are conveniences that users could write themselves, which this basically is -- there's nothing special about this method, other than having a preferred alternative to `new Object()` which users will understand. So parking this where the Object conveniences go seems slightly lower friction. Yes, Objects is a good backup option of we don't like the IdentityObject approach. With the added friction of "import java.util.Objects". From brian.goetz at oracle.com Thu Jun 3 17:17:50 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 3 Jun 2021 13:17:50 -0400 Subject: Making Object abstract In-Reply-To: References: <580432CB-6575-4C4E-92B5-83CAB6C73467@oracle.com> <65df60bb-100a-25f8-1145-b7091518f0fb@oracle.com> <87B00510-0F0C-4961-B2EA-6816AEF810ED@oracle.com> <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> Message-ID: >> After a few minutes of thought, I think it might be a better fit to put this at Objects::newIdentity. The methods in Objects are conveniences that users could write themselves, which this basically is -- there's nothing special about this method, other than having a preferred alternative to `new Object()` which users will understand. So parking this where the Object conveniences go seems slightly lower friction. > Yes, Objects is a good backup option of we don't like the IdentityObject approach. With the added friction of "import java.util.Objects". What I like about the Objects placement (thought took me a while to come around to this) is that it fits into the other Objects operations, in that they are all sugar for code we could write without help.? I don't think we need or want a canonical "class of all anonymous identity objects" type. From daniel.smith at oracle.com Fri Jun 4 16:41:33 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 4 Jun 2021 16:41:33 +0000 Subject: JEP draft: Better-defined JVM class file validation Message-ID: <046E16C8-DC27-4266-853F-19623EC731D1@oracle.com> Posted a new JEP draft, here: http://openjdk.java.net/jeps/8267650 This is some preparatory work to allow us to more cleanly introduce the variety of new JVM features coming with primitive objects (JEP 401). Most of the spec work is already done; you can browse the proposed spec changes here: http://cr.openjdk.java.net/~dlsmith/8267650/8267650-20210603/specs/ From daniel.smith at oracle.com Fri Jun 4 17:11:11 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 4 Jun 2021 17:11:11 +0000 Subject: JEP draft: Better-defined JVM class file validation In-Reply-To: <046E16C8-DC27-4266-853F-19623EC731D1@oracle.com> References: <046E16C8-DC27-4266-853F-19623EC731D1@oracle.com> Message-ID: <67A334C1-E729-498A-AD28-B176C3F45F32@oracle.com> > On Jun 4, 2021, at 10:41 AM, Dan Smith wrote: > > Posted a new JEP draft, here: > > http://openjdk.java.net/jeps/8267650 I'll draw your attention to this section, in case it raises any red flags: --- Risks and Assumptions Changing JVM validation behavior is often a risk, because it may cause legacy classfiles to fail with new errors, or, more subtly, new class files with old version numbers to be accepted, but then fail on older JVMs. In general, the HotSpot changes proposed in this JEP are narrow in scope, often in corner cases that real world code is unlikely to probe. And many of the changes only modify the type of error being thrown or the timing of an error check. That said, the most likely areas of concern are: ? New errors caused by improper appearances of the Module, ModulePackages, ModuleMainClass, and ConstantValue attributes. ? New errors caused by pre-51 class files that declare a useless method with name and 1 or more parameters. ? Accepting class files with malformed optional attributes, even though those class files could fail to load on an older JVM. Besides the risk to JVM users, there is some risk that, by relaxing the constraints on optional attributes, downstream tools will be surprised by unvalidated attribute contents in class files that can be successfully loaded. These risks need to be balanced against the cost of the extra complexity required to fully specify and maintain longstanding, often ad hoc HotSpot behavior. --- From brian.goetz at oracle.com Sat Jun 5 15:21:11 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 5 Jun 2021 11:21:11 -0400 Subject: [External] : Re: Making Object abstract In-Reply-To: References: <580432CB-6575-4C4E-92B5-83CAB6C73467@oracle.com> <65df60bb-100a-25f8-1145-b7091518f0fb@oracle.com> <87B00510-0F0C-4961-B2EA-6816AEF810ED@oracle.com> <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> Message-ID: <8759d894-0da3-664b-df9b-c9cdb292f138@oracle.com> Rampdown is next week; time is fleeting. I think the path of adding Objects::newIdentity in 17 seems the best alternative.? If there are objections, make them now. On 6/4/2021 4:10 PM, Dan Heidinga wrote: > Quoting from several previous emails in this thread: > > Brian said: >> I agree that we can introduce the new API point immediately. The 17 window hasn't even closed yet! But we'd have to get a move on. But realistically, we can expect it to be several years before we are comfortable erroring on the `new Object()` constructor. > Dan S. responded: >> Of course these details can evolve, but as it stands, there's not a good path to introducing the API before we introduce primitive objects?IdentityObject has no reason to exist without primitive objects. So efforts to get people to migrate to something new will need to wait until then. (As discussed earlier in the thread, that's fine?we're inevitably going to have a period of time when 'new Object()' does something weird.) > And Brian proposes: >> What I like about the Objects placement (thought took me a while to come >> around to this) is that it fits into the other Objects operations, in >> that they are all sugar for code we could write without help. I don't >> think we need or want a canonical "class of all anonymous identity >> objects" type. >> > This gives us a pretty reasonable story for where to put a minimal > helper API today, before the window for 17 closes. Even taking the > "several years before we are comfortable erroring" into account, it > would help the ecosystem migrate to Valhalla if we get the replacement > API into the upcoming LTS so the adoption is higher before we start > erroring on it. > > We know - for better or for worse - that many applications leap from > LTS to LTS which requires library authors to support previous LTS > releases while adding support for current release. And that apps > can't leap faster than their dependencies so we want to smooth the > path that lets applications track the release cadence. > > This Objects::newIdentity api seems like an easy one to add today that > we can point libraries at it as they adopt 17. I don't see much risk > that Valhalla will change in a way that invalidates this api. > Wouldn't it be better to lay that foundation now, before 17 ships, > then to wait? > > --Dan > From forax at univ-mlv.fr Sat Jun 5 15:33:59 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 5 Jun 2021 17:33:59 +0200 (CEST) Subject: [External] : Re: Making Object abstract In-Reply-To: <8759d894-0da3-664b-df9b-c9cdb292f138@oracle.com> References: <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> <8759d894-0da3-664b-df9b-c9cdb292f138@oracle.com> Message-ID: <1951128283.666728.1622907238999.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Dan Heidinga" > Cc: "daniel smith" , "valhalla-spec-experts" > > Envoy?: Samedi 5 Juin 2021 17:21:11 > Objet: Re: [External] : Re: Making Object abstract > Rampdown is next week; time is fleeting. > I think the path of adding Objects::newIdentity in 17 seems the best > alternative. If there are objections, make them now. no objection, quite the opposite, i agree with Dan H analysis. R?mi > On 6/4/2021 4:10 PM, Dan Heidinga wrote: >> Quoting from several previous emails in this thread: >> Brian said: >>> I agree that we can introduce the new API point immediately. The 17 window >>> hasn't even closed yet! But we'd have to get a move on. But realistically, we >>> can expect it to be several years before we are comfortable erroring on the >>> `new Object()` constructor. >> Dan S. responded: >>> Of course these details can evolve, but as it stands, there's not a good path to >>> introducing the API before we introduce primitive objects?IdentityObject has no >>> reason to exist without primitive objects. So efforts to get people to migrate >>> to something new will need to wait until then. (As discussed earlier in the >>> thread, that's fine?we're inevitably going to have a period of time when 'new >>> Object()' does something weird.) >> And Brian proposes: >>> What I like about the Objects placement (thought took me a while to come >>> around to this) is that it fits into the other Objects operations, in >>> that they are all sugar for code we could write without help. I don't >>> think we need or want a canonical "class of all anonymous identity >>> objects" type. >> This gives us a pretty reasonable story for where to put a minimal >> helper API today, before the window for 17 closes. Even taking the >> "several years before we are comfortable erroring" into account, it >> would help the ecosystem migrate to Valhalla if we get the replacement >> API into the upcoming LTS so the adoption is higher before we start >> erroring on it. >> We know - for better or for worse - that many applications leap from >> LTS to LTS which requires library authors to support previous LTS >> releases while adding support for current release. And that apps >> can't leap faster than their dependencies so we want to smooth the >> path that lets applications track the release cadence. >> This Objects::newIdentity api seems like an easy one to add today that >> we can point libraries at it as they adopt 17. I don't see much risk >> that Valhalla will change in a way that invalidates this api. >> Wouldn't it be better to lay that foundation now, before 17 ships, >> then to wait? >> --Dan From forax at univ-mlv.fr Sun Jun 6 16:17:34 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 6 Jun 2021 18:17:34 +0200 (CEST) Subject: JEP draft: Better-defined JVM class file validation In-Reply-To: <67A334C1-E729-498A-AD28-B176C3F45F32@oracle.com> References: <046E16C8-DC27-4266-853F-19623EC731D1@oracle.com> <67A334C1-E729-498A-AD28-B176C3F45F32@oracle.com> Message-ID: <1295647884.891877.1622996254815.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "daniel smith" > ?: "valhalla-spec-experts" > Envoy?: Vendredi 4 Juin 2021 19:11:11 > Objet: Re: JEP draft: Better-defined JVM class file validation >> On Jun 4, 2021, at 10:41 AM, Dan Smith wrote: >> >> Posted a new JEP draft, here: >> >> http://openjdk.java.net/jeps/8267650 Hi Dan, that's a lot of nice cleanups. If i can emit a critic, i would have preferred to have the text of the JEP to clearly separate spec issues from Hotspot issues, to the point where i wonder if it's not better to have two JEPs. > > I'll draw your attention to this section, in case it raises any red flags: > > --- > > Risks and Assumptions > > Changing JVM validation behavior is often a risk, because it may cause legacy > classfiles to fail with new errors, or, more subtly, new class files with old > version numbers to be accepted, but then fail on older JVMs. > > In general, the HotSpot changes proposed in this JEP are narrow in scope, often > in corner cases that real world code is unlikely to probe. And many of the > changes only modify the type of error being thrown or the timing of an error > check. That said, the most likely areas of concern are: > > ? New errors caused by improper appearances of the Module, ModulePackages, > ModuleMainClass, and ConstantValue attributes. yes, those attributes should be checked by the VM an not only in Java code. > > ? New errors caused by pre-51 class files that declare a useless method with > name and 1 or more parameters. I don't see the point of this change, unlike all the others, this one will reject classes that were valid before. > > ? Accepting class files with malformed optional attributes, even though those > class files could fail to load on an older JVM. It's not clear to me if this change is only for classfile version >= V18 or not. I would vote for having that change applied on all classes given it's an Hotspot bug, not a spec bug. > > Besides the risk to JVM users, there is some risk that, by relaxing the > constraints on optional attributes, downstream tools will be surprised by > unvalidated attribute contents in class files that can be successfully loaded. Actually, the fact that the InnerClasses attribute is validated by the VM is confusing because it implies that the VM uses the values of that attributes to check access which it does not. For me, the spec says that those attributes are optionals so Hotspot should behave as the spec says. And i'm a little worry about rejecting the ConstantValue attribute if the field is not static because i think i've seen a code using ConstantValue on all fields but i'm not able to recall where so i've not idea if it was in library used or not. I suppose this behavior will be gated with a V18 checks so it should not be an issue anyway. > > These risks need to be balanced against the cost of the extra complexity > required to fully specify and maintain longstanding, often ad hoc HotSpot > behavior. > > --- regards, R?mi From daniel.smith at oracle.com Mon Jun 7 15:13:45 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 7 Jun 2021 15:13:45 +0000 Subject: [External] : Re: Making Object abstract In-Reply-To: <8759d894-0da3-664b-df9b-c9cdb292f138@oracle.com> References: <580432CB-6575-4C4E-92B5-83CAB6C73467@oracle.com> <65df60bb-100a-25f8-1145-b7091518f0fb@oracle.com> <87B00510-0F0C-4961-B2EA-6816AEF810ED@oracle.com> <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> <8759d894-0da3-664b-df9b-c9cdb292f138@oracle.com> Message-ID: > On Jun 5, 2021, at 9:21 AM, Brian Goetz wrote: > > Rampdown is next week; time is fleeting. > > I think the path of adding Objects::newIdentity in 17 seems the best alternative. If there are objections, make them now. I still think that's a second-best option. Utility classes were all the rage 20 years ago. Default/static interface methods have largely replaced them. Aesthetics aside, in the Objects approach, what's the return type? It would have to be Object. But then we're going to want to turn around and make it IdentityObject?a binary incompatible change. And there's also some uncertainty about whether we're going to want to come up with some sort of factory method feature as we iterate on primitive objects through the preview process. Maybe this whole conversation will become moot. On the other side, I'm not that persuaded by the argument for urgency. We want to encourage a slow migration from 'new Object()' to 'SomeClass.newIdentity()'. It's not going to be completed for a number of years. We can start that migration in 17, or wait another release or two. What's the rush? From brian.goetz at oracle.com Mon Jun 7 15:33:55 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 7 Jun 2021 11:33:55 -0400 Subject: [External] : Re: Making Object abstract In-Reply-To: References: <580432CB-6575-4C4E-92B5-83CAB6C73467@oracle.com> <65df60bb-100a-25f8-1145-b7091518f0fb@oracle.com> <87B00510-0F0C-4961-B2EA-6816AEF810ED@oracle.com> <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> <8759d894-0da3-664b-df9b-c9cdb292f138@oracle.com> Message-ID: <4ee054ba-e6d8-87a5-15b0-b9775baf127d@oracle.com> On 6/7/2021 11:13 AM, Dan Smith wrote: >> On Jun 5, 2021, at 9:21 AM, Brian Goetz wrote: >> >> Rampdown is next week; time is fleeting. >> >> I think the path of adding Objects::newIdentity in 17 seems the best alternative. If there are objections, make them now. > I still think that's a second-best option. Utility classes were all the rage 20 years ago. Default/static interface methods have largely replaced them. But this _is_ a utility.? The user can always get this behavior by (say) `new Object(){}`, or any number of other means.? The existence of an API point here is a convenience, and one primarily for readability of code (so its clear that the reason someone is creating an object in a weird way is to get the identity.) To help illustrate this point, consider the other end of the coin. Suppose our implementation is: ??? interface IdentityObject { ??????? static newIdentity() { return new PlaceholderIdentityClass(); } ??? } Should PlaceholderIdentityClass be public?? I argue that not only is it not beneficial to expose it, but it is actively a mistake to expose it.? Because the name does not correspond to any useful user-observable semantics, and might even inspire bad assumptions that _all_ identity-only instances are of this type.? Defining a public type (in java.lang, no less) for something that is useless to the user is actively harmful, because people are going to wonder what to use it for (or not use it for.)? The same argument applies, at varying degrees, to where you place this method. > Aesthetics aside, in the Objects approach, what's the return type? It would have to be Object. But then we're going to want to turn around and make it IdentityObject?a binary incompatible change. Maybe.? We've talked about whether it makes more sense to erase IdentityObject to Object, to allow compatible migration of existing identity-assuming code.?? We've also talked about finally allowing user control over bridge generation, in which case we change it to IdentityObject and have an Object bridge for binary compatibility. From daniel.smith at oracle.com Mon Jun 7 19:00:05 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 7 Jun 2021 19:00:05 +0000 Subject: [External] : Re: Making Object abstract In-Reply-To: References: <580432CB-6575-4C4E-92B5-83CAB6C73467@oracle.com> <65df60bb-100a-25f8-1145-b7091518f0fb@oracle.com> <87B00510-0F0C-4961-B2EA-6816AEF810ED@oracle.com> <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> <8759d894-0da3-664b-df9b-c9cdb292f138@oracle.com> Message-ID: <3ACDFF6C-ECB6-41E8-B6C1-5CE5246189C4@oracle.com> > On Jun 7, 2021, at 9:13 AM, Dan Smith wrote: > > On the other side, I'm not that persuaded by the argument for urgency. We want to encourage a slow migration from 'new Object()' to 'SomeClass.newIdentity()'. It's not going to be completed for a number of years. We can start that migration in 17, or wait another release or two. What's the rush? Just to clarify this, because I know, when it comes to compatibility impact, adoption patterns will tend to have quantum leaps between LTS releases?from 11 to 17 to 26. It's potentially meaningful that "17" is a number in that set. However: in some future day (when we're all running Java in our self-driving electric cars), we will decide the time has come to turn 'new Object()' from a warning into an error. At that point, we will want to consider the body of source code that plans to compile with --release NN (NN >> 26), and how much of that source code still has lingering 'new Object()' uses. This universe of source code i) is comfortable with all the Valhalla features, and ii) does not intend to run on any pre-NN JVMs. Where there are a few lingering 'new Object()' uses, presumably there will be tooling in place to quickly fix them up. Meanwhile, there's no impact in Java NN on legacy binaries (the JVM will indefinitely support 'new java/lang/Object'), and there's no impact on sources compiled with --release 17 or --release 26, etc. I hope NN is a relatively small number, but... shrug. We'll get there when we get there. There are no downstream dependencies. Nor do I expect an API point available in 17 to hasten this much, because there won't be any *push* to change source code until JEP 401 comes along with a warning about 'new Object()'. From daniel.smith at oracle.com Mon Jun 7 23:04:45 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 7 Jun 2021 23:04:45 +0000 Subject: [External] : Re: Making Object abstract In-Reply-To: References: <580432CB-6575-4C4E-92B5-83CAB6C73467@oracle.com> <65df60bb-100a-25f8-1145-b7091518f0fb@oracle.com> <87B00510-0F0C-4961-B2EA-6816AEF810ED@oracle.com> <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> <8759d894-0da3-664b-df9b-c9cdb292f138@oracle.com> <3ACDFF6C-ECB6-41E8-B6C1-5CE5246189C4@oracle.com> Message-ID: <99124DD5-B892-4143-97D8-F9DC2F978C38@oracle.com> > On Jun 7, 2021, at 2:46 PM, Dan Heidinga wrote: > > On Mon, Jun 7, 2021 at 3:00 PM Dan Smith wrote: >> >>> On Jun 7, 2021, at 9:13 AM, Dan Smith wrote: >>> >>> On the other side, I'm not that persuaded by the argument for urgency. We want to encourage a slow migration from 'new Object()' to 'SomeClass.newIdentity()'. It's not going to be completed for a number of years. We can start that migration in 17, or wait another release or two. What's the rush? >> >> Just to clarify this, because I know, when it comes to compatibility impact, adoption patterns will tend to have quantum leaps between LTS releases?from 11 to 17 to 26. It's potentially meaningful that "17" is a number in that set. (Err, I guess the next magic number is 23.) > Right. "17" is a meaningful number in that set, especially if we see > the possibility that "new Object();" becomes illegal by "26". For comparison, the wrapper class constructors first got warnings in 9 (2017), and can probably expect to get errors by 23 (2024). So, nah, I don't see cramming that into a 3-year cycle. More like warnings in ~23 (2024), errors in ~35 (2030). (Even if we have an API point in 17, I don't think we'd anticipate any warnings until JEP 401 is finalized.) >> Nor do I expect an API point available in 17 to hasten this much, because there won't be any *push* to change source code until JEP 401 comes along with a warning about 'new Object()'. >> > > Sure javac might not warn now but if it starts to warn in 17 < version > < 26 then even libraries still targeting 17 can "do the right thing". Here's the thing though: if you compile with '--release 17', you're not going to get any warnings. You'll get class files that still do 'new java/lang/Object', but these will be future-proof?none of this is a binary problem. Once we turn on the warnings, by '--release 23', we'll definitely have an alternative API point to use. And javac can generate bytecode that uses it, whether you use the "right syntax" or not. We'll gently encourage you to update your sources, and then eventually, for '--release 35' or whatever, report an error. From daniel.smith at oracle.com Tue Jun 8 21:10:39 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Tue, 8 Jun 2021 21:10:39 +0000 Subject: JEP draft: Better-defined JVM class file validation In-Reply-To: <1295647884.891877.1622996254815.JavaMail.zimbra@u-pem.fr> References: <046E16C8-DC27-4266-853F-19623EC731D1@oracle.com> <67A334C1-E729-498A-AD28-B176C3F45F32@oracle.com> <1295647884.891877.1622996254815.JavaMail.zimbra@u-pem.fr> Message-ID: <8CE05B38-F90A-4A24-A13C-6A1B42B88788@oracle.com> > On Jun 6, 2021, at 10:17 AM, Remi Forax wrote: > > ----- Mail original ----- >> De: "daniel smith" >> ?: "valhalla-spec-experts" >> Envoy?: Vendredi 4 Juin 2021 19:11:11 >> Objet: Re: JEP draft: Better-defined JVM class file validation > >>> On Jun 4, 2021, at 10:41 AM, Dan Smith wrote: >>> >>> Posted a new JEP draft, here: >>> >>> http://openjdk.java.net/jeps/8267650 > > Hi Dan, > that's a lot of nice cleanups. > > If i can emit a critic, i would have preferred to have the text of the JEP to clearly separate spec issues from Hotspot issues, > to the point where i wonder if it's not better to have two JEPs. It's not something that is cleanly separable?a lot of times JVMS says X, HotSpot does Y, and the best way to reconcile the difference is with Z. But I have tried to call out exactly what HotSpot behavioral changes can be expected in each section (look for the bulleted lists). >> ? New errors caused by improper appearances of the Module, ModulePackages, >> ModuleMainClass, and ConstantValue attributes. > > yes, those attributes should be checked by the VM an not only in Java code. It's tricky because, strictly speaking, the JVM spec never "loads" a module class file, so never performs format checking on it. But we assert plenty of rules about what a properly-formed module class file looks like (see JVMS 4.1), and that seems like a reasonable thing to do, given that modules are a fundamental part of the runtime system. Exactly when those rules are enforced is left out of JVMS, and is up to libraries (like ClassLoader, for example), which should have their own analog to format checking. >> ? New errors caused by pre-51 class files that declare a useless method with >> name and 1 or more parameters. > > I don't see the point of this change, unlike all the others, this one will reject classes that were valid before. It comes from trying to sort out the definition of "class initialization method". Often, the name '' is interpreted as an unambiguous indicator of a class initialization method. If I recall correctly, HotSpot has certain behaviors that are unique to methods named '', like verifying them as if they are static, whether ACC_STATIC is set or not. This is derived from some assertions in 4.6 about "class initialization methods". But there's also a notion that a class only has one initialization method, and that method has the expected descriptor (see 2.9.2, 5.5 step 9). How do we reconcile these? By not letting anything through that is named '' but that has the wrong descriptor. Yes, it's an incompatible change, but it seems less disruptive than pulling on the thread of "what does it mean to have a method named '' that isn't a class initialization method, and what bugs do we have to track down and fix for this model to make sense?" If we have evidence of these methods in the wild, that might push me to find a different solution, but otherwise I expect it's just a theoretical incompatibility that nobody will notice. >> ? Accepting class files with malformed optional attributes, even though those >> class files could fail to load on an older JVM. > > It's not clear to me if this change is only for classfile version >= V18 or not. > I would vote for having that change applied on all classes given it's an Hotspot bug, not a spec bug. Everything in the JEP applies to class files of all version numbers, unless explicitly stated otherwise. This is a significant change to both the spec and implementation, in this case expanding the set of legal class files. It's a bug fix, yes, but one that is resolved with a very broad stroke. The rationale is that reconciling all the differences between JVMS and HotSpot in this area would be a bunch of work that adds a lot of complexity to both, and all to serve... no good purpose, really. We'll all be happier (we hope) if we draw a bright, hands-off line for format checking of optional attributes, and leave further validation as an implementation/API detail. >> Besides the risk to JVM users, there is some risk that, by relaxing the >> constraints on optional attributes, downstream tools will be surprised by >> unvalidated attribute contents in class files that can be successfully loaded. > > Actually, the fact that the InnerClasses attribute is validated by the VM is confusing because it implies that the VM uses the values of that attributes to check access which it does not. > For me, the spec says that those attributes are optionals so Hotspot should behave as the spec says. The spec says some things that are unclear about how/whether InnerClass attributes should be validated, but yes, agree that there will be less ambiguity if we're totally hands-off. > And i'm a little worry about rejecting the ConstantValue attribute if the field is not static because i think i've seen a code using ConstantValue on all fields but i'm not able to recall where so i've not idea if it was in library used or not. I suppose this behavior will be gated with a V18 checks so it should not be an issue anyway. Again, no, not expecting to gate this with a version check. (If we did, it's more trouble than it's worth, because we've now got to specify and implement two different behaviors, when the goal is simplification.) The particular problem that led me here is that HotSpot currently doesn't recognize a ConstantValue attribute *at all* if it is applied to a non-static field. It is treated like a nonstandard attribute, just as it (rightly) is if applied to a method. I'm okay with specifying that an attribute is recognized, or not, depending on where it appears, but I would really prefer not to bring the access flags of a field declaration into it! (JVMS backs up the HotSpot interpretation in 4.7.2, but if we're going to have a special rule, it really belongs in 4.7.1. This feels like the kind of thing where somebody noticed an anomaly in the implementation and tried to document it in the spec, but didn't follow through on the implications.) Again, if there is evidence of this that we can find in the wild, that could shift the balance in the compatibility conversation, and going out of our way to support it may be justified. From daniel.smith at oracle.com Tue Jun 8 21:21:08 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Tue, 8 Jun 2021 21:21:08 +0000 Subject: JEP draft: Universal Generics Message-ID: Please see this JEP draft: http://openjdk.java.net/jeps/8261529 This is the third anticipated piece in our initial suite of Valhalla preview features (along with JEPs 401 and 402). It's also the first step in the revised generics story, to be followed up in the future with JVM enhancements for better performance (including species & type restrictions). This is entirely a language enhancement, and will be experienced by developers as a number of new warnings for generic classes and methods. Addressing the warnings makes generic APIs interoperate smoothly with primitive value types and prepares them for the future JVM enhancements. From amalloy at google.com Tue Jun 8 23:51:09 2021 From: amalloy at google.com (Alan Malloy) Date: Tue, 8 Jun 2021 16:51:09 -0700 Subject: JEP draft: Universal Generics In-Reply-To: References: Message-ID: > Type variables can thus range over almost any type I'm curious what exceptions you had in mind there. void? The "Null pollution and null warnings" section mentions > A warning occurs when a non-final field with a type variable type is left uninitialized by a constructor. which made me wonder: Are there any bounds where this warning isn't necessary? For example, what about ? I think the "Reference type variable types" section covers that, but maybe the prior section could be worded less universally. "with a type variable type that could be instantiated with a primitive type" or something. On Tue, Jun 8, 2021 at 2:21 PM Dan Smith wrote: > Please see this JEP draft: > > http://openjdk.java.net/jeps/8261529 > > This is the third anticipated piece in our initial suite of Valhalla > preview features (along with JEPs 401 and 402). It's also the first step in > the revised generics story, to be followed up in the future with JVM > enhancements for better performance (including species & type restrictions). > > This is entirely a language enhancement, and will be experienced by > developers as a number of new warnings for generic classes and methods. > Addressing the warnings makes generic APIs interoperate smoothly with > primitive value types and prepares them for the future JVM enhancements. > > From harold.seigel at oracle.com Wed Jun 9 14:30:04 2021 From: harold.seigel at oracle.com (Harold Seigel) Date: Wed, 9 Jun 2021 10:30:04 -0400 Subject: JEP draft: Better-defined JVM class file validation In-Reply-To: <8CE05B38-F90A-4A24-A13C-6A1B42B88788@oracle.com> References: <046E16C8-DC27-4266-853F-19623EC731D1@oracle.com> <67A334C1-E729-498A-AD28-B176C3F45F32@oracle.com> <1295647884.891877.1622996254815.JavaMail.zimbra@u-pem.fr> <8CE05B38-F90A-4A24-A13C-6A1B42B88788@oracle.com> Message-ID: <0def4cc6-6857-c0ab-d05b-da371f0a0131@oracle.com> The JVM does use the values of the InnerClasses attribute.? It uses the values to compile a set of class modifier flags and stores the set in the class mirror.? The set of flags is also used by JVM TI. So, rather than having, for example, JVM TI parse the contents of the InnerClasses attrbute, the JVM would still parse the attribute at class load time, but just ignore the attribute if it was invalid? Thanks, Harold On 6/8/2021 5:10 PM, Dan Smith wrote: >> Actually, the fact that the InnerClasses attribute is validated by the VM is confusing because it implies that the VM uses the values of that attributes to check access which it does not. >> For me, the spec says that those attributes are optionals so Hotspot should behave as the spec says. > The spec says some things that are unclear about how/whether InnerClass attributes should be validated, but yes, agree that there will be less ambiguity if we're totally hands-off. > From john.r.rose at oracle.com Thu Jun 10 00:15:29 2021 From: john.r.rose at oracle.com (John Rose) Date: Thu, 10 Jun 2021 00:15:29 +0000 Subject: JEP draft: Universal Generics In-Reply-To: References: Message-ID: On Jun 8, 2021, at 2:21 PM, Dan Smith wrote: > > Please see this JEP draft: > > http://openjdk.java.net/jeps/8261529 > > This is the third anticipated piece in our initial suite of Valhalla preview features (along with JEPs 401 and 402). It's also the first step in the revised generics story, to be followed up in the future with JVM enhancements for better performance (including species & type restrictions). > > This is entirely a language enhancement, and will be experienced by developers as a number of new warnings for generic classes and methods. Addressing the warnings makes generic APIs interoperate smoothly with primitive value types and prepares them for the future JVM enhancements. I like this JEP. I think it proposes reasonable tactics for repositioning type variables for success with Valhalla. From the way it reads (notably, where it says syntax is subject to change), it seems a provisional design, to be validated by actual experience using the language features to create new library APIs and adapt existing ones to help deal with null pollution gracefully. To put it negatively, I don?t fully trust the design here, until we have a chance to use it for some time with real APIs. But I think it?s very reasonable first cut. One item that is a wrong note for me is the place where you say, ?The proof is similar to the control-flow analysis that determines whether a variable has been initialized before use.? I know something about those proofs, having contributed the ?definite unassignment? rules in Java 1.1. The basic rules do not produce different answers along diverging control paths (such as the ?then? and ?else? sides of an if). The rules for flow-based assignment in pattern matching do something like this, with the ?assigned when true? type clauses, but they are tied to new testing sytnax (instanceof patterns). But null testing can be done in many ways, and so there is no ?bright line? for determining if a variable is null or not on a taken path. It?s a slippery slope. If you look at null-checking frameworks, or JITs, you?ll see dozens of rules regarding null deduction. Also, none of the existing DA/DU rules *change* the type of a variable; they just make it available or not available, but you are promising a rule which makes the *same* variable nullable along some paths and not nullable along others. That?s not a small or incremental change on the existing language machinery. All this is to say, I don?t think you can display a clean proof, based on a clean language design, that will get what you are claiming. I have a better proposal instead: Just make sure it is possible to build user-defined API points which have the appropriate null-isolation effects. For example, this should type-check, and should be a usable idiom for statically checked null control: foo(T.ref xOrNull) { T x = Objects.requireNonNull(xOrNull); } The internals of Objects.requireNonNull probably contain an unchecked cast from T.ref to T, where the language cannot ?see? the invariant, but the programmer can. A @SuppressWarnings completes the story. A similar point might work for the API of Class.cast, if we can figure out how to find the right Class witnesses: foo(Class witness, T.ref xOrNull) { T x = witness.cast(xOrNull); } foo(Class witness, T.ref xOrNull) { T x = witness.valueType().cast(xOrNull); } (The fact that valueType returns NONE instead of self is a problem here.) The rules for instanceof can also be adjusted to narrow from a target of T.ref to a variable binding of T. This is (IMO) a better use of language complexity than an open-ended hunting season for nulls. Anyway, I think the above ideas are less of a blind alley than promising magic flow checking of nulls. ? John From harold.seigel at oracle.com Fri Jun 11 18:38:58 2021 From: harold.seigel at oracle.com (Harold Seigel) Date: Fri, 11 Jun 2021 14:38:58 -0400 Subject: JEP draft: Better-defined JVM class file validation In-Reply-To: <046E16C8-DC27-4266-853F-19623EC731D1@oracle.com> References: <046E16C8-DC27-4266-853F-19623EC731D1@oracle.com> Message-ID: Hi Dan, * Rejecting non-static field declarations with|ConstantValue|attributes, similarly on the basis that if the attribute appears in the|attributes|table of a|field_info|structure, it should be recognized and checked. I tried implementing the above change and encountered errors when building the JDK because ConstantValue attributes were generated by javac for private final fields that were not static.? For example, these fields in jdk.tools.jlink.internal.plugins.SystemModulesPlugin had ConstantValue attributes: ??????? private final int BUILDER_VAR??? = 0; ??????? private final int MD_VAR???????? = 1;? // variable for ModuleDescriptor ??????? private final int MT_VAR???????? = 1;? // variable for ModuleTarget ??????? private final int MH_VAR???????? = 1;? // variable for ModuleHashes There were a few other additional cases such as field MAX_FLATER in ZipFileSystem.java and NUM_COMMON_CPOOL_ENTRIES in AccessorGenerator.java. Should? the above check be class file version dependent? Thanks, Harold On 6/4/2021 12:41 PM, Dan Smith wrote: > Posted a new JEP draft, here: > > http://openjdk.java.net/jeps/8267650 > > This is some preparatory work to allow us to more cleanly introduce the variety of new JVM features coming with primitive objects (JEP 401). > > Most of the spec work is already done; you can browse the proposed spec changes here: > http://cr.openjdk.java.net/~dlsmith/8267650/8267650-20210603/specs/ > From daniel.smith at oracle.com Wed Jun 16 00:04:47 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 16 Jun 2021 00:04:47 +0000 Subject: EG meeting, 2021-06-16 Message-ID: <0111F2FB-42A5-4C7A-979F-A3452A5B66F0@oracle.com> The next EG Zoom meeting is Wednesday at 4pm UTC (9am PDT, 12pm EDT). Topics to discuss: "JEP draft: Better-defined JVM class file validation": I created a JEP for some JVMS fixes and associated behavioral changes related to class file validation "JEP draft: Universal Generics": A JEP covering the language piece of generics for primitive value types "Making Object abstract": Some followup discussion here about the details & timing of a generic IdentityObject factory method From john.r.rose at oracle.com Wed Jun 16 21:50:35 2021 From: john.r.rose at oracle.com (John Rose) Date: Wed, 16 Jun 2021 21:50:35 +0000 Subject: Making Object abstract In-Reply-To: <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> References: <580432CB-6575-4C4E-92B5-83CAB6C73467@oracle.com> <65df60bb-100a-25f8-1145-b7091518f0fb@oracle.com> <87B00510-0F0C-4961-B2EA-6816AEF810ED@oracle.com> <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> Message-ID: <7973586C-D796-4164-B352-0F8D3B91BFFA@oracle.com> On Jun 2, 2021, at 7:57 AM, Brian Goetz wrote: > > A minor bikeshed comment: We're asking users to change their `new Object()` to `IdentityObject.newIdentity()`, and they may ask "why do I have to say 'Identity' twice"? (And by ask, I mean grumble, because we're already asking them to change their working code.) > > After a few minutes of thought, I think it might be a better fit to put this at Objects::newIdentity. The methods in Objects are conveniences that users could write themselves, which this basically is -- there's nothing special about this method, other than having a preferred alternative to `new Object()` which users will understand. So parking this where the Object conveniences go seems slightly lower friction. I think this is OK. As a stretch move, I think we can even retro-upgrade the type checking of Objects::newIdentity with type variable trickery, when IdentityObject becomes real. Please see: https://bugs.openjdk.java.net/secure/attachment/95170/Foo.java https://bugs.openjdk.java.net/browse/JDK-8268919 ? John From mcnepp02 at googlemail.com Thu Jun 17 05:22:16 2021 From: mcnepp02 at googlemail.com (Gernot Neppert) Date: Thu, 17 Jun 2021 07:22:16 +0200 Subject: Making Object abstract In-Reply-To: <7973586C-D796-4164-B352-0F8D3B91BFFA@oracle.com> References: <580432CB-6575-4C4E-92B5-83CAB6C73467@oracle.com> <65df60bb-100a-25f8-1145-b7091518f0fb@oracle.com> <87B00510-0F0C-4961-B2EA-6816AEF810ED@oracle.com> <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> <7973586C-D796-4164-B352-0F8D3B91BFFA@oracle.com> Message-ID: <0e898a83-fa2b-5c46-6202-774e8609de2e@gmail.com> A lot of thinking has gone into the effort of making Object abstract. And why is this huge effort undertaken? Because the valhalla-ready Java specification is supposed to state that every class implements exactly one of either "IdentityObject" or "PrimitiveObject". I'm wondering: can't we simply relax that requirement to "Every class implements either IdentityObject or PrimitveObject, except for java.lang.Object, which implements none of these". Yes, java.lang.Object would still be a special-case, but a much less painful one. Using "Object monitor = new Object()"; would continue to work indefinitely. The implications for compile-time checks would not be affected at all. With both proposals: - a future compile could issue warnings if "synchronized" is used on a variable for which it cannot determine that it references an IdentityObject. - likewise for invoking "wait()", "notify()" etc. Only the *runtime* behaviour would be affected. Under the current proposal: - Invoking a "synchronized" statement on an Object not implementing "IdentityObject" shall throw a RuntimeException. With my proposal: - Invoking a "synchronized" statement on an Object implementing "PrimitiveObject" shall throw a RuntimeException. Also on the plus-side of my propsal: only when you actually ship valhalla will you have to offer the method "public static IdentityObject newIdentityObject()". There's no need for a migration path. Am 16.06.2021 um 23:50 schrieb John Rose: > On Jun 2, 2021, at 7:57 AM, Brian Goetz wrote: >> A minor bikeshed comment: We're asking users to change their `new Object()` to `IdentityObject.newIdentity()`, and they may ask "why do I have to say 'Identity' twice"? (And by ask, I mean grumble, because we're already asking them to change their working code.) >> >> After a few minutes of thought, I think it might be a better fit to put this at Objects::newIdentity. The methods in Objects are conveniences that users could write themselves, which this basically is -- there's nothing special about this method, other than having a preferred alternative to `new Object()` which users will understand. So parking this where the Object conveniences go seems slightly lower friction. > I think this is OK. > > As a stretch move, I think we can even retro-upgrade > the type checking of Objects::newIdentity with type > variable trickery, when IdentityObject becomes real. > > Please see: > > https://bugs.openjdk.java.net/secure/attachment/95170/Foo.java > https://bugs.openjdk.java.net/browse/JDK-8268919 > > ? John From forax at univ-mlv.fr Thu Jun 17 11:40:09 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 17 Jun 2021 13:40:09 +0200 (CEST) Subject: Making Object abstract In-Reply-To: <7973586C-D796-4164-B352-0F8D3B91BFFA@oracle.com> References: <65df60bb-100a-25f8-1145-b7091518f0fb@oracle.com> <87B00510-0F0C-4961-B2EA-6816AEF810ED@oracle.com> <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> <7973586C-D796-4164-B352-0F8D3B91BFFA@oracle.com> Message-ID: <819395960.375124.1623930009189.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "John Rose" > ?: "Brian Goetz" > Cc: "daniel smith" , "valhalla-spec-experts" > Envoy?: Mercredi 16 Juin 2021 23:50:35 > Objet: Re: Making Object abstract > On Jun 2, 2021, at 7:57 AM, Brian Goetz wrote: >> >> A minor bikeshed comment: We're asking users to change their `new Object()` to >> `IdentityObject.newIdentity()`, and they may ask "why do I have to say >> 'Identity' twice"? (And by ask, I mean grumble, because we're already asking >> them to change their working code.) >> >> After a few minutes of thought, I think it might be a better fit to put this at >> Objects::newIdentity. The methods in Objects are conveniences that users could >> write themselves, which this basically is -- there's nothing special about this >> method, other than having a preferred alternative to `new Object()` which users >> will understand. So parking this where the Object conveniences go seems >> slightly lower friction. > > I think this is OK. > > As a stretch move, I think we can even retro-upgrade > the type checking of Objects::newIdentity with type > variable trickery, when IdentityObject becomes real. > > Please see: > > https://bugs.openjdk.java.net/secure/attachment/95170/Foo.java > https://bugs.openjdk.java.net/browse/JDK-8268919 I wonder if a simple way to avoid to allow any Ts if to allow to specify an intersection type as parameter type and/or return type of methods (we have 'var' inside the body) so instead of public static T newIdentity() { we can write public static Object & IdentityObject newIdentity() { This requires a grammar change but it's exactly the type we want. > > ? John R?mi From brian.goetz at oracle.com Wed Jun 23 15:13:27 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 23 Jun 2021 11:13:27 -0400 Subject: JEP 401 -- reflection and class literals Message-ID: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> In working through the details of reflective support in JEP 401, I think we've fallen into a slight "false consistency" regarding class literals. (This is a complicated network with many interrelated moving parts, so I'm not looking for "quick answers" here, as much as balancing global consistency with an understandable user story.) In the language, a primitive class declaration gives rise to a class (P) and three types: P, P.ref and P.val.? P is an alias for one of them; most of the time, it's an alias for P.val, but for migrated value-based class, it's an alias for P.ref.? The language has a concept for tuning this mapping, currently under the name `ref-default`.? (The VM would like to remain ignorant of details like this.) In the VM, there is one class (P), whose instances can have two representations, flattened and indirect, described by two descriptors (QP and LP). The VM and reflection need mirrors for both of these descriptor types.? This is a problem we face today to some degree for the primitive types; there's a "neutered" mirror for int.class that doesn't correspond to an actual declared class with limited operational capability.? These "secondary" mirrors are only used for reflection / method handles, to reflect method and field descriptors. We double down on this story for mirrors for primitive classes. Each primitive class has a primary (unrestricted) mirror corresponding to the L descriptor, and a secondary (restricted, flattened, possibly null-free) mirror corresponding to the Q descriptor.? The secondary mirror has only one job: reflecting Q descriptors in method and field descriptors (and supporting method handles in their emulation of same.) When you ask an object for getClass(), it always hands back the primary mirror. As a bonus, this has a nice compatibility story with Integer today; reflective code is used to testing for Integer.class when an int is expected (reflection is always boxed), so this will continue to work, because instances of `int` will report `Integer.class` from `getClass()`. All of this feels like the low-energy-state for extending mirrors to primitives.? Here's where I think we bobbled a little bit, and can correct. Currently, we say "Well, if Point is an alias for Point.val, then Point.class must be Point.val.class".? This is consistent, but kind of useless.? Because then, for example: ??? void puzzler() { ??????? assertEquals(Point.class, new Point(0,0).getClass()); ??? } will fail (unless Point is ref-default, at which point it succeeds.)? I think the mistake we made is too literally following the model of "Point is an alias for Point.val".? So, I'm proposing a slight adjustment, that treats the unqualified class name contextually: ?- In the context of a variable type, Point is an alias for Point.val, or Point.ref, if ref-default (no change from current); ?- Where a class is needed (e.g., new Point(), instanceof Point), Point refers to the class Point, not any of its derived types, and Point.{ref,val} are not valid in these contexts (no change from current); ?- Where a reflective literal is needed, the unqualified Point.class is always the primary mirror, which always matches the behavior of Object::getClass (change); ?- If an explicit literal is needed for reflection (e.g., calling getMethod()), you can be explicit and say Point.{ref,val}.class to say what you mean (no change). This aligns the meaning of "Point.class" with the actual behavior of other reflective API points. From daniel.smith at oracle.com Wed Jun 23 16:44:47 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 23 Jun 2021 16:44:47 +0000 Subject: JEP 401 -- reflection and class literals In-Reply-To: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> Message-ID: > On Jun 23, 2021, at 9:13 AM, Brian Goetz wrote: > > - In the context of a variable type, Point is an alias for Point.val, or Point.ref, if ref-default (no change from current); > - Where a class is needed (e.g., new Point(), instanceof Point), Point refers to the class Point, not any of its derived types, and Point.{ref,val} are not valid in these contexts (no change from current); > - Where a reflective literal is needed, the unqualified Point.class is always the primary mirror, which always matches the behavior of Object::getClass (change); > - If an explicit literal is needed for reflection (e.g., calling getMethod()), you can be explicit and say Point.{ref,val}.class to say what you mean (no change). > > This aligns the meaning of "Point.class" with the actual behavior of other reflective API points. I find that all solutions in this space tend to be bad in one way or another; this one seems as good as, or better than, most. Where it is weakest is when someone wants to talk about both classes and types in the same vicinity, and would rather not think through the subtle distinctions. Example: primitive record Point(int x, int y) { double distance(Point p2) { ... } } assert new Point(1, 2).getClass() == Point.class; // good assert Point.class.getMethod("distance", Point.class) != null; // not good, have to say Point.val.class We can "fix" this behavior by supporting "fuzzy matching" in the 'getMethod' method, so that both Point.val.class and Point.ref.class are considered matches for Point.val.class in method signatures. That feels to me like a bridge too far in our efforts to hide complexity from API users. YMMV. (Also doesn't work great if the language supports val/ref overloads; I think we lean towards *not* supporting these.) --- For completeness, here are a couple of other solutions we talked about, both of which are plausible, but we haven't enthusiastically embraced them: 1) Orient reflection more towards the language by tying 'getClass' to the ref-default flag. So the "primary mirror" is the L type for ref-default primitive classes, and the Q type for others. 'getClass' always returns the primary mirror, so always returns Foo.class (where 'Foo.class' always means the same thing as the type 'Foo'). Big problem here is tying such core runtime behavior to the ref-default flag, which we've envisioned as a pure compile-time name resolution feature. JVM internals may be uncomfortable with this definition of "primary mirror", depending on how closely they are tied to java.lang.Class. 2) Keep pulling on the thread of "people just want to say Foo.class" by backing off of the "one java.lang.Class per descriptor type" invariant. Instead, there is just one Point.class; that's what you get from 'getClass()', that's what you see when you query Fields for their type (whether it's Point.val or Point.ref), that's what you use to match 'getMethod'. APIs that need more precise expressiveness (MethodHandle) should use a different abstraction. This gets us out of the problem of wanting most users to pretend that Point.val.class and Point.ref.class are the same, except where the seams are exposed (like ==). Biggest problem here is that changing something like MethodHandle to no longer build on java.lang.Class is a significant change, with tentacles in the JVM. From brian.goetz at oracle.com Wed Jun 23 18:39:02 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 23 Jun 2021 14:39:02 -0400 Subject: JEP 401 -- reflection and class literals In-Reply-To: References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> Message-ID: <84024fc7-5b86-75f4-c755-8c95cd3b5a9f@oracle.com> > We can "fix" this behavior by supporting "fuzzy matching" in the 'getMethod' method, so that both Point.val.class and Point.ref.class are considered matches for Point.val.class in method signatures. That feels to me like a bridge too far in our efforts to hide complexity from API users. YMMV. (Also doesn't work great if the language supports val/ref overloads; I think we lean towards *not* supporting these.) We can surely try to prevent them, but I don't think it really does it much good in this area.? We will surely not want the JVM to be trying to figure out at class load time that: ??? class Foo { (LPoint;LString;I)V m } ??? class Bar extends Foo { (QPoint;LString;I) m } that Bar is an invalid class.? So given that classfiles will have these potential conflicts, getMethod(Point.class, String.class, int.class) would have to do the fuzzy thing anyway, and that's a mess. From daniel.smith at oracle.com Wed Jun 23 18:54:30 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 23 Jun 2021 18:54:30 +0000 Subject: JEP 401 -- reflection and class literals In-Reply-To: <84024fc7-5b86-75f4-c755-8c95cd3b5a9f@oracle.com> References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> <84024fc7-5b86-75f4-c755-8c95cd3b5a9f@oracle.com> Message-ID: <444FBE45-BB43-47BA-934A-8C3E522754EB@oracle.com> > On Jun 23, 2021, at 12:39 PM, Brian Goetz wrote: > > >> We can "fix" this behavior by supporting "fuzzy matching" in the 'getMethod' method, so that both Point.val.class and Point.ref.class are considered matches for Point.val.class in method signatures. That feels to me like a bridge too far in our efforts to hide complexity from API users. YMMV. (Also doesn't work great if the language supports val/ref overloads; I think we lean towards *not* supporting these.) > > We can surely try to prevent them, but I don't think it really does it much good in this area. We will surely not want the JVM to be trying to figure out at class load time that: > > class Foo { (LPoint;LString;I)V m } > class Bar extends Foo { (QPoint;LString;I) m } > > that Bar is an invalid class. So given that classfiles will have these potential conflicts, getMethod(Point.class, String.class, int.class) would have to do the fuzzy thing anyway, and that's a mess. Oh, sure, not suggesting a JVM validation check here. However, if the language rejects these overloads, then reflection could do something reasonable to just pick one when it encounters them (just like you can overload on return types in the JVM, but 'getMethod' doesn't let you explicitly disambiguate). But anyway, if there are distinct Class objects for QPoint and LPoint, I don't love the idea of 'getMethod' letting the LPoint class object ever match a QPoint descriptor, regardless of any overloads present. From john.r.rose at oracle.com Wed Jun 23 22:43:32 2021 From: john.r.rose at oracle.com (John Rose) Date: Wed, 23 Jun 2021 22:43:32 +0000 Subject: [External] : Re: Making Object abstract In-Reply-To: <819395960.375124.1623930009189.JavaMail.zimbra@u-pem.fr> References: <65df60bb-100a-25f8-1145-b7091518f0fb@oracle.com> <87B00510-0F0C-4961-B2EA-6816AEF810ED@oracle.com> <50b40e1e-ac19-b97b-afc6-43de88f5ee66@oracle.com> <78db0322-c462-a062-db75-71e18cfad4b5@oracle.com> <7973586C-D796-4164-B352-0F8D3B91BFFA@oracle.com> <819395960.375124.1623930009189.JavaMail.zimbra@u-pem.fr> Message-ID: On Jun 17, 2021, at 4:40 AM, Remi Forax > wrote: As a stretch move, I think we can even retro-upgrade the type checking of Objects::newIdentity with type variable trickery, when IdentityObject becomes real. Please see: https://bugs.openjdk.java.net/secure/attachment/95170/Foo.java https://bugs.openjdk.java.net/browse/JDK-8268919 I wonder if a simple way to avoid to allow any Ts if to allow to specify an intersection type as parameter type and/or return type of methods (we have 'var' inside the body) so instead of public static T newIdentity() { we can write public static Object & IdentityObject newIdentity() { This requires a grammar change but it's exactly the type we want. Yes. I was trying to avoid that. My attempt (see above) is wrong because (as Dan points out) it can infer T to be String, which is false. The type variable T would need lower bound of Identity, as well as an upper bound (and erasure) of Object. But, as with return type intersections, there is no syntax to express that, apparently. Only wildcards can have lower bounds, right? And you can?t have a (lower-bounded) wildcard for a return type: public static List newIdentityList(); //OK public static ? super IdentityObject newIdentity(); //not OK From mcnepp02 at googlemail.com Thu Jun 24 07:16:41 2021 From: mcnepp02 at googlemail.com (Gernot Neppert) Date: Thu, 24 Jun 2021 09:16:41 +0200 Subject: JEP 401 -- reflection and class literals In-Reply-To: References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> Message-ID: <7e44d83e-ecf6-8981-371c-b7993f0191fd@gmail.com> I'm reading your various proposals to reconcile descriptors with type-mirrors, and they all seem to have their awkward corner-cases. Granted, we have "Q-type" descriptor vs. "L-type" descriptors for class members, method argumens and method return-types, and we need to make this disctinction visible *somehow* via reflection. But: do we really need to have two different type-mirrors corresponding to these descriptors? Isn't the "Q-ness" rather a property of the Field, method Parameter or method return-type, and therefore should be available there via reflection as a simple boolean property? Given a value-favoring class "Point" and the following declaration: public class Foo { ??? Point.ref pt; ?? public void setPoint(Point p) { ??? ??? this.pt = pt; ??? } ??? public Point.ref getPoint() { ??? ??? return pt; ??? } } Extending the reflection API for the corresponding classes from java.lang.reflect would look something like this: assertFalse(Point.class.getDeclaredField("pt").isByValue()); assertTrue(Point.class.getMethod("setPoint", Point.class).getParameters()[0].isByValue()); assertFalse(Point.class.getMethod("getPoint").getParameters()[0].isReturnByValue()); Am 23.06.2021 um 18:44 schrieb Dan Smith: >> On Jun 23, 2021, at 9:13 AM, Brian Goetz wrote: >> >> - In the context of a variable type, Point is an alias for Point.val, or Point.ref, if ref-default (no change from current); >> - Where a class is needed (e.g., new Point(), instanceof Point), Point refers to the class Point, not any of its derived types, and Point.{ref,val} are not valid in these contexts (no change from current); >> - Where a reflective literal is needed, the unqualified Point.class is always the primary mirror, which always matches the behavior of Object::getClass (change); >> - If an explicit literal is needed for reflection (e.g., calling getMethod()), you can be explicit and say Point.{ref,val}.class to say what you mean (no change). >> >> This aligns the meaning of "Point.class" with the actual behavior of other reflective API points. > I find that all solutions in this space tend to be bad in one way or another; this one seems as good as, or better than, most. > > Where it is weakest is when someone wants to talk about both classes and types in the same vicinity, and would rather not think through the subtle distinctions. Example: > > primitive record Point(int x, int y) { > double distance(Point p2) { ... } > } > > assert new Point(1, 2).getClass() == Point.class; // good > assert Point.class.getMethod("distance", Point.class) != null; // not good, have to say Point.val.class > > We can "fix" this behavior by supporting "fuzzy matching" in the 'getMethod' method, so that both Point.val.class and Point.ref.class are considered matches for Point.val.class in method signatures. That feels to me like a bridge too far in our efforts to hide complexity from API users. YMMV. (Also doesn't work great if the language supports val/ref overloads; I think we lean towards *not* supporting these.) > > --- > > For completeness, here are a couple of other solutions we talked about, both of which are plausible, but we haven't enthusiastically embraced them: > > 1) Orient reflection more towards the language by tying 'getClass' to the ref-default flag. So the "primary mirror" is the L type for ref-default primitive classes, and the Q type for others. 'getClass' always returns the primary mirror, so always returns Foo.class (where 'Foo.class' always means the same thing as the type 'Foo'). > > Big problem here is tying such core runtime behavior to the ref-default flag, which we've envisioned as a pure compile-time name resolution feature. JVM internals may be uncomfortable with this definition of "primary mirror", depending on how closely they are tied to java.lang.Class. > > 2) Keep pulling on the thread of "people just want to say Foo.class" by backing off of the "one java.lang.Class per descriptor type" invariant. Instead, there is just one Point.class; that's what you get from 'getClass()', that's what you see when you query Fields for their type (whether it's Point.val or Point.ref), that's what you use to match 'getMethod'. APIs that need more precise expressiveness (MethodHandle) should use a different abstraction. > > This gets us out of the problem of wanting most users to pretend that Point.val.class and Point.ref.class are the same, except where the seams are exposed (like ==). Biggest problem here is that changing something like MethodHandle to no longer build on java.lang.Class is a significant change, with tentacles in the JVM. > From peter.levart at gmail.com Sat Jun 26 15:41:16 2021 From: peter.levart at gmail.com (Peter Levart) Date: Sat, 26 Jun 2021 17:41:16 +0200 Subject: JEP 401 -- reflection and class literals In-Reply-To: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> Message-ID: On Wed, 23 Jun 2021, 17:13 Brian Goetz, wrote: > > > When you ask an object for getClass(), it always hands back the primary > mirror. > > As a bonus, this has a nice compatibility story with Integer today; > reflective code is used to testing for Integer.class when an int is > expected (reflection is always boxed), so this will continue to work, > because instances of `int` will report `Integer.class` from `getClass()`. > I haven't been following the story closely, so please excuse me for asking: why this is so? Doesn't VM always "know" which type it is dealing with when .getClass() (or any instance method) is called? Oh, I know these are just hints. VM can flatten the representation, but it is not obliged to do so. Anyway, doesn't VM always "know" which "hint" is in effect? What I'm asking is this: why couldn't the following always hold (local vars): Foo.val v = ... Foo.ref r = ... assert v.getClass() == Foo.val.class; assert r.getClass() == Foo.ref.class; There must be an obvious answer that I can't see. Is it too hard implementation wise? If above could hold, then there would be no problem for Foo.class to follow the ref-default / val-default-ness... > > void puzzler() { > assertEquals(Point.class, new Point(0,0).getClass()); > } > This would succeed regardless of val-default or ref-default-ness of Point class. > Regards, Peter > From forax at univ-mlv.fr Sun Jun 27 09:58:05 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 27 Jun 2021 11:58:05 +0200 (CEST) Subject: JEP 401 -- reflection and class literals In-Reply-To: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> Message-ID: <1550290072.7564.1624787885215.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "valhalla-spec-experts" > Sent: Mercredi 23 Juin 2021 17:13:27 > Subject: JEP 401 -- reflection and class literals > In working through the details of reflective support in JEP 401, I think we've > fallen into a slight "false consistency" regarding class literals. (This is a > complicated network with many interrelated moving parts, so I'm not looking for > "quick answers" here, as much as balancing global consistency with an > understandable user story.) > In the language, a primitive class declaration gives rise to a class (P) and > three types: P, P.ref and P.val. P is an alias for one of them; most of the > time, it's an alias for P.val, but for migrated value-based class, it's an > alias for P.ref. The language has a concept for tuning this mapping, currently > under the name `ref-default`. (The VM would like to remain ignorant of details > like this.) > In the VM, there is one class (P), whose instances can have two representations, > flattened and indirect, described by two descriptors (QP and LP). > The VM and reflection need mirrors for both of these descriptor types. This is a > problem we face today to some degree for the primitive types; there's a > "neutered" mirror for int.class that doesn't correspond to an actual declared > class with limited operational capability. These "secondary" mirrors are only > used for reflection / method handles, to reflect method and field descriptors. > We double down on this story for mirrors for primitive classes. Each primitive > class has a primary (unrestricted) mirror corresponding to the L descriptor, > and a secondary (restricted, flattened, possibly null-free) mirror > corresponding to the Q descriptor. The secondary mirror has only one job: > reflecting Q descriptors in method and field descriptors (and supporting method > handles in their emulation of same.) > When you ask an object for getClass(), it always hands back the primary mirror. yes ! > As a bonus, this has a nice compatibility story with Integer today; reflective > code is used to testing for Integer.class when an int is expected (reflection > is always boxed), so this will continue to work, because instances of `int` > will report `Integer.class` from `getClass()`. yes ! > All of this feels like the low-energy-state for extending mirrors to primitives. > Here's where I think we bobbled a little bit, and can correct. > Currently, we say "Well, if Point is an alias for Point.val, then Point.class > must be Point.val.class". This is consistent, but kind of useless. Because > then, for example: > void puzzler() { > assertEquals(Point.class, new Point(0,0).getClass()); > } > will fail (unless Point is ref-default, at which point it succeeds.) I think the > mistake we made is too literally following the model of "Point is an alias for > Point.val". So, I'm proposing a slight adjustment, that treats the unqualified > class name contextually: > - In the context of a variable type, Point is an alias for Point.val, or > Point.ref, if ref-default (no change from current); > - Where a class is needed (e.g., new Point(), instanceof Point), Point refers to > the class Point, not any of its derived types, and Point.{ref,val} are not > valid in these contexts (no change from current); > - Where a reflective literal is needed, the unqualified Point.class is always > the primary mirror, which always matches the behavior of Object::getClass > (change); > - If an explicit literal is needed for reflection (e.g., calling getMethod()), > you can be explicit and say Point.{ref,val}.class to say what you mean (no > change). > This aligns the meaning of "Point.class" with the actual behavior of other > reflective API points. I disagree on your last bullet point, I think that less is more in that context, we do not have to have a syntax to express the secondary class exactly like we do not allow Foo.class. So instead of allowing Point.ref.class and Point.val.class, i think it's better to have no language support given that we can already write Point.class.asPrimitiveObjectClass(). Not having a syntax for Point.ref.class/Point.val.class is important because as you say it's inconsistent, for good, with Point being either an identity class or a primitive object class. Not surfacing that inconsistency in the language make things easier to understand and using methods of java.lang.Class instead make the intent more clear at the expanse of being more verbose. regards, R?mi From peter.levart at gmail.com Sun Jun 27 16:42:04 2021 From: peter.levart at gmail.com (Peter Levart) Date: Sun, 27 Jun 2021 18:42:04 +0200 Subject: JEP 401 -- reflection and class literals In-Reply-To: References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> Message-ID: On 26/06/2021 17:41, Peter Levart wrote: > What I'm asking is this: why couldn't the following always hold (local > vars): > > Foo.val v = ... > Foo.ref r = ... > > assert v.getClass() == Foo.val.class; > assert r.getClass() == Foo.ref.class; I knew this was a stupid question. Immediately after considering: Object vo = v; Object ro = r; assert vo.getClass() == ???; assert ro.getClass() == ???; ...I realized that the primitive class type (val vs. ref) is not a property of the object, but rather the property of how its value is passed around and stored by VM. Since .getClass() can only return something that characterizes the object and not how its value is stored or passed, the primitive class type (through Q or L descriptors) is more a property of a field or array element or method parameter or method return as Gernot Nappert said in his reply: On 24/06/2021 09:16, Gernot Neppert wrote: > I'm reading your various proposals to reconcile descriptors with > type-mirrors, and they all seem to have their awkward corner-cases. > > Granted, we have "Q-type" descriptor vs. "L-type" descriptors for > class members, method argumens and method return-types, and we need to > make this disctinction visible *somehow* via reflection. > > But: do we really need to have two different type-mirrors > corresponding to these descriptors? > Isn't the "Q-ness" rather a property of the Field, method Parameter or > method return-type, and therefore should be available there via > reflection as a simple boolean property? > > Given a value-favoring class "Point" and the following declaration: > > public class Foo { > > ??? Point.ref pt; > > ?? public void setPoint(Point p) { > ??? ??? this.pt = pt; > > ??? } > > ??? public Point.ref getPoint() { > ??? ??? return pt; > ??? } > } > > Extending the reflection API for the corresponding classes from > java.lang.reflect would look something like this: > > assertFalse(Point.class.getDeclaredField("pt").isByValue()); > > assertTrue(Point.class.getMethod("setPoint", > Point.class).getParameters()[0].isByValue()); > > assertFalse(Point.class.getMethod("getPoint").getParameters()[0].isReturnByValue()); Extending reflection API in this way could work for querying the properties of fields, methods and constructors, but there are also those "find me a method / constructor with specific parameter types" reflection methods: Class.get(Declared)Method(String name, Class... parameterTypes) Class.get(Declared)Constructor(Class... parameterTypes) It would be possible, but awkward to add methods like: Class.get(Declared)Method(String name, Class[] parameterTypes, boolean[] parameterByValue) Seems like we are forced to re-use Class objects to hold this additional info. Remi says that there should only be "primary" (ref type) primitive class literals (Foo.class) and no (Foo.ref.class or Foo.val.class) and that val type mirrors should be obtained by calling a method (such as Foo.class.asPrimitiveValType()), but that makes methods/constructors using val-typed parameters second class citizens when using reflection. There's nothing wrong with Foo.ref.class and Foo.val.class per-se. It seems the question is more whether we actually need type mirrors for val-types and not how to obtain them (Foo.val.class is surely nicer than Foo.class.asPrimitiveValType() and not a bit less expressive). We already have two kinds of primitive class type mirrors since primitive types are going to be retro-fitted as primitive classes (int.class vs. Integer.class). So today, when you say: Math.class.getMethod("min", Integer.class, Integer.class) ...you get "NoSuchMethodException", so there's no fuzziness in matching the parameter types. You have to use the correct type mirrors: Math.class.getMethod("min", int.class, int.class) So I thing introducing fuzziness for primitive class parameter types except for retrofitted primitive types would just make things more irregular. I also think what Brian proposed (Foo.class is aligned with .getClass()) is pretty consistent when you think about it long enough. Regards, Peter From mcnepp02 at googlemail.com Sun Jun 27 17:54:52 2021 From: mcnepp02 at googlemail.com (Gernot Neppert) Date: Sun, 27 Jun 2021 19:54:52 +0200 Subject: JEP 401 -- reflection and class literals In-Reply-To: References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> Message-ID: <657880fb-159e-1c1b-522e-b82843fbe647@gmail.com> Am 27.06.2021 um 18:42 schrieb Peter Levart: > ...I realized that the primitive class type (val vs. ref) is not a > property of the object, but rather the property of how its value is > passed around and stored by VM. Since .getClass() can only return > something that characterizes the object and not how its value is > stored or passed, the primitive class type (through Q or L > descriptors) is more a property of a field or array element or method > parameter or method return as Gernot Nappert said in his reply Exactly - thank you for acknowleding the point I was trying to make! > Extending reflection API in this way could work for querying the > properties of fields, methods and constructors, but there are also > those "find me a method / constructor with specific parameter types" > reflection methods: > > Class.get(Declared)Method(String name, Class... parameterTypes) > Class.get(Declared)Constructor(Class... parameterTypes) > > It would be possible, but awkward to add methods like: > > Class.get(Declared)Method(String name, Class[] parameterTypes, > boolean[] parameterByValue) > > Seems like we are forced to re-use Class objects to hold this > additional info. Hmm, if you otherwise agree that we do not need distinct type-mirrors, this argument looks a bit weak to me. If there was only one type mirror per primitive class and thus no overloading on "by-value-ness" possible, I don't think you would actually need those kind of query-methods. It would surely suffice to be able to test the "by-value-ness" of? the parameters after getting hold of the method/constructor! From brian.goetz at oracle.com Sun Jun 27 20:01:21 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 27 Jun 2021 16:01:21 -0400 Subject: [External] : Re: JEP 401 -- reflection and class literals In-Reply-To: References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> Message-ID: > > > Seems like we are forced to re-use Class objects to hold this > additional info. Remi says that there should only be "primary" (ref > type) primitive class literals (Foo.class) and no (Foo.ref.class or > Foo.val.class) and that val type mirrors should be obtained by calling > a method (such as Foo.class.asPrimitiveValType()), but that makes > methods/constructors using val-typed parameters second class citizens > when using reflection. And even worse with MethodHandle. From forax at univ-mlv.fr Sun Jun 27 21:27:42 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 27 Jun 2021 23:27:42 +0200 (CEST) Subject: [External] : Re: JEP 401 -- reflection and class literals In-Reply-To: References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> Message-ID: <1180405842.70470.1624829262712.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Brian Goetz" > To: "Peter Levart" > Cc: "valhalla-spec-experts" > Sent: Dimanche 27 Juin 2021 22:01:21 > Subject: Re: [External] : Re: JEP 401 -- reflection and class literals >> >> >> Seems like we are forced to re-use Class objects to hold this >> additional info. Remi says that there should only be "primary" (ref >> type) primitive class literals (Foo.class) and no (Foo.ref.class or >> Foo.val.class) and that val type mirrors should be obtained by calling >> a method (such as Foo.class.asPrimitiveValType()), but that makes >> methods/constructors using val-typed parameters second class citizens >> when using reflection. > > And even worse with MethodHandle. java.lang.reflect and java.lang.invoke are actually second class citizen APIs, they are built that way. At least until we have to deal with API points throwing Throwable where the only sensible way to deal with it is to rethrow it. R?mi From brian.goetz at oracle.com Tue Jun 29 16:48:25 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 29 Jun 2021 12:48:25 -0400 Subject: JEP 401 -- reflection and class literals In-Reply-To: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> Message-ID: <8dbc9e88-c859-016d-4733-44b16b7da9d3@oracle.com> The general consensus here is that this stacking is slightly better than the previous one, so let's take this as the plan of record.? Now, to explore the next turn of the crank... The concerns that were raised could be characterized by "do we *really* need to even have separate class literals for Foo.ref and Foo.val?"? And the answer is, sort of yes, sort of no, so there is a possible next turn of the crank that might or might not be a further improvement. Two existing constraints we have are: ?- Reflection (and MethodHandle) already do everything with jl.Class.? That means, for example, we had to invent the primitive mirrors a long time ago, so that you could describe a field or method parameter of type int.? "Fixing" this is not really on the table right now. ?- The language has given this some surface; you can say `int.class` (which maps to`getstatic Integer.TYPE`.) Given all this, reflection needs to be able to differentiate between LFoo and QFoo, which suggests a second mirror.? And the user needs to be able to express this mirror to call getMethod() or Lookup::findXxx (and to interpret the results of reflective lookups).? But maybe we don't need to give it so much air *in the language*, especially given how confusing it is because the language and VM views (which reflection is closer to) don't align entirely. Rather than talking about the ref vs val mirror (which is confusing, because getClass returns the same thing whether something is stored in a .ref or a .val), perhaps we can reframe in terms of the "class mirror and the flattened representation mirror", or the "primary mirror and the restricted mirror", or some other such (though, even the term "mirror" is foreign to users, since all the see is jl.Class.) In this model, Point.class would be the only mirror expressible directly in the language, and it would always be the primary mirror.? Reflection users would need to navigate to the secondary mirror through something like `Point.class.secondaryMirror()` or some other method on jl.Class.? By not putting this in the language, we avoid some of the confusing aspects of this story. On 6/23/2021 11:13 AM, Brian Goetz wrote: > In working through the details of reflective support in JEP 401, I > think we've fallen into a slight "false consistency" regarding class > literals. (This is a complicated network with many interrelated moving > parts, so I'm not looking for "quick answers" here, as much as > balancing global consistency with an understandable user story.) > > In the language, a primitive class declaration gives rise to a class > (P) and three types: P, P.ref and P.val.? P is an alias for one of > them; most of the time, it's an alias for P.val, but for migrated > value-based class, it's an alias for P.ref. The language has a concept > for tuning this mapping, currently under the name `ref-default`.? (The > VM would like to remain ignorant of details like this.) > > In the VM, there is one class (P), whose instances can have two > representations, flattened and indirect, described by two descriptors > (QP and LP). > > The VM and reflection need mirrors for both of these descriptor > types.? This is a problem we face today to some degree for the > primitive types; there's a "neutered" mirror for int.class that > doesn't correspond to an actual declared class with limited > operational capability.? These "secondary" mirrors are only used for > reflection / method handles, to reflect method and field descriptors. > > We double down on this story for mirrors for primitive classes.? Each > primitive class has a primary (unrestricted) mirror corresponding to > the L descriptor, and a secondary (restricted, flattened, possibly > null-free) mirror corresponding to the Q descriptor.? The secondary > mirror has only one job: reflecting Q descriptors in method and field > descriptors (and supporting method handles in their emulation of same.) > > When you ask an object for getClass(), it always hands back the > primary mirror. > > As a bonus, this has a nice compatibility story with Integer today; > reflective code is used to testing for Integer.class when an int is > expected (reflection is always boxed), so this will continue to work, > because instances of `int` will report `Integer.class` from `getClass()`. > > All of this feels like the low-energy-state for extending mirrors to > primitives.? Here's where I think we bobbled a little bit, and can > correct. > > Currently, we say "Well, if Point is an alias for Point.val, then > Point.class must be Point.val.class".? This is consistent, but kind of > useless.? Because then, for example: > > ??? void puzzler() { > ??????? assertEquals(Point.class, new Point(0,0).getClass()); > ??? } > > will fail (unless Point is ref-default, at which point it succeeds.)? > I think the mistake we made is too literally following the model of > "Point is an alias for Point.val".? So, I'm proposing a slight > adjustment, that treats the unqualified class name contextually: > > ?- In the context of a variable type, Point is an alias for Point.val, > or Point.ref, if ref-default (no change from current); > ?- Where a class is needed (e.g., new Point(), instanceof Point), > Point refers to the class Point, not any of its derived types, and > Point.{ref,val} are not valid in these contexts (no change from current); > ?- Where a reflective literal is needed, the unqualified Point.class > is always the primary mirror, which always matches the behavior of > Object::getClass (change); > ?- If an explicit literal is needed for reflection (e.g., calling > getMethod()), you can be explicit and say Point.{ref,val}.class to say > what you mean (no change). > > This aligns the meaning of "Point.class" with the actual behavior of > other reflective API points. > > From forax at univ-mlv.fr Tue Jun 29 17:22:47 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 29 Jun 2021 19:22:47 +0200 (CEST) Subject: JEP 401 -- reflection and class literals In-Reply-To: <8dbc9e88-c859-016d-4733-44b16b7da9d3@oracle.com> References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> <8dbc9e88-c859-016d-4733-44b16b7da9d3@oracle.com> Message-ID: <924922954.1276403.1624987367654.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "valhalla-spec-experts" > Sent: Mardi 29 Juin 2021 18:48:25 > Subject: Re: JEP 401 -- reflection and class literals > The general consensus here is that this stacking is slightly better than the > previous one, so let's take this as the plan of record. Now, to explore the > next turn of the crank... > The concerns that were raised could be characterized by "do we *really* need to > even have separate class literals for Foo.ref and Foo.val?" And the answer is, > sort of yes, sort of no, so there is a possible next turn of the crank that > might or might not be a further improvement. > Two existing constraints we have are: > - Reflection (and MethodHandle) already do everything with jl.Class. That means, > for example, we had to invent the primitive mirrors a long time ago, so that > you could describe a field or method parameter of type int. "Fixing" this is > not really on the table right now. > - The language has given this some surface; you can say `int.class` (which maps > to`getstatic Integer.TYPE`.) > Given all this, reflection needs to be able to differentiate between LFoo and > QFoo, which suggests a second mirror. And the user needs to be able to express > this mirror to call getMethod() or Lookup::findXxx (and to interpret the > results of reflective lookups). But maybe we don't need to give it so much air > *in the language*, especially given how confusing it is because the language > and VM views (which reflection is closer to) don't align entirely. > Rather than talking about the ref vs val mirror (which is confusing, because > getClass returns the same thing whether something is stored in a .ref or a > .val), perhaps we can reframe in terms of the "class mirror and the flattened > representation mirror", or the "primary mirror and the restricted mirror", or > some other such (though, even the term "mirror" is foreign to users, since all > the see is jl.Class.) > In this model, Point.class would be the only mirror expressible directly in the > language, and it would always be the primary mirror. Reflection users would > need to navigate to the secondary mirror through something like > `Point.class.secondaryMirror()` or some other method on jl.Class. By not > putting this in the language, we avoid some of the confusing aspects of this > story. +1 R?mi > On 6/23/2021 11:13 AM, Brian Goetz wrote: >> In working through the details of reflective support in JEP 401, I think we've >> fallen into a slight "false consistency" regarding class literals. (This is a >> complicated network with many interrelated moving parts, so I'm not looking for >> "quick answers" here, as much as balancing global consistency with an >> understandable user story.) >> In the language, a primitive class declaration gives rise to a class (P) and >> three types: P, P.ref and P.val. P is an alias for one of them; most of the >> time, it's an alias for P.val, but for migrated value-based class, it's an >> alias for P.ref. The language has a concept for tuning this mapping, currently >> under the name `ref-default`. (The VM would like to remain ignorant of details >> like this.) >> In the VM, there is one class (P), whose instances can have two representations, >> flattened and indirect, described by two descriptors (QP and LP). >> The VM and reflection need mirrors for both of these descriptor types. This is a >> problem we face today to some degree for the primitive types; there's a >> "neutered" mirror for int.class that doesn't correspond to an actual declared >> class with limited operational capability. These "secondary" mirrors are only >> used for reflection / method handles, to reflect method and field descriptors. >> We double down on this story for mirrors for primitive classes. Each primitive >> class has a primary (unrestricted) mirror corresponding to the L descriptor, >> and a secondary (restricted, flattened, possibly null-free) mirror >> corresponding to the Q descriptor. The secondary mirror has only one job: >> reflecting Q descriptors in method and field descriptors (and supporting method >> handles in their emulation of same.) >> When you ask an object for getClass(), it always hands back the primary mirror. >> As a bonus, this has a nice compatibility story with Integer today; reflective >> code is used to testing for Integer.class when an int is expected (reflection >> is always boxed), so this will continue to work, because instances of `int` >> will report `Integer.class` from `getClass()`. >> All of this feels like the low-energy-state for extending mirrors to primitives. >> Here's where I think we bobbled a little bit, and can correct. >> Currently, we say "Well, if Point is an alias for Point.val, then Point.class >> must be Point.val.class". This is consistent, but kind of useless. Because >> then, for example: >> void puzzler() { >> assertEquals(Point.class, new Point(0,0).getClass()); >> } >> will fail (unless Point is ref-default, at which point it succeeds.) I think the >> mistake we made is too literally following the model of "Point is an alias for >> Point.val". So, I'm proposing a slight adjustment, that treats the unqualified >> class name contextually: >> - In the context of a variable type, Point is an alias for Point.val, or >> Point.ref, if ref-default (no change from current); >> - Where a class is needed (e.g., new Point(), instanceof Point), Point refers to >> the class Point, not any of its derived types, and Point.{ref,val} are not >> valid in these contexts (no change from current); >> - Where a reflective literal is needed, the unqualified Point.class is always >> the primary mirror, which always matches the behavior of Object::getClass >> (change); >> - If an explicit literal is needed for reflection (e.g., calling getMethod()), >> you can be explicit and say Point.{ref,val}.class to say what you mean (no >> change). >> This aligns the meaning of "Point.class" with the actual behavior of other >> reflective API points. From kevinb at google.com Tue Jun 29 17:54:41 2021 From: kevinb at google.com (Kevin Bourrillion) Date: Tue, 29 Jun 2021 10:54:41 -0700 Subject: Revisiting default values In-Reply-To: <741e0451-0b6b-05a7-5fae-f56e5c312092@oracle.com> References: <3D227EB1-4D86-4F97-BFCB-A5949C63A717@oracle.com> <741e0451-0b6b-05a7-5fae-f56e5c312092@oracle.com> Message-ID: Sorry for quietness of late. Some new thoughts. - Default behaviors of language features should be based *first* on bug-proof-ness; if a user has to opt into safety that means they were not safe. - `null` and nullable types are a very good thing for safety; NPE protects us from more nasty bugs than we can imagine. - A world where *all* user-defined primitive classes must be nullable (following Brian's outline) is completely *sane*, just not optimized. - (We'd like to still be able to fashion a *non-nullable type* when the class itself allows nullability, but this is a general need we already have for ref types and shouldn't have much bearing here. Still working hard on jspecify.org...) - It's awkward that `Instant` would have to add a `boolean valid = true` field, but it's not inappropriate. It has the misfortune that it both can't restrict its range of values *and* has no logical zero/default. - A type that does have a restricted range of legal values, but where that range includes the `.default` value, might do some very ugly tricks to avoid adding that boolean field; not sure what to think about this. - Among all the use cases for primitive classes, the ones where the default value is non-degenerate and expected are the special cases! We use `Complex` as a go-to example, but if most of what we did with complex numbers was divide them by each other then even this would be dubious. We'd be letting an invalid value masquerade as a valid one when we'd rather it just manifest as `null` and be subject to NPEs. - If we don't do something like Brian describes here, then I suppose second-best is that we make a *lot* of these things ref-default (beginning with Instant and not stopping there!) and warn about the dangers of `.val` tl;dr nullable by default! Would be glad to hear what I'm missing or not understanding right. On Wed, Mar 17, 2021 at 8:14 AM Brian Goetz wrote: > Let me propose another strategy for Bucket 3. It could be implemented at > either the VM or language level, but the latter probably needs some help > from the VM anyway. The idea is that the default value is > _indistinguishable from null_. Strawman: > > - Classes can be marked as default-hostile (e.g., `primitive class X > implements NoGoodDefault`); > - Prior to dereferencing a default-hostile class, a check is made against > the default value, and an NPE is thrown if it is the default value; > - When widening to a reference type, a check is made if it is the default > value, and if so, is converted to null; > - When narrowing from a reference type, a check is made for null, and if > so, converted to the default value; > - It is allowable to compare `x == null`, which is intepreted as "widen x > to X.ref, and compare"; > - (optional) the interface NoGoodDefault could have a method that > optimizes the check, such as by using a pivot field, or the language/VM > could try to automatically pick a pivot field. > > Classes which opt for NoGoodDefault will be slower than those that do not > due to the check, but they will flatten. Essentially, this lets authors > choose between "zero means default" and "zero means null", at some cost. > > A risk here is that ignorant users who don't understand the tradeoffs will > say "oh, great, there's my nullable primitive types", overuse them, and > then say "primitive types are slow, java sucks." The goal here would be to > provide _safety_ for primitive types for which the default is dangerous. > > > On 3/15/2021 11:52 AM, Brian Goetz wrote: > > Picking this issue up again. To summarize Dan's buckets: > > Bucket 1 -- the zero default is in the domain, and is a sensible default > value. Zero for numerics, empty optionals. > > Bucket 2 -- there is a sensible default value, but all-zero-bits isn't > it. > > Bucket 3 -- there simply is no sensible default value. > > > Ultimately, though, this is not about defaults; it is about _uninitialized > variables_. The default only comes into play when the user uses an > uninitialized variable, which usually means (a) uninitialized fields or (b) > uninitialized array elements. It is possible that the language could give > us seat belts to dramatically narrow the chance of uninitialized fields, > but uninitialized array elements are much harder to stamp out. > > It is an attractive distraction to get caught up in designing mechanisms > for supplying an alternate default ("just let the user declare a no-arg > constructor"), but this is focusing on the "writing code" part of the > problem, not the "keeping code safe" part of the problem. > > In some sense, it is the existence (and size) of Bucket 1 that causes the > problem; Bucket 1 is what gives us our sense that it is safe to use > uninitialized variables. In the current language, uninitialized reference > variables are also safe in that if you use them before they are > initialized, you get an exception before anything bad can happen. > Uninitialized primitives in today's language are more dangerous, because we > may interpret the uninitialized value, but this has been a problem we've > been able to live with because today's primitives are pretty limited and > zero is usually a good-enough default in most domains. As we extend > primitives to look more like objects, with behavior, this gets harder. > > > Both buckets 2 and 3 can be remediated without help from the language or > VM, perhaps inconveniently, by careful coding on the part of the author of > the primitive class: > > - don't expose fields to users (a good practice anyway) > - check for zero on entry to each method > > These are options A and E. The difference between Buckets 2 (A) and 3 (E) > in this model is what do we do when we find a zero; for bucket 2, we > substitute some pre-baked value and use that, and for bucket 3, we throw > something (what we throw is a separate discussion.) The various > remediation techniques Dan offers represents a menu which allows us to > trade off reliability/cost/intrusiveness. > > I think we should lean on the model currently implemented by reference > types, where _accessing_ an uninitialized field is OK, but _using_ the > value in the field is not. If we have: > > String s; > > All of the following are fine: > > String t = s; > if (s == null) { ... } > if (s == t) { ... } > > The thing that is not fine is s-dot-something. These are the E/F/G > options, not the H/I options. > > Secondarily, H/I, which attempt to hide the default, create another > problem down the road: when we get to specialized generics, `T.default` > would become partial. > > Some of the solutions for Bucket 3 generalize well enough to Bucket 2 that > we might consider merging them (though there are still messy details). > Option F, for example, injects code at the top of each method body: > > int m() { > if (this == ) > throw new NullPointerException(); > /* body of m */ > } > > into the top of each method; a corresponding feature for Bucket 2 might > inject slightly different code: > > int m() { > if (this == ) > return .m(); > /* body of m */ > } > > > Another thing that has evolved since we started this discussion is > recognizing the difference between .val and .ref projections. Imagine you > could declare your membership in bucket 3: > > __bucket_3 primitive class NGD { ... } > > If, in addition to some way of generating an NPE on dereference (F, G, > etc), we mucked with the conversion of NGD.val to NGD.ref (which the > compiler can inject code on), we could actually put a null on top of the > stack. Then, code like: > > if (ngd == null) { ... } > > would actually work, because to do the comparison, we'd first promote ngd > to a reference type (null is already a reference), and we'd compare two > nulls. > > > > On 7/10/2020 2:23 PM, Dan Smith wrote: > > Brian pointed out that my list of candidate inline classes in the Identity Warnings JEP (JDK-8249100) includes a number of classes that, despite being "value-based classes" and disavowing their identity, might not end up as inline classes. The problem? Default values. > > This might be a good time to revisit the open design issues surrounding default values and see if we can make some progress. > > Background/status quo: every inline class has a default instance, which provides the initial value of fields and array components that have the inline type (e.g., in 'new Point[10]'). It's also the prototype instance used to create all other instances (start with 'vdefault', then apply 'withfield' as needed). The default value is, by fiat, the class instance produced by setting all fields to *their* default values. Often, but not always, this means field/array initialization amounts to setting all the bits to 0. Importantly, no user code is involved in creating a default instance. > > Real code is always useful for grounding design discussions, so let's start there. Among the classes I listed as inline class candidates, we can put them in three buckets: > > Bucket #1: Have a reasonable default, as declared. > - wrapper classes (the primitive zeros) > - Optional & friends (empty) > - From java.time: Instant (start of 1970-01-01), LocalTime (midnight), Duration (0s), Period (0d), Year (1 BC, if that's acceptable) > > Bucket #2: Could have a reasonable default after re-interpreting fields. > - From java.time: LocalDate, YearMonth, MonthDay, LocalDateTime, ZonedDateTime, OffsetTime, OffsetDateTime, ZoneOffset, ZoneRegion, MinguoDate, HijrahDate, JapaneseDate, ThaiBuddhistDate (months and days should be nonzero; null Strings, ZoneIds, HijrahChronologies, and JapaneseEras require special handling) > - ListN, SetN, MapN (null array interpreted as empty) > > Bucket #3: No good default. > - Runtime.Version (need a non-null List) > - ProcessHandleImpl (need a valid process ID) > - List12, Set12, Map1 (need a non-null value) > - All ConstantDesc implementations (need real class & method names, etc.) > > There's some subjectivity between the 2nd and 3rd buckets, but the idea behind the 2nd is that, with some translation layer between physical fields and interpretation of those fields, we can come up with an intuitive default (e.g., "0 means January"; "a null String means time zone 'UTC'"). In contrast, in the third bucket, any attempt to define a default value is going to be pretty unintuitive ("A null method name means 'toString'"). > > The question here is how much work the JVM and language are willing to do, or how much work we're willing to ask clients to do, in order to support use cases that don't fall into Bucket #1. > > I don't think totally excluding Buckets #2 and #3 is a very good outcome. It means that, in many cases, inline classes need to be built up exclusively from primitives or other inline types, because if you use reference types, your default value will have a null field. (Sometimes, as in Optional, null fields have straightforward interpretations, but most of the time programs are designed to prevent them.) > > Whether we support Bucket #2 but not Bucket #3 is a harder question. It wouldn't be so bad if none of the examples above in Bucket #3 become inline classes?for the most part they're handled via interfaces, anyway. (Counterpoint: inline class instances that are immediately typed with interface types still potentially provide a performance boost.) But I'm also not sure this is representative. We've noted before that many use cases, like database records or data structure cursors, don't have meaningful defaults (what's a default mailing address?). The ConstantDesc classes really illustrate this, even though they happen to not be public. > > Another observation is that if we support Bucket #3 but not Bucket #2, that's probably not a big deal?I'm not sure anybody really *wants* to deal with the default instance; it's just the price you pay for being an inline class. If there's a way to opt out of that extra weirdness and move from Bucket #2 to Bucket #3, great. > > With that discussion in mind, here are some summaries of approaches we've considered, or that I think we ought to consider, for supporting buckets #2 and #3. (This is as best as I recall. If there's something I've missed, add it to the list!) > > [Weighing in for myself: my current preference is to do one of F, G, or I. I'm not that interested in supporting Bucket #2, for reasons given above, although Option A works for programmers who really want it.] > > > > === Solutions to support Bucket #2 === > > Two broad strategies here: re-interpreting fields (A, B), and re-interpreting the default instance (C, D). > > --- > > Option A: Encourage programmers to re-interpret fields > > Guidance to programmers: when you declare an inline class, identify any fields for which the default instance should hold something other than zero/null; define a mapping for your implementation from zero/null to the value you want. > > One way to do this is to define a (possibly private) getter for each field, and include logic like 'return month + 1' or 'return id == null ? "UTC" : id'. Or maybe you inline that logic, as long as you're careful to do so everywhere. Importantly, you also need to reverse the logic in your constructor?for the sake of '==', if somebody manually creates the default instance, you should set fields to zero/null. > > This doesn't work if you want public fields, but that's life as an OO programmer. > > In this approach, it would be important that inline classes be expected to document their default instance in Javadoc (perhaps with a new Javadoc tag)?the interpretation of the default instance is less apparent to users than "all zeros". > > Limitations: > > - It's a fairly error-prone approach. Programmers will absolutely forget to apply the mapping in one place, and everything will be fine until somebody tries to invoke a particular method on the default instance. Put that bug in a security-sensitive context, and maybe you have an exploit. (Something that could help some is choosing good names?call your field 'monthIndex', not plain 'month', to remind yourself that it's zero-based.) > > - Performance impact of an extra layer of computation on all field accesses. Probably not a big deal in general, but all those null checks, etc., could have a negative impact in certain contexts. And the *appearance* of extra cost might scare programmers away from doing the right thing ("eh, I probably won't use the default value anyway, I'll just ignore it to make my code faster"). > > --- > > Option B: Language support for field re-interpretation > > The language allows inline classes to declare fields with mappings to/from an internal representation. Just like Option A, but with guarantees that the internal representation isn't inappropriately accessed directly. > > This pulls on a thread we explored a bit for Amber awhile back, some form of "abstract fields" or "virtual fields". Maybe there's something there, but it seems like a general-purpose feature, and one we're not likely to reach a final solution on anytime soon. > > --- > > Option C: Language support for a designated default > > The language provides some way for programmers to declare the "logical" default instance (something like a special static field). The compiler inserts a test for the "physical" default on any field/array access, and replaces it with the logical default. > > That is: > > Point p = points[3]; > > compiles to > > point p$0 = points[3]; > Point p = (p$0 == [vdefault Point]) ? Point.DEFAULT : p$0; > > This is much less bug-prone than Option A?the compiler does all the work?and much more achievable in the short/medium term than Option B. > > Compared to Option B, this pushes the computation overhead from inline class field accesses to reads of the inline type from fields/arrays. I don't know if that's good or bad?maybe a wash, heavily dependent on the use case. > > A few big problems: > > - The physical default still exists, and malicious bytecode can use it. If programmers want strong guarantees, they'll have to check and throw wherever an untrusted instance is provided. (Clients with access to the inline class's fields have to do so, too.) > > - Covariant arrays mean every read from any array type that might be flattened (Object[], Runnable[], ConstantDesc[], ...) has to go through translation logic. > > - There's an assumption here that the programmer doesn't intend to use the physical default as a valid non-default instance. That's hard for the compiler to enforce, and weird stuff happens in fields/arrays if the programmer doesn't prevent it. (Could be mitigated with extra implicit logic on field/array writes or in constructors.) > > --- > > Option D: JVM support for a designated default > > The VM allows inline classes to designate a logical default instance, and the field/array access instructions map from the physical default to the logical default. The 'vdefault' instruction produces the logical default instance; something else is used by the class's factories to build from the physical default. > > This addresses the first two problems with Option C?the VM gives strong guarantees, and can make the translation a virtual operation of certain arrays. > > To address the second problem, it seems like we'd need the more complex logic I hinted at: on writes, map the physical default to the logical default, and map the logical default to the physical default. Do the reverse on reads. > > The problem here is bytecode complexity/slowdowns. We've already added some complexity to 'aaload'/'aastore' (covariant flattened arrays), and anticipate similar changes to 'putfield'/'getfield' (specialized fields), so maybe that means we might as well do more. Or maybe it means we're already over budget. :-) > > From the users' perspective, if any performance reduction on reads/writes can be limited to the inline classes in Bucket #2, *all* the options have a similar cost, whether imposed by the programmer, language, or VM. So, to a first approximation, slower opcode execution is fine. > > > > === Solutions to support Bucket #3 === > > Two broad strategies here: rejecting member accesses on the default instance (E, F, G), and preventing programs from ever seeing the default instance (H, I). > > --- > > Option E: Encourage programmers to guard against default instances > > Guidance to programmers: if you don't like your class's default instance, check for it in your methods and throw. Maybe Java SE defines a new RuntimeException to encourage this. > > The simple way to do this is with some boilerplate at the start of all your methods: > > if (this == MyClass.default) throw new InvalidDefaultException(); > > More permissive classes could just do some validation on the fields that are relevant to a particular operation. (E.g., 'getMonth' doesn't care if 'zoneId' is null.) > > This doesn't work if you want public fields, but that's life as an OO programmer. > > It's not ideal that an invalid instance can float around a program until somebody trips on one of these checks, rather than detecting the invalid value earlier?we're propagating the NPE problem. And it takes some getting used to that there are two null-like values in the reference type's domain. > > --- > > Option F: Language support for default instance guards > > An inline class declaration can indicate that the default instance is invalid. The compiler generates guards, as in Option E, at the start of all instance method bodies, and perhaps on all field accesses outside of those methods. > > Programmers give up finer-grained control, but get more safety. I'm sure most would be happy with that trade. > > Improper/separately-compiled bytecode can skip the field access checks, but that's a minor concern. > > Same issues as Option E regarding adding a "new NPE" to the platform. > > --- > > Option G: JVM support for default instance guards > > Inline class files can indicate that their default instance is invalid. All attempts to operate on that instance (via field/method accesses, other than 'withfield') result in an exception. > > This tightens up Option F, making it just as impossible to access members of the default instance as it is to access members of 'null'. > > Same issues as Option E regarding adding a "new NPE" to the platform. > > --- > > Option H: Language checks on field/array reads > > An inline class declaration can indicate that the default instance is invalid. Every field and array access that may involved an uninitialized field/array component of that inline type gets augmented with a check that rejects reads of the default value (treating it as "you forgot to initialize this variable"). > > That is: > > Point p = points[3]; > > compiles to > > point p$0 = points[3]; > if (p$0 == [vdefault Point]) throw new UninitializedVariableException(); > Point p = p$0; > > This is much like Option C, and has roughly the same advantages/problems. There's not a strong guarantee that the default value won't pop up from untrusted bytecode (or unreliable inline class authors), and lots of array types need guards. > > --- > > Option I: JVM checks on field/array reads > > Inline class files can indicate that their default instance is invalid. When reading from a field/array component of the inline type ('getfield'/'getstatic'/'aaload'), an exception is thrown if the default value is found (treating it as "you forgot to initialize this variable"). The 'vdefault' instruction, like 'withfield', is illegal outside of the inline class's nest. > > Better than Option H in that it can be optimized to occur on only certain reads, and in that it provides strong guarantees?only the inline class can ever "see" the default instance. > > Well, unless the inline class chooses to share that instance with the world. Not sure how we prevent that. But maybe at that point, anything bad/weird that happens is the author's own fault. (E.g., putting the default value in an array will make that component effectively "uninitialized" again.) > > Like Option D, there's a question of whether we're willing to add this complexity to the 'getifled'/'getstatic'/'aaload' instructions. My sense is that at least it's less complexity than you have in Option D. > > > > > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From daniel.smith at oracle.com Tue Jun 29 19:56:13 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Tue, 29 Jun 2021 19:56:13 +0000 Subject: Revisiting default values In-Reply-To: References: <3D227EB1-4D86-4F97-BFCB-A5949C63A717@oracle.com> <741e0451-0b6b-05a7-5fae-f56e5c312092@oracle.com> Message-ID: > On Jun 29, 2021, at 11:54 AM, Kevin Bourrillion wrote: > > Sorry for quietness of late. Glad to have you back! Unfortunately, there's not much new to report in this area, other than the fact that we are aware that more design and prototyping work is needed. Here's an open task to prototype an initial javac-only strategy: https://bugs.openjdk.java.net/browse/JDK-8252781 > Some new thoughts. > ? Default behaviors of language features should be based first on bug-proof-ness; if a user has to opt into safety that means they were not safe. > ? `null` and nullable types are a very good thing for safety; NPE protects us from more nasty bugs than we can imagine. > ? A world where all user-defined primitive classes must be nullable (following Brian's outline) is completely sane, just not optimized. These are good principles, and I'm sympathetic to them (with the implication that the right "default default" is null/*; using a different default should be opt-in). (*By , I mean the all-zero-bits instance of a no-good-default class, without picking a particular semantics for that value.) But... these principles potentially conflict with engineering constraints. E.g., I can imagine a world in which a no-good-default primitive class is no better than an identity class in most use cases, and at that point, we're best off simply not supporting the no-good-default feature at all. (With the implication that many of the primitive-candidate classes we are imagining should continue to be identity classes.) I don't like that world, and I don't know how realistic it is, but there's pressure coming from that direction. To move forward on the "what is the best default default?" question, we're going to need more engineering on no-good-default classes, and get a better sense of their performance characteristics. > ? (We'd like to still be able to fashion a non-nullable type when the class itself allows nullability, but this is a general need we already have for ref types and shouldn't have much bearing here. Still working hard on jspecify.org...) I think we're pretty locked in to: - Some primitive class types like Complex must be non-nullable (for compactness) - We won't (at least for now) support non-nullable types in full generality Always possible that we'd want to step back and revisit this design, but it's pretty mature. Speaking of orthogonality, there *is* an open question about how we interpret , and this is orthogonal to the question of whether should be the "default default". We've talked about: - It's interchangeable with null - It's null-like (i.e., detected on member access), but distinct - It's a separate concept, and it is an error to ever read it from fields/arrays All still on the table. (And within each of these, we still need to further explore the implications of JVM vs. language implementation strategies.) > ? It's awkward that `Instant` would have to add a `boolean valid = true` field, but it's not inappropriate. It has the misfortune that it both can't restrict its range of values and has no logical zero/default. > ? A type that does have a restricted range of legal values, but where that range includes the `.default` value, might do some very ugly tricks to avoid adding that boolean field; not sure what to think about this. How we encode is an interesting question that deserves more exploration. There's a potential trade-off here between safety and performance, and like you I'm inclined to prioritize safety. Maybe there are reasonable ways we can get them both... > ? Among all the use cases for primitive classes, the ones where the default value is non-degenerate and expected are the special cases! We use `Complex` as a go-to example, but if most of what we did with complex numbers was divide them by each other then even this would be dubious. We'd be letting an invalid value masquerade as a valid one when we'd rather it just manifest as `null` and be subject to NPEs. Complex and friends are special cases, but they're also the *most important* cases. I'd really prefer not to have to pick, but if forced to, it may be more important for primitive classes to support optimally the 10% "has a good default" cases (roughly, those that are number-like) than the 90% "no good default" cases (roughly, those that wrap references). > ? If we don't do something like Brian describes here, then I suppose second-best is that we make a lot of these things ref-default (beginning with Instant and not stopping there!) and warn about the dangers of `.val` I'm not a big fan of this approach. It gives you the illusion of safety (well-written code only sees valid values) but blows up in unpredictable ways when a bug or a hostile actor leaks into your program. If we don't offer stronger guarantees, and your code isn't willing to check for , you really shouldn't be programming with a primitive class. From kevinb at google.com Tue Jun 29 21:36:47 2021 From: kevinb at google.com (Kevin Bourrillion) Date: Tue, 29 Jun 2021 14:36:47 -0700 Subject: Revisiting default values In-Reply-To: References: <3D227EB1-4D86-4F97-BFCB-A5949C63A717@oracle.com> <741e0451-0b6b-05a7-5fae-f56e5c312092@oracle.com> Message-ID: Thanks for giving this your attention! On Tue, Jun 29, 2021 at 12:56 PM Dan Smith wrote: E.g., I can imagine a world in which a no-good-default primitive class is > no better than an identity class in most use cases, and at that point, > we're best off simply not supporting the no-good-default feature at all. Ah. I don't have that imagination at the moment. Maybe my picture of this whole feature went straight from overly choleric to overly sanguine! > (With the implication that many of the primitive-candidate classes we are > imagining should continue to be identity classes.) I'm not sure that follows, because to the library owner, this should be about letting their users have the *choice* between ref/val, and also about not senselessly exposing identity when identity is senseless. I guess the main reason I can see resisting the change for some data type (that's appropriate for primitive-ness) is if "most users will want/have to use the .ref projection, *and* they'll perform way worse than if ref was their only choice." Is that a concern? (And of course, in this thread I'm talking about *removing* one of the reasons for wide use of .ref, nullability.) > I think we're pretty locked in to: > - Some primitive class types like Complex must be non-nullable (for > compactness) > - We won't (at least for now) support non-nullable types in full generality > Good good. I guess I'm submitting #3 for consideration, "We will deliberately not worry about problems caused by nullability of primitive types if those problems are just the same ones we already have with reference types." Speaking of orthogonality, there *is* an open question about how we > interpret , and this is orthogonal to the question of whether > should be the "default default". We've talked about: > - It's interchangeable with null > - It's null-like (i.e., detected on member access), but distinct > - It's a separate concept, and it is an error to ever read it from > fields/arrays > > All still on the table. > Oh. Yeah, if you look at all the work we've all poured into how we manage null and its attendant risks, and ongoing work (perhaps charitably assume JSpecify will be successful! :-)), then it's kiiiind of a disaster if there's suddenly a second kind of nullness. #nonewnulls > Complex and friends are special cases, but they're also the *most > important* cases. I'd really prefer not to have to pick, but if forced to, > it may be more important for primitive classes to support optimally the 10% > "has a good default" cases (roughly, those that are number-like) than the > 90% "no good default" cases (roughly, those that wrap references). > To clarify, I don't think I meant "special case" as "deprioritize", only as "that's the case I think I'd have users opt into intentionally". > > ? If we don't do something like Brian describes here, then I > suppose second-best is that we make a lot of these things ref-default > (beginning with Instant and not stopping there!) and warn about the dangers > of `.val` > > I'm not a big fan of this approach. It gives you the illusion of safety > (well-written code only sees valid values) but blows up in unpredictable > ways when a bug or a hostile actor leaks into your program. If we > don't offer stronger guarantees, and your code isn't willing to check for > , you really shouldn't be programming with a primitive class. > Indeed, calling it my second-best was not meant to imply I don't also hate it. :-) -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From mcnepp02 at googlemail.com Wed Jun 30 06:53:41 2021 From: mcnepp02 at googlemail.com (Gernot Neppert) Date: Wed, 30 Jun 2021 08:53:41 +0200 Subject: JEP 401 -- reflection and class literals In-Reply-To: <1550290072.7564.1624787885215.JavaMail.zimbra@u-pem.fr> References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> <1550290072.7564.1624787885215.JavaMail.zimbra@u-pem.fr> Message-ID: Am So., 27. Juni 2021 um 11:59 Uhr schrieb Remi Forax : > I think that less is more in that context, we do not have to have a syntax > to express the secondary class exactly like we do not allow > Foo.class. > So instead of allowing Point.ref.class and Point.val.class, i think it's > better to have no language support given that we can already write > Point.class.asPrimitiveObjectClass(). > > Not having a syntax for Point.ref.class/Point.val.class is important > because as you say it's inconsistent, for good, with Point being either an > identity class or a primitive object class. > Not surfacing that inconsistency in the language make things easier to > understand and using methods of java.lang.Class instead make the intent > more clear at the expanse of being more verbose. > > regards, > R?mi > OTOH, proper "value-favoring" primitive classes are *meant to* be passed around by-value, right? So, given primitive class Point { Point transform(Point origin); } programmers might by deeply puzzled to find out that this will fail: /*1*/ Point.class.getMethod("transform", Point.class); ...and they are instead expected to write /*2*/ Point.class.getMethod("transform", Point.class.asPrimitiveObjectClass()); Does that really "make the intent more clear", as you claim? I think, being able to write /*3*/ Point.class.getMethod("transform", Point.val.class); would better express the intent. However, being able to use /*1*/ without further ado would be even more consistent - which would be automatically possible if the meaning of "T.class" did depend on the "value/reference-favoring" characteristic of the primitive type. Back to square one :) From brian.goetz at oracle.com Wed Jun 30 13:29:57 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 30 Jun 2021 09:29:57 -0400 Subject: [External] : Re: JEP 401 -- reflection and class literals In-Reply-To: References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> <1550290072.7564.1624787885215.JavaMail.zimbra@u-pem.fr> Message-ID: <4ec64c0f-8bf4-8ac0-0ab9-405a177863f5@oracle.com> Yes, as mentioned, this is where the problem moves to next.?? (Did we mention that none of the choices are perfect?) It's not clear it is desirable or practical to try and prohibit overloads such as ??? m(Point.val x) ??? m(Point.ref x) Even if we tried to, it is a game of whack-a-mole, and the best we could do it prevent them when the entire application was consistently compiled with `javac`.? But, primitive classes are a JVM feature, the JVM has L and Q descriptors, and reflection is *classfile* reflection, not *Java language* reflection, as much as we would like to pretend otherwise.? So it is a given that eventually reflection will encounter an overload like ??? m(int a, float b, Point.ref c) ??? m(int a, float b, Point.val c) Now, what is getMethod() supposed to do?? A fuzzy match?? That's a pretty massive departure from what "getMethod" has historically meant.? (And, for MethodHandles, its worse; lookup is supposed to be precise.) The reality is these overloads are going to happen, and reflection needs sharp enough tools to distinguish between them. On 6/30/2021 2:53 AM, Gernot Neppert wrote: > > > Am So., 27. Juni 2021 um 11:59?Uhr schrieb Remi Forax > >: > > I think that less is more in that context, we do not have to have > a syntax to express the secondary class exactly like we do not > allow Foo.class. > So instead of allowing Point.ref.class and Point.val.class, i > think it's better to have no language support given that we can > already write Point.class.asPrimitiveObjectClass(). > > Not having a syntax for Point.ref.class/Point.val.class is > important because as you say it's inconsistent, for good, with > Point being either an identity class or a primitive object class. > Not surfacing that inconsistency in the language make things > easier to understand and using methods of java.lang.Class instead > make the intent more clear at the expanse of being more verbose. > > regards, > R?mi > > > OTOH, proper "value-favoring" primitive classes are *meant to* be > passed around by-value, right? > > So, given > > primitive class Point { > Point transform(Point origin); > } > > programmers might by deeply puzzled to find out that this will fail: > > /*1*/ Point.class.getMethod("transform", Point.class); > > ...and they are instead expected to write > > /*2*/ Point.class.getMethod("transform", > Point.class.asPrimitiveObjectClass()); > > Does that really "make the intent more clear", as you claim? > > I think, being able to write > > /*3*/ Point.class.getMethod("transform", Point.val.class); > > would better express the intent. > However, being able to use /*1*/ without further ado would be even > more consistent - which would be automatically possible if the meaning > of "T.class" did depend on the "value/reference-favoring" > characteristic of the primitive type. Back to square one :) > From mcnepp02 at googlemail.com Wed Jun 30 14:05:57 2021 From: mcnepp02 at googlemail.com (Gernot Neppert) Date: Wed, 30 Jun 2021 16:05:57 +0200 Subject: [External] : Re: JEP 401 -- reflection and class literals In-Reply-To: <4ec64c0f-8bf4-8ac0-0ab9-405a177863f5@oracle.com> References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> <1550290072.7564.1624787885215.JavaMail.zimbra@u-pem.fr> <4ec64c0f-8bf4-8ac0-0ab9-405a177863f5@oracle.com> Message-ID: Well, if you guys decide that there will be two distinct type-mirrors per primitive class, the tools to distinguish overloaded methods will obviously be there, no matter what the details were. I was merely pointing out that reflective use would be more consistent with non-reflective use if the expression "T.class" took into account the value/reference-favoring flavour: the most concise notation would work "as expected" for the most common usecase, while the less likely scenarios would require more boilerplate code... Am Mi., 30. Juni 2021 um 15:30 Uhr schrieb Brian Goetz < brian.goetz at oracle.com>: > > Now, what is getMethod() supposed to do? A fuzzy match? That's a pretty > massive departure from what "getMethod" has historically meant. (And, for > MethodHandles, its worse; lookup is supposed to be precise.) > > The reality is these overloads are going to happen, and reflection needs > sharp enough tools to distinguish between them. > > > From brian.goetz at oracle.com Wed Jun 30 14:07:48 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 30 Jun 2021 10:07:48 -0400 Subject: [External] : Re: JEP 401 -- reflection and class literals In-Reply-To: References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> <1550290072.7564.1624787885215.JavaMail.zimbra@u-pem.fr> <4ec64c0f-8bf4-8ac0-0ab9-405a177863f5@oracle.com> Message-ID: Internally, there pretty much have to be, unless we want to entirely rewrite reflection, MethodHandle, MethodType, etc, to use something other than jl.Class to represent field descriptors.? Not being able to distinguish between LFoo and QFoo in reflection/method handles would be a no-go.? So when you pull on that string ... On 6/30/2021 10:05 AM, Gernot Neppert wrote: > Well, if you guys decide that there will be two distinct type-mirrors > per primitive class, the tools to distinguish overloaded methods will > obviously be there, no matter what the details were. > > I was merely pointing out that reflective use would be more consistent > with non-reflective use if the expression "T.class" took into account > the value/reference-favoring flavour: the most concise notation would > work "as expected" for the most common usecase, while the less likely > scenarios would require more boilerplate code... > > > > Am Mi., 30. Juni 2021 um 15:30?Uhr schrieb Brian Goetz > >: > > > Now, what is getMethod() supposed to do?? A fuzzy match?? That's a > pretty massive departure from what "getMethod" has historically > meant.? (And, for MethodHandles, its worse; lookup is supposed to > be precise.) > > The reality is these overloads are going to happen, and reflection > needs sharp enough tools to distinguish between them. > > From daniel.smith at oracle.com Wed Jun 30 14:38:22 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 30 Jun 2021 14:38:22 +0000 Subject: EG meeting *canceled*, 2021-06-30 Message-ID: <9B3D47F3-FEEC-4AFF-B580-E166373E72E6@oracle.com> There's been a little bit of traffic on the list, but I've got a conflict today and I think we can defer talking about those things until next time. Next meeting July 14. From mcnepp02 at googlemail.com Wed Jun 30 14:40:08 2021 From: mcnepp02 at googlemail.com (Gernot Neppert) Date: Wed, 30 Jun 2021 16:40:08 +0200 Subject: [External] : Re: JEP 401 -- reflection and class literals In-Reply-To: References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> <1550290072.7564.1624787885215.JavaMail.zimbra@u-pem.fr> <4ec64c0f-8bf4-8ac0-0ab9-405a177863f5@oracle.com> Message-ID: Sorry, I don't know what to make of your latest reply - maybe I'm not expressing myself clearly. What I was referring to was that, until quite recently, the proposal (1) said that T.class should denote the non-nullable type for a value-favoring primitive class and the nullable type for a reference-favoring one. The latest propsal (2) suggests to have T.class always refer to the nullable type. Right? Now, for our famous value-favoring primitive type Point, the most common usecase will be methods such as transform(Point pt); Less common will be methods such as move(Point.ref pt) that require some additional thinking & typing. With the proposal (1): In reflective code, the common usecase of finding "transform" would simply require Point.class.getMethod("transform", Point.class), whereas finding "move" would require some more afterthought, either Point.class.getMethod("move", Point.ref.class) or Point.class.getMethod("move", Point.class.asIdentityClass()). This looks consistent to me. With the new proposal (2): In reflective code, the common usecase of finding "transform" would now require either Point.class.getMethod("transform", Point.val.class) or Point.class.getMethod("move", Point.class.asPrimitiveClass()). This strikes me as inconsistent, and that's all there is to it :) Am Mi., 30. Juni 2021 um 16:09 Uhr schrieb Brian Goetz < brian.goetz at oracle.com>: > Internally, there pretty much have to be, unless we want to entirely > rewrite reflection, MethodHandle, MethodType, etc, to use something other > than jl.Class to represent field descriptors. Not being able to > distinguish between LFoo and QFoo in reflection/method handles would be a > no-go. So when you pull on that string ... > > On 6/30/2021 10:05 AM, Gernot Neppert wrote: > > Well, if you guys decide that there will be two distinct type-mirrors per > primitive class, the tools to distinguish overloaded methods will obviously > be there, no matter what the details were. > > I was merely pointing out that reflective use would be more consistent > with non-reflective use if the expression "T.class" took into account the > value/reference-favoring flavour: the most concise notation would work "as > expected" for the most common usecase, while the less likely scenarios > would require more boilerplate code... > > > > Am Mi., 30. Juni 2021 um 15:30 Uhr schrieb Brian Goetz < > brian.goetz at oracle.com>: > >> >> Now, what is getMethod() supposed to do? A fuzzy match? That's a pretty >> massive departure from what "getMethod" has historically meant. (And, for >> MethodHandles, its worse; lookup is supposed to be precise.) >> >> The reality is these overloads are going to happen, and reflection needs >> sharp enough tools to distinguish between them. >> >> >> > > From forax at univ-mlv.fr Wed Jun 30 14:46:07 2021 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Wed, 30 Jun 2021 16:46:07 +0200 (CEST) Subject: [External] : Re: JEP 401 -- reflection and class literals In-Reply-To: <4ec64c0f-8bf4-8ac0-0ab9-405a177863f5@oracle.com> References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> <1550290072.7564.1624787885215.JavaMail.zimbra@u-pem.fr> <4ec64c0f-8bf4-8ac0-0ab9-405a177863f5@oracle.com> Message-ID: <2084818723.1846142.1625064367067.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "Gernot Neppert" , "Valhalla Expert Group > Observers" , "Remi Forax" > > Sent: Mercredi 30 Juin 2021 15:29:57 > Subject: Re: [External] : Re: JEP 401 -- reflection and class literals > Yes, as mentioned, this is where the problem moves to next. (Did we mention that > none of the choices are perfect?) > It's not clear it is desirable or practical to try and prohibit overloads such > as > m(Point.val x) > m(Point.ref x) > Even if we tried to, it is a game of whack-a-mole, and the best we could do it > prevent them when the entire application was consistently compiled with > `javac`. But, primitive classes are a JVM feature, the JVM has L and Q > descriptors, and reflection is *classfile* reflection, not *Java language* > reflection, as much as we would like to pretend otherwise. So it is a given > that eventually reflection will encounter an overload like > m(int a, float b, Point.ref c) > m(int a, float b, Point.val c) > Now, what is getMethod() supposed to do? A fuzzy match? That's a pretty massive > departure from what "getMethod" has historically meant. I don't follow you here, getField() and getMethod() already do fuzzy matching, you can have more than one field with the same name and you can have several methods with the same name but different return types. In particular, getMethod() tries to avoid to select a bridge if there is a non bridge equivalent. > (And, for MethodHandles, its worse; lookup is supposed to be precise.) yes, lookup.find*() is precise because the semantics is the same as the equivalent bytecode (invoke*). > The reality is these overloads are going to happen, and reflection needs sharp > enough tools to distinguish between them. Given that we want to retrofit Integer to be the ref of int, these overloads already exist. So reflection can still do fuzzy matching but the selection algorithm has to prefer precise matching to fuzzy match. R?mi > On 6/30/2021 2:53 AM, Gernot Neppert wrote: >> Am So., 27. Juni 2021 um 11:59 Uhr schrieb Remi Forax < [ >> mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] >: >>> I think that less is more in that context, we do not have to have a syntax to >>> express the secondary class exactly like we do not allow Foo.class. >>> So instead of allowing Point.ref.class and Point.val.class, i think it's better >>> to have no language support given that we can already write >>> Point.class.asPrimitiveObjectClass(). >>> Not having a syntax for Point.ref.class/Point.val.class is important because as >>> you say it's inconsistent, for good, with Point being either an identity class >>> or a primitive object class. >>> Not surfacing that inconsistency in the language make things easier to >>> understand and using methods of java.lang.Class instead make the intent more >>> clear at the expanse of being more verbose. >>> regards, >>> R?mi >> OTOH, proper "value-favoring" primitive classes are *meant to* be passed around >> by-value, right? >> So, given >> primitive class Point { >> Point transform(Point origin); >> } >> programmers might by deeply puzzled to find out that this will fail: >> /*1*/ Point.class.getMethod("transform", Point.class); >> ...and they are instead expected to write >> /*2*/ Point.class.getMethod("transform", Point.class.asPrimitiveObjectClass()); >> Does that really "make the intent more clear", as you claim? >> I think, being able to write >> /*3*/ Point.class.getMethod("transform", Point.val.class); >> would better express the intent. >> However, being able to use /*1*/ without further ado would be even more >> consistent - which would be automatically possible if the meaning of "T.class" >> did depend on the "value/reference-favoring" >> characteristic of the primitive type. Back to square one :) From brian.goetz at oracle.com Wed Jun 30 14:51:27 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 30 Jun 2021 10:51:27 -0400 Subject: [External] : Re: JEP 401 -- reflection and class literals In-Reply-To: References: <3de06bc5-9d67-18be-e3a3-e7ade4d71a30@oracle.com> <1550290072.7564.1624787885215.JavaMail.zimbra@u-pem.fr> <4ec64c0f-8bf4-8ac0-0ab9-405a177863f5@oracle.com> Message-ID: <0cdcfbd3-6f28-5e92-1d48-62947ec6f0bb@oracle.com> > Sorry, I don't know what to make of your latest reply - maybe I'm not > expressing myself clearly. > What I was referring to was that, until quite recently, the proposal > (1) said that T.class should denote the non-nullable type for a > value-favoring primitive class and the nullable type for a > reference-favoring one. > The latest propsal (2) suggests to have T.class always refer to the > nullable type. Right? Yes, but I think even thinking of it as "the nullable type" is probably the wrong mental model.? It is the _unrestricted projection of the class_.? There's only one class -- Point -- which has two ways to store its objects.? All instances are instances of Point, regardless of how they're stored.? No object is an instance of Point.ref or of Point.val; you're an instance of *Point*, stored either directly or by reference. > Now, for our famous value-favoring primitive type Point, > the most common usecase will be methods such as transform(Point pt); > Less common will be methods such as move(Point.ref pt) that require > some additional thinking & typing. > > With the proposal (1): > > In reflective code, the common usecase of finding "transform" would > simply require Point.class.getMethod("transform", Point.class), > whereas finding "move" would require some more afterthought, either > Point.class.getMethod("move", Point.ref.class) or > Point.class.getMethod("move", Point.class.asIdentityClass()). > This looks consistent to me. > > With the new proposal (2): > > In reflective code, the common usecase of finding "transform" would > now require either Point.class.getMethod("transform", Point.val.class) > or Point.class.getMethod("move", Point.class.asPrimitiveClass()). > > This strikes me as inconsistent, and that's all there is to it :) The problem is the language, JVM, and reflection views are subtly different, so there is no avoiding inconsistency.? The only question is where to put it so as to minimize the damage. From brian.goetz at oracle.com Wed Jun 30 15:39:49 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 30 Jun 2021 11:39:49 -0400 Subject: [External] : Re: Revisiting default values In-Reply-To: References: <3D227EB1-4D86-4F97-BFCB-A5949C63A717@oracle.com> <741e0451-0b6b-05a7-5fae-f56e5c312092@oracle.com> Message-ID: Of your points, I think this is the controversial one, and it goes straight to "what is the point of primitive classes." You kind of dismiss optimization, but of course a big part of the point is classes that are more optimizable; if we didn't care about optimization, we wouldn't bother with primitive classes, we'd just say "write classes." The unfortunate bit is that the reason we're stuck with zeroes as the default value comes from the VM's desire to provide both safety _and_ performance.? Painting with a roller is cheaper than with a brush, but, more importantly, all-zeroes is the only value the JVM can really give while promising that some other OOTA value will never be observed, regardless of races and other timing hazards. Now, let's talk more about null.? Null is a *reference* that refers to no object.? For a flattened/direct object (P.val), null cannot be in the value set (it's the wrong "kind"), though we can arrange for a primitive to behave like null in various ways.? It's not clear whether this helps or hurts the mental model, since it is distorting what null is. Really, what this discussion is about is about whether _uninitialized primitive objects_ can be used or not; since T.default is a hidden constructor, we want to know whether that value is safe to use or whether it is merely a tombstone. We can arrange that x.m NPEs when x == X.default, but that doesn't really mean that x is null.? It means that you've dereferenced something that should have been initialized before dereferencing it. The alternative approaches are worse, since they involve creating a second null (see John's "No New Nulls" manifesto), which I think we can agree no one will like. What worries me about this approach is that it muddies what null means, since a P.val is not a reference to anything. I think we are better off treating this as a discussion about initialization safety, rather than nullity, until we have a clear story of how we want things to behave. On 6/29/2021 1:54 PM, Kevin Bourrillion wrote: > Among all the use cases for primitive classes, the ones where the > default value is non-degenerate and expected are the special cases! We > use `Complex` as a go-to example, but if most of what we did with > complex numbers was divide them by each other then even this would be > dubious. We'd be letting an invalid value masquerade as a valid one > when we'd rather it just manifest as `null` and be subject to NPEs.