My original thought had been that this would benefit new code that knew it had to do something identity-full (lock, make weak references, etc) and wanted to enforce it would never see a value.
 
The migration path adds a cherry on top.
 
--Dan
 
----- Original message -----
From: Brian Goetz <brian.go...@oracle.com>
To: Daniel Heidinga <daniel_heidi...@ca.ibm.com>
Cc: valhalla-spec-experts <valhalla-spec-experts@openjdk.java.net>
Subject: Re: RefObject and ValObject
Date: Fri, Apr 12, 2019 11:51 AM
 
High-order tradeoffs:

  - Having R/VObject be classes helps from the pedagogical perspective
(it paints an accurate map of the object model.)

  - There were some anomalies raised that were the result of rewriting
an Object supertype to RefObject, and some concerns about "all our
tables got one level deeper."  I don't really have a strong opinion on
these.

  - Using interfaces is less intrusive, but less powerful.

  - None of the approaches give an obvious solution for the "make me a
lock Object" problem.


I think the useful new observation in this line of discussion is this:

  - The premise of L-World is that legacy Object-consuming code can keep
working with values.
  - We think that's a good thing.
  - But .... we also think there will be some cases where that's not a
good thing, and that code will wish it had said `m(RefObject)` instead
of `m(Object)`.  [ this is the new thing ]

Combining this with the migration stuff going on in a separate thread, I
think what you're saying is you want to be able to take a method:

     m(Object o) { }

and _migrate_ it to be

     m(RefObject o) { }

with a forwarder

     @ForwardTo( m(RefObject) )
     m(Object o);

So that code could, eventually, be migrated to RefObject-consuming code,
and all is good again.  And the JIT can see that o is a RefObject and
credibly fall back to a legacy interpretation of ACMP and locking.





On 4/12/2019 11:16 AM, Daniel Heidinga wrote:
> During the last EG call, I suggested there are benefits to having both RefObject and ValObject be classes rather than interfaces.
>
> Old code should be able work with both values and references (that's the promise of L-World after all!). New code should be able to opt into whether it wants to handle only references or values as there are APIs that may only make sense for one or the other. A good example of this is java.lang.Reference-subtypes which can't reasonably deal with values. Having RefObject in their method signatures would ensure that they're never passed a ValObject. (ie: the ctor becomes WeakReference(RefObject o) {...})
>
> For good or ill, interfaces are not checked by the verifier. They're passed as though they are object and the interface check is delayed until invokeinterface, etc. Using interfaces for Ref/Val Object doesn't provide verifier guarantees that the methods will never be passed the wrong type. Javac may not generate the code but the VM can't count on that being the case due to bytecode instrumentation, other compilers, etc.
>
> Using classes does provide a strong guarantee to the VM which will help to alleviate any costs (acmp, array access) for methods that are declared in terms of RefObject and ensures that the user is getting exactly what they asked for when they declared their method to take RefObject.
>
> It does leave some oddities as you mention:
> * new Object() -> returns a new RefObject
> * getSuperclass() for old code may return a new superclass (though this may be the case already when using instrumentation in the classfile load hook)
> * others?
>
> though adding interfaces does as well:
> * getInterfaces() would return an interface not declared in the source
> * Object would need to implement RefObject for the 'new Object()` case which would mean all values implemented RefObject (yuck!)
>
> Letting users say what they mean and have it strongly enforced by the verifier is preferable in my view, especially as getSuperclass() issue will only apply to old code as newly compiled code will have the correct superclass in its classfile.
>
> --Dan
>
>
> -----"valhalla-spec-experts" <valhalla-spec-experts-boun...@openjdk.java.net> wrote: -----
>
>> To: valhalla-spec-experts <valhalla-spec-experts@openjdk.java.net>
>> From: Brian Goetz
>> Sent by: "valhalla-spec-experts"
>> Date: 04/08/2019 04:00PM
>> Subject: RefObject and ValObject
>>
>> We never reached consensus on how to surface Ref/ValObject.
>>
>> Here are some places we might want to use these type names:
>>
>> - Parameter types / variables: we might want to restrict the domain
>> of a parameter or variable to only hold a reference, or a value:
>>
>> void m(RefObject ro) { … }
>>
>> - Type bounds: we might want to restrict the instantiation of a
>> generic class to only hold a reference (say, because we’re going to
>> lock on it):
>>
>> class Foo<T extends RefObject> { … }
>>
>> - Dynamic tests: if locking on a value is to throw, there must be a
>> reasonable idiom that users can use to detect lockability without
>> just trying to lock:
>>
>> if (x instanceof RefObject) {
>> synchronized(x) { … }
>> }
>>
>> - Ref- or Val-specific methods. This one is more vague, but its
>> conceivable we may want methods on ValObject that are members of all
>> values.
>>
>>
>> There’s been three ways proposed (so far) that we might reflect these
>> as top types:
>>
>> - RefObject and ValObject are (somewhat special) classes. We spell
>> (at least in the class file) “value class” as “class X extends
>> ValObject”. We implicitly rewrite reference classes at runtime that
>> extend Object to extend RefObject instead. This has obvious
>> pedagogical value, but there are some (small) risks of anomalies.
>>
>> - RefObject and ValObject are interfaces. We ensure that no class
>> can implement both. (Open question whether an interface could extend
>> one or the other, acting as an implicit constraint that it only be
>> implemented by value classes or reference classes.). Harder to do
>> things like put final implementations of wait/notify in ValObject,
>> though maybe this isn’t of as much value as it would have been if
>> we’d done this 25 years ago.
>>
>> - Split the difference; ValObject is a class, RefObject is an
>> interface. Sounds weird at first, but acknowledges that we’re
>> grafting this on to refs after the fact, and eliminates most of the
>> obvious anomalies.
>>
>> No matter which way we go, we end up with an odd anomaly: “new
>> Object()” should yield an instance of RefObject, but we don’t want
>> Object <: RefObject for obvious reasons. Its possible that “new
>> Object()” could result in an instance of a _species_ of Object that
>> implement RefObject… but our theory of species doesn’t quite go there
>> and it seems a little silly to add new requirements just for this.
>>
>>
>>
>>

 
 

Reply via email to