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)
This commit is contained in:
Denis Zharkov
2018-08-10 11:14:11 +07:00
parent 1f7776981d
commit 99fe6de52a
2 changed files with 15 additions and 9 deletions
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.contracts.description.ContractDescription
import org.jetbrains.kotlin.contracts.description.ContractProviderKey import org.jetbrains.kotlin.contracts.description.ContractProviderKey
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Errors 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.psi.psiUtil.isContractDescriptionCallPsiCheck
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace 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) { fun checkContractAndRecordIfPresent(expression: KtExpression, trace: BindingTrace, scope: LexicalScope, isFirstStatement: Boolean) {
val ownerDescriptor = scope.ownerDescriptor val ownerDescriptor = scope.ownerDescriptor
if (!expression.isContractDescriptionCallPsiCheck() || ownerDescriptor !is FunctionDescriptor) return if (!expression.isContractDescriptionCallPsiCheck() || ownerDescriptor !is FunctionDescriptor) return
val contractProvider = ownerDescriptor.getUserData(ContractProviderKey) ?: return val contractProvider = ownerDescriptor.getUserData(ContractProviderKey)
val isFeatureTurnedOn = languageVersionSettings.supportsFeature(LanguageFeature.AllowContractsForCustomFunctions) || val isFeatureTurnedOn = languageVersionSettings.supportsFeature(LanguageFeature.AllowContractsForCustomFunctions) ||
// This condition is here for technical purposes of compiling 1.2-runtime with contracts // 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) else -> parseContract(expression, trace, ownerDescriptor)
} }
contractProvider.setContractDescription(contractDescriptor) contractProvider?.setContractDescription(contractDescriptor)
} }
internal fun isContractDescriptionCall(expression: KtExpression, context: BindingContext): Boolean = internal fun isContractDescriptionCall(expression: KtExpression, context: BindingContext): Boolean =
@@ -76,4 +76,4 @@ class ContractParsingServices(val languageVersionSettings: LanguageVersionSettin
private fun KtExpression.isContractDescriptionCallPreciseCheck(context: BindingContext): Boolean = private fun KtExpression.isContractDescriptionCallPreciseCheck(context: BindingContext): Boolean =
getResolvedCall(context)?.resultingDescriptor?.isContractCallDescriptor() ?: false getResolvedCall(context)?.resultingDescriptor?.isContractCallDescriptor() ?: false
} }
@@ -39,9 +39,9 @@ import org.jetbrains.kotlin.diagnostics.PsiDiagnosticUtils
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.* 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.hasActualModifier
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier 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.getDefaultModality
import org.jetbrains.kotlin.resolve.DescriptorResolver.getDefaultVisibility import org.jetbrains.kotlin.resolve.DescriptorResolver.getDefaultVisibility
import org.jetbrains.kotlin.resolve.DescriptorUtils.getDispatchReceiverParameterIfNeeded import org.jetbrains.kotlin.resolve.DescriptorUtils.getDispatchReceiverParameterIfNeeded
@@ -198,7 +198,13 @@ class FunctionDescriptorResolver(
function, getDefaultModality(container, visibility, function.hasBody()), function, getDefaultModality(container, visibility, function.hasBody()),
trace.bindingContext, container trace.bindingContext, container
) )
val contractProvider = getContractProvider(functionDescriptor, trace, scope, dataFlowInfo, function) val contractProvider = getContractProvider(functionDescriptor, trace, scope, dataFlowInfo, function)
val userData = mutableMapOf<FunctionDescriptor.UserDataKey<*>, Any>().apply {
if (contractProvider != null) {
put(ContractProviderKey, contractProvider)
}
}
functionDescriptor.initialize( functionDescriptor.initialize(
receiverType, receiverType,
@@ -208,8 +214,9 @@ class FunctionDescriptorResolver(
returnType, returnType,
modality, modality,
visibility, visibility,
mapOf(ContractProviderKey to contractProvider) userData.takeIf { it.isNotEmpty() }
) )
functionDescriptor.isOperator = function.hasModifier(KtTokens.OPERATOR_KEYWORD) functionDescriptor.isOperator = function.hasModifier(KtTokens.OPERATOR_KEYWORD)
functionDescriptor.isInfix = function.hasModifier(KtTokens.INFIX_KEYWORD) functionDescriptor.isInfix = function.hasModifier(KtTokens.INFIX_KEYWORD)
functionDescriptor.isExternal = function.hasModifier(KtTokens.EXTERNAL_KEYWORD) functionDescriptor.isExternal = function.hasModifier(KtTokens.EXTERNAL_KEYWORD)
@@ -232,17 +239,16 @@ class FunctionDescriptorResolver(
scope: LexicalScope, scope: LexicalScope,
dataFlowInfo: DataFlowInfo, dataFlowInfo: DataFlowInfo,
function: KtFunction function: KtFunction
): LazyContractProvider { ): LazyContractProvider? {
val provideByDeferredForceResolve = LazyContractProvider { val provideByDeferredForceResolve = LazyContractProvider {
expressionTypingServices.getBodyExpressionType(trace, scope, dataFlowInfo, function, functionDescriptor) expressionTypingServices.getBodyExpressionType(trace, scope, dataFlowInfo, function, functionDescriptor)
} }
val emptyContract = LazyContractProvider.createInitialized(null)
val isContractsEnabled = languageVersionSettings.supportsFeature(LanguageFeature.AllowContractsForCustomFunctions) || 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 // 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) languageVersionSettings.getFlag(AnalysisFlag.allowKotlinPackage)
if (!isContractsEnabled || !function.isContractPresentPsiCheck()) return emptyContract if (!isContractsEnabled || !function.isContractPresentPsiCheck()) return null
return provideByDeferredForceResolve return provideByDeferredForceResolve
} }