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