value type hygiene

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Thu May 17 16:15:00 UTC 2018



On 15/05/18 22:26, John Rose wrote:
> On May 15, 2018, at 5:06 AM, Maurizio Cimadamore <maurizio.cimadamore at oracle.com> wrote:
>> I wonder if we shouldn't also consider something along the lines of restricting migration compatibility only for _nullable_ value types, where a nullable value type is a value whose representation is big enough that it can afford one spare value to denote null-ness. So, if you want to convert existing legacy reference classes to value types, they'd better be nullable values; this way you don't lose any value in the legacy domain - nulls will be remapped accordingly (using a logic specified in the nullable value type declaration).
>>
>> It seems like we've been somewhere along this path before (when we were exploring the Q vs. L split) - why would something like that not be workable?
> We do something similar explicitly (not globally) with the combinator
> MHs.explicitCastArguments, which converts null to zeroes of
> primitive types.  But not vice versa:  Zeroes don't re-box to null.
> And it's a localized thing.
>
> I think you are suggesting a def-site opt-in where a class VT somehow
> nominates a special value VT.N (perhaps its VT.default default, perhaps
> not) with one or both of these behaviors:
>     assert( (VT)null == VT.N );   // unbox null to N
>     assert( (Object)VT.N == null );  // box N to null (probably not!)
Yes, that is the spirit.
>
> For simplicity, let's say N must be VT.default, and that the conversion
> is just one way (null to VT).  Then the opt-in could be as simple as
> mixing in an interface NullConvertsToDefault.  See the P.S. of this
> message:
>    http://mail.openjdk.java.net/pipermail/valhalla-spec-experts/2018-May/000634.html
>
> (Perhaps you are suggesting something different?)
Well, I'm suggesting something along those lines, but I don't think that 
generally you can assume that null pattern and default pattern are the 
same thing, semantically; we've been there before, and came away with 
the feeling that these were two different things; e.g. null is often 
used as a special sentinel value, denoting some missing element. That's 
not default (which could be a totally legitimate value within the domain).
>
> It might be workable.  It's complicated, of course.  It would need to inject
> special logic into many paths in the JVM which are currently just error
> paths.  There might be collateral damage on performance.  For optimized
> code, throwing an NPE is always simpler than patching and continuing.
> This is roughly because, in optimized code, control flow merges are harder
> to optimize than control flow forks.
>
> (Adding in the symmetric feature, of converting N to null, is probably
> very expensive, since it would seem to require lots of new tests of
> the form, "are you N?"  And there's little value in converting N to
> null and then having List.of or some other null-hostile API throw
> an error.  So then you have a puzzler:  The seam between N and
> null is not completely hidden.)
But doesn't this more complex path only come up in migration cases? E.g. 
maybe there's a way to have this w/o totally compromising the 
performance model for regularly co-compiled classes?

Maurizio
>
> AFAIK C# does something like this as a one-time deal, which cannot be
> mixed in as an interface:
>    https://docs.microsoft.com/en-us/dotnet/api/system.nullable
>
> In C#, at the use-site of a type you can opt into it with an emotional
> type like 'int?'.  But we could make it opt in at the def-site, too,
> if the value type has a spare code-point it's not using.  I'm sure
> if C# doesn't do this there are excellent reasons for them not to.
> Anybody got information on this?
>
> I am hoping to avoid playing such a card.  (In case anyone didn't notice,
> we are playing with a large deck here, if not a full deck.  There are
> lots of potential moves we can make.)  I want us to win with a small
> number of moves.
>
> — John
>
> P.S. Other examples of moves:  Adding another type descriptor, having
> one array type be polymorphically boxed or flattened, having two
> VM-level types per source value type, waiting for reified generics, waiting
> for primitive convergence, adding large infrastructure for migration.
> Maybe we will be forced to do one or all of these before we can
> get anywhere.  I hope not; I'm trying to sneak across a meaningful
> waypoint (not finish line) simply with L-world.
>



More information about the valhalla-spec-experts mailing list