Skip to content

Commit

Permalink
Add instanceof statement
Browse files Browse the repository at this point in the history
  • Loading branch information
biboudis committed Sep 25, 2024
1 parent 934602d commit af57b19
Show file tree
Hide file tree
Showing 20 changed files with 509 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.sun.source.tree;

import jdk.internal.javac.PreviewFeature;

/**
* A tree node for an {@code instanceof} statement.
*
* For example:
* <pre>
* <em>expression</em> instanceof <em>record pattern</em>;
* </pre>
*
* @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();
}
8 changes: 8 additions & 0 deletions src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,14 @@ public interface TreeVisitor<R,P> {
*/
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}
*
Expand Down Expand Up @@ -1070,4 +1084,6 @@ public R visitYield(YieldTree node, P p) {
public R visitMatchStatement(MatchTree node, P p) {
return defaultAction(node, p);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -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}
*
Expand Down
13 changes: 13 additions & 0 deletions src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
28 changes: 28 additions & 0 deletions src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,34 @@ public void visitMatch(JCMatch tree) {
recordExit(new PendingExit(tree));
}

public void visitTypeTestStatement(JCInstanceOfStatement tree) {
ListBuffer<PendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>();

if (tree.pattern instanceof JCRecordPattern rp) {
visitRecordPattern(rp);
}

List<JCCase> 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<PendingExit> prevPendingExits = pendingExits;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -266,6 +268,44 @@ public void visitTypeTest(JCInstanceOf tree) {
}
}

@Override
public void visitTypeTestStatement(JCInstanceOfStatement tree) {
/**
* A statement of the form
*
* <pre>
* <expression> instanceof <pattern>;
* </pre>
*
* (where <pattern> is any pattern) is translated to:
*
* <pre>{@code
* if (!(<expression> instanceof(<pattern>)) {
* throw new MatchException(null, null);
* }
* }</pre>
*
*/
bindingContext = new BasicBindingContext();
try {
List<JCExpression> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -3016,11 +3017,17 @@ List<JCStatement> 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);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ public enum Tag {
*/
TYPETEST,

/** Type test statements, of type TypeTest.
*/
TYPETEST_STATEMENT,

/** Patterns.
*/
ANYPATTERN,
Expand Down Expand Up @@ -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,D> R accept(TreeVisitor<R,D> v, D d) {
return v.visitInstanceOfStatement(this, d);
}
@Override
public Tag getTag() {
return TYPETEST_STATEMENT;
}
}

/**
* A continue of a loop.
*/
Expand Down Expand Up @@ -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); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Loading

0 comments on commit af57b19

Please sign in to comment.