diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfStatementTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfStatementTree.java new file mode 100644 index 00000000000..31de7182de9 --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfStatementTree.java @@ -0,0 +1,40 @@ +package com.sun.source.tree; + +import jdk.internal.javac.PreviewFeature; + +/** + * A tree node for an {@code instanceof} statement. + * + * For example: + *
+ *   expression instanceof record pattern;
+ * 
+ * + * @jls 14.22 The {@code instanceof} Statement + * + * @author Aggelos Biboudis + * @since 23 + */ +public interface InstanceOfStatementTree extends StatementTree { + /** + * Returns the pattern for the {@code instanceof} statement. + * @return pattern + */ + @PreviewFeature(feature=PreviewFeature.Feature.PATTERN_DECLARATIONS, reflective=true) + Tree getPattern(); + + /** + * Returns the expression to be pattern matched. + * @return the expression + */ + @PreviewFeature(feature=PreviewFeature.Feature.PATTERN_DECLARATIONS, reflective=true) + ExpressionTree getExpression(); + + /** + * Returns the type for which to check. + * @return the type + * @see #getPattern() + */ + @PreviewFeature(feature=PreviewFeature.Feature.PATTERN_DECLARATIONS, reflective=true) + Tree getType(); +} \ No newline at end of file diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java index af444818faa..5ec2c219120 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java @@ -175,6 +175,14 @@ public enum Kind { */ INSTANCE_OF(InstanceOfTree.class), + /** + * Used for instances of {@link InstanceOfStatementTree}. + * + * @since 23 + */ + @PreviewFeature(feature=PreviewFeature.Feature.PATTERN_DECLARATIONS, reflective=true) + INSTANCEOF_STATEMENT(InstanceOfStatementTree.class), + /** * Used for instances of {@link LabeledStatementTree}. */ diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java index 0947d0e38ff..7fd27a1e943 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java @@ -514,6 +514,14 @@ public interface TreeVisitor { */ R visitInstanceOf(InstanceOfTree node, P p); + /** + * Visits an {@code InstanceOfStatementTree} node. + * @param node the node being visited + * @param p a parameter value + * @return a result value + */ + R visitInstanceOfStatement(InstanceOfStatementTree node, P p); + /** * Visits a {@code UnaryTree} node. * @param node the node being visited diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java index c16140ba69e..828212ecbab 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java @@ -629,6 +629,20 @@ public R visitInstanceOf(InstanceOfTree node, P p) { return defaultAction(node, p); } + /** + * {@inheritDoc} + * + * @implSpec This implementation calls {@code defaultAction}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code defaultAction} + */ + @Override + public R visitInstanceOfStatement(InstanceOfStatementTree node, P p) { + return defaultAction(node, p); + } + /** * {@inheritDoc} * @@ -1070,4 +1084,6 @@ public R visitYield(YieldTree node, P p) { public R visitMatchStatement(MatchTree node, P p) { return defaultAction(node, p); } + + } diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java index 85db93f533d..f733bbc81d5 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java @@ -762,6 +762,22 @@ public R visitInstanceOf(InstanceOfTree node, P p) { return r; } + /** + * {@inheritDoc} + * + * @implSpec This implementation scans the children in left to right order. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of scanning + */ + @Override + public R visitInstanceOfStatement(InstanceOfStatementTree node, P p) { + R r = scan(node.getPattern(), p); + r = scanAndReduce(node.getExpression(), p, r); + return r; + } + /** * {@inheritDoc} * diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 376c510fb91..99266fd6aab 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -2439,6 +2439,19 @@ public void visitMatch(JCMatch tree) { result = null; } + public void visitTypeTestStatement(JCInstanceOfStatement tree) { + attribExpr(tree.expr, env); + attribExpr(tree.pattern, env); + + matchBindings.bindingsWhenTrue.forEach(env.info.scope::enter); + matchBindings.bindingsWhenTrue.forEach(BindingSymbol::preserveBinding); + + Type clazztype = tree.pattern.type; + checkCastablePattern(tree.expr.pos(), tree.expr.type, clazztype); + + result = null; + } + public void visitContinue(JCContinue tree) { tree.target = findJumpTarget(tree.pos(), tree.getTag(), tree.label, env); result = null; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index a8e3c41fa70..97f8e553fe7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -696,6 +696,34 @@ public void visitMatch(JCMatch tree) { recordExit(new PendingExit(tree)); } + public void visitTypeTestStatement(JCInstanceOfStatement tree) { + ListBuffer prevPendingExits = pendingExits; + pendingExits = new ListBuffer<>(); + + if (tree.pattern instanceof JCRecordPattern rp) { + visitRecordPattern(rp); + } + + List singletonCaseList = List.of(make.Case( + CaseTree.CaseKind.STATEMENT, + List.of(make.PatternCaseLabel(tree.pattern)), + null, + List.nil(), + null) + ); + + boolean isExhaustive = + exhausts(tree.expr, singletonCaseList); + + if (!isExhaustive) { + log.error(tree, Errors.NotExhaustiveStatement); // TODO replace with instanceof-related error since this refers to switch + } + + scan(tree.expr); + + alive = alive.or(resolveBreaks(tree, prevPendingExits)); + } + public void visitForeachLoop(JCEnhancedForLoop tree) { visitVarDef(tree.var); ListBuffer prevPendingExits = pendingExits; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java index af6bdc4f639..40e32bdc934 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java @@ -56,6 +56,8 @@ 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.JCInstanceOfStatement; +import com.sun.tools.javac.tree.JCTree.JCThrow; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCSwitch; import com.sun.tools.javac.tree.JCTree.JCMatch; @@ -266,6 +268,44 @@ public void visitTypeTest(JCInstanceOf tree) { } } + @Override + public void visitTypeTestStatement(JCInstanceOfStatement tree) { + /** + * A statement of the form + * + *
+         *      instanceof ;
+         * 
+ * + * (where is any pattern) is translated to: + * + *
{@code
+         *     if (!( instanceof()) {
+         *         throw new MatchException(null, null);
+         *     }
+         * }
+ * + */ + bindingContext = new BasicBindingContext(); + try { + List matchExParams = List.of(makeNull(), makeNull()); + JCThrow thr = make.Throw(makeNewClass(syms.matchExceptionType, matchExParams)); + + JCExpression expr = translate(tree.expr); + + JCInstanceOf instanceOfTree = make.TypeTest(expr, tree.pattern); + tree.type = syms.booleanType; + + JCIf ifNode = make.If(makeUnary(Tag.NOT, + translate(instanceOfTree)).setType(syms.booleanType), thr, null); + + result = bindingContext.decorateStatement(ifNode); + } finally { + bindingContext.pop(); + } + } + + @Override public void visitAnyPattern(JCTree.JCAnyPattern that) { result = make.Literal(true); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java index db3199dcc22..e5d45a78b7f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java @@ -1078,6 +1078,12 @@ public void visitTypeTest(JCInstanceOf tree) { result = tree; } + public void visitTypeTestStatement(JCInstanceOfStatement tree) { + tree.expr = translate(tree.expr, null); + tree.pattern = translate(tree.pattern, null); + result = tree; + } + public void visitIndexed(JCArrayAccess tree) { tree.indexed = translate(tree.indexed, erasure(tree.indexed.type)); tree.index = translate(tree.index, syms.intType); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index d7c7b785f59..585b376509d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -33,6 +33,7 @@ import javax.lang.model.SourceVersion; import com.sun.source.tree.CaseTree; +import com.sun.source.tree.InstanceOfStatementTree; import com.sun.source.tree.MemberReferenceTree.ReferenceMode; import com.sun.source.tree.ModuleTree.ModuleKind; @@ -3016,11 +3017,17 @@ List blockStatement() { F.at(pos); return localVariableDeclarations(mods, t, dc); } else { - // This Exec is an "ExpressionStatement"; it subsumes the terminating semicolon - t = checkExprStat(t); - accept(SEMI); - JCExpressionStatement expr = toP(F.at(pos).Exec(t)); - return List.of(expr); + if (t.getTag() == TYPETEST && allowPatternDeclarations) { + t = term2Rest(t, TreeInfo.orPrec); + accept(SEMI); + return List.of(toP(F.at(pos).TypeTestStatement(((JCInstanceOf) t).expr, ((JCInstanceOf)t ).getPattern()))); + } else { + // This Exec is an "ExpressionStatement"; it subsumes the terminating semicolon + t = checkExprStat(t); + accept(SEMI); + JCExpressionStatement expr = toP(F.at(pos).Exec(t)); + return List.of(expr); + } } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index 93abe29bb42..4fca6fd5c06 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -245,6 +245,10 @@ public enum Tag { */ TYPETEST, + /** Type test statements, of type TypeTest. + */ + TYPETEST_STATEMENT, + /** Patterns. */ ANYPATTERN, @@ -1769,6 +1773,38 @@ public Tag getTag() { } } + /** + * The match statement + */ + public static class JCInstanceOfStatement extends JCStatement implements InstanceOfStatementTree { + public JCPattern pattern; + public JCExpression expr; + + protected JCInstanceOfStatement(JCExpression expr, JCPattern pattern) { + this.pattern = pattern; + this.expr = expr; + } + @Override + public void accept(Visitor v) { v.visitTypeTestStatement(this); } + + @DefinedBy(Api.COMPILER_TREE) + public Kind getKind() { return Kind.INSTANCEOF_STATEMENT; } + @Override @DefinedBy(Api.COMPILER_TREE) + public Tree getPattern() { return pattern; } + @DefinedBy(Api.COMPILER_TREE) + public JCTree getType() { return pattern instanceof JCPattern ? pattern.hasTag(BINDINGPATTERN) ? ((JCBindingPattern) pattern).var.vartype : null : pattern; } + @DefinedBy(Api.COMPILER_TREE) + public JCExpression getExpression() { return expr; } + @Override @DefinedBy(Api.COMPILER_TREE) + public R accept(TreeVisitor v, D d) { + return v.visitInstanceOfStatement(this, d); + } + @Override + public Tag getTag() { + return TYPETEST_STATEMENT; + } + } + /** * A continue of a loop. */ @@ -3605,6 +3641,7 @@ public abstract static class Visitor { public void visitBinary(JCBinary that) { visitTree(that); } public void visitTypeCast(JCTypeCast that) { visitTree(that); } public void visitTypeTest(JCInstanceOf that) { visitTree(that); } + public void visitTypeTestStatement(JCInstanceOfStatement that) { visitTree(that); } public void visitAnyPattern(JCAnyPattern that) { visitTree(that); } public void visitBindingPattern(JCBindingPattern that) { visitTree(that); } public void visitDefaultCaseLabel(JCDefaultCaseLabel that) { visitTree(that); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java index c918f5ce7c8..fc16645dca0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java @@ -1420,6 +1420,23 @@ public void visitTypeTest(JCInstanceOf tree) { } } + public void visitTypeTestStatement(JCInstanceOfStatement tree) { + try { + open(prec, TreeInfo.ordPrec); + printExpr(tree.expr, TreeInfo.ordPrec); + print(" instanceof "); + if (tree.pattern instanceof JCPattern) { + printPattern(tree.pattern); + } else { + printExpr(tree.getType(), TreeInfo.ordPrec + 1); + } + close(prec, TreeInfo.ordPrec); + print(';'); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + public void visitIndexed(JCArrayAccess tree) { try { printExpr(tree.indexed, TreeInfo.postfixPrec); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java index ffd97458079..1637132a2f2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java @@ -507,6 +507,14 @@ public JCTree visitInstanceOf(InstanceOfTree node, P p) { return M.at(t.pos).TypeTest(expr, pattern); } + @DefinedBy(Api.COMPILER_TREE) + public JCTree visitInstanceOfStatement(InstanceOfStatementTree node, P p) { + JCInstanceOfStatement t = (JCInstanceOfStatement) node; + JCExpression expr = copy(t.expr, p); + JCPattern pattern = copy(t.pattern, p); + return M.at(t.pos).TypeTestStatement(expr, pattern); + } + @DefinedBy(Api.COMPILER_TREE) public JCTree visitAnyPattern(AnyPatternTree node, P p) { JCAnyPattern t = (JCAnyPattern) node; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java index 466148f6c3e..9596c098a01 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java @@ -498,6 +498,13 @@ public JCInstanceOf TypeTest(JCExpression expr, JCTree clazz) { return tree; } + public JCInstanceOfStatement TypeTestStatement(JCExpression expr, JCPattern pattern) { + JCInstanceOfStatement tree = new JCInstanceOfStatement(expr, pattern); + tree.pos = pos; + return tree; + } + + public JCAnyPattern AnyPattern() { JCAnyPattern tree = new JCAnyPattern(); tree.pos = pos; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java index cf9244ab941..d5d473d6a5c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java @@ -315,6 +315,11 @@ public void visitTypeTest(JCInstanceOf tree) { scan(tree.pattern); } + public void visitTypeTestStatement(JCInstanceOfStatement tree) { + scan(tree.expr); + scan(tree.pattern); + } + public void visitBindingPattern(JCBindingPattern tree) { scan(tree.var); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java index 440b74b4a2e..c7d1b8bab74 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java @@ -371,6 +371,12 @@ public void visitTypeTest(JCInstanceOf tree) { result = tree; } + public void visitTypeTestStatement(JCInstanceOfStatement tree) { + tree.pattern = translate(tree.pattern); + tree.expr = translate(tree.expr); + result = tree; + } + public void visitBindingPattern(JCBindingPattern tree) { tree.var = translate(tree.var); result = tree; diff --git a/test/langtools/tools/javac/patterns/declarations/InstanceOfStatementErrors.java b/test/langtools/tools/javac/patterns/declarations/InstanceOfStatementErrors.java new file mode 100644 index 00000000000..e31976f82c3 --- /dev/null +++ b/test/langtools/tools/javac/patterns/declarations/InstanceOfStatementErrors.java @@ -0,0 +1,18 @@ +/* + * @test /nodynamiccopyright/ + * @summary + * @enablePreview + * @compile/fail/ref=InstanceOfStatementErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW InstanceOfStatementErrors.java + */ + +import java.util.List; + +public class InstanceOfStatementErrors { + static void exhaustivity_error1(Object point) { + point instanceof Point(var x, var y); + } + + sealed interface IPoint permits Point {} + record Point(Integer x, Integer y) implements IPoint { } + record OPoint(Object x, Object y) { } +} \ No newline at end of file diff --git a/test/langtools/tools/javac/patterns/declarations/InstanceOfStatementErrors.out b/test/langtools/tools/javac/patterns/declarations/InstanceOfStatementErrors.out new file mode 100644 index 00000000000..15d9970521c --- /dev/null +++ b/test/langtools/tools/javac/patterns/declarations/InstanceOfStatementErrors.out @@ -0,0 +1,2 @@ +InstanceOfStatementErrors.java:12:9: compiler.err.not.exhaustive.statement +1 error diff --git a/test/langtools/tools/javac/patterns/declarations/InstanceOfStatementInMethodsTest.java b/test/langtools/tools/javac/patterns/declarations/InstanceOfStatementInMethodsTest.java new file mode 100644 index 00000000000..48827ffe213 --- /dev/null +++ b/test/langtools/tools/javac/patterns/declarations/InstanceOfStatementInMethodsTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024, 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.util.Objects; +import java.util.function.Supplier; + +/** + * @test + * @enablePreview + * @compile -parameters InstanceOfStatementInMethodsTest.java + * @run main InstanceOfStatementInMethodsTest + */ +public class InstanceOfStatementInMethodsTest { + public static void main(String[] args) { + basicTest(); + assertMatchExceptionWithNested(InstanceOfStatementInMethodsTest::raiseExceptionTest, TestPatternFailed.class); + } + + static void basicTest() { + Point p = new Point(1, 2); + p instanceof Point(Integer a, Integer b); + assertEquals(3, a + b); + + IPoint ip = new Point(3, 4); + ip instanceof Point(var c, var d); + assertEquals(7, c + d); + + p = new Point(1, null); + p instanceof Point(var e, var f); + assertEquals(null, f); + + PointP wp = new PointP(1, 2); + wp instanceof PointP(int ap, double bp); + assertEquals(2.0d, bp); + } + + static Integer raiseExceptionTest() { + PointEx pointEx = new PointEx(1, 2); + pointEx instanceof PointEx(Integer a_ex, Integer b_noex); + return a_ex; + } + + sealed interface IPoint permits Point {} + record Point(Integer x, Integer y) implements IPoint { } + record PointP(int x, double y) { } + record PointEx(Integer x, Integer y) { + @Override + public Integer x() { + throw new TestPatternFailed(EXCEPTION_MESSAGE); + } + } + static final String EXCEPTION_MESSAGE = "exception-message"; + public static class TestPatternFailed extends AssertionError { + public TestPatternFailed(String message) { + super(message); + } + } + + // error handling + static void fail(String message) { + throw new AssertionError(message); + } + + static void assertEquals(Object expected, Object actual) { + if (!Objects.equals(expected, actual)) { + throw new AssertionError("Expected: " + expected + "," + + "got: " + actual); + } + } + + static void assertMatchExceptionWithNested(Supplier f, Class nestedExceptionClass) { + try { + f.get(); + fail("Expected an exception, but none happened!"); + } + catch(Exception ex) { + assertEquals(MatchException.class, ex.getClass()); + + MatchException me = (MatchException) ex; + + assertEquals(nestedExceptionClass, me.getCause().getClass()); + } + } + + static void assertEx(Supplier f, Class exceptionClass) { + try { + f.get(); + fail("Expected an exception, but none happened!"); + } + catch(Exception ex) { + assertEquals(exceptionClass, ex.getClass()); + } + } +} \ No newline at end of file diff --git a/test/langtools/tools/javac/patterns/declarations/InstanceOfStatementInPatternsTest.java b/test/langtools/tools/javac/patterns/declarations/InstanceOfStatementInPatternsTest.java new file mode 100644 index 00000000000..ff5eaeb0b04 --- /dev/null +++ b/test/langtools/tools/javac/patterns/declarations/InstanceOfStatementInPatternsTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024, 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.util.Objects; + +/** + * @test + * @enablePreview + * @compile -parameters InstanceOfStatementInPatternsTest.java + * @run main InstanceOfStatementInPatternsTest + */ +public class InstanceOfStatementInPatternsTest { + + public static void main(String... args) { + assertEquals("B", testB(new B(new H("B")))); + assertEquals("AB", testC(new C(new A("A"), new B(new H("B"))))); + try { + testC(new C(new A("A"), new B(null))); + throw new AssertionError("Expected an MatchException, but none thrown."); + } catch (MatchException ex) { + ; + } + } + + static String testB(B b) { + return switch(b) { + case B(String s) -> s; + }; + } + + static String testC(C c) { + return switch(c) { + case C(String x, String y) -> x + y; + }; + } + + // dependency to super class + static class Base { + String s; + public Base(String s) { + this.s = s; + } + pattern Base(String s) { + match Base(this.s); + } + } + static class A extends Base { + public A(String s) { + super(s); + } + pattern A(String s) { + this instanceof Base(String ss); // instanceof calls super + match A(ss); + } + } + + // dependency to field + record H(T t) { } + static class B { + H h; + + public B(H s) { h = s; } + + pattern B(String s) { + h instanceof H(String ss); // not calling in super, just a regulard instanceof + // unconditional apart from null in the remainder + match B(ss); + } + } + + // multiple instanceof statements + static class C { + A a; + B b; + public C(A a, B b) {this.a = a; this.b = b;} + public pattern C(String x, String y) { + a instanceof A(var xx); // not calling in super, just a regulard instanceof + b instanceof B(var yy); + match C(xx, yy); + } + } + + private static void assertEquals(T expected, T actual) { + if (!Objects.equals(expected, actual)) { + throw new AssertionError("Expected: " + expected + ", but got: " + actual); + } + } +} \ No newline at end of file