# HG changeset patch # User jlahoda # Date 1533102955 -7200 # Wed Aug 01 07:55:55 2018 +0200 # Node ID 59e51c39b9c2dbe917fa5403799a32a06051253f # Parent 979e349059eb9f7d0c0cffceb30bc4b3433302cf imported patch switch diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/BreakTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/BreakTree.java --- a/src/jdk.compiler/share/classes/com/sun/source/tree/BreakTree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/BreakTree.java @@ -35,6 +35,8 @@ * break; * * break label ; + * + * break expression ; * * * @jls section 14.15 @@ -49,4 +51,18 @@ * @return the label */ Name getLabel(); + + /** + * Returns the expression for this {@code break} statement. + * + * @return the expression + * @since 12 + * + * @deprecated This method is modeling value breaks, which are part of + * a preview feature and may be removed if the preview feature + * is removed. + * + */ + @Deprecated(forRemoval=true, since="12") + ExpressionTree getValue(); } diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/CaseTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/CaseTree.java --- a/src/jdk.compiler/share/classes/com/sun/source/tree/CaseTree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/CaseTree.java @@ -28,7 +28,7 @@ import java.util.List; /** - * A tree node for a {@code case} in a {@code switch} statement. + * A tree node for a {@code case} in a {@code switch} statement or expression. * * For example: *
@@ -49,13 +49,90 @@
     /**
      * Returns the expression for the case, or
      * {@code null} if this is the default case.
+     * If this case has multiple lables, returns the first label.
      * @return the expression for the case, or null
      */
     ExpressionTree getExpression();
 
     /**
+     * Returns the labels for this case.
+     * For default case, returns an empty list.
+     *
+     * @return labels for this case
+     * @since 12
+     *
+     * @deprecated This method is modeling a case with multiple labels,
+     * which is part of a preview feature and may be removed
+     * if the preview feature is removed.
+     */
+    @Deprecated(forRemoval=true, since="12")
+    List getExpressions();
+
+    /**
      * Returns the statements labeled by the case.
-     * @return the statements labeled by the case
+     * If this is a case in switch expression in for
+     * {@code case expression -> value}, returns a list
+     * containing a single synthetic BreakTree with
+     * the given value.
+     * @return the statements labeled by the case or null
      */
     List getStatements();
+
+    /**
+     * For case with kind {@linkplain CaseKind#RULE},
+     * returns the statement or expression after the arrow.
+     * Returns {@code null} for case with kind
+     * {@linkplain CaseKind#STATEMENT}.
+     *
+     * @return case value or null
+     * @since 12
+     *
+     * @deprecated This method is modeling a rule case,
+     * which is part of a preview feature and may be removed
+     * if the preview feature is removed.
+     */
+    @Deprecated(forRemoval=true, since="12")
+    public default Tree getBody() {
+        return null;
+    }
+
+    /**
+     * Returns the kind of this case.
+     *
+     * @return the kind of this case
+     * @since 12
+     *
+     * @deprecated This method is used to model a rule case,
+     * which is part of a preview feature and may be removed
+     * if the preview feature is removed.
+     */
+    @Deprecated(forRemoval=true, since="12")
+    public default CaseKind getCaseKind() {
+        return CaseKind.STATEMENT;
+    }
+
+    /**
+     * The syntatic form of this case:
+     * 
+     *
+     * @since 12
+     *
+     * @deprecated This enum is used to model a rule case,
+     * which is part of a preview feature and may be removed
+     * if the preview feature is removed.
+     */
+    @Deprecated(forRemoval=true, since="12")
+    public enum CaseKind {
+        /**
+         * Case is in the form: {@code case : }.
+         */
+        STATEMENT,
+        /**
+         * Case is in the form: {@code case  -> }.
+         */
+        RULE;
+    }
 }
diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/SwitchExpressionTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/SwitchExpressionTree.java
new file mode 100644
--- /dev/null
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/SwitchExpressionTree.java
@@ -0,0 +1,61 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package com.sun.source.tree;
+
+import java.util.List;
+
+/**
+ * A tree node for a {@code switch} expression.
+ *
+ * For example:
+ * 
+ *   switch ( expression ) {
+ *     cases
+ *   }
+ * 
+ * + * @jls section 15.29 + * + * @since 12 + * + * @deprecated This method is modeling switch expressions, + * which are part of a preview feature and may be removed + * if the preview feature is removed. + */ +@Deprecated(forRemoval=true, since="12") +public interface SwitchExpressionTree extends ExpressionTree { + /** + * Returns the expression for the {@code switch} expression. + * @return the expression + */ + ExpressionTree getExpression(); + + /** + * Returns the cases for the {@code switch} expression. + * @return the cases + */ + List getCases(); +} diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java --- a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java @@ -240,6 +240,20 @@ SWITCH(SwitchTree.class), /** + * Used for instances of {@link SwitchExpressionTree}. + * + * @since 12 + * + * @deprecated + * This enum constant is modeling switch expressions, + * which are part of a preview feature and may be removed + * if the preview feature is removed. + */ + @Deprecated(forRemoval=true, since="12") + @SuppressWarnings("removal") + SWITCH_EXPRESSION(SwitchExpressionTree.class), + + /** * Used for instances of {@link SynchronizedTree}. */ SYNCHRONIZED(SynchronizedTree.class), diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java --- a/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java @@ -354,6 +354,23 @@ R visitSwitch(SwitchTree node, P p); /** + * Visits a SwitchExpressionTree node. + * + * @param node the node being visited + * @param p a parameter value + * @return a result value + * @since 12 + * + * @deprecated + * This method is modeling switch expressions, + * which are part of a preview feature and may be removed + * if the preview feature is removed. + */ + @Deprecated(forRemoval=true, since="12") + @SuppressWarnings("removal") + R visitSwitchExpression(SwitchExpressionTree node, P p); + + /** * Visits a SynchronizedTree node. * @param node the node being visited * @param p a parameter value diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java --- a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java @@ -269,6 +269,25 @@ * @param node {@inheritDoc} * @param p {@inheritDoc} * @return the result of {@code defaultAction} + * + * @deprecated + * This method is modeling switch expressions, + * which are part of a preview feature and may be removed + * if the preview feature is removed. + */ + @Override + @Deprecated(forRemoval=true, since="12") + @SuppressWarnings("removal") + public R visitSwitchExpression(SwitchExpressionTree node, P p) { + return defaultAction(node, p); + } + + /** + * {@inheritDoc} This implementation calls {@code defaultAction}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code defaultAction} */ @Override public R visitCase(CaseTree node, P p) { diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java --- a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java @@ -26,6 +26,7 @@ package com.sun.source.util; import com.sun.source.tree.*; +import com.sun.source.tree.CaseTree.CaseKind; /** * A TreeVisitor that visits all the child tree nodes. @@ -339,11 +340,36 @@ * @param node {@inheritDoc} * @param p {@inheritDoc} * @return the result of scanning + * + * @deprecated + * This method is modeling switch expressions, + * which are part of a preview feature and may be removed + * if the preview feature is removed. */ @Override + @Deprecated(forRemoval=true, since="12") + @SuppressWarnings("removal") + public R visitSwitchExpression(SwitchExpressionTree node, P p) { + R r = scan(node.getExpression(), p); + r = scanAndReduce(node.getCases(), p, r); + return r; + } + + /** + * {@inheritDoc} This implementation scans the children in left to right order. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of scanning + */ + @Override + @SuppressWarnings("removal") public R visitCase(CaseTree node, P p) { - R r = scan(node.getExpression(), p); - r = scanAndReduce(node.getStatements(), p, r); + R r = scan(node.getExpressions(), p); + if (node.getCaseKind() == CaseKind.RULE) + r = scanAndReduce(node.getBody(), p, r); + else + r = scanAndReduce(node.getStatements(), p, r); return r; } @@ -441,8 +467,9 @@ * @return the result of scanning */ @Override + @SuppressWarnings("removal") public R visitBreak(BreakTree node, P p) { - return null; + return scan(node.getValue(), p); } /** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java @@ -165,6 +165,10 @@ * @return true, if given feature is a preview feature. */ public boolean isPreview(Feature feature) { + if (feature == Feature.SWITCH_EXPRESSION || + feature == Feature.SWITCH_MULTIPLE_CASE_LABELS || + feature == Feature.SWITCH_RULE) + return true; //Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing). //When real preview features will be added, this method can be implemented to return 'true' //for those selected features, and 'false' for all the others. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java @@ -180,7 +180,10 @@ UNDERSCORE_IDENTIFIER(MIN, JDK8), PRIVATE_INTERFACE_METHODS(JDK9, Fragments.FeaturePrivateIntfMethods, DiagKind.PLURAL), LOCAL_VARIABLE_TYPE_INFERENCE(JDK10), - IMPORT_ON_DEMAND_OBSERVABLE_PACKAGES(JDK1_2, JDK8); + IMPORT_ON_DEMAND_OBSERVABLE_PACKAGES(JDK1_2, JDK8), + SWITCH_MULTIPLE_CASE_LABELS(JDK12, Fragments.FeatureMultipleCaseLabels, DiagKind.PLURAL), + SWITCH_RULE(JDK12, Fragments.FeatureSwitchRules, DiagKind.PLURAL), + SWITCH_EXPRESSION(JDK12, Fragments.FeatureSwitchExpressions, DiagKind.PLURAL); enum DiagKind { NORMAL, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java @@ -184,6 +184,7 @@ public final Type noClassDefFoundErrorType; public final Type noSuchFieldErrorType; public final Type assertionErrorType; + public final Type incompatibleClassChangeErrorType; public final Type cloneNotSupportedExceptionType; public final Type annotationType; public final TypeSymbol enumSym; @@ -526,6 +527,7 @@ noClassDefFoundErrorType = enterClass("java.lang.NoClassDefFoundError"); noSuchFieldErrorType = enterClass("java.lang.NoSuchFieldError"); assertionErrorType = enterClass("java.lang.AssertionError"); + incompatibleClassChangeErrorType = enterClass("java.lang.IncompatibleClassChangeError"); cloneNotSupportedExceptionType = enterClass("java.lang.CloneNotSupportedException"); annotationType = enterClass("java.lang.annotation.Annotation"); classLoaderType = enterClass("java.lang.ClassLoader"); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java @@ -25,7 +25,6 @@ package com.sun.tools.javac.comp; -import com.sun.source.tree.LambdaExpressionTree.BodyKind; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symtab; @@ -39,10 +38,12 @@ import com.sun.tools.javac.comp.DeferredAttr.DeferredType; import com.sun.tools.javac.comp.DeferredAttr.DeferredTypeCompleter; import com.sun.tools.javac.comp.DeferredAttr.LambdaReturnScanner; +import com.sun.tools.javac.comp.DeferredAttr.SwitchExpressionScanner; import com.sun.tools.javac.comp.Infer.PartiallyInferredMethodType; import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase; import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCBreak; import com.sun.tools.javac.tree.JCTree.JCConditional; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCLambda; @@ -52,6 +53,7 @@ import com.sun.tools.javac.tree.JCTree.JCNewClass; import com.sun.tools.javac.tree.JCTree.JCParens; import com.sun.tools.javac.tree.JCTree.JCReturn; +import com.sun.tools.javac.tree.JCTree.JCSwitchExpression; import com.sun.tools.javac.tree.TreeCopier; import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.Assert; @@ -145,7 +147,7 @@ * Checks a type in the speculative tree against a given result; the type can be either a plain * type or an argument type,in which case a more complex check is required. */ - Type checkSpeculative(JCExpression expr, ResultInfo resultInfo) { + Type checkSpeculative(JCTree expr, ResultInfo resultInfo) { return checkSpeculative(expr, expr.type, resultInfo); } @@ -256,6 +258,11 @@ } @Override + public void visitSwitchExpression(JCSwitchExpression that) { + processArg(that, speculativeTree -> new SwitchExpressionType(that, env, speculativeTree)); + } + + @Override public void visitReference(JCMemberReference tree) { //perform arity-based check Env localEnv = env.dup(tree); @@ -456,6 +463,61 @@ } /** + * Argument type for switch expressions. + */ + class SwitchExpressionType extends ArgumentType { + /** List of break expressions (lazily populated). */ + Optional> breakExpressions = Optional.empty(); + + SwitchExpressionType(JCExpression tree, Env env, JCSwitchExpression speculativeCond) { + this(tree, env, speculativeCond, new HashMap<>()); + } + + SwitchExpressionType(JCExpression tree, Env env, JCSwitchExpression speculativeCond, Map speculativeTypes) { + super(tree, env, speculativeCond, speculativeTypes); + } + + @Override + Type overloadCheck(ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { + ResultInfo localInfo = resultInfo.dup(attr.conditionalContext(resultInfo.checkContext)); + if (resultInfo.pt.hasTag(VOID)) { + //this means we are returning a poly switch expression from void-compatible lambda expression + resultInfo.checkContext.report(tree, attr.diags.fragment(Fragments.SwitchExpressionTargetCantBeVoid)); + return attr.types.createErrorType(resultInfo.pt); + } else { + //poly + for (JCBreak brk : breakExpressions()) { + checkSpeculative(brk.value, brk.value.type, resultInfo); + } + return localInfo.pt; + } + } + + /** Compute return expressions (if needed). */ + List breakExpressions() { + return breakExpressions.orElseGet(() -> { + final List res; + ListBuffer buf = new ListBuffer<>(); + new SwitchExpressionScanner() { + @Override + public void visitBreak(JCBreak tree) { + if (tree.target == speculativeTree) + buf.add(tree); + } + }.scan(speculativeTree.cases); + res = buf.toList(); + breakExpressions = Optional.of(res); + return res; + }); + } + + @Override + ArgumentType dup(JCSwitchExpression tree, Env env) { + return new SwitchExpressionType(tree, env, speculativeTree, speculativeTypes); + } + } + + /** * Argument type for explicit lambdas. */ class ExplicitLambdaType extends ArgumentType { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -26,10 +26,13 @@ package com.sun.tools.javac.comp; import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; import javax.lang.model.element.ElementKind; import javax.tools.JavaFileObject; +import com.sun.source.tree.CaseTree.CaseKind; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.MemberReferenceTree.ReferenceMode; import com.sun.source.tree.MemberSelectTree; @@ -1395,41 +1398,123 @@ } public void visitSwitch(JCSwitch tree) { - Type seltype = attribExpr(tree.selector, env); + handleSwitch(tree, tree.selector, tree.cases, (c, caseEnv) -> { + attribStats(c.stats, caseEnv); + }); + result = null; + } + + public void visitSwitchExpression(JCSwitchExpression tree) { + tree.polyKind = (pt().hasTag(NONE) && pt() != Type.recoveryType && pt() != Infer.anyPoly) ? + PolyKind.STANDALONE : PolyKind.POLY; + + if (tree.polyKind == PolyKind.POLY && resultInfo.pt.hasTag(VOID)) { + //this means we are returning a poly conditional from void-compatible lambda expression + resultInfo.checkContext.report(tree, diags.fragment(Fragments.SwitchExpressionTargetCantBeVoid)); + result = tree.type = types.createErrorType(resultInfo.pt); + return; + } + + ResultInfo condInfo = tree.polyKind == PolyKind.STANDALONE ? + unknownExprInfo : + resultInfo.dup(switchExpressionContext(resultInfo.checkContext)); + + ListBuffer caseTypePositions = new ListBuffer<>(); + ListBuffer caseTypes = new ListBuffer<>(); + + handleSwitch(tree, tree.selector, tree.cases, (c, caseEnv) -> { + caseEnv.info.breakResult = condInfo; + attribStats(c.stats, caseEnv); + new TreeScanner() { + @Override + public void visitBreak(JCBreak brk) { + if (brk.target == tree) { + caseTypePositions.append(brk.value != null ? brk.value.pos() : brk.pos()); + 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) {} + }.scan(c.stats); + }); + + if (tree.cases.isEmpty()) { + log.error(tree.pos(), + Errors.SwitchExpressionEmpty); + } + + Type owntype = (tree.polyKind == PolyKind.STANDALONE) ? condType(caseTypePositions.toList(), caseTypes.toList()) : pt(); + + result = tree.type = check(tree, owntype, KindSelector.VAL, resultInfo); + } + //where: + CheckContext switchExpressionContext(CheckContext checkContext) { + return new Check.NestedCheckContext(checkContext) { + //this will use enclosing check context to check compatibility of + //subexpression against target type; if we are in a method check context, + //depending on whether boxing is allowed, we could have incompatibilities + @Override + public void report(DiagnosticPosition pos, JCDiagnostic details) { + enclosingContext.report(pos, diags.fragment(Fragments.IncompatibleTypeInSwitchExpression(details))); + } + }; + } + + private void handleSwitch(JCTree switchTree, + JCExpression selector, + List cases, + BiConsumer> attribCase) { + Type seltype = attribExpr(selector, env); Env switchEnv = - env.dup(tree, env.info.dup(env.info.scope.dup())); + env.dup(switchTree, env.info.dup(env.info.scope.dup())); try { - boolean enumSwitch = (seltype.tsym.flags() & Flags.ENUM) != 0; boolean stringSwitch = types.isSameType(seltype, syms.stringType); if (!enumSwitch && !stringSwitch) - seltype = chk.checkType(tree.selector.pos(), seltype, syms.intType); + seltype = chk.checkType(selector.pos(), seltype, syms.intType); // Attribute all cases and // check that there are no duplicate case labels or default clauses. Set labels = new HashSet<>(); // The set of case labels. boolean hasDefault = false; // Is there a default label? - for (List l = tree.cases; l.nonEmpty(); l = l.tail) { + @SuppressWarnings("removal") + CaseKind caseKind = null; + boolean wasError = false; + for (List l = cases; l.nonEmpty(); l = l.tail) { JCCase c = l.head; - if (c.pat != null) { - if (enumSwitch) { - Symbol sym = enumConstant(c.pat, seltype); - if (sym == null) { - log.error(c.pat.pos(), Errors.EnumLabelMustBeUnqualifiedEnum); - } else if (!labels.add(sym)) { - log.error(c.pos(), Errors.DuplicateCaseLabel); - } - } else { - Type pattype = attribExpr(c.pat, switchEnv, seltype); - if (!pattype.hasTag(ERROR)) { - if (pattype.constValue() == null) { - log.error(c.pat.pos(), - (stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq)); - } else if (!labels.add(pattype.constValue())) { + if (caseKind == null) { + caseKind = c.caseKind; + } else if (caseKind != c.caseKind && !wasError) { + log.error(c.pos(), + Errors.SwitchMixingCaseTypes); + wasError = true; + } + if (c.getExpressions().nonEmpty()) { + for (JCExpression pat : c.getExpressions()) { + if (TreeInfo.isNull(pat)) { + log.error(pat.pos(), + Errors.SwitchNullNotAllowed); + } else if (enumSwitch) { + Symbol sym = enumConstant(pat, seltype); + if (sym == null) { + log.error(pat.pos(), Errors.EnumLabelMustBeUnqualifiedEnum); + } else if (!labels.add(sym)) { log.error(c.pos(), Errors.DuplicateCaseLabel); } + } else { + Type pattype = attribExpr(pat, switchEnv, seltype); + if (!pattype.hasTag(ERROR)) { + if (pattype.constValue() == null) { + log.error(pat.pos(), + (stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq)); + } else if (!labels.add(pattype.constValue())) { + log.error(c.pos(), Errors.DuplicateCaseLabel); + } + } } } } else if (hasDefault) { @@ -1440,16 +1525,13 @@ Env caseEnv = switchEnv.dup(c, env.info.dup(switchEnv.info.scope.dup())); try { - attribStats(c.stats, caseEnv); + attribCase.accept(c, caseEnv); } finally { caseEnv.info.scope.leave(); addVars(c.stats, switchEnv.info.scope); } } - - result = null; - } - finally { + } finally { switchEnv.info.scope.leave(); } } @@ -1609,7 +1691,9 @@ Type truetype = attribTree(tree.truepart, env, condInfo); Type falsetype = attribTree(tree.falsepart, env, condInfo); - Type owntype = (tree.polyKind == PolyKind.STANDALONE) ? condType(tree, truetype, falsetype) : pt(); + Type owntype = (tree.polyKind == PolyKind.STANDALONE) ? + condType(List.of(tree.truepart.pos(), tree.falsepart.pos()), + List.of(truetype, falsetype)) : pt(); if (condtype.constValue() != null && truetype.constValue() != null && falsetype.constValue() != null && @@ -1690,67 +1774,66 @@ * @param thentype The type of the expression's then-part. * @param elsetype The type of the expression's else-part. */ - Type condType(DiagnosticPosition pos, - Type thentype, Type elsetype) { + Type condType(List positions, List condTypes) { + if (condTypes.isEmpty()) { + return syms.objectType; //TODO: how to handle? + } + if (condTypes.size() == 1) { + return condTypes.head; + } + Type first = condTypes.head; // If same type, that is the result - if (types.isSameType(thentype, elsetype)) - return thentype.baseType(); - - Type thenUnboxed = (thentype.isPrimitive()) - ? thentype : types.unboxedType(thentype); - Type elseUnboxed = (elsetype.isPrimitive()) - ? elsetype : types.unboxedType(elsetype); + if (condTypes.tail.stream().allMatch(t -> types.isSameType(first, t))) + return first.baseType(); + + List unboxedTypes = condTypes.stream() + .map(t -> t.isPrimitive() ? t : types.unboxedType(t)) + .collect(List.collector()); // Otherwise, if both arms can be converted to a numeric // type, return the least numeric type that fits both arms // (i.e. return larger of the two, or return int if one // arm is short, the other is char). - if (thenUnboxed.isPrimitive() && elseUnboxed.isPrimitive()) { + if (unboxedTypes.stream().allMatch(t -> t.isPrimitive())) { // If one arm has an integer subrange type (i.e., byte, // short, or char), and the other is an integer constant // that fits into the subrange, return the subrange type. - if (thenUnboxed.getTag().isStrictSubRangeOf(INT) && - elseUnboxed.hasTag(INT) && - types.isAssignable(elseUnboxed, thenUnboxed)) { - return thenUnboxed.baseType(); - } - if (elseUnboxed.getTag().isStrictSubRangeOf(INT) && - thenUnboxed.hasTag(INT) && - types.isAssignable(thenUnboxed, elseUnboxed)) { - return elseUnboxed.baseType(); + for (Type type : unboxedTypes) { + if (!type.getTag().isStrictSubRangeOf(INT)) { + continue; + } + if (unboxedTypes.stream().filter(t -> t != type).allMatch(t -> t.hasTag(INT) && types.isAssignable(t, type))) + return type.baseType(); } for (TypeTag tag : primitiveTags) { Type candidate = syms.typeOfTag[tag.ordinal()]; - if (types.isSubtype(thenUnboxed, candidate) && - types.isSubtype(elseUnboxed, candidate)) { + if (unboxedTypes.stream().allMatch(t -> types.isSubtype(t, candidate))) { return candidate; } } } // Those were all the cases that could result in a primitive - if (thentype.isPrimitive()) - thentype = types.boxedClass(thentype).type; - if (elsetype.isPrimitive()) - elsetype = types.boxedClass(elsetype).type; - - if (types.isSubtype(thentype, elsetype)) - return elsetype.baseType(); - if (types.isSubtype(elsetype, thentype)) - return thentype.baseType(); - - if (thentype.hasTag(VOID) || elsetype.hasTag(VOID)) { - log.error(pos, - Errors.NeitherConditionalSubtype(thentype, - elsetype)); - return thentype.baseType(); - } + condTypes = condTypes.stream() + .map(t -> t.isPrimitive() ? types.boxedClass(t).type : t) + .collect(List.collector()); + + for (Type type : condTypes) { + if (condTypes.stream().filter(t -> t != type).allMatch(t -> types.isAssignable(t, type))) + return type.baseType(); + } + + Iterator posIt = positions.iterator(); + + condTypes = condTypes.stream() + .map(t -> chk.checkNonVoid(posIt.next(), t)) + .collect(List.collector()); // both are known to be reference types. The result is // lub(thentype,elsetype). This cannot fail, as it will // always be possible to infer "Object" if nothing better. - return types.lub(thentype.baseType(), elsetype.baseType()); + return types.lub(condTypes.stream().map(t -> t.baseType()).collect(List.collector())); } final static TypeTag[] primitiveTags = new TypeTag[]{ @@ -1782,7 +1865,68 @@ } 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_EXPRESSION)) { + 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