changeset 48740:0fe63dbc5e54 switch

Prototype of an updated switch expression: -both case: and case -> allowed -break may return a value out of switch expression.
author jlahoda
date Fri, 12 Jan 2018 20:05:51 +0100
parents ccb3467caa68
children 9278248c6c71
files src/jdk.compiler/share/classes/com/sun/source/tree/BreakTree.java src/jdk.compiler/share/classes/com/sun/source/tree/CaseExpressionTree.java src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java test/langtools/tools/javac/desugar/BoxingAndSuper.java test/langtools/tools/javac/lib/DPrinter.java test/langtools/tools/javac/switchexpr/BlockExpression.java test/langtools/tools/javac/switchexpr/ExpressionSwitch.java test/langtools/tools/javac/switchexpr/ExpressionSwitchBreaks1.java test/langtools/tools/javac/switchexpr/ExpressionSwitchBreaks2.java test/langtools/tools/javac/switchexpr/ExpressionSwitchBreaks2.out test/langtools/tools/javac/switchexpr/ExpressionSwitchInfer.java test/langtools/tools/javac/switchexpr/ExpressionSwitchNotExhaustive.java test/langtools/tools/javac/switchexpr/ExpressionSwitchNotExhaustive.out
diffstat 27 files changed, 556 insertions(+), 109 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/BreakTree.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/BreakTree.java	Fri Jan 12 20:05:51 2018 +0100
@@ -49,4 +49,10 @@
      * @return the label
      */
     Name getLabel();
+
+    /**
+     * Returns the expression for this {@code break} statement.
+     * @return the expression
+     */
+    ExpressionTree getValue();
 }
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/CaseExpressionTree.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/CaseExpressionTree.java	Fri Jan 12 20:05:51 2018 +0100
@@ -25,6 +25,8 @@
 
 package com.sun.source.tree;
 
+import java.util.List;
+
 /**
  * TODO
  */
@@ -41,5 +43,12 @@
      * TODO
      * @return TODO.
      */
-    Tree getBody();
+    public List<? extends StatementTree> getStatements();
+
+    /**
+     * TODO
+     * @return TODO.
+     */
+    public ExpressionTree getValue();
+
 }
--- a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java	Fri Jan 12 20:05:51 2018 +0100
@@ -371,7 +371,8 @@
     @Override
     public R visitCaseExpression(CaseExpressionTree node, P p) {
         R r = scan(node.getExpression(), p);
-        r = scanAndReduce(node.getBody(), p, r);
+        r = scanAndReduce(node.getStatements(), p, r);
+        r = scanAndReduce(node.getValue(), p, r);
         return r;
     }
 
@@ -470,7 +471,7 @@
      */
     @Override
     public R visitBreak(BreakTree node, P p) {
-        return null;
+        return scan(node.getValue(), p);
     }
 
     /**
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java	Fri Jan 12 20:05:51 2018 +0100
@@ -75,6 +75,8 @@
 import static com.sun.tools.javac.code.TypeTag.FORALL;
 import static com.sun.tools.javac.code.TypeTag.METHOD;
 import static com.sun.tools.javac.code.TypeTag.VOID;
+import com.sun.tools.javac.comp.DeferredAttr.SwitchExpressionScanner;
+import com.sun.tools.javac.tree.JCTree.JCBreak;
 import com.sun.tools.javac.tree.JCTree.JCCaseExpression;
 import com.sun.tools.javac.tree.JCTree.JCSwitchExpression;
 
@@ -464,6 +466,9 @@
      * Argument type for switch expressions.
      */
     class SwitchExpressionType extends ArgumentType<JCSwitchExpression> {
+        /** List of break expressions (lazily populated). */
+        Optional<List<JCBreak>> breakExpressions = Optional.empty();
+
         SwitchExpressionType(JCExpression tree, Env<AttrContext> env, JCSwitchExpression speculativeCond) {
             this(tree, env, speculativeCond, new HashMap<>());
         }
@@ -485,12 +490,35 @@
             } else {
                 //poly
                 for (JCCaseExpression c : speculativeTree.cases) {
-                    checkSpeculative(c.expr, localInfo);
+                    if (c.stats != null) {
+                        for (JCBreak brk : breakExpressions()) {
+                            checkSpeculative(brk.value, brk.value.type, resultInfo);
+                        }
+                    } else {
+                        checkSpeculative(c.value, localInfo);
+                    }
                 }
                 return localInfo.pt;
             }
         }
 
