[ moving to amber-spec-experts ]

There are a few ways we can interpret a primitive type test pattern (case int).  We could interpret them strictly, as Remi says, permitting only boxing adaptation when the target is a reference type; or we could interpret them more broadly, treating them as an assignability check (could I assign the target type to a variable of this type.)

Assignment supports a combination of unboxing and widening; if we say

    Short s = ...
    int i = s

this succeeds without a cast.  This seems like the correct spirit of "x matches int i"; could I assign, without explicit adaptation, the target to an int.  (On the other hand, assignment won't let us assign an Integer to a Short.)

This has consequences for two forms of dead-code testing the compiler wants to do -- static applicability tests (where we reject nonsense combinations of target type and pattern) and dominance testing.

Every pattern has a "bounding type":

 - For a constant pattern "case c", it is the type of c;
 - For a type test pattern "case T t", it is T;
 - For a destructuring pattern "case Foo(..)", it is Foo.

Let's say: For a match "x matches P" or "switch (x) { case P: }", where X is the static type of x, P is considered inapplicable if a cast from X to B=bounding(P) would fail according to existing cast conversion rules, and should result in a compilation error.

The above works when B and X are reference types; it needs to be further adjusted for the case when B is a primitive type; I think the refinement needed is we want to check for cast conversion from X to maybeBox(bounding(P)), where maybeBox is the identity on reference types and maps to the box type for primitives?

Dominance testing is more complicated when it comes to primitives, but it means we have to treat int as dominating short (and Short).  Again, we can appeal to assignability; primitive type T dominates primitive Q (or Q's box) if Q can be assigned to T without a cast.  So int dominates short and Short.

Bottom line: use cast conversion for "could this pattern match", use assignment conversion for dynamic type tests involving boxing, widening, and subtyping.





On 10/15/2017 5:33 AM, Remi Forax wrote:
I do not think that a Short should matches a long if the pattern matching is 
defined as a suite of matches, the behavior will be weird.
By example with,
   Object x = new Short();
   switch(x) {
     case long l: ...
     case short s: ...
   }
it will be surprising that the first case matches.

In my opinion, we should stick to
   matches = instanceof + cast
the instanceof part is a plain old instanceof and the cast can be an unboxing 
but not a widening conversion
so with x an object
   x matches long l <=> x instanceof Long + unbox Long

With your example,
   void test(Short x) {
     assert(x matches long l);  // compile error, because x instanceof Long is 
a compile error
   }

regards,
Rémi

----- Mail original -----
De: "John Rose" <john.r.r...@oracle.com>
À: "amber-dev" <amber-...@openjdk.java.net>
Envoyé: Dimanche 15 Octobre 2017 01:02:14
Objet: [patterns] primitive widening
Interesting question:  Should a "Short" box value
match a "long" primitive?  More generally, should
pattern matching emulate assignment conversions?
Including primitive widening conversions?

Probably the answer is yes, under the theory that
a pattern match digs through dynamic information to
search for a *possible* assignment, and then (along
that "match succeeded" path, is ready to make the
assignment to a binding variable.

    void test(Short x) {
        long y = x; //OK
        longConsumer(x); //OK
        assert(x matches long);  //OK??
    }
    void longConsumer(long z) { }

Does the prototype work that way?  No, there is just an
open-coded instanceof test of the wrapper type
(makeTypeTest … boxedTypeOrType).

This is tricky to implement dynamically, but MH.invoke
gets these details correct already, since it makes a
dynamic emulation of (many of) Java's static rules for
method argument conversion.

("Many of" means that the dynamic emulation is aware
only of reified types, as if only raw types were present.
Happily, the Core Reflection API also does the same
emulation, although with different exceptions thrown.)

These are the relevant routines from the MH runtime:
  sun.invoke.util.Wrapper::isConvertibleFrom
  sun.invoke.util.Wrapper::convert

One good thing about this messy stuff:  The widening
primitive conversions (and narrowing ones, in the case
of a cast) only come into play when the target type is
really a primitive, not just a reference wrapping one.

So we don't have to match a Short to a Long, just
a Byte, Short, Character, Integer, Float, and Long to
a primitive long (if the source type is a reference
type that can contain any or all of the above).

A factored runtime for pattern matching will be able
to use the MH routines, if we decide that's the correct
behavior.  By "factored runtime" I hope for, of course,
something with a metafactory for each switch and
pattern.

— John

Reply via email to