diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/DeconstructionPatternTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/DeconstructionPatternTree.java
index 9102d865714d2..8eb067948e71b 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/DeconstructionPatternTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/DeconstructionPatternTree.java
@@ -48,11 +48,5 @@ public interface DeconstructionPatternTree extends PatternTree {
*/
List extends PatternTree> getNestedPatterns();
- /**
- * Returns the binding variable.
- * @return the binding variable
- */
- VariableTree getVariable();
-
}
diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java
index ad255bcc6d055..a294a8f0f6868 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java
@@ -25,6 +25,8 @@
package com.sun.source.tree;
+import jdk.internal.javac.PreviewFeature;
+
/**
* A tree node for an {@code instanceof} expression.
*
@@ -40,6 +42,23 @@
* @since 1.6
*/
public interface InstanceOfTree extends ExpressionTree {
+
+ /**
+ * Two possible variants of instanceof expressions:
+ *
+ * - testing types, and
+ *
- performing pattern matching
+ *
+ * @since 20
+ */
+ @PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
+ public enum TestKind {
+ /** instanceof only testing a type */
+ TYPE,
+ /** instanceof doing a pattern matching */
+ PATTERN
+ }
+
/**
* Returns the expression to be tested.
* @return the expression
@@ -73,4 +92,13 @@ public interface InstanceOfTree extends ExpressionTree {
* @since 16
*/
PatternTree getPattern();
+
+ /**
+ * Returns the kind of this instanceof expression.
+ *
+ * @return the kind of this instanceof expression
+ * @since 20
+ */
+ @PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
+ TestKind getTestKind();
}
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 36537cef76fba..bb16d05457198 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
@@ -839,7 +839,6 @@ public R visitPatternCaseLabel(PatternCaseLabelTree node, P p) {
public R visitDeconstructionPattern(DeconstructionPatternTree node, P p) {
R r = scan(node.getDeconstructor(), p);
r = scanAndReduce(node.getNestedPatterns(), p, r);
- r = scanAndReduce(node.getVariable(), p, r);
return r;
}
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java
index 562298a99bde3..c5c2d15b7c1ed 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java
@@ -2042,6 +2042,11 @@ public void setThrow() {
this.kind = Kind.THROWS;
}
+ public void setNormal() {
+ Assert.check(this.kind == Kind.CAPTURED);
+ this.kind = Kind.NORMAL;
+ }
+
/**
* Returns a new copy of this undet var.
*/
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 7e73fffc31734..db81bda499ec5 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
@@ -1665,7 +1665,8 @@ private void handleSwitch(JCTree switchTree,
} else {
patternSwitch = cases.stream()
.flatMap(c -> c.labels.stream())
- .anyMatch(l -> l.hasTag(PATTERNCASELABEL));
+ .anyMatch(l -> l.hasTag(PATTERNCASELABEL) ||
+ TreeInfo.isNullCaseLabel(l));
}
// Attribute all cases and
@@ -1677,7 +1678,6 @@ private void handleSwitch(JCTree switchTree,
boolean hasNullPattern = false; // Is there a null pattern?
CaseTree.CaseKind caseKind = null;
boolean wasError = false;
- MatchBindings prevBindings = null;
for (List l = cases; l.nonEmpty(); l = l.tail) {
JCCase c = l.head;
if (caseKind == null) {
@@ -1687,7 +1687,7 @@ private void handleSwitch(JCTree switchTree,
Errors.SwitchMixingCaseTypes);
wasError = true;
}
- MatchBindings currentBindings = prevBindings;
+ MatchBindings currentBindings = null;
boolean wasUnconditionalPattern = hasUnconditionalPattern;
for (JCCaseLabel label : c.labels) {
if (label instanceof JCConstantCaseLabel constLabel) {
@@ -1751,7 +1751,7 @@ private void handleSwitch(JCTree switchTree,
} else if (label instanceof JCPatternCaseLabel patternlabel) {
//pattern
JCPattern pat = patternlabel.pat;
- attribExpr(pat, switchEnv);
+ attribExpr(pat, switchEnv, seltype);
Type primaryType = TreeInfo.primaryPatternType(pat);
if (!primaryType.hasTag(TYPEVAR)) {
primaryType = chk.checkClassOrArrayType(pat.pos(), primaryType);
@@ -1805,9 +1805,6 @@ private void handleSwitch(JCTree switchTree,
preFlow(c);
c.completesNormally = flow.aliveAfter(caseEnv, c, make);
-
- prevBindings = c.caseKind == CaseTree.CaseKind.STATEMENT && c.completesNormally ? currentBindings
- : null;
}
if (patternSwitch) {
chk.checkSwitchCaseStructure(cases);
@@ -4078,7 +4075,7 @@ public void visitTypeTest(JCInstanceOf tree) {
if (tree.pattern.getTag() == BINDINGPATTERN ||
tree.pattern.getTag() == PARENTHESIZEDPATTERN ||
tree.pattern.getTag() == RECORDPATTERN) {
- attribTree(tree.pattern, env, unknownExprInfo);
+ attribExpr(tree.pattern, env, exprtype);
clazztype = tree.pattern.type;
if (types.isSubtype(exprtype, clazztype) &&
!exprtype.isErroneous() && !clazztype.isErroneous() &&
@@ -4146,8 +4143,7 @@ private boolean checkCastablePattern(DiagnosticPosition pos,
public void visitBindingPattern(JCBindingPattern tree) {
Type type;
if (tree.var.vartype != null) {
- ResultInfo varInfo = new ResultInfo(KindSelector.TYP, resultInfo.pt, resultInfo.checkContext);
- type = attribTree(tree.var.vartype, env, varInfo);
+ type = attribType(tree.var.vartype, env);
} else {
type = resultInfo.pt;
}
@@ -4170,14 +4166,20 @@ public void visitBindingPattern(JCBindingPattern tree) {
@Override
public void visitRecordPattern(JCRecordPattern tree) {
- tree.type = attribType(tree.deconstructor, env);
+ Type type = attribType(tree.deconstructor, env);
+ if (type.isRaw() && type.tsym.getTypeParameters().nonEmpty()) {
+ Type inferred = infer.instantiatePatternType(resultInfo.pt, type.tsym);
+ if (inferred == null) {
+ log.error(tree.pos(), Errors.PatternTypeCannotInfer);
+ } else {
+ type = inferred;
+ }
+ }
+ tree.type = tree.deconstructor.type = type;
Type site = types.removeWildcards(tree.type);
List expectedRecordTypes;
if (site.tsym.kind == Kind.TYP && ((ClassSymbol) site.tsym).isRecord()) {
ClassSymbol record = (ClassSymbol) site.tsym;
- if (record.type.getTypeArguments().nonEmpty() && tree.type.isRaw()) {
- log.error(tree.pos(),Errors.RawDeconstructionPattern);
- }
expectedRecordTypes = record.getRecordComponents()
.stream()
.map(rc -> types.memberType(site, rc)).collect(List.collector());
@@ -4195,10 +4197,7 @@ public void visitRecordPattern(JCRecordPattern tree) {
Env localEnv = env.dup(tree, env.info.dup(env.info.scope.dup()));
try {
while (recordTypes.nonEmpty() && nestedPatterns.nonEmpty()) {
- boolean nestedIsVarPattern = false;
- nestedIsVarPattern |= nestedPatterns.head.hasTag(BINDINGPATTERN) &&
- ((JCBindingPattern) nestedPatterns.head).var.vartype == null;
- attribExpr(nestedPatterns.head, localEnv, nestedIsVarPattern ? recordTypes.head : Type.noType);
+ attribExpr(nestedPatterns.head, localEnv, recordTypes.head);
checkCastablePattern(nestedPatterns.head.pos(), recordTypes.head, nestedPatterns.head.type);
outBindings.addAll(matchBindings.bindingsWhenTrue);
matchBindings.bindingsWhenTrue.forEach(localEnv.info.scope::enter);
@@ -4216,21 +4215,6 @@ public void visitRecordPattern(JCRecordPattern tree) {
Errors.IncorrectNumberOfNestedPatterns(expectedRecordTypes,
nestedTypes));
}
- if (tree.var != null) {
- BindingSymbol v = new BindingSymbol(tree.var.mods.flags, tree.var.name, tree.type,
- localEnv.info.scope.owner);
- v.pos = tree.pos;
- tree.var.sym = v;
- if (chk.checkUnique(tree.var.pos(), v, localEnv.info.scope)) {
- chk.checkTransparentVar(tree.var.pos(), v, localEnv.info.scope);
- }
- if (tree.var.vartype != null) {
- annotate.annotateLater(tree.var.mods.annotations, localEnv, v, tree.pos());
- annotate.queueScanTreeAndTypeAnnotate(tree.var.vartype, localEnv, v, tree.var.pos());
- annotate.flush();
- }
- outBindings.add(v);
- }
} finally {
localEnv.info.scope.leave();
}
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
index cc88e0a856b50..cd7827411e4a0 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
@@ -4346,74 +4346,107 @@ void checkModuleRequires(final DiagnosticPosition pos, final RequiresDirective r
* @param cases the cases that should be checked.
*/
void checkSwitchCaseStructure(List cases) {
- boolean wasConstant = false; // Seen a constant in the same case label
- boolean wasDefault = false; // Seen a default in the same case label
- boolean wasNullPattern = false; // Seen a null pattern in the same case label,
- //or fall through from a null pattern
- boolean wasPattern = false; // Seen a pattern in the same case label
- //or fall through from a pattern
- boolean wasTypePattern = false; // Seen a pattern in the same case label
- //or fall through from a type pattern
- boolean wasNonEmptyFallThrough = false;
for (List l = cases; l.nonEmpty(); l = l.tail) {
JCCase c = l.head;
- for (JCCaseLabel label : c.labels) {
- if (label.hasTag(CONSTANTCASELABEL)) {
- JCExpression expr = ((JCConstantCaseLabel) label).expr;
- if (TreeInfo.isNull(expr)) {
- if (wasPattern && !wasTypePattern && !wasNonEmptyFallThrough) {
- log.error(label.pos(), Errors.FlowsThroughFromPattern);
- }
- wasNullPattern = true;
- } else {
- if (wasPattern && !wasNonEmptyFallThrough) {
- log.error(label.pos(), Errors.FlowsThroughFromPattern);
+ if (c.labels.head instanceof JCConstantCaseLabel constLabel) {
+ if (TreeInfo.isNull(constLabel.expr)) {
+ if (c.labels.tail.nonEmpty()) {
+ if (c.labels.tail.head instanceof JCDefaultCaseLabel defLabel) {
+ if (c.labels.tail.tail.nonEmpty()) {
+ log.error(c.labels.tail.tail.head.pos(), Errors.InvalidCaseLabelCombination);
+ }
+ } else {
+ log.error(c.labels.tail.head.pos(), Errors.InvalidCaseLabelCombination);
}
- wasConstant = true;
- }
- } else if (label.hasTag(DEFAULTCASELABEL)) {
- if (wasPattern && !wasNonEmptyFallThrough) {
- log.error(label.pos(), Errors.FlowsThroughFromPattern);
}
- wasDefault = true;
} else {
- JCPattern pat = ((JCPatternCaseLabel) label).pat;
- while (pat instanceof JCParenthesizedPattern parenthesized) {
- pat = parenthesized.pattern;
- }
- boolean isTypePattern = pat.hasTag(BINDINGPATTERN);
- if (wasPattern || wasConstant || wasDefault ||
- (wasNullPattern && (!isTypePattern || wasNonEmptyFallThrough))) {
- log.error(label.pos(), Errors.FlowsThroughToPattern);
+ for (JCCaseLabel label : c.labels.tail) {
+ if (!(label instanceof JCConstantCaseLabel) || TreeInfo.isNullCaseLabel(label)) {
+ log.error(label.pos(), Errors.InvalidCaseLabelCombination);
+ break;
+ }
}
- wasPattern = true;
- wasTypePattern = isTypePattern;
}
+ } else {
+ if (c.labels.tail.nonEmpty()) {
+ log.error(c.labels.tail.head.pos(), Errors.FlowsThroughFromPattern);
+ }
+ }
+ }
+
+ boolean isCaseStatementGroup = cases.nonEmpty() &&
+ cases.head.caseKind == CaseTree.CaseKind.STATEMENT;
+
+ if (isCaseStatementGroup) {
+ boolean previousCompletessNormally = false;
+ for (List l = cases; l.nonEmpty(); l = l.tail) {
+ JCCase c = l.head;
+ if (previousCompletessNormally &&
+ c.stats.nonEmpty() &&
+ c.labels.head instanceof JCPatternCaseLabel patternLabel &&
+ hasBindings(patternLabel.pat)) {
+ log.error(c.labels.head.pos(), Errors.FlowsThroughToPattern);
+ } else if (c.stats.isEmpty() &&
+ c.labels.head instanceof JCPatternCaseLabel patternLabel &&
+ hasBindings(patternLabel.pat) &&
+ hasStatements(l.tail)) {
+ log.error(c.labels.head.pos(), Errors.FlowsThroughFromPattern);
+ }
+ previousCompletessNormally = c.completesNormally;
}
+ }
+ }
- boolean completesNormally = c.caseKind == CaseTree.CaseKind.STATEMENT ? c.completesNormally
- : false;
+ boolean hasBindings(JCPattern p) {
+ boolean[] bindings = new boolean[1];
- if (c.stats.nonEmpty()) {
- wasConstant = false;
- wasDefault = false;
- wasNullPattern &= completesNormally;
- wasPattern &= completesNormally;
- wasTypePattern &= completesNormally;
+ new TreeScanner() {
+ @Override
+ public void visitBindingPattern(JCBindingPattern tree) {
+ bindings[0] = true;
+ super.visitBindingPattern(tree);
}
+ }.scan(p);
- wasNonEmptyFallThrough = c.stats.nonEmpty() && completesNormally;
- }
+ return bindings[0];
}
+ boolean hasStatements(List cases) {
+ for (List l = cases; l.nonEmpty(); l = l.tail) {
+ if (l.head.stats.nonEmpty()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
void checkSwitchCaseLabelDominated(List cases) {
List caseLabels = List.nil();
+ boolean seenDefault = false;
+ boolean seenDefaultLabel = false;
+ boolean warnDominatedByDefault = false;
for (List l = cases; l.nonEmpty(); l = l.tail) {
JCCase c = l.head;
for (JCCaseLabel label : c.labels) {
- if (label.hasTag(DEFAULTCASELABEL) || TreeInfo.isNullCaseLabel(label)) {
+ if (label.hasTag(DEFAULTCASELABEL)) {
+ seenDefault = true;
+ seenDefaultLabel |=
+ TreeInfo.isNullCaseLabel(c.labels.head);
continue;
}
+ if (TreeInfo.isNullCaseLabel(label)) {
+ if (seenDefault) {
+ log.error(label.pos(), Errors.PatternDominated);
+ }
+ continue;
+ }
+ if (seenDefault && !warnDominatedByDefault) {
+ if (label.hasTag(PATTERNCASELABEL) ||
+ (label instanceof JCConstantCaseLabel && seenDefaultLabel)) {
+ log.error(label.pos(), Errors.PatternDominated);
+ warnDominatedByDefault = true;
+ }
+ }
Type currentType = labelType(label);
for (JCCaseLabel testCaseLabel : caseLabels) {
Type testType = labelType(testCaseLabel);
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 daa12588044aa..586d93a8bbc60 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
@@ -57,6 +57,7 @@
import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
import static com.sun.tools.javac.code.TypeTag.NONE;
import static com.sun.tools.javac.code.TypeTag.VOID;
+import com.sun.tools.javac.code.Types.UniqueType;
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
import com.sun.tools.javac.util.JCDiagnostic.Fragment;
@@ -688,7 +689,7 @@ public void visitSwitch(JCSwitch tree) {
tree.isExhaustive = tree.hasUnconditionalPattern ||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases);
if (exhaustiveSwitch) {
- Set coveredSymbols = coveredSymbolsForCases(tree.pos(), tree.selector, tree.cases);
+ Set coveredSymbols = coveredSymbolsForCases(tree.pos(), tree.cases);
tree.isExhaustive |= isExhaustive(tree.selector.pos(), tree.selector.type, coveredSymbols);
if (!tree.isExhaustive) {
log.error(tree, Errors.NotExhaustiveStatement);
@@ -723,7 +724,7 @@ public void visitSwitchExpression(JCSwitchExpression tree) {
}
}
}
- Set coveredSymbols = coveredSymbolsForCases(tree.pos(), tree.selector, tree.cases);
+ Set coveredSymbols = coveredSymbolsForCases(tree.pos(), tree.cases);
tree.isExhaustive = tree.hasUnconditionalPattern ||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) ||
isExhaustive(tree.selector.pos(), tree.selector.type, coveredSymbols);
@@ -735,7 +736,7 @@ public void visitSwitchExpression(JCSwitchExpression tree) {
}
private Set coveredSymbolsForCases(DiagnosticPosition pos,
- JCExpression selector, List cases) {
+ List cases) {
HashSet labelValues = cases.stream()
.flatMap(c -> c.labels.stream())
.filter(TreeInfo::unguardedCaseLabel)
@@ -743,13 +744,13 @@ private Set coveredSymbolsForCases(DiagnosticPosition pos,
.map(l -> l.hasTag(CONSTANTCASELABEL) ? ((JCConstantCaseLabel) l).expr
: ((JCPatternCaseLabel) l).pat)
.collect(Collectors.toCollection(HashSet::new));
- return coveredSymbols(pos, selector.type, labelValues);
+ return coveredSymbols(pos, labelValues);
}
- private Set coveredSymbols(DiagnosticPosition pos, Type targetType,
+ private Set coveredSymbols(DiagnosticPosition pos,
Iterable extends JCTree> labels) {
Set coveredSymbols = new HashSet<>();
- Map> deconstructionPatternsBySymbol = new HashMap<>();
+ Map> deconstructionPatternsByType = new HashMap<>();
for (JCTree labelValue : labels) {
switch (labelValue.getTag()) {
@@ -761,12 +762,12 @@ private Set coveredSymbols(DiagnosticPosition pos, Type targetType,
}
case RECORDPATTERN -> {
JCRecordPattern dpat = (JCRecordPattern) labelValue;
- Symbol type = dpat.record;
+ UniqueType type = new UniqueType(dpat.type, types);
List augmentedPatterns =
- deconstructionPatternsBySymbol.getOrDefault(type, List.nil())
+ deconstructionPatternsByType.getOrDefault(type, List.nil())
.prepend(dpat);
- deconstructionPatternsBySymbol.put(type, augmentedPatterns);
+ deconstructionPatternsByType.put(type, augmentedPatterns);
}
default -> {
@@ -777,19 +778,18 @@ private Set coveredSymbols(DiagnosticPosition pos, Type targetType,
}
}
}
- for (Entry> e : deconstructionPatternsBySymbol.entrySet()) {
+ for (Entry> e : deconstructionPatternsByType.entrySet()) {
if (e.getValue().stream().anyMatch(r -> r.nested.size() != r.record.getRecordComponents().size())) {
coveredSymbols.add(syms.errSymbol);
- }
- else if (coversDeconstructionFromComponent(pos, targetType, e.getValue(), 0)) {
- coveredSymbols.add(e.getKey());
+ } else if (coversDeconstructionFromComponent(pos, e.getKey().type, e.getValue(), 0)) {
+ coveredSymbols.add(e.getKey().type.tsym);
}
}
return coveredSymbols;
}
private boolean coversDeconstructionFromComponent(DiagnosticPosition pos,
- Type targetType,
+ Type recordType,
List deconstructionPatterns,
int component) {
//Given a set of record patterns for the same record, and a starting component,
@@ -812,9 +812,9 @@ private boolean coversDeconstructionFromComponent(DiagnosticPosition pos,
}
//for the first tested component, gather symbols covered by the nested patterns:
- Type instantiatedComponentType = types.memberType(targetType, components.get(component));
+ Type instantiatedComponentType = types.memberType(recordType, components.get(component));
List nestedComponentPatterns = deconstructionPatterns.map(d -> d.nested.get(component));
- Set coveredSymbolsForComponent = coveredSymbols(pos, instantiatedComponentType,
+ Set coveredSymbolsForComponent = coveredSymbols(pos,
nestedComponentPatterns);
//for each of the symbols covered by the starting component, find all deconstruction patterns
@@ -854,7 +854,7 @@ private boolean coversDeconstructionFromComponent(DiagnosticPosition pos,
Set covered = new HashSet<>();
for (Entry> e : coveredSymbol2Patterns.entrySet()) {
- if (coversDeconstructionFromComponent(pos, targetType, e.getValue(), component + 1)) {
+ if (coversDeconstructionFromComponent(pos, recordType, e.getValue(), component + 1)) {
covered.add(e.getKey());
}
}
@@ -935,7 +935,7 @@ private boolean isExhaustive(DiagnosticPosition pos, Type seltype, Set c
}
case TYPEVAR -> isExhaustive(pos, ((TypeVar) seltype).getUpperBound(), covered);
default -> {
- yield covered.contains(seltype.tsym);
+ yield covered.contains(types.erasure(seltype).tsym);
}
};
}
@@ -2970,14 +2970,6 @@ public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
scan(tree.guard);
}
- @Override
- public void visitRecordPattern(JCRecordPattern tree) {
- super.visitRecordPattern(tree);
- if (tree.var != null) {
- initParam(tree.var);
- }
- }
-
void referenced(Symbol sym) {
unrefdResources.remove(sym);
}
@@ -3152,7 +3144,6 @@ public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
public void visitRecordPattern(JCRecordPattern tree) {
scan(tree.deconstructor);
scan(tree.nested);
- scan(tree.var);
}
@Override
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java
index e109b92c5dfc7..a88dcfa7dd4d6 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java
@@ -69,6 +69,7 @@
import java.util.function.Predicate;
import static com.sun.tools.javac.code.TypeTag.*;
+import java.util.Comparator;
/** Helper class for type parameter inference, used by the attribution phase.
*
@@ -507,8 +508,11 @@ void instantiateAsUninferredVars(List vars, InferenceContext inferenceCont
}
}
//step 2 - replace fresh tvars in their bounds
- List formals = vars;
- for (Type t : todo) {
+ replaceTypeVarsInBounds(todo.toList(), inferenceContext);
+ }
+
+ private void replaceTypeVarsInBounds(List vars, InferenceContext inferenceContext) {
+ for (Type t : vars) {
UndetVar uv = (UndetVar)t;
TypeVar ct = (TypeVar)uv.getInst();
ct.setUpperBound( types.glb(inferenceContext.asInstTypes(types.getBounds(ct))) );
@@ -516,7 +520,6 @@ void instantiateAsUninferredVars(List vars, InferenceContext inferenceCont
//report inference error if glb fails
reportBoundError(uv, InferenceBound.UPPER);
}
- formals = formals.tail;
}
}
@@ -651,6 +654,122 @@ public Type instantiateFunctionalInterface(DiagnosticPosition pos, Type funcInte
return owntype;
}
}
+
+ /**
+ * Infer record type for pattern matching. Given an expression type
+ * (@code expressionType}), and a given record ({@code patternTypeSymbol}),
+ * a parameterized type of {@code patternTypeSymbol} is inferred
+ * according to JLS 18.5.5.
+ *
+ * @param expressionType
+ * @param patternTypeSymbol
+ * @return
+ */
+ public Type instantiatePatternType(Type expressionType, TypeSymbol patternTypeSymbol) {
+ if (expressionType.tsym == patternTypeSymbol)
+ return expressionType;
+
+ //step 1:
+ List expressionTypes = List.nil();
+ List params = patternTypeSymbol.type.allparams();
+ List capturedWildcards = List.nil();
+ List todo = List.of(expressionType);
+ while (todo.nonEmpty()) {
+ Type current = todo.head;
+ todo = todo.tail;
+ switch (current.getTag()) {
+ case CLASS -> {
+ if (current.isCompound()) {
+ todo = todo.prependList(types.directSupertypes(current));
+ } else {
+ Type captured = types.capture(current);
+
+ for (Type ta : captured.getTypeArguments()) {
+ if (ta.hasTag(TYPEVAR) && ((TypeVar) ta).isCaptured()) {
+ params = params.prepend((TypeVar) ta);
+ capturedWildcards = capturedWildcards.prepend(ta);
+ }
+ }
+ expressionTypes = expressionTypes.prepend(captured);
+ }
+ }
+ case TYPEVAR -> {
+ todo = todo.prepend(types.skipTypeVars(current, false));
+ }
+ default -> expressionTypes = expressionTypes.prepend(current);
+ }
+ }
+ //add synthetic captured ivars
+ InferenceContext c = new InferenceContext(this, params);
+ Type patternType = c.asUndetVar(patternTypeSymbol.type);
+ List exprTypes = expressionTypes.map(t -> c.asUndetVar(t));
+
+ capturedWildcards.forEach(s -> ((UndetVar) c.asUndetVar(s)).setNormal());
+
+ try {
+ //step 2:
+ for (Type exprType : exprTypes) {
+ if (exprType.isParameterized()) {
+ Type patternAsExpression =
+ types.asSuper(patternType, exprType.tsym);
+ if (patternAsExpression == null ||
+ !types.isSameType(patternAsExpression, exprType)) {
+ return null;
+ }
+ }
+ }
+
+ doIncorporation(c, types.noWarnings);
+
+ //step 3:
+ List freshVars = instantiatePatternVars(params, c);
+
+ Type substituted = c.asInstType(patternTypeSymbol.type);
+
+ //step 4:
+ return types.upward(substituted, freshVars);
+ } catch (Infer.InferenceException ex) {
+ return null;
+ }
+ }
+
+ private List instantiatePatternVars(List vars, InferenceContext c) {
+ ListBuffer freshVars = new ListBuffer<>();
+ ListBuffer todo = new ListBuffer<>();
+
+ //step 1 - create fresh tvars
+ for (Type t : vars) {
+ UndetVar undet = (UndetVar) c.asUndetVar(t);
+ List bounds = InferenceStep.EQ.filterBounds(undet, c);
+ if (bounds.nonEmpty()) {
+ undet.setInst(bounds.head);
+ } else {
+ List upperBounds = undet.getBounds(InferenceBound.UPPER);
+ Type upper;
+ boolean recursive = Type.containsAny(upperBounds, vars);
+ if (recursive) {
+ upper = types.makeIntersectionType(upperBounds);
+ todo.append(undet);
+ } else if (upperBounds.nonEmpty()) {
+ upper = types.glb(upperBounds);
+ } else {
+ upper = syms.objectType;
+ }
+ List lowerBounds = undet.getBounds(InferenceBound.LOWER);
+ Type lower = lowerBounds.isEmpty() ? syms.botType
+ : lowerBounds.tail.isEmpty() ? lowerBounds.head
+ : types.lub(lowerBounds);
+ TypeVar vt = new TypeVar(syms.noSymbol, upper, lower);
+ freshVars.add(vt);
+ undet.setInst(vt);
+ }
+ }
+
+ //step 2 - replace fresh tvars in their bounds
+ replaceTypeVarsInBounds(todo.toList(), c);
+
+ return freshVars.toList();
+ }
//
//
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 5d42348d21379..362fd99d5185f 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
@@ -323,23 +323,6 @@ public void visitRecordPattern(JCRecordPattern tree) {
nestedPatterns = nestedPatterns.tail;
}
- if (tree.var != null) {
- BindingSymbol binding = (BindingSymbol) tree.var.sym;
- Type castTargetType = principalType(tree);
- VarSymbol bindingVar = bindingContext.bindingDeclared(binding);
-
- JCAssign fakeInit =
- (JCAssign) make.at(TreeInfo.getStartPos(tree))
- .Assign(make.Ident(bindingVar),
- convert(make.Ident(currentValue), castTargetType))
- .setType(bindingVar.erasure(types));
- LetExpr nestedLE = make.LetExpr(List.of(make.Exec(fakeInit)),
- make.Literal(true));
- nestedLE.needsCond = true;
- nestedLE.setType(syms.booleanType);
- test = test != null ? makeBinary(Tag.AND, test, nestedLE) : nestedLE;
- }
-
Assert.check(components.isEmpty() == nestedPatterns.isEmpty());
Assert.check(components.isEmpty() == nestedFullComponentTypes.isEmpty());
result = test != null ? test : makeLit(syms.booleanType, 1);
@@ -441,15 +424,6 @@ private void handleSwitch(JCTree tree,
// return -1 when the input is null
//
//note the selector is evaluated only once and stored in a temporary variable
- ListBuffer newCases = new ListBuffer<>();
- for (List c = cases; c.nonEmpty(); c = c.tail) {
- if (c.head.stats.isEmpty() && c.tail.nonEmpty()) {
- c.tail.head.labels = c.tail.head.labels.prependList(c.head.labels);
- } else {
- newCases.add(c.head);
- }
- }
- cases = newCases.toList();
ListBuffer statements = new ListBuffer<>();
VarSymbol temp = new VarSymbol(Flags.SYNTHETIC,
names.fromString("selector" + tree.pos + target.syntheticNameChar() + "temp"),
@@ -757,7 +731,7 @@ public void visitIdent(JCIdent tree) {
if (bindingVar == null) {
super.visitIdent(tree);
} else {
- result = make.at(tree.pos).Ident(bindingVar);
+ result = make.at(tree.pos).Ident(bindingVar).setType(bindingVar.erasure(types));
}
}
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 567a0303fd515..7bc2c6e7fca2b 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
@@ -61,6 +61,7 @@
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.ImplicitAndExplicitNotAllowed;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.VarAndExplicitNotAllowed;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.VarAndImplicitNotAllowed;
+import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
import java.util.function.BiFunction;
/**
@@ -796,18 +797,7 @@ public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType
nextToken();
}
accept(RPAREN);
- JCVariableDecl var;
- if (token.kind == IDENTIFIER) {
- if (!checkGuard || token.name() != names.when) {
- var = to(F.at(token.pos).VarDef(F.Modifiers(0), token.name(), e, null));
- nextToken();
- } else {
- var = null;
- }
- } else {
- var = null;
- }
- pattern = toP(F.at(pos).RecordPattern(e, nested.toList(), var));
+ pattern = toP(F.at(pos).RecordPattern(e, nested.toList()));
} else {
//type test pattern:
JCVariableDecl var = toP(F.at(token.pos).VarDef(mods, ident(), e, null));
@@ -1536,12 +1526,14 @@ private List switchExpressionStatementGroup() {
pats.append(toP(F.at(casePos).DefaultCaseLabel()));
} else {
accept(CASE);
+ boolean allowDefault = false;
while (true) {
- JCCaseLabel label = parseCaseLabel();
+ JCCaseLabel label = parseCaseLabel(allowDefault);
pats.append(label);
if (token.kind != COMMA) break;
checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS);
nextToken();
+ allowDefault = TreeInfo.isNullCaseLabel(label);
};
}
List stats = null;
@@ -3034,11 +3026,14 @@ protected List switchBlockStatementGroup() {
case CASE: {
nextToken();
ListBuffer pats = new ListBuffer<>();
+ boolean allowDefault = false;
while (true) {
- pats.append(parseCaseLabel());
+ JCCaseLabel label = parseCaseLabel(allowDefault);
+ pats.append(label);
if (token.kind != COMMA) break;
nextToken();
checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS);
+ allowDefault = TreeInfo.isNullCaseLabel(label);
};
CaseTree.CaseKind caseKind;
JCTree body = null;
@@ -3091,12 +3086,16 @@ protected List switchBlockStatementGroup() {
throw new AssertionError("should not reach here");
}
- private JCCaseLabel parseCaseLabel() {
+ private JCCaseLabel parseCaseLabel(boolean allowDefault) {
int patternPos = token.pos;
JCCaseLabel label;
if (token.kind == DEFAULT) {
checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
+ if (!allowDefault) {
+ reportSyntaxError(new SimpleDiagnosticPosition(token.pos),
+ Errors.DefaultLabelNotAllowed);
+ }
nextToken();
label = toP(F.at(patternPos).DefaultCaseLabel());
} else {
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 1c6cdd5429adf..2da7a247fc17b 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
@@ -525,6 +525,15 @@ compiler.err.flows.through.to.pattern=\
compiler.err.flows.through.from.pattern=\
illegal fall-through from a pattern
+compiler.err.invalid.case.label.combination=\
+ invalid case label combination
+
+compiler.err.default.label.not.allowed=\
+ default label not allowed here
+
+compiler.err.pattern.type.cannot.infer=\
+ cannot infer pattern type
+
compiler.err.else.without.if=\
''else'' without ''if''
@@ -3910,9 +3919,6 @@ compiler.err.incorrect.number.of.nested.patterns=\
required: {0}\n\
found: {1}
-compiler.err.raw.deconstruction.pattern=\
- raw deconstruction patterns are not allowed
-
# 0: kind name, 1: symbol
compiler.warn.declared.using.preview=\
{0} {1} is declared using a preview feature, which may be removed in a future release.
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 488e386ab5d7c..abac952be8d4b 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
@@ -2227,6 +2227,10 @@ public JCPattern getPattern() {
@DefinedBy(Api.COMPILER_TREE)
public JCExpression getExpression() { return expr; }
+ @DefinedBy(Api.COMPILER_TREE)
+ public TestKind getTestKind() {
+ return pattern instanceof JCPatternCaseLabel ? TestKind.PATTERN : TestKind.TYPE;
+ }
@Override @DefinedBy(Api.COMPILER_TREE)
public R accept(TreeVisitor v, D d) {
return v.visitInstanceOf(this, d);
@@ -2428,15 +2432,12 @@ public static class JCRecordPattern extends JCPattern
implements DeconstructionPatternTree {
public JCExpression deconstructor;
public List nested;
- public JCVariableDecl var;
public ClassSymbol record;
public List fullComponentTypes;
- protected JCRecordPattern(JCExpression deconstructor, List nested,
- JCVariableDecl var) {
+ protected JCRecordPattern(JCExpression deconstructor, List nested) {
this.deconstructor = deconstructor;
this.nested = nested;
- this.var = var;
}
@DefinedBy(Api.COMPILER_TREE)
@@ -2475,11 +2476,6 @@ public Tag getTag() {
return RECORDPATTERN;
}
- @Override @DefinedBy(Api.COMPILER_TREE)
- public VariableTree getVariable() {
- return var;
- }
-
}
/**
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 3b07bf9f6d7fb..e2c942a352314 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
@@ -945,10 +945,6 @@ public void visitRecordPattern(JCRecordPattern tree) {
print("(");
printExprs(tree.nested);
print(")");
- if (tree.var != null) {
- print(" ");
- print(tree.var.name);
- }
} catch (IOException e) {
throw new UncheckedIOException(e);
}
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 90f7a8c4f1e71..4e73f8def85ed 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
@@ -530,8 +530,7 @@ public JCTree visitDeconstructionPattern(DeconstructionPatternTree node, P p) {
JCRecordPattern t = (JCRecordPattern) node;
JCExpression deconstructor = copy(t.deconstructor, p);
List nested = copy(t.nested, p);
- JCVariableDecl var = copy(t.var, p);
- return M.at(t.pos).RecordPattern(deconstructor, nested, var);
+ return M.at(t.pos).RecordPattern(deconstructor, nested);
}
@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 f1c1d2fea1e8f..986e786a12972 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
@@ -512,9 +512,8 @@ public JCParenthesizedPattern ParenthesizedPattern(JCPattern pattern) {
return tree;
}
- public JCRecordPattern RecordPattern(JCExpression deconstructor, List nested,
- JCVariableDecl var) {
- JCRecordPattern tree = new JCRecordPattern(deconstructor, nested, var);
+ public JCRecordPattern RecordPattern(JCExpression deconstructor, List nested) {
+ JCRecordPattern tree = new JCRecordPattern(deconstructor, nested);
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 dd4129fa85b7c..f64684bda0560 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
@@ -331,9 +331,6 @@ public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
public void visitRecordPattern(JCRecordPattern that) {
scan(that.deconstructor);
scan(that.nested);
- if (that.var != null) {
- scan(that.var);
- }
}
public void visitIndexed(JCArrayAccess tree) {
diff --git a/test/langtools/tools/javac/diags/examples/RawDeconstructionPattern.java b/test/langtools/tools/javac/diags/examples/DefaultLabelNotAllowed.java
similarity index 75%
rename from test/langtools/tools/javac/diags/examples/RawDeconstructionPattern.java
rename to test/langtools/tools/javac/diags/examples/DefaultLabelNotAllowed.java
index 007ac1a85a38a..65d339fb33bf9 100644
--- a/test/langtools/tools/javac/diags/examples/RawDeconstructionPattern.java
+++ b/test/langtools/tools/javac/diags/examples/DefaultLabelNotAllowed.java
@@ -21,15 +21,15 @@
* questions.
*/
-// key: compiler.err.raw.deconstruction.pattern
-// key: compiler.note.preview.filename
-// key: compiler.note.preview.recompile
-// options: --enable-preview -source ${jdk.version}
+// key: compiler.err.default.label.not.allowed
+// key: compiler.misc.feature.pattern.switch
+// key: compiler.warn.preview.feature.use.plural
+// options: --enable-preview -source ${jdk.version} -Xlint:preview
-class RawDeconstructionPattern {
- boolean test(Object o) {
- return o instanceof R(String s);
+class DefaultLabelNotAllowed {
+ private void doSwitch(Object o) {
+ switch (o) {
+ case default: break;
+ }
}
-
- record R(T t) {}
}
diff --git a/test/langtools/tools/javac/diags/examples/FlowsThroughToPattern.java b/test/langtools/tools/javac/diags/examples/FlowsThroughToPattern.java
index 1ab22992f0340..6c212b232819d 100644
--- a/test/langtools/tools/javac/diags/examples/FlowsThroughToPattern.java
+++ b/test/langtools/tools/javac/diags/examples/FlowsThroughToPattern.java
@@ -24,12 +24,14 @@
// key: compiler.err.flows.through.to.pattern
// key: compiler.misc.feature.pattern.switch
// key: compiler.warn.preview.feature.use.plural
+// key: compiler.misc.feature.case.null
+// key: compiler.warn.preview.feature.use
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class FlowsThroughToPattern {
private void doSwitch(Object o) {
switch (o) {
- case String str:
+ case null:
case Object obj: break;
}
}
diff --git a/test/langtools/tools/javac/diags/examples/InvalidCaseLabelCombination.java b/test/langtools/tools/javac/diags/examples/InvalidCaseLabelCombination.java
new file mode 100644
index 0000000000000..505b73df595bb
--- /dev/null
+++ b/test/langtools/tools/javac/diags/examples/InvalidCaseLabelCombination.java
@@ -0,0 +1,36 @@
+/*
+ * 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.err.invalid.case.label.combination
+// key: compiler.misc.feature.case.null
+// key: compiler.warn.preview.feature.use
+// options: --enable-preview -source ${jdk.version} -Xlint:preview
+
+class InvalidCaseLabelCombination {
+ private void doSwitch(Integer i) {
+ switch (i) {
+ case null, 1: break;
+ default: break;
+ }
+ }
+}
diff --git a/test/langtools/tools/javac/diags/examples/PatternTypeCannotInfer.java b/test/langtools/tools/javac/diags/examples/PatternTypeCannotInfer.java
new file mode 100644
index 0000000000000..3d500a4543130
--- /dev/null
+++ b/test/langtools/tools/javac/diags/examples/PatternTypeCannotInfer.java
@@ -0,0 +1,36 @@
+/*
+ * 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.err.pattern.type.cannot.infer
+// key: compiler.warn.preview.feature.use.plural
+// key: compiler.misc.feature.deconstruction.patterns
+// options: --enable-preview -source ${jdk.version} -Xlint:preview
+
+class PatternTypeCannotInfer {
+ interface A {}
+ record R() implements A {}
+ void test(A i) {
+ if (i instanceof R()) {
+ }
+ }
+}
diff --git a/test/langtools/tools/javac/patterns/CaseDefault.java b/test/langtools/tools/javac/patterns/CaseDefault.java
deleted file mode 100644
index 8b36386af8599..0000000000000
--- a/test/langtools/tools/javac/patterns/CaseDefault.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * @test /nodynamiccopyright/
- * @bug 8262891
- * @summary Check null handling for non-pattern switches.
- * @compile/fail/ref=CaseDefault.out --release 16 -XDrawDiagnostics CaseDefault.java
- * @compile --enable-preview -source ${jdk.version} CaseDefault.java
- * @run main/othervm --enable-preview CaseDefault
- */
-
-public class CaseDefault {
-
- public static void main(String[] args) {
- new CaseDefault().run();
- }
-
- void run() {
- String str = "other";
- switch (str) {
- case "a": throw new AssertionError("Wrong branch.");
- case default: break; //OK
- }
- switch (str) {
- case "a" -> throw new AssertionError("Wrong branch.");
- case default -> {} //OK
- }
- int i;
- i = switch (str) {
- case "a": throw new AssertionError("Wrong branch.");
- case default: yield 0; //OK
- };
- i = switch (str) {
- case "a" -> throw new AssertionError("Wrong branch.");
- case default -> 0; //OK
- };
- }
-
-}
diff --git a/test/langtools/tools/javac/patterns/CaseDefault.out b/test/langtools/tools/javac/patterns/CaseDefault.out
deleted file mode 100644
index ffea67361fb0e..0000000000000
--- a/test/langtools/tools/javac/patterns/CaseDefault.out
+++ /dev/null
@@ -1,2 +0,0 @@
-CaseDefault.java:20:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)
-1 error
diff --git a/test/langtools/tools/javac/patterns/CaseStructureTest.java b/test/langtools/tools/javac/patterns/CaseStructureTest.java
index bdc6e3851287e..3d0e1e1d21704 100644
--- a/test/langtools/tools/javac/patterns/CaseStructureTest.java
+++ b/test/langtools/tools/javac/patterns/CaseStructureTest.java
@@ -95,7 +95,6 @@ protected void doWork() throws Throwable {
task.generate(result -> {
boolean shouldPass = true;
long patternCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.TYPE_PATTERN || l == CaseLabel.PARENTHESIZED_PATTERN).count();
- long typePatternCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.TYPE_PATTERN || l == CaseLabel.PARENTHESIZED_PATTERN).count();
long constantCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.CONSTANT).count();
long nullCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.NULL).count();
long defaultCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.DEFAULT).count();
@@ -105,14 +104,22 @@ protected void doWork() throws Throwable {
if (constantCases > 0) {
shouldPass &= patternCases == 0;
}
+ if (defaultCases > 0) {
+ shouldPass &= asCaseLabelElements && nullCases > 0;
+ }
if (defaultCases > 1) {
shouldPass &= false;
}
if (nullCases > 1) {
shouldPass &= false;
}
- if (nullCases > 0 && patternCases > 0) {
- shouldPass &= patternCases == typePatternCases;
+ if (nullCases > 0) {
+ shouldPass &= patternCases == 0 && (constantCases == 0 || !asCaseLabelElements);
+ if (defaultCases > 0 && asCaseLabelElements) {
+ int nullIndex = Arrays.asList(caseLabels).indexOf(CaseLabel.NULL);
+ int defaultIndex = Arrays.asList(caseLabels).indexOf(CaseLabel.DEFAULT);
+ shouldPass &= nullIndex < defaultIndex;
+ }
}
if (patternCases > 1) {
shouldPass &= false;
diff --git a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java
index fc82d00a10843..7c7d8a90a74c6 100644
--- a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java
+++ b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java
@@ -26,8 +26,8 @@ public static void main(String... args) throws Throwable {
if (p instanceof GenRecord(var v)); //incorrect generic type
if (p instanceof P4(GenRecord(var v))); //incorrect generic type
if (p instanceof GenRecord(Integer v)); //inconsistency in types
- if (p instanceof P2(var v, var v) v); //duplicated variables
- if (p instanceof P6(P2(var v1, var v2) v1, P2(var v1, var v2) v2) v1); //duplicated variables
+ if (p instanceof P2(var v, var v)); //duplicated variables
+ if (p instanceof P6(P2(var v1, var v2), P2(var v1, var v2))); //duplicated variables
if (p instanceof P7(byte b)); //incorrect pattern type
if (p instanceof P7(long l)); //incorrect pattern type
switch (p) {
@@ -44,6 +44,7 @@ case GenRecord(String s) -> {}
switch (r1) {
case GenRecord<>(String s) -> {}
}
+ boolean b = p instanceof P(int i) p; //introducing a variable for the record pattern
}
public record P(int i) {
diff --git a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out
index cf7a8ab7301fb..d4fbf80b199dd 100644
--- a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out
+++ b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out
@@ -2,6 +2,8 @@ DeconstructionPatternErrors.java:15:28: compiler.err.underscore.as.identifier
DeconstructionPatternErrors.java:15:29: compiler.err.expected: token.identifier
DeconstructionPatternErrors.java:43:37: compiler.err.illegal.start.of.type
DeconstructionPatternErrors.java:45:28: compiler.err.illegal.start.of.type
+DeconstructionPatternErrors.java:47:42: compiler.err.expected: ';'
+DeconstructionPatternErrors.java:47:43: compiler.err.not.stmt
DeconstructionPatternErrors.java:16:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List, java.util.ArrayList)
DeconstructionPatternErrors.java:17:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, java.util.ArrayList
DeconstructionPatternErrors.java:18:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int)
@@ -18,18 +20,13 @@ DeconstructionPatternErrors.java:27:29: compiler.err.instanceof.reifiable.not.sa
DeconstructionPatternErrors.java:28:44: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
DeconstructionPatternErrors.java:28:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord
DeconstructionPatternErrors.java:29:40: compiler.err.match.binding.exists
-DeconstructionPatternErrors.java:29:43: compiler.err.match.binding.exists
-DeconstructionPatternErrors.java:30:48: compiler.err.match.binding.exists
-DeconstructionPatternErrors.java:30:59: compiler.err.already.defined: kindname.variable, v1, kindname.method, main(java.lang.String...)
-DeconstructionPatternErrors.java:30:67: compiler.err.already.defined: kindname.variable, v2, kindname.method, main(java.lang.String...)
-DeconstructionPatternErrors.java:30:71: compiler.err.match.binding.exists
-DeconstructionPatternErrors.java:30:75: compiler.err.match.binding.exists
+DeconstructionPatternErrors.java:30:56: compiler.err.already.defined: kindname.variable, v1, kindname.method, main(java.lang.String...)
+DeconstructionPatternErrors.java:30:64: compiler.err.already.defined: kindname.variable, v2, kindname.method, main(java.lang.String...)
DeconstructionPatternErrors.java:31:29: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, byte)
DeconstructionPatternErrors.java:32:29: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
DeconstructionPatternErrors.java:34:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, byte)
DeconstructionPatternErrors.java:35:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
-DeconstructionPatternErrors.java:39:27: compiler.err.raw.deconstruction.pattern
-DeconstructionPatternErrors.java:41:18: compiler.err.raw.deconstruction.pattern
+DeconstructionPatternErrors.java:44:9: compiler.err.not.exhaustive.statement
- compiler.note.preview.filename: DeconstructionPatternErrors.java, DEFAULT
- compiler.note.preview.recompile
-32 errors
+29 errors
diff --git a/test/langtools/tools/javac/patterns/Exhaustiveness.java b/test/langtools/tools/javac/patterns/Exhaustiveness.java
index 39fe1e558ce32..95c24b0a6e3db 100644
--- a/test/langtools/tools/javac/patterns/Exhaustiveness.java
+++ b/test/langtools/tools/javac/patterns/Exhaustiveness.java
@@ -1170,6 +1170,29 @@ int test(A arg) {
"1 error");
}
+ @Test
+ public void testInferenceExhaustive(Path base) throws Exception {
+ doTest(base,
+ new String[0],
+ """
+ package test;
+ public class Test {
+ sealed interface Opt {}
+ record Some(T t) implements Opt {}
+ final class None implements Opt {}
+
+ void test(Opt optValue) {
+ switch (optValue) {
+ case Some(String s) ->
+ System.out.printf("got string: %s%n", s);
+ case None none ->
+ System.out.println("got none");
+ }
+ }
+ }
+ """);
+ }
+
private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException {
Path current = base.resolve(".");
Path libClasses = current.resolve("libClasses");
diff --git a/test/langtools/tools/javac/patterns/GenericRecordDeconstructionPattern.java b/test/langtools/tools/javac/patterns/GenericRecordDeconstructionPattern.java
index 52bbbb781c1a4..55c8bbd11597d 100644
--- a/test/langtools/tools/javac/patterns/GenericRecordDeconstructionPattern.java
+++ b/test/langtools/tools/javac/patterns/GenericRecordDeconstructionPattern.java
@@ -39,6 +39,11 @@ void run() {
runTest(this::runIf);
runTest(this::runSwitch);
runTest(this::runSwitchExpression);
+ runTest(this::runSwitchInference1);
+ runTest(this::runSwitchInference2);
+ runTest(this::runSwitchInference3);
+ runTest(this::runSwitchInference4);
+ testInference3();
}
void runTest(Function, Integer> test) {
@@ -65,7 +70,49 @@ int runSwitchExpression(Box b) {
};
}
- record Box(V v) {
+ int runSwitchInference1(I b) {
+ switch (b) {
+ case Box(String s): return s == null ? 1 : s.length();
+ default: return -1;
+ }
+ }
+
+ int runSwitchInference2(I b) {
+ switch (b) {
+ case Box(var s): return s == null ? 1 : s.length();
+ default: return -1;
+ }
+ }
+
+ int runSwitchInference3(I b) {
+ return b instanceof Box(var s) ? s == null ? 1 : s.length()
+ : -1;
+ }
+
+ > int runSwitchInference4(Z b) {
+ return b instanceof Box(var s) ? s == null ? 1 : s.length()
+ : -1;
+ }
+
+ > int runSwitchInference5(Z b) {
+ return b instanceof Box(var s) ? s == null ? 1 : s.length()
+ : -1;
+ }
+
+ void testInference3() {
+ I> b = new Box<>(new Box<>(null));
+ assertEquals(1, runSwitchInferenceNested(b));
+ }
+
+ int runSwitchInferenceNested(I> b) {
+ switch (b) {
+ case Box(Box(var s)): return s == null ? 1 : s.length();
+ default: return -1;
+ }
+ }
+
+ sealed interface I {}
+ record Box(V v) implements I {
}
void assertEquals(Object expected, Object actual) {
diff --git a/test/langtools/tools/javac/patterns/Guards.java b/test/langtools/tools/javac/patterns/Guards.java
index eb9d982678946..f8cffd41655a9 100644
--- a/test/langtools/tools/javac/patterns/Guards.java
+++ b/test/langtools/tools/javac/patterns/Guards.java
@@ -48,7 +48,6 @@ void run() {
runIfTrue(this::typeGuardAfterParenthesizedTrueSwitchStatement);
runIfTrue(this::typeGuardAfterParenthesizedTrueSwitchExpression);
runIfTrue(this::typeGuardAfterParenthesizedTrueIfStatement);
- testGuardNPE();
}
void run(Function