From fb77e1f0bd3b3e31aeec2c867c87a891d0a0383f Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Tue, 21 May 2019 10:58:29 +0300 Subject: [PATCH] [NI] Fix recursion in contract declaration analysis. #KT-30410 Fixed --- .../kotlin/resolve/RecursiveContractHelper.kt | 37 +++++++++++++++++++ .../kotlin/resolve/calls/CallCompleter.kt | 14 +------ .../tower/KotlinResolutionCallbacksImpl.kt | 6 +++ .../calls/components/ExternalComponents.kt | 2 + .../calls/components/KotlinCallCompleter.kt | 5 ++- 5 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/RecursiveContractHelper.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/RecursiveContractHelper.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/RecursiveContractHelper.kt new file mode 100644 index 00000000000..513c69889e2 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/RecursiveContractHelper.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve + +import org.jetbrains.kotlin.contracts.description.ContractProviderKey +import org.jetbrains.kotlin.contracts.description.LazyContractProvider +import org.jetbrains.kotlin.contracts.parsing.isContractCallDescriptor +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.psi.Call +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.psiUtil.isContractDescriptionCallPsiCheck +import org.jetbrains.kotlin.psi.psiUtil.isFirstStatement +import org.jetbrains.kotlin.resolve.scopes.LexicalScope +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +/* + * See KT-26386 and KT-30410 + */ +fun disableContractsInsideContractsBlock(call: Call, descriptor: CallableDescriptor?, scope: LexicalScope, trace: BindingTrace) { + call.callElement.safeAs()?.let { callExpression -> + if (callExpression.isFirstStatement() && callExpression.isContractDescriptionCallPsiCheck()) { + if (descriptor?.isContractCallDescriptor() != true) { + scope.ownerDescriptor + .safeAs() + ?.getUserData(ContractProviderKey) + ?.safeAs() + ?.setContractDescription(null) + } else { + trace.record(BindingContext.IS_CONTRACT_DECLARATION_BLOCK, callExpression, true) + } + } + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt index b5d3e248e88..82f538517d5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt @@ -135,19 +135,7 @@ class CallCompleter( context: BasicCallResolutionContext, tracing: TracingStrategy ) { - context.call.callElement.safeAs()?.let { callExpression -> - if (callExpression.isFirstStatement() && callExpression.isContractDescriptionCallPsiCheck()) { - if (resolvedCall?.resultingDescriptor?.isContractCallDescriptor() != true) { - context.scope.ownerDescriptor - .safeAs() - ?.getUserData(ContractProviderKey) - ?.safeAs() - ?.setContractDescription(null) - } else { - context.trace.record(BindingContext.IS_CONTRACT_DECLARATION_BLOCK, callExpression, true) - } - } - } + disableContractsInsideContractsBlock(context.call, resolvedCall?.resultingDescriptor, context.scope, context.trace) if (resolvedCall == null || resolvedCall.isCompleted || resolvedCall.constraintSystem == null) { completeArguments(context, results) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt index 15b14e31454..049044d0ba9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt @@ -265,4 +265,10 @@ class KotlinResolutionCallbacksImpl( trace.record(BindingContext.CAST_TYPE_USED_AS_EXPECTED_TYPE, binaryParent) return resultType } + + override fun disableContractsIfNecessary(resolvedAtom: ResolvedCallAtom) { + val atom = resolvedAtom.atom as? PSIKotlinCall ?: return + val context = topLevelCallContext ?: return + disableContractsInsideContractsBlock(atom.psiCall, resolvedAtom.candidateDescriptor, context.scope, trace) + } } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt index 72e7e3fb94b..5e477a10642 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt @@ -55,6 +55,8 @@ interface KotlinResolutionCallbacks { val inferenceSession: InferenceSession fun getExpectedTypeFromAsExpressionAndRecordItInTrace(resolvedAtom: ResolvedCallAtom): UnwrappedType? + + fun disableContractsIfNecessary(resolvedAtom: ResolvedCallAtom) } interface SamConversionTransformer { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt index 36ac1aab449..9fe2e4c0114 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt @@ -133,7 +133,10 @@ class KotlinCallCompleter( val candidate = candidates.singleOrNull() // this is needed at least for non-local return checker, because when we analyze lambda we should already bind descriptor for outer call - candidate?.resolvedCall?.let { resolutionCallbacks.bindStubResolvedCallForCandidate(it) } + candidate?.resolvedCall?.let { + resolutionCallbacks.bindStubResolvedCallForCandidate(it) + resolutionCallbacks.disableContractsIfNecessary(it) + } return candidate ?: factory.createErrorCandidate().forceResolution() }