Skip to content

Commit

Permalink
Resolve component patterns after enclosing pattern type is resolved
Browse files Browse the repository at this point in the history
possibly involving record pattern type inference

* Apply missing capture conversion.

* Apply upward projection on the component type not on record type.

* Fix spurious code in RecordPattern.dominates

* Ignore parameterization when checking whether a class is a permitted
subclass of a sealed class

Fixes #2007
  • Loading branch information
srikanth-sankaran committed Mar 5, 2024
1 parent 85be6d2 commit 9e5215a
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,6 @@ public TypeBinding resolveType(BlockScope scope) {
return this.resolvedType = null;
}

LocalVariableBinding [] bindings = NO_VARIABLES;
for (Pattern p : this.patterns) {
p.resolveTypeWithBindings(bindings, scope);
bindings = LocalVariableBinding.merge(bindings, p.bindingsWhenTrue());
}

if (this.resolvedType.isRawType()) {
TypeBinding expressionType = expectedType();
if (expressionType instanceof ReferenceBinding) {
Expand All @@ -130,10 +124,16 @@ public TypeBinding resolveType(BlockScope scope) {
scope.problemReporter().cannotInferRecordPatternTypes(this);
return this.resolvedType = null;
}
this.resolvedType = binding;
this.resolvedType = binding.capture(scope, this.sourceStart, this.sourceEnd);
}
}

LocalVariableBinding [] bindings = NO_VARIABLES;
for (Pattern p : this.patterns) {
p.resolveTypeWithBindings(bindings, scope);
bindings = LocalVariableBinding.merge(bindings, p.bindingsWhenTrue());
}

