From e418a763dbfc089a74bae6886dac3a7bbc8b7755 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Tue, 7 Oct 2014 14:05:55 +0400 Subject: [PATCH] Fix JetType.equals(): flexible types are not equal to non-flexible ones, when we store them in a HashSet --- .../BasicExpressionTypingVisitor.java | 7 ++++++- .../tests/inference/immutableArrayList.kt | 17 ++++++++++++++++ .../tests/inference/immutableArrayList.txt | 9 +++++++++ .../commonSupertype/typeOfElvis.kt | 20 +++++++++++++++++++ .../commonSupertype/typeOfElvis.txt | 4 ++++ .../checkers/JetDiagnosticsTestGenerated.java | 12 +++++++++++ .../jet/lang/resolve/DescriptorUtils.java | 7 ++++--- .../calls/inference/ConstraintSystemImpl.java | 3 ++- .../jet/lang/types/AbstractJetType.java | 2 +- .../jet/lang/types/DelegatingType.java | 7 ++++++- .../lang/types/checker/JetTypeChecker.java | 7 +++++++ .../types/checker/TypeCheckingProcedure.java | 2 +- 12 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inference/immutableArrayList.kt create mode 100644 compiler/testData/diagnostics/tests/inference/immutableArrayList.txt create mode 100644 compiler/testData/diagnostics/tests/platformTypes/commonSupertype/typeOfElvis.kt create mode 100644 compiler/testData/diagnostics/tests/platformTypes/commonSupertype/typeOfElvis.txt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index 9e7fd4261d3..491c5f7cfb1 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -1081,7 +1081,12 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { JetType type = resolvedCall.getResultingDescriptor().getReturnType(); if (type == null || rightType == null) return JetTypeInfo.create(null, dataFlowInfo); - return JetTypeInfo.create(TypeUtils.makeNullableAsSpecified(type, rightType.isNullable()), dataFlowInfo); + // Sometimes return type for special call for elvis operator might be nullable, + // but result is not nullable if the right type is not nullable + if (!TypeUtils.isNullableType(rightType) && TypeUtils.isNullableType(type)) { + type = TypeUtils.makeNotNullable(type); + } + return JetTypeInfo.create(type, dataFlowInfo); } @NotNull diff --git a/compiler/testData/diagnostics/tests/inference/immutableArrayList.kt b/compiler/testData/diagnostics/tests/inference/immutableArrayList.kt new file mode 100644 index 00000000000..db6c4843960 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/immutableArrayList.kt @@ -0,0 +1,17 @@ +// FILE: p/J.java +package p; + +public interface J { + public interface Super {} + public interface Sub extends Super {} +} + +// FILE: k.kt + +import p.J.* + +class Foo: Sub { + fun foo(): Super { + return Foo() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/immutableArrayList.txt b/compiler/testData/diagnostics/tests/inference/immutableArrayList.txt new file mode 100644 index 00000000000..e1c8bc22b31 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/immutableArrayList.txt @@ -0,0 +1,9 @@ +package + +internal final class Foo : p.J.Sub { + public constructor Foo() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final fun foo(): p.J.Super + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/platformTypes/commonSupertype/typeOfElvis.kt b/compiler/testData/diagnostics/tests/platformTypes/commonSupertype/typeOfElvis.kt new file mode 100644 index 00000000000..33ab0921e77 --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/commonSupertype/typeOfElvis.kt @@ -0,0 +1,20 @@ +// FILE: p/J.java + +package p; + +public class J { + public static J j() { return null; } +} + +// FILE: k.kt + +import p.* + +fun foo(): J? = null + +fun main(args: Array) { + val v = foo() ?: J.j() + if (v != null) { + + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/platformTypes/commonSupertype/typeOfElvis.txt b/compiler/testData/diagnostics/tests/platformTypes/commonSupertype/typeOfElvis.txt new file mode 100644 index 00000000000..62117a5a01a --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/commonSupertype/typeOfElvis.txt @@ -0,0 +1,4 @@ +package + +internal fun foo(): p.J? +internal fun main(/*0*/ args: kotlin.Array): kotlin.Unit \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 5352c710ce8..33bdbdc3df0 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -4865,6 +4865,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("immutableArrayList.kt") + public void testImmutableArrayList() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/immutableArrayList.kt"); + doTest(fileName); + } + @TestMetadata("inferInFunctionLiterals.kt") public void testInferInFunctionLiterals() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/inferInFunctionLiterals.kt"); @@ -7799,6 +7805,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("typeOfElvis.kt") + public void testTypeOfElvis() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/commonSupertype/typeOfElvis.kt"); + doTest(fileName); + } + } @TestMetadata("compiler/testData/diagnostics/tests/platformTypes/methodCall") diff --git a/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java index 34c4a397460..03de287ab8a 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java @@ -31,6 +31,7 @@ import org.jetbrains.jet.lang.types.ErrorUtils; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.LazyType; import org.jetbrains.jet.lang.types.TypeConstructor; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import java.util.ArrayList; @@ -397,9 +398,9 @@ public class DescriptorUtils { KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance(); return builtIns.isPrimitiveType(type) || - builtIns.getStringType().equals(type) || - builtIns.getNumber().getDefaultType().equals(type) || - builtIns.getAnyType().equals(type); + JetTypeChecker.DEFAULT.equalTypes(builtIns.getStringType(), type) || + JetTypeChecker.DEFAULT.equalTypes(builtIns.getNumber().getDefaultType(), type) || + JetTypeChecker.DEFAULT.equalTypes(builtIns.getAnyType(), type); } public static boolean classCanHaveAbstractMembers(@NotNull ClassDescriptor classDescriptor) { diff --git a/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java b/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java index 6fe21e61495..640b4e05b35 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor; import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import org.jetbrains.jet.lang.types.*; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure; import org.jetbrains.jet.lang.types.checker.TypingConstraints; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; @@ -388,7 +389,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { // can be equal for the recursive invocations: // fun foo(i: Int) : T { ... return foo(i); } => T <: T - if (subType.equals(superType)) return; + if (JetTypeChecker.DEFAULT.equalTypes(subType, superType)) return; assert !isMyTypeVariable(subType) || !isMyTypeVariable(superType) : "The constraint shouldn't contain different type variables on both sides: " + subType + " <: " + superType; diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/AbstractJetType.java b/core/descriptors/src/org/jetbrains/jet/lang/types/AbstractJetType.java index 7d7f7070d67..24a2c043361 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/AbstractJetType.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/AbstractJetType.java @@ -49,7 +49,7 @@ public abstract class AbstractJetType implements JetType { JetType type = (JetType) obj; - return isNullable() == type.isNullable() && JetTypeChecker.DEFAULT.equalTypes(this, type); + return isNullable() == type.isNullable() && JetTypeChecker.FLEXIBLE_UNEQUAL_TO_INFLEXIBLE.equalTypes(this, type); } @Override diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/DelegatingType.java b/core/descriptors/src/org/jetbrains/jet/lang/types/DelegatingType.java index 73382ef1968..053eab1e1c4 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/DelegatingType.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/DelegatingType.java @@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.annotations.Annotations; import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import java.util.List; @@ -77,7 +78,11 @@ public abstract class DelegatingType implements JetType { @Override public boolean equals(Object obj) { - return getDelegate().equals(obj); + if (this == obj) return true; + if (!(obj instanceof JetType)) return false; + + JetType type = (JetType) obj; + return JetTypeChecker.FLEXIBLE_UNEQUAL_TO_INFLEXIBLE.equalTypes(this, type); } @Override diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/checker/JetTypeChecker.java b/core/descriptors/src/org/jetbrains/jet/lang/types/checker/JetTypeChecker.java index aaa4cc0a3b9..e4b3b20cd6d 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/checker/JetTypeChecker.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/checker/JetTypeChecker.java @@ -28,6 +28,13 @@ public class JetTypeChecker { public static final JetTypeChecker DEFAULT = new JetTypeChecker(new TypeCheckingProcedure(new TypeCheckerTypingConstraints())); + public static final JetTypeChecker FLEXIBLE_UNEQUAL_TO_INFLEXIBLE = new JetTypeChecker(new TypeCheckingProcedure(new TypeCheckerTypingConstraints()) { + @Override + protected boolean heterogeneousEquivalence(JetType inflexibleType, JetType flexibleType) { + return false; + } + }); + @NotNull public static JetTypeChecker withAxioms(@NotNull final TypeConstructorEquality equalityAxioms) { return new JetTypeChecker(new TypeCheckingProcedure(new TypeCheckerTypingConstraints() { diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/checker/TypeCheckingProcedure.java b/core/descriptors/src/org/jetbrains/jet/lang/types/checker/TypeCheckingProcedure.java index c937aeea481..d211b868155 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/checker/TypeCheckingProcedure.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/checker/TypeCheckingProcedure.java @@ -118,7 +118,7 @@ public class TypeCheckingProcedure { return true; } - private boolean heterogeneousEquivalence(JetType inflexibleType, JetType flexibleType) { + protected boolean heterogeneousEquivalence(JetType inflexibleType, JetType flexibleType) { // This is to account for the case when we have Collection vs (Mutable)Collection! or K(java.util.Collection) assert !TypesPackage.isFlexible(inflexibleType) : "Only inflexible types are allowed here: " + inflexibleType; return isSubtypeOf(TypesPackage.flexibility(flexibleType).getLowerBound(), inflexibleType)