Reference-default style
Brian Goetz
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
winding lead-up.
#### 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
on these.
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
code.)
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
val projection.
- 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
`QC$val`, depending.)
`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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/valhalla-spec-experts/attachments/20200207/ca3f5069/attachment.htm>
More information about the valhalla-spec-experts
mailing list