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: + * + */ + 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 points) { + for (Object p: points) { + System.out.println(p); + } + return -1; + } + + static void method() {} + + static void for_parsing(int i) { + List points = null; + List> generic_points = null; + + for (Point(Integer a, Integer b) : points) { } + for (ForEachPatterns.Point(Integer a, Integer b) : points) { } + for (GPoint(Integer a, Integer b) : generic_points) { } + for (@Annot(field = "test") Point p : points) {} + for (method(); i == 0;) { i++; } + for (method(), method(); i == 0;) { i++; } + for (ForEachPatterns.method(); i == 0;) { i++; } + } + + static void fail(String message) { + throw new AssertionError(message); + } + + static void assertEquals(Object expected, Object actual) { + if (!Objects.equals(expected, actual)) { + throw new AssertionError("Expected: " + expected + "," + + "got: " + actual); + } + } + + static void assertEx(Function, Integer> f, List points, Class exceptionClass) { + try { + f.apply(points); + fail("Expected an exception, but none happened!"); + } + catch(Exception ex) { + assertEquals(exceptionClass, ex.getClass()); + } + } + + sealed interface IPoint permits Point {} + record Point(Integer x, Integer y) implements IPoint { } + record GPoint(T x, T y) { } + @interface Annot { + String field(); + } + record Frog(Integer x, Integer y) { } + record PointEx(Integer x, Integer y) { + @Override + public Integer x() { + throw new TestPatternFailed(EXCEPTION_MESSAGE); + } + } + static final String EXCEPTION_MESSAGE = "exception-message"; + public static class TestPatternFailed extends AssertionError { + public TestPatternFailed(String message) { + super(message); + } + } +} diff --git a/test/langtools/tools/javac/patterns/ForEachPatternsErrors.java b/test/langtools/tools/javac/patterns/ForEachPatternsErrors.java new file mode 100644 index 00000000000..31f07fdda86 --- /dev/null +++ b/test/langtools/tools/javac/patterns/ForEachPatternsErrors.java @@ -0,0 +1,38 @@ +/* + * @test /nodynamiccopyright/ + * @summary + * @compile/fail/ref=ForEachPatternsErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW --enable-preview -source ${jdk.version} ForEachPatternsErrors.java + */ + +import java.util.List; + +public class ForEachPatternsErrors { + + static void applicability_error1(List points) { // error + for (Point(var x, var y): points) { + System.out.println(); + } + } + + static void applicability_error2(List points) { // error + for (Point(var x, var y): points) { + System.out.println(); + } + } + + static void applicability_error3(List points) { // error + for (Interface p: points) { + System.out.println(p); + } + } + + static void exhaustivity_error(List opoints) { // error + for (OPoint(String s, String t) : opoints) { + System.out.println(s); + } + } + interface Interface {} + sealed interface IPoint permits Point {} + record Point(Integer x, Integer y) implements IPoint { } + record OPoint(Object x, Object y) { } +} diff --git a/test/langtools/tools/javac/patterns/ForEachPatternsErrors.out b/test/langtools/tools/javac/patterns/ForEachPatternsErrors.out new file mode 100644 index 00000000000..7f708993f68 --- /dev/null +++ b/test/langtools/tools/javac/patterns/ForEachPatternsErrors.out @@ -0,0 +1,9 @@ +ForEachPatternsErrors.java:12:35: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Object, ForEachPatternsErrors.Point) +ForEachPatternsErrors.java:18:35: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Object, ForEachPatternsErrors.Point) +ForEachPatternsErrors.java:24:27: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Object, ForEachPatternsErrors.Interface) +ForEachPatternsErrors.java:12:9: compiler.err.not.exhaustive +ForEachPatternsErrors.java:18:9: compiler.err.not.exhaustive +ForEachPatternsErrors.java:30:9: compiler.err.not.exhaustive +- compiler.note.preview.filename: ForEachPatternsErrors.java, DEFAULT +- compiler.note.preview.recompile +6 errors \ No newline at end of file