There’s a middle ground, which is: to pursue B now, in conjunction with trying 
to evolve the rules for conditionals over time by proceeding from warning to 
error.  That gets us to a consistent and simpler place in the long run while 
mitigating the effect of a sudden inconsistent change.

> On Mar 28, 2018, at 3:37 PM, Dan Smith <daniel.sm...@oracle.com> wrote:
> 
> (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

Reply via email to