SoV-3: constructor questions

Dan Heidinga heidinga at redhat.com
Thu Jan 27 15:09:58 UTC 2022


(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.

>
> > 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.
>

I think we agree here - the useful restrictions are on only allowing
aconst_init / withfield with a Nest.  Given we've already agreed to
that restriction, I'm trying to understand the benefits of further
restrictions on the descriptor for <new>.

--Dan

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-experts mailing list