From forax at univ-mlv.fr Thu Apr 2 11:58:52 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 2 Apr 2020 13:58:52 +0200 (CEST) Subject: Semantics of an empty PermittedSubtypes attribute for the VM Message-ID: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> Hi all, With my ASM hat, from the JVMS POV, javac can not generate an empty list of permitted subtypes and the implementation in Hotspot ignores the attribute PermittedSubtypes if there is no subtype listed. So currently, ASM doesn't allow you to create an attribute PermittedSubtypes with an empty number of subtypes. I believe this is the correct semantics, i just want to be sure that i've not overlook something. regards, R?mi From vicente.romero at oracle.com Thu Apr 2 13:56:11 2020 From: vicente.romero at oracle.com (Vicente Romero) Date: Thu, 2 Apr 2020 09:56:11 -0400 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> Message-ID: Hi Remi, Right, IMO an empty PermittedSubtypes attribute makes no sense for the same reasons you mentioned. Vicente On 4/2/20 7:58 AM, Remi Forax wrote: > Hi all, > With my ASM hat, > from the JVMS POV, javac can not generate an empty list of permitted subtypes and the implementation in Hotspot ignores the attribute PermittedSubtypes if there is no subtype listed. > > So currently, ASM doesn't allow you to create an attribute PermittedSubtypes with an empty number of subtypes. > I believe this is the correct semantics, i just want to be sure that i've not overlook something. > > regards, > R?mi > From brian.goetz at oracle.com Thu Apr 2 14:19:15 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 2 Apr 2020 10:19:15 -0400 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> Message-ID: At the language level, we won?t let you define a class with an empty set of permitted types; you should define a final class instead. But, having the VM treat an empty PS attribute as if it were not there seems wrong; I would think an empty PS attribute would be a class file error. > On Apr 2, 2020, at 7:58 AM, Remi Forax wrote: > > Hi all, > With my ASM hat, > from the JVMS POV, javac can not generate an empty list of permitted subtypes and the implementation in Hotspot ignores the attribute PermittedSubtypes if there is no subtype listed. > > So currently, ASM doesn't allow you to create an attribute PermittedSubtypes with an empty number of subtypes. > I believe this is the correct semantics, i just want to be sure that i've not overlook something. > > regards, > R?mi > From vicente.romero at oracle.com Thu Apr 2 18:12:56 2020 From: vicente.romero at oracle.com (Vicente Romero) Date: Thu, 2 Apr 2020 14:12:56 -0400 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> Message-ID: <4d927d03-dc24-ec7d-5502-250816453e55@oracle.com> On 4/2/20 10:19 AM, Brian Goetz wrote: > At the language level, we won?t let you define a class with an empty set of permitted types; you should define a final class instead. > > But, having the VM treat an empty PS attribute as if it were not there seems wrong; I would think an empty PS attribute would be a class file error. I was chatting about this with Harold and we can tweak the VM implementation to generate an error in this case instead of ignoring an empty PS attribute. Same for final classes with a PS attribute, but these are still open questions in the VM spec that need clarification. Thanks, Vicente > >> On Apr 2, 2020, at 7:58 AM, Remi Forax wrote: >> >> Hi all, >> With my ASM hat, >> from the JVMS POV, javac can not generate an empty list of permitted subtypes and the implementation in Hotspot ignores the attribute PermittedSubtypes if there is no subtype listed. >> >> So currently, ASM doesn't allow you to create an attribute PermittedSubtypes with an empty number of subtypes. >> I believe this is the correct semantics, i just want to be sure that i've not overlook something. >> >> regards, >> R?mi >> From forax at univ-mlv.fr Thu Apr 2 19:37:43 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 2 Apr 2020 21:37:43 +0200 (CEST) Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> Message-ID: <1726036443.643584.1585856263785.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Jeudi 2 Avril 2020 16:19:15 > Objet: Re: Semantics of an empty PermittedSubtypes attribute for the VM > At the language level, we won?t let you define a class with an empty set of > permitted types; you should define a final class instead. > > But, having the VM treat an empty PS attribute as if it were not there seems > wrong; I would think an empty PS attribute would be a class file error. For comparison, i believe the VM also allows attribute NestMembers or BootstrapMethods to be empty. R?mi > >> On Apr 2, 2020, at 7:58 AM, Remi Forax wrote: >> >> Hi all, >> With my ASM hat, >> from the JVMS POV, javac can not generate an empty list of permitted subtypes >> and the implementation in Hotspot ignores the attribute PermittedSubtypes if >> there is no subtype listed. >> >> So currently, ASM doesn't allow you to create an attribute PermittedSubtypes >> with an empty number of subtypes. >> I believe this is the correct semantics, i just want to be sure that i've not >> overlook something. >> >> regards, >> R?mi From brian.goetz at oracle.com Thu Apr 2 21:31:23 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 2 Apr 2020 17:31:23 -0400 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: <1726036443.643584.1585856263785.JavaMail.zimbra@u-pem.fr> References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> <1726036443.643584.1585856263785.JavaMail.zimbra@u-pem.fr> Message-ID: >> At the language level, we won?t let you define a class with an empty set of >> permitted types; you should define a final class instead. >> >> But, having the VM treat an empty PS attribute as if it were not there seems >> wrong; I would think an empty PS attribute would be a class file error. > For comparison, i believe the VM also allows attribute NestMembers or BootstrapMethods to be empty. > True.? But the semantics of these are not ambiguous, as they are with PS.?? An empty "NestMembers" attribute, and no "NestMembers" attribute, have the same effect -- no nest members.? But an empty PermittedSubtypes attribute means (in the absence of ACC_FINAL) anything goes.? So we have two things to be "consistent" with: ?- No PS means no restriction.? And an empty PS should be like no PS, right?? So empty PS is "no restrictions." ?- PS == list of permited subtypes, so empty PS is "no permitted subtypes." Given that these things are on a collision course, and there's no need to have an empty PS, better to make an empty PS illegal. From forax at univ-mlv.fr Thu Apr 2 21:58:28 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 2 Apr 2020 23:58:28 +0200 (CEST) Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> <1726036443.643584.1585856263785.JavaMail.zimbra@u-pem.fr> Message-ID: <666268974.673972.1585864708575.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Jeudi 2 Avril 2020 23:31:23 > Objet: Re: Semantics of an empty PermittedSubtypes attribute for the VM >>> At the language level, we won?t let you define a class with an empty set of >>> permitted types; you should define a final class instead. >>> >>> But, having the VM treat an empty PS attribute as if it were not there seems >>> wrong; I would think an empty PS attribute would be a class file error. >> For comparison, i believe the VM also allows attribute NestMembers or >> BootstrapMethods to be empty. >> > > True.? But the semantics of these are not ambiguous, as they are with > PS.?? An empty "NestMembers" attribute, and no "NestMembers" attribute, > have the same effect -- no nest members.? But an empty PermittedSubtypes > attribute means (in the absence of ACC_FINAL) anything goes.? So we have > two things to be "consistent" with: > > ?- No PS means no restriction.? And an empty PS should be like no PS, > right?? So empty PS is "no restrictions." > ?- PS == list of permited subtypes, so empty PS is "no permitted subtypes." > > Given that these things are on a collision course, and there's no need > to have an empty PS, better to make an empty PS illegal. yes, make sense. R?mi From john.r.rose at oracle.com Thu Apr 2 23:38:43 2020 From: john.r.rose at oracle.com (John Rose) Date: Thu, 2 Apr 2020 16:38:43 -0700 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> Message-ID: <38079815-DDAC-4E19-BBBB-48D239567342@oracle.com> On Apr 2, 2020, at 4:58 AM, Remi Forax wrote: > > the implementation in Hotspot ignores the attribute PermittedSubtypes if there is no subtype listed AFAICT this behavior is an accident of prototype engineering, and I?m glad we are clarifying it to be illegal. Here are principles that I think are generally applicable to cases like this one. I put them roughly in priority order. - Strive for simple regular ?clean? specifications. (so, edge cases are corollaries not requiring separate spec rules) - Avoid and/or reject useless degrees of freedom in specified inputs. (so, don?t encourage inputs which say the same thing in two different ways) - Avoid needlessly confusing the spec. reader. (while satisfying the previous principles; specs are always somewhat tricky) - Be ready to disregard prototype behavior and discard prototype code. (because the spec. will have a 10x longer career than the prototype) (The rest of this note expands a little more, FTR, on those points as they apply to this case.) The best approach to an edge case like this is either to base its meaning on the same definition that applies to the non-edge cases, or make it a checked error if the edge case occurs. It is bad practice to accept the behavior of a prototype implementation without change into a specification, if that adds edge cases to the specification which don?t provide value. The specification will long outlive the code of the prototype, and it is the specification that provides the long-term behavior of the JVM, not the code. Therefore, spec. errors tend to be more costly than code errors. We strive to get the spec right because we?re going to have to live with it for many years. We know from bad experience what it?s like to live with the trail of bugs caused by a sloppy, inexact, or needlessly complex spec. In this case, the general rule for permitted subtypes of C is that it denotes a set S of potential subtypes of C. If that set S is empty, it?s just good logic that this means, according to the specified rule, that there are no permitted subtypes, which means C acts like it?s final (permits no subtypes). It?s not good logic to say, ?the writer of the subclass must have meant to drop the attribute but wrote an empty set instead?, although this is the sort of interpretation that VM code might accidentally give to the occurrence, if it fails to distinguish ?missing attribute? from ?empty list attribute?. We could choose to import this irregular interpretation as an edge case *in the spec*, and we are sometimes forced to, but we prefer not to. You might think I?m arguing here for allowing S to be an empty set, and I might in similar cases, but there are two other reasons to outlaw the edge case, rather than ask the spec. to account for it (either by extending the general rule, or adding a special rule). First, if C wants to permit exactly zero subclasses, there?s already a notation for that. Another principle of spec design is to try to avoid allowing two ways to say that same thing. If two classfiles would appear to be asking for different classes, but they actually mean the same thing, there?s a smell in the classfile language. We already have a way to say ?permit no subs?, so it?s a smell if we add another way. Also, although often edge cases can be interpreted consistently with a general rule (as above), they can have misleading connotations. Zero length arrays and empty sets are notoriously confusing. Apparently it took humans millennia to realize that zero is a number. Although it is surely reasonable to demand that people working with JVMs and classfiles understand how to reason consistently about zero and empty sets, it?s still graceful to avoid the occasion, if there are other reasons to do so. And maybe there?s a way to color the behavior in the present code as more than just an accident of prototyping, though I doubt it. If so we?d have even more motivation to outlaw the edge case, if there were two reasonable rules leading to conflicting outcomes. But I think the present case is a conflict between an unnecessary but reasonable extension to the main rule, versus irregular behavior arising from an incompletely considered decision during prototyping. Which is fine, as long as we give it its due weight, and throw away any behavior that muddies the spec. Bottom line (for those who read to the bottom): The JVM specification must be free of needless complications. Given prototype behavior vs. clean spec, clean spec wins. ? John From brian.goetz at oracle.com Fri Apr 3 13:39:35 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 3 Apr 2020 09:39:35 -0400 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: <38079815-DDAC-4E19-BBBB-48D239567342@oracle.com> References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> <38079815-DDAC-4E19-BBBB-48D239567342@oracle.com> Message-ID: <2B81B2F8-8099-426C-9346-470131B1D2F1@oracle.com> > You might think I?m arguing here for allowing S to be > an empty set, and I might in similar cases, but there are two > other reasons to outlaw the edge case, rather than ask > the spec. to account for it (either by extending the general > rule, or adding a special rule). First, if C wants to permit > exactly zero subclasses, there?s already a notation for that. > ? unless C is an interface. From john.r.rose at oracle.com Fri Apr 3 20:17:42 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 3 Apr 2020 13:17:42 -0700 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: <2B81B2F8-8099-426C-9346-470131B1D2F1@oracle.com> References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> <38079815-DDAC-4E19-BBBB-48D239567342@oracle.com> <2B81B2F8-8099-426C-9346-470131B1D2F1@oracle.com> Message-ID: <33C18AAC-4AE7-480F-A07D-B72045C46388@oracle.com> On Apr 3, 2020, at 6:39 AM, Brian Goetz wrote: > > > >> You might think I?m arguing here for allowing S to be >> an empty set, and I might in similar cases, but there are two >> other reasons to outlaw the edge case, rather than ask >> the spec. to account for it (either by extending the general >> rule, or adding a special rule). First, if C wants to permit >> exactly zero subclasses, there?s already a notation for that. >> > > > ? unless C is an interface. Yes, sure. If there?s a use case for a never-implemented interface (I can think of a few) then we could either (a) allow interfaces to be marked final, or (b) allow empty permit lists, at the cost of the earlier objections (hey, it?s kinda confusing + two ways to say the same thing for non-interfaces). The most straightforward way to get the effect of a never-implemented interface is to give it a permits list which references no implemented types. The most straightforward way to do *that* is make the permits list empty. But there are other ways to get that effect. Make the permits list be a singleton reference to a private class that is either non-existent (never loaded because no classfile) or that is abstract and (somehow) permits no subtypes. In either case the JVM can be asked to observe that no instances are possible. Which tactic is best here? I suppose that depends on the use cases. If we are supposing users will want to use non-implemented interfaces as some sort of design pattern, then ?final? interfaces, or empty permits lists, are in the cards. But if it?s some kind of one-off thing (like an interface version of jl.Void) then the notation can be ugly, since it can be done once with adequate explanatory comments. ? John From brian.goetz at oracle.com Fri Apr 3 20:36:32 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 3 Apr 2020 16:36:32 -0400 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: <33C18AAC-4AE7-480F-A07D-B72045C46388@oracle.com> References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> <38079815-DDAC-4E19-BBBB-48D239567342@oracle.com> <2B81B2F8-8099-426C-9346-470131B1D2F1@oracle.com> <33C18AAC-4AE7-480F-A07D-B72045C46388@oracle.com> Message-ID: <5a652475-e1be-ae48-a349-6dd39b5bb189@oracle.com> I'm mostly thinking of interfaces that will only be implemented by hidden classes. On 4/3/2020 4:17 PM, John Rose wrote: > On Apr 3, 2020, at 6:39 AM, Brian Goetz > wrote: >> >> >> >>> You might think I?m arguing here for allowing S to be >>> an empty set, and I might in similar cases, but there are two >>> other reasons to outlaw the edge case, rather than ask >>> the spec. to account for it (either by extending the general >>> rule, or adding a special rule). ?First, if C wants to permit >>> exactly zero subclasses, there?s already a notation for that. >>> >> >> ? unless C is an interface. > > Yes, sure. ?If there?s a use case for a never-implemented interface > (I can think of a few) then we could either (a) allow interfaces to > be marked final, or (b) allow empty permit lists, at the cost of > the earlier objections (hey, it?s kinda confusing + two ways to > say the same thing for non-interfaces). > > The most straightforward way to get the effect of a never-implemented > interface is to give it a permits list which references no implemented > types. > > The most straightforward way to do *that* is make the permits list > empty. ?But there are other ways to get that effect. ?Make the permits > list be a singleton reference to a private class that is either > non-existent > (never loaded because no classfile) or that is abstract and (somehow) > permits no subtypes. ?In either case the JVM can be asked to observe > that no instances are possible. > > Which tactic is best here? ?I suppose that depends on the use cases. > If we are supposing users will want to use non-implemented > interfaces as some sort of design pattern, then ?final? interfaces, or > empty permits lists, are in the cards. ? But if it?s some kind of one-off > thing (like an interface version of jl.Void) then the notation can be > ugly, since it can be done once with adequate explanatory comments. > > ? John From john.r.rose at oracle.com Fri Apr 3 20:48:42 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 3 Apr 2020 13:48:42 -0700 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: <5a652475-e1be-ae48-a349-6dd39b5bb189@oracle.com> References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> <38079815-DDAC-4E19-BBBB-48D239567342@oracle.com> <2B81B2F8-8099-426C-9346-470131B1D2F1@oracle.com> <33C18AAC-4AE7-480F-A07D-B72045C46388@oracle.com> <5a652475-e1be-ae48-a349-6dd39b5bb189@oracle.com> Message-ID: <700BBA89-6907-4987-BFFE-F633ACEB0149@oracle.com> On Apr 3, 2020, at 1:36 PM, Brian Goetz wrote: > > I'm mostly thinking of interfaces that will only be implemented by hidden classes. In order to do that we need a way to hook the HCs into the permits list. That can be done with a private abstract class that nobody extends except the desired HCs. The HCs themselves cannot be placed on the permits list because they cannot be named, and the permits list contains names. (N.B. the experimental constant pool patching mechanism would also, in principle, allow HCs to be ?named? on permit lists, by direct patching of a relevant CP entry. This is one sort of use case I was thinking of when I joined the CP patching feature to the HC prototype. Basically, imagine a whole ecosystem of HC hierarchies, which would require patching the CPs of their various classfiles. It would be a powerful mess. But there are less powerful ways to get the same effects as CP patching, for all the use cases we?ve looked at.) From brian.goetz at oracle.com Fri Apr 3 20:52:16 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 3 Apr 2020 16:52:16 -0400 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: <700BBA89-6907-4987-BFFE-F633ACEB0149@oracle.com> References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> <38079815-DDAC-4E19-BBBB-48D239567342@oracle.com> <2B81B2F8-8099-426C-9346-470131B1D2F1@oracle.com> <33C18AAC-4AE7-480F-A07D-B72045C46388@oracle.com> <5a652475-e1be-ae48-a349-6dd39b5bb189@oracle.com> <700BBA89-6907-4987-BFFE-F633ACEB0149@oracle.com> Message-ID: An easier way to get there might be: ??? public sealed interface I ??????? permits C { } ??? private non-sealed abstract class C implements I { } and have the HCs extend C. On 4/3/2020 4:48 PM, John Rose wrote: > On Apr 3, 2020, at 1:36 PM, Brian Goetz wrote: >> I'm mostly thinking of interfaces that will only be implemented by hidden classes. > In order to do that we need a way to hook the HCs > into the permits list. That can be done with a private > abstract class that nobody extends except the desired HCs. > The HCs themselves cannot be placed on the permits list > because they cannot be named, and the permits list contains > names. (N.B. the experimental constant pool patching > mechanism would also, in principle, allow HCs to be > ?named? on permit lists, by direct patching of a relevant > CP entry. This is one sort of use case I was thinking of > when I joined the CP patching feature to the HC prototype. > Basically, imagine a whole ecosystem of HC hierarchies, > which would require patching the CPs of their various > classfiles. It would be a powerful mess. But there are > less powerful ways to get the same effects as CP patching, > for all the use cases we?ve looked at.) > > > From john.r.rose at oracle.com Fri Apr 3 20:58:08 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 3 Apr 2020 13:58:08 -0700 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> <38079815-DDAC-4E19-BBBB-48D239567342@oracle.com> <2B81B2F8-8099-426C-9346-470131B1D2F1@oracle.com> <33C18AAC-4AE7-480F-A07D-B72045C46388@oracle.com> <5a652475-e1be-ae48-a349-6dd39b5bb189@oracle.com> <700BBA89-6907-4987-BFFE-F633ACEB0149@oracle.com> Message-ID: That?s what I think I said? On Apr 3, 2020, at 1:52 PM, Brian Goetz wrote: > > An easier way to get there might be: > > public sealed interface I > permits C { } > > private non-sealed abstract class C implements I { } > > and have the HCs extend C. > > On 4/3/2020 4:48 PM, John Rose wrote: >> On Apr 3, 2020, at 1:36 PM, Brian Goetz wrote: >>> I'm mostly thinking of interfaces that will only be implemented by hidden classes. >> In order to do that we need a way to hook the HCs >> into the permits list. That can be done with a private >> abstract class that nobody extends except the desired HCs. >> The HCs themselves cannot be placed on the permits list >> because they cannot be named, and the permits list contains >> names. (N.B. the experimental constant pool patching >> mechanism would also, in principle, allow HCs to be >> ?named? on permit lists, by direct patching of a relevant >> CP entry. This is one sort of use case I was thinking of >> when I joined the CP patching feature to the HC prototype. >> Basically, imagine a whole ecosystem of HC hierarchies, >> which would require patching the CPs of their various >> classfiles. It would be a powerful mess. But there are >> less powerful ways to get the same effects as CP patching, >> for all the use cases we?ve looked at.) >> >> >> > From john.r.rose at oracle.com Fri Apr 3 21:03:45 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 3 Apr 2020 14:03:45 -0700 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> <38079815-DDAC-4E19-BBBB-48D239567342@oracle.com> <2B81B2F8-8099-426C-9346-470131B1D2F1@oracle.com> <33C18AAC-4AE7-480F-A07D-B72045C46388@oracle.com> <5a652475-e1be-ae48-a349-6dd39b5bb189@oracle.com> <700BBA89-6907-4987-BFFE-F633ACEB0149@oracle.com> Message-ID: <179D9493-5097-40F7-BA94-C594F34758A7@oracle.com> On Apr 3, 2020, at 1:52 PM, Brian Goetz wrote: > > An easier way to get there might be: > > public sealed interface I > permits C { } > > private non-sealed abstract class C implements I { } > > and have the HCs extend C. Also, as a special case, if no such HCs ever show up, then the JVM can infer that (at least for now) the interface I has no implementors at all. This can be enforced more permanently by this formula: public sealed interface I permits C { } private final class C { private C() {} } Here, the JVM would be encouraged (somehow) to believe that C is never instantiated; a belief that is easy to verify by observing that C::new is never executed. To enforce it even more statically I think we would need a class which is both abstract and final. public sealed interface I permits C { } private abstract final class C { } Offhand I can?t think of an equally ?airtight? way to define a class this is (a) never directly instantiated and (b) never subclassed. ? John From forax at univ-mlv.fr Fri Apr 3 21:42:28 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Fri, 3 Apr 2020 23:42:28 +0200 (CEST) Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: <5a652475-e1be-ae48-a349-6dd39b5bb189@oracle.com> References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> <38079815-DDAC-4E19-BBBB-48D239567342@oracle.com> <2B81B2F8-8099-426C-9346-470131B1D2F1@oracle.com> <33C18AAC-4AE7-480F-A07D-B72045C46388@oracle.com> <5a652475-e1be-ae48-a349-6dd39b5bb189@oracle.com> Message-ID: <596943647.1216954.1585950148348.JavaMail.zimbra@u-pem.fr> And more generally, should the VM offer a way to update the permitted subtypes list dynamically ? Most of the mocking API don't work with PermittedSubtypes, either because they try to do sneak a new subtypes using a classloader or for those that are using an agent because they try to change the bytecode at runtime and changing the attribute PermittedSubtypes at runtime is not allowed by JVMTI. R?mi > De: "Brian Goetz" > ?: "John Rose" > Cc: "Remi Forax" , "amber-spec-experts" > > Envoy?: Vendredi 3 Avril 2020 22:36:32 > Objet: Re: Semantics of an empty PermittedSubtypes attribute for the VM > I'm mostly thinking of interfaces that will only be implemented by hidden > classes. > On 4/3/2020 4:17 PM, John Rose wrote: >> On Apr 3, 2020, at 6:39 AM, Brian Goetz < [ mailto:brian.goetz at oracle.com | >> brian.goetz at oracle.com ] > wrote: >>>> You might think I?m arguing here for allowing S to be >>>> an empty set, and I might in similar cases, but there are two >>>> other reasons to outlaw the edge case, rather than ask >>>> the spec. to account for it (either by extending the general >>>> rule, or adding a special rule). First, if C wants to permit >>>> exactly zero subclasses, there?s already a notation for that. >>> ? unless C is an interface. >> Yes, sure. If there?s a use case for a never-implemented interface >> (I can think of a few) then we could either (a) allow interfaces to >> be marked final, or (b) allow empty permit lists, at the cost of >> the earlier objections (hey, it?s kinda confusing + two ways to >> say the same thing for non-interfaces). >> The most straightforward way to get the effect of a never-implemented >> interface is to give it a permits list which references no implemented types. >> The most straightforward way to do *that* is make the permits list >> empty. But there are other ways to get that effect. Make the permits >> list be a singleton reference to a private class that is either non-existent >> (never loaded because no classfile) or that is abstract and (somehow) >> permits no subtypes. In either case the JVM can be asked to observe >> that no instances are possible. >> Which tactic is best here? I suppose that depends on the use cases. >> If we are supposing users will want to use non-implemented >> interfaces as some sort of design pattern, then ?final? interfaces, or >> empty permits lists, are in the cards. But if it?s some kind of one-off >> thing (like an interface version of jl.Void) then the notation can be >> ugly, since it can be done once with adequate explanatory comments. >> ? John From john.r.rose at oracle.com Fri Apr 3 22:12:16 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 3 Apr 2020 15:12:16 -0700 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: <596943647.1216954.1585950148348.JavaMail.zimbra@u-pem.fr> References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> <38079815-DDAC-4E19-BBBB-48D239567342@oracle.com> <2B81B2F8-8099-426C-9346-470131B1D2F1@oracle.com> <33C18AAC-4AE7-480F-A07D-B72045C46388@oracle.com> <5a652475-e1be-ae48-a349-6dd39b5bb189@oracle.com> <596943647.1216954.1585950148348.JavaMail.zimbra@u-pem.fr> Message-ID: <1B5DCA65-B0AE-49EE-9E18-8B7A6471198A@oracle.com> On Apr 3, 2020, at 2:42 PM, forax at univ-mlv.fr wrote: > > And more generally, > should the VM offer a way to update the permitted subtypes list dynamically ? To me that?s an easy ?yes?, in the setting of HCs at least. Here?s the reasoning: - A HC can be dynamically injected into a nest, by invitation only. - The purpose of this is to allow any nest to invite additional private actions. - This allows us to defer building of bridges and adapters to runtime. - A ?private action? is any action which the static compiler could have arranged for. - Private actions define encapsulation boundaries. (They can be made public, protected, etc.) - A private action can be (wait for it?) access to private members. But a private action (as defined here) can also depend on other access control mechanisms besides literal ACC_PRIVATE checks, as long as they are associated with the job of the static compiler to create and protect encapsulation boundaries. Therefore: - Given a Lookup with full powers on some nest N, a HC can be created which behaves as if it were on the permits list of any member of N. Or, what?s to the same effect (assuming seamless teleports within a nest): - Given a Lookup with full powers on some type C, a HC can be created which behaves as if it were on the permits list of C. From john.r.rose at oracle.com Fri Apr 3 23:05:38 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 3 Apr 2020 16:05:38 -0700 Subject: Semantics of an empty PermittedSubtypes attribute for the VM In-Reply-To: <1B5DCA65-B0AE-49EE-9E18-8B7A6471198A@oracle.com> References: <1936619573.361824.1585828732260.JavaMail.zimbra@u-pem.fr> <38079815-DDAC-4E19-BBBB-48D239567342@oracle.com> <2B81B2F8-8099-426C-9346-470131B1D2F1@oracle.com> <33C18AAC-4AE7-480F-A07D-B72045C46388@oracle.com> <5a652475-e1be-ae48-a349-6dd39b5bb189@oracle.com> <596943647.1216954.1585950148348.JavaMail.zimbra@u-pem.fr> <1B5DCA65-B0AE-49EE-9E18-8B7A6471198A@oracle.com> Message-ID: <066AF993-125A-4944-92AD-104073C1BC58@oracle.com> On Apr 3, 2020, at 3:12 PM, John Rose wrote: > > - A ?private action? is any action which the static compiler could have arranged for. > - Private actions define encapsulation boundaries. (They can be made public, protected, etc.) P.S. This is the important part. A clean and secure VM design requires principles like this, as opposed to of patchwork of ?if this then that? rules about access control features, as if they were all unrelated. Patchworks without principles get subverted, eventually. From gavin.bierman at oracle.com Mon Apr 6 10:10:55 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 6 Apr 2020 11:10:55 +0100 Subject: Final issues regarding records Message-ID: Dear Experts: I?d like to circle back on a couple of issues regarding records. I am finalizing the draft language spec - would really like to complete this very soon - so it would be great to get some EG feedback so we cancomplete the spec (and tweak the implementation as necessary). I'm especially keen to hear from anyone who has actually been playing with the records prototype already. #1. Accessibility of various record members. In an earlier email, I wrote: > Currently the mandated members are public regardless of the accessibility of > the record type. What should be the default accessibility for record > members? > > A: Currently the most popular proposal is that mandated members get > accessibility of the record type. Other members get package level > accessibility as per a regular class. (Note: it should be legal for explicit > declarations of mandated members to specify something _more_ accessible > (same rules as overriding.) ) Just to clarify which members we are talking about; there are actually two categories to consider: 1. The implicitly declared accessor methods corresponding to the record components. 2. The canonical constructor (including possibly a compact constructor). For these members, the current spec and implementation require that they are `public` regardless of the accessibility of the enclosing record type. The question before the house is whether this is the right design. A gut reaction is that it feels wrong; why insist on a public accessibility regardless of the enclosing declaration? But after a little thought, it is not so clear that this is the wrong design. First, note that enums already have this implicit-members-are-`public` feature - the implicitly declared `values` and `valueOf` methods are `public` regardless of the accessibility of the enum type. As far as I am aware, this feature of enums has not caused any confusion (or much comment). Second, consider the case where we are using interfaces to specify the behavior of the record type, including the accessor methods. For example: interface I { int i(); } record R(int i) implements I {} If we make R private, then there is no way we can satisfy our contract as interfaces can?t have private methods. In this example, there?s not a lot we can do other than explicitly declare the accessor method ourselves. This is a shame - we can't utilize the implicit declaration of accessor methods because of a mismatch of accessibility. Whilst this example is a little contrived, it asks whether the essence of the (public) contract that records satisfy, even private ones, is that they support accessor methods to access the values of the record components. Regarding the canonical constructor: it's indeed the case that for normal class declarations the *default* constructor gets the access modifier from the enclosing class declaration. But, this is a weak argument for *canonical* constructors, which are different in a number of ways already. I can see 4 options: 1. Keep the everything public option as currently spec-ed and implemented. This is simple to remember, and not without precedent. 2. Change the current spec and implementation so the accessor methods and canonical constructor inherit the accessibility from the record type. This is also simple to remember, but it ignores the issues with interfaces detailed above. 3. Incorporate both strategies: Perhaps that the canonical constructor inherits accessibility from the record type, whereas accessor methods are required to be public. 4. Some variant of (2) that can deal with the interface example (?) What are your thoughts? #2. Annotating explicit accessor methods > Q10. Special annotation for explicit declaration of accessors. > > Tagir [6] proposes a new `@RecordAccessor` annotation to mark explicit > accessors, much as `@Override` is used to mark method overrides. > > A: Rather than introduce a new accessor, we will consider extending the > meaning of the `@Override` annotation to include this case. There's been some discussion about this, but we were hoping for more! The motivation for co-opting @Override is that its purpose already is to allow the compiler to give us better type checking because we?ve captured a bit of user intent, so this feels similar. However, Peter has made the excellent point that extending `@Override` in this way might be confusing in record declarations that also contain implementations of methods from interfaces. Essentially, I think our options are: 1. Do nothing. (We can come back to this at the next release.) 2. Co-op `@Override` 3. Invent a new annotation, as suggested by Tagir. Any further thoughts? --- Thanks for your consideration. I look forward to hearing your feedback. Best wishes, Gavin From forax at univ-mlv.fr Mon Apr 6 11:26:25 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 6 Apr 2020 13:26:25 +0200 (CEST) Subject: Final issues regarding records In-Reply-To: References: Message-ID: <615206035.272328.1586172385225.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Gavin Bierman" > ?: "amber-spec-experts" > Envoy?: Lundi 6 Avril 2020 12:10:55 > Objet: Final issues regarding records > Dear Experts: > > I?d like to circle back on a couple of issues regarding records. I am finalizing > the draft language spec - would really like to complete this very soon - so it > would be great to get some EG feedback so we cancomplete the spec (and tweak > the implementation as necessary). I'm especially keen to hear from anyone who > has actually been playing with the records prototype already. > > > #1. Accessibility of various record members. > > In an earlier email, I wrote: > >> Currently the mandated members are public regardless of the accessibility of >> the record type. What should be the default accessibility for record >> members? >> >> A: Currently the most popular proposal is that mandated members get >> accessibility of the record type. Other members get package level >> accessibility as per a regular class. (Note: it should be legal for explicit >> declarations of mandated members to specify something _more_ accessible >> (same rules as overriding.) ) > > Just to clarify which members we are talking about; there are actually two > categories to consider: > > 1. The implicitly declared accessor methods corresponding to the record > components. > > 2. The canonical constructor (including possibly a compact constructor). > > For these members, the current spec and implementation require that they are > `public` regardless of the accessibility of the enclosing record type. > > The question before the house is whether this is the right design. A gut > reaction is that it feels wrong; why insist on a public accessibility regardless > of the enclosing declaration? But after a little thought, it is not so clear > that this is the wrong design. > > First, note that enums already have this implicit-members-are-`public` feature - > the implicitly declared `values` and `valueOf` methods are `public` regardless > of the accessibility of the enum type. As far as I am aware, this feature of > enums has not caused any confusion (or much comment). > > Second, consider the case where we are using interfaces to specify the behavior > of the record type, including the accessor methods. For example: > > interface I { > int i(); > } > > record R(int i) implements I {} > > If we make R private, then there is no way we can satisfy our contract as > interfaces can?t have private methods. In this example, there?s not a lot we can > do other than explicitly declare the accessor method ourselves. This is a shame > - we can't utilize the implicit declaration of accessor methods because of a > mismatch of accessibility. > > Whilst this example is a little contrived, it asks whether the essence of the > (public) contract that records satisfy, even private ones, is that they support > accessor methods to access the values of the record components. > > Regarding the canonical constructor: it's indeed the case that for normal class > declarations the *default* constructor gets the access modifier from the > enclosing class declaration. But, this is a weak argument for *canonical* > constructors, which are different in a number of ways already. > > I can see 4 options: > > 1. Keep the everything public option as currently spec-ed and implemented. This > is simple to remember, and not without precedent. > > 2. Change the current spec and implementation so the accessor methods and > canonical constructor inherit the accessibility from the record type. This is > also simple to remember, but it ignores the issues with interfaces detailed > above. > > 3. Incorporate both strategies: Perhaps that the canonical constructor inherits > accessibility from the record type, whereas accessor methods are required to be > public. > > 4. Some variant of (2) that can deal with the interface example (?) > > What are your thoughts? 1/ We have carefully to provide a path to refactor an existing class to a record, so a record constructor and a class mandated constructor so should have the same accessibility. In the following code, the class R should be able to be refactored to a record R. class A { private class R {} private record R() {} } 2/ I don't understand how a non public accessor can be useful, if the class has non public access, it can use the fields instead of the accessor, so i fail to see a use case for non public accessors. Given 1/ and 2/, the corresponding option is (3). > > > #2. Annotating explicit accessor methods > >> Q10. Special annotation for explicit declaration of accessors. >> >> Tagir [6] proposes a new `@RecordAccessor` annotation to mark explicit >> accessors, much as `@Override` is used to mark method overrides. >> >> A: Rather than introduce a new accessor, we will consider extending the >> meaning of the `@Override` annotation to include this case. > > There's been some discussion about this, but we were hoping for more! The > motivation for co-opting @Override is that its purpose already is to allow the > compiler to give us better type checking because we?ve captured a bit of user > intent, so this feels similar. However, Peter has made the excellent point that > extending `@Override` in this way might be confusing in record declarations that > also contain implementations of methods from interfaces. > > Essentially, I think our options are: > > 1. Do nothing. (We can come back to this at the next release.) > > 2. Co-op `@Override` > > 3. Invent a new annotation, as suggested by Tagir. Co-op @Overrride. > > > Any further thoughts ? > > > --- > > Thanks for your consideration. I look forward to hearing your feedback. regards, R?mi > > Best wishes, > Gavin From brian.goetz at oracle.com Mon Apr 6 12:59:28 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 6 Apr 2020 08:59:28 -0400 Subject: Final issues regarding records In-Reply-To: <615206035.272328.1586172385225.JavaMail.zimbra@u-pem.fr> References: <615206035.272328.1586172385225.JavaMail.zimbra@u-pem.fr> Message-ID: >> #1. Accessibility of various record members. Let?s write down the various (competing) considerations here: - Consistency with precedent of default constructors. When the compiler fills in a default no-arg constructor in a class that has no constructors, the default constructor gets the accessibility of the class. - Consistency between mandated members. It seems nominally simpler to have all the mandated members have the same accessibility. - Consistency between class and member accessibility. It seems weird if a class is private but its constructor is public. - Consistency between members mandated by record and members inherited from interfaces (as Gavin?s example illustrates.) Note that all of these can be cast as ?for consistency? arguments. And you all know what i think of ?for consistency? arguments. And this set (and possibly others) illustrates the swan song of consistency: in a complex system, one can find consistency precedent for nearly anything. Observation: users will ?override" the constructor much, much more often than accessors; to the extent we get the rules wrong regarding accessibility, rules regarding constructor accessibility will bite much more often. While few developers are likely to even know the rule for default constructors, they will surely be surprised to have to write a public constructor in a private class. The reality here is that every category of mandated member(ctor, accessor, equals/hashCode) are in a different bucket regarding accessibility. For equals/hashCode, these are forced moves ? they must be public. I think the ?consistent with default constructors? rule for constructors makes the most sense, not because of consistency, but because of user intuition ? this is likely what they are going to type first. But accessors are in their own weird new world. I think (3) ? make them public, as if they were specified by a hidden interface ? is probably the least bad choice here. > > Co-op @Overrride. Agree on this as well. I don?t think having a separate thing helps anyone, nor does having no way to capture this intent. It will be a little weird that you can ?override? a constructor now, but that will only likely be weird for a few minutes. From james.laskey at oracle.com Mon Apr 6 17:03:51 2020 From: james.laskey at oracle.com (Jim Laskey) Date: Mon, 6 Apr 2020 14:03:51 -0300 Subject: @since specification for preview features Message-ID: FTR: A question was raised about which Java version should be used with the @since tag of method associated with a preview feature. The evident answer is that while previewing, that the value should be the version of Java where the preview feature was introduced. When the feature becomes standard, then the value should be the Java version where the feature became standard. Ex. Text Blocks became a preview feature in JDK 13, thus String::stripIndent had a @since 13 tag. Since, Text Blocks continued as preview feature in JDK 14 and the tag remained the same. When Text Blocks become standard in JDK 15, the tag in String::stripIndent will become @since 15. Cheers, -- Jim From alex.buckley at oracle.com Mon Apr 6 17:10:45 2020 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 6 Apr 2020 10:10:45 -0700 Subject: @since specification for preview features In-Reply-To: References: Message-ID: Thanks Jim. I recorded this policy in JEP 12, under "Relationship to Java SE APIs": "The API developer must also add an @since tag that indicates the release when @preview was first added. (If the essential API element is eventually made final and permanent in Java SE $Z, then the @since tag must be changed to indicate the $Z release.)" Alex On 4/6/2020 10:03 AM, Jim Laskey wrote: > FTR: A question was raised about which Java version should be used with > the @since tag of method associated with a preview feature. The evident > answer is that while previewing, that the value should be the version of > Java where the preview feature was introduced. When the feature becomes > standard, then the value should be the Java version where the feature > became standard. > > Ex. Text Blocks became a preview feature in JDK 13, thus > String::stripIndent had a @since 13tag. Since, Text Blocks continued as > preview feature in JDK 14 and the tag remained the same. ?When Text > Blocks become standard in JDK 15, the tag in String::stripIndent will > become @since 15. > > Cheers, > > -- Jim > From james.laskey at oracle.com Mon Apr 6 18:43:30 2020 From: james.laskey at oracle.com (Jim Laskey) Date: Mon, 6 Apr 2020 15:43:30 -0300 Subject: JLS question regarding Text Blocks Message-ID: <8C4D61F1-A7DE-49A4-B461-E98E10E102E9@oracle.com> In section 3.10.6 Text Blocks of the updated spec; "The opening delimiter is a sequence that starts with three double quote characters ("""), continues with zero or more space, tab, and form feed characters, and concludes with a line terminator." However, the JEP 378 description reads "The opening delimiter is a sequence of three double quote characters (""") followed by zero or more white spaces followed by a line terminator. javac uses the Character.isWhitespace test for characters following the opening three double quote characters. Shouldn't the spec read as; "The opening delimiter is a sequence that starts with three double quote characters ("""), continues with zero or more white space characters (Ex. space, tab, and form feed), and concludes with a line terminator."? Cheers, -- Jim From alex.buckley at oracle.com Mon Apr 6 19:05:06 2020 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 6 Apr 2020 12:05:06 -0700 Subject: JLS question regarding Text Blocks In-Reply-To: <8C4D61F1-A7DE-49A4-B461-E98E10E102E9@oracle.com> References: <8C4D61F1-A7DE-49A4-B461-E98E10E102E9@oracle.com> Message-ID: <970ccc7e-8bc3-c9a0-c114-df89140a45f6@oracle.com> On 4/6/2020 11:43 AM, Jim Laskey wrote: > In section 3.10.6 Text Blocks of the updated spec; > > "The opening delimiter is a sequence that starts with three double quote > characters ("""), continues with zero or more space, tab, and form feed > characters, and concludes with a line terminator." > > However, the JEP 378 description reads "The opening delimiter is a > sequence of three double quote characters (""") followed by zero or more > white spaces followed by a line terminator. > > javac uses the Character.isWhitespace test for characters following the > opening three double quote characters. Character::isWhiteSpace allows not only the JLS definition of whitespace -- space, tab, and FF -- but also LF and CR. So, javac would accept """ followed by some LF/CR characters (its idea of "zero or more white spaces") followed by a final LF/CR character (the "line terminator", at least according to the definition in JLS 3.4; can't find a definition in the API). That can't be right -- it extends the delimiter line into the content, so to speak. Alex From daniel.smith at oracle.com Mon Apr 6 23:46:10 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 6 Apr 2020 17:46:10 -0600 Subject: JLS question regarding Text Blocks In-Reply-To: <8C4D61F1-A7DE-49A4-B461-E98E10E102E9@oracle.com> References: <8C4D61F1-A7DE-49A4-B461-E98E10E102E9@oracle.com> Message-ID: > On Apr 6, 2020, at 12:43 PM, Jim Laskey wrote: > > In section 3.10.6 Text Blocks of the updated spec; > > "The opening delimiter is a sequence that starts with three double quote characters ("""), continues with zero or more space, tab, and form feed characters, and concludes with a line terminator." > > However, the JEP 378 description reads "The opening delimiter is a sequence of three double quote characters (""") followed by zero or more white spaces followed by a line terminator. > > javac uses the Character.isWhitespace test for characters following the opening three double quote characters. > > Shouldn't the spec read as; > > "The opening delimiter is a sequence that starts with three double quote characters ("""), continues with zero or more white space characters (Ex. space, tab, and form feed), and concludes with a line terminator."? Is there a question about semantics here, or just a question of how best to present it? Right now, TextBlockWhiteSpace defines precisely which characters can appear. Per 3.6, WhiteSpace is a grammar production that consists of 3 specific characters (space, horizontal tab, and form feed), plus the line terminators. TextBlockWhiteSpace excludes the line terminators. Given this, I don't think redirecting through an abstract notion of "white space" or the "Character.isWhitespace" API would be helpful?the grammar has already nailed it down to exactly three characters. From james.laskey at oracle.com Tue Apr 7 00:42:09 2020 From: james.laskey at oracle.com (Jim Laskey) Date: Mon, 6 Apr 2020 21:42:09 -0300 Subject: JLS question regarding Text Blocks In-Reply-To: References: <8C4D61F1-A7DE-49A4-B461-E98E10E102E9@oracle.com> Message-ID: The spec is correct as-is. I have been made aware that white space in spec terms, as well as the compiler, is only space, tab and form-feed. Other white space (such as non-breaking space, em space, ...) is illegal and produces a javac error. -- Jim > On Apr 6, 2020, at 8:46 PM, Dan Smith wrote: > >> On Apr 6, 2020, at 12:43 PM, Jim Laskey wrote: >> >> In section 3.10.6 Text Blocks of the updated spec; >> >> "The opening delimiter is a sequence that starts with three double quote characters ("""), continues with zero or more space, tab, and form feed characters, and concludes with a line terminator." >> >> However, the JEP 378 description reads "The opening delimiter is a sequence of three double quote characters (""") followed by zero or more white spaces followed by a line terminator. >> >> javac uses the Character.isWhitespace test for characters following the opening three double quote characters. >> >> Shouldn't the spec read as; >> >> "The opening delimiter is a sequence that starts with three double quote characters ("""), continues with zero or more white space characters (Ex. space, tab, and form feed), and concludes with a line terminator."? > > Is there a question about semantics here, or just a question of how best to present it? > > Right now, TextBlockWhiteSpace defines precisely which characters can appear. Per 3.6, WhiteSpace is a grammar production that consists of 3 specific characters (space, horizontal tab, and form feed), plus the line terminators. TextBlockWhiteSpace excludes the line terminators. > > Given this, I don't think redirecting through an abstract notion of "white space" or the "Character.isWhitespace" API would be helpful?the grammar has already nailed it down to exactly three characters. From james.laskey at oracle.com Tue Apr 7 00:54:32 2020 From: james.laskey at oracle.com (James Laskey) Date: Mon, 6 Apr 2020 21:54:32 -0300 Subject: JLS question regarding Text Blocks In-Reply-To: <970ccc7e-8bc3-c9a0-c114-df89140a45f6@oracle.com> References: <970ccc7e-8bc3-c9a0-c114-df89140a45f6@oracle.com> Message-ID: <8D0E4C76-8A57-487A-8D01-46F90861EEA8@oracle.com> My interpretation was that concludes with a line terminator would make this a non-greedy consumption of white space. But as I said in my response to Dan I didn?t understand white space in spec terms. I had been thinking in terms of the core lib interpretation. Apologies. > On Apr 6, 2020, at 4:05 PM, Alex Buckley wrote: > > ?On 4/6/2020 11:43 AM, Jim Laskey wrote: >> In section 3.10.6 Text Blocks of the updated spec; >> "The opening delimiter is a sequence that starts with three double quote characters ("""), continues with zero or more space, tab, and form feed characters, and concludes with a line terminator." >> However, the JEP 378 description reads "The opening delimiter is a sequence of three double quote characters (""") followed by zero or more white spaces followed by a line terminator. >> javac uses the Character.isWhitespace test for characters following the opening three double quote characters. > > Character::isWhiteSpace allows not only the JLS definition of whitespace -- space, tab, and FF -- but also LF and CR. So, javac would accept """ followed by some LF/CR characters (its idea of "zero or more white spaces") followed by a final LF/CR character (the "line terminator", at least according to the definition in JLS 3.4; can't find a definition in the API). That can't be right -- it extends the delimiter line into the content, so to speak. > > Alex From forax at univ-mlv.fr Tue Apr 7 18:20:03 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 7 Apr 2020 20:20:03 +0200 (CEST) Subject: Deconstruction but no destructuring when declaring local variables Message-ID: <437191446.1118496.1586283603333.JavaMail.zimbra@u-pem.fr> There was discussions about the danger of only providing instanceof + the deconstruction pattern and not a way to have the same kind of destructuring when declaring local variables By example, instead of Point p = ... int x = p.x(); int y = p.y(); one can write Point p = ... if (!(p instanceof Point(int x, int y))); I think we should restart those discussions because variables declarations is like a third places where patterns can appear (instanceof and switch being the other two). I don't really want to propose a syntax, so the next examples will be to give an idea of the semantics The idea is to allow patterns on the left side of an '='. So pattern = value; We already have Point p2 = p; // the type pattern so if we expand it with the new patterns proposed for instanceof, we get Point(int x, int y) = p; // the deconstruction pattern with explicit type and Point(var x, var y) = p; // the deconstruction pattern with var regards, R?mi From brian.goetz at oracle.com Tue Apr 7 18:29:52 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 7 Apr 2020 14:29:52 -0400 Subject: Deconstruction but no destructuring when declaring local variables In-Reply-To: <437191446.1118496.1586283603333.JavaMail.zimbra@u-pem.fr> References: <437191446.1118496.1586283603333.JavaMail.zimbra@u-pem.fr> Message-ID: <58d67020-0aa2-16e7-13cf-0292252f38a5@oracle.com> I'd like to take this up later, but let me read into the record a few points we've already discussed on this matter: ?- Yes, it is very tempting to like this syntax because in the degenerate case, where `Foo f` is a type pattern, it looks just like a declaration with an initializer, so much so that we can say "aha, you never knew Java had patterns from day 1!".? But, ultimately this runs out of gas when patterns get more complicated, so be wary of being seduced by how natural it looks in the simplest case. Even the middle ground: ??? Point(var x, var y) = p doesn't look much like a declaration, or worse ??? Foo() = f looks like, well, who knows. ?- This really only works when the pattern is _total_ on the target type, so that the pattern provably applies (modulo null.)? That can work for record deconstruction patterns but not always for other pattern types.? Alternately, we can have a statement form "bind P = target else { ... }" for partial patterns, but then we lose the nice connection noted above with declarations. On 4/7/2020 2:20 PM, Remi Forax wrote: > There was discussions about the danger of only providing instanceof + the deconstruction pattern and not a way to have the same kind of destructuring when declaring local variables > > By example, instead of > Point p = ... > int x = p.x(); > int y = p.y(); > > one can write > Point p = ... > if (!(p instanceof Point(int x, int y))); > > I think we should restart those discussions because variables declarations is like a third places where patterns can appear (instanceof and switch being the other two). > > I don't really want to propose a syntax, so the next examples will be to give an idea of the semantics > The idea is to allow patterns on the left side of an '='. > So > pattern = value; > > We already have > Point p2 = p; // the type pattern > > so if we expand it with the new patterns proposed for instanceof, we get > Point(int x, int y) = p; // the deconstruction pattern with explicit type > and > Point(var x, var y) = p; // the deconstruction pattern with var > > regards, > R?mi > > > > > > > From guy.steele at oracle.com Tue Apr 7 18:45:24 2020 From: guy.steele at oracle.com (Guy Steele) Date: Tue, 7 Apr 2020 14:45:24 -0400 Subject: Deconstruction but no destructuring when declaring local variables In-Reply-To: <58d67020-0aa2-16e7-13cf-0292252f38a5@oracle.com> References: <437191446.1118496.1586283603333.JavaMail.zimbra@u-pem.fr> <58d67020-0aa2-16e7-13cf-0292252f38a5@oracle.com> Message-ID: <4792C182-E5CC-4BB5-93E3-414D9CD5091C@oracle.com> To summarize and highlight the second problem: patterns can fail. So any use of patterns in the language must address how failure is to be handled. > On Apr 7, 2020, at 2:29 PM, Brian Goetz wrote: > > I'd like to take this up later, but let me read into the record a few points we've already discussed on this matter: > > - Yes, it is very tempting to like this syntax because in the degenerate case, where `Foo f` is a type pattern, it looks just like a declaration with an initializer, so much so that we can say "aha, you never knew Java had patterns from day 1!". But, ultimately this runs out of gas when patterns get more complicated, so be wary of being seduced by how natural it looks in the simplest case. > > Even the middle ground: > > Point(var x, var y) = p > > doesn't look much like a declaration, or worse > > Foo() = f > > looks like, well, who knows. > > - This really only works when the pattern is _total_ on the target type, so that the pattern provably applies (modulo null.) That can work for record deconstruction patterns but not always for other pattern types. Alternately, we can have a statement form "bind P = target else { ... }" for partial patterns, but then we lose the nice connection noted above with declarations. > > > > On 4/7/2020 2:20 PM, Remi Forax wrote: >> There was discussions about the danger of only providing instanceof + the deconstruction pattern and not a way to have the same kind of destructuring when declaring local variables >> >> By example, instead of >> Point p = ... >> int x = p.x(); >> int y = p.y(); >> >> one can write >> Point p = ... >> if (!(p instanceof Point(int x, int y))); >> >> I think we should restart those discussions because variables declarations is like a third places where patterns can appear (instanceof and switch being the other two). >> >> I don't really want to propose a syntax, so the next examples will be to give an idea of the semantics >> The idea is to allow patterns on the left side of an '='. >> So >> pattern = value; >> >> We already have >> Point p2 = p; // the type pattern >> >> so if we expand it with the new patterns proposed for instanceof, we get >> Point(int x, int y) = p; // the deconstruction pattern with explicit type >> and >> Point(var x, var y) = p; // the deconstruction pattern with var >> >> regards, >> R?mi >> >> >> >> >> >> >> > From forax at univ-mlv.fr Tue Apr 7 18:50:37 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 7 Apr 2020 20:50:37 +0200 (CEST) Subject: Deconstruction but no destructuring when declaring local variables In-Reply-To: <58d67020-0aa2-16e7-13cf-0292252f38a5@oracle.com> References: <437191446.1118496.1586283603333.JavaMail.zimbra@u-pem.fr> <58d67020-0aa2-16e7-13cf-0292252f38a5@oracle.com> Message-ID: <1205237824.1121427.1586285437028.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" , "amber-spec-experts" > > Envoy?: Mardi 7 Avril 2020 20:29:52 > Objet: Re: Deconstruction but no destructuring when declaring local variables > I'd like to take this up later, but let me read into the record a few points > we've already discussed on this matter: > - Yes, it is very tempting to like this syntax because in the degenerate case, > where `Foo f` is a type pattern, it looks just like a declaration with an > initializer, so much so that we can say "aha, you never knew Java had patterns > from day 1!". But, ultimately this runs out of gas when patterns get more > complicated, so be wary of being seduced by how natural it looks in the > simplest case. > Even the middle ground: > Point(var x, var y) = p > doesn't look much like a declaration, I don't know, it's maybe familiarity. It's weird the first time you see it, but it''s close the kind of deconstructing you have in JavaScript or C# > or worse > Foo() = f > looks like, well, who knows. x instanceof Foo() looks as weird for me. We should not allow deconstruction pattern that doesn't introduce at least a variable, in instanceof and in this case. > - This really only works when the pattern is _total_ on the target type, so that > the pattern provably applies (modulo null.) That can work for record > deconstruction patterns but not always for other pattern types. Alternately, we > can have a statement form "bind P = target else { ... }" for partial patterns, > but then we lose the nice connection noted above with declarations. Yes, for both local variable declarations and instanceof, the pattern has to be total, that a major difference with switch. I don't think it's wise to allow by example constants with an instanceof x instanceof Point(var x, 11) at the same time, if you allow local variables in patterns, you have a nice way to write equals class A { int foo; int bar; public boolean equals(Object o) { return o instanceof A(this.foo, this.bar); } } which can be simplified to public boolean equals(Object o) { return o instanceof A(foo, bar); } but it's a step roo far IMO. R?mi > On 4/7/2020 2:20 PM, Remi Forax wrote: >> There was discussions about the danger of only providing instanceof + the >> deconstruction pattern and not a way to have the same kind of destructuring >> when declaring local variables >> By example, instead of >> Point p = ... >> int x = p.x(); >> int y = p.y(); >> one can write >> Point p = ... >> if (!(p instanceof Point(int x, int y))); >> I think we should restart those discussions because variables declarations is >> like a third places where patterns can appear (instanceof and switch being the >> other two). >> I don't really want to propose a syntax, so the next examples will be to give an >> idea of the semantics >> The idea is to allow patterns on the left side of an '='. >> So >> pattern = value; >> We already have >> Point p2 = p; // the type pattern >> so if we expand it with the new patterns proposed for instanceof, we get >> Point(int x, int y) = p; // the deconstruction pattern with explicit type >> and >> Point(var x, var y) = p; // the deconstruction pattern with var >> regards, >> R?mi From gavin.bierman at oracle.com Mon Apr 13 16:37:14 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 13 Apr 2020 17:37:14 +0100 Subject: Record component type can be an inner class of a record In-Reply-To: <594699329.1500670.1585083459947.JavaMail.zimbra@u-pem.fr> References: <594699329.1500670.1585083459947.JavaMail.zimbra@u-pem.fr> Message-ID: Thanks for pointing this out Remi. We definitely need to tweak the draft spec to deal with this scoping question. However, I am of the opinion that this example should not be allowed. I would expect the scope of things defined in a record body to be the record body. I don?t think the record header should be considered part of the body. Analogously: class Foo { class Bar { ? } ? } This doesn?t work as the scope of the Bar declaration is the class body. What do you think? Gavin > On 24 Mar 2020, at 20:57, Remi Forax wrote: > > Hi all, > a record component can use as type a type declared inside the record itself, > in term of scoping it's like if the record component is part of the internal scope of the record. > > record Foo(Bar bar) { > class Bar { > > } > } > > I think it's the right behaviour but i was not able to find any reference to that in the spec. > > regards, > R?mi From forax at univ-mlv.fr Mon Apr 13 17:08:10 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 13 Apr 2020 19:08:10 +0200 (CEST) Subject: Record component type can be an inner class of a record In-Reply-To: References: <594699329.1500670.1585083459947.JavaMail.zimbra@u-pem.fr> Message-ID: <486125830.193897.1586797690659.JavaMail.zimbra@u-pem.fr> > De: "Gavin Bierman" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Lundi 13 Avril 2020 18:37:14 > Objet: Re: Record component type can be an inner class of a record > Thanks for pointing this out Remi. We definitely need to tweak the draft spec to > deal with this scoping question. > However, I am of the opinion that this example should not be allowed. I would > expect the scope of things defined in a record body to be the record body. I > don?t think the record header should be considered part of the body. > Analogously: > class Foo { > class Bar { ? } > ? > } > This doesn?t work as the scope of the Bar declaration is the class body. > What do you think? I believe you're right, the following code should not compile, apart if you want to write puzzler for a living :) class A { int y; } record B(A a) implements I { public static void main(String[] args) { System.out.println(new B(null).a().x); } } interface I { class A { int x; } } > Gavin R?mi >> On 24 Mar 2020, at 20:57, Remi Forax < [ mailto:forax at univ-mlv.fr | >> forax at univ-mlv.fr ] > wrote: >> Hi all, >> a record component can use as type a type declared inside the record itself, >> in term of scoping it's like if the record component is part of the internal >> scope of the record. >> record Foo(Bar bar) { >> class Bar { >> } >> } >> I think it's the right behaviour but i was not able to find any reference to >> that in the spec. >> regards, >> R?mi From daniel.smith at oracle.com Mon Apr 13 19:13:51 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 13 Apr 2020 13:13:51 -0600 Subject: Record component type can be an inner class of a record In-Reply-To: <486125830.193897.1586797690659.JavaMail.zimbra@u-pem.fr> References: <594699329.1500670.1585083459947.JavaMail.zimbra@u-pem.fr> <486125830.193897.1586797690659.JavaMail.zimbra@u-pem.fr> Message-ID: > On Apr 13, 2020, at 11:08 AM, forax at univ-mlv.fr wrote: > > I believe you're right, the following code should not compile, apart if you want to write puzzler for a living :) > > class A { int y; } > > record B(A a) implements I { > public static void main(String[] args) { > System.out.println(new B(null).a().x); > } > } > > interface I { > class A { int x; } > } You're proposing a novel error check, which I'm not sure is a good idea. I believe the argument is that 'A' as it appears in the record declaration header means something different than 'A' as it would appear in an accessor or constructor declaration. That's true?the context in which a name appears in the header is not exactly the same as the context in which a name appears in the body. But I don't think it's our job to detect those cases. Instead, the compiler should interpret the name in the context in which it actually appears, and then ensure implicit members refer to that same entity. (E.g., the return type of the implicit accessor would effectively be spelled 'package.A'. But I don't think implicit members should be thought of as existing at the level of syntax at all.) I worry that if we go in the other direction?deciding it's an error if a name in implicit code would mean something different than the explicit name in the program?we're taking on a complex task of proving, wherever a name is used in implicit code, that the actual and implicit contexts are equivalent. I'd prefer not to have that responsibility. (For example: interfaces have implicit members (JLS 9.2). We don't do anything to ensure that, say, 'String' isn't shadowed by a member class of the interface.) From gavin.bierman at oracle.com Tue Apr 14 17:12:23 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Tue, 14 Apr 2020 18:12:23 +0100 Subject: [patterns] Draft Spec for JEP 375 Pattern Matching for instanceof (Preview 2) In-Reply-To: References: Message-ID: Dear experts: The latest (and hopefully final) draft of JEP 375 (Pattern matching for instanceof) is available at: http://cr.openjdk.java.net/~gbierman/jep375/latest This spec is now equivalent to the spec for the first preview version (JEP 305). The only changes are: 1. Some minor typos corrected. 2. Added missing case dealing with parenthesized expressions in the definition of scope of pattern variables. 3. Added missing case dealing with labeled statements in the definition of scope of pattern variables. We have decided to remove the deconstruction patterns for record types from this preview release. There are some complications surrounding generics and deconstruction patterns, along with varargs and deconstruction patterns, and we'd like some more time to consider our design. I'll write separately on these issues. Thanks, Gavin From forax at univ-mlv.fr Thu Apr 16 13:19:02 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 16 Apr 2020 15:19:02 +0200 (CEST) Subject: RFR: JEP 360: Sealed Types (Preview) In-Reply-To: <794b4e0e-3ec0-c9d3-388e-3c8f218dc78e@oracle.com> References: <794b4e0e-3ec0-c9d3-388e-3c8f218dc78e@oracle.com> Message-ID: <1435546474.294773.1587043142196.JavaMail.zimbra@u-pem.fr> About the JEP text, It's not clear to me if "compilers" in the sentence "The lack of documented exhaustiveness also prevents compilers from being able to do exhaustiveness analysis." refers to java compiler or JIT compilers. Obviously the answer is both :) The example of "rotate" is a weird example, currently there is no exhaustiveness analysis on a switch statement (we have decided that backward compatibility was more important than exhaustiveness, something i'm already regretting). Or worst does it mean that switch on Object will do exhaustiveness analysis while switch on enum don't ? cheers, R?mi ----- Mail original ----- > De: "Vicente Romero" > ?: "amber-dev" , "compiler-dev" > Envoy?: Lundi 13 Avril 2020 18:03:18 > Objet: RFR: JEP 360: Sealed Types (Preview) > Hi all, > > The sealed types JEP was already reviewed a while back when we were > planning to include it in JDK14. It finally fell off that boat but it is > being considered now for JDK15. There have been some changes since then > mostly related to subtypes of a sealed type. Before we were planning to > infer finality, sealness or non-sealness in the subtypes. We steered > away from that direction in favor of explicit declaration at the > subtype. I would like to ask for another review of the current version > of the JEP that reflects these changes. The JEP is at [1] and the last > version of the spec is at [2], > > Thanks, > Vicente > > [1] https://bugs.openjdk.java.net/browse/JDK-8227043 > [2] > http://cr.openjdk.java.net/~gbierman/jep360/jep360-20200228/specs/sealed-types-jls.html From forax at univ-mlv.fr Sun Apr 19 16:40:16 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sun, 19 Apr 2020 18:40:16 +0200 (CEST) Subject: Record component type can be an inner class of a record In-Reply-To: References: <594699329.1500670.1585083459947.JavaMail.zimbra@u-pem.fr> <486125830.193897.1586797690659.JavaMail.zimbra@u-pem.fr> Message-ID: <489239011.1667370.1587314416053.JavaMail.zimbra@u-pem.fr> Hi Dan, ----- Mail original ----- > De: "daniel smith" > ?: "Remi Forax" > Cc: "Gavin Bierman" , "amber-spec-experts" > Envoy?: Lundi 13 Avril 2020 21:13:51 > Objet: Re: Record component type can be an inner class of a record >> On Apr 13, 2020, at 11:08 AM, forax at univ-mlv.fr wrote: >> >> I believe you're right, the following code should not compile, apart if you want >> to write puzzler for a living :) >> >> class A { int y; } >> >> record B(A a) implements I { >> public static void main(String[] args) { >> System.out.println(new B(null).a().x); >> } >> } >> >> interface I { >> class A { int x; } >> } > > You're proposing a novel error check, which I'm not sure is a good idea. I think you mis-read it. It's an example that shows that depending if the record component type is resolve as part of outside of the record or inside the record, not the same A is picked. The A at top level declare a field "y" while the A inside I declare a field "x", that why it doesn't compile. So it's not a new error check. regards, R?mi From brian.goetz at oracle.com Mon Apr 20 18:18:14 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 20 Apr 2020 14:18:14 -0400 Subject: Reader mail bag Message-ID: <630489c3-00f7-69e7-b35d-5a57ba52d575@oracle.com> A number of comments have come in on the -comments list.? If you want to continue the discussion on any of these, please start a new thread. Regarding the brief thread on do expressions: > Hi All, > I have seen your discussion regarding using a new expression to place > field's initialization block with the field instead of in class/instance > initializer. > > While I understand reluctance to introduce new keywords, there is the idea > of using lazy static final fields [1] instead of complicated class static > initializers. > I think that scenario might also need a new expression more. > > Has there been any thoughts how to declare source-code static final field > to be translated into a LazyValue backed by condy? > Moreover, has there been any thoughts how to declare such field to throw > checked exception on field read when its initialization might? > > Sorry if the issue has already been discussed. > > Thanks, > Mateusz Romanowski Regarding the j.l.Record superclass: > Hi All, > Do you think that specialization could be used for record's implicit > methods, while rehabilitating meaning of @Override? > > specialized public abstract class j.l.Record> { > specialized int hashCode(); > specialized boolean equals(Object o); > specialized accessor for each RecordComponent; > } > > Then for a record named Coordinates, the Coordinates.class would only > contain fields, constructor and explicitly overridden methods. > > Sorry if such idea has already been raised? > > Thanks, > Mateusz We did give some thought to making `Record` an F-bounded supetype, as `Enum` is.? We found (a) there is no current need for it, and f-bounds are a tool we should reserve for when they are really needed, and (b) doing so would increase the cost of specialization, since now `Record` must be specialized for each record class.? Further, the "specialized accessor for each component" is not something that can be easily expressed with the envisioned specialization mechanism. Regarding a potential mismatch between the JEP and the spec: > From: > Maarten Van Puymbroeck > > > Hello, > > Tagir's answer is clear, but some confusion might arise since the > grammar in the JEP [1] is not consistent with the spec [2]. > > The JEP still says: > |RecordConstructorDeclaration: {Annotation} {ConstructorModifier} > [TypeParameters] SimpleTypeName [Throws] ConstructorBody| > > Kind regards, > Maarten. Regarding "withers": > Subject: > "Setters" in records > > From: > Mark Staller > > > Dear Amber EG, > > According to JEP 359: Records (Preview), records will automatically > acquire public read accessors. Have there been considerations about > builder-like, automatically acquired public setters for each field? > Let's look at a quick example to make this a little clearer. > > record Point(int x, int y) { > > ??? public Point x(int x) { > ??????? return new Point(x, y); > ??? } > > ??? public Point y(int y) { > ??????? return new Point(x, y); > ??? } > > } > > In this Point example, x(int x) and y(int y) would be automatically > generated "setters". I could imagine, this way of building up your > object might come in very handy. If this has already been considered and > discarded, I would be very much interested in the reasoning. > > Thanks a and nice regards, > Mark We call these "withers", as they are essentially saying "this instance, but with y=3".? One could imagine a convention where the immutable equivalent of `setX(x)` is called `withX(x)`; while we are not proposing such a convention, it is a useful enough mental model. The reason that these are not included in records is that not all records would want all the one-argument withers; if one of the components participates in an invariant, the author might prefer an API that replaces them all at once.? And its also arbitrary to stop with the one-argument ones, which leads to a (2^n) explosion of possibly-useless API methods.? We didn't think that every tuple needed 2^n withers, so we instead will let authors pick their own. There's a bigger problem here, and one we also know is going to be an issue for inline classes, which is a more general way of describing a parameterized transform on immutable aggregates. There are many possible ways this could go, but so far we haven't found one we like (and are currently prioritizing other problems.) From gavin.bierman at oracle.com Mon Apr 20 21:50:00 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 20 Apr 2020 14:50:00 -0700 (PDT) Subject: [sealed-types] Draft Spec for JEP 360 Sealed Types (Preview) Message-ID: <4312D433-78F6-4BD3-880A-B635B5E89DFB@oracle.com> The latest (and hopefully final) draft of JEP 360 (Sealed Types) is available at: http://cr.openjdk.java.net/~gbierman/jep360/latest The changes since the last draft was circulated in February [1]: * Some minor typos have been corrected, including changing the title of 8.1.6. * We have make corrections in a number of places to make it clear that the name in a `permits` clause is not a type (and can not be annotated, for example). * We now require a functional interface to not be `sealed`, rather than imposing checks on target types of lambda expressions. * We have removed the changes to narrowing reference conversion which allowed for stricter checking of cast conversions wrt sealed type hierarchies. We have decided to defer this feature until a later release to allow us to develop a broader treatment of "disjoint types" that can be used not just in cast conversion, but in other places such as bounds checking and pattern matching. The refined cast conversion was nice to have, but really only will make a difference when we get to patterns in switches, so it makes sense to spend some more time now considering our design rather than refining cast conversion in a piecewise manner. Thanks, Gavin [1] https://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-February/002031.html From daniel.smith at oracle.com Mon Apr 20 22:32:34 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 20 Apr 2020 16:32:34 -0600 Subject: Record component type can be an inner class of a record In-Reply-To: <489239011.1667370.1587314416053.JavaMail.zimbra@u-pem.fr> References: <594699329.1500670.1585083459947.JavaMail.zimbra@u-pem.fr> <486125830.193897.1586797690659.JavaMail.zimbra@u-pem.fr> <489239011.1667370.1587314416053.JavaMail.zimbra@u-pem.fr> Message-ID: > On Apr 19, 2020, at 10:40 AM, forax at univ-mlv.fr wrote: > > Hi Dan, > > ----- Mail original ----- >> De: "daniel smith" >> ?: "Remi Forax" >> Cc: "Gavin Bierman" , "amber-spec-experts" >> Envoy?: Lundi 13 Avril 2020 21:13:51 >> Objet: Re: Record component type can be an inner class of a record > >>> On Apr 13, 2020, at 11:08 AM, forax at univ-mlv.fr wrote: >>> >>> I believe you're right, the following code should not compile, apart if you want >>> to write puzzler for a living :) >>> >>> class A { int y; } >>> >>> record B(A a) implements I { >>> public static void main(String[] args) { >>> System.out.println(new B(null).a().x); >>> } >>> } >>> >>> interface I { >>> class A { int x; } >>> } >> >> You're proposing a novel error check, which I'm not sure is a good idea. > > I think you mis-read it. > It's an example that shows that depending if the record component type is resolve as part of outside of the record or inside the record, not the same A is picked. > The A at top level declare a field "y" while the A inside I declare a field "x", that why it doesn't compile. > So it's not a new error check. My point: there are no references to 'A' inside the body. The only reference to 'A' in this program unambiguously refers to the top-level A. You seem to be assuming that there will be resolution problems involved in checking the implicit members. And my response is that it's not our job to perform name resolution for implicit members?they are defined more abstractly than that. Specifically, the return type of the 'a' method is the type referenced by the type name 'A' appearing after 'record B(' in the program, as it is resolved *at that location*. From john.r.rose at oracle.com Wed Apr 8 22:46:08 2020 From: john.r.rose at oracle.com (John Rose) Date: Wed, 8 Apr 2020 15:46:08 -0700 Subject: ClassValue performance model and Record::toString Message-ID: This note is prompted by work in a parallel project, Amber, on the implementation record types, but is properly a JVM question about JSR 292 functionality. Since we?ve got a quorum of experts here, and since we briefly raised the topic this morning on a Zoom chat, I?ll raise the question here of ClassValue performance. I?m BCC-ing amber-spec-experts so they know we are takling about this. (In fact the EGs overlap.) JSR 292 introduced ClassValue as a hook for libraries (especially dynamic language implementations) to efficiently store library specific metadata on JVM classes. A general use case envisioned was to store method handles (or tuples of them) on classes, where a lazy link step (tied to the semantics of ClassValue::get) would materialize the required M?s as needed. A specific use case was to be able to create extensible v-table-like structures, where a CV would embody a v-table position, and each CV::get binding would embody a filled slot at that v-table position, for a particular class. The assumption was that dynamic languages using CV would continue to use the JVM?s built-in class mechanism for part or all of their own types, and also that it would be helpful for a dynamic language to adjoin metadata to system classes like java.lang.String. Both tactics have been used in the field. In the future, template classes may provide an even richer substrate for the types of non-Java languages. JSR 292 was envisioned for dynamic languages, but was built according to the inherent capabilities of the JVM, and so eventually (actually, in the next release!) it has been used for Java language implementations as well (indy for lambda). ClassValue has not yet been used to implement Java language features, but I believe the time may have come to do so. The general use case I have in mind is an efficient translation strategy for generic algorithms, where the genericity is in the receiver type. The specific use case is the default toString method of records (and also the equals and hashCode methods). The logic of this method is generic over the receiver type. For each record type (unless that record type overrides its toString method in source code), the toString method is defined to iterate over the fields of the record type, and produce a printed representation that mentions both the names and values of the fields. The name of the record?s class is also mentioned. If you ask an intermediate Java coder for an implementation of this spec., you will get something resembling an interpreter which walks over the metadata of ?this.getClass()? and collects the necessary strings into a string builder. If you then deliver this code to users, after about a microsecond you will get complaints about its performance. We?re old hands who don?t fall for such traps, so we asked an experienced coder for better code. That code runs the interpreter-like logic once per distinct record type, collecting the distinct field accesses and folding up the string concatenations into a spongy mass of method handles, depositing the result in a cache. That?s better! (Programming with method handles is, alas, not an improvement over source code. Java hasn?t found its best self yet for doing partial evaluation algorithms, though there is good work out there, like Truffle.) In order not to have bad performance numbers, we are also preconditioning the v-table slot for each record?s toString method, as follows: 0. If the record already has a source-code definition, do nothing special. 1. Otherwise, synthesize a synthetic override method to Object::toString which contains a single indy instruction. (There is also data movement via aload and return.) 2. Set up the indy to run the fancy partial MH-builder mentioned above, the first time, and use the cached MH the second time. 3. Profit. In essence, toString works like a generic algorithm, where the generic type parameter is the receiver type. (If we had template methods we?d have another route to take but not today?) This works great. But there?s a flaw, because it doesn?t use ClassValue. As far as I can tell, it would be better for the translation strategy to *not* generate synthetic methods, but instead to put steps 1. and 2. above into a plain old Java method called Record::toString. This method would call x=this.getClass() and then y=R_TOSTRING.get(x) and then y.invokeExact(this). Non-use of CV is not the flaw, it?s the cause of the flaw. The flaw is apparent if you read the javadoc for Record::toString. It doesn?t say there?s a method there (because there isn?t) but it says weaselly stuff about ?the default method provided does this and that?. In a purely dynamic OOL, the default method is just method bound to Record::toString, and it?s active as long as nobody overrides it (or calls super.toString). People spend years learning to reason about overrides in OOLs like Java, and we should cater to that. We could in this case, but we don?t, because we are pulling a non-OOL trick under the covers, and we have to be honest about it in the Javadoc. So there?s a concern with CV (though I don?t think an overriding one) that we don?t get to step 3 and profit, because the lookups of x and y appear to be interpreter-like overheads. Won?t record types suffer in performance by having those extra indirections happen every time toString (or equals or hashCode) is called? (This problem isn?t unique to Records, but Records are an early case of this sort of problem, of the need for link-time optimization of inheritable OO methods. If you look around you might find similar opportunities with interfaces and default methods.) This is where CV has to get up out of its chair and make itself useful. I think the JVM should take three steps, two sooner and the other later, and both without changing any public API points. 1. Encourage the JIT to constant-fold through ClassValue::get. This would fold up the proposed Record::toString method at all points where the type of the receiver record is known to the JIT. (That?s most places.) 2. Ensure that, if the operand to CV::get is not constant, we get good code anyway. (This is already true, probably.) Look for any small optimization cleanups getting through CV::get and on into MH::invokeExact. 3. Later on, consider v-table slot splitting in response to polymorphic methods which perform CV::get on their receiver. In general, v-table slot splitting is the practice of installing differently compiled code in different v-table slots of the same method. It can make sense if the JIT can do different jobs optimizing the same code on different classes of receivers. It?s usually a heroic hand optimization, but can also be done by the JVM. One more item, not directly related to CV?s but related to the above optimizations: 4. We should invest in one or more auto-bridging features in the JVM, where a call site (such as MyRecord::toString) can be rerouted through an intermediate step before it gets to the built-in target mandated by the JVMS (such as Object::toString or Record::toString), and can also be routed somewhere even if the supposed target method doesn?t even exist. Perhaps the target method symbolic reference is Foo::equals(int) and statically matching method is Foo::equals(Object); normally the static compiler puts in an auto-boxing step to fix the descriptor but there are reasons to consider a more dynamic bridging solution. Such a rerouting decision would be very naturally cached in v-table slots, obviating some or all of step 3 above. In the presence of feature #4, we might rewrite Record::toString to (somehow) advertise that it had no regular method body, but that it would be very happy to bridge any and all calls, using some advertised BSM, and decoupling the implementation from ClassValue. This implementation decision could be hidden from the user (and the Javadoc), but only if we did the ClassValue trick today, so we could advertise Record::toString as a regular old object-oriented method (with clever optimizations inside its implementation, natch). So, let?s take ClassValue off the bench, and start warming up Bridge-O-Matic. ? John From stevenselectronicmail at gmail.com Fri Apr 17 22:08:43 2020 From: stevenselectronicmail at gmail.com (stevenselectronicmail at gmail.com) Date: Fri, 17 Apr 2020 15:08:43 -0700 Subject: Feedback on Preview Records and Pattern Matching Message-ID: Hi, I was playing around with a toy interpreter that compiles a tree of nodes to a kind of monadic code and then runs it. There seem to be maybe 2/3 main issues. 1. Pattern matching bytecode seems to be bloated. static Branch then(Branch branch, Prog next) { if (branch instanceof Dynamic) { return Dynamic.then((Dynamic)branch, next); } if (branch instanceof Then) { return Then.then((Then)branch, next); } if (branch instanceof Ap) { return Ap.then((Ap)branch, next); } if (branch instanceof Mp) { return Mp.then((Mp)branch, next); } throw null; } is two times smaller than the same with pattern matching. 2. If you use records you can't use abstract classes and must use interfaces but interfaces have slower virtual method dispatching. This is just a tiny nit. If you really need this then don't use records I guess. 3. Pattern matching doesn't work well with generics. Literal code from my interpreter. public Step evaluate(Effect effect) { Object result; Interpreter newState; if (effect instanceof PushEnvEffect push) { var newEnv = ((Object[])push.env()).clone(); result = null; newState = new Prod(newEnv, new Stack(env, stack)); } else if (effect == SaveEnvEffect.SAVE) { var envCopy = env.clone(); result = null; newState = new Prod(envCopy, new Stack(env, stack)); } else if (effect == CopyEnvEffect.COPY) { // .... } else { throw new UnsupportedOperationException(effect.toString()); } return new Step<>((A)result, newState); } If you think about it the type of A should be different depending on the pattern. But that requires flow typing which is one of the things pattern matching wanted to avoid! It's kind of a puzzle isn't it? Thanks Steven Stewart-Gallus From daniel.smith at oracle.com Wed Apr 22 19:38:31 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 22 Apr 2020 13:38:31 -0600 Subject: [sealed] Runtime checking of PermittedSubtypes Message-ID: <0CABA3DC-478E-40D0-AD08-8AA16AE2C161@oracle.com> Iterating on the JVMS changes for sealed types, I've been refining the details of the runtime check. Two assertions, which I want to validate: - The run-time modules of a subclass and a sealed superclass must be the same. - The defining class loaders of a subclass and a sealed superclass must be the same. The first is a forced move, as discussed previously: the subclass and superclass must refer to each other, and mutual recursion is impossible between different modules (there are no module dependency loops). The second is implied by the first: "A run-time module is implicitly bound to exactly one class loader, by the semantics of defineModules." (JVMS 5.3.6) If this is true, then when we load the subclass, the superclass validation check should look like this: 1) If the superclass is ACC_FINAL, error. 2) If the superclass has a PermittedSubtypes attribute, then: 2a) If the superclass belongs to a different run-time module, error. 2b) If the superclass doesn't have the subclass's name in its PermittedSubtypes, error. [Would love to have a module expert weigh in on the following:] 2b doesn't need to load anything, because 2a has guaranteed that both classes have the same defining loader. (We do the same thing with nestmates.) I'm a little unsure about 2a, though, because I don't have a great grasp of how modules work?when I'm still deriving a class (JVMS 5.3.5), can I tell what my runtime module will be? We could check for the same loader in 2a instead, but then there would still be a hole: - A class p1/Foo is loaded by loader L in module m1 - A class p2/Bar is loaded by loader L in module m2 - m1 requires m2 - p1/Foo extends p2/Bar - p2/Bar has a PermittedSubtypes attribute that names "p1/Foo" - If we were to resolve "p1/Foo", we'd get a NCDFE (I think? Again, I'm hazy on modules.) Ideally, class p1/Foo should fail to load. From forax at univ-mlv.fr Wed Apr 22 20:43:43 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 22 Apr 2020 22:43:43 +0200 (CEST) Subject: [sealed] Runtime checking of PermittedSubtypes In-Reply-To: <0CABA3DC-478E-40D0-AD08-8AA16AE2C161@oracle.com> References: <0CABA3DC-478E-40D0-AD08-8AA16AE2C161@oracle.com> Message-ID: <1658486918.1446132.1587588223921.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "daniel smith" > ?: "amber-spec-experts" > Envoy?: Mercredi 22 Avril 2020 21:38:31 > Objet: [sealed] Runtime checking of PermittedSubtypes > Iterating on the JVMS changes for sealed types, I've been refining the details > of the runtime check. > > Two assertions, which I want to validate: > > - The run-time modules of a subclass and a sealed superclass must be the same. > - The defining class loaders of a subclass and a sealed superclass must be the > same. > > The first is a forced move, as discussed previously: the subclass and superclass > must refer to each other, and mutual recursion is impossible between different > modules (there are no module dependency loops). > > The second is implied by the first: "A run-time module is implicitly bound to > exactly one class loader, by the semantics of defineModules." (JVMS 5.3.6) > > If this is true, then when we load the subclass, the superclass validation check > should look like this: > > 1) If the superclass is ACC_FINAL, error. > 2) If the superclass has a PermittedSubtypes attribute, then: > 2a) If the superclass belongs to a different run-time module, error. > 2b) If the superclass doesn't have the subclass's name in its PermittedSubtypes, > error. > > [Would love to have a module expert weigh in on the following:] > > 2b doesn't need to load anything, because 2a has guaranteed that both classes > have the same defining loader. (We do the same thing with nestmates.) > > I'm a little unsure about 2a, though, because I don't have a great grasp of how > modules work?when I'm still deriving a class (JVMS 5.3.5), can I tell what my > runtime module will be? >From the class name which is qualified, you have the package name and from a package name and a classloader, you have the module. A module knows all the packages inside itself, during the modules resolution, each module advertise all its packages, so when a classloader on a resulting configuration, it knows the Map. If the classloader doesn't find the module associated to a package, it means that it's a package from the classpath, so the package will be created lazily using the unamed module of the classloader. > > We could check for the same loader in 2a instead, but then there would still be > a hole: > - A class p1/Foo is loaded by loader L in module m1 > - A class p2/Bar is loaded by loader L in module m2 > - m1 requires m2 > - p1/Foo extends p2/Bar > - p2/Bar has a PermittedSubtypes attribute that names "p1/Foo" > - If we were to resolve "p1/Foo", we'd get a NCDFE (I think? Again, I'm hazy on > modules.) > > Ideally, class p1/Foo should fail to load. R?mi From alex.buckley at oracle.com Wed Apr 22 22:48:29 2020 From: alex.buckley at oracle.com (Alex Buckley) Date: Wed, 22 Apr 2020 15:48:29 -0700 Subject: [sealed] Runtime checking of PermittedSubtypes In-Reply-To: <1658486918.1446132.1587588223921.JavaMail.zimbra@u-pem.fr> References: <0CABA3DC-478E-40D0-AD08-8AA16AE2C161@oracle.com> <1658486918.1446132.1587588223921.JavaMail.zimbra@u-pem.fr> Message-ID: <0e368f72-7c29-478a-8871-cdf4ea120dda@oracle.com> On 4/22/2020 1:43 PM, Remi Forax wrote: >> 2a) If the superclass belongs to a different run-time module, error. >> 2b) If the superclass doesn't have the subclass's name in its PermittedSubtypes, >> error. >> >> 2b doesn't need to load anything, because 2a has guaranteed that both classes >> have the same defining loader. (We do the same thing with nestmates.) >> >> I'm a little unsure about 2a, though, because I don't have a great grasp of how >> modules work?when I'm still deriving a class (JVMS 5.3.5), can I tell what my >> runtime module will be? > > From the class name which is qualified, you have the package name and from a package name and a classloader, you have the module. > A module knows all the packages inside itself, during the modules resolution, each module advertise all its packages, so when a classloader on a resulting configuration, it knows the Map. > If the classloader doesn't find the module associated to a package, it means that it's a package from the classpath, so the package will be created lazily using the unamed module of the classloader. As Remi says, by the time class loading happens, a layer has already been set up to map modules (and hence their packages) to loaders. JVMS 5.3.6 discusses this, including the possibility that a class is "in a run-time module". We made a big deal in Java 9 about how "Class Loading Doesn't Change" for the module system -- there's no additional argument to loadClass or defineClass to indicate module membership -- but it is legitimate in Java 15 for defineClass to start caring about module membership because it's done to respect sealing rather than to support the module system itself. Alex From daniel.smith at oracle.com Wed Apr 22 23:15:28 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 22 Apr 2020 17:15:28 -0600 Subject: [sealed] Runtime checking of PermittedSubtypes In-Reply-To: <0e368f72-7c29-478a-8871-cdf4ea120dda@oracle.com> References: <0CABA3DC-478E-40D0-AD08-8AA16AE2C161@oracle.com> <1658486918.1446132.1587588223921.JavaMail.zimbra@u-pem.fr> <0e368f72-7c29-478a-8871-cdf4ea120dda@oracle.com> Message-ID: > On Apr 22, 2020, at 4:48 PM, Alex Buckley wrote: > >>> 2a) If the superclass belongs to a different run-time module, error. > > As Remi says, by the time class loading happens, a layer has already been set up to map modules (and hence their packages) to loaders. JVMS 5.3.6 discusses this, including the possibility that a class is "in a run-time module". Okay, so it would be valid to talk about "the run-time module of C" while C is being derived? I think this sentence from 5.3.6 anticipates this sort of early query, but I might be misinterpreting the parenthetical: "We say that a class is in a run-time module iff the class's run-time package is associated (or will be associated, if the class is actually created) with that run-time module." I'm a little nervous about the idea that some associations haven't happened yet. I'm not clear on the timing. From daniel.smith at oracle.com Wed Apr 22 23:32:14 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 22 Apr 2020 17:32:14 -0600 Subject: [sealed] Runtime checking of PermittedSubtypes In-Reply-To: <0CABA3DC-478E-40D0-AD08-8AA16AE2C161@oracle.com> References: <0CABA3DC-478E-40D0-AD08-8AA16AE2C161@oracle.com> Message-ID: <7F7A73C8-2B86-4AB8-BF8A-5DD326C10EDC@oracle.com> > On Apr 22, 2020, at 1:38 PM, Dan Smith wrote: > > Iterating on the JVMS changes for sealed types, I've been refining the details of the runtime check. > > Two assertions, which I want to validate: > > - The run-time modules of a subclass and a sealed superclass must be the same. > - The defining class loaders of a subclass and a sealed superclass must be the same. > > The first is a forced move, as discussed previously: the subclass and superclass must refer to each other, and mutual recursion is impossible between different modules (there are no module dependency loops). > > The second is implied by the first: "A run-time module is implicitly bound to exactly one class loader, by the semantics of defineModules." (JVMS 5.3.6) Another module system sanity check: is mutual recursion allowed between unnamed modules of different loaders? (Pre-9, mutual recursion between different loaders was certainly possible...) If so, there's a design choice here about whether we will prohibit a class in unnamed module M1 from extending a sealed class in unnamed module M2. > If this is true, then when we load the subclass, the superclass validation check should look like this: > > 1) If the superclass is ACC_FINAL, error. > 2) If the superclass has a PermittedSubtypes attribute, then: > 2a) If the superclass belongs to a different run-time module, error. > 2b) If the superclass doesn't have the subclass's name in its PermittedSubtypes, error. If we're simulating resolution, which I think is the goal here, then I guess we also need: 2c) If the subclass isn't accessible to the superclass, error. It's a little risky talking about the accessibility of a class that doesn't exist yet, but this currently amounts to testing for ACC_PUBLIC or same package name. (We'll want to revisit this if we eventually support ACC_PRIVATE classes in nests.) From alex.buckley at oracle.com Wed Apr 22 23:59:56 2020 From: alex.buckley at oracle.com (Alex Buckley) Date: Wed, 22 Apr 2020 16:59:56 -0700 Subject: [sealed] Runtime checking of PermittedSubtypes In-Reply-To: References: <0CABA3DC-478E-40D0-AD08-8AA16AE2C161@oracle.com> <1658486918.1446132.1587588223921.JavaMail.zimbra@u-pem.fr> <0e368f72-7c29-478a-8871-cdf4ea120dda@oracle.com> Message-ID: <44d461c3-9aff-fef3-31b2-8fdc835c08c4@oracle.com> On 4/22/2020 4:15 PM, Dan Smith wrote: > Okay, so it would be valid to talk about "the run-time module of C" > while C is being derived? > > I think this sentence from 5.3.6 anticipates this sort of early > query, but I might be misinterpreting the parenthetical: > > "We say that a class is in a run-time module iff the class's run-time > package is associated (or will be associated, if the class is > actually created) with that run-time module." > > I'm a little nervous about the idea that some associations haven't > happened yet. I'm not clear on the timing. Yes, it would be valid. The wording of the 5.3.6 sentence is trying to afford a degree of laziness to implementations in how they they make the association between run-time package and run-time module. This is because "run-time package" is a pretty ephemeral concept: before any class is created, does any run-time package exist? Many implementors would say "no" -- and yet, the invocation of ModuleLayer::defineModules has already happened, so e.g. the VM has already associated the run-time module `java.base` with the run-time package `java.lang` of the bootstrap loader, even before the class `Object` is loaded. So, if you have the name of a class, and a loader, you can find out from the VM which run-time module the class has been or will be created as a member of ("in"). The loader narrows down the run-time modules a bit; the package name narrows them down further. The module:loader:package relationships are immutable. Alex From alex.buckley at oracle.com Thu Apr 23 00:18:51 2020 From: alex.buckley at oracle.com (Alex Buckley) Date: Wed, 22 Apr 2020 17:18:51 -0700 Subject: [sealed] Runtime checking of PermittedSubtypes In-Reply-To: <7F7A73C8-2B86-4AB8-BF8A-5DD326C10EDC@oracle.com> References: <0CABA3DC-478E-40D0-AD08-8AA16AE2C161@oracle.com> <7F7A73C8-2B86-4AB8-BF8A-5DD326C10EDC@oracle.com> Message-ID: On 4/22/2020 4:32 PM, Dan Smith wrote: > Another module system sanity check: is mutual recursion allowed between unnamed modules of different loaders? (Pre-9, mutual recursion between different loaders was certainly possible...) Yes. Anything that was possible with class loaders on JDK 8 is still possible on JDK 9+. Class loaders in JDK 9+ are free to mutually delegate for classes that are in unnamed modules rather than in layer-defined run-time modules. We even arrange a complete readability graph between those unnamed modules -- "Every unnamed module reads every run-time module." -- in case class C in one unnamed module attempts to access (possibly via a `Class` object, so no need for c.p. resolution) class D in another unnamed module, without any concern or distress over the higher-level story that C refers to D and D refers to C. Alex From daniel.smith at oracle.com Thu Apr 23 18:27:30 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 23 Apr 2020 12:27:30 -0600 Subject: [sealed] Module & package constraints Message-ID: I'm scrutinizing this rule from the sealed types language spec (8.1.6): "It is a compile-time error if any class named in a permits clause of a sealed class declaration C is not a member of the same module as C. If the sealed class C is a member of the unnamed module, then it is a compile-time error if any class named in the permits clause of the declaration of C is not in the same package as C." I'm wondering what run-time checks should correspond to this rule (prompted by the "runtime checking of PermittedSubtypes" thread), and whether we want to say something slightly different. Here are some knobs. Where do we want to turn them? 1) Is it legal to have a permitted subclass/subinterface that does not actually extend the permitting class or interface? In past discussions about compilation, I feel like we've leaned towards "no", but I don't see a corresponding rule. We're definitely not interested in scenarios involving separate compilation; so if the child and parent disagree, we're compiling an inconsistent set of classes. Seems reasonable to ask the programmer to fix it. If we don't, we end up with downstream design issues like whether to trust the 'permits' clause when defining exhaustiveness. At run time, it's convenient for the JVM if the answer is "yes". It's expensive to try to validate a PermittedSubtypes attribute all at once (could require O(nm) class loads, n=sealed hierarchy height, m=branching factor; although both are typically small). Instead, the best time to validate is when another class/interface attempts to extend the sealed class/interface. 2) What are the constraints on module membership? At compilation time, the only way to extend across a module boundary is if the parent permits the child, but the child doesn't reciprocate. (If the child attempts to reciprocate, it won't be able to access the parent class, or there's an illegal module circularity.) So depending on the choice for (1), a restriction on modules may be redundant. At run time, when we validate an 'extends'/'implements' clause, the mutual references *almost* imply membership in the same run-time module, with one exception: unnamed modules can have circular references. For example: class Foo extends sealed class Bar, Foo belongs to the unnamed module of loader L1, Bar belongs to the unnamed module of loader L2. The loaders can "see" each other, and all class references resolve safely. I'm not sure how likely this scenario is, but it could be a major problem for a program if the JVM starts blowing up after someone makes Bar sealed. (On the other hand, it's really helpful for the JVM if it can require the classes to have to same loader.) 3) What are the constraints on package membership? The use case here is a class/interface extending a sealed class/interface, both in the same unnamed module. At compile time, if the child and parent must successfully refer to each other, then we've already guaranteed that they're compiled at the same time. Is there something more to be gained from forcing them into the same package? (java.lang.reflect.Type is an example of a potential sealed interface that couldn't be declared under this rule if it didn't belong to a named module, because java.lang.Class is in a different package. I imagine lots of real code bases will have relationships like this. Then again, it's not totally unreasonable to tell these code bases they need to declare a module if they want cross-package sealed types.) At run time, it's fairly straightforward to check for the same package name when we validate a subclass. But it's also doesn't benefit the JVM in any way, so maybe this is more of a language-specific restriction that should be ignored by the JVM. (E.g., maybe Kotlin doesn't mind compiling sealed hierarchies across different packages in the unnamed module, even if Java won't do it.) From forax at univ-mlv.fr Thu Apr 23 20:01:49 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 23 Apr 2020 22:01:49 +0200 (CEST) Subject: [sealed] Module & package constraints In-Reply-To: References: Message-ID: <780159528.463577.1587672109190.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "daniel smith" > ?: "amber-spec-experts" > Envoy?: Jeudi 23 Avril 2020 20:27:30 > Objet: [sealed] Module & package constraints > I'm scrutinizing this rule from the sealed types language spec (8.1.6): > > "It is a compile-time error if any class named in a permits clause of a sealed > class declaration C is not a member of the same module as C. If the sealed > class C is a member of the unnamed module, then it is a compile-time error if > any class named in the permits clause of the declaration of C is not in the > same package as C." > > I'm wondering what run-time checks should correspond to this rule (prompted by > the "runtime checking of PermittedSubtypes" thread), and whether we want to say > something slightly different. > > Here are some knobs. Where do we want to turn them? > > 1) Is it legal to have a permitted subclass/subinterface that does not actually > extend the permitting class or interface? > > In past discussions about compilation, I feel like we've leaned towards "no", > but I don't see a corresponding rule. We're definitely not interested in > scenarios involving separate compilation; so if the child and parent disagree, > we're compiling an inconsistent set of classes. Seems reasonable to ask the > programmer to fix it. If we don't, we end up with downstream design issues like > whether to trust the 'permits' clause when defining exhaustiveness. > > At run time, it's convenient for the JVM if the answer is "yes". It's expensive > to try to validate a PermittedSubtypes attribute all at once (could require > O(nm) class loads, n=sealed hierarchy height, m=branching factor; although both > are typically small). Instead, the best time to validate is when another > class/interface attempts to extend the sealed class/interface. The answer is yes, until you load the subtype and discover that it's not a subtype, it's like the rules for nestmates. > > 2) What are the constraints on module membership? > > At compilation time, the only way to extend across a module boundary is if the > parent permits the child, but the child doesn't reciprocate. (If the child > attempts to reciprocate, it won't be able to access the parent class, or > there's an illegal module circularity.) So depending on the choice for (1), a > restriction on modules may be redundant. > > At run time, when we validate an 'extends'/'implements' clause, the mutual > references *almost* imply membership in the same run-time module, with one > exception: unnamed modules can have circular references. For example: class Foo > extends sealed class Bar, Foo belongs to the unnamed module of loader L1, Bar > belongs to the unnamed module of loader L2. The loaders can "see" each other, > and all class references resolve safely. I'm not sure how likely this scenario > is, but it could be a major problem for a program if the JVM starts blowing up > after someone makes Bar sealed. (On the other hand, it's really helpful for the > JVM if it can require the classes to have to same loader.) yes, that the missing rule, sealed classes has to have the same class loader. So if there is no module, there are still in the same unnamed module at runtime. > > 3) What are the constraints on package membership? > > The use case here is a class/interface extending a sealed class/interface, both > in the same unnamed module. > > At compile time, if the child and parent must successfully refer to each other, > then we've already guaranteed that they're compiled at the same time. Is there > something more to be gained from forcing them into the same package? > > (java.lang.reflect.Type is an example of a potential sealed interface that > couldn't be declared under this rule if it didn't belong to a named module, > because java.lang.Class is in a different package. I imagine lots of real code > bases will have relationships like this. Then again, it's not totally > unreasonable to tell these code bases they need to declare a module if they > want cross-package sealed types.) > > At run time, it's fairly straightforward to check for the same package name when > we validate a subclass. But it's also doesn't benefit the JVM in any way, so > maybe this is more of a language-specific restriction that should be ignored by > the JVM. (E.g., maybe Kotlin doesn't mind compiling sealed hierarchies across > different packages in the unnamed module, even if Java won't do it.) Very good question. The compiler verifying that the classes are in the same package if there is no module is a sanity check, if there are not in the same package, they may be in different jars. It's not fool proof because you can have split packages at runtime but it's good enough because at least you have to have all classes at compile time. So it may not benefit the JVM but benefit the poor soul trying to debug the code at runtime. >From the POV on any languages running on the JVM like Scala or Kotlin that already have the concept of sealed types, there are free to map it to the JVM concept or not, so if they want to support a case which is not supported by the JVM spec, the simplest solution is just to not generated the PermittedSubtypes attributes. So I don't see the need to have the Java spec and the JVM spec to diverge on that specific point. R?mi From brian.goetz at oracle.com Thu Apr 23 20:11:17 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 23 Apr 2020 16:11:17 -0400 Subject: Clarifications on SoV documents Message-ID: Srikanth asked me to make a few clarifications on the SoV documents.? I'll summarize them here and then later work them into the docs. 1.? Array subtyping.? Historically, array covariance worked like this: ??? if T <: U, then T[] <: U[] In Valhalla, this is refined to be: ??? if T.ref <: U, then T[] <: U[] Because T.ref == T for reference types, this rule generalizes the previous rule. There is no inline widening/narrowing conversion between array types; array types are reference types. Example: ??? V[] va = ... ??? V.ref[] vb = ... ??? vb = va // allowed by ordinary subtyping ??? va = vb // not allowed ??? va = (V[]) vb // allowed, may CCE 2.? Bounds checking.? If we have a bound: ??? class Foo { } then this was historically satisfied if T <: U.? In Valhalla, this is amended in the same way as (1): ??? X is within bound `T extends U` if X.ref <: U If `U` is an inline type, the bounds `T extends U` does not make sense, so is rejected. 3.? This story isn't written yet, but where we're aiming (everyone but Srikanth, please hold questions on "how would that work" until I write this up) is that inline types can be used as type parameters for erased generics _without_ having to say `Foo`.? This represents a shift from where we were before, where we didn't allow inlines as type arguments. From forax at univ-mlv.fr Thu Apr 23 20:23:40 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 23 Apr 2020 22:23:40 +0200 (CEST) Subject: Clarifications on SoV documents In-Reply-To: References: Message-ID: <1548373960.466059.1587673420518.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "amber-spec-experts" > Envoy?: Jeudi 23 Avril 2020 22:11:17 > Objet: Clarifications on SoV documents > Srikanth asked me to make a few clarifications on the SoV documents. I'll > summarize them here and then later work them into the docs. > 1. Array subtyping. Historically, array covariance worked like this: > if T <: U, then T[] <: U[] > In Valhalla, this is refined to be: > if T.ref <: U, then T[] <: U[] > Because T.ref == T for reference types, this rule generalizes the previous rule. > There is no inline widening/narrowing conversion between array types; array > types are reference types. > Example: > V[] va = ... > V.ref[] vb = ... > vb = va // allowed by ordinary subtyping > va = vb // not allowed > va = (V[]) vb // allowed, may CCE > 2. Bounds checking. If we have a bound: > class Foo { } > then this was historically satisfied if T <: U. In Valhalla, this is amended in > the same way as (1): > X is within bound `T extends U` if X.ref <: U > If `U` is an inline type, the bounds `T extends U` does not make sense, so is > rejected. > 3. This story isn't written yet, but where we're aiming (everyone but Srikanth, > please hold questions on "how would that work" until I write this up) is that > inline types can be used as type parameters for erased generics _without_ > having to say `Foo`. This represents a shift from where we were before, > where we didn't allow inlines as type arguments. No, we know, you want to use Foo[V] for reified generics (so we can make reified generics covariant). R?mi From brian.goetz at oracle.com Thu Apr 23 20:26:15 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 23 Apr 2020 16:26:15 -0400 Subject: Clarifications on SoV documents In-Reply-To: <1548373960.466059.1587673420518.JavaMail.zimbra@u-pem.fr> References: <1548373960.466059.1587673420518.JavaMail.zimbra@u-pem.fr> Message-ID: <09961f81-e3bc-6846-b6af-500cfa6a49c3@oracle.com> That's exactly what I meant by "hold questions"!? To be fair, you didn't ask a question, as much as make a statement, but ...? what I'm saying is that my thinking here has evolved.? And that I'll write up exactly how and why soon ... and that you'll wait for that :) On 4/23/2020 4:23 PM, Remi Forax wrote: > > > > 3.? This story isn't written yet, but where we're aiming (everyone > but Srikanth, please hold questions on "how would that work" until > I write this up) is that inline types can be used as type > parameters for erased generics _without_ having to say > `Foo`.? This represents a shift from where we were before, > where we didn't allow inlines as type arguments. > > > No, we know, you want to use Foo[V] for reified generics (so we can > make reified generics covariant). From brian.goetz at oracle.com Fri Apr 24 18:24:39 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 24 Apr 2020 14:24:39 -0400 Subject: Possible records tweak Message-ID: I have been reviewing various Stack Overflow and other queries regarding the use of records, and in particular the compact constructor. The compact constructor was intended to support a model where the constructor body minimally mutates the _constructor arguments_ (if they need to be normalized, defaulted, or clamped), which are then automatically committed to the fields on successful exit: ??? record Rational(int num, int denom) { ??????? Rational { ??????????? int gcd = gcd(num, denom); ??????????? num /= gcd; ??????????? denom /= gcd; ??????? } ??? } However, developers do not seem to be getting this, and they are instead appealing to the old idiom: ??? record Rational(int num, int denom) { ??????? Rational { ??????????? int gcd = gcd(num, denom); ??????????? this.num = num / gcd; ??????????? this.denom = denom / gcd; ??????? } ??? } My concern here is severalfold: ?- Mixing the "user initialized" and "auto initialized" idioms is potentially confusing, as some fields ?? will be DA at some points in the constructor body, and others won't.? This invites confusion both ?? for writers and readers. ?- There will be "style wars" over which idiom is better; since this is a new feature and was designed ?? with a particular idiom in mind, we may simply prefer to enforce that idiom. ?- My intent is to carry this idiom elsewhere into the language, at the appropriate point in time. ?? While for records, we have an implicit mapping of ctor args to fields that enables this auto- ?? init feature, it may be useful to also allow for an explicit mapping in non-record cases. ?? This might look like this (please, let's not design this feature now, I share this as a ?? "where we might want to go" thought as it affects the feature currently on the table): ??? class Foo(int x, int y) { ??????? int x; ??????? int y; ??????? Foo(int __this.x, int __this.y) { } ??? } Here, we are annotating the constructor argument in a way that says "the name and type correspondence between this constructor argument and the field of the same name is not accidental, please fill in the boilerplate for me", and we would get the same auto-init benefit as we do with compact constructors in records. (Also (and please, we are not designing this one now either), at some point we will have declared deconstruction patterns, which may look very much like "reverse constructors", and will be candidates for the same "this binding variable corresponds to a field" treatment, with the same potential for boilerplate elimination.) So there's room in the future for the user to fill in the "this similarity is not an accident" and be rewarded with more compact, less error-prone code just like we do with compact constructors. So, the question I want to ask is: should we simply _ban_ reads and writes to `this.x` in the body of a compact constructor, and let the auto mechanism take care of it, so there is no confusion about mixing initialization modes, the correct idiom, or reading possibly uninitialized fields? (If we did, we would probabably extend this to `this`-bound fields in the future, should that feature come to pass.) From guy.steele at oracle.com Fri Apr 24 18:59:57 2020 From: guy.steele at oracle.com (Guy Steele) Date: Fri, 24 Apr 2020 14:59:57 -0400 Subject: Possible records tweak In-Reply-To: References: Message-ID: > On Apr 24, 2020, at 2:24 PM, Brian Goetz wrote: > > I have been reviewing various Stack Overflow and other queries regarding the use of records, and in particular the compact constructor. > > The compact constructor was intended to support a model where the constructor body minimally mutates the _constructor arguments_ (if they need to be normalized, defaulted, or clamped), which are then automatically committed to the fields on successful exit: > > record Rational(int num, int denom) { > Rational { > int gcd = gcd(num, denom); > num /= gcd; > denom /= gcd; > } > } > > However, developers do not seem to be getting this, and they are instead appealing to the old idiom: > > record Rational(int num, int denom) { > Rational { > int gcd = gcd(num, denom); > this.num = num / gcd; > this.denom = denom / gcd; > } > } > > . . . > > So, the question I want to ask is: should we simply _ban_ reads and writes to `this.x` in the body of a compact constructor, and let the auto mechanism take care of it, so there is no confusion about mixing initialization modes, the correct idiom, or reading possibly uninitialized fields? (If we did, we would probabably extend this to `this`-bound fields in the future, should that feature come to pass.) Not a bad idea, but here are two and a half alternatives we could consider: (1) Simply ban use of ?this? within a compact constructor. This might seem like overkill, but it is very easy to explain: the keyword ?this? is not available in a compact constructor, period. Because the argument names shadow the field names, you don?t have access to the fields at all in a compact constructor. If you don?t like it, write a non-compact constructor. (2) Change the model so that the constructor arguments are committed to the fields _before_ executing the body of a compact constructor, then for each argument and corresponding field use this assignment analysis to possibly take additional action on exit: if the argument is definitely not assigned in the body of the compact constructor take no action on exit else if the corresponding field is definitely not assigned in the body of the compact constructor assign the argument to its corresponding field on exit else compile-time error This rule is intended to allow the programmer to use either style safely. (2a) Same as (2), but apply it on a bulk basis rather than a per-argument basis: if all the arguments are definitely not assigned in the body of the compact constructor take no action on exit else if all the corresponding fields are definitely not assigned in the body of the compact constructor assign all the arguments to their corresponding fields on exit else compile-time error This rule would further require the programmer to adopt the same style uniformly for all arguments. All of these suggestions depend on compile-time analysis to eliminate redundant assignments. ?Guy From john.r.rose at oracle.com Fri Apr 24 19:04:36 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 24 Apr 2020 12:04:36 -0700 Subject: Possible records tweak In-Reply-To: References: Message-ID: <96F77552-D7DD-4B79-9AFA-9D0518139063@oracle.com> On Apr 24, 2020, at 11:24 AM, Brian Goetz wrote: > > the question I want to ask is: should we simply _ban_ reads and writes to `this.x` in the body of a compact constructor, and let the auto mechanism take care of it, so there is no confusion about mixing initialization modes, the correct idiom, or reading possibly uninitialized fields If we can get away with it, yes^100. Assigning to a final field is one of the darkest corners of the language. In Java 1.1 we had to create a jungle of DA/DU rules for those assignments. It would be great if they just went away, for compact constructors. When reading the first 1/2 of your message, I was partly afraid the ask was going to be, ?drop the auto-initialize feature? on the grounds of user confusion. One of our hardest problems historically is to reduce the user confusion, and *still* have our nice things. I also like Guy?s doubling down on the solution which forbids `this` in the body of the constructor. Any of these restrictions can be lifted in a future version of the language, so it?s relatively safe to make them now. ? John From brian.goetz at oracle.com Fri Apr 24 19:08:02 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 24 Apr 2020 15:08:02 -0400 Subject: Possible records tweak In-Reply-To: References: Message-ID: > Not a bad idea, but here are two and a half alternatives we could > consider: > > (1) Simply ban use of ?this? within a compact constructor. ?This might > seem like overkill, but it is very easy to explain: the keyword ?this? > is not available in a compact constructor, period. ?Because the > argument names shadow the field names, you don?t have access to the > fields at all in a compact constructor. ?If you don?t like it, write a > non-compact constructor. That's a nice clean way to do it, because under this (heh) model, `this` is useless -- can't read the fields (they're DU), can't write them (cause I say so), shouldn't call instance methods anyway (because the object is uninitilized and therefore may be in an invalid state), and you shouldn't pass `this` to alien code (because that undermines the final field guarantees of the JMM.)? So if its useless, just don't utter it. (Which, in hindsight, might have been a good rule for _all_ constructors, if there was another way to initialize the fields. Surely would have eliminated much verifier complexity.) > (2) Change the model so that the constructor arguments are committed > to the fields _before_ executing the body of a compact constructor, > then for each argument and corresponding field use this assignment > analysis to possibly take additional action on exit: > > if the argument is definitely not assigned in the body of the compact > constructor > take no action on exit > else if the corresponding field is definitely not assigned in the body > of the compact constructor > assign the argument to its corresponding field on exit > else compile-time error > > This rule is intended to allow the programmer to use either style safely. > > (2a) Same as (2), but apply it on a bulk basis rather than a > per-argument basis: > > if all the arguments are definitely not assigned in the body of the > compact constructor > take no action on exit > else if all the corresponding fields are definitely not assigned in > the body of the compact constructor > assign all the arguments to their corresponding fields on exit > else compile-time error > > This rule would further require the programmer to adopt the same style > uniformly for all arguments. > > > All of these suggestions depend on compile-time analysis to eliminate > redundant assignments. > We already do this analysis now, but I'm having a bit of buyers remorse on it. I like (1)!? It is safe and easy to explain. From daniel.smith at oracle.com Fri Apr 24 19:16:30 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 24 Apr 2020 13:16:30 -0600 Subject: [sealed] Module & package constraints In-Reply-To: References: Message-ID: <9574B824-4AD3-4FFD-82F1-1733592A010F@oracle.com> I presented this as an outline of our choices, but to put a stake in the ground, here are my preferences. > On Apr 23, 2020, at 12:27 PM, Dan Smith wrote: > > 1) Is it legal to have a permitted subclass/subinterface that does not actually extend the permitting class or interface? At compile time, no. Let's prohibit that. At run time, delay any validation until a particular subclass is loaded. > 2) What are the constraints on module membership? At compile time, do nothing. Per (1), it's already impossible to successfully declare a sealed parent/child across a module boundary. At run time, require the same run-time module. In the corner case of a parent/child split across class loaders (in unnamed modules), we say "sorry, don't do that." (I do think this will come up. Extension is an important way to communicate across class loader boundaries. But I think programs doing so need to accept that sealed types are not for them.) > 3) What are the constraints on package membership? > > The use case here is a class/interface extending a sealed class/interface, both in the same unnamed module. At compile time, do nothing. Per (1), it's impossible to separately compile the parent and child, no matter what packages they are declared in. At run time, there's nothing to enforce. I don't care how you've deployed your class files, as long as they successfully compiled and have the same module/loader. From kevinb at google.com Fri Apr 24 19:27:26 2020 From: kevinb at google.com (Kevin Bourrillion) Date: Fri, 24 Apr 2020 12:27:26 -0700 Subject: Possible records tweak In-Reply-To: References: Message-ID: On Fri, Apr 24, 2020 at 12:11 PM Brian Goetz wrote: I like (1)! It is safe and easy to explain. > I love it. Nothing good can come of accessing `this` in a record constructor afaict. The parameter reassignment model is elegant and safe and simply takes getting used to, since there's been no precedent for a parameter being more than just a local variable (unless I'm forgetting something). (Also contributing to this: parameter reassignment is in *general *an commonly and unfairly maligned practice!) But once people can't do it any other way, that will help a lot. +1 -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From guy.steele at oracle.com Fri Apr 24 19:32:50 2020 From: guy.steele at oracle.com (Guy Steele) Date: Fri, 24 Apr 2020 15:32:50 -0400 Subject: Possible records tweak In-Reply-To: References: Message-ID: <95F28DA7-3D83-483A-AFC1-DB6B180B29D6@oracle.com> > On Apr 24, 2020, at 3:08 PM, Brian Goetz wrote: > > >> Not a bad idea, but here are two and a half alternatives we could consider: >> >> (1) Simply ban use of ?this? within a compact constructor. This might seem like overkill, but it is very easy to explain: the keyword ?this? is not available in a compact constructor, period. Because the argument names shadow the field names, you don?t have access to the fields at all in a compact constructor. If you don?t like it, write a non-compact constructor. > > That's a nice clean way to do it, because under this (heh) model, `this` is useless -- can't read the fields (they're DU), can't write them (cause I say so), shouldn't call instance methods anyway (because the object is uninitilized and therefore may be in an invalid state), and you shouldn't pass `this` to alien code (because that undermines the final field guarantees of the JMM.) So if its useless, just don't utter it. > > (Which, in hindsight, might have been a good rule for _all_ constructors, if there was another way to initialize the fields. Surely would have eliminated much verifier complexity.) This is the ?if only? theory we could have had back in 1995: In any constructor, for every field not shadowed by a constructor argument, the constructor has an implicit local variable with the same name that is not initialized. It is a compile-time error unless every such implicit local variable is definitely assigned on exit. On exit, every field is assigned from the constructor argument or implicit local variable of the same name. The keyword ?this? is not available in the bod of the constructor. But I?m sure that back in those days such implicit local variables, and the storage consumption they would imply, and even the extra assignment operations required, would have been anathema. ?Guy From brian.goetz at oracle.com Fri Apr 24 19:35:52 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 24 Apr 2020 15:35:52 -0400 Subject: [sealed] Module & package constraints In-Reply-To: References: Message-ID: > 1) Is it legal to have a permitted subclass/subinterface that does not actually extend the permitting class or interface? > > In past discussions about compilation, I feel like we've leaned towards "no", but I don't see a corresponding rule. We're definitely not interested in scenarios involving separate compilation; so if the child and parent disagree, we're compiling an inconsistent set of classes. Seems reasonable to ask the programmer to fix it. If we don't, we end up with downstream design issues like whether to trust the 'permits' clause when defining exhaustiveness. I think we have slowly roped ourselves into this position.? We started out with "no action at a distance", realized this was suboptimal for various reasons, and ended up having a global view at compile time of the hierarchy, so it is reasonable to enforce consistency constraints on that. > At run time, it's convenient for the JVM if the answer is "yes". It's expensive to try to validate a PermittedSubtypes attribute all at once (could require O(nm) class loads, n=sealed hierarchy height, m=branching factor; although both are typically small). Instead, the best time to validate is when another class/interface attempts to extend the sealed class/interface. Yes.? At the VM level, its perfectly OK if the subtype drops a superclass.? This isn't binary compatible, so might result in ICCE, but that's why we have ICCE. > 2) What are the constraints on module membership? The intention is that sealed types are for classes that share a _maintenance domain_ and therefore are routinely co-compiled. I am worried about being too permissive here with the unnamed module, because we would like for code in the unnamed module to be able to graduate to named modules, and allowing too much flexibility here will be yet another source of circular references which cannot modularize. > 3) What are the constraints on package membership? The "same package if unnamed module" rule was in part an attempt to capture the co-maintained constraint. > At compile time, if the child and parent must successfully refer to each other, then we've already guaranteed that they're compiled at the same time. Is there something more to be gained from forcing them into the same package? But have we?? Imagine two packages, A and B, in separate maintenance domains.? First we have: ??? package a; ??? interface I { } ??? package b; ??? class C implements a.I { } We can compile A, and then compile B against A.jar. Now modify A, and recompile with B.jar on the classpath: ??? package a; ??? sealed interface I permits b.C { } This is fine.? Thereafter, these JARs can be recompiled independently.? And I could easily imagine lore surrounding this trick growing up as a "workaround" against the "stupid rule." > Then again, it's not totally unreasonable to tell these code bases they need to declare a module if they want cross-package sealed types.) Yep, that's the answer. > At run time, it's fairly straightforward to check for the same package name when we validate a subclass. But it's also doesn't benefit the JVM in any way, so maybe this is more of a language-specific restriction that should be ignored by the JVM. (E.g., maybe Kotlin doesn't mind compiling sealed hierarchies across different packages in the unnamed module, even if Java won't do it.) Probably right. From forax at univ-mlv.fr Fri Apr 24 19:32:49 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 24 Apr 2020 21:32:49 +0200 (CEST) Subject: Possible records tweak In-Reply-To: References: Message-ID: <1879266571.855821.1587756769720.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Guy Steele" > Cc: "amber-spec-experts" > Envoy?: Vendredi 24 Avril 2020 21:08:02 > Objet: Re: Possible records tweak >> Not a bad idea, but here are two and a half alternatives we could consider: >> (1) Simply ban use of ?this? within a compact constructor. This might seem like >> overkill, but it is very easy to explain: the keyword ?this? is not available >> in a compact constructor, period. Because the argument names shadow the field >> names, you don?t have access to the fields at all in a compact constructor. If >> you don?t like it, write a non-compact constructor. > That's a nice clean way to do it, because under this (heh) model, `this` is > useless -- can't read the fields (they're DU), can't write them (cause I say > so), shouldn't call instance methods anyway (because the object is uninitilized > and therefore may be in an invalid state), and you shouldn't pass `this` to > alien code (because that undermines the final field guarantees of the JMM.) So > if its useless, just don't utter it. It's bold but i like 'this' rule. Easy to explain, easy to remember. > (Which, in hindsight, might have been a good rule for _all_ constructors, if > there was another way to initialize the fields. Surely would have eliminated > much verifier complexity.) accessing to the identity hashcode or the current class inside a constructor is valid (i believe) but those are a corner cases. R?mi From daniel.smith at oracle.com Fri Apr 24 19:36:34 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 24 Apr 2020 13:36:34 -0600 Subject: Possible records tweak In-Reply-To: References: Message-ID: > On Apr 24, 2020, at 1:08 PM, Brian Goetz wrote: > >> Not a bad idea, but here are two and a half alternatives we could consider: >> >> (1) Simply ban use of ?this? within a compact constructor. This might seem like overkill, but it is very easy to explain: the keyword ?this? is not available in a compact constructor, period. Because the argument names shadow the field names, you don?t have access to the fields at all in a compact constructor. If you don?t like it, write a non-compact constructor. > > That's a nice clean way to do it, because under this (heh) model, `this` is useless -- can't read the fields (they're DU), can't write them (cause I say so), shouldn't call instance methods anyway (because the object is uninitilized and therefore may be in an invalid state), and you shouldn't pass `this` to alien code (because that undermines the final field guarantees of the JMM.) So if its useless, just don't utter it. > > (Which, in hindsight, might have been a good rule for _all_ constructors, if there was another way to initialize the fields. Surely would have eliminated much verifier complexity.) I wouldn't suggest a syntactic restriction on 'this'?that's a brittle way to try to provide guarantees about not leaking the object being instantiated. (What about the entities in scope? What about 'super'? What about inner class instantiations? It's whack-a-mole.) The more principled thing to do is to say that the constructor appears in a static context. That almost works, except that you'd typically need the class type variables to be in scope... It's sort of a "mostly static context". I'd mildly prefer just prohibiting references to the fields rather than adopting that extra complexity. Anyway, in terms of the wisdom of going there: it's a different model. Java could have been designed that way, and wasn't. But this is a special circumstance, maybe deserving of special treatment. There are good use cases for calling instance methods. But they typically would involve refactoring code shared by constructors (less useful here, because everything flows to a single constructor) and relying on a subset of initialized fields (useless here, because you don't want the fields to be touched). Early publishing of 'this' is important if you're building a circular data structure. That's not really the focus of records, given that the caller is supposed to (at least morally) have all of the fields in hand before allocating the record. Anyway, whatever use cases we can come up with are corner cases, and there's a simple fallback for the corner cases. From brian.goetz at oracle.com Fri Apr 24 19:40:55 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 24 Apr 2020 15:40:55 -0400 Subject: Possible records tweak In-Reply-To: References: Message-ID: <20925e3a-7fae-a89f-243e-4ce0ebf17d5b@oracle.com> > I wouldn't suggest a syntactic restriction on 'this'?that's a brittle way to try to provide guarantees about not leaking the object being instantiated. (What about the entities in scope? What about 'super'? What about inner class instantiations? It's whack-a-mole.) What if we said that `this` were DU in the entire body of the compact ctor, just as it is before the this/super call?? This would obviate the need to create yet another model (like static, but not quite), and instead would keep you from depending on `this`. From daniel.smith at oracle.com Fri Apr 24 19:43:48 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 24 Apr 2020 13:43:48 -0600 Subject: Possible records tweak In-Reply-To: <1879266571.855821.1587756769720.JavaMail.zimbra@u-pem.fr> References: <1879266571.855821.1587756769720.JavaMail.zimbra@u-pem.fr> Message-ID: <2E79FBAD-CE8E-4042-8E2C-51928B2E23EF@oracle.com> > On Apr 24, 2020, at 1:32 PM, Remi Forax wrote: > >> (Which, in hindsight, might have been a good rule for _all_ constructors, if there was another way to initialize the fields. Surely would have eliminated much verifier complexity.) >> > accessing to the identity hashcode or the current class inside a constructor is valid (i believe) but those are a corner cases. > Ah, yes. This generalizes to calling methods that safely operate on an already-initialized superclass (typically instance methods of the superclass). If we someday have abstract records or other forms of user-defined superclasses, it will be quite reasonable to call the superclass's instance methods from the subclass's constructor. From daniel.smith at oracle.com Fri Apr 24 19:45:46 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 24 Apr 2020 13:45:46 -0600 Subject: Possible records tweak In-Reply-To: <20925e3a-7fae-a89f-243e-4ce0ebf17d5b@oracle.com> References: <20925e3a-7fae-a89f-243e-4ce0ebf17d5b@oracle.com> Message-ID: <3E1F7622-89C5-444E-8F38-C45BAFDCE2B3@oracle.com> > On Apr 24, 2020, at 1:40 PM, Brian Goetz wrote: > >> I wouldn't suggest a syntactic restriction on 'this'?that's a brittle way to try to provide guarantees about not leaking the object being instantiated. (What about the entities in scope? What about 'super'? What about inner class instantiations? It's whack-a-mole.) > What if we said that `this` were DU in the entire body of the compact ctor, just as it is before the this/super call? This would obviate the need to create yet another model (like static, but not quite), and instead would keep you from depending on `this`. I understand what you mean, but it doesn't work like that. "'this' is DU" is handled by the spec as "you're in a static context". Many operations that morally make use of 'this' don't actually talk about it as if it were a variable. From daniel.smith at oracle.com Fri Apr 24 20:04:57 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 24 Apr 2020 14:04:57 -0600 Subject: [sealed] Module & package constraints In-Reply-To: References: Message-ID: <887167B7-57A6-472E-8455-D633993907C3@oracle.com> > On Apr 24, 2020, at 1:35 PM, Brian Goetz wrote: > >> if the child and parent must successfully refer to each other, then we've already guaranteed that they're compiled at the same time. Is there something more to be gained from forcing them into the same package? > But have we? Imagine two packages, A and B, in separate maintenance domains. First we have: > > package a; > interface I { } > > package b; > class C implements a.I { } > > We can compile A, and then compile B against A.jar. > > Now modify A, and recompile with B.jar on the classpath: > > package a; > sealed interface I permits b.C { } > > This is fine. Thereafter, these JARs can be recompiled independently. And I could easily imagine lore surrounding this trick growing up as a "workaround" against the "stupid rule." Okay, so not compiled at the same time, because the bottom-up half of the mutual reference can exist independently of the top-down half. But: if 'I' must be compiled with 'C' on its class path, and 'C' must be compiled with 'I' on its class path, isn't that effectively one maintenance domain? Practically, the only way the maintainer of 'I' is going to be able to declare a module is if they include 'C' in that module, too. (I can kind of see a counter-argument that the compiler rule is designed to prevent this unwanted tight coupling by demanding that the classes belong to the same package. But it seems unrealistic that a programmer would succeed in *adding* a dependency on b.jar to their system, just to support a 'sealed' declaration. More likely, the dependency was already there for deeper reasons, which would have to be grappled with anyway to migrate to modules.) From brian.goetz at oracle.com Fri Apr 24 20:09:28 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 24 Apr 2020 16:09:28 -0400 Subject: [sealed] Module & package constraints In-Reply-To: <887167B7-57A6-472E-8455-D633993907C3@oracle.com> References: <887167B7-57A6-472E-8455-D633993907C3@oracle.com> Message-ID: > But: if 'I' must be compiled with 'C' on its class path, and 'C' must be compiled with 'I' on its class path, isn't that effectively one maintenance domain? Practically, the only way the maintainer of 'I' is going to be able to declare a module is if they include 'C' in that module, too. This is analogous to the statement that "cyclic module dependencies are effectively one module."? Which is eminently true, and yet many developers refuse to see it, and in fact see cyclic dependencies as an important missing feature in the module system.? Which means that dealing with situations like this has already become a part of the body of "tolerated programming practices." That's not to say that we should always be in the business of stamping out bad practices, but this is a new feature and thus our one chance to set things rolling in the right direction. > (I can kind of see a counter-argument that the compiler rule is designed to prevent this unwanted tight coupling by demanding that the classes belong to the same package. But it seems unrealistic that a programmer would succeed in *adding* a dependency on b.jar to their system, just to support a 'sealed' declaration. More likely, the dependency was already there for deeper reasons, which would have to be grappled with anyway to migrate to modules.) And code that meets those "deeper reasons" will likely never modularize.? But, I would also rather not give them one more excuse why they can't.... From daniel.smith at oracle.com Fri Apr 24 21:24:59 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 24 Apr 2020 15:24:59 -0600 Subject: [sealed] Module & package constraints In-Reply-To: <9574B824-4AD3-4FFD-82F1-1733592A010F@oracle.com> References: <9574B824-4AD3-4FFD-82F1-1733592A010F@oracle.com> Message-ID: <2F7EDFB8-8031-411E-B3A6-698EBB43C102@oracle.com> Sounds like the conclusion is: > On Apr 24, 2020, at 1:16 PM, Dan Smith wrote: > > I presented this as an outline of our choices, but to put a stake in the ground, here are my preferences. > >> On Apr 23, 2020, at 12:27 PM, Dan Smith wrote: >> >> 1) Is it legal to have a permitted subclass/subinterface that does not actually extend the permitting class or interface? > > At compile time, no. Let's prohibit that. > > At run time, delay any validation until a particular subclass is loaded. Agreement on both of these. (Requires a new rule in the language spec.) >> 2) What are the constraints on module membership? > > At compile time, do nothing. Per (1), it's already impossible to successfully declare a sealed parent/child across a module boundary. > > At run time, require the same run-time module. In the corner case of a parent/child split across class loaders (in unnamed modules), we say "sorry, don't do that." (I do think this will come up. Extension is an important way to communicate across class loader boundaries. But I think programs doing so need to accept that sealed types are not for them.) Agreement on both of these. (Language spec can mention the module implication, but doesn't need to make it a rule.) >> 3) What are the constraints on package membership? >> >> The use case here is a class/interface extending a sealed class/interface, both in the same unnamed module. > > At compile time, do nothing. Per (1), it's impossible to separately compile the parent and child, no matter what packages they are declared in. > > At run time, there's nothing to enforce. I don't care how you've deployed your class files, as long as they successfully compiled and have the same module/loader. The compile-time package restriction should stay, as a nudge away from unwanted circularities. There's a choice to make at run time, with some disagreement. I think it makes sense to leave it out for now. (Only way to get into this situation is to generate bytecode with something other than javac. You can't get there with separate compilation.) From john.r.rose at oracle.com Fri Apr 24 21:34:14 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 24 Apr 2020 14:34:14 -0700 Subject: Possible records tweak In-Reply-To: <2E79FBAD-CE8E-4042-8E2C-51928B2E23EF@oracle.com> References: <1879266571.855821.1587756769720.JavaMail.zimbra@u-pem.fr> <2E79FBAD-CE8E-4042-8E2C-51928B2E23EF@oracle.com> Message-ID: On Apr 24, 2020, at 12:43 PM, Dan Smith wrote: > >> On Apr 24, 2020, at 1:32 PM, Remi Forax wrote: >> >>> (Which, in hindsight, might have been a good rule for _all_ constructors, if there was another way to initialize the fields. Surely would have eliminated much verifier complexity.) >>> >> accessing to the identity hashcode or the current class inside a constructor is valid (i believe) but those are a corner cases. >> > > Ah, yes. This generalizes to calling methods that safely operate on an already-initialized superclass (typically instance methods of the superclass). > > If we someday have abstract records or other forms of user-defined superclasses, it will be quite reasonable to call the superclass's instance methods from the subclass's constructor. > So maybe `super` is DA but `this` is DU, just like in the code before the super-constructor call. (I?m abusing the terms DA/DU like Brian is, and you call out, but they are close to correct.) From info at j-kuhn.de Sat Apr 25 10:14:39 2020 From: info at j-kuhn.de (Johannes Kuhn) Date: Sat, 25 Apr 2020 12:14:39 +0200 Subject: [sealed] Module & package constraints In-Reply-To: <2F7EDFB8-8031-411E-B3A6-698EBB43C102@oracle.com> References: <9574B824-4AD3-4FFD-82F1-1733592A010F@oracle.com> <2F7EDFB8-8031-411E-B3A6-698EBB43C102@oracle.com> Message-ID: The scenario I think about is the following: a.Foo is in A.jar. It was never designed to be extended, but the maintainer made a mistake and forgot to restrict that (for example did not remove the default constructor). b.Bar in B.jar extends a.Foo. This was a bad idea, and should not have been done in the first place. The maintainer of A.jar decides to finally fix the mistake of a.Foo being extensible. But doing so would break the popular B.jar. So they use a hack and add B.jar to the classpath, add sealed to a.Foo and explicitly permit b.Bar. (People will do stupid things) Now the maintainer of B.jar comes around, notices that this class was never meant for extension in the first place, composition is better than inheritance... But if they now remove the "extends a.Foo", their class won't compile anymore. At this point, any solution becomes more messy. Maybe I missed the small but important detail that both a.Foo and b.Bar has to be compiled at the same time from source code. Then people would create a dummy "class Bar extends a.Foo {}" to get around that. (Also, incremental compilation?). In my opinion, the mistake was to allow a.Foo to permit b.Bar. And this would break if A.jar, B.jar or both are put on the module path. So in my opinion, the requirement that both types are in the same package for the unnamed module *during compilation* must stay. On 24-Apr-20 23:24, Dan Smith wrote: > Sounds like the conclusion is: > >> On Apr 24, 2020, at 1:16 PM, Dan Smith wrote: >> >> I presented this as an outline of our choices, but to put a stake in the ground, here are my preferences. >> >>> On Apr 23, 2020, at 12:27 PM, Dan Smith wrote: >>> >>> 1) Is it legal to have a permitted subclass/subinterface that does not actually extend the permitting class or interface? >> At compile time, no. Let's prohibit that. >> >> At run time, delay any validation until a particular subclass is loaded. > Agreement on both of these. (Requires a new rule in the language spec.) > >>> 2) What are the constraints on module membership? >> At compile time, do nothing. Per (1), it's already impossible to successfully declare a sealed parent/child across a module boundary. >> >> At run time, require the same run-time module. In the corner case of a parent/child split across class loaders (in unnamed modules), we say "sorry, don't do that." (I do think this will come up. Extension is an important way to communicate across class loader boundaries. But I think programs doing so need to accept that sealed types are not for them.) > Agreement on both of these. (Language spec can mention the module implication, but doesn't need to make it a rule.) > >>> 3) What are the constraints on package membership? >>> >>> The use case here is a class/interface extending a sealed class/interface, both in the same unnamed module. >> At compile time, do nothing. Per (1), it's impossible to separately compile the parent and child, no matter what packages they are declared in. >> >> At run time, there's nothing to enforce. I don't care how you've deployed your class files, as long as they successfully compiled and have the same module/loader. > The compile-time package restriction should stay, as a nudge away from unwanted circularities. > > There's a choice to make at run time, with some disagreement. I think it makes sense to leave it out for now. (Only way to get into this situation is to generate bytecode with something other than javac. You can't get there with separate compilation.) From forax at univ-mlv.fr Sun Apr 26 21:08:56 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 26 Apr 2020 23:08:56 +0200 (CEST) Subject: Why are the curly braces required for a minimal Record declaration? In-Reply-To: <409C3844-EA21-4970-9960-E5DE899891DB@oracle.com> References: <409C3844-EA21-4970-9960-E5DE899891DB@oracle.com> Message-ID: <1344252464.1516.1587935336328.JavaMail.zimbra@u-pem.fr> I'm glad you just send that message on amber-dev, playing with the parsing of JSON [1], I stumble upon one of our decisions that i now regret, switch expression doesn't allow return and continue while a switch statement do. >From now, it look like a gratuitous "Tuesday rule". As a user if you want to use return and/or continue, you can transform your switch expression to a switch statement and it will work. So by adding the fact that a switch expression doesn't support return/continue, we are making the code less readable because we are implicitly saying you should use a switch statement here, not a switch expression. Trying to remember why we have decided to not support return/continue, i believe it was at the time we were using "break" as keyword instead of yield, and at that point the decision was making a lot more sense because we did want people to have trouble with break working differently from continue. The good news is that currently the code doesn't compile, so if everybody agree, we can revert that decision and at the same time simplify the JLS (it will not simplify the compiler until the support of Java 14 is dropped). regards, R?mi [1] https://github.com/forax/valuetype-lworld/blob/master/fr.umlv.jsonapi/src/main/java/fr/umlv/jsonapi/JsonReader.java#L206 ----- Mail original ----- > De: "Brian Goetz" > ?: "Swaranga Sarma" > Cc: "amber-dev" > Envoy?: Dimanche 26 Avril 2020 17:42:19 > Objet: Re: Why are the curly braces required for a minimal Record declaration? > John and Remi correctly pointed out that some punctuation is necessary, and that > having a special rule (like allowing a semicolon instead of a pair of braces) > doesn?t really carry its weight, since it only saves one keystroke. Here?s > another reason. > > We actually did prototype it originally where you could drop the braces, and say > > record Foo(int x); > > But it doesn?t take long before you start asking ?can I omit the braces on an > empty class or interface? what about methods too?? Which brings us to a > general principle that is in play for a feature like this: minimizing the > gratuitous differences between records and classes, because we don?t want to > burden users with keeping a lot of silly rules in their head like ?on tuesdays, > you get two braces for the price of one.? > > This principle comes up over and over again; we saw one yesterday on this list > (?can I please have non-nullable records?), and there are many more, ranging > from the very reasonable-seeming ?can I invoke a record constructor by > parameter name rather than positionally? to the absurd ?can we omit semicolons > in records, it?s not incompatible? (yes, we actually got this one.) > > Every one of these differences adds friction: > > - Users have to remember that they can (do the new thing) in records, but not > classes; > - Users will complain ?why can?t classes do this too?, and are quite likely to > view the glass as being mostly empty rather than partially full; > - It is harder or less compatible to migrate records to classes, because they > will have features that can?t be represented in otherwise-equivalent classes. > > Like all ?syntactic sugar?, there is the risk of a sugar high, and the resulting > glycemic crash. We tried to limit the sugar in records to that needed to > achieve the semantic goal ? nominal tuples. > >> On Apr 25, 2020, at 4:39 PM, Swaranga Sarma wrote: >> >> Records is a new feature and does not carry the same baggage as classes do. >> Looking at a minimal Record Point declaration: >> >> record Point(int x, int y) {} >> >> Every part of this declaration makes sense to me expect the curly braces >> '{}'. Why is that required if I do no intend do override any part of the >> record? >> >> I am probably missing something; appreciate the insight. >> > > -Swaranga From brian.goetz at oracle.com Sun Apr 26 21:15:03 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 26 Apr 2020 17:15:03 -0400 Subject: Why are the curly braces required for a minimal Record declaration? In-Reply-To: <1344252464.1516.1587935336328.JavaMail.zimbra@u-pem.fr> References: <409C3844-EA21-4970-9960-E5DE899891DB@oracle.com> <1344252464.1516.1587935336328.JavaMail.zimbra@u-pem.fr> Message-ID: <40c1173b-7f7b-18c0-09bf-37e30d57da6c@oracle.com> No, this is not why we did it. This was the first form of expression that can embed statements.? This raises the ugly question of nonlocal control flow.? We felt it would simply be too complicated to be able to return/continue out of the middle of evaluating an expression -- this would be a significantly new form of control flow for expressions.? Instead, evaluating an expression must either yield a value, or throw, as it has always been with expressions. On 4/26/2020 5:08 PM, Remi Forax wrote: > I'm glad you just send that message on amber-dev, > playing with the parsing of JSON [1], I stumble upon one of our decisions that i now regret, > switch expression doesn't allow return and continue while a switch statement do. > > From now, it look like a gratuitous "Tuesday rule". > > As a user if you want to use return and/or continue, you can transform your switch expression to a switch statement and it will work. > So by adding the fact that a switch expression doesn't support return/continue, we are making the code less readable because we are implicitly saying you should use a switch statement here, not a switch expression. > > Trying to remember why we have decided to not support return/continue, i believe it was at the time we were using "break" as keyword instead of yield, and at that point the decision was making a lot more sense because we did want people to have trouble with break working differently from continue. > > The good news is that currently the code doesn't compile, so if everybody agree, we can revert that decision and at the same time simplify the JLS (it will not simplify the compiler until the support of Java 14 is dropped). > > regards, > R?mi > > [1] https://github.com/forax/valuetype-lworld/blob/master/fr.umlv.jsonapi/src/main/java/fr/umlv/jsonapi/JsonReader.java#L206 > > ----- Mail original ----- >> De: "Brian Goetz" >> ?: "Swaranga Sarma" >> Cc: "amber-dev" >> Envoy?: Dimanche 26 Avril 2020 17:42:19 >> Objet: Re: Why are the curly braces required for a minimal Record declaration? >> John and Remi correctly pointed out that some punctuation is necessary, and that >> having a special rule (like allowing a semicolon instead of a pair of braces) >> doesn?t really carry its weight, since it only saves one keystroke. Here?s >> another reason. >> >> We actually did prototype it originally where you could drop the braces, and say >> >> record Foo(int x); >> >> But it doesn?t take long before you start asking ?can I omit the braces on an >> empty class or interface? what about methods too?? Which brings us to a >> general principle that is in play for a feature like this: minimizing the >> gratuitous differences between records and classes, because we don?t want to >> burden users with keeping a lot of silly rules in their head like ?on tuesdays, >> you get two braces for the price of one.? >> >> This principle comes up over and over again; we saw one yesterday on this list >> (?can I please have non-nullable records?), and there are many more, ranging >> from the very reasonable-seeming ?can I invoke a record constructor by >> parameter name rather than positionally? to the absurd ?can we omit semicolons >> in records, it?s not incompatible? (yes, we actually got this one.) >> >> Every one of these differences adds friction: >> >> - Users have to remember that they can (do the new thing) in records, but not >> classes; >> - Users will complain ?why can?t classes do this too?, and are quite likely to >> view the glass as being mostly empty rather than partially full; >> - It is harder or less compatible to migrate records to classes, because they >> will have features that can?t be represented in otherwise-equivalent classes. >> >> Like all ?syntactic sugar?, there is the risk of a sugar high, and the resulting >> glycemic crash. We tried to limit the sugar in records to that needed to >> achieve the semantic goal ? nominal tuples. >> >>> On Apr 25, 2020, at 4:39 PM, Swaranga Sarma wrote: >>> >>> Records is a new feature and does not carry the same baggage as classes do. >>> Looking at a minimal Record Point declaration: >>> >>> record Point(int x, int y) {} >>> >>> Every part of this declaration makes sense to me expect the curly braces >>> '{}'. Why is that required if I do no intend do override any part of the >>> record? >>> >>> I am probably missing something; appreciate the insight. >>> >>> -Swaranga From donraab at gmail.com Sun Apr 26 22:22:30 2020 From: donraab at gmail.com (Donald Raab) Date: Sun, 26 Apr 2020 18:22:30 -0400 Subject: Experience Report using various Project Amber Features Message-ID: <119D484E-F10B-4429-B884-39B06B6B6B67@gmail.com> Hi All, I wanted to share our recent experience upgrading several OSS code katas to Java 14 with this group. We will continue leveraging new Project Amber and other OpenJDK project features in these katas when we find opportunities to. Overall, our experience has been quite positive. Here?s a link to the collection of katas, all of which are now compiling with Java 14. The katas started out on Java 8, and were upgraded to Java 10 two years ago. We just upgraded to Java 14 a few weeks ago. https://github.com/BNYMellon/CodeKatas We tagged the repo with ?Java-14? on GitHub to make the Java 14 examples easy for developers to find. https://github.com/topics/java-14 The specific Project Amber features we are using in the katas are: 1. Local Variable Type Inference We have experimented with using the LVTI feature throughout the katas since Java 10 was released. This feature did not make a huge impact in terms of readability in the kata code itself but did prove useful in some cases. With the Java 14 upgrade, we introduced a proof of concept collections framework in the Deck of Cards Kata that adds eager APIs mirroring Stream protocols directly collection interfaces. The APIs we built in the proof of concept are implemented via default methods in the interfaces. Here for the first time, I was finally able to experiment with using LVTI in library code. This had a positive impact on the readability of the the library code IMO. This makes me wonder how much LVTI is being leveraged for good benefit in the JDK code itself. Examples: https://github.com/BNYMellon/CodeKatas/blob/master/deck-of-cards-kata/src/main/java/bnymellon/codekatas/deckofcards/custom/collections/MutableList.java https://github.com/BNYMellon/CodeKatas/blob/master/deck-of-cards-kata/src/main/java/bnymellon/codekatas/deckofcards/custom/collections/CustomCollectionsDeckOfCardsAsList.java 2. Records I tried leveraging records with Card class in the Deck Of Cards Kata. This worked out well and delivered the savings I had expected. Kudos! Example: https://github.com/BNYMellon/CodeKatas/blob/master/deck-of-cards-kata/src/main/java/bnymellon/codekatas/deckofcards/Card.java#L30 I will look to use the record feature in the Donut Kata as well for implementing the Donut Class. 3. Pattern Matching for instanceof This feature worked out great. Well done! Example: https://github.com/BNYMellon/CodeKatas/blob/master/deck-of-cards-kata/src/main/java/bnymellon/codekatas/deckofcards/custom/collections/MutableCollection.java#L24 As an OSS library developer, it is not often I get to use the latest available features in production library code, as providing compatibility to our existing client base which remains mostly on Java 8 makes it impossible. It was a pleasure leveraging these new features in the katas. Thanks, Don From brian.goetz at oracle.com Sun Apr 26 22:51:58 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 26 Apr 2020 18:51:58 -0400 Subject: Experience Report using various Project Amber Features In-Reply-To: <119D484E-F10B-4429-B884-39B06B6B6B67@gmail.com> References: <119D484E-F10B-4429-B884-39B06B6B6B67@gmail.com> Message-ID: This bears out one of my background assumptions about these features -- that clean, carefully engineered codebases will benefit more from these features than most, because they will already be well factored, single-responsibility, leaning heavily on immutability, etc.? So it's not surprising you found immediate applications for records, for example.? The codebases that lean more heavily on mutation, probably will have a harder time.? The hope is that these features will provide a nudge towards these practices which are good in their own right.... On 4/26/2020 6:22 PM, Donald Raab wrote: > Hi All, > > I wanted to share our recent experience upgrading several OSS code > katas to Java 14 with this group. We will continue leveraging new > Project Amber and other OpenJDK project features in these katas when > we find opportunities to. Overall, our experience has been quite > positive. > > Here?s a link to the collection of katas, all of which are now > compiling with Java 14. The katas started out on Java 8, and were > upgraded to Java 10 two years ago. We just upgraded to Java 14 a few > weeks ago. > > https://github.com/BNYMellon/CodeKatas > > We tagged the repo with ?Java-14? on GitHub to make the Java 14 > examples easy for developers to find. > > https://github.com/topics/java-14 > > The specific Project Amber features we are using in the katas are: > > 1. Local Variable Type Inference > > We have experimented with using the LVTI feature throughout the katas > since Java 10 was released. This feature did not make a huge impact in > terms of readability in the kata code itself but did prove useful in > some cases. With the Java 14 upgrade, we introduced a proof of concept > collections framework in the Deck of Cards Kata that adds eager APIs > mirroring Stream protocols directly collection interfaces. The APIs we > built in the proof of concept are implemented via default methods in > the interfaces. Here for the first time, I was finally able to > experiment with using LVTI in library code. This had a positive impact > on the readability of the the library code IMO. This makes me wonder > how much LVTI is being leveraged for good benefit in the JDK code itself. > > Examples: > https://github.com/BNYMellon/CodeKatas/blob/master/deck-of-cards-kata/src/main/java/bnymellon/codekatas/deckofcards/custom/collections/MutableList.java > https://github.com/BNYMellon/CodeKatas/blob/master/deck-of-cards-kata/src/main/java/bnymellon/codekatas/deckofcards/custom/collections/CustomCollectionsDeckOfCardsAsList.java > > 2. Records > > I tried leveraging records with Card class in the Deck Of Cards Kata. > This worked out well and delivered the savings I had expected. Kudos! > > Example: > https://github.com/BNYMellon/CodeKatas/blob/master/deck-of-cards-kata/src/main/java/bnymellon/codekatas/deckofcards/Card.java#L30 > > I will look to use the record feature in the Donut Kata as well for > implementing the Donut Class. > > 3. Pattern Matching for instanceof > > This feature worked out great. Well done! > > Example: > https://github.com/BNYMellon/CodeKatas/blob/master/deck-of-cards-kata/src/main/java/bnymellon/codekatas/deckofcards/custom/collections/MutableCollection.java#L24 > > As an OSS library developer, it is not often I get to use the latest > available features in production library code, as providing > compatibility to our existing client base which remains mostly on Java > 8 makes it impossible. It was a pleasure leveraging these new features > in the katas. > > Thanks, > Don > From forax at univ-mlv.fr Sun Apr 26 23:38:26 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 27 Apr 2020 01:38:26 +0200 (CEST) Subject: Why are the curly braces required for a minimal Record declaration? In-Reply-To: <40c1173b-7f7b-18c0-09bf-37e30d57da6c@oracle.com> References: <409C3844-EA21-4970-9960-E5DE899891DB@oracle.com> <1344252464.1516.1587935336328.JavaMail.zimbra@u-pem.fr> <40c1173b-7f7b-18c0-09bf-37e30d57da6c@oracle.com> Message-ID: <686934326.62126.1587944306534.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Dimanche 26 Avril 2020 23:15:03 > Objet: Re: Why are the curly braces required for a minimal Record declaration? > No, this is not why we did it. > This was the first form of expression that can embed statements. This raises the > ugly question of nonlocal control flow. We felt it would simply be too > complicated to be able to return/continue out of the middle of evaluating an > expression -- this would be a significantly new form of control flow for > expressions. Instead, evaluating an expression must either yield a value, or > throw, as it has always been with expressions. Thanks, I've forgotten that you have to trash/cleanup the stack before being able to jump or return. By forcing, users to use the switch statement, we avoid that case. regards, R?mi > On 4/26/2020 5:08 PM, Remi Forax wrote: >> I'm glad you just send that message on amber-dev, >> playing with the parsing of JSON [1], I stumble upon one of our decisions that i >> now regret, >> switch expression doesn't allow return and continue while a switch statement do. >> From now, it look like a gratuitous "Tuesday rule". >> As a user if you want to use return and/or continue, you can transform your >> switch expression to a switch statement and it will work. >> So by adding the fact that a switch expression doesn't support return/continue, >> we are making the code less readable because we are implicitly saying you >> should use a switch statement here, not a switch expression. >> Trying to remember why we have decided to not support return/continue, i believe >> it was at the time we were using "break" as keyword instead of yield, and at >> that point the decision was making a lot more sense because we did want people >> to have trouble with break working differently from continue. >> The good news is that currently the code doesn't compile, so if everybody agree, >> we can revert that decision and at the same time simplify the JLS (it will not >> simplify the compiler until the support of Java 14 is dropped). >> regards, >> R?mi >> [1] [ >> https://github.com/forax/valuetype-lworld/blob/master/fr.umlv.jsonapi/src/main/java/fr/umlv/jsonapi/JsonReader.java#L206 >> | >> https://github.com/forax/valuetype-lworld/blob/master/fr.umlv.jsonapi/src/main/java/fr/umlv/jsonapi/JsonReader.java#L206 >> ] ----- Mail original ----- >>> De: "Brian Goetz" [ mailto:brian.goetz at oracle.com | ] >>> ?: "Swaranga Sarma" [ mailto:sarma.swaranga at gmail.com | >>> ] Cc: "amber-dev" [ >>> mailto:amber-dev at openjdk.java.net | ] Envoy?: >>> Dimanche 26 Avril 2020 17:42:19 >>> Objet: Re: Why are the curly braces required for a minimal Record declaration? >>> John and Remi correctly pointed out that some punctuation is necessary, and that >>> having a special rule (like allowing a semicolon instead of a pair of braces) >>> doesn?t really carry its weight, since it only saves one keystroke. Here?s >>> another reason. >>> We actually did prototype it originally where you could drop the braces, and say >>> record Foo(int x); >>> But it doesn?t take long before you start asking ?can I omit the braces on an >>> empty class or interface? what about methods too?? Which brings us to a >>> general principle that is in play for a feature like this: minimizing the >>> gratuitous differences between records and classes, because we don?t want to >>> burden users with keeping a lot of silly rules in their head like ?on tuesdays, >>> you get two braces for the price of one.? >>> This principle comes up over and over again; we saw one yesterday on this list >>> (?can I please have non-nullable records?), and there are many more, ranging >>> from the very reasonable-seeming ?can I invoke a record constructor by >>> parameter name rather than positionally? to the absurd ?can we omit semicolons >>> in records, it?s not incompatible? (yes, we actually got this one.) >>> Every one of these differences adds friction: >>> - Users have to remember that they can (do the new thing) in records, but not >>> classes; >>> - Users will complain ?why can?t classes do this too?, and are quite likely to >>> view the glass as being mostly empty rather than partially full; >>> - It is harder or less compatible to migrate records to classes, because they >>> will have features that can?t be represented in otherwise-equivalent classes. >>> Like all ?syntactic sugar?, there is the risk of a sugar high, and the resulting >>> glycemic crash. We tried to limit the sugar in records to that needed to >>> achieve the semantic goal ? nominal tuples. >>>> On Apr 25, 2020, at 4:39 PM, Swaranga Sarma [ mailto:sarma.swaranga at gmail.com | >>>> ] wrote: >>>> Records is a new feature and does not carry the same baggage as classes do. >>>> Looking at a minimal Record Point declaration: >>>> record Point(int x, int y) {} >>>> Every part of this declaration makes sense to me expect the curly braces >>>> '{}'. Why is that required if I do no intend do override any part of the >>>> record? >>>> I am probably missing something; appreciate the insight. >>>> -Swaranga From forax at univ-mlv.fr Mon Apr 27 12:03:14 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 27 Apr 2020 14:03:14 +0200 (CEST) Subject: Experience Report using various Project Amber Features In-Reply-To: References: <119D484E-F10B-4429-B884-39B06B6B6B67@gmail.com> Message-ID: <712257033.462373.1587988994621.JavaMail.zimbra@u-pem.fr> And it's like with enums in 2004, i remember a guy predicting that nobody will use them because values() or valueOf() is always generated, you can not inherits from an enum, etc Once a feature existed, we collectively start to twist our codes to use records instead of plain old classes. By example, choosing to use an interface with default methods instead of an abstract class with no field so subclasses can be records. R?mi ----- Mail original ----- > De: "Brian Goetz" > ?: "Donald Raab" , "amber-spec-experts" > Cc: "Nikhil Nanivadekar" , "Chandra Guntur" > Envoy?: Lundi 27 Avril 2020 00:51:58 > Objet: Re: Experience Report using various Project Amber Features > This bears out one of my background assumptions about these features -- > that clean, carefully engineered codebases will benefit more from these > features than most, because they will already be well factored, > single-responsibility, leaning heavily on immutability, etc.? So it's > not surprising you found immediate applications for records, for > example.? The codebases that lean more heavily on mutation, probably > will have a harder time.? The hope is that these features will provide a > nudge towards these practices which are good in their own right.... > > On 4/26/2020 6:22 PM, Donald Raab wrote: >> Hi All, >> >> I wanted to share our recent experience upgrading several OSS code >> katas to Java 14 with this group. We will continue leveraging new >> Project Amber and other OpenJDK project features in these katas when >> we find opportunities to. Overall, our experience has been quite >> positive. >> >> Here?s a link to the collection of katas, all of which are now >> compiling with Java 14. The katas started out on Java 8, and were >> upgraded to Java 10 two years ago. We just upgraded to Java 14 a few >> weeks ago. >> >> https://github.com/BNYMellon/CodeKatas >> >> We tagged the repo with ?Java-14? on GitHub to make the Java 14 >> examples easy for developers to find. >> >> https://github.com/topics/java-14 >> >> The specific Project Amber features we are using in the katas are: >> >> 1. Local Variable Type Inference >> >> We have experimented with using the LVTI feature throughout the katas >> since Java 10 was released. This feature did not make a huge impact in >> terms of readability in the kata code itself but did prove useful in >> some cases. With the Java 14 upgrade, we introduced a proof of concept >> collections framework in the Deck of Cards Kata that adds eager APIs >> mirroring Stream protocols directly collection interfaces. The APIs we >> built in the proof of concept are implemented via default methods in >> the interfaces. Here for the first time, I was finally able to >> experiment with using LVTI in library code. This had a positive impact >> on the readability of the the library code IMO. This makes me wonder >> how much LVTI is being leveraged for good benefit in the JDK code itself. >> >> Examples: >> https://github.com/BNYMellon/CodeKatas/blob/master/deck-of-cards-kata/src/main/java/bnymellon/codekatas/deckofcards/custom/collections/MutableList.java >> https://github.com/BNYMellon/CodeKatas/blob/master/deck-of-cards-kata/src/main/java/bnymellon/codekatas/deckofcards/custom/collections/CustomCollectionsDeckOfCardsAsList.java >> >> 2. Records >> >> I tried leveraging records with Card class in the Deck Of Cards Kata. >> This worked out well and delivered the savings I had expected. Kudos! >> >> Example: >> https://github.com/BNYMellon/CodeKatas/blob/master/deck-of-cards-kata/src/main/java/bnymellon/codekatas/deckofcards/Card.java#L30 >> >> I will look to use the record feature in the Donut Kata as well for >> implementing the Donut Class. >> >> 3. Pattern Matching for instanceof >> >> This feature worked out great. Well done! >> >> Example: >> https://github.com/BNYMellon/CodeKatas/blob/master/deck-of-cards-kata/src/main/java/bnymellon/codekatas/deckofcards/custom/collections/MutableCollection.java#L24 >> >> As an OSS library developer, it is not often I get to use the latest >> available features in production library code, as providing >> compatibility to our existing client base which remains mostly on Java >> 8 makes it impossible. It was a pleasure leveraging these new features >> in the katas. >> >> Thanks, >> Don From daniel.smith at oracle.com Mon Apr 27 21:00:28 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 27 Apr 2020 15:00:28 -0600 Subject: Possible records tweak In-Reply-To: References: <1879266571.855821.1587756769720.JavaMail.zimbra@u-pem.fr> <2E79FBAD-CE8E-4042-8E2C-51928B2E23EF@oracle.com> Message-ID: > On Apr 24, 2020, at 3:34 PM, John Rose wrote: > > On Apr 24, 2020, at 12:43 PM, Dan Smith wrote: >> >>> On Apr 24, 2020, at 1:32 PM, Remi Forax wrote: >>> >>>> (Which, in hindsight, might have been a good rule for _all_ constructors, if there was another way to initialize the fields. Surely would have eliminated much verifier complexity.) >>>> >>> accessing to the identity hashcode or the current class inside a constructor is valid (i believe) but those are a corner cases. >>> >> >> Ah, yes. This generalizes to calling methods that safely operate on an already-initialized superclass (typically instance methods of the superclass). >> >> If we someday have abstract records or other forms of user-defined superclasses, it will be quite reasonable to call the superclass's instance methods from the subclass's constructor. >> > > So maybe `super` is DA but `this` is DU, just like in the code > before the super-constructor call. (I?m abusing the terms DA/DU > like Brian is, and you call out, but they are close to correct.) I think this means I can't call inherited method 'getParentWidget()' or some static utility 'computeWidgetOfParent(this)' method (e.g., 'identityHashCode(this)'). I *can* call 'super.getParentWidget()'. It's a do-able way to apply some discipline. Feels a little heavy on the "you can't do that" side and light on the "we'll guarantee better programs" side?because of course, once you've called the super method, it can do whatever it wants with the object. Overall, my sense is that controlling all accesses of 'this' (explicit and implicit) is more ambitious than we really want/need here. Airtight constructors are a problem for another day. Prohibiting assignment to the instance fields is more manageable. From john.r.rose at oracle.com Mon Apr 27 22:31:38 2020 From: john.r.rose at oracle.com (John Rose) Date: Mon, 27 Apr 2020 15:31:38 -0700 Subject: Possible records tweak In-Reply-To: References: <1879266571.855821.1587756769720.JavaMail.zimbra@u-pem.fr> <2E79FBAD-CE8E-4042-8E2C-51928B2E23EF@oracle.com> Message-ID: <6090F3BE-6329-4A60-B81D-CD0596987F4E@oracle.com> On Apr 27, 2020, at 2:00 PM, Dan Smith wrote: > > >> On Apr 24, 2020, at 3:34 PM, John Rose wrote: >> >> On Apr 24, 2020, at 12:43 PM, Dan Smith wrote: >>> >>>> On Apr 24, 2020, at 1:32 PM, Remi Forax wrote: >>>> >>>>> (Which, in hindsight, might have been a good rule for _all_ constructors, if there was another way to initialize the fields. Surely would have eliminated much verifier complexity.) >>>>> >>>> accessing to the identity hashcode or the current class inside a constructor is valid (i believe) but those are a corner cases. >>>> >>> >>> Ah, yes. This generalizes to calling methods that safely operate on an already-initialized superclass (typically instance methods of the superclass). >>> >>> If we someday have abstract records or other forms of user-defined superclasses, it will be quite reasonable to call the superclass's instance methods from the subclass's constructor. >>> >> >> So maybe `super` is DA but `this` is DU, just like in the code >> before the super-constructor call. (I?m abusing the terms DA/DU >> like Brian is, and you call out, but they are close to correct.) > > I think this means I can't call inherited method 'getParentWidget()' or some static utility 'computeWidgetOfParent(this)' method (e.g., 'identityHashCode(this)'). I *can* call 'super.getParentWidget()'. > > It's a do-able way to apply some discipline. Feels a little heavy on the "you can't do that" side and light on the "we'll guarantee better programs" side?because of course, once you've called the super method, it can do whatever it wants with the object. > > Overall, my sense is that controlling all accesses of 'this' (explicit and implicit) is more ambitious than we really want/need here. Airtight constructors are a problem for another day. Prohibiting assignment to the instance fields is more manageable. I?m obviously not proposing airtight constructors?although you get that as a forced move (and probably a pleasant one, to boot), for free, whenever you choose to use inline classes. Let's keep the existing problems with constructors as they are: You can call a super method, which via an override can ?see? an incomplete instance. This means that if we forbid ?this? in record default constructors, if there is a sneaky way to get ?super?, then the usual sneaky things can happen. In fact, if we forbid ?this.x = y? (only) then the same sneaky things can happen. The question is see right now is whether forbidding ?this? or forbidding ?this.x = y? is simpler. I think the former is simpler. The latter is less restrictive, but more complex. Which leads to a better user experience? The simpler, as long as the extra restriction does not require the user to resort to complex workarounds. My observations about ?super? are a red herring, I guess; I?m just reaching for an escape hatch away from the proposed restrictions about ?this?. (I just noticed that ?super? is DU before the ?super? call, so it?s not as useful as I thought. I don?t want to entangle the present discussion with lame proposals for extending the usefulness of the ?super? keyword.) ? John From brian.goetz at oracle.com Mon Apr 27 22:37:05 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 27 Apr 2020 18:37:05 -0400 Subject: Possible records tweak In-Reply-To: References: <1879266571.855821.1587756769720.JavaMail.zimbra@u-pem.fr> <2E79FBAD-CE8E-4042-8E2C-51928B2E23EF@oracle.com> Message-ID: <4b3c473e-1034-cb05-8b88-899ecd4a5936@oracle.com> >> So maybe `super` is DA but `this` is DU, just like in the code >> before the super-constructor call. (I?m abusing the terms DA/DU >> like Brian is, and you call out, but they are close to correct.) > I think this means I can't call inherited method 'getParentWidget()' or some static utility 'computeWidgetOfParent(this)' method (e.g., 'identityHashCode(this)'). I *can* call 'super.getParentWidget()'. Except records only inherit from Record, which has no non-inherited instance methods. From daniel.smith at oracle.com Mon Apr 27 22:55:01 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 27 Apr 2020 16:55:01 -0600 Subject: Possible records tweak In-Reply-To: <4b3c473e-1034-cb05-8b88-899ecd4a5936@oracle.com> References: <1879266571.855821.1587756769720.JavaMail.zimbra@u-pem.fr> <2E79FBAD-CE8E-4042-8E2C-51928B2E23EF@oracle.com> <4b3c473e-1034-cb05-8b88-899ecd4a5936@oracle.com> Message-ID: <9D3D0780-32BC-4849-981E-C3FD77D9C39D@oracle.com> > On Apr 27, 2020, at 4:37 PM, Brian Goetz wrote: > > >>> So maybe `super` is DA but `this` is DU, just like in the code >>> before the super-constructor call. (I?m abusing the terms DA/DU >>> like Brian is, and you call out, but they are close to correct.) >> I think this means I can't call inherited method 'getParentWidget()' or some static utility 'computeWidgetOfParent(this)' method (e.g., 'identityHashCode(this)'). I *can* call 'super.getParentWidget()'. > > Except records only inherit from Record, which has no non-inherited instance methods. Yes, this is in the context of: what if we have, say, abstract super-records some day? Now there are really good use cases for wanting access to some members of 'this'. The usual argument that we could always relax the constraint later works here, although once there's a precedent, it may be hard to say "just kidding, use 'this' however you like!" From forax at univ-mlv.fr Mon Apr 27 22:54:36 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 28 Apr 2020 00:54:36 +0200 (CEST) Subject: Possible records tweak In-Reply-To: <4b3c473e-1034-cb05-8b88-899ecd4a5936@oracle.com> References: <1879266571.855821.1587756769720.JavaMail.zimbra@u-pem.fr> <2E79FBAD-CE8E-4042-8E2C-51928B2E23EF@oracle.com> <4b3c473e-1034-cb05-8b88-899ecd4a5936@oracle.com> Message-ID: <1393465323.827868.1588028076413.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "daniel smith" , "John Rose" > Cc: "amber-spec-experts" > Envoy?: Mardi 28 Avril 2020 00:37:05 > Objet: Re: Possible records tweak >>> So maybe `super` is DA but `this` is DU, just like in the code >>> before the super-constructor call. (I?m abusing the terms DA/DU >>> like Brian is, and you call out, but they are close to correct.) >> I think this means I can't call inherited method 'getParentWidget()' or some >> static utility 'computeWidgetOfParent(this)' method (e.g., >> 'identityHashCode(this)'). I *can* call 'super.getParentWidget()'. > > Except records only inherit from Record, which has no non-inherited > instance methods. super.wait(), etc should work R?mi From john.r.rose at oracle.com Tue Apr 28 00:13:57 2020 From: john.r.rose at oracle.com (John Rose) Date: Mon, 27 Apr 2020 17:13:57 -0700 Subject: Record component type can be an inner class of a record In-Reply-To: References: <594699329.1500670.1585083459947.JavaMail.zimbra@u-pem.fr> <486125830.193897.1586797690659.JavaMail.zimbra@u-pem.fr> <489239011.1667370.1587314416053.JavaMail.zimbra@u-pem.fr> Message-ID: On Apr 20, 2020, at 3:32 PM, Dan Smith wrote: > > My point: there are no references to 'A' inside the body. The only reference to 'A' in this program unambiguously refers to the top-level A. > > You seem to be assuming that there will be resolution problems involved in checking the implicit members. And my response is that it's not our job to perform name resolution for implicit members?they are defined more abstractly than that. Specifically, the return type of the 'a' method is the type referenced by the type name 'A' appearing after 'record B(' in the program, as it is resolved *at that location*. Perhaps this sort of question can be avoided if we ensure that desugaring pseudocode specifies that introduced occurrences of types are not re-resolved at their introduced points. One way to do this (with overkill) is to make sure there are fake package prefixes on introduced occurrences: record R(A x) extends AnotherADeclarer { } (where A is fully qualified as pa.A) => final class R extends AnotherADeclarer { pa.A x; } From gavin.bierman at oracle.com Tue Apr 28 05:48:30 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Tue, 28 Apr 2020 06:48:30 +0100 Subject: [records] Latest spec available Message-ID: <28BF30EB-0DB2-4CEF-BCEF-D532597F568B@oracle.com> Dear all: The latest draft of the spec for the second preview of records is available at: http://cr.openjdk.java.net/~gbierman/records2/20200428/specs/records-jls.html The changes from the first preview spec [1] have been discussed on this list and are listed below for reference. However, with an eye to a better treatment of nesting in Java as discussed by Brian [2] we have taken the opportunity to refactor the spec somewhat to allow for a better treatment of local declarations including local records. We have kept these refactorings as two separate JLS change documents, linked to from the records spec. The first document clarifies the usage of terms related to classes and interfaces, and more clearly distinguishes them from types. The second builds on this and regularizes the treatment of nested and local declarations. In particular, it relaxes previous restrictions and permits *local interface and enum declarations*. The records spec then builds on top of these two changes to the JLS to support records (and local record declarations). Comments welcome! Gavin PS: The URLs will change once we have a JEP number for the second preview of records. [1] http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200115/specs/records-jls.html [2] https://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-January/001904.html Details: The changes are the same as those in the first preview of Records in Java SE 14, except for the following: - All text now uses the terminology of "Consistent Class and Interface Terminology" spec change document - The handling of local records now builds on the changes in "Local Static Interfaces and Enum Classes" spec change document - Removed unnecessary change to 4.12.4 - 8.10.1: Removed possibility of `final` modifier for record components - 8.10.1: Clarified that annotations on a record component only remain on the component if its annotation type is applicable in the record component context - 8.10.1: Corrected text around use of `@SafeVarArgs` annotation - 8.10.4: Removed requirement that canonical constructor must be `public`. Any access modifier must provide at least as much access as the record class. If a canonical constructor is implicitly declared, then its access modifier is the same as the record class. - 8.10.4: Added requirement each formal parameter in the formal parameter list of the constructor must have the same name and type as the corresponding record component. The formal parameter must be a variable arity parameter if and only if the corresponding record component is a variable arity record component. - 8.10.4 Added error condition if a field corresponding to a record component of a record class is neither DA nor DU in the body of a compact constructor. - 9.6.4.4 New case for using `@Override` annotation to declare that a method is an accessor method for a record component. From gavin.bierman at oracle.com Tue Apr 28 07:10:47 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Tue, 28 Apr 2020 08:10:47 +0100 Subject: [sealed] Module & package constraints In-Reply-To: <2F7EDFB8-8031-411E-B3A6-698EBB43C102@oracle.com> References: <9574B824-4AD3-4FFD-82F1-1733592A010F@oracle.com> <2F7EDFB8-8031-411E-B3A6-698EBB43C102@oracle.com> Message-ID: <26EB39CC-74CA-4030-9763-E6830BD921AC@oracle.com> > On 24 Apr 2020, at 22:24, Dan Smith wrote: > > Sounds like the conclusion is: > >> On Apr 24, 2020, at 1:16 PM, Dan Smith wrote: >> >> I presented this as an outline of our choices, but to put a stake in the ground, here are my preferences. >> >>> On Apr 23, 2020, at 12:27 PM, Dan Smith wrote: >>> >>> 1) Is it legal to have a permitted subclass/subinterface that does not actually extend the permitting class or interface? >> >> At compile time, no. Let's prohibit that. >> >> At run time, delay any validation until a particular subclass is loaded. > > Agreement on both of these. (Requires a new rule in the language spec.) Apologies. That rule was in the Feb spec, but got left out by mistake in the last draft. Now reinstated. Gavin From daniel.smith at oracle.com Tue Apr 28 16:53:33 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Tue, 28 Apr 2020 10:53:33 -0600 Subject: Record component type can be an inner class of a record In-Reply-To: References: <594699329.1500670.1585083459947.JavaMail.zimbra@u-pem.fr> <486125830.193897.1586797690659.JavaMail.zimbra@u-pem.fr> <489239011.1667370.1587314416053.JavaMail.zimbra@u-pem.fr> Message-ID: <19ED53F5-4B83-4C5F-95E1-B56A2C406906@oracle.com> > On Apr 27, 2020, at 6:13 PM, John Rose wrote: > > On Apr 20, 2020, at 3:32 PM, Dan Smith wrote: >> >> My point: there are no references to 'A' inside the body. The only reference to 'A' in this program unambiguously refers to the top-level A. >> >> You seem to be assuming that there will be resolution problems involved in checking the implicit members. And my response is that it's not our job to perform name resolution for implicit members?they are defined more abstractly than that. Specifically, the return type of the 'a' method is the type referenced by the type name 'A' appearing after 'record B(' in the program, as it is resolved *at that location*. > > Perhaps this sort of question can be avoided if we ensure that > desugaring pseudocode specifies that introduced occurrences > of types are not re-resolved at their introduced points. One > way to do this (with overkill) is to make sure there are fake > package prefixes on introduced occurrences: > > record R(A x) extends AnotherADeclarer { } > (where A is fully qualified as pa.A) > => > final class R extends AnotherADeclarer { > pa.A x; > } > Unless AnotherADeclarer also declares a class named pa. :-) There is no 100% reliable desugaring to Java syntax. But that's not a problem, because implicit members aren't expressed with Java syntax. (This problem is not unique to records. For example, every interface has an implicit 'toString' member (the language doesn't have a concept of inheritance from Object). The return type of 'toString' is 'String', even if the interface has member classes named 'String' and 'java'. Another example: repeated annotations.) From john.r.rose at oracle.com Tue Apr 28 19:53:02 2020 From: john.r.rose at oracle.com (John Rose) Date: Tue, 28 Apr 2020 12:53:02 -0700 Subject: Record component type can be an inner class of a record In-Reply-To: <19ED53F5-4B83-4C5F-95E1-B56A2C406906@oracle.com> References: <594699329.1500670.1585083459947.JavaMail.zimbra@u-pem.fr> <486125830.193897.1586797690659.JavaMail.zimbra@u-pem.fr> <489239011.1667370.1587314416053.JavaMail.zimbra@u-pem.fr> <19ED53F5-4B83-4C5F-95E1-B56A2C406906@oracle.com> Message-ID: <74A91241-C0B7-44F8-9ACD-448DCE78479B@oracle.com> On Apr 28, 2020, at 9:53 AM, Dan Smith wrote: > > Unless AnotherADeclarer also declares a class named pa. :-) > > There is no 100% reliable desugaring to Java syntax. Thank you; I stand corrected! I needed a quick review of JLS 6.5.2. From gavin.bierman at oracle.com Wed Apr 29 17:07:02 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Wed, 29 Apr 2020 18:07:02 +0100 Subject: Possible records tweak In-Reply-To: <1393465323.827868.1588028076413.JavaMail.zimbra@u-pem.fr> References: <1879266571.855821.1587756769720.JavaMail.zimbra@u-pem.fr> <2E79FBAD-CE8E-4042-8E2C-51928B2E23EF@oracle.com> <4b3c473e-1034-cb05-8b88-899ecd4a5936@oracle.com> <1393465323.827868.1588028076413.JavaMail.zimbra@u-pem.fr> Message-ID: Talking it through with Dan, I think the best thing for the JLS is to avoid issues of `this` etc and simply state: It is a compile-time error to assign to the instance fields of the record class in the body of the compact constructor. Thoughts? Gavin > On 27 Apr 2020, at 23:54, Remi Forax wrote: > > > > ----- Mail original ----- >> De: "Brian Goetz" >> ?: "daniel smith" , "John Rose" >> Cc: "amber-spec-experts" >> Envoy?: Mardi 28 Avril 2020 00:37:05 >> Objet: Re: Possible records tweak > >>>> So maybe `super` is DA but `this` is DU, just like in the code >>>> before the super-constructor call. (I?m abusing the terms DA/DU >>>> like Brian is, and you call out, but they are close to correct.) >>> I think this means I can't call inherited method 'getParentWidget()' or some >>> static utility 'computeWidgetOfParent(this)' method (e.g., >>> 'identityHashCode(this)'). I *can* call 'super.getParentWidget()'. >> >> Except records only inherit from Record, which has no non-inherited >> instance methods. > > super.wait(), etc should work > > R?mi > From guy.steele at oracle.com Wed Apr 29 17:28:06 2020 From: guy.steele at oracle.com (Guy Steele) Date: Wed, 29 Apr 2020 13:28:06 -0400 Subject: Possible records tweak In-Reply-To: References: Message-ID: <95278C68-D12C-43A1-9140-EC7333A10EC5@oracle.com> Appealing, but it does raise the question of whether, if the programmer uses some sneaky trick such as calling super, this can really be enforced ar compile time on all cases. If it can (after all), then your proposed wording looks good to me. ?Guy Sent from my iPhone > On Apr 29, 2020, at 1:08 PM, Gavin Bierman wrote: > > ?Talking it through with Dan, I think the best thing for the JLS is to avoid issues of `this` etc and simply state: > > It is a compile-time error to assign to the instance fields of the record class in the body of the compact constructor. > > Thoughts? > Gavin > > >> On 27 Apr 2020, at 23:54, Remi Forax wrote: >> >> >> >> ----- Mail original ----- >>> De: "Brian Goetz" >>> ?: "daniel smith" , "John Rose" >>> Cc: "amber-spec-experts" >>> Envoy?: Mardi 28 Avril 2020 00:37:05 >>> Objet: Re: Possible records tweak >> >>>>> So maybe `super` is DA but `this` is DU, just like in the code >>>>> before the super-constructor call. (I?m abusing the terms DA/DU >>>>> like Brian is, and you call out, but they are close to correct.) >>>> I think this means I can't call inherited method 'getParentWidget()' or some >>>> static utility 'computeWidgetOfParent(this)' method (e.g., >>>> 'identityHashCode(this)'). I *can* call 'super.getParentWidget()'. >>> >>> Except records only inherit from Record, which has no non-inherited >>> instance methods. >> >> super.wait(), etc should work >> >> R?mi >> > From daniel.smith at oracle.com Wed Apr 29 18:23:27 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 29 Apr 2020 12:23:27 -0600 Subject: Possible records tweak In-Reply-To: <95278C68-D12C-43A1-9140-EC7333A10EC5@oracle.com> References: <95278C68-D12C-43A1-9140-EC7333A10EC5@oracle.com> Message-ID: <69290043-5496-457A-80A4-21B741C8DAA4@oracle.com> > On Apr 29, 2020, at 11:28 AM, Guy Steele wrote: > > Appealing, but it does raise the question of whether, if the programmer uses some sneaky trick such as calling super, this can really be enforced ar compile time on all cases. If it can (after all), then your proposed wording looks good to me. The idea would be to rely on the definition of "assign to" in JLS 16. That uses a heuristic that counts "x =" or "this.x =" (and, in javac, but not currently specified properly, "(this).x", etc.) Stepping back: in general, it's illegal to assign to a final field. There's one exception: inside a constructor, where the field is DU, using an assignment that satisfies the heuristic. In any other case, you get an error. So the language has carved out a small hole permitting assignments, and this rule closes that hole. From GUY.STEELE at ORACLE.COM Wed Apr 29 18:35:22 2020 From: GUY.STEELE at ORACLE.COM (Guy Steele) Date: Wed, 29 Apr 2020 14:35:22 -0400 Subject: Possible records tweak In-Reply-To: <69290043-5496-457A-80A4-21B741C8DAA4@oracle.com> References: <95278C68-D12C-43A1-9140-EC7333A10EC5@oracle.com> <69290043-5496-457A-80A4-21B741C8DAA4@oracle.com> Message-ID: <01BE482E-9125-4E60-B617-935546E765A2@ORACLE.COM> > On Apr 29, 2020, at 2:23 PM, Dan Smith wrote: > > >> On Apr 29, 2020, at 11:28 AM, Guy Steele wrote: >> >> Appealing, but it does raise the question of whether, if the programmer uses some sneaky trick such as calling super, this can really be enforced ar compile time on all cases. If it can (after all), then your proposed wording looks good to me. > > The idea would be to rely on the definition of "assign to" in JLS 16. That uses a heuristic that counts "x =" or "this.x =" (and, in javac, but not currently specified properly, "(this).x", etc.) > > Stepping back: in general, it's illegal to assign to a final field. There's one exception: inside a constructor, where the field is DU, using an assignment that satisfies the heuristic. In any other case, you get an error. > > So the language has carved out a small hole permitting assignments, and this rule closes that hole. That's all very good. But I'm worried about cases where you use some escape catch such as calling super or a static method, passing it "this" as an argument if necessary, and arriving at a place where you need to solve the halting problem to decide at compile time whether the forbidden behavior might occur. I haven't swapped all the current rules back into head enough to decide for myself whether this sort of scenario can actually arise. From john.r.rose at oracle.com Wed Apr 29 18:37:20 2020 From: john.r.rose at oracle.com (John Rose) Date: Wed, 29 Apr 2020 11:37:20 -0700 Subject: Possible records tweak In-Reply-To: References: <1879266571.855821.1587756769720.JavaMail.zimbra@u-pem.fr> <2E79FBAD-CE8E-4042-8E2C-51928B2E23EF@oracle.com> <4b3c473e-1034-cb05-8b88-899ecd4a5936@oracle.com> <1393465323.827868.1588028076413.JavaMail.zimbra@u-pem.fr> Message-ID: On Apr 29, 2020, at 10:07 AM, Gavin Bierman wrote: > > Talking it through with Dan, I think the best thing for the JLS is to avoid issues of `this` etc and simply state: > > It is a compile-time error to assign to the instance fields of the record class in the body of the compact constructor. > > Thoughts? That sounds fine to me. There was at least one reason to be more restrictive, and that was to help users avoid accidentally observing non-initialized fields. So I?ve lost track of this bit: What happens if the compact constructor calls a virtual method on this? (Say it doesn?t explicitly mention ?this?.) What are the states of the fields when the virtual method runs before and after variable assignments in the compact constructor? All default? Some default some not? To really rub it in: class RecordFieldChangeExperiment { record R0(int x) { public R0 { observeField(); } void observeField() { System.out.println(x); } } record R1(int x) { public R1 { observeField(); ++x; // could be x = Math.max(x, 0); //WAS: this.x = x+1; // could be this.x = Math.max(x, 0); observeField(); } void observeField() { System.out.println(x); } } record R2(int x) { public R2 { observeField(); ++x; // could be x = Math.max(x, 0); observeField(); ++x; // could be x = Math.min(x, 1000); observeField(); } void observeField() { System.out.println(x); } } public static void main(String... av) { new R0(100).observeField(); // 100 100 NO? // 0 100 YES? new R1(100).observeField(); // 100 101 101 NO? // 0 101 101 NO? // 0 0 101 YES? new R2(100).observeField(); // 100 101 102 102 NO? // 0 0 102 102 NO? // 0 0 0 102 YES? } } I think the principled answer is to specify that fields have their default values until after the primary constructor exits. This will surprise people who expect to read the fields in virtual methods called from the constructor. Having all the fields always in their default values is, at least, predictable. IMO that makes up for the surprise, because it can be learned. The other compromises (especially making fields mutable multiple times) lead to more variable behaviors, ensuring a long train of future surprises. (And do any of these observations apply to non-compact constructors? I suppose the answer is ?whatever rules apply to non-record classes also apply to records with non-compact canonical constructors?.) ? John From daniel.smith at oracle.com Wed Apr 29 18:47:35 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 29 Apr 2020 12:47:35 -0600 Subject: Possible records tweak In-Reply-To: <01BE482E-9125-4E60-B617-935546E765A2@ORACLE.COM> References: <95278C68-D12C-43A1-9140-EC7333A10EC5@oracle.com> <69290043-5496-457A-80A4-21B741C8DAA4@oracle.com> <01BE482E-9125-4E60-B617-935546E765A2@ORACLE.COM> Message-ID: <00C2B2C3-5D6B-4F9F-BDCD-0FFA6DA11171@oracle.com> > On Apr 29, 2020, at 12:35 PM, Guy Steele wrote: > > > >> On Apr 29, 2020, at 2:23 PM, Dan Smith wrote: >> >> >>> On Apr 29, 2020, at 11:28 AM, Guy Steele wrote: >>> >>> Appealing, but it does raise the question of whether, if the programmer uses some sneaky trick such as calling super, this can really be enforced ar compile time on all cases. If it can (after all), then your proposed wording looks good to me. >> >> The idea would be to rely on the definition of "assign to" in JLS 16. That uses a heuristic that counts "x =" or "this.x =" (and, in javac, but not currently specified properly, "(this).x", etc.) >> >> Stepping back: in general, it's illegal to assign to a final field. There's one exception: inside a constructor, where the field is DU, using an assignment that satisfies the heuristic. In any other case, you get an error. >> >> So the language has carved out a small hole permitting assignments, and this rule closes that hole. > > That's all very good. > > But I'm worried about cases where you use some escape catch such as calling super or a static method, passing it "this" as an argument if necessary, and arriving at a place where you need to solve the halting problem to decide at compile time whether the forbidden behavior might occur. I haven't swapped all the current rules back into head enough to decide for myself whether this sort of scenario can actually arise. I'm leaning heavily on "in general, it's illegal to assign to a final field". If you try to construct your exploit, you will find that when you try to assign to the field, you'll get a "can't assign to final field" error. From brian.goetz at oracle.com Wed Apr 29 18:52:37 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 29 Apr 2020 14:52:37 -0400 Subject: Possible records tweak In-Reply-To: <00C2B2C3-5D6B-4F9F-BDCD-0FFA6DA11171@oracle.com> References: <95278C68-D12C-43A1-9140-EC7333A10EC5@oracle.com> <69290043-5496-457A-80A4-21B741C8DAA4@oracle.com> <01BE482E-9125-4E60-B617-935546E765A2@ORACLE.COM> <00C2B2C3-5D6B-4F9F-BDCD-0FFA6DA11171@oracle.com> Message-ID: <9be2ab08-6f81-f584-6397-d270652e9a9e@oracle.com> I think Guy's concern is that you could cause code to _read_ an uninitialized, final field?? Of course, we have this problem all over the place today, so having it in records does not make things significantly worse. On 4/29/2020 2:47 PM, Dan Smith wrote: > >> On Apr 29, 2020, at 12:35 PM, Guy Steele wrote: >> >> >> >>> On Apr 29, 2020, at 2:23 PM, Dan Smith wrote: >>> >>> >>>> On Apr 29, 2020, at 11:28 AM, Guy Steele wrote: >>>> >>>> Appealing, but it does raise the question of whether, if the programmer uses some sneaky trick such as calling super, this can really be enforced ar compile time on all cases. If it can (after all), then your proposed wording looks good to me. >>> The idea would be to rely on the definition of "assign to" in JLS 16. That uses a heuristic that counts "x =" or "this.x =" (and, in javac, but not currently specified properly, "(this).x", etc.) >>> >>> Stepping back: in general, it's illegal to assign to a final field. There's one exception: inside a constructor, where the field is DU, using an assignment that satisfies the heuristic. In any other case, you get an error. >>> >>> So the language has carved out a small hole permitting assignments, and this rule closes that hole. >> That's all very good. >> >> But I'm worried about cases where you use some escape catch such as calling super or a static method, passing it "this" as an argument if necessary, and arriving at a place where you need to solve the halting problem to decide at compile time whether the forbidden behavior might occur. I haven't swapped all the current rules back into head enough to decide for myself whether this sort of scenario can actually arise. > I'm leaning heavily on "in general, it's illegal to assign to a final field". > > If you try to construct your exploit, you will find that when you try to assign to the field, you'll get a "can't assign to final field" error. From daniel.smith at oracle.com Wed Apr 29 18:55:05 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 29 Apr 2020 12:55:05 -0600 Subject: Possible records tweak In-Reply-To: References: <1879266571.855821.1587756769720.JavaMail.zimbra@u-pem.fr> <2E79FBAD-CE8E-4042-8E2C-51928B2E23EF@oracle.com> <4b3c473e-1034-cb05-8b88-899ecd4a5936@oracle.com> <1393465323.827868.1588028076413.JavaMail.zimbra@u-pem.fr> Message-ID: <7DD6DFBE-256E-47C3-9656-3C97998AE143@oracle.com> > On Apr 29, 2020, at 12:37 PM, John Rose wrote: > > So I?ve lost track of this bit: What happens if the compact constructor > calls a virtual method on this? (Say it doesn?t explicitly mention ?this?.) > What are the states of the fields when the virtual method runs before > and after variable assignments in the compact constructor? None of this discussion has proposed changing the underlying runtime semantics: the instance fields of a record are uninitialized (have their default values) at the start of the compact constructor. Without field assignments in the constructor body, the fields get assigned their "actual" value at the end of the constructor. From GUY.STEELE at ORACLE.COM Wed Apr 29 18:59:27 2020 From: GUY.STEELE at ORACLE.COM (Guy Steele) Date: Wed, 29 Apr 2020 14:59:27 -0400 Subject: Possible records tweak In-Reply-To: <00C2B2C3-5D6B-4F9F-BDCD-0FFA6DA11171@oracle.com> References: <95278C68-D12C-43A1-9140-EC7333A10EC5@oracle.com> <69290043-5496-457A-80A4-21B741C8DAA4@oracle.com> <01BE482E-9125-4E60-B617-935546E765A2@ORACLE.COM> <00C2B2C3-5D6B-4F9F-BDCD-0FFA6DA11171@oracle.com> Message-ID: > On Apr 29, 2020, at 2:47 PM, Dan Smith wrote: > > > >> On Apr 29, 2020, at 12:35 PM, Guy Steele wrote: >> >> >> >>> On Apr 29, 2020, at 2:23 PM, Dan Smith wrote: >>> >>> >>>> On Apr 29, 2020, at 11:28 AM, Guy Steele wrote: >>>> >>>> Appealing, but it does raise the question of whether, if the programmer uses some sneaky trick such as calling super, this can really be enforced ar compile time on all cases. If it can (after all), then your proposed wording looks good to me. >>> >>> The idea would be to rely on the definition of "assign to" in JLS 16. That uses a heuristic that counts "x =" or "this.x =" (and, in javac, but not currently specified properly, "(this).x", etc.) >>> >>> Stepping back: in general, it's illegal to assign to a final field. There's one exception: inside a constructor, where the field is DU, using an assignment that satisfies the heuristic. In any other case, you get an error. >>> >>> So the language has carved out a small hole permitting assignments, and this rule closes that hole. >> >> That's all very good. >> >> But I'm worried about cases where you use some escape catch such as calling super or a static method, passing it "this" as an argument if necessary, and arriving at a place where you need to solve the halting problem to decide at compile time whether the forbidden behavior might occur. I haven't swapped all the current rules back into head enough to decide for myself whether this sort of scenario can actually arise. > > I'm leaning heavily on "in general, it's illegal to assign to a final field". > > If you try to construct your exploit, you will find that when you try to assign to the field, you'll get a "can't assign to final field" error. I agree, but when is "when you try to assign" detected in this situation? If the answer is "run time", then we have a counterexample to Gavin's proposed promise that "It is a COMPILE-TIME error to assign . . ." (emphasis added). That's all I am worried about: is it truly a compile-time error in ALL cases, or an error that is detected at compile time in all the "obvious" cases but must sometimes be detected at run time in obscure cases? From daniel.smith at oracle.com Wed Apr 29 19:13:28 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 29 Apr 2020 13:13:28 -0600 Subject: Possible records tweak In-Reply-To: References: <95278C68-D12C-43A1-9140-EC7333A10EC5@oracle.com> <69290043-5496-457A-80A4-21B741C8DAA4@oracle.com> <01BE482E-9125-4E60-B617-935546E765A2@ORACLE.COM> <00C2B2C3-5D6B-4F9F-BDCD-0FFA6DA11171@oracle.com> Message-ID: > On Apr 29, 2020, at 12:59 PM, Guy Steele wrote: > > > >> On Apr 29, 2020, at 2:47 PM, Dan Smith wrote: >> >> >> >>> On Apr 29, 2020, at 12:35 PM, Guy Steele wrote: >>> >>> >>> >>>> On Apr 29, 2020, at 2:23 PM, Dan Smith wrote: >>>> >>>> >>>>> On Apr 29, 2020, at 11:28 AM, Guy Steele wrote: >>>>> >>>>> Appealing, but it does raise the question of whether, if the programmer uses some sneaky trick such as calling super, this can really be enforced ar compile time on all cases. If it can (after all), then your proposed wording looks good to me. >>>> >>>> The idea would be to rely on the definition of "assign to" in JLS 16. That uses a heuristic that counts "x =" or "this.x =" (and, in javac, but not currently specified properly, "(this).x", etc.) >>>> >>>> Stepping back: in general, it's illegal to assign to a final field. There's one exception: inside a constructor, where the field is DU, using an assignment that satisfies the heuristic. In any other case, you get an error. >>>> >>>> So the language has carved out a small hole permitting assignments, and this rule closes that hole. >>> >>> That's all very good. >>> >>> But I'm worried about cases where you use some escape catch such as calling super or a static method, passing it "this" as an argument if necessary, and arriving at a place where you need to solve the halting problem to decide at compile time whether the forbidden behavior might occur. I haven't swapped all the current rules back into head enough to decide for myself whether this sort of scenario can actually arise. >> >> I'm leaning heavily on "in general, it's illegal to assign to a final field". >> >> If you try to construct your exploit, you will find that when you try to assign to the field, you'll get a "can't assign to final field" error. > > I agree, but when is "when you try to assign" detected in this situation? If the answer is "run time", then we have a counterexample to Gavin's proposed promise that "It is a COMPILE-TIME error to assign . . ." (emphasis added). That's all I am worried about: is it truly a compile-time error in ALL cases, or an error that is detected at compile time in all the "obvious" cases but must sometimes be detected at run time in obscure cases? This is all compile-time. The status quo in Java is that to assign to a final field at compile time, you need to do all of the following: - Be in a constructor for the same class - Reference the field using an unqualified name or qualified by 'this' - Use a simple assignment expression (fieldref = expr) - Place the assignment at a point where the field is DU (has not yet been assigned to through any local control flow path) All other references to the field as an lvalue will be compile-time errors. From guy.steele at oracle.com Wed Apr 29 19:44:14 2020 From: guy.steele at oracle.com (Guy Steele) Date: Wed, 29 Apr 2020 15:44:14 -0400 Subject: Possible records tweak In-Reply-To: References: <95278C68-D12C-43A1-9140-EC7333A10EC5@oracle.com> <69290043-5496-457A-80A4-21B741C8DAA4@oracle.com> <01BE482E-9125-4E60-B617-935546E765A2@ORACLE.COM> <00C2B2C3-5D6B-4F9F-BDCD-0FFA6DA11171@oracle.com> Message-ID: <8F3784AC-3487-4A03-9347-AF6AF7E15A08@oracle.com> > On Apr 29, 2020, at 3:13 PM, Dan Smith wrote: > > . . . > This is all compile-time. > > The status quo in Java is that to assign to a final field at compile time, you need to do all of the following: > - Be in a constructor for the same class > - Reference the field using an unqualified name or qualified by 'this' > - Use a simple assignment expression (fieldref = expr) > - Place the assignment at a point where the field is DU (has not yet been assigned to through any local control flow path) > > All other references to the field as an lvalue will be compile-time errors. Okay, thanks. (Clearly you DO have it all swapped into your head!) From john.r.rose at oracle.com Wed Apr 29 20:46:13 2020 From: john.r.rose at oracle.com (John Rose) Date: Wed, 29 Apr 2020 13:46:13 -0700 Subject: Possible records tweak In-Reply-To: <7DD6DFBE-256E-47C3-9656-3C97998AE143@oracle.com> References: <1879266571.855821.1587756769720.JavaMail.zimbra@u-pem.fr> <2E79FBAD-CE8E-4042-8E2C-51928B2E23EF@oracle.com> <4b3c473e-1034-cb05-8b88-899ecd4a5936@oracle.com> <1393465323.827868.1588028076413.JavaMail.zimbra@u-pem.fr> <7DD6DFBE-256E-47C3-9656-3C97998AE143@oracle.com> Message-ID: On Apr 29, 2020, at 11:55 AM, Dan Smith wrote: > >> On Apr 29, 2020, at 12:37 PM, John Rose wrote: >> >> So I?ve lost track of this bit: What happens if the compact constructor >> calls a virtual method on this? (Say it doesn?t explicitly mention ?this?.) >> What are the states of the fields when the virtual method runs before >> and after variable assignments in the compact constructor? > > None of this discussion has proposed changing the underlying runtime semantics: the instance fields of a record are uninitialized (have their default values) at the start of the compact constructor. Without field assignments in the constructor body, the fields get assigned their "actual" value at the end of the constructor. Thanks; I thought I heard something like that floated at one point, and am glad that, if so, it floated away.