+        /** Compute return expressions (if needed). */
+        List<JCBreak> breakExpressions() {
+            return breakExpressions.orElseGet(() -> {
+                final List<JCBreak> res;
+                ListBuffer<JCBreak> buf = new ListBuffer<>();
+                new SwitchExpressionScanner() {
+                    @Override
+                    public void visitBreak(JCBreak tree) {
+                        buf.add(tree);
+                    }
+                }.scan(speculativeTree.cases);
+                res = buf.toList();
+                breakExpressions = Optional.of(res);
+                return res;
+            });
+        }
+
         @Override
         ArgumentType<JCSwitchExpression> dup(JCSwitchExpression tree, Env<AttrContext> env) {
             return new SwitchExpressionType(tree, env, speculativeTree, speculativeTypes);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Jan 12 20:05:51 2018 +0100
@@ -71,6 +71,7 @@
 import static com.sun.tools.javac.code.TypeTag.WILDCARD;
 import static com.sun.tools.javac.tree.JCTree.Tag.*;
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
+import com.sun.tools.javac.util.JCDiagnostic.Error;
 
 /** This is the main context-dependent analysis phase in GJC. It
  *  encompasses name resolution, type checking and constant folding as
@@ -1564,29 +1565,23 @@
                     switchEnv.dup(c, env.info.dup(switchEnv.info.scope.dup()));
 
                 try {
-                    ResultInfo ri;
-
-                    if (c.expr.hasTag(Tag.BLOCK)) {
-                        ri = statInfo;
-                        caseEnv.info.returnResult = condInfo;
-                    } else {
-                        ri = condInfo;
-                    }
-
-                    if (c.expr.hasTag(Tag.BLOCK)) {
-                        attribTree(c.expr, caseEnv, ri);
+                    if (c.stats != null) {
+                        caseEnv.info.breakResult = condInfo;
+                        attribStats(c.stats, caseEnv);
                         new TreeScanner() {
                             @Override
-                            public void visitReturn(JCReturn tree) {
-                                caseTypes.append(tree.expr.type);
+                            public void visitBreak(JCBreak brk) {
+                                if (brk.target == tree)
+                                    caseTypes.append(brk.value != null ? brk.value.type : syms.errType);
+                                super.visitBreak(brk);
                             }
 
                             @Override public void visitClassDef(JCClassDecl tree) {}
                             @Override public void visitLambda(JCLambda tree) {}
                             @Override public void visitMethodDef(JCMethodDecl tree) {}
-                        }.scan(c.expr);
+                        }.scan(c.stats);
                     } else {
-                        caseTypes.append(attribExpr(c.expr, caseEnv, ri));
+                        caseTypes.append(attribExpr(c.value, caseEnv, condInfo));
                     }
                 } finally {
                     caseEnv.info.scope.leave();
@@ -1905,7 +1900,56 @@
     }
 
     public void visitBreak(JCBreak tree) {
-        tree.target = findJumpTarget(tree.pos(), tree.getTag(), tree.label, env);
+        if (env.info.breakResult != null) {
+            if (tree.value == null) {
+                tree.target = findJumpTarget(tree.pos(), tree.getTag(), null, env);
+                if (tree.target.hasTag(SWITCH_EXPRESSIOM)) {
+                    log.error(tree.pos(), Errors.BreakMissingValue);
+                }
+            } else {
+                if (env.info.breakResult.pt.hasTag(VOID)) {
+                    //can happen?
+                    env.info.breakResult.checkContext.report(tree.value.pos(),
+                              diags.fragment(Fragments.UnexpectedRetVal));
+                }
+                boolean attribute = true;
+                if (tree.value.hasTag(IDENT)) {
+                    //disambiguate break <LABEL> and break <ident-as-an-expression>:
+                    Name label = ((JCIdent) tree.value).name;
+                    Pair<JCTree, Error> jumpTarget = findJumpTargetNoError(tree.getTag(), label, env);
+
+                    if (jumpTarget.fst != null) {
+                        //TODO: add test - identifier whose type does not match the expected type! (test for unknownExprInfo)
+                        JCTree speculative = deferredAttr.attribSpeculative(tree.value, env, unknownExprInfo);
+                        if (!speculative.type.hasTag(ERROR)) {
+                            log.error(tree.pos(), Errors.BreakAmbiguousTarget(label));
+                        }
+                        tree.target = jumpTarget.fst;
+                        attribute = false;
+                        if (jumpTarget.snd != null) {
+                            log.error(tree.pos(), jumpTarget.snd);
+                        }
+                    }
+                }
+                if (attribute) {
+                    attribTree(tree.value, env, env.info.breakResult);
+                    Env<AttrContext> env1 = env;
+                    while (env1 != null && env1.tree.getTag() != SWITCH_EXPRESSIOM) {
+                        env1 = env1.next;
+                    }
+                    Assert.checkNonNull(env1);
+                    tree.target = env1.tree;
+                }
+            }
+        } else {
+            if (tree.value == null || tree.value.hasTag(IDENT)) {
+                Name label = tree.value != null ? ((JCIdent) tree.value).name : null;
+                tree.target = findJumpTarget(tree.pos(), tree.getTag(), label, env);
+            } else {
+                log.error(tree.pos(), Errors.BreakComplexValueNoSwitchExpression);
+                attribTree(tree.value, env, unknownExprInfo);
+            }
+        }
         result = null;
     }
 
@@ -1928,11 +1972,35 @@
          *  @param env     The environment current at the jump statement.
          */
         private JCTree findJumpTarget(DiagnosticPosition pos,
-                                    JCTree.Tag tag,
-                                    Name label,
-                                    Env<AttrContext> env) {
+                                                   JCTree.Tag tag,
+                                                   Name label,
+                                                   Env<AttrContext> env) {
+            Pair<JCTree, Error> jumpTarget = findJumpTargetNoError(tag, label, env);
+
+            if (jumpTarget.snd != null) {
+                log.error(pos, jumpTarget.snd);
+            }
+
+            return jumpTarget.fst;
+        }
+        /** Return the target of a break or continue statement, if it exists,
+         *  report an error if not.
+         *  Note: The target of a labelled break or continue is the
+         *  (non-labelled) statement tree referred to by the label,
+         *  not the tree representing the labelled statement itself.
+         *
+         *  @param tag     The tag of the jump statement. This is either
+         *                 Tree.BREAK or Tree.CONTINUE.
+         *  @param label   The label of the jump statement, or null if no
+         *                 label is given.
+         *  @param env     The environment current at the jump statement.
+         */
+        private Pair<JCTree, JCDiagnostic.Error> findJumpTargetNoError(JCTree.Tag tag,
+                                                                       Name label,
+                                                                       Env<AttrContext> env) {
             // Search environments outwards from the point of jump.
             Env<AttrContext> env1 = env;
+            JCDiagnostic.Error pendingError = null;
             LOOP:
             while (env1 != null) {
                 switch (env1.tree.getTag()) {
@@ -1944,13 +2012,14 @@
                                 if (!labelled.body.hasTag(DOLOOP) &&
                                         !labelled.body.hasTag(WHILELOOP) &&
                                         !labelled.body.hasTag(FORLOOP) &&
-                                        !labelled.body.hasTag(FOREACHLOOP))
-                                    log.error(pos, Errors.NotLoopLabel(label));
+                                        !labelled.body.hasTag(FOREACHLOOP)) {
+                                    pendingError = Errors.NotLoopLabel(label);
+                                }
                                 // Found labelled statement target, now go inwards
                                 // to next non-labelled tree.
-                                return TreeInfo.referencedStatement(labelled);
+                                return Pair.of(TreeInfo.referencedStatement(labelled), pendingError);
                             } else {
-                                return labelled;
+                                return Pair.of(labelled, pendingError);
                             }
                         }
                         break;
@@ -1958,10 +2027,19 @@
                     case WHILELOOP:
                     case FORLOOP:
                     case FOREACHLOOP:
-                        if (label == null) return env1.tree;
+                        if (label == null) return Pair.of(env1.tree, null);
                         break;
                     case SWITCH:
-                        if (label == null && tag == BREAK) return env1.tree;
+                        if (label == null && tag == BREAK) return Pair.of(env1.tree, null);
+                        break;
+                    case SWITCH_EXPRESSIOM:
+                        if (tag == BREAK) {
+                            if (label == null) {
+                                return Pair.of(env1.tree, null);
+                            } else {
+                                pendingError = Errors.BreakOutsideSwitchExpression;
+                            }
+                        }
                         break;
                     case LAMBDA:
                     case METHODDEF:
@@ -1972,12 +2050,11 @@
                 env1 = env1.next;
             }
             if (label != null)
