Force-resolve body in contract computation under lock

Even though acquire/release pattern guarantees memory visibility across
threads, it doesn't prevents concurrent access to critical section (i.e.
to force-resolve of the corresponding body).

This can lead to multiple resolution passes over one and the same PSI in
IDE, which, in turn, leads to 'rewrite at slice'-exceptions. See
KT-30030 for case description and details.

^KT-30030 Fixed
This commit is contained in:
Dmitry Savvinov
2019-02-21 11:14:22 +03:00
parent 705ec503cf
commit 50d1c013fc
5 changed files with 26 additions and 13 deletions
@@ -41,14 +41,14 @@ class ContractDeserializerImpl(
ownerFunction: FunctionDescriptor,
typeTable: TypeTable,
typeDeserializer: TypeDeserializer
): Pair<CallableDescriptor.UserDataKey<*>, LazyContractProvider>? {
): Pair<CallableDescriptor.UserDataKey<*>, AbstractContractProvider>? {
if (!proto.hasContract()) return null
if (!configuration.readDeserializedContracts) return null
val worker = ContractDeserializationWorker(typeTable, typeDeserializer, ownerFunction, storageManager)
val contract = worker.deserializeContract(proto.contract) ?: return null
return ContractProviderKey to LazyContractProvider.createInitialized(contract)
return ContractProviderKey to ContractProviderImpl(contract)
}
private class ContractDeserializationWorker(
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.description.ContractDescription
import org.jetbrains.kotlin.contracts.description.ContractProviderKey
import org.jetbrains.kotlin.contracts.description.LazyContractProvider
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
@@ -50,7 +51,8 @@ class ContractParsingServices(val languageVersionSettings: LanguageVersionSettin
if (!expression.isContractDescriptionCallPsiCheck()) return
val callContext = ContractCallContext(expression, isFirstStatement, scope, trace)
val contractProviderIfAny = (scope.ownerDescriptor as? FunctionDescriptor)?.getUserData(ContractProviderKey)
val contractProviderIfAny =
(scope.ownerDescriptor as? FunctionDescriptor)?.getUserData(ContractProviderKey) as? LazyContractProvider?
var resultingContractDescription: ContractDescription? = null
try {
@@ -263,7 +263,7 @@ class FunctionDescriptorResolver(
if (!isContractsEnabled || !function.mayHaveContract()) return null
return LazyContractProvider {
return LazyContractProvider(storageManager) {
AstLoadingFilter.forceAllowTreeLoading(function.containingFile) {
expressionTypingServices.getBodyExpressionType(trace, scope, dataFlowInfo, function, functionDescriptor)
}