Skip to content

Commit 756dd5b

Browse files
lahodajbiboudismcimadamore
committed
8294942: Compiler implementation for Record Patterns (Second Preview)
8294945: Compiler implementation for Pattern Matching for switch (Fourth Preview) Co-authored-by: Aggelos Biboudis <abimpoudis@openjdk.org> Co-authored-by: Maurizio Cimadamore <mcimadamore@openjdk.org> Reviewed-by: mcimadamore, vromero
1 parent c612512 commit 756dd5b

38 files changed

+1260
-363
lines changed

src/jdk.compiler/share/classes/com/sun/source/tree/DeconstructionPatternTree.java

-6
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,5 @@ public interface DeconstructionPatternTree extends PatternTree {
4848
*/
4949
List<? extends PatternTree> getNestedPatterns();
5050

51-
/**
52-
* Returns the binding variable.
53-
* @return the binding variable
54-
*/
55-
VariableTree getVariable();
56-
5751
}
5852

src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java

+28
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
package com.sun.source.tree;
2727

28+
import jdk.internal.javac.PreviewFeature;
29+
2830
/**
2931
* A tree node for an {@code instanceof} expression.
3032
*
@@ -40,6 +42,23 @@
4042
* @since 1.6
4143
*/
4244
public interface InstanceOfTree extends ExpressionTree {
45+
46+
/**
47+
* Two possible variants of instanceof expressions:
48+
* <ul>
49+
* <li> testing types, and
50+
* <li> performing pattern matching
51+
* </ul>
52+
* @since 20
53+
*/
54+
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
55+
public enum TestKind {
56+
/** instanceof only testing a type */
57+
TYPE,
58+
/** instanceof doing a pattern matching */
59+
PATTERN
60+
}
61+
4362
/**
4463
* Returns the expression to be tested.
4564
* @return the expression
@@ -73,4 +92,13 @@ public interface InstanceOfTree extends ExpressionTree {
7392
* @since 16
7493
*/
7594
PatternTree getPattern();
95+
96+
/**
97+
* Returns the kind of this instanceof expression.
98+
*
99+
* @return the kind of this instanceof expression
100+
* @since 20
101+
*/
102+
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
103+
TestKind getTestKind();
76104
}

src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java