-                log.error(pos, Errors.UndefLabel(label));
+                return Pair.of(null, Errors.UndefLabel(label));
             else if (tag == CONTINUE)
-                log.error(pos, Errors.ContOutsideLoop);
+                return Pair.of(null, Errors.ContOutsideLoop);
             else
-                log.error(pos, Errors.BreakOutsideSwitchLoop);
-            return null;
+                return Pair.of(null, Errors.BreakOutsideSwitchLoop);
         }
 
     public void visitReturn(JCReturn tree) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java	Fri Jan 12 20:05:51 2018 +0100
@@ -101,6 +101,11 @@
      */
     Attr.ResultInfo returnResult = null;
 
+    /** ResultInfo to be used for attributing 'break' statement expressions
+     * (set by Attr.visitSwitchExpression)
+     */
+    Attr.ResultInfo breakResult = null;
+
     /** Symbol corresponding to the site of a qualified default super call
      */
     Type defaultSuperCallSite = null;
@@ -124,6 +129,7 @@
         info.lint = lint;
         info.enclVar = enclVar;
         info.returnResult = returnResult;
+        info.breakResult = breakResult;
         info.defaultSuperCallSite = defaultSuperCallSite;
         info.isSerializable = isSerializable;
         info.isLambda = isLambda;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	Fri Jan 12 20:05:51 2018 +0100
