{@code
+ * for ( N$temp : coll) {
+ * switch (N$temp) {
+ * case : stmt;
+ * case null: throw new MatchException();
+ * }
+ * }
+ *
+ */
+ Type selectorType = types.classBound(tree.elementType);
+
+ 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 nestedNPEParams = List.of(makeNull());
+ JCNewClass nestedNPE = makeNewClass(syms.nullPointerExceptionType, nestedNPEParams);
+
+ List matchExParams = List.of(makeNull(), nestedNPE);
+ JCThrow thr = make.Throw(makeNewClass(syms.matchExceptionType, matchExParams));
+
+ 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(jcRecordPattern, 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 class TransTypes extends TreeTranslator {
}
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 @@ public class TreeDiffer extends TreeScanner {
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 @@ implements CRTFlags {
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 10b3c77d4ee..197856eeab5 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
@@ -760,7 +760,6 @@ public class JavacParser implements Parser {
/** parses patterns.
*/
-
public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType,
boolean allowVar, boolean checkGuard) {
JCPattern pattern;
@@ -2818,25 +2817,47 @@ public class JavacParser implements Parser {
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: {
@@ -2953,6 +2974,91 @@ public class JavacParser implements Parser {
}
}
+ private enum ForInitResult {
+ LocalVarDecl,
+ RecordPattern
+ }
+
+ @SuppressWarnings("fallthrough")
+ ForInitResult analyzeForInit() {
+ boolean inType = false;
+ boolean inSelectionAndParenthesis = false;
+ int typeParameterPossibleStart = -1;
+ outer: for (int lookahead = 0; ; lookahead++) {
+ TokenKind tk = S.token(lookahead).kind;
+ switch (tk) {
+ case DOT:
+ if (inType) break; // in qualified type
+ case COMMA:
+ typeParameterPossibleStart = lookahead;
+ break;
+ case QUES:
+ // "?" only allowed in a type parameter position - otherwise it's an expression
+ if (typeParameterPossibleStart == lookahead - 1) break;
+ else return ForInitResult.LocalVarDecl;
+ case EXTENDS: case SUPER: case AMP:
+ case GTGTGT: case GTGT: case GT:
+ case FINAL: case ELLIPSIS:
+ break;
+ case BYTE: case SHORT: case INT: case LONG: case FLOAT:
+ case DOUBLE: case BOOLEAN: case CHAR: case VOID:
+ if (peekToken(lookahead, IDENTIFIER)) {
+ return inSelectionAndParenthesis ? ForInitResult.RecordPattern
+ : 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, DOT) ||
+ peekToken(lookahead, SEMI) ||
+ peekToken(lookahead, ARROW)) {
+ return ForInitResult.LocalVarDecl;
+ }
+ else if(peekToken(lookahead, COLON)) {
+ return ForInitResult.RecordPattern;
+ }
+ break;
+ }
+ case UNDERSCORE:
+ case ASSERT:
+ case ENUM:
+ case IDENTIFIER:
+ if (lookahead == 0) {
+ inType = true;
+ }
+ break;
+ case MONKEYS_AT: {
+ int prevLookahead = lookahead;
+ lookahead = skipAnnotation(lookahead);
+ if (typeParameterPossibleStart == prevLookahead - 1) {
+ // move possible start of type param after the anno
+ typeParameterPossibleStart = lookahead;
+ }
+ break;
+ }
+ case LBRACKET:
+ if (peekToken(lookahead, RBRACKET)) {
+ return inSelectionAndParenthesis ? ForInitResult.RecordPattern
+ : ForInitResult.LocalVarDecl;
+ }
+ return ForInitResult.LocalVarDecl;
+ case LT:
+ typeParameterPossibleStart = lookahead;
+ break;
+ default:
+ //this includes EOF
+ return ForInitResult.LocalVarDecl;
+ }
+ }
+ }
+
@Override
public JCStatement parseStatement() {
return parseStatementAsBlock();
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
index 2da7a247fc1..13c3099c790 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
@@ -603,6 +603,10 @@ compiler.err.foreach.not.applicable.to.type=\
required: {1}\n\
found: {0}
+# 0: type, 1: type
+compiler.err.foreach.not.exhaustive.on.type=\
+ Pattern {0} is not exhaustive on {1}
+
compiler.err.fp.number.too.large=\
floating-point number too large
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 abac952be8d..ccb414fdb3c 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,13 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
* 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;
+ public Type elementType;
+
+ protected JCEnhancedForLoop(JCTree varOrRecordPattern, JCExpression expr, JCStatement body) {
+ this.varOrRecordPattern = varOrRecordPattern;
this.expr = expr;
this.body = body;
}
@@ -1224,7 +1226,11 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
@DefinedBy(Api.COMPILER_TREE)
public Kind getKind() { return Kind.ENHANCED_FOR_LOOP; }
@DefinedBy(Api.COMPILER_TREE)
- public JCVariableDecl getVariable() { return var; }
+ public JCVariableDecl getVariable() {
+ return varOrRecordPattern instanceof JCVariableDecl var ? var : null;
+ }
+ @DefinedBy(Api.COMPILER_TREE)
+ public JCTree getVariableOrRecordPattern() { return varOrRecordPattern; }
@DefinedBy(Api.COMPILER_TREE)
public JCExpression getExpression() { return expr; }
@DefinedBy(Api.COMPILER_TREE)
@@ -1237,6 +1243,10 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public Tag getTag() {
return FOREACHLOOP;
}
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public EnhancedForLoopTree.DeclarationKind getDeclarationKind() {
+ return varOrRecordPattern.hasTag(VARDEF) ? DeclarationKind.VARIABLE : DeclarationKind.PATTERN;
+ }
}
/**
@@ -3410,7 +3420,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
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 e2c942a3523..7ae8fa59746 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 class Pretty extends JCTree.Visitor {
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 4e73f8def85..e382f8038a8 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 class TreeCopier
implements TreeVisitor {
@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 986e786a129..98485cb9038 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 class TreeMaker implements JCTree.Factory {
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 f64684bda05..c7e437dc529 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 class TreeScanner extends Visitor {
}
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 class TreeTranslator extends JCTree.Visitor {
}
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/diags/examples/ForeachNotExhaustive.java b/test/langtools/tools/javac/diags/examples/ForeachNotExhaustive.java
new file mode 100644
index 00000000000..97ce07467f8
--- /dev/null
+++ b/test/langtools/tools/javac/diags/examples/ForeachNotExhaustive.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// key: compiler.misc.feature.deconstruction.patterns
+// key: compiler.misc.feature.pattern.switch
+// key: compiler.warn.preview.feature.use.plural
+// key: compiler.err.foreach.not.exhaustive.on.type
+// options: --enable-preview -source ${jdk.version} -Xlint:preview
+
+import java.util.List;
+
+class ForeachNotExhaustive {
+ void m(List