-1
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,6 @@ public R visitPatternCaseLabel(PatternCaseLabelTree node, P p) {
839839
public R visitDeconstructionPattern(DeconstructionPatternTree node, P p) {
840840
R r = scan(node.getDeconstructor(), p);
841841
r = scanAndReduce(node.getNestedPatterns(), p, r);
842-
r = scanAndReduce(node.getVariable(), p, r);
843842
return r;
844843
}
845844

src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java

+5
Original file line numberDiff line numberDiff line change
@@ -2042,6 +2042,11 @@ public void setThrow() {
20422042
this.kind = Kind.THROWS;
20432043
}
20442044

2045+
public void setNormal() {
2046+
Assert.check(this.kind == Kind.CAPTURED);
2047+
this.kind = Kind.NORMAL;
2048+
}
2049+
20452050
/**
20462051
* Returns a new copy of this undet var.
20472052
*/

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java

+17-33
Original file line numberDiff line numberDiff line change
@@ -1665,7 +1665,8 @@ private void handleSwitch(JCTree switchTree,
16651665
} else {
16661666
patternSwitch = cases.stream()
16671667
.flatMap(c -> c.labels.stream())
1668-
.anyMatch(l -> l.hasTag(PATTERNCASELABEL));
1668+
.anyMatch(l -> l.hasTag(PATTERNCASELABEL) ||
1669+
TreeInfo.isNullCaseLabel(l));
16691670
}
16701671

16711672
// Attribute all cases and
@@ -1677,7 +1678,6 @@ private void handleSwitch(JCTree switchTree,
16771678
boolean hasNullPattern = false; // Is there a null pattern?
16781679
CaseTree.CaseKind caseKind = null;
16791680
boolean wasError = false;
1680-
MatchBindings prevBindings = null;
16811681
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
16821682
JCCase c = l.head;
16831683
if (caseKind == null) {
@@ -1687,7 +1687,7 @@ private void handleSwitch(JCTree switchTree,
16871687
Errors.SwitchMixingCaseTypes);
16881688
wasError = true;
16891689
}
1690-
MatchBindings currentBindings = prevBindings;
1690+
MatchBindings currentBindings = null;
16911691
boolean wasUnconditionalPattern = hasUnconditionalPattern;
16921692
for (JCCaseLabel label : c.labels) {
16931693
if (label instanceof JCConstantCaseLabel constLabel) {
@@ -1751,7 +1751,7 @@ private void handleSwitch(JCTree switchTree,
17511751
} else if (label instanceof JCPatternCaseLabel patternlabel) {
17521752
//pattern
17531753
JCPattern pat = patternlabel.pat;
1754-
attribExpr(pat, switchEnv);
1754+
attribExpr(pat, switchEnv, seltype);
17551755
Type primaryType = TreeInfo.primaryPatternType(pat);
17561756
if (!primaryType.hasTag(TYPEVAR)) {
17571757
primaryType = chk.checkClassOrArrayType(pat.pos(), primaryType);
@@ -1805,9 +1805,6 @@ private void handleSwitch(JCTree switchTree,
18051805

18061806
preFlow(c);
18071807
c.completesNormally = flow.aliveAfter(caseEnv, c, make);
1808-
1809-
prevBindings = c.caseKind == CaseTree.CaseKind.STATEMENT && c.completesNormally ? currentBindings
1810-
: null;
18111808
}
18121809
if (patternSwitch) {
18131810
chk.checkSwitchCaseStructure(cases);
@@ -4078,7 +4075,7 @@ public void visitTypeTest(JCInstanceOf tree) {
40784075
if (tree.pattern.getTag() == BINDINGPATTERN ||
40794076
tree.pattern.getTag() == PARENTHESIZEDPATTERN ||
40804077
tree.pattern.getTag() == RECORDPATTERN) {
4081-
attribTree(tree.pattern, env, unknownExprInfo);
4078+
attribExpr(tree.pattern, env, exprtype);
40824079
clazztype = tree.pattern.type;
40834080
if (types.isSubtype(exprtype, clazztype) &&
40844081
!exprtype.isErroneous() && !clazztype.isErroneous() &&
@@ -4146,8 +4143,7 @@ private boolean checkCastablePattern(DiagnosticPosition pos,
41464143
public void visitBindingPattern(JCBindingPattern tree) {
41474144
Type type;
41484145
if (tree.var.vartype != null) {
4149-
ResultInfo varInfo = new ResultInfo(KindSelector.TYP, resultInfo.pt, resultInfo.checkContext);
4150-
type = attribTree(tree.var.vartype, env, varInfo);
4146+
type = attribType(tree.var.vartype, env);
41514147
} else {
41524148
type = resultInfo.pt;
41534149
}
@@ -4170,14 +4166,20 @@ public void visitBindingPattern(JCBindingPattern tree) {
41704166

41714167
@Override
41724168
public void visitRecordPattern(JCRecordPattern tree) {
4173-
tree.type = attribType(tree.deconstructor, env);
4169+
Type type = attribType(tree.deconstructor, env);
4170+
if (type.isRaw() && type.tsym.getTypeParameters().nonEmpty()) {
4171+
Type inferred = infer.instantiatePatternType(resultInfo.pt, type.tsym);
4172+
if (inferred == null) {
4173+
log.error(tree.pos(), Errors.PatternTypeCannotInfer);
4174+
} else {
4175+
type = inferred;
4176+
}
4177+
}
4178+
tree.type = tree.deconstructor.type = type;
41744179
Type site = types.removeWildcards(tree.type);
41754180
List<Type> expectedRecordTypes;
41764181
if (site.tsym.kind == Kind.TYP && ((ClassSymbol) site.tsym).isRecord()) {
41774182
ClassSymbol record = (ClassSymbol) site.tsym;
4178-
if (record.type.getTypeArguments().nonEmpty() && tree.type.isRaw()) {
4179-
log.error(tree.pos(),Errors.RawDeconstructionPattern);
4180-
}
41814183
expectedRecordTypes = record.getRecordComponents()
41824184
.stream()
41834185
.map(rc -> types.memberType(site, rc)).collect(List.collector());
@@ -4195,10 +4197,7 @@ public void visitRecordPattern(JCRecordPattern tree) {
41954197
Env<AttrContext> localEnv = env.dup(tree, env.info.dup(env.info.scope.dup()));
41964198
try {
41974199
while (recordTypes.nonEmpty() && nestedPatterns.nonEmpty()) {
4198-
boolean nestedIsVarPattern = false;
4199-
nestedIsVarPattern |= nestedPatterns.head.hasTag(BINDINGPATTERN) &&
4200-
((JCBindingPattern) nestedPatterns.head).var.vartype == null;
4201-
attribExpr(nestedPatterns.head, localEnv, nestedIsVarPattern ? recordTypes.head : Type.noType);
4200+
attribExpr(nestedPatterns.head, localEnv, recordTypes.head);
42024201
checkCastablePattern(nestedPatterns.head.pos(), recordTypes.head, nestedPatterns.head.type);
42034202
outBindings.addAll(matchBindings.bindingsWhenTrue);
42044203
matchBindings.bindingsWhenTrue.forEach(localEnv.info.scope::enter);
@@ -4216,21 +4215,6 @@ public void visitRecordPattern(JCRecordPattern tree) {
42164215
Errors.IncorrectNumberOfNestedPatterns(expectedRecordTypes,
42174216
nestedTypes));
42184217
}
4219-
if (tree.var != null) {
4220-
BindingSymbol v = new BindingSymbol(tree.var.mods.flags, tree.var.name, tree.type,
4221-
localEnv.info.scope.owner);
4222-
v.pos = tree.pos;
4223-
tree.var.sym = v;
4224-
if (chk.checkUnique(tree.var.pos(), v, localEnv.info.scope)) {
4225-
chk.checkTransparentVar(tree.var.pos(), v, localEnv.info.scope);
4226-
}
4227-
if (tree.var.vartype != null) {
4228-
annotate.annotateLater(tree.var.mods.annotations, localEnv, v, tree.pos());
4229-
annotate.queueScanTreeAndTypeAnnotate(tree.var.vartype, localEnv, v, tree.var.pos());
4230-
annotate.flush();
4231-
}
4232-
outBindings.add(v);
4233-
}
42344218
} finally {
42354219
localEnv.info.scope.leave();
42364220
}

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java

+80-47
Original file line numberDiff line numberDiff line change
@@ -4346,74 +4346,107 @@ void checkModuleRequires(final DiagnosticPosition pos, final RequiresDirective r
43464346
* @param cases the cases that should be checked.
43474347
*/
43484348
void checkSwitchCaseStructure(List<JCCase> cases) {
4349-
boolean wasConstant = false; // Seen a constant in the same case label
4350-
boolean wasDefault = false; // Seen a default in the same case label
4351-
boolean wasNullPattern = false; // Seen a null pattern in the same case label,
4352-
//or fall through from a null pattern
4353-
boolean wasPattern = false; // Seen a pattern in the same case label
4354-
//or fall through from a pattern
4355-
boolean wasTypePattern = false; // Seen a pattern in the same case label
4356-
//or fall through from a type pattern
4357-
boolean wasNonEmptyFallThrough = false;
43584349
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
43594350
JCCase c = l.head;
4360-
for (JCCaseLabel label : c.labels) {
4361-
if (label.hasTag(CONSTANTCASELABEL)) {
4362-
JCExpression expr = ((JCConstantCaseLabel) label).expr;
4363-
if (TreeInfo.isNull(expr)) {
4364-
if (wasPattern && !wasTypePattern && !wasNonEmptyFallThrough) {
4365-
log.error(label.pos(), Errors.FlowsThroughFromPattern);
4366-
}
4367-
wasNullPattern = true;
4368-
} else {
4369-
if (wasPattern && !wasNonEmptyFallThrough) {
4370-
log.error(label.pos(), Errors.FlowsThroughFromPattern);
4351+
if (c.labels.head instanceof JCConstantCaseLabel constLabel) {
4352+
if (TreeInfo.isNull(constLabel.expr)) {
4353+
if (c.labels.tail.nonEmpty()) {
4354+
if (c.labels.tail.head instanceof JCDefaultCaseLabel defLabel) {
4355+
if (c.labels.tail.tail.nonEmpty()) {
4356+
log.error(c.labels.tail.tail.head.pos(), Errors.InvalidCaseLabelCombination);
4357+
}
4358+
} else {
4359+
log.error(c.labels.tail.head.pos(), Errors.InvalidCaseLabelCombination);
43714360
}
4372-
wasConstant = true;
4373-
}
4374-
} else if (label.hasTag(DEFAULTCASELABEL)) {
4375-
if (wasPattern && !wasNonEmptyFallThrough) {
4376-
log.error(label.pos(), Errors.FlowsThroughFromPattern);
43774361
}
4378-
wasDefault = true;
43794362
} else {
4380-
JCPattern pat = ((JCPatternCaseLabel) label).pat;
4381-
while (pat instanceof JCParenthesizedPattern parenthesized) {
4382-
pat = parenthesized.pattern;
4383-
}
4384-
boolean isTypePattern = pat.hasTag(BINDINGPATTERN);
4385-
if (wasPattern || wasConstant || wasDefault ||
4386-
(wasNullPattern && (!isTypePattern || wasNonEmptyFallThrough))) {
4387-
log.error(label.pos(), Errors.FlowsThroughToPattern);
4363+
for (JCCaseLabel label : c.labels.tail) {
4364+
if (!(label instanceof JCConstantCaseLabel) || TreeInfo.isNullCaseLabel(label)) {
4365+
log.error(label.pos(), Errors.InvalidCaseLabelCombination);
4366+
break;
4367+
}
43884368
}
4389-
wasPattern = true;
4390-
wasTypePattern = isTypePattern;
43914369
}
4370+
} else {
4371+
if (c.labels.tail.nonEmpty()) {
4372+
log.error(c.labels.tail.head.pos(), Errors.FlowsThroughFromPattern);
4373+
}
4374+
}
4375+
}
4376+
4377+
boolean isCaseStatementGroup = cases.nonEmpty() &&
4378+
cases.head.caseKind == CaseTree.CaseKind.STATEMENT;
4379+
4380+
if (isCaseStatementGroup) {
4381+
boolean previousCompletessNormally = false;
4382+
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
4383+
JCCase c = l.head;
4384+
if (previousCompletessNormally &&
4385+
c.stats.nonEmpty() &&
4386+
c.labels.head instanceof JCPatternCaseLabel patternLabel &&
4387+
hasBindings(patternLabel.pat)) {
4388+
log.error(c.labels.head.pos(), Errors.FlowsThroughToPattern);
4389+
} else if (c.stats.isEmpty() &&
4390+
c.labels.head instanceof JCPatternCaseLabel patternLabel &&
4391+
hasBindings(patternLabel.pat) &&
4392+
hasStatements(l.tail)) {
4393+
log.error(c.labels.head.pos(), Errors.FlowsThroughFromPattern);
4394+
}
4395+
previousCompletessNormally = c.completesNormally;
43924396
}
4397+
}
4398+
}
43934399

4394-
boolean completesNormally = c.caseKind == CaseTree.CaseKind.STATEMENT ? c.completesNormally
4395-
: false;
4400+
boolean hasBindings(JCPattern p) {
4401+
boolean[] bindings = new boolean[1];
43964402

4397-
if (c.stats.nonEmpty()) {
4398-
wasConstant = false;
4399-
wasDefault = false;
4400-
wasNullPattern &= completesNormally;
4401-
wasPattern &= completesNormally;
4402-
wasTypePattern &= completesNormally;
4403+
new TreeScanner() {
4404+
@Override
4405+
public void visitBindingPattern(JCBindingPattern tree) {
4406+
bindings[0] = true;
4407+
super.visitBindingPattern(tree);
44034408
}
4409+
}.scan(p);
44044410

4405-
wasNonEmptyFallThrough = c.stats.nonEmpty() && completesNormally;
4406-
}
4411+
return bindings[0];
44074412
}
44084413

4414+
boolean hasStatements(List<JCCase> cases) {
4415+
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
4416+
if (l.head.stats.nonEmpty()) {
4417+
return true;
4418+
}
4419+
}
4420+
4421+
return false;
4422+
}
44094423
void checkSwitchCaseLabelDominated(List<JCCase> cases) {
44104424
List<JCCaseLabel> caseLabels = List.nil();
4425+
boolean seenDefault = false;
4426+
boolean seenDefaultLabel = false;
4427+
boolean warnDominatedByDefault = false;
44114428
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
44124429
JCCase c = l.head;
44134430
for (JCCaseLabel label : c.labels) {
4414-
if (label.hasTag(DEFAULTCASELABEL) || TreeInfo.isNullCaseLabel(label)) {
4431+
if (label.hasTag(DEFAULTCASELABEL)) {
4432+
seenDefault = true;
4433+
seenDefaultLabel |=
4434+
TreeInfo.isNullCaseLabel(c.labels.head);
44154435
continue;
44164436
}
4437+
if (TreeInfo.isNullCaseLabel(label)) {
4438+
if (seenDefault) {
4439+
log.error(label.pos(), Errors.PatternDominated);
4440+
}
4441+
continue;
4442+
}
4443+
if (seenDefault && !warnDominatedByDefault) {
4444+
if (label.hasTag(PATTERNCASELABEL) ||
4445+
(label instanceof JCConstantCaseLabel && seenDefaultLabel)) {
4446+
log.error(label.pos(), Errors.PatternDominated);
4447+
warnDominatedByDefault = true;
4448+
}
4449+
}
44174450
Type currentType = labelType(label);
44184451
for (JCCaseLabel testCaseLabel : caseLabels) {
44194452
Type testType = labelType(testCaseLabel);

0 commit comments

Comments
 (0)