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
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.contracts.model
import org.jetbrains.kotlin.contracts.model.structure.ESKotlinType
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
/**
@@ -27,13 +28,19 @@ import org.jetbrains.kotlin.contracts.model.visitors.Reducer
* values, it takes effects and returns effects.
*/
interface Functor {
fun invokeWithArguments(arguments: List<Computation>, reducer: Reducer): List<ESEffect>
fun invokeWithArguments(arguments: List<Computation>, typeSubstitution: ESTypeSubstitution, reducer: Reducer): List<ESEffect>
}
abstract class AbstractFunctor : Functor {
override fun invokeWithArguments(arguments: List<Computation>, reducer: Reducer): List<ESEffect> =
reducer.reduceEffects(doInvocation(arguments, reducer))
override fun invokeWithArguments(arguments: List<Computation>, typeSubstitution: ESTypeSubstitution, reducer: Reducer): List<ESEffect> =
reducer.reduceEffects(doInvocation(arguments, typeSubstitution, reducer))
protected abstract fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect>
}
protected abstract fun doInvocation(
arguments: List<Computation>,
typeSubstitution: ESTypeSubstitution,
reducer: Reducer
): List<ESEffect>
}
typealias ESTypeSubstitution = Map<ESKotlinType, ESKotlinType>
@@ -17,11 +17,14 @@
package org.jetbrains.kotlin.contracts.model.functors
import org.jetbrains.kotlin.contracts.model.*
import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.contracts.model.structure.ESConstant
import org.jetbrains.kotlin.contracts.model.structure.ESOr
import org.jetbrains.kotlin.contracts.model.structure.isReturns
import org.jetbrains.kotlin.contracts.model.structure.isWildcard
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
abstract class AbstractBinaryFunctor : AbstractFunctor() {
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
override fun doInvocation(arguments: List<Computation>, typeSubstitution: ESTypeSubstitution, reducer: Reducer): List<ESEffect> {
assert(arguments.size == 2) { "Wrong size of arguments list for Binary functor: expected 2, got ${arguments.size}" }
return invokeWithArguments(arguments[0], arguments[1])
}
@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.contracts.model.visitors.Reducer
* be called only on clauses that haven't failed before reaching functor transformation.
*/
abstract class AbstractUnaryFunctor : AbstractFunctor() {
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
override fun doInvocation(arguments: List<Computation>, typeSubstitution: ESTypeSubstitution, reducer: Reducer): List<ESEffect> {
assert(arguments.size == 1) { "Wrong size of arguments list for Unary operator: expected 1, got ${arguments.size}" }
return invokeWithArguments(arguments[0])
}
@@ -38,7 +38,7 @@ class EqualsFunctor(val isNegated: Boolean) : AbstractFunctor() {
We don't want to code here fair analysis for general cases, because it's too complex. Instead, we just
check some specific cases, which are useful enough in practice
*/
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
override fun doInvocation(arguments: List<Computation>, typeSubstitution: ESTypeSubstitution, reducer: Reducer): List<ESEffect> {
assert(arguments.size == 2) { "Equals functor expected 2 arguments, got ${arguments.size}" }
// TODO: AnnotationConstructorCaller kills this with implicit receiver. Investigate, how.
@@ -24,21 +24,23 @@ import org.jetbrains.kotlin.contracts.model.structure.ESType
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
class IsFunctor(val type: ESType, val isNegated: Boolean) : AbstractFunctor() {
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
override fun doInvocation(arguments: List<Computation>, typeSubstitution: ESTypeSubstitution, reducer: Reducer): List<ESEffect> {
assert(arguments.size == 1) { "Wrong size of arguments list for Unary operator: expected 1, got ${arguments.size}" }
return invokeWithArguments(arguments[0])
return invokeWithArguments(arguments[0], typeSubstitution)
}
fun invokeWithArguments(arg: Computation): List<ESEffect> {
fun invokeWithArguments(arg: Computation, typeSubstitution: ESTypeSubstitution): List<ESEffect> {
return if (arg is ESValue)
invokeWithValue(arg)
invokeWithValue(arg, typeSubstitution)
else
emptyList()
}
private fun invokeWithValue(value: ESValue): List<ConditionalEffect> {
val trueIs = ESIs(value, this)
val falseIs = ESIs(value, IsFunctor(type, isNegated.not()))
private fun invokeWithValue(value: ESValue, typeSubstitution: ESTypeSubstitution): List<ConditionalEffect> {
val substitutedType = typeSubstitution[type] ?: type
val trueIs = ESIs(value, IsFunctor(substitutedType, isNegated))
val falseIs = ESIs(value, IsFunctor(substitutedType, isNegated.not()))
val trueResult = ConditionalEffect(trueIs, ESReturns(ESConstants.trueValue))
val falseResult = ConditionalEffect(falseIs, ESReturns(ESConstants.falseValue))
@@ -28,7 +28,7 @@ class SubstitutingFunctor(
private val basicEffects: List<ESEffect>,
private val ownerFunction: FunctionDescriptor
) : AbstractFunctor() {
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
override fun doInvocation(arguments: List<Computation>, typeSubstitution: ESTypeSubstitution, reducer: Reducer): List<ESEffect> {
if (basicEffects.isEmpty()) return emptyList()
val receiver =
@@ -40,7 +40,7 @@ class SubstitutingFunctor(
}
val substitutions = parameters.zip(arguments).toMap()
val substitutor = Substitutor(substitutions, reducer)
val substitutor = Substitutor(substitutions, typeSubstitution, reducer)
val substitutedClauses = mutableListOf<ESEffect>()
effectsLoop@ for (effect in basicEffects) {
@@ -36,6 +36,20 @@ object ESBooleanType : ESType() {
class ESKotlinType(val type: KotlinType) : ESType() {
override fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType = type
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ESKotlinType
if (type != other.type) return false
return true
}
override fun hashCode(): Int {
return type.hashCode()
}
}
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.contracts.model.visitors
import org.jetbrains.kotlin.contracts.model.Computation
import org.jetbrains.kotlin.contracts.model.ESExpression
import org.jetbrains.kotlin.contracts.model.ESExpressionVisitor
import org.jetbrains.kotlin.contracts.model.ESTypeSubstitution
import org.jetbrains.kotlin.contracts.model.structure.*
/**
@@ -28,11 +29,12 @@ import org.jetbrains.kotlin.contracts.model.structure.*
*/
class Substitutor(
private val substitutions: Map<ESVariable, Computation>,
private val typeSubstitution: ESTypeSubstitution,
private val reducer: Reducer
) : ESExpressionVisitor<Computation?> {
override fun visitIs(isOperator: ESIs): Computation? {
val arg = isOperator.left.accept(this) ?: return null
return CallComputation(ESBooleanType, isOperator.functor.invokeWithArguments(arg))
return CallComputation(ESBooleanType, isOperator.functor.invokeWithArguments(arg, typeSubstitution))
}
override fun visitNot(not: ESNot): Computation? {
@@ -43,7 +45,7 @@ class Substitutor(
override fun visitEqual(equal: ESEqual): Computation? {
val left = equal.left.accept(this) ?: return null
val right = equal.right.accept(this) ?: return null
return CallComputation(ESBooleanType, equal.functor.invokeWithArguments(listOf(left, right), reducer))
return CallComputation(ESBooleanType, equal.functor.invokeWithArguments(listOf(left, right), typeSubstitution, reducer))
}
override fun visitAnd(and: ESAnd): Computation? {