Skip to content

Commit

Permalink
[23] exhaustiveness signalling difference between javac and ecj
Browse files Browse the repository at this point in the history
+ precise implementation of TypePattern.isUnconditional()
+ defer setting SwitchStatement.totalPattern until we know if a
  default case is present
  (otherwise we would generated inconsistent code for default).

fixes eclipse-jdt#2915
  • Loading branch information
stephan-herrmann committed Sep 7, 2024
1 parent 9706123 commit 5892326
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,6 @@ private Constant resolveCasePattern(BlockScope scope, TypeBinding caseType, Type
switchStatement.switchBits |= SwitchStatement.TotalPattern;
if (switchStatement.defaultCase != null && !(e instanceof RecordPattern))
scope.problemReporter().illegalTotalPatternWithDefault(this);
switchStatement.totalPattern = e;
}
e.isTotalTypeNode = true;
if (switchStatement.nullCase == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,6 @@ public boolean matchFailurePossible() {
return !isUnguarded() || this.primaryPattern.matchFailurePossible();
}

@Override
public boolean isUnconditional(TypeBinding t, Scope scope) {
return isUnguarded() && this.primaryPattern.isUnconditional(t, scope);
}

@Override
public boolean isUnguarded() {
Constant cst = this.condition.optimizedBooleanConstant();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public boolean matchFailurePossible() {
}

public boolean isUnconditional(TypeBinding t, Scope scope) {
return isUnguarded() && coversType(t, scope);
return false;
}

public abstract void generateCode(BlockScope currentScope, CodeStream codeStream, BranchLabel patternMatchLabel, BranchLabel matchFailLabel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,6 @@ public boolean matchFailurePossible() {
return this.patterns.length != 0; // if no deconstruction is involved, no failure is possible.
}

@Override
public boolean isUnconditional(TypeBinding t, Scope scope) {
return false;
}

@Override
public boolean dominates(Pattern p) {
/* 14.30.3: A record pattern with type R and pattern list L dominates another record pattern
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,7 @@ public void resolve(BlockScope upperScope) {
LocalVariableBinding[] patternVariables = NO_VARIABLES;
boolean caseNullDefaultFound = false;
boolean defaultFound = false;
Pattern aTotalPattern = null;
for (int i = 0; i < length; i++) {
ResolvedCase[] constantsList;
final Statement statement = this.statements[i];
Expand All @@ -1197,6 +1198,8 @@ public void resolve(BlockScope upperScope) {
constantsList = caseStmt.resolveCase(this.scope, expressionType, this);
patternVariables = statement.bindingsWhenTrue();
for (ResolvedCase c : constantsList) {
if (c.e instanceof Pattern p && p.isTotalTypeNode && p.isUnguarded)
aTotalPattern = p;
Constant con = c.c;
if (con == Constant.NotAConstant)
continue;
Expand Down Expand Up @@ -1272,6 +1275,9 @@ public void resolve(BlockScope upperScope) {
patternVariables = LocalVariableBinding.merge(patternVariables, statement.bindingsWhenComplete());
}
}
if (!defaultFound && aTotalPattern != null) {
this.totalPattern = aTotalPattern;
}
if (expressionType != null
&& (expressionType.id == TypeIds.T_boolean || expressionType.id == TypeIds.T_JavaLangBoolean)
&& defaultFound && isExhaustive()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.impl.JavaFeature;
import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.RecordComponentBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
Expand Down Expand Up @@ -177,6 +179,37 @@ public void generateTestingConversion(BlockScope scope, CodeStream codeStream) {
break;
}
}

@Override
public boolean isUnconditional(TypeBinding t, Scope scope) {
// §14.30.3: A type pattern that declares a pattern variable of a type S is unconditional for a type T
// if there is a testing conversion that is unconditionally exact (5.7.2) from |T| to |S|.
// §5.7.2 lists:
// * an identity conversion
// * an exact widening primitive conversion
// * a widening reference conversion
// * a boxing conversion
// * a boxing conversion followed by a widening reference conversion
if (TypeBinding.equalsEquals(t, this.resolvedType))
return true;
PrimitiveConversionRoute route = findPrimitiveConversionRoute(this.resolvedType, t, scope);
return switch(route) {
case IDENTITY_CONVERSION,
BOXING_CONVERSION,
BOXING_CONVERSION_AND_WIDENING_REFERENCE_CONVERSION
-> true;
case WIDENING_PRIMITIVE_CONVERSION -> BaseTypeBinding.isExactWidening(this.resolvedType.id, t.id);
case NO_CONVERSION_ROUTE -> { // a widening reference conversion?
if (!this.resolvedType.isPrimitiveOrBoxedPrimitiveType() || !t.isPrimitiveOrBoxedPrimitiveType()) {
yield t.isCompatibleWith(this.resolvedType);
} else {
yield false;
}
}
default -> false;
};
}

@Override
public boolean dominates(Pattern p) {
if (!isUnguarded())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2220,6 +2220,27 @@ public static void main(String... args) {

}

public void testCoversTypePlusDefault() {
runConformTest(new String[] {
"X.java",
"""
public class X {
public static int foo(Integer myInt) {
return switch (myInt) {
case int i -> i;
default -> 0;
};
}
public static void main(String argv[]) {
Integer i = 100;
System.out.println(X.foo(i) == i);
}
}
"""
},
"true");
}
// test from spec
public void _testSpec001() {
runConformTest(new String[] {
Expand Down

0 comments on commit 5892326

Please sign in to comment.