OpenJDK / amber / amber
changeset 53705:bc7ce655824b amber-demo-II
Automatic merge with patterns-stage-1
author | mcimadamore |
---|---|
date | Thu, 29 Nov 2018 08:16:17 +0100 |
parents | cf8bb0debfad 8927cb6ca12c |
children | 4b7f11a2072c |
files | |
diffstat | 55 files changed, 2450 insertions(+), 82 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/BindingPatternTree.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,48 @@ +/* + * 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. 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 javax.lang.model.element.Name; + +/** + * A binding pattern tree + */ +public interface BindingPatternTree extends PatternTree { + + /** + * Returns the type of the bind variable. + * @return the type + */ + Tree getType(); + + /** + * A binding variable name. + * @return something + */ + Name getBinding(); + +} +
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java Thu Nov 29 08:16:17 2018 +0100 @@ -51,4 +51,10 @@ * @return the type */ Tree getType(); + + /** + * Returns the tested pattern. + * @return the tested pattern + */ + PatternTree getPattern(); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/PatternTree.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,35 @@ +/* + * 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. 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 javax.lang.model.element.Name; + +/** + * A super-type for all the patterns. + */ +public interface PatternTree extends Tree { + +}
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java Thu Nov 29 08:16:17 2018 +0100 @@ -220,6 +220,11 @@ PARENTHESIZED(ParenthesizedTree.class), /** + * Used for instances of {@link BindingPatternTree}. + */ + BINDING_PATTERN(BindingPatternTree.class), + + /** * Used for instances of {@link PrimitiveTypeTree}. */ PRIMITIVE_TYPE(PrimitiveTypeTree.class),
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java Thu Nov 29 08:16:17 2018 +0100 @@ -258,6 +258,14 @@ R visitLiteral(LiteralTree node, P p); /** + * Visits an BindingPattern node. + * @param node the node being visited + * @param p a parameter value + * @return a result value + */ + R visitBindingPattern(BindingPatternTree node, P p); + + /** * Visits a MethodTree node. * @param node the node being visited * @param p a parameter value
--- a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java Thu Nov 29 08:16:17 2018 +0100 @@ -566,6 +566,18 @@ * @return the result of {@code defaultAction} */ @Override + public R visitBindingPattern(BindingPatternTree node, P p) { + return defaultAction(node, p); + } + + /** + * {@inheritDoc} This implementation calls {@code defaultAction}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code defaultAction} + */ + @Override public R visitArrayAccess(ArrayAccessTree node, P p) { return defaultAction(node, p); }
--- a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java Thu Nov 29 08:16:17 2018 +0100 @@ -677,7 +677,11 @@ @Override public R visitInstanceOf(InstanceOfTree node, P p) { R r = scan(node.getExpression(), p); - r = scanAndReduce(node.getType(), p, r); + if (node.getPattern() != null) { + r = scanAndReduce(node.getPattern(), p, r); + } else { + r = scanAndReduce(node.getType(), p, r); + } return r; } @@ -689,6 +693,18 @@ * @return the result of scanning */ @Override + public R visitBindingPattern(BindingPatternTree node, P p) { + return scan(node.getType(), p); + } + + /** + * {@inheritDoc} This implementation scans the children in left to right order. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of scanning + */ + @Override public R visitArrayAccess(ArrayAccessTree node, P p) { R r = scan(node.getExpression(), p); r = scanAndReduce(node.getIndex(), p, r);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java Thu Nov 29 08:16:17 2018 +0100 @@ -319,6 +319,16 @@ */ public static final long BODY_ONLY_FINALIZE = 1L<<17; //blocks only + /** + * Flag to indicate the given variable is a match binding variable. + */ + public static final long MATCH_BINDING = 1L<<58; + + /** + * A flag to indicate a match binding variable whose scope extends after the current statement. + */ + public static final long MATCH_BINDING_TO_OUTER = 1L<<59; + /** Modifier masks. */ public static final int
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Thu Nov 29 08:16:17 2018 +0100 @@ -49,6 +49,7 @@ import com.sun.tools.javac.comp.ArgumentAttr.LocalCacheContext; import com.sun.tools.javac.comp.Check.CheckContext; import com.sun.tools.javac.comp.DeferredAttr.AttrMode; +import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol; import com.sun.tools.javac.jvm.*; import static com.sun.tools.javac.resources.CompilerProperties.Fragments.Diamond; import static com.sun.tools.javac.resources.CompilerProperties.Fragments.DiamondInvalidArg; @@ -75,6 +76,8 @@ import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.code.TypeTag.WILDCARD; import com.sun.tools.javac.comp.Analyzer.AnalyzerMode; +import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol; +import com.sun.tools.javac.tree.JCTree.JCBreak; import static com.sun.tools.javac.tree.JCTree.Tag.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; @@ -293,6 +296,8 @@ isAssignableAsBlankFinal(v, env)))) { if (v.isResourceVariable()) { //TWR resource log.error(pos, Errors.TryResourceMayNotBeAssigned(v)); + } else if ((v.flags() & MATCH_BINDING) != 0) { + log.error(pos, Errors.PatternBindingMayNotBeAssigned(v)); } else { log.error(pos, Errors.CantAssignValToFinalVar(v)); } @@ -1315,28 +1320,76 @@ public void visitDoLoop(JCDoWhileLoop tree) { attribStat(tree.body, env.dup(tree)); attribExpr(tree.cond, env, syms.booleanType); + if (!breaksOutOf(tree, tree.body)) { + List<BindingSymbol> bindings = getMatchBindings(types, log, tree.cond, false); + + bindings.forEach(env.info.scope::enter); + bindings.forEach(BindingSymbol::preserveBinding); + } result = null; } public void visitWhileLoop(JCWhileLoop tree) { attribExpr(tree.cond, env, syms.booleanType); - attribStat(tree.body, env.dup(tree)); + // include x.T in while's body + Env<AttrContext> whileEnv = bindingEnv(env, getMatchBindings(types, log, tree.cond, true)); + try { + attribStat(tree.body, whileEnv.dup(tree)); + } finally { + whileEnv.info.scope.leave(); + } + if (!breaksOutOf(tree, tree.body)) { + List<BindingSymbol> bindings = getMatchBindings(types, log, tree.cond, false); + + bindings.forEach(env.info.scope::enter); + bindings.forEach(BindingSymbol::preserveBinding); + } result = null; } + private boolean breaksOutOf(JCTree loop, JCTree body) { + //TODO: should correctly reflect liveness: + boolean[] breaksOut = new boolean[1]; + new TreeScanner() { + @Override + public void visitBreak(JCBreak tree) { + breaksOut[0] |= tree.target == loop; + super.visitBreak(tree); + } + }.scan(body); + + return breaksOut[0]; + } + public void visitForLoop(JCForLoop tree) { Env<AttrContext> loopEnv = env.dup(env.tree, env.info.dup(env.info.scope.dup())); try { attribStats(tree.init, loopEnv); - if (tree.cond != null) attribExpr(tree.cond, loopEnv, syms.booleanType); - loopEnv.tree = tree; // before, we were not in loop! - attribStats(tree.step, loopEnv); - attribStat(tree.body, loopEnv); + List<BindingSymbol> matchBindings = List.nil(); + if (tree.cond != null) { + attribExpr(tree.cond, loopEnv, syms.booleanType); + // include x.T in the evaluation scopes of body & step. + matchBindings = getMatchBindings(types, log, tree.cond, true); + } + Env<AttrContext> bodyEnv = bindingEnv(loopEnv, matchBindings); + try { + bodyEnv.tree = tree; // before, we were not in loop! + attribStats(tree.step, bodyEnv); + attribStat(tree.body, bodyEnv); + } finally { + bodyEnv.info.scope.leave(); + } + if (!breaksOutOf(tree, tree.body)) { + List<BindingSymbol> bindings = getMatchBindings(types, log, tree.cond, false); + + bindings.forEach(env.info.scope::enter); + bindings.forEach(BindingSymbol::preserveBinding); + } result = null; } finally { - loopEnv.info.scope.leave(); + loopEnv.info.scope.leave(); // all injected match bindings vanish here. } } @@ -1688,8 +1741,26 @@ unknownExprInfo : resultInfo.dup(conditionalContext(resultInfo.checkContext)); - Type truetype = attribTree(tree.truepart, env, condInfo); - Type falsetype = attribTree(tree.falsepart, env, condInfo); + /* if e = "x ? y : z", then: + include x.T in y + include x.F in z + */ + + Type truetype; + Env<AttrContext> trueEnv = bindingEnv(env, getMatchBindings(types, log, tree.cond, true)); + try { + truetype = attribTree(tree.truepart, trueEnv, condInfo); + } finally { + trueEnv.info.scope.leave(); + } + + Type falsetype; + Env<AttrContext> falseEnv = bindingEnv(env, getMatchBindings(types, log, tree.cond, false)); + try { + falsetype = attribTree(tree.falsepart, falseEnv, condInfo); + } finally { + falseEnv.info.scope.leave(); + } Type owntype = (tree.polyKind == PolyKind.STANDALONE) ? condType(List.of(tree.truepart.pos(), tree.falsepart.pos()), @@ -1844,15 +1915,77 @@ BOOLEAN, }; + Env<AttrContext> bindingEnv(Env<AttrContext> env, List<BindingSymbol> bindings) { + Env<AttrContext> env1 = env.dup(env.tree, env.info.dup(env.info.scope.dup())); + bindings.forEach(env1.info.scope::enter); + return env1; + } + public void visitIf(JCIf tree) { attribExpr(tree.cond, env, syms.booleanType); - attribStat(tree.thenpart, env); - if (tree.elsepart != null) - attribStat(tree.elsepart, env); + + // if (x) { y } [ else z ] include x.T in y; include x.F in z + + List<BindingSymbol> thenBindings = getMatchBindings(types, log, tree.cond, true); + Env<AttrContext> thenEnv = bindingEnv(env, thenBindings); + + try { + attribStat(tree.thenpart, thenEnv); + } finally { + thenEnv.info.scope.leave(); + } + + preFlow(tree.thenpart); + boolean aliveAfterThen = flow.aliveAfter(env, tree.thenpart, make); + boolean aliveAfterElse; + List<BindingSymbol> elseBindings = List.nil(); + + if (tree.elsepart != null) { + elseBindings = getMatchBindings(types, log, tree.cond, false); + + Env<AttrContext> elseEnv = bindingEnv(env, elseBindings); + try { + attribStat(tree.elsepart, elseEnv); + } finally { + elseEnv.info.scope.leave(); + } + preFlow(tree.elsepart); + aliveAfterElse = flow.aliveAfter(env, tree.elsepart, make); + } else { + aliveAfterElse = true; + } + chk.checkEmptyIf(tree); + + List<BindingSymbol> afterIfBindings = List.nil(); + + if (aliveAfterThen && !aliveAfterElse) { + afterIfBindings = thenBindings; + } else if (aliveAfterElse && !aliveAfterThen) { + afterIfBindings = elseBindings; + } + + afterIfBindings.forEach(env.info.scope::enter); + afterIfBindings.forEach(BindingSymbol::preserveBinding); + result = null; } + void preFlow(JCTree tree) { + new PostAttrAnalyzer() { + @Override + public void scan(JCTree tree) { + if (tree == null || + (tree.type != null && + tree.type == Type.stuckType)) { + //don't touch stuck expressions! + return; + } + super.scan(tree); + } + }.scan(tree); + } + public void visitExec(JCExpressionStatement tree) { //a fresh environment is required for 292 inference to work properly --- //see Infer.instantiatePolymorphicSignatureInstance() @@ -3582,7 +3715,27 @@ public void visitBinary(JCBinary tree) { // Attribute arguments. Type left = chk.checkNonVoid(tree.lhs.pos(), attribExpr(tree.lhs, env)); - Type right = chk.checkNonVoid(tree.rhs.pos(), attribExpr(tree.rhs, env)); + // If e = "x && y", then, include x.T in y; If e = "x || y", then, include x.F in y + List<BindingSymbol> matchBindings; + switch (tree.getTag()) { + case AND: + matchBindings = getMatchBindings(types, log, tree.lhs, true); + break; + case OR: + matchBindings = getMatchBindings(types, log, tree.lhs, false); + break; + default: + matchBindings = List.nil(); + break; + } + Env<AttrContext> rhsEnv = bindingEnv(env, matchBindings); + Type right; + try { + right = chk.checkNonVoid(tree.rhs.pos(), attribExpr(tree.rhs, rhsEnv)); + } finally { + rhsEnv.info.scope.leave(); + } + // Find operator. Symbol operator = tree.operator = operators.resolveBinary(tree, tree.getTag(), left, right); Type owntype = types.createErrorType(tree.type); @@ -3648,19 +3801,43 @@ public void visitTypeTest(JCInstanceOf tree) { Type exprtype = chk.checkNullOrRefType( tree.expr.pos(), attribExpr(tree.expr, env)); - Type clazztype = attribType(tree.clazz, env); - if (!clazztype.hasTag(TYPEVAR)) { - clazztype = chk.checkClassOrArrayType(tree.clazz.pos(), clazztype); - } - if (!clazztype.isErroneous() && !types.isReifiable(clazztype)) { - log.error(tree.clazz.pos(), Errors.IllegalGenericTypeForInstof); - clazztype = types.createErrorType(clazztype); - } - chk.validate(tree.clazz, env, false); + Type clazztype; + if (tree.pattern.getTag() == BINDINGPATTERN) { + attribTree(tree.pattern, env, unknownExprInfo); + clazztype = tree.pattern.type; + if (!clazztype.hasTag(TYPEVAR)) { + clazztype = chk.checkClassOrArrayType(tree.pattern.pos(), clazztype); + } + } else { + clazztype = attribType(tree.pattern, env); + if (!clazztype.hasTag(TYPEVAR)) { + clazztype = chk.checkClassOrArrayType(tree.pattern.pos(), clazztype); + } + if (!clazztype.isErroneous() && !types.isReifiable(clazztype)) { + log.error(tree.pattern.pos(), Errors.IllegalGenericTypeForInstof); + clazztype = types.createErrorType(clazztype); + } + chk.validate(tree.pattern, env, false); + } chk.checkCastable(tree.expr.pos(), exprtype, clazztype); result = check(tree, syms.booleanType, KindSelector.VAL, resultInfo); } + public void visitBindingPattern(JCBindingPattern tree) { + if (tree.vartype != null) { + ResultInfo varInfo = new ResultInfo(KindSelector.TYP, resultInfo.pt, resultInfo.checkContext); + tree.type = attribTree(tree.vartype, env, varInfo); + } else { + tree.type = resultInfo.pt; + } + VarSymbol v = tree.symbol = new BindingSymbol(tree.name, tree.vartype != null ? tree.vartype.type : (tree.type.hasTag(BOT) ? syms.objectType : tree.type), env.info.scope.owner); + if (chk.checkUnique(tree.pos(), v, env.info.scope)) { + chk.checkTransparentVar(tree.pos(), v, env.info.scope); + // env.info.scope.enter(v); // we inject into scopes expressly at various points. + } + result = tree.type; + } + public void visitIndexed(JCArrayAccess tree) { Type owntype = types.createErrorType(tree.type); Type atype = attribExpr(tree.indexed, env); @@ -5051,8 +5228,8 @@ super.visitTypeCast(tree); } public void visitTypeTest(JCInstanceOf tree) { - if (tree.clazz != null && tree.clazz.type != null) - validateAnnotatedType(tree.clazz, tree.clazz.type); + if (tree.pattern != null && !(tree.pattern instanceof JCPattern) && tree.pattern.type != null) + validateAnnotatedType(tree.pattern, tree.pattern.type); super.visitTypeTest(tree); } public void visitNewClass(JCNewClass tree) { @@ -5313,6 +5490,16 @@ } @Override + public void visitBindingPattern(JCBindingPattern that) { + //initTypeIfNeeded(that); + if (that.symbol == null) { + that.symbol = new BindingSymbol(that.name, that.type, syms.noSymbol); + that.symbol.adr = 0; + } + super.visitBindingPattern(that); + } + + @Override public void visitNewClass(JCNewClass that) { if (that.constructor == null) { that.constructor = new MethodSymbol(0, names.init, @@ -5379,4 +5566,12 @@ }.scan(pid); } + + public static List<BindingSymbol> getMatchBindings(Types types, Log log, JCTree expression, boolean whenTrue) { + return getMatchBindings(types, log, expression, whenTrue, null); + } + + public static List<BindingSymbol> getMatchBindings(Types types, Log log, JCTree expression, boolean whenTrue, List<BindingSymbol> intersectWith) { + return new MatchBindingsComputer(types, log, expression, whenTrue).getBindings(intersectWith); + } }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java Thu Nov 29 08:16:17 2018 +0100 @@ -60,8 +60,9 @@ FLOW(5), TRANSTYPES(6), UNLAMBDA(7), - LOWER(8), - GENERATE(9); + TRANSPATTERNS(8), + LOWER(9), + GENERATE(10); CompileState(int value) { this.value = value;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java Thu Nov 29 08:16:17 2018 +0100 @@ -255,6 +255,23 @@ } } + public boolean aliveAfter(Env<AttrContext> env, JCTree that, TreeMaker make) { + //we need to disable diagnostics temporarily; the problem is that if + //a lambda expression contains e.g. an unreachable statement, an error + //message will be reported and will cause compilation to skip the flow analyis + //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis + //related errors, which will allow for more errors to be detected + Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); + try { + SnippetAliveAnalyzer analyzer = new SnippetAliveAnalyzer(); + + analyzer.analyzeTree(env, that, make); + return analyzer.isAlive(); + } finally { + log.popDiagnosticHandler(diagHandler); + } + } + /** * Definite assignment scan mode */ @@ -517,7 +534,7 @@ while (exits.nonEmpty()) { PendingExit exit = exits.head; exits = exits.tail; - Assert.check(exit.tree.hasTag(RETURN)); + Assert.check(exit.tree.hasTag(RETURN), () -> exit.tree.toString()); } } finally { lint = lintPrev; @@ -1434,6 +1451,19 @@ } /** + * Determine if alive after the given tree. + */ + class SnippetAliveAnalyzer extends AliveAnalyzer { + @Override + public void visitClassDef(JCClassDecl tree) { + //skip + } + public boolean isAlive() { + return super.alive; + } + } + + /** * Specialized pass that performs DA/DU on a lambda */ class LambdaAssignAnalyzer extends AssignAnalyzer { @@ -1630,7 +1660,7 @@ */ protected boolean trackable(VarSymbol sym) { return - sym.pos >= startPos && + sym.pos >= startPos && ((sym.flags() & MATCH_BINDING) == 0) && ((sym.owner.kind == MTH || isFinalUninitializedField(sym))); } @@ -2564,6 +2594,11 @@ // Do nothing for modules } + // TODO: 2017-02-02 JUST TO ALLOW THINGS TO CONTINUE + public void visitTypeTestPattern(JCBindingPattern tree) { + // Do nothing + } + /************************************************************************** * main method *************************************************************************/ @@ -2764,6 +2799,11 @@ // Do nothing for modules } + // TODO: 2017-02-02 JUST TO ALLOW THINGS TO CONTINUE + public void visitTypeTestPattern(JCBindingPattern tree) { + // Do nothing + } + /************************************************************************** * main method *************************************************************************/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MatchBindingsComputer.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,239 @@ +/* + * 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. 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.tools.javac.comp; + +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.Symbol.VarSymbol; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Types; +import com.sun.tools.javac.resources.CompilerProperties.Errors; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCBinary; +import com.sun.tools.javac.tree.JCTree.JCConditional; +import com.sun.tools.javac.tree.JCTree.JCUnary; +import com.sun.tools.javac.tree.JCTree.JCBindingPattern; +import com.sun.tools.javac.tree.TreeScanner; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Log; +import com.sun.tools.javac.util.Name; + + +public class MatchBindingsComputer extends TreeScanner { + + private final JCTree tree; + private final Log log; + private final Types types; + boolean whenTrue; + List<BindingSymbol> bindings; + + public MatchBindingsComputer(Types types, Log log, JCTree tree, boolean whenTrue) { + this.tree = tree; + this.whenTrue = whenTrue; + this.log = log; + this.types = types; + } + + @Override + public void visitBindingPattern(JCBindingPattern tree) { + bindings = whenTrue ? List.of(tree.symbol) : List.nil(); + } + + @Override + public void visitBinary(JCBinary tree) { + switch (tree.getTag()) { + case AND: + // e.T = union(x.T, y.T) + // e.F = intersection(x.F, y.F) + scan(tree.lhs); + List<BindingSymbol> lhsBindings = bindings; + scan(tree.rhs); + List<BindingSymbol> rhsBindings = bindings; + bindings = whenTrue ? union(tree, lhsBindings, rhsBindings) : intersection(tree, lhsBindings, rhsBindings); + break; + case OR: + // e.T = intersection(x.T, y.T) + // e.F = union(x.F, y.F) + scan(tree.lhs); + lhsBindings = bindings; + scan(tree.rhs); + rhsBindings = bindings; + bindings = whenTrue ? intersection(tree, lhsBindings, rhsBindings) : union(tree, lhsBindings, rhsBindings); + break; + default: + super.visitBinary(tree); + break; + } + } + + @Override + public void visitUnary(JCUnary tree) { + switch (tree.getTag()) { + case NOT: + // e.T = x.F // flip 'em + // e.F = x.T + whenTrue = !whenTrue; + scan(tree.arg); + whenTrue = !whenTrue; + break; + default: + super.visitUnary(tree); + break; + } + } + + @Override + public void visitConditional(JCConditional tree) { + /* if e = "x ? y : z", then: + e.T = union(intersect(y.T, z.T), intersect(x.T, z.T), intersect(x.F, y.T)) + e.F = union(intersect(y.F, z.F), intersect(x.T, z.F), intersect(x.F, y.F)) + */ + if (whenTrue) { + List<BindingSymbol> xT, yT, zT, xF; + scan(tree.cond); + xT = bindings; + scan(tree.truepart); + yT = bindings; + scan(tree.falsepart); + zT = bindings; + whenTrue = false; + scan(tree.cond); + xF = bindings; + whenTrue = true; + bindings = union(tree, intersection(tree, yT, zT), intersection(tree, xT, zT), intersection(tree, xF, yT)); + } else { + List<BindingSymbol> xF, yF, zF, xT; + scan(tree.cond); + xF = bindings; + scan(tree.truepart); + yF = bindings; + scan(tree.falsepart); + zF = bindings; + whenTrue = true; + scan(tree.cond); + xT = bindings; + whenTrue = false; + bindings = union(tree, intersection(tree, yF, zF), intersection(tree, xT, zF), intersection(tree, xF, yF)); + } + } + + private List<BindingSymbol> intersection(JCTree tree, List<BindingSymbol> lhsBindings, List<BindingSymbol> rhsBindings) { + // It is an error if, for intersection(a,b), if a and b contain the same variable name but with different types. + List<BindingSymbol> list = List.nil(); + for (BindingSymbol v1 : lhsBindings) { + for (BindingSymbol v2 : rhsBindings) { + if (v1.name == v2.name) { + if (types.isSameType(v1.type, v2.type)) { + list = list.append(new IntersectionBindingSymbol(List.of(v1, v2))); + } else { + log.error(tree.pos(), Errors.MatchBindingExistsWithDifferentType); + } + } + } + } + return list; + } + + @SafeVarargs + private final List<BindingSymbol> union(JCTree tree, List<BindingSymbol> lhsBindings, List<BindingSymbol> ... rhsBindings_s) { + // It is an error if for union(a,b), a and b contain the same name (disjoint union). + List<BindingSymbol> list = lhsBindings; + for (List<BindingSymbol> rhsBindings : rhsBindings_s) { + for (BindingSymbol v : rhsBindings) { + for (BindingSymbol ov : list) { + if (ov.name == v.name) { + log.error(tree.pos(), Errors.MatchBindingExists); + } + } + list = list.append(v); + } + } + return list; + } + + @Override + public void scan(JCTree tree) { + bindings = List.nil(); + super.scan(tree); + } + + public List<BindingSymbol> getBindings(List<BindingSymbol> intersectWith) { + scan(tree); + if (intersectWith != null) { + bindings = intersection(tree, intersectWith, bindings); + } + return bindings; + } + + public static class BindingSymbol extends VarSymbol { + + public BindingSymbol(Name name, Type type, Symbol owner) { + super(Flags.FINAL | Flags.HASINIT | Flags.MATCH_BINDING, name, type, owner); + } + + public boolean isAliasFor(BindingSymbol b) { + return aliases().containsAll(b.aliases()); + } + + List<BindingSymbol> aliases() { + return List.of(this); + } + + public void preserveBinding() { + flags_field |= Flags.MATCH_BINDING_TO_OUTER; + } + + public boolean isPreserved() { + return (flags_field & Flags.MATCH_BINDING_TO_OUTER) != 0; + } + } + + public static class IntersectionBindingSymbol extends BindingSymbol { + + List<BindingSymbol> aliases = List.nil(); + + public IntersectionBindingSymbol(List<BindingSymbol> aliases) { + super(aliases.head.name, aliases.head.type, aliases.head.owner); + this.aliases = aliases.stream() + .flatMap(b -> b.aliases().stream()) + .collect(List.collector()); + } + + @Override + List<BindingSymbol> aliases() { + return aliases; + } + + @Override + public void preserveBinding() { + aliases.stream().forEach(BindingSymbol::preserveBinding); + } + + public boolean isPreserved() { + return aliases.stream().allMatch(BindingSymbol::isPreserved); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,445 @@ +/* + * 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. 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.tools.javac.comp; + +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Symbol.VarSymbol; +import com.sun.tools.javac.code.Symtab; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Types; +import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCAssign; +import com.sun.tools.javac.tree.JCTree.JCBinary; +import com.sun.tools.javac.tree.JCTree.JCConditional; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCForLoop; +import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCIf; +import com.sun.tools.javac.tree.JCTree.JCInstanceOf; +import com.sun.tools.javac.tree.JCTree.JCLabeledStatement; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.tree.JCTree.JCBindingPattern; +import com.sun.tools.javac.tree.JCTree.JCWhileLoop; +import com.sun.tools.javac.tree.JCTree.Tag; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.tree.TreeTranslator; +import com.sun.tools.javac.util.Assert; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Log; +import com.sun.tools.javac.util.Names; +import com.sun.tools.javac.util.Options; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import com.sun.tools.javac.code.Symbol.MethodSymbol; +import static com.sun.tools.javac.code.TypeTag.BOOLEAN; +import static com.sun.tools.javac.code.TypeTag.BOT; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCStatement; + +/** + * This pass translates pattern-matching constructs, such as instanceof <pattern>. + */ +public class TransPatterns extends TreeTranslator { + + protected static final Context.Key<TransPatterns> transPatternsKey = new Context.Key<>(); + + public static TransPatterns instance(Context context) { + TransPatterns instance = context.get(transPatternsKey); + if (instance == null) + instance = new TransPatterns(context); + return instance; + } + + private Symtab syms; + private TreeMaker make; + private Types types; + private Operators operators; + private Log log; + private ConstFold constFold; + private Names names; + + BindingContext bindingContext = new BindingContext() { + @Override + VarSymbol getBindingFor(BindingSymbol varSymbol) { + return null; + } + + @Override + JCStatement decorateStatement(JCStatement stat) { + return stat; + } + + @Override + JCExpression decorateExpression(JCExpression expr) { + return expr; + } + + @Override + BindingContext pop() { + //do nothing + return this; + } + + @Override + boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) { + return false; + } + }; + + JCLabeledStatement pendingMatchLabel = null; + + boolean debugTransPatterns; + + private MethodSymbol currentMethodSym = null; + + protected TransPatterns(Context context) { + context.put(transPatternsKey, this); + syms = Symtab.instance(context); + make = TreeMaker.instance(context); + types = Types.instance(context); + operators = Operators.instance(context); + log = Log.instance(context); + constFold = ConstFold.instance(context); + names = Names.instance(context); + debugTransPatterns = Options.instance(context).isSet("debug.patterns"); + } + + @Override + public void visitTypeTest(JCInstanceOf tree) { + if (tree.pattern.hasTag(Tag.BINDINGPATTERN)) { + JCBindingPattern patt = (JCBindingPattern)tree.pattern; + VarSymbol pattSym = patt.symbol; + Type tempType = tree.expr.type.hasTag(BOT) ? + syms.objectType + : tree.expr.type; + VarSymbol temp = new VarSymbol(pattSym.flags(), + pattSym.name.append(names.fromString("$temp")), + tempType, + patt.symbol.owner); + JCExpression translatedExpr = translate(tree.expr); + Type castTargetType = types.boxedTypeOrType(pattSym.erasure(types)); + if (patt.vartype == null || tree.expr.type.isPrimitive()) { + result = make.Literal(BOOLEAN,1).setType(syms.booleanType); + } else { + result = makeTypeTest(make.Ident(temp), make.Type(castTargetType)); + } + + VarSymbol bindingVar = bindingContext.getBindingFor(patt.symbol); + if (bindingVar != null) { + JCAssign fakeInit = (JCAssign)make.at(tree.pos).Assign( + make.Ident(bindingVar), convert(make.Ident(temp), castTargetType)).setType(bindingVar.erasure(types)); + result = makeBinary(Tag.AND, (JCExpression)result, + makeBinary(Tag.EQ, fakeInit, convert(make.Ident(temp), castTargetType))); + } + result = make.at(tree.pos).LetExpr(make.VarDef(temp, translatedExpr), (JCExpression)result).setType(syms.booleanType); + } else { + super.visitTypeTest(tree); + } + } + + @Override + public void visitBinary(JCBinary tree) { + List<BindingSymbol> matchBindings; + switch (tree.getTag()) { + case AND: + matchBindings = Attr.getMatchBindings(types, log, tree.lhs, true); + break; + case OR: + matchBindings = Attr.getMatchBindings(types, log, tree.lhs, false); + break; + default: + matchBindings = List.nil(); + break; + } + + bindingContext = new BasicBindingContext(matchBindings); + try { + super.visitBinary(tree); + result = bindingContext.decorateExpression(tree); + } finally { + bindingContext.pop(); + } + } + + @Override + public void visitConditional(JCConditional tree) { + bindingContext = new BasicBindingContext( + Attr.getMatchBindings(types, log, tree.cond, true) + .appendList(Attr.getMatchBindings(types, log, tree.cond, false))); + try { + super.visitConditional(tree); + result = bindingContext.decorateExpression(tree); + } finally { + bindingContext.pop(); + } + } + + @Override + public void visitIf(JCIf tree) { + bindingContext = new BasicBindingContext( + Attr.getMatchBindings(types, log, tree.cond, true) + .appendList(Attr.getMatchBindings(types, log, tree.cond, false))); + try { + super.visitIf(tree); + result = bindingContext.decorateStatement(tree); + } finally { + bindingContext.pop(); + } + } + + @Override + public void visitForLoop(JCForLoop tree) { + bindingContext = new BasicBindingContext(Attr.getMatchBindings(types, log, tree.cond, true)); + try { + super.visitForLoop(tree); + result = bindingContext.decorateStatement(tree); + } finally { + bindingContext.pop(); + } + } + + @Override + public void visitWhileLoop(JCWhileLoop tree) { + bindingContext = new BasicBindingContext(Attr.getMatchBindings(types, log, tree.cond, true)); + try { + super.visitWhileLoop(tree); + result = bindingContext.decorateStatement(tree); + } finally { + bindingContext.pop(); + } + } + + @Override + public void visitMethodDef(JCMethodDecl tree) { + MethodSymbol prevMethodSym = currentMethodSym; + try { + currentMethodSym = tree.sym; + super.visitMethodDef(tree); + } finally { + currentMethodSym = prevMethodSym; + } + } + + @Override + public void visitIdent(JCIdent tree) { + VarSymbol bindingVar = null; + if ((tree.sym.flags() & Flags.MATCH_BINDING) != 0) { + bindingVar = bindingContext.getBindingFor((BindingSymbol)tree.sym); + } + if (bindingVar == null) { + super.visitIdent(tree); + } else { + result = make.at(tree.pos).Ident(bindingVar); + } + } + + @Override + public void visitBlock(JCBlock tree) { + ListBuffer<JCStatement> statements = new ListBuffer<>(); + bindingContext = new BasicBindingContext(List.nil()) { + boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) { + hoistedVarMap.put(binding, var.sym); + statements.append(var); + return true; + } + }; + try { + for (List<JCStatement> l = tree.stats; l.nonEmpty(); l = l.tail) { + statements.append(translate(l.head)); + } + + tree.stats = statements.toList(); + result = tree; + } finally { + bindingContext.pop(); + } + } + + public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) { + try { + this.make = make; + translate(cdef); + } finally { + // note that recursive invocations of this method fail hard + this.make = null; + } + + if (debugTransPatterns) { + System.err.println(cdef); + } + return cdef; + } + + /** Make an instanceof expression. + * @param lhs The expression. + * @param type The type to be tested. + */ + + JCInstanceOf makeTypeTest(JCExpression lhs, JCExpression type) { + JCInstanceOf tree = make.TypeTest(lhs, type); + tree.type = syms.booleanType; + return tree; + } + + /** Make an attributed binary expression (copied from Lower). + * @param optag The operators tree tag. + * @param lhs The operator's left argument. + * @param rhs The operator's right argument. + */ + JCBinary makeBinary(JCTree.Tag optag, JCExpression lhs, JCExpression rhs) { + JCBinary tree = make.Binary(optag, lhs, rhs); + tree.operator = operators.resolveBinary(tree, optag, lhs.type, rhs.type); + tree.type = tree.operator.type.getReturnType(); + return tree; + } + + JCExpression convert(JCExpression expr, Type target) { + JCExpression result = make.at(expr.pos()).TypeCast(make.Type(target), expr); + result.type = (expr.type.constValue() != null) ? + constFold.coerce(expr.type, target) : target; + return result; + } + + JCExpression makeDefaultValue(int pos, Type type) { + if (type.isReference()) { + return make.at(pos).Literal(BOT, null).setType(syms.botType); + } else { + final Object value; + switch (type.getTag()) { + case BYTE: + value = (byte)0; + break; + case SHORT: + value = (short)0; + break; + case INT: + value = 0; + break; + case FLOAT: + value = 0f; + break; + case LONG: + value = 0L; + break; + case DOUBLE: + value = 0D; + break; + case CHAR: + value = (char)0; + break; + case BOOLEAN: + value = false; + break; + default: + Assert.error(); + return null; + } + return make.at(pos).Literal(value); + } + } + + abstract class BindingContext { + abstract VarSymbol getBindingFor(BindingSymbol varSymbol); + abstract JCStatement decorateStatement(JCStatement stat); + abstract JCExpression decorateExpression(JCExpression expr); + abstract BindingContext pop(); + abstract boolean tryPrepend(BindingSymbol binding, JCVariableDecl var); + } + + class BasicBindingContext extends BindingContext { + List<BindingSymbol> matchBindings; + Map<BindingSymbol, VarSymbol> hoistedVarMap; + BindingContext parent; + + public BasicBindingContext(List<BindingSymbol> matchBindings) { + this.matchBindings = matchBindings; + this.parent = bindingContext; + this.hoistedVarMap = matchBindings.stream() + .filter(v -> parent.getBindingFor(v) == null) + .collect(Collectors.toMap(v -> v, v -> new VarSymbol(v.flags(), v.name.append(names.fromString("$binding")), v.type, v.owner))); + } + + @Override + VarSymbol getBindingFor(BindingSymbol varSymbol) { + VarSymbol res = parent.getBindingFor(varSymbol); + if (res != null) { + return res; + } + return hoistedVarMap.entrySet().stream() + .filter(e -> e.getKey().isAliasFor(varSymbol)) + .findFirst() + .map(e -> e.getValue()).orElse(null); + } + + @Override + JCStatement decorateStatement(JCStatement stat) { + if (hoistedVarMap.isEmpty()) return stat; + ListBuffer<JCStatement> stats = new ListBuffer<>(); + for (Entry<BindingSymbol, VarSymbol> e : hoistedVarMap.entrySet()) { + JCVariableDecl decl = makeHoistedVarDecl(stat.pos, e.getValue()); + if (!e.getKey().isPreserved() || + !parent.tryPrepend(e.getKey(), decl)) { + stats.add(decl); + } + } + if (stats.nonEmpty()) { + stats.add(stat); + stat = make.at(stat.pos).Block(0, stats.toList()); + } + return stat; + } + + @Override + JCExpression decorateExpression(JCExpression expr) { + for (VarSymbol vsym : hoistedVarMap.values()) { + expr = make.at(expr.pos).LetExpr(makeHoistedVarDecl(expr.pos, vsym), expr).setType(expr.type); + } + return expr; + } + + @Override + BindingContext pop() { + return bindingContext = parent; + } + + @Override + boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) { + return false; + } + + private JCVariableDecl makeHoistedVarDecl(int pos, VarSymbol varSymbol) { + return make.at(pos).VarDef(varSymbol, makeDefaultValue(pos, varSymbol.erasure(types))); + } + } +}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java Thu Nov 29 08:16:17 2018 +0100 @@ -567,6 +567,13 @@ result = tree; } + public void visitBindingPattern(JCBindingPattern tree) { + if (tree.vartype != null) { + tree.vartype = translate(tree.vartype, null); + } + result = tree; + } + public void visitSwitchExpression(JCSwitchExpression tree) { Type selsuper = types.supertype(tree.selector.type); boolean enumSwitch = selsuper != null && @@ -777,7 +784,7 @@ public void visitTypeTest(JCInstanceOf tree) { tree.expr = translate(tree.expr, null); - tree.clazz = translate(tree.clazz, null); + tree.pattern = translate(tree.pattern, null); result = tree; }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java Thu Nov 29 08:16:17 2018 +0100 @@ -577,7 +577,7 @@ @Override public void visitTypeTest(JCInstanceOf tree) { JCInstanceOf that = (JCInstanceOf) parameter; - result = scan(tree.expr, that.expr) && scan(tree.clazz, that.clazz); + result = scan(tree.expr, that.expr) && scan(tree.pattern, that.pattern); } @Override
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java Thu Nov 29 08:16:17 2018 +0100 @@ -459,7 +459,7 @@ public void visitTypeTest(JCInstanceOf tree) { SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); sr.mergeWith(csp(tree.expr)); - sr.mergeWith(csp(tree.clazz)); + sr.mergeWith(csp(tree.pattern)); result = sr; }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Thu Nov 29 08:16:17 2018 +0100 @@ -2024,7 +2024,7 @@ public void visitTypeTest(JCInstanceOf tree) { genExpr(tree.expr, tree.expr.type).load(); setTypeAnnotationPositions(tree.pos); - code.emitop2(instanceof_, makeRef(tree.pos(), tree.clazz.type)); + code.emitop2(instanceof_, makeRef(tree.pos(), tree.pattern.type)); result = items.makeStackItem(syms.booleanType); }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Thu Nov 29 08:16:17 2018 +0100 @@ -1561,6 +1561,12 @@ compileStates.put(env, CompileState.UNLAMBDA); } + if (shouldStop(CompileState.TRANSPATTERNS)) + return; + + env.tree = TransPatterns.instance(context).translateTopLevelClass(env, env.tree, localMake); + compileStates.put(env, CompileState.TRANSPATTERNS); + if (shouldStop(CompileState.LOWER)) return;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java Thu Nov 29 08:16:17 2018 +0100 @@ -886,6 +886,7 @@ /* Expression2Rest = {infixop Expression3} * | Expression3 instanceof Type + * | Expression3 instanceof Pattern * infixop = "||" * | "&&" * | "|" @@ -908,13 +909,24 @@ Token topOp = Tokens.DUMMY; while (prec(token.kind) >= minprec) { opStack[top] = topOp; - top++; - topOp = token; - nextToken(); - odStack[top] = (topOp.kind == INSTANCEOF) ? parseType() : term3(); + + if (token.kind == INSTANCEOF) { + int pos = token.pos; + nextToken(); + int patternPos = token.pos; + JCTree pattern = parseType(); + if (token.kind == IDENTIFIER) { + pattern = toP(F.at(patternPos).BindingPattern(ident(), pattern)); + } + odStack[top] = F.at(pos).TypeTest(odStack[top], pattern); + } else { + topOp = token; + nextToken(); + top++; + odStack[top] = term3(); + } while (top > 0 && prec(topOp.kind) >= prec(token.kind)) { - odStack[top-1] = makeOp(topOp.pos, topOp.kind, odStack[top-1], - odStack[top]); + odStack[top - 1] = F.at(topOp.pos).Binary(optag(topOp.kind), odStack[top - 1], odStack[top]); top--; topOp = opStack[top]; } @@ -931,19 +943,6 @@ return t; } //where - /** Construct a binary or type test node. - */ - private JCExpression makeOp(int pos, - TokenKind topOp, - JCExpression od1, - JCExpression od2) - { - if (topOp == INSTANCEOF) { - return F.at(pos).TypeTest(od1, od2); - } else { - return F.at(pos).Binary(optag(topOp), od1, od2); - } - } /** If tree is a concatenation of string literals, replace it * by a single literal representing the concatenated string. */ @@ -2542,25 +2541,25 @@ log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.LocalEnum); dc = token.comment(CommentStyle.JAVADOC); return List.of(classOrInterfaceOrEnumDeclaration(modifiersOpt(), dc)); - default: - Token prevToken = token; - JCExpression t = term(EXPR | TYPE); - if (token.kind == COLON && t.hasTag(IDENT)) { - nextToken(); - JCStatement stat = parseStatementAsBlock(); - return List.of(F.at(pos).Labelled(prevToken.name(), stat)); - } else if ((lastmode & TYPE) != 0 && LAX_IDENTIFIER.accepts(token.kind)) { - pos = token.pos; - JCModifiers mods = F.at(Position.NOPOS).Modifiers(0); - F.at(pos); - return localVariableDeclarations(mods, t); - } else { - // This Exec is an "ExpressionStatement"; it subsumes the terminating semicolon - t = checkExprStat(t); - accept(SEMI); - JCExpressionStatement expr = toP(F.at(pos).Exec(t)); - return List.of(expr); - } + } + //otherwise + Token prevToken = token; + JCExpression t = term(EXPR | TYPE); + if (token.kind == COLON && t.hasTag(IDENT)) { + nextToken(); + JCStatement stat = parseStatementAsBlock(); + return List.of(F.at(pos).Labelled(prevToken.name(), stat)); + } else if ((lastmode & TYPE) != 0 && LAX_IDENTIFIER.accepts(token.kind)) { + pos = token.pos; + JCModifiers mods = F.at(Position.NOPOS).Modifiers(0); + F.at(pos); + return localVariableDeclarations(mods, t); + } else { + // This Exec is an "ExpressionStatement"; it subsumes the terminating semicolon + t = checkExprStat(t); + accept(SEMI); + JCExpressionStatement expr = toP(F.at(pos).Exec(t)); + return List.of(expr); } } //where
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Thu Nov 29 08:16:17 2018 +0100 @@ -543,6 +543,10 @@ auto-closeable resource {0} may not be assigned # 0: symbol +compiler.err.pattern.binding.may.not.be.assigned=\ + pattern binding {0} may not be assigned + +# 0: symbol compiler.err.multicatch.parameter.may.not.be.assigned=\ multi-catch parameter {0} may not be assigned @@ -3332,6 +3336,12 @@ compiler.err.illegal.argument.for.option=\ illegal argument for {0}: {1} +compiler.err.match.binding.exists.with.different.type=\ + illegal attempt to redefine an existing match binding with different type + +compiler.err.match.binding.exists=\ + illegal attempt to redefine an existing match binding + compiler.err.switch.null.not.allowed=\ null label in case is not allowed
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java Thu Nov 29 08:16:17 2018 +0100 @@ -38,6 +38,7 @@ import com.sun.tools.javac.code.Directive.RequiresDirective; import com.sun.tools.javac.code.Scope.*; import com.sun.tools.javac.code.Symbol.*; +import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; @@ -235,6 +236,10 @@ */ TYPETEST, + /** Patterns. + */ + BINDINGPATTERN, + /** Indexed array expressions, of type Indexed. */ INDEXED, @@ -2117,10 +2122,10 @@ */ public static class JCInstanceOf extends JCExpression implements InstanceOfTree { public JCExpression expr; - public JCTree clazz; - protected JCInstanceOf(JCExpression expr, JCTree clazz) { + public JCTree pattern; + protected JCInstanceOf(JCExpression expr, JCTree pattern) { this.expr = expr; - this.clazz = clazz; + this.pattern = pattern; } @Override public void accept(Visitor v) { v.visitTypeTest(this); } @@ -2128,7 +2133,13 @@ @DefinedBy(Api.COMPILER_TREE) public Kind getKind() { return Kind.INSTANCE_OF; } @DefinedBy(Api.COMPILER_TREE) - public JCTree getType() { return clazz; } + public JCTree getType() { return pattern instanceof JCPattern ? pattern.hasTag(BINDINGPATTERN) ? ((JCBindingPattern) pattern).vartype : null : pattern; } + + @Override @DefinedBy(Api.COMPILER_TREE) + public JCPattern getPattern() { + return pattern instanceof JCPattern ? (JCPattern) pattern : null; + } + @DefinedBy(Api.COMPILER_TREE) public JCExpression getExpression() { return expr; } @Override @DefinedBy(Api.COMPILER_TREE) @@ -2142,6 +2153,60 @@ } /** + * Pattern matching forms. + */ + public static abstract class JCPattern extends JCTree + implements PatternTree { + public JCExpression constExpression() { + return null; + } + } + + public static class JCBindingPattern extends JCPattern + implements BindingPatternTree { + public Name name; + public BindingSymbol symbol; + public JCTree vartype; + + protected JCBindingPattern(Name name, BindingSymbol symbol, JCTree vartype) { + this.name = name; + this.symbol = symbol; + this.vartype = vartype; + } + + @DefinedBy(Api.COMPILER_TREE) + public Name getBinding() { + return name; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Tree getType() { + return vartype; + } + + @Override + public void accept(Visitor v) { + v.visitBindingPattern(this); + } + + @DefinedBy(Api.COMPILER_TREE) + public Kind getKind() { + return Kind.BINDING_PATTERN; + } + + @Override + @DefinedBy(Api.COMPILER_TREE) + public <R, D> R accept(TreeVisitor<R, D> v, D d) { + return v.visitBindingPattern(this, d); + } + + @Override + public Tag getTag() { + return BINDINGPATTERN; + } + } + + /** * An array selection */ public static class JCArrayAccess extends JCExpression implements ArrayAccessTree { @@ -3112,6 +3177,7 @@ JCBinary Binary(Tag opcode, JCExpression lhs, JCExpression rhs); JCTypeCast TypeCast(JCTree expr, JCExpression type); JCInstanceOf TypeTest(JCExpression expr, JCTree clazz); + JCBindingPattern BindingPattern(Name name, JCTree vartype); JCArrayAccess Indexed(JCExpression indexed, JCExpression index); JCFieldAccess Select(JCExpression selected, Name selector); JCIdent Ident(Name idname); @@ -3175,6 +3241,7 @@ public void visitBinary(JCBinary that) { visitTree(that); } public void visitTypeCast(JCTypeCast that) { visitTree(that); } public void visitTypeTest(JCInstanceOf that) { visitTree(that); } + public void visitBindingPattern(JCBindingPattern that) { visitTree(that); } public void visitIndexed(JCArrayAccess that) { visitTree(that); } public void visitSelect(JCFieldAccess that) { visitTree(that); } public void visitReference(JCMemberReference that) { visitTree(that); }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java Thu Nov 29 08:16:17 2018 +0100 @@ -235,6 +235,23 @@ printExprs(trees, ", "); } + + /** Derived visitor method: print pattern. + */ + + public void printPattern(JCTree tree) throws IOException { + printExpr(tree); + } + + public <T extends JCTree> void printPatterns(List<T> trees) throws IOException { + if (trees.nonEmpty()) { + printPattern(trees.head); + for (List<T> l = trees.tail; l.nonEmpty(); l = l.tail) { + print(", "); + printPattern(l.head); + } + } + } /** Derived visitor method: print list of statements, each on a separate line. */ public void printStats(List<? extends JCTree> trees) throws IOException { @@ -878,6 +895,21 @@ } } + public void visitBindingPattern(JCBindingPattern patt) { + try { + if (patt.vartype == null) { + print("var "); + print(patt.name); + } else { + printExpr(patt.vartype); + print(" "); + print(patt.name); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + public void visitSynchronized(JCSynchronized tree) { try { print("synchronized "); @@ -1273,7 +1305,11 @@ open(prec, TreeInfo.ordPrec); printExpr(tree.expr, TreeInfo.ordPrec); print(" instanceof "); - printExpr(tree.clazz, TreeInfo.ordPrec + 1); + if (tree.pattern instanceof JCPattern) { + printPattern(tree.pattern); + } else { + printExpr(tree.getType(), TreeInfo.ordPrec + 1); + } close(prec, TreeInfo.ordPrec); } catch (IOException e) { throw new UncheckedIOException(e);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java Thu Nov 29 08:16:17 2018 +0100 @@ -26,7 +26,6 @@ package com.sun.tools.javac.tree; import com.sun.source.tree.*; -import com.sun.source.tree.Tree.Kind; import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.util.DefinedBy; import com.sun.tools.javac.util.DefinedBy.Api; @@ -476,8 +475,15 @@ public JCTree visitInstanceOf(InstanceOfTree node, P p) { JCInstanceOf t = (JCInstanceOf) node; JCExpression expr = copy(t.expr, p); - JCTree clazz = copy(t.clazz, p); - return M.at(t.pos).TypeTest(expr, clazz); + JCTree pattern = copy(t.pattern, p); + return M.at(t.pos).TypeTest(expr, pattern); + } + + @DefinedBy(Api.COMPILER_TREE) + public JCTree visitBindingPattern(BindingPatternTree node, P p) { + JCBindingPattern t = (JCBindingPattern) node; + JCTree vartype = copy(t.vartype, p); + return M.at(t.pos).BindingPattern(t.name, vartype); } @DefinedBy(Api.COMPILER_TREE)
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java Thu Nov 29 08:16:17 2018 +0100 @@ -574,7 +574,7 @@ case TYPECAST: return getEndPos(((JCTypeCast) tree).expr, endPosTable); case TYPETEST: - return getEndPos(((JCInstanceOf) tree).clazz, endPosTable); + return getEndPos(((JCInstanceOf) tree).pattern, endPosTable); case WHILELOOP: return getEndPos(((JCWhileLoop) tree).body, endPosTable); case ANNOTATED_TYPE: @@ -847,6 +847,8 @@ if (node.type != null) return node.type.tsym; return null; + case BINDINGPATTERN: + return ((JCBindingPattern) node).symbol; default: return null; } @@ -1225,4 +1227,5 @@ public static boolean isPackageInfo(JCCompilationUnit tree) { return tree.sourcefile.isNameCompatible("package-info", JavaFileObject.Kind.SOURCE); } + }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java Thu Nov 29 08:16:17 2018 +0100 @@ -29,7 +29,6 @@ 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.*; import com.sun.tools.javac.code.Attribute.UnresolvedClass; import com.sun.tools.javac.code.Symbol.*; @@ -459,6 +458,12 @@ return tree; } + public JCBindingPattern BindingPattern(Name name, JCTree vartype) { + JCBindingPattern tree = new JCBindingPattern(name, null, vartype); + tree.pos = pos; + return tree; + } + public JCArrayAccess Indexed(JCExpression indexed, JCExpression index) { JCArrayAccess tree = new JCArrayAccess(indexed, index); tree.pos = pos;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java Thu Nov 29 08:16:17 2018 +0100 @@ -296,7 +296,12 @@ public void visitTypeTest(JCInstanceOf tree) { scan(tree.expr); - scan(tree.clazz); + scan(tree.pattern); + } + + public void visitBindingPattern(JCBindingPattern tree) { + if (tree.vartype != null) + scan(tree.vartype); } public void visitIndexed(JCArrayAccess tree) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java Wed Nov 28 21:34:22 2018 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java Thu Nov 29 08:16:17 2018 +0100 @@ -351,7 +351,12 @@ public void visitTypeTest(JCInstanceOf tree) { tree.expr = translate(tree.expr); - tree.clazz = translate(tree.clazz); + tree.pattern = translate(tree.pattern); + result = tree; + } + + public void visitBindingPattern(JCBindingPattern tree) { + tree.vartype = translate(tree.vartype); result = tree; }
--- a/test/langtools/tools/javac/api/TestGetElementReferenceData.java Wed Nov 28 21:34:22 2018 -0500 +++ b/test/langtools/tools/javac/api/TestGetElementReferenceData.java Thu Nov 29 08:16:17 2018 +0100 @@ -35,6 +35,8 @@ java.util.List< /*getElement:INTERFACE:java.util.List*/ String> l; utility/*getElement:METHOD:test.TestGetElementReferenceData.Base.utility()*/(); target(TestGetElementReferenceData :: test/*getElement:METHOD:test.TestGetElementReferenceData.test()*/); + Object o = null; + if (o instanceof String/*getElement:CLASS:java.lang.String*/ str/*getElement:LOCAL_VARIABLE:str*/) ; } private static void target(Runnable r) { r.run(); } public static class Base {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/diags/examples/MatchBindingExists.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,38 @@ +/* + * 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. + */ + +// key: compiler.err.match.binding.exists +// key: compiler.err.match.binding.exists.with.different.type +//TODO: the following error is extraneous? +// key: compiler.err.already.defined + +class X { + public static void main(String [] args) { + String s = "Hello"; + Integer i = 42; + Object o1 = s, o2 = i; + + if (o1 instanceof String k && o2 instanceof Integer k) {} + if (o1 instanceof String k || o2 instanceof Integer k) {} + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/diags/examples/PatternBindingMayNotBeAssigned.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,32 @@ +/* + * 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. + */ + +// key: compiler.err.pattern.binding.may.not.be.assigned + +class ResourceMayNotBeAssigned { + void m(Object o) { + if (o instanceof String s) { + s = ""; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/BindingsTest1.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,143 @@ +/* + * @test + * @summary Basic tests for bindings from instanceof + * @compile BindingsTest1.java + * @run main BindingsTest1 + */ + +public class BindingsTest1 { + public static boolean Ktrue() { return true; } + public static void main(String[] args) { + Object o1 = "hello"; + Integer i = 42; + Object o2 = i; + Object o3 = "there"; + + + // Test for (e matches P).T = { binding variables in P } + if (o1 instanceof String s) { + s.length(); + } + + // Test for e1 && e2.T = union(e1.T, e2.T) + if (o1 instanceof String s && o2 instanceof Integer in) { + s.length(); + in.intValue(); + } + // Test for e1 && e2.F = intersect(e1.F, e2.F) + if (!(o1 instanceof String s) && !(o1 instanceof String s)) { + + } else { + s.length(); + } + + // test for e1&&e2 - include e1.T in e2 + if (o1 instanceof String s && s.length()>0) { + System.out.print("done"); + } + + // Test for (e1 || e2).T = intersect(e1.T, e2.T) + if (o1 instanceof String s || o3 instanceof String s){ + System.out.println(s); // ? + } + + // Test for (e1 || e2).F = union(e1.F, e2.F) + if (!(o1 instanceof String s) || !(o3 instanceof Integer in)){ + } else { + s.length(); + i.intValue(); + } + + // Test for e1||e2 - include e1.F in e2 + + if (!(o1 instanceof String s) || s.length()>0) { + System.out.println("done"); + } + + // Test for (e1 ? e2 : e3).T contains intersect(e2.T, e3.T) + if (Ktrue() ? o2 instanceof Integer x : o2 instanceof Integer x) { + x.intValue(); + } + + // Test for (e1 ? e2 : e3).T contains intersect(e1.T, e3.T) + if (o1 instanceof String s ? true : o1 instanceof String s) { + s.length(); + } + + // Test for (e1 ? e2 : e3).T contains intersect(e1.F, e2.T) + if (!(o1 instanceof String s) ? (o1 instanceof String s) : true) { + s.length(); + } + + // Test for (e1 ? e2 : e3).F contains intersect(e2.F, e3.F) + if (Ktrue() ? !(o2 instanceof Integer x) : !(o2 instanceof Integer x)){ + } else { + x.intValue(); + } + + // Test for (e1 ? e2 : e3).F contains intersect(e1.T, e3.F) + if (o1 instanceof String s ? true : !(o1 instanceof String s)){ + } else { + s.length(); + } + + // Test for (e1 ? e2 : e3).F contains intersect(e1.F, e2.F) + if (!(o1 instanceof String s) ? !(o1 instanceof String s) : true){ + } else { + s.length(); + } + + // Test for e1 ? e2: e3 - include e1.T in e2 + if (o1 instanceof String s ? s.length()>0 : false) { + System.out.println("done"); + } + + // Test for e1 ? e2 : e3 - include e1.F in e3 + if (!(o1 instanceof String s) ? false : s.length()>0){ + System.out.println("done"); + } + + // Test for (!e).T = e.F + + if (!(!(o1 instanceof String s) || !(o3 instanceof Integer in))){ + s.length(); + i.intValue(); + } + + // Test for (!e).F = e.T + if (!(o1 instanceof String s)) { + + } else { + s.length(); + } + + L1: { + if (o1 instanceof String s) { + s.length(); + } else { + break L1; + } + s.length(); + } + + L2: { + if (!(o1 instanceof String s)) { + break L2; + } else { + s.length(); + } + s.length(); + } + + L3: { + if ((o1 instanceof String s) || (o3 instanceof String s)) { + s.length(); + } else { + break L3; + } + s.length(); + } + + System.out.println("BindingsTest1 complete"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/BindingsTest2.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,116 @@ +/* + * @test /nodynamiccopyright/ + * @summary Ensure that scopes arising from conditionalExpressions are handled corrected. + * @compile/fail/ref=BindingsTest2.out -XDrawDiagnostics BindingsTest2.java + */ +public class BindingsTest2 { + public static boolean Ktrue() { return true; } + public static void main(String[] args) { + Object o1 = "hello"; + Integer in = 42; + Object o2 = in; + Object o3 = "there"; + + + if (Ktrue() ? o2 instanceof Integer x : o2 instanceof String x) { + x.intValue(); + } + if (Ktrue() ? o2 instanceof Integer x : true) { + x.intValue(); + } + + if (o1 instanceof String s ? true : true) { + s.length(); + } + if (o1 instanceof String s ? true : o2 instanceof Integer s) { + s.length(); + } + if (o1 instanceof String s ? true : o2 instanceof Integer i) { + s.length(); + } + + // Test for (e1 ? e2 : e3).T contains intersect(e1.F, e2.T) + if (!(o1 instanceof String s) ? true : true) { + s.length(); + } + if (!(o1 instanceof String s) ? (o2 instanceof Integer s) : true) { + s.length(); + } + if (!(o1 instanceof String s) ? (o2 instanceof Integer i) : true) { + s.length(); + i.intValue(); + } + if (!(o1 instanceof String s) ? (o1 instanceof String s2) : true) { + s.length(); + s2.length(); + } + + + // Test for (e1 ? e2 : e3).F contains intersect(e2.F, e3.F) + if (Ktrue() ? !(o2 instanceof Integer x) : !(o1 instanceof String x)){ + } else { + x.intValue(); + } + if (Ktrue() ? !(o2 instanceof Integer x) : !(o1 instanceof String s)){ + } else { + x.intValue(); + } + if (Ktrue() ? !(o2 instanceof Integer x) : !(o2 instanceof Integer x1)){ + } else { + x.intValue(); + x1.intValue(); + } + if (Ktrue() ? !(o2 instanceof Integer x) : false){ + } else { + x.intValue(); + } + + // Test for (e1 ? e2 : e3).F contains intersect(e1.T, e3.F) + if (o1 instanceof String s ? true : !(o2 instanceof Integer s)){ + } else { + s.length(); + } + if (o1 instanceof String s ? true : !(o2 instanceof Integer i)){ + } else { + s.length(); + i.intValue(); + } + if (o1 instanceof String s ? true : !(o2 instanceof String s1)){ + } else { + s.length(); + s1.length(); + } + // Test for (e1 ? e2 : e3).F contains intersect(e1.F, e2.F) + if (!(o1 instanceof String s) ? !(o1 instanceof String s1) : true){ + } else { + s.length(); + s1.length(); + } + if (!(o1 instanceof String s) ? !(o2 instanceof Integer s) : true){ + } else { + s.length(); + } + if (!(o1 instanceof String s) ? !(o2 instanceof Integer i) : true){ + } else { + s.length(); + i.intValue(); + } + + // Test for e1 ? e2: e3 - include e1.T in e2 + if (o1 instanceof String s ? false : s.length()>0) { + System.out.println("done"); + } + if (o1 instanceof String s ? false : s.intValue!=0) { + System.out.println("done"); + } + + // Test for e1 ? e2 : e3 - include e1.F in e3 + if (!(o1 instanceof String s) ? s.length()>0 : false){ + System.out.println("done"); + } + if (!(o1 instanceof String s) ? s.intValue>0 : false){ + System.out.println("done"); + } + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/BindingsTest2.out Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,37 @@ +BindingsTest2.java:15:21: compiler.err.match.binding.exists.with.different.type +BindingsTest2.java:16:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:19:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:23:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:25:36: compiler.err.match.binding.exists.with.different.type +BindingsTest2.java:26:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:29:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:34:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:36:39: compiler.err.match.binding.exists.with.different.type +BindingsTest2.java:37:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:40:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:41:13: compiler.err.cant.resolve.location: kindname.variable, i, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:44:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:45:13: compiler.err.cant.resolve.location: kindname.variable, s2, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:50:21: compiler.err.match.binding.exists.with.different.type +BindingsTest2.java:52:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:56:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:60:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:61:13: compiler.err.cant.resolve.location: kindname.variable, x1, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:65:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:69:36: compiler.err.match.binding.exists.with.different.type +BindingsTest2.java:71:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:75:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:76:13: compiler.err.cant.resolve.location: kindname.variable, i, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:80:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:81:13: compiler.err.cant.resolve.location: kindname.variable, s1, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:86:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:87:13: compiler.err.cant.resolve.location: kindname.variable, s1, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:89:39: compiler.err.match.binding.exists.with.different.type +BindingsTest2.java:91:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:95:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:96:13: compiler.err.cant.resolve.location: kindname.variable, i, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:100:46: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:103:46: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:108:41: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:111:41: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +36 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/CastConversionMatch.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,40 @@ +/* + * 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 + * @summary Match which involves a cast conversion + * @compile/fail/ref=CastConversionMatch.out -XDrawDiagnostics CastConversionMatch.java + */ + +public class CastConversionMatch { + public static void main(String [] args) { + Object o = 42; + if (o instanceof int s) { + System.out.println("Okay"); + } else { + throw new AssertionError("broken"); + } + System.out.println(">Test complete"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/CastConversionMatch.out Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,2 @@ +CastConversionMatch.java:33:26: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array) +1 error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/DuplicateBindingTest.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,21 @@ +/* + * @test /nodynamiccopyright/ + * @summary Basic pattern bindings scope test + * @compile/fail/ref=DuplicateBindingTest.out -XDrawDiagnostics DuplicateBindingTest.java + */ + +public class DuplicateBindingTest { + + int f; + + public static void main(String[] args) { + + if (args != null) { + int s; + if (args[0] instanceof String s) { // NOT OK. Redef same scope. + } + if (args[0] instanceof String f) { // OK to redef field. + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/DuplicateBindingTest.out Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,2 @@ +DuplicateBindingTest.java:15:36: compiler.err.already.defined: kindname.variable, s, kindname.method, main(java.lang.String[]) +1 error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/EnsureTypesOrderTest.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,13 @@ +/* + * @test /nodynamiccopyright/ + @bug 8187420 + * @summary Error message mentions relevant types transposed + * @compile/fail/ref=EnsureTypesOrderTest.out -XDrawDiagnostics EnsureTypesOrderTest.java + */ +public class EnsureTypesOrderTest { + public static void main(String [] args) { + if (args instanceof String s) { + System.out.println("Broken"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/EnsureTypesOrderTest.out Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,2 @@ +EnsureTypesOrderTest.java:9:13: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String[], java.lang.String) +1 error \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/ExamplesFromProposal.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,191 @@ +/* + * @test + * @summary All example code from "Pattern Matching for Java" document, released April 2017, adjusted to current state (no switches, etc) + * @compile ExamplesFromProposal.java + * @run main ExamplesFromProposal + */ + +interface Node { +} + +class IntNode implements Node { + int value; + + IntNode(int value) { + this.value = value; + } +} + +class NegNode implements Node { + Node node; + + NegNode(Node node) { + this.node = node; + } +} + +class MulNode implements Node { + Node left, right; + + MulNode(Node left, Node right) { + this.left = left; + this.right = right; + } +} + +class AddNode implements Node { + Node left, right; + + AddNode(Node left, Node right) { + this.left = left; + this.right = right; + } +} + +public class ExamplesFromProposal { + + public static Object getSomething() { + return new Long(42); + } + + public static int eval(Node n) { + if (n instanceof IntNode in) return in.value; + else if (n instanceof NegNode nn) return -eval(nn.node); + else if (n instanceof AddNode an) return eval(an.left) + eval(an.right); + else if (n instanceof MulNode mn) return eval(mn.left) * eval(mn.right); + else { + // should never happen + throw new AssertionError("broken"); + } + } + + public static String toString(Node n) { + if (n instanceof IntNode in) return String.valueOf(in.value); + else if (n instanceof NegNode nn) return "-"+eval(nn.node); + else if (n instanceof AddNode an) return eval(an.left) + " + " + eval(an.right); + else if (n instanceof MulNode mn) return eval(mn.left) + " * " + eval(mn.right); + else { + // should never happen + throw new AssertionError("broken"); + } + } + + public static Node simplify(Node n) { + if (n instanceof IntNode in) { + return n; + } else if (n instanceof NegNode nn) { + return new NegNode(simplify(nn.node)); + } else if (n instanceof AddNode ad) { + n = simplify(ad.left); + if (n instanceof IntNode intn) { + if (intn.value == 0) + return simplify(ad.right); + else + return new AddNode(intn, simplify(ad.right)); + } else { + return new AddNode(simplify(ad.left), simplify(ad.right)); + } + } else if (n instanceof MulNode mn) { + return new MulNode(simplify(mn.left), simplify(mn.right)); + } else { + //should never happen + throw new AssertionError("broken"); + } + } + + public static void testNode(Node n, int expected) { + if (eval(n) != expected) + throw new AssertionError("broken"); + } + + public static void main(String[] args) { + Object x = new Integer(42); + + if (x instanceof Integer i) { + // can use i here + System.out.println(i.intValue()); + } + + Object obj = getSomething(); + + String formatted = "unknown"; + if (obj instanceof Integer i) { + formatted = String.format("int %d", i); + } + else if (obj instanceof Byte b) { + formatted = String.format("byte %d", b); + } + else if (obj instanceof Long l) { + formatted = String.format("long %d", l); + } + else if (obj instanceof Double d) { + formatted = String.format("double %f", d); + } + else if (obj instanceof String s) { + formatted = String.format("String %s", s); + } + System.out.println(formatted); + + if (obj instanceof Integer i) formatted = String.format("int %d", i); + else if (obj instanceof Byte b) formatted = String.format("byte %d", b); + else if (obj instanceof Long l) formatted = String.format("long %d", l); + else if (obj instanceof Double d) formatted = String.format("double %f", d); + else if (obj instanceof String s) formatted = String.format("String %s", s); + else formatted = String.format("Something else "+ obj.toString()); + System.out.println(formatted); + + Node zero = new IntNode(0); + Node one = new IntNode(1); + Node ft = new IntNode(42); + + Node temp = new AddNode(zero,ft); + + testNode(temp,42); + + + + if (toString(simplify(temp)).equals(toString(ft))) + System.out.println("Simplify worked!"); + else + throw new AssertionError("broken"); + + + if (toString(simplify(new AddNode(zero,temp))).equals(toString(ft))) + System.out.println("Simplify worked!"); + else + throw new AssertionError("broken"); + + + temp = new AddNode(zero,ft); + temp = new AddNode(one,temp); + temp = new AddNode(zero,temp); + + Node fortythree = new AddNode(one,ft); + + if (toString(simplify(temp)).equals(toString(fortythree))) + System.out.println("Simplify worked!"); + else + throw new AssertionError("broken"); + + + x = "Hello"; + + if (x instanceof String s1) { + System.out.println(s1); + } + if (x instanceof String s1 && s1.length() > 0) { + System.out.println(s1); + } + if (x instanceof String s1) { + System.out.println(s1 + " is a string"); + } else { + System.out.println("not a string"); + } + + if (!(x instanceof String s1)) { + System.out.println("not a string"); + } else { + System.out.println(s1 + " is a string"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/ImpossibleTypeTest.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,20 @@ +/* + * @test /nodynamiccopyright/ + * @summary Ensure that in type test patterns, the predicate is not trivially provable false. + * @compile/fail/ref=ImpossibleTypeTest.out -XDrawDiagnostics ImpossibleTypeTest.java + */ +public class ImpossibleTypeTest { + + public static void main(String[] args) { + + int in = 42; + Integer i = 42; + + if (i instanceof String s ) { + System.out.println("Broken"); + } + if (i instanceof Undefined u ) { + System.out.println("Broken"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/ImpossibleTypeTest.out Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,3 @@ +ImpossibleTypeTest.java:13:13: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.String) +ImpossibleTypeTest.java:16:26: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, ImpossibleTypeTest, null) +2 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/MatchBindingScopeTest.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,67 @@ +/* + * @test /nodynamiccopyright/ + * @summary Basic pattern bindings scope test + * @compile/fail/ref=MatchBindingScopeTest.out -XDrawDiagnostics MatchBindingScopeTest.java + */ +public class MatchBindingScopeTest { + + static Integer i = 42; + static String s = "Hello"; + static Object o1 = s; + static Object o2 = i; + + public static void main(String[] args) { + + if (o1 instanceof String j && j.length() == 5) { // OK + System.out.println(j); // OK + } else { + System.out.println(j); // NOT OK + } + + // NOT OK, name reused. + if (o1 instanceof String j && o2 instanceof Integer j) { + } + + if (o1 instanceof String j && j.length() == 5 && o2 instanceof Integer k && k == 42) { // OK + System.out.println(j); // OK + System.out.println(k); // OK + } else { + System.out.println(j); // NOT OK + System.out.println(k); // NOT OK + } + + if (o1 instanceof String j || j.length() == 5) { // NOT OK + System.out.println(j); // NOT OK + } + + if (o1 instanceof String j || o2 instanceof Integer j) { // NOT OK, types differ + System.out.println(j); // NOT OK + } else { + System.out.println(j); // NOT OK. + } + + while (o1 instanceof String j && j.length() == 5) { // OK + System.out.println(j); // OK + } + + while (o1 instanceof String j || true) { + System.out.println(j); // Not OK + } + + for (; o1 instanceof String j; j.length()) { // OK + System.out.println(j); // OK + } + + for (; o1 instanceof String j || true; j.length()) { // NOT OK + System.out.println(j); // Not OK + } + + int x = o1 instanceof String j ? + j.length() : // OK. + j.length(); // NOT OK. + + x = !(o1 instanceof String j) ? + j.length() : // NOT OK. + j.length(); // OK. + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/MatchBindingScopeTest.out Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,16 @@ +MatchBindingScopeTest.java:18:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:22:53: compiler.err.already.defined: kindname.variable, j, kindname.method, main(java.lang.String[]) +MatchBindingScopeTest.java:22:36: compiler.err.match.binding.exists +MatchBindingScopeTest.java:29:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:30:32: compiler.err.cant.resolve.location: kindname.variable, k, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:33:39: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:34:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:37:36: compiler.err.match.binding.exists.with.different.type +MatchBindingScopeTest.java:38:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:40:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:48:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:55:48: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:56:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:61:23: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:64:23: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +15 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/NullsInPatterns.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,50 @@ +/* + * 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 + * @summary Testing pattern matching against the null constant + * @run main NullsInPatterns + */ +import java.util.List; + +public class NullsInPatterns { + + public static void main(String[] args) { + if (null instanceof List t) { + throw new AssertionError("broken"); + } else { + System.out.println("null does not match List type pattern"); + } + if (null instanceof List<Integer> l) { + throw new AssertionError("broken"); + } else { + System.out.println("null does not match List<Integer> type pattern"); + } + if (null instanceof List<?> l) { + throw new AssertionError("broken"); + } else { + System.out.println("null does not match List<?> type pattern"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternMatchPosTest.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,109 @@ +/* + * 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 + * @summary Check proper positions. + * @build PatternMatchPosTest + * @compile/ref=PatternMatchPosTest.out -processor PatternMatchPosTest -Xlint:unchecked PatternMatchPosTest.java + */ + +import java.io.IOException; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; + +import com.sun.source.tree.CaseTree; +import com.sun.source.tree.IfTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.SourcePositions; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreeScanner; +import com.sun.source.util.Trees; + +@SupportedAnnotationTypes("*") +public class PatternMatchPosTest extends AbstractProcessor { + + int round; + + @Override + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { + if (round++ != 0) + return false; + + try { + TypeElement data = processingEnv.getElementUtils().getTypeElement("PatternMatchPosTestData"); + Trees trees = Trees.instance(processingEnv); + SourcePositions sp = trees.getSourcePositions(); + TreePath dataPath = trees.getPath(data); + String text = dataPath.getCompilationUnit().getSourceFile().getCharContent(true).toString(); + + new TreeScanner<Void, Void>() { + boolean print; + @Override + public Void visitIf(IfTree node, Void p) { + boolean prevPrint = print; + try { + print = true; + scan(node.getCondition(), p); + } finally { + print = prevPrint; + } + scan(node.getThenStatement(), p); + scan(node.getElseStatement(), p); + return null; + } + @Override + public Void scan(Tree tree, Void p) { + if (tree == null) + return null; + if (print) { + int start = (int) sp.getStartPosition(dataPath.getCompilationUnit(), tree); + int end = (int) sp.getEndPosition(dataPath.getCompilationUnit(), tree); + System.out.println(text.substring(start, end)); + } + return super.scan(tree, p); + } + }.scan(dataPath.getLeaf(), null); + return false; + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + +} + +class PatternMatchPosTestData { + void data(Object o) { + if (o instanceof String s) { } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternMatchPosTest.out Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,5 @@ +(o instanceof String s) +o instanceof String s +o +String s +String
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternTypeTest2.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,43 @@ +/* + * 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 + * @summary Basic pattern test + * @run main PatternTypeTest2 + */ +public class PatternTypeTest2 { + + public static void main(String[] args) { + + Integer i = 42; + String s = "Hello"; + Object o = i; + + if (o instanceof Integer j) { + System.out.println("It's an Integer"); + } else { + throw new AssertionError("Broken"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternVariablesAreFinal.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,15 @@ +/* + * @test /nodynamiccopyright/ + * @summary Ensure that in type test patterns, the predicate is not trivially provable false. + * @compile/fail/ref=PatternVariablesAreFinal.out -XDrawDiagnostics PatternVariablesAreFinal.java + */ +public class PatternVariablesAreFinal { + public static void main(String[] args) { + Object o = 32; + if (o instanceof String s) { + s = "hello again"; + System.out.println(s); + } + System.out.println("test complete"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternVariablesAreFinal.out Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,2 @@ +PatternVariablesAreFinal.java:10:13: compiler.err.pattern.binding.may.not.be.assigned: s +1 error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternVariablesAreFinal2.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,15 @@ +/* + * @test + * @summary Pattern variables are final so should be allowed to be referenced in an inner class + * @run main PatternVariablesAreFinal2 + */ +public class PatternVariablesAreFinal2 { + public static void main(String[] args) { + Object o = "42"; + if (o instanceof String s) { + new Object() { + void run() { System.err.println(s); } + }.run(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,18 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8187429 + * @summary Missing unchecked conversion warning + * @compile/fail/ref=UncheckedWarningOnMatchesTest.out -Xlint:unchecked -Werror -XDrawDiagnostics UncheckedWarningOnMatchesTest.java + */ +import java.util.ArrayList; + +public class UncheckedWarningOnMatchesTest { + + public static void main(String [] args) { + + Object o = new ArrayList<UncheckedWarningOnMatchesTest>(); + if (o instanceof ArrayList<Integer> ai) { // unchecked conversion + System.out.println("Blah"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.out Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,4 @@ +UncheckedWarningOnMatchesTest.java:14:13: compiler.warn.prob.found.req: (compiler.misc.unchecked.cast.to.type), java.lang.Object, java.util.ArrayList<java.lang.Integer> +- compiler.err.warnings.and.werror +1 error +1 warning
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/scope/ScopeTest.java Thu Nov 29 08:16:17 2018 +0100 @@ -0,0 +1,147 @@ +/* + * 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.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +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 ScopeTest extends JavacTemplateTestBase { + + private static String st_block(String... statements) { + return Arrays.stream(statements).collect(Collectors.joining("", "{", "}")); + } + + private static String st_if(String condition, String then, String els) { + return "if (" + condition + ") " + then + " else " + els; + } + + private static String st_while(String condition, String body) { + return "while (" + condition + ") " + body; + } + + private static String st_do_while(String body, String condition) { + return "do " + body + " while (" + condition + ");"; + } + + private static String st_for(String init, String condition, String update, String body) { + return "for (" + init + "; " + condition + "; " + update + ") " + body; + } + + private static String st_s_use() { + return "s.length();"; + } + + private static String st_break() { + return "break;"; + } + + private static String st_return() { + return "return;"; + } + + private static String st_noop() { + return ";"; + } + + private static String expr_empty() { + return ""; + } + + private static String expr_o_match_str() { + return "o instanceof String s"; + } + + private static String expr_not(String expr) { + return "!(" + expr + ")"; + } + + @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 block) { + String s = "class C { void m(Object o) " + block + "}"; + addSourceFile("C.java", new StringTemplate(s)); + } + + private void assertOK(String block) { + reset(); + program(block); + try { + compile(); + } + catch (IOException e) { + throw new RuntimeException(e); + } + assertCompileSucceeded(); + } + + private void assertFail(String expectedDiag, String block) { + reset(); + addCompileOptions("--enable-preview", "-source", "12"); + program(block); + try { + compile(); + } + catch (IOException e) { + throw new RuntimeException(e); + } + assertCompileFailed(expectedDiag); + } + + public void testIf() { + assertOK(st_block(st_if(expr_o_match_str(), st_s_use(), st_return()), st_s_use())); + assertOK(st_block(st_if(expr_not(expr_o_match_str()), st_return(), st_s_use()), st_s_use())); + assertFail("compiler.err.cant.resolve.location", st_block(st_if(expr_o_match_str(), st_s_use(), st_noop()), st_s_use())); + assertFail("compiler.err.cant.resolve.location", st_block(st_if(expr_not(expr_o_match_str()), st_noop(), st_s_use()), st_s_use())); + } + + public void testWhile() { + assertOK(st_block(st_while(expr_not(expr_o_match_str()), st_noop()), st_s_use())); + assertFail("compiler.err.cant.resolve.location", st_block(st_while(expr_not(expr_o_match_str()), st_break()), st_s_use())); + } + + public void testDoWhile() { + assertOK(st_block(st_do_while(st_noop(), expr_not(expr_o_match_str())), st_s_use())); + assertFail("compiler.err.cant.resolve.location", st_block(st_do_while(st_break(), expr_not(expr_o_match_str())), st_s_use())); + } + + public void testFor() { + assertOK(st_block(st_for(expr_empty(), expr_not(expr_o_match_str()), expr_empty(), st_noop()), st_s_use())); + assertFail("compiler.err.cant.resolve.location", st_block(st_for(expr_empty(), expr_not(expr_o_match_str()), expr_empty(), st_break()), st_s_use())); + } + +}