From 99fe6de52a4205787d0c38ce0776212a0a35e4d2 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 10 Aug 2018 11:14:11 +0700 Subject: [PATCH] Avoid storing ContractProviderKey in user data of every function Whenever the key is absent it means that for some reason contract can't be defined there. But we still want to check them properly, thus checkContractAndRecordIfPresent should work even if contract is inapplicable here (no ContractProviderKey in owner's user data) --- .../contracts/parsing/ContractParsingServices.kt | 8 ++++---- .../kotlin/resolve/FunctionDescriptorResolver.kt | 16 +++++++++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/ContractParsingServices.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/ContractParsingServices.kt index ef96c35fe1d..6fba898c4b5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/ContractParsingServices.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/ContractParsingServices.kt @@ -23,7 +23,7 @@ import org.jetbrains.kotlin.contracts.description.ContractDescription import org.jetbrains.kotlin.contracts.description.ContractProviderKey import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.psiUtil.isContractDescriptionCallPsiCheck import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTrace @@ -35,7 +35,7 @@ class ContractParsingServices(val languageVersionSettings: LanguageVersionSettin fun checkContractAndRecordIfPresent(expression: KtExpression, trace: BindingTrace, scope: LexicalScope, isFirstStatement: Boolean) { val ownerDescriptor = scope.ownerDescriptor if (!expression.isContractDescriptionCallPsiCheck() || ownerDescriptor !is FunctionDescriptor) return - val contractProvider = ownerDescriptor.getUserData(ContractProviderKey) ?: return + val contractProvider = ownerDescriptor.getUserData(ContractProviderKey) val isFeatureTurnedOn = languageVersionSettings.supportsFeature(LanguageFeature.AllowContractsForCustomFunctions) || // This condition is here for technical purposes of compiling 1.2-runtime with contracts @@ -62,7 +62,7 @@ class ContractParsingServices(val languageVersionSettings: LanguageVersionSettin else -> parseContract(expression, trace, ownerDescriptor) } - contractProvider.setContractDescription(contractDescriptor) + contractProvider?.setContractDescription(contractDescriptor) } internal fun isContractDescriptionCall(expression: KtExpression, context: BindingContext): Boolean = @@ -76,4 +76,4 @@ class ContractParsingServices(val languageVersionSettings: LanguageVersionSettin private fun KtExpression.isContractDescriptionCallPreciseCheck(context: BindingContext): Boolean = getResolvedCall(context)?.resultingDescriptor?.isContractCallDescriptor() ?: false -} \ No newline at end of file +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt index 07914dd6b71..8b037e3a113 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt @@ -39,9 +39,9 @@ import org.jetbrains.kotlin.diagnostics.PsiDiagnosticUtils import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.isContractPresentPsiCheck import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier +import org.jetbrains.kotlin.psi.psiUtil.isContractPresentPsiCheck import org.jetbrains.kotlin.resolve.DescriptorResolver.getDefaultModality import org.jetbrains.kotlin.resolve.DescriptorResolver.getDefaultVisibility import org.jetbrains.kotlin.resolve.DescriptorUtils.getDispatchReceiverParameterIfNeeded @@ -198,7 +198,13 @@ class FunctionDescriptorResolver( function, getDefaultModality(container, visibility, function.hasBody()), trace.bindingContext, container ) + val contractProvider = getContractProvider(functionDescriptor, trace, scope, dataFlowInfo, function) + val userData = mutableMapOf, Any>().apply { + if (contractProvider != null) { + put(ContractProviderKey, contractProvider) + } + } functionDescriptor.initialize( receiverType, @@ -208,8 +214,9 @@ class FunctionDescriptorResolver( returnType, modality, visibility, - mapOf(ContractProviderKey to contractProvider) + userData.takeIf { it.isNotEmpty() } ) + functionDescriptor.isOperator = function.hasModifier(KtTokens.OPERATOR_KEYWORD) functionDescriptor.isInfix = function.hasModifier(KtTokens.INFIX_KEYWORD) functionDescriptor.isExternal = function.hasModifier(KtTokens.EXTERNAL_KEYWORD) @@ -232,17 +239,16 @@ class FunctionDescriptorResolver( scope: LexicalScope, dataFlowInfo: DataFlowInfo, function: KtFunction - ): LazyContractProvider { + ): LazyContractProvider? { val provideByDeferredForceResolve = LazyContractProvider { expressionTypingServices.getBodyExpressionType(trace, scope, dataFlowInfo, function, functionDescriptor) } - val emptyContract = LazyContractProvider.createInitialized(null) val isContractsEnabled = languageVersionSettings.supportsFeature(LanguageFeature.AllowContractsForCustomFunctions) || // We need to enable contracts if we're compiling "kotlin"-package to be able to ship contracts in stdlib in 1.2 languageVersionSettings.getFlag(AnalysisFlag.allowKotlinPackage) - if (!isContractsEnabled || !function.isContractPresentPsiCheck()) return emptyContract + if (!isContractsEnabled || !function.isContractPresentPsiCheck()) return null return provideByDeferredForceResolve }