Skip to content

Commit 711f8f6

Browse files
rlublecopybara-github
authored andcommitted
Cleanup InstanceOfPatternRewriter in preparation for the implementation of record patterns.
Simplifies the translation of `o instanceof A a` to `o instanceof A && (a = (A) o, true)` which will become useful for primitive binding patterns that result from record patterns. PiperOrigin-RevId: 830699577
1 parent 7afbfe7 commit 711f8f6

File tree

7 files changed

+213
-145
lines changed

7 files changed

+213
-145
lines changed

translator/src/main/java/com/google/devtools/j2objc/ast/Pattern.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,24 @@
1414

1515
package com.google.devtools.j2objc.ast;
1616

17+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
1718
import javax.lang.model.element.VariableElement;
19+
import javax.lang.model.type.TypeMirror;
1820

1921
/**
20-
* A tree node used as the base class for the different kinds of patterns
21-
* (introduced in Java 16).
22+
* A tree node used as the base class for the different kinds of patterns (introduced in Java 16).
2223
*/
2324
public abstract class Pattern extends TreeNode {
2425

2526
public Pattern() {}
2627

2728
public Pattern(Pattern other) {}
2829

30+
public abstract TypeMirror getTypeMirror();
31+
32+
@Override
33+
public abstract Pattern copy();
34+
2935
/**
3036
* A tree node for a binding pattern that matches a pattern with a variable
3137
* of any name and a type of the match candidate; an unnamed pattern. For
@@ -40,6 +46,11 @@ public AnyPattern() {}
4046

4147
public AnyPattern(AnyPattern other) {}
4248

49+
@Override
50+
public TypeMirror getTypeMirror() {
51+
return null;
52+
}
53+
4354
@Override
4455
public Kind getKind() {
4556
return Kind.ANY_PATTERN;
@@ -63,6 +74,7 @@ public AnyPattern copy() {
6374
* variable declared by the binding pattern.
6475
*/
6576
public static class BindingPattern extends Pattern {
77+
private TypeMirror typeMirror;
6678
private final ChildLink<SingleVariableDeclaration> var =
6779
ChildLink.create(SingleVariableDeclaration.class, this);
6880

@@ -75,6 +87,17 @@ public BindingPattern(BindingPattern other) {
7587
var.copyFrom(other.getVariable());
7688
}
7789

90+
@Override
91+
public TypeMirror getTypeMirror() {
92+
return typeMirror;
93+
}
94+
95+
@CanIgnoreReturnValue
96+
public BindingPattern setTypeMirror(TypeMirror typeMirror) {
97+
this.typeMirror = typeMirror;
98+
return this;
99+
}
100+
78101
@Override
79102
public Kind getKind() {
80103
return Kind.BINDING_PATTERN;
@@ -84,6 +107,7 @@ public SingleVariableDeclaration getVariable() {
84107
return var.get();
85108
}
86109

110+
@CanIgnoreReturnValue
87111
public BindingPattern setVariable(SingleVariableDeclaration e) {
88112
var.set(e);
89113
return this;

translator/src/main/java/com/google/devtools/j2objc/javac/TreeConverter.java

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,13 +1004,9 @@ private TreeNode convertIf(IfTree node, TreePath parent) {
10041004

10051005
private TreeNode convertInstanceOf(InstanceOfTree node, TreePath parent) {
10061006
TreePath path = getTreePath(parent, node);
1007+
1008+
Pattern pattern = node.getPattern() == null ? null : convertPattern(node.getPattern(), path);
10071009
TypeMirror clazz = getTypeMirror(getTreePath(path, node.getType()));
1008-
Pattern pattern = null;
1009-
PatternTree patternTree = node.getPattern();
1010-
if (patternTree instanceof BindingPatternTree bindingPattern
1011-
&& bindingPattern.getVariable() instanceof JCVariableDecl var) {
1012-
pattern = new Pattern.BindingPattern(var.sym);
1013-
}
10141010

10151011
return new InstanceofExpression()
10161012
.setLeftOperand((Expression) convert(node.getExpression(), path))
@@ -1019,7 +1015,17 @@ private TreeNode convertInstanceOf(InstanceOfTree node, TreePath parent) {
10191015
.setPattern(pattern);
10201016
}
10211017

1022-
private TreeNode convertLabeledStatement(LabeledStatementTree node, TreePath parent) {
1018+
private Pattern convertPattern(PatternTree patternTree, TreePath unused) {
1019+
return switch (patternTree) {
1020+
case BindingPatternTree bindingPattern
1021+
when bindingPattern.getVariable() instanceof JCVariableDecl var ->
1022+
new Pattern.BindingPattern(var.sym).setTypeMirror(var.sym.asType());
1023+
1024+
default -> throw new IllegalArgumentException("Unhandled pattern: " + patternTree);
1025+
};
1026+
}
1027+
1028+
private LabeledStatement convertLabeledStatement(LabeledStatementTree node, TreePath parent) {
10231029
TreePath path = getTreePath(parent, node);
10241030
return new LabeledStatement()
10251031
.setLabel(
@@ -1416,14 +1422,7 @@ private SwitchCase convertSwitchCaseLabel(CaseTree caseTree, TreePath parent) {
14161422
}
14171423
case PATTERN_CASE_LABEL -> {
14181424
PatternCaseLabelTree patternCaseLabelTree = (PatternCaseLabelTree) caseLabelTree;
1419-
if (patternCaseLabelTree.getPattern() instanceof BindingPatternTree bindingPatternTree) {
1420-
VariableTree varTree = (VariableTree) bindingPatternTree.getVariable();
1421-
VariableElement var =
1422-
((VariableDeclaration) convertVariableDeclaration(varTree, switchCasePath))
1423-
.getVariableElement();
1424-
Pattern.BindingPattern pattern = new Pattern.BindingPattern(var);
1425-
switchCase.setPattern(pattern);
1426-
}
1425+
switchCase.setPattern(convertPattern(patternCaseLabelTree.getPattern(), switchCasePath));
14271426
}
14281427
case DEFAULT_CASE_LABEL -> switchCase.setIsDefault(true);
14291428
default -> throw new AssertionError("unknown case label type: " + caseLabelTree.getKind());

translator/src/main/java/com/google/devtools/j2objc/translate/InstanceOfPatternRewriter.java

Lines changed: 66 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,27 @@
1616

1717
import com.google.devtools.j2objc.ast.Assignment;
1818
import com.google.devtools.j2objc.ast.Block;
19+
import com.google.devtools.j2objc.ast.BooleanLiteral;
1920
import com.google.devtools.j2objc.ast.CastExpression;
2021
import com.google.devtools.j2objc.ast.CommaExpression;
2122
import com.google.devtools.j2objc.ast.CompilationUnit;
22-
import com.google.devtools.j2objc.ast.ConditionalExpression;
2323
import com.google.devtools.j2objc.ast.Expression;
2424
import com.google.devtools.j2objc.ast.InfixExpression;
2525
import com.google.devtools.j2objc.ast.InfixExpression.Operator;
2626
import com.google.devtools.j2objc.ast.InstanceofExpression;
2727
import com.google.devtools.j2objc.ast.NullLiteral;
2828
import com.google.devtools.j2objc.ast.Pattern;
29+
import com.google.devtools.j2objc.ast.Pattern.BindingPattern;
2930
import com.google.devtools.j2objc.ast.SimpleName;
31+
import com.google.devtools.j2objc.ast.Type;
3032
import com.google.devtools.j2objc.ast.UnitTreeVisitor;
3133
import com.google.devtools.j2objc.ast.VariableDeclarationStatement;
3234
import com.google.devtools.j2objc.types.GeneratedVariableElement;
3335
import com.google.devtools.j2objc.util.ElementUtil;
3436
import java.util.ArrayDeque;
37+
import java.util.ArrayList;
3538
import java.util.Deque;
39+
import java.util.List;
3640
import javax.lang.model.element.VariableElement;
3741

3842
/**
@@ -69,62 +73,75 @@ public void endVisit(InstanceofExpression node) {
6973
return;
7074
}
7175

72-
VariableElement patternVariable =
73-
((Pattern.BindingPattern) node.getPattern()).getVariable().getVariableElement();
74-
75-
if (ElementUtil.isUnnamed(patternVariable)) {
76-
// If the pattern variable is unnamed this is a regular instanceof expression.
77-
node.setPattern(null);
78-
return;
79-
}
80-
81-
// Initialize the patternVariable to null since the instanceof pattern is used in the rewrite
82-
// of switches with pattern and the control flow of the rewrite prevents the objective-c
83-
// compiler from determining that the variable is never access uninitialized.
84-
// Type patternVariable = null;
85-
enclosingScopes
86-
.peek()
87-
.addStatement(
88-
0,
89-
new VariableDeclarationStatement(
90-
patternVariable, new NullLiteral(patternVariable.asType())));
91-
CommaExpression replacement = new CommaExpression();
92-
9376
Expression expression = node.getLeftOperand();
9477
// No need to generate a temporary variable if it is already a SimpleName.
9578
if (!(expression instanceof SimpleName)) {
9679
// Generate a temporary variable to preserve evaluation semantics since we can't guarantee
9780
// that the expression doesn't have side effects and can be evaluated multiple times.
9881
VariableElement tempVariable =
99-
GeneratedVariableElement.newLocalVar(
100-
"tmp", node.getLeftOperand().getTypeMirror(), patternVariable.getEnclosingElement());
101-
enclosingScopes.peek().addStatement(0, new VariableDeclarationStatement(tempVariable, null));
102-
// tmp = expr
103-
replacement.addExpression(
104-
new Assignment(new SimpleName(tempVariable), node.getLeftOperand().copy()));
82+
GeneratedVariableElement.newLocalVar("tmp", node.getLeftOperand().getTypeMirror(), null);
83+
enclosingScopes
84+
.peek()
85+
// Type tmp = expr
86+
.addStatement(
87+
0, new VariableDeclarationStatement(tempVariable, node.getLeftOperand().copy()));
10588
expression = new SimpleName(tempVariable);
10689
}
10790

108-
replacement.addExpressions(
109-
// patternVariable = expression instanceof T ? (T) expression : null
110-
new Assignment(
111-
new SimpleName(patternVariable),
112-
new ConditionalExpression()
113-
.setExpression(
114-
new InstanceofExpression(node)
115-
.setLeftOperand(expression.copy())
116-
.setPattern(null))
117-
.setThenExpression(
118-
new CastExpression(patternVariable.asType(), expression.copy())
119-
.setNeedsCastChk(false))
120-
.setElseExpression(new NullLiteral(patternVariable.asType()))
121-
.setTypeMirror(patternVariable.asType())),
122-
// patternVariable != null
123-
new InfixExpression()
124-
.setTypeMirror(typeUtil.getBoolean())
125-
.setOperator(Operator.NOT_EQUALS)
126-
.addOperand(new SimpleName(patternVariable))
127-
.addOperand(new NullLiteral(patternVariable.asType())));
128-
node.replaceWith(replacement);
91+
List<VariableElement> variablesToDeclare = new ArrayList<>();
92+
Expression condition =
93+
computePatternCondition(expression, node.getPattern(), variablesToDeclare);
94+
95+
for (var variableToDeclare : variablesToDeclare) {
96+
// Initialize the patternVariables to null. The implementation of patterns in switches creates
97+
// logic that prevents the objective-c compiler from determining that the variable is never
98+
// accessed uninitialized.
99+
100+
// Type patternVariable = null;
101+
enclosingScopes
102+
.peek()
103+
.addStatement(
104+
0,
105+
new VariableDeclarationStatement(
106+
variableToDeclare, new NullLiteral(variableToDeclare.asType())));
107+
}
108+
node.replaceWith(condition);
109+
}
110+
111+
private Expression computePatternCondition(
112+
Expression expression, Pattern pattern, List<VariableElement> variablesToDeclare) {
113+
switch (pattern) {
114+
case BindingPattern bindingPattern -> {
115+
VariableElement patternVariable = bindingPattern.getVariable().getVariableElement();
116+
117+
if (ElementUtil.isUnnamed(patternVariable)) {
118+
return new InstanceofExpression()
119+
.setLeftOperand(expression.copy())
120+
.setRightOperand(Type.newType(patternVariable.asType()))
121+
.setPattern(null)
122+
.setTypeMirror(typeUtil.getBoolean());
123+
}
124+
125+
variablesToDeclare.add(patternVariable);
126+
// expression instanceof T && (patternVariable = (T) expression, true)
127+
return andCondition(
128+
new InstanceofExpression()
129+
.setLeftOperand(expression.copy())
130+
.setRightOperand(Type.newType(patternVariable.asType()))
131+
.setTypeMirror(typeUtil.getBoolean()),
132+
new CommaExpression()
133+
.addExpressions(
134+
new Assignment(
135+
new SimpleName(patternVariable),
136+
new CastExpression(patternVariable.asType(), expression.copy())
137+
.setNeedsCastChk(false)),
138+
new BooleanLiteral(true, typeUtil.getBoolean())));
139+
}
140+
default -> throw new IllegalArgumentException("Unhandled pattern" + pattern);
141+
}
142+
}
143+
144+
private Expression andCondition(Expression lhs, Expression rhs) {
145+
return new InfixExpression(typeUtil.getBoolean(), Operator.CONDITIONAL_AND, lhs, rhs);
129146
}
130147
}

translator/src/main/java/com/google/devtools/j2objc/translate/SwitchConstructRewriter.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package com.google.devtools.j2objc.translate;
1616

17+
import static com.google.common.base.Preconditions.checkNotNull;
1718
import static com.google.common.base.Preconditions.checkState;
1819

1920
import com.google.devtools.j2objc.ast.Assignment;
@@ -38,6 +39,7 @@
3839
import com.google.devtools.j2objc.ast.SwitchStatement;
3940
import com.google.devtools.j2objc.ast.TreeNode;
4041
import com.google.devtools.j2objc.ast.TreeUtil;
42+
import com.google.devtools.j2objc.ast.Type;
4143
import com.google.devtools.j2objc.ast.UnitTreeVisitor;
4244
import com.google.devtools.j2objc.ast.VariableDeclarationStatement;
4345
import com.google.devtools.j2objc.ast.YieldStatement;
@@ -211,15 +213,15 @@ private Expression buildCondition(Expression switchExpression, SwitchCase switch
211213
.addOperand(expression.copy()));
212214
} else {
213215
checkState(switchCase.getExpressions().isEmpty());
214-
Pattern.BindingPattern pattern = (Pattern.BindingPattern) switchCase.getPattern();
216+
Pattern pattern = switchCase.getPattern();
215217
if (pattern != null) {
216218
condition =
217219
andCondition(
218220
condition,
219221
new InstanceofExpression()
220222
.setTypeMirror(typeUtil.getBoolean())
221223
.setLeftOperand(switchExpression.copy())
222-
.setRightOperand(pattern.getVariable().getType().copy())
224+
.setRightOperand(Type.newType(checkNotNull(pattern.getTypeMirror())))
223225
.setPattern(pattern.copy()));
224226
}
225227
}

translator/src/test/java/com/google/devtools/j2objc/gen/StatementGeneratorTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2802,8 +2802,8 @@ int test(Object o) {
28022802
translation,
28032803
"""
28042804
NSString *s = nil;
2805-
if ((s = [o isKindOfClass:[NSString class]] ? (NSString *) o : nil, !JreStringEqualsEquals(s, nil))) {
2806-
return [s java_length];
2805+
if ([o isKindOfClass:[NSString class]] && (s = (NSString *) o, true)) {
2806+
return [((NSString *) nil_chk(s)) java_length];
28072807
}
28082808
return 0;
28092809
""");
@@ -2833,7 +2833,7 @@ public void testInstanceOfPatternVariableTranslationWithGuards() throws IOExcept
28332833
translation,
28342834
"""
28352835
Point *p = nil;
2836-
if ((p = [o isKindOfClass:[Point class]] ? (Point *) o : nil, !JreObjectEqualsEquals(p, nil)) && x_ == p->x_ && y_ == p->y_) {
2836+
if ([o isKindOfClass:[Point class]] && (p = (Point *) o, true) && x_ == ((Point *) nil_chk(p))->x_ && y_ == p->y_) {
28372837
return true;
28382838
}
28392839
else {
@@ -2874,9 +2874,9 @@ java.lang.String test( java.lang.String str){
28742874
java.lang.String s=null;
28752875
java.lang.String s=null;
28762876
int selector=0;
2877-
if ((s=str instanceof java.lang.String ? str : null, !JreStringEqualsEquals(s, null)) && s.length() > 10) selector=1;
2877+
if (str instanceof java.lang.String && (s=str, true) && ((java.lang.String)nil_chk(s)).length() > 10) selector=1;
28782878
else if (str == null) selector=2;
2879-
else if ((s=str instanceof java.lang.String ? str : null, !JreStringEqualsEquals(s, null))) selector=3;
2879+
else if (str instanceof java.lang.String && (s=str, true)) selector=3;
28802880
switch (selector) {
28812881
case 1: return JreStrcat($$, "Long string: ", s);
28822882
case 2: return "null";

0 commit comments

Comments
 (0)