SoV-3: constructor questions
Remi Forax
forax at univ-mlv.fr
Thu Jan 27 15:26:14 UTC 2022
----- Original Message -----
> From: "Dan Heidinga" <heidinga at redhat.com>
> To: "daniel smith" <daniel.smith at oracle.com>
> Cc: "valhalla-spec-experts" <valhalla-spec-experts at openjdk.java.net>
> Sent: Thursday, January 27, 2022 4:09:58 PM
> Subject: Re: SoV-3: constructor questions
> (Resending as I forgot to CC the list - Sorry for the duplicate email Dan)
>
>> We can come up with a rule to specify, in the Java language, what an "empty
>> constructor" looks like. (Nothing syntactically in the body, for example.)
>>
>> It's harder for the JVM to specify what an "empty <init> method" looks like. It
>> must at least have an 'invokespecial' of its superclass's <init>. Might do some
>> stacking of arguments. Etc. JVMs don't want to be in the business of
>> recognizing code patterns.
>
> Ok. This is due to a class having a single no-args `<init>()V` method
> that has to do double duty - supply the super-call for identity
> subclasses and be ignored by value subclasses. Seems reasonable.
>
>> > 2) What is the rationale behind the return type restrictions on <new> methods?
> ....
>> Treatment of <new> methods is still unresolved, so this (and the JEP) is just
>> describing one possible approach. I tried to reach a conclusion on this a few
>> months ago on this list, but we ended in an unresolved place. I'll try again...
>>
>> Anyway, in this incarnation: the rule is that the return type must be a type
>> that includes instances of the current class. So, in class Point, QPoint is
>> okay, LObject is okay, but LString is not.
>
> I don't understand the point of this restriction. Since
> Ljava/lang/Object; is acceptable (and has to be), I can use a `<new>`
> method to return *any* class but the caller will need to downcast to
> use it.
>
> class QPoint {
> static Object <new>() { return new String("string factory"); }
> static void user() {
> String s = (String) QPoint.<new>:()Ljava/lang/Object;
> }
> }
> We've made the bytecode patterns ugly by requiring a checkcast - and
> maybe slightly harder to write a large set of factory methods due to
> needing different descriptors for each one - and gained what? Javac
> may not generate this kind of code but it still seems odd to restrict
> it.
The reason John gave for allowing a <new> method to return a super type is for lambda proxies.
A lambda proxies is a hidden value class, i.e. a value class loaded by lookup.defineHiddenClass(),
given that a hidden class as no real name, the idea is to use Object or perhaps the functional interface as return type.
If the functional interface is used, there will be no weird cast in the bytecode.
[...]
>
> --Dan
Rémi
>
> On Wed, Jan 26, 2022 at 5:15 PM Dan Smith <daniel.smith at oracle.com> wrote:
>>
>> > On Jan 26, 2022, at 2:18 PM, Dan Heidinga <heidinga at redhat.com> wrote:
>> >
>> > After re-reading the State of Valhalla part 3 again [1], I have a
>> > couple of questions on constructor handling:
>> >
>> > 1) The rules for handling ACC_PERMITS_VALUE are different between
>> > SoV-2 and SoV-3 in that the language imposes constraints the VM
>> > doesn't check. Is this deliberate?
>> >
>> > SoV-2 says:
>> >> The classes they can extend are restricted: Object or abstract classes with no
>> >> fields, empty no-arg constructor bodies, no other constructors, no instance
>> >> initializers, no synchronized methods, and whose superclasses all meet this
>> >> same set of conditions. (Number is an example of such a class.)
>> >
>> > while SoV-3 says:
>> >> Perhaps surprisingly, the JVM does not perform the following checks for
>> >> value-superclass candidates, although the source language compiler will
>> >> typically do so:
>> >>
>> >> It should have declared an empty no-argument constructor. (Or if it didn’t, then
>> >> the author has somehow consented to having all of the constructors being
>> >> skipped by the unnamed factory methods of value subclasses.)
>> >
>> > "Perhaps surprisingly" is right as I'm surprised =) and not sure I
>> > follow why the VM wouldn't enforce the restriction. Is it to avoid
>> > having to specify the attributes of that constructor?
>>
>> We can come up with a rule to specify, in the Java language, what an "empty
>> constructor" looks like. (Nothing syntactically in the body, for example.)
>>
>> It's harder for the JVM to specify what an "empty <init> method" looks like. It
>> must at least have an 'invokespecial' of its superclass's <init>. Might do some
>> stacking of arguments. Etc. JVMs don't want to be in the business of
>> recognizing code patterns.
>>
>> So the model for the JVM is: you can declare <init> methods if you want to
>> support identity subclass creation, and you can declare ACC_PERMITS_VALUE if
>> you want to support value subclass creation. Independent channels, no need for
>> them to interact.
>>
>> > Which leads me to the next concern: how javac will compile the "empty
>> > no-arg constructor bodies" required by SoV-2? Or is the answer we
>> > don't care because the VM won't check anyway?
>>
>> The Java language will produce class files for qualifying abstract classes with:
>> - ACC_PERMITS_VALUE set
>> - The same <init> methods it would have produced in previous versions (involving
>> a super invokespecial call)
>>
>> For a non-qualifying abstract class, you'll get
>> - ACC_PERMITS_VALUE *not* set
>> - The same <init> methods it would have produced in previous versions
>> (potentially involving arbitrary user code)
>>
>> And, yes, the JVM doesn't care. Other patterns are possible in legal class files
>> (but javac won't produce them).
>>
>> > 2) What is the rationale behind the return type restrictions on <new> methods?
>> >
>> >> A <new> method must return the type of its declaring class, or a supertype.
>> > ....
>> >> While <new> methods must always be static and must return a type consistent with
>> >> their class, they can (unlike <init> methods) be declared in any class file, as
>> >> far as the JVM is concerned.
>> >
>> > If I'm reading this correctly, to enforce the first quote we'll need a
>> > verifier check to ensure that the declared return type of the <new>
>> > method is consistent with the current class or a supertype. But
>> > Object is a common supertype, as is ValueObject, so I'm not sure what
>> > we're gaining with this restriction as any type is a valid candidate
>> > for return from a <new> method as anything can be a subclass of
>> > Object.
>>
>> Treatment of <new> methods is still unresolved, so this (and the JEP) is just
>> describing one possible approach. I tried to reach a conclusion on this a few
>> months ago on this list, but we ended in an unresolved place. I'll try again...
>>
>> Anyway, in this incarnation: the rule is that the return type must be a type
>> that includes instances of the current class. So, in class Point, QPoint is
>> okay, LObject is okay, but LString is not.
>>
>> > We get a better restriction from the `aconst_init` and `withfield`
>> > bytecodes which "can only be executed within the nest of the class
>> > that declares the value class being initialized or modified". Do we
>> > actually need the restriction on the <new> method or should it be
>> > considered non-normative (aka a best practice)?
>>
>> I think there are certainly use cases for class instantiation outside of a
>> method named '<new>' (even if javac won't generate them), and wouldn't want to
>> limit those instructions to methods named '<new>'. It gives '<new>' more power
>> than I think we intend—it's supposed to be a convenient place to put stuff, not
>> a mandatory feature of instance creation.
More information about the valhalla-spec-observers
mailing list