Skip to content

Commit

Permalink
Code generation for lists of Patterns in case statements
Browse files Browse the repository at this point in the history
Signed-off-by: David Thompson <[email protected]>
  • Loading branch information
datho7561 committed Jan 24, 2024
1 parent 967a245 commit 80a8db4
Show file tree
Hide file tree
Showing 8 changed files with 423 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,12 @@ public void setFieldIndex(int depth) {
* @param scope used to determine source level
*/
public boolean isUnnamed(BlockScope scope) {
return ((this.name.length == 1 && this.name[0] == '_') || ("\\u005F".equals(this.name.toString()))) //$NON-NLS-1$
return isPotentiallyUnnamed()
&& scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK21
&& scope.compilerOptions().enablePreviewFeatures;
}

public boolean isPotentiallyUnnamed() {
return (this.name.length == 1 && this.name[0] == '_') || ("\\u005F".equals(this.name.toString())); //$NON-NLS-1$
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ public FlowInfo analyseCode(
for(int i=0; i < this.constantExpressions.length; i++) {
Expression e = this.constantExpressions[i];
nullPatternCount += e instanceof NullLiteral ? 1 : 0;
if (i > 0 && (e instanceof Pattern)) {
if (i > 0 && (e instanceof Pattern)
&& (currentScope.compilerOptions().sourceLevel < ClassFileConstants.JDK21 || !currentScope.compilerOptions().enablePreviewFeatures)) {
if (!(i == nullPatternCount && e instanceof TypePattern))
currentScope.problemReporter().IllegalFallThroughToPattern(e);
}
Expand Down Expand Up @@ -170,14 +171,28 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream) {

private void casePatternExpressionGenerateCode(BlockScope currentScope, CodeStream codeStream) {
if (this.patternIndex != -1) {
Pattern pattern = ((Pattern) this.constantExpressions[this.patternIndex]);
if (containsPatternVariable()) {
BranchLabel thenTarget = new BranchLabel(codeStream);
for (int i = 0; i < this.constantExpressions.length - 1; i++) {
Pattern pattern = ((Pattern)this.constantExpressions[i]);
pattern.thenTarget = thenTarget;
LocalVariableBinding local = currentScope.findVariable(SwitchStatement.SecretPatternVariableName, null);
codeStream.load(local);
pattern.generateCode(currentScope, codeStream);
pattern.fullWrapupGeneration(codeStream);
codeStream.goto_(pattern.thenTarget);
pattern.elseTarget.place();
}
Pattern pattern = ((Pattern)this.constantExpressions[this.constantExpressions.length - 1]);
pattern.thenTarget = thenTarget;
LocalVariableBinding local = currentScope.findVariable(SwitchStatement.SecretPatternVariableName, null);
codeStream.load(local);
pattern.generateCode(currentScope, codeStream);
if (pattern.countNamedVariables(currentScope) > 0) {
pattern.wrapupGeneration(codeStream);
} else {
pattern.setTargets(codeStream);
pattern.fullWrapupGeneration(codeStream);
}
pattern.setTargets(codeStream);

if (!(pattern instanceof GuardedPattern))
codeStream.goto_(pattern.thenTarget);
Expand Down Expand Up @@ -285,9 +300,11 @@ private Expression getFirstValidExpression(BlockScope scope, SwitchStatement swi
scope.problemReporter().validateJavaFeatureSupport(JavaFeature.PATTERN_MATCHING_IN_SWITCH,
e.sourceStart, e.sourceEnd);
if (this.constantExpressions.length > 1 || e instanceof GuardedPattern gp && gp.patterns.length > 1) {
PatternVariableCounter myVisitor = new PatternVariableCounter();
this.traverse(myVisitor, scope);
if (myVisitor.numVars > 0) {
int count = 0;
for (Expression expr : this.constantExpressions) {
count += ((Pattern)expr).countNamedVariables(scope);
}
if (count > 0) {
scope.problemReporter().illegalCaseLabelWithMultiplePatterns(this);
}
}
Expand Down Expand Up @@ -563,24 +580,4 @@ public LocalDeclaration getLocalDeclaration() {
return patternVariableIntroduced;
}

private class PatternVariableCounter extends ASTVisitor {

public int numVars = 0;

@Override
public boolean visit(TypePattern pattern, BlockScope scope) {
if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$
this.numVars++;
}
return true;
}

@Override
public boolean visit(RecordPattern pattern, BlockScope scope) {
if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$
this.numVars++;
}
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,29 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel) {
this.thenTarget = new BranchLabel(codeStream);
this.elseTarget = new BranchLabel(codeStream);
for (Pattern pattern : this.patterns ) {
pattern.generateOptimizedBoolean(currentScope, codeStream, this.thenTarget, this.elseTarget);

LocalVariableBinding local = currentScope.findVariable(SwitchStatement.SecretPatternVariableName, null);
BranchLabel toGuard = new BranchLabel(codeStream);

for (int i = 0; i < this.patterns.length - 1; i++) {
Pattern pattern = this.patterns[i];
pattern.thenTarget = toGuard;
pattern.generateCode(currentScope, codeStream);
pattern.fullWrapupGeneration(codeStream);
codeStream.goto_(pattern.thenTarget);
pattern.elseTarget.place();
codeStream.load(local);
}
Pattern pattern = this.patterns[this.patterns.length - 1];
pattern.thenTarget = toGuard;
pattern.elseTarget = this.elseTarget;
pattern.generateCode(currentScope, codeStream);
if (pattern.countNamedVariables(currentScope) > 0) {
pattern.wrapupGeneration(codeStream);
} else {
pattern.fullWrapupGeneration(codeStream);
}
toGuard.place();
Constant cst = this.condition.optimizedBooleanConstant();

setGuardedElseTarget(currentScope, this.elseTarget);
Expand All @@ -92,6 +112,9 @@ public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStr
this.thenTarget,
null,
cst == Constant.NotAConstant);
if (cst != Constant.NotAConstant) {
codeStream.goto_(this.thenTarget);
}
if (this.thenInitStateIndex2 != -1) {
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.thenInitStateIndex2);
codeStream.addDefinitelyAssignedVariables(currentScope, this.thenInitStateIndex2);
Expand Down Expand Up @@ -251,11 +274,32 @@ public void wrapupGeneration(CodeStream codeStream) {
}
@Override
public void fullWrapupGeneration(CodeStream codeStream) {
this.primaryPattern.fullWrapupGeneration(codeStream);
this.primary().fullWrapupGeneration(codeStream);
}
@Override
protected void generatePatternVariable(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel,
BranchLabel falseLabel) {
this.primary().generatePatternVariable(currentScope, codeStream, trueLabel, falseLabel);
}
private class PatternVariableCounter extends ASTVisitor {

public int numVars = 0;

@Override
public boolean visit(TypePattern pattern, BlockScope scope) {
if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$
this.numVars++;
}
return true;
}

@Override
public boolean visit(RecordPattern pattern, BlockScope scope) {
if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$
this.numVars++;
}
return true;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,36 @@ public Pattern primary() {
return this;
}

public int countNamedVariables(BlockScope scope) {
var pvc = new PatternVariableCounter();
this.traverse(pvc, scope);
return pvc.numVars;
}

@Override
public StringBuilder print(int indent, StringBuilder output) {
return this.printExpression(indent, output);
}

private class PatternVariableCounter extends ASTVisitor {

public int numVars = 0;

@Override
public boolean visit(TypePattern pattern, BlockScope scope) {
if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$
this.numVars++;
}
return true;
}

@Override
public boolean visit(RecordPattern pattern, BlockScope scope) {
if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$
this.numVars++;
}
return true;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@ public boolean dominates(Pattern p) {
return false;
}
}
} else if (p instanceof GuardedPattern gp) {
for (Pattern gpChild : gp.patterns) {
if (!this.dominates(gpChild)) {
return false;
}
}
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -985,15 +985,13 @@ private void generateCodePatternCaseEpilogue(CodeStream codeStream, int caseInde
if (this.switchPatternRestartTarget != null && caseStatement != null
&& caseStatement.patternIndex != -1 // for null
) {
Pattern pattern = (Pattern) caseStatement.constantExpressions[caseStatement.patternIndex];
pattern.elseTarget.place();
Pattern pattern = (Pattern) caseStatement.constantExpressions[caseStatement.constantExpressions.length - 1];
pattern.suspendVariables(codeStream, this.scope);
pattern.elseTarget.place();
caseIndex = this.nullProcessed ? caseIndex - 1 : caseIndex;
if (!pattern.isAlwaysTrue()) {
codeStream.loadInt(caseIndex);
codeStream.store(this.restartIndexLocal, false);
codeStream.goto_(this.switchPatternRestartTarget);
}
codeStream.loadInt(caseIndex);
codeStream.store(this.restartIndexLocal, false);
codeStream.goto_(this.switchPatternRestartTarget);
pattern.thenTarget.place();
pattern.resumeVariables(codeStream, this.scope);
} else if (this.containsNull && caseStatement != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2227,7 +2227,7 @@ public void testBug574563_004() {
3. ERROR in X.java (at line 4)
case null, Integer i when i > 10 -> System.out.println(0);
^^^^^
Syntax error on tokens, TypeElidedFormalParameter expected instead
Syntax error on tokens, TypeElidedUnnamedFormalParameter expected instead
----------
4. ERROR in X.java (at line 4)
case null, Integer i when i > 10 -> System.out.println(0);
Expand Down Expand Up @@ -2270,7 +2270,7 @@ public void testBug574563_005() {
3. ERROR in X.java (at line 4)
case Integer i when i > 10, null -> System.out.println(0);
^^^^^^^^
Syntax error on tokens, TypeElidedFormalParameter expected instead
Syntax error on tokens, TypeElidedUnnamedFormalParameter expected instead
----------
4. ERROR in X.java (at line 4)
case Integer i when i > 10, null -> System.out.println(0);
Expand Down Expand Up @@ -6666,7 +6666,7 @@ public void testIssue1351_7() {
3. ERROR in X.java (at line 5)
case Byte p when p.equals(exp), (byte) 0 -> {
^^^^
Syntax error on tokens, TypeElidedFormalParameter expected instead
Syntax error on tokens, TypeElidedUnnamedFormalParameter expected instead
----------
4. ERROR in X.java (at line 8)
}
Expand Down Expand Up @@ -6807,7 +6807,7 @@ public void testIssue1351_10() {
3. ERROR in X.java (at line 5)
case Byte p when p.equals(exp), null -> {
^^^^^^^
Syntax error on tokens, TypeElidedFormalParameter expected instead
Syntax error on tokens, TypeElidedUnnamedFormalParameter expected instead
----------
4. ERROR in X.java (at line 8)
}
Expand Down Expand Up @@ -6853,7 +6853,7 @@ public void testIssue1351_11() {
3. ERROR in X.java (at line 5)
case Byte p when p.equals(exp), default -> {
^^^^^^^^^^^^^^^^^^^^^^^^^
Syntax error on tokens, TypeElidedFormalParameter expected instead
Syntax error on tokens, TypeElidedUnnamedFormalParameter expected instead
----------
4. ERROR in X.java (at line 8)
}
Expand Down Expand Up @@ -7186,6 +7186,7 @@ public static void main(String[] args) {
},
"Default");
}

// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1856
// [switch][record patterns] NPE: Cannot invoke "org.eclipse.jdt.internal.compiler.lookup.MethodBinding.isStatic()"
public void testGHI1856() {
Expand Down Expand Up @@ -7275,4 +7276,5 @@ case WrapperRec(ExhaustiveSwitch.Data data) when data.name.isEmpty() -> { }
+ "Record component with type Data is not compatible with type ExhaustiveSwitch.Data\n"
+ "----------\n");
}

}
Loading

0 comments on commit 80a8db4

Please sign in to comment.