Feedback wanted: switch expression typing

Remi Forax forax at univ-mlv.fr
Fri Mar 30 16:54:21 UTC 2018


I do not see (B) as sacrifying the consistency because the premise is that an expression switch should be consistent with ?:

But an expression switch can also be modeled as a classical switch that returns it's value to a local variable.

  int a = switch(foo) {
    case 'a' -> 2;
    case 'b' -> 3;
  }
can be see as
  int a = $switch(foo);
with
  int $switch(char foo) {
    case 'a': return 2;
    case 'b': return 3;
  }

In that case, given that return allows target typing, (B) seems to be the only consistent choice, or (B) is as consistent as (A) with a different premise.

regards,
Rémi

----- Mail original -----
> De: "daniel smith" <daniel.smith at oracle.com>
> À: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Mercredi 28 Mars 2018 21:37:51
> Objet: Feedback wanted: switch expression typing

> (Looking for some feedback on real-world code usage. Please read to the end,
> then if you can experiment with the code you work on and report back, I'd
> appreciate it!)
> 
> Switch expressions, from a type checking perspective, are basically
> generalizations of conditional expressions: instead of 2 operands to check, we
> have n.
> 
> A reasonable expectation is that, if I rewrite my conditional expression as a
> switch expression, it will behave the same:
> 
> test ? foo() : bar()
> is equivalent to
> switch (test) { case true -> foo(); case false -> bar(); }
> 
> So, as a starting point, the typing rules for switches should be the same as the
> typing rules for conditionals, but generalized to an arbitrary number of
> results.
> 
> (The "results" of a switch expression are all expressions appearing after a '->'
> or a 'break'.)
> 
> Conditional expressions and switch expressions are typically used as poly
> expressions (in a context that has a target type). But that won't always be the
> case. One notable usage that doesn't have a target type is an initializer for
> 'var': "var x = ...". So they are sometimes poly expressions, sometimes
> standalone.
> 
> Conditional expression typing is driven by an ad hoc categorization scheme which
> looks at the result expressions and tries to predict whether they will all have
> type boolean/Boolean, primitive/boxed number, or something else/a mix ("tries
> to predict" because in some cases we can't type-check the expression until
> we've completed the categorization).
> 
> In the numeric case, we then identify the narrowest primitive type that can
> contain the results.
> 
> In the other/mixed case, we then type check by pushing down a target type, or,
> if none is available, producing a reference type from the lub operation.
> 
> A couple of observations:
> 
> - The primitive vs. reference choice is meaningful, because the primitive and
> reference type hierarchies are different (e.g., int can be widened to long, but
> Integer can't be widened to Long). Preferring primitive typing where possible
> seems like the right choice.
> 
> - The ad hoc categorization is a bit of a mess. It's complex and imperfect. What
> people probably expect is that, where a target type is available, that's what
> the compiler will use—but the compiler ignores the target type in the primitive
> cases.
> 
> Why? Well, in 8, when we introduced target typing of conditionals, we identified
> some incompatibilities that would occur if we changed the handing of
> primitives, and we didn't want to be disruptive.
> 
> Some examples:
> Boolean x = test ? z : zbox; // specified: can NPE; target typing: no null check
> Integer x = test ? s : i; // specified: ok; target typing: can't convert
> short->Integer
> Number x = test ? s : i; // specified: box to Integer; target typing: box to
> Short or Integer
> double d = test ? l : f; // specified: long->float loses precision; target
> typing: long->double better precision
> m(test ? z : zbox); // specified: prefers m(boolean); target typing: m(boolean)
> and m(Boolean) are ambiguous
> 
> At this point, we've got a choice:
> A) Fully mimic the conditional behavior in switch expressions
> B) Do target typing (when available) for all switch expressions, diverging from
> conditionals
> C) Do target typing (when available) for all switches and conditionals,
> accepting the incompatibilities
> 
> (A) sacrifices simplicity. (B) sacrifices consistency. (C) sacrifices
> compatibility.
> 
> General thoughts on simplicity (is the current behavior hard to understand?) and
> consistency (is it bad if the conditional/switch refactoring leads to subtly
> different typing?) are welcome.
> 
> And we could use some clarification is just how significant the compatibility
> costs of (C) are. With that in mind, here's a javac patch:
> 
> http://cr.openjdk.java.net/~dlsmith/logPrimitiveConditionals.patch
> 
> A javac built with this patch supports an option that will output diagnostics
> wherever conditionals at risk of incompatible change are detected:
> 
> javac -XDlogPrimitiveConditionals Foo.java
> 
> If you're able to build OpenJDK with this patch and run it on some real-world
> code, I'd appreciate any insights about what you find.
> 
> —Dan


More information about the amber-spec-experts mailing list