@@ -1085,6 +1085,18 @@
     }
 
     /**
+     * A tree scanner suitable for visiting the target-type dependent nodes nested
+     * within a switch expression body.
+     */
+    static class SwitchExpressionScanner extends FilterScanner {
+
+        SwitchExpressionScanner() {
+            super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP,
+                    FORLOOP, IF, BREAK, SYNCHRONIZED, SWITCH, TRY, WHILELOOP));
+        }
+    }
+
+    /**
      * This visitor is used to check that structural expressions conform
      * to their target - this step is required as inference could end up
      * inferring types that make some of the nested expressions incompatible
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java	Fri Jan 12 20:05:51 2018 +0100
@@ -646,7 +646,10 @@
                         constants.remove(((JCIdent) c.pat).name);
                     }
                 }
-                scanStat(c.expr);
+                if (c.stats != null)
+                    scanStats(c.stats);
+                if (c.value != null)
+                    scanStat(c.value);
             }
             if ((constants == null || !constants.isEmpty()) && !hasDefault) {
                 log.error(tree, Errors.NotExhaustive);
@@ -720,6 +723,8 @@
         }
 
         public void visitBreak(JCBreak tree) {
+            if (tree.isValueBreak())
+                scan(tree.value);
             recordExit(new PendingExit(tree));
         }
 
@@ -1093,6 +1098,21 @@
             resolveBreaks(tree, prevPendingExits);
         }
 
+        @Override
+        public void visitSwitchExpression(JCSwitchExpression tree) {
+            ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
+            pendingExits = new ListBuffer<>();
+            scan(tree.selector);
+            for (List<JCCaseExpression> l = tree.cases; l.nonEmpty(); l = l.tail) {
+                JCCaseExpression c = l.head;
+                if (c.pat != null) {
+                    scan(c.pat);
+                }
+                scan(c.stats);
+            }
+            resolveBreaks(tree, prevPendingExits);
+        }
+
         public void visitTry(JCTry tree) {
             List<Type> caughtPrev = caught;
             List<Type> thrownPrev = thrown;
@@ -1233,6 +1253,8 @@
             }
 
         public void visitBreak(JCBreak tree) {
+            if (tree.isValueBreak())
+                scan(tree.value);
             recordExit(new FlowPendingExit(tree, null));
         }
 
@@ -2182,6 +2204,46 @@
             resolveBreaks(tree, prevPendingExits);
             nextadr = nextadrPrev;
         }
+
+        public void visitSwitchExpression(JCSwitchExpression tree) {
+            ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
+            pendingExits = new ListBuffer<>();
+            int nextadrPrev = nextadr;
+            scanExpr(tree.selector);
+            final Bits initsSwitch = new Bits(inits);
+            final Bits uninitsSwitch = new Bits(uninits);
+            boolean hasDefault = false;
+            for (List<JCCaseExpression> l = tree.cases; l.nonEmpty(); l = l.tail) {
+                inits.assign(initsSwitch);
+                uninits.assign(uninits.andSet(uninitsSwitch));
+                JCCaseExpression c = l.head;
+                if (c.pat == null) {
+                    hasDefault = true;
+                } else {
+                    scanExpr(c.pat);
+                }
+                if (hasDefault) {
+                    inits.assign(initsSwitch);
+                    uninits.assign(uninits.andSet(uninitsSwitch));
+                }
+                if (c.stats != null) {
+                    scan(c.stats);
+                    addVars(c.stats, initsSwitch, uninitsSwitch);
+                } else {
+                    scan(c.value);
+                }
+                if (!hasDefault) {
+                    inits.assign(initsSwitch);
+                    uninits.assign(uninits.andSet(uninitsSwitch));
+                }
+                // Warn about fall-through if lint switch fallthrough enabled.
+            }
+            if (!hasDefault) {
+                inits.andSet(initsSwitch);
+            }
+            resolveBreaks(tree, prevPendingExits);
+            nextadr = nextadrPrev;
+        }
         // where
             /** Add any variables defined in stats to inits and uninits. */
             private void addVars(List<JCStatement> stats, final Bits inits,
@@ -2344,6 +2406,8 @@
 
         @Override
         public void visitBreak(JCBreak tree) {
+            if (tree.isValueBreak())
+                scan(tree.value);
             recordExit(new AssignPendingExit(tree, inits, uninits));
         }
 
@@ -2718,6 +2782,12 @@
             super.visitTry(tree);
         }
 
+        @Override
+        public void visitBreak(JCBreak tree) {
+            if (tree.isValueBreak())
+                scan(tree.value);
+        }
+
         public void visitModuleDef(JCModuleDecl tree) {
             // Do nothing for modules
         }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Fri Jan 12 20:05:51 2018 +0100
@@ -367,6 +367,13 @@
             }
             super.visitApply(tree);
         }
+
+        @Override
+        public void visitBreak(JCBreak tree) {
+            if (tree.isValueBreak())
+                scan(tree.value);
+        }
+        
     }
 
     ClassSymbol ownerToCopyFreeVarsFrom(ClassSymbol c) {
@@ -3990,38 +3997,51 @@
 
         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, c)).collect(List.collector());
+        switchStatement.cases = tree.cases.stream().map(c -> convertCase(dollar_switchexpr, switchStatement, tree, c)).collect(List.collector());
         if (tree.cases.stream().noneMatch(c -> c.pat == null)) {
             JCThrow thr = make.Throw(makeNewClass(syms.incompatibleClassChangeErrorType, List.nil()));
-            switchStatement.cases = switchStatement.cases.prepend(make.Case(null, List.of(thr)));
+            switchStatement.cases = switchStatement.cases.append(make.Case(null, List.of(thr)));
         }
         stmtList.append(translate(switchStatement));
 
         result = make.LetExpr(stmtList.toList(), make.Ident(dollar_switchexpr)).setType(dollar_switchexpr.type);
     }
         //where:
