RefObject and ValObject

Maurizio Cimadamore maurizio.cimadamore at
Mon Apr 15 14:30:24 UTC 2019

Mu suggestion here is to leave subtyping on the side, at least for now 
and use some other way to describe what's going on.

Let's model the value vs. reference constraint explicitly - e.g. with 
'val' and 'ref' type kinds (and let's not open the can worm as to what 
the syntax should be, whether it should be a type anno, etc.)


val Object ---> accepts all values
ref Object ---> accepts all references
any Object ---> accepts all references

Now that everything is explicit, for types we have two possible moves:

1) reinterpret `Object` as `ref Object`

This will keep semantics as is - that is, upon recompilation, if source 
code doesn't change, a program that expected references cannot start 
receiving value parameters. If code wants to work on both references and 
values, it will have to opt in (by using `any`).

2) reinterpret `Object` as `any Object`

That is, the semantics of `Object` is being redefined here - code which 
assumed to work with references might need to opt-in to additional 
constraints (e.g. add `ref`) in order to make sure it still work as 

I think we are leaning towards (2) - that is, we want meaning of Object 
to be upgraded, and we want users that are not happy with that to opt 
out in some form.

Ok, now let's think about expressions; given an expression E, I have to 
figure out (i) its type and its (ii) kind, since the type system I'm 
describing here takes both factors into account.

Here I'm expecting rules of the kind:

a) if E is an identifier pointing to a variable decl, then type and kind 
are derived from the declaration
b) if E is a method call, where declared method is M, type and kind are 
derived from M's return type declaration
z) if E is a new expression, of the kind `new T()`, the type is T and 
the kind can be either `ref` or `val` depending on whether T is a 
reference class or not. If T can be both, then kind `any` is inferred.

So, we can use (z) e.g. to say that `new String()` has kind `ref`. But, 
if we want Object to be the top type for both values and references, I 
believe one consequence is that `new Object` is interpreted as `any` 
which means you cannot pass it to `ref Object`.

I don't see another way out of this conundrum - other than adding a 
special rule (z2) which says that `new Object()` is treated specially 
and always has kind `ref`. But doing so will run afoul in almost every 
possible way - as soon as you manipulate the result of the `new` in any 
way (cast, assignment to variable of type `Object`, ...) you go back to 
`any` and you are back to a place that is incompatible with `ref Object`.

Your idea of treating Object as abstract is, I believe, a sound one 
(which doesn't need any extra rule) - but we might have to figure out 
some story for anonymous inner classes of the kind `new Object() { ... }`.


On 15/04/2019 14:26, Brian Goetz wrote:
>> But the other concerns remain, e.g. as to the fact that the boundary 
>> between reinterpreted types (Object as RefObject) and 
>> non-reinterpreted types (Object as top type) seems very fuzzy.
> Right, which is why we’re still searching for an answer :)
> We really, really want to be able to represent ref/val-ness in the 
> type system.  Why?  Ignoring pedagogical concerns (which are 
> significant):
>  - If certain operations (e.g., locking) are partial, we want to be 
> able to provide a way to ask if the operation could succeed.  Such as:
>     if (x instanceof RefObejct) { … lock on x … }
> Saying “lock, and hope it doesn’t throw” is not a very good answer. 
>  We already have a tool for querying the dynamic type of an object — 
> instanceof.
>  - Saying that a method should only accept reference objects should be 
> something expressible in the method signature, as in
>     m(RefObject o) { … }
> Types are how we do that.
>  - Similarly, we might want to express the above constraint 
> generically; again, types are the way we do that:
>     class Foo<T extends RefObject> { }
> And, Q-world already taught us that we wanted to retain Object as the 
> top type.  This mean, necessarily, that Object gets a little weirder; 
> it takes on some partly-class, partly-interface behavior.
> Here’s an idea: What if we migrated `Object` to be an abstract class? 
>  The casualty would be the code that says `new Object()`.  While 
> there’s certainly a lot of it out there, perhaps this is something 
> amenable to migration:
>  - At the source level, for N versions, `new Object()` gets a warning 
> that says “I’ll pretend you said `Object.newLockInstance()` or something.
>  - At the bytemode level, for M versions, we do something similar, 
> likely for M > N.
> We can start this now, before Valhalla even previews.

More information about the valhalla-spec-observers mailing list