< prev index next >
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
Print this page
rev 51258 : imported patch switch.diff
@@ -26,10 +26,12 @@
//todo: one might eliminate uninits.andSets when monotonic
package com.sun.tools.javac.comp;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
import com.sun.source.tree.LambdaExpressionTree.BodyKind;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Scope.WriteableScope;
import com.sun.tools.javac.code.Source.Feature;
@@ -209,11 +211,11 @@
return instance;
}
public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
new AliveAnalyzer().analyzeTree(env, make);
- new AssignAnalyzer().analyzeTree(env);
+ new AssignAnalyzer().analyzeTree(env, make);
new FlowAnalyzer().analyzeTree(env, make);
new CaptureAnalyzer().analyzeTree(env, make);
}
public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) {
@@ -242,11 +244,11 @@
//message will be reported and will cause compilation to skip the flow analyis
//step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
//related errors, which will allow for more errors to be detected
Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
try {
- new LambdaAssignAnalyzer(env).analyzeTree(env, that);
+ new LambdaAssignAnalyzer(env).analyzeTree(env, that, make);
LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer();
flowAnalyzer.analyzeTree(env, that, make);
return flowAnalyzer.inferredThrownTypes;
} finally {
log.popDiagnosticHandler(diagHandler);
@@ -394,10 +396,16 @@
}
public void visitPackageDef(JCPackageDecl tree) {
// Do nothing for PackageDecl
}
+
+ protected void scanSyntheticBreak(TreeMaker make, JCTree swtch) {
+ JCBreak brk = make.at(Position.NOPOS).Break(null);
+ brk.target = swtch;
+ scan(brk);
+ }
}
/**
* This pass implements the first step of the dataflow analysis, namely
* the liveness analysis check. This checks that every statement is reachable.
@@ -594,15 +602,23 @@
scan(tree.selector);
boolean hasDefault = false;
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
alive = true;
JCCase c = l.head;
- if (c.pat == null)
+ if (c.pats.isEmpty())
hasDefault = true;
- else
- scan(c.pat);
+ else {
+ for (JCExpression pat : c.pats) {
+ scan(pat);
+ }
+ }
scanStats(c.stats);
+ c.completesNormally = alive;
+ if (alive && c.caseKind == JCCase.RULE) {
+ scanSyntheticBreak(make, tree);
+ alive = false;
+ }
// Warn about fall-through if lint switch fallthrough enabled.
if (alive &&
lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
c.stats.nonEmpty() && l.tail.nonEmpty())
log.warning(Lint.LintCategory.FALLTHROUGH,
@@ -613,10 +629,50 @@
alive = true;
}
alive |= resolveBreaks(tree, prevPendingExits);
}
+ @Override
+ public void visitSwitchExpression(JCSwitchExpression tree) {
+ ListBuffer<PendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<>();
+ scan(tree.selector);
+ Set<Object> constants = null;
+ if ((tree.selector.type.tsym.flags() & ENUM) != 0) {
+ constants = new HashSet<>();
+ for (Symbol s : tree.selector.type.tsym.members().getSymbols(s -> (s.flags() & ENUM) != 0)) {
+ constants.add(s.name);
+ }
+ }
+ boolean hasDefault = false;
+ boolean prevAlive = alive;
+ for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
+ alive = true;
+ JCCase c = l.head;
+ if (c.pats.isEmpty())
+ hasDefault = true;
+ else {
+ for (JCExpression pat : c.pats) {
+ scan(pat);
+ if (constants != null) {
+ if (pat.hasTag(IDENT))
+ constants.remove(((JCIdent) pat).name);
+ if (pat.type != null)
+ constants.remove(pat.type.constValue());
+ }
+ }
+ }
+ scanStats(c.stats);
+ c.completesNormally = alive;
+ }
+ if ((constants == null || !constants.isEmpty()) && !hasDefault) {
+ log.error(tree, Errors.NotExhaustive);
+ }
+ alive = prevAlive;
+ alive |= resolveBreaks(tree, prevPendingExits);
+ }
+
public void visitTry(JCTry tree) {
ListBuffer<PendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>();
for (JCTree resource : tree.resources) {
if (resource instanceof JCVariableDecl) {
@@ -678,10 +734,12 @@
alive = true;
}
}
public void visitBreak(JCBreak tree) {
+ if (tree.isValueBreak())
+ scan(tree.value);
recordExit(new PendingExit(tree));
}
public void visitContinue(JCContinue tree) {
recordExit(new PendingExit(tree));
@@ -1038,18 +1096,25 @@
scan(tree.body);
resolveBreaks(tree, prevPendingExits);
}
public void visitSwitch(JCSwitch tree) {
+ handleSwitch(tree, tree.selector, tree.cases);
+ }
+
+ @Override
+ public void visitSwitchExpression(JCSwitchExpression tree) {
+ handleSwitch(tree, tree.selector, tree.cases);
+ }
+
+ private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>();
- scan(tree.selector);
- for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
+ scan(selector);
+ for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
JCCase c = l.head;
- if (c.pat != null) {
- scan(c.pat);
- }
+ scan(c.pats);
scan(c.stats);
}
resolveBreaks(tree, prevPendingExits);
}
@@ -1189,10 +1254,12 @@
return exc.tsym == syms.throwableType.tsym ||
exc.tsym == syms.exceptionType.tsym;
}
public void visitBreak(JCBreak tree) {
+ if (tree.isValueBreak())
+ scan(tree.value);
recordExit(new FlowPendingExit(tree, null));
}
public void visitContinue(JCContinue tree) {
recordExit(new FlowPendingExit(tree, null));
@@ -2103,31 +2170,44 @@
scan(tree.body);
resolveBreaks(tree, prevPendingExits);
}
public void visitSwitch(JCSwitch tree) {
+ handleSwitch(tree, tree.selector, tree.cases);
+ }
+
+ public void visitSwitchExpression(JCSwitchExpression tree) {
+ handleSwitch(tree, tree.selector, tree.cases);
+ }
+
+ private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>();
int nextadrPrev = nextadr;
- scanExpr(tree.selector);
+ scanExpr(selector);
final Bits initsSwitch = new Bits(inits);
final Bits uninitsSwitch = new Bits(uninits);
boolean hasDefault = false;
- for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
+ for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
inits.assign(initsSwitch);
uninits.assign(uninits.andSet(uninitsSwitch));
JCCase c = l.head;
- if (c.pat == null) {
+ if (c.pats.isEmpty()) {
hasDefault = true;
} else {
- scanExpr(c.pat);
+ for (JCExpression pat : c.pats) {
+ scanExpr(pat);
+ }
}
if (hasDefault) {
inits.assign(initsSwitch);
uninits.assign(uninits.andSet(uninitsSwitch));
}
scan(c.stats);
+ if (c.completesNormally && c.caseKind == JCCase.RULE) {
+ scanSyntheticBreak(make, tree);
+ }
addVars(c.stats, initsSwitch, uninitsSwitch);
if (!hasDefault) {
inits.assign(initsSwitch);
uninits.assign(uninits.andSet(uninitsSwitch));
}
@@ -2299,10 +2379,12 @@
}
}
@Override
public void visitBreak(JCBreak tree) {
+ if (tree.isValueBreak())
+ scan(tree.value);
recordExit(new AssignPendingExit(tree, inits, uninits));
}
@Override
public void visitContinue(JCContinue tree) {
@@ -2477,25 +2559,26 @@
* main method
*************************************************************************/
/** Perform definite assignment/unassignment analysis on a tree.
*/
- public void analyzeTree(Env<?> env) {
- analyzeTree(env, env.tree);
+ public void analyzeTree(Env<?> env, TreeMaker make) {
+ analyzeTree(env, env.tree, make);
}
- public void analyzeTree(Env<?> env, JCTree tree) {
+ public void analyzeTree(Env<?> env, JCTree tree, TreeMaker make) {
try {
startPos = tree.pos().getStartPosition();
if (vardecls == null)
vardecls = new JCVariableDecl[32];
else
for (int i=0; i<vardecls.length; i++)
vardecls[i] = null;
firstadr = 0;
nextadr = 0;
+ Flow.this.make = make;
pendingExits = new ListBuffer<>();
this.classDef = null;
unrefdResources = WriteableScope.create(env.enclClass.sym);
scan(tree);
} finally {
@@ -2507,10 +2590,11 @@
for (int i=0; i<vardecls.length; i++)
vardecls[i] = null;
}
firstadr = 0;
nextadr = 0;
+ Flow.this.make = null;
pendingExits = null;
this.classDef = null;
unrefdResources = null;
}
}
@@ -2659,10 +2743,16 @@
}
}
super.visitTry(tree);
}
+ @Override
+ public void visitBreak(JCBreak tree) {
+ if (tree.isValueBreak())
+ scan(tree.value);
+ }
+
public void visitModuleDef(JCModuleDecl tree) {
// Do nothing for modules
}
/**************************************************************************
< prev index next >