Allow use reference to reified type parameters in contracts since 1.4

This commit is contained in:
Dmitriy Novozhilov
2019-12-26 13:08:32 +03:00
parent 9c1b68f839
commit 5dfe100ae5
32 changed files with 232 additions and 47 deletions
@@ -20,10 +20,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.description.ContractProviderKey
import org.jetbrains.kotlin.contracts.model.Computation
import org.jetbrains.kotlin.contracts.model.ConditionalEffect
import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.Functor
import org.jetbrains.kotlin.contracts.model.*
import org.jetbrains.kotlin.contracts.model.functors.*
import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
@@ -71,12 +68,13 @@ class EffectsExtractingVisitor(
if (resolvedCall.isCallWithUnsupportedReceiver()) return UNKNOWN_COMPUTATION
val arguments = resolvedCall.getCallArgumentsAsComputations() ?: return UNKNOWN_COMPUTATION
val typeSubstitution = resolvedCall.getTypeSubstitution()
val descriptor = resolvedCall.resultingDescriptor
return when {
descriptor.isEqualsDescriptor() -> CallComputation(
ESBooleanType,
EqualsFunctor(false).invokeWithArguments(arguments, reducer)
EqualsFunctor(false).invokeWithArguments(arguments, typeSubstitution, reducer)
)
descriptor is ValueDescriptor -> ESVariableWithDataFlowValue(
descriptor,
@@ -86,7 +84,7 @@ class EffectsExtractingVisitor(
val esType = descriptor.returnType?.toESType()
CallComputation(
esType,
descriptor.getFunctor()?.invokeWithArguments(arguments, reducer) ?: emptyList()
descriptor.getFunctor()?.invokeWithArguments(arguments, typeSubstitution, reducer) ?: emptyList()
)
}
else -> UNKNOWN_COMPUTATION
@@ -123,7 +121,7 @@ class EffectsExtractingVisitor(
val arg = extractOrGetCached(expression.leftHandSide)
return CallComputation(
ESBooleanType,
IsFunctor(rightType, expression.isNegated).invokeWithArguments(listOf(arg), reducer)
IsFunctor(rightType, expression.isNegated).invokeWithArguments(listOf(arg), emptyMap(), reducer)
)
}
@@ -148,10 +146,10 @@ class EffectsExtractingVisitor(
val args = listOf(left, right)
return when (expression.operationToken) {
KtTokens.EXCLEQ -> CallComputation(ESBooleanType, EqualsFunctor(true).invokeWithArguments(args, reducer))
KtTokens.EQEQ -> CallComputation(ESBooleanType, EqualsFunctor(false).invokeWithArguments(args, reducer))
KtTokens.ANDAND -> CallComputation(ESBooleanType, AndFunctor().invokeWithArguments(args, reducer))
KtTokens.OROR -> CallComputation(ESBooleanType, OrFunctor().invokeWithArguments(args, reducer))
KtTokens.EXCLEQ -> CallComputation(ESBooleanType, EqualsFunctor(true).invokeWithArguments(args, emptyMap(), reducer))
KtTokens.EQEQ -> CallComputation(ESBooleanType, EqualsFunctor(false).invokeWithArguments(args, emptyMap(), reducer))
KtTokens.ANDAND -> CallComputation(ESBooleanType, AndFunctor().invokeWithArguments(args, emptyMap(), reducer))
KtTokens.OROR -> CallComputation(ESBooleanType, OrFunctor().invokeWithArguments(args, emptyMap(), reducer))
else -> UNKNOWN_COMPUTATION
}
}
@@ -215,6 +213,14 @@ class EffectsExtractingVisitor(
return arguments
}
private fun ResolvedCall<*>.getTypeSubstitution(): ESTypeSubstitution {
val result = mutableMapOf<ESKotlinType, ESKotlinType>()
for ((typeParameter, typeArgument) in typeArguments) {
result[ESKotlinType(typeParameter.defaultType)] = ESKotlinType(typeArgument)
}
return result
}
private fun ResolvedValueArgument.toComputation(): Computation? {
return when (this) {
// Assume that we don't know anything about default arguments
@@ -16,14 +16,14 @@
package org.jetbrains.kotlin.contracts.parsing
import org.jetbrains.kotlin.config.AnalysisFlags
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.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
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
@@ -46,7 +46,7 @@ class ContractParsingServices(val languageVersionSettings: LanguageVersionSettin
// is a *necessary* (but not sufficient, actually) condition for presence of 'LazyContractProvider'
if (!expression.isContractDescriptionCallPsiCheck()) return
val callContext = ContractCallContext(expression, scope, trace)
val callContext = ContractCallContext(expression, scope, trace, languageVersionSettings)
val contractProviderIfAny =
(scope.ownerDescriptor as? FunctionDescriptor)?.getUserData(ContractProviderKey) as? LazyContractProvider?
var resultingContractDescription: ContractDescription? = null
@@ -104,7 +104,8 @@ class ContractParsingServices(val languageVersionSettings: LanguageVersionSettin
class ContractCallContext(
val contractCallExpression: KtExpression,
val scope: LexicalScope,
val trace: BindingTrace
val trace: BindingTrace,
val languageVersionSettings: LanguageVersionSettings
) {
val ownerDescriptor: DeclarationDescriptor = scope.ownerDescriptor
val functionDescriptor: FunctionDescriptor = ownerDescriptor as FunctionDescriptor
@@ -16,7 +16,8 @@
package org.jetbrains.kotlin.contracts.parsing
import org.jetbrains.kotlin.contracts.description.*
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.contracts.description.BooleanExpression
import org.jetbrains.kotlin.contracts.description.expressions.*
import org.jetbrains.kotlin.descriptors.ValueDescriptor
import org.jetbrains.kotlin.descriptors.impl.AbstractTypeParameterDescriptor
@@ -47,8 +48,16 @@ internal class PsiConditionParser(
}
if (descriptor is AbstractTypeParameterDescriptor) {
collector.badDescription("references to type parameters are forbidden in contracts", typeReference)
return null
val reifiedGenericsAllowed = callContext.languageVersionSettings.supportsFeature(LanguageFeature.AllowReifiedGenericsInContracts)
if (!reifiedGenericsAllowed || !descriptor.isReified) {
val message = if (reifiedGenericsAllowed) {
"references to not reified type parameters are forbidden in contracts"
} else {
"references to type parameters are forbidden in contracts"
}
collector.badDescription(message, typeReference)
return null
}
}
// This should be reported as "Can't check for erased" error, but we explicitly abort contract parsing. Just in case.