From 220c360081ff70fb6d9eddb57864ce7fc024078a Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Tue, 26 Aug 2014 16:07:23 +0400 Subject: [PATCH] Null-aware types introduced Flexible types should drive their own conversions to nullable/not-null --- .../commonSupertype/collectionOrNull.kt | 19 +++++++ .../inferenceWithBound.kt | 0 .../commonSupertype/stringOrNull.kt | 19 +++++++ .../checkers/JetDiagnosticsTestGenerated.java | 57 ++++++++++++++----- .../java/lazy/types/LazyJavaTypeResolver.kt | 16 +++++- .../java/sam/SingleAbstractMethodUtils.java | 3 +- .../jet/lang/types/CommonSupertypes.java | 2 +- .../jet/lang/types/TypeSubstitutor.java | 2 +- .../jetbrains/jet/lang/types/TypeUtils.java | 4 +- .../jetbrains/jet/lang/types/flexibleTypes.kt | 23 +++++++- 10 files changed, 122 insertions(+), 23 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/platformTypes/commonSupertype/collectionOrNull.kt rename compiler/testData/diagnostics/tests/platformTypes/{methodCall => commonSupertype}/inferenceWithBound.kt (100%) create mode 100644 compiler/testData/diagnostics/tests/platformTypes/commonSupertype/stringOrNull.kt diff --git a/compiler/testData/diagnostics/tests/platformTypes/commonSupertype/collectionOrNull.kt b/compiler/testData/diagnostics/tests/platformTypes/commonSupertype/collectionOrNull.kt new file mode 100644 index 00000000000..82966d7f8c0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/commonSupertype/collectionOrNull.kt @@ -0,0 +1,19 @@ +// FILE: p/Utils.java + +package p; + +public class Utils { + public static java.util.Collection c() { return null; } +} + +// FILE: k.kt + +import p.* + +fun T.foo() {} + +fun test(b: Boolean) { + val c = if (b) Utils.c() else null + + c?.foo() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/platformTypes/methodCall/inferenceWithBound.kt b/compiler/testData/diagnostics/tests/platformTypes/commonSupertype/inferenceWithBound.kt similarity index 100% rename from compiler/testData/diagnostics/tests/platformTypes/methodCall/inferenceWithBound.kt rename to compiler/testData/diagnostics/tests/platformTypes/commonSupertype/inferenceWithBound.kt diff --git a/compiler/testData/diagnostics/tests/platformTypes/commonSupertype/stringOrNull.kt b/compiler/testData/diagnostics/tests/platformTypes/commonSupertype/stringOrNull.kt new file mode 100644 index 00000000000..d7ec612e5a2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/commonSupertype/stringOrNull.kt @@ -0,0 +1,19 @@ +// FILE: p/Utils.java + +package p; + +public class Utils { + public static String str() { return null; } +} + +// FILE: k.kt + +import p.* + +fun T.foo() {} + +fun test(b: Boolean) { + val str = if (b) Utils.str() else null + + str?.foo() +} \ 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 7032c30df18..6a4883b3b2b 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -6157,7 +6157,8 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { @TestMetadata("GenericsInSupertypes.kt") public void testGenericsInSupertypes() throws Exception { - doTest("compiler/testData/diagnostics/tests/j+k/GenericsInSupertypes.kt"); + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/j+k/GenericsInSupertypes.kt"); + doTest(fileName); } @TestMetadata("inheritAbstractSamAdapter.kt") @@ -6168,7 +6169,8 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { @TestMetadata("InheritedGenericFunction.kt") public void testInheritedGenericFunction() throws Exception { - doTest("compiler/testData/diagnostics/tests/j+k/InheritedGenericFunction.kt"); + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/j+k/InheritedGenericFunction.kt"); + doTest(fileName); } @TestMetadata("innerNestedClassFromJava.kt") @@ -7698,7 +7700,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { @TestMetadata("compiler/testData/diagnostics/tests/platformTypes") @TestDataPath("$PROJECT_ROOT") - @InnerTestClasses({PlatformTypes.MethodCall.class}) + @InnerTestClasses({PlatformTypes.CommonSupertype.class, PlatformTypes.MethodCall.class}) @RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class) public static class PlatformTypes extends AbstractJetDiagnosticsTest { public void testAllFilesPresentInPlatformTypes() throws Exception { @@ -7719,7 +7721,8 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { @TestMetadata("getParentOfType.kt") public void testGetParentOfType() throws Exception { - doTest("compiler/testData/diagnostics/tests/platformTypes/getParentOfType.kt"); + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/getParentOfType.kt"); + doTest(fileName); } @TestMetadata("inference.kt") @@ -7728,15 +7731,44 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("override.kt") + public void testOverride() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/override.kt"); + doTest(fileName); + } + @TestMetadata("safeCall.kt") public void testSafeCall() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/safeCall.kt"); doTest(fileName); } - @TestMetadata("override.kt") - public void testOverride() throws Exception { - doTest("compiler/testData/diagnostics/tests/platformTypes/override.kt"); + @TestMetadata("compiler/testData/diagnostics/tests/platformTypes/commonSupertype") + @TestDataPath("$PROJECT_ROOT") + @RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class) + public static class CommonSupertype extends AbstractJetDiagnosticsTest { + public void testAllFilesPresentInCommonSupertype() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/commonSupertype"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("collectionOrNull.kt") + public void testCollectionOrNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/commonSupertype/collectionOrNull.kt"); + doTest(fileName); + } + + @TestMetadata("inferenceWithBound.kt") + public void testInferenceWithBound() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/commonSupertype/inferenceWithBound.kt"); + doTest(fileName); + } + + @TestMetadata("stringOrNull.kt") + public void testStringOrNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/commonSupertype/stringOrNull.kt"); + doTest(fileName); + } + } @TestMetadata("compiler/testData/diagnostics/tests/platformTypes/methodCall") @@ -7747,11 +7779,6 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/methodCall"), Pattern.compile("^(.+)\\.kt$"), true); } - @TestMetadata("inferenceWithBound.kt") - public void testInferenceWithBound() throws Exception { - doTest("compiler/testData/diagnostics/tests/platformTypes/methodCall/inferenceWithBound.kt"); - } - @TestMetadata("int.kt") public void testInt() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/methodCall/int.kt"); @@ -7802,7 +7829,8 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { @TestMetadata("singleton.kt") public void testSingleton() throws Exception { - doTest("compiler/testData/diagnostics/tests/platformTypes/methodCall/singleton.kt"); + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/methodCall/singleton.kt"); + doTest(fileName); } @TestMetadata("string.kt") @@ -7813,7 +7841,8 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { @TestMetadata("visitor.kt") public void testVisitor() throws Exception { - doTest("compiler/testData/diagnostics/tests/platformTypes/methodCall/visitor.kt"); + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/methodCall/visitor.kt"); + doTest(fileName); } } diff --git a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/types/LazyJavaTypeResolver.kt b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/types/LazyJavaTypeResolver.kt index 7781fd2e157..a719bf42b88 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/types/LazyJavaTypeResolver.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/types/LazyJavaTypeResolver.kt @@ -61,7 +61,7 @@ class LazyJavaTypeResolver( val jetType = JavaToKotlinClassMap.getInstance().mapPrimitiveKotlinClass("[" + javaComponentType.getCanonicalText()) if (jetType != null) { return if (PLATFORM_TYPES && attr.allowFlexible) - DelegatingFlexibleType(jetType, TypeUtils.makeNullable(jetType)) + FlexibleJavaClassifierType.create(jetType, TypeUtils.makeNullable(jetType)) else TypeUtils.makeNullableAsSpecified(jetType, !attr.isMarkedNotNull) } } @@ -72,7 +72,7 @@ class LazyJavaTypeResolver( val componentType = transformJavaType(javaComponentType, howArgumentTypeIsUsed.toAttributes(attr.allowFlexible)) val result = KotlinBuiltIns.getInstance().getArrayType(projectionKind, componentType) return if (PLATFORM_TYPES && attr.allowFlexible) - DelegatingFlexibleType( + FlexibleJavaClassifierType.create( KotlinBuiltIns.getInstance().getArrayType(INVARIANT, componentType), TypeUtils.makeNullable( KotlinBuiltIns.getInstance().getArrayType(OUT_VARIANCE, componentType) @@ -264,10 +264,20 @@ class LazyJavaTypeResolver( override fun isNullable(): Boolean = _nullable() } - private open class FlexibleJavaClassifierType( + public open class FlexibleJavaClassifierType protected ( lowerBound: JetType, upperBound: JetType ) : DelegatingFlexibleType(lowerBound, upperBound), CustomTypeVariable { + public class object { + public fun create(lowerBound: JetType, upperBound: JetType): JetType { + if (lowerBound == upperBound) return lowerBound + return FlexibleJavaClassifierType(lowerBound, upperBound) + } + } + + override fun create(lowerBound: JetType, upperBound: JetType): JetType { + return FlexibleJavaClassifierType.create(lowerBound, upperBound) + } override val isTypeVariable: Boolean = lowerBound.getConstructor() == upperBound.getConstructor() && lowerBound.getConstructor().getDeclarationDescriptor() is TypeParameterDescriptor diff --git a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/sam/SingleAbstractMethodUtils.java b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/sam/SingleAbstractMethodUtils.java index b4de0b638f0..49fd0f79e20 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/sam/SingleAbstractMethodUtils.java +++ b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/sam/SingleAbstractMethodUtils.java @@ -24,6 +24,7 @@ import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl; import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl; import org.jetbrains.jet.lang.resolve.java.JavaPackage; import org.jetbrains.jet.lang.resolve.java.descriptor.*; +import org.jetbrains.jet.lang.resolve.java.lazy.types.LazyJavaTypeResolver; import org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils; import org.jetbrains.jet.lang.resolve.java.structure.*; import org.jetbrains.jet.lang.resolve.name.FqName; @@ -101,7 +102,7 @@ public class SingleAbstractMethodUtils { if (fixedProjections == null) return null; if (JavaPackage.getPLATFORM_TYPES() && !isSamConstructor) { - return new DelegatingFlexibleType(fixedProjections, TypeUtils.makeNullable(fixedProjections)); + return LazyJavaTypeResolver.FlexibleJavaClassifierType.OBJECT$.create(fixedProjections, TypeUtils.makeNullable(fixedProjections)); } return TypeUtils.makeNullableAsSpecified(fixedProjections, !isSamConstructor && samType.isNullable()); diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/CommonSupertypes.java b/core/descriptors/src/org/jetbrains/jet/lang/types/CommonSupertypes.java index 30d326dc325..427f4baa5c1 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/CommonSupertypes.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/CommonSupertypes.java @@ -64,7 +64,7 @@ public class CommonSupertypes { } if (!hasFlexible) return commonSuperTypeForInflexible(types); - return new DelegatingFlexibleType(commonSuperTypeForInflexible(lower), commonSuperTypeForInflexible(upper)); + return DelegatingFlexibleType.OBJECT$.create(commonSuperTypeForInflexible(lower), commonSuperTypeForInflexible(upper)); } @NotNull diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java b/core/descriptors/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java index 3cdfc107673..2554d8ebf72 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java @@ -163,7 +163,7 @@ public class TypeSubstitutor { unsafeSubstitute(new TypeProjectionImpl(originalProjectionKind, flexibleType.getUpperBound()), recursionDepth + 1); // todo: projection kind is neglected return new TypeProjectionImpl(originalProjectionKind, - new DelegatingFlexibleType( + DelegatingFlexibleType.OBJECT$.create( substitutedLower.getType(), substitutedUpper.getType() ) diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java b/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java index a0ab218889d..51303cf8751 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java @@ -111,7 +111,9 @@ public class TypeUtils { @NotNull public static JetType makeNullableAsSpecified(@NotNull JetType type, boolean nullable) { - if (TypesPackage.isFlexible(type)) return type; + if (type instanceof NullAwareType) { + return ((NullAwareType) type).makeNullableAsSpecified(nullable); + } // Wrapping serves two purposes here // 1. It's requires less memory than copying with a changed nullability flag: a copy has many fields, while a wrapper has only one diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/flexibleTypes.kt b/core/descriptors/src/org/jetbrains/jet/lang/types/flexibleTypes.kt index 21f85007c81..5d957cebc23 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/flexibleTypes.kt +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/flexibleTypes.kt @@ -18,6 +18,10 @@ package org.jetbrains.jet.lang.types import org.jetbrains.jet.lang.types.checker.JetTypeChecker +public trait NullAwareType { + public fun makeNullableAsSpecified(nullable: Boolean): JetType +} + public trait FlexibleType : JetType { public val lowerBound: JetType public val upperBound: JetType @@ -27,19 +31,34 @@ public fun JetType.isFlexible(): Boolean = this is FlexibleType public fun JetType.lowerIfFlexible(): JetType = if (this is FlexibleType) lowerBound else this -public open class DelegatingFlexibleType( +public open class DelegatingFlexibleType protected ( override val lowerBound: JetType, override val upperBound: JetType -) : DelegatingType(), FlexibleType { +) : DelegatingType(), FlexibleType, NullAwareType { + class object { + public fun create(lowerBound: JetType, upperBound: JetType): JetType { + if (lowerBound == upperBound) return lowerBound + return DelegatingFlexibleType(lowerBound, upperBound) + } + } { assert (!lowerBound.isFlexible()) { "Lower bound of a flexible type can not be flexible: $lowerBound" } assert (!upperBound.isFlexible()) { "Upper bound of a flexible type can not be flexible: $upperBound" } + assert (lowerBound != upperBound) { "Lower and upper bounds are equal: $lowerBound == $upperBound" } assert (JetTypeChecker.DEFAULT.isSubtypeOf(lowerBound, upperBound)) { "Lower bound $lowerBound of a flexible type must be a subtype of the upper bound $upperBound" } } + protected open fun create(lowerBound: JetType, upperBound: JetType): JetType { + return DelegatingFlexibleType.create(lowerBound, upperBound) + } + + override fun makeNullableAsSpecified(nullable: Boolean): JetType { + return create(TypeUtils.makeNullableAsSpecified(lowerBound, nullable), TypeUtils.makeNullableAsSpecified(upperBound, nullable)) + } + override fun getDelegate() = lowerBound override fun toString() = "('$lowerBound'..'$upperBound')"