Skip to content

Commit

Permalink
8294944: Implement record pattern improvements for Record Patterns (S…
Browse files Browse the repository at this point in the history
…econd Preview)
  • Loading branch information
biboudis committed Oct 9, 2022
1 parent ad7b7d4 commit b00c64f
Show file tree
Hide file tree
Showing 21 changed files with 590 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,25 @@
* @since 1.6
*/
public interface EnhancedForLoopTree extends StatementTree {
/**
* Enhanced for declarations come in two forms:
* <ul>
* <li> local variable declarations and
* <li> record patterns
* </ul>
*/
public enum DeclarationKind {
/** enum constant for local variable declarations */
VARDECL,
/** enum constant for record pattern declarations */
RECORDPATTERNDECL
}

/**
* Returns the control variable for the loop.
* @return the control variable
*/
VariableTree getVariable();
Tree getVariableOrRecordPattern();

/**
* Returns the expression yielding the values for the control variable.
Expand All @@ -58,4 +72,10 @@ public interface EnhancedForLoopTree extends StatementTree {
* @return the body of the loop
*/
StatementTree getStatement();

/**
* Returns the kind of the declaration of the enhanced for.
* @return the kind of the declaration
*/
EnhancedForLoopTree.DeclarationKind getDeclarationKind();
}
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ public R visitForLoop(ForLoopTree node, P p) {
*/
@Override
public R visitEnhancedForLoop(EnhancedForLoopTree node, P p) {
R r = scan(node.getVariable(), p);
R r = scan(node.getVariableOrRecordPattern(), p);
r = scanAndReduce(node.getExpression(), p, r);
r = scanAndReduce(node.getStatement(), p, r);
return r;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.Queue;
import java.util.stream.Collectors;

import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.VariableTree;
Expand Down Expand Up @@ -424,18 +425,25 @@ class RedundantLocalVarTypeAnalyzerForEach extends RedundantLocalVarTypeAnalyzer

@Override
boolean match(JCEnhancedForLoop tree){
return !isImplicitlyTyped(tree.var);
Assert.check(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARDECL);

return !isImplicitlyTyped((JCVariableDecl) tree.varOrRecordPattern);
}
@Override
List<JCEnhancedForLoop> rewrite(JCEnhancedForLoop oldTree) {
Assert.check(oldTree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARDECL);

JCEnhancedForLoop newTree = copier.copy(oldTree);
newTree.var = rewriteVarType(oldTree.var);
newTree.varOrRecordPattern = rewriteVarType((JCVariableDecl) oldTree.varOrRecordPattern);
newTree.body = make.at(oldTree.body).Block(0, List.nil());
return List.of(newTree);
}
@Override
void process(JCEnhancedForLoop oldTree, JCEnhancedForLoop newTree, boolean hasErrors){
processVar(oldTree.var, newTree.var, hasErrors);
Assert.check(oldTree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARDECL);

processVar((JCVariableDecl) oldTree.varOrRecordPattern,
(JCVariableDecl) newTree.varOrRecordPattern, hasErrors);
}
}

Expand Down
44 changes: 37 additions & 7 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 @@ -34,6 +34,7 @@
import javax.tools.JavaFileObject;

import com.sun.source.tree.CaseTree;
import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.source.tree.MemberSelectTree;
Expand Down Expand Up @@ -1513,6 +1514,7 @@ public void visitForLoop(JCForLoop tree) {
public void visitForeachLoop(JCEnhancedForLoop tree) {
Env<AttrContext> loopEnv =
env.dup(env.tree, env.info.dup(env.info.scope.dup()));

try {
//the Formal Parameter of a for-each loop is not in the scope when
//attributing the for-each expression; we mimic this by attributing
Expand Down Expand Up @@ -1544,14 +1546,42 @@ public void visitForeachLoop(JCEnhancedForLoop tree) {
}
}
}
if (tree.var.isImplicitlyTyped()) {
Type inferredType = chk.checkLocalVarType(tree.var, elemtype, tree.var.name);
setSyntheticVariableType(tree.var, inferredType);
if(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARDECL) {
JCVariableDecl var = (JCVariableDecl) tree.varOrRecordPattern;

if (var.isImplicitlyTyped()) {
Type inferredType = chk.checkLocalVarType(var, elemtype, var.name);
setSyntheticVariableType(var, inferredType);
}
attribStat(var, loopEnv);
chk.checkType(tree.expr.pos(), elemtype, var.sym.type);

loopEnv.tree = tree; // before, we were not in loop!
attribStat(tree.body, loopEnv);
}
else if (tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.RECORDPATTERNDECL) {
JCRecordPattern recordPattern = (JCRecordPattern) tree.varOrRecordPattern;

attribTree(recordPattern, loopEnv, unknownExprInfo);

// for(<pattern> x : xs) { y }
// we include x's bindings when true in y
// we don't do anything with x's bindings when false

MatchBindings forWithRecordPatternBindings = matchBindings;
Env<AttrContext> recordPatternEnv = bindingEnv(loopEnv, forWithRecordPatternBindings.bindingsWhenTrue);

Type clazztype = recordPattern.type;

chk.checkType(tree.expr.pos(), elemtype, clazztype);

recordPatternEnv.tree = tree; // before, we were not in loop!
try {
attribStat(tree.body, recordPatternEnv);
} finally {
recordPatternEnv.info.scope.leave();
}
}
attribStat(tree.var, loopEnv);
chk.checkType(tree.expr.pos(), elemtype, tree.var.sym.type);
loopEnv.tree = tree; // before, we were not in loop!
attribStat(tree.body, loopEnv);
result = null;
}
finally {
Expand Down
51 changes: 46 additions & 5 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 @@ -644,7 +644,40 @@ public void visitForLoop(JCForLoop tree) {
}

public void visitForeachLoop(JCEnhancedForLoop tree) {
visitVarDef(tree.var);
if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) {
visitVarDef(jcVariableDecl);
} else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
visitRecordPattern(jcRecordPattern);

// copied from Attr.java
Type exprType = types.cvarUpperBound(tree.expr.type);
Type elemtype = types.elemtype(exprType);
if (elemtype == null) {
Type base = types.asSuper(exprType, syms.iterableType.tsym);
if (base == null) {
// TODO: repeating error here, should be covered from Attr
log.error(tree.expr.pos(),
Errors.ForeachNotApplicableToType(exprType,
Fragments.TypeReqArrayOrIterable));
elemtype = types.createErrorType(exprType);
} else {
List<Type> iterableParams = base.allparams();
elemtype = iterableParams.isEmpty()
? syms.objectType
: types.wildUpperBound(iterableParams.head);
}
}

Set<Symbol> coveredSymbols =
coveredSymbols(jcRecordPattern.pos(), elemtype, List.of(jcRecordPattern));

boolean isExhaustive =
isExhaustive(jcRecordPattern.pos(), elemtype, coveredSymbols);

if (!isExhaustive) {
log.error(tree, Errors.NotExhaustive);
}
}
ListBuffer<PendingExit> prevPendingExits = pendingExits;
scan(tree.expr);
pendingExits = new ListBuffer<>();
Expand Down Expand Up @@ -1355,7 +1388,11 @@ public void visitForLoop(JCForLoop tree) {
}

public void visitForeachLoop(JCEnhancedForLoop tree) {
visitVarDef(tree.var);
if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) {
visitVarDef(jcVariableDecl);
} else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
visitRecordPattern(jcRecordPattern);
}
ListBuffer<PendingExit> prevPendingExits = pendingExits;
scan(tree.expr);
pendingExits = new ListBuffer<>();
Expand Down Expand Up @@ -2516,8 +2553,6 @@ public void visitForLoop(JCForLoop tree) {
}

public void visitForeachLoop(JCEnhancedForLoop tree) {
visitVarDef(tree.var);

ListBuffer<PendingExit> prevPendingExits = pendingExits;
FlowKind prevFlowKind = flowKind;
flowKind = FlowKind.NORMAL;
Expand All @@ -2526,7 +2561,13 @@ public void visitForeachLoop(JCEnhancedForLoop tree) {
final Bits initsStart = new Bits(inits);
final Bits uninitsStart = new Bits(uninits);

letInit(tree.pos(), tree.var.sym);
if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) {
visitVarDef(jcVariableDecl);
letInit(tree.pos(), jcVariableDecl.sym);
} else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
visitRecordPattern(jcRecordPattern);
}

pendingExits = new ListBuffer<>();
int prevErrors = log.nerrors;
do {
Expand Down
46 changes: 28 additions & 18 deletions src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.*;
import java.util.stream.Collectors;

import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Kinds.KindSelector;
import com.sun.tools.javac.code.Scope.WriteableScope;
Expand Down Expand Up @@ -3458,13 +3459,18 @@ private void visitArrayForeachLoop(JCEnhancedForLoop tree) {
Type elemtype = types.elemtype(tree.expr.type);
JCExpression loopvarinit = make.Indexed(make.Ident(arraycache),
make.Ident(index)).setType(elemtype);
JCVariableDecl loopvardef = (JCVariableDecl)make.VarDef(tree.var.mods,
tree.var.name,
tree.var.vartype,
loopvarinit).setType(tree.var.type);
loopvardef.sym = tree.var.sym;
JCBlock body = make.
Block(0, List.of(loopvardef, tree.body));

JCBlock body = null;

Assert.check(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARDECL);
JCVariableDecl oldVar = (JCVariableDecl) tree.varOrRecordPattern;
JCVariableDecl loopvardef = (JCVariableDecl) make.VarDef(oldVar.mods,
oldVar.name,
oldVar.vartype,
loopvarinit).setType(oldVar.type);
loopvardef.sym = oldVar.sym;
body = make.
Block(0, List.of(loopvardef, tree.body));

result = translate(make.
ForLoop(loopinit,
Expand Down Expand Up @@ -3544,22 +3550,26 @@ private void visitIterableForeachLoop(JCEnhancedForLoop tree) {
itvar.type,
List.nil());
JCExpression vardefinit = make.App(make.Select(make.Ident(itvar), next));
if (tree.var.type.isPrimitive())

Assert.check(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARDECL);

JCVariableDecl var = (JCVariableDecl) tree.varOrRecordPattern;
if (var.type.isPrimitive())
vardefinit = make.TypeCast(types.cvarUpperBound(iteratorTarget), vardefinit);
else
vardefinit = make.TypeCast(tree.var.type, vardefinit);
JCVariableDecl indexDef = (JCVariableDecl)make.VarDef(tree.var.mods,
tree.var.name,
tree.var.vartype,
vardefinit).setType(tree.var.type);
indexDef.sym = tree.var.sym;
vardefinit = make.TypeCast(var.type, vardefinit);
JCVariableDecl indexDef = (JCVariableDecl) make.VarDef(var.mods,
var.name,
var.vartype,
vardefinit).setType(var.type);
indexDef.sym = var.sym;
JCBlock body = make.Block(0, List.of(indexDef, tree.body));
body.endpos = TreeInfo.endPos(tree.body);
result = translate(make.
ForLoop(List.of(init),
cond,
List.nil(),
body));
ForLoop(List.of(init),
cond,
List.nil(),
body));
patchTargets(body, tree, result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
package com.sun.tools.javac.comp;

import com.sun.source.tree.CaseTree;
import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Kinds;
Expand Down Expand Up @@ -715,6 +716,73 @@ public void visitForLoop(JCForLoop tree) {
}
}

@Override
public void visitForeachLoop(JCTree.JCEnhancedForLoop tree) {
bindingContext = new BasicBindingContext();
VarSymbol prevCurrentValue = currentValue;
try {
if (tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.RECORDPATTERNDECL) {
/**
* A statement of the form
*
* <pre>
* for (<pattern> : coll ) stmt ;
* </pre>
*
* (where coll implements {@code Iterable<R>}) gets translated to
*
* <pre>{@code
* for (<type-of-coll-item> N$temp : coll) {
* switch (N$temp) {
* case <pattern>: stmt;
* case null: throw new MatchException();
* }
* }</pre>
*
*/
Type selectorType = tree.varOrRecordPattern.type;

currentValue = new VarSymbol(Flags.FINAL | Flags.SYNTHETIC,
names.fromString("patt" + tree.pos + target.syntheticNameChar() + "temp"),
selectorType,
currentMethodSym);

JCStatement newForVariableDeclaration =
make.at(tree.pos).VarDef(currentValue, null).setType(selectorType);

List<JCExpression> params = List.of(makeNull(), makeNull());
JCTree.JCThrow thr = make.Throw(makeNewClass(syms.matchExceptionType, params));
JCCase caseNull = make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(makeNull())), List.of(thr), null);

JCCase casePattern = make.Case(CaseTree.CaseKind.STATEMENT,
List.of(make.PatternCaseLabel((JCPattern) tree.varOrRecordPattern, null)),
List.of(translate(tree.body)),
null);

JCSwitch switchBody =
make.Switch(make.Ident(currentValue).setType(selectorType),
List.of(caseNull, casePattern));

switchBody.patternSwitch = true;

// re-using the same node to eliminate the need to re-patch targets (break/continue)
tree.varOrRecordPattern = newForVariableDeclaration.setType(selectorType);
tree.expr = translate(tree.expr);
tree.body = translate(switchBody);

JCTree.JCEnhancedForLoop newForEach = tree;

result = bindingContext.decorateStatement(newForEach);
} else {
super.visitForeachLoop(tree);
result = bindingContext.decorateStatement(tree);
}
} finally {
currentValue = prevCurrentValue;
bindingContext.pop();
}
}

@Override
public void visitWhileLoop(JCWhileLoop tree) {
bindingContext = new BasicBindingContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ public void visitForLoop(JCForLoop tree) {
}

public void visitForeachLoop(JCEnhancedForLoop tree) {
tree.var = translate(tree.var, null);
tree.varOrRecordPattern = translate(tree.varOrRecordPattern, null);
Type iterableType = tree.expr.type;
tree.expr = translate(tree.expr, erasure(tree.expr.type));
if (types.elemtype(tree.expr.type) == null)
Expand Down
Loading

0 comments on commit b00c64f

Please sign in to comment.