From zjx001202 at gmail.com Thu Dec 1 11:46:10 2022 From: zjx001202 at gmail.com (Glavo) Date: Thu, 1 Dec 2022 19:46:10 +0800 Subject: Replace the primitive class with a more general null safety? In-Reply-To: <82E11A2C-9F71-46E5-9A6E-56483C11FFF6@oracle.com> References: <82E11A2C-9F71-46E5-9A6E-56483C11FFF6@oracle.com> Message-ID: I haven't read the description of binary compatibility between Q type and L type anywhere. I have made several attempts on LW4, but the attempt to change the L type to Q type in the API method signature always causes NoSuchMethodError to be thrown when calling it. Of course, I know that the implementation of the primitive class is still incomplete. However, I haven't seen any description of the migration scheme so far, so I'm worried about it. I'm very surprised that you have questions about Valhalla breaking compatibility. Am I misunderstanding JEP draft 8261529? What I've learned from reading this draft is that Universal Generics will cause a large portion of existing code to have null pollution warnings and be completely broken in the future. Also, parts of the code require the use of new language features to eliminate null pollution. This is a big obstacle to migration. When we talk about migration, a lot of times it's not Java 8 to Java 25 (assuming we can see Valhalla in Java 25), and often what we need is to migrate from Java 8 to Java 8: We want the migrated code to continue to compile on Java 8, but at the same time to fit Java 25 as perfectly as possible. This is a fairly common requirement, since most third-party libraries distributed through maven repositories want to work on as many Java versions as possible. I know that multi-release jars can help us achieve this, but it is not easy to use and will cause too much trouble, so many projects try to avoid this as much as possible. Therefore, requiring users to use new language features, I think, is extremely disruptive. Let's get back to Universal Generics. It breaks so much generic code to introduce the concept of non-nullable types. This seems unavoidable, because the difference between value types and reference types exists objectively. But I'm sad that it's causing so much trouble just for primitive classes. That's why I sent this email. If my previous words mean "it's easy to make non-nullable types", I apologize for my inaccurate expression. I know this is not an easy thing to implement, such as default values and thread safety issues are issues to consider. I'm glad to know you've been experimenting, but please forgive my dissatisfaction with the current draft of primitive classes and universal generics, which I think is causing too much confusion and disruption. On Mon, Nov 28, 2022 at 10:04 PM Brian Goetz wrote: > There?s a lot of good thoughts in here (and some wishful thinking too), > but most of it is well-covered ground. > > > After I read the JEP and related documents of Project Valhalla, I > doubted the necessity of the existence of primitive classes. > > We have been asking ourselves this question every day. As the old saying > goes, things should be as simple as possible, but no simpler. Past > attempts to simplify them away did not succeed, but that doesn?t mean we > don?t keep trying. > > > It is impossible to migrate from identity class to primitive class, > which will break binary compatibility. > > This is not entirely true. There are constraints involved, and at the > appropriate time we will publish a migration guide, but it is not the case > that you can?t migrate from B1 to either B2 or B3. But this isn?t your > real point. > > > At the same time, in order to support non null types, the universal > generics draft also greatly undermines compatibility. > > This is a pretty strong statement, and I?ve certainly not seen evidence > for this. But, again this is tangential to your point (and likely > overstated to make your point sound better, though there?s no need for > that), so let?s get to your point. > > > Although the value class is more compatible, it may not be inlined when > it is a field or array member. > > You?re confronting the essential physics of the problem; rejigging the > packaging won?t change this. But again, not your point. > > > I don't like the current draft very much. In particular, I am very > dissatisfied with the huge cost of providing null safety only for primitive > types. > > The second sentence is the best part of your mail, pointing out that we?re > incurring a lot of complexity whose benefit only accrues to some of the > types. This is indeed suboptimal. > > > Now I have an idea: it's time to provide generic non null types for Java. > > You say this like it is a new idea! We have explored this in *literally > every iteration of Valhalla.*. At each iteration, we have asked ourselves > whether the key distinctions could be replaced by general-purpose > ?emotional? (bang and question) type operators, or whether even that > notation is helpful or harmful to user intuition. At each point, the answer > has been ?almost, but not quite.? And in each iteration, we?ve continued > to chip away at the difference, and ask ourselves the question again. The > current iteration is still a ?no?, but it is still a matter of active > exploration, because *of course* Java developers would prefer this to the > current val/ref scheme. (Or at least some of them think they would.). > > > Since the universal generics draft has decided to break compatibility, > we can go further > > I don?t know what you mean by ?decided to break compatibility?, but even > if that were true (and yes, there are small incompatibilities, such as > ?what happens when you lock on Integer?), the rest would not remotely > follow. > > > and provide universal non null types in Java, just like Kotlin. > > > Haha, ?just?. No. Even if we were to find our way to non-null types, it > would not be ?just? like anything. > > > If we do, we no longer need the primitive class. It is enough to mark > the value class as non null. > > Actually, no. This is not the only difference between primitives and > references. If it were, we would have adopted this ?solution? a long time > ago. > > As I said, there?s a lot of good thoughts in here, and the relationship > between values and non-rulllable types has been a constant question along > this journey, and still is. We may be getting closer to having these > concepts meet up. But it is not ?just? anything. Stay tuned. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Dec 1 14:50:39 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 1 Dec 2022 09:50:39 -0500 Subject: Replace the primitive class with a more general null safety? In-Reply-To: References: <82E11A2C-9F71-46E5-9A6E-56483C11FFF6@oracle.com> Message-ID: On 12/1/2022 6:46 AM, Glavo wrote: > I haven't read the description of binary compatibility between Q type > and L type anywhere. > I have made several attempts on LW4, but the attempt to change the L > type to Q type in the API method signature always causes > NoSuchMethodError to be thrown when calling it. > Of course, I know that the implementation of the primitive class is > still incomplete. > However, I haven't seen any description of the migration scheme so > far, so I'm worried about it. In the current framing, if you have a class "Optional" which is currently an identity class (therefore translated to LOptional), and you migrate it to a value / primitive class, the language-level type "Optional" still translates to LOptional (though without identity, so it can be more heavily optimized.)? Existing APIs that are full of Optional will still work fine, because all source utterances of "Optional" and all binary utterances of "LOptional" still mean what they meant before (minus identity.)? This is source- and binary- compatible. If you go and change all your Optional to Optional.val in your APIs (e.g., public methods), now you've made a binary-incompatible change, which might be a problem in the face of separate compilation.? (If you do this for internal APIs, such as a private field, no problem, because you're recompiling all the code that depends on the translation.)? But this is no different than *you* changing an API from `m(Integer)` to `m(int)`, so I don't see why you'd expect that was a compatible change. Simply migrating a class from identity to value will not break linkage of uses of that type. > I'm very surprised that you have questions about Valhalla breaking > compatibility.?Am I misunderstanding JEP draft 8261529? > What I've learned from reading this draft is that Universal Generics > will cause a large portion of existing code to have null pollution > warnings and be completely broken in the future. Hyperbolic phrases like "completely broken" are not helpful (even worse when they are extrapolations from incorrect assumptions.) > When we talk about migration, a lot of times it's not Java 8 to Java > 25 (assuming we can see Valhalla in Java 25), and often what we need > is to migrate from Java 8 to Java 8: > We want the migrated code to continue to compile on Java 8, but at the > same time to fit Java 25 as perfectly as possible. > This is a fairly common requirement, since most third-party libraries > distributed through maven repositories want to work on as many Java > versions as possible. Do you really think we need to be lectured on the value migration compatibility? From zjx001202 at gmail.com Thu Dec 1 17:50:43 2022 From: zjx001202 at gmail.com (Glavo) Date: Fri, 2 Dec 2022 01:50:43 +0800 Subject: Replace the primitive class with a more general null safety? In-Reply-To: References: <82E11A2C-9F71-46E5-9A6E-56483C11FFF6@oracle.com> Message-ID: Optional.val? Do you mean that the reference-favoring primitive class still exists? I know it used to exist in JEP 401, but after the value class JEP draft came out, the description of reference-favoring primitive class was removed. This makes me think that Valhalla wants the identity class to be migrated to the value class and not allowed to migrate to the primitive class. Is my understanding wrong? I'm sorry for the misuse of words. I can't understand these English words accurately, because my English is very poor, so I use Google Translate to write the email. I mean, the code containing null polling will be unusable (the original words in the JEP draft). If my understanding is correct, it means that it will destroy the code that has not solved the null polling, right? For migration related matters, I don't think you need me to introduce these, but I just remind you that although they are very basic, they often don't get the attention they deserve, thus becoming an obstacle to migration. This has happened many times. Like Jigsaw and JavaFX 9, they bring extra difficulties to all versions that support Java 8+at the same time. Even a widely used library like Jackson will make mistakes. Some of these problems could have been avoided. Then, I apologize again for my English level. My mother tongue is not English. I can only read English slowly. Writing is difficult for me. Therefore, I need to use translation software, which may cause deviation in my meaning. I apologize for the inconvenience this has caused you. On Thu, Dec 1, 2022 at 10:50 PM Brian Goetz wrote: > > > On 12/1/2022 6:46 AM, Glavo wrote: > > I haven't read the description of binary compatibility between Q type > > and L type anywhere. > > I have made several attempts on LW4, but the attempt to change the L > > type to Q type in the API method signature always causes > > NoSuchMethodError to be thrown when calling it. > > Of course, I know that the implementation of the primitive class is > > still incomplete. > > However, I haven't seen any description of the migration scheme so > > far, so I'm worried about it. > > In the current framing, if you have a class "Optional" which is > currently an identity class (therefore translated to LOptional), and you > migrate it to a value / primitive class, the language-level type > "Optional" still translates to LOptional (though without identity, so it > can be more heavily optimized.) Existing APIs that are full of Optional > will still work fine, because all source utterances of "Optional" and > all binary utterances of "LOptional" still mean what they meant before > (minus identity.) This is source- and binary- compatible. > > If you go and change all your Optional to Optional.val in your APIs > (e.g., public methods), now you've made a binary-incompatible change, > which might be a problem in the face of separate compilation. (If you > do this for internal APIs, such as a private field, no problem, because > you're recompiling all the code that depends on the translation.) But > this is no different than *you* changing an API from `m(Integer)` to > `m(int)`, so I don't see why you'd expect that was a compatible change. > > Simply migrating a class from identity to value will not break linkage > of uses of that type. > > > I'm very surprised that you have questions about Valhalla breaking > > compatibility. Am I misunderstanding JEP draft 8261529? > > What I've learned from reading this draft is that Universal Generics > > will cause a large portion of existing code to have null pollution > > warnings and be completely broken in the future. > > Hyperbolic phrases like "completely broken" are not helpful (even worse > when they are extrapolations from incorrect assumptions.) > > > When we talk about migration, a lot of times it's not Java 8 to Java > > 25 (assuming we can see Valhalla in Java 25), and often what we need > > is to migrate from Java 8 to Java 8: > > We want the migrated code to continue to compile on Java 8, but at the > > same time to fit Java 25 as perfectly as possible. > > This is a fairly common requirement, since most third-party libraries > > distributed through maven repositories want to work on as many Java > > versions as possible. > > Do you really think we need to be lectured on the value migration > compatibility? > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Dec 1 18:34:27 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 1 Dec 2022 13:34:27 -0500 Subject: Replace the primitive class with a more general null safety? In-Reply-To: References: <82E11A2C-9F71-46E5-9A6E-56483C11FFF6@oracle.com> Message-ID: On 12/1/2022 12:50 PM, Glavo wrote: > Optional.val??Do you mean that the reference-favoring primitive class > still exists? Not quite. The names in the model have changed over the iterations, but the concepts basically have stayed the same.? Some of the classes we want to write with Valhalla are just "objects without identity", but are not really "like primitives"; LocalDateTime is an example of this.? Others are much closer to pure numerics, such as Complex or HalfFloat, and are much more "like primitives".? A key difference is that one has a natural zero and can therefore be used uninitialized (like int fields), whereas the other does not and therefore must be handled through a reference, whose initial value is null.? (Though we can still obtain some substantial optimizations even for these reference types.)? We've been calling these "bucket 2" (objects without identity) and "bucket 3" (primitive-like types), which in the more recent model were described with value classes and primitive classes. Regardless of the stacking, a primitive class gives rise to TWO types, which I'll call P.val and P.ref here.? One is a reference type, the other is a value type.? These are analogous to int and Integer, and they have the usual array of differences: nullability, default values, direct vs indirect, tearing under race, etc. A pretty clear decision that has emerged is that for both ??? value class V { } and ??? primitive class P { } the language-level types V and P are *reference types* (so P is an alias for P.ref in the ref/val model.)? I won't rehash the arguments in favor and against here, but this means any "unadorned" type is a reference type, regardless of whether it is an identity, value, or primitive class.? In this way, all primitive/value classes are "ref favoring", and there's no explicit way to flip this, so no need for the ref-default/val-default syntax of a previous round.? One of the benefits of this is that migration is smoother because migrating an identity class to either of the new buckets is source- and binary-compatible. > For migration related matters, I don't think you need me to introduce > these, but I just remind you that although they are very basic, > they often don't get the attention they deserve, thus becoming an > obstacle to migration. One of the reasons Valhalla is taking so long is we take migration compatibility very seriously. From forax at univ-mlv.fr Sat Dec 3 08:19:33 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 3 Dec 2022 09:19:33 +0100 (CET) Subject: Replace the primitive class with a more general null safety? In-Reply-To: References: Message-ID: <104664647.58531181.1670055573849.JavaMail.zimbra@u-pem.fr> > From: "Glavo" > To: "valhalla-dev" > Sent: Monday, November 28, 2022 11:10:18 AM > Subject: Replace the primitive class with a more general null safety? > After I read the JEP and related documents of Project Valhalla, I doubted the > necessity of the existence of primitive classes. > It is impossible to migrate from identity class to primitive class, which will > break binary compatibility. > This is a big obstacle for third-party libraries to support Valhalla. [...] yes, it's something we are working on. Obviously, it will never be fully backward compatible because the default value of a value class and a primitive class are different (null and zeroes respectively). > At the same time, in order to support non null types, the universal generics > draft also greatly undermines compatibility. ?? > Although the value class is more compatible, it may not be inlined when it is a > field or array member. > Therefore, sometimes value objects are repeatedly boxed, which may have worse > performance than non value classes. The VM goes into great length to avoid that, by re-using the box. If the box escapes the inlining horizon, yes, you will see the allocation, but it's the same issue with a lot of the other optimizations today, outside of the inline horizon the JIT is almost blind. > Providing both value class and primitive class may also make it more difficult > for users to choose. Yes, we tried really hard, for several years in fact, to have only one kind of value classes. But the physics of those two is different enough that trying to unify them or disguise one as the other is a worst than exposing that choice to the user. > I don't like the current draft very much. In particular, I am very dissatisfied > with the huge cost of providing null safety only for primitive types. Java null safety is a separate discussion ... [...] > If we do, we no longer need the primitive class. It is enough to mark the value > class as non null. > The problem that prevents us from migrating to the primitive class no longer > exists, because we don't need the primitive class and Q type anymore. I think you are mixing the syntax and the semantics here. The is an early access build that you can test, so you will get a better idea of how things work [ https://jdk.java.net/valhalla/ | https://jdk.java.net/valhalla/ ] By default, you only have value class [1] and if you want primitive class [2] you have to enable it at compile time and runtime. We know that primitive classes as describe in the JEP [2], do not fully integrate with the rest of the ecosystem that's a work in progress. regards, R?mi [1] [ https://openjdk.org/jeps/8277163 | https://openjdk.org/jeps/8277163 ] [2] [ https://openjdk.org/jeps/401 | https://openjdk.org/jeps/401 ] -------------- next part -------------- An HTML attachment was scrubbed... URL: From zjx001202 at gmail.com Sat Dec 3 09:38:56 2022 From: zjx001202 at gmail.com (Glavo) Date: Sat, 3 Dec 2022 17:38:56 +0800 Subject: Replace the primitive class with a more general null safety? In-Reply-To: References: <82E11A2C-9F71-46E5-9A6E-56483C11FFF6@oracle.com> Message-ID: > > the language-level types V and P are *reference types* (so P is an alias > for P.ref in the ref/val model.) I won't rehash the arguments in favor > and against here, but this means any "unadorned" type is a reference > type, regardless of whether it is an identity, value, or primitive > class. In this way, all primitive/value classes are "ref favoring", and > there's no explicit way to flip this, so no need for the > ref-default/val-default syntax of a previous round. One of the benefits > of this is that migration is smoother because migrating an identity > class to either of the new buckets is source- and binary-compatible. > Well done! This is the best news I have heard recently. It sounds like the information I'm getting from the JEP docs is outdated, so there's a misunderstanding. Do you have plans to update JEP 401? On Fri, Dec 2, 2022 at 3:11 AM Brian Goetz wrote: > > > On 12/1/2022 12:50 PM, Glavo wrote: > > Optional.val? Do you mean that the reference-favoring primitive class > > still exists? > > Not quite. > > The names in the model have changed over the iterations, but the > concepts basically have stayed the same. Some of the classes we want to > write with Valhalla are just "objects without identity", but are not > really "like primitives"; LocalDateTime is an example of this. Others > are much closer to pure numerics, such as Complex or HalfFloat, and are > much more "like primitives". A key difference is that one has a natural > zero and can therefore be used uninitialized (like int fields), whereas > the other does not and therefore must be handled through a reference, > whose initial value is null. (Though we can still obtain some > substantial optimizations even for these reference types.) We've been > calling these "bucket 2" (objects without identity) and "bucket 3" > (primitive-like types), which in the more recent model were described > with value classes and primitive classes. > > Regardless of the stacking, a primitive class gives rise to TWO types, > which I'll call P.val and P.ref here. One is a reference type, the > other is a value type. These are analogous to int and Integer, and they > have the usual array of differences: nullability, default values, direct > vs indirect, tearing under race, etc. > > A pretty clear decision that has emerged is that for both > > value class V { } > and > primitive class P { } > > the language-level types V and P are *reference types* (so P is an alias > for P.ref in the ref/val model.) I won't rehash the arguments in favor > and against here, but this means any "unadorned" type is a reference > type, regardless of whether it is an identity, value, or primitive > class. In this way, all primitive/value classes are "ref favoring", and > there's no explicit way to flip this, so no need for the > ref-default/val-default syntax of a previous round. One of the benefits > of this is that migration is smoother because migrating an identity > class to either of the new buckets is source- and binary-compatible. > > > For migration related matters, I don't think you need me to introduce > > these, but I just remind you that although they are very basic, > > they often don't get the attention they deserve, thus becoming an > > obstacle to migration. > > One of the reasons Valhalla is taking so long is we take migration > compatibility very seriously. > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Dec 3 09:45:06 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 3 Dec 2022 10:45:06 +0100 (CET) Subject: Replace the primitive class with a more general null safety? In-Reply-To: References: <82E11A2C-9F71-46E5-9A6E-56483C11FFF6@oracle.com> Message-ID: <993688111.58549638.1670060706823.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Brian Goetz" > To: "Glavo" > Cc: "valhalla-dev" > Sent: Thursday, December 1, 2022 7:34:27 PM > Subject: Re: Replace the primitive class with a more general null safety? > On 12/1/2022 12:50 PM, Glavo wrote: >> Optional.val??Do you mean that the reference-favoring primitive class >> still exists? > > Not quite. > > The names in the model have changed over the iterations, but the > concepts basically have stayed the same.? Some of the classes we want to > write with Valhalla are just "objects without identity", but are not > really "like primitives"; LocalDateTime is an example of this.? Others > are much closer to pure numerics, such as Complex or HalfFloat, and are > much more "like primitives".? A key difference is that one has a natural > zero and can therefore be used uninitialized (like int fields), whereas > the other does not and therefore must be handled through a reference, > whose initial value is null.? (Though we can still obtain some > substantial optimizations even for these reference types.)? We've been > calling these "bucket 2" (objects without identity) and "bucket 3" > (primitive-like types), which in the more recent model were described > with value classes and primitive classes. > > Regardless of the stacking, a primitive class gives rise to TWO types, > which I'll call P.val and P.ref here.? One is a reference type, the > other is a value type.? These are analogous to int and Integer, and they > have the usual array of differences: nullability, default values, direct > vs indirect, tearing under race, etc. > > A pretty clear decision that has emerged is that for both > > ??? value class V { } > and > ??? primitive class P { } > > the language-level types V and P are *reference types* (so P is an alias > for P.ref in the ref/val model.)? I won't rehash the arguments in favor > and against here, but this means any "unadorned" type is a reference > type, regardless of whether it is an identity, value, or primitive > class.? In this way, all primitive/value classes are "ref favoring", and > there's no explicit way to flip this, so no need for the > ref-default/val-default syntax of a previous round.? One of the benefits > of this is that migration is smoother because migrating an identity > class to either of the new buckets is source- and binary-compatible. yes, it something that can be seen clearly with the current prototype when trying to use a primitive class (old style val-default) with generics. You can not declare a List

, you have to use a List. Primitive classes being ref-default solve that until we get proper specialized generics. R?mi From brian.goetz at oracle.com Sat Dec 3 17:09:27 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 3 Dec 2022 12:09:27 -0500 Subject: Replace the primitive class with a more general null safety? In-Reply-To: References: <82E11A2C-9F71-46E5-9A6E-56483C11FFF6@oracle.com> Message-ID: <3b4fdb82-77c8-7ad2-395b-fad708c6cf83@oracle.com> Yes, but we want to arrive at a consistent point before updating the docs, so naturally the design process skates out ahead of the written collateral. On 12/3/2022 4:38 AM, Glavo wrote: > It sounds like the information I'm getting from the JEP docs is > outdated, so there's a misunderstanding. > Do you have plans to update JEP 401? -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Sun Dec 4 09:06:49 2022 From: redio.development at gmail.com (Red IO) Date: Sun, 4 Dec 2022 10:06:49 +0100 Subject: Providing runtime type information without changing type erasure Message-ID: I was once again writing some generic Java class and suddenly I stand there again, I could not get the type of T. I?m sure this scenario sound familiar to every Java developer out there. If you first stumble across this problem type erasure comes up as the culprit: ?Type T is not there at runtime it is replaced with Object? There are many solutions to this problem some hacky some require braking change to the generic system. Now my idea was why don?t we trace back the types at compile time? I mean every generic class is constructed somewhere in 6 possible ways: Foo foo = new Foo<>(); Foo foo2 = new Foo<>(); Foo foo3 = new Foo(); Foo foo4 = new Foo<>(); Foo foo5 = new Foo<>(); Foo foo6 = new Foo<>(); In case 1 we are already at our destination. The code the compiler gets contains the information we want. Why don?t we attach the type written in plain sight to the generic constructor? Like an implicit Foo foo = new Foo<>(java.lang.String.class); (which explicitly is a current way of solving this. Which is in my opinion pretty ugly.) It would be syntactic sugar for the syntax above which spares the developers of repeating themselves. (just like <> does) If the compiler would just use the information he is erasing to fill it into the constructor. The only thing changing would be that the constructor would take 1 additional hidden argument and since the argument is filled at compile time it would not brake any code. In case 2 we are in some generic context like a nested class or a generic method. In both cases the actual type of T will be available. Such situations might require multiple passes to solve all dependencies. Then we can proceed like in 1. In case 3 we are dealing with legacy code and the type is always object. (Foo foo = new Foo(java.lang.Object)) In case 4 we are dealing with an open type bound. The type is not constrained so we can only assume Object (Foo foo = new Foo<>(java.lang.Object)) In case 5 we have a upper constrained type bound. We can assume any Object passing that bound Is a subclass of String (Foo foo = new Foo<>(java.lang.String)) In case 6 we have a lower constrained type bound. The Object passing this type bound could be Object. (Foo foo = new Foo<>(java.lang.Object)) We would store the types at constructor or Method Invocation as arguments and then map them to the accessor T.class. This would allow type checking of T at runtime by tracing the real type at compile time. Since the type of T.class would be Class or Class we would not need to create a method or a class for each different version of the method or class like other languages do. I?m not quite sure how reflection is implemented but I?m sure there is a central invocation where the compiler could add the T.class argument. Of course we would need a way for reflection to specify the type T for this to work. We would simply need a method that is designed for generic classes like ? Constructor getGenericConstructor(Class[] typeParameterTypes, Class? parameterTypes)? (could have a better signature). If the ?getConstructor? is used on a generic class there would be a warning and the parameters would be set to Object (resulting in a raw object like any Objects generated by reflection currently are). Risks: It would require the addition of n fields to any generic class or method where n stands for the number of generic arguments, which could result in performance and memory issues when adding this many new fields to the heap and stack. This issue could be reduced by only adding this mechanism when T.class or instanceof T are actually used in the context and would be skiped if the field is not used in the first place. Resulting in unchanged field count in all class files if the feature is used nowhere. Another rist would be the obvious change in class files, since the hidden arguments need to be stored at compile time near the invoke of the method/constructor. This is just an idea based on my knowledge on generics in java. Please feel free to correct any misconceptions in this idea and tell me if I missed something. Great regards RedIODev -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Dec 4 18:43:53 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 4 Dec 2022 13:43:53 -0500 Subject: Providing runtime type information without changing type erasure In-Reply-To: References: Message-ID: <2928c46b-f4ff-b5cb-897f-6ea2f5d1b297@oracle.com> You posted this earlier, but I was kind of at a loss for how to respond, because it reads to me like "why don't we 'just' reify generics", as if we were just waiting for someone to come up with the idea of reification.? I think you are underestimating the scope of what you are suggesting by several orders of magnitude. Reification is invasive.? You would have to plumb new data paths in method calling conventions to pass type witnesses through generic methods (so when someone says `new Foo`, where T is a method type variable, you know what T is), and through object layout in the heap (so when someone asks a List what it is a List of, it can find out), and through reflection, and through serialization, and ...? And even if you did that, you might discover that some runtime typechecking that you'd like to do is undecideable (see e.g. https://www.researchgate.net/publication/303969346_On_Decidability_of_Nominal_Subtyping_with_Variance). The mechanics of reification are well enough understood; see e.g. the Kennedy and Syme paper of how they did it in .NET (https://www.microsoft.com/en-us/research/publication/design-and-implementation-of-generics-for-the-net-common-language-runtime/), or Maurizio Cimadamore's PhD thesis.? But in addition to the substantial re-architecting of the VM that is required here, you also have to face the problem of migration compatibility.? A key requirement when we did generics is that libraries and clients could be generified independently; if you generify a library, its clients can upgrade to generics now, later, or never, at their choice.? When .NET chose heterogeneous translation over homogeneous, they had to write new collection libraries.? (That was OK because they had relatively few users at the time; Java does not have this luxury.) This is not a "why don't you just" problem. Several thousands of expert-hours have already gone into researching how these things could be achieved; the best capturing so far is John's discussion of the "Parametric VM", which is still in the "working out the ideas" stage.? You can read the current state here: http://cr.openjdk.java.net/~jrose/values/parametric-vm.pdf There's a ton there, and a ton left to go. On 12/4/2022 4:06 AM, Red IO wrote: > > I was once again writing some generic Java class and suddenly I stand > there again, I could not get the type of T. > > I?m sure this scenario sound familiar to every Java developer out there. > > If you first stumble across this problem type erasure comes up as the > culprit: ?Type T is not there at runtime it is replaced with Object? > > There are many solutions to this problem some hacky some require > braking change to the generic system. > > Now my idea was why don?t we trace back the types at compile time? I > mean every generic class is constructed somewhere in 6 possible ways: > > Foo foo = new Foo<>(); > Foo foo2 = new Foo<>(); > Foo foo3 = new Foo(); > Foo foo4 = new Foo<>(); > Foo foo5 = new Foo<>(); > Foo foo6 = new Foo<>(); > In case 1 we are already at our destination. The code the compiler > gets contains the information we want. Why don?t we attach the type > written in plain sight to the generic constructor? > > Like an implicit Foo foo = new Foo<>(java.lang.String.class); > (which explicitly is a current way of solving this. Which is in my > opinion pretty ugly.) > > It would be syntactic sugar for the syntax above which spares the > developers of repeating themselves. (just like <> does) > > If the compiler would just use the information he is erasing to fill > it into the constructor. The only thing changing would be that the > constructor would take 1 additional hidden argument and since the > argument is filled at compile time it would not brake any code. > > In case 2 we are in some generic context like a nested class or a > generic method. In both cases the actual type of T will be available. > Such situations might require multiple passes to solve all > dependencies. Then we can proceed like in 1. > > In case 3 we are dealing with legacy code and the type is always > object. (Foo foo = new Foo(java.lang.Object)) > > In case 4 we are dealing with an open type bound. The type is not > constrained so we can only assume Object (Foo foo = new > Foo<>(java.lang.Object)) > > In case 5 we have a upper constrained type bound. We can assume any > Object passing that bound Is a subclass of String (Foo String> foo = new Foo<>(java.lang.String)) > > In case 6 we have a lower constrained type bound. The Object passing > this type bound could be Object. (Foo foo = new > Foo<>(java.lang.Object)) > > > We would store the types at constructor or Method Invocation as > arguments and then map them to the accessor T.class. This would allow > type checking of T at runtime by tracing the real type at compile time. > > Since the type of T.class would be Class or Class we would not > need to create a method or a class for each different version of the > method or class like other languages do. > > I?m not quite sure how reflection is implemented but I?m sure there is > a central invocation where the compiler could add the T.class > argument. Of course we would need a way for reflection to specify the > type T for this to work. We would simply need a method that is > designed for generic classes like ? Constructor > getGenericConstructor(Class[] typeParameterTypes, Class? > parameterTypes)? (could have a better signature). > > If the ?getConstructor? is used on a generic class there would be a > warning and the parameters would be set to Object (resulting in a raw > object like any Objects generated by reflection currently are). > > > Risks: > > It would require the addition of n fields to any generic class or > method where n stands for the number of generic arguments, which could > result in performance and memory issues when adding this many new > fields to the heap and stack. This issue could be reduced by only > adding this mechanism when T.class or instanceof T are actually used > in the context and would be skiped if the field is not used in the > first place. Resulting in unchanged field count in all class files if > the feature is used nowhere. > > Another rist would be the obvious change in class files, since the > hidden arguments need to be stored at compile time near the invoke of > the method/constructor. > > > This is just an idea based on my knowledge on generics in java. > > Please feel free to correct any misconceptions in this idea and tell > me if I missed something. > > > Great regards > > RedIODev > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From zjx001202 at gmail.com Tue Dec 13 08:50:54 2022 From: zjx001202 at gmail.com (Glavo) Date: Tue, 13 Dec 2022 16:50:54 +0800 Subject: Replace the primitive class with a more general null safety? In-Reply-To: <3b4fdb82-77c8-7ad2-395b-fad708c6cf83@oracle.com> References: <82E11A2C-9F71-46E5-9A6E-56483C11FFF6@oracle.com> <3b4fdb82-77c8-7ad2-395b-fad708c6cf83@oracle.com> Message-ID: Another question, will the constructor of the value/primitive class have a new migration solution? For example, most of the record classes may be candidates for becoming value classes, but at least in the LW4 prototype, making a record class as value class will break the code in other classes that call its constructor (throws InstantiationError). Will this problem be solved? On Sun, Dec 4, 2022 at 1:09 AM Brian Goetz wrote: > Yes, but we want to arrive at a consistent point before updating the docs, > so naturally the design process skates out ahead of the written collateral. > > On 12/3/2022 4:38 AM, Glavo wrote: > > It sounds like the information I'm getting from the JEP docs is outdated, > so there's a misunderstanding. > Do you have plans to update JEP 401? > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From dsimms at openjdk.org Tue Dec 13 12:43:00 2022 From: dsimms at openjdk.org (David Simms) Date: Tue, 13 Dec 2022 12:43:00 GMT Subject: [lworld] RFR: Merge jdk Message-ID: Merge tag 'jdk-21+1' ------------- Commit messages: - Fixed merge - Re-enabled JDK-8242115 now that issues were fixed by JDK-8295066 - PrimitiveObjectMethods -> ValueObjectMethods renaming in test code - Merge branch 'lworld_merge_jdk_21_1' of https://github.com/MrSimms/valhalla into lworld_merge_jdk_21_1 - Missed handrolled class version - IR framework fixes - Missed aarch64 InstanceKlass misc_status changes - Classfile Version to 65 - Missing code after previous back-out of JDK-8242115 (fixed by JDK-8295066) - Logical merge fixes - ... and 639 more: https://git.openjdk.org/valhalla/compare/a4c6473b...ad66cd3b The webrevs contain the adjustments done while merging with regards to each parent branch: - lworld: https://webrevs.openjdk.org/?repo=valhalla&pr=812&range=00.0 - jdk: https://webrevs.openjdk.org/?repo=valhalla&pr=812&range=00.1 Changes: https://git.openjdk.org/valhalla/pull/812/files Stats: 210974 lines in 3145 files changed: 92700 ins; 92071 del; 26203 mod Patch: https://git.openjdk.org/valhalla/pull/812.diff Fetch: git fetch https://git.openjdk.org/valhalla pull/812/head:pull/812 PR: https://git.openjdk.org/valhalla/pull/812 From dsimms at openjdk.org Tue Dec 13 16:13:59 2022 From: dsimms at openjdk.org (David Simms) Date: Tue, 13 Dec 2022 16:13:59 GMT Subject: [lworld] RFR: Merge jdk [v2] In-Reply-To: References: Message-ID: > Merge tag 'jdk-21+1' David Simms has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains 1479 commits: - Fixed merge - Re-enabled JDK-8242115 now that issues were fixed by JDK-8295066 - PrimitiveObjectMethods -> ValueObjectMethods renaming in test code - Merge branch 'lworld_merge_jdk_21_1' of https://github.com/MrSimms/valhalla into lworld_merge_jdk_21_1 - Missed handrolled class version - IR framework fixes - Missed aarch64 InstanceKlass misc_status changes - Classfile Version to 65 - Missing code after previous back-out of JDK-8242115 (fixed by JDK-8295066) - Logical merge fixes - ... and 1469 more: https://git.openjdk.org/valhalla/compare/175e3d3f...ad66cd3b ------------- Changes: https://git.openjdk.org/valhalla/pull/812/files Webrev: https://webrevs.openjdk.org/?repo=valhalla&pr=812&range=01 Stats: 200602 lines in 1553 files changed: 191912 ins; 2006 del; 6684 mod Patch: https://git.openjdk.org/valhalla/pull/812.diff Fetch: git fetch https://git.openjdk.org/valhalla pull/812/head:pull/812 PR: https://git.openjdk.org/valhalla/pull/812 From dsimms at openjdk.org Tue Dec 13 16:16:23 2022 From: dsimms at openjdk.org (David Simms) Date: Tue, 13 Dec 2022 16:16:23 GMT Subject: [lworld] Integrated: Merge jdk In-Reply-To: References: Message-ID: <2kKvr6fNXILEkc1S2j7i_o5er5SnvYoB51hHyjdGQ_E=.74010078-0377-4cee-b03e-191379fda6e4@github.com> On Tue, 13 Dec 2022 12:31:11 GMT, David Simms wrote: > Merge tag 'jdk-21+1' This pull request has now been integrated. Changeset: 6cd250ff Author: David Simms URL: https://git.openjdk.org/valhalla/commit/6cd250ff9f85458b9de6129bc7ff297a1d029042 Stats: 210974 lines in 3145 files changed: 92700 ins; 92071 del; 26203 mod Merge jdk Merge tag 'jdk-21+1' ------------- PR: https://git.openjdk.org/valhalla/pull/812 From vromero at openjdk.org Fri Dec 16 18:11:41 2022 From: vromero at openjdk.org (Vicente Romero) Date: Fri, 16 Dec 2022 18:11:41 GMT Subject: Integrated: Merge lworld In-Reply-To: References: Message-ID: On Fri, 16 Dec 2022 18:01:06 GMT, Vicente Romero wrote: > Merge lworld into universal-tvars This pull request has now been integrated. Changeset: f52d7148 Author: Vicente Romero URL: https://git.openjdk.org/valhalla/commit/f52d71485ab7fb0d5ea6311f546f5fbf05f9542a Stats: 525796 lines in 6916 files changed: 267569 ins; 183476 del; 74751 mod Merge lworld ------------- PR: https://git.openjdk.org/valhalla/pull/813 From vromero at openjdk.org Fri Dec 16 18:11:36 2022 From: vromero at openjdk.org (Vicente Romero) Date: Fri, 16 Dec 2022 18:11:36 GMT Subject: Integrated: Merge lworld Message-ID: Merge lworld into universal-tvars ------------- Commit messages: - additional changes - Merge branch 'lworld' into universal-tvars_merge_lworld_last - Merge lworld - Merge lworld - left-overs from last type system refactoring - Merge lworld - addressing several issues in the implementation of universal tvars - Merge lworld - Merge lworld - changing universal lint warning to unchecked warning - ... and 24 more: https://git.openjdk.org/valhalla/compare/6cd250ff...1dc5cbae The merge commit only contains trivial merges, so no merge-specific webrevs have been generated. Changes: https://git.openjdk.org/valhalla/pull/813/files Stats: 1608 lines in 33 files changed: 1493 ins; 28 del; 87 mod Patch: https://git.openjdk.org/valhalla/pull/813.diff Fetch: git fetch https://git.openjdk.org/valhalla pull/813/head:pull/813 PR: https://git.openjdk.org/valhalla/pull/813 From andvasp at gmail.com Tue Dec 20 12:52:44 2022 From: andvasp at gmail.com (Anderson Vasconcelos Pires) Date: Tue, 20 Dec 2022 09:52:44 -0300 Subject: Replace the primitive class with a more general null safety? In-Reply-To: References: <82E11A2C-9F71-46E5-9A6E-56483C11FFF6@oracle.com> <3b4fdb82-77c8-7ad2-395b-fad708c6cf83@oracle.com> Message-ID: Hi Glavo, Below is my opinion. I do not know if it is right. I believe in your scenario you are changing a lib without changing your application, right? When you change from identity to value class there are other implications that are not just the constructor call. For example, synchronization. So maybe it should be considered a breaking change. Another point is if you are going to change a lib in your application you should recompile your application, make tests. And recompiling would fix this particular problem given in your example. So I do not know if a migration solution is/(should be) necessary. Regards, Anderson. On Tue, Dec 13, 2022 at 5:53 AM Glavo wrote: > Another question, will the constructor of the value/primitive class have a > new migration solution? > > For example, most of the record classes may be candidates for becoming > value classes, but at least in the LW4 prototype, > making a record class as value class will break the code in other classes > that call its constructor (throws InstantiationError). > Will this problem be solved? > > On Sun, Dec 4, 2022 at 1:09 AM Brian Goetz wrote: > >> Yes, but we want to arrive at a consistent point before updating the >> docs, so naturally the design process skates out ahead of the written >> collateral. >> >> On 12/3/2022 4:38 AM, Glavo wrote: >> >> It sounds like the information I'm getting from the JEP docs is outdated, >> so there's a misunderstanding. >> Do you have plans to update JEP 401? >> >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From rriggs at openjdk.org Tue Dec 20 21:27:04 2022 From: rriggs at openjdk.org (Roger Riggs) Date: Tue, 20 Dec 2022 21:27:04 GMT Subject: [lworld] RFR: 8296950: [lworld] AccessFlags should be specific to class file version [v2] In-Reply-To: <39YntreEgkmZr5fVA84OCgX62FqbWzUO5_9kWSPXaDk=.7c54f113-109d-4154-8831-5b4a2bd8f391@github.com> References: <39YntreEgkmZr5fVA84OCgX62FqbWzUO5_9kWSPXaDk=.7c54f113-109d-4154-8831-5b4a2bd8f391@github.com> Message-ID: > Add java.lang.reflect.AccessFlag.maskToAccessFlags(mask, location, cffv) > that requests the flags for a specific class file version number. > Update Class.accessFlags() to request the flags for the specific class file format version of the class file. > Update class file format version tests to match. Roger Riggs has updated the pull request with a new target base due to a merge or a rebase. The incremental webrev excludes the unrelated changes brought in by the merge/rebase. The pull request contains three additional commits since the last revision: - Update to include text describing access flags as may depend on class file version. - Merge branch 'lworld' into 8296950-access-flags-cffv - 8296950: [lworld] AccessFlags should be specific to class file version. Add java.lang.reflect.AccessFlag.maskToAccessFlags that requests the flags for a specific class file version number. ------------- Changes: - all: https://git.openjdk.org/valhalla/pull/808/files - new: https://git.openjdk.org/valhalla/pull/808/files/4a42ad33..41e0abc5 Webrevs: - full: https://webrevs.openjdk.org/?repo=valhalla&pr=808&range=01 - incr: https://webrevs.openjdk.org/?repo=valhalla&pr=808&range=00-01 Stats: 213908 lines in 3224 files changed: 94443 ins; 93243 del; 26222 mod Patch: https://git.openjdk.org/valhalla/pull/808.diff Fetch: git fetch https://git.openjdk.org/valhalla pull/808/head:pull/808 PR: https://git.openjdk.org/valhalla/pull/808 From jbhateja at openjdk.org Mon Dec 26 01:46:56 2022 From: jbhateja at openjdk.org (Jatin Bhateja) Date: Mon, 26 Dec 2022 01:46:56 GMT Subject: [lworld+vector] RFR: Merge lworld Message-ID: Merge latest commits from lworld branch into lworld+vector. Best Regards, Jatin ------------- Commit messages: - Merge branch 'lworld' of http://github.com/openjdk/valhalla into merge_lworld - Merge jdk - 8296149: Start of release updates for JDK 21 - 8297642: PhaseIdealLoop::only_has_infinite_loops must detect all loops that never lead to termination - 8298255: JFR provide information about dynamization of number of compiler threads - 8298383: JFR: GenerateJfrFiles.java lacks copyright header - 8298379: JFR: Some UNTIMED events only sets endTime - 8298129: Let checkpoint event sizes grow beyond u4 limit - 8297718: Make NMT free:ing protocol more granular - 8298173: GarbageCollectionNotificationContentTest test failed: no decrease in Eden usage - ... and 1056 more: https://git.openjdk.org/valhalla/compare/8159184e...b2926b5f The webrevs contain the adjustments done while merging with regards to each parent branch: - lworld+vector: https://webrevs.openjdk.org/?repo=valhalla&pr=814&range=00.0 - lworld: https://webrevs.openjdk.org/?repo=valhalla&pr=814&range=00.1 Changes: https://git.openjdk.org/valhalla/pull/814/files Stats: 415139 lines in 5580 files changed: 213593 ins; 137072 del; 64474 mod Patch: https://git.openjdk.org/valhalla/pull/814.diff Fetch: git fetch https://git.openjdk.org/valhalla pull/814/head:pull/814 PR: https://git.openjdk.org/valhalla/pull/814 From jbhateja at openjdk.org Mon Dec 26 23:34:27 2022 From: jbhateja at openjdk.org (Jatin Bhateja) Date: Mon, 26 Dec 2022 23:34:27 GMT Subject: [lworld+vector] RFR: Merge lworld In-Reply-To: References: Message-ID: On Mon, 26 Dec 2022 01:38:29 GMT, Jatin Bhateja wrote: > Merge latest commits from lworld branch into lworld+vector. > > Best Regards, > Jatin Post merge tests are not clean, will create another PR after resolution. ------------- PR: https://git.openjdk.org/valhalla/pull/814 From jbhateja at openjdk.org Mon Dec 26 23:34:27 2022 From: jbhateja at openjdk.org (Jatin Bhateja) Date: Mon, 26 Dec 2022 23:34:27 GMT Subject: [lworld+vector] Withdrawn: Merge lworld In-Reply-To: References: Message-ID: On Mon, 26 Dec 2022 01:38:29 GMT, Jatin Bhateja wrote: > Merge latest commits from lworld branch into lworld+vector. > > Best Regards, > Jatin This pull request has been closed without being integrated. ------------- PR: https://git.openjdk.org/valhalla/pull/814 From jbhateja at openjdk.org Thu Dec 29 08:45:28 2022 From: jbhateja at openjdk.org (Jatin Bhateja) Date: Thu, 29 Dec 2022 08:45:28 GMT Subject: [lworld+vector] RFR: Merge lworld Message-ID: <7GRRfillens3_MOVq5Hb2MWfuyTEQuzlyRIuB7KRdFI=.2aa85234-b0c5-4673-9f4b-0fa2ef8df336@github.com> Merge latest code from lworld into lworld+vector branch. Best Regards, Jatin ------------- Commit messages: - post-merge fixes. - Merge branch 'lworld' of http://github.com/openjdk/valhalla into merge_lworld - Merge jdk - 8296149: Start of release updates for JDK 21 - 8297642: PhaseIdealLoop::only_has_infinite_loops must detect all loops that never lead to termination - 8298255: JFR provide information about dynamization of number of compiler threads - 8298383: JFR: GenerateJfrFiles.java lacks copyright header - 8298379: JFR: Some UNTIMED events only sets endTime - 8298129: Let checkpoint event sizes grow beyond u4 limit - 8297718: Make NMT free:ing protocol more granular - ... and 1057 more: https://git.openjdk.org/valhalla/compare/8159184e...1f749c90 The webrevs contain the adjustments done while merging with regards to each parent branch: - lworld+vector: https://webrevs.openjdk.org/?repo=valhalla&pr=815&range=00.0 - lworld: https://webrevs.openjdk.org/?repo=valhalla&pr=815&range=00.1 Changes: https://git.openjdk.org/valhalla/pull/815/files Stats: 415200 lines in 5582 files changed: 213641 ins; 137073 del; 64486 mod Patch: https://git.openjdk.org/valhalla/pull/815.diff Fetch: git fetch https://git.openjdk.org/valhalla pull/815/head:pull/815 PR: https://git.openjdk.org/valhalla/pull/815 From jbossons at gmail.com Wed Dec 28 16:02:15 2022 From: jbossons at gmail.com (John Bossons) Date: Wed, 28 Dec 2022 16:02:15 -0000 Subject: P.val as a no-nulls flag Message-ID: *> A pretty clear decision that has emerged is that for both* *>* *> value class V { }* *> and* *> primitive class P { }* *>* *> the language-level types V and P are *reference types* (so P is an alias * *> for P.ref in the ref/val model.) I won't rehash the arguments in favor * *> and against here, but this means any "unadorned" type is a reference * *> type, regardless of whether it is an identity, value, or primitive * *> class. In this way, all primitive/value classes are "ref favoring", and * *> there's no explicit way to flip this, so no need for the * *> ref-default/val-default syntax of a previous round. One of the benefits * *> of this is that migration is smoother because migrating an identity * *> class to either of the new buckets is source- and binary-compatible.* For backwards compatibility (and greater simplicity) this is clearly a better solution ? it follows the well-known Integer vs int distinction much more closely ? but does this imply that the ?P.val? designation will no longer be available for primitive object fields? (I assume the P.ref designation is redundant and so can be deleted.) I think the answer has to be ?No?, so maybe I?m just asking that the JEP 401 description be updated. The ability to be able to specify a P field in a value class as a P.val type allows it to be specified as non-nullable (like being able to specify a field as either a nullable Integer or non-nullable int). It allows more efficient storage (no extra bit to denote null or non-null) and so easier inlining. It also potentially allows the compiler to automatically cause an NPE or other appropriate exception to be thrown if a client attempts to instantiate an object containing a required P.val field with a null input value. An example: value class ContactInfo { private PhoneNumber.val mobile; // required, non-nullable private PhoneNumber landLine; // nullable, often missing ... // PhoneNumber is a primitive class ContactInfo(PhoneNumber pn1, PhoneNumber pn2) { Objects.requireNonNull(pn1, ?mobile?); this.mobile = pn1; this.landLine = pn2; } } If the constructor is autogenerated (e.g. in a record), will the compiler augment the byte code to throw an exception if pn1 = null? Or better still, cause the following client code to be rejected? PhoneNumber landLine = new PhoneNumber(...); ContactInfo info = new ContactInfo(null, landLine); // invalid code It shouldn?t be necessary to have to specifhy ?.val? explicitly in constructor parameters; the compiler has the information in the field type. Indeed, it would be nice if one could specify any value class field as non-nullable, *regardless* of whether the field type is a primitive class. That would enable the compiler and JVM to store/flatten the field without having to allocate space to store a distinguishing bit for null (and also to autogenerate an exception if a null is passed for that field to a constructor). Using the Objects.requireNonNull method alone doesn?t accomplish the more efficient storage. Example: value class ContactInfo { private nonull EmailAddress email; // required field ... } On a different note, the framework now sounds very close to being final. Any chance of a preview version of value and primitive classes in JDK 21? (I?m presuming that the preview feature would allow you to do so while postponing previews of JEP 402 and modified (universal) generics.) Or, if not primitive classes, value classes alone? -------------- next part -------------- An HTML attachment was scrubbed... URL: