diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/MemoryOptimizationsTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/MemoryOptimizationsTest.kt new file mode 100644 index 00000000000..f851bdeadf7 --- /dev/null +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/MemoryOptimizationsTest.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.jvm.compiler + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi2ir.findFirstFunction +import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil +import org.jetbrains.kotlin.test.ConfigurationKind +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.TestJdkKind +import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase +import org.jetbrains.kotlin.types.FlexibleType +import org.jetbrains.kotlin.types.lowerIfFlexible +import org.jetbrains.kotlin.types.upperIfFlexible +import org.junit.Test + +class MemoryOptimizationsTest : KtUsefulTestCase() { + @Test + fun testBasicFlexibleTypeCase() { + val moduleDescriptor = JvmResolveUtil.analyze( + KotlinTestUtils.createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(myTestRootDisposable, ConfigurationKind.ALL, TestJdkKind.FULL_JDK) + ).moduleDescriptor + + val appendableClass = + moduleDescriptor.findClassAcrossModuleDependencies(ClassId.topLevel(FqName("java.lang.Appendable")))!! + + val append = appendableClass + .unsubstitutedMemberScope + .findFirstFunction("append") { it.valueParameters.singleOrNull()?.type?.let(KotlinBuiltIns::isChar) == false } + + val parameterType = append.valueParameters.single().type + + assertTrue(parameterType is FlexibleType) + val upperBound = parameterType.upperIfFlexible() + + assertTrue(upperBound.javaClass.simpleName == "NullableSimpleType") + // NullableSimpleType should store and return the same instance as lower bound of flexible type + assertTrue(parameterType.lowerIfFlexible() === upperBound.makeNullableAsSpecified(false)) + } +} diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt index 01704a2e694..57fceca7a83 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt @@ -90,11 +90,11 @@ class JavaTypeResolver( return computeSimpleJavaClassifierType(javaType, attr) ?: errorType() } - fun computeBound(flexibility: JavaTypeFlexibility) = - computeSimpleJavaClassifierType(javaType, attr.withFlexibility(flexibility)) + fun computeBound(flexibility: JavaTypeFlexibility, lowerResult: SimpleType? = null) = + computeSimpleJavaClassifierType(javaType, attr.withFlexibility(flexibility), lowerResult) val lower = computeBound(FLEXIBLE_LOWER_BOUND) ?: return errorType() - val upper = computeBound(FLEXIBLE_UPPER_BOUND) ?: return errorType() + val upper = computeBound(FLEXIBLE_UPPER_BOUND, lowerResult = lower) ?: return errorType() return if (isRaw) { RawTypeImpl(lower, upper) @@ -104,12 +104,21 @@ class JavaTypeResolver( } } - private fun computeSimpleJavaClassifierType(javaType: JavaClassifierType, attr: JavaTypeAttributes): SimpleType? { - val annotations = LazyJavaAnnotations(c, javaType) + private fun computeSimpleJavaClassifierType( + javaType: JavaClassifierType, attr: JavaTypeAttributes, + lowerResult: SimpleType? = null + ): SimpleType? { + val annotations = + lowerResult?.annotations ?: LazyJavaAnnotations(c, javaType) val constructor = computeTypeConstructor(javaType, attr) ?: return null - val arguments = computeArguments(javaType, attr, constructor) val isNullable = attr.isNullable() + if (lowerResult?.constructor == constructor && !javaType.isRaw && isNullable) { + return lowerResult.makeNullableAsSpecified(true) + } + + val arguments = computeArguments(javaType, attr, constructor) + return KotlinTypeFactory.simpleType(annotations, constructor, arguments, isNullable) } @@ -340,4 +349,3 @@ internal fun TypeParameterDescriptor.getErasedUpperBound( return defaultValue() } - diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/KotlinTypeFactory.kt b/core/descriptors/src/org/jetbrains/kotlin/types/KotlinTypeFactory.kt index 6ea0ba6dedf..0d68f304b61 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/KotlinTypeFactory.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/KotlinTypeFactory.kt @@ -89,10 +89,18 @@ private class SimpleTypeImpl( override val memberScope: MemberScope ) : SimpleType() { override fun replaceAnnotations(newAnnotations: Annotations) = - SimpleTypeImpl(newAnnotations, constructor, arguments, isMarkedNullable, memberScope) + if (newAnnotations === annotations) + this + else + SimpleTypeImpl(newAnnotations, constructor, arguments, isMarkedNullable, memberScope) override fun makeNullableAsSpecified(newNullability: Boolean) = - SimpleTypeImpl(annotations, constructor, arguments, newNullability, memberScope) + if (newNullability == isMarkedNullable) + this + else if (newNullability) + NullableSimpleType(this) + else + SimpleTypeImpl(annotations, constructor, arguments, newNullability, memberScope) init { if (memberScope is ErrorUtils.ErrorScope) { @@ -100,3 +108,19 @@ private class SimpleTypeImpl( } } } + +private class NullableSimpleType(override val delegate: SimpleType) : DelegatingSimpleType() { + override val isMarkedNullable: Boolean + get() = true + + override fun replaceAnnotations(newAnnotations: Annotations) = + if (newAnnotations !== delegate.annotations) + NullableSimpleType(delegate.replaceAnnotations(newAnnotations)) + else + this + + override fun makeNullableAsSpecified(newNullability: Boolean): SimpleType { + if (newNullability) return this + return delegate.makeNullableAsSpecified(newNullability) + } +} diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt index da13e7a78d2..85780093df2 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt @@ -53,6 +53,9 @@ object StrictEqualityTypeChecker { ) { return false } + + if (a.arguments === b.arguments) return true + for (i in a.arguments.indices) { val aArg = a.arguments[i] val bArg = b.arguments[i] @@ -322,6 +325,9 @@ object NewKotlinTypeChecker : KotlinTypeChecker { capturedSubArguments: List, superType: SimpleType ): Boolean { + + if (capturedSubArguments === superType.arguments) return true + val parameters = superType.constructor.parameters for (index in parameters.indices) { @@ -433,4 +439,4 @@ val SimpleType.isSingleClassifierType: Boolean (constructor.declarationDescriptor != null || this is CapturedType || this is NewCapturedType) val SimpleType.isIntersectionType: Boolean - get() = constructor is IntersectionTypeConstructor \ No newline at end of file + get() = constructor is IntersectionTypeConstructor