On Wed, Feb 23, 2022 at 1:36 PM Dan Smith <daniel.sm...@oracle.com> wrote: > > Fred suggested that we enumerate the whole space here. So, some cases to > consider: > > { ACC_PERMITS_VALUE, not } > { has an <init> declaration, not } > { implements IdentityObject, not } > { implements ValueObject, not } > > "implements" here refers to both direct and indirect superinterfaces. > > I'll focus on the first two, which affect the inference of superinterfaces. > > (1) ACC_PERMITS_VALUE, <init> declaration > > This is a class that is able to support both identity and value subclasses. > It implements no extra interfaces, but can restrict its subclasses via > 'implements IdentityObject' or 'implements ValueObject'. > > (2) ACC_PERMITS_VALUE, no <init> declaration > > The JVM infers that this class implements ValueObject, if it doesn't already. > If it also implements IdentityObject, an error occurs at class load time. > > (Design alternative: we could ignore the <init> declarations and treat this > like case (1). In that case, the class could implement IdentityObject or be > extended by identity classes without error (as long as it doesn't also > implement ValueObject). But those identity subclasses couldn't declare > verification-compatible <init> methods, just like subclasses of abstract > classes that have no <init> methods today.)
I think ignoring the <init> declarations is the model we want here. Both (1) and (2) should be treated the same by the VM - in either case the subclasses can implement IdentityObject or ValueObject. Whether they can be instantiated is a decision better left to other parts of the spec (in this case, I believe verification will succeed and resolution of the `super()` <init> call will fail). > > (3) no ACC_PERMITS_VALUE, <init> declaration > > The JVM infers that this class implements IdentityObject, if it doesn't > already. If it also implements ValueObject, an error occurs at class load > time. I think this should be driven purely by the presence of the ACC_PERMITS_VALUE flag and the VM shouldn't be looking at the <init> methods. The JVM shouldn't infer either IdentityObject or ValueObject for this abstract class - any inference decision should be delayed to the subclasses that extend this abstract class. An abstract class that doesn't have the ACC_PERMITS_VALUE flag binds tightly to the IdentityObject interface. The presence of the ACC_PERMITS_VALUE flag delays the interface binding until we hit either a concrete class or an abstract class without the flag. > > (4) no ACC_PERMITS_VALUE, no <init> declaration > > This is a class that effectively supports *no* subclasses. We don't infer any > superinterfaces, but it can choose to implement IdentityObject or > ValueObject. A value class that extends this class will fail to load. See above. No ACC_PERMITS_VALUE flag means it binds tightly to IdentityObject and can only be subclassed by IdentityObject-compatible classes. All value classes extending this abstract will fail to load. > If the class doesn't implement ValueObject, an identity class that extends > this class could load, but couldn't declare verification-compatible <init> > methods, just like subclasses of abstract classes that have no <init> methods > today. The class extending this will load but cannot be instantiated. Verification succeeds but resolution of any super() calls in the constructor will fail to resolve. > > (Design alternative: we could ignore the <init> declarations and treat this > like case (3). In that case, it would be an error for the class to implement > ValueObject, because it also implicitly implements IdentityObject.) +1. > > --- > > Spelling this out makes me feel like treating the presence of <init> methods > as an inference signal may be overreaching and overcomplicating things. > Today, declaring an <init> method, or not, has no direct impact on anything, > other than the side-effect that you can't write verification-compatible > <init> methods in your subclasses. I like the parallel between "permits > identity (via <init>)" and "permits value (via flag)", but flags and <init> > methods aren't really parallel constructs; in cases (2) and (4), we still > "permit" identity subclasses, even if they're pretty useless. > > (And it doesn't help that javac doesn't give you any way to create these > <init>-free classes, so in practice they certainly don't have parallel > prevalence.) > > Pursuing the "design alternative" strategies would essentially collapse this > down to two cases: (1) ACC_PERMITS_VALUE, no superinterfaces inferred, but > various checks performed (e.g., no instance fields); and (3) no > ACC_PERMITS_VALUE, IdentityObject is inferred, error if there's also an > explicit 'implements ValueObject'. > > How do we feel about that? > I think this design alternative is the right strategy and more inline with existing conventions for how the VM handles classes. --Dan