diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/MemoryOptimizationsTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/MemoryOptimizationsTest.kt index f851bdeadf7..6835a70bef6 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/MemoryOptimizationsTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/MemoryOptimizationsTest.kt @@ -20,14 +20,18 @@ 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.name.Name import org.jetbrains.kotlin.psi2ir.findFirstFunction +import org.jetbrains.kotlin.psi2ir.findSingleFunction 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.TypeConstructorSubstitution import org.jetbrains.kotlin.types.lowerIfFlexible +import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.types.upperIfFlexible import org.junit.Test @@ -54,4 +58,49 @@ class MemoryOptimizationsTest : KtUsefulTestCase() { // NullableSimpleType should store and return the same instance as lower bound of flexible type assertTrue(parameterType.lowerIfFlexible() === upperBound.makeNullableAsSpecified(false)) } + + + + @Test + fun testSubstitutorDoNotRecreateUnchangedDescriptor() { + val text = + """ + |package test + |interface A : java.lang.Appendable { + | fun foo(x: T) + |} + """.trimMargin() + + val environment = + KotlinTestUtils + .createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea( + myTestRootDisposable, ConfigurationKind.ALL, TestJdkKind.FULL_JDK + ) + val moduleDescriptor = + JvmResolveUtil.analyze( + KotlinTestUtils.createFile("main.kt", text, environment.project), + environment + ).moduleDescriptor + + val aClass = + moduleDescriptor.findClassAcrossModuleDependencies(ClassId.topLevel(FqName("test.A")))!! + + val memberScope = + aClass.getMemberScope( + TypeConstructorSubstitution.create( + aClass.typeConstructor, listOf(moduleDescriptor.builtIns.stringType.asTypeProjection()) + ) + ) + + val append = + memberScope.findFirstFunction("append") { + it.valueParameters.singleOrNull()?.type?.let(KotlinBuiltIns::isChar) == false + } + + assertTrue(append.original === append) + + val foo = memberScope.findSingleFunction(Name.identifier("foo")) + + assertTrue(foo.original !== foo) + } } diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/AbstractReceiverParameterDescriptor.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/AbstractReceiverParameterDescriptor.java index 5fc23f14821..a33ae7ea44d 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/AbstractReceiverParameterDescriptor.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/AbstractReceiverParameterDescriptor.java @@ -56,6 +56,7 @@ public abstract class AbstractReceiverParameterDescriptor extends DeclarationDes } if (substitutedType == null) return null; + if (substitutedType == getType()) return this; return new ReceiverParameterDescriptorImpl(getContainingDeclaration(), new TransientReceiver(substitutedType)); } diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/FunctionDescriptorImpl.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/FunctionDescriptorImpl.java index 603811a87d2..6978b3c113c 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/FunctionDescriptorImpl.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/FunctionDescriptorImpl.java @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.descriptors.impl; +import kotlin.collections.CollectionsKt; import kotlin.jvm.functions.Function0; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -334,7 +335,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo if (originalSubstitutor.isEmpty()) { return this; } - return newCopyBuilder(originalSubstitutor).setOriginal(getOriginal()).build(); + return newCopyBuilder(originalSubstitutor).setOriginal(getOriginal()).setJustForTypeSubstitution(true).build(); } @Nullable @@ -370,6 +371,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo private boolean isHiddenForResolutionEverywhereBesideSupercalls = isHiddenForResolutionEverywhereBesideSupercalls(); private Map, Object> userDataMap = new LinkedHashMap, Object>(); private Boolean newHasSynthesizedParameterNames = null; + protected boolean justForTypeSubstitution = false; public CopyConfiguration( @NotNull TypeSubstitution substitution, @@ -553,6 +555,12 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo public TypeSubstitution getSubstitution() { return substitution; } + + @NotNull + public CopyConfiguration setJustForTypeSubstitution(boolean value) { + justForTypeSubstitution = value; + return this; + } } @Override @@ -571,6 +579,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo @Nullable protected FunctionDescriptor doSubstitute(@NotNull CopyConfiguration configuration) { + boolean[] wereChanges = new boolean[1]; Annotations resultAnnotations = configuration.additionalAnnotations != null ? AnnotationsKt.composeAnnotations(getAnnotations(), configuration.additionalAnnotations) @@ -583,10 +592,12 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo List unsubstitutedTypeParameters = configuration.newTypeParameters == null ? getTypeParameters() : configuration.newTypeParameters; + wereChanges[0] |= !unsubstitutedTypeParameters.isEmpty(); + List substitutedTypeParameters = new ArrayList(unsubstitutedTypeParameters.size()); final TypeSubstitutor substitutor = DescriptorSubstitutor.substituteTypeParameters( - unsubstitutedTypeParameters, configuration.substitution, substitutedDescriptor, substitutedTypeParameters + unsubstitutedTypeParameters, configuration.substitution, substitutedDescriptor, substitutedTypeParameters, wereChanges ); KotlinType substitutedReceiverParameterType = null; @@ -595,6 +606,8 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo if (substitutedReceiverParameterType == null) { return null; } + + wereChanges[0] |= substitutedReceiverParameterType != configuration.newExtensionReceiverParameterType; } ReceiverParameterDescriptor substitutedExpectedThis = null; @@ -613,11 +626,13 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo if (substitutedExpectedThis == null) { return null; } + + wereChanges[0] |= substitutedExpectedThis != configuration.dispatchReceiverParameter; } List substitutedValueParameters = getSubstitutedValueParameters( substitutedDescriptor, configuration.newValueParameterDescriptors, substitutor, configuration.dropOriginalInContainingParts, - configuration.preserveSourceElement + configuration.preserveSourceElement, wereChanges ); if (substitutedValueParameters == null) { return null; @@ -628,6 +643,12 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo return null; } + wereChanges[0] |= substitutedReturnType != configuration.newReturnType; + + if (!wereChanges[0] && configuration.justForTypeSubstitution) { + return this; + } + substitutedDescriptor.initialize( substitutedReceiverParameterType, substitutedExpectedThis, @@ -682,7 +703,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo if (configuration.copyOverrides && !getOriginal().getOverriddenDescriptors().isEmpty()) { if (configuration.substitution.isEmpty()) { - Function0> overriddenFunctionsTask = lazyOverriddenFunctionsTask; + Function0> overriddenFunctionsTask = lazyOverriddenFunctionsTask; if (overriddenFunctionsTask != null) { substitutedDescriptor.lazyOverriddenFunctionsTask = overriddenFunctionsTask; } @@ -753,7 +774,8 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo @NotNull List unsubstitutedValueParameters, @NotNull TypeSubstitutor substitutor, boolean dropOriginal, - boolean preserveSourceElement + boolean preserveSourceElement, + @Nullable boolean[] wereChanges ) { List result = new ArrayList(unsubstitutedValueParameters.size()); for (ValueParameterDescriptor unsubstitutedValueParameter : unsubstitutedValueParameters) { @@ -763,6 +785,11 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo KotlinType substituteVarargElementType = varargElementType == null ? null : substitutor.substitute(varargElementType, Variance.IN_VARIANCE); if (substitutedType == null) return null; + if (substitutedType != unsubstitutedValueParameter.getType() || varargElementType != substituteVarargElementType) { + if (wereChanges != null) { + wereChanges[0] = true; + } + } result.add( new ValueParameterDescriptorImpl( substitutedDescriptor, diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PropertyDescriptorImpl.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PropertyDescriptorImpl.java index 96837e55dcd..d18c4934c3c 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PropertyDescriptorImpl.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PropertyDescriptorImpl.java @@ -385,7 +385,7 @@ public class PropertyDescriptorImpl extends VariableDescriptorWithInitializerImp if (newSetter != null) { List substitutedValueParameters = FunctionDescriptorImpl.getSubstitutedValueParameters( newSetter, setter.getValueParameters(), substitutor, /* dropOriginal = */ false, - false + false, null ); if (substitutedValueParameters == null) { // The setter is projected out, e.g. in this case: diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/TypeAliasConstructorDescriptor.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/TypeAliasConstructorDescriptor.kt index fd9b39985c1..34b19756edd 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/TypeAliasConstructorDescriptor.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/TypeAliasConstructorDescriptor.kt @@ -179,7 +179,8 @@ class TypeAliasConstructorDescriptorImpl private constructor( val valueParameters = FunctionDescriptorImpl.getSubstitutedValueParameters( - typeAliasConstructor, constructor.valueParameters, substitutorForUnderlyingClass, false, false + typeAliasConstructor, constructor.valueParameters, substitutorForUnderlyingClass, false, false, + null ) ?: return null val returnType = run { diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/DescriptorSubstitutor.java b/core/descriptors/src/org/jetbrains/kotlin/types/DescriptorSubstitutor.java index bee202cf7b5..2d8940785b1 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/DescriptorSubstitutor.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/DescriptorSubstitutor.java @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.types; import org.jetbrains.annotations.Mutable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.ReadOnly; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.SourceElement; @@ -38,6 +39,17 @@ public class DescriptorSubstitutor { @NotNull TypeSubstitution originalSubstitution, @NotNull DeclarationDescriptor newContainingDeclaration, @NotNull @Mutable List result + ) { + return substituteTypeParameters(typeParameters, originalSubstitution, newContainingDeclaration, result, null); + } + + @NotNull + public static TypeSubstitutor substituteTypeParameters( + @ReadOnly @NotNull List typeParameters, + @NotNull TypeSubstitution originalSubstitution, + @NotNull DeclarationDescriptor newContainingDeclaration, + @NotNull @Mutable List result, + @Nullable boolean[] wereChanges ) { Map mutableSubstitution = new HashMap(); @@ -69,6 +81,11 @@ public class DescriptorSubstitutor { for (KotlinType upperBound : descriptor.getUpperBounds()) { KotlinType substitutedBound = substitutor.substitute(upperBound, Variance.IN_VARIANCE); assert substitutedBound != null : "Upper bound failed to substitute: " + descriptor; + + if (substitutedBound != upperBound && wereChanges != null) { + wereChanges[0] = true; + } + substituted.addUpperBound(substitutedBound); } substituted.setInitialized();