8214031: Assertion error in value break statement with conditional operator in switch expression
Correcting handling of boolean-valued switch expressions when true/false; generating them directly rather than desugaring in Lower. Reviewed-by: mcimadamore
This commit is contained in:
parent
b31f251889
commit
835c863ba8
@ -300,7 +300,7 @@ public class Flow {
|
||||
* Base visitor class for all visitors implementing dataflow analysis logic.
|
||||
* This class define the shared logic for handling jumps (break/continue statements).
|
||||
*/
|
||||
static abstract class BaseAnalyzer<P extends BaseAnalyzer.PendingExit> extends TreeScanner {
|
||||
static abstract class BaseAnalyzer extends TreeScanner {
|
||||
|
||||
enum JumpKind {
|
||||
BREAK(JCTree.Tag.BREAK) {
|
||||
@ -328,7 +328,7 @@ public class Flow {
|
||||
/** The currently pending exits that go from current inner blocks
|
||||
* to an enclosing block, in source order.
|
||||
*/
|
||||
ListBuffer<P> pendingExits;
|
||||
ListBuffer<PendingExit> pendingExits;
|
||||
|
||||
/** A pending exit. These are the statements return, break, and
|
||||
* continue. In addition, exception-throwing expressions or
|
||||
@ -351,20 +351,20 @@ public class Flow {
|
||||
abstract void markDead();
|
||||
|
||||
/** Record an outward transfer of control. */
|
||||
void recordExit(P pe) {
|
||||
void recordExit(PendingExit pe) {
|
||||
pendingExits.append(pe);
|
||||
markDead();
|
||||
}
|
||||
|
||||
/** Resolve all jumps of this statement. */
|
||||
private Liveness resolveJump(JCTree tree,
|
||||
ListBuffer<P> oldPendingExits,
|
||||
ListBuffer<PendingExit> oldPendingExits,
|
||||
JumpKind jk) {
|
||||
boolean resolved = false;
|
||||
List<P> exits = pendingExits.toList();
|
||||
List<PendingExit> exits = pendingExits.toList();
|
||||
pendingExits = oldPendingExits;
|
||||
for (; exits.nonEmpty(); exits = exits.tail) {
|
||||
P exit = exits.head;
|
||||
PendingExit exit = exits.head;
|
||||
if (exit.tree.hasTag(jk.treeTag) &&
|
||||
jk.getTarget(exit.tree) == tree) {
|
||||
exit.resolveJump();
|
||||
@ -378,11 +378,11 @@ public class Flow {
|
||||
|
||||
/** Resolve all continues of this statement. */
|
||||
Liveness resolveContinues(JCTree tree) {
|
||||
return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE);
|
||||
return resolveJump(tree, new ListBuffer<PendingExit>(), JumpKind.CONTINUE);
|
||||
}
|
||||
|
||||
/** Resolve all breaks of this statement. */
|
||||
Liveness resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) {
|
||||
Liveness resolveBreaks(JCTree tree, ListBuffer<PendingExit> oldPendingExits) {
|
||||
return resolveJump(tree, oldPendingExits, JumpKind.BREAK);
|
||||
}
|
||||
|
||||
@ -412,7 +412,7 @@ public class Flow {
|
||||
* The output of this analysis pass are used by other analyzers. This analyzer
|
||||
* sets the 'finallyCanCompleteNormally' field in the JCTry class.
|
||||
*/
|
||||
class AliveAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
|
||||
class AliveAnalyzer extends BaseAnalyzer {
|
||||
|
||||
/** A flag that indicates whether the last statement could
|
||||
* complete normally.
|
||||
@ -831,7 +831,7 @@ public class Flow {
|
||||
* thrown is declared or caught. The analyzer uses some info that has been set by
|
||||
* the liveliness analyzer.
|
||||
*/
|
||||
class FlowAnalyzer extends BaseAnalyzer<FlowAnalyzer.FlowPendingExit> {
|
||||
class FlowAnalyzer extends BaseAnalyzer {
|
||||
|
||||
/** A flag that indicates whether the last statement could
|
||||
* complete normally.
|
||||
@ -851,11 +851,11 @@ public class Flow {
|
||||
*/
|
||||
List<Type> caught;
|
||||
|
||||
class FlowPendingExit extends BaseAnalyzer.PendingExit {
|
||||
class ThrownPendingExit extends BaseAnalyzer.PendingExit {
|
||||
|
||||
Type thrown;
|
||||
|
||||
FlowPendingExit(JCTree tree, Type thrown) {
|
||||
ThrownPendingExit(JCTree tree, Type thrown) {
|
||||
super(tree);
|
||||
this.thrown = thrown;
|
||||
}
|
||||
@ -871,21 +871,23 @@ public class Flow {
|
||||
/** Complain that pending exceptions are not caught.
|
||||
*/
|
||||
void errorUncaught() {
|
||||
for (FlowPendingExit exit = pendingExits.next();
|
||||
for (PendingExit exit = pendingExits.next();
|
||||
exit != null;
|
||||
exit = pendingExits.next()) {
|
||||
Assert.check(exit instanceof ThrownPendingExit);
|
||||
ThrownPendingExit thrownExit = (ThrownPendingExit) exit;
|
||||
if (classDef != null &&
|
||||
classDef.pos == exit.tree.pos) {
|
||||
log.error(exit.tree.pos(),
|
||||
Errors.UnreportedExceptionDefaultConstructor(exit.thrown));
|
||||
Errors.UnreportedExceptionDefaultConstructor(thrownExit.thrown));
|
||||
} else if (exit.tree.hasTag(VARDEF) &&
|
||||
((JCVariableDecl)exit.tree).sym.isResourceVariable()) {
|
||||
log.error(exit.tree.pos(),
|
||||
Errors.UnreportedExceptionImplicitClose(exit.thrown,
|
||||
Errors.UnreportedExceptionImplicitClose(thrownExit.thrown,
|
||||
((JCVariableDecl)exit.tree).sym.name));
|
||||
} else {
|
||||
log.error(exit.tree.pos(),
|
||||
Errors.UnreportedExceptionNeedToCatchOrThrow(exit.thrown));
|
||||
Errors.UnreportedExceptionNeedToCatchOrThrow(thrownExit.thrown));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -896,7 +898,7 @@ public class Flow {
|
||||
void markThrown(JCTree tree, Type exc) {
|
||||
if (!chk.isUnchecked(tree.pos(), exc)) {
|
||||
if (!chk.isHandled(exc, caught)) {
|
||||
pendingExits.append(new FlowPendingExit(tree, exc));
|
||||
pendingExits.append(new ThrownPendingExit(tree, exc));
|
||||
}
|
||||
thrown = chk.incl(exc, thrown);
|
||||
}
|
||||
@ -914,7 +916,7 @@ public class Flow {
|
||||
JCClassDecl classDefPrev = classDef;
|
||||
List<Type> thrownPrev = thrown;
|
||||
List<Type> caughtPrev = caught;
|
||||
ListBuffer<FlowPendingExit> pendingExitsPrev = pendingExits;
|
||||
ListBuffer<PendingExit> pendingExitsPrev = pendingExits;
|
||||
Lint lintPrev = lint;
|
||||
boolean anonymousClass = tree.name == names.empty;
|
||||
pendingExits = new ListBuffer<>();
|
||||
@ -1024,12 +1026,12 @@ public class Flow {
|
||||
|
||||
scan(tree.body);
|
||||
|
||||
List<FlowPendingExit> exits = pendingExits.toList();
|
||||
List<PendingExit> exits = pendingExits.toList();
|
||||
pendingExits = new ListBuffer<>();
|
||||
while (exits.nonEmpty()) {
|
||||
FlowPendingExit exit = exits.head;
|
||||
PendingExit exit = exits.head;
|
||||
exits = exits.tail;
|
||||
if (exit.thrown == null) {
|
||||
if (!(exit instanceof ThrownPendingExit)) {
|
||||
Assert.check(exit.tree.hasTag(RETURN));
|
||||
} else {
|
||||
// uncaught throws will be reported later
|
||||
@ -1059,7 +1061,7 @@ public class Flow {
|
||||
}
|
||||
|
||||
public void visitDoLoop(JCDoWhileLoop tree) {
|
||||
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
pendingExits = new ListBuffer<>();
|
||||
scan(tree.body);
|
||||
resolveContinues(tree);
|
||||
@ -1068,7 +1070,7 @@ public class Flow {
|
||||
}
|
||||
|
||||
public void visitWhileLoop(JCWhileLoop tree) {
|
||||
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
pendingExits = new ListBuffer<>();
|
||||
scan(tree.cond);
|
||||
scan(tree.body);
|
||||
@ -1077,7 +1079,7 @@ public class Flow {
|
||||
}
|
||||
|
||||
public void visitForLoop(JCForLoop tree) {
|
||||
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
scan(tree.init);
|
||||
pendingExits = new ListBuffer<>();
|
||||
if (tree.cond != null) {
|
||||
@ -1091,7 +1093,7 @@ public class Flow {
|
||||
|
||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||
visitVarDef(tree.var);
|
||||
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
scan(tree.expr);
|
||||
pendingExits = new ListBuffer<>();
|
||||
scan(tree.body);
|
||||
@ -1100,7 +1102,7 @@ public class Flow {
|
||||
}
|
||||
|
||||
public void visitLabelled(JCLabeledStatement tree) {
|
||||
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
pendingExits = new ListBuffer<>();
|
||||
scan(tree.body);
|
||||
resolveBreaks(tree, prevPendingExits);
|
||||
@ -1116,7 +1118,7 @@ public class Flow {
|
||||
}
|
||||
|
||||
private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
|
||||
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
pendingExits = new ListBuffer<>();
|
||||
scan(selector);
|
||||
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
|
||||
@ -1140,7 +1142,7 @@ public class Flow {
|
||||
}
|
||||
}
|
||||
|
||||
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
pendingExits = new ListBuffer<>();
|
||||
for (JCTree resource : tree.resources) {
|
||||
if (resource instanceof JCVariableDecl) {
|
||||
@ -1204,7 +1206,7 @@ public class Flow {
|
||||
if (tree.finalizer != null) {
|
||||
List<Type> savedThrown = thrown;
|
||||
thrown = List.nil();
|
||||
ListBuffer<FlowPendingExit> exits = pendingExits;
|
||||
ListBuffer<PendingExit> exits = pendingExits;
|
||||
pendingExits = prevPendingExits;
|
||||
scan(tree.finalizer);
|
||||
if (!tree.finallyCanCompleteNormally) {
|
||||
@ -1221,7 +1223,7 @@ public class Flow {
|
||||
}
|
||||
} else {
|
||||
thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry));
|
||||
ListBuffer<FlowPendingExit> exits = pendingExits;
|
||||
ListBuffer<PendingExit> exits = pendingExits;
|
||||
pendingExits = prevPendingExits;
|
||||
while (exits.nonEmpty()) pendingExits.append(exits.next());
|
||||
}
|
||||
@ -1267,16 +1269,16 @@ public class Flow {
|
||||
public void visitBreak(JCBreak tree) {
|
||||
if (tree.isValueBreak())
|
||||
scan(tree.value);
|
||||
recordExit(new FlowPendingExit(tree, null));
|
||||
recordExit(new PendingExit(tree));
|
||||
}
|
||||
|
||||
public void visitContinue(JCContinue tree) {
|
||||
recordExit(new FlowPendingExit(tree, null));
|
||||
recordExit(new PendingExit(tree));
|
||||
}
|
||||
|
||||
public void visitReturn(JCReturn tree) {
|
||||
scan(tree.expr);
|
||||
recordExit(new FlowPendingExit(tree, null));
|
||||
recordExit(new PendingExit(tree));
|
||||
}
|
||||
|
||||
public void visitThrow(JCThrow tree) {
|
||||
@ -1343,18 +1345,18 @@ public class Flow {
|
||||
}
|
||||
List<Type> prevCaught = caught;
|
||||
List<Type> prevThrown = thrown;
|
||||
ListBuffer<FlowPendingExit> prevPending = pendingExits;
|
||||
ListBuffer<PendingExit> prevPending = pendingExits;
|
||||
try {
|
||||
pendingExits = new ListBuffer<>();
|
||||
caught = tree.getDescriptorType(types).getThrownTypes();
|
||||
thrown = List.nil();
|
||||
scan(tree.body);
|
||||
List<FlowPendingExit> exits = pendingExits.toList();
|
||||
List<PendingExit> exits = pendingExits.toList();
|
||||
pendingExits = new ListBuffer<>();
|
||||
while (exits.nonEmpty()) {
|
||||
FlowPendingExit exit = exits.head;
|
||||
PendingExit exit = exits.head;
|
||||
exits = exits.tail;
|
||||
if (exit.thrown == null) {
|
||||
if (!(exit instanceof ThrownPendingExit)) {
|
||||
Assert.check(exit.tree.hasTag(RETURN));
|
||||
} else {
|
||||
// uncaught throws will be reported later
|
||||
@ -1488,7 +1490,7 @@ public class Flow {
|
||||
}
|
||||
List<Type> prevCaught = caught;
|
||||
List<Type> prevThrown = thrown;
|
||||
ListBuffer<FlowPendingExit> prevPending = pendingExits;
|
||||
ListBuffer<PendingExit> prevPending = pendingExits;
|
||||
inLambda = true;
|
||||
try {
|
||||
pendingExits = new ListBuffer<>();
|
||||
@ -1517,7 +1519,7 @@ public class Flow {
|
||||
* effectively-final local variables/parameters.
|
||||
*/
|
||||
|
||||
public class AssignAnalyzer extends BaseAnalyzer<AssignAnalyzer.AssignPendingExit> {
|
||||
public class AssignAnalyzer extends BaseAnalyzer {
|
||||
|
||||
/** The set of definitely assigned variables.
|
||||
*/
|
||||
@ -1835,7 +1837,7 @@ public class Flow {
|
||||
JCClassDecl classDefPrev = classDef;
|
||||
int firstadrPrev = firstadr;
|
||||
int nextadrPrev = nextadr;
|
||||
ListBuffer<AssignPendingExit> pendingExitsPrev = pendingExits;
|
||||
ListBuffer<PendingExit> pendingExitsPrev = pendingExits;
|
||||
|
||||
pendingExits = new ListBuffer<>();
|
||||
if (tree.name != names.empty) {
|
||||
@ -1970,14 +1972,15 @@ public class Flow {
|
||||
}
|
||||
}
|
||||
}
|
||||
List<AssignPendingExit> exits = pendingExits.toList();
|
||||
List<PendingExit> exits = pendingExits.toList();
|
||||
pendingExits = new ListBuffer<>();
|
||||
while (exits.nonEmpty()) {
|
||||
AssignPendingExit exit = exits.head;
|
||||
PendingExit exit = exits.head;
|
||||
exits = exits.tail;
|
||||
Assert.check(exit.tree.hasTag(RETURN), exit.tree);
|
||||
if (isInitialConstructor) {
|
||||
inits.assign(exit.exit_inits);
|
||||
Assert.check(exit instanceof AssignPendingExit);
|
||||
inits.assign(((AssignPendingExit) exit).exit_inits);
|
||||
for (int i = firstadr; i < nextadr; i++) {
|
||||
checkInit(exit.tree.pos(), vardecls[i].sym);
|
||||
}
|
||||
@ -2027,7 +2030,7 @@ public class Flow {
|
||||
}
|
||||
|
||||
public void visitDoLoop(JCDoWhileLoop tree) {
|
||||
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
FlowKind prevFlowKind = flowKind;
|
||||
flowKind = FlowKind.NORMAL;
|
||||
final Bits initsSkip = new Bits(true);
|
||||
@ -2059,7 +2062,7 @@ public class Flow {
|
||||
}
|
||||
|
||||
public void visitWhileLoop(JCWhileLoop tree) {
|
||||
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
FlowKind prevFlowKind = flowKind;
|
||||
flowKind = FlowKind.NORMAL;
|
||||
final Bits initsSkip = new Bits(true);
|
||||
@ -2095,7 +2098,7 @@ public class Flow {
|
||||
}
|
||||
|
||||
public void visitForLoop(JCForLoop tree) {
|
||||
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
FlowKind prevFlowKind = flowKind;
|
||||
flowKind = FlowKind.NORMAL;
|
||||
int nextadrPrev = nextadr;
|
||||
@ -2143,7 +2146,7 @@ public class Flow {
|
||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||
visitVarDef(tree.var);
|
||||
|
||||
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
FlowKind prevFlowKind = flowKind;
|
||||
flowKind = FlowKind.NORMAL;
|
||||
int nextadrPrev = nextadr;
|
||||
@ -2174,7 +2177,7 @@ public class Flow {
|
||||
}
|
||||
|
||||
public void visitLabelled(JCLabeledStatement tree) {
|
||||
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
pendingExits = new ListBuffer<>();
|
||||
scan(tree.body);
|
||||
resolveBreaks(tree, prevPendingExits);
|
||||
@ -2189,7 +2192,7 @@ public class Flow {
|
||||
}
|
||||
|
||||
private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
|
||||
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
pendingExits = new ListBuffer<>();
|
||||
int nextadrPrev = nextadr;
|
||||
scanExpr(selector);
|
||||
@ -2245,7 +2248,7 @@ public class Flow {
|
||||
public void visitTry(JCTry tree) {
|
||||
ListBuffer<JCVariableDecl> resourceVarDecls = new ListBuffer<>();
|
||||
final Bits uninitsTryPrev = new Bits(uninitsTry);
|
||||
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
|
||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||
pendingExits = new ListBuffer<>();
|
||||
final Bits initsTry = new Bits(inits);
|
||||
uninitsTry.assign(uninits);
|
||||
@ -2302,7 +2305,7 @@ public class Flow {
|
||||
if (tree.finalizer != null) {
|
||||
inits.assign(initsTry);
|
||||
uninits.assign(uninitsTry);
|
||||
ListBuffer<AssignPendingExit> exits = pendingExits;
|
||||
ListBuffer<PendingExit> exits = pendingExits;
|
||||
pendingExits = prevPendingExits;
|
||||
scan(tree.finalizer);
|
||||
if (!tree.finallyCanCompleteNormally) {
|
||||
@ -2312,10 +2315,10 @@ public class Flow {
|
||||
// FIX: this doesn't preserve source order of exits in catch
|
||||
// versus finally!
|
||||
while (exits.nonEmpty()) {
|
||||
AssignPendingExit exit = exits.next();
|
||||
if (exit.exit_inits != null) {
|
||||
exit.exit_inits.orSet(inits);
|
||||
exit.exit_uninits.andSet(uninits);
|
||||
PendingExit exit = exits.next();
|
||||
if (exit instanceof AssignPendingExit) {
|
||||
((AssignPendingExit) exit).exit_inits.orSet(inits);
|
||||
((AssignPendingExit) exit).exit_uninits.andSet(uninits);
|
||||
}
|
||||
pendingExits.append(exit);
|
||||
}
|
||||
@ -2324,7 +2327,7 @@ public class Flow {
|
||||
} else {
|
||||
inits.assign(initsEnd);
|
||||
uninits.assign(uninitsEnd);
|
||||
ListBuffer<AssignPendingExit> exits = pendingExits;
|
||||
ListBuffer<PendingExit> exits = pendingExits;
|
||||
pendingExits = prevPendingExits;
|
||||
while (exits.nonEmpty()) pendingExits.append(exits.next());
|
||||
}
|
||||
@ -2390,8 +2393,34 @@ public class Flow {
|
||||
|
||||
@Override
|
||||
public void visitBreak(JCBreak tree) {
|
||||
if (tree.isValueBreak())
|
||||
if (tree.isValueBreak()) {
|
||||
if (tree.target.hasTag(SWITCH_EXPRESSION)) {
|
||||
JCSwitchExpression expr = (JCSwitchExpression) tree.target;
|
||||
if (expr.type.hasTag(BOOLEAN)) {
|
||||
scanCond(tree.value);
|
||||
Bits initsAfterBreakWhenTrue = new Bits(initsWhenTrue);
|
||||
Bits initsAfterBreakWhenFalse = new Bits(initsWhenFalse);
|
||||
Bits uninitsAfterBreakWhenTrue = new Bits(uninitsWhenTrue);
|
||||
Bits uninitsAfterBreakWhenFalse = new Bits(uninitsWhenFalse);
|
||||
PendingExit exit = new PendingExit(tree) {
|
||||
@Override
|
||||
void resolveJump() {
|
||||
if (!inits.isReset()) {
|
||||
split(true);
|
||||
}
|
||||
initsWhenTrue.andSet(initsAfterBreakWhenTrue);
|
||||
initsWhenFalse.andSet(initsAfterBreakWhenFalse);
|
||||
uninitsWhenTrue.andSet(uninitsAfterBreakWhenTrue);
|
||||
uninitsWhenFalse.andSet(uninitsAfterBreakWhenFalse);
|
||||
}
|
||||
};
|
||||
merge();
|
||||
recordExit(exit);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
scan(tree.value);
|
||||
}
|
||||
recordExit(new AssignPendingExit(tree, inits, uninits));
|
||||
}
|
||||
|
||||
@ -2428,7 +2457,7 @@ public class Flow {
|
||||
final Bits prevInits = new Bits(inits);
|
||||
int returnadrPrev = returnadr;
|
||||
int nextadrPrev = nextadr;
|
||||
ListBuffer<AssignPendingExit> prevPending = pendingExits;
|
||||
ListBuffer<PendingExit> prevPending = pendingExits;
|
||||
try {
|
||||
returnadr = nextadr;
|
||||
pendingExits = new ListBuffer<>();
|
||||
@ -2618,7 +2647,7 @@ public class Flow {
|
||||
* As effectively final variables are marked as such during DA/DU, this pass must run after
|
||||
* AssignAnalyzer.
|
||||
*/
|
||||
class CaptureAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
|
||||
class CaptureAnalyzer extends BaseAnalyzer {
|
||||
|
||||
JCTree currentTree; //local class or lambda
|
||||
|
||||
|
@ -63,6 +63,7 @@ import com.sun.tools.javac.tree.JCTree.JCCase;
|
||||
import com.sun.tools.javac.tree.JCTree.JCExpression;
|
||||
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT;
|
||||
import com.sun.tools.javac.tree.JCTree.JCSwitchExpression;
|
||||
import static com.sun.tools.javac.tree.JCTree.Tag.*;
|
||||
|
||||
/** This pass translates away some syntactic sugar: inner classes,
|
||||
@ -3362,14 +3363,29 @@ public class Lower extends TreeTranslator {
|
||||
}
|
||||
|
||||
public void visitSwitch(JCSwitch tree) {
|
||||
//expand multiple label cases:
|
||||
ListBuffer<JCCase> cases = new ListBuffer<>();
|
||||
handleSwitch(tree, tree.selector, tree.cases);
|
||||
}
|
||||
|
||||
for (JCCase c : tree.cases) {
|
||||
@Override
|
||||
public void visitSwitchExpression(JCSwitchExpression tree) {
|
||||
if (tree.cases.stream().noneMatch(c -> c.pats.isEmpty())) {
|
||||
JCThrow thr = make.Throw(makeNewClass(syms.incompatibleClassChangeErrorType,
|
||||
List.nil()));
|
||||
JCCase c = make.Case(JCCase.STATEMENT, List.nil(), List.of(thr), null);
|
||||
tree.cases = tree.cases.append(c);
|
||||
}
|
||||
handleSwitch(tree, tree.selector, tree.cases);
|
||||
}
|
||||
|
||||
private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
|
||||
//expand multiple label cases:
|
||||
ListBuffer<JCCase> convertedCases = new ListBuffer<>();
|
||||
|
||||
for (JCCase c : cases) {
|
||||
switch (c.pats.size()) {
|
||||
case 0: //default
|
||||
case 1: //single label
|
||||
cases.append(c);
|
||||
convertedCases.append(c);
|
||||
break;
|
||||
default: //multiple labels, expand:
|
||||
//case C1, C2, C3: ...
|
||||
@ -3379,19 +3395,19 @@ public class Lower extends TreeTranslator {
|
||||
//case C3: ...
|
||||
List<JCExpression> patterns = c.pats;
|
||||
while (patterns.tail.nonEmpty()) {
|
||||
cases.append(make_at(c.pos()).Case(JCCase.STATEMENT,
|
||||
convertedCases.append(make_at(c.pos()).Case(JCCase.STATEMENT,
|
||||
List.of(patterns.head),
|
||||
List.nil(),
|
||||
null));
|
||||
patterns = patterns.tail;
|
||||
}
|
||||
c.pats = patterns;
|
||||
cases.append(c);
|
||||
convertedCases.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (JCCase c : cases) {
|
||||
for (JCCase c : convertedCases) {
|
||||
if (c.caseKind == JCCase.RULE && c.completesNormally) {
|
||||
JCBreak b = make_at(c.pos()).Break(null);
|
||||
b.target = tree;
|
||||
@ -3399,58 +3415,75 @@ public class Lower extends TreeTranslator {
|
||||
}
|
||||
}
|
||||
|
||||
tree.cases = cases.toList();
|
||||
cases = convertedCases.toList();
|
||||
|
||||
Type selsuper = types.supertype(tree.selector.type);
|
||||
Type selsuper = types.supertype(selector.type);
|
||||
boolean enumSwitch = selsuper != null &&
|
||||
(tree.selector.type.tsym.flags() & ENUM) != 0;
|
||||
(selector.type.tsym.flags() & ENUM) != 0;
|
||||
boolean stringSwitch = selsuper != null &&
|
||||
types.isSameType(tree.selector.type, syms.stringType);
|
||||
Type target = enumSwitch ? tree.selector.type :
|
||||
types.isSameType(selector.type, syms.stringType);
|
||||
Type target = enumSwitch ? selector.type :
|
||||
(stringSwitch? syms.stringType : syms.intType);
|
||||
tree.selector = translate(tree.selector, target);
|
||||
tree.cases = translateCases(tree.cases);
|
||||
selector = translate(selector, target);
|
||||
cases = translateCases(cases);
|
||||
if (tree.hasTag(SWITCH)) {
|
||||
((JCSwitch) tree).selector = selector;
|
||||
((JCSwitch) tree).cases = cases;
|
||||
} else if (tree.hasTag(SWITCH_EXPRESSION)) {
|
||||
((JCSwitchExpression) tree).selector = selector;
|
||||
((JCSwitchExpression) tree).cases = cases;
|
||||
} else {
|
||||
Assert.error();
|
||||
}
|
||||
if (enumSwitch) {
|
||||
result = visitEnumSwitch(tree);
|
||||
result = visitEnumSwitch(tree, selector, cases);
|
||||
} else if (stringSwitch) {
|
||||
result = visitStringSwitch(tree);
|
||||
result = visitStringSwitch(tree, selector, cases);
|
||||
} else {
|
||||
result = tree;
|
||||
}
|
||||
}
|
||||
|
||||
public JCTree visitEnumSwitch(JCSwitch tree) {
|
||||
TypeSymbol enumSym = tree.selector.type.tsym;
|
||||
public JCTree visitEnumSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
|
||||
TypeSymbol enumSym = selector.type.tsym;
|
||||
EnumMapping map = mapForEnum(tree.pos(), enumSym);
|
||||
make_at(tree.pos());
|
||||
Symbol ordinalMethod = lookupMethod(tree.pos(),
|
||||
names.ordinal,
|
||||
tree.selector.type,
|
||||
selector.type,
|
||||
List.nil());
|
||||
JCArrayAccess selector = make.Indexed(map.mapVar,
|
||||
make.App(make.Select(tree.selector,
|
||||
JCArrayAccess newSelector = make.Indexed(map.mapVar,
|
||||
make.App(make.Select(selector,
|
||||
ordinalMethod)));
|
||||
ListBuffer<JCCase> cases = new ListBuffer<>();
|
||||
for (JCCase c : tree.cases) {
|
||||
ListBuffer<JCCase> newCases = new ListBuffer<>();
|
||||
for (JCCase c : cases) {
|
||||
if (c.pats.nonEmpty()) {
|
||||
VarSymbol label = (VarSymbol)TreeInfo.symbol(c.pats.head);
|
||||
JCLiteral pat = map.forConstant(label);
|
||||
cases.append(make.Case(JCCase.STATEMENT, List.of(pat), c.stats, null));
|
||||
newCases.append(make.Case(JCCase.STATEMENT, List.of(pat), c.stats, null));
|
||||
} else {
|
||||
cases.append(c);
|
||||
newCases.append(c);
|
||||
}
|
||||
}
|
||||
JCSwitch enumSwitch = make.Switch(selector, cases.toList());
|
||||
JCTree enumSwitch;
|
||||
if (tree.hasTag(SWITCH)) {
|
||||
enumSwitch = make.Switch(newSelector, newCases.toList());
|
||||
} else if (tree.hasTag(SWITCH_EXPRESSION)) {
|
||||
enumSwitch = make.SwitchExpression(newSelector, newCases.toList());
|
||||
enumSwitch.setType(tree.type);
|
||||
} else {
|
||||
Assert.error();
|
||||
throw new AssertionError();
|
||||
}
|
||||
patchTargets(enumSwitch, tree, enumSwitch);
|
||||
return enumSwitch;
|
||||
}
|
||||
|
||||
public JCTree visitStringSwitch(JCSwitch tree) {
|
||||
List<JCCase> caseList = tree.getCases();
|
||||
public JCTree visitStringSwitch(JCTree tree, JCExpression selector, List<JCCase> caseList) {
|
||||
int alternatives = caseList.size();
|
||||
|
||||
if (alternatives == 0) { // Strange but legal possibility
|
||||
return make.at(tree.pos()).Exec(attr.makeNullCheck(tree.getExpression()));
|
||||
if (alternatives == 0) { // Strange but legal possibility (only legal for switch statement)
|
||||
return make.at(tree.pos()).Exec(attr.makeNullCheck(selector));
|
||||
} else {
|
||||
/*
|
||||
* The general approach used is to translate a single
|
||||
@ -3551,7 +3584,7 @@ public class Lower extends TreeTranslator {
|
||||
names.fromString("s" + tree.pos + target.syntheticNameChar()),
|
||||
syms.stringType,
|
||||
currentMethodSym);
|
||||
stmtList.append(make.at(tree.pos()).VarDef(dollar_s, tree.getExpression()).setType(dollar_s.type));
|
||||
stmtList.append(make.at(tree.pos()).VarDef(dollar_s, selector).setType(dollar_s.type));
|
||||
|
||||
VarSymbol dollar_tmp = new VarSymbol(SYNTHETIC,
|
||||
names.fromString("tmp" + tree.pos + target.syntheticNameChar()),
|
||||
@ -3601,12 +3634,7 @@ public class Lower extends TreeTranslator {
|
||||
// position map.
|
||||
|
||||
ListBuffer<JCCase> lb = new ListBuffer<>();
|
||||
JCSwitch switch2 = make.Switch(make.Ident(dollar_tmp), lb.toList());
|
||||
for(JCCase oneCase : caseList ) {
|
||||
// Rewire up old unlabeled break statements to the
|
||||
// replacement switch being created.
|
||||
patchTargets(oneCase, tree, switch2);
|
||||
|
||||
boolean isDefault = (oneCase.pats.isEmpty());
|
||||
JCExpression caseExpr;
|
||||
if (isDefault)
|
||||
@ -3617,85 +3645,44 @@ public class Lower extends TreeTranslator {
|
||||
}
|
||||
|
||||
lb.append(make.Case(JCCase.STATEMENT, caseExpr == null ? List.nil() : List.of(caseExpr),
|
||||
oneCase.getStatements(), null));
|
||||
oneCase.stats, null));
|
||||
}
|
||||
|
||||
switch2.cases = lb.toList();
|
||||
stmtList.append(switch2);
|
||||
if (tree.hasTag(SWITCH)) {
|
||||
JCSwitch switch2 = make.Switch(make.Ident(dollar_tmp), lb.toList());
|
||||
// Rewire up old unlabeled break statements to the
|
||||
// replacement switch being created.
|
||||
patchTargets(switch2, tree, switch2);
|
||||
|
||||
return make.Block(0L, stmtList.toList());
|
||||
stmtList.append(switch2);
|
||||
|
||||
return make.Block(0L, stmtList.toList());
|
||||
} else {
|
||||
JCSwitchExpression switch2 = make.SwitchExpression(make.Ident(dollar_tmp), lb.toList());
|
||||
|
||||
// Rewire up old unlabeled break statements to the
|
||||
// replacement switch being created.
|
||||
patchTargets(switch2, tree, switch2);
|
||||
|
||||
switch2.setType(tree.type);
|
||||
|
||||
LetExpr res = make.LetExpr(stmtList.toList(), switch2);
|
||||
|
||||
res.needsCond = true;
|
||||
res.setType(tree.type);
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSwitchExpression(JCSwitchExpression tree) {
|
||||
//translates switch expression to statement switch:
|
||||
//switch (selector) {
|
||||
// case C: break value;
|
||||
// ...
|
||||
//}
|
||||
//=>
|
||||
//(letexpr T exprswitch$;
|
||||
// switch (selector) {
|
||||
// case C: { exprswitch$ = value; break; }
|
||||
// }
|
||||
// exprswitch$
|
||||
//)
|
||||
VarSymbol dollar_switchexpr = new VarSymbol(Flags.FINAL|Flags.SYNTHETIC,
|
||||
names.fromString("exprswitch" + tree.pos + target.syntheticNameChar()),
|
||||
tree.type,
|
||||
currentMethodSym);
|
||||
|
||||
ListBuffer<JCStatement> stmtList = new ListBuffer<>();
|
||||
|
||||
stmtList.append(make.at(tree.pos()).VarDef(dollar_switchexpr, null).setType(dollar_switchexpr.type));
|
||||
JCSwitch switchStatement = make.Switch(tree.selector, null);
|
||||
switchStatement.cases =
|
||||
tree.cases.stream()
|
||||
.map(c -> convertCase(dollar_switchexpr, switchStatement, tree, c))
|
||||
.collect(List.collector());
|
||||
if (tree.cases.stream().noneMatch(c -> c.pats.isEmpty())) {
|
||||
JCThrow thr = make.Throw(makeNewClass(syms.incompatibleClassChangeErrorType,
|
||||
List.nil()));
|
||||
JCCase c = make.Case(JCCase.STATEMENT, List.nil(), List.of(thr), null);
|
||||
switchStatement.cases = switchStatement.cases.append(c);
|
||||
public void visitBreak(JCBreak tree) {
|
||||
if (tree.isValueBreak()) {
|
||||
tree.value = translate(tree.value, tree.target.type);
|
||||
}
|
||||
|
||||
stmtList.append(translate(switchStatement));
|
||||
|
||||
result = make.LetExpr(stmtList.toList(), make.Ident(dollar_switchexpr))
|
||||
.setType(dollar_switchexpr.type);
|
||||
result = tree;
|
||||
}
|
||||
//where:
|
||||
private JCCase convertCase(VarSymbol dollar_switchexpr, JCSwitch switchStatement,
|
||||
JCSwitchExpression switchExpr, JCCase c) {
|
||||
make.at(c.pos());
|
||||
ListBuffer<JCStatement> statements = new ListBuffer<>();
|
||||
statements.addAll(new TreeTranslator() {
|
||||
@Override
|
||||
public void visitLambda(JCLambda tree) {}
|
||||
@Override
|
||||
public void visitClassDef(JCClassDecl tree) {}
|
||||
@Override
|
||||
public void visitMethodDef(JCMethodDecl tree) {}
|
||||
@Override
|
||||
public void visitBreak(JCBreak tree) {
|
||||
if (tree.target == switchExpr) {
|
||||
tree.target = switchStatement;
|
||||
JCExpressionStatement assignment =
|
||||
make.Exec(make.Assign(make.Ident(dollar_switchexpr),
|
||||
translate(tree.value))
|
||||
.setType(dollar_switchexpr.type));
|
||||
result = make.Block(0, List.of(assignment,
|
||||
tree));
|
||||
tree.value = null;
|
||||
} else {
|
||||
result = tree;
|
||||
}
|
||||
}
|
||||
}.translate(c.stats));
|
||||
return make.Case(JCCase.STATEMENT, c.pats, statements.toList(), null);
|
||||
}
|
||||
|
||||
public void visitNewArray(JCNewArray tree) {
|
||||
tree.elemtype = translate(tree.elemtype);
|
||||
|
@ -33,6 +33,7 @@ import com.sun.tools.javac.util.*;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import com.sun.tools.javac.tree.JCTree.*;
|
||||
import com.sun.tools.javac.tree.EndPosTable;
|
||||
import com.sun.tools.javac.tree.JCTree.JCSwitchExpression;
|
||||
|
||||
/** This class contains the CharacterRangeTable for some method
|
||||
* and the hashtable for mapping trees or lists of trees to their
|
||||
@ -311,6 +312,14 @@ implements CRTFlags {
|
||||
result = sr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSwitchExpression(JCSwitchExpression tree) {
|
||||
SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
|
||||
sr.mergeWith(csp(tree.selector));
|
||||
sr.mergeWith(cspCases(tree.cases));
|
||||
result = sr;
|
||||
}
|
||||
|
||||
public void visitCase(JCCase tree) {
|
||||
SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
|
||||
sr.mergeWith(csp(tree.pats));
|
||||
|
@ -1220,7 +1220,7 @@ public class Code {
|
||||
}
|
||||
|
||||
public boolean isStatementStart() {
|
||||
return state.stacksize == letExprStackPos;
|
||||
return !alive || state.stacksize == letExprStackPos;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package com.sun.tools.javac.jvm;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import com.sun.tools.javac.tree.TreeInfo.PosKind;
|
||||
import com.sun.tools.javac.util.*;
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
|
||||
@ -161,6 +163,10 @@ public class Gen extends JCTree.Visitor {
|
||||
*/
|
||||
EndPosTable endPosTable;
|
||||
|
||||
boolean inCondSwitchExpression;
|
||||
Chain switchExpressionTrueChain;
|
||||
Chain switchExpressionFalseChain;
|
||||
|
||||
/** Generate code to load an integer constant.
|
||||
* @param n The integer to be loaded.
|
||||
*/
|
||||
@ -719,6 +725,42 @@ public class Gen extends JCTree.Visitor {
|
||||
Code.mergeChains(falseJumps, second.falseJumps));
|
||||
if (markBranches) result.tree = tree.falsepart;
|
||||
return result;
|
||||
} else if (inner_tree.hasTag(SWITCH_EXPRESSION)) {
|
||||
boolean prevInCondSwitchExpression = inCondSwitchExpression;
|
||||
Chain prevSwitchExpressionTrueChain = switchExpressionTrueChain;
|
||||
Chain prevSwitchExpressionFalseChain = switchExpressionFalseChain;
|
||||
try {
|
||||
inCondSwitchExpression = true;
|
||||
switchExpressionTrueChain = null;
|
||||
switchExpressionFalseChain = null;
|
||||
try {
|
||||
doHandleSwitchExpression((JCSwitchExpression) inner_tree);
|
||||
} catch (CompletionFailure ex) {
|
||||
chk.completionError(_tree.pos(), ex);
|
||||
code.state.stacksize = 1;
|
||||
}
|
||||
CondItem result = items.makeCondItem(goto_,
|
||||
switchExpressionTrueChain,
|
||||
switchExpressionFalseChain);
|
||||
if (markBranches) result.tree = _tree;
|
||||
return result;
|
||||
} finally {
|
||||
inCondSwitchExpression = prevInCondSwitchExpression;
|
||||
switchExpressionTrueChain = prevSwitchExpressionTrueChain;
|
||||
switchExpressionFalseChain = prevSwitchExpressionFalseChain;
|
||||
}
|
||||
} else if (inner_tree.hasTag(LETEXPR) && ((LetExpr) inner_tree).needsCond) {
|
||||
LetExpr tree = (LetExpr) inner_tree;
|
||||
int limit = code.nextreg;
|
||||
int prevLetExprStart = code.setLetExprStackPos(code.state.stacksize);
|
||||
try {
|
||||
genStats(tree.defs, env);
|
||||
} finally {
|
||||
code.setLetExprStackPos(prevLetExprStart);
|
||||
}
|
||||
CondItem result = genCond(tree.expr, markBranches);
|
||||
code.endScopes(limit);
|
||||
return result;
|
||||
} else {
|
||||
CondItem result = genExpr(_tree, syms.booleanType).mkCond();
|
||||
if (markBranches) result.tree = _tree;
|
||||
@ -1119,25 +1161,50 @@ public class Gen extends JCTree.Visitor {
|
||||
}
|
||||
|
||||
public void visitSwitch(JCSwitch tree) {
|
||||
handleSwitch(tree, tree.selector, tree.cases);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSwitchExpression(JCSwitchExpression tree) {
|
||||
code.resolvePending();
|
||||
boolean prevInCondSwitchExpression = inCondSwitchExpression;
|
||||
try {
|
||||
inCondSwitchExpression = false;
|
||||
doHandleSwitchExpression(tree);
|
||||
} finally {
|
||||
inCondSwitchExpression = prevInCondSwitchExpression;
|
||||
}
|
||||
result = items.makeStackItem(pt);
|
||||
}
|
||||
|
||||
private void doHandleSwitchExpression(JCSwitchExpression tree) {
|
||||
int prevLetExprStart = code.setLetExprStackPos(code.state.stacksize);
|
||||
try {
|
||||
handleSwitch(tree, tree.selector, tree.cases);
|
||||
} finally {
|
||||
code.setLetExprStackPos(prevLetExprStart);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSwitch(JCTree swtch, JCExpression selector, List<JCCase> cases) {
|
||||
int limit = code.nextreg;
|
||||
Assert.check(!tree.selector.type.hasTag(CLASS));
|
||||
Assert.check(!selector.type.hasTag(CLASS));
|
||||
int startpcCrt = genCrt ? code.curCP() : 0;
|
||||
Assert.check(code.isStatementStart());
|
||||
Item sel = genExpr(tree.selector, syms.intType);
|
||||
List<JCCase> cases = tree.cases;
|
||||
Item sel = genExpr(selector, syms.intType);
|
||||
if (cases.isEmpty()) {
|
||||
// We are seeing: switch <sel> {}
|
||||
sel.load().drop();
|
||||
if (genCrt)
|
||||
code.crt.put(TreeInfo.skipParens(tree.selector),
|
||||
code.crt.put(TreeInfo.skipParens(selector),
|
||||
CRT_FLOW_CONTROLLER, startpcCrt, code.curCP());
|
||||
} else {
|
||||
// We are seeing a nonempty switch.
|
||||
sel.load();
|
||||
if (genCrt)
|
||||
code.crt.put(TreeInfo.skipParens(tree.selector),
|
||||
code.crt.put(TreeInfo.skipParens(selector),
|
||||
CRT_FLOW_CONTROLLER, startpcCrt, code.curCP());
|
||||
Env<GenContext> switchEnv = env.dup(tree, new GenContext());
|
||||
Env<GenContext> switchEnv = env.dup(swtch, new GenContext());
|
||||
switchEnv.info.isSwitch = true;
|
||||
|
||||
// Compute number of labels and minimum and maximum label values.
|
||||
@ -1593,10 +1660,35 @@ public class Gen extends JCTree.Visitor {
|
||||
|
||||
public void visitBreak(JCBreak tree) {
|
||||
int tmpPos = code.pendingStatPos;
|
||||
Assert.check(code.isStatementStart());
|
||||
Env<GenContext> targetEnv = unwind(tree.target, env);
|
||||
code.pendingStatPos = tmpPos;
|
||||
Assert.check(code.isStatementStart());
|
||||
targetEnv.info.addExit(code.branch(goto_));
|
||||
if (tree.isValueBreak()) {
|
||||
if (inCondSwitchExpression) {
|
||||
CondItem value = genCond(tree.value, CRT_FLOW_TARGET);
|
||||
Chain falseJumps = value.jumpFalse();
|
||||
code.resolve(value.trueJumps);
|
||||
Chain trueJumps = code.branch(goto_);
|
||||
if (switchExpressionTrueChain == null) {
|
||||
switchExpressionTrueChain = trueJumps;
|
||||
} else {
|
||||
switchExpressionTrueChain =
|
||||
Code.mergeChains(switchExpressionTrueChain, trueJumps);
|
||||
}
|
||||
if (switchExpressionFalseChain == null) {
|
||||
switchExpressionFalseChain = falseJumps;
|
||||
} else {
|
||||
switchExpressionFalseChain =
|
||||
Code.mergeChains(switchExpressionFalseChain, falseJumps);
|
||||
}
|
||||
} else {
|
||||
genExpr(tree.value, pt).load();
|
||||
code.state.forceStackTop(tree.target.type);
|
||||
targetEnv.info.addExit(code.branch(goto_));
|
||||
}
|
||||
} else {
|
||||
targetEnv.info.addExit(code.branch(goto_));
|
||||
}
|
||||
endFinalizerGaps(env, targetEnv);
|
||||
}
|
||||
|
||||
|
@ -3016,6 +3016,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
||||
public static class LetExpr extends JCExpression {
|
||||
public List<JCStatement> defs;
|
||||
public JCExpression expr;
|
||||
/**true if a expr should be run through Gen.genCond:*/
|
||||
public boolean needsCond;
|
||||
protected LetExpr(List<JCStatement> defs, JCExpression expr) {
|
||||
this.defs = defs;
|
||||
this.expr = expr;
|
||||
|
184
test/langtools/tools/javac/switchexpr/CRT.java
Normal file
184
test/langtools/tools/javac/switchexpr/CRT.java
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8214031
|
||||
* @summary Test the CharacterRangeTable generated for switch expressions
|
||||
* @library /tools/lib
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* jdk.jdeps/com.sun.tools.javap
|
||||
* @build toolbox.Assert toolbox.ToolBox toolbox.JavacTask
|
||||
* @run main CRT
|
||||
*/
|
||||
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import toolbox.JavacTask;
|
||||
import toolbox.JavapTask;
|
||||
import toolbox.Task.OutputKind;
|
||||
import toolbox.ToolBox;
|
||||
|
||||
public class CRT {
|
||||
public static void main(String... args) throws Exception {
|
||||
new CRT().run();
|
||||
}
|
||||
|
||||
private ToolBox tb = new ToolBox();
|
||||
|
||||
private void run() throws Exception {
|
||||
doTest(" private String convert(int i) {\n" +
|
||||
" String res;" +
|
||||
" switch (i) {\n" +
|
||||
" case 0: res = \"a\"; break;\n" +
|
||||
" default: res = \"\"; break;\n" +
|
||||
" };\n" +
|
||||
" return res;\n" +
|
||||
" }\n",
|
||||
"CharacterRangeTable:\n" +
|
||||
" 0, 0, c24, c25, 8 // 0, 0, 3:36, 3:37, flow-controller\n" +
|
||||
" 20, 22, 1015, 101f, 1 // 20, 22, 4:21, 4:31, statement\n" +
|
||||
" 23, 25, 1020, 1026, 1 // 23, 25, 4:32, 4:38, statement\n" +
|
||||
" 20, 25, 1015, 1026, 10 // 20, 25, 4:21, 4:38, flow-target\n" +
|
||||
" 26, 28, 1416, 141f, 1 // 26, 28, 5:22, 5:31, statement\n" +
|
||||
" 29, 31, 1420, 1426, 1 // 29, 31, 5:32, 5:38, statement\n" +
|
||||
" 26, 31, 1416, 1426, 10 // 26, 31, 5:22, 5:38, flow-target\n" +
|
||||
" 0, 31, c1c, 180a, 1 // 0, 31, 3:28, 6:10, statement\n" +
|
||||
" 32, 33, 1c09, 1c14, 1 // 32, 33, 7:09, 7:20, statement\n" +
|
||||
" 0, 33, 823, 2006, 2 // 0, 33, 2:35, 8:06, block\n");
|
||||
doTest(" private String convert(int i) {\n" +
|
||||
" return switch (i) {\n" +
|
||||
" case 0 -> \"a\";\n" +
|
||||
" default -> \"\";\n" +
|
||||
" };\n" +
|
||||
" }\n",
|
||||
"CharacterRangeTable:\n" +
|
||||
" 0, 0, c18, c19, 8 // 0, 0, 3:24, 3:25, flow-controller\n" +
|
||||
" 20, 24, 1017, 101b, 11 // 20, 24, 4:23, 4:27, statement, flow-target\n" +
|
||||
" 25, 29, 1418, 141b, 11 // 25, 29, 5:24, 5:27, statement, flow-target\n" +
|
||||
" 0, 30, c09, 180b, 1 // 0, 30, 3:09, 6:11, statement\n" +
|
||||
" 0, 30, 823, 1c06, 2 // 0, 30, 2:35, 7:06, block");
|
||||
doTest(" private boolean convert(int i) {\n" +
|
||||
" return switch (i) {\n" +
|
||||
" case 0 -> true;\n" +
|
||||
" default -> false;\n" +
|
||||
" } && i == 0;\n" +
|
||||
" }\n",
|
||||
"CharacterRangeTable:\n" +
|
||||
" 0, 0, c18, c19, 8 // 0, 0, 3:24, 3:25, flow-controller\n" +
|
||||
" 20, 22, 1017, 101c, 11 // 20, 22, 4:23, 4:28, statement, flow-target\n" +
|
||||
" 23, 25, 1418, 141e, 11 // 23, 25, 5:24, 5:30, statement, flow-target\n" +
|
||||
" 0, 25, c10, 180a, 8 // 0, 25, 3:16, 6:10, flow-controller\n" +
|
||||
" 26, 26, 180e, 1814, 10 // 26, 26, 6:14, 6:20, flow-target\n" +
|
||||
" 0, 35, c09, 1815, 1 // 0, 35, 3:09, 6:21, statement\n" +
|
||||
" 0, 35, 824, 1c06, 2 // 0, 35, 2:36, 7:06, block\n");
|
||||
doTest(" private boolean convert(int i) {\n" +
|
||||
" return i >= 0 ? i == 0\n" +
|
||||
" ? true\n" +
|
||||
" : false\n" +
|
||||
" : i == -1\n" +
|
||||
" ? false\n" +
|
||||
" : true;\n" +
|
||||
" }\n",
|
||||
"CharacterRangeTable:\n" +
|
||||
" 0, 0, c10, c16, 8 // 0, 0, 3:16, 3:22, flow-controller\n" +
|
||||
" 1, 3, c10, c16, 100 // 1, 3, 3:16, 3:22, branch-false\n" +
|
||||
" 4, 4, c19, c1f, 8 // 4, 4, 3:25, 3:31, flow-controller\n" +
|
||||
" 5, 7, c19, c1f, 100 // 5, 7, 3:25, 3:31, branch-false\n" +
|
||||
" 8, 8, 101b, 101f, 10 // 8, 8, 4:27, 4:31, flow-target\n" +
|
||||
" 12, 12, 141b, 1420, 10 // 12, 12, 5:27, 5:32, flow-target\n" +
|
||||
" 4, 12, c19, 1420, 10 // 4, 12, 3:25, 5:32, flow-target\n" +
|
||||
" 16, 17, 1819, 1820, 8 // 16, 17, 6:25, 6:32, flow-controller\n" +
|
||||
" 18, 20, 1819, 1820, 100 // 18, 20, 6:25, 6:32, branch-false\n" +
|
||||
" 21, 21, 1c1b, 1c20, 10 // 21, 21, 7:27, 7:32, flow-target\n" +
|
||||
" 25, 25, 201b, 201f, 10 // 25, 25, 8:27, 8:31, flow-target\n" +
|
||||
" 16, 25, 1819, 201f, 10 // 16, 25, 6:25, 8:31, flow-target\n" +
|
||||
" 0, 26, c09, 2020, 1 // 0, 26, 3:09, 8:32, statement\n" +
|
||||
" 0, 26, 824, 2406, 2 // 0, 26, 2:36, 9:06, block\n");
|
||||
doTest(" private boolean convert(int i) {\n" +
|
||||
" return i >= 0 ? switch (i) {\n" +
|
||||
" case 0 -> true;\n" +
|
||||
" default -> false;\n" +
|
||||
" } : switch (i) {\n" +
|
||||
" case -1 -> false;\n" +
|
||||
" default -> true;\n" +
|
||||
" };\n" +
|
||||
" }\n",
|
||||
"CharacterRangeTable:\n" +
|
||||
" 0, 0, c10, c16, 8 // 0, 0, 3:16, 3:22, flow-controller\n" +
|
||||
" 1, 3, c10, c16, 100 // 1, 3, 3:16, 3:22, branch-false\n" +
|
||||
" 4, 4, c21, c22, 8 // 4, 4, 3:33, 3:34, flow-controller\n" +
|
||||
" 24, 27, 1017, 101c, 11 // 24, 27, 4:23, 4:28, statement, flow-target\n" +
|
||||
" 28, 31, 1418, 141e, 11 // 28, 31, 5:24, 5:30, statement, flow-target\n" +
|
||||
" 4, 31, c19, 180a, 10 // 4, 31, 3:25, 6:10, flow-target\n" +
|
||||
" 35, 35, 1815, 1816, 8 // 35, 35, 6:21, 6:22, flow-controller\n" +
|
||||
" 56, 59, 1c18, 1c1e, 11 // 56, 59, 7:24, 7:30, statement, flow-target\n" +
|
||||
" 60, 63, 2018, 201d, 11 // 60, 63, 8:24, 8:29, statement, flow-target\n" +
|
||||
" 35, 63, 180d, 240a, 10 // 35, 63, 6:13, 9:10, flow-target\n" +
|
||||
" 0, 64, c09, 240b, 1 // 0, 64, 3:09, 9:11, statement\n" +
|
||||
" 0, 64, 824, 2806, 2 // 0, 64, 2:36, 10:06, block\n");
|
||||
}
|
||||
|
||||
private void doTest(String code, String expected) throws Exception {
|
||||
Path base = Paths.get(".");
|
||||
Path classes = base.resolve("classes");
|
||||
tb.createDirectories(classes);
|
||||
tb.cleanDirectory(classes);
|
||||
new JavacTask(tb)
|
||||
.options("-Xjcov",
|
||||
"--enable-preview",
|
||||
"-source", "12")
|
||||
.outdir(classes)
|
||||
.sources("public class Test {\n" +
|
||||
code +
|
||||
"}\n")
|
||||
.run()
|
||||
.writeAll();
|
||||
String out = new JavapTask(tb)
|
||||
.options("-private",
|
||||
"-verbose",
|
||||
"-s")
|
||||
.classpath(classes.toString())
|
||||
.classes("Test")
|
||||
.run()
|
||||
.writeAll()
|
||||
.getOutputLines(OutputKind.DIRECT)
|
||||
.stream()
|
||||
.collect(Collectors.joining("\n"));
|
||||
String crt = cutCRT(out);
|
||||
if (!expected.trim().equals(crt.trim())) {
|
||||
throw new AssertionError("Expected CharacterRangeTable not found, found: " + crt);
|
||||
}
|
||||
}
|
||||
|
||||
private static String cutCRT(String from) {
|
||||
int start = from.indexOf("CharacterRangeTable:", from.indexOf("convert(int);"));
|
||||
int end = from.indexOf("StackMapTable:");
|
||||
return from.substring(start, end);
|
||||
}
|
||||
|
||||
}
|
411
test/langtools/tools/javac/switchexpr/DefiniteAssignment1.java
Normal file
411
test/langtools/tools/javac/switchexpr/DefiniteAssignment1.java
Normal file
@ -0,0 +1,411 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8214031
|
||||
* @summary Verify that definite assignment when true works (legal code)
|
||||
* @compile --enable-preview --source 12 DefiniteAssignment1.java
|
||||
* @run main/othervm --enable-preview DefiniteAssignment1
|
||||
*/
|
||||
public class DefiniteAssignment1 {
|
||||
public static void main(String[] args) {
|
||||
int a = 0;
|
||||
boolean b = true;
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
boolean t1 = (b && switch(a) {
|
||||
case 0: break (x = 1) == 1 || true;
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t1) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
boolean t1 = (b && switch(a) {
|
||||
case 0: break (x = 1) == 1 || isTrue();
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t1) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
boolean t1a = (b && switch(a) {
|
||||
case 0: break (x = 1) == 1;
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t1a) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
boolean t1b = (switch(a) {
|
||||
case 0: break (x = 1) == 1;
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t1b) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
boolean t2 = !(b && switch(a) {
|
||||
case 0: break (x = 1) == 1 || true;
|
||||
default: break false;
|
||||
}) || x == 1;
|
||||
|
||||
if (!t2) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
boolean t2 = !(b && switch(a) {
|
||||
case 0: break (x = 1) == 1 || isTrue();
|
||||
default: break false;
|
||||
}) || x == 1;
|
||||
|
||||
if (!t2) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
boolean t3 = !(switch(a) {
|
||||
case 0: break (x = 1) == 1 || true;
|
||||
default: break false;
|
||||
}) || x == 2;
|
||||
|
||||
if (t3) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
boolean t3 = !(switch(a) {
|
||||
case 0: break (x = 1) == 1 || isTrue();
|
||||
default: break false;
|
||||
}) || x == 2;
|
||||
|
||||
if (t3) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
boolean t4 = (b && switch(a) {
|
||||
case 0: break (x = 1) == 1 || true;
|
||||
default: throw new IllegalStateException();
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t4) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
boolean t4 = (b && switch(a) {
|
||||
case 0: break (x = 1) == 1 || isTrue();
|
||||
default: throw new IllegalStateException();
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t4) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
String s = "a";
|
||||
|
||||
boolean t5 = (switch(s) {
|
||||
case "a": break (x = 1) == 1 || true;
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t5) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
String s = "a";
|
||||
|
||||
boolean t5 = (switch(s) {
|
||||
case "a": break (x = 1) == 1 || isTrue();
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t5) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
E e = E.B;
|
||||
|
||||
boolean t6 = (switch(e) {
|
||||
case B: break (x = 1) == 1 || true;
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t6) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
E e = E.B;
|
||||
|
||||
boolean t6 = (switch(e) {
|
||||
case B: break (x = 1) == 1 || isTrue();
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t6) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
int t7 = new DefiniteAssignment1().id(switch(0) {
|
||||
default -> true;
|
||||
} && (x = 1) == 1 && x == 1 ? 2 : -1);
|
||||
|
||||
if (t7 != 2) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
int t7 = new DefiniteAssignment1().id(switch(0) {
|
||||
default -> isTrue();
|
||||
} && (x = 1) == 1 && x == 1 ? 2 : -1);
|
||||
|
||||
if (t7 != 2) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
E e = E.B;
|
||||
|
||||
boolean t8 = (switch(e) {
|
||||
case A: x = 1; break true;
|
||||
case B: break (x = 1) == 1 || true;
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t8) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
E e = E.B;
|
||||
|
||||
boolean t8 = (switch(e) {
|
||||
case A: x = 1; break isTrue();
|
||||
case B: break (x = 1) == 1 || isTrue();
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t8) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
E e = E.A;
|
||||
|
||||
boolean t9 = (switch(e) {
|
||||
case A: x = 1; break true;
|
||||
case B: break (x = 1) == 1 || true;
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t9) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
E e = E.A;
|
||||
|
||||
boolean t9 = (switch(e) {
|
||||
case A: x = 1; break isTrue();
|
||||
case B: break (x = 1) == 1 || isTrue();
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!t9) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
E e = E.C;
|
||||
|
||||
boolean tA = (switch(e) {
|
||||
case A: x = 1; break true;
|
||||
case B: break (x = 1) == 1 || true;
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (tA) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
E e = E.C;
|
||||
|
||||
boolean tA = (switch(e) {
|
||||
case A: x = 1; break isTrue();
|
||||
case B: break (x = 1) == 1 || isTrue();
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (tA) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
final int x;
|
||||
E e = E.C;
|
||||
|
||||
boolean tA = (switch(e) {
|
||||
case A: x = 1; break true;
|
||||
case B: break (x = 2) == 2 || true;
|
||||
default: break false;
|
||||
}) || (x = 3) == 3; //x is definitelly unassigned here
|
||||
|
||||
if (x != 3) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
E e = E.A;
|
||||
|
||||
boolean tA = (switch(e) {
|
||||
case A: break isTrue() && (x = 1) == 1;
|
||||
case B: break (x = 1) == 1 || isTrue();
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!tA) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
E e = E.A;
|
||||
|
||||
boolean tA = (switch(e) {
|
||||
case A: break isTrue() && e != E.C ? (x = 1) == 1 && e != E.B : false;
|
||||
case B: break (x = 1) == 1 || true;
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!tA) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
E e = E.A;
|
||||
|
||||
boolean tA = (switch(e) {
|
||||
case A: break isTrue() && e != E.C ? (x = 1) == 1 && e != E.B : false;
|
||||
case B: break (x = 1) == 1 || isTrue();
|
||||
default: break false;
|
||||
}) && x == 1; //x is definitelly assigned here
|
||||
|
||||
if (!tA) {
|
||||
throw new IllegalStateException("Unexpected result.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int id(int v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
private static boolean isTrue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
enum E {
|
||||
A, B, C;
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @test /nodynamiccopyright/
|
||||
* @bug 8214031
|
||||
* @summary Verify that definite assignment when true works (illegal code)
|
||||
* @compile/fail/ref=DefiniteAssignment2.out --enable-preview --source 12 -XDrawDiagnostics DefiniteAssignment2.java
|
||||
*/
|
||||
public class DefiniteAssignment2 {
|
||||
|
||||
public static void main(String[] args) {
|
||||
int a = 0;
|
||||
boolean b = true;
|
||||
boolean t;
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
t = (b && switch(a) {
|
||||
case 0: break (x = 1) == 1 || true;
|
||||
default: break false;
|
||||
}) || x == 1;
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
t = (switch(a) {
|
||||
case 0: break (x = 1) == 1;
|
||||
default: break false;
|
||||
}) || x == 1;
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
t = (switch(a) {
|
||||
case 0: x = 1; break true;
|
||||
case 1: break (x = 1) == 1;
|
||||
default: break false;
|
||||
}) || x == 1;
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
t = (switch(a) {
|
||||
case 0: break true;
|
||||
case 1: break (x = 1) == 1;
|
||||
default: break false;
|
||||
}) && x == 1;
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
t = (switch(a) {
|
||||
case 0: break false;
|
||||
case 1: break isTrue() || (x = 1) == 1;
|
||||
default: break false;
|
||||
}) && x == 1;
|
||||
}
|
||||
|
||||
{
|
||||
int x;
|
||||
|
||||
t = (switch(a) {
|
||||
case 0: break false;
|
||||
case 1: break isTrue() ? true : (x = 1) == 1;
|
||||
default: break false;
|
||||
}) && x == 1;
|
||||
}
|
||||
|
||||
{
|
||||
final int x;
|
||||
|
||||
t = (switch(a) {
|
||||
case 0: break false;
|
||||
case 1: break isTrue() ? true : (x = 1) == 1;
|
||||
default: break false;
|
||||
}) && (x = 1) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isTrue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
DefiniteAssignment2.java:20:19: compiler.err.var.might.not.have.been.initialized: x
|
||||
DefiniteAssignment2.java:29:19: compiler.err.var.might.not.have.been.initialized: x
|
||||
DefiniteAssignment2.java:39:19: compiler.err.var.might.not.have.been.initialized: x
|
||||
DefiniteAssignment2.java:49:19: compiler.err.var.might.not.have.been.initialized: x
|
||||
DefiniteAssignment2.java:59:19: compiler.err.var.might.not.have.been.initialized: x
|
||||
DefiniteAssignment2.java:69:19: compiler.err.var.might.not.have.been.initialized: x
|
||||
DefiniteAssignment2.java:79:20: compiler.err.var.might.already.be.assigned: x
|
||||
- compiler.note.preview.filename: DefiniteAssignment2.java
|
||||
- compiler.note.preview.recompile
|
||||
7 errors
|
@ -1,3 +1,4 @@
|
||||
ExpressionSwitch.java:30:16: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.switch.expressions)
|
||||
ExpressionSwitch.java:31:20: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.switch.rules)
|
||||
2 errors
|
||||
ExpressionSwitch.java:38:16: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.switch.expressions)
|
||||
ExpressionSwitch.java:39:20: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.switch.rules)
|
||||
ExpressionSwitch.java:89:21: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.multiple.case.labels)
|
||||
3 errors
|
||||
|
@ -23,6 +23,14 @@ public class ExpressionSwitch {
|
||||
assertEquals(scopesIsolated(T.B), "B");
|
||||
assertEquals(lambdas1(T.B).get(), "B");
|
||||
assertEquals(lambdas2(T.B).get(), "B");
|
||||
assertEquals(convert1("A"), 0);
|
||||
assertEquals(convert1("B"), 0);
|
||||
assertEquals(convert1("C"), 1);
|
||||
assertEquals(convert1(""), -1);
|
||||
assertEquals(convert2("A"), 0);
|
||||
assertEquals(convert2("B"), 0);
|
||||
assertEquals(convert2("C"), 1);
|
||||
assertEquals(convert2(""), -1);
|
||||
localClass(T.A);
|
||||
}
|
||||
|
||||
@ -76,6 +84,22 @@ public class ExpressionSwitch {
|
||||
};
|
||||
}
|
||||
|
||||
private int convert1(String s) {
|
||||
return switch (s) {
|
||||
case "A", "B" -> 0;
|
||||
case "C" -> { break 1; }
|
||||
default -> -1;
|
||||
};
|
||||
}
|
||||
|
||||
private int convert2(String s) {
|
||||
return switch (s) {
|
||||
case "A", "B": break 0;
|
||||
case "C": break 1;
|
||||
default: break -1;
|
||||
};
|
||||
}
|
||||
|
||||
private void localClass(T t) {
|
||||
String good = "good";
|
||||
class L {
|
||||
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8214031
|
||||
* @summary Verify various corner cases with nested switch expressions.
|
||||
* @compile --enable-preview -source 12 ExpressionSwitchBugsInGen.java
|
||||
* @run main/othervm --enable-preview ExpressionSwitchBugsInGen
|
||||
*/
|
||||
|
||||
public class ExpressionSwitchBugsInGen {
|
||||
public static void main(String... args) {
|
||||
new ExpressionSwitchBugsInGen().test(0, 0, 0, false);
|
||||
new ExpressionSwitchBugsInGen().test(0, 0, 1, true);
|
||||
new ExpressionSwitchBugsInGen().test(0, 1, 1, false);
|
||||
new ExpressionSwitchBugsInGen().test(1, 1, -1, true);
|
||||
new ExpressionSwitchBugsInGen().test(1, 12, -1, false);
|
||||
new ExpressionSwitchBugsInGen().testCommonSuperType(0, "a", new StringBuilder(), "a");
|
||||
new ExpressionSwitchBugsInGen().testCommonSuperType(1, "", new StringBuilder("a"), "a");
|
||||
new ExpressionSwitchBugsInGen().testSwitchExpressionInConditional(0, null, -1);
|
||||
new ExpressionSwitchBugsInGen().testSwitchExpressionInConditional(1, "", 0);
|
||||
new ExpressionSwitchBugsInGen().testSwitchExpressionInConditional(1, 1, 1);
|
||||
new ExpressionSwitchBugsInGen().testIntBoxing(0, 10, 10);
|
||||
new ExpressionSwitchBugsInGen().testIntBoxing(1, 10, -1);
|
||||
}
|
||||
|
||||
private void test(int a, int b, int c, boolean expected) {
|
||||
if ( !(switch (a) {
|
||||
case 0 -> b == (switch (c) { case 0 -> 0; default -> 1; });
|
||||
default -> b == 12;
|
||||
})) {
|
||||
if (!expected) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
} else {
|
||||
if (expected) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void testCommonSuperType(int a, String s1, StringBuilder s2, String expected) {
|
||||
String r = (switch (a) {
|
||||
case 0 -> s1;
|
||||
default -> s2;
|
||||
}).toString();
|
||||
if (!expected.equals(r)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
private void testSwitchExpressionInConditional(int a, Object o, int expected) {
|
||||
int v = a == 0 ? -1
|
||||
: switch (o instanceof String ? 0 : 1) {
|
||||
case 0 -> 0;
|
||||
default -> 1;
|
||||
};
|
||||
if (v != expected) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
private void testIntBoxing(int a, Integer res, int expected) {
|
||||
int r = switch (a) {
|
||||
case 0 -> res;
|
||||
default -> -1;
|
||||
};
|
||||
if (r != expected) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8214031
|
||||
* @summary Verify switch expressions embedded in various statements work properly.
|
||||
* @compile --enable-preview -source 12 ExpressionSwitchEmbedding.java
|
||||
* @run main/othervm --enable-preview ExpressionSwitchEmbedding
|
||||
*/
|
||||
|
||||
public class ExpressionSwitchEmbedding {
|
||||
public static void main(String... args) {
|
||||
new ExpressionSwitchEmbedding().run();
|
||||
}
|
||||
|
||||
private void run() {
|
||||
{
|
||||
int i = 6;
|
||||
int o = 0;
|
||||
while (switch (i) {
|
||||
case 1: i = 0; break true;
|
||||
case 2: i = 1; break true;
|
||||
case 3, 4: i--;
|
||||
if (i == 2 || i == 4) {
|
||||
break switch (i) {
|
||||
case 2 -> true;
|
||||
case 4 -> false;
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
} else {
|
||||
break true;
|
||||
}
|
||||
default: i--; break switch (i) {
|
||||
case -1 -> false;
|
||||
case 3 -> true;
|
||||
default -> true;
|
||||
};
|
||||
}) {
|
||||
o++;
|
||||
}
|
||||
if (o != 6 && i >= 0) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
{
|
||||
int i = 6;
|
||||
int o = 0;
|
||||
if (switch (i) {
|
||||
case 1: i = 0; break true;
|
||||
case 2: i = 1; break true;
|
||||
case 3, 4: i--;
|
||||
if (i == 2 || i == 4) {
|
||||
break (switch (i) {
|
||||
case 2 -> 3;
|
||||
case 4 -> 5;
|
||||
default -> throw new IllegalStateException();
|
||||
}) == i + 1;
|
||||
} else {
|
||||
break true;
|
||||
}
|
||||
default: i--; break switch (i) {
|
||||
case -1 -> false;
|
||||
case 3 -> true;
|
||||
default -> true;
|
||||
};
|
||||
}) {
|
||||
o++;
|
||||
}
|
||||
if (o != 1 && i != 5) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
{
|
||||
int o = 0;
|
||||
for (int i = 6; (switch (i) {
|
||||
case 1: i = 0; break true;
|
||||
case 2: i = 1; break true;
|
||||
case 3, 4: i--;
|
||||
if (i == 2 || i == 4) {
|
||||
break switch (i) {
|
||||
case 2 -> true;
|
||||
case 4 -> false;
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
} else {
|
||||
break true;
|
||||
}
|
||||
default: i--; break switch (i) {
|
||||
case -1 -> false;
|
||||
case 3 -> true;
|
||||
default -> true;
|
||||
};
|
||||
}); ) {
|
||||
o++;
|
||||
}
|
||||
if (o != 6) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
{
|
||||
int i = 6;
|
||||
int o = 0;
|
||||
do {
|
||||
o++;
|
||||
} while (switch (i) {
|
||||
case 1: i = 0; break true;
|
||||
case 2: i = 1; break true;
|
||||
case 3, 4: i--;
|
||||
if (i == 2 || i == 4) {
|
||||
break switch (i) {
|
||||
case 2 -> true;
|
||||
case 4 -> false;
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
} else {
|
||||
break true;
|
||||
}
|
||||
default: i--; break switch (i) {
|
||||
case -1 -> false;
|
||||
case 3 -> true;
|
||||
default -> true;
|
||||
};
|
||||
});
|
||||
if (o != 6 && i >= 0) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user