-        private JCCase convertCase(VarSymbol dollar_switchexpr, JCSwitch switchStatement, JCCaseExpression c) {
+        private JCCase convertCase(VarSymbol dollar_switchexpr, JCSwitch switchStatement, JCSwitchExpression switchExpr, JCCaseExpression c) {
             make.at(c.pos());
             ListBuffer<JCStatement> statements = new ListBuffer<>();
-            if (c.expr.hasTag(Tag.BLOCK)) {
+            if (c.stats != null) {
                 statements.addAll(new TreeTranslator() {
                     @Override
-                    public void visitReturn(JCReturn tree) {
-                        result = make.Exec(make.Assign(make.Ident(dollar_switchexpr), tree.expr).setType(tree.expr.type));
-                    }
-                    @Override
                     public void visitLambda(JCLambda tree) {}
                     @Override
                     public void visitClassDef(JCClassDecl tree) {}
                     @Override
                     public void visitMethodDef(JCMethodDecl tree) {}
-                }.translate(((JCBlock) c.expr).stats));
+                    @SuppressWarnings("unchecked")
+                    public <T extends JCTree> List<T> translate(List<T> trees) {
+                        if (trees == null) return null;
+                        ListBuffer<T> result = new ListBuffer<>();
+                        for (List<T> l = trees; l.nonEmpty(); l = l.tail) {
+                            if (l.head.hasTag(BREAK) && ((JCBreak) l.head).target == switchExpr) {
+                                JCBreak tree = (JCBreak) l.head;
+                                tree.target = switchStatement;
+                                result.append((T) make.Exec(make.Assign(make.Ident(dollar_switchexpr), translate(tree.value)).setType(dollar_switchexpr.type)));
+                                result.append((T) tree);
+                                tree.value = null;
+                            } else {
+                                result.append(translate(l.head));
+                            }
+                        }
+                        return result.toList();
+                    }
+                }.translate(c.stats));
             } else {
-                statements.add(make.Exec(make.Assign(make.Ident(dollar_switchexpr), (JCExpression) c.expr).setType(dollar_switchexpr.type)));
+                statements.add(make.Exec(make.Assign(make.Ident(dollar_switchexpr), c.value).setType(dollar_switchexpr.type)));
+                JCBreak brk = make.Break(null);
+                brk.target = switchStatement;
+                statements.add(brk);
             }
-            JCBreak brk = make.Break(null);
-            brk.target = switchStatement;
-            statements.add(brk);
             return make.Case(c.pat, statements.toList());
         }
 
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java	Fri Jan 12 20:05:51 2018 +0100
@@ -629,7 +629,8 @@
 
     public void visitCaseExpression(JCCaseExpression tree) {
         tree.pat = translate(tree.pat, null);
-        tree.expr = translate(tree.expr);
+        tree.stats = translate(tree.stats);
+        tree.value = translate(tree.value);
         result = tree;
     }
 
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Fri Jan 12 20:05:51 2018 +0100
@@ -1383,16 +1383,25 @@
                     accept(CASE);
                     pat = term(EXPR | NOLAMBDA);
                 }
-                accept(ARROW);
-                JCTree caseExpr;
-                if (token.kind == LBRACE) {
-                    caseExpr = block();
-                } else {
-                    caseExpr = parseExpression();
-                    accept(SEMI);
+                JCExpression value = null;
+                List<JCStatement> stats = null;
+                switch (token.kind) {
+                    case ARROW:
+                        nextToken();
+                        if (token.kind == TokenKind.THROW) {
+                            //TODO: record the arrow used?
+                            stats = List.of(parseStatement());
+                        } else {
+                            value = parseExpression();
+                            accept(SEMI);
+                        }
+                        break;
+                    case COLON:
+                        nextToken();
+                        stats = blockStatements();
+                        break;
                 }
-
-                caseExprs.append(F.at(token.pos/*XXX*/).CaseExpression(pat, caseExpr));
+                caseExprs.append(F.at(token.pos/*XXX*/).CaseExpression(pat, stats, value));
             }
 
             nextToken();
