8231826: Implement javac changes for pattern matching for instanceof
Reviewed-by: mcimadamore
Contributed-by: brian.goetz@oracle.com, gavin.bierman@oracle.com, maurizio.cimadamore@oracle.com, srikanth.adayapalam@oracle.com, vicente.romero@oracle.com, jan.lahoda@oracle.com
--- a/src/java.base/share/classes/jdk/internal/PreviewFeature.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/java.base/share/classes/jdk/internal/PreviewFeature.java Wed Nov 27 09:00:01 2019 +0100
@@ -54,6 +54,8 @@
public boolean essentialAPI() default false;
public enum Feature {
- TEXT_BLOCKS;
+ PATTERN_MATCHING_IN_INSTANCEOF,
+ TEXT_BLOCKS,
+ ;
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/BindingPatternTree.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017, 2019, 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;
+
+/**
+ * {@preview Associated with pattern matching for instanceof, a preview feature of
+ * the Java language.
+ *
+ * This interface is associated with <i>pattern matching for instanceof</i>, a preview
+ * feature of the Java language. Preview features
+ * may be removed in a future release, or upgraded to permanent
+ * features of the Java language.}
+ *
+ * A binding pattern tree
+ *
+ * @since 14
+ */
+public interface BindingPatternTree extends PatternTree {
+
+ /**
+ * Returns the type of the bind variable.
+ * @return the type
+ */
+ Tree getType();
+
+ /**
+ * A binding variable name.
+ * @return the name of the binding variable
+ */
+ Name getBinding();
+
+}
+
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java Wed Nov 27 09:00:01 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -51,4 +51,33 @@
* @return the type
*/
Tree getType();
+
+ /**
+ * {@preview Associated with pattern matching for instanceof, a preview feature of
+ * the Java language.
+ *
+ * This method is associated with <i>pattern matching for instanceof</i>, a preview
+ * feature of the Java language. Preview features
+ * may be removed in a future release, or upgraded to permanent
+ * features of the Java language.}
+ *
+ * Returns the tested pattern, or null if this instanceof does not use
+ * a pattern.
+ *
+ * <p>For instanceof with a pattern, i.e. in the following form:
+ * <pre>
+ * <em>expression</em> instanceof <em>type</em> <em>variable name</em>
+ * </pre>
+ * returns the pattern.
+ *
+ * <p>For instanceof without a pattern, i.e. in the following form:
+ * <pre>
+ * <em>expression</em> instanceof <em>type</em>
+ * </pre>
+ * returns null.
+ *
+ * @return the tested pattern, or null if this instanceof does not use a pattern.
+ * @since 14
+ */
+ PatternTree getPattern();
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/PatternTree.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017, 2019, 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;
+
+/**
+ * {@preview Associated with pattern matching for instanceof, a preview feature of
+ * the Java language.
+ *
+ * This interface is associated with <i>pattern matching for instanceof</i>, a preview
+ * feature of the Java language. Preview features
+ * may be removed in a future release, or upgraded to permanent
+ * features of the Java language.}
+ *
+ * A tree node used as the base class for the different kinds of
+ * statements.
+ *
+ * @since 14
+ */
+public interface PatternTree extends Tree {}
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java Wed Nov 27 09:00:01 2019 +0100
@@ -220,6 +220,21 @@
PARENTHESIZED(ParenthesizedTree.class),
/**
+ * {@preview Associated with pattern matching for instanceof, a preview feature of
+ * the Java language.
+ *
+ * This enum constant is associated with <i>pattern matching for instanceof</i>, a preview
+ * feature of the Java language. Preview features
+ * may be removed in a future release, or upgraded to permanent
+ * features of the Java language.}
+ *
+ * Used for instances of {@link BindingPatternTree}.
+ *
+ * @since 14
+ */
+ 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 Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java Wed Nov 27 09:00:01 2019 +0100
@@ -258,6 +258,23 @@
R visitLiteral(LiteralTree node, P p);
/**
+ * {@preview Associated with pattern matching for instanceof, a preview feature of
+ * the Java language.
+ *
+ * This method is associated with <i>pattern matching for instanceof</i>, a preview
+ * feature of the Java language. Preview features
+ * may be removed in a future release, or upgraded to permanent
+ * features of the Java language.}
+ *
+ * Visits an BindingPattern node.
+ * @param node the node being visited
+ * @param p a parameter value
+ * @return a result value
+ * @since 14
+ */
+ 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 Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java Wed Nov 27 09:00:01 2019 +0100
@@ -557,6 +557,19 @@
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
+ * @since 14
+ */
+ @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) {
--- a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java Wed Nov 27 09:00:01 2019 +0100
@@ -667,7 +667,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;
}
@@ -677,6 +681,19 @@
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of scanning
+ * @since 14
+ */
+ @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) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java Wed Nov 27 09:00:01 2019 +0100
@@ -334,6 +334,16 @@
*/
public static final long PREVIEW_ESSENTIAL_API = 1L<<58; //any Symbol kind
+ /**
+ * Flag to indicate the given variable is a match binding variable.
+ */
+ public static final long MATCH_BINDING = 1L<<59;
+
+ /**
+ * A flag to indicate a match binding variable whose scope extends after the current statement.
+ */
+ public static final long MATCH_BINDING_TO_OUTER = 1L<<60;
+
/** Modifier masks.
*/
public static final int
@@ -453,7 +463,9 @@
ANONCONSTR_BASED(Flags.ANONCONSTR_BASED),
NAME_FILLED(Flags.NAME_FILLED),
PREVIEW_API(Flags.PREVIEW_API),
- PREVIEW_ESSENTIAL_API(Flags.PREVIEW_ESSENTIAL_API);
+ PREVIEW_ESSENTIAL_API(Flags.PREVIEW_ESSENTIAL_API),
+ MATCH_BINDING(Flags.MATCH_BINDING),
+ MATCH_BINDING_TO_OUTER(Flags.MATCH_BINDING_TO_OUTER);
Flag(long flag) {
this.value = flag;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java Wed Nov 27 09:00:01 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -165,7 +165,9 @@
* @return true, if given feature is a preview feature.
*/
public boolean isPreview(Feature feature) {
- if (feature == Feature.TEXT_BLOCKS)
+ if (feature == Feature.PATTERN_MATCHING_IN_INSTANCEOF ||
+ feature == Feature.REIFIABLE_TYPES_INSTANCEOF ||
+ feature == Feature.TEXT_BLOCKS)
return true;
//Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
//When real preview features will be added, this method can be implemented to return 'true'
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java Wed Nov 27 09:00:01 2019 +0100
@@ -198,7 +198,10 @@
SWITCH_MULTIPLE_CASE_LABELS(JDK14, Fragments.FeatureMultipleCaseLabels, DiagKind.PLURAL),
SWITCH_RULE(JDK14, Fragments.FeatureSwitchRules, DiagKind.PLURAL),
SWITCH_EXPRESSION(JDK14, Fragments.FeatureSwitchExpressions, DiagKind.PLURAL),
- TEXT_BLOCKS(JDK14, Fragments.FeatureTextBlocks, DiagKind.PLURAL);
+ TEXT_BLOCKS(JDK14, Fragments.FeatureTextBlocks, DiagKind.PLURAL),
+ PATTERN_MATCHING_IN_INSTANCEOF(JDK14, Fragments.FeaturePatternMatchingInstanceof, DiagKind.NORMAL),
+ REIFIABLE_TYPES_INSTANCEOF(JDK14, Fragments.FeatureReifiableTypesInstanceof, DiagKind.PLURAL),
+ ;
enum DiagKind {
NORMAL,
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java Wed Nov 27 09:00:01 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2019, 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
@@ -390,22 +390,26 @@
sym.getKind() == ElementKind.LOCAL_VARIABLE ||
sym.getKind() == ElementKind.RESOURCE_VARIABLE ||
sym.getKind() == ElementKind.EXCEPTION_PARAMETER) {
- // Make sure all type annotations from the symbol are also
- // on the owner. If the owner is an initializer block, propagate
- // to the type.
- final long ownerFlags = sym.owner.flags();
- if ((ownerFlags & Flags.BLOCK) != 0) {
- // Store init and clinit type annotations with the ClassSymbol
- // to allow output in Gen.normalizeDefs.
- ClassSymbol cs = (ClassSymbol) sym.owner.owner;
- if ((ownerFlags & Flags.STATIC) != 0) {
- cs.appendClassInitTypeAttributes(typeAnnotations);
- } else {
- cs.appendInitTypeAttributes(typeAnnotations);
- }
+ appendTypeAnnotationsToOwner(sym, typeAnnotations);
+ }
+ }
+
+ private void appendTypeAnnotationsToOwner(Symbol sym, List<Attribute.TypeCompound> typeAnnotations) {
+ // Make sure all type annotations from the symbol are also
+ // on the owner. If the owner is an initializer block, propagate
+ // to the type.
+ final long ownerFlags = sym.owner.flags();
+ if ((ownerFlags & Flags.BLOCK) != 0) {
+ // Store init and clinit type annotations with the ClassSymbol
+ // to allow output in Gen.normalizeDefs.
+ ClassSymbol cs = (ClassSymbol) sym.owner.owner;
+ if ((ownerFlags & Flags.STATIC) != 0) {
+ cs.appendClassInitTypeAttributes(typeAnnotations);
} else {
- sym.owner.appendUniqueTypeAttributes(sym.getRawTypeAttributes());
+ cs.appendInitTypeAttributes(typeAnnotations);
}
+ } else {
+ sym.owner.appendUniqueTypeAttributes(typeAnnotations);
}
}
@@ -943,10 +947,11 @@
" within frame " + frame);
}
+ case BINDING_PATTERN:
case VARIABLE:
- VarSymbol v = ((JCVariableDecl)frame).sym;
+ VarSymbol v = frame.hasTag(Tag.BINDINGPATTERN) ? ((JCBindingPattern) frame).symbol : ((JCVariableDecl) frame).sym;
if (v.getKind() != ElementKind.FIELD) {
- v.owner.appendUniqueTypeAttributes(v.getRawTypeAttributes());
+ appendTypeAnnotationsToOwner(v, v.getRawTypeAttributes());
}
switch (v.getKind()) {
case LOCAL_VARIABLE:
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java Wed Nov 27 09:00:01 2019 +0100
@@ -1122,6 +1122,13 @@
}
@Override
+ public void visitBindingPattern(JCTree.JCBindingPattern tree) {
+ //type binding pattern's type will be annotated separatelly, avoid
+ //adding its annotations into the owning method here (would clash
+ //with repeatable annotations).
+ }
+
+ @Override
public void visitClassDef(JCClassDecl tree) {
// We can only hit a classdef if it is declared within
// a method. Ignore it - the class will be visited
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Wed Nov 27 09:00:01 2019 +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;
@@ -110,6 +111,7 @@
final Enter enter;
final Target target;
final Types types;
+ final Preview preview;
final JCDiagnostic.Factory diags;
final TypeAnnotations typeAnnotations;
final DeferredLintHandler deferredLintHandler;
@@ -117,6 +119,7 @@
final Dependencies dependencies;
final Annotate annotate;
final ArgumentAttr argumentAttr;
+ final MatchBindingsComputer matchBindingsComputer;
public static Attr instance(Context context) {
Attr instance = context.get(attrKey);
@@ -145,6 +148,7 @@
cfolder = ConstFold.instance(context);
target = Target.instance(context);
types = Types.instance(context);
+ preview = Preview.instance(context);
diags = JCDiagnostic.Factory.instance(context);
annotate = Annotate.instance(context);
typeAnnotations = TypeAnnotations.instance(context);
@@ -152,6 +156,7 @@
typeEnvs = TypeEnvs.instance(context);
dependencies = Dependencies.instance(context);
argumentAttr = ArgumentAttr.instance(context);
+ matchBindingsComputer = MatchBindingsComputer.instance(context);
Options options = Options.instance(context);
@@ -161,6 +166,9 @@
allowLambda = Feature.LAMBDA.allowedInSource(source);
allowDefaultMethods = Feature.DEFAULT_METHODS.allowedInSource(source);
allowStaticInterfaceMethods = Feature.STATIC_INTERFACE_METHODS.allowedInSource(source);
+ allowReifiableTypesInInstanceof =
+ Feature.REIFIABLE_TYPES_INSTANCEOF.allowedInSource(source) &&
+ (!preview.isPreview(Feature.REIFIABLE_TYPES_INSTANCEOF) || preview.isEnabled());
sourceName = source.name;
useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning");
@@ -193,6 +201,10 @@
*/
boolean allowStaticInterfaceMethods;
+ /** Switch: reifiable types in instanceof enabled?
+ */
+ boolean allowReifiableTypesInInstanceof;
+
/**
* Switch: warn about use of variable before declaration?
* RFE: 6425594
@@ -292,6 +304,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));
}
@@ -1298,29 +1312,73 @@
public void visitDoLoop(JCDoWhileLoop tree) {
attribStat(tree.body, env.dup(tree));
attribExpr(tree.cond, env, syms.booleanType);
+ if (!breaksOutOf(tree, tree.body)) {
+ //include condition's body when false after the while, if cannot get out of the loop
+ List<BindingSymbol> bindings = matchBindingsComputer.getMatchBindings(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 condition's bindings when true in the body:
+ Env<AttrContext> whileEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, true));
+ try {
+ attribStat(tree.body, whileEnv.dup(tree));
+ } finally {
+ whileEnv.info.scope.leave();
+ }
+ if (!breaksOutOf(tree, tree.body)) {
+ //include condition's bindings when false after the while, if cannot get out of the loop
+ List<BindingSymbol> bindings =
+ matchBindingsComputer.getMatchBindings(tree.cond, false);
+
+ bindings.forEach(env.info.scope::enter);
+ bindings.forEach(BindingSymbol::preserveBinding);
+ }
result = null;
}
+ private boolean breaksOutOf(JCTree loop, JCTree body) {
+ preFlow(body);
+ return flow.breaksOutOf(env, loop, body, make);
+ }
+
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 condition's bindings when true in the body and step:
+ matchBindings = matchBindingsComputer.getMatchBindings(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();
+ }
result = null;
}
finally {
loopEnv.info.scope.leave();
}
+ if (!breaksOutOf(tree, tree.body)) {
+ //include condition's body when false after the while, if cannot get out of the loop
+ List<BindingSymbol> bindings =
+ matchBindingsComputer.getMatchBindings(tree.cond, false);
+
+ bindings.forEach(env.info.scope::enter);
+ bindings.forEach(BindingSymbol::preserveBinding);
+ }
}
public void visitForeachLoop(JCEnhancedForLoop tree) {
@@ -1673,8 +1731,26 @@
unknownExprInfo :
resultInfo.dup(conditionalContext(resultInfo.checkContext));
- Type truetype = attribTree(tree.truepart, env, condInfo);
- Type falsetype = attribTree(tree.falsepart, env, condInfo);
+
+ // x ? y : z
+ // include x's bindings when true in y
+ // include x's bindings when false in z
+
+ Type truetype;
+ Env<AttrContext> trueEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, true));
+ try {
+ truetype = attribTree(tree.truepart, trueEnv, condInfo);
+ } finally {
+ trueEnv.info.scope.leave();
+ }
+
+ Type falsetype;
+ Env<AttrContext> falseEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(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()),
@@ -1829,15 +1905,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's bindings when true in y
+ // include x's bindings when false in z
+
+ List<BindingSymbol> thenBindings = matchBindingsComputer.getMatchBindings(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 = matchBindingsComputer.getMatchBindings(tree.cond, false);
+
+ if (tree.elsepart != null) {
+ 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()
@@ -3521,7 +3659,32 @@
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));
+ // x && y
+ // include x's bindings when true in y
+
+ // x || y
+ // include x's bindings when false in y
+
+ List<BindingSymbol> matchBindings;
+ switch (tree.getTag()) {
+ case AND:
+ matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, true);
+ break;
+ case OR:
+ matchBindings = matchBindingsComputer.getMatchBindings(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);
@@ -3587,19 +3750,63 @@
public void visitTypeTest(JCInstanceOf tree) {
Type exprtype = chk.checkNullOrRefType(
tree.expr.pos(), attribExpr(tree.expr, env));
- Type clazztype = attribType(tree.clazz, env);
+ Type clazztype;
+ JCTree typeTree;
+ if (tree.pattern.getTag() == BINDINGPATTERN) {
+ attribTree(tree.pattern, env, unknownExprInfo);
+ clazztype = tree.pattern.type;
+ JCBindingPattern pattern = (JCBindingPattern) tree.pattern;
+ typeTree = pattern.vartype;
+ if (!clazztype.hasTag(TYPEVAR)) {
+ clazztype = chk.checkClassOrArrayType(pattern.vartype.pos(), clazztype);
+ }
+ } else {
+ clazztype = attribType(tree.pattern, env);
+ typeTree = tree.pattern;
+ }
if (!clazztype.hasTag(TYPEVAR)) {
- clazztype = chk.checkClassOrArrayType(tree.clazz.pos(), clazztype);
+ clazztype = chk.checkClassOrArrayType(typeTree.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);
+ boolean valid = false;
+ if (allowReifiableTypesInInstanceof) {
+ if (preview.isPreview(Feature.REIFIABLE_TYPES_INSTANCEOF)) {
+ preview.warnPreview(tree.expr.pos(), Feature.REIFIABLE_TYPES_INSTANCEOF);
+ }
+ Warner warner = new Warner();
+ if (!types.isCastable(exprtype, clazztype, warner)) {
+ chk.basicHandler.report(tree.expr.pos(),
+ diags.fragment(Fragments.InconvertibleTypes(exprtype, clazztype)));
+ } else if (warner.hasLint(LintCategory.UNCHECKED)) {
+ log.error(tree.expr.pos(),
+ Errors.InstanceofReifiableNotSafe(exprtype, clazztype));
+ } else {
+ valid = true;
+ }
+ } else {
+ log.error(typeTree.pos(), Errors.IllegalGenericTypeForInstof);
+ }
+ if (!valid) {
+ clazztype = types.createErrorType(clazztype);
+ }
+ }
+ chk.validate(typeTree, env, false);
chk.checkCastable(tree.expr.pos(), exprtype, clazztype);
result = check(tree, syms.booleanType, KindSelector.VAL, resultInfo);
}
+ public void visitBindingPattern(JCBindingPattern tree) {
+ ResultInfo varInfo = new ResultInfo(KindSelector.TYP, resultInfo.pt, resultInfo.checkContext);
+ tree.type = attribTree(tree.vartype, env, varInfo);
+ VarSymbol v = tree.symbol = new BindingSymbol(tree.name, tree.vartype.type, env.info.scope.owner);
+ if (chk.checkUnique(tree.pos(), v, env.info.scope)) {
+ chk.checkTransparentVar(tree.pos(), v, env.info.scope);
+ }
+ annotate.queueScanTreeAndTypeAnnotate(tree.vartype, env, v, tree.pos());
+ annotate.flush();
+ result = tree.type;
+ }
+
public void visitIndexed(JCArrayAccess tree) {
Type owntype = types.createErrorType(tree.type);
Type atype = attribExpr(tree.indexed, env);
@@ -4991,8 +5198,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) {
@@ -5253,6 +5460,15 @@
}
@Override
+ public void visitBindingPattern(JCBindingPattern 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,
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java Wed Nov 27 09:00:01 2019 +0100
@@ -3486,6 +3486,11 @@
duplicateErasureError(pos, sym, byName);
sym.flags_field |= CLASH;
return true;
+ } else if ((sym.flags() & MATCH_BINDING) != 0 &&
+ (byName.flags() & MATCH_BINDING) != 0 &&
+ (byName.flags() & MATCH_BINDING_TO_OUTER) == 0) {
+ //this error will be reported separatelly in MatchBindingsComputer
+ return false;
} else {
duplicateError(pos, byName);
return false;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java Wed Nov 27 09:00:01 2019 +0100
@@ -59,9 +59,10 @@
ATTR(4),
FLOW(5),
TRANSTYPES(6),
- UNLAMBDA(7),
- LOWER(8),
- GENERATE(9);
+ TRANSPATTERNS(7),
+ UNLAMBDA(8),
+ LOWER(9),
+ GENERATE(10);
CompileState(int value) {
this.value = value;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java Wed Nov 27 09:00:01 2019 +0100
@@ -255,6 +255,41 @@
}
}
+ public boolean aliveAfter(Env<AttrContext> env, JCTree that, TreeMaker make) {
+ //we need to disable diagnostics temporarily; the problem is that if
+ //"that" 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);
+ }
+ }
+
+ public boolean breaksOutOf(Env<AttrContext> env, JCTree loop, JCTree body, TreeMaker make) {
+ //we need to disable diagnostics temporarily; the problem is that if
+ //"that" 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 {
+ boolean[] breaksOut = new boolean[1];
+ SnippetBreakAnalyzer analyzer = new SnippetBreakAnalyzer(loop);
+
+ analyzer.analyzeTree(env, body, make);
+ return analyzer.breaksOut();
+ } finally {
+ log.popDiagnosticHandler(diagHandler);
+ }
+ }
+
/**
* Definite assignment scan mode
*/
@@ -1467,6 +1502,38 @@
}
/**
+ * Determine if alive after the given tree.
+ */
+ class SnippetAliveAnalyzer extends AliveAnalyzer {
+ @Override
+ public void visitClassDef(JCClassDecl tree) {
+ //skip
+ }
+ public boolean isAlive() {
+ return super.alive != Liveness.DEAD;
+ }
+ }
+
+ class SnippetBreakAnalyzer extends AliveAnalyzer {
+ private final JCTree loop;
+ private boolean breaksOut;
+
+ public SnippetBreakAnalyzer(JCTree loop) {
+ this.loop = loop;
+ }
+
+ @Override
+ public void visitBreak(JCBreak tree) {
+ breaksOut |= (super.alive == Liveness.ALIVE && tree.target == loop);
+ super.visitBreak(tree);
+ }
+
+ public boolean breaksOut() {
+ return breaksOut;
+ }
+ }
+
+ /**
* Specialized pass that performs DA/DU on a lambda
*/
class LambdaAssignAnalyzer extends AssignAnalyzer {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MatchBindingsComputer.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2017, 2019, 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.Context;
+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 {
+ protected static final Context.Key<MatchBindingsComputer> matchBindingsComputerKey = new Context.Key<>();
+
+ private final Log log;
+ private final Types types;
+ boolean whenTrue;
+ List<BindingSymbol> bindings;
+
+ public static MatchBindingsComputer instance(Context context) {
+ MatchBindingsComputer instance = context.get(matchBindingsComputerKey);
+ if (instance == null)
+ instance = new MatchBindingsComputer(context);
+ return instance;
+ }
+
+ protected MatchBindingsComputer(Context context) {
+ this.log = Log.instance(context);
+ this.types = Types.instance(context);
+ }
+
+ public List<BindingSymbol> getMatchBindings(JCTree expression, boolean whenTrue) {
+ this.whenTrue = whenTrue;
+ this.bindings = List.nil();
+ scan(expression);
+ return bindings;
+ }
+
+ @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 (may be eventually relaxed to merge variables of same type)
+ List<BindingSymbol> list = List.nil();
+ for (BindingSymbol v1 : lhsBindings) {
+ for (BindingSymbol v2 : rhsBindings) {
+ if (v1.name == v2.name) {
+ log.error(tree.pos(), Errors.MatchBindingExists);
+ list = list.append(v2);
+ }
+ }
+ }
+ 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 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;
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 2017, 2019, 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.comp.MatchBindingsComputer.BindingSymbol;
+import com.sun.tools.javac.jvm.Target;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.LetExpr;
+import com.sun.tools.javac.util.List;
+
+/**
+ * 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 final Symtab syms;
+ private final Types types;
+ private final Operators operators;
+ private final Log log;
+ private final ConstFold constFold;
+ private final Names names;
+ private final Target target;
+ private final MatchBindingsComputer matchBindingsComputer;
+ private TreeMaker make;
+
+ 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);
+ target = Target.instance(context);
+ matchBindingsComputer = MatchBindingsComputer.instance(context);
+ debugTransPatterns = Options.instance(context).isSet("debug.patterns");
+ }
+
+ @Override
+ public void visitTypeTest(JCInstanceOf tree) {
+ if (tree.pattern.hasTag(Tag.BINDINGPATTERN)) {
+ //E instanceof T N
+ //=>
+ //(let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))
+ 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() | Flags.SYNTHETIC,
+ names.fromString(pattSym.name.toString() + target.syntheticNameChar() + "temp"),
+ tempType,
+ patt.symbol.owner);
+ JCExpression translatedExpr = translate(tree.expr);
+ Type castTargetType = types.boxedTypeOrType(pattSym.erasure(types));
+
+ 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);
+ ((LetExpr) result).needsCond = true;
+ } else {
+ super.visitTypeTest(tree);
+ }
+ }
+
+ @Override
+ public void visitBinary(JCBinary tree) {
+ List<BindingSymbol> matchBindings;
+ switch (tree.getTag()) {
+ case AND:
+ matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, true);
+ break;
+ case OR:
+ matchBindings = matchBindingsComputer.getMatchBindings(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(
+ matchBindingsComputer.getMatchBindings(tree.cond, true)
+ .appendList(matchBindingsComputer.getMatchBindings(tree.cond, false)));
+ try {
+ super.visitConditional(tree);
+ result = bindingContext.decorateExpression(tree);
+ } finally {
+ bindingContext.pop();
+ }
+ }
+
+ @Override
+ public void visitIf(JCIf tree) {
+ bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
+ try {
+ super.visitIf(tree);
+ result = bindingContext.decorateStatement(tree);
+ } finally {
+ bindingContext.pop();
+ }
+ }
+
+ @Override
+ public void visitForLoop(JCForLoop tree) {
+ bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
+ try {
+ super.visitForLoop(tree);
+ result = bindingContext.decorateStatement(tree);
+ } finally {
+ bindingContext.pop();
+ }
+ }
+
+ @Override
+ public void visitWhileLoop(JCWhileLoop tree) {
+ bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
+ try {
+ super.visitWhileLoop(tree);
+ result = bindingContext.decorateStatement(tree);
+ } finally {
+ bindingContext.pop();
+ }
+ }
+
+ @Override
+ public void visitDoLoop(JCDoWhileLoop tree) {
+ bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
+ try {
+ super.visitDoLoop(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) {
+ //{
+ // if (E instanceof T N) {
+ // return ;
+ // }
+ // //use of N:
+ //}
+ //=>
+ //{
+ // T N;
+ // if ((let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))) {
+ // return ;
+ // }
+ // //use of N:
+ //}
+ 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;
+ }
+
+ 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 = target;
+ return result;
+ }
+
+ private List<BindingSymbol> getMatchBindings(JCExpression cond) {
+ return matchBindingsComputer.getMatchBindings(cond, true)
+ .appendList(matchBindingsComputer.getMatchBindings(cond, false));
+ }
+ 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 -> {
+ VarSymbol res = new VarSymbol(v.flags(), v.name, v.type, v.owner);
+ res.setTypeAttributes(v.getRawTypeAttributes());
+ return res;
+ }));
+ }
+
+ @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;
+ //if (E instanceof T N) {
+ // //use N
+ //}
+ //=>
+ //{
+ // T N;
+ // if ((let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))) {
+ // //use N
+ // }
+ //}
+ 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) {
+ //E instanceof T N && /*use of N*/
+ //=>
+ //(let T N; (let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp)) && /*use of N*/)
+ 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, null);
+ }
+ }
+}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java Wed Nov 27 09:00:01 2019 +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 &&
@@ -780,7 +787,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 Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java Wed Nov 27 09:00:01 2019 +0100
@@ -35,6 +35,7 @@
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCAssignOp;
import com.sun.tools.javac.tree.JCTree.JCBinary;
+import com.sun.tools.javac.tree.JCTree.JCBindingPattern;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCBreak;
import com.sun.tools.javac.tree.JCTree.JCCase;
@@ -253,6 +254,18 @@
}
@Override
+ public void visitBindingPattern(JCBindingPattern tree) {
+ JCBindingPattern that = (JCBindingPattern) parameter;
+ result =
+ scan(tree.vartype, that.vartype)
+ && tree.name == that.name;
+ if (!result) {
+ return;
+ }
+ equiv.put(tree.symbol, that.symbol);
+ }
+
+ @Override
public void visitBlock(JCBlock tree) {
JCBlock that = (JCBlock) parameter;
result = tree.flags == that.flags && scan(tree.stats, that.stats);
@@ -591,7 +604,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/comp/TreeHasher.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeHasher.java Wed Nov 27 09:00:01 2019 +0100
@@ -106,6 +106,12 @@
}
@Override
+ public void visitBindingPattern(JCTree.JCBindingPattern tree) {
+ symbolHashes.computeIfAbsent(tree.symbol, k -> symbolHashes.size());
+ super.visitBindingPattern(tree);
+ }
+
+ @Override
public void visitVarDef(JCVariableDecl tree) {
symbolHashes.computeIfAbsent(tree.sym, k -> symbolHashes.size());
super.visitVarDef(tree);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java Wed Nov 27 09:00:01 2019 +0100
@@ -473,7 +473,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/Code.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java Wed Nov 27 09:00:01 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, 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
@@ -50,6 +50,7 @@
import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_String;
import static com.sun.tools.javac.jvm.UninitializedType.*;
import static com.sun.tools.javac.jvm.ClassWriter.StackMapTableFrame;
+import java.util.Arrays;
/** An internal structure that corresponds to the code attribute of
* methods in a classfile. The class also provides some utility operations to
@@ -2075,6 +2076,7 @@
lvar[adr] = v.dup();
v.closeRange(length);
putVar(v);
+ fillLocalVarPosition(v);
} else {
v.removeLastRange();
}
@@ -2106,20 +2108,31 @@
private void fillLocalVarPosition(LocalVar lv) {
if (lv == null || lv.sym == null || lv.sym.isExceptionParameter()|| !lv.sym.hasTypeAnnotations())
return;
- LocalVar.Range widestRange = lv.getWidestRange();
+ LocalVar.Range[] validRanges = lv.aliveRanges.stream().filter(r -> r.closed() && r.length > 0).toArray(s -> new LocalVar.Range[s]);
+ if (validRanges.length == 0)
+ return ;
+ int[] lvarOffset = Arrays.stream(validRanges).mapToInt(r -> r.start_pc).toArray();
+ int[] lvarLength = Arrays.stream(validRanges).mapToInt(r -> r.length).toArray();
+ int[] lvarIndex = Arrays.stream(validRanges).mapToInt(r -> lv.reg).toArray();
for (Attribute.TypeCompound ta : lv.sym.getRawTypeAttributes()) {
TypeAnnotationPosition p = ta.position;
- if (widestRange.closed() && widestRange.length > 0) {
- p.lvarOffset = new int[] { (int)widestRange.start_pc };
- p.lvarLength = new int[] { (int)widestRange.length };
- p.lvarIndex = new int[] { (int)lv.reg };
- p.isValidOffset = true;
- } else {
- p.isValidOffset = false;
- }
+ p.lvarOffset = appendArray(p.lvarOffset, lvarOffset);
+ p.lvarLength = appendArray(p.lvarLength, lvarLength);
+ p.lvarIndex = appendArray(p.lvarIndex, lvarIndex);
+ p.isValidOffset = true;
}
}
+ private int[] appendArray(int[] source, int[] append) {
+ if (source == null || source.length == 0) return append;
+
+ int[] result = new int[source.length + append.length];
+
+ System.arraycopy(source, 0, result, 0, source.length);
+ System.arraycopy(append, 0, result, source.length, append.length);
+ return result;
+ }
+
// Method to be called after compressCatchTable to
// fill in the exception table index for type
// annotations on exception parameters.
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Wed Nov 27 09:00:01 2019 +0100
@@ -2216,7 +2216,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 Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Wed Nov 27 09:00:01 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, 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
@@ -1558,6 +1558,12 @@
env.tree = transTypes.translateTopLevelClass(env.tree, localMake);
compileStates.put(env, CompileState.TRANSTYPES);
+ if (shouldStop(CompileState.TRANSPATTERNS))
+ return;
+
+ env.tree = TransPatterns.instance(context).translateTopLevelClass(env, env.tree, localMake);
+ compileStates.put(env, CompileState.TRANSPATTERNS);
+
if (Feature.LAMBDA.allowedInSource(source) && scanner.hasLambdas) {
if (shouldStop(CompileState.UNLAMBDA))
return;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java Wed Nov 27 09:00:01 2019 +0100
@@ -893,6 +893,7 @@
/* Expression2Rest = {infixop Expression3}
* | Expression3 instanceof Type
+ * | Expression3 instanceof Pattern
* infixop = "||"
* | "&&"
* | "|"
@@ -915,13 +916,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();
+ JCTree pattern = parseType();
+ if (token.kind == IDENTIFIER) {
+ checkSourceLevel(token.pos, Feature.PATTERN_MATCHING_IN_INSTANCEOF);
+ pattern = toP(F.at(token.pos).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];
}
@@ -938,19 +950,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.
*/
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Wed Nov 27 09:00:01 2019 +0100
@@ -546,6 +546,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
@@ -1416,6 +1420,10 @@
compiler.misc.varargs.trustme.on.reifiable.varargs=\
Varargs element type {0} is reifiable.
+# 0: type, 1: type
+compiler.err.instanceof.reifiable.not.safe=\
+ {0} cannot be safely cast to {1}
+
# 0: symbol
compiler.misc.varargs.trustme.on.non.varargs.meth=\
Method {0} is not a varargs method.
@@ -2909,6 +2917,12 @@
compiler.misc.feature.var.syntax.in.implicit.lambda=\
var syntax in implicit lambdas
+compiler.misc.feature.pattern.matching.instanceof=\
+ pattern matching in instanceof
+
+compiler.misc.feature.reifiable.types.instanceof=\
+ reifiable types in instanceof
+
compiler.warn.underscore.as.identifier=\
as of release 9, ''_'' is a keyword, and may not be used as an identifier
@@ -3399,6 +3413,9 @@
compiler.err.illegal.argument.for.option=\
illegal argument for {0}: {1}
+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 Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java Wed Nov 27 09:00:01 2019 +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;
@@ -238,6 +239,10 @@
*/
TYPETEST,
+ /** Patterns.
+ */
+ BINDINGPATTERN,
+
/** Indexed array expressions, of type Indexed.
*/
INDEXED,
@@ -2135,10 +2140,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); }
@@ -2146,7 +2151,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)
@@ -2160,6 +2171,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 {
@@ -3133,6 +3198,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);
@@ -3197,6 +3263,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 Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java Wed Nov 27 09:00:01 2019 +0100
@@ -234,6 +234,14 @@
printExprs(trees, ", ");
}
+
+ /** Derived visitor method: print pattern.
+ */
+
+ public void printPattern(JCTree tree) throws IOException {
+ printExpr(tree);
+ }
+
/** Derived visitor method: print list of statements, each on a separate line.
*/
public void printStats(List<? extends JCTree> trees) throws IOException {
@@ -877,6 +885,16 @@
}
}
+ public void visitBindingPattern(JCBindingPattern patt) {
+ try {
+ printExpr(patt.vartype);
+ print(" ");
+ print(patt.name);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
public void visitSynchronized(JCSynchronized tree) {
try {
print("synchronized ");
@@ -1283,7 +1301,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 Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java Wed Nov 27 09:00:01 2019 +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;
@@ -481,8 +480,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 Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java Wed Nov 27 09:00:01 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, 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
@@ -490,6 +490,10 @@
return getStartPos(node.vartype);
}
}
+ case BINDINGPATTERN: {
+ JCBindingPattern node = (JCBindingPattern)tree;
+ return getStartPos(node.vartype);
+ }
case ERRONEOUS: {
JCErroneous node = (JCErroneous)tree;
if (node.errs != null && node.errs.nonEmpty())
@@ -574,7 +578,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 +851,8 @@
if (node.type != null)
return node.type.tsym;
return null;
+ case BINDINGPATTERN:
+ return ((JCBindingPattern) node).symbol;
default:
return null;
}
@@ -1225,4 +1231,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 Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java Wed Nov 27 09:00:01 2019 +0100
@@ -29,7 +29,6 @@
import com.sun.source.tree.CaseTree;
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.*;
@@ -465,6 +464,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 Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java Wed Nov 27 09:00:01 2019 +0100
@@ -299,7 +299,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 Mon Sep 09 11:43:16 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java Wed Nov 27 09:00:01 2019 +0100
@@ -354,7 +354,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/annotations/typeAnnotations/classfile/ClassfileTestHelper.java Mon Sep 09 11:43:16 2019 -0400
+++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/ClassfileTestHelper.java Wed Nov 27 09:00:01 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -27,12 +27,14 @@
import java.util.List;
import com.sun.tools.classfile.*;
+import java.util.ArrayList;
public class ClassfileTestHelper {
int expected_tinvisibles = 0;
int expected_tvisibles = 0;
int expected_invisibles = 0;
int expected_visibles = 0;
+ List<String> extraOptions = List.of();
//Makes debugging much easier. Set to 'false' for less output.
public Boolean verbose = true;
@@ -48,8 +50,9 @@
}
File compile(File f) {
- int rc = com.sun.tools.javac.Main.compile(new String[] {
- "-g", f.getPath() });
+ List<String> options = new ArrayList<>(List.of("-g", f.getPath()));
+ options.addAll(extraOptions);
+ int rc = com.sun.tools.javac.Main.compile(options.toArray(new String[0]));
if (rc != 0)
throw new Error("compilation failed. rc=" + rc);
String path = f.getPath();
--- a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest2.java Mon Sep 09 11:43:16 2019 -0400
+++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest2.java Wed Nov 27 09:00:01 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -30,9 +30,13 @@
import com.sun.tools.classfile.*;
import java.io.File;
+import java.util.List;
public class CombinationsTargetTest2 extends ClassfileTestHelper {
+ private static final String JDK_VERSION =
+ Integer.toString(Runtime.getRuntime().version().feature());
+
// Test count helps identify test case in event of failure.
int testcount = 0;
@@ -45,7 +49,9 @@
src5("(repeating) type annotations on field in anonymous class", false),
src6("(repeating) type annotations on void method declaration", false),
src7("(repeating) type annotations in use of instanceof", true),
- src8("(repeating) type annotations in use of instanceof in method", true);
+ src7p("(repeating) type annotations in use of instanceof with type test pattern", true),
+ src8("(repeating) type annotations in use of instanceof in method", true),
+ src8p("(repeating) type annotations in use of instanceof with type test pattern in method", true);
String description;
Boolean local;
@@ -92,8 +98,12 @@
test( 0, 0, 0, 2, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src6);
test( 2, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src7);
test( 0, 2, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src7);
+ test( 2, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src7p);
+ test( 0, 2, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src7p);
test( 4, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src8);
test( 0, 4, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src8);
+ test( 4, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src8p);
+ test( 0, 4, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src8p);
break;
case "FIELD":
test( 8, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src1);
@@ -122,6 +132,7 @@
expected_tinvisibles = tinv;
expected_visibles = vis;
expected_invisibles = inv;
+ extraOptions = List.of();
File testFile = null;
String tname="Test" + N.toString();
hasInnerClass=false;
@@ -385,6 +396,24 @@
"\n\n";
hasInnerClass=false;
break;
+ case src7p: // (repeating) type annotations in use of instanceof with type test pattern
+ /*
+ * class Test10{
+ * String data = "test";
+ * boolean dataIsString = ( data instanceof @A @B @A @B String str);
+ * }
+ */
+ source = new String( source +
+ "// " + src.description + "\n" +
+ "class "+ testname + "{\n" +
+ " String data = \"test\";\n" +
+ " boolean dataIsString = ( data instanceof _As_ _Bs_ String str && str.isEmpty());\n" +
+ "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
+ "\n\n";
+ extraOptions = List.of("--enable-preview",
+ "-source", JDK_VERSION);
+ hasInnerClass=false;
+ break;
case src8: // (repeating) type annotations in use of instanceof
/*
* class Test20{
@@ -411,6 +440,34 @@
"\n\n";
hasInnerClass=false;
break;
+ case src8p: // (repeating) type annotations in use of instanceof with type test pattern
+ /*
+ * class Test20{
+ * String data = "test";
+ * Boolean isString() {
+ * if( data instanceof @A @B @A @B String )
+ * return true;
+ * else
+ * return( data instanceof @A @B @A @B String );
+ * }
+ * }
+ */
+ source = new String( source +
+ "// " + src.description + "\n" +
+ "class "+ testname + "{\n" +
+ " String data = \"test\";\n" +
+ " Boolean isString() { \n" +
+ " if( data instanceof _As_ _Bs_ String str)\n" +
+ " return true;\n" +
+ " else\n" +
+ " return( data instanceof _As_ _Bs_ String str && str.isEmpty());\n" +
+ " }\n" +
+ "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
+ "\n\n";
+ extraOptions = List.of("--enable-preview",
+ "-source", JDK_VERSION);
+ hasInnerClass=false;
+ break;
}
return imports + source;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/Patterns.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2009, 2019, 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 Verify type annotation on binding patterns
+ * @library /tools/lib
+ * @modules java.compiler
+ * jdk.jdeps/com.sun.tools.javap
+ * @build toolbox.JavapTask
+ * @compile --enable-preview -source ${jdk.version} Patterns.java
+ * @run main/othervm --enable-preview Patterns
+ */
+
+import java.lang.annotation.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import toolbox.JavapTask;
+import toolbox.Task;
+import toolbox.ToolBox;
+
+public class Patterns {
+
+ private ToolBox tb = new ToolBox();
+
+ public static void main(String[] args) throws Exception {
+ new Patterns().run();
+ }
+
+ public void run() throws Exception {
+ String out = new JavapTask(tb)
+ .options("-private",
+ "-verbose")
+ .classpath(System.getProperty("test.classes"))
+ .classes("Patterns$SimpleBindingPattern")
+ .run()
+ .getOutputLines(Task.OutputKind.DIRECT)
+ .stream()
+ .collect(Collectors.joining("\n"));
+
+ String constantPool = out.substring(0, out.indexOf('{'));
+
+ out = out.replaceAll("(?ms) *Code:.*?\n( *RuntimeInvisibleTypeAnnotations:)", "$1");
+ out = out.substring(out.indexOf('{'));
+ out = out.substring(0, out.lastIndexOf('}') + 1);
+
+ String A = snipCPNumber(constantPool, "LPatterns$SimpleBindingPattern$A;");
+ String CA = snipCPNumber(constantPool, "LPatterns$SimpleBindingPattern$CA;");
+ String value = snipCPNumber(constantPool, "value");
+
+ String expected = """
+ {
+ private static final java.lang.Object o;
+ descriptor: Ljava/lang/Object;
+ flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL
+
+ private static final boolean B1s;
+ descriptor: Z
+ flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL
+
+ private static final boolean B1m;
+ descriptor: Z
+ flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL
+
+ private final boolean B2s;
+ descriptor: Z
+ flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+
+ private final boolean B2m;
+ descriptor: Z
+ flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+
+ public Patterns$SimpleBindingPattern();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeInvisibleTypeAnnotations:
+ 0: #_A_(): LOCAL_VARIABLE, {start_pc=257, length=18, index=2}
+ Patterns$SimpleBindingPattern$A
+ 1: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=297, length=19, index=3}
+ Patterns$SimpleBindingPattern$CA(
+ value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
+ )
+ 2: #_A_(): LOCAL_VARIABLE, {start_pc=22, length=18, index=1}
+ Patterns$SimpleBindingPattern$A
+ 3: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=62, length=18, index=1}
+ Patterns$SimpleBindingPattern$CA(
+ value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
+ )
+ 4: #_A_(): LOCAL_VARIABLE, {start_pc=101, length=18, index=2}
+ Patterns$SimpleBindingPattern$A
+ 5: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=141, length=19, index=3}
+ Patterns$SimpleBindingPattern$CA(
+ value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
+ )
+ 6: #_A_(): LOCAL_VARIABLE, {start_pc=179, length=18, index=2}
+ Patterns$SimpleBindingPattern$A
+ 7: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=219, length=19, index=3}
+ Patterns$SimpleBindingPattern$CA(
+ value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
+ )
+
+ void testPatterns();
+ descriptor: ()V
+ flags: (0x0000)
+ RuntimeInvisibleTypeAnnotations:
+ 0: #_A_(): LOCAL_VARIABLE, {start_pc=17, length=18, index=2}
+ Patterns$SimpleBindingPattern$A
+ 1: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=57, length=19, index=3}
+ Patterns$SimpleBindingPattern$CA(
+ value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
+ )
+
+ void testPatternsDesugared();
+ descriptor: ()V
+ flags: (0x0000)
+ RuntimeInvisibleTypeAnnotations:
+ 0: #_A_(): LOCAL_VARIABLE, {start_pc=17, length=15, index=1; start_pc=51, length=15, index=1}
+ Patterns$SimpleBindingPattern$A
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ RuntimeInvisibleTypeAnnotations:
+ 0: #_A_(): LOCAL_VARIABLE, {start_pc=22, length=18, index=0}
+ Patterns$SimpleBindingPattern$A
+ 1: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=61, length=18, index=0}
+ Patterns$SimpleBindingPattern$CA(
+ value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
+ )
+ 2: #_A_(): LOCAL_VARIABLE, {start_pc=100, length=18, index=1}
+ Patterns$SimpleBindingPattern$A
+ 3: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=137, length=18, index=2}
+ Patterns$SimpleBindingPattern$CA(
+ value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
+ )
+ }""".replace("_A_", A).replace("_CA_", CA).replace("_value_", value);
+
+ if (!expected.equals(out)) {
+ throw new AssertionError("Unexpected output:\n" + out + "\nexpected:\n" + expected);
+ }
+ }
+
+ private String snipCPNumber(String constantPool, String expectedConstant) {
+ Matcher m = Pattern.compile("#([0-9]+).*" + Pattern.quote(expectedConstant))
+ .matcher(constantPool);
+ if (!m.find()) {
+ throw new AssertionError("Cannot find constant pool item");
+ }
+
+ return m.group(1);
+ }
+
+ /*********************** Test class *************************/
+ static class SimpleBindingPattern {
+ @Target(ElementType.TYPE_USE)
+ @Repeatable(CA.class)
+ @interface A {}
+ @Target(ElementType.TYPE_USE)
+ @interface CA {
+ public A[] value();
+ }
+
+ private static final Object o = "";
+ private static final boolean B1s = o instanceof @A String s && s.isEmpty();
+ private static final boolean B1m = o instanceof @A @A String s && s.isEmpty();
+ private final boolean B2s = o instanceof @A String s && s.isEmpty();
+ private final boolean B2m = o instanceof @A @A String s && s.isEmpty();
+
+ static {
+ boolean B3s = o instanceof @A String s && s.isEmpty();
+ boolean B3m = o instanceof @A @A String s && s.isEmpty();
+ }
+
+ {
+ boolean B4s = o instanceof @A String s && s.isEmpty();
+ boolean B4m = o instanceof @A @A String s && s.isEmpty();
+ }
+
+ {
+ boolean B5s = o instanceof @A String s && s.isEmpty();
+ boolean B5m = o instanceof @A @A String s && s.isEmpty();
+ }
+
+ public SimpleBindingPattern() {
+ boolean B6s = o instanceof @A String s && s.isEmpty();
+ boolean B6m = o instanceof @A @A String s && s.isEmpty();
+ }
+
+ void testPatterns() {
+ boolean B7s = o instanceof @A String s && s.isEmpty();
+ boolean B7m = o instanceof @A @A String s && s.isEmpty();
+ }
+
+ void testPatternsDesugared() {
+ @A String s;
+ boolean B8s = o instanceof String && (s = (String) o) == s && s.isEmpty();
+ boolean B8sx = o instanceof String && (s = (String) o) == s && s.isEmpty();
+ }
+ }
+}
--- a/test/langtools/tools/javac/api/TestGetElementReference.java Mon Sep 09 11:43:16 2019 -0400
+++ b/test/langtools/tools/javac/api/TestGetElementReference.java Wed Nov 27 09:00:01 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -51,6 +51,9 @@
public class TestGetElementReference {
+ private static final String JDK_VERSION =
+ Integer.toString(Runtime.getRuntime().version().feature());
+
public static void main(String... args) throws IOException {
analyze("TestGetElementReferenceData.java");
analyze("mod/module-info.java", "mod/api/pkg/Api.java");
@@ -66,7 +69,10 @@
}
}
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
- JavacTask ct = (JavacTask) ToolProvider.getSystemJavaCompiler().getTask(null, null, diagnostics, Arrays.asList("-Xjcov"), null, files);
+ List<String> options = List.of("-Xjcov",
+ "--enable-preview",
+ "-source", JDK_VERSION);
+ JavacTask ct = (JavacTask) ToolProvider.getSystemJavaCompiler().getTask(null, null, diagnostics, options, null, files);
Trees trees = Trees.instance(ct);
CompilationUnitTree cut = ct.parse().iterator().next();
--- a/test/langtools/tools/javac/api/TestGetElementReferenceData.java Mon Sep 09 11:43:16 2019 -0400
+++ b/test/langtools/tools/javac/api/TestGetElementReferenceData.java Wed Nov 27 09:00:01 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -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/*getElement:CLASS:java.lang.Object*/ o = null;
+ if (o/*getElement:LOCAL_VARIABLE: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/InstanceofReifiableNotSafe.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019, 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.instanceof.reifiable.not.safe
+// key: compiler.note.preview.filename
+// key: compiler.note.preview.recompile
+// options: --enable-preview -source ${jdk.version}
+
+import java.util.List;
+
+class InstanceofReifiableNotSafe {
+ boolean test(Object o) {
+ return o instanceof List<String> l;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/diags/examples/MatchBindingExists.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017, 2019, 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.note.preview.filename
+// key: compiler.note.preview.recompile
+// options: --enable-preview -source ${jdk.version}
+
+class MatchBindingExists {
+ public void test(Object o1, Object o2) {
+ 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 Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017, 2019, 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
+// key: compiler.note.preview.filename
+// key: compiler.note.preview.recompile
+// options: --enable-preview -source ${jdk.version}
+
+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/diags/examples/PatternMatchingInstanceof.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// key: compiler.misc.feature.pattern.matching.instanceof
+// key: compiler.warn.preview.feature.use
+// options: --enable-preview -source ${jdk.version} -Xlint:preview
+
+class PatternMatchingInstanceof {
+ boolean m(Object o) {
+ return o instanceof String s && s.isEmpty();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/diags/examples/ReifiableTypesInstanceof.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// key: compiler.misc.feature.reifiable.types.instanceof
+// key: compiler.warn.preview.feature.use.plural
+// options: --enable-preview -source ${jdk.version} -Xlint:preview
+
+class PatternMatchingInstanceof {
+ boolean m(I<String> i) {
+ return i instanceof C<String>;
+ }
+ interface I<T> {}
+ class C<T> implements I<T> {}
+}
--- a/test/langtools/tools/javac/lambda/deduplication/Deduplication.java Mon Sep 09 11:43:16 2019 -0400
+++ b/test/langtools/tools/javac/lambda/deduplication/Deduplication.java Wed Nov 27 09:00:01 2019 +0100
@@ -163,6 +163,9 @@
group((Function<Integer, Integer>) x -> switch (x) { default: yield x; },
(Function<Integer, Integer>) x -> switch (x) { default: yield x; });
+
+ group((Function<Object, Integer>) x -> x instanceof Integer i ? i : -1,
+ (Function<Object, Integer>) x -> x instanceof Integer i ? i : -1);
}
void f() {}
--- a/test/langtools/tools/javac/lib/DPrinter.java Mon Sep 09 11:43:16 2019 -0400
+++ b/test/langtools/tools/javac/lib/DPrinter.java Wed Nov 27 09:00:01 2019 +0100
@@ -880,7 +880,7 @@
@Override
public void visitTypeTest(JCInstanceOf tree) {
printTree("expr", tree.expr);
- printTree("clazz", tree.clazz);
+ printTree("pattern", tree.pattern);
}
@Override
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/BindingsExistTest.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,30 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8231827
+ * @summary Clashing bindings are reported correctly
+ * @compile/fail/ref=BindingsExistTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} BindingsExistTest.java
+ */
+public class BindingsExistTest {
+ public void t(Object o1, Object o2) {
+ if (o1 instanceof String k && o2 instanceof Integer k) {}
+
+ if (o1 instanceof String k || o2 instanceof Integer k) {}
+
+ if (!(o1 instanceof String k)) {
+ return ;
+ }
+ if (o1 instanceof Integer k) {}
+
+ String s2 = "";
+ if (o1 instanceof String s2) {}
+
+ if (o1 instanceof String s3) {
+ String s3 = "";
+ }
+
+ if (!(o1 instanceof String s4)) {
+ return ;
+ }
+ String s4 = "";
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/BindingsExistTest.out Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,9 @@
+BindingsExistTest.java:9:36: compiler.err.match.binding.exists
+BindingsExistTest.java:11:36: compiler.err.match.binding.exists
+BindingsExistTest.java:16:35: compiler.err.already.defined: kindname.variable, k, kindname.method, t(java.lang.Object,java.lang.Object)
+BindingsExistTest.java:19:34: compiler.err.already.defined: kindname.variable, s2, kindname.method, t(java.lang.Object,java.lang.Object)
+BindingsExistTest.java:22:20: compiler.err.already.defined: kindname.variable, s3, kindname.method, t(java.lang.Object,java.lang.Object)
+BindingsExistTest.java:28:16: compiler.err.already.defined: kindname.variable, s4, kindname.method, t(java.lang.Object,java.lang.Object)
+- compiler.note.preview.filename: BindingsExistTest.java
+- compiler.note.preview.recompile
+6 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/BindingsTest1.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8231827
+ * @summary Basic tests for bindings from instanceof
+ * @compile --enable-preview -source ${jdk.version} BindingsTest1.java
+ * @run main/othervm --enable-preview 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 - include e1.T in e2
+ if (o1 instanceof String s && s.length()>0) {
+ System.out.print("done");
+ }
+
+ // 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 - 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();
+ }
+
+ L4: {
+ if (!(o1 instanceof String s)) {
+ break L4;
+ }
+ s.length();
+ }
+
+ {
+ while (!(o1 instanceof String s)) {
+ }
+
+ s.length();
+ }
+
+ L5: {
+ while (!(o1 instanceof String s)) {
+ }
+
+ s.length();
+ }
+
+ {
+ L6: for ( ;!(o1 instanceof String s); ) {
+
+ }
+
+ s.length();
+ }
+
+ {
+ L7: do {
+
+ } while (!(o1 instanceof String s));
+
+ s.length();
+ }
+
+ if (o1 instanceof String s) {
+ Runnable r1 = new Runnable() {
+ @Override
+ public void run() {
+ s.length();
+ }
+ };
+ r1.run();
+ Runnable r2 = () -> {
+ s.length();
+ };
+ r2.run();
+ String s2 = s;
+ }
+
+ System.out.println("BindingsTest1 complete");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/BindingsTest1Merging.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,72 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8231827
+ * @summary Basic tests for bindings from instanceof - tests for merging pattern variables
+ * @compile/fail/ref=BindingsTest1Merging.out -XDrawDiagnostics --enable-preview -source ${jdk.version} BindingsTest1Merging.java
+ */
+
+public class BindingsTest1Merging {
+ 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 e1 && e2.F = intersect(e1.F, e2.F)
+ if (!(o1 instanceof String s) && !(o1 instanceof String s)) {
+
+ } else {
+ s.length();
+ }
+
+ // 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 : 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();
+ }
+
+ L3: {
+ if ((o1 instanceof String s) || (o3 instanceof String s)) {
+ s.length();
+ } else {
+ break L3;
+ }
+ s.length();
+ }
+
+ System.out.println("BindingsTest1Merging complete");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/BindingsTest1Merging.out Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,12 @@
+BindingsTest1Merging.java:17:39: compiler.err.match.binding.exists
+BindingsTest1Merging.java:24:36: compiler.err.match.binding.exists
+BindingsTest1Merging.java:29:21: compiler.err.match.binding.exists
+BindingsTest1Merging.java:34:36: compiler.err.match.binding.exists
+BindingsTest1Merging.java:39:39: compiler.err.match.binding.exists
+BindingsTest1Merging.java:44:21: compiler.err.match.binding.exists
+BindingsTest1Merging.java:50:36: compiler.err.match.binding.exists
+BindingsTest1Merging.java:56:39: compiler.err.match.binding.exists
+BindingsTest1Merging.java:62:42: compiler.err.match.binding.exists
+- compiler.note.preview.filename: BindingsTest1Merging.java
+- compiler.note.preview.recompile
+9 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/BindingsTest2.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,191 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8231827
+ * @summary Ensure that scopes arising from conditionalExpressions are handled corrected.
+ * @compile/fail/ref=BindingsTest2.out -XDrawDiagnostics -XDshould-stop.at=FLOW --enable-preview -source ${jdk.version} 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");
+ }
+
+ {
+ while (!(o1 instanceof String s)) {
+ break;
+ }
+
+ s.length();
+ }
+
+ {
+ while (!(o1 instanceof String s)) {
+ if (false) break;
+ }
+
+ s.length();
+ }
+
+ {
+ while (!(o1 instanceof String s)) {
+ while (true);
+ break;
+ }
+
+ s.length();
+ }
+
+ {
+ for (; !(o1 instanceof String s); ) {
+ break;
+ }
+
+ s.length();
+ }
+
+ {
+ for (; !(o1 instanceof String s); ) {
+ if (false) break;
+ }
+
+ s.length();
+ }
+
+ {
+ for (; !(o1 instanceof String s); ) {
+ while (true);
+ break;
+ }
+
+ s.length();
+ }
+
+ {
+ do {
+ break;
+ } while (!(o1 instanceof String s));
+
+ s.length();
+ }
+
+ {
+ do {
+ if (false) break;
+ } while (!(o1 instanceof String s));
+
+ s.length();
+ }
+
+ {
+ do {
+ while (true);
+ break;
+ } while (!(o1 instanceof String s));
+
+ s.length();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/BindingsTest2.out Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,48 @@
+BindingsTest2.java:16:21: compiler.err.match.binding.exists
+BindingsTest2.java:17:14: compiler.err.cant.resolve.location.args: kindname.method, intValue, , , (compiler.misc.location.1: kindname.variable, x, java.lang.String)
+BindingsTest2.java:20:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:24:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:26:36: compiler.err.match.binding.exists
+BindingsTest2.java:27:14: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer)
+BindingsTest2.java:30:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:35:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:37:39: compiler.err.match.binding.exists
+BindingsTest2.java:38:14: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer)
+BindingsTest2.java:41:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:42:13: compiler.err.cant.resolve.location: kindname.variable, i, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:45:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:46:13: compiler.err.cant.resolve.location: kindname.variable, s2, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:51:21: compiler.err.match.binding.exists
+BindingsTest2.java:53:14: compiler.err.cant.resolve.location.args: kindname.method, intValue, , , (compiler.misc.location.1: kindname.variable, x, java.lang.String)
+BindingsTest2.java:57: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, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:62:13: compiler.err.cant.resolve.location: kindname.variable, x1, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:66:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:70:36: compiler.err.match.binding.exists
+BindingsTest2.java:72:14: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer)
+BindingsTest2.java:76:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:77:13: compiler.err.cant.resolve.location: kindname.variable, i, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:81:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:82:13: compiler.err.cant.resolve.location: kindname.variable, s1, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:87:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:88:13: compiler.err.cant.resolve.location: kindname.variable, s1, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:90:39: compiler.err.match.binding.exists
+BindingsTest2.java:92:14: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer)
+BindingsTest2.java:96:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:97:13: compiler.err.cant.resolve.location: kindname.variable, i, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:101:46: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:104:46: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:109:41: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:112:41: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:121:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:129:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:146:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:154:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:171:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:179:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
+BindingsTest2.java:135:17: compiler.err.unreachable.stmt
+BindingsTest2.java:160:17: compiler.err.unreachable.stmt
+BindingsTest2.java:185:17: compiler.err.unreachable.stmt
+- compiler.note.preview.filename: BindingsTest2.java
+- compiler.note.preview.recompile
+45 errors
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/CastConversionMatch.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,18 @@
+/*
+ * @test /nodynamicopyright/
+ * @bug 8231827
+ * @summary Match which involves a cast conversion
+ * @compile/fail/ref=CastConversionMatch.out -XDrawDiagnostics --enable-preview -source ${jdk.version} 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 Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,4 @@
+CastConversionMatch.java:11:26: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array)
+- compiler.note.preview.filename: CastConversionMatch.java
+- compiler.note.preview.recompile
+1 error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/DuplicateBindingTest.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,22 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8231827
+ * @summary Basic pattern bindings scope test
+ * @compile/fail/ref=DuplicateBindingTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} 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 Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,4 @@
+DuplicateBindingTest.java:16:43: compiler.err.already.defined: kindname.variable, s, kindname.method, main(java.lang.String[])
+- compiler.note.preview.filename: DuplicateBindingTest.java
+- compiler.note.preview.recompile
+1 error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/EnsureTypesOrderTest.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,13 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8187420 8231827
+ * @summary Error message mentions relevant types transposed
+ * @compile/fail/ref=EnsureTypesOrderTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} 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 Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,4 @@
+EnsureTypesOrderTest.java:9:13: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String[], java.lang.String)
+- compiler.note.preview.filename: EnsureTypesOrderTest.java
+- compiler.note.preview.recompile
+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 Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8231827
+ * @summary All example code from "Pattern Matching for Java" document, released April 2017, adjusted to current state (no switches, etc)
+ * @compile --enable-preview -source ${jdk.version} ExamplesFromProposal.java
+ * @run main/othervm --enable-preview 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 Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,21 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8231827
+ * @summary Ensure that in type test patterns, the predicate is not trivially provable false.
+ * @compile/fail/ref=ImpossibleTypeTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} 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 Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,5 @@
+ImpossibleTypeTest.java:14:13: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.String)
+ImpossibleTypeTest.java:17:26: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, ImpossibleTypeTest, null)
+- compiler.note.preview.filename: ImpossibleTypeTest.java
+- compiler.note.preview.recompile
+2 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/LocalVariableTable.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8231827
+ * @summary Ensure the LV table entries are generated for bindings
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ * @compile -g --enable-preview -source ${jdk.version} LocalVariableTable.java
+ * @run main/othervm --enable-preview LocalVariableTable
+ */
+
+import java.io.*;
+import java.lang.annotation.*;
+import java.util.*;
+import com.sun.tools.classfile.*;
+
+/*
+ * The test checks that a LocalVariableTable attribute is generated for the
+ * method bodies containing patterns, and checks that the expected
+ * set of entries is found in the attribute.
+ *
+ * The test looks for test cases represented by nested classes whose
+ * name begins with "Pattern". Each such class contains a method
+ * with patterns, and because the test is compiled
+ * with -g, these methods should have a LocalVariableTable. The set of
+ * expected names in the LVT is provided in an annotation on the class for
+ * the test case.
+ */
+//Copied from: test/langtools/tools/javac/lambda/LocalVariableTable.java
+public class LocalVariableTable {
+ public static void main(String... args) throws Exception {
+ new LocalVariableTable().run();
+ }
+
+ void run() throws Exception {
+ // the declared classes are returned in an unspecified order,
+ // so for neatness, sort them by name before processing them
+ Class<?>[] classes = getClass().getDeclaredClasses();
+ Arrays.sort(classes, (c1, c2) -> c1.getName().compareTo(c2.getName()));
+
+ for (Class<?> c : classes) {
+ if (c.getSimpleName().startsWith("Pattern"))
+ check(c);
+ }
+ if (errors > 0)
+ throw new Exception(errors + " errors found");
+ }
+
+ /** Check an individual test case. */
+ void check(Class<?> c) throws Exception {
+ System.err.println("Checking " + c.getSimpleName());
+
+ Expect expect = c.getAnnotation(Expect.class);
+ if (expect == null) {
+ error("@Expect not found for class " + c.getSimpleName());
+ return;
+ }
+
+ ClassFile cf = ClassFile.read(getClass().getResource(c.getName() + ".class").openStream());
+ Method m = getMethodByName(cf, c.getSimpleName().contains("Lambda") ? "lambda$" : "test");
+ if (m == null) {
+ error("test method not found");
+ return;
+ }
+
+ Code_attribute code = (Code_attribute) m.attributes.get(Attribute.Code);
+ if (code == null) {
+ error("Code attribute not found");
+ return;
+ }
+
+ LocalVariableTable_attribute lvt =
+ (LocalVariableTable_attribute) code.attributes.get(Attribute.LocalVariableTable);
+ if (lvt == null) {
+ error("LocalVariableTable attribute not found");
+ return;
+ }
+
+ Set<String> foundNames = new LinkedHashSet<>();
+ for (LocalVariableTable_attribute.Entry e: lvt.local_variable_table) {
+ foundNames.add(cf.constant_pool.getUTF8Value(e.name_index));
+ }
+
+ Set<String> expectNames = new LinkedHashSet<>(Arrays.asList(expect.value()));
+ if (!foundNames.equals(expectNames)) {
+ Set<String> foundOnly = new LinkedHashSet<>(foundNames);
+ foundOnly.removeAll(expectNames);
+ for (String s: foundOnly)
+ error("Unexpected name found: " + s);
+ Set<String> expectOnly = new LinkedHashSet<>(expectNames);
+ expectOnly.removeAll(foundNames);
+ for (String s: expectOnly)
+ error("Expected name not found: " + s);
+ }
+ }
+
+ Method getMethodByName(ClassFile cf, String name) throws ConstantPoolException {
+ for (Method m: cf.methods) {
+ if (m.getName(cf.constant_pool).startsWith(name))
+ return m;
+ }
+ return null;
+ }
+
+ /** Report an error. */
+ void error(String msg) {
+ System.err.println("Error: " + msg);
+ errors++;
+ }
+
+ int errors;
+
+ /**
+ * Annotation used to provide the set of names expected in the LVT attribute.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface Expect {
+ String[] value();
+ }
+
+ /*
+ * ---------- Test cases ---------------------------------------------------
+ */
+
+ @Expect({ "o", "s" })
+ static class Pattern_Simple {
+ public static void test(Object o) {
+ if (o instanceof String s) {}
+ }
+ }
+
+ @Expect({ "s" })
+ static class Pattern_Lambda {
+ public static void test(Object o) {
+ if (o instanceof String s) {
+ Runnable r = () -> {
+ s.length();
+ };
+ }
+ }
+ }
+
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/MatchBindingScopeTest.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,68 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8231827
+ * @summary Basic pattern bindings scope test
+ * @compile/fail/ref=MatchBindingScopeTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} 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);
+ } 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 Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,16 @@
+MatchBindingScopeTest.java:19:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
+MatchBindingScopeTest.java:23:36: compiler.err.match.binding.exists
+MatchBindingScopeTest.java:30:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
+MatchBindingScopeTest.java:31:32: compiler.err.cant.resolve.location: kindname.variable, k, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
+MatchBindingScopeTest.java:34:39: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
+MatchBindingScopeTest.java:35:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
+MatchBindingScopeTest.java:38:36: compiler.err.match.binding.exists
+MatchBindingScopeTest.java:41:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
+MatchBindingScopeTest.java:49:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
+MatchBindingScopeTest.java:56:48: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
+MatchBindingScopeTest.java:57:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
+MatchBindingScopeTest.java:62:23: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
+MatchBindingScopeTest.java:65:23: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
+- compiler.note.preview.filename: MatchBindingScopeTest.java
+- compiler.note.preview.recompile
+13 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/NullsInPatterns.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8231827
+ * @summary Testing pattern matching against the null constant
+ * @compile --enable-preview -source ${jdk.version} NullsInPatterns.java
+ * @run main/othervm --enable-preview 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");
+ }
+ //reifiable types not allowed in type test patterns in instanceof:
+// 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 Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8231827
+ * @summary Check proper positions.
+ * @build PatternMatchPosTest
+ * @compile/ref=PatternMatchPosTest.out -processor PatternMatchPosTest -Xlint:unchecked -XDrawDiagnostics --enable-preview -source ${jdk.version} PatternMatchPosTestData.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.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;
+import javax.tools.Diagnostic;
+
+@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);
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
+ 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();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/PatternMatchPosTest.out Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,14 @@
+- compiler.note.proc.messager: (o instanceof String s)
+- compiler.note.proc.messager: o instanceof String s
+- compiler.note.proc.messager: o
+- compiler.note.proc.messager: String s
+- compiler.note.proc.messager: String
+- compiler.note.proc.messager: (o instanceof java.lang.String s)
+- compiler.note.proc.messager: o instanceof java.lang.String s
+- compiler.note.proc.messager: o
+- compiler.note.proc.messager: java.lang.String s
+- compiler.note.proc.messager: java.lang.String
+- compiler.note.proc.messager: java.lang
+- compiler.note.proc.messager: java
+- compiler.note.preview.filename: PatternMatchPosTestData.java
+- compiler.note.preview.recompile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/PatternMatchPosTestData.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017, 2019, 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.
+ */
+
+class PatternMatchPosTestData {
+ void data(Object o) {
+ if (o instanceof String s) { }
+ if (o instanceof java.lang.String s) { }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/PatternTypeTest2.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8231827
+ * @summary Basic pattern test
+ * @compile --enable-preview -source ${jdk.version} PatternTypeTest2.java
+ * @run main/othervm --enable-preview 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 Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,16 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8231827
+ * @summary Ensure that in type test patterns, the predicate is not trivially provable false.
+ * @compile/fail/ref=PatternVariablesAreFinal.out -XDrawDiagnostics --enable-preview -source ${jdk.version} 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 Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,4 @@
+PatternVariablesAreFinal.java:11:13: compiler.err.pattern.binding.may.not.be.assigned: s
+- compiler.note.preview.filename: PatternVariablesAreFinal.java
+- compiler.note.preview.recompile
+1 error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/PatternVariablesAreFinal2.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8231827
+ * @summary Pattern variables are final so should be allowed to be referenced in an inner class
+ * @compile --enable-preview -source ${jdk.version} PatternVariablesAreFinal2.java
+ * @run main/othervm --enable-preview 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/PatternsSimpleVisitorTest.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8231827
+ * @summary Ensure SimpleTreeVisitor.visitBindingPattern and visitInstanceOf behaves as it should
+ * @modules jdk.compiler
+ */
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.tools.*;
+
+import com.sun.source.tree.BindingPatternTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.InstanceOfTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.SimpleTreeVisitor;
+import com.sun.source.util.TreePathScanner;
+
+public class PatternsSimpleVisitorTest {
+
+ public static void main(String[] args) throws Exception {
+ new PatternsSimpleVisitorTest().run();
+ }
+
+ void run() throws Exception {
+ String code = "class Test {\n" +
+ " boolean t(Object o) {\n" +
+ " return o instanceof String s ? s.isEmpty() : false;\n" +
+ " }\n" +
+ "}\n";
+ int[] callCount = new int[1];
+ int[] instanceOfNodeCount = new int[1];
+ int[] bindingPatternNodeCount = new int[1];
+ new TreePathScanner<Void, Void>() {
+ @Override
+ public Void visitInstanceOf(InstanceOfTree node, Void p) {
+ node.accept(new SimpleTreeVisitor<Void, Void>() {
+ @Override
+ protected Void defaultAction(Tree defaultActionNode, Void p) {
+ callCount[0]++;
+ if (node == defaultActionNode) {
+ instanceOfNodeCount[0]++;
+ }
+ return null;
+ }
+ }, null);
+ return super.visitInstanceOf(node, p);
+ }
+ @Override
+ public Void visitBindingPattern(BindingPatternTree node, Void p) {
+ node.accept(new SimpleTreeVisitor<Void, Void>() {
+ @Override
+ protected Void defaultAction(Tree defaultActionNode, Void p) {
+ callCount[0]++;
+ if (node == defaultActionNode) {
+ bindingPatternNodeCount[0]++;
+ }
+ return null;
+ }
+ }, null);
+ return super.visitBindingPattern(node, p);
+ }
+ }.scan(parse(code), null);
+
+ if (callCount[0] != 2 || instanceOfNodeCount[0] != 1 ||
+ bindingPatternNodeCount[0] != 1) {
+ throw new AssertionError("Unexpected counts; callCount=" + callCount[0] +
+ ", switchExprNodeCount=" + instanceOfNodeCount[0] +
+ ", yieldNodeCount=" + bindingPatternNodeCount[0]);
+ }
+ }
+
+ private CompilationUnitTree parse(String code) throws IOException {
+ final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
+ assert tool != null;
+ DiagnosticListener<JavaFileObject> noErrors = d -> {};
+
+ StringWriter out = new StringWriter();
+ JavacTask ct = (JavacTask) tool.getTask(out, null, noErrors,
+ List.of("--enable-preview", "-source", Integer.toString(Runtime.version().feature())), null,
+ Arrays.asList(new MyFileObject(code)));
+ return ct.parse().iterator().next();
+ }
+
+ static class MyFileObject extends SimpleJavaFileObject {
+ private String text;
+
+ public MyFileObject(String text) {
+ super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+ this.text = text;
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return text;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/Reifiable.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,22 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8231827
+ * @summary Verify behavior w.r.t. non-reifiable types and type test patterns in instanceof
+ * @compile/fail/ref=Reifiable.out --enable-preview -source ${jdk.version} -XDrawDiagnostics Reifiable.java
+ */
+
+public class Reifiable implements ReifiableI {
+ private static boolean test(Object o, List<Reifiable> l1, List<String> l2) {
+ return o instanceof ListImpl<Reifiable> li1 &&
+ l1 instanceof ListImpl<Reifiable> li2 &&
+ l2 instanceof ListImpl<Reifiable> li3 &&
+ l2 instanceof ListImpl<String> li4 &&
+ l1 instanceof Unrelated<Reifiable> li5;
+ }
+
+ public class List<T> {}
+ public class ListImpl<T extends ReifiableI> extends List<T> {}
+ public class Unrelated<T> {}
+}
+
+interface ReifiableI {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/Reifiable.out Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,7 @@
+Reifiable.java:10:16: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, Reifiable.ListImpl<Reifiable>
+Reifiable.java:12:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Reifiable.List<java.lang.String>, Reifiable.ListImpl<Reifiable>)
+Reifiable.java:13:39: compiler.err.not.within.bounds: java.lang.String, T
+Reifiable.java:14:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Reifiable.List<Reifiable>, Reifiable.Unrelated<Reifiable>)
+- compiler.note.preview.filename: Reifiable.java
+- compiler.note.preview.recompile
+4 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/ReifiableOld-old.out Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,7 @@
+ReifiableOld.java:12:37: compiler.err.illegal.generic.type.for.instof
+ReifiableOld.java:13:38: compiler.err.illegal.generic.type.for.instof
+ReifiableOld.java:14:38: compiler.err.illegal.generic.type.for.instof
+ReifiableOld.java:15:38: compiler.err.illegal.generic.type.for.instof
+ReifiableOld.java:15:39: compiler.err.not.within.bounds: java.lang.String, T
+ReifiableOld.java:16:39: compiler.err.illegal.generic.type.for.instof
+6 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/ReifiableOld.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,24 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8231827
+ * @summary Verify behavior w.r.t. non-reifiable types in instanceof
+ * @compile/fail/ref=ReifiableOld-old.out -source 13 -Xlint:-options -XDrawDiagnostics ReifiableOld.java
+ * @compile/fail/ref=ReifiableOld-old.out -source ${jdk.version} -XDrawDiagnostics ReifiableOld.java
+ * @compile/fail/ref=ReifiableOld.out --enable-preview -source ${jdk.version} -XDrawDiagnostics ReifiableOld.java
+ */
+
+public class ReifiableOld implements ReifiableOldI {
+ private static boolean test(Object o, List<ReifiableOld> l1, List<String> l2) {
+ return o instanceof ListImpl<ReifiableOld> &&
+ l1 instanceof ListImpl<ReifiableOld> &&
+ l2 instanceof ListImpl<ReifiableOld> &&
+ l2 instanceof ListImpl<String> &&
+ l1 instanceof Unrelated<ReifiableOld>;
+ }
+
+ public class List<T> {}
+ public class ListImpl<T extends ReifiableOldI> extends List<T> {}
+ public class Unrelated<T> {}
+}
+
+interface ReifiableOldI {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/ReifiableOld.out Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,7 @@
+ReifiableOld.java:12:16: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, ReifiableOld.ListImpl<ReifiableOld>
+ReifiableOld.java:14:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: ReifiableOld.List<java.lang.String>, ReifiableOld.ListImpl<ReifiableOld>)
+ReifiableOld.java:15:39: compiler.err.not.within.bounds: java.lang.String, T
+ReifiableOld.java:16:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: ReifiableOld.List<ReifiableOld>, ReifiableOld.Unrelated<ReifiableOld>)
+- compiler.note.preview.filename: ReifiableOld.java
+- compiler.note.preview.recompile
+4 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,18 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8187429 8231827
+ * @summary Missing unchecked conversion warning
+ * @compile/fail/ref=UncheckedWarningOnMatchesTest.out -Xlint:unchecked -Werror -XDrawDiagnostics --enable-preview -source ${jdk.version} 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 Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,4 @@
+UncheckedWarningOnMatchesTest.java:14:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, java.util.ArrayList<java.lang.Integer>
+- compiler.note.preview.filename: UncheckedWarningOnMatchesTest.java
+- compiler.note.preview.recompile
+1 error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/scope/ScopeTest.java Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2018, 2019, 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) {
+ String sourceVersion = Integer.toString(Runtime.version().feature());
+
+ reset();
+ addCompileOptions("--enable-preview", "-source", sourceVersion);
+ program(block);
+ try {
+ compile();
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ assertCompileSucceeded();
+ }
+
+ private void assertFail(String expectedDiag, String block) {
+ String sourceVersion = Integer.toString(Runtime.version().feature());
+
+ reset();
+ addCompileOptions("--enable-preview", "-source", sourceVersion);
+ 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()));
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/scope/TEST.properties Wed Nov 27 09:00:01 2019 +0100
@@ -0,0 +1,6 @@
+TestNG.dirs = .
+
+lib.dirs = /lib/combo
+
+modules = \
+ jdk.compiler/com.sun.tools.javac.util