diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java index 76cc00545b6..6fae5a0dc73 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java @@ -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) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/GuardedPattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/GuardedPattern.java index a6424343045..79e2b421ec6 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/GuardedPattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/GuardedPattern.java @@ -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(); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Pattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Pattern.java index e441c7fbbad..bc40821370a 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Pattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Pattern.java @@ -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); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/RecordPattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/RecordPattern.java index 540f97dc0d0..94d32e561fb 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/RecordPattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/RecordPattern.java @@ -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 diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java index 7a6183feec6..24a8c834e70 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java @@ -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]; @@ -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; @@ -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()) { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java index 567199bbd2e..1c1720a2719 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java @@ -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; @@ -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()) diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java index 815bb6707cf..fad38fa1e6a 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java @@ -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[] {