@@ -2583,9 +2592,9 @@
         }
         case BREAK: {
             nextToken();
-            Name label = LAX_IDENTIFIER.accepts(token.kind) ? ident() : null;
+            JCExpression value = token.kind == SEMI ? null : parseExpression();
             accept(SEMI);
-            JCBreak t = toP(F.at(pos).Break(label));
+            JCBreak t = toP(F.at(pos).Break(value));
             return t;
         }
         case CONTINUE: {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Jan 12 20:05:51 2018 +0100
@@ -182,6 +182,19 @@
 compiler.err.break.outside.switch.loop=\
     break outside switch or loop
 
+compiler.err.break.missing.value=\
+    break is missing a value to return from switch expression
+
+compiler.err.break.outside.switch.expression=\
+    break is jumping outside of the enclosing switch expression
+
+# 0: name
+compiler.err.break.ambiguous.target=\
+    ambiguous break - {0} is both a valid expression and a valid label
+
+compiler.err.break.complex.value.no.switch.expression=\
+    complex value in break outside of switch expression
+
 # 0: name
 compiler.err.call.must.be.first.stmt.in.ctor=\
     call to {0} must be first statement in constructor
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Fri Jan 12 20:05:51 2018 +0100
@@ -1310,10 +1310,12 @@
      */
     public static class JCCaseExpression extends JCExpression implements CaseExpressionTree {
         public JCExpression pat;
-        public JCTree expr;
-        protected JCCaseExpression(JCExpression pat, JCTree expr) {
+        public List<JCStatement> stats;
+        public JCExpression value;
+        protected JCCaseExpression(JCExpression pat, List<JCStatement> stats, JCExpression value) {
             this.pat = pat;
-            this.expr = expr;
+            this.stats = stats;
+            this.value = value;
         }
         @Override
         public void accept(Visitor v) { v.visitCaseExpression(this); }
@@ -1323,9 +1325,9 @@
         @DefinedBy(Api.COMPILER_TREE)
         public JCExpression getExpression() { return pat; }
         @DefinedBy(Api.COMPILER_TREE)
-        public JCTree getBody() {
-            return expr;
-        }
+        public List<JCStatement> getStatements() { return stats; }
+        @DefinedBy(Api.COMPILER_TREE)
+        public JCExpression getValue() { return value; }
         @Override @DefinedBy(Api.COMPILER_TREE)
         public <R,D> R accept(TreeVisitor<R,D> v, D d) {
             return v.visitCaseExpression(this, d);
@@ -1559,19 +1561,27 @@
      * A break from a loop or switch.
      */
     public static class JCBreak extends JCStatement implements BreakTree {
-        public Name label;
+        public JCExpression value; //TODO: should be expr?
         public JCTree target;
-        protected JCBreak(Name label, JCTree target) {
-            this.label = label;
+        protected JCBreak(JCExpression value, JCTree target) {
+            this.value = value;
             this.target = target;
         }
         @Override
         public void accept(Visitor v) { v.visitBreak(this); }
+        public boolean isValueBreak() {
+            return target != null && target.hasTag(SWITCH_EXPRESSIOM);
+        }
 
         @DefinedBy(Api.COMPILER_TREE)
         public Kind getKind() { return Kind.BREAK; }
         @DefinedBy(Api.COMPILER_TREE)
-        public Name getLabel() { return label; }
+        public Name getLabel() {
+            //TODO: OK for compatibility?
+            return value.getKind() == Kind.IDENTIFIER ? ((JCIdent) value).getName() : null;
+        }
+        @DefinedBy(Api.COMPILER_TREE)
+        public JCExpression getValue() { return value; }
         @Override @DefinedBy(Api.COMPILER_TREE)
         public <R,D> R accept(TreeVisitor<R,D> v, D d) {
             return v.visitBreak(this, d);
@@ -3082,7 +3092,7 @@
                                 JCExpression elsepart);
         JCIf If(JCExpression cond, JCStatement thenpart, JCStatement elsepart);
         JCExpressionStatement Exec(JCExpression expr);
-        JCBreak Break(Name label);
+        JCBreak Break(JCExpression value);
         JCContinue Continue(Name label);
         JCReturn Return(JCExpression expr);
         JCThrow Throw(JCExpression expr);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java	Fri Jan 12 20:05:51 2018 +0100
@@ -856,11 +856,16 @@
                 print("case ");
                 printExpr(tree.pat);
             }
-            print(" -> ");
-            println();
-            indent();
-            printExpr(tree.expr);
-            undent();
+            if (tree.stats != null) {
+                print(":");
+                println();
+                indent();
+                printStats(tree.stats);
+                undent();
+            } else {
+                print(" -> ");
+                printExpr(tree.value);
+            }
             align();
         } catch (IOException e) {
             throw new UncheckedIOException(e);
@@ -971,7 +976,7 @@
     public void visitBreak(JCBreak tree) {
         try {
             print("break");
-            if (tree.label != null) print(" " + tree.label);
+            if (tree.value != null) print(" " + tree.value);
             print(";");
         } catch (IOException e) {
             throw new UncheckedIOException(e);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java	Fri Jan 12 20:05:51 2018 +0100
@@ -140,7 +140,8 @@
     @DefinedBy(Api.COMPILER_TREE)
     public JCTree visitBreak(BreakTree node, P p) {
         JCBreak t = (JCBreak) node;
-        return M.at(t.pos).Break(t.label);
+        JCExpression value = copy(t.value, p);
+        return M.at(t.pos).Break(value);
     }
 
     @DefinedBy(Api.COMPILER_TREE)
@@ -155,8 +156,9 @@
     public JCTree visitCaseExpression(CaseExpressionTree node, P p) {
         JCCaseExpression t = (JCCaseExpression) node;
         JCExpression pat = copy(t.pat, p);
-        JCTree expr = copy(t.expr, p);
-        return M.at(t.pos).CaseExpression(pat, expr);
+        List<JCStatement> stats = copy(t.stats, p);
+        JCExpression value = copy(t.value, p);
+        return M.at(t.pos).CaseExpression(pat, stats, value);
     }
 
     @DefinedBy(Api.COMPILER_TREE)
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Fri Jan 12 20:05:51 2018 +0100
@@ -284,8 +284,8 @@
         return tree;
     }
 
-    public JCCaseExpression CaseExpression(JCExpression pat, JCTree expr) {
-        JCCaseExpression tree = new JCCaseExpression(pat, expr);
+    public JCCaseExpression CaseExpression(JCExpression pat, List<JCStatement> stats, JCExpression value) {
+        JCCaseExpression tree = new JCCaseExpression(pat, stats, value);
         tree.pos = pos;
         return tree;
     }
@@ -336,7 +336,7 @@
         return tree;
     }
 
-    public JCBreak Break(Name label) {
+    public JCBreak Break(JCExpression label) {
         JCBreak tree = new JCBreak(label, null);
         tree.pos = pos;
         return tree;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java	Fri Jan 12 20:05:51 2018 +0100
@@ -187,7 +187,8 @@
 
     public void visitCaseExpression(JCCaseExpression tree) {
         scan(tree.pat);
-        scan(tree.expr);
+        scan(tree.stats);
+        scan(tree.value);
     }
 
     public void visitSynchronized(JCSynchronized tree) {
@@ -224,6 +225,7 @@
     }
 
     public void visitBreak(JCBreak tree) {
+        scan(tree.value);
     }
 
     public void visitContinue(JCContinue tree) {
--- a/test/langtools/tools/javac/desugar/BoxingAndSuper.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/test/langtools/tools/javac/desugar/BoxingAndSuper.java	Fri Jan 12 20:05:51 2018 +0100
@@ -61,6 +61,7 @@
 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
 import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
 import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
 import com.sun.tools.javac.tree.JCTree.LetExpr;
 import com.sun.tools.javac.tree.JCTree.Tag;
@@ -327,8 +328,8 @@
                     if (tree.hasTag(Tag.LETEXPR)) {
                         LetExpr le = (LetExpr) tree;
 
-                        for (JCVariableDecl var : le.defs) {
-                            letExprRemap.put(var.name.toString(), "$le" + i++);
+                        for (JCStatement var : le.defs) {
+                            letExprRemap.put(((JCVariableDecl) var).name.toString(), "$le" + i++);
                         }
                     }
                     return super.visitOther(node, p);
--- a/test/langtools/tools/javac/lib/DPrinter.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/test/langtools/tools/javac/lib/DPrinter.java	Fri Jan 12 20:05:51 2018 +0100
@@ -781,7 +781,7 @@
 
         @Override
         public void visitBreak(JCBreak tree) {
-            printName("label", tree.label);
+            printTree("value", tree.value);
         }
 
         @Override
--- a/test/langtools/tools/javac/switchexpr/BlockExpression.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/test/langtools/tools/javac/switchexpr/BlockExpression.java	Fri Jan 12 20:05:51 2018 +0100
@@ -35,7 +35,7 @@
         try {
             int ii = switch (t) {
                 case A -> 0;
-                default -> { throw new IllegalStateException(); }
+                default -> throw new IllegalStateException();
             };
             throw new AssertionError("Expected exception not thrown.");
         } catch (IllegalStateException ex) {
--- a/test/langtools/tools/javac/switchexpr/ExpressionSwitch.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitch.java	Fri Jan 12 20:05:51 2018 +0100
@@ -39,7 +39,8 @@
         check(T.A, "A");
         check(T.B, "B");
         check(T.C, "other");
-        exhaustive(T.C);
+        exhaustive1(T.C);
+        exhaustive2(null);
     }
 
     private String print(T t) {
@@ -47,16 +48,26 @@
             case null -> "NULL";
             case A -> "A";
             case B -> "B";
-            default -> "other";
+            default: break "other";
         };
     }
 
-    private String exhaustive(T t) {
+    private String exhaustive1(T t) {
         return switch (t) {
             case A -> "A";
             case B -> "B";
             case C -> "C";
-            case D -> "D";
+            case D: break "D";
+        };
+    }
+
+    private String exhaustive2(T t) {
+        return switch (t) {
+            case null -> "NULL";
+            case A -> "A";
+            case B -> "B";
+            case C -> "C";
+            case D: break "D";
         };
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitchBreaks1.java	Fri Jan 12 20:05:51 2018 +0100
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2017, 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
+ * @compile ExpressionSwitchBreaks1.java
+ * @run main ExpressionSwitchBreaks1
+ */
+
+import java.util.Objects;
+
+public class ExpressionSwitchBreaks1 {
+    public static void main(String... args) {
+        new ExpressionSwitchBreaks1().run();
+    }
+
+    private void run() {
+        check(print1(0, 0), "0-0");
+        check(print1(0, 1), "0-1");
+        check(print1(0, -1), "0-X");
+        check(print1(-1, -1), "X");
+        check(print2(0, 0, 0), "0-0-0");
+        check(print2(0, 0, 1), "0-0-1");
+        check(print2(0, 0, 2), "0-0-2");
+        check(print2(0, 0, -1), "0-0-X");
+        check(print2(0, 1, -1), "0-1");
+        check(print2(0, -1, -1), "0-X");
+        check(print2(1, -1, -1), "1");
+        check(print2(-11, -1, -1), "X");
+    }
+
+    private String print1(int i, int j) {
+        switch (i) {
+            case 0:
+                return switch (j) {
+                    case 0:
+                        break "0-0";
+                    case 1:
+                        break "0-1";
+                    default:
+                        break "0-X";
+                };
+            default: return "X";
+        }
+    }
+
+    private String print2(int i, int j, int k) {
+        return switch (i) {
+            case 0:
+                String r;
+                OUTER: switch (j) {
+                    case 0:
+                        String res;
+                        INNER: switch (k) {
+                            case 0: break "0-0-0";
+                            case 1: res = "0-0-1"; break;
+                            case 2: res = "0-0-2"; break INNER;
+                            default: r = "0-0-X"; break OUTER;
+                        }
+                        break res;
+                    case 1:
+                        break "0-1";
+                    default:
+                        break "0-X";
+                }
+                break r;
+            case 1:
+                break "1";
+            default:
+                break "X";
+        };
+    }
+
+    private void check(String result, String expected) {
+        if (!Objects.equals(result, expected)) {
+            throw new AssertionError("Unexpected result: " + result);
+        }
+    }
+
+    enum T {
+        A, B;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitchBreaks2.java	Fri Jan 12 20:05:51 2018 +0100
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017, 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
+ * @compile/fail/ref=ExpressionSwitchBreaks2.out -XDrawDiagnostics ExpressionSwitchBreaks2.java
+ */
+
+public class ExpressionSwitchBreaks2 {
+    private String print(int i, int j) {
+        OUTER: switch (i) {
+            case 0:
+                return switch (j) {
+                    case 0:
+                        break "0-0";
+                    case 1:
+                        break ; //error: missing value
+                    case 2:
+                        break OUTER; //error: jumping outside of the switch expression
+                    default: {
+                        String x = "X";
+                        x: switch (i + j) {
+                            case 0: break x; //error: cannot disambiguate
+                        }
+                        break "X";
+                    }
+                };
+            case 1:
+                break "1" + undef; //error: complex value and no switch expression
+        }
+        return null;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitchBreaks2.out	Fri Jan 12 20:05:51 2018 +0100
@@ -0,0 +1,6 @@
+ExpressionSwitchBreaks2.java:37:25: compiler.err.break.missing.value
+ExpressionSwitchBreaks2.java:39:25: compiler.err.break.outside.switch.expression
+ExpressionSwitchBreaks2.java:43:37: compiler.err.break.ambiguous.target: x
+ExpressionSwitchBreaks2.java:49:17: compiler.err.break.complex.value.no.switch.expression
+ExpressionSwitchBreaks2.java:49:29: compiler.err.cant.resolve.location: kindname.variable, undef, , , (compiler.misc.location: kindname.class, ExpressionSwitchBreaks2, null)
+5 errors
--- a/test/langtools/tools/javac/switchexpr/ExpressionSwitchInfer.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitchInfer.java	Fri Jan 12 20:05:51 2018 +0100
@@ -9,7 +9,7 @@
 
 public class ExpressionSwitchInfer {
 
-    private <T> T test(List<T> l, Class<T> c, Object param) {
+    private <T> T test(List<T> l, Class<T> c, String param) {
         test(param == null ? new ArrayList<>() : new ArrayList<>(), CharSequence.class, param).charAt(0);
         test(param == null ? new ArrayList<>() : new ArrayList<>(), CharSequence.class, param).substring(0);
 
@@ -25,18 +25,4 @@
         return null;
     }
 
-    private String print(Object o) {
-        return switch (o) {
-            case Integer i -> String.format("int %d", i);
-            case Double d  -> String.format("double %2.1f", d);
-            default        -> String.format("String %s", o);
-        };
-    }
-
-    private void check(Object input, String expected) {
-        String result = print(input);
-        if (!Objects.equals(result, expected)) {
-            throw new AssertionError("Unexpected result: " + result);
-        }
-    }
 }
--- a/test/langtools/tools/javac/switchexpr/ExpressionSwitchNotExhaustive.java	Thu Jan 11 22:05:45 2018 +0100
+++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitchNotExhaustive.java	Fri Jan 12 20:05:51 2018 +0100
@@ -15,6 +15,20 @@
             case A -> "42";
         };
     }
+    private String f(int i, E e) {
+        return switch (i) {
+            case 0:
+                String s;
+                switch (e) {
+                    case A:
+                        s = "42";
+                        break;
+                }
+                break s;
+            default:
+                break "43";
+        };
+    }
     enum E {
         A, B;
     }
--- a/test/langtools/tools/javac/switchexpr/ExpressionSwitchNotExhaustive.out	Thu Jan 11 22:05:45 2018 +0100
+++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitchNotExhaustive.out	Fri Jan 12 20:05:51 2018 +0100
@@ -1,3 +1,4 @@
-ExpressionSwitchNotExhaustive.java:11:10: compiler.err.not.exhaustive
-ExpressionSwitchNotExhaustive.java:16:10: compiler.err.not.exhaustive
-2 errors
+ExpressionSwitchNotExhaustive.java:8:16: compiler.err.not.exhaustive
+ExpressionSwitchNotExhaustive.java:14:16: compiler.err.not.exhaustive
+ExpressionSwitchNotExhaustive.java:27:23: compiler.err.var.might.not.have.been.initialized: s
+3 errors