Skip to content

Commit

Permalink
Simplify CaseStatement
Browse files Browse the repository at this point in the history
  • Loading branch information
srikanth-sankaran committed Nov 16, 2024
1 parent 7c2dacc commit 1fff64f
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 232 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
* Advantest R & D - Enhanced Switches v2.0
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

Expand Down Expand Up @@ -39,6 +40,20 @@

public class CaseStatement extends Statement {

class LabelExpression extends Expression {

Expression labelExpression;

public LabelExpression(Expression e) {
this.labelExpression = e;
}

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

}

public BranchLabel targetLabel;
public Expression[] constantExpressions; // case with multiple expressions - if you want a under-the-hood view, use peeledLabelExpressions()
Expand All @@ -60,65 +75,36 @@ public CaseStatement(Expression[] constantExpressions, int sourceStart, int sour
public Expression [] peeledLabelExpressions() {
Expression [] constants = Expression.NO_EXPRESSIONS;
for (Expression e : this.constantExpressions) {
if (e instanceof Pattern p1) {
constants = Stream.concat(Arrays.stream(constants), Arrays.stream(p1.getAlternatives())).toArray(Expression[]::new);
} else {
if (e instanceof Pattern p)
constants = Stream.concat(Arrays.stream(constants), Arrays.stream(p.getAlternatives())).toArray(Expression[]::new);
else
constants = Stream.concat(Arrays.stream(constants), Stream.of(e)).toArray(Expression[]::new);
}
}
return constants;
}
@Override
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {

int nullPatternCount = 0;
for (int i = 0, length = this.constantExpressions.length; i < length; i++) {
Expression e = this.constantExpressions[i];
CompilerOptions compilerOptions = currentScope.compilerOptions();
long sourceLevel = compilerOptions.sourceLevel;
boolean enablePreviewFeatures = compilerOptions.enablePreviewFeatures;
if (!JavaFeature.UNNAMMED_PATTERNS_AND_VARS.isSupported(sourceLevel, enablePreviewFeatures)) {
for (LocalVariableBinding local : e.bindingsWhenTrue()) {
local.useFlag = LocalVariableBinding.USED; // these are structurally required even if not touched
}
}
nullPatternCount += e instanceof NullLiteral ? 1 : 0;
if (i > 0 && (e instanceof Pattern) && !JavaFeature.UNNAMMED_PATTERNS_AND_VARS.isSupported(sourceLevel, enablePreviewFeatures)) {
if (!(i == nullPatternCount && e instanceof TypePattern))
currentScope.problemReporter().IllegalFallThroughToPattern(e);
}
flowInfo = analyseConstantExpression(currentScope, flowContext, flowInfo, e);
if (nullPatternCount > 0 && e instanceof TypePattern) {
LocalVariableBinding binding = ((TypePattern) e).local.binding;
if (binding != null)
flowInfo.markNullStatus(binding, FlowInfo.POTENTIALLY_NULL);
}
if (!JavaFeature.UNNAMMED_PATTERNS_AND_VARS.isSupported(currentScope.compilerOptions())) {
for (LocalVariableBinding local : bindingsWhenTrue())
local.useFlag = LocalVariableBinding.USED; // these are structurally required even if not touched
}

return flowInfo;
}
private FlowInfo analyseConstantExpression(
BlockScope currentScope,
FlowContext flowContext,
FlowInfo flowInfo,
Expression e) {
if (e.constant == Constant.NotAConstant
&& !e.resolvedType.isEnum()) {
boolean caseNullorDefaultAllowed =
JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(currentScope.compilerOptions())
&& (e instanceof NullLiteral || e instanceof FakeDefaultLiteral || e instanceof Pattern);
if (!caseNullorDefaultAllowed)
currentScope.problemReporter().caseExpressionMustBeConstant(e);
if (e instanceof NullLiteral && flowContext.associatedNode instanceof SwitchStatement) {
Expression switchValue = ((SwitchStatement) flowContext.associatedNode).expression;
if (switchValue != null && switchValue.nullStatus(flowInfo, flowContext) == FlowInfo.NON_NULL) {
currentScope.problemReporter().unnecessaryNullCaseInSwitchOverNonNull(this);
for (Expression e : this.constantExpressions) {
if (e.constant == Constant.NotAConstant && !e.resolvedType.isEnum()) {
if (!(e instanceof NullLiteral) && !(e instanceof FakeDefaultLiteral) && !(e instanceof Pattern))
currentScope.problemReporter().caseExpressionMustBeConstant(e);

if (e instanceof NullLiteral && flowContext.associatedNode instanceof SwitchStatement swichStatement) {
Expression switchValue = swichStatement.expression;
if (switchValue != null && switchValue.nullStatus(flowInfo, flowContext) == FlowInfo.NON_NULL)
currentScope.problemReporter().unnecessaryNullCaseInSwitchOverNonNull(this);
}
}
flowInfo = e.analyseCode(currentScope, flowContext, flowInfo);
}
return e.analyseCode(currentScope, flowContext, flowInfo);
return flowInfo;
}

/**
* No-op : should use resolveCase(...) instead.
*/
Expand Down Expand Up @@ -162,13 +148,7 @@ public boolean isQualifiedEnum() {
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("case "); //$NON-NLS-1$
builder.append(this.e);
builder.append(" [CONSTANT="); //$NON-NLS-1$
builder.append(this.c);
builder.append("]"); //$NON-NLS-1$
return builder.toString();
return "case " + this.e + " [CONSTANT=" + this.c + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}

Expand All @@ -177,24 +157,22 @@ public String toString() {
*/
public ResolvedCase[] resolveCase(BlockScope scope, TypeBinding switchExpressionType, SwitchStatement switchStatement) {
this.swich = switchStatement;
switchStatement.cases[switchStatement.caseCount++] = this;

if (this.isSwitchRule)
this.swich.switchBits |= SwitchStatement.LabeledRules;
else
this.swich.switchBits |= SwitchStatement.LabeledBlockStatementGroup;

if ((this.swich.switchBits & (SwitchStatement.LabeledRules | SwitchStatement.LabeledBlockStatementGroup)) == (SwitchStatement.LabeledRules | SwitchStatement.LabeledBlockStatementGroup)) {
if ((this.swich.switchBits & (SwitchStatement.LabeledRules | SwitchStatement.LabeledBlockStatementGroup)) == (SwitchStatement.LabeledRules | SwitchStatement.LabeledBlockStatementGroup))
scope.problemReporter().arrowColonMixup(this);
}

scope.enclosingCase = this; // record entering in a switch case block
if (this.constantExpressions == Expression.NO_EXPRESSIONS) {
checkDuplicateDefault(scope, switchStatement, this);
return ResolvedCase.UnresolvedCase;
}

switchStatement.cases[switchStatement.caseCount++] = this;

List<ResolvedCase> cases = new ArrayList<>();
int count = 0;
int nullCaseCount = 0;
Expand Down Expand Up @@ -228,7 +206,6 @@ public ResolvedCase[] resolveCase(BlockScope scope, TypeBinding switchExpression
}

TypeBinding caseType = e.resolveType(scope);

if (caseType == null || switchExpressionType == null) {
hasResolveErrors = true;
continue;
Expand All @@ -249,10 +226,7 @@ public ResolvedCase[] resolveCase(BlockScope scope, TypeBinding switchExpression
// - [...]
// - if T is one of long, float, double, or boolean, the type of the case constant is T.
// - if T is one of Long, Float, Double, or Boolean, the type of the case constant is, respectively, long, float, double, or boolean.
TypeBinding expectedCaseType = switchExpressionType;
if (switchExpressionType.isBoxedPrimitiveType()) {
expectedCaseType = scope.environment().computeBoxingType(switchExpressionType); // in this case it's actually 'computeUnboxingType()'
}
TypeBinding expectedCaseType = switchExpressionType.isBoxedPrimitiveType() ? switchExpressionType.unboxedType() : switchExpressionType;
switch (expectedCaseType.id) {
case TypeIds.T_long, TypeIds.T_float, TypeIds.T_double, TypeIds.T_boolean -> {
if (caseType.id != expectedCaseType.id) {
Expand All @@ -262,7 +236,6 @@ public ResolvedCase[] resolveCase(BlockScope scope, TypeBinding switchExpression
switchExpressionType = expectedCaseType;
}
}
//
Constant con = resolveConstantExpression(scope, caseType, switchExpressionType, switchStatement, e, cases);
if (con != Constant.NotAConstant) {
int index = this == switchStatement.nullCase && e instanceof NullLiteral ?
Expand All @@ -275,63 +248,42 @@ public ResolvedCase[] resolveCase(BlockScope scope, TypeBinding switchExpression
return hasResolveErrors ? ResolvedCase.UnresolvedCase : cases.toArray(new ResolvedCase[cases.size()]);
}

/**
* Precondition: this is a default case.
* Check if (a) another default or (b) a total type pattern has been recorded already
*/
private void checkDuplicateDefault(BlockScope scope, SwitchStatement switchStatement, ASTNode node) {
if (switchStatement.defaultCase != null) {
if (switchStatement.defaultCase != null)
scope.problemReporter().duplicateDefaultCase(node);
} else if ((switchStatement.switchBits & SwitchStatement.TotalPattern) != 0) {
else if ((switchStatement.switchBits & SwitchStatement.TotalPattern) != 0)
scope.problemReporter().illegalTotalPatternWithDefault(this);
}

// remember the default case into the associated switch statement
// on error the last default will be the selected one ...
switchStatement.defaultCase = this;
}

@Override
public LocalVariableBinding[] bindingsWhenTrue() {
LocalVariableBinding [] variables = NO_VARIABLES;
for (Expression e : this.constantExpressions) {
for (Expression e : this.constantExpressions)
variables = LocalVariableBinding.merge(variables, e.bindingsWhenTrue());
}
return variables;
}

public Constant resolveConstantExpression(BlockScope scope,
TypeBinding caseType,
TypeBinding switchType,
SwitchStatement switchStatement,
Expression expression,
List<ResolvedCase> cases) {
public Constant resolveConstantExpression(BlockScope scope, TypeBinding caseType, TypeBinding switchType, SwitchStatement switchStatement, Expression expression, List<ResolvedCase> cases) {

if (expression instanceof NullLiteral) {
if (!caseType.isCompatibleWith(switchType, scope))
scope.problemReporter().caseConstantIncompatible(TypeBinding.NULL, switchType, expression);
switchStatement.switchBits |= SwitchStatement.NullCase;
return IntConstant.fromValue(-1);
}

CompilerOptions options = scope.compilerOptions();
boolean patternSwitchAllowed = JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(options);
if (patternSwitchAllowed) {
if (expression instanceof Pattern) {
throw new AssertionError("Unexpected control flow"); //$NON-NLS-1$
} else if (expression instanceof NullLiteral) {
if (!caseType.isCompatibleWith(switchType, scope)) {
scope.problemReporter().caseConstantIncompatible(TypeBinding.NULL, switchType, expression);
}
switchStatement.switchBits |= SwitchStatement.NullCase;
return IntConstant.fromValue(-1);
} else if (expression instanceof FakeDefaultLiteral) {
// do nothing
} else {
if (switchStatement.isNonTraditional) {
if (switchType.isBaseType() && !expression.isConstantValueOfTypeAssignableToType(caseType, switchType)) {
scope.problemReporter().caseConstantIncompatible(caseType, switchType, expression);
return Constant.NotAConstant;
}
if (switchStatement.isNonTraditional) {
if (switchType.isBaseType() && !expression.isConstantValueOfTypeAssignableToType(caseType, switchType)) {
scope.problemReporter().caseConstantIncompatible(caseType, switchType, expression);
return Constant.NotAConstant;
}

}
}
}
boolean boxing = !patternSwitchAllowed ||
switchStatement.isAllowedType(switchType);
boolean boxing = !patternSwitchAllowed || switchStatement.isAllowedType(switchType);

if (expression.isConstantValueOfTypeAssignableToType(caseType, switchType)
||(caseType.isCompatibleWith(switchType)
Expand Down Expand Up @@ -386,14 +338,11 @@ private Constant resolveCasePattern(BlockScope scope, TypeBinding caseType, Type
switchStatement.caseLabelElementTypes.add(type);

TypeBinding expressionType = switchStatement.expression.resolvedType;
// The following code is copied from InstanceOfExpression#resolve()
// But there are enough differences to warrant a copy
if (!type.isReifiable()) {
if (!e.isApplicable(switchExpressionType, scope, e)) {
return Constant.NotAConstant;
}
} else if (type.isValidBinding()) {
// if not a valid binding, an error has already been reported for unresolved type
} else if (type.isValidBinding()) { // already complained if invalid
if (Pattern.findPrimitiveConversionRoute(type, expressionType, scope) == PrimitiveConversionRoute.NO_CONVERSION_ROUTE) {
if (type.isPrimitiveType() && !JavaFeature.PRIMITIVES_IN_PATTERNS.isSupported(scope.compilerOptions())) {
scope.problemReporter().unexpectedTypeinSwitchPattern(type, e);
Expand All @@ -420,14 +369,13 @@ private Constant resolveCasePattern(BlockScope scope, TypeBinding caseType, Type

@Override
public void generateCode(BlockScope currentScope, CodeStream codeStream) {
if ((this.bits & ASTNode.IsReachable) == 0) {
if ((this.bits & ASTNode.IsReachable) == 0)
return;
}
int pc = codeStream.position;
// check default behavior here
if (this.targetLabels != null) {
for (BranchLabel label : this.targetLabels) {
for (BranchLabel label : this.targetLabels)
label.place();
}
}
if (this.targetLabel != null)
this.targetLabel.place();
Expand Down Expand Up @@ -458,35 +406,32 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream) {
}
patternMatchLabel.place();
} else {
if (this.swich.containsNull) {
if (this.swich.containsNull)
this.swich.nullProcessed |= true;
}
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
}

@Override
public StringBuilder printStatement(int tab, StringBuilder output) {
printIndent(tab, output);
if (this.constantExpressions == Expression.NO_EXPRESSIONS) {
if (this.constantExpressions == Expression.NO_EXPRESSIONS)
output.append("default"); //$NON-NLS-1$
} else {
else {
output.append("case "); //$NON-NLS-1$
for (int i = 0, l = this.constantExpressions.length; i < l; ++i) {
for (int i = 0, length = this.constantExpressions.length; i < length; ++i) {
this.constantExpressions[i].printExpression(0, output);
if (i < l -1) output.append(',');
if (i < length -1) output.append(',');
}
}
output.append(this.isSwitchRule ? " ->" : " :"); //$NON-NLS-1$ //$NON-NLS-2$
return output;
return output.append(this.isSwitchRule ? " ->" : " :"); //$NON-NLS-1$ //$NON-NLS-2$
}

@Override
public void traverse(ASTVisitor visitor, BlockScope blockScope) {
if (visitor.visit(this, blockScope)) {
for (Expression e : this.constantExpressions) {
for (Expression e : this.constantExpressions)
e.traverse(visitor, blockScope);
}
}
visitor.endVisit(this, blockScope);
}
Expand Down
Loading

0 comments on commit 1fff64f

Please sign in to comment.