[ 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