LWorld1 initial phase proposal

John Rose john.r.rose at oracle.com
Thu Jan 4 00:46:45 UTC 2018

On Jan 3, 2018, at 8:59 AM, Karen Kinnear <karen.kinnear at oracle.com> wrote:
> Many thanks.
> A couple of points:
>> Example:  A field of value type either needs an explicit flatten flag,
>> or a Q-descriptor.  The flatten flag (ACC_VALUE) makes an unambiguous
>> context for the L-descriptor of the field type, so that it means a Q-type
>> rather than an L-type.
> Just to clarify: a value type is “flattenable” - the JVM makes the decision to flatten or not -
> which may be implementation, platform, command-line-flag, etc. dependent.


(The internals could sometimes—or even always—use the same
non-flat representation as a heap buffer.  The JVM's virtualization
doesn't guarantee actual flattening, but allows it, and in order to do
so the user of the JVM's virtual ISA must agree on a special distinction
for "flattenable" types.  One user model for this is that a "flattenable"
type is "virtually flattened" which means it is impossible to prove or
disprove, except maybe for resource constraints, whether the JVM
has flattened or not.  Similarly, it is impossible to prove or disprove
whether the GC has deallocated an unreachable object, but it may
be regarded as "virtually deallocated" the moment it becomes
unreachable.  Sound good?)

>>> II. Assumptions:
>>> 1. New root: LObject - for all references and value types
>> java.lang.Object is a U-type.
>> Object classes (other than java.lang.Object) define R-types.
>> Value classes define Q-types.
>> Interfaces define U-types.
>> Abstract classes define R-types.
>> The following are possibilities but I hope most of them can be false:
>> Perhaps some object classes will *also* imply frozen versions which are Q-types; TBD.
> Could we possibly decouple the conversations about immutable classes from frozen instances?

Sorry, I'm not sure what you are asking here, since the concept of
"immutable class" could have several meanings.  I'm talking here
about object classes, which are internally mutable, supporting
stronger forms of immutability, notably "frozen" states.  Such
forms may be strong enough to be treated as Q-types.

I'm happy to decouple stuff, but I don't see a clean line here yet.

Object class immutability requires mutability control at the instance
level, because final fields are sometimes assignable.  Object classes
must support mutable larval states for their instances while under
construction, even if they are logically immutable (like jl.String).

Value classes are inherently immutable, of course, but that's just
because they work like values, even in private methods or

Along the same lines, some mutable (not all) classes could be
upgraded to support immutability on a per-instance basis.  This
can be viewed as a separate feature (instance "freezing") but IMO
should be considered alongside the need for larval construction
states of fully immutable object classes.

> Or am I misunderstanding what the “freeze” behavior would do? I thought the goal was to
> freeze a given instance (or array instance) - which does not imply any of the other value-based
> class characteristics that we can optimize for.

My point is that at some point a frozen object, if frozen "solidly" enough,
can be treated as if it were a heap-box for a corresponding value.
When that happens, you could perform an R-to-Q conversion on it,
and walk away with "just the value".  How is that better than doing
an R-to-U conversion?  Not much better, but it *does* imply that the
user of the resulting Q-value cannot accidentally witness the object
identity, by synchronizing or acmp-ing it.  It would be a safety feature,
if we did it.

Even if you had an unfrozen mutable object, such as an AtomicInteger
or an int array, there would be some value in performing an R-to-Q
conversion on it, as long as you pinky-promised that there were no
future or outstanding concurrent writes to it.  The memory model for
this could be similar to that of today's arrays when reached by a final
field, or @Stable fields:  Something bad *could* happen if code had
a buggy mutation operation, but it won't, because the code doesn't.

Such an AtomicInteger could be optimized by loading its current int
value and walking away with it, forgetting about the buffer.  Passing
it around under a Q-type would enable such optimizations, while
passing it around under an R-type or U-type would not, since the
JVM has to assume that someone might look at the box's identity
at some time in the future.  With a Q-type, the JVM knows that the
box's identity may be suppressed and/or discarded.

If, in addition, the AtomicInteger is marked by a JVM-enforced frozen
bit, then the pinky-promise is enforceable.  But the same optimizations
apply in either case.  We can deal just fine with unenforceable promises
by saying that breaking an unenforced promise leads to a modicum of
"indeterminate behavior", as with races in the JMM.

>> Perhaps some value classes will *also* imply heavy-boxed versions which are R-types; TBD.
>> Perhaps some interfaces will be *restricted* to Q-types or R-types; TBD.
>> Perhaps some abstract classes can help define Q-types or U-types; TBD.
>>>   flattenable when contained in reference, value type or array
>> "contained in reference" is ambiguous.  I suggest this language:
>> flattenable when contained in a variable (instance field, static field, array element, or local)
> Much clearer. Thanks.
> Current proposal does not flatten in static fields - we don’t expect a significant benefit for that.

That's an implementation matter; I'm talking mainly about the virtual machine.
Static fields should be virtually flattenable, as a matter of design regularity.
It's OK to ignore the ACC_VALUE bit for statics, if that simplifies the
implementation.  IMO it's not OK to say the ACC_VALUE bit is illegal on
statics, since that just puts in a sharp edge where we don't need one.

>>>   support interfaces
>> Yes.  Interfaces define U-types.  (TBD whether there are any variations on this theme.)
>> And we can say java.lang.Object is an "honorary interface".  It is a U-type
>> which all classes implement.  Like interfaces, it is a valid bound for all types
>> and all generic variables.
> Still trying to figure out what we mean by “honorary interface”?

I mean that getClass/toString/equals/hashCode API points are
programmable from value types and generics and default methods
just like compareTo, and under analogous conditions.  So Object
works like Comparable, where it matters.  If Object or Comparable
is my super, I can override its methods (in a value class or interface,
as well as an object class).  If a variable (including 'this') has Object
or Comparable as a super, I can make a call against its API points.

At some point we'll have to make it official with real spec. language.
For now we limp along by saying "an interface" and then adding weasel
words like "or Object, excluding certain methods like wait/notify/finalize".

I suppose we could say that there is a notional type, an interface
derived "in the obvious way" from Object, that looks like this:

package java.lang;
interface I$Object {
  Class<?> getClass();
  String toString();
  boolean equals(Object x);
  int hashCode();

(Maybe it's even an explicit type, coded in the JDK?)

And then some occurrences of Object are rewritten as if
they were I$Object.  And we work the rules so that all types
implement I$Object, and all calls to Object methods also
mentioned in Object are rewritten to call against I$Object.

> Not allowed fields?

Nope; nobody wants fields on Object anyway.  Not constructors
either.  The important thing is methods (override and call).

> Can be implemented by value classes and object classes?


> Other?

Methods of [I$]Object are always available on 'this' in value
classes, on 'this' in object classes, on 'this' in interface default
methods, and on any variable of a generic type.

More information about the valhalla-spec-observers mailing list