brian.goetz at oracle.com
Fri Feb 7 22:05:35 UTC 2020
> So, summary:
> - Yes, we should figure out how to support abstract class supertypes
> of inline classes, if only at the VM level;
> - There should be one way to declare an inline class, with a modifier
> saying which projection gets the good name;
> - Both the ref and val projections should have the same
> accessibility, in part so that the compiler can freely use inline
> widening/narrowing as convenient;
> - We would prefer to avoid duplication of the methods on both
> projections, where possible;
> - The migration case requires that, for ref-default inline classes,
> we translate so that the methods appear on the ref projection.
Let me flesh this out some more, since the previous mail was a bit of a
#### Abstract class supertypes
It is desirable, both for the migration story and for the language in
general, for inline classes to be able to extend abstract classes.
There are restrictions: no fields, no constructors, no instance
initializers, no synchronized methods. These can be checked by the
compiler at compile time, and need to be re-checked by the VM at load time.
The VM folks have indicated that their preferred way to say
"inline-friendly abstract class" is to have only a no-arg constructor
which is ACC_ABSTRACT. For abstract classes that meet the
inline-friendly requirement, the static compiler can replace the default
constructor we generate now with an abstract one. The VM would have to
be able to deal with subclasses doing `invokespecial <init>` super-calls
My current bikeshed preference for how to indicate these is to do just
the test structurally, with good error messages, and back it up with
annotation support similar to `@FunctionalInterface` that turns on
stricter type checking and documentation support. (The case we would
worry about, which stronger declaration-site indication would help with,
would be: a public accidentally-inline-friendly abstract class in one
maintenance domain, extended by an inline class in another maintenance
domain, and then subsequently the abstract class is modified to, say,
add a field. This could happen, but likely would not happen that often;
we can warn users of the risks by additionally issuing a warning on the
subclass when the superclass is not marked with the annotation.)
#### Val and ref projections
An inline class `C` declaration defines three types; `C`, `C.ref`, and
`C.val`. One of the latter two is always an alias for the first.
`C.ref` is always a reference type, and we extend the `.ref` operator to
extend to all classes (thus allowing us to speak of `T.ref` in generic
Which of the two projections (.ref and .val) is aliased to C is
determined by a modifier, either `ref-default` or `val-default` (the
latter being the, er, default.)
The three types are defined to have the same members, type variables,
and accessibility; the only differences between `C.ref` and `C.val` are:
- The value set of `C.val` consists of values, whereas the value set
of `C.ref` consists of references to values, and includes null;
- `C.ref <: Object` and `C.ref <: I` for all implemented interfaces `I`.
- `C.val` can be _converted_ to `Object` or `I` via an _inline
widening conversion_ (but, this conversion is cheaper than you might think.)
- Variables of type `C.val` are routinely flattened by the VM, whereas
variables of type `C.ref` are not.
#### Translation -- classfiles
A val-default inline class `C` is translated to two classfiles, `C` (val
projection) and `C$ref` (ref projection). A ref-default inline class
`D` is translated to two classfiles, `D` (ref projection) and `D$val`
(val projection), as follows:
- The two classfiles are members of the same nest.
- The ref projection is a sealed abstract class that permits only the
- Instance fields are lifted onto the val projection.
- Supertypes, methods (including static methods, and including the
static "constructor" factory), and static fields are lifted onto the ref
projection. Method bodies may internally require downcasting to `C.val`
to access fields.
#### Translation -- uses
Variables of type `C.ref` are translated as L types (`LC` or `LC$ref`,
depending); variables of type `C.val` are translated as Q types (`QC` or
`C.val` is widened to `C.ref` by direct assignment, since in the VM, an
inline class is related to its supertypes by subtyping. `C.ref` is
narrowed to `C.val` by casting, which the VM can optimize to a null check.
Instance field accesses on `C.val` are translated to `getfield`; field
accesses on `C.ref` are translated by casting to `C.val` and `getfield`.
Method invocation on `C.val` or `C.ref` can be translated directly,
except for private methods, which would require casting `C.val` to
`C.ref` first (not because they are inaccessible, but because they are
not inherited.) Same for static fields.
Conversion of `C.ref` to supertypes is ordinary subtyping; conversion of
`C.val` goes through widening to `C.ref`. Similarly, `instanceof` on an
operand of type `C.val` goes through casting to `C.ref`.
There are other stackings, of course, but this is a starting point,
chosen for simplicity and compatibility.
More information about the valhalla-spec-observers