if (this.resolvedType == null || !this.resolvedType.isValidBinding()) {
return this.resolvedType;
}
Expand Down Expand Up @@ -183,21 +183,32 @@ public boolean isAlwaysTrue() {

@Override
public boolean dominates(Pattern p) {
if (!this.resolvedType.isValidBinding())
/* 14.30.3: A record pattern with type R and pattern list L dominates another record pattern
with type S and pattern list M if (i) R and S name the same record class, and (ii)
every component pattern, if any, in L dominates the corresponding component
pattern in M.
*/
if (this.resolvedType == null || !this.resolvedType.isValidBinding() || p.resolvedType == null || !p.resolvedType.isValidBinding())
return false;
if (!super.coversType(p.resolvedType)) {

if (TypeBinding.notEquals(this.resolvedType.erasure(), p.resolvedType.erasure()))
return false;
}
if (p instanceof RecordPattern rp) {

if (!this.resolvedType.erasure().isRecord())
return false;

if (p instanceof RecordPattern) {
RecordPattern rp = (RecordPattern) p;
if (this.patterns.length != rp.patterns.length)
return false;
for(int i = 0; i < this.patterns.length; i++) {
for (int i = 0, length = this.patterns.length; i < length; i++) {
if (!this.patterns[i].dominates(rp.patterns[i])) {
return false;
}
}
return true;
}
return true;
return false;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1481,7 +1481,8 @@ private boolean isExhaustiveWithCaseTypes(List<ReferenceBinding> allAllowedTypes
continue;
}
for (TypeBinding type : listedTypes) {
if (pt.isCompatibleWith(type)) {
// permits specifies classes, not parameterizations
if (pt.erasure().isCompatibleWith(type.erasure())) {
--pendingTypes;
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
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;
Expand Down Expand Up @@ -116,13 +117,8 @@ public TypeBinding resolveType(BlockScope scope) {

Pattern enclosingPattern = this.getEnclosingPattern();
if (this.local.type == null || this.local.type.isTypeNameVar(scope)) {
/*
* If the LocalVariableType is var then the pattern variable must appear in a pattern list of a
* record pattern with type R. Let T be the type of the corresponding component field in R. The type
* of the pattern variable is the upward projection of T with respect to all synthetic type
* variables mentioned by T.
*/
if (enclosingPattern instanceof RecordPattern) {
// 14.30.1: The type of a pattern variable declared in a nested type pattern is determined as follows ...
ReferenceBinding recType = (ReferenceBinding) enclosingPattern.resolvedType;
if (recType != null) {
RecordComponentBinding[] components = recType.components();
Expand All @@ -131,15 +127,10 @@ public TypeBinding resolveType(BlockScope scope) {
if (rcb.type != null && (rcb.tagBits & TagBits.HasMissingType) != 0) {
scope.problemReporter().invalidType(this, rcb.type);
}
TypeVariableBinding[] mentionedTypeVariables = rcb.type != null ? rcb.type.syntheticTypeVariablesMentioned() : null;
if (mentionedTypeVariables != null && mentionedTypeVariables.length > 0) {
this.local.type.resolvedType = recType.upwardsProjection(scope,
mentionedTypeVariables);
} else {
if (this.local.type != null)
this.local.type.resolvedType = rcb.type;
this.resolvedType = rcb.type;
}
TypeVariableBinding[] mentionedTypeVariables = rcb.type != null ? rcb.type.syntheticTypeVariablesMentioned() : Binding.NO_TYPE_VARIABLES;
this.resolvedType = mentionedTypeVariables.length > 0 ? rcb.type.upwardsProjection(scope, mentionedTypeVariables) : rcb.type;
if (this.local.type != null)
this.local.type.resolvedType = this.resolvedType;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ public boolean visit(TypeVariableBinding typeVariable) {
return super.visit(typeVariable);
}
}, this);
if (mentioned.isEmpty()) return null;
if (mentioned.isEmpty()) return NO_TYPE_VARIABLES;
return mentioned.toArray(new TypeVariableBinding[mentioned.size()]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4468,4 +4468,88 @@ public static void main(String argv[]) {
"----------\n",
null, true, options);
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2007
public void testIssue2007() {
runConformTest(new String[] { "X.java", """
record R<T>(T t) {}
public class X<T> {
public boolean foo(R<T> r) {
return (r instanceof R<?>(X x));
}
public static void main(String argv[]) {
System.out.println(new X<>().foo(new R<>(new X())));
}
}
""" },
"true");
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2007
public void testIssue2007_2() {
runConformTest(new String[] { "X.java", """
record R<T>(T t) {}
public class X<T> {
public boolean foo(R<T> r) {
return (r instanceof R<? extends T>(X x));
}
public static void main(String argv[]) {
System.out.println(new X<>().foo(new R<>(new X())));
}
}
""" },
"true");
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2007
public void testIssue2007_3() {
runConformTest(new String[] { "X.java", """
record R<T>(T t) {}
public class X<T> {
public boolean foo(R<T> r) {
return switch (r) {
case R<?>(X x) -> true;
default -> false;
};
}
public static void main(String argv[]) {
System.out.println(new X<>().foo(new R<>(new X())));
}
}
""" },
"true");
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2007
public void testIssue2007_4() {
runConformTest(new String[] { "X.java", """
record R<T>(T t) {}
public class X<T> {
public boolean foo(R<T> r) {
return switch (r) {
case R<? extends T>(X x) -> true;
default -> false;
};
}
public static void main(String argv[]) {
System.out.println(new X<>().foo(new R<>(new X())));
}
}
""" },
"true");
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2007
public void testIssue2007_5() {
runConformTest(new String[] { "X.java", """
record R<T>(T t) {}
public class X<T> {
public boolean foo(R<T> r) {
return switch (r) {
case R<? extends T>(Integer i) -> true;
default -> false;
};
}
public static void main(String argv[]) {
System.out.println(new X<>().foo(new R<>(new X())));
}
}
""" },
"false");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1199,4 +1199,26 @@ public static void main(String argv[]) {
},
"true");
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2007
// [Patterns][Unnamed] VerifyError with unnamed pattern variable in instanceof
public void testIssue2007() {
runConformTest(new String[] {
"X.java",
"""
record I<J> (int x) {}
record O<T> (I<T> i) {}
public class X<T> {
public static void main(String argv[]) {
Object o = null;
if (o instanceof O(_)) {
System.out.println("Fail");
} else {
System.out.println("Pass");
}
}
}
"""
},
"Pass");
}
}

0 comments on commit 9e5215a

Please sign in to comment.