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 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: + * + * @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 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 convert) { @@ -162,34 +161,6 @@ String testPatternInGuard(Object o) { return null; } - void testGuardNPE() { - doTestGuardNPE(this::guardNPE1); - doTestGuardNPE(this::guardNPE2); - } - - void doTestGuardNPE(Function test) { - assertEquals("empty", test.apply("")); - assertEquals("A", test.apply("A")); - assertEquals("other", test.apply(1)); - assertEquals("empty", test.apply(null)); - } - - String guardNPE1(Object o) { - return switch (o) { - case null, String s when s.isEmpty() -> "empty"; - case String s -> s; - case Object x -> "other"; - }; - } - - String guardNPE2(Object o) { - return switch (o) { - case null, ((((String s)))) when s.isEmpty() -> "empty"; - case ((((String s)))) -> s; - case Object x -> "other"; - }; - } - record Box(Object o) {} void assertEquals(String expected, String actual) { diff --git a/test/langtools/tools/javac/patterns/InferenceUnitTest.java b/test/langtools/tools/javac/patterns/InferenceUnitTest.java new file mode 100644 index 0000000000000..2c246664686c4 --- /dev/null +++ b/test/langtools/tools/javac/patterns/InferenceUnitTest.java @@ -0,0 +1,210 @@ +/* + * 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. + */ + +/* + * @test + * @summary Verify Infer.instantiatePatternType provides correct results + * @library /tools/lib/types + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.code + * jdk.compiler/com.sun.tools.javac.comp + * jdk.compiler/com.sun.tools.javac.model + * jdk.compiler/com.sun.tools.javac.parser + * jdk.compiler/com.sun.tools.javac.tree + * jdk.compiler/com.sun.tools.javac.util + * @run main InferenceUnitTest + */ + +import com.sun.tools.javac.api.JavacTaskImpl; +import com.sun.tools.javac.code.Symbol.ClassSymbol; +import com.sun.tools.javac.code.Symbol.TypeSymbol; + +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Types; +import com.sun.tools.javac.comp.Attr; +import com.sun.tools.javac.comp.Infer; +import com.sun.tools.javac.model.JavacElements; +import com.sun.tools.javac.parser.ParserFactory; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Objects; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; + +public class InferenceUnitTest { + + Context context; + Infer infer; + Types types; + + public static void main(String... args) throws Exception { + new InferenceUnitTest().runAll(); + } + + void runAll() throws URISyntaxException { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, null, null, null, null, List.of(new SimpleJavaFileObject(new URI("mem://Test.java"), JavaFileObject.Kind.SOURCE) { + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return """ + interface A {} + interface B extends A {} + interface C extends A {} + interface D extends A {} + interface E extends C {} + interface F extends A> {} + interface G extends A {} + interface H extends A {} + interface I extends H {} + class Test { + } + interface RecursiveTest1Interface> { } + interface RecursiveTest1Use> extends RecursiveTest1Interface { } + interface RecursiveTest2Interface { } + interface RecursiveTest2Use, Y> extends RecursiveTest2Interface { } + """; + } + })); + task.enter(); + context = task.getContext(); + infer = Infer.instance(context); + types = Types.instance(context); + + checkInferedType("A", "B", "B"); + checkInferedType("A", "C", "C"); + checkInferedType("A", "D", "D"); + checkInferedType("A", "E", "E"); + checkInferedType("A", "F", null); + checkInferedType("A", "G", null); // doesn't check bounds + checkInferedType("A", "H", "H"); + checkInferedType("A", "I", "I"); + + checkInferedType("A>", "B", "B>"); + checkInferedType("A>", "C", "C,?>"); + checkInferedType("A>", "F", "F"); + checkInferedType("A>", "H", null); + checkInferedType("A>", "I", null); + + checkInferedType("C", "E", "E"); + checkInferedType("C", "E", null); + checkInferedType("C, A>", "E", "E>"); + checkInferedType("C, A>", "E", "E>"); + + if (false) { + checkInferedType("A", "B", "B"); + checkInferedType("A", "C", "C"); + checkInferedType("A", "D", "D"); + checkInferedType("A", "E", "E"); + checkInferedType("A", "F", "F"); + checkInferedType("A", "G", "G"); + checkInferedType("A", "H", "H"); + } + + checkInferedType("A", "I", "I"); // always erases if input is raw + + checkInferedType("A", "B", "B"); + checkInferedType("A", "C", "C"); + checkInferedType("A", "D", "D"); + checkInferedType("A", "E", "E"); + checkInferedType("A", "F", "F"); + checkInferedType("A", "G", "G"); + checkInferedType("A", "H", "H"); + checkInferedType("A", "I", "I"); + + checkInferedType("A", "B", "B"); + checkInferedType("A", "C", "C"); + checkInferedType("A", "D", "D"); + checkInferedType("A", "E", "E"); + checkInferedType("A", "F", null); + checkInferedType("A", "G", "G"); // should infer an intersection bound + checkInferedType("A", "H", null); + checkInferedType("A", "I", null); + + checkInferedType("A>", "F", "F"); // inference doesn't recur on bounds checks + checkInferedType("A>", "F", "F"); // inference doesn't recur on bounds checks + + checkInferedType("C", "E", "E"); // doesn't know how to mix types and wildcards + checkInferedType("C", "E", "E"); // doesn't know how to mix types and wildcards + checkInferedType("C", "E", "E"); + checkInferedType("C", "E", "E"); + + checkInferedType("C", "E", "E"); + checkInferedType("C", "E", "E"); + checkInferedType("C", "E", "E"); // should infer an intersection bound + checkInferedType("C", "E", "E"); // doesn't know how to mix lower/upper + checkInferedType("C", "E", "E"); + checkInferedType("C, ? super C>", "E", "E>"); // doesn't do lub + + checkInferedType("H", "I", "I"); + + checkInferedType("B", "C", null); // no sideways casts + + checkInferedType("A", "B", "B"); + checkInferedType("RecursiveTest1Interface", "RecursiveTest1Use", "RecursiveTest1Use&RecursiveTest1Interface>>"); + checkInferedType("RecursiveTest2Interface", "RecursiveTest2Use", "RecursiveTest2Use,?>"); + } + + private void checkInferedType(String base, String test, String expected) { + Type baseType = parseType(base); + TypeSymbol testType = parseType(test).tsym; + Type actualType = infer.instantiatePatternType(baseType, testType); + String actualTypeString = actualType != null ? actualType.toString() : null; + if (!Objects.equals(expected, actualTypeString)) { + error("Unexpected type, expected: " + expected + ", got: " + actualTypeString); + } + } + Type parseType(String spec) { + ParserFactory fact = ParserFactory.instance(context); + JCExpression specTypeTree = fact.newParser(spec, false, false, false).parseType(); + Attr attr = Attr.instance(context); + JavacElements elementUtils = JavacElements.instance(context); + ClassSymbol testClass = elementUtils.getTypeElement("Test"); + return attr.attribType(specTypeTree, testClass); + } + + /** assert that 's' is the same type as 't' */ + public void assertSameType(Type s, Type t) { + assertSameType(s, t, true); + } + + /** assert that 's' is/is not the same type as 't' */ + public void assertSameType(Type s, Type t, boolean expected) { + if (types.isSameType(s, t) != expected) { + String msg = expected ? + " is not the same type as " : + " is the same type as "; + error(s + msg + t); + } + } + + private void error(String msg) { + throw new AssertionError("Unexpected result: " + msg); + } + +} diff --git a/test/langtools/tools/javac/patterns/NewCaseStructureTest.java b/test/langtools/tools/javac/patterns/NewCaseStructureTest.java new file mode 100644 index 0000000000000..edb9d3265f06d --- /dev/null +++ b/test/langtools/tools/javac/patterns/NewCaseStructureTest.java @@ -0,0 +1,457 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8294942 + * @summary Check compilation outcomes for various combinations of case label element. + * @library /tools/lib /tools/javac/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.file + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * @build toolbox.ToolBox toolbox.JavacTask + * @compile NewCaseStructureTest.java + * @run main NewCaseStructureTest + */ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.TestRunner; +import toolbox.ToolBox; + +public class NewCaseStructureTest extends TestRunner { + + private static final String JAVA_VERSION = System.getProperty("java.specification.version"); + + ToolBox tb; + + public static void main(String... args) throws Exception { + new NewCaseStructureTest().runTests(); + } + + NewCaseStructureTest() { + super(System.err); + tb = new ToolBox(); + } + + public void runTests() throws Exception { + runTests(m -> new Object[] { Paths.get(m.getName()) }); + } + + @Test + public void testCorrectMultiLabelCaseStructure(Path base) throws Exception { + for (String pattern : new String[] {"String s", + "(String s)", + "R(int i)", + "(R(int i))", + "null, default", + "null", + "1", + "1, 2", + "1, 2, 3"}) { + for (String sep : new String[] {":", "->"}) { + doTest(base, + """ + package test; + public class Test { + private int test(${switchType} obj) { + return switch (obj) { + case ${pattern} ${sep} { yield 0; } + ${default} + }; + } + } + record R(int i) {} + """.replace("${switchType}", pattern.contains("1") ? "Integer" : "Object") + .replace("${pattern}", pattern) + .replace("${sep}", sep) + .replace("${default}", pattern.contains("default") ? "" : "default " + sep + " { yield 1; }"), + false); + } + } + } + + @Test + public void testMalformedCaseStructure(Path base) throws Exception { + for (String pattern : new String[] {"String s, Integer i", + "String s, R(int i)", + "E1(), E2()", + "String s, null", + "String s, default", + "String s, null, default", + "null, String s", + "null, default, String s", + "default, String s", + "1, Integer i", + "1, 2, 3, Integer i", + "Integer i, 1, 2, 3", + "1, null", + "1, 2, 3, null", + "null, 1, 2, 3", + "default, null", + "default"}) { + for (String sep : new String[] {":", "->"}) { + doTest(base, + """ + package test; + public class Test { + private int test(${switchType} obj) { + return switch (obj) { + case ${pattern} ${sep} { yield 0; } + ${default} + }; + } + } + record R(int i) {} + record E1() {} + record E2() {} + """.replace("${switchType}", pattern.contains("1") ? "Integer" : "Object") + .replace("${pattern}", pattern) + .replace("${sep}", sep) + .replace("${default}", pattern.contains("default") ? "" : "default " + sep + " { yield 1; }"), + true); + } + } + } + + @Test + public void testSwitchLabeledStatementGroups(Path base) throws Exception { + doTest(base, + """ + package test; + public class Test { + private int test(Object o) { + return switch (o) { + case null: + case Object obj: System.err.println(); yield 0; + }; + } + } + """, + "Test.java:6:18: compiler.err.flows.through.to.pattern", + "1 error"); + doTest(base, + """ + package test; + public class Test { + private int test(Object o) { + return switch (o) { + case null: System.err.println(); + case Object obj: System.err.println(); yield 0; + }; + } + } + """, + "Test.java:6:18: compiler.err.flows.through.to.pattern", + "1 error"); + doTest(base, + """ + package test; + public class Test { + private int test(Object o) { + return switch (o) { + case Object obj: + case null: System.err.println(); yield 0; + }; + } + } + """, + "Test.java:5:18: compiler.err.flows.through.from.pattern", + "1 error"); + doTest(base, + """ + package test; + public class Test { + private int test(Object o) { + return switch (o) { + case Object obj: System.err.println(); + case null: System.err.println(); + yield 0; + }; + } + } + """); + doTest(base, + """ + package test; + public class Test { + private int test(Object o) { + return switch (o) { + case String s: System.err.println(); + case E1(): System.err.println(); yield 0; + default: yield 0; + }; + } + } + record E1() {} + """); + doTest(base, + """ + package test; + public class Test { + private int test(Object o) { + return switch (o) { + case String s: System.err.println(); + default: System.err.println(); yield 0; + }; + } + } + """); + doTest(base, + """ + package test; + public class Test { + private void test(Object o) { + switch (o) { + case String s: + case Integer i: + case Object obj: + } + } + } + """); + doTest(base, + """ + package test; + public class Test { + private void test(Object o) { + switch (o) { + case Object obj: System.err.println(); + case null: System.err.println(obj); + } + } + } + """, + "Test.java:6:43: compiler.err.cant.resolve.location: kindname.variable, obj, , , (compiler.misc.location: kindname.class, test.Test, null)", + "1 error"); + + doTest(base, + """ + package test; + public class Test { + private int test(Integer o) { + return switch (o) { + case default -> 0; + case 0 -> 0; + }; + } + } + """, + "Test.java:5:18: compiler.err.default.label.not.allowed", + "1 error"); + } + + @Test + public void testDominance(Path base) throws Exception { + //A case label with a case pattern p (guarded or unguarded) dominates another case label with a case constant c if p dominates c, which is defined as follows: + // A type pattern that declares a pattern variable of type T dominates a constant c of a primitive type P if the wrapper class of P ([5.1.7]) is a subtype of the erasure of T. + doTest(base, + """ + package test; + public class Test { + private int test(Integer o) { + return switch (o) { + case Integer i when i > 0 -> 0; + case 0 -> 0; + case Integer i -> 0; + }; + } + } + """, + "Test.java:6:18: compiler.err.pattern.dominated", + "1 error"); + // A type pattern that declares a pattern variable of type T dominates an enum constant c of type E if E is a subtype of the erasure of the type of T. + doTest(base, + """ + package test; + public class Test { + private int test(E o) { + return switch (o) { + case E e when e == E.A -> 0; + case B -> 0; + case E e -> 0; + }; + } + } + enum E {A, B;} + """, + "Test.java:6:18: compiler.err.pattern.dominated", + "1 error"); + //dtto for String: + doTest(base, + """ + package test; + public class Test { + private int test(String o) { + return switch (o) { + case String s when s.isEmpty() -> 0; + case "a" -> 0; + case String s -> 0; + }; + } + } + """, + "Test.java:6:18: compiler.err.pattern.dominated", + "1 error"); + // A parenthesized pattern dominates a constant c if its contained pattern dominates c. + doTest(base, + """ + package test; + public class Test { + private int test(Integer o) { + return switch (o) { + case (Integer i) when i > 0 -> 0; + case 0 -> 0; + case Integer i -> 0; + }; + } + } + """, + "Test.java:6:18: compiler.err.pattern.dominated", + "1 error"); + // A default label dominates a case label with a case pattern, and it also dominates a case label with a null case constant. + doTest(base, + """ + package test; + public class Test { + private int test(Integer o) { + return switch (o) { + default -> 0; + case (Integer i) when i > 0 -> 0; + case (Integer i) when i > 0 -> 0; + }; + } + } + """, + "Test.java:6:18: compiler.err.pattern.dominated", + "1 error"); + doTest(base, + """ + package test; + public class Test { + private int test(Integer o) { + return switch (o) { + case (Integer i) when i > 0 -> 0; + default -> 0; + case null -> 0; + }; + } + } + """, + "Test.java:7:18: compiler.err.pattern.dominated", + "1 error"); + // case label with a default dominates all other switch labels. + doTest(base, + """ + package test; + public class Test { + private int test(Integer o) { + return switch (o) { + case null, default -> 0; + case (Integer i) when i > 0 -> 0; + }; + } + } + """, + "Test.java:6:18: compiler.err.pattern.dominated", + "1 error"); + doTest(base, + """ + package test; + public class Test { + private int test(Integer o) { + return switch (o) { + case null, default -> 0; + case 0 -> 0; + case 1 -> 0; + }; + } + } + """, + "Test.java:6:18: compiler.err.pattern.dominated", + "1 error"); + + doTest(base, + """ + package test; + public class Test { + private int test(Integer o) { + return switch (o) { + default -> 0; + case 0 -> 0; + }; + } + } + """); + } + + private void doTest(Path base, String testCode, boolean expectErrors) throws IOException { + doTest(base, testCode, expectErrors, (String[]) null); + } + + private void doTest(Path base, String testCode, String... output) throws IOException { + doTest(base, testCode, output != null && output.length > 0, output); + } + + private void doTest(Path base, String testCode, boolean expectErrors, String... output) throws IOException { + Path current = base.resolve("."); + Path src = current.resolve("src"); + + tb.writeJavaFiles(src, testCode); + + Path classes = current.resolve("classes"); + + Files.createDirectories(classes); + + List actual = new JavacTask(tb) + .options("--enable-preview", + "-source", JAVA_VERSION, + "-XDrawDiagnostics", + "-Xlint:-preview", + "-XDshould-stop.at=FLOW") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(expectErrors? Task.Expect.FAIL : Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + if (output != null) { + actual.remove("- compiler.note.preview.filename: Test.java, DEFAULT"); + actual.remove("- compiler.note.preview.recompile"); + actual.remove(""); + + List expected = List.of(output); + + if (!Objects.equals(expected, actual)) { + throw new AssertionError("Unexpected output: " + actual); + } + } + } +} diff --git a/test/langtools/tools/javac/patterns/NullSwitch.java b/test/langtools/tools/javac/patterns/NullSwitch.java index a218ac686236e..0211aaa4de3de 100644 --- a/test/langtools/tools/javac/patterns/NullSwitch.java +++ b/test/langtools/tools/javac/patterns/NullSwitch.java @@ -25,9 +25,6 @@ void switchTest() { assertEquals(100, matchingSwitch3(0)); assertEquals(-1, matchingSwitch3(null)); assertEquals(-2, matchingSwitch3(0.0)); - assertEquals(0, matchingSwitch4("")); - assertEquals(1, matchingSwitch4(null)); - assertEquals(1, matchingSwitch4(0.0)); assertEquals(0, matchingSwitch5("")); assertEquals(1, matchingSwitch5("a")); assertEquals(100, matchingSwitch5(0)); @@ -92,7 +89,8 @@ private int matchingSwitch15(R r) { private int matchingSwitch1(Object obj) { return switch (obj) { case String s -> s.length(); - case null, Integer i -> i == null ? -1 : 100 + i; + case Integer i -> 100 + i; + case null -> -1; default -> -2; }; } @@ -107,23 +105,17 @@ private int matchingSwitch2(Object obj) { private int matchingSwitch3(Object obj) { return switch (obj) { case String s -> s.length(); - case Integer i, null -> i == null ? -1 : 100 + i; + case Integer i -> 100 + i; + case null -> -1; default -> -2; }; } - private int matchingSwitch4(Object obj) { - return switch (obj) { - case String s -> 0; - case default, null -> 1; - }; - } - private int matchingSwitch5(Object obj) { return switch (obj) { case String s: yield s.length(); - case null: - case Integer i: yield i == null ? -1 : 100 + i; + case null: yield -1; + case Integer i: yield 100 + i; default: yield -2; }; } @@ -131,7 +123,7 @@ private int matchingSwitch5(Object obj) { private int matchingSwitch6(Object obj) { return switch (obj) { case String s: yield 0; - case null: + case null: yield 1; default: yield 1; }; } @@ -139,8 +131,8 @@ private int matchingSwitch6(Object obj) { private int matchingSwitch7(Object obj) { return switch (obj) { case String s: yield s.length(); - case Integer i: - case null: yield i == null ? -1 : 100 + i; + case Integer i: yield 100 + i; + case null: yield -1; default: yield -2; }; } @@ -148,22 +140,24 @@ private int matchingSwitch7(Object obj) { private int matchingSwitch8(Object obj) { return switch (obj) { case String s: yield 0; - default: - case null: yield 1; + case null: + default: yield 1; }; } private int matchingSwitch9a(Object obj) { return switch (obj) { case String s: yield 0; - case null, Object o: yield 1; + case null: yield 1; + case Object o: yield 1; }; } private int matchingSwitch10a(Object obj) { switch (obj) { case String s: return 0; - case null, Object o: return 1; + case null: return 1; + case Object o: return 1; } } @@ -214,8 +208,8 @@ private int matchingSwitch12(Object obj) { private int matchingSwitch13(Object obj) { try { switch (obj) { - default: return 1; case String s: return 0; + default: return 1; } } catch (NullPointerException ex) { return 2; diff --git a/test/langtools/tools/javac/patterns/PrettyTest.java b/test/langtools/tools/javac/patterns/PrettyTest.java index 4cd897c3cd951..d51ebe4ccefec 100644 --- a/test/langtools/tools/javac/patterns/PrettyTest.java +++ b/test/langtools/tools/javac/patterns/PrettyTest.java @@ -54,7 +54,6 @@ void run() throws Exception { " b = o instanceof R(var s);\n" + " b = o instanceof R2(R(var s), String t);\n" + " b = o instanceof R2(R(var s), var t);\n" + - " b = o instanceof R(String s) r;\n" + " }\n" + " record R(String s) {}\n" + " record R2(R r, String s) {}\n" + @@ -71,7 +70,6 @@ boolean t(Object o) { b = o instanceof R(/*missing*/ s); b = o instanceof R2(R(/*missing*/ s), String t); b = o instanceof R2(R(/*missing*/ s), /*missing*/ t); - b = o instanceof R(String s) r; } \n\ class R { diff --git a/test/langtools/tools/javac/patterns/SimpleDeconstructionPattern.java b/test/langtools/tools/javac/patterns/SimpleDeconstructionPattern.java index 5388c3f6b90da..eb222d09bd96a 100644 --- a/test/langtools/tools/javac/patterns/SimpleDeconstructionPattern.java +++ b/test/langtools/tools/javac/patterns/SimpleDeconstructionPattern.java @@ -91,9 +91,6 @@ public static void main(String... args) throws Throwable { if (!testC(new P6(new P3("")))) { throw new IllegalStateException(); } - if (!testD(new P4("test"))) { - throw new IllegalStateException(); - } if (!testE(new P6(new P3(null)))) { throw new IllegalStateException(); } @@ -171,10 +168,6 @@ private static boolean testC(Object o) throws Throwable { return o instanceof P6(P3(String s)) && s.isEmpty(); } - private static boolean testD(Object o) throws Throwable { - return o instanceof P4(String s) p && (s.isEmpty() || "test".equals(p.o())); - } - private static boolean testE(Object o) throws Throwable { return o instanceof P6(P3(String s)) && s == null; } diff --git a/test/langtools/tools/javac/patterns/SimpleDeconstructionPatternNoPreview.out b/test/langtools/tools/javac/patterns/SimpleDeconstructionPatternNoPreview.out index 297edb08b6268..c808a37064d89 100644 --- a/test/langtools/tools/javac/patterns/SimpleDeconstructionPatternNoPreview.out +++ b/test/langtools/tools/javac/patterns/SimpleDeconstructionPatternNoPreview.out @@ -1,2 +1,2 @@ -SimpleDeconstructionPattern.java:121:27: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.deconstruction.patterns) +SimpleDeconstructionPattern.java:118:27: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.deconstruction.patterns) 1 error diff --git a/test/langtools/tools/javac/patterns/SwitchErrors.java b/test/langtools/tools/javac/patterns/SwitchErrors.java index f25815ae4c36b..6127e49e9e497 100644 --- a/test/langtools/tools/javac/patterns/SwitchErrors.java +++ b/test/langtools/tools/javac/patterns/SwitchErrors.java @@ -277,4 +277,27 @@ record R(Object o) {} default: break; } } + void noDiamond(Object o) { + record R(T t) {} + switch (o) { + case R<> r -> {} + default -> {} + } + if (o instanceof R<> r) {} + } + void noRawInferenceNonDeconstruction() { + record R(T t) {} + R o = null; + switch (o) { + case R r -> System.out.println(r.t().length()); + } + if (o instanceof R r) System.out.println(r.t().length()); + } + void cannotInfer() { + interface A {} + record R() implements A {} + A i = null; + if (i instanceof R()) { + } + } } diff --git a/test/langtools/tools/javac/patterns/SwitchErrors.out b/test/langtools/tools/javac/patterns/SwitchErrors.out index c37db6f9e7c71..de0413f16a44f 100644 --- a/test/langtools/tools/javac/patterns/SwitchErrors.out +++ b/test/langtools/tools/javac/patterns/SwitchErrors.out @@ -1,3 +1,15 @@ +SwitchErrors.java:66:18: compiler.err.default.label.not.allowed +SwitchErrors.java:72:18: compiler.err.default.label.not.allowed +SwitchErrors.java:72:27: compiler.err.default.label.not.allowed +SwitchErrors.java:138:28: compiler.err.default.label.not.allowed +SwitchErrors.java:144:18: compiler.err.default.label.not.allowed +SwitchErrors.java:149:18: compiler.err.default.label.not.allowed +SwitchErrors.java:154:18: compiler.err.default.label.not.allowed +SwitchErrors.java:213:29: compiler.err.default.label.not.allowed +SwitchErrors.java:220:47: compiler.err.default.label.not.allowed +SwitchErrors.java:227:47: compiler.err.default.label.not.allowed +SwitchErrors.java:283:20: compiler.err.illegal.start.of.type +SwitchErrors.java:286:28: compiler.err.illegal.start.of.type SwitchErrors.java:11:18: compiler.err.constant.label.not.compatible: java.lang.String, java.lang.Object SwitchErrors.java:17:18: compiler.err.constant.label.not.compatible: int, java.lang.Object SwitchErrors.java:23:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer) @@ -11,10 +23,10 @@ SwitchErrors.java:49:18: compiler.err.unconditional.pattern.and.default SwitchErrors.java:55:18: compiler.err.duplicate.unconditional.pattern SwitchErrors.java:61:13: compiler.err.duplicate.default.label SwitchErrors.java:67:13: compiler.err.duplicate.default.label -SwitchErrors.java:72:27: compiler.err.duplicate.default.label SwitchErrors.java:78:18: compiler.err.duplicate.case.label SwitchErrors.java:83:24: compiler.err.duplicate.case.label -SwitchErrors.java:88:28: compiler.err.flows.through.to.pattern +SwitchErrors.java:88:28: compiler.err.flows.through.from.pattern +SwitchErrors.java:93:18: compiler.err.flows.through.from.pattern SwitchErrors.java:94:18: compiler.err.flows.through.to.pattern SwitchErrors.java:101:18: compiler.err.flows.through.to.pattern SwitchErrors.java:108:18: compiler.err.flows.through.to.pattern @@ -22,25 +34,27 @@ SwitchErrors.java:113:18: compiler.err.prob.found.req: (compiler.misc.inconverti SwitchErrors.java:119:18: compiler.err.instanceof.reifiable.not.safe: java.util.List, java.util.List SwitchErrors.java:125:18: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, SwitchErrors, null) SwitchErrors.java:132:18: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array) -SwitchErrors.java:138:28: compiler.err.flows.through.from.pattern -SwitchErrors.java:144:18: compiler.err.flows.through.from.pattern -SwitchErrors.java:149:27: compiler.err.flows.through.to.pattern +SwitchErrors.java:143:18: compiler.err.flows.through.from.pattern +SwitchErrors.java:149:27: compiler.err.flows.through.from.pattern SwitchErrors.java:155:18: compiler.err.flows.through.to.pattern SwitchErrors.java:167:18: compiler.err.pattern.expected SwitchErrors.java:173:78: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null) SwitchErrors.java:179:73: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null) -SwitchErrors.java:186:21: compiler.err.flows.through.to.pattern +SwitchErrors.java:186:21: compiler.err.invalid.case.label.combination SwitchErrors.java:195:44: compiler.err.flows.through.from.pattern -SwitchErrors.java:213:29: compiler.err.unconditional.pattern.and.default -SwitchErrors.java:220:21: compiler.err.flows.through.to.pattern -SwitchErrors.java:220:47: compiler.err.flows.through.from.pattern +SwitchErrors.java:204:24: compiler.err.invalid.case.label.combination +SwitchErrors.java:220:21: compiler.err.invalid.case.label.combination SwitchErrors.java:227:44: compiler.err.flows.through.from.pattern -SwitchErrors.java:227:47: compiler.err.flows.through.from.pattern SwitchErrors.java:239:18: compiler.err.duplicate.unconditional.pattern SwitchErrors.java:244:18: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, java.lang.Integer) SwitchErrors.java:249:18: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array) -SwitchErrors.java:262:24: compiler.err.flows.through.to.pattern -SwitchErrors.java:276:37: compiler.err.flows.through.from.pattern +SwitchErrors.java:255:24: compiler.err.invalid.case.label.combination +SwitchErrors.java:262:24: compiler.err.invalid.case.label.combination +SwitchErrors.java:269:18: compiler.err.flows.through.from.pattern +SwitchErrors.java:276:18: compiler.err.flows.through.from.pattern +SwitchErrors.java:292:49: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location: kindname.class, java.lang.Object, null) +SwitchErrors.java:294:55: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location: kindname.class, java.lang.Object, null) +SwitchErrors.java:300:26: compiler.err.pattern.type.cannot.infer SwitchErrors.java:10:9: compiler.err.not.exhaustive.statement SwitchErrors.java:16:9: compiler.err.not.exhaustive.statement SwitchErrors.java:22:9: compiler.err.not.exhaustive.statement @@ -55,4 +69,4 @@ SwitchErrors.java:159:9: compiler.err.not.exhaustive.statement SwitchErrors.java:232:9: compiler.err.not.exhaustive.statement - compiler.note.preview.filename: SwitchErrors.java, DEFAULT - compiler.note.preview.recompile -55 errors +69 errors diff --git a/test/langtools/tools/javac/patterns/Switches.java b/test/langtools/tools/javac/patterns/Switches.java index 8b4e83f6666a3..48eda83e8cb30 100644 --- a/test/langtools/tools/javac/patterns/Switches.java +++ b/test/langtools/tools/javac/patterns/Switches.java @@ -47,8 +47,6 @@ void run() { assertTrue(testNullSwitch("")); runArrayTypeTest(this::testArrayTypeStatement); runArrayTypeTest(this::testArrayTypeExpression); - runDefaultTest(this::testDefaultDoesNotDominateStatement); - runDefaultTest(this::testDefaultDoesNotDominateExpression); runEnumTest(this::testEnumExpression1); runEnumTest(this::testEnumExpression2); runEnumTest(this::testEnumWithGuards1); @@ -98,6 +96,9 @@ void run() { assertEquals("OK", totalPatternAndNull(null)); assertEquals("1", nullAfterTotal(Integer.valueOf(42))); assertEquals("OK", nullAfterTotal(null)); + emptyFallThrough(1); + emptyFallThrough(""); + emptyFallThrough(1.0); } void run(Function mapper) { @@ -226,22 +227,6 @@ String testArrayTypeExpression(Object o) { }; } - String testDefaultDoesNotDominateStatement(Object o) { - String res; - switch (o) { - default -> res = "default"; - case String str -> res = "str" + str.length(); - } - return res; - } - - String testDefaultDoesNotDominateExpression(Object o) { - return switch (o) { - case default -> "default"; - case String str -> "str" + str.length(); - }; - } - int testStringWithConstant(String str) { switch (str) { case "A": return 1; @@ -262,7 +247,8 @@ String testEnumExpression1(E e) { return switch (e) { case A -> "a"; case B -> "b"; - case null, E x -> String.valueOf(x); + case E x -> String.valueOf(x); + case null -> "null"; }; } @@ -270,7 +256,8 @@ String testEnumExpression2(E e) { return switch (e) { case A -> "a"; case B -> "b"; - case E x, null -> String.valueOf(x); + case E x -> String.valueOf(x); + case null -> "null"; }; } @@ -280,7 +267,8 @@ String testEnumWithGuards1(E e) { case B: return "b"; case C: return String.valueOf(e); case E x when "A".equals(x.name()): return "broken"; - case null, E x: return String.valueOf(x); + case E x: return String.valueOf(x); + case null: return "null"; } } @@ -290,7 +278,8 @@ String testEnumWithGuardsExpression1(E e) { case B -> "b"; case C -> String.valueOf(e); case E x when "A".equals(x.name()) -> "broken"; - case null, E x -> String.valueOf(x); + case E x -> String.valueOf(x); + case null -> "null"; }; } @@ -299,7 +288,8 @@ String testEnumWithGuards2(E e) { case A: return "a"; case B: return "b"; case E x when "C".equals(x.name()): return "C"; - case null, E x: return e == E.C ? "broken" : String.valueOf(x); + case E x: return e == E.C ? "broken" : String.valueOf(x); + case null: return "null"; } } @@ -308,7 +298,8 @@ String testEnumWithGuardsExpression2(E e) { case A -> "a"; case B -> "b"; case E x when "C".equals(x.name()) -> "C"; - case null, E x -> e == E.C ? "broken" : String.valueOf(x); + case E x -> e == E.C ? "broken" : String.valueOf(x); + case null -> "null"; }; } @@ -317,7 +308,8 @@ String testEnumWithGuards3(E e) { case A: return "a"; case B: return "b"; case Object x when "C".equals(x.toString()): return "C"; - case null, E x: return e == E.C ? "broken" : String.valueOf(x); + case E x: return e == E.C ? "broken" : String.valueOf(x); + case null: return "null"; } } @@ -326,7 +318,8 @@ String testEnumWithGuardsExpression3(E e) { case A -> "a"; case B -> "b"; case Object x when "C".equals(x.toString()) -> "C"; - case null, E x -> e == E.C ? "broken" : String.valueOf(x); + case E x -> e == E.C ? "broken" : String.valueOf(x); + case null -> "null"; }; } @@ -335,7 +328,8 @@ String testEnumWithGuards4(E e) { case A: return "a"; case B: return "b"; case Runnable x when "C".equals(x.toString()): return "C"; - case null, E x: return e == E.C ? "broken" : String.valueOf(x); + case E x: return e == E.C ? "broken" : String.valueOf(x); + case null: return "null"; } } @@ -344,7 +338,8 @@ String testEnumWithGuardsExpression4(E e) { case A -> "a"; case B -> "b"; case Runnable x when "C".equals(x.toString()) -> "C"; - case null, E x -> e == E.C ? "broken" : String.valueOf(x); + case E x -> e == E.C ? "broken" : String.valueOf(x); + case null -> "null"; }; } @@ -353,7 +348,8 @@ String testStringWithGuards1(E e) { case "A": return "a"; case Switches.ConstantClassClash: return "b"; case String x when "C".equals(x): return "C"; - case null, String x: return "C".equals(x) ? "broken" : String.valueOf(x); + case String x: return "C".equals(x) ? "broken" : String.valueOf(x); + case null: return "null"; } } @@ -362,7 +358,8 @@ String testStringWithGuardsExpression1(E e) { case "A" -> "a"; case ConstantClassClash -> "b"; case String x when "C".equals(x) -> "C"; - case null, String x -> e == E.C ? "broken" : String.valueOf(x); + case String x -> e == E.C ? "broken" : String.valueOf(x); + case null -> "null"; }; } @@ -371,7 +368,8 @@ String testIntegerWithGuards1(E e) { case 0: return "a"; case 1: return "b"; case Integer x when x.equals(2): return "C"; - case null, Integer x: return Objects.equals(x, 2) ? "broken" : String.valueOf(x); + case Integer x: return Objects.equals(x, 2) ? "broken" : String.valueOf(x); + case null: return "null"; } } @@ -380,7 +378,8 @@ String testIntegerWithGuardsExpression1(E e) { case 0 -> "a"; case 1 -> "b"; case Integer x when x.equals(2) -> "C"; - case null, Integer x -> Objects.equals(x, 2) ? "broken" : String.valueOf(x); + case Integer x -> Objects.equals(x, 2) ? "broken" : String.valueOf(x); + case null -> "null"; }; } @@ -462,21 +461,21 @@ void exhaustiveStatementSane(Object o) { } } switch (o) { - case null, Object obj:; //no break intentionally - should not fall through to any possible default - } - switch (o) { - case Object obj, null:; //no break intentionally - should not fall through to any possible default + case Object obj: int i; + case null:; //no break intentionally - should not fall through to any possible default } } void exhaustiveStatementSane2(I i) { switch (i) { case A a: break; - case null, B b:; //no break intentionally - should not fall through to any possible default + case B b:; //no break intentionally - should not fall through to any possible default + case null:; } switch (i) { case A a -> {} - case null, B b -> {} + case B b -> {} + case null -> {} } } @@ -624,7 +623,7 @@ private int switchOverPrimitiveInt(Integer i) { String deconstructStatement(Object o) { switch (o) { case R(String s) -> {return s;} - case R(Integer i) r -> {return r.o().toString();} + case R(Integer i) -> {return i.toString();} case Object x -> {return "other";} } } @@ -632,7 +631,7 @@ String deconstructStatement(Object o) { String deconstructExpression(Object o) { return switch (o) { case R(String s) -> s; - case R(Integer i) r -> r.o().toString(); + case R(Integer i) -> i.toString(); case Object x -> "other"; }; } @@ -640,7 +639,8 @@ String deconstructExpression(Object o) { String totalPatternAndNull(Integer in) { return switch (in) { case -1: { yield "";} - case Integer i: case null: { yield "OK";} + case null: { yield "OK";} + case Integer i: { yield "OK";} }; } @@ -651,6 +651,14 @@ String nullAfterTotal(Object o) { }; } + void emptyFallThrough(Object o) { + switch (o) { + case Integer i: + case String s: + case Object obj: + } + } + //verify that for cases like: //case ConstantClassClash -> //ConstantClassClash is interpreted as a field, not as a class diff --git a/test/langtools/tools/javac/switchnull/SwitchNull.java b/test/langtools/tools/javac/switchnull/SwitchNull.java index 3edc43f3d74ec..79e651348f100 100644 --- a/test/langtools/tools/javac/switchnull/SwitchNull.java +++ b/test/langtools/tools/javac/switchnull/SwitchNull.java @@ -97,8 +97,8 @@ private int switchEnum(E e) { private int switchEnumWithDefault(E e) { switch (e) { case A: return 0; - default: return 1; case null: return -1; + default: return 1; } }