# HG changeset patch # User jlahoda # Date 1543829856 -3600 # Node ID e4ba5414c8b492c5b7aa8b517152cb38a61f3560 # Parent df065f8356d7e0bae785c2870628b43cdc7f603e 8214031: Assertion error in value break statement with conditional operator in switch expression Summary: Correcting handling of boolean-valued switch expressions when true/false; generating them directly rather than desugaring in Lower. Reviewed-by: mcimadamore diff -r df065f8356d7 -r e4ba5414c8b4 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java Mon Dec 03 12:35:27 2018 +0530 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java Mon Dec 03 10:37:36 2018 +0100 @@ -300,7 +300,7 @@ * 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

extends TreeScanner { + static abstract class BaseAnalyzer extends TreeScanner { enum JumpKind { BREAK(JCTree.Tag.BREAK) { @@ -328,7 +328,7 @@ /** The currently pending exits that go from current inner blocks * to an enclosing block, in source order. */ - ListBuffer

pendingExits; + ListBuffer pendingExits; /** A pending exit. These are the statements return, break, and * continue. In addition, exception-throwing expressions or @@ -351,20 +351,20 @@ 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

oldPendingExits, + ListBuffer oldPendingExits, JumpKind jk) { boolean resolved = false; - List

exits = pendingExits.toList(); + List 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 @@ /** Resolve all continues of this statement. */ Liveness resolveContinues(JCTree tree) { - return resolveJump(tree, new ListBuffer

(), JumpKind.CONTINUE); + return resolveJump(tree, new ListBuffer(), JumpKind.CONTINUE); } /** Resolve all breaks of this statement. */ - Liveness resolveBreaks(JCTree tree, ListBuffer

oldPendingExits) { + Liveness resolveBreaks(JCTree tree, ListBuffer oldPendingExits) { return resolveJump(tree, oldPendingExits, JumpKind.BREAK); } @@ -412,7 +412,7 @@ * 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 { + class AliveAnalyzer extends BaseAnalyzer { /** A flag that indicates whether the last statement could * complete normally. @@ -831,7 +831,7 @@ * thrown is declared or caught. The analyzer uses some info that has been set by * the liveliness analyzer. */ - class FlowAnalyzer extends BaseAnalyzer { + class FlowAnalyzer extends BaseAnalyzer { /** A flag that indicates whether the last statement could * complete normally. @@ -851,11 +851,11 @@ */ List 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 @@ /** 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 @@ 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 @@ JCClassDecl classDefPrev = classDef; List thrownPrev = thrown; List caughtPrev = caught; - ListBuffer pendingExitsPrev = pendingExits; + ListBuffer pendingExitsPrev = pendingExits; Lint lintPrev = lint; boolean anonymousClass = tree.name == names.empty; pendingExits = new ListBuffer<>(); @@ -1024,12 +1026,12 @@ scan(tree.body); - List exits = pendingExits.toList(); + List 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 void visitDoLoop(JCDoWhileLoop tree) { - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; pendingExits = new ListBuffer<>(); scan(tree.body); resolveContinues(tree); @@ -1068,7 +1070,7 @@ } public void visitWhileLoop(JCWhileLoop tree) { - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; pendingExits = new ListBuffer<>(); scan(tree.cond); scan(tree.body); @@ -1077,7 +1079,7 @@ } public void visitForLoop(JCForLoop tree) { - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; scan(tree.init); pendingExits = new ListBuffer<>(); if (tree.cond != null) { @@ -1091,7 +1093,7 @@ public void visitForeachLoop(JCEnhancedForLoop tree) { visitVarDef(tree.var); - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; scan(tree.expr); pendingExits = new ListBuffer<>(); scan(tree.body); @@ -1100,7 +1102,7 @@ } public void visitLabelled(JCLabeledStatement tree) { - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; pendingExits = new ListBuffer<>(); scan(tree.body); resolveBreaks(tree, prevPendingExits); @@ -1116,7 +1118,7 @@ } private void handleSwitch(JCTree tree, JCExpression selector, List cases) { - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; pendingExits = new ListBuffer<>(); scan(selector); for (List l = cases; l.nonEmpty(); l = l.tail) { @@ -1140,7 +1142,7 @@ } } - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; pendingExits = new ListBuffer<>(); for (JCTree resource : tree.resources) { if (resource instanceof JCVariableDecl) { @@ -1204,7 +1206,7 @@ if (tree.finalizer != null) { List savedThrown = thrown; thrown = List.nil(); - ListBuffer exits = pendingExits; + ListBuffer exits = pendingExits; pendingExits = prevPendingExits; scan(tree.finalizer); if (!tree.finallyCanCompleteNormally) { @@ -1221,7 +1223,7 @@ } } else { thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); - ListBuffer exits = pendingExits; + ListBuffer exits = pendingExits; pendingExits = prevPendingExits; while (exits.nonEmpty()) pendingExits.append(exits.next()); } @@ -1267,16 +1269,16 @@ 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 @@ } List prevCaught = caught; List prevThrown = thrown; - ListBuffer prevPending = pendingExits; + ListBuffer prevPending = pendingExits; try { pendingExits = new ListBuffer<>(); caught = tree.getDescriptorType(types).getThrownTypes(); thrown = List.nil(); scan(tree.body); - List exits = pendingExits.toList(); + List 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 @@ } List prevCaught = caught; List prevThrown = thrown; - ListBuffer prevPending = pendingExits; + ListBuffer prevPending = pendingExits; inLambda = true; try { pendingExits = new ListBuffer<>(); @@ -1517,7 +1519,7 @@ * effectively-final local variables/parameters. */ - public class AssignAnalyzer extends BaseAnalyzer { + public class AssignAnalyzer extends BaseAnalyzer { /** The set of definitely assigned variables. */ @@ -1835,7 +1837,7 @@ JCClassDecl classDefPrev = classDef; int firstadrPrev = firstadr; int nextadrPrev = nextadr; - ListBuffer pendingExitsPrev = pendingExits; + ListBuffer pendingExitsPrev = pendingExits; pendingExits = new ListBuffer<>(); if (tree.name != names.empty) { @@ -1970,14 +1972,15 @@ } } } - List exits = pendingExits.toList(); + List 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 void visitDoLoop(JCDoWhileLoop tree) { - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; FlowKind prevFlowKind = flowKind; flowKind = FlowKind.NORMAL; final Bits initsSkip = new Bits(true); @@ -2059,7 +2062,7 @@ } public void visitWhileLoop(JCWhileLoop tree) { - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; FlowKind prevFlowKind = flowKind; flowKind = FlowKind.NORMAL; final Bits initsSkip = new Bits(true); @@ -2095,7 +2098,7 @@ } public void visitForLoop(JCForLoop tree) { - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; FlowKind prevFlowKind = flowKind; flowKind = FlowKind.NORMAL; int nextadrPrev = nextadr; @@ -2143,7 +2146,7 @@ public void visitForeachLoop(JCEnhancedForLoop tree) { visitVarDef(tree.var); - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; FlowKind prevFlowKind = flowKind; flowKind = FlowKind.NORMAL; int nextadrPrev = nextadr; @@ -2174,7 +2177,7 @@ } public void visitLabelled(JCLabeledStatement tree) { - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; pendingExits = new ListBuffer<>(); scan(tree.body); resolveBreaks(tree, prevPendingExits); @@ -2189,7 +2192,7 @@ } private void handleSwitch(JCTree tree, JCExpression selector, List cases) { - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; pendingExits = new ListBuffer<>(); int nextadrPrev = nextadr; scanExpr(selector); @@ -2245,7 +2248,7 @@ public void visitTry(JCTry tree) { ListBuffer resourceVarDecls = new ListBuffer<>(); final Bits uninitsTryPrev = new Bits(uninitsTry); - ListBuffer prevPendingExits = pendingExits; + ListBuffer prevPendingExits = pendingExits; pendingExits = new ListBuffer<>(); final Bits initsTry = new Bits(inits); uninitsTry.assign(uninits); @@ -2302,7 +2305,7 @@ if (tree.finalizer != null) { inits.assign(initsTry); uninits.assign(uninitsTry); - ListBuffer exits = pendingExits; + ListBuffer exits = pendingExits; pendingExits = prevPendingExits; scan(tree.finalizer); if (!tree.finallyCanCompleteNormally) { @@ -2312,10 +2315,10 @@ // 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 @@ } else { inits.assign(initsEnd); uninits.assign(uninitsEnd); - ListBuffer exits = pendingExits; + ListBuffer exits = pendingExits; pendingExits = prevPendingExits; while (exits.nonEmpty()) pendingExits.append(exits.next()); } @@ -2390,8 +2393,34 @@ @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 @@ final Bits prevInits = new Bits(inits); int returnadrPrev = returnadr; int nextadrPrev = nextadr; - ListBuffer prevPending = pendingExits; + ListBuffer prevPending = pendingExits; try { returnadr = nextadr; pendingExits = new ListBuffer<>(); @@ -2618,7 +2647,7 @@ * As effectively final variables are marked as such during DA/DU, this pass must run after * AssignAnalyzer. */ - class CaptureAnalyzer extends BaseAnalyzer { + class CaptureAnalyzer extends BaseAnalyzer { JCTree currentTree; //local class or lambda diff -r df065f8356d7 -r e4ba5414c8b4 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java Mon Dec 03 12:35:27 2018 +0530 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java Mon Dec 03 10:37:36 2018 +0100 @@ -63,6 +63,7 @@ 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 void visitSwitch(JCSwitch tree) { + handleSwitch(tree, tree.selector, 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 cases) { //expand multiple label cases: - ListBuffer cases = new ListBuffer<>(); - - for (JCCase c : tree.cases) { + ListBuffer 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 @@ //case C3: ... List 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 @@ } } - tree.cases = cases.toList(); - - Type selsuper = types.supertype(tree.selector.type); + cases = convertedCases.toList(); + + 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 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 cases = new ListBuffer<>(); - for (JCCase c : tree.cases) { + ListBuffer 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 caseList = tree.getCases(); + public JCTree visitStringSwitch(JCTree tree, JCExpression selector, List 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 @@ 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 @@ // position map. ListBuffer 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 @@ } 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); - - return make.Block(0L, stmtList.toList()); + 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); + + 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 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 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); diff -r df065f8356d7 -r e4ba5414c8b4 src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java Mon Dec 03 12:35:27 2018 +0530 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java Mon Dec 03 10:37:36 2018 +0100 @@ -33,6 +33,7 @@ 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 @@ 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)); diff -r df065f8356d7 -r e4ba5414c8b4 src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java Mon Dec 03 12:35:27 2018 +0530 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java Mon Dec 03 10:37:36 2018 +0100 @@ -1220,7 +1220,7 @@ } public boolean isStatementStart() { - return state.stacksize == letExprStackPos; + return !alive || state.stacksize == letExprStackPos; } /************************************************************************** diff -r df065f8356d7 -r e4ba5414c8b4 src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Mon Dec 03 12:35:27 2018 +0530 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Mon Dec 03 10:37:36 2018 +0100 @@ -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 @@ */ 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 @@ 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 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 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 cases = tree.cases; + Item sel = genExpr(selector, syms.intType); if (cases.isEmpty()) { // We are seeing: switch {} 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 switchEnv = env.dup(tree, new GenContext()); + Env 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 void visitBreak(JCBreak tree) { int tmpPos = code.pendingStatPos; + Assert.check(code.isStatementStart()); Env 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); } diff -r df065f8356d7 -r e4ba5414c8b4 src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java Mon Dec 03 12:35:27 2018 +0530 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java Mon Dec 03 10:37:36 2018 +0100 @@ -3016,6 +3016,8 @@ public static class LetExpr extends JCExpression { public List defs; public JCExpression expr; + /**true if a expr should be run through Gen.genCond:*/ + public boolean needsCond; protected LetExpr(List defs, JCExpression expr) { this.defs = defs; this.expr = expr; diff -r df065f8356d7 -r e4ba5414c8b4 test/langtools/tools/javac/switchexpr/CRT.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/switchexpr/CRT.java Mon Dec 03 10:37:36 2018 +0100 @@ -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); + } + +} diff -r df065f8356d7 -r e4ba5414c8b4 test/langtools/tools/javac/switchexpr/DefiniteAssignment1.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/switchexpr/DefiniteAssignment1.java Mon Dec 03 10:37:36 2018 +0100 @@ -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; + } +} diff -r df065f8356d7 -r e4ba5414c8b4 test/langtools/tools/javac/switchexpr/DefiniteAssignment2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/switchexpr/DefiniteAssignment2.java Mon Dec 03 10:37:36 2018 +0100 @@ -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; + } + +} diff -r df065f8356d7 -r e4ba5414c8b4 test/langtools/tools/javac/switchexpr/DefiniteAssignment2.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/switchexpr/DefiniteAssignment2.out Mon Dec 03 10:37:36 2018 +0100 @@ -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 diff -r df065f8356d7 -r e4ba5414c8b4 test/langtools/tools/javac/switchexpr/ExpressionSwitch-old.out --- a/test/langtools/tools/javac/switchexpr/ExpressionSwitch-old.out Mon Dec 03 12:35:27 2018 +0530 +++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitch-old.out Mon Dec 03 10:37:36 2018 +0100 @@ -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 diff -r df065f8356d7 -r e4ba5414c8b4 test/langtools/tools/javac/switchexpr/ExpressionSwitch.java --- a/test/langtools/tools/javac/switchexpr/ExpressionSwitch.java Mon Dec 03 12:35:27 2018 +0530 +++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitch.java Mon Dec 03 10:37:36 2018 +0100 @@ -23,6 +23,14 @@ 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 @@ }; } + 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 { diff -r df065f8356d7 -r e4ba5414c8b4 test/langtools/tools/javac/switchexpr/ExpressionSwitchBugsInGen.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitchBugsInGen.java Mon Dec 03 10:37:36 2018 +0100 @@ -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(); + } + } + +} diff -r df065f8356d7 -r e4ba5414c8b4 test/langtools/tools/javac/switchexpr/ExpressionSwitchEmbedding.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitchEmbedding.java Mon Dec 03 10:37:36 2018 +0100 @@ -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(); + } + } + } + +}