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.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
}
}
@@ -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<FunctionDescriptor.UserDataKey<*>, 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
}