8206986: Compiler support for Switch Expressions (Preview)

8207405: Compiler Tree API support for Switch Expressions (Preview)

Support for switch expression, switch with rules and multiple constants for cases.

Reviewed-by: jjg, mcimadamore, vromero
This commit is contained in:
Jan Lahoda 2018-08-29 09:36:17 +02:00
parent 3f4b55c4df
commit b3b644438e
121 changed files with 4753 additions and 268 deletions

View File

@ -95,6 +95,7 @@ public interface MessageType {
KIND_NAME("kind name", "KindName", "com.sun.tools.javac.code.Kinds"), KIND_NAME("kind name", "KindName", "com.sun.tools.javac.code.Kinds"),
TARGET("target", "Target", "com.sun.tools.javac.jvm"), TARGET("target", "Target", "com.sun.tools.javac.jvm"),
TOKEN("token", "TokenKind", "com.sun.tools.javac.parser.Tokens"), TOKEN("token", "TokenKind", "com.sun.tools.javac.parser.Tokens"),
TREE_TAG("tree tag", "Tag", "com.sun.tools.javac.tree.JCTree"),
TYPE("type", "Type", "com.sun.tools.javac.code"), TYPE("type", "Type", "com.sun.tools.javac.code"),
URL("url", "URL", "java.net"), URL("url", "URL", "java.net"),
SET("set", "Set", "java.util"), SET("set", "Set", "java.util"),

View File

@ -35,6 +35,8 @@ import javax.lang.model.element.Name;
* break; * break;
* *
* break <em>label</em> ; * break <em>label</em> ;
*
* break <em>expression</em> ;
* </pre> * </pre>
* *
* @jls section 14.15 * @jls section 14.15
@ -49,4 +51,18 @@ public interface BreakTree extends StatementTree {
* @return the label * @return the label
*/ */
Name getLabel(); 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();
} }

View File

@ -28,7 +28,7 @@ package com.sun.source.tree;
import java.util.List; 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: * For example:
* <pre> * <pre>
@ -49,13 +49,89 @@ public interface CaseTree extends Tree {
/** /**
* Returns the expression for the case, or * Returns the expression for the case, or
* {@code null} if this is the default case. * {@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 * @return the expression for the case, or null
*/ */
ExpressionTree getExpression(); ExpressionTree getExpression();
/** /**
* Returns the statements labeled by the case. * Returns the labels for this case.
* @return the statements labeled by the 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<? extends ExpressionTree> getExpressions();
/**
* For case with kind {@linkplain CaseKind#STATEMENT},
* returns the statements labeled by the case.
* Returns {@code null} for case with kind
* {@linkplain CaseKind#RULE}.
* @return the statements labeled by the case or null
*/ */
List<? extends StatementTree> getStatements(); List<? extends StatementTree> 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:
* <ul>
* <li>STATEMENT: {@code case <expression>: <statements>}</li>
* <li>RULE: {@code case <expression> -> <expression>/<statement>}</li>
* </ul>
*
* @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 <expression>: <statements>}.
*/
STATEMENT,
/**
* Case is in the form: {@code case <expression> -> <expression>}.
*/
RULE;
}
} }

View File

@ -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:
* <pre>
* switch ( <em>expression</em> ) {
* <em>cases</em>
* }
* </pre>
*
* @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<? extends CaseTree> getCases();
}

View File

@ -239,6 +239,20 @@ public interface Tree {
*/ */
SWITCH(SwitchTree.class), 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}. * Used for instances of {@link SynchronizedTree}.
*/ */

View File

@ -353,6 +353,23 @@ public interface TreeVisitor<R,P> {
*/ */
R visitSwitch(SwitchTree node, P p); 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. * Visits a SynchronizedTree node.
* @param node the node being visited * @param node the node being visited

View File

@ -263,6 +263,25 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
return defaultAction(node, p); return defaultAction(node, p);
} }
/**
* {@inheritDoc} This implementation calls {@code defaultAction}.
*
* @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}. * {@inheritDoc} This implementation calls {@code defaultAction}.
* *

View File

@ -26,6 +26,7 @@
package com.sun.source.util; package com.sun.source.util;
import com.sun.source.tree.*; import com.sun.source.tree.*;
import com.sun.source.tree.CaseTree.CaseKind;
/** /**
* A TreeVisitor that visits all the child tree nodes. * A TreeVisitor that visits all the child tree nodes.
@ -339,10 +340,35 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
* @param node {@inheritDoc} * @param node {@inheritDoc}
* @param p {@inheritDoc} * @param p {@inheritDoc}
* @return the result of scanning * @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 @Override
public R visitCase(CaseTree node, P p) { @Deprecated(forRemoval=true, since="12")
@SuppressWarnings("removal")
public R visitSwitchExpression(SwitchExpressionTree node, P p) {
R r = scan(node.getExpression(), 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.getExpressions(), p);
if (node.getCaseKind() == CaseKind.RULE)
r = scanAndReduce(node.getBody(), p, r);
else
r = scanAndReduce(node.getStatements(), p, r); r = scanAndReduce(node.getStatements(), p, r);
return r; return r;
} }
@ -441,8 +467,9 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
* @return the result of scanning * @return the result of scanning
*/ */
@Override @Override
@SuppressWarnings("removal")
public R visitBreak(BreakTree node, P p) { public R visitBreak(BreakTree node, P p) {
return null; return scan(node.getValue(), p);
} }
/** /**

View File

@ -165,6 +165,10 @@ public class Preview {
* @return true, if given feature is a preview feature. * @return true, if given feature is a preview feature.
*/ */
public boolean isPreview(Feature 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). //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' //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. //for those selected features, and 'false' for all the others.

View File

