diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/EnhancedForLoopTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/EnhancedForLoopTree.java
index 7c56985e4fc..05f53542846 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/EnhancedForLoopTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/EnhancedForLoopTree.java
@@ -41,11 +41,25 @@
* @since 1.6
*/
public interface EnhancedForLoopTree extends StatementTree {
+ /**
+ * Enhanced for declarations come in two forms:
+ *
+ * - local variable declarations and
+ *
- record patterns
+ *
+ */
+ 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.
@@ -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();
}
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 36537cef76f..0814008cc75 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
@@ -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;
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java
index 0a17a27a046..0fc9014f1d0 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java
@@ -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;
@@ -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 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);
}
}
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 9cd9b6492fe..d4206edef9f 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
@@ -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;
@@ -1513,6 +1514,7 @@ public void visitForLoop(JCForLoop tree) {
public void visitForeachLoop(JCEnhancedForLoop tree) {
Env 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
@@ -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( 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 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 {
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 20abb281211..ef8007b4df9 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
@@ -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 iterableParams = base.allparams();
+ elemtype = iterableParams.isEmpty()
+ ? syms.objectType
+ : types.wildUpperBound(iterableParams.head);
+ }
+ }
+
+ Set coveredSymbols =
+ coveredSymbols(jcRecordPattern.pos(), elemtype, List.of(jcRecordPattern));
+
+ boolean isExhaustive =
+ isExhaustive(jcRecordPattern.pos(), elemtype, coveredSymbols);
+
+ if (!isExhaustive) {
+ log.error(tree, Errors.NotExhaustive);
+ }
+ }
ListBuffer prevPendingExits = pendingExits;
scan(tree.expr);
pendingExits = new ListBuffer<>();
@@ -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 prevPendingExits = pendingExits;
scan(tree.expr);
pendingExits = new ListBuffer<>();
@@ -2516,8 +2553,6 @@ public void visitForLoop(JCForLoop tree) {
}
public void visitForeachLoop(JCEnhancedForLoop tree) {
- visitVarDef(tree.var);
-
ListBuffer prevPendingExits = pendingExits;
FlowKind prevFlowKind = flowKind;
flowKind = FlowKind.NORMAL;
@@ -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 {
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
index 1b2d23a5f4a..b7433890599 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
@@ -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;
@@ -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,
@@ -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);
}
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 5d42348d213..945b7b6712a 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
@@ -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;
@@ -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
+ *
+ *
+ * for ( : coll ) stmt ;
+ *
+ *
+ * (where coll implements {@code Iterable}) gets translated to
+ *
+ * {@code
+ * for ( N$temp : coll) {
+ * switch (N$temp) {
+ * case : stmt;
+ * case null: throw new MatchException();
+ * }
+ * }
+ *
+ */
+ 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 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();
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 b1261f9205c..4ed244fd88a 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
@@ -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)
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java
index 554c6c5c7a2..f8d61fce126 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java
@@ -373,7 +373,7 @@ && scan(tree.step, that.step)
public void visitForeachLoop(JCEnhancedForLoop tree) {
JCEnhancedForLoop that = (JCEnhancedForLoop) parameter;
result =
- scan(tree.var, that.var)
+ scan(tree.varOrRecordPattern, that.varOrRecordPattern)
&& scan(tree.expr, that.expr)
&& scan(tree.body, that.body);
}
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java
index 2b752e6ca29..8e16d7e3c48 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java
@@ -293,7 +293,7 @@ public void visitForLoop(JCForLoop tree) {
public void visitForeachLoop(JCEnhancedForLoop tree) {
SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
- sr.mergeWith(csp(tree.var));
+ sr.mergeWith(csp(tree.varOrRecordPattern));
sr.mergeWith(csp(tree.expr));
sr.mergeWith(csp(tree.body));
result = sr;
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 0518adc134e..ff3c862dccf 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
@@ -759,7 +759,6 @@ public JCExpression parseExpression() {
/** parses patterns.
*/
-
public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType,
boolean allowVar, boolean checkGuard) {
JCPattern pattern;
@@ -2826,25 +2825,47 @@ public JCStatement parseSimpleStatement() {
case FOR: {
nextToken();
accept(LPAREN);
- List inits = token.kind == SEMI ? List.nil() : forInit();
- if (inits.length() == 1 &&
- inits.head.hasTag(VARDEF) &&
- ((JCVariableDecl) inits.head).init == null &&
- token.kind == COLON) {
- JCVariableDecl var = (JCVariableDecl)inits.head;
+ JCTree pattern;
+
+ ForInitResult initResult = analyzeForInit();
+
+ if (initResult == ForInitResult.RecordPattern) {
+ int patternPos = token.pos;
+ JCModifiers mods = optFinal(0);
+ int typePos = token.pos;
+ JCExpression type = unannotatedType(false);
+
+ pattern = parsePattern(patternPos, mods, type, false, false);
+
+ if (pattern != null) {
+ checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
+ }
accept(COLON);
JCExpression expr = parseExpression();
accept(RPAREN);
JCStatement body = parseStatementAsBlock();
- return F.at(pos).ForeachLoop(var, expr, body);
+ return F.at(pos).ForeachLoop(pattern, expr, body);
} else {
- accept(SEMI);
- JCExpression cond = token.kind == SEMI ? null : parseExpression();
- accept(SEMI);
- List steps = token.kind == RPAREN ? List.nil() : forUpdate();
- accept(RPAREN);
- JCStatement body = parseStatementAsBlock();
- return F.at(pos).ForLoop(inits, cond, steps, body);
+ List inits = token.kind == SEMI ? List.nil() : forInit();
+ if (inits.length() == 1 &&
+ inits.head.hasTag(VARDEF) &&
+ ((JCVariableDecl) inits.head).init == null &&
+ token.kind == COLON) {
+ JCVariableDecl var = (JCVariableDecl) inits.head;
+ accept(COLON);
+ JCExpression expr = parseExpression();
+ accept(RPAREN);
+ JCStatement body = parseStatementAsBlock();
+ return F.at(pos).ForeachLoop(var, expr, body);
+ } else {
+ accept(SEMI);
+ JCExpression cond = token.kind == SEMI ? null : parseExpression();
+ accept(SEMI);
+ List steps = token.kind == RPAREN ? List.nil() : forUpdate();
+ accept(RPAREN);
+ JCStatement body = parseStatementAsBlock();
+ return F.at(pos).ForLoop(inits, cond, steps, body);
+ }
}
}
case WHILE: {
@@ -2961,6 +2982,79 @@ public JCStatement parseSimpleStatement() {
}
}
+ private enum ForInitResult {
+ LocalVarDecl,
+ RecordPattern
+ }
+
+ @SuppressWarnings("fallthrough")
+ ForInitResult analyzeForInit() {
+ int depth = 0;
+ boolean inType = false;
+ boolean inSelectionAndParenthesis = false;
+ ForInitResult defaultResult = ForInitResult.LocalVarDecl;
+ outer: for (int lookahead = 0; ; lookahead++) {
+ TokenKind tk = S.token(lookahead).kind;
+ switch (tk) {
+ case DOT:
+ if (inType) break; // in qualified type
+ case COMMA: case EXTENDS: case SUPER: case AMP: case QUES:
+ break;
+ case BYTE: case SHORT: case INT: case LONG: case FLOAT:
+ case DOUBLE: case BOOLEAN: case CHAR: case VOID:
+ if (peekToken(lookahead, IDENTIFIER)) {
+ return ForInitResult.LocalVarDecl;
+ }
+ break;
+ case LPAREN:
+ if (lookahead != 0 && inType) {
+ inSelectionAndParenthesis = true;
+ inType = false;
+ }
+ break;
+ case RPAREN:
+ // a method call in the init part or a record pattern?
+ if (inSelectionAndParenthesis) {
+ if (peekToken(lookahead, COMMA) ||
+ peekToken(lookahead, DOT) ||
+ peekToken(lookahead, SEMI)) {
+ return ForInitResult.LocalVarDecl;
+ }
+ return ForInitResult.RecordPattern;
+ }
+ case UNDERSCORE:
+ case ASSERT:
+ case ENUM:
+ case IDENTIFIER:
+ if (lookahead == 0) {
+ inType = true;
+ }
+ break;
+ case FINAL:
+ case ELLIPSIS:
+ case MONKEYS_AT:
+ lookahead = skipAnnotation(lookahead);
+ break;
+ case LBRACKET:
+ if (peekToken(lookahead, RBRACKET)) {
+ return ForInitResult.LocalVarDecl;
+ }
+ case LT:
+ depth++; break;
+ case GTGTGT:
+ depth--;
+ case GTGT:
+ depth--;
+ case GT:
+ depth--;
+ break;
+ default:
+ //this includes EOF
+ return defaultResult;
+ }
+ }
+ }
+
@Override
public JCStatement parseStatement() {
return parseStatementAsBlock();
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 488e386ab5d..895d24d0b91 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
@@ -1210,11 +1210,12 @@ public Tag getTag() {
* The enhanced for loop.
*/
public static class JCEnhancedForLoop extends JCStatement implements EnhancedForLoopTree {
- public JCVariableDecl var;
+ public JCTree varOrRecordPattern;
public JCExpression expr;
public JCStatement body;
- protected JCEnhancedForLoop(JCVariableDecl var, JCExpression expr, JCStatement body) {
- this.var = var;
+
+ protected JCEnhancedForLoop(JCTree varOrRecordPattern, JCExpression expr, JCStatement body) {
+ this.varOrRecordPattern = varOrRecordPattern;
this.expr = expr;
this.body = body;
}
@@ -1224,7 +1225,7 @@ protected JCEnhancedForLoop(JCVariableDecl var, JCExpression expr, JCStatement b
@DefinedBy(Api.COMPILER_TREE)
public Kind getKind() { return Kind.ENHANCED_FOR_LOOP; }
@DefinedBy(Api.COMPILER_TREE)
- public JCVariableDecl getVariable() { return var; }
+ public JCTree getVariableOrRecordPattern() { return varOrRecordPattern; }
@DefinedBy(Api.COMPILER_TREE)
public JCExpression getExpression() { return expr; }
@DefinedBy(Api.COMPILER_TREE)
@@ -1237,6 +1238,15 @@ public R accept(TreeVisitor v, D d) {
public Tag getTag() {
return FOREACHLOOP;
}
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public EnhancedForLoopTree.DeclarationKind getDeclarationKind() {
+ if (varOrRecordPattern instanceof JCVariableDecl) {
+ return DeclarationKind.VARDECL;
+ }
+ else {
+ return DeclarationKind.RECORDPATTERNDECL;
+ }
+ }
}
/**
@@ -3414,7 +3424,7 @@ JCForLoop ForLoop(List init,
JCExpression cond,
List step,
JCStatement body);
- JCEnhancedForLoop ForeachLoop(JCVariableDecl var, JCExpression expr, JCStatement body);
+ JCEnhancedForLoop ForeachLoop(JCTree var, JCExpression expr, JCStatement body);
JCLabeledStatement Labelled(Name label, JCStatement body);
JCSwitch Switch(JCExpression selector, List cases);
JCSwitchExpression SwitchExpression(JCExpression selector, List cases);
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 3b07bf9f6d7..4ceae53248b 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
@@ -801,7 +801,7 @@ public void visitForLoop(JCForLoop tree) {
public void visitForeachLoop(JCEnhancedForLoop tree) {
try {
print("for (");
- printExpr(tree.var);
+ printExpr(tree.varOrRecordPattern);
print(" : ");
printExpr(tree.expr);
print(") ");
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 90f7a8c4f1e..cd1d691061a 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
@@ -223,10 +223,10 @@ public JCTree visitExpressionStatement(ExpressionStatementTree node, P p) {
@DefinedBy(Api.COMPILER_TREE)
public JCTree visitEnhancedForLoop(EnhancedForLoopTree node, P p) {
JCEnhancedForLoop t = (JCEnhancedForLoop) node;
- JCVariableDecl var = copy(t.var, p);
+ JCTree varOrRecordPattern = copy(t.varOrRecordPattern, p);
JCExpression expr = copy(t.expr, p);
JCStatement body = copy(t.body, p);
- return M.at(t.pos).ForeachLoop(var, expr, body);
+ return M.at(t.pos).ForeachLoop(varOrRecordPattern, expr, body);
}
@DefinedBy(Api.COMPILER_TREE)
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 f1c1d2fea1e..8f80dec9034 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
@@ -273,8 +273,8 @@ public JCForLoop ForLoop(List init,
return tree;
}
- public JCEnhancedForLoop ForeachLoop(JCVariableDecl var, JCExpression expr, JCStatement body) {
- JCEnhancedForLoop tree = new JCEnhancedForLoop(var, expr, body);
+ public JCEnhancedForLoop ForeachLoop(JCTree varOrRecordPattern, JCExpression expr, JCStatement body) {
+ JCEnhancedForLoop tree = new JCEnhancedForLoop(varOrRecordPattern, expr, body);
tree.pos = pos;
return tree;
}
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 dd4129fa85b..7c28e949102 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
@@ -162,7 +162,7 @@ public void visitForLoop(JCForLoop tree) {
}
public void visitForeachLoop(JCEnhancedForLoop tree) {
- scan(tree.var);
+ scan(tree.varOrRecordPattern);
scan(tree.expr);
scan(tree.body);
}
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 abd01ffdb2f..a3cf0040175 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
@@ -189,7 +189,7 @@ public void visitForLoop(JCForLoop tree) {
}
public void visitForeachLoop(JCEnhancedForLoop tree) {
- tree.var = translate(tree.var);
+ tree.varOrRecordPattern = translate(tree.varOrRecordPattern);
tree.expr = translate(tree.expr);
tree.body = translate(tree.body);
result = tree;
diff --git a/test/langtools/tools/javac/lib/DPrinter.java b/test/langtools/tools/javac/lib/DPrinter.java
index a5c0f974518..c32f1135b73 100644
--- a/test/langtools/tools/javac/lib/DPrinter.java
+++ b/test/langtools/tools/javac/lib/DPrinter.java
@@ -720,7 +720,7 @@ public void visitForLoop(JCForLoop tree) {
@Override
public void visitForeachLoop(JCEnhancedForLoop tree) {
- printTree("var", tree.var);
+ printTree("var", tree.varOrRecordPattern);
printTree("expr", tree.expr);
printTree("body", tree.body);
}
diff --git a/test/langtools/tools/javac/patterns/ForEachPatterns.java b/test/langtools/tools/javac/patterns/ForEachPatterns.java
new file mode 100644
index 00000000000..6efe296230c
--- /dev/null
+++ b/test/langtools/tools/javac/patterns/ForEachPatterns.java
@@ -0,0 +1,196 @@
+/*
+ * @test /nodynamiccopyright/
+ * @summary
+ * @compile --enable-preview -source ${jdk.version} ForEachPatterns.java
+ * @run main/othervm --enable-preview ForEachPatterns
+ */
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+
+public class ForEachPatterns {
+ public static void main(String[] args) {
+
+ List in = List.of(new Point(1, 2), new Point(2, 3));
+ List inRaw = List.of(new Point(1, 2), new Point(2, 3), new Frog(3, 4));
+ List inWithPointEx = List.of(new PointEx(1, 2));
+ byte[] inBytes = { (byte) 127, (byte) 127 };
+ List inWithNullComponent = List.of(new Point(1, null), new Point(2, 3));
+ List inWithNull = new ArrayList<>();
+ Point[] inArray = in.toArray(Point[]::new);
+
+ inWithNull.add(new Point(2, 3));
+ inWithNull.add(null);
+
+ assertEquals(8, iteratorEnhancedFor(in));
+ assertEquals(8, arrayEnhancedFor(inArray));
+ assertEquals(8, iteratorEnhancedForWithBinding(in));
+ assertEquals(8, simpleDecostructionPatternWithAccesses(in));
+ assertEx(ForEachPatterns::simpleDecostructionPatternWithAccesses, inWithNull, MatchException.class);
+ assertEx(ForEachPatterns::simpleDecostructionPatternWithAccesses, inWithNullComponent, NullPointerException.class);
+ assertEx(ForEachPatterns::simpleDecostructionPatternException, inWithPointEx, MatchException.class);
+ assertEx(ForEachPatterns::simpleDecostructionPatternWithAccesses, (List) inRaw, ClassCastException.class);
+ assertEquals(2, simpleDecostructionPatternNoComponentAccess(in));
+ assertEx(ForEachPatterns::simpleDecostructionPatternNoComponentAccess, inWithNull, MatchException.class);
+ assertEquals(2, simpleDecostructionPatternNoComponentAccess(inWithNullComponent));
+ assertEquals(8, varAndConcrete(in));
+ assertEquals(3, returnFromEnhancedFor(in));
+ assertEquals(0, breakFromEnhancedFor(in));
+ assertEquals(254, primitiveWidening(inBytes));
+ }
+
+ static int iteratorEnhancedFor(List points) {
+ int result = 0;
+ for (Point(Integer a, Integer b) : points) {
+ result += a + b;
+ }
+ return result;
+ }
+
+ static int arrayEnhancedFor(Point[] points) {
+ int result = 0;
+ for (Point(Integer a, Integer b) : points) {
+ result += a + b;
+ }
+ return result;
+ }
+
+ static int iteratorEnhancedForWithBinding(List points) {
+ int result = 0;
+ for (Point(Integer a, Integer b) p: points) {
+ result += p.x() + p.y();
+ }
+ return result;
+ }
+
+ static int simpleDecostructionPatternWithAccesses(List points) {
+ int result = 0;
+ for (Point(var a, var b): points) {
+ result += a + b;
+ }
+ return result;
+ }
+
+ static int simpleDecostructionPatternException(List points) {
+ int result = 0;
+ for (PointEx(var a, var b): points) {
+ result += a + b;
+ }
+ return result;
+ }
+
+ static int simpleDecostructionPatternNoComponentAccess(List points) {
+ int result = 0;
+ for (Point(var a, var b): points) {
+ result += 1;
+ }
+ return result;
+ }
+
+ static int varAndConcrete(List points) {
+ int result = 0;
+ for (Point(Integer a, var b): points) {
+ result += a + b;
+ }
+ return result;
+ }
+
+ static int returnFromEnhancedFor(List points) {
+ for (Point(var a, var b): points) {
+ return a + b;
+ }
+ return -1;
+ }
+
+ static int breakFromEnhancedFor(List points) {
+ int i = 1;
+ int result = 0;
+ for (Point(var a, var b): points) {
+ if (i == 1) break;
+ else result += a + b;
+ }
+ return result;
+ }
+
+ // Simpler pos tests with local variable declarations
+ // Should pass now and in the future if local variable
+ // declaration is subsumed by patterns (not just record patterns)
+ static int primitiveWidening(byte[] inBytes) {
+ int acc = 0;
+ for (int i: inBytes) {
+ acc += i;
+ }
+ return acc;
+ }
+
+ static int applicability1(List points) {
+ for (IPoint p: points) {
+ System.out.println(p);
+ }
+ return -1;
+ }
+
+ static int applicability2(List