Skip to content
This repository has been archived by the owner on Apr 24, 2023. It is now read-only.
/ jdk20 Public archive

Commit

Permalink
8294943: Implement record patterns in enhanced for
Browse files Browse the repository at this point in the history
8296802: Parse errors when deconstructing a record using the enhanced for loop of JEP 432

Co-authored-by: Jan Lahoda <[email protected]>
Co-authored-by: Aggelos Biboudis <[email protected]>
Co-authored-by: Maurizio Cimadamore <[email protected]>
Reviewed-by: mcimadamore, vromero
  • Loading branch information
3 people committed Dec 1, 2022
1 parent fc9d419 commit 2cb64a7
Show file tree
Hide file tree
Showing 27 changed files with 838 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

package com.sun.source.tree;

import jdk.internal.javac.PreviewFeature;

/**
* A tree node for an "enhanced" {@code for} loop statement.
*
Expand All @@ -41,12 +43,37 @@
* @since 1.6
*/
public interface EnhancedForLoopTree extends StatementTree {
/**
* "Enhanced" {@code for} declarations come in two forms:
* <ul>
* <li> local variable declarations and
* <li> record patterns
* </ul>
*
* @since 20
*/
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
public enum DeclarationKind {
/** enum constant for local variable declarations */
VARIABLE,
/** enum constant for record pattern declarations */
PATTERN
}

/**
* Returns the control variable for the loop.
* @return the control variable
* @return the control variable, or {@code null} if this "enhanced" {@code for} uses a pattern
*/
VariableTree getVariable();

/**
* Returns the control variable or pattern for the loop.
* @return the control variable or pattern
* @since 20
*/
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
Tree getVariableOrRecordPattern();

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

/**
* Returns the kind of the declaration of the "enhanced" {@code for}.
* @return the kind of the declaration
* @since 20
*/
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
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 @@ -190,6 +190,7 @@ public static Symtab instance(Context context) {
public final Type incompatibleClassChangeErrorType;
public final Type cloneNotSupportedExceptionType;
public final Type matchExceptionType;
public final Type nullPointerExceptionType;
public final Type annotationType;
public final TypeSymbol enumSym;
public final Type listType;
Expand Down Expand Up @@ -555,6 +556,7 @@ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
incompatibleClassChangeErrorType = enterClass("java.lang.IncompatibleClassChangeError");
cloneNotSupportedExceptionType = enterClass("java.lang.CloneNotSupportedException");
matchExceptionType = enterClass("java.lang.MatchException");
nullPointerExceptionType = enterClass("java.lang.NullPointerException");
annotationType = enterClass("java.lang.annotation.Annotation");
classLoaderType = enterClass("java.lang.ClassLoader");
enumSym = enterClass(java_base, names.java_lang_Enum);
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,24 @@ class RedundantLocalVarTypeAnalyzerForEach extends RedundantLocalVarTypeAnalyzer

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

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.VARIABLE);

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

Expand Down
50 changes: 39 additions & 11 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,24 +1514,25 @@ 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
//the for-each expression first (against original scope).
Type exprType = types.cvarUpperBound(attribExpr(tree.expr, loopEnv));
chk.checkNonVoid(tree.pos(), exprType);
Type elemtype = types.elemtype(exprType); // perhaps expr is an array?
if (elemtype == null) {
tree.elementType = types.elemtype(exprType); // perhaps expr is an array?
if (tree.elementType == null) {
// or perhaps expr implements Iterable<T>?
Type base = types.asSuper(exprType, syms.iterableType.tsym);
if (base == null) {
log.error(tree.expr.pos(),
Errors.ForeachNotApplicableToType(exprType,
Fragments.TypeReqArrayOrIterable));
elemtype = types.createErrorType(exprType);
tree.elementType = types.createErrorType(exprType);
} else {
List<Type> iterableParams = base.allparams();
elemtype = iterableParams.isEmpty()
tree.elementType = iterableParams.isEmpty()
? syms.objectType
: types.wildUpperBound(iterableParams.head);

Expand All @@ -1544,14 +1546,40 @@ 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.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) {
if (jcVariableDecl.isImplicitlyTyped()) {
Type inferredType = chk.checkLocalVarType(jcVariableDecl, tree.elementType, jcVariableDecl.name);
setSyntheticVariableType(jcVariableDecl, inferredType);
}
attribStat(jcVariableDecl, loopEnv);
chk.checkType(tree.expr.pos(), tree.elementType, jcVariableDecl.sym.type);

loopEnv.tree = tree; // before, we were not in loop!
attribStat(tree.body, loopEnv);
} else {
Assert.check(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.PATTERN);
JCRecordPattern jcRecordPattern = (JCRecordPattern) tree.varOrRecordPattern;

attribExpr(jcRecordPattern, loopEnv, tree.elementType);

// 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 = jcRecordPattern.type;

checkCastablePattern(tree.expr.pos(), tree.elementType, 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
32 changes: 27 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 @@ -645,7 +645,21 @@ 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);

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

boolean isExhaustive =
isExhaustive(jcRecordPattern.pos(), tree.elementType, coveredSymbols);

if (!isExhaustive) {
log.error(tree, Errors.ForeachNotExhaustiveOnType(jcRecordPattern.type, tree.elementType));
}
}
ListBuffer<PendingExit> prevPendingExits = pendingExits;
scan(tree.expr);
pendingExits = new ListBuffer<>();
Expand Down Expand Up @@ -1358,7 +1372,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 @@ -2506,8 +2524,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 @@ -2516,7 +2532,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
44 changes: 27 additions & 17 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 @@ -3467,13 +3468,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;

Assert.check(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE);
JCVariableDecl jcVariableDecl = (JCVariableDecl) tree.varOrRecordPattern;

JCVariableDecl loopvardef = (JCVariableDecl)make.VarDef(jcVariableDecl.mods,
jcVariableDecl.name,
jcVariableDecl.vartype,
loopvarinit).setType(jcVariableDecl.type);
loopvardef.sym = jcVariableDecl.sym;
JCBlock body = make.
Block(0, List.of(loopvardef, tree.body));
Block(0, List.of(loopvardef, tree.body));


result = translate(make.
ForLoop(loopinit,
Expand Down Expand Up @@ -3552,22 +3558,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.VARIABLE);

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
Loading

0 comments on commit 2cb64a7

Please sign in to comment.