New candidate JEP: 361: Switch Expressions (Standard)

Brian Goetz brian.goetz at oracle.com
Fri Sep 27 21:20:33 UTC 2019


Various aspects of range support (as case labels, as loop bounds, and 
more generally, as patterns) are a totally reasonable feature idea, but 
also something that is way too big to be considered a "small enhancement 
to the current proposal."  It is also something that is totally 
independent of the switch expressions feature (we could have done it 
before switch expressions, concurrently, or after.)  So it really is its 
own whole separate feature.

What Gavin was going for was:
  - I have tried the feature out on real code, and am reporting my 
experience; or
  - I have tried it out and found bugs; or
  - I have reviewed the spec and have some concerns that it doesn't say 
what I think it should say.

(About ranges: if we were to consider this, I would not treat it as a 
special kind of switch label, but as a more general thing, where 1..n 
was a literal for a range object (ideally an inline object) in an 
expression context and a pattern in a pattern-aware context, so it could 
be used not only in switch, but also nested patterns, instanceof and 
other constructs.  (And eventually, array slices.)  All of which makes 
it an even bigger feature.)

On 9/27/2019 5:05 PM, JARvis PROgrammer wrote:
> As you state that the feature is going to become a permanent one very soon,
> then just brainstorming any possible additions to it which may be taken
> into consideration as those which are not big enough for separate JEPs but
> may come in handy as another small enhancements to the current proposal.
> The following is not critical at all but might be a pleasant addition
>
> Syntax like case *begin* .. *end*: might be allowed as a more user-friendly
> literal for multiple cases of ∀ �� ∈ ([*begin*, *end*] ∧ Z) ⇔ case *begin*,
> (*begin* + 1), (*begin* + 2) ... (*end* - 2), (*end* - 1), *end*:. This is
> nothing but an alias for enumeration of all numbers in range and so this
> just requires the compiler to be able to handle such literals in case
> labels as equivalent explicit range enumerations. From point of generated
> bytecode there of course are no differences (+ this form is usually
> friendly towards tableswitch as the values are explicitly sequential).
> One detail is that overlapping ranges may be allowed thus meaning that the
> values of all the previous ones should be excluded. For sets of included
> values declared as ranges (in order they appear as cases) A, B, C we
> effectively have B \= A and C \= (A ∧ B).
> Consider the example:
>
> String result = switch (value) {
>      case -1 .. 1 -> "Very small";
>      case -4 .. 4 -> "Smalls";
>      case 5 .. 8 -> "Up-to-8s";
>      default -> "Bad guys";
> };
> System.out.println(result);
>
> This is equivalent to
>
> String result = switch (value) {
>      case -1, 0,  1 -> "Very small";
>      case -4, -3, -2, 2, 3,  4 -> "Smalls";
>      case 5, 6, 7, 8 -> "Up-to-8s";
>      default -> "Bad guys";
> };
> System.out.println(result);
>
> For which the following bytecode gets generated:
>
>   ILOAD 1 // push the value by which the switch happens onto the stack
>   TABLESWITCH
>          -4: L0
>          -3: L0
>          -2: L0
>          -1: L1
>           0: L1
>           1: L1
>           2: L0
>           3: L0
>           4: L0
>           5: L2
>           6: L2
>           7: L2
>           8: L2
>     default: L3
> L1 // "Very small" string value pushed onto the stack for [-1; 1] range
> FRAME APPEND [I]
>   LDC "Very small"
>   GOTO L4
> L0 // "Smalls" string value pushed onto the stack for [-4; -2] ∨ [2 ; 4] range
> FRAME SAME
>   LDC "Smalls"
>   GOTO L4
> L2 // "Up-to-8s" string value pushed onto the stack for [5; 8] range
> FRAME SAME
>   LDC "Up-to-8s"
>   GOTO L4
> L3 // "Bad guys" string value pushed for all other values
> FRAME SAME
>   LDC "Bad guys"
> L4 // invoke the `System.out.println(String);` method passing the
> selected String
> FRAME SAME1 java/lang/String
>   ASTORE 2
>   GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
>   ALOAD 2
>   INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
>
> Another point is that an optimization may be made in order to handle big
> ranges as part of default branch + if_icmp.
>
> As an example, consider the following switch-statement (method is an
> instance of an example class Main.Method with virtual method
> #addIntOpcode(Main.Opcode,
> int) with Main.Opcode being an example enum with ICONST, BIPUSH, SIPUSH and
> LDC constants):
>
> method.addIntOpcode(switch (value) {
>      case -1 .. 5 -> Opcode.ICONST;
>      case Byte.MIN_VALUE .. Byte.MAX_VALUE -> Opcode.BIPUSH;
>      case Short.MIN_VALUE .. Short.MAX_VALUE -> Opcode.SIPUSH;
>      default: -> Opcode.LDC;
> }, value);
>
> Considering both notes, this is effectively identical to
>
> method.addIntOpcode(switch (value) {
>      case -1, 0, 1, 2, 3, 4, 5 -> Opcode.ICONST;
>      default -> (Byte.MIN_VALUE <= value && value <= Byte.MAX_VALUE) ?
> Opcode.BIPUSH
>              : (Short.MIN_VALUE <= value && value <= Short.MAX_VALUE) ?
> Opcode.SIPUSH : Opcode.LDC;
> }, value);
>
> Which in bytecode looks like:
>
>   ALOAD 1 // push Main$Method instance onto the stack
>   ILOAD 2 // push the value by which the switch happens onto the stack
>   TABLESWITCH
>          -1: L0
>           0: L0
>           1: L0
>           2: L0
>           3: L0
>           4: L0
>           5: L0
>     default: L1
> L0 // ICONST enum value pushed onto the stack for [-1; 5] range
> FRAME FULL [[Ljava/lang/String; Main$Method I] [Main$Method]
>   GETSTATIC Main$Opcode.ICONST : LMain$Opcode;
>   GOTO L2
> L1 // BIPUSH enum value pushed onto the stack for [-128; -1) ∨ (5; 127] range
> FRAME SAME1 Main$Method
>   BIPUSH -128
>   ILOAD 2
>   IF_ICMPGT L3
>   ILOAD 2
>   BIPUSH 127
>   IF_ICMPGT L3
>   GETSTATIC Main$Opcode.BIPUSH : LMain$Opcode;
>   GOTO L2
> L3 // SIPUSH enum value pushed onto the stack for [-32768; -128) ∨
> (127; 32767] range
> FRAME SAME1 Main$Method
>   SIPUSH -32768
>   ILOAD 2
>   IF_ICMPGT L4
>   ILOAD 2
>   SIPUSH 32767
>   IF_ICMPGT L4
>   GETSTATIC Main$Opcode.SIPUSH : LMain$Opcode;
>   GOTO L2
> L4
> FRAME SAME1 Main$Method // LDC enum value pushed onto the stack for
> [-2^31; -32768) ∨ (32767; 2^31-1] range
>   GETSTATIC Main$Opcode.LDC : LMain$Opcode;
> L2
> FRAME FULL [[Ljava/lang/String; Main$Method I] [Main$Method Main$Opcode]
>   ILOAD 2 // push the `value` local-variable's value onto the stack as
> a method parameter
>   INVOKEVIRTUAL Main$Method.addIntOpcode (LMain$Opcode;I)V // invoke
> the very addIntOpcode method
>
>
> Seems like that's all for this idea, wish that it gets implemented as while
> not being a killer-feature it still is a way to reducs code-size making it
> more obvious (which is one of the whole switch-expressions JEP) yet having
> the most effective bytecode generated.
>
> Excuse me if it is not what was meant by your request on feedback (just
> inform me please about it in this case so that I don't continue generating
> such ideas)
>
> Thanks,
> Peter
> пт, 27 сент. 2019 г. в 19:47, Gavin Bierman <gavin.bierman at oracle.com>:
>
>> Please note that we are considering making this a permanent feature, so
>> this is your last chance to provide substantive feedback based on any new
>> experience you may have had with this feature.
>>
>> A new, draft language spec for JEP 361 (Switch Expressions) is available
>> at:
>>
>>
>> http://cr.openjdk.java.net/~gbierman/jep361/jep361-20190927/specs/switch-expressions-jls.html
>> <
>> http://cr.openjdk.java.net/~gbierman/jep305/jep305-20190918/specs/patterns-instanceof-jls.html
>> This is identical to the version made available for JEP354, apart from
>> some cosmetic changes around terminology following some feedback.
>>
>> [For spec nerds: The primary change is that what was previously called a
>> "switch labeled rule" is now called, more simply, a "switch rule”. “Switch
>> labeled expression” is now a “switch rule expression”, “switch labeled
>> block” is now a “switch rule block” and a “switch labeled throw statement”
>> is now a “switch rule throw statement”.]
>>
>> All feedback welcomed!
>> Gavin
>>
>>> On 25 Sep 2019, at 23:32, mark.reinhold at oracle.com wrote:
>>>
>>> https://openjdk.java.net/jeps/361
>>>
>>> - Mark
>>



More information about the amber-dev mailing list