@ -180,7 +180,10 @@ public enum Source {
UNDERSCORE_IDENTIFIER(MIN, JDK8), UNDERSCORE_IDENTIFIER(MIN, JDK8),
PRIVATE_INTERFACE_METHODS(JDK9, Fragments.FeaturePrivateIntfMethods, DiagKind.PLURAL), PRIVATE_INTERFACE_METHODS(JDK9, Fragments.FeaturePrivateIntfMethods, DiagKind.PLURAL),
LOCAL_VARIABLE_TYPE_INFERENCE(JDK10), 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 { enum DiagKind {
NORMAL, NORMAL,

View File

@ -184,6 +184,7 @@ public class Symtab {
public final Type noClassDefFoundErrorType; public final Type noClassDefFoundErrorType;
public final Type noSuchFieldErrorType; public final Type noSuchFieldErrorType;
public final Type assertionErrorType; public final Type assertionErrorType;
public final Type incompatibleClassChangeErrorType;
public final Type cloneNotSupportedExceptionType; public final Type cloneNotSupportedExceptionType;
public final Type annotationType; public final Type annotationType;
public final TypeSymbol enumSym; public final TypeSymbol enumSym;
@ -526,6 +527,7 @@ public class Symtab {
noClassDefFoundErrorType = enterClass("java.lang.NoClassDefFoundError"); noClassDefFoundErrorType = enterClass("java.lang.NoClassDefFoundError");
noSuchFieldErrorType = enterClass("java.lang.NoSuchFieldError"); noSuchFieldErrorType = enterClass("java.lang.NoSuchFieldError");
assertionErrorType = enterClass("java.lang.AssertionError"); assertionErrorType = enterClass("java.lang.AssertionError");
incompatibleClassChangeErrorType = enterClass("java.lang.IncompatibleClassChangeError");
cloneNotSupportedExceptionType = enterClass("java.lang.CloneNotSupportedException"); cloneNotSupportedExceptionType = enterClass("java.lang.CloneNotSupportedException");
annotationType = enterClass("java.lang.annotation.Annotation"); annotationType = enterClass("java.lang.annotation.Annotation");
classLoaderType = enterClass("java.lang.ClassLoader"); classLoaderType = enterClass("java.lang.ClassLoader");

View File

@ -25,7 +25,6 @@
package com.sun.tools.javac.comp; 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.Flags;
import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Symtab;
@ -39,10 +38,12 @@ import com.sun.tools.javac.comp.DeferredAttr.DeferredAttrContext;
import com.sun.tools.javac.comp.DeferredAttr.DeferredType; import com.sun.tools.javac.comp.DeferredAttr.DeferredType;
import com.sun.tools.javac.comp.DeferredAttr.DeferredTypeCompleter; import com.sun.tools.javac.comp.DeferredAttr.DeferredTypeCompleter;
import com.sun.tools.javac.comp.DeferredAttr.LambdaReturnScanner; 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.Infer.PartiallyInferredMethodType;
import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase; import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase;
import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.resources.CompilerProperties.Fragments;
import com.sun.tools.javac.tree.JCTree; 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.JCConditional;
import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCLambda; import com.sun.tools.javac.tree.JCTree.JCLambda;
@ -52,6 +53,7 @@ import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCNewClass; import com.sun.tools.javac.tree.JCTree.JCNewClass;
import com.sun.tools.javac.tree.JCTree.JCParens; import com.sun.tools.javac.tree.JCTree.JCParens;
import com.sun.tools.javac.tree.JCTree.JCReturn; 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.TreeCopier;
import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Assert;
@ -145,7 +147,7 @@ public class ArgumentAttr extends JCTree.Visitor {
* Checks a type in the speculative tree against a given result; the type can be either a plain * 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 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); return checkSpeculative(expr, expr.type, resultInfo);
} }
@ -255,6 +257,11 @@ public class ArgumentAttr extends JCTree.Visitor {
processArg(that, speculativeTree -> new ConditionalType(that, env, speculativeTree)); processArg(that, speculativeTree -> new ConditionalType(that, env, speculativeTree));
} }
@Override
public void visitSwitchExpression(JCSwitchExpression that) {
processArg(that, speculativeTree -> new SwitchExpressionType(that, env, speculativeTree));
}
@Override @Override
public void visitReference(JCMemberReference tree) { public void visitReference(JCMemberReference tree) {
//perform arity-based check //perform arity-based check
@ -455,6 +462,61 @@ public class ArgumentAttr extends JCTree.Visitor {
} }
} }
/**
* 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<>());
}
SwitchExpressionType(JCExpression tree, Env<AttrContext> env, JCSwitchExpression speculativeCond, Map<ResultInfo, Type> 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<JCBreak> breakExpressions() {
return breakExpressions.orElseGet(() -> {
final List<JCBreak> res;
ListBuffer<JCBreak> 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<JCSwitchExpression> dup(JCSwitchExpression tree, Env<AttrContext> env) {
return new SwitchExpressionType(tree, env, speculativeTree, speculativeTypes);
}
}
/** /**
* Argument type for explicit lambdas. * Argument type for explicit lambdas.
*/ */

View File

@ -26,10 +26,13 @@
package com.sun.tools.javac.comp; package com.sun.tools.javac.comp;
import java.util.*; import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import javax.lang.model.element.ElementKind; import javax.lang.model.element.ElementKind;
import javax.tools.JavaFileObject; import javax.tools.JavaFileObject;
import com.sun.source.tree.CaseTree.CaseKind;
import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberReferenceTree.ReferenceMode; import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MemberSelectTree;
@ -1395,43 +1398,125 @@ public class Attr extends JCTree.Visitor {
} }
public void visitSwitch(JCSwitch tree) { 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<DiagnosticPosition> caseTypePositions = new ListBuffer<>();
ListBuffer<Type> 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<JCCase> cases,
BiConsumer<JCCase, Env<AttrContext>> attribCase) {
Type seltype = attribExpr(selector, env);
Env<AttrContext> switchEnv = Env<AttrContext> switchEnv =
env.dup(tree, env.info.dup(env.info.scope.dup())); env.dup(switchTree, env.info.dup(env.info.scope.dup()));
try { try {
boolean enumSwitch = (seltype.tsym.flags() & Flags.ENUM) != 0; boolean enumSwitch = (seltype.tsym.flags() & Flags.ENUM) != 0;
boolean stringSwitch = types.isSameType(seltype, syms.stringType); boolean stringSwitch = types.isSameType(seltype, syms.stringType);
if (!enumSwitch && !stringSwitch) if (!enumSwitch && !stringSwitch)
seltype = chk.checkType(tree.selector.pos(), seltype, syms.intType); seltype = chk.checkType(selector.pos(), seltype, syms.intType);
// Attribute all cases and // Attribute all cases and
// check that there are no duplicate case labels or default clauses. // check that there are no duplicate case labels or default clauses.
Set<Object> labels = new HashSet<>(); // The set of case labels. Set<Object> labels = new HashSet<>(); // The set of case labels.
boolean hasDefault = false; // Is there a default label? boolean hasDefault = false; // Is there a default label?
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { @SuppressWarnings("removal")
CaseKind caseKind = null;
boolean wasError = false;
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
JCCase c = l.head; JCCase c = l.head;
if (c.pat != null) { if (caseKind == null) {
if (enumSwitch) { caseKind = c.caseKind;
Symbol sym = enumConstant(c.pat, seltype); } 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) { if (sym == null) {
log.error(c.pat.pos(), Errors.EnumLabelMustBeUnqualifiedEnum); log.error(pat.pos(), Errors.EnumLabelMustBeUnqualifiedEnum);
} else if (!labels.add(sym)) { } else if (!labels.add(sym)) {
log.error(c.pos(), Errors.DuplicateCaseLabel); log.error(c.pos(), Errors.DuplicateCaseLabel);
} }
} else { } else {
Type pattype = attribExpr(c.pat, switchEnv, seltype); Type pattype = attribExpr(pat, switchEnv, seltype);
if (!pattype.hasTag(ERROR)) { if (!pattype.hasTag(ERROR)) {
if (pattype.constValue() == null) { if (pattype.constValue() == null) {
log.error(c.pat.pos(), log.error(pat.pos(),
(stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq)); (stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq));
} else if (!labels.add(pattype.constValue())) { } else if (!labels.add(pattype.constValue())) {
log.error(c.pos(), Errors.DuplicateCaseLabel); log.error(c.pos(), Errors.DuplicateCaseLabel);
} }
} }
} }
}
} else if (hasDefault) { } else if (hasDefault) {
log.error(c.pos(), Errors.DuplicateDefaultLabel); log.error(c.pos(), Errors.DuplicateDefaultLabel);
} else { } else {
@ -1440,16 +1525,13 @@ public class Attr extends JCTree.Visitor {
Env<AttrContext> caseEnv = Env<AttrContext> caseEnv =
switchEnv.dup(c, env.info.dup(switchEnv.info.scope.dup())); switchEnv.dup(c, env.info.dup(switchEnv.info.scope.dup()));
try { try {
attribStats(c.stats, caseEnv); attribCase.accept(c, caseEnv);
} finally { } finally {
caseEnv.info.scope.leave(); caseEnv.info.scope.leave();
addVars(c.stats, switchEnv.info.scope); addVars(c.stats, switchEnv.info.scope);
} }
} }
} finally {
result = null;
}
finally {
switchEnv.info.scope.leave(); switchEnv.info.scope.leave();
} }
} }
@ -1609,7 +1691,9 @@ public class Attr extends JCTree.Visitor {
Type truetype = attribTree(tree.truepart, env, condInfo); Type truetype = attribTree(tree.truepart, env, condInfo);
Type falsetype = attribTree(tree.falsepart, 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 && if (condtype.constValue() != null &&
truetype.constValue() != null && truetype.constValue() != null &&
falsetype.constValue() != null && falsetype.constValue() != null &&
@ -1690,67 +1774,66 @@ public class Attr extends JCTree.Visitor {
* @param thentype The type of the expression's then-part. * @param thentype The type of the expression's then-part.
* @param elsetype The type of the expression's else-part. * @param elsetype The type of the expression's else-part.
*/ */
Type condType(DiagnosticPosition pos, Type condType(List<DiagnosticPosition> positions, List<Type> condTypes) {
Type thentype, Type elsetype) { 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 same type, that is the result
if (types.isSameType(thentype, elsetype)) if (condTypes.tail.stream().allMatch(t -> types.isSameType(first, t)))
return thentype.baseType(); return first.baseType();
Type thenUnboxed = (thentype.isPrimitive()) List<Type> unboxedTypes = condTypes.stream()
? thentype : types.unboxedType(thentype); .map(t -> t.isPrimitive() ? t : types.unboxedType(t))
Type elseUnboxed = (elsetype.isPrimitive()) .collect(List.collector());
? elsetype : types.unboxedType(elsetype);
// Otherwise, if both arms can be converted to a numeric // Otherwise, if both arms can be converted to a numeric
// type, return the least numeric type that fits both arms // type, return the least numeric type that fits both arms
// (i.e. return larger of the two, or return int if one // (i.e. return larger of the two, or return int if one
// arm is short, the other is char). // 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, // If one arm has an integer subrange type (i.e., byte,
// short, or char), and the other is an integer constant // short, or char), and the other is an integer constant
// that fits into the subrange, return the subrange type. // that fits into the subrange, return the subrange type.
if (thenUnboxed.getTag().isStrictSubRangeOf(INT) && for (Type type : unboxedTypes) {
elseUnboxed.hasTag(INT) && if (!type.getTag().isStrictSubRangeOf(INT)) {
types.isAssignable(elseUnboxed, thenUnboxed)) { continue;
return thenUnboxed.baseType();
} }
if (elseUnboxed.getTag().isStrictSubRangeOf(INT) && if (unboxedTypes.stream().filter(t -> t != type).allMatch(t -> t.hasTag(INT) && types.isAssignable(t, type)))
thenUnboxed.hasTag(INT) && return type.baseType();
types.isAssignable(thenUnboxed, elseUnboxed)) {
return elseUnboxed.baseType();
} }
for (TypeTag tag : primitiveTags) { for (TypeTag tag : primitiveTags) {
Type candidate = syms.typeOfTag[tag.ordinal()]; Type candidate = syms.typeOfTag[tag.ordinal()];
if (types.isSubtype(thenUnboxed, candidate) && if (unboxedTypes.stream().allMatch(t -> types.isSubtype(t, candidate))) {
types.isSubtype(elseUnboxed, candidate)) {
return candidate; return candidate;
} }
} }
} }
// Those were all the cases that could result in a primitive // Those were all the cases that could result in a primitive
if (thentype.isPrimitive()) condTypes = condTypes.stream()
thentype = types.boxedClass(thentype).type; .map(t -> t.isPrimitive() ? types.boxedClass(t).type : t)
if (elsetype.isPrimitive()) .collect(List.collector());
elsetype = types.boxedClass(elsetype).type;
if (types.isSubtype(thentype, elsetype)) for (Type type : condTypes) {
return elsetype.baseType(); if (condTypes.stream().filter(t -> t != type).allMatch(t -> types.isAssignable(t, type)))
if (types.isSubtype(elsetype, thentype)) return type.baseType();
return thentype.baseType();
if (thentype.hasTag(VOID) || elsetype.hasTag(VOID)) {
log.error(pos,
Errors.NeitherConditionalSubtype(thentype,
elsetype));
return thentype.baseType();
} }
Iterator<DiagnosticPosition> 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 // both are known to be reference types. The result is
// lub(thentype,elsetype). This cannot fail, as it will // lub(thentype,elsetype). This cannot fail, as it will
// always be possible to infer "Object" if nothing better. // 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[]{ final static TypeTag[] primitiveTags = new TypeTag[]{
@ -1782,7 +1865,68 @@ public class Attr extends JCTree.Visitor {
} }
public void visitBreak(JCBreak tree) { 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 <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) {
JCTree speculative = deferredAttr.attribSpeculative(tree.value, env, unknownExprInfo);
if (!speculative.type.hasTag(ERROR)) {
log.error(tree.pos(), Errors.BreakAmbiguousTarget(label));
if (jumpTarget.snd == null) {
tree.target = jumpTarget.fst;
attribute = false;
} else {
//nothing
}
} else {
if (jumpTarget.snd != null) {
log.error(tree.pos(), jumpTarget.snd);
}
tree.target = jumpTarget.fst;
attribute = false;
}
}
}
if (attribute) {
attribTree(tree.value, env, env.info.breakResult);
JCTree immediateTarget = findJumpTarget(tree.pos(), tree.getTag(), null, env);
if (immediateTarget.getTag() != SWITCH_EXPRESSION) {
log.error(tree.pos(), Errors.BreakExprNotImmediate(immediateTarget.getTag()));
Env<AttrContext> env1 = env;
while (env1 != null && env1.tree.getTag() != SWITCH_EXPRESSION) {
env1 = env1.next;
}
Assert.checkNonNull(env1);
tree.target = env1.tree;
} else {
tree.target = immediateTarget;
}
}
}
} 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; result = null;
} }
@ -1808,8 +1952,32 @@ public class Attr extends JCTree.Visitor {
JCTree.Tag tag, JCTree.Tag tag,
Name label, Name label,
Env<AttrContext> env) { 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. // Search environments outwards from the point of jump.
Env<AttrContext> env1 = env; Env<AttrContext> env1 = env;
JCDiagnostic.Error pendingError = null;
LOOP: LOOP:
while (env1 != null) { while (env1 != null) {
switch (env1.tree.getTag()) { switch (env1.tree.getTag()) {
@ -1821,13 +1989,14 @@ public class Attr extends JCTree.Visitor {
if (!labelled.body.hasTag(DOLOOP) && if (!labelled.body.hasTag(DOLOOP) &&
!labelled.body.hasTag(WHILELOOP) && !labelled.body.hasTag(WHILELOOP) &&
!labelled.body.hasTag(FORLOOP) && !labelled.body.hasTag(FORLOOP) &&
!labelled.body.hasTag(FOREACHLOOP)) !labelled.body.hasTag(FOREACHLOOP)) {
log.error(pos, Errors.NotLoopLabel(label)); pendingError = Errors.NotLoopLabel(label);
}
// Found labelled statement target, now go inwards // Found labelled statement target, now go inwards
// to next non-labelled tree. // to next non-labelled tree.
return TreeInfo.referencedStatement(labelled); return Pair.of(TreeInfo.referencedStatement(labelled), pendingError);
} else { } else {
return labelled; return Pair.of(labelled, pendingError);
} }
} }
break; break;
@ -1835,10 +2004,21 @@ public class Attr extends JCTree.Visitor {
case WHILELOOP: case WHILELOOP:
case FORLOOP: case FORLOOP:
case FOREACHLOOP: case FOREACHLOOP:
if (label == null) return env1.tree; if (label == null) return Pair.of(env1.tree, pendingError);
break; break;
case SWITCH: case SWITCH:
if (label == null && tag == BREAK) return env1.tree; if (label == null && tag == BREAK) return Pair.of(env1.tree, null);
break;
case SWITCH_EXPRESSION:
if (tag == BREAK) {
if (label == null) {
return Pair.of(env1.tree, null);
} else {
pendingError = Errors.BreakOutsideSwitchExpression;
}
} else {
pendingError = Errors.ContinueOutsideSwitchExpression;
}
break; break;
case LAMBDA: case LAMBDA:
case METHODDEF: case METHODDEF:
@ -1849,12 +2029,11 @@ public class Attr extends JCTree.Visitor {
env1 = env1.next; env1 = env1.next;
} }
if (label != null) if (label != null)
log.error(pos, Errors.UndefLabel(label)); return Pair.of(null, Errors.UndefLabel(label));
else if (tag == CONTINUE) else if (tag == CONTINUE)
log.error(pos, Errors.ContOutsideLoop); return Pair.of(null, Errors.ContOutsideLoop);
else else
log.error(pos, Errors.BreakOutsideSwitchLoop); return Pair.of(null, Errors.BreakOutsideSwitchLoop);
return null;
} }
public void visitReturn(JCReturn tree) { public void visitReturn(JCReturn tree) {
@ -1862,6 +2041,8 @@ public class Attr extends JCTree.Visitor {
// nested within than the enclosing class. // nested within than the enclosing class.
if (env.info.returnResult == null) { if (env.info.returnResult == null) {
log.error(tree.pos(), Errors.RetOutsideMeth); log.error(tree.pos(), Errors.RetOutsideMeth);
} else if (env.info.breakResult != null) {
log.error(tree.pos(), Errors.ReturnOutsideSwitchExpression);
} else { } else {
// Attribute return expression, if it exists, and check that // Attribute return expression, if it exists, and check that
// it conforms to result type of enclosing method. // it conforms to result type of enclosing method.
@ -2944,6 +3125,7 @@ public class Attr extends JCTree.Visitor {
} else { } else {
lambdaEnv = env.dup(that, env.info.dup(env.info.scope.dup())); lambdaEnv = env.dup(that, env.info.dup(env.info.scope.dup()));
} }
lambdaEnv.info.breakResult = null;
return lambdaEnv; return lambdaEnv;
} }

View File

@ -101,6 +101,11 @@ public class AttrContext {
*/ */
Attr.ResultInfo returnResult = null; 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 /** Symbol corresponding to the site of a qualified default super call
*/ */
Type defaultSuperCallSite = null; Type defaultSuperCallSite = null;
@ -124,6 +129,7 @@ public class AttrContext {
info.lint = lint; info.lint = lint;
info.enclVar = enclVar; info.enclVar = enclVar;
info.returnResult = returnResult; info.returnResult = returnResult;
info.breakResult = breakResult;
info.defaultSuperCallSite = defaultSuperCallSite; info.defaultSuperCallSite = defaultSuperCallSite;
info.isSerializable = isSerializable; info.isSerializable = isSerializable;
info.isLambda = isLambda; info.isLambda = isLambda;

View File

@ -1153,6 +1153,18 @@ public class DeferredAttr extends JCTree.Visitor {
} }
} }
/**
* 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 * This visitor is used to check that structural expressions conform
* to their target - this step is required as inference could end up * to their target - this step is required as inference could end up

View File

@ -28,6 +28,8 @@
package com.sun.tools.javac.comp; package com.sun.tools.javac.comp;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import com.sun.source.tree.LambdaExpressionTree.BodyKind; import com.sun.source.tree.LambdaExpressionTree.BodyKind;
import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.*;
@ -211,7 +213,7 @@ public class Flow {
public void analyzeTree(Env<AttrContext> env, TreeMaker make) { public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
new AliveAnalyzer().analyzeTree(env, make); new AliveAnalyzer().analyzeTree(env, make);
new AssignAnalyzer().analyzeTree(env); new AssignAnalyzer().analyzeTree(env, make);
new FlowAnalyzer().analyzeTree(env, make); new FlowAnalyzer().analyzeTree(env, make);
new CaptureAnalyzer().analyzeTree(env, make); new CaptureAnalyzer().analyzeTree(env, make);
} }
@ -244,7 +246,7 @@ public class Flow {
//related errors, which will allow for more errors to be detected //related errors, which will allow for more errors to be detected
Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
try { try {
new LambdaAssignAnalyzer(env).analyzeTree(env, that); new LambdaAssignAnalyzer(env).analyzeTree(env, that, make);
LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer(); LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer();
flowAnalyzer.analyzeTree(env, that, make); flowAnalyzer.analyzeTree(env, that, make);
return flowAnalyzer.inferredThrownTypes; return flowAnalyzer.inferredThrownTypes;
@ -396,6 +398,12 @@ public class Flow {
public void visitPackageDef(JCPackageDecl tree) { public void visitPackageDef(JCPackageDecl tree) {
// Do nothing for PackageDecl // Do nothing for PackageDecl
} }
protected void scanSyntheticBreak(TreeMaker make, JCTree swtch) {
JCBreak brk = make.at(Position.NOPOS).Break(null);
brk.target = swtch;
scan(brk);
}
} }
/** /**
@ -596,11 +604,19 @@ public class Flow {
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
alive = true; alive = true;
JCCase c = l.head; JCCase c = l.head;
if (c.pat == null) if (c.pats.isEmpty())
hasDefault = true; hasDefault = true;
else else {
scan(c.pat); for (JCExpression pat : c.pats) {
scan(pat);
}
}
scanStats(c.stats); scanStats(c.stats);
c.completesNormally = alive;
if (alive && c.caseKind == JCCase.RULE) {
scanSyntheticBreak(make, tree);
alive = false;
}
// Warn about fall-through if lint switch fallthrough enabled. // Warn about fall-through if lint switch fallthrough enabled.
if (alive && if (alive &&
lint.isEnabled(Lint.LintCategory.FALLTHROUGH) && lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
@ -615,6 +631,46 @@ public class Flow {
alive |= resolveBreaks(tree, prevPendingExits); alive |= resolveBreaks(tree, prevPendingExits);
} }
@Override
public void visitSwitchExpression(JCSwitchExpression tree) {
ListBuffer<PendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>();
scan(tree.selector);
Set<Object> constants = null;
if ((tree.selector.type.tsym.flags() & ENUM) != 0) {
constants = new HashSet<>();
for (Symbol s : tree.selector.type.tsym.members().getSymbols(s -> (s.flags() & ENUM) != 0)) {
constants.add(s.name);
}
}
boolean hasDefault = false;
boolean prevAlive = alive;
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
alive = true;
JCCase c = l.head;
if (c.pats.isEmpty())
hasDefault = true;
else {
for (JCExpression pat : c.pats) {
scan(pat);
if (constants != null) {
if (pat.hasTag(IDENT))
constants.remove(((JCIdent) pat).name);
if (pat.type != null)
constants.remove(pat.type.constValue());
}
}
}
scanStats(c.stats);
c.completesNormally = alive;
}
if ((constants == null || !constants.isEmpty()) && !hasDefault) {
log.error(tree, Errors.NotExhaustive);
}
alive = prevAlive;
alive |= resolveBreaks(tree, prevPendingExits);
}
public void visitTry(JCTry tree) { public void visitTry(JCTry tree) {
ListBuffer<PendingExit> prevPendingExits = pendingExits; ListBuffer<PendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>(); pendingExits = new ListBuffer<>();
@ -680,6 +736,8 @@ public class Flow {
} }
public void visitBreak(JCBreak tree) { public void visitBreak(JCBreak tree) {
if (tree.isValueBreak())
scan(tree.value);
recordExit(new PendingExit(tree)); recordExit(new PendingExit(tree));
} }
@ -1040,14 +1098,21 @@ public class Flow {
} }
public void visitSwitch(JCSwitch tree) { public void visitSwitch(JCSwitch tree) {
handleSwitch(tree, tree.selector, tree.cases);
}
@Override
public void visitSwitchExpression(JCSwitchExpression tree) {
handleSwitch(tree, tree.selector, tree.cases);
}
private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>(); pendingExits = new ListBuffer<>();
scan(tree.selector); scan(selector);
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
JCCase c = l.head; JCCase c = l.head;
if (c.pat != null) { scan(c.pats);
scan(c.pat);
}
scan(c.stats); scan(c.stats);
} }
resolveBreaks(tree, prevPendingExits); resolveBreaks(tree, prevPendingExits);
@ -1191,6 +1256,8 @@ public class Flow {
} }
public void visitBreak(JCBreak tree) { public void visitBreak(JCBreak tree) {
if (tree.isValueBreak())
scan(tree.value);
recordExit(new FlowPendingExit(tree, null)); recordExit(new FlowPendingExit(tree, null));
} }
@ -2105,27 +2172,40 @@ public class Flow {
} }
public void visitSwitch(JCSwitch tree) { public void visitSwitch(JCSwitch tree) {
handleSwitch(tree, tree.selector, tree.cases);
}
public void visitSwitchExpression(JCSwitchExpression tree) {
handleSwitch(tree, tree.selector, tree.cases);
}
private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>(); pendingExits = new ListBuffer<>();
int nextadrPrev = nextadr; int nextadrPrev = nextadr;
scanExpr(tree.selector); scanExpr(selector);
final Bits initsSwitch = new Bits(inits); final Bits initsSwitch = new Bits(inits);
final Bits uninitsSwitch = new Bits(uninits); final Bits uninitsSwitch = new Bits(uninits);
boolean hasDefault = false; boolean hasDefault = false;
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
inits.assign(initsSwitch); inits.assign(initsSwitch);
uninits.assign(uninits.andSet(uninitsSwitch)); uninits.assign(uninits.andSet(uninitsSwitch));
JCCase c = l.head; JCCase c = l.head;
if (c.pat == null) { if (c.pats.isEmpty()) {
hasDefault = true; hasDefault = true;
} else { } else {
scanExpr(c.pat); for (JCExpression pat : c.pats) {
scanExpr(pat);
}
} }
if (hasDefault) { if (hasDefault) {
inits.assign(initsSwitch); inits.assign(initsSwitch);
uninits.assign(uninits.andSet(uninitsSwitch)); uninits.assign(uninits.andSet(uninitsSwitch));
} }
scan(c.stats); scan(c.stats);
if (c.completesNormally && c.caseKind == JCCase.RULE) {
scanSyntheticBreak(make, tree);
}
addVars(c.stats, initsSwitch, uninitsSwitch); addVars(c.stats, initsSwitch, uninitsSwitch);
if (!hasDefault) { if (!hasDefault) {
inits.assign(initsSwitch); inits.assign(initsSwitch);
@ -2301,6 +2381,8 @@ public class Flow {
@Override @Override
public void visitBreak(JCBreak tree) { public void visitBreak(JCBreak tree) {
if (tree.isValueBreak())
scan(tree.value);
recordExit(new AssignPendingExit(tree, inits, uninits)); recordExit(new AssignPendingExit(tree, inits, uninits));
} }
@ -2479,11 +2561,11 @@ public class Flow {
/** Perform definite assignment/unassignment analysis on a tree. /** Perform definite assignment/unassignment analysis on a tree.
*/ */
public void analyzeTree(Env<?> env) { public void analyzeTree(Env<?> env, TreeMaker make) {
analyzeTree(env, env.tree); analyzeTree(env, env.tree, make);
} }
public void analyzeTree(Env<?> env, JCTree tree) { public void analyzeTree(Env<?> env, JCTree tree, TreeMaker make) {
try { try {
startPos = tree.pos().getStartPosition(); startPos = tree.pos().getStartPosition();
@ -2494,6 +2576,7 @@ public class Flow {
vardecls[i] = null; vardecls[i] = null;
firstadr = 0; firstadr = 0;
nextadr = 0; nextadr = 0;
Flow.this.make = make;
pendingExits = new ListBuffer<>(); pendingExits = new ListBuffer<>();
this.classDef = null; this.classDef = null;
unrefdResources = WriteableScope.create(env.enclClass.sym); unrefdResources = WriteableScope.create(env.enclClass.sym);
@ -2509,6 +2592,7 @@ public class Flow {
} }
firstadr = 0; firstadr = 0;
nextadr = 0; nextadr = 0;
Flow.this.make = null;
pendingExits = null; pendingExits = null;
this.classDef = null; this.classDef = null;
unrefdResources = null; unrefdResources = null;
@ -2661,6 +2745,12 @@ public class Flow {
super.visitTry(tree); super.visitTry(tree);
} }
@Override
public void visitBreak(JCBreak tree) {
if (tree.isValueBreak())
scan(tree.value);
}
public void visitModuleDef(JCModuleDecl tree) { public void visitModuleDef(JCModuleDecl tree) {
// Do nothing for modules // Do nothing for modules
} }

View File

@ -715,7 +715,7 @@ public class LambdaToMethod extends TreeTranslator {
JCBreak br = make.Break(null); JCBreak br = make.Break(null);
breaks.add(br); breaks.add(br);
List<JCStatement> stmts = entry.getValue().append(br).toList(); List<JCStatement> stmts = entry.getValue().append(br).toList();
cases.add(make.Case(make.Literal(entry.getKey()), stmts)); cases.add(make.Case(JCCase.STATEMENT, List.of(make.Literal(entry.getKey())), stmts, null));
} }
JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList()); JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
for (JCBreak br : breaks) { for (JCBreak br : breaks) {

View File

@ -26,7 +26,11 @@
package com.sun.tools.javac.comp; package com.sun.tools.javac.comp;
import java.util.*; import java.util.*;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.stream.Stream;
import com.sun.source.tree.CaseTree.CaseKind;
import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Kinds.KindSelector; import com.sun.tools.javac.code.Kinds.KindSelector;
import com.sun.tools.javac.code.Scope.WriteableScope; import com.sun.tools.javac.code.Scope.WriteableScope;
@ -54,6 +58,10 @@ import static com.sun.tools.javac.code.TypeTag.*;
import static com.sun.tools.javac.code.Kinds.Kind.*; import static com.sun.tools.javac.code.Kinds.Kind.*;
import static com.sun.tools.javac.code.Symbol.OperatorSymbol.AccessCode.DEREF; import static com.sun.tools.javac.code.Symbol.OperatorSymbol.AccessCode.DEREF;
import static com.sun.tools.javac.jvm.ByteCodes.*; import static com.sun.tools.javac.jvm.ByteCodes.*;
import com.sun.tools.javac.tree.JCTree.JCBreak;
import com.sun.tools.javac.tree.JCTree.JCCase;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT; import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT;
import static com.sun.tools.javac.tree.JCTree.Tag.*; import static com.sun.tools.javac.tree.JCTree.Tag.*;
@ -263,6 +271,13 @@ public class Lower extends TreeTranslator {
} }
super.visitApply(tree); super.visitApply(tree);
} }
@Override
public void visitBreak(JCBreak tree) {
if (tree.isValueBreak())
scan(tree.value);
}
} }
/** /**
@ -364,6 +379,7 @@ public class Lower extends TreeTranslator {
} }
super.visitApply(tree); super.visitApply(tree);
} }
} }
ClassSymbol ownerToCopyFreeVarsFrom(ClassSymbol c) { ClassSymbol ownerToCopyFreeVarsFrom(ClassSymbol c) {
@ -3340,6 +3356,45 @@ public class Lower extends TreeTranslator {
} }
public void visitSwitch(JCSwitch tree) { public void visitSwitch(JCSwitch tree) {
//expand multiple label cases:
ListBuffer<JCCase> cases = new ListBuffer<>();
for (JCCase c : tree.cases) {
switch (c.pats.size()) {
case 0: //default
case 1: //single label
cases.append(c);
break;
default: //multiple labels, expand:
//case C1, C2, C3: ...
//=>
//case C1:
//case C2:
//case C3: ...
List<JCExpression> patterns = c.pats;
while (patterns.tail.nonEmpty()) {
cases.append(make_at(c.pos()).Case(JCCase.STATEMENT,
List.of(patterns.head),
List.nil(),
null));
patterns = patterns.tail;
}
c.pats = patterns;
cases.append(c);
break;
}
}
for (JCCase c : cases) {
if (c.caseKind == JCCase.RULE && c.completesNormally) {
JCBreak b = make_at(c.pos()).Break(null);
b.target = tree;
c.stats = c.stats.append(b);
}
}
tree.cases = cases.toList();
Type selsuper = types.supertype(tree.selector.type); Type selsuper = types.supertype(tree.selector.type);
boolean enumSwitch = selsuper != null && boolean enumSwitch = selsuper != null &&
(tree.selector.type.tsym.flags() & ENUM) != 0; (tree.selector.type.tsym.flags() & ENUM) != 0;
@ -3371,10 +3426,10 @@ public class Lower extends TreeTranslator {
ordinalMethod))); ordinalMethod)));
ListBuffer<JCCase> cases = new ListBuffer<>(); ListBuffer<JCCase> cases = new ListBuffer<>();
for (JCCase c : tree.cases) { for (JCCase c : tree.cases) {
if (c.pat != null) { if (c.pats.nonEmpty()) {
VarSymbol label = (VarSymbol)TreeInfo.symbol(c.pat); VarSymbol label = (VarSymbol)TreeInfo.symbol(c.pats.head);
JCLiteral pat = map.forConstant(label); JCLiteral pat = map.forConstant(label);
cases.append(make.Case(pat, c.stats)); cases.append(make.Case(JCCase.STATEMENT, List.of(pat), c.stats, null));
} else { } else {
cases.append(c); cases.append(c);
} }
@ -3442,10 +3497,10 @@ public class Lower extends TreeTranslator {
Map<Integer, Set<String>> hashToString = new LinkedHashMap<>(alternatives + 1, 1.0f); Map<Integer, Set<String>> hashToString = new LinkedHashMap<>(alternatives + 1, 1.0f);
int casePosition = 0; int casePosition = 0;
for(JCCase oneCase : caseList) {
JCExpression expression = oneCase.getExpression();
if (expression != null) { // expression for a "default" case is null for(JCCase oneCase : caseList) {
if (oneCase.pats.nonEmpty()) { // pats is empty for a "default" case
JCExpression expression = oneCase.pats.head;
String labelExpr = (String) expression.type.constValue(); String labelExpr = (String) expression.type.constValue();
Integer mapping = caseLabelToPosition.put(labelExpr, casePosition); Integer mapping = caseLabelToPosition.put(labelExpr, casePosition);
Assert.checkNull(mapping); Assert.checkNull(mapping);
@ -3529,7 +3584,7 @@ public class Lower extends TreeTranslator {
breakStmt.target = switch1; breakStmt.target = switch1;
lb.append(elsepart).append(breakStmt); lb.append(elsepart).append(breakStmt);
caseBuffer.append(make.Case(make.Literal(hashCode), lb.toList())); caseBuffer.append(make.Case(JCCase.STATEMENT, List.of(make.Literal(hashCode)), lb.toList(), null));
} }
switch1.cases = caseBuffer.toList(); switch1.cases = caseBuffer.toList();
@ -3546,18 +3601,17 @@ public class Lower extends TreeTranslator {
// replacement switch being created. // replacement switch being created.
patchTargets(oneCase, tree, switch2); patchTargets(oneCase, tree, switch2);
boolean isDefault = (oneCase.getExpression() == null); boolean isDefault = (oneCase.pats.isEmpty());
JCExpression caseExpr; JCExpression caseExpr;
if (isDefault) if (isDefault)
caseExpr = null; caseExpr = null;
else { else {
caseExpr = make.Literal(caseLabelToPosition.get((String)TreeInfo.skipParens(oneCase. caseExpr = make.Literal(caseLabelToPosition.get((String)TreeInfo.skipParens(oneCase.pats.head).
getExpression()).
type.constValue())); type.constValue()));
} }
lb.append(make.Case(caseExpr, lb.append(make.Case(JCCase.STATEMENT, caseExpr == null ? List.nil() : List.of(caseExpr),
oneCase.getStatements())); oneCase.getStatements(), null));
} }
switch2.cases = lb.toList(); switch2.cases = lb.toList();
@ -3567,6 +3621,76 @@ public class Lower extends TreeTranslator {
} }
} }
@Override
public void visitSwitchExpression(JCSwitchExpression tree) {
//translates switch expression to statement switch:
//switch (selector) {
// case C: break value;
// ...
//}
//=>
//(letexpr T exprswitch$;
// switch (selector) {
// case C: { exprswitch$ = value; break; }
// }
// exprswitch$
//)
VarSymbol dollar_switchexpr = new VarSymbol(Flags.FINAL|Flags.SYNTHETIC,
names.fromString("exprswitch" + tree.pos + target.syntheticNameChar()),
tree.type,
currentMethodSym);
ListBuffer<JCStatement> stmtList = new ListBuffer<>();
stmtList.append(make.at(tree.pos()).VarDef(dollar_switchexpr, null).setType(dollar_switchexpr.type));
JCSwitch switchStatement = make.Switch(tree.selector, null);
switchStatement.cases =
tree.cases.stream()
.map(c -> convertCase(dollar_switchexpr, switchStatement, tree, c))
.collect(List.collector());
if (tree.cases.stream().noneMatch(c -> c.pats.isEmpty())) {
JCThrow thr = make.Throw(makeNewClass(syms.incompatibleClassChangeErrorType,
List.nil()));
JCCase c = make.Case(JCCase.STATEMENT, List.nil(), List.of(thr), null);
switchStatement.cases = switchStatement.cases.append(c);
}
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,
JCSwitchExpression switchExpr, JCCase c) {
make.at(c.pos());
ListBuffer<JCStatement> statements = new ListBuffer<>();
statements.addAll(new TreeTranslator() {
@Override
public void visitLambda(JCLambda tree) {}
@Override
public void visitClassDef(JCClassDecl tree) {}
@Override
public void visitMethodDef(JCMethodDecl tree) {}
@Override
public void visitBreak(JCBreak tree) {
if (tree.target == switchExpr) {
tree.target = switchStatement;
JCExpressionStatement assignment =
make.Exec(make.Assign(make.Ident(dollar_switchexpr),
translate(tree.value))
.setType(dollar_switchexpr.type));
result = make.Block(0, List.of(assignment,
tree));
tree.value = null;
} else {
result = tree;
}
}
}.translate(c.stats));
return make.Case(JCCase.STATEMENT, c.pats, statements.toList(), null);
}
public void visitNewArray(JCNewArray tree) { public void visitNewArray(JCNewArray tree) {
tree.elemtype = translate(tree.elemtype); tree.elemtype = translate(tree.elemtype);
for (List<JCExpression> t = tree.dims; t.tail != null; t = t.tail) for (List<JCExpression> t = tree.dims; t.tail != null; t = t.tail)
@ -3602,7 +3726,7 @@ public class Lower extends TreeTranslator {
} }
public void visitLetExpr(LetExpr tree) { public void visitLetExpr(LetExpr tree) {
tree.defs = translateVarDefs(tree.defs); tree.defs = translate(tree.defs);
tree.expr = translate(tree.expr, tree.type); tree.expr = translate(tree.expr, tree.type);
result = tree; result = tree;
} }

View File

@ -48,6 +48,7 @@ import static com.sun.tools.javac.code.TypeTag.CLASS;
import static com.sun.tools.javac.code.TypeTag.TYPEVAR; import static com.sun.tools.javac.code.TypeTag.TYPEVAR;
import static com.sun.tools.javac.code.TypeTag.VOID; import static com.sun.tools.javac.code.TypeTag.VOID;
import static com.sun.tools.javac.comp.CompileStates.CompileState; import static com.sun.tools.javac.comp.CompileStates.CompileState;
import com.sun.tools.javac.tree.JCTree.JCBreak;
/** This pass translates Generic Java to conventional Java. /** This pass translates Generic Java to conventional Java.
* *
@ -561,11 +562,22 @@ public class TransTypes extends TreeTranslator {
} }
public void visitCase(JCCase tree) { public void visitCase(JCCase tree) {
tree.pat = translate(tree.pat, null); tree.pats = translate(tree.pats, null);
tree.stats = translate(tree.stats); tree.stats = translate(tree.stats);
result = tree; result = tree;
} }
public void visitSwitchExpression(JCSwitchExpression tree) {
Type selsuper = types.supertype(tree.selector.type);
boolean enumSwitch = selsuper != null &&
selsuper.tsym == syms.enumSym;
Type target = enumSwitch ? erasure(tree.selector.type) : syms.intType;
tree.selector = translate(tree.selector, target);
tree.cases = translate(tree.cases);
tree.type = erasure(tree.type);
result = retype(tree, tree.type, pt);
}
public void visitSynchronized(JCSynchronized tree) { public void visitSynchronized(JCSynchronized tree) {
tree.lock = translate(tree.lock, erasure(tree.lock.type)); tree.lock = translate(tree.lock, erasure(tree.lock.type));
tree.body = translate(tree.body); tree.body = translate(tree.body);
@ -606,6 +618,16 @@ public class TransTypes extends TreeTranslator {
result = tree; result = tree;
} }
@Override
public void visitBreak(JCBreak tree) {
if (tree.isValueBreak()) {
tree.value = translate(tree.value, erasure(tree.value.type));
tree.value.type = erasure(tree.value.type);
tree.value = retype(tree.value, tree.value.type, pt);
}
result = tree;
}
public void visitThrow(JCThrow tree) { public void visitThrow(JCThrow tree) {
tree.expr = translate(tree.expr, erasure(tree.expr.type)); tree.expr = translate(tree.expr, erasure(tree.expr.type));
result = tree; result = tree;

View File

@ -259,13 +259,13 @@ public class TreeDiffer extends TreeScanner {
@Override @Override
public void visitBreak(JCBreak tree) { public void visitBreak(JCBreak tree) {
JCBreak that = (JCBreak) parameter; JCBreak that = (JCBreak) parameter;
result = tree.label == that.label; result = scan(tree.value, that.value);
} }
@Override @Override
public void visitCase(JCCase tree) { public void visitCase(JCCase tree) {
JCCase that = (JCCase) parameter; JCCase that = (JCCase) parameter;
result = scan(tree.pat, that.pat) && scan(tree.stats, that.stats); result = scan(tree.pats, that.pats) && scan(tree.stats, that.stats);
} }
@Override @Override

View File

@ -313,7 +313,7 @@ implements CRTFlags {
public void visitCase(JCCase tree) { public void visitCase(JCCase tree) {
SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
sr.mergeWith(csp(tree.pat)); sr.mergeWith(csp(tree.pats));
sr.mergeWith(csp(tree.stats)); sr.mergeWith(csp(tree.stats));
result = sr; result = sr;
} }

View File

@ -183,6 +183,8 @@ public class Code {
final MethodSymbol meth; final MethodSymbol meth;
private int letExprStackPos = 0;
/** Construct a code object, given the settings of the fatcode, /** Construct a code object, given the settings of the fatcode,
* debugging info switches and the CharacterRangeTable. * debugging info switches and the CharacterRangeTable.
*/ */
@ -382,7 +384,7 @@ public class Code {
} }
void postop() { void postop() {
Assert.check(alive || state.stacksize == 0); Assert.check(alive || isStatementStart());
} }
/** Emit a ldc (or ldc_w) instruction, taking into account operand size /** Emit a ldc (or ldc_w) instruction, taking into account operand size
@ -1211,6 +1213,15 @@ public class Code {
return pc; return pc;
} }
public int setLetExprStackPos(int pos) {
int res = letExprStackPos;
letExprStackPos = pos;
return res;
}
public boolean isStatementStart() {
return state.stacksize == letExprStackPos;
}
/************************************************************************** /**************************************************************************
* Stack map generation * Stack map generation
@ -1474,7 +1485,7 @@ public class Code {
State newState = state; State newState = state;
for (; chain != null; chain = chain.next) { for (; chain != null; chain = chain.next) {
Assert.check(state != chain.state Assert.check(state != chain.state
&& (target > chain.pc || state.stacksize == 0)); && (target > chain.pc || isStatementStart()));
if (target >= cp) { if (target >= cp) {
target = cp; target = cp;
} else if (get1(target) == goto_) { } else if (get1(target) == goto_) {

View File

@ -81,11 +81,6 @@ public class Gen extends JCTree.Visitor {
*/ */
private final Type methodType; private final Type methodType;
/**
* Are we presently traversing a let expression ? Yes if depth != 0
*/
private int letExprDepth;
public static Gen instance(Context context) { public static Gen instance(Context context) {
Gen instance = context.get(genKey); Gen instance = context.get(genKey);
if (instance == null) if (instance == null)
@ -1011,10 +1006,10 @@ public class Gen extends JCTree.Visitor {
if (tree.init != null) { if (tree.init != null) {
checkStringConstant(tree.init.pos(), v.getConstValue()); checkStringConstant(tree.init.pos(), v.getConstValue());
if (v.getConstValue() == null || varDebugInfo) { if (v.getConstValue() == null || varDebugInfo) {
Assert.check(letExprDepth != 0 || code.state.stacksize == 0); Assert.check(code.isStatementStart());
genExpr(tree.init, v.erasure(types)).load(); genExpr(tree.init, v.erasure(types)).load();
items.makeLocalItem(v).store(); items.makeLocalItem(v).store();
Assert.check(letExprDepth != 0 || code.state.stacksize == 0); Assert.check(code.isStatementStart());
} }
} }
checkDimension(tree.pos(), v.type); checkDimension(tree.pos(), v.type);
@ -1069,14 +1064,14 @@ public class Gen extends JCTree.Visitor {
CondItem c; CondItem c;
if (cond != null) { if (cond != null) {
code.statBegin(cond.pos); code.statBegin(cond.pos);
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER); c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER);
} else { } else {
c = items.makeCondItem(goto_); c = items.makeCondItem(goto_);
} }
Chain loopDone = c.jumpFalse(); Chain loopDone = c.jumpFalse();
code.resolve(c.trueJumps); code.resolve(c.trueJumps);
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET);
code.resolve(loopEnv.info.cont); code.resolve(loopEnv.info.cont);
genStats(step, loopEnv); genStats(step, loopEnv);
@ -1090,13 +1085,13 @@ public class Gen extends JCTree.Visitor {
CondItem c; CondItem c;
if (cond != null) { if (cond != null) {
code.statBegin(cond.pos); code.statBegin(cond.pos);
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER); c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER);
} else { } else {
c = items.makeCondItem(goto_); c = items.makeCondItem(goto_);
} }
code.resolve(c.jumpTrue(), startpc); code.resolve(c.jumpTrue(), startpc);
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
code.resolve(c.falseJumps); code.resolve(c.falseJumps);
} }
} }
@ -1125,7 +1120,7 @@ public class Gen extends JCTree.Visitor {
int limit = code.nextreg; int limit = code.nextreg;
Assert.check(!tree.selector.type.hasTag(CLASS)); Assert.check(!tree.selector.type.hasTag(CLASS));
int startpcCrt = genCrt ? code.curCP() : 0; int startpcCrt = genCrt ? code.curCP() : 0;
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
Item sel = genExpr(tree.selector, syms.intType); Item sel = genExpr(tree.selector, syms.intType);
List<JCCase> cases = tree.cases; List<JCCase> cases = tree.cases;
if (cases.isEmpty()) { if (cases.isEmpty()) {
@ -1154,8 +1149,9 @@ public class Gen extends JCTree.Visitor {
List<JCCase> l = cases; List<JCCase> l = cases;
for (int i = 0; i < labels.length; i++) { for (int i = 0; i < labels.length; i++) {
if (l.head.pat != null) { if (l.head.pats.nonEmpty()) {
int val = ((Number)l.head.pat.type.constValue()).intValue(); Assert.check(l.head.pats.size() == 1);
int val = ((Number)l.head.pats.head.type.constValue()).intValue();
labels[i] = val; labels[i] = val;
if (val < lo) lo = val; if (val < lo) lo = val;
if (hi < val) hi = val; if (hi < val) hi = val;
@ -1294,7 +1290,7 @@ public class Gen extends JCTree.Visitor {
int limit = code.nextreg; int limit = code.nextreg;
// Generate code to evaluate lock and save in temporary variable. // Generate code to evaluate lock and save in temporary variable.
final LocalItem lockVar = makeTemp(syms.objectType); final LocalItem lockVar = makeTemp(syms.objectType);
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
genExpr(tree.lock, tree.lock.type).load().duplicate(); genExpr(tree.lock, tree.lock.type).load().duplicate();
lockVar.store(); lockVar.store();
@ -1556,11 +1552,11 @@ public class Gen extends JCTree.Visitor {
public void visitIf(JCIf tree) { public void visitIf(JCIf tree) {
int limit = code.nextreg; int limit = code.nextreg;
Chain thenExit = null; Chain thenExit = null;
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
CondItem c = genCond(TreeInfo.skipParens(tree.cond), CondItem c = genCond(TreeInfo.skipParens(tree.cond),
CRT_FLOW_CONTROLLER); CRT_FLOW_CONTROLLER);
Chain elseChain = c.jumpFalse(); Chain elseChain = c.jumpFalse();
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
if (!c.isFalse()) { if (!c.isFalse()) {
code.resolve(c.trueJumps); code.resolve(c.trueJumps);
genStat(tree.thenpart, env, CRT_STATEMENT | CRT_FLOW_TARGET); genStat(tree.thenpart, env, CRT_STATEMENT | CRT_FLOW_TARGET);
@ -1574,7 +1570,7 @@ public class Gen extends JCTree.Visitor {
} }
code.resolve(thenExit); code.resolve(thenExit);
code.endScopes(limit); code.endScopes(limit);
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
} }
public void visitExec(JCExpressionStatement tree) { public void visitExec(JCExpressionStatement tree) {
@ -1588,16 +1584,16 @@ public class Gen extends JCTree.Visitor {
((JCUnary) e).setTag(PREDEC); ((JCUnary) e).setTag(PREDEC);
break; break;
} }
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
genExpr(tree.expr, tree.expr.type).drop(); genExpr(tree.expr, tree.expr.type).drop();
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
} }
public void visitBreak(JCBreak tree) { public void visitBreak(JCBreak tree) {
int tmpPos = code.pendingStatPos; int tmpPos = code.pendingStatPos;
Env<GenContext> targetEnv = unwind(tree.target, env); Env<GenContext> targetEnv = unwind(tree.target, env);
code.pendingStatPos = tmpPos; code.pendingStatPos = tmpPos;
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
targetEnv.info.addExit(code.branch(goto_)); targetEnv.info.addExit(code.branch(goto_));
endFinalizerGaps(env, targetEnv); endFinalizerGaps(env, targetEnv);
} }
@ -1606,7 +1602,7 @@ public class Gen extends JCTree.Visitor {
int tmpPos = code.pendingStatPos; int tmpPos = code.pendingStatPos;
Env<GenContext> targetEnv = unwind(tree.target, env); Env<GenContext> targetEnv = unwind(tree.target, env);
code.pendingStatPos = tmpPos; code.pendingStatPos = tmpPos;
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
targetEnv.info.addCont(code.branch(goto_)); targetEnv.info.addCont(code.branch(goto_));
endFinalizerGaps(env, targetEnv); endFinalizerGaps(env, targetEnv);
} }
@ -1620,7 +1616,7 @@ public class Gen extends JCTree.Visitor {
*/ */
int tmpPos = code.pendingStatPos; int tmpPos = code.pendingStatPos;
if (tree.expr != null) { if (tree.expr != null) {
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
Item r = genExpr(tree.expr, pt).load(); Item r = genExpr(tree.expr, pt).load();
if (hasFinally(env.enclMethod, env)) { if (hasFinally(env.enclMethod, env)) {
r = makeTemp(pt); r = makeTemp(pt);
@ -1640,10 +1636,10 @@ public class Gen extends JCTree.Visitor {
} }
public void visitThrow(JCThrow tree) { public void visitThrow(JCThrow tree) {
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
genExpr(tree.expr, tree.expr.type).load(); genExpr(tree.expr, tree.expr.type).load();
code.emitop0(athrow); code.emitop0(athrow);
Assert.check(code.state.stacksize == 0); Assert.check(code.isStatementStart());
} }
/* ************************************************************************ /* ************************************************************************
@ -2147,12 +2143,15 @@ public class Gen extends JCTree.Visitor {
} }
public void visitLetExpr(LetExpr tree) { public void visitLetExpr(LetExpr tree) {
letExprDepth++;
int limit = code.nextreg; int limit = code.nextreg;
int prevLetExprStart = code.setLetExprStackPos(code.state.stacksize);
try {
genStats(tree.defs, env); genStats(tree.defs, env);
} finally {
code.setLetExprStackPos(prevLetExprStart);
}
result = genExpr(tree.expr, tree.expr.type).load(); result = genExpr(tree.expr, tree.expr.type).load();
code.endScopes(limit); code.endScopes(limit);
letExprDepth--;
} }
private void generateReferencesToPrunedTree(ClassSymbol classSymbol, Pool pool) { private void generateReferencesToPrunedTree(ClassSymbol classSymbol, Pool pool) {

View File

@ -26,8 +26,10 @@
package com.sun.tools.javac.parser; package com.sun.tools.javac.parser;
import java.util.*; import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.sun.source.tree.CaseTree.CaseKind;
import com.sun.source.tree.MemberReferenceTree.ReferenceMode; import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.source.tree.ModuleTree.ModuleKind; import com.sun.source.tree.ModuleTree.ModuleKind;
@ -218,12 +220,22 @@ public class JavacParser implements Parser {
* mode = TYPE : a type * mode = TYPE : a type
* mode = NOPARAMS : no parameters allowed for type * mode = NOPARAMS : no parameters allowed for type
* mode = TYPEARG : type argument * mode = TYPEARG : type argument
* mode |= NOLAMBDA : lambdas are not allowed
*/ */
protected static final int EXPR = 0x1; protected static final int EXPR = 0x1;
protected static final int TYPE = 0x2; protected static final int TYPE = 0x2;
protected static final int NOPARAMS = 0x4; protected static final int NOPARAMS = 0x4;
protected static final int TYPEARG = 0x8; protected static final int TYPEARG = 0x8;
protected static final int DIAMOND = 0x10; protected static final int DIAMOND = 0x10;
protected static final int NOLAMBDA = 0x20;
protected void selectExprMode() {
mode = (mode & NOLAMBDA) | EXPR;
}
protected void selectTypeMode() {
mode = (mode & NOLAMBDA) | TYPE;
}
/** The current mode. /** The current mode.
*/ */
@ -427,11 +439,18 @@ public class JavacParser implements Parser {
* an error. * an error.
*/ */
public void accept(TokenKind tk) { public void accept(TokenKind tk) {
accept(tk, Errors::Expected);
}
/** If next input token matches given token, skip it, otherwise report
* an error.
*/
public void accept(TokenKind tk, Function<TokenKind, Error> errorProvider) {
if (token.kind == tk) { if (token.kind == tk) {
nextToken(); nextToken();
} else { } else {
setErrorEndPos(token.pos); setErrorEndPos(token.pos);
reportSyntaxError(S.prevToken().endPos, Errors.Expected(tk)); reportSyntaxError(S.prevToken().endPos, errorProvider.apply(tk));
} }
} }
@ -796,7 +815,7 @@ public class JavacParser implements Parser {
case EQ: { case EQ: {
int pos = token.pos; int pos = token.pos;
nextToken(); nextToken();
mode = EXPR; selectExprMode();
JCExpression t1 = term(); JCExpression t1 = term();
return toP(F.at(pos).Assign(t, t1)); return toP(F.at(pos).Assign(t, t1));
} }
@ -814,7 +833,7 @@ public class JavacParser implements Parser {
int pos = token.pos; int pos = token.pos;
TokenKind tk = token.kind; TokenKind tk = token.kind;
nextToken(); nextToken();
mode = EXPR; selectExprMode();
JCExpression t1 = term(); JCExpression t1 = term();
return F.at(pos).Assignop(optag(tk), t, t1); return F.at(pos).Assignop(optag(tk), t, t1);
default: default:
@ -829,7 +848,7 @@ public class JavacParser implements Parser {
JCExpression term1() { JCExpression term1() {
JCExpression t = term2(); JCExpression t = term2();
if ((mode & EXPR) != 0 && token.kind == QUES) { if ((mode & EXPR) != 0 && token.kind == QUES) {
mode = EXPR; selectExprMode();
return term1Rest(t); return term1Rest(t);
} else { } else {
return t; return t;
@ -858,7 +877,7 @@ public class JavacParser implements Parser {
JCExpression term2() { JCExpression term2() {
JCExpression t = term3(); JCExpression t = term3();
if ((mode & EXPR) != 0 && prec(token.kind) >= TreeInfo.orPrec) { if ((mode & EXPR) != 0 && prec(token.kind) >= TreeInfo.orPrec) {
mode = EXPR; selectExprMode();
return term2Rest(t, TreeInfo.orPrec); return term2Rest(t, TreeInfo.orPrec);
} else { } else {
return t; return t;
@ -1058,7 +1077,7 @@ public class JavacParser implements Parser {
switch (token.kind) { switch (token.kind) {
case QUES: case QUES:
if ((mode & TYPE) != 0 && (mode & (TYPEARG|NOPARAMS)) == TYPEARG) { if ((mode & TYPE) != 0 && (mode & (TYPEARG|NOPARAMS)) == TYPEARG) {
mode = TYPE; selectTypeMode();
return typeArgument(); return typeArgument();
} else } else
return illegal(); return illegal();
@ -1066,11 +1085,11 @@ public class JavacParser implements Parser {
if (typeArgs == null && (mode & EXPR) != 0) { if (typeArgs == null && (mode & EXPR) != 0) {
TokenKind tk = token.kind; TokenKind tk = token.kind;
nextToken(); nextToken();
mode = EXPR; selectExprMode();
if (tk == SUB && if (tk == SUB &&
(token.kind == INTLITERAL || token.kind == LONGLITERAL) && (token.kind == INTLITERAL || token.kind == LONGLITERAL) &&
token.radix() == 10) { token.radix() == 10) {
mode = EXPR; selectExprMode();
t = literal(names.hyphen, pos); t = literal(names.hyphen, pos);
} else { } else {
t = term3(); t = term3();
@ -1084,7 +1103,7 @@ public class JavacParser implements Parser {
switch (pres) { switch (pres) {
case CAST: case CAST:
accept(LPAREN); accept(LPAREN);
mode = TYPE; selectTypeMode();
int pos1 = pos; int pos1 = pos;
List<JCExpression> targets = List.of(t = parseType()); List<JCExpression> targets = List.of(t = parseType());
while (token.kind == AMP) { while (token.kind == AMP) {
@ -1096,7 +1115,7 @@ public class JavacParser implements Parser {
t = toP(F.at(pos1).TypeIntersection(targets.reverse())); t = toP(F.at(pos1).TypeIntersection(targets.reverse()));
} }
accept(RPAREN); accept(RPAREN);
mode = EXPR; selectExprMode();
JCExpression t1 = term3(); JCExpression t1 = term3();
return F.at(pos).TypeCast(t, t1); return F.at(pos).TypeCast(t, t1);
case IMPLICIT_LAMBDA: case IMPLICIT_LAMBDA:
@ -1105,7 +1124,7 @@ public class JavacParser implements Parser {
break; break;
default: //PARENS default: //PARENS
accept(LPAREN); accept(LPAREN);
mode = EXPR; selectExprMode();
t = termRest(term1Rest(term2Rest(term3(), TreeInfo.orPrec))); t = termRest(term1Rest(term2Rest(term3(), TreeInfo.orPrec)));
accept(RPAREN); accept(RPAREN);
t = toP(F.at(pos).Parens(t)); t = toP(F.at(pos).Parens(t));
@ -1117,7 +1136,7 @@ public class JavacParser implements Parser {
break; break;
case THIS: case THIS:
if ((mode & EXPR) != 0) { if ((mode & EXPR) != 0) {
mode = EXPR; selectExprMode();
t = to(F.at(pos).Ident(names._this)); t = to(F.at(pos).Ident(names._this));
nextToken(); nextToken();
if (typeArgs == null) if (typeArgs == null)
@ -1129,7 +1148,7 @@ public class JavacParser implements Parser {
break; break;
case SUPER: case SUPER:
if ((mode & EXPR) != 0) { if ((mode & EXPR) != 0) {
mode = EXPR; selectExprMode();
t = to(F.at(pos).Ident(names._super)); t = to(F.at(pos).Ident(names._super));
t = superSuffix(typeArgs, t); t = superSuffix(typeArgs, t);
typeArgs = null; typeArgs = null;
@ -1139,14 +1158,14 @@ public class JavacParser implements Parser {
case CHARLITERAL: case STRINGLITERAL: case CHARLITERAL: case STRINGLITERAL:
case TRUE: case FALSE: case NULL: case TRUE: case FALSE: case NULL:
if (typeArgs == null && (mode & EXPR) != 0) { if (typeArgs == null && (mode & EXPR) != 0) {
mode = EXPR; selectExprMode();
t = literal(names.empty); t = literal(names.empty);
} else return illegal(); } else return illegal();
break; break;
case NEW: case NEW:
if (typeArgs != null) return illegal(); if (typeArgs != null) return illegal();
if ((mode & EXPR) != 0) { if ((mode & EXPR) != 0) {
mode = EXPR; selectExprMode();
nextToken(); nextToken();
if (token.kind == LT) typeArgs = typeArguments(false); if (token.kind == LT) typeArgs = typeArguments(false);
t = creator(pos, typeArgs); t = creator(pos, typeArgs);
@ -1193,7 +1212,7 @@ public class JavacParser implements Parser {
break; break;
case UNDERSCORE: case IDENTIFIER: case ASSERT: case ENUM: case UNDERSCORE: case IDENTIFIER: case ASSERT: case ENUM:
if (typeArgs != null) return illegal(); if (typeArgs != null) return illegal();
if ((mode & EXPR) != 0 && peekToken(ARROW)) { if ((mode & EXPR) != 0 && (mode & NOLAMBDA) == 0 && peekToken(ARROW)) {
t = lambdaExpressionOrStatement(false, false, pos); t = lambdaExpressionOrStatement(false, false, pos);
} else { } else {
t = toP(F.at(token.pos).Ident(ident())); t = toP(F.at(token.pos).Ident(ident()));
@ -1219,7 +1238,7 @@ public class JavacParser implements Parser {
t = bracketsSuffix(t); t = bracketsSuffix(t);
} else { } else {
if ((mode & EXPR) != 0) { if ((mode & EXPR) != 0) {
mode = EXPR; selectExprMode();
JCExpression t1 = term(); JCExpression t1 = term();
if (!annos.isEmpty()) t = illegal(annos.head.pos); if (!annos.isEmpty()) t = illegal(annos.head.pos);
t = to(F.at(pos).Indexed(t, t1)); t = to(F.at(pos).Indexed(t, t1));
@ -1229,7 +1248,7 @@ public class JavacParser implements Parser {
break loop; break loop;
case LPAREN: case LPAREN:
if ((mode & EXPR) != 0) { if ((mode & EXPR) != 0) {
mode = EXPR; selectExprMode();
t = arguments(typeArgs, t); t = arguments(typeArgs, t);
if (!annos.isEmpty()) t = illegal(annos.head.pos); if (!annos.isEmpty()) t = illegal(annos.head.pos);
typeArgs = null; typeArgs = null;
@ -1248,25 +1267,25 @@ public class JavacParser implements Parser {
switch (token.kind) { switch (token.kind) {
case CLASS: case CLASS:
if (typeArgs != null) return illegal(); if (typeArgs != null) return illegal();
mode = EXPR; selectExprMode();
t = to(F.at(pos).Select(t, names._class)); t = to(F.at(pos).Select(t, names._class));
nextToken(); nextToken();
break loop; break loop;
case THIS: case THIS:
if (typeArgs != null) return illegal(); if (typeArgs != null) return illegal();
mode = EXPR; selectExprMode();
t = to(F.at(pos).Select(t, names._this)); t = to(F.at(pos).Select(t, names._this));
nextToken(); nextToken();
break loop; break loop;
case SUPER: case SUPER:
mode = EXPR; selectExprMode();
t = to(F.at(pos).Select(t, names._super)); t = to(F.at(pos).Select(t, names._super));
t = superSuffix(typeArgs, t); t = superSuffix(typeArgs, t);
typeArgs = null; typeArgs = null;
break loop; break loop;
case NEW: case NEW:
if (typeArgs != null) return illegal(); if (typeArgs != null) return illegal();
mode = EXPR; selectExprMode();
int pos1 = token.pos; int pos1 = token.pos;
nextToken(); nextToken();
if (token.kind == LT) typeArgs = typeArguments(false); if (token.kind == LT) typeArgs = typeArguments(false);
@ -1310,7 +1329,7 @@ public class JavacParser implements Parser {
t = toP(F.at(pos1).TypeApply(t, args.toList())); t = toP(F.at(pos1).TypeApply(t, args.toList()));
while (token.kind == DOT) { while (token.kind == DOT) {
nextToken(); nextToken();
mode = TYPE; selectTypeMode();
t = toP(F.at(token.pos).Select(t, ident())); t = toP(F.at(token.pos).Select(t, ident()));
t = typeArgumentsOpt(t); t = typeArgumentsOpt(t);
} }
@ -1319,7 +1338,7 @@ public class JavacParser implements Parser {
//method reference expected here //method reference expected here
t = illegal(); t = illegal();
} }
mode = EXPR; selectExprMode();
return term3Rest(t, typeArgs); return term3Rest(t, typeArgs);
} }
break loop; break loop;
@ -1356,12 +1375,82 @@ public class JavacParser implements Parser {
//return illegal(); //return illegal();
} }
break; break;
case SWITCH:
checkSourceLevel(Feature.SWITCH_EXPRESSION);
int switchPos = token.pos;
nextToken();
JCExpression selector = parExpression();
accept(LBRACE);
ListBuffer<JCCase> cases = new ListBuffer<>();
while (true) {
pos = token.pos;
switch (token.kind) {
case CASE:
case DEFAULT:
cases.appendList(switchExpressionStatementGroup());
break;
case RBRACE: case EOF:
JCSwitchExpression e = to(F.at(switchPos).SwitchExpression(selector,
cases.toList()));
accept(RBRACE);
return e;
default:
nextToken(); // to ensure progress
syntaxError(pos, Errors.Expected3(CASE, DEFAULT, RBRACE));
}
}
default: default:
return illegal(); return illegal();
} }
return term3Rest(t, typeArgs); return term3Rest(t, typeArgs);
} }
private List<JCCase> switchExpressionStatementGroup() {
ListBuffer<JCCase> caseExprs = new ListBuffer<>();
int casePos = token.pos;
ListBuffer<JCExpression> pats = new ListBuffer<>();
if (token.kind == DEFAULT) {
nextToken();
} else {
accept(CASE);
while (true) {
pats.append(term(EXPR | NOLAMBDA));
if (token.kind != COMMA) break;
checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS);
nextToken();
};
}
List<JCStatement> stats = null;
JCTree body = null;
@SuppressWarnings("removal")
CaseKind kind;
switch (token.kind) {
case ARROW:
checkSourceLevel(Feature.SWITCH_RULE);
nextToken();
if (token.kind == TokenKind.THROW || token.kind == TokenKind.LBRACE) {
stats = List.of(parseStatement());
body = stats.head;
kind = JCCase.RULE;
} else {
JCExpression value = parseExpression();
stats = List.of(to(F.at(value).Break(value)));
body = value;
kind = JCCase.RULE;
accept(SEMI);
}
break;
default:
accept(COLON, tk -> Errors.Expected2(COLON, ARROW));
stats = blockStatements();
kind = JCCase.STATEMENT;
break;
}
caseExprs.append(toP(F.at(casePos).Case(kind, pats.toList(), stats, body)));
return caseExprs.toList();
}
JCExpression term3Rest(JCExpression t, List<JCExpression> typeArgs) { JCExpression term3Rest(JCExpression t, List<JCExpression> typeArgs) {
if (typeArgs != null) illegal(); if (typeArgs != null) illegal();
while (true) { while (true) {
@ -1372,13 +1461,13 @@ public class JavacParser implements Parser {
nextToken(); nextToken();
if ((mode & TYPE) != 0) { if ((mode & TYPE) != 0) {
int oldmode = mode; int oldmode = mode;
mode = TYPE; selectTypeMode();
if (token.kind == RBRACKET) { if (token.kind == RBRACKET) {
nextToken(); nextToken();
t = bracketsOpt(t); t = bracketsOpt(t);
t = toP(F.at(pos1).TypeArray(t)); t = toP(F.at(pos1).TypeArray(t));
if (token.kind == COLCOL) { if (token.kind == COLCOL) {
mode = EXPR; selectExprMode();
continue; continue;
} }
if (annos.nonEmpty()) { if (annos.nonEmpty()) {
@ -1389,7 +1478,7 @@ public class JavacParser implements Parser {
mode = oldmode; mode = oldmode;
} }
if ((mode & EXPR) != 0) { if ((mode & EXPR) != 0) {
mode = EXPR; selectExprMode();
JCExpression t1 = term(); JCExpression t1 = term();
t = to(F.at(pos1).Indexed(t, t1)); t = to(F.at(pos1).Indexed(t, t1));
} }
@ -1398,14 +1487,14 @@ public class JavacParser implements Parser {
nextToken(); nextToken();
typeArgs = typeArgumentsOpt(EXPR); typeArgs = typeArgumentsOpt(EXPR);
if (token.kind == SUPER && (mode & EXPR) != 0) { if (token.kind == SUPER && (mode & EXPR) != 0) {
mode = EXPR; selectExprMode();
t = to(F.at(pos1).Select(t, names._super)); t = to(F.at(pos1).Select(t, names._super));
nextToken(); nextToken();
t = arguments(typeArgs, t); t = arguments(typeArgs, t);
typeArgs = null; typeArgs = null;
} else if (token.kind == NEW && (mode & EXPR) != 0) { } else if (token.kind == NEW && (mode & EXPR) != 0) {
if (typeArgs != null) return illegal(); if (typeArgs != null) return illegal();
mode = EXPR; selectExprMode();
int pos2 = token.pos; int pos2 = token.pos;
nextToken(); nextToken();
if (token.kind == LT) typeArgs = typeArguments(false); if (token.kind == LT) typeArgs = typeArguments(false);
@ -1425,7 +1514,7 @@ public class JavacParser implements Parser {
typeArgs = null; typeArgs = null;
} }
} else if ((mode & EXPR) != 0 && token.kind == COLCOL) { } else if ((mode & EXPR) != 0 && token.kind == COLCOL) {
mode = EXPR; selectExprMode();
if (typeArgs != null) return illegal(); if (typeArgs != null) return illegal();
accept(COLCOL); accept(COLCOL);
t = memberReferenceSuffix(pos1, t); t = memberReferenceSuffix(pos1, t);
@ -1440,7 +1529,7 @@ public class JavacParser implements Parser {
} }
} }
while ((token.kind == PLUSPLUS || token.kind == SUBSUB) && (mode & EXPR) != 0) { while ((token.kind == PLUSPLUS || token.kind == SUBSUB) && (mode & EXPR) != 0) {
mode = EXPR; selectExprMode();
t = to(F.at(token.pos).Unary( t = to(F.at(token.pos).Unary(
token.kind == PLUSPLUS ? POSTINC : POSTDEC, t)); token.kind == PLUSPLUS ? POSTINC : POSTDEC, t));
nextToken(); nextToken();
@ -1580,7 +1669,8 @@ public class JavacParser implements Parser {
return ParensResult.EXPLICIT_LAMBDA; return ParensResult.EXPLICIT_LAMBDA;
} else if (peekToken(lookahead, RPAREN, ARROW)) { } else if (peekToken(lookahead, RPAREN, ARROW)) {
// Identifier, ')' '->' -> implicit lambda // Identifier, ')' '->' -> implicit lambda
return ParensResult.IMPLICIT_LAMBDA; return (mode & NOLAMBDA) == 0 ? ParensResult.IMPLICIT_LAMBDA
: ParensResult.PARENS;
} else if (depth == 0 && peekToken(lookahead, COMMA)) { } else if (depth == 0 && peekToken(lookahead, COMMA)) {
defaultResult = ParensResult.IMPLICIT_LAMBDA; defaultResult = ParensResult.IMPLICIT_LAMBDA;
} }
@ -1818,7 +1908,7 @@ public class JavacParser implements Parser {
*/ */
JCExpression argumentsOpt(List<JCExpression> typeArgs, JCExpression t) { JCExpression argumentsOpt(List<JCExpression> typeArgs, JCExpression t) {
if ((mode & EXPR) != 0 && token.kind == LPAREN || typeArgs != null) { if ((mode & EXPR) != 0 && token.kind == LPAREN || typeArgs != null) {
mode = EXPR; selectExprMode();
return arguments(typeArgs, t); return arguments(typeArgs, t);
} else { } else {
return t; return t;
@ -1857,7 +1947,7 @@ public class JavacParser implements Parser {
if (token.kind == LT && if (token.kind == LT &&
(mode & TYPE) != 0 && (mode & TYPE) != 0 &&
(mode & NOPARAMS) == 0) { (mode & NOPARAMS) == 0) {
mode = TYPE; selectTypeMode();
return typeArguments(t, false); return typeArguments(t, false);
} else { } else {
return t; return t;
@ -2019,7 +2109,7 @@ public class JavacParser implements Parser {
*/ */
JCExpression bracketsSuffix(JCExpression t) { JCExpression bracketsSuffix(JCExpression t) {
if ((mode & EXPR) != 0 && token.kind == DOT) { if ((mode & EXPR) != 0 && token.kind == DOT) {
mode = EXPR; selectExprMode();
int pos = token.pos; int pos = token.pos;
nextToken(); nextToken();
accept(CLASS); accept(CLASS);
@ -2044,7 +2134,7 @@ public class JavacParser implements Parser {
} }
} else if ((mode & TYPE) != 0) { } else if ((mode & TYPE) != 0) {
if (token.kind != COLCOL) { if (token.kind != COLCOL) {
mode = TYPE; selectTypeMode();
} }
} else if (token.kind != COLCOL) { } else if (token.kind != COLCOL) {
syntaxError(token.pos, Errors.DotClassExpected); syntaxError(token.pos, Errors.DotClassExpected);
@ -2064,7 +2154,7 @@ public class JavacParser implements Parser {
JCExpression memberReferenceSuffix(int pos1, JCExpression t) { JCExpression memberReferenceSuffix(int pos1, JCExpression t) {
checkSourceLevel(Feature.METHOD_REFERENCES); checkSourceLevel(Feature.METHOD_REFERENCES);
mode = EXPR; selectExprMode();
List<JCExpression> typeArgs = null; List<JCExpression> typeArgs = null;
if (token.kind == LT) { if (token.kind == LT) {
typeArgs = typeArguments(false); typeArgs = typeArguments(false);
@ -2103,7 +2193,7 @@ public class JavacParser implements Parser {
JCExpression t = qualident(true); JCExpression t = qualident(true);
int oldmode = mode; int oldmode = mode;
mode = TYPE; selectTypeMode();
boolean diamondFound = false; boolean diamondFound = false;
int lastTypeargsPos = -1; int lastTypeargsPos = -1;
if (token.kind == LT) { if (token.kind == LT) {
@ -2617,9 +2707,9 @@ public class JavacParser implements Parser {
} }
case BREAK: { case BREAK: {
nextToken(); nextToken();
Name label = LAX_IDENTIFIER.accepts(token.kind) ? ident() : null; JCExpression value = token.kind == SEMI ? null : parseExpression();
accept(SEMI); accept(SEMI);
JCBreak t = toP(F.at(pos).Break(label)); JCBreak t = toP(F.at(pos).Break(value));
return t; return t;
} }
case CONTINUE: { case CONTINUE: {
@ -2713,7 +2803,7 @@ public class JavacParser implements Parser {
switch (token.kind) { switch (token.kind) {
case CASE: case CASE:
case DEFAULT: case DEFAULT:
cases.append(switchBlockStatementGroup()); cases.appendList(switchBlockStatementGroup());
break; break;
case RBRACE: case EOF: case RBRACE: case EOF:
return cases.toList(); return cases.toList();
@ -2724,28 +2814,69 @@ public class JavacParser implements Parser {
} }
} }
protected JCCase switchBlockStatementGroup() { protected List<JCCase> switchBlockStatementGroup() {
int pos = token.pos; int pos = token.pos;
List<JCStatement> stats; List<JCStatement> stats;
JCCase c; JCCase c;
ListBuffer<JCCase> cases = new ListBuffer<JCCase>();
switch (token.kind) { switch (token.kind) {
case CASE: case CASE: {
nextToken(); nextToken();
JCExpression pat = parseExpression(); ListBuffer<JCExpression> pats = new ListBuffer<>();
accept(COLON); while (true) {
pats.append(term(EXPR | NOLAMBDA));
if (token.kind != COMMA) break;
nextToken();
checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS);
};
@SuppressWarnings("removal")
CaseKind caseKind;
JCTree body = null;
if (token.kind == ARROW) {
checkSourceLevel(Feature.SWITCH_RULE);
accept(ARROW);
caseKind = JCCase.RULE;
JCStatement statement = parseStatementAsBlock();
if (!statement.hasTag(EXEC) && !statement.hasTag(BLOCK) && !statement.hasTag(Tag.THROW)) {
log.error(statement.pos(), Errors.SwitchCaseUnexpectedStatement);
}
stats = List.of(statement);
body = stats.head;
} else {
accept(COLON, tk -> Errors.Expected2(COLON, ARROW));
caseKind = JCCase.STATEMENT;
stats = blockStatements(); stats = blockStatements();
c = F.at(pos).Case(pat, stats); }
c = F.at(pos).Case(caseKind, pats.toList(), stats, body);
if (stats.isEmpty()) if (stats.isEmpty())
storeEnd(c, S.prevToken().endPos); storeEnd(c, S.prevToken().endPos);
return c; return cases.append(c).toList();
case DEFAULT: }
case DEFAULT: {
nextToken(); nextToken();
accept(COLON); @SuppressWarnings("removal")
CaseKind caseKind;
JCTree body = null;
if (token.kind == ARROW) {
checkSourceLevel(Feature.SWITCH_RULE);
accept(ARROW);
caseKind = JCCase.RULE;
JCStatement statement = parseStatementAsBlock();
if (!statement.hasTag(EXEC) && !statement.hasTag(BLOCK) && !statement.hasTag(Tag.THROW)) {
log.error(statement.pos(), Errors.SwitchCaseUnexpectedStatement);
}
stats = List.of(statement);
body = stats.head;
} else {
accept(COLON, tk -> Errors.Expected2(COLON, ARROW));
caseKind = JCCase.STATEMENT;
stats = blockStatements(); stats = blockStatements();
c = F.at(pos).Case(null, stats); }
c = F.at(pos).Case(caseKind, List.nil(), stats, body);
if (stats.isEmpty()) if (stats.isEmpty())
storeEnd(c, S.prevToken().endPos); storeEnd(c, S.prevToken().endPos);
return c; return cases.append(c).toList();
}
} }
throw new AssertionError("should not reach here"); throw new AssertionError("should not reach here");
} }
@ -2946,7 +3077,7 @@ public class JavacParser implements Parser {
*/ */
JCExpression annotationFieldValue() { JCExpression annotationFieldValue() {
if (LAX_IDENTIFIER.accepts(token.kind)) { if (LAX_IDENTIFIER.accepts(token.kind)) {
mode = EXPR; selectExprMode();
JCExpression t1 = term1(); JCExpression t1 = term1();
if (t1.hasTag(IDENT) && token.kind == EQ) { if (t1.hasTag(IDENT) && token.kind == EQ) {
int pos = token.pos; int pos = token.pos;
@ -2988,7 +3119,7 @@ public class JavacParser implements Parser {
accept(RBRACE); accept(RBRACE);
return toP(F.at(pos).NewArray(null, List.nil(), buf.toList())); return toP(F.at(pos).NewArray(null, List.nil(), buf.toList()));
default: default:
mode = EXPR; selectExprMode();
return term1(); return term1();
} }
} }

View File

@ -49,6 +49,7 @@
# kind name an informative description of the kind of a declaration; see compiler.misc.kindname.* # kind name an informative description of the kind of a declaration; see compiler.misc.kindname.*
# target a target version number, such as 1.5, 1.6, 1.7, taken from a com.sun.tools.javac.jvm.Target # target a target version number, such as 1.5, 1.6, 1.7, taken from a com.sun.tools.javac.jvm.Target
# token the name of a non-terminal in source code; see compiler.misc.token.* # token the name of a non-terminal in source code; see compiler.misc.token.*
# tree tag the name of a non-terminal in source code; see compiler.misc.token.*
# type a Java type; e.g. int, X, X<T> # type a Java type; e.g. int, X, X<T>
# url a URL # url a URL
# object a Java object (unspecified) # object a Java object (unspecified)
@ -187,6 +188,33 @@ compiler.err.bad.initializer=\
compiler.err.break.outside.switch.loop=\ compiler.err.break.outside.switch.loop=\
break outside switch or loop break outside switch or loop
compiler.err.break.missing.value=\
missing break value
compiler.err.break.outside.switch.expression=\
break outside of enclosing switch expression
compiler.err.continue.outside.switch.expression=\
continue outside of enclosing switch expression
compiler.err.return.outside.switch.expression=\
return outside of enclosing switch expression
# 0: name
compiler.err.break.ambiguous.target=\
ambiguous reference to ''{0}''\n\
(''{0}'' is both a label and an expression)
# 0: tree tag
compiler.err.break.expr.not.immediate=\
value break not supported in ''{0}''
compiler.err.break.complex.value.no.switch.expression=\
unexpected value break
compiler.err.switch.expression.empty=\
switch expression does not have any case clauses
# 0: name # 0: name
compiler.err.call.must.be.first.stmt.in.ctor=\ compiler.err.call.must.be.first.stmt.in.ctor=\
call to {0} must be first statement in constructor call to {0} must be first statement in constructor
@ -799,12 +827,6 @@ compiler.err.name.reserved.for.internal.use=\
compiler.err.native.meth.cant.have.body=\ compiler.err.native.meth.cant.have.body=\
native methods cannot have a body native methods cannot have a body
# 0: type, 1: type
compiler.err.neither.conditional.subtype=\
incompatible types for ?: neither is a subtype of the other\n\
second operand: {0}\n\
third operand : {1}
# 0: message segment # 0: message segment
compiler.misc.incompatible.type.in.conditional=\ compiler.misc.incompatible.type.in.conditional=\
@ -814,6 +836,14 @@ compiler.misc.incompatible.type.in.conditional=\
compiler.misc.conditional.target.cant.be.void=\ compiler.misc.conditional.target.cant.be.void=\
target-type for conditional expression cannot be void target-type for conditional expression cannot be void
compiler.misc.switch.expression.target.cant.be.void=\
target-type for switch expression cannot be void
# 0: message segment
compiler.misc.incompatible.type.in.switch.expression=\
bad type in switch expression\n\
{0}
# 0: message segment # 0: message segment
compiler.misc.incompatible.ret.type.in.lambda=\ compiler.misc.incompatible.ret.type.in.lambda=\
bad return type in lambda expression\n\ bad return type in lambda expression\n\
@ -1289,6 +1319,9 @@ compiler.misc.cant.apply.diamond.1=\
compiler.err.unreachable.stmt=\ compiler.err.unreachable.stmt=\
unreachable statement unreachable statement
compiler.err.not.exhaustive=\
the switch expression does not cover all possible input values
compiler.err.initializer.must.be.able.to.complete.normally=\ compiler.err.initializer.must.be.able.to.complete.normally=\
initializer must be able to complete normally initializer must be able to complete normally
@ -2579,6 +2612,22 @@ compiler.misc.kindname.static.init=\
compiler.misc.kindname.instance.init=\ compiler.misc.kindname.instance.init=\
instance initializer instance initializer
# the following are names of tree kinds:
compiler.misc.tree.tag.forloop=\
for
compiler.misc.tree.tag.foreachloop=\
for
compiler.misc.tree.tag.whileloop=\
while
compiler.misc.tree.tag.doloop=\
do
compiler.misc.tree.tag.switch=\
switch
##### #####
compiler.misc.no.args=\ compiler.misc.no.args=\
@ -2768,6 +2817,15 @@ compiler.misc.feature.static.intf.method.invoke=\
compiler.misc.feature.private.intf.methods=\ compiler.misc.feature.private.intf.methods=\
private interface methods private interface methods
compiler.misc.feature.multiple.case.labels=\
multiple case labels
compiler.misc.feature.switch.rules=\
switch rules
compiler.misc.feature.switch.expressions=\
switch expressions
compiler.warn.underscore.as.identifier=\ compiler.warn.underscore.as.identifier=\
as of release 9, ''_'' is a keyword, and may not be used as an identifier as of release 9, ''_'' is a keyword, and may not be used as an identifier
@ -3257,6 +3315,14 @@ compiler.warn.leaks.not.accessible.unexported.qualified=\
compiler.err.illegal.argument.for.option=\ compiler.err.illegal.argument.for.option=\
illegal argument for {0}: {1} illegal argument for {0}: {1}
compiler.err.switch.null.not.allowed=\
null label in case is not allowed
compiler.err.switch.case.unexpected.statement=\
unexpected statement in case, expected is an expression, a block or a throw statement
compiler.err.switch.mixing.case.types=\
different case kinds used in the switch
############################################ ############################################
# messages previouly at javac.properties # messages previouly at javac.properties

View File

@ -47,10 +47,12 @@ import static com.sun.tools.javac.tree.JCTree.Tag.*;
import javax.tools.JavaFileManager.Location; import javax.tools.JavaFileManager.Location;
import com.sun.source.tree.CaseTree.CaseKind;
import com.sun.source.tree.ModuleTree.ModuleKind; import com.sun.source.tree.ModuleTree.ModuleKind;
import com.sun.tools.javac.code.Directive.ExportsDirective; import com.sun.tools.javac.code.Directive.ExportsDirective;
import com.sun.tools.javac.code.Directive.OpensDirective; import com.sun.tools.javac.code.Directive.OpensDirective;
import com.sun.tools.javac.code.Type.ModuleType; import com.sun.tools.javac.code.Type.ModuleType;
import com.sun.tools.javac.tree.JCTree.JCPolyExpression.PolyKind;
/** /**
* Root class for abstract syntax tree nodes. It provides definitions * Root class for abstract syntax tree nodes. It provides definitions
@ -149,10 +151,14 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
*/ */
SWITCH, SWITCH,
/** Case parts in switch statements, of type Case. /** Case parts in switch statements/expressions, of type Case.
*/ */
CASE, CASE,
/** Switch expression statements, of type Switch.
*/
SWITCH_EXPRESSION,
/** Synchronized statements, of type Synchonized. /** Synchronized statements, of type Synchonized.
*/ */
SYNCHRONIZED, SYNCHRONIZED,
@ -1238,21 +1244,50 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
* A "case :" of a switch. * A "case :" of a switch.
*/ */
public static class JCCase extends JCStatement implements CaseTree { public static class JCCase extends JCStatement implements CaseTree {
public JCExpression pat; //as CaseKind is deprecated for removal (as it is part of a preview feature),
//using indirection through these fields to avoid unnecessary @SuppressWarnings:
@SuppressWarnings("removal")
public static final CaseKind STATEMENT = CaseKind.STATEMENT;
@SuppressWarnings("removal")
public static final CaseKind RULE = CaseKind.RULE;
@SuppressWarnings("removal")
public final CaseKind caseKind;
public List<JCExpression> pats;
public List<JCStatement> stats; public List<JCStatement> stats;
protected JCCase(JCExpression pat, List<JCStatement> stats) { public JCTree body;
this.pat = pat; public boolean completesNormally;
protected JCCase(@SuppressWarnings("removal") CaseKind caseKind, List<JCExpression> pats,
List<JCStatement> stats, JCTree body) {
Assert.checkNonNull(pats);
Assert.check(pats.isEmpty() || pats.head != null);
this.caseKind = caseKind;
this.pats = pats;
this.stats = stats; this.stats = stats;
this.body = body;
} }
@Override @Override
public void accept(Visitor v) { v.visitCase(this); } public void accept(Visitor v) { v.visitCase(this); }
@DefinedBy(Api.COMPILER_TREE) @Override @DefinedBy(Api.COMPILER_TREE)
public Kind getKind() { return Kind.CASE; } public Kind getKind() { return Kind.CASE; }
@DefinedBy(Api.COMPILER_TREE) @Override @DefinedBy(Api.COMPILER_TREE)
public JCExpression getExpression() { return pat; } public JCExpression getExpression() { return pats.head; }
@DefinedBy(Api.COMPILER_TREE) @Override @DefinedBy(Api.COMPILER_TREE)
public List<JCStatement> getStatements() { return stats; } @SuppressWarnings("removal")
public List<JCExpression> getExpressions() { return pats; }
@Override @DefinedBy(Api.COMPILER_TREE)
@SuppressWarnings("removal")
public List<JCStatement> getStatements() {
return caseKind == CaseKind.STATEMENT ? stats : null;
}
@Override @DefinedBy(Api.COMPILER_TREE)
@SuppressWarnings("removal")
public JCTree getBody() { return body; }
@Override @DefinedBy(Api.COMPILER_TREE)
@SuppressWarnings("removal")
public CaseKind getCaseKind() {
return caseKind;
}
@Override @DefinedBy(Api.COMPILER_TREE) @Override @DefinedBy(Api.COMPILER_TREE)
public <R,D> R accept(TreeVisitor<R,D> v, D d) { public <R,D> R accept(TreeVisitor<R,D> v, D d) {
return v.visitCase(this, d); return v.visitCase(this, d);
@ -1263,6 +1298,36 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
} }
} }
/**
* A "switch ( ) { }" construction.
*/
@SuppressWarnings("removal")
public static class JCSwitchExpression extends JCPolyExpression implements SwitchExpressionTree {
public JCExpression selector;
public List<JCCase> cases;
protected JCSwitchExpression(JCExpression selector, List<JCCase> cases) {
this.selector = selector;
this.cases = cases;
}
@Override
public void accept(Visitor v) { v.visitSwitchExpression(this); }
@DefinedBy(Api.COMPILER_TREE)
public Kind getKind() { return Kind.SWITCH_EXPRESSION; }
@DefinedBy(Api.COMPILER_TREE)
public JCExpression getExpression() { return selector; }
@DefinedBy(Api.COMPILER_TREE)
public List<JCCase> getCases() { return cases; }
@Override @DefinedBy(Api.COMPILER_TREE)
public <R,D> R accept(TreeVisitor<R,D> v, D d) {
return v.visitSwitchExpression(this, d);
}
@Override
public Tag getTag() {
return SWITCH_EXPRESSION;
}
}
/** /**
* A synchronized block. * A synchronized block.
*/ */
@ -1484,19 +1549,27 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
* A break from a loop or switch. * A break from a loop or switch.
*/ */
public static class JCBreak extends JCStatement implements BreakTree { public static class JCBreak extends JCStatement implements BreakTree {
public Name label; public JCExpression value;
public JCTree target; public JCTree target;
protected JCBreak(Name label, JCTree target) { protected JCBreak(JCExpression value, JCTree target) {
this.label = label; this.value = value;
this.target = target; this.target = target;
} }
@Override @Override
public void accept(Visitor v) { v.visitBreak(this); } public void accept(Visitor v) { v.visitBreak(this); }
public boolean isValueBreak() {
return target != null && target.hasTag(SWITCH_EXPRESSION);
}
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
public Kind getKind() { return Kind.BREAK; } public Kind getKind() { return Kind.BREAK; }
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
public Name getLabel() { return label; } public Name getLabel() {
return value != null && value.getKind() == Kind.IDENTIFIER ? ((JCIdent) value).getName() : null;
}
@DefinedBy(Api.COMPILER_TREE)
@SuppressWarnings("removal")
public JCExpression getValue() { return value; }
@Override @DefinedBy(Api.COMPILER_TREE) @Override @DefinedBy(Api.COMPILER_TREE)
public <R,D> R accept(TreeVisitor<R,D> v, D d) { public <R,D> R accept(TreeVisitor<R,D> v, D d) {
return v.visitBreak(this, d); return v.visitBreak(this, d);
@ -2934,9 +3007,9 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
/** (let int x = 3; in x+2) */ /** (let int x = 3; in x+2) */
public static class LetExpr extends JCExpression { public static class LetExpr extends JCExpression {
public List<JCVariableDecl> defs; public List<JCStatement> defs;
public JCExpression expr; public JCExpression expr;
protected LetExpr(List<JCVariableDecl> defs, JCExpression expr) { protected LetExpr(List<JCStatement> defs, JCExpression expr) {
this.defs = defs; this.defs = defs;
this.expr = expr; this.expr = expr;
} }
@ -2994,7 +3067,9 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
JCEnhancedForLoop ForeachLoop(JCVariableDecl var, JCExpression expr, JCStatement body); JCEnhancedForLoop ForeachLoop(JCVariableDecl var, JCExpression expr, JCStatement body);
JCLabeledStatement Labelled(Name label, JCStatement body); JCLabeledStatement Labelled(Name label, JCStatement body);
JCSwitch Switch(JCExpression selector, List<JCCase> cases); JCSwitch Switch(JCExpression selector, List<JCCase> cases);
JCCase Case(JCExpression pat, List<JCStatement> stats); JCSwitchExpression SwitchExpression(JCExpression selector, List<JCCase> cases);
JCCase Case(@SuppressWarnings("removal") CaseKind caseKind, List<JCExpression> pat,
List<JCStatement> stats, JCTree body);
JCSynchronized Synchronized(JCExpression lock, JCBlock body); JCSynchronized Synchronized(JCExpression lock, JCBlock body);
JCTry Try(JCBlock body, List<JCCatch> catchers, JCBlock finalizer); JCTry Try(JCBlock body, List<JCCatch> catchers, JCBlock finalizer);
JCTry Try(List<JCTree> resources, JCTry Try(List<JCTree> resources,
@ -3007,7 +3082,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
JCExpression elsepart); JCExpression elsepart);
JCIf If(JCExpression cond, JCStatement thenpart, JCStatement elsepart); JCIf If(JCExpression cond, JCStatement thenpart, JCStatement elsepart);
JCExpressionStatement Exec(JCExpression expr); JCExpressionStatement Exec(JCExpression expr);
JCBreak Break(Name label); JCBreak Break(JCExpression value);
JCContinue Continue(Name label); JCContinue Continue(Name label);
JCReturn Return(JCExpression expr); JCReturn Return(JCExpression expr);
JCThrow Throw(JCExpression expr); JCThrow Throw(JCExpression expr);
@ -3049,7 +3124,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
JCProvides Provides(JCExpression serviceName, List<JCExpression> implNames); JCProvides Provides(JCExpression serviceName, List<JCExpression> implNames);
JCRequires Requires(boolean isTransitive, boolean isStaticPhase, JCExpression qualId); JCRequires Requires(boolean isTransitive, boolean isStaticPhase, JCExpression qualId);
JCUses Uses(JCExpression qualId); JCUses Uses(JCExpression qualId);
LetExpr LetExpr(List<JCVariableDecl> defs, JCExpression expr); LetExpr LetExpr(List<JCStatement> defs, JCExpression expr);
} }
/** A generic visitor class for trees. /** A generic visitor class for trees.
@ -3070,6 +3145,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public void visitLabelled(JCLabeledStatement that) { visitTree(that); } public void visitLabelled(JCLabeledStatement that) { visitTree(that); }
public void visitSwitch(JCSwitch that) { visitTree(that); } public void visitSwitch(JCSwitch that) { visitTree(that); }
public void visitCase(JCCase that) { visitTree(that); } public void visitCase(JCCase that) { visitTree(that); }
public void visitSwitchExpression(JCSwitchExpression that) { visitTree(that); }
public void visitSynchronized(JCSynchronized that) { visitTree(that); } public void visitSynchronized(JCSynchronized that) { visitTree(that); }
public void visitTry(JCTry that) { visitTree(that); } public void visitTry(JCTry that) { visitTree(that); }
public void visitCatch(JCCatch that) { visitTree(that); } public void visitCatch(JCCatch that) { visitTree(that); }

View File

@ -27,6 +27,7 @@ package com.sun.tools.javac.tree;
import java.io.*; import java.io.*;
import com.sun.source.tree.CaseTree.CaseKind;
import com.sun.source.tree.MemberReferenceTree.ReferenceMode; import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.source.tree.ModuleTree.ModuleKind; import com.sun.source.tree.ModuleTree.ModuleKind;
import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.*;
@ -835,18 +836,43 @@ public class Pretty extends JCTree.Visitor {
public void visitCase(JCCase tree) { public void visitCase(JCCase tree) {
try { try {
if (tree.pat == null) { if (tree.pats.isEmpty()) {
print("default"); print("default");
} else { } else {
print("case "); print("case ");
printExpr(tree.pat); printExprs(tree.pats);
} }
if (tree.caseKind == JCCase.STATEMENT) {
print(":"); print(":");
println(); println();
indent(); indent();
printStats(tree.stats); printStats(tree.stats);
undent(); undent();
align(); align();
} else {
print(" -> ");
printStat(tree.stats.head);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void visitSwitchExpression(JCSwitchExpression tree) {
try {
print("switch ");
if (tree.selector.hasTag(PARENS)) {
printExpr(tree.selector);
} else {
print("(");
printExpr(tree.selector);
print(")");
}
print(" {");
println();
printStats(tree.cases);
align();
print("}");
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} }
@ -956,7 +982,7 @@ public class Pretty extends JCTree.Visitor {
public void visitBreak(JCBreak tree) { public void visitBreak(JCBreak tree) {
try { try {
print("break"); print("break");
if (tree.label != null) print(" " + tree.label); if (tree.value != null) print(" " + tree.value);
print(";"); print(";");
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);

View File

@ -140,15 +140,17 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
public JCTree visitBreak(BreakTree node, P p) { public JCTree visitBreak(BreakTree node, P p) {
JCBreak t = (JCBreak) node; 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) @DefinedBy(Api.COMPILER_TREE)
public JCTree visitCase(CaseTree node, P p) { public JCTree visitCase(CaseTree node, P p) {
JCCase t = (JCCase) node; JCCase t = (JCCase) node;
JCExpression pat = copy(t.pat, p); List<JCExpression> pats = copy(t.pats, p);
List<JCStatement> stats = copy(t.stats, p); List<JCStatement> stats = copy(t.stats, p);
return M.at(t.pos).Case(pat, stats); JCTree body = copy(t.body, p);
return M.at(t.pos).Case(t.caseKind, pats, stats, body);
} }
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
@ -370,6 +372,15 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
return M.at(t.pos).Switch(selector, cases); return M.at(t.pos).Switch(selector, cases);
} }
@DefinedBy(Api.COMPILER_TREE)
@SuppressWarnings("removal")
public JCTree visitSwitchExpression(SwitchExpressionTree node, P p) {
JCSwitchExpression t = (JCSwitchExpression) node;
JCExpression selector = copy(t.selector, p);
List<JCCase> cases = copy(t.cases, p);
return M.at(t.pos).SwitchExpression(selector, cases);
}
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
public JCTree visitSynchronized(SynchronizedTree node, P p) { public JCTree visitSynchronized(SynchronizedTree node, P p) {
JCSynchronized t = (JCSynchronized) node; JCSynchronized t = (JCSynchronized) node;
@ -559,7 +570,7 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
switch (tree.getTag()) { switch (tree.getTag()) {
case LETEXPR: { case LETEXPR: {
LetExpr t = (LetExpr) node; LetExpr t = (LetExpr) node;
List<JCVariableDecl> defs = copy(t.defs, p); List<JCStatement> defs = copy(t.defs, p);
JCExpression expr = copy(t.expr, p); JCExpression expr = copy(t.expr, p);
return M.at(t.pos).LetExpr(defs, expr); return M.at(t.pos).LetExpr(defs, expr);
} }

View File

@ -27,6 +27,7 @@ package com.sun.tools.javac.tree;
import java.util.Iterator; import java.util.Iterator;
import com.sun.source.tree.CaseTree.CaseKind;
import com.sun.source.tree.ModuleTree.ModuleKind; import com.sun.source.tree.ModuleTree.ModuleKind;
import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.*;
@ -273,8 +274,15 @@ public class TreeMaker implements JCTree.Factory {
return tree; return tree;
} }
public JCCase Case(JCExpression pat, List<JCStatement> stats) { public JCCase Case(@SuppressWarnings("removal") CaseKind caseKind, List<JCExpression> pats,
JCCase tree = new JCCase(pat, stats); List<JCStatement> stats, JCTree body) {
JCCase tree = new JCCase(caseKind, pats, stats, body);
tree.pos = pos;
return tree;
}
public JCSwitchExpression SwitchExpression(JCExpression selector, List<JCCase> cases) {
JCSwitchExpression tree = new JCSwitchExpression(selector, cases);
tree.pos = pos; tree.pos = pos;
return tree; return tree;
} }
@ -325,7 +333,7 @@ public class TreeMaker implements JCTree.Factory {
return tree; return tree;
} }
public JCBreak Break(Name label) { public JCBreak Break(JCExpression label) {
JCBreak tree = new JCBreak(label, null); JCBreak tree = new JCBreak(label, null);
tree.pos = pos; tree.pos = pos;
return tree; return tree;
@ -599,7 +607,7 @@ public class TreeMaker implements JCTree.Factory {
return tree; return tree;
} }
public LetExpr LetExpr(List<JCVariableDecl> defs, JCExpression expr) { public LetExpr LetExpr(List<JCStatement> defs, JCExpression expr) {
LetExpr tree = new LetExpr(defs, expr); LetExpr tree = new LetExpr(defs, expr);
tree.pos = pos; tree.pos = pos;
return tree; return tree;

View File

@ -176,10 +176,15 @@ public class TreeScanner extends Visitor {
} }
public void visitCase(JCCase tree) { public void visitCase(JCCase tree) {
scan(tree.pat); scan(tree.pats);
scan(tree.stats); scan(tree.stats);
} }
public void visitSwitchExpression(JCSwitchExpression tree) {
scan(tree.selector);
scan(tree.cases);
}
public void visitSynchronized(JCSynchronized tree) { public void visitSynchronized(JCSynchronized tree) {
scan(tree.lock); scan(tree.lock);
scan(tree.body); scan(tree.body);
@ -214,6 +219,7 @@ public class TreeScanner extends Visitor {
} }
public void visitBreak(JCBreak tree) { public void visitBreak(JCBreak tree) {
scan(tree.value);
} }
public void visitContinue(JCContinue tree) { public void visitContinue(JCContinue tree) {

View File

@ -207,11 +207,17 @@ public class TreeTranslator extends JCTree.Visitor {
} }
public void visitCase(JCCase tree) { public void visitCase(JCCase tree) {
tree.pat = translate(tree.pat); tree.pats = translate(tree.pats);
tree.stats = translate(tree.stats); tree.stats = translate(tree.stats);
result = tree; result = tree;
} }
public void visitSwitchExpression(JCSwitchExpression tree) {
tree.selector = translate(tree.selector);
tree.cases = translateCases(tree.cases);
result = tree;
}
public void visitSynchronized(JCSynchronized tree) { public void visitSynchronized(JCSynchronized tree) {
tree.lock = translate(tree.lock); tree.lock = translate(tree.lock);
tree.body = translate(tree.body); tree.body = translate(tree.body);
@ -252,6 +258,8 @@ public class TreeTranslator extends JCTree.Visitor {
} }
public void visitBreak(JCBreak tree) { public void visitBreak(JCBreak tree) {
if (tree.isValueBreak())
tree.value = translate(tree.value);
result = tree; result = tree;
} }
@ -419,7 +427,7 @@ public class TreeTranslator extends JCTree.Visitor {
} }
public void visitLetExpr(LetExpr tree) { public void visitLetExpr(LetExpr tree) {
tree.defs = translateVarDefs(tree.defs); tree.defs = translate(tree.defs);
tree.expr = translate(tree.expr); tree.expr = translate(tree.expr);
result = tree; result = tree;
} }

View File

@ -219,6 +219,10 @@ public abstract class AbstractDiagnosticFormatter implements DiagnosticFormatter
else if (arg instanceof Source) { else if (arg instanceof Source) {
return ((Source)arg).name; return ((Source)arg).name;
} }
else if (arg instanceof Tag) {
return messages.getLocalizedString(l, "compiler.misc.tree.tag." +
StringUtils.toLowerCase(((Tag) arg).name()));
}
else { else {
return String.valueOf(arg); return String.valueOf(arg);
} }

View File

@ -158,6 +158,8 @@ public final class RawDiagnosticFormatter extends AbstractDiagnosticFormatter {
s = "@" + rawDiagnosticPosHelper.getPosition((JCExpression)arg); s = "@" + rawDiagnosticPosHelper.getPosition((JCExpression)arg);
} else if (arg instanceof PathFileObject) { } else if (arg instanceof PathFileObject) {
s = ((PathFileObject) arg).getShortName(); s = ((PathFileObject) arg).getShortName();
} else if (arg instanceof Tag) {
s = "compiler.misc.tree.tag." + StringUtils.toLowerCase(((Tag) arg).name());
} else { } else {
s = super.formatArgument(diag, arg, null); s = super.formatArgument(diag, arg, null);
} }

View File

@ -247,7 +247,7 @@ class CompletenessAnalyzer {
FOR(TokenKind.FOR, XSTMT1|XSTART), // for FOR(TokenKind.FOR, XSTMT1|XSTART), // for
IF(TokenKind.IF, XSTMT1|XSTART), // if IF(TokenKind.IF, XSTMT1|XSTART), // if
RETURN(TokenKind.RETURN, XSTMT1|XTERM|XSTART), // return RETURN(TokenKind.RETURN, XSTMT1|XTERM|XSTART), // return
SWITCH(TokenKind.SWITCH, XSTMT1|XSTART), // switch SWITCH(TokenKind.SWITCH, XSTMT1|XEXPR), // switch
SYNCHRONIZED(TokenKind.SYNCHRONIZED, XSTMT1|XDECL), // synchronized SYNCHRONIZED(TokenKind.SYNCHRONIZED, XSTMT1|XDECL), // synchronized
THROW(TokenKind.THROW, XSTMT1|XSTART), // throw THROW(TokenKind.THROW, XSTMT1|XSTART), // throw
TRY(TokenKind.TRY, XSTMT1|XSTART), // try TRY(TokenKind.TRY, XSTMT1|XSTART), // try

View File

@ -185,7 +185,12 @@ public class CompletenessTest extends KullaTesting {
"int n,", "int n,",
"int[] m = {1, 2},", "int[] m = {1, 2},",
"int[] m = {1, 2}, n = {3, 4},", "int[] m = {1, 2}, n = {3, 4},",
"Map<String," "Map<String,",
"switch (x) {",
"var v = switch (x) {",
"var v = switch (x) { case ",
"var v = switch (x) { case 0:",
"var v = switch (x) { case 0: break 12; ",
}; };
static final String[] unknown = new String[] { static final String[] unknown = new String[] {

View File

@ -65,6 +65,13 @@ public class Diagnostics implements javax.tools.DiagnosticListener<JavaFileObjec
.anyMatch(d -> d.getCode().equals(key)); .anyMatch(d -> d.getCode().equals(key));
} }
/** Do the diagnostics contain the specified warning key? */
public boolean containsWarningKey(String key) {
return diags.stream()
.filter(d -> d.getKind() == Diagnostic.Kind.WARNING)
.anyMatch(d -> d.getCode().equals(key));
}
/** Get the error keys */ /** Get the error keys */
public List<String> errorKeys() { public List<String> errorKeys() {
return diags.stream() return diags.stream()

View File

@ -178,6 +178,14 @@ public abstract class JavacTemplateTestBase {
fail("Expected successful compilation"); fail("Expected successful compilation");
} }
/** Assert that all previous calls to compile() succeeded */
protected void assertCompileSucceededWithWarning(String warning) {
if (diags.errorsFound())
fail("Expected successful compilation");
if (!diags.containsWarningKey(warning))
fail("Expected compilation warning " + warning);
}
/** /**
* If the provided boolean is true, assert all previous compiles succeeded, * If the provided boolean is true, assert all previous compiles succeeded,
* otherwise assert that a compile failed. * otherwise assert that a compile failed.
@ -196,9 +204,22 @@ public abstract class JavacTemplateTestBase {
} }
/** Assert that a previous call to compile() failed with a specific error key */ /** Assert that a previous call to compile() failed with a specific error key */
protected void assertCompileFailed(String message) { protected void assertCompileFailed(String key) {
if (!diags.errorsFound()) if (!diags.errorsFound())
fail("Expected failed compilation: " + message); fail("Expected failed compilation: " + key);
if (!diags.containsErrorKey(key))
fail("Expected compilation error " + key);
}
/** Assert that a previous call to compile() failed with a specific error key */
protected void assertCompileFailedOneOf(String... keys) {
if (!diags.errorsFound())
fail("Expected failed compilation with one of: " + Arrays.asList(keys));
boolean found = false;
for (String k : keys)
if (diags.containsErrorKey(k))
found = true;
fail(String.format("Expected compilation error with one of %s, found %s", Arrays.asList(keys), diags.keys()));
} }
/** Assert that a previous call to compile() failed with all of the specified error keys */ /** Assert that a previous call to compile() failed with all of the specified error keys */

View File

@ -4,13 +4,17 @@
* @summary The compiler was allowing void types in its parsing of conditional expressions. * @summary The compiler was allowing void types in its parsing of conditional expressions.
* @author tball * @author tball
* *
* @compile/fail/ref=ConditionalWithVoid.out -XDrawDiagnostics ConditionalWithVoid.java * @compile/fail/ref=ConditionalWithVoid.out --enable-preview -source 12 -XDrawDiagnostics ConditionalWithVoid.java
*/ */
public class ConditionalWithVoid { public class ConditionalWithVoid {
public void test(Object o) { public void test(Object o, String s) {
// Should fail to compile since Object.wait() has a void return type. Poly case. // Should fail to compile since Object.wait() has a void return type. Poly case.
System.out.println(o instanceof String ? o.hashCode() : o.wait()); System.out.println(o instanceof String ? o.hashCode() : o.wait());
// Should fail to compile since Object.wait() has a void return type. Standalone case. // Should fail to compile since Object.wait() has a void return type. Standalone case.
(o instanceof String ? o.hashCode() : o.wait()).toString(); (o instanceof String ? o.hashCode() : o.wait()).toString();
// Should fail to compile since Object.wait() has a void return type. Poly case.
System.out.println(switch (s) {case "" -> o.hashCode(); default -> o.wait();});
// Should fail to compile since Object.wait() has a void return type. Standalone case.
(switch (s) {case "" -> o.hashCode(); default -> o.wait();}).toString();
} }
} }

View File

@ -1,3 +1,7 @@
ConditionalWithVoid.java:12:71: compiler.err.void.not.allowed.here ConditionalWithVoid.java:12:71: compiler.err.void.not.allowed.here
ConditionalWithVoid.java:14:30: compiler.err.neither.conditional.subtype: java.lang.Integer, void ConditionalWithVoid.java:14:53: compiler.err.void.not.allowed.here
2 errors ConditionalWithVoid.java:16:82: compiler.err.void.not.allowed.here
ConditionalWithVoid.java:18:64: compiler.err.void.not.allowed.here
- compiler.note.preview.filename: ConditionalWithVoid.java
- compiler.note.preview.recompile
4 errors

View File

@ -61,6 +61,7 @@ import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers; 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.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.LetExpr; import com.sun.tools.javac.tree.JCTree.LetExpr;
import com.sun.tools.javac.tree.JCTree.Tag; import com.sun.tools.javac.tree.JCTree.Tag;
@ -327,8 +328,8 @@ public class BoxingAndSuper {
if (tree.hasTag(Tag.LETEXPR)) { if (tree.hasTag(Tag.LETEXPR)) {
LetExpr le = (LetExpr) tree; LetExpr le = (LetExpr) tree;
for (JCVariableDecl var : le.defs) { for (JCStatement var : le.defs) {
letExprRemap.put(var.name.toString(), "$le" + i++); letExprRemap.put(((JCVariableDecl) var).name.toString(), "$le" + i++);
} }
} }
return super.visitOther(node, p); return super.visitOther(node, p);

View File

@ -302,6 +302,7 @@ public class CheckResourceKeys {
// prefix/embedded strings // prefix/embedded strings
"compiler.", "compiler.",
"compiler.misc.", "compiler.misc.",
"compiler.misc.tree.tag.",
"opt.Xlint.desc.", "opt.Xlint.desc.",
"count.", "count.",
"illegal.", "illegal.",

View File

@ -0,0 +1,36 @@
/*
* 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.
*/
// key: compiler.err.break.ambiguous.target
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source 12
class BreakAmbiguousTarget {
void m(int i, int j) {
j: print(switch (i) {
default: break j;
});
}
void print(int i) { }
}

View File

@ -0,0 +1,32 @@
/*
* 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.
*/
// key: compiler.err.break.complex.value.no.switch.expression
class BreakComplexValueNoSwitchExpressions {
void t() {
while (true) {
break 1 + 1;
}
}
}

View File

@ -0,0 +1,65 @@
/*
* 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.
*/
// key: compiler.err.break.expr.not.immediate
// key: compiler.misc.tree.tag.doloop
// key: compiler.misc.tree.tag.foreachloop
// key: compiler.misc.tree.tag.forloop
// key: compiler.misc.tree.tag.switch
// key: compiler.misc.tree.tag.whileloop
// key: compiler.note.note
// key: compiler.err.error
// key: compiler.misc.count.error.plural
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// key: compiler.note.note
// options: --enable-preview -source 12
// run: backdoor
class BreakExprNotImmediate {
int t(int i) {
return switch (i) {
case 0:
for (; ;) {
break 1 + 1;
}
case 1:
for (String s : new String[0]) {
break 1 + 1;
}
case 2:
while (true) {
break 1 + 1;
}
case 3:
do {
break 1 + 1;
} while (true);
case 4:
switch (i) {
default: break 1 + 1;
}
break 0;
};
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.
*/
// key: compiler.err.break.missing.value
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source 12
class BreakMissingValue {
int t(int i) {
return switch (i) {
default: break;
};
}
}

View File

@ -0,0 +1,38 @@
/*
* 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.
*/
// key: compiler.err.break.outside.switch.expression
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source 12
class BreakOutsideSwitchExpression {
int t(int i) {
OUT: while (true) {
return switch (i) {
default: break OUT;
};
}
return -1;
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.
*/
// key: compiler.err.continue.outside.switch.expression
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source 12
class ContinueOutsideSwitchExpression {
int t(int i) {
OUT: while (true) {
return switch (i) {
default: continue OUT;
};
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2012, 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.
*/
// key: compiler.err.prob.found.req
// key: compiler.misc.incompatible.type.in.switch.expression
// key: compiler.misc.inconvertible.types
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source 12
class IncompatibleTypesInSwitchExpression {
interface A { }
interface B { }
B b = switch (0) { case 0 -> (A)null; default -> (B)null; };
}

View File

@ -0,0 +1,34 @@
/*
* 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.
*/
// key: compiler.misc.feature.multiple.case.labels
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source 12 -Xlint:preview
class MultipleCaseLabels {
void m(int i) {
switch (i) {
case 0, 1, 2: break;
}
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.
*/
// key: compiler.err.not.exhaustive
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source 12
class NotExhaustive {
int t(int i) {
return switch (i) {
case 0 -> -1;
};
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.
*/
// key: compiler.err.return.outside.switch.expression
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source 12
class ReturnOutsideSwitchExpression {
int t(int i) {
return switch (i) {
default: return -1;
};
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.
*/
// key: compiler.err.switch.case.unexpected.statement
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source 12
class ReturnOutsideSwitchExpression {
void t(int i) {
switch (i) {
case 0 -> if (true);
};
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.
*/
// key: compiler.err.switch.expression.empty
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source 12
class BreakOutsideSwitchExpression {
String t(E e) {
return switch (e) {
};
}
enum E {}
}

View File

@ -0,0 +1,40 @@
/*
* 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.
*/
// key: compiler.err.prob.found.req
// key: compiler.misc.incompatible.ret.type.in.lambda
// key: compiler.misc.switch.expression.target.cant.be.void
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source 12
class SwitchExpressionTargetCantBeVoid {
interface SAM {
void m();
}
void test(int cond, Object o1, Object o2, Object o3) {
SAM s = ()-> switch (cond) { case 0 -> o1; case 1 -> o2; default -> o3; };
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.
*/
// key: compiler.misc.feature.switch.expressions
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source 12 -Xlint:preview
class SwitchExpressions {
int m(int i) {
return switch (i) {
default: break -1;
};
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.
*/
// key: compiler.err.switch.mixing.case.types
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source 12
class SwitchMixingCaseTypes {
void test(int i) {
switch (i) {
case 0: break;
case 1 -> System.out.println();
}
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -21,14 +21,14 @@
* questions. * questions.
*/ */
// key: compiler.err.neither.conditional.subtype // key: compiler.err.switch.null.not.allowed
class NeitherConditionalSubtype { class SwitchNullNotAllowed {
public int test(boolean cond, Object o) {
// Should fail to compile since Object.wait() has a void return type. void test(Integer i) {
(o instanceof String ? o.hashCode() : o.wait()).toString(); switch (i) {
return 0; case null: break;
case 0: break;
}
} }
} }

View File

@ -0,0 +1,34 @@
/*
* 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.
*/
// key: compiler.misc.feature.switch.rules
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source 12 -Xlint:preview
class SwitchExpressions {
void m(int i) {
switch (i) {
default -> { break; }
};
}
}

View File

@ -0,0 +1,250 @@
/*
* 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.
*/
import java.io.IOException;
import java.util.List;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
import tools.javac.combo.JavacTemplateTestBase;
import static java.util.stream.Collectors.toList;
@Test
public class ExpSwitchNestingTest extends JavacTemplateTestBase {
private static final String RUNNABLE = "Runnable r = () -> { # };";
private static final String INT_FN = "java.util.function.IntSupplier r = () -> { # };";
private static final String LABEL = "label: #";
private static final String DEF_LABEL_VAR = "int label = 0; { # }";
private static final String FOR = "for (int i=0; i<10; i++) { # }";
private static final String FOR_EACH = "for (int i : new int[] {}) { # }";
private static final String WHILE = "while (cond) { # }";
private static final String DO = "do { # } while (cond);";
private static final String SSWITCH = "switch (x) { case 0: # };";
private static final String ESWITCH_Z = "int res = switch (x) { case 0 -> { # } default -> 0; };";
private static final String ESWITCH_S = "String res_string = switch (x) { case 0 -> { # } default -> \"default\"; };";
private static final String INT_FN_ESWITCH = "java.util.function.IntSupplier r = switch (x) { case 0 -> { # } default -> null; };";
private static final String INT_ESWITCH_DEFAULT = "int res = switch (x) { default -> { # } };";
private static final String IF = "if (cond) { # }";
private static final String BLOCK = "{ # }";
private static final String BREAK_Z = "break 0;";
private static final String BREAK_S = "break \"hello world\";";
private static final String BREAK_INT_FN = "break () -> 0 ;";
private static final String BREAK_N = "break;";
private static final String BREAK_L = "break label;";
private static final String RETURN_Z = "return 0;";
private static final String RETURN_N = "return;";
private static final String RETURN_S = "return \"Hello\";";
private static final String CONTINUE_N = "continue;";
private static final String CONTINUE_L = "continue label;";
private static final String NOTHING = "System.out.println();";
// containers that do not require exhaustiveness
private static final List<String> CONTAINERS
= List.of(RUNNABLE, FOR, WHILE, DO, SSWITCH, IF, BLOCK);
// containers that do not require exhaustiveness that are statements
private static final List<String> CONTAINER_STATEMENTS
= List.of(FOR, WHILE, DO, SSWITCH, IF, BLOCK);
@AfterMethod
public void dumpTemplateIfError(ITestResult result) {
// Make sure offending template ends up in log file on failure
if (!result.isSuccess()) {
System.err.printf("Diagnostics: %s%nTemplate: %s%n", diags.errorKeys(), sourceFiles.stream().map(p -> p.snd).collect(toList()));
}
}
private void program(String... constructs) {
String s = "class C { static boolean cond = false; static int x = 0; void m() { # } }";
for (String c : constructs)
s = s.replace("#", c);
addSourceFile("C.java", new StringTemplate(s));
}
private void assertOK(String... constructs) {
reset();
addCompileOptions("--enable-preview", "-source", "12");
program(constructs);
try {
compile();
}
catch (IOException e) {
throw new RuntimeException(e);
}
assertCompileSucceeded();
}
private void assertOKWithWarning(String warning, String... constructs) {
reset();
addCompileOptions("--enable-preview", "-source", "12");
program(constructs);
try {
compile();
}
catch (IOException e) {
throw new RuntimeException(e);
}
assertCompileSucceededWithWarning(warning);
}
private void assertFail(String expectedDiag, String... constructs) {
reset();
addCompileOptions("--enable-preview", "-source", "12");
program(constructs);
try {
compile();
}
catch (IOException e) {
throw new RuntimeException(e);
}
assertCompileFailed(expectedDiag);
}
public void testReallySimpleCases() {
for (String s : CONTAINERS)
assertOK(s, NOTHING);
for (String s : CONTAINER_STATEMENTS)
assertOK(LABEL, s, NOTHING);
}
public void testLambda() {
assertOK(RUNNABLE, RETURN_N);
assertOK(RUNNABLE, NOTHING);
assertOK(INT_FN, RETURN_Z);
assertFail("compiler.err.break.outside.switch.loop", RUNNABLE, BREAK_N);
assertFail("compiler.err.break.complex.value.no.switch.expression", RUNNABLE, BREAK_Z);
assertFail("compiler.err.break.complex.value.no.switch.expression", RUNNABLE, BREAK_S);
assertFail("compiler.err.break.outside.switch.loop", INT_FN, BREAK_N);
assertFail("compiler.err.break.complex.value.no.switch.expression", INT_FN, BREAK_Z);
assertFail("compiler.err.break.complex.value.no.switch.expression", INT_FN, BREAK_S);
assertFail("compiler.err.cont.outside.loop", RUNNABLE, CONTINUE_N);
assertFail("compiler.err.undef.label", RUNNABLE, BREAK_L);
assertFail("compiler.err.undef.label", RUNNABLE, CONTINUE_L);
assertFail("compiler.err.cont.outside.loop", INT_FN, CONTINUE_N);
assertFail("compiler.err.undef.label", INT_FN, BREAK_L);
assertFail("compiler.err.undef.label", INT_FN, CONTINUE_L);
assertFail("compiler.err.undef.label", LABEL, BLOCK, RUNNABLE, BREAK_L);
assertFail("compiler.err.undef.label", LABEL, BLOCK, RUNNABLE, CONTINUE_L);
assertFail("compiler.err.undef.label", LABEL, BLOCK, INT_FN, BREAK_L);
assertFail("compiler.err.undef.label", LABEL, BLOCK, INT_FN, CONTINUE_L);
}
public void testEswitch() {
//Int-valued switch expressions
assertOK(ESWITCH_Z, BREAK_Z);
assertOK(LABEL, BLOCK, ESWITCH_Z, BREAK_Z);
assertFail("compiler.err.break.missing.value", ESWITCH_Z, BREAK_N);
assertFail("compiler.err.prob.found.req", ESWITCH_Z, BREAK_S);
assertFail("compiler.err.cant.resolve.location", ESWITCH_Z, BREAK_L);
assertFail("compiler.err.break.outside.switch.expression", LABEL, BLOCK, ESWITCH_Z, BREAK_L);
assertFail("compiler.err.undef.label", ESWITCH_Z, CONTINUE_L);
assertFail("compiler.err.cont.outside.loop", ESWITCH_Z, CONTINUE_N);
assertFail("compiler.err.return.outside.switch.expression", ESWITCH_Z, RETURN_N);
assertFail("compiler.err.return.outside.switch.expression", ESWITCH_Z, RETURN_Z);
assertOK(INT_ESWITCH_DEFAULT, BREAK_Z);
assertFail("compiler.err.break.missing.value", INT_ESWITCH_DEFAULT, BREAK_N);
assertFail("compiler.err.prob.found.req", INT_ESWITCH_DEFAULT, BREAK_S);
assertFail("compiler.err.cant.resolve.location", INT_ESWITCH_DEFAULT, BREAK_L);
// String-valued switch expressions
assertOK(ESWITCH_S, BREAK_S);
assertOK(LABEL, BLOCK, ESWITCH_S, BREAK_S);
assertFail("compiler.err.break.missing.value", ESWITCH_S, BREAK_N);
assertFail("compiler.err.prob.found.req", ESWITCH_S, BREAK_Z);
assertFail("compiler.err.cant.resolve.location", ESWITCH_S, BREAK_L);
assertFail("compiler.err.break.outside.switch.expression", LABEL, BLOCK, ESWITCH_S, BREAK_L);
assertFail("compiler.err.undef.label", ESWITCH_S, CONTINUE_L);
assertFail("compiler.err.cont.outside.loop", ESWITCH_S, CONTINUE_N);
assertFail("compiler.err.return.outside.switch.expression", ESWITCH_S, RETURN_N);
assertFail("compiler.err.return.outside.switch.expression", ESWITCH_S, RETURN_S);
// Function-valued switch expression
assertOK(INT_FN_ESWITCH, BREAK_INT_FN);
assertFail("compiler.err.break.missing.value", INT_FN_ESWITCH, BREAK_N);
assertFail("compiler.err.prob.found.req", INT_FN_ESWITCH, BREAK_Z);
assertFail("compiler.err.prob.found.req", INT_FN_ESWITCH, BREAK_S);
assertFail("compiler.err.cant.resolve.location", INT_FN_ESWITCH, BREAK_L);
assertFail("compiler.err.break.outside.switch.expression", LABEL, BLOCK, INT_FN_ESWITCH, BREAK_L);
assertFail("compiler.err.undef.label", INT_FN_ESWITCH, CONTINUE_L);
assertFail("compiler.err.cont.outside.loop", INT_FN_ESWITCH, CONTINUE_N);
assertFail("compiler.err.return.outside.switch.expression", INT_FN_ESWITCH, RETURN_N);
assertFail("compiler.err.return.outside.switch.expression", INT_FN_ESWITCH, RETURN_S);
}
public void testNestedInExpSwitch() {
assertOK(ESWITCH_Z, IF, BREAK_Z);
assertOK(ESWITCH_Z, BLOCK, BREAK_Z);
//
assertOK(ESWITCH_Z, IF, IF, BREAK_Z);
assertOK(ESWITCH_Z, IF, BLOCK, BREAK_Z);
assertOK(ESWITCH_Z, BLOCK, IF, BREAK_Z);
assertOK(ESWITCH_Z, BLOCK, BLOCK, BREAK_Z);
//
assertOK(ESWITCH_Z, IF, IF, IF, BREAK_Z);
assertOK(ESWITCH_Z, IF, IF, BLOCK, BREAK_Z);
assertOK(ESWITCH_Z, IF, BLOCK, IF, BREAK_Z);
assertOK(ESWITCH_Z, IF, BLOCK, BLOCK, BREAK_Z);
assertOK(ESWITCH_Z, BLOCK, IF, IF, BREAK_Z);
assertOK(ESWITCH_Z, BLOCK, IF, BLOCK, BREAK_Z);
assertOK(ESWITCH_Z, BLOCK, BLOCK, IF, BREAK_Z);
assertOK(ESWITCH_Z, BLOCK, BLOCK, BLOCK, BREAK_Z);
//
assertFail("compiler.err.break.expr.not.immediate", ESWITCH_Z, SSWITCH, BREAK_Z);
assertFail("compiler.err.break.expr.not.immediate", ESWITCH_Z, FOR, BREAK_Z);
assertFail("compiler.err.break.expr.not.immediate", ESWITCH_Z, WHILE, BREAK_Z);
assertFail("compiler.err.break.expr.not.immediate", ESWITCH_Z, DO, BREAK_Z);
assertFail("compiler.err.break.complex.value.no.switch.expression", ESWITCH_Z, INT_FN, BREAK_Z);
assertFail("compiler.err.break.expr.not.immediate", ESWITCH_Z, SSWITCH, IF, BREAK_Z);
assertFail("compiler.err.break.expr.not.immediate", ESWITCH_Z, FOR, IF, BREAK_Z);
assertFail("compiler.err.break.expr.not.immediate", ESWITCH_Z, WHILE, IF, BREAK_Z);
assertFail("compiler.err.break.expr.not.immediate", ESWITCH_Z, DO, IF, BREAK_Z);
assertFail("compiler.err.break.expr.not.immediate", ESWITCH_Z, BLOCK, SSWITCH, IF, BREAK_Z);
assertFail("compiler.err.break.expr.not.immediate", ESWITCH_Z, BLOCK, FOR, IF, BREAK_Z);
assertFail("compiler.err.break.expr.not.immediate", ESWITCH_Z, BLOCK, WHILE, IF, BREAK_Z);
assertFail("compiler.err.break.expr.not.immediate", ESWITCH_Z, BLOCK, DO, IF, BREAK_Z);
}
public void testBreakExpressionLabelDisambiguation() {
assertOK(DEF_LABEL_VAR, ESWITCH_Z, BREAK_L);
assertFail("compiler.err.break.ambiguous.target", LABEL, FOR, BLOCK, DEF_LABEL_VAR, ESWITCH_Z, BREAK_L);
assertFail("compiler.err.break.ambiguous.target", DEF_LABEL_VAR, ESWITCH_Z, LABEL, FOR, BREAK_L); //label break
assertFail("compiler.err.break.ambiguous.target", DEF_LABEL_VAR, LABEL, BLOCK, ESWITCH_Z, BREAK_L); //expression break
//
}
public void testFunReturningSwitchExp() {
assertOK(INT_FN_ESWITCH, BREAK_INT_FN);
}
public void testContinueLoops() {
assertOK(LABEL, FOR, CONTINUE_L);
assertOK(LABEL, FOR_EACH, CONTINUE_L);
assertOK(LABEL, WHILE, CONTINUE_L);
assertOK(LABEL, DO, CONTINUE_L);
assertFail("compiler.err.not.loop.label", LABEL, CONTINUE_L);
}
}

View File

@ -0,0 +1,6 @@
TestNG.dirs = .
lib.dirs = /lib/combo
modules = \
jdk.compiler/com.sun.tools.javac.util

View File

@ -95,6 +95,7 @@ import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.EndPosTable;
import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCBreak;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCImport; import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.tree.TreeInfo;
@ -446,6 +447,12 @@ public class CheckAttributedTree {
scan(tree.defs); scan(tree.defs);
} }
@Override
public void visitBreak(JCBreak tree) {
if (tree.isValueBreak())
super.visitBreak(tree);
}
JavaFileObject sourcefile; JavaFileObject sourcefile;
EndPosTable endPosTable; EndPosTable endPosTable;
Info encl; Info encl;

View File

@ -0,0 +1,24 @@
/*
* @test /nodynamiccopyright/
* @bug 8206986
* @summary Adding switch expressions
* @compile/fail/ref=BadSwitchExpressionLambda.out -XDrawDiagnostics --enable-preview -source 12 BadSwitchExpressionLambda.java
*/
class BadSwitchExpressionLambda {
interface SAM {
void invoke();
}
public static void m() {}
public static void r(SAM sam) {}
void test(int i) {
SAM sam1 = () -> m(); //ok
SAM sam2 = () -> switch (i) { case 0 -> m(); default -> m(); }; //not ok
r(() -> m()); //ok
r(() -> switch (i) { case 0 -> m(); default -> m(); }); //not ok
return switch (i) { case 0 -> m(); default -> m(); }; //not ok
}
}

View File

@ -0,0 +1,6 @@
BadSwitchExpressionLambda.java:19:26: compiler.err.prob.found.req: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.switch.expression.target.cant.be.void))
BadSwitchExpressionLambda.java:21:9: compiler.err.cant.apply.symbol: kindname.method, r, BadSwitchExpressionLambda.SAM, @11, kindname.class, BadSwitchExpressionLambda, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.switch.expression.target.cant.be.void)))
BadSwitchExpressionLambda.java:22:16: compiler.err.prob.found.req: (compiler.misc.unexpected.ret.val)
- compiler.note.preview.filename: BadSwitchExpressionLambda.java
- compiler.note.preview.recompile
3 errors

View File

@ -737,7 +737,7 @@ public class DPrinter {
@Override @Override
public void visitCase(JCCase tree) { public void visitCase(JCCase tree) {
printTree("pat", tree.pat); printList("pat", tree.pats);
printList("stats", tree.stats); printList("stats", tree.stats);
} }
@ -782,7 +782,7 @@ public class DPrinter {
@Override @Override
public void visitBreak(JCBreak tree) { public void visitBreak(JCBreak tree) {
printName("label", tree.label); printTree("value", tree.value);
} }
@Override @Override

View File

@ -82,6 +82,9 @@ import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject; import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider; import javax.tools.ToolProvider;
import com.sun.source.tree.CaseTree;
import com.sun.source.util.TreePathScanner;
public class JavacParserTest extends TestCase { public class JavacParserTest extends TestCase {
static final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); static final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
static final JavaFileManager fm = tool.getStandardFileManager(null, null, null); static final JavaFileManager fm = tool.getStandardFileManager(null, null, null);
@ -1038,6 +1041,135 @@ public class JavacParserTest extends TestCase {
assertEquals("the error message is not correct, actual: " + actualErrors, expectedErrors, actualErrors); assertEquals("the error message is not correct, actual: " + actualErrors, expectedErrors, actualErrors);
} }
@Test
void testCaseBodyStatements() throws IOException {
String code = "class C {" +
" void t(int i) {" +
" switch (i) {" +
" case 0 -> i++;" +
" case 1 -> { i++; }" +
" case 2 -> throw new RuntimeException();" +
" case 3 -> if (true) ;" +
" default -> i++;" +
" }" +
" switch (i) {" +
" case 0: i++; break;" +
" case 1: { i++; break;}" +
" case 2: throw new RuntimeException();" +
" case 3: if (true) ; break;" +
" default: i++; break;" +
" }" +
" int j = switch (i) {" +
" case 0 -> i + 1;" +
" case 1 -> { break i + 1; }" +
" default -> throw new RuntimeException();" +
" };" +
" int k = switch (i) {" +
" case 0: break i + 1;" +
" case 1: { break i + 1; }" +
" default: throw new RuntimeException();" +
" };" +
" }" +
"}";
String expectedErrors = "Test.java:1:178: compiler.err.switch.case.unexpected.statement\n";
StringWriter out = new StringWriter();
JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(out, fm, null,
Arrays.asList("-XDrawDiagnostics", "--enable-preview", "-source", "12"),
null, Arrays.asList(new MyFileObject(code)));
CompilationUnitTree cut = ct.parse().iterator().next();
Trees trees = Trees.instance(ct);
List<String> spans = new ArrayList<>();
new TreePathScanner<Void, Void>() {
@Override
public Void visitCase(CaseTree tree, Void v) {
if (tree.getBody() != null) {
int start = (int) trees.getSourcePositions().getStartPosition(cut, tree.getBody());
int end = (int) trees.getSourcePositions().getEndPosition(cut, tree.getBody());
spans.add(code.substring(start, end));
} else {
spans.add("<null>");
}
return super.visitCase(tree, v);
}
}.scan(cut, null);
List<String> expectedSpans = List.of(
"i++;", "{ i++; }", "throw new RuntimeException();", "if (true) ;", "i++;",
"<null>", "<null>", "<null>", "<null>", "<null>",
"i + 1"/*TODO semicolon?*/, "{ break i + 1; }", "throw new RuntimeException();",
"<null>", "<null>", "<null>");
assertEquals("the error spans are not correct; actual:" + spans, expectedSpans, spans);
String toString = normalize(cut.toString());
String expectedToString =
"\n" +
"class C {\n" +
" \n" +
" void t(int i) {\n" +
" switch (i) {\n" +
" case 0 -> i++;\n" +
" case 1 -> {\n" +
" i++;\n" +
" }\n" +
" case 2 -> throw new RuntimeException();\n" +
" case 3 -> if (true) ;\n" +
" default -> i++;\n" +
" }\n" +
" switch (i) {\n" +
" case 0:\n" +
" i++;\n" +
" break;\n" +
" \n" +
" case 1:\n" +
" {\n" +
" i++;\n" +
" break;\n" +
" }\n" +
" \n" +
" case 2:\n" +
" throw new RuntimeException();\n" +
" \n" +
" case 3:\n" +
" if (true) ;\n" +
" break;\n" +
" \n" +
" default:\n" +
" i++;\n" +
" break;\n" +
" \n" +
" }\n" +
" int j = switch (i) {\n" +
" case 0 -> break i + 1;\n" +
" case 1 -> {\n" +
" break i + 1;\n" +
" }\n" +
" default -> throw new RuntimeException();\n" +
" };\n" +
" int k = switch (i) {\n" +
" case 0:\n" +
" break i + 1;\n" +
" \n" +
" case 1:\n" +
" {\n" +
" break i + 1;\n" +
" }\n" +
" \n" +
" default:\n" +
" throw new RuntimeException();\n" +
" \n" +
" };\n" +
" }\n" +
"}";
System.err.println("toString:");
System.err.println(toString);
System.err.println("expectedToString:");
System.err.println(expectedToString);
assertEquals("the error spans are not correct; actual:" + toString, expectedToString, toString);
String actualErrors = normalize(out.toString());
assertEquals("the error message is not correct, actual: " + actualErrors, expectedErrors, actualErrors);
}
@Test @Test
void testTypeParamsWithoutMethod() throws IOException { void testTypeParamsWithoutMethod() throws IOException {
assert tool != null; assert tool != null;

View File

@ -0,0 +1,52 @@
/*
* 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
* @bug 8206986
* @summary Verify rule cases with expression statements and throw statements work.
* @compile --enable-preview -source 12 BlockExpression.java
* @run main/othervm --enable-preview BlockExpression
*/
public class BlockExpression {
public static void main(String... args) {
T t = T.B;
try {
int ii = switch (t) {
case A -> 0;
default -> throw new IllegalStateException();
};
throw new AssertionError("Expected exception not thrown.");
} catch (IllegalStateException ex) {
//OK
}
}
enum T {
A, B;
}
}

View File

@ -0,0 +1,26 @@
/*
* @test /nodynamiccopyright/
* @bug 8206986
* @summary Verify the type of a conditional expression with nested switch expression is computed properly
* @compile/fail/ref=BooleanNumericNonNumeric.out -XDrawDiagnostics --enable-preview -source 12 BooleanNumericNonNumeric.java
*/
public class BooleanNumericNonNumeric {
private void test(boolean b, int i) {
int r1 = 1 + (b ? switch (i) { //boolean, error
default -> true;
} : false);
int r2 = 1 + (b ? switch (i) { //int, ok
default -> 0;
} : 1);
(b ? switch (i) { //int, error
default -> 0;
} : 1).toString();
(b ? switch (i) { //"object", ok
case 0 -> true;
default -> 0;
} : 1).toString();
}
}

View File

@ -0,0 +1,5 @@
BooleanNumericNonNumeric.java:11:20: compiler.err.operator.cant.be.applied.1: +, int, boolean
BooleanNumericNonNumeric.java:19:15: compiler.err.cant.deref: int
- compiler.note.preview.filename: BooleanNumericNonNumeric.java
- compiler.note.preview.recompile
2 errors

View File

@ -0,0 +1,101 @@
/*
* 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 8206986
* @summary Ensure BreakTree.getLabel returns reasonable values
* @modules jdk.compiler
*/
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.lang.model.element.Name;
import javax.tools.*;
import com.sun.source.tree.BreakTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TreePathScanner;
public class BreakTest {
private static final String CODE =
"public class C {" +
" void t1(Integer i) {" +
" LABEL: switch (i) {" +
" case null: i++; break LABEL;" +
" default: i++; break;" +
" }" +
" }" +
" int t2(Integer i) {" +
" return switch (i) {" +
" case null: break LABEL;" +
" default: break 2;" +
" }" +
" }" +
"}";
public static void main(String[] args) throws Exception {
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
assert tool != null;
DiagnosticListener<JavaFileObject> noErrors = d -> {};
StringWriter out = new StringWriter();
JavacTask ct = (JavacTask) tool.getTask(out, null, noErrors,
List.of("-XDdev", "--enable-preview", "-source", "12"), null,
Arrays.asList(new MyFileObject(CODE)));
List<String> labels = new ArrayList<>();
new TreePathScanner<Void, Void>() {
@Override
public Void visitBreak(BreakTree node, Void p) {
Name label = node.getLabel();
labels.add(label != null ? label.toString() : null);
return super.visitBreak(node, p);
}
}.scan(ct.parse(), null);
List<String> expected = Arrays.asList("LABEL", null, "LABEL", null);
if (!expected.equals(labels)) {
throw new AssertionError("Unexpected labels found: " + labels);
}
}
static class MyFileObject extends SimpleJavaFileObject {
private String text;
public MyFileObject(String text) {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
this.text = text;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return text;
}
}
}

View File

@ -0,0 +1,39 @@
/*
* 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 8206986
* @summary Verify than an empty switch expression is rejected.
* @compile/fail/ref=EmptySwitch.out --enable-preview -source 12 -XDrawDiagnostics EmptySwitch.java
*/
public class EmptySwitch {
private void print(EmptySwitchEnum t) {
(switch (t) {
}).toString();
}
enum EmptySwitchEnum {
}
}

View File

@ -0,0 +1,4 @@
EmptySwitch.java:33:10: compiler.err.switch.expression.empty
- compiler.note.preview.filename: EmptySwitch.java
- compiler.note.preview.recompile
1 error

View File

@ -0,0 +1,59 @@
/*
* 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
* @bug 8206986
* @summary Verify that an switch expression over enum can be exhaustive without default.
* @compile --enable-preview -source 12 ExhaustiveEnumSwitch.java
* @compile ExhaustiveEnumSwitchExtra.java
* @run main/othervm --enable-preview ExhaustiveEnumSwitch
*/
public class ExhaustiveEnumSwitch {
public static void main(String... args) {
new ExhaustiveEnumSwitch().run();
}
private void run() {
ExhaustiveEnumSwitchEnum v = ExhaustiveEnumSwitchEnum.valueOf("F");
try {
print(v);
throw new AssertionError("Expected exception did not occur.");
} catch (IncompatibleClassChangeError err) {
//ok
}
}
private String print(ExhaustiveEnumSwitchEnum t) {
return switch (t) {
case A -> "A";
case B -> "B";
};
}
}
enum ExhaustiveEnumSwitchEnum {
A, B;
}

View File

@ -0,0 +1,26 @@
/*
* 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.
*/
enum ExhaustiveEnumSwitchEnum {
A, B, F;
}

View File

@ -0,0 +1,3 @@
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

View File

@ -0,0 +1,115 @@
/*
* @test /nodynamiccopyright/
* @bug 8206986
* @summary Check expression switch works.
* @compile/fail/ref=ExpressionSwitch-old.out -source 9 -Xlint:-options -XDrawDiagnostics ExpressionSwitch.java
* @compile --enable-preview -source 12 ExpressionSwitch.java
* @run main/othervm --enable-preview ExpressionSwitch
*/
import java.util.Objects;
import java.util.function.Supplier;
public class ExpressionSwitch {
public static void main(String... args) {
new ExpressionSwitch().run();
}
private void run() {
check(T.A, "A");
check(T.B, "B");
check(T.C, "other");
assertEquals(exhaustive1(T.C), "C");
assertEquals(scopesIsolated(T.B), "B");
assertEquals(lambdas1(T.B).get(), "B");
assertEquals(lambdas2(T.B).get(), "B");
localClass(T.A);
}
private String print(T t) {
return switch (t) {
case A -> "A";
case B -> { break "B"; }
default -> { break "other"; }
};
}
private String exhaustive1(T t) {
return switch (t) {
case A -> "A";
case B -> { break "B"; }
case C -> "C";
case D -> "D";
};
}
private String exhaustive2(T t) {
return switch (t) {
case A -> "A";
case B -> "B";
case C -> "C";
case D -> "D";
};
}
private String scopesIsolated(T t) {
return switch (t) {
case A -> { String res = "A"; break res;}
case B -> { String res = "B"; break res;}
default -> { String res = "default"; break res;}
};
}
private Supplier<String> lambdas1(T t) {
return switch (t) {
case A -> () -> "A";
case B -> { break () -> "B"; }
default -> () -> "default";
};
}
private Supplier<String> lambdas2(T t) {
return switch (t) {
case A: break () -> "A";
case B: { break () -> "B"; }
default: break () -> "default";
};
}
private void localClass(T t) {
String good = "good";
class L {
public String c() {
STOP: switch (t) {
default: break STOP;
}
return switch (t) {
default: break good;
};
}
}
String result = new L().c();
if (!Objects.equals(result, good)) {
throw new AssertionError("Unexpected result: " + result);
}
}
private void check(T t, String expected) {
String result = print(t);
assertEquals(result, expected);
}
private void assertEquals(Object result, Object expected) {
if (!Objects.equals(result, expected)) {
throw new AssertionError("Unexpected result: " + result);
}
}
enum T {
A, B, C, D;
}
void t() {
Runnable r = () -> {};
r.run();
}
}

View File

@ -0,0 +1,120 @@
/*
* 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
* @bug 8206986
* @summary Verify behavior of various kinds of breaks.
* @compile --enable-preview -source 12 ExpressionSwitchBreaks1.java
* @run main/othervm --enable-preview ExpressionSwitchBreaks1
*/
import java.util.Objects;
import java.util.function.Supplier;
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(2, 5, 5), "2-X-5");
check(print2(-11, -1, -1), "X");
}
private String print1(int i, int j) {
switch (i) {
case 0:
return switch (j) {
case 0:
if (true) 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: res = "0-0-0"; break;
case 1: res = "0-0-1"; break;
case 2: res = "0-0-2"; break INNER;
default: r = "0-0-X"; break OUTER;
}
r = res;
break;
case 1:
r = "0-1";
break;
default:
r = "0-X";
break;
}
break r;
case 1:
break "1";
case 2:
LOP: while (j-- > 0) {
if (k == 5) {
k--;
continue;
}
break LOP;
}
Supplier<String> getter = () -> { return "2-X-5"; };
break getter.get();
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;
}
}

View File

@ -0,0 +1,52 @@
/*
* @test /nodynamiccopyright/
* @bug 8206986
* @summary Check behavior for invalid breaks.
* @compile/fail/ref=ExpressionSwitchBreaks2.out -XDrawDiagnostics --enable-preview -source 12 ExpressionSwitchBreaks2.java
*/
public class ExpressionSwitchBreaks2 {
private String print(int i, int j) {
LOOP: while (true) {
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
case 3: {
int x = -1;
x: switch (i + j) {
case 0: break x; //error: cannot disambiguate, wrong type as well
}
break "X";
}
case 4: return "X"; //error: no returns from inside of the switch expression
case 5: continue; //error: no continue out of the switch expression
case 6: continue LOOP; //error: dtto, but with a label
case 7: continue UNKNOWN; //error: unknown label
default: {
String x = "X";
x: switch (i + j) {
case 0: break ""; //error: cannot break from switch expression that is not immediatelly enclosing
}
break "X";
}
};
case 1:
break "1" + undef; //error: complex value and no switch expression
}
}
j: print(switch (i) {
default: break j; //error: "j" is ambiguous (expression/label)
}, 0);
j2: print(switch (i) {
default: break j2;
}, 0);
return null;
}
}

View File

@ -0,0 +1,15 @@
ExpressionSwitchBreaks2.java:17:25: compiler.err.break.missing.value
ExpressionSwitchBreaks2.java:19:25: compiler.err.break.outside.switch.expression
ExpressionSwitchBreaks2.java:23:37: compiler.err.break.ambiguous.target: x
ExpressionSwitchBreaks2.java:27:29: compiler.err.return.outside.switch.expression
ExpressionSwitchBreaks2.java:28:29: compiler.err.continue.outside.switch.expression
ExpressionSwitchBreaks2.java:29:29: compiler.err.continue.outside.switch.expression
ExpressionSwitchBreaks2.java:30:29: compiler.err.undef.label: UNKNOWN
ExpressionSwitchBreaks2.java:34:37: compiler.err.break.expr.not.immediate: compiler.misc.tree.tag.switch
ExpressionSwitchBreaks2.java:40:17: compiler.err.break.complex.value.no.switch.expression
ExpressionSwitchBreaks2.java:40:29: compiler.err.cant.resolve.location: kindname.variable, undef, , , (compiler.misc.location: kindname.class, ExpressionSwitchBreaks2, null)
ExpressionSwitchBreaks2.java:44:22: compiler.err.break.ambiguous.target: j
ExpressionSwitchBreaks2.java:47:22: compiler.err.break.outside.switch.expression
- compiler.note.preview.filename: ExpressionSwitchBreaks2.java
- compiler.note.preview.recompile
12 errors

View File

@ -0,0 +1,82 @@
/*
* 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 8206986
* @summary Verify various corner cases with nested switch expressions.
* @compile --enable-preview -source 12 ExpressionSwitchBugs.java
* @run main/othervm --enable-preview ExpressionSwitchBugs
*/
public class ExpressionSwitchBugs {
public static void main(String... args) {
new ExpressionSwitchBugs().testNested();
}
private void testNested() {
int i = 0;
check(42, id(switch (42) {
default: i++; break 42;
}));
i = 0;
check(43, id(switch (42) {
case 42: while (i == 0) {
i++;
}
break 42 + i;
default: i++; break 42;
}));
i = 0;
check(42, id(switch (42) {
case 42: if (i == 0) {
break 42;
}
default: i++; break 43;
}));
i = 0;
check(42, id(switch (42) {
case 42: if (i == 0) {
break 41 + switch (0) {
case 0 -> 1;
default -> -1;
};
}
default: i++; break 43;
}));
}
private int id(int i) {
return i;
}
private int id(Object o) {
return -1;
}
private void check(int actual, int expected) {
if (actual != expected) {
throw new AssertionError("Unexpected result: " + actual);
}
}
}

View File

@ -0,0 +1,68 @@
/*
* 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 8206986
* @summary Check switch expressions
* @compile --enable-preview -source 12 ExpressionSwitchCodeFromJLS.java
* @run main/othervm --enable-preview ExpressionSwitchCodeFromJLS
*/
public class ExpressionSwitchCodeFromJLS {
static void howMany(int k) {
switch (k) {
case 1: System.out.print("one ");
case 2: System.out.print("too ");
case 3: System.out.println("many");
}
}
static void howManyRule(int k) {
switch (k) {
case 1 -> System.out.println("one");
case 2 -> System.out.println("two");
case 3 -> System.out.println("many");
}
}
static void howManyGroup(int k) {
switch (k) {
case 1: System.out.println("one");
break; // exit the switch
case 2: System.out.println("two");
break; // exit the switch
case 3: System.out.println("many");
break; // not needed, but good style
}
}
public static void main(String[] args) {
howMany(3);
howMany(2);
howMany(1);
howManyRule(1);
howManyRule(2);
howManyRule(3);
howManyGroup(1);
howManyGroup(2);
howManyGroup(3);
}
}

View File

@ -0,0 +1,185 @@
/*
* 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 8206986
* @summary Check definite (un)assignment for in switch expressions.
* @compile --enable-preview -source 12 ExpressionSwitchDA.java
* @run main/othervm --enable-preview ExpressionSwitchDA
*/
public class ExpressionSwitchDA {
public static void test1() {
int i;
int j = 0;
switch (j) {
case 0 : i=42; break;
default: i=42;
}
System.out.println(i);
}
public static void test2(){
int i;
int j = 0;
switch (j) {
case 0 -> i=42;
default -> i=42;
}
System.out.println(i);
}
public static void test3(){
int i;
int j = 0;
switch (j) {
case 0 -> { i=42; }
default -> { i=42; }
}
System.out.println(i);
}
public static void test4(){
int i;
int j = 0;
int k = switch (j) {
case 0 -> i=42;
default -> i=42;
};
System.out.println(i);
}
public static void test5(){
int i;
int j = 0;
int k = switch (j) {
case 0 -> { i=42; break 42; }
default -> i=42;
};
System.out.println(i);
}
public static void test6(){
int i;
int j = 0;
int k = switch (j) {
case 0 -> i=42;
default -> { i=42; break 42; }
};
System.out.println(i);
}
public static void test7(){
int i;
int j = 0;
int k = switch (j) {
case 0 -> { i=42; break 42; }
default -> { i=42; break 42; }
};
System.out.println(i);
}
public static void test8() {
int i;
int j = 0;
switch (j) {
case 0 : i=42; break;
default: throw new NullPointerException();
}
System.out.println(i);
}
public static void test9() {
int i;
int j = 0;
switch (j) {
case 0 -> i=42;
default -> throw new NullPointerException();
}
System.out.println(i);
}
public static void test10() {
int i;
int j = 0;
switch (j) {
case 0 -> { i=42; System.out.print(i);}
default -> throw new NullPointerException();
}
System.out.println(i);
}
public static void test11() {
int i;
int j = 0;
switch (j) {
case 0 -> { i=42; System.out.print(i);}
default -> { throw new NullPointerException(); }
}
System.out.println(i);
}
public static void test12() {
int i;
int j = 0;
switch (j) {
case 0 : i=42; break;
default: return;
}
System.out.println(i);
}
public static void test13() {
int i;
int j = 0;
switch (j) {
case 0 -> i=42;
default -> { return; }
}
System.out.println(i);
}
public static void test14() {
int i;
int j = 0;
switch (j) {
case 0 -> { i=42; }
default -> { return; }
}
System.out.println(i);
}
public static void test15() {
final int i;
int j = 0;
switch (j) {
case 0 -> { i=42; }
default -> { i=42; }
}
System.out.println(i);
}
public static void main(String[] args) {
test1();
test2();
test3();
test4();
test5();
test6();
test7();
test8();
test9();
test10();
test11();
test12();
test13();
test14();
test15();
}
}

View File

@ -0,0 +1,79 @@
/*
* 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 8206986
* @summary Check fall through in switch expressions.
* @compile --enable-preview -source 12 ExpressionSwitchFallThrough.java
* @run main/othervm --enable-preview ExpressionSwitchFallThrough
*/
import java.util.Objects;
import java.util.function.Function;
public class ExpressionSwitchFallThrough {
public static void main(String... args) {
new ExpressionSwitchFallThrough().run();
}
private void run() {
runTest(this::expression1);
runTest(this::expression2);
}
private void runTest(Function<T, String> print) {
check(T.A, print, "ab");
check(T.B, print, "b");
check(T.C, print, "");
}
private String expression1(T t) {
String help = "";
return switch (t) {
case A: help = "a";
case B: help += "b";
default: break help;
};
}
private String expression2(T t) {
String help = "";
return switch (t) {
case A: help = "a";
case B: help += "b";
default: break help;
};
}
private void check(T t, Function<T, String> print, String expected) {
String result = print.apply(t);
if (!Objects.equals(result, expected)) {
throw new AssertionError("Unexpected result: " + result);
}
}
enum T {
A, B, C;
}
}

View File

@ -0,0 +1,75 @@
/*
* 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
* @bug 8206986
* @summary Check fall through in switch expressions.
* @compile --enable-preview -source 12 ExpressionSwitchFallThrough1.java
* @run main/othervm --enable-preview ExpressionSwitchFallThrough1
*/
import java.util.Objects;
public class ExpressionSwitchFallThrough1 {
public static void main(String... args) {
new ExpressionSwitchFallThrough1().test();
}
private void test() {
assertEquals("01", printExprFallThrough(0));
assertEquals("1", printExprFallThrough(1));
assertEquals("other", printExprFallThrough(3));
assertEquals("01", printStatementFallThrough(0));
assertEquals("1", printStatementFallThrough(1));
assertEquals("other", printStatementFallThrough(3));
}
private String printExprFallThrough(Integer p) {
String result = "";
return switch (p) {
case 0: result += "0";
case 1: result += "1";
break result;
default: break "other";
};
}
private String printStatementFallThrough(Integer p) {
String result = "";
switch (p) {
case 0: result += "0";
case 1: result += "1";
break ;
default: result = "other";
break;
}
return result;
}
private static void assertEquals(Object expected, Object actual) {
if (!Objects.equals(actual, expected)) {
throw new AssertionError("Unexpected result: " + actual + ", expected: " + expected);
}
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.
*/
/*
* @test
* @bug 8206986
* @summary Check switch expressions embedded in switch expressions.
* @compile --enable-preview -source 12 ExpressionSwitchInExpressionSwitch.java
* @run main/othervm --enable-preview ExpressionSwitchInExpressionSwitch
*/
public class ExpressionSwitchInExpressionSwitch {
public static void main(String[] args) {
int j = 42;
int i = switch (j) {
default -> (switch (j) { default -> 0; } )+1;
};
if (i!=1) {
throw new AssertionError("Unexpected result: " + i);
}
i = switch (j) {
default -> {
int k = switch (j) {
default -> {
break 42;
}
};
System.out.println("didn't break to the top level");
break 43;
}
};
if (i!=43) {
throw new AssertionError("Unexpected result: " + i);
}
}
}

View File

@ -0,0 +1,37 @@
/*
* @test /nodynamiccopyright/
* @bug 8206986
* @summary Check types inferred for switch expressions.
* @compile/fail/ref=ExpressionSwitchInfer.out -XDrawDiagnostics --enable-preview -source 12 ExpressionSwitchInfer.java
*/
import java.util.ArrayList;
import java.util.List;
public class ExpressionSwitchInfer {
private static final String NULL = "null";
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);
test(switch (param) {
case NULL -> new ArrayList<>();
default -> new ArrayList<>();
}, CharSequence.class, param).charAt(0);
test(switch (param) {
case NULL -> new ArrayList<>();
default -> new ArrayList<>();
}, CharSequence.class, param).substring(0);
String str = switch (param) {
case "" -> {
break 0;
} default ->"default";
};
return null;
}
}

View File

@ -0,0 +1,6 @@
ExpressionSwitchInfer.java:17:95: compiler.err.cant.resolve.location.args: kindname.method, substring, , int, (compiler.misc.location: kindname.interface, java.lang.CharSequence, null)
ExpressionSwitchInfer.java:26:38: compiler.err.cant.resolve.location.args: kindname.method, substring, , int, (compiler.misc.location: kindname.interface, java.lang.CharSequence, null)
ExpressionSwitchInfer.java:30:23: compiler.err.prob.found.req: (compiler.misc.incompatible.type.in.switch.expression: (compiler.misc.inconvertible.types: int, java.lang.String))
- compiler.note.preview.filename: ExpressionSwitchInfer.java
- compiler.note.preview.recompile
3 errors

View File

@ -0,0 +1,63 @@
/*
* 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 8206986
* @summary Verify behavior when an intersection type is inferred for switch expression.
* @compile --enable-preview -source 12 ExpressionSwitchIntersectionTypes.java
* @run main/othervm --enable-preview ExpressionSwitchIntersectionTypes
*/
public class ExpressionSwitchIntersectionTypes<X extends java.io.Serializable & Runnable> {
void test1(int i, X x) {
Runnable r1 = switch (i) {
default -> x;
};
r1.run();
}
void test2(int i, X x) {
(switch (i) {
default -> x;
}).run();
}
public static void main(String[] args) {
ExpressionSwitchIntersectionTypes t = new ExpressionSwitchIntersectionTypes();
try {
t.test1(0, "");
throw new AssertionError("Expected exception didn't occur.");
} catch (ClassCastException ex) {
//good
}
try {
t.test2(0, "");
throw new AssertionError("Expected exception didn't occur.");
} catch (ClassCastException ex) {
//good
}
}
}

View File

@ -0,0 +1,37 @@
/*
* @test /nodynamiccopyright/
* @bug 8206986
* @summary Verify behavior of not exhaustive switch expressions.
* @compile/fail/ref=ExpressionSwitchNotExhaustive.out -XDrawDiagnostics --enable-preview -source 12 ExpressionSwitchNotExhaustive.java
*/
public class ExpressionSwitchNotExhaustive {
private String print(int i) {
return switch (i) {
case 42 -> "42";
case 43 -> "43";
};
}
private String e(E e) {
return switch (e) {
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;
}
}

View File

@ -0,0 +1,6 @@
ExpressionSwitchNotExhaustive.java:10:16: compiler.err.not.exhaustive
ExpressionSwitchNotExhaustive.java:16:16: compiler.err.not.exhaustive
ExpressionSwitchNotExhaustive.java:29:23: compiler.err.var.might.not.have.been.initialized: s
- compiler.note.preview.filename: ExpressionSwitchNotExhaustive.java
- compiler.note.preview.recompile
3 errors

View File

@ -0,0 +1,57 @@
/*
* @test /nodynamiccopyright/
* @bug 8206986
* @summary Verify reachability in switch expressions.
* @compile/fail/ref=ExpressionSwitchUnreachable.out -XDrawDiagnostics --enable-preview -source 12 ExpressionSwitchUnreachable.java
*/
public class ExpressionSwitchUnreachable {
public static void main(String[] args) {
int z = 42;
int i = switch (z) {
case 0 -> {
break 42;
System.out.println("Unreachable"); //Unreachable
}
default -> 0;
};
i = switch (z) {
case 0 -> {
break 42;
break 42; //Unreachable
}
default -> 0;
};
i = switch (z) {
case 0:
System.out.println("0");
break 42;
System.out.println("1"); //Unreachable
default : break 42;
};
i = switch (z) {
case 0 -> 42;
default -> {
break 42;
System.out.println("Unreachable"); //Unreachable
}
};
i = switch (z) {
case 0: break 42;
default:
System.out.println("0");
break 42;
System.out.println("1"); //Unreachable
};
i = switch (z) {
case 0:
default:
System.out.println("0");
break 42;
System.out.println("1"); //Unreachable
};
}
}

View File

@ -0,0 +1,9 @@
ExpressionSwitchUnreachable.java:15:17: compiler.err.unreachable.stmt
ExpressionSwitchUnreachable.java:22:17: compiler.err.unreachable.stmt
ExpressionSwitchUnreachable.java:30:17: compiler.err.unreachable.stmt
ExpressionSwitchUnreachable.java:37:17: compiler.err.unreachable.stmt
ExpressionSwitchUnreachable.java:45:17: compiler.err.unreachable.stmt
ExpressionSwitchUnreachable.java:52:17: compiler.err.unreachable.stmt
- compiler.note.preview.filename: ExpressionSwitchUnreachable.java
- compiler.note.preview.recompile
6 errors

View File

@ -0,0 +1,99 @@
/*
* 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 8206986
* @summary Ensure than parser can parse incomplete sources
* @modules jdk.compiler
*/
import java.io.StringWriter;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import javax.tools.*;
import com.sun.source.util.JavacTask;
public class ParseIncomplete {
private static final String CODE =
"public class C {" +
" void t1(Integer i) {" +
" switch (i) {" +
" case null: i++; break;" +
" case 0, 1: i++; break;" +
" default: i++; break;" +
" }" +
" }" +
" int t2(Integer i) {" +
" return switch (i) {" +
" case null: break 0;" +
" case 0, 1: break 1;" +
" default: break 2;" +
" }" +
" }" +
"}";
public static void main(String[] args) throws Exception {
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
assert tool != null;
DiagnosticListener<JavaFileObject> noErrors = d -> {};
for (int i = 0; i < CODE.length(); i++) {
String code = CODE.substring(0, i + 1);
StringWriter out = new StringWriter();
try {
JavacTask ct = (JavacTask) tool.getTask(out, null, noErrors,
List.of("-XDdev", "--enable-preview", "-source", "12"), null,
Arrays.asList(new MyFileObject(code)));
ct.parse().iterator().next();
} catch (Throwable t) {
System.err.println("Unexpected exception for code: " + code);
System.err.println("output: " + out);
throw t;
}
if (!out.toString().isEmpty()) {
System.err.println("Unexpected compiler for code: " + code);
System.err.println(out);
throw new AssertionError();
}
}
}
static class MyFileObject extends SimpleJavaFileObject {
private String text;
public MyFileObject(String text) {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
this.text = text;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return text;
}
}
}

View File

@ -0,0 +1,15 @@
/*
* @test /nodynamiccopyright/
* @bug 8206986
* @summary Verify the parser handles broken input gracefully.
* @compile/fail/ref=ParserRecovery.out -XDrawDiagnostics --enable-preview -source 12 ParserRecovery.java
*/
public class ParserRecovery {
void t1(int e) {
int i = switch (e) { case any; };
}
void t2(int e) {
switch (e) { case any; }
}
}

View File

@ -0,0 +1,5 @@
ParserRecovery.java:10:39: compiler.err.expected2: :, ->
ParserRecovery.java:13:31: compiler.err.expected2: :, ->
- compiler.note.preview.filename: ParserRecovery.java
- compiler.note.preview.recompile
2 errors

View File

@ -0,0 +1,18 @@
/*
* @test /nodynamiccopyright/
* @bug 8206986
* @summary Verify that scopes in rule cases are isolated.
* @compile/fail/ref=SwitchExpressionScopesIsolated.out -XDrawDiagnostics --enable-preview -source 12 SwitchExpressionScopesIsolated.java
*/
public class SwitchExpressionScopesIsolated {
private String scopesIsolated(int i) {
return switch (i) {
case 0 -> { String res = ""; break res; }
case 1 -> { res = ""; break res; }
default -> { res = ""; break res; }
};
}
}

View File

@ -0,0 +1,7 @@
SwitchExpressionScopesIsolated.java:13:25: compiler.err.cant.resolve.location: kindname.variable, res, , , (compiler.misc.location: kindname.class, SwitchExpressionScopesIsolated, null)
SwitchExpressionScopesIsolated.java:13:41: compiler.err.cant.resolve.location: kindname.variable, res, , , (compiler.misc.location: kindname.class, SwitchExpressionScopesIsolated, null)
SwitchExpressionScopesIsolated.java:14:26: compiler.err.cant.resolve.location: kindname.variable, res, , , (compiler.misc.location: kindname.class, SwitchExpressionScopesIsolated, null)
SwitchExpressionScopesIsolated.java:14:42: compiler.err.cant.resolve.location: kindname.variable, res, , , (compiler.misc.location: kindname.class, SwitchExpressionScopesIsolated, null)
- compiler.note.preview.filename: SwitchExpressionScopesIsolated.java
- compiler.note.preview.recompile
4 errors

View File

@ -0,0 +1,110 @@
/*
* 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 8206986
* @summary Ensure SimpleTreeVisitor.visitSwitchExpression behaves as it should
* @modules jdk.compiler
*/
import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import javax.tools.*;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.SwitchExpressionTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.SimpleTreeVisitor;
import com.sun.source.util.TreePathScanner;
public class SwitchExpressionSimpleVisitorTest {
public static void main(String[] args) throws Exception {
new SwitchExpressionSimpleVisitorTest().run();
}
void run() throws Exception {
String code = "class Test {\n" +
" int t(int i) {\n" +
" return switch(i) {\n" +
" default: break -1;\n" +
" }\n" +
" }\n" +
"}\n";
int[] callCount = new int[1];
int[] switchExprNodeCount = new int[1];
new TreePathScanner<Void, Void>() {
@Override
public Void visitSwitchExpression(SwitchExpressionTree node, Void p) {
node.accept(new SimpleTreeVisitor<Void, Void>() {
@Override
protected Void defaultAction(Tree defaultActionNode, Void p) {
callCount[0]++;
if (node == defaultActionNode) {
switchExprNodeCount[0]++;
}
return null;
}
}, null);
return super.visitSwitchExpression(node, p);
}
}.scan(parse(code), null);
if (callCount[0] != 1 || switchExprNodeCount[0] != 1) {
throw new AssertionError("Unexpected counts; callCount=" + callCount[0] +
", switchExprNodeCount=" + switchExprNodeCount[0]);
}
}
private CompilationUnitTree parse(String code) throws IOException {
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
assert tool != null;
DiagnosticListener<JavaFileObject> noErrors = d -> {};
StringWriter out = new StringWriter();
JavacTask ct = (JavacTask) tool.getTask(out, null, noErrors,
List.of("--enable-preview", "-source", "12"), null,
Arrays.asList(new MyFileObject(code)));
return ct.parse().iterator().next();
}
static class MyFileObject extends SimpleJavaFileObject {
private String text;
public MyFileObject(String text) {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
this.text = text;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return text;
}
}
}

View File

@ -0,0 +1,157 @@
/*
* 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 8206986
* @summary Ensure CaseTree methods return expected values
* @modules jdk.compiler
*/
import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.tools.*;
import com.sun.source.tree.CaseTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TreePathScanner;
public class CaseTest {
public static void main(String[] args) throws Exception {
new CaseTest().testLabels();
new CaseTest().testStatement();
new CaseTest().testRule();
}
void testLabels() throws Exception {
String code = "class Test {\n" +
" void t(int i) {\n" +
" switch(i) {\n" +
" case 0: break;\n" +
" case 1, 2: breal;\n" +
" default: breal;\n" +
" }\n" +
" }\n" +
"}\n";
List<String> labels = new ArrayList<>();
new TreePathScanner<Void, Void>() {
@Override
public Void visitCase(CaseTree node, Void p) {
labels.add(String.valueOf(node.getExpression()));
labels.add(node.getExpressions().stream()
.map(String::valueOf)
.collect(Collectors.joining(",", "[", "]")));
return super.visitCase(node, p);
}
}.scan(parse(code), null);
List<String> expected = Arrays.asList("0", "[0]", "1", "[1,2]", "null", "[]");
if (!expected.equals(labels)) {
throw new AssertionError("Unexpected labels found: " + labels);
}
}
void testStatement() throws Exception {
String code = "class Test {\n" +
" void t(int i) {\n" +
" switch(i) {\n" +
" case 0:" +
" System.err.println();\n" +
" break;\n" +
" }\n" +
" }\n" +
"}\n";
new TreePathScanner<Void, Void>() {
@Override
public Void visitCase(CaseTree node, Void p) {
if (node.getStatements().size() != 2) {
throw new AssertionError("Unexpected statements: " + node.getStatements());
}
if (node.getBody() != null) {
throw new AssertionError("Unexpected body: " + node.getBody());
}
return super.visitCase(node, p);
}
}.scan(parse(code), null);
}
void testRule() throws Exception {
String code = "class Test {\n" +
" void t(int i) {\n" +
" switch(i) {\n" +
" case 0 -> {" +
" System.err.println();\n" +
" };\n" +
" }\n" +
" }\n" +
"}\n";
new TreePathScanner<Void, Void>() {
@Override
public Void visitCase(CaseTree node, Void p) {
if (node.getStatements() != null) {
throw new AssertionError("Unexpected statements: " + node.getStatements());
}
if (node.getBody().getKind() != Tree.Kind.BLOCK) {
throw new AssertionError("Unexpected body: " + node.getBody());
}
return super.visitCase(node, p);
}
}.scan(parse(code), null);
}
private CompilationUnitTree parse(String code) throws IOException {
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
assert tool != null;
DiagnosticListener<JavaFileObject> noErrors = d -> {};
StringWriter out = new StringWriter();
JavacTask ct = (JavacTask) tool.getTask(out, null, noErrors,
List.of("-XDdev", "--enable-preview", "-source", "12"), null,
Arrays.asList(new MyFileObject(code)));
return ct.parse().iterator().next();
}
static class MyFileObject extends SimpleJavaFileObject {
private String text;
public MyFileObject(String text) {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
this.text = text;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return text;
}
}
}

View File

@ -0,0 +1,4 @@
MultipleLabelsExpression.java:31:16: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.switch.expressions)
MultipleLabelsExpression.java:32:20: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.switch.rules)
MultipleLabelsExpression.java:33:19: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.multiple.case.labels)
3 errors

View File

@ -0,0 +1,49 @@
/*
* @test /nodynamiccopyright/
* @bug 8206986
* @summary Verify cases with multiple labels work properly.
* @compile/fail/ref=MultipleLabelsExpression-old.out -source 9 -Xlint:-options -XDrawDiagnostics MultipleLabelsExpression.java
* @compile --enable-preview -source 12 MultipleLabelsExpression.java
* @run main/othervm --enable-preview MultipleLabelsExpression
*/
import java.util.Objects;
import java.util.function.Function;
public class MultipleLabelsExpression {
public static void main(String... args) {
new MultipleLabelsExpression().run();
}
private void run() {
runTest(this::expression1);
}
private void runTest(Function<T, String> print) {
check(T.A, print, "A");
check(T.B, print, "B-C");
check(T.C, print, "B-C");
check(T.D, print, "D");
check(T.E, print, "other");
}
private String expression1(T t) {
return switch (t) {
case A -> "A";
case B, C -> { break "B-C"; }
case D -> "D";
default -> "other";
};
}
private void check(T t, Function<T, String> print, String expected) {
String result = print.apply(t);
if (!Objects.equals(result, expected)) {
throw new AssertionError("Unexpected result: " + result);
}
}
enum T {
A, B, C, D, E;
}
}

Some files were not shown because too many files have changed in this diff Show More