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"),
TARGET("target", "Target", "com.sun.tools.javac.jvm"),
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"),
URL("url", "URL", "java.net"),
SET("set", "Set", "java.util"),

View File

@ -35,6 +35,8 @@ import javax.lang.model.element.Name;
* break;
*
* break <em>label</em> ;
*
* break <em>expression</em> ;
* </pre>
*
* @jls section 14.15
@ -49,4 +51,18 @@ public interface BreakTree extends StatementTree {
* @return the label
*/
Name getLabel();
/**
* Returns the expression for this {@code break} statement.
*
* @return the expression
* @since 12
*
* @deprecated This method is modeling value breaks, which are part of
* a preview feature and may be removed if the preview feature
* is removed.
*
*/
@Deprecated(forRemoval=true, since="12")
ExpressionTree getValue();
}

View File

@ -28,7 +28,7 @@ package com.sun.source.tree;
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:
* <pre>
@ -49,13 +49,89 @@ public interface CaseTree extends Tree {
/**
* Returns the expression for the case, or
* {@code null} if this is the default case.
* If this case has multiple lables, returns the first label.
* @return the expression for the case, or null
*/
ExpressionTree getExpression();
/**
* Returns the statements labeled by the case.
* @return the statements labeled by the case
* Returns the labels for this case.
* For default case, returns an empty list.
*
* @return labels for this case
* @since 12
*
* @deprecated This method is modeling a case with multiple labels,
* which is part of a preview feature and may be removed
* if the preview feature is removed.
*/
@Deprecated(forRemoval=true, since="12")
List<? 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();
/**
* 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),
/**
* 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}.
*/

View File

@ -353,6 +353,23 @@ public interface TreeVisitor<R,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.
* @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);
}
/**
* {@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}.
*

View File

@ -26,6 +26,7 @@
package com.sun.source.util;
import com.sun.source.tree.*;
import com.sun.source.tree.CaseTree.CaseKind;
/**
* A TreeVisitor that visits all the child tree nodes.
@ -339,11 +340,36 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of scanning
*
* @deprecated
* This method is modeling switch expressions,
* which are part of a preview feature and may be removed
* if the preview feature is removed.
*/
@Override
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 = scanAndReduce(node.getStatements(), p, r);
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);
return r;
}
@ -441,8 +467,9 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
* @return the result of scanning
*/
@Override
@SuppressWarnings("removal")
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.
*/
public boolean isPreview(Feature feature) {
if (feature == Feature.SWITCH_EXPRESSION ||
feature == Feature.SWITCH_MULTIPLE_CASE_LABELS ||
feature == Feature.SWITCH_RULE)
return true;
//Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
//When real preview features will be added, this method can be implemented to return 'true'
//for those selected features, and 'false' for all the others.

View File

@ -180,7 +180,10 @@ public enum Source {
UNDERSCORE_IDENTIFIER(MIN, JDK8),
PRIVATE_INTERFACE_METHODS(JDK9, Fragments.FeaturePrivateIntfMethods, DiagKind.PLURAL),
LOCAL_VARIABLE_TYPE_INFERENCE(JDK10),
IMPORT_ON_DEMAND_OBSERVABLE_PACKAGES(JDK1_2, JDK8);
IMPORT_ON_DEMAND_OBSERVABLE_PACKAGES(JDK1_2, JDK8),
SWITCH_MULTIPLE_CASE_LABELS(JDK12, Fragments.FeatureMultipleCaseLabels, DiagKind.PLURAL),
SWITCH_RULE(JDK12, Fragments.FeatureSwitchRules, DiagKind.PLURAL),
SWITCH_EXPRESSION(JDK12, Fragments.FeatureSwitchExpressions, DiagKind.PLURAL);
enum DiagKind {
NORMAL,

View File

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

View File

@ -25,7 +25,6 @@
package com.sun.tools.javac.comp;
import com.sun.source.tree.LambdaExpressionTree.BodyKind;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symtab;
@ -39,10 +38,12 @@ import com.sun.tools.javac.comp.DeferredAttr.DeferredAttrContext;
import com.sun.tools.javac.comp.DeferredAttr.DeferredType;
import com.sun.tools.javac.comp.DeferredAttr.DeferredTypeCompleter;
import com.sun.tools.javac.comp.DeferredAttr.LambdaReturnScanner;
import com.sun.tools.javac.comp.DeferredAttr.SwitchExpressionScanner;
import com.sun.tools.javac.comp.Infer.PartiallyInferredMethodType;
import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase;
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCBreak;
import com.sun.tools.javac.tree.JCTree.JCConditional;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCLambda;
@ -52,6 +53,7 @@ import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCNewClass;
import com.sun.tools.javac.tree.JCTree.JCParens;
import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCSwitchExpression;
import com.sun.tools.javac.tree.TreeCopier;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.util.Assert;
@ -145,7 +147,7 @@ public class ArgumentAttr extends JCTree.Visitor {
* Checks a type in the speculative tree against a given result; the type can be either a plain
* type or an argument type,in which case a more complex check is required.
*/
Type checkSpeculative(JCExpression expr, ResultInfo resultInfo) {
Type checkSpeculative(JCTree expr, ResultInfo resultInfo) {
return checkSpeculative(expr, expr.type, resultInfo);
}
@ -255,6 +257,11 @@ public class ArgumentAttr extends JCTree.Visitor {
processArg(that, speculativeTree -> new ConditionalType(that, env, speculativeTree));
}
@Override
public void visitSwitchExpression(JCSwitchExpression that) {
processArg(that, speculativeTree -> new SwitchExpressionType(that, env, speculativeTree));
}
@Override
public void visitReference(JCMemberReference tree) {
//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.
*/

View File

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

View File

@ -101,6 +101,11 @@ public class AttrContext {
*/
Attr.ResultInfo returnResult = null;
/** ResultInfo to be used for attributing 'break' statement expressions
* (set by Attr.visitSwitchExpression)
*/
Attr.ResultInfo breakResult = null;
/** Symbol corresponding to the site of a qualified default super call
*/
Type defaultSuperCallSite = null;
@ -124,6 +129,7 @@ public class AttrContext {
info.lint = lint;
info.enclVar = enclVar;
info.returnResult = returnResult;
info.breakResult = breakResult;
info.defaultSuperCallSite = defaultSuperCallSite;
info.isSerializable = isSerializable;
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
* to their target - this step is required as inference could end up

View File

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

View File

@ -715,7 +715,7 @@ public class LambdaToMethod extends TreeTranslator {
JCBreak br = make.Break(null);
breaks.add(br);
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());
for (JCBreak br : breaks) {

View File

@ -26,7 +26,11 @@
package com.sun.tools.javac.comp;
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.Kinds.KindSelector;
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.Symbol.OperatorSymbol.AccessCode.DEREF;
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.Tag.*;
@ -263,6 +271,13 @@ public class Lower extends TreeTranslator {
}
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);
}
}
ClassSymbol ownerToCopyFreeVarsFrom(ClassSymbol c) {
@ -3340,6 +3356,45 @@ public class Lower extends TreeTranslator {
}
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);
boolean enumSwitch = selsuper != null &&
(tree.selector.type.tsym.flags() & ENUM) != 0;
@ -3371,10 +3426,10 @@ public class Lower extends TreeTranslator {
ordinalMethod)));
ListBuffer<JCCase> cases = new ListBuffer<>();
for (JCCase c : tree.cases) {
if (c.pat != null) {
VarSymbol label = (VarSymbol)TreeInfo.symbol(c.pat);
if (c.pats.nonEmpty()) {
VarSymbol label = (VarSymbol)TreeInfo.symbol(c.pats.head);
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 {
cases.append(c);
}
@ -3442,10 +3497,10 @@ public class Lower extends TreeTranslator {
Map<Integer, Set<String>> hashToString = new LinkedHashMap<>(alternatives + 1, 1.0f);
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();
Integer mapping = caseLabelToPosition.put(labelExpr, casePosition);
Assert.checkNull(mapping);
@ -3529,7 +3584,7 @@ public class Lower extends TreeTranslator {
breakStmt.target = switch1;
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();
@ -3546,18 +3601,17 @@ public class Lower extends TreeTranslator {
// replacement switch being created.
patchTargets(oneCase, tree, switch2);
boolean isDefault = (oneCase.getExpression() == null);
boolean isDefault = (oneCase.pats.isEmpty());
JCExpression caseExpr;
if (isDefault)
caseExpr = null;
else {
caseExpr = make.Literal(caseLabelToPosition.get((String)TreeInfo.skipParens(oneCase.
getExpression()).
caseExpr = make.Literal(caseLabelToPosition.get((String)TreeInfo.skipParens(oneCase.pats.head).
type.constValue()));
}
lb.append(make.Case(caseExpr,
oneCase.getStatements()));
lb.append(make.Case(JCCase.STATEMENT, caseExpr == null ? List.nil() : List.of(caseExpr),
oneCase.getStatements(), null));
}
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) {
tree.elemtype = translate(tree.elemtype);
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) {
tree.defs = translateVarDefs(tree.defs);
tree.defs = translate(tree.defs);
tree.expr = translate(tree.expr, tree.type);
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.VOID;
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.
*
@ -561,11 +562,22 @@ public class TransTypes extends TreeTranslator {
}
public void visitCase(JCCase tree) {
tree.pat = translate(tree.pat, null);
tree.pats = translate(tree.pats, null);
tree.stats = translate(tree.stats);
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) {
tree.lock = translate(tree.lock, erasure(tree.lock.type));
tree.body = translate(tree.body);
@ -606,6 +618,16 @@ public class TransTypes extends TreeTranslator {
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) {
tree.expr = translate(tree.expr, erasure(tree.expr.type));
result = tree;

View File

@ -259,13 +259,13 @@ public class TreeDiffer extends TreeScanner {
@Override
public void visitBreak(JCBreak tree) {
JCBreak that = (JCBreak) parameter;
result = tree.label == that.label;
result = scan(tree.value, that.value);
}
@Override
public void visitCase(JCCase tree) {
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

View File

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

View File

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

View File

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

View File

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

View File

@ -49,6 +49,7 @@
# 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
# 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>
# url a URL
# object a Java object (unspecified)
@ -187,6 +188,33 @@ compiler.err.bad.initializer=\
compiler.err.break.outside.switch.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
compiler.err.call.must.be.first.stmt.in.ctor=\
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=\
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
compiler.misc.incompatible.type.in.conditional=\
@ -814,6 +836,14 @@ compiler.misc.incompatible.type.in.conditional=\
compiler.misc.conditional.target.cant.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
compiler.misc.incompatible.ret.type.in.lambda=\
bad return type in lambda expression\n\
@ -1289,6 +1319,9 @@ compiler.misc.cant.apply.diamond.1=\
compiler.err.unreachable.stmt=\
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=\
initializer must be able to complete normally
@ -2579,6 +2612,22 @@ compiler.misc.kindname.static.init=\
compiler.misc.kindname.instance.init=\
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=\
@ -2768,6 +2817,15 @@ compiler.misc.feature.static.intf.method.invoke=\
compiler.misc.feature.private.intf.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=\
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=\
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

View File

@ -47,10 +47,12 @@ import static com.sun.tools.javac.tree.JCTree.Tag.*;
import javax.tools.JavaFileManager.Location;
import com.sun.source.tree.CaseTree.CaseKind;
import com.sun.source.tree.ModuleTree.ModuleKind;
import com.sun.tools.javac.code.Directive.ExportsDirective;
import com.sun.tools.javac.code.Directive.OpensDirective;
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
@ -149,10 +151,14 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
*/
SWITCH,
/** Case parts in switch statements, of type Case.
/** Case parts in switch statements/expressions, of type Case.
*/
CASE,
/** Switch expression statements, of type Switch.
*/
SWITCH_EXPRESSION,
/** Synchronized statements, of type Synchonized.
*/
SYNCHRONIZED,
@ -1238,21 +1244,50 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
* A "case :" of a switch.
*/
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;
protected JCCase(JCExpression pat, List<JCStatement> stats) {
this.pat = pat;
public JCTree body;
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.body = body;
}
@Override
public void accept(Visitor v) { v.visitCase(this); }
@DefinedBy(Api.COMPILER_TREE)
@Override @DefinedBy(Api.COMPILER_TREE)
public Kind getKind() { return Kind.CASE; }
@DefinedBy(Api.COMPILER_TREE)
public JCExpression getExpression() { return pat; }
@DefinedBy(Api.COMPILER_TREE)
public List<JCStatement> getStatements() { return stats; }
@Override @DefinedBy(Api.COMPILER_TREE)
public JCExpression getExpression() { return pats.head; }
@Override @DefinedBy(Api.COMPILER_TREE)
@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)
public <R,D> R accept(TreeVisitor<R,D> v, D 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.
*/
@ -1484,19 +1549,27 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
* A break from a loop or switch.
*/
public static class JCBreak extends JCStatement implements BreakTree {
public Name label;
public JCExpression value;
public JCTree target;
protected JCBreak(Name label, JCTree target) {
this.label = label;
protected JCBreak(JCExpression value, JCTree target) {
this.value = value;
this.target = target;
}
@Override
public void accept(Visitor v) { v.visitBreak(this); }
public boolean isValueBreak() {
return target != null && target.hasTag(SWITCH_EXPRESSION);
}
@DefinedBy(Api.COMPILER_TREE)
public Kind getKind() { return Kind.BREAK; }
@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)
public <R,D> R accept(TreeVisitor<R,D> v, D 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) */
public static class LetExpr extends JCExpression {
public List<JCVariableDecl> defs;
public List<JCStatement> defs;
public JCExpression expr;
protected LetExpr(List<JCVariableDecl> defs, JCExpression expr) {
protected LetExpr(List<JCStatement> defs, JCExpression expr) {
this.defs = defs;
this.expr = expr;
}
@ -2994,7 +3067,9 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
JCEnhancedForLoop ForeachLoop(JCVariableDecl var, JCExpression expr, JCStatement body);
JCLabeledStatement Labelled(Name label, JCStatement body);
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);
JCTry Try(JCBlock body, List<JCCatch> catchers, JCBlock finalizer);
JCTry Try(List<JCTree> resources,
@ -3007,7 +3082,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
JCExpression elsepart);
JCIf If(JCExpression cond, JCStatement thenpart, JCStatement elsepart);
JCExpressionStatement Exec(JCExpression expr);
JCBreak Break(Name label);
JCBreak Break(JCExpression value);
JCContinue Continue(Name label);
JCReturn Return(JCExpression expr);
JCThrow Throw(JCExpression expr);
@ -3049,7 +3124,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
JCProvides Provides(JCExpression serviceName, List<JCExpression> implNames);
JCRequires Requires(boolean isTransitive, boolean isStaticPhase, 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.
@ -3070,6 +3145,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public void visitLabelled(JCLabeledStatement that) { visitTree(that); }
public void visitSwitch(JCSwitch 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 visitTry(JCTry 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 com.sun.source.tree.CaseTree.CaseKind;
import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.source.tree.ModuleTree.ModuleKind;
import com.sun.tools.javac.code.*;
@ -835,18 +836,43 @@ public class Pretty extends JCTree.Visitor {
public void visitCase(JCCase tree) {
try {
if (tree.pat == null) {
if (tree.pats.isEmpty()) {
print("default");
} else {
print("case ");
printExpr(tree.pat);
printExprs(tree.pats);
}
print(": ");
if (tree.caseKind == JCCase.STATEMENT) {
print(":");
println();
indent();
printStats(tree.stats);
undent();
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();
indent();
printStats(tree.stats);
undent();
printStats(tree.cases);
align();
print("}");
} catch (IOException e) {
throw new UncheckedIOException(e);
}
@ -956,7 +982,7 @@ public class Pretty extends JCTree.Visitor {
public void visitBreak(JCBreak tree) {
try {
print("break");
if (tree.label != null) print(" " + tree.label);
if (tree.value != null) print(" " + tree.value);
print(";");
} catch (IOException e) {
throw new UncheckedIOException(e);

View File

@ -140,15 +140,17 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
@DefinedBy(Api.COMPILER_TREE)
public JCTree visitBreak(BreakTree node, P p) {
JCBreak t = (JCBreak) node;
return M.at(t.pos).Break(t.label);
JCExpression value = copy(t.value, p);
return M.at(t.pos).Break(value);
}
@DefinedBy(Api.COMPILER_TREE)
public JCTree visitCase(CaseTree node, P p) {
JCCase t = (JCCase) node;
JCExpression pat = copy(t.pat, p);
List<JCExpression> pats = copy(t.pats, 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)
@ -370,6 +372,15 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
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)
public JCTree visitSynchronized(SynchronizedTree node, P p) {
JCSynchronized t = (JCSynchronized) node;
@ -559,7 +570,7 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
switch (tree.getTag()) {
case LETEXPR: {
LetExpr t = (LetExpr) node;
List<JCVariableDecl> defs = copy(t.defs, p);
List<JCStatement> defs = copy(t.defs, p);
JCExpression expr = copy(t.expr, p);
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 com.sun.source.tree.CaseTree.CaseKind;
import com.sun.source.tree.ModuleTree.ModuleKind;
import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.code.*;
@ -273,8 +274,15 @@ public class TreeMaker implements JCTree.Factory {
return tree;
}
public JCCase Case(JCExpression pat, List<JCStatement> stats) {
JCCase tree = new JCCase(pat, stats);
public JCCase Case(@SuppressWarnings("removal") CaseKind caseKind, List<JCExpression> pats,
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;
return tree;
}
@ -325,7 +333,7 @@ public class TreeMaker implements JCTree.Factory {
return tree;
}
public JCBreak Break(Name label) {
public JCBreak Break(JCExpression label) {
JCBreak tree = new JCBreak(label, null);
tree.pos = pos;
return tree;
@ -599,7 +607,7 @@ public class TreeMaker implements JCTree.Factory {
return tree;
}
public LetExpr LetExpr(List<JCVariableDecl> defs, JCExpression expr) {
public LetExpr LetExpr(List<JCStatement> defs, JCExpression expr) {
LetExpr tree = new LetExpr(defs, expr);
tree.pos = pos;
return tree;

View File

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

View File

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

View File

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

View File

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

View File

@ -247,7 +247,7 @@ class CompletenessAnalyzer {
FOR(TokenKind.FOR, XSTMT1|XSTART), // for
IF(TokenKind.IF, XSTMT1|XSTART), // if
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
THROW(TokenKind.THROW, XSTMT1|XSTART), // throw
TRY(TokenKind.TRY, XSTMT1|XSTART), // try

View File

@ -185,7 +185,12 @@ public class CompletenessTest extends KullaTesting {
"int n,",
"int[] m = {1, 2},",
"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[] {

View File

@ -65,6 +65,13 @@ public class Diagnostics implements javax.tools.DiagnosticListener<JavaFileObjec
.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 */
public List<String> errorKeys() {
return diags.stream()

View File

@ -178,6 +178,14 @@ public abstract class JavacTemplateTestBase {
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,
* 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 */
protected void assertCompileFailed(String message) {
protected void assertCompileFailed(String key) {
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 */

View File

@ -4,13 +4,17 @@
* @summary The compiler was allowing void types in its parsing of conditional expressions.
* @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 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.
System.out.println(o instanceof String ? o.hashCode() : o.wait());
// Should fail to compile since Object.wait() has a void return type. Standalone case.
(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:14:30: compiler.err.neither.conditional.subtype: java.lang.Integer, void
2 errors
ConditionalWithVoid.java:14:53: compiler.err.void.not.allowed.here
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.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.LetExpr;
import com.sun.tools.javac.tree.JCTree.Tag;
@ -327,8 +328,8 @@ public class BoxingAndSuper {
if (tree.hasTag(Tag.LETEXPR)) {
LetExpr le = (LetExpr) tree;
for (JCVariableDecl var : le.defs) {
letExprRemap.put(var.name.toString(), "$le" + i++);
for (JCStatement var : le.defs) {
letExprRemap.put(((JCVariableDecl) var).name.toString(), "$le" + i++);
}
}
return super.visitOther(node, p);

View File

@ -302,6 +302,7 @@ public class CheckResourceKeys {
// prefix/embedded strings
"compiler.",
"compiler.misc.",
"compiler.misc.tree.tag.",
"opt.Xlint.desc.",
"count.",
"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.
*
* This code is free software; you can redistribute it and/or modify it
@ -21,14 +21,14 @@
* questions.
*/
// key: compiler.err.neither.conditional.subtype
// key: compiler.err.switch.null.not.allowed
class NeitherConditionalSubtype {
public int test(boolean cond, Object o) {
// Should fail to compile since Object.wait() has a void return type.
(o instanceof String ? o.hashCode() : o.wait()).toString();
return 0;
class SwitchNullNotAllowed {
void test(Integer i) {
switch (i) {
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.tree.EndPosTable;
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.JCImport;
import com.sun.tools.javac.tree.TreeInfo;
@ -446,6 +447,12 @@ public class CheckAttributedTree {
scan(tree.defs);
}
@Override
public void visitBreak(JCBreak tree) {
if (tree.isValueBreak())
super.visitBreak(tree);
}
JavaFileObject sourcefile;
EndPosTable endPosTable;
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
public void visitCase(JCCase tree) {
printTree("pat", tree.pat);
printList("pat", tree.pats);
printList("stats", tree.stats);
}
@ -782,7 +782,7 @@ public class DPrinter {
@Override
public void visitBreak(JCBreak tree) {
printName("label", tree.label);
printTree("value", tree.value);
}
@Override

View File

@ -82,6 +82,9 @@ import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import com.sun.source.tree.CaseTree;
import com.sun.source.util.TreePathScanner;
public class JavacParserTest extends TestCase {
static final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
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);
}
@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
void testTypeParamsWithoutMethod() throws IOException {
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