diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt index 8fcf1ea903c..8192e1a631f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt @@ -73,7 +73,7 @@ class KotlinToResolvedCallTransformer( fun onlyTransform( resolvedCallAtom: ResolvedCallAtom - ): ResolvedCall = transformToResolvedCall(resolvedCallAtom, completed = false) + ): ResolvedCall = transformToResolvedCall(resolvedCallAtom, null) fun transformAndReport( baseResolvedCall: CallResolutionResult, @@ -103,7 +103,7 @@ class KotlinToResolvedCallTransformer( } fun createStubResolvedCallAndWriteItToTrace(candidate: ResolvedCallAtom, trace: BindingTrace): ResolvedCall { - val result = onlyTransform(candidate) + val result = transformToResolvedCall(candidate, trace) val psiKotlinCall = candidate.atom.psiKotlinCall val tracing = psiKotlinCall.safeAs()?.baseCall?.tracingStrategy ?: psiKotlinCall.tracingStrategy @@ -114,22 +114,38 @@ class KotlinToResolvedCallTransformer( fun transformToResolvedCall( completedCallAtom: ResolvedCallAtom, - completed: Boolean, - resultSubstitutor: NewTypeSubstitutor = FreshVariableNewTypeSubstitutor.Empty + trace: BindingTrace?, + resultSubstitutor: NewTypeSubstitutor? = null // if substitutor is not null, it means that this call is completed ): ResolvedCall { val psiKotlinCall = completedCallAtom.atom.psiKotlinCall return if (psiKotlinCall is PSIKotlinCallForInvoke) { @Suppress("UNCHECKED_CAST") NewVariableAsFunctionResolvedCallImpl( - NewResolvedCallImpl(psiKotlinCall.variableCall.resolvedCall, completed, resultSubstitutor), - NewResolvedCallImpl(completedCallAtom, completed, resultSubstitutor) + createOrGet(psiKotlinCall.variableCall.resolvedCall, trace, resultSubstitutor), + createOrGet(completedCallAtom, trace, resultSubstitutor) ) as ResolvedCall } else { - NewResolvedCallImpl(completedCallAtom, completed, resultSubstitutor) + createOrGet(completedCallAtom, trace, resultSubstitutor) } } + private fun createOrGet( + completedSimpleAtom: ResolvedCallAtom, + trace: BindingTrace?, + resultSubstitutor: NewTypeSubstitutor? + ): NewResolvedCallImpl { + if (trace != null) { + val storedResolvedCall = completedSimpleAtom.atom.psiKotlinCall.psiCall.getResolvedCall(trace.bindingContext)?. + safeAs>() + if (storedResolvedCall != null) { + storedResolvedCall.setResultingSubstitutor(resultSubstitutor) + return storedResolvedCall + } + } + return NewResolvedCallImpl(completedSimpleAtom, resultSubstitutor) + } + fun runCallCheckers(resolvedCall: ResolvedCall<*>, callCheckerContext: CallCheckerContext) { val calleeExpression = if (resolvedCall is VariableAsFunctionResolvedCall) resolvedCall.variableCall.call.calleeExpression @@ -329,12 +345,17 @@ sealed class NewAbstractResolvedCall(): ResolvedCall abstract val argumentMappingByOriginal: Map abstract val kotlinCall: KotlinCall - private var argumentToParameterMap: Map? = null - private val _valueArguments: Map by lazy { createValueArguments() } + protected var argumentToParameterMap: Map? = null + protected var _valueArguments: Map? = null override fun getCall(): Call = kotlinCall.psiKotlinCall.psiCall - override fun getValueArguments(): Map = _valueArguments + override fun getValueArguments(): Map { + if (_valueArguments == null) { + _valueArguments = createValueArguments() + } + return _valueArguments!! + } override fun getValueArgumentsByIndex(): List? { val arguments = ArrayList(candidateDescriptor.valueParameters.size) @@ -412,28 +433,13 @@ sealed class NewAbstractResolvedCall(): ResolvedCall class NewResolvedCallImpl( val resolvedCallAtom: ResolvedCallAtom, - val completed: Boolean, - substitutor: NewTypeSubstitutor + substitutor: NewTypeSubstitutor? ): NewAbstractResolvedCall() { - private val resultingDescriptor = run { - val candidateDescriptor = resolvedCallAtom.candidateDescriptor - val containsCapturedTypes = resolvedCallAtom.candidateDescriptor.returnType?.contains { it is NewCapturedType } ?: false + var isCompleted = false + private set + private lateinit var resultingDescriptor: D - when { - candidateDescriptor is FunctionDescriptor || - (candidateDescriptor is PropertyDescriptor && (candidateDescriptor.typeParameters.isNotEmpty() || containsCapturedTypes)) -> - // this code is very suspicious. Now it is very useful for BE, because they cannot do nothing with captured types, - // but it seems like temporary solution. - candidateDescriptor.substitute(resolvedCallAtom.substitutor).substituteAndApproximateCapturedTypes(substitutor) - else -> - candidateDescriptor - } - } - - val typeArguments = resolvedCallAtom.substitutor.freshVariables.map { - val substituted = substitutor.safeSubstitute(it.defaultType) - TypeApproximator().approximateToSuperType(substituted, TypeApproximatorConfiguration.CapturedTypesApproximation) ?: substituted - } + private lateinit var typeArguments: List private var extensionReceiver = resolvedCallAtom.extensionReceiverArgument?.receiver?.receiverValue private var smartCastDispatchReceiverType: KotlinType? = null @@ -446,7 +452,7 @@ class NewResolvedCallImpl( get() = resolvedCallAtom.argumentMappingByOriginal override fun getCandidateDescriptor(): D = resolvedCallAtom.candidateDescriptor as D - override fun getResultingDescriptor(): D = resultingDescriptor as D + override fun getResultingDescriptor(): D = resultingDescriptor override fun getExtensionReceiver(): ReceiverValue? = extensionReceiver override fun getDispatchReceiver(): ReceiverValue? = resolvedCallAtom.dispatchReceiverArgument?.receiver?.receiverValue override fun getExplicitReceiverKind(): ExplicitReceiverKind = resolvedCallAtom.explicitReceiverKind @@ -470,6 +476,41 @@ class NewResolvedCallImpl( fun setSmartCastDispatchReceiverType(smartCastDispatchReceiverType: KotlinType) { this.smartCastDispatchReceiverType = smartCastDispatchReceiverType } + + fun setResultingSubstitutor(substitutor: NewTypeSubstitutor?) { + //clear cached values + argumentToParameterMap = null + _valueArguments = null + if (substitutor != null) { + // todo: add asset that we do not complete call many times + isCompleted = true + } + + resultingDescriptor = run { + val candidateDescriptor = resolvedCallAtom.candidateDescriptor + val containsCapturedTypes = resolvedCallAtom.candidateDescriptor.returnType?.contains { it is NewCapturedType } ?: false + + when { + candidateDescriptor is FunctionDescriptor || + (candidateDescriptor is PropertyDescriptor && (candidateDescriptor.typeParameters.isNotEmpty() || containsCapturedTypes)) -> + // this code is very suspicious. Now it is very useful for BE, because they cannot do nothing with captured types, + // but it seems like temporary solution. + candidateDescriptor.substitute(resolvedCallAtom.substitutor).substituteAndApproximateCapturedTypes( + substitutor ?: FreshVariableNewTypeSubstitutor.Empty) + else -> + candidateDescriptor + } + } as D + + typeArguments = resolvedCallAtom.substitutor.freshVariables.map { + val substituted = (substitutor ?: FreshVariableNewTypeSubstitutor.Empty).safeSubstitute(it.defaultType) + TypeApproximator().approximateToSuperType(substituted, TypeApproximatorConfiguration.CapturedTypesApproximation) ?: substituted + } + } + + init { + setResultingSubstitutor(substitutor) + } } fun ResolutionCandidateApplicability.toResolutionStatus(): ResolutionStatus = when (this) { @@ -487,10 +528,10 @@ class NewVariableAsFunctionResolvedCallImpl( fun ResolvedCall<*>.isNewNotCompleted(): Boolean { if (this is NewVariableAsFunctionResolvedCallImpl) { - return !functionCall.completed + return !functionCall.isCompleted } if (this is NewResolvedCallImpl<*>) { - return !completed + return !isCompleted } return false } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt index eaca4710562..aa3a0df1ee3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt @@ -179,7 +179,7 @@ class PSICallResolver( if (result.type == CallResolutionResult.Type.ALL_CANDIDATES) { val resolvedCalls = result.allCandidates?.map { val resultingSubstitutor = it.getSystem().asReadOnlyStorage().buildResultingSubstitutor() - kotlinToResolvedCallTransformer.transformToResolvedCall(it.resolvedCall, false, resultingSubstitutor) + kotlinToResolvedCallTransformer.transformToResolvedCall(it.resolvedCall, null, resultingSubstitutor) } return AllCandidates(resolvedCalls ?: emptyList()) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt index d93077482a6..da28ba2831d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt @@ -74,7 +74,7 @@ class ResolvedAtomCompleter( fun completeResolvedCall(resolvedCallAtom: ResolvedCallAtom): ResolvedCall<*>? { if (resolvedCallAtom.atom.psiKotlinCall is PSIKotlinCallForVariable) return null - val resolvedCall = kotlinToResolvedCallTransformer.transformToResolvedCall(resolvedCallAtom, true, resultSubstitutor) + val resolvedCall = kotlinToResolvedCallTransformer.transformToResolvedCall(resolvedCallAtom, trace, resultSubstitutor) kotlinToResolvedCallTransformer.bindAndReport(topLevelCallContext, trace, resolvedCall) kotlinToResolvedCallTransformer.runCallCheckers(resolvedCall, callCheckerContext) diff --git a/compiler/testData/codegen/box/regressions/resolvedCallForGetOperator.kt b/compiler/testData/codegen/box/regressions/resolvedCallForGetOperator.kt new file mode 100644 index 00000000000..8b0e9bd07b0 --- /dev/null +++ b/compiler/testData/codegen/box/regressions/resolvedCallForGetOperator.kt @@ -0,0 +1,8 @@ +// WITH_RUNTIME + +val targetNameLists: Map = mapOf("1" to "OK") + +fun id(t: T) = t +fun foo(argumentName: String?): String? = id(targetNameLists[argumentName]) + +fun box() = foo("1") diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 35bf985d648..90fb86e4001 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -17078,6 +17078,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("resolvedCallForGetOperator.kt") + public void testResolvedCallForGetOperator() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/resolvedCallForGetOperator.kt"); + doTest(fileName); + } + @TestMetadata("supertypeDepth.kt") public void testSupertypeDepth() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/supertypeDepth.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index a902bdac98b..473fb9a9504 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -17078,6 +17078,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("resolvedCallForGetOperator.kt") + public void testResolvedCallForGetOperator() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/resolvedCallForGetOperator.kt"); + doTest(fileName); + } + @TestMetadata("supertypeDepth.kt") public void testSupertypeDepth() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/supertypeDepth.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 58fff15da44..f14e00a52c4 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -17078,6 +17078,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("resolvedCallForGetOperator.kt") + public void testResolvedCallForGetOperator() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/resolvedCallForGetOperator.kt"); + doTest(fileName); + } + @TestMetadata("supertypeDepth.kt") public void testSupertypeDepth() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/supertypeDepth.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index ce6eb2f3deb..e8a1c588b67 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -20870,6 +20870,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); } + @TestMetadata("resolvedCallForGetOperator.kt") + public void testResolvedCallForGetOperator() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/resolvedCallForGetOperator.kt"); + doTest(fileName); + } + @TestMetadata("supertypeDepth.kt") public void testSupertypeDepth() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/supertypeDepth.kt");