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.bier...@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.reinh...@oracle.com wrote:

https://openjdk.java.net/jeps/361

- Mark


Reply via email to