Do not use DefaultBuiltIns.Instance in ESConstant

Add ESComponents and ESConstants to encapsulate usages of built-ins in
different functors and operators
This commit is contained in:
Alexander Udalov
2018-12-21 16:23:29 +01:00
parent 9516d6e89b
commit 6cd3d9f19a
22 changed files with 202 additions and 162 deletions
@@ -16,13 +16,18 @@
package org.jetbrains.kotlin.contracts
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.model.Computation
import org.jetbrains.kotlin.contracts.model.ESComponents
import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.MutableContextInfo
import org.jetbrains.kotlin.contracts.model.functors.EqualsFunctor
import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.contracts.model.structure.ESCalls
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
import org.jetbrains.kotlin.contracts.model.structure.ESReturns
import org.jetbrains.kotlin.contracts.model.structure.UNKNOWN_COMPUTATION
import org.jetbrains.kotlin.contracts.model.visitors.InfoCollector
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.psi.KtCallExpression
@@ -35,7 +40,17 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.ConditionalDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dataFlowValueFactory: DataFlowValueFactory) {
class EffectSystem(
val languageVersionSettings: LanguageVersionSettings,
val dataFlowValueFactory: DataFlowValueFactory,
val builtIns: KotlinBuiltIns
) {
// Lazy because this code is executed when the container is set up (before any resolution starts),
// so builtins are not fully functional yet at that moment
val components: ESComponents by lazy(LazyThreadSafetyMode.NONE) { ESComponents(builtIns) }
val constants: ESConstants
get() = components.constants
fun getDataFlowInfoForFinishedCall(
resolvedCall: ResolvedCall<*>,
@@ -48,9 +63,9 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return DataFlowInfo.EMPTY
if (callExpression is KtDeclaration) return DataFlowInfo.EMPTY
val resultContextInfo = getContextInfoWhen(ESReturns(ESConstant.WILDCARD), callExpression, bindingTrace, moduleDescriptor)
val resultContextInfo = getContextInfoWhen(ESReturns(constants.wildcard), callExpression, bindingTrace, moduleDescriptor)
return resultContextInfo.toDataFlowInfo(languageVersionSettings, moduleDescriptor.builtIns)
return resultContextInfo.toDataFlowInfo(languageVersionSettings, builtIns)
}
fun getDataFlowInfoWhenEquals(
@@ -67,11 +82,10 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
val rightComputation =
getNonTrivialComputation(rightExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY
val effects = EqualsFunctor(false).invokeWithArguments(leftComputation, rightComputation)
val effects = EqualsFunctor(constants, false).invokeWithArguments(leftComputation, rightComputation)
val builtIns = moduleDescriptor.builtIns
val equalsContextInfo = InfoCollector(ESReturns(true.lift())).collectFromSchema(effects)
val notEqualsContextInfo = InfoCollector(ESReturns(false.lift())).collectFromSchema(effects)
val equalsContextInfo = InfoCollector(ESReturns(constants.trueValue), constants).collectFromSchema(effects)
val notEqualsContextInfo = InfoCollector(ESReturns(constants.falseValue), constants).collectFromSchema(effects)
return ConditionalDataFlowInfo(
equalsContextInfo.toDataFlowInfo(languageVersionSettings, builtIns),
@@ -86,7 +100,7 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return
if (callExpression is KtDeclaration) return
val resultingContextInfo = getContextInfoWhen(ESReturns(ESConstant.WILDCARD), callExpression, bindingTrace, moduleDescriptor)
val resultingContextInfo = getContextInfoWhen(ESReturns(constants.wildcard), callExpression, bindingTrace, moduleDescriptor)
for (effect in resultingContextInfo.firedEffects) {
val callsEffect = effect as? ESCalls ?: continue
val lambdaExpression = (callsEffect.callable as? ESLambda)?.lambda ?: continue
@@ -103,7 +117,7 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
if (!languageVersionSettings.supportsFeature(LanguageFeature.UseReturnsEffect)) return DataFlowInfo.EMPTY
if (condition == null) return DataFlowInfo.EMPTY
return getContextInfoWhen(ESReturns(value.lift()), condition, bindingTrace, moduleDescriptor)
return getContextInfoWhen(ESReturns(constants.booleanValue(value)), condition, bindingTrace, moduleDescriptor)
.toDataFlowInfo(languageVersionSettings, moduleDescriptor.builtIns)
}
@@ -114,11 +128,11 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
moduleDescriptor: ModuleDescriptor
): MutableContextInfo {
val computation = getNonTrivialComputation(expression, bindingTrace, moduleDescriptor) ?: return MutableContextInfo.EMPTY
return InfoCollector(observedEffect).collectFromSchema(computation.effects)
return InfoCollector(observedEffect, constants).collectFromSchema(computation.effects)
}
private fun getNonTrivialComputation(expression: KtExpression, trace: BindingTrace, moduleDescriptor: ModuleDescriptor): Computation? {
val computation = EffectsExtractingVisitor(trace, moduleDescriptor, dataFlowValueFactory).extractOrGetCached(expression)
return if (computation == UNKNOWN_COMPUTATION) null else computation
val visitor = EffectsExtractingVisitor(trace, moduleDescriptor, dataFlowValueFactory, constants)
return visitor.extractOrGetCached(expression).takeUnless { it == UNKNOWN_COMPUTATION }
}
}
@@ -23,7 +23,10 @@ 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.functors.*
import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.contracts.model.structure.CallComputation
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
import org.jetbrains.kotlin.contracts.model.structure.UNKNOWN_COMPUTATION
import org.jetbrains.kotlin.contracts.model.structure.isReturns
import org.jetbrains.kotlin.contracts.parsing.isEqualsDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -51,9 +54,10 @@ import org.jetbrains.kotlin.utils.addIfNotNull
class EffectsExtractingVisitor(
private val trace: BindingTrace,
private val moduleDescriptor: ModuleDescriptor,
private val dataFlowValueFactory: DataFlowValueFactory
private val dataFlowValueFactory: DataFlowValueFactory,
private val constants: ESConstants
) : KtVisitor<Computation, Unit>() {
private val builtIns: KotlinBuiltIns = moduleDescriptor.builtIns
private val builtIns: KotlinBuiltIns get() = moduleDescriptor.builtIns
fun extractOrGetCached(element: KtElement): Computation {
trace[BindingContext.EXPRESSION_EFFECTS, element]?.let { return it }
@@ -70,7 +74,7 @@ class EffectsExtractingVisitor(
return when {
descriptor.isEqualsDescriptor() -> CallComputation(
builtIns.booleanType,
EqualsFunctor(false).invokeWithArguments(arguments)
EqualsFunctor(constants, false).invokeWithArguments(arguments)
)
descriptor is ValueDescriptor -> ESDataFlowValue(
descriptor,
@@ -103,8 +107,8 @@ class EffectsExtractingVisitor(
val value: Any? = compileTimeConstant.getValue(type)
return when (value) {
is Boolean -> value.lift()
null -> ESConstant.NULL
is Boolean -> constants.booleanValue(value)
null -> constants.nullValue
else -> UNKNOWN_COMPUTATION
}
}
@@ -114,7 +118,7 @@ class EffectsExtractingVisitor(
val arg = extractOrGetCached(expression.leftHandSide)
return CallComputation(
builtIns.booleanType,
IsFunctor(rightType, expression.isNegated).invokeWithArguments(listOf(arg))
IsFunctor(constants, rightType, expression.isNegated).invokeWithArguments(listOf(arg))
)
}
@@ -126,7 +130,7 @@ class EffectsExtractingVisitor(
// null bypassing function's contract, so we have to filter them out
fun ESEffect.containsReturnsNull(): Boolean =
isReturns { value == ESConstant.NULL } || this is ConditionalEffect && this.simpleEffect.containsReturnsNull()
isReturns { value == constants.nullValue } || this is ConditionalEffect && this.simpleEffect.containsReturnsNull()
val effectsWithoutReturnsNull = computation.effects.filter { !it.containsReturnsNull() }
return CallComputation(computation.type, effectsWithoutReturnsNull)
@@ -139,10 +143,10 @@ class EffectsExtractingVisitor(
val args = listOf(left, right)
return when (expression.operationToken) {
KtTokens.EXCLEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(true).invokeWithArguments(args))
KtTokens.EQEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(false).invokeWithArguments(args))
KtTokens.ANDAND -> CallComputation(builtIns.booleanType, AndFunctor().invokeWithArguments(args))
KtTokens.OROR -> CallComputation(builtIns.booleanType, OrFunctor().invokeWithArguments(args))
KtTokens.EXCLEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(constants, true).invokeWithArguments(args))
KtTokens.EQEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(constants, false).invokeWithArguments(args))
KtTokens.ANDAND -> CallComputation(builtIns.booleanType, AndFunctor(constants).invokeWithArguments(args))
KtTokens.OROR -> CallComputation(builtIns.booleanType, OrFunctor(constants).invokeWithArguments(args))
else -> UNKNOWN_COMPUTATION
}
}
@@ -150,7 +154,7 @@ class EffectsExtractingVisitor(
override fun visitUnaryExpression(expression: KtUnaryExpression, data: Unit): Computation {
val arg = extractOrGetCached(expression.baseExpression ?: return UNKNOWN_COMPUTATION)
return when (expression.operationToken) {
KtTokens.EXCL -> CallComputation(builtIns.booleanType, NotFunctor().invokeWithArguments(arg))
KtTokens.EXCL -> CallComputation(builtIns.booleanType, NotFunctor(constants).invokeWithArguments(arg))
else -> UNKNOWN_COMPUTATION
}
}
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.contracts.description
import org.jetbrains.kotlin.contracts.interpretation.ContractInterpretationDispatcher
import org.jetbrains.kotlin.contracts.model.ESComponents
import org.jetbrains.kotlin.contracts.model.Functor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -40,7 +41,8 @@ open class ContractDescription(
storageManager: StorageManager
) {
private val computeFunctor = storageManager.createMemoizedFunctionWithNullableValues<ModuleDescriptor, Functor> { module ->
ContractInterpretationDispatcher(module).convertContractDescriptorToFunctor(this)
val components = ESComponents(module.builtIns)
ContractInterpretationDispatcher(components).convertContractDescriptorToFunctor(this)
}
fun getFunctor(usageModule: ModuleDescriptor): Functor? = computeFunctor(usageModule)
@@ -16,39 +16,42 @@
package org.jetbrains.kotlin.contracts.interpretation
import org.jetbrains.kotlin.contracts.description.*
import org.jetbrains.kotlin.contracts.description.ContractDescriptionVisitor
import org.jetbrains.kotlin.contracts.description.expressions.*
import org.jetbrains.kotlin.contracts.model.ESExpression
import org.jetbrains.kotlin.contracts.model.functors.IsFunctor
import org.jetbrains.kotlin.contracts.model.structure.*
internal class ConditionInterpreter(private val dispatcher: ContractInterpretationDispatcher) :
ContractDescriptionVisitor<ESExpression?, Unit> {
internal class ConditionInterpreter(
private val dispatcher: ContractInterpretationDispatcher
) : ContractDescriptionVisitor<ESExpression?, Unit> {
private val constants = dispatcher.components.constants
override fun visitLogicalOr(logicalOr: LogicalOr, data: Unit): ESExpression? {
val left = logicalOr.left.accept(this, data) ?: return null
val right = logicalOr.right.accept(this, data) ?: return null
return ESOr(left, right)
return ESOr(constants, left, right)
}
override fun visitLogicalAnd(logicalAnd: LogicalAnd, data: Unit): ESExpression? {
val left = logicalAnd.left.accept(this, data) ?: return null
val right = logicalAnd.right.accept(this, data) ?: return null
return ESAnd(left, right)
return ESAnd(constants, left, right)
}
override fun visitLogicalNot(logicalNot: LogicalNot, data: Unit): ESExpression? {
val arg = logicalNot.arg.accept(this, data) ?: return null
return ESNot(arg)
return ESNot(constants, arg)
}
override fun visitIsInstancePredicate(isInstancePredicate: IsInstancePredicate, data: Unit): ESExpression? {
val esVariable = dispatcher.interpretVariable(isInstancePredicate.arg) ?: return null
return ESIs(esVariable, IsFunctor(isInstancePredicate.type, isInstancePredicate.isNegated))
return ESIs(esVariable, IsFunctor(constants, isInstancePredicate.type, isInstancePredicate.isNegated))
}
override fun visitIsNullPredicate(isNullPredicate: IsNullPredicate, data: Unit): ESExpression? {
val variable = dispatcher.interpretVariable(isNullPredicate.arg) ?: return null
return ESEqual(variable, ESConstant.NULL, isNullPredicate.isNegated)
return ESEqual(constants, variable, constants.nullValue, isNullPredicate.isNegated)
}
override fun visitBooleanConstantDescriptor(booleanConstantDescriptor: BooleanConstantReference, data: Unit): ESExpression? =
@@ -56,4 +59,4 @@ internal class ConditionInterpreter(private val dispatcher: ContractInterpretati
override fun visitBooleanVariableReference(booleanVariableReference: BooleanVariableReference, data: Unit): ESExpression? =
dispatcher.interpretVariable(booleanVariableReference)
}
}
@@ -19,15 +19,15 @@ package org.jetbrains.kotlin.contracts.interpretation
import org.jetbrains.kotlin.contracts.description.expressions.BooleanConstantReference
import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference
import org.jetbrains.kotlin.contracts.model.structure.ESConstant
import org.jetbrains.kotlin.contracts.model.structure.lift
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
internal class ConstantValuesInterpreter {
fun interpretConstant(constantReference: ConstantReference): ESConstant? = when (constantReference) {
BooleanConstantReference.TRUE -> true.lift()
BooleanConstantReference.FALSE -> false.lift()
ConstantReference.NULL -> ESConstant.NULL
ConstantReference.NOT_NULL -> ESConstant.NOT_NULL
ConstantReference.WILDCARD -> ESConstant.WILDCARD
fun interpretConstant(constantReference: ConstantReference, constants: ESConstants): ESConstant? = when (constantReference) {
BooleanConstantReference.TRUE -> constants.trueValue
BooleanConstantReference.FALSE -> constants.falseValue
ConstantReference.NULL -> constants.nullValue
ConstantReference.NOT_NULL -> constants.notNullValue
ConstantReference.WILDCARD -> constants.wildcard
else -> null
}
}
}
@@ -22,18 +22,18 @@ import org.jetbrains.kotlin.contracts.description.ContractDescription
import org.jetbrains.kotlin.contracts.description.EffectDeclaration
import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference
import org.jetbrains.kotlin.contracts.description.expressions.VariableReference
import org.jetbrains.kotlin.contracts.model.ESComponents
import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.ESExpression
import org.jetbrains.kotlin.contracts.model.Functor
import org.jetbrains.kotlin.contracts.model.functors.SubstitutingFunctor
import org.jetbrains.kotlin.contracts.model.structure.ESConstant
import org.jetbrains.kotlin.contracts.model.structure.ESVariable
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
/**
* This class manages conversion of [ContractDescription] to [Functor]
*/
class ContractInterpretationDispatcher(internal val module: ModuleDescriptor) {
class ContractInterpretationDispatcher(val components: ESComponents) {
private val constantsInterpreter = ConstantValuesInterpreter()
private val conditionInterpreter = ConditionInterpreter(this)
private val conditionalEffectInterpreter = ConditionalEffectInterpreter(this)
@@ -51,7 +51,7 @@ class ContractInterpretationDispatcher(internal val module: ModuleDescriptor) {
}
}
return SubstitutingFunctor(resultingClauses, contractDescription.ownerFunction, module.builtIns)
return SubstitutingFunctor(components, resultingClauses, contractDescription.ownerFunction)
}
internal fun interpretEffect(effectDeclaration: EffectDeclaration): ESEffect? {
@@ -60,7 +60,7 @@ class ContractInterpretationDispatcher(internal val module: ModuleDescriptor) {
}
internal fun interpretConstant(constantReference: ConstantReference): ESConstant? =
constantsInterpreter.interpretConstant(constantReference)
constantsInterpreter.interpretConstant(constantReference, components.constants)
internal fun interpretCondition(booleanExpression: BooleanExpression): ESExpression? =
booleanExpression.accept(conditionInterpreter, Unit)
@@ -51,4 +51,4 @@ internal class ReturnsEffectInterpreter(private val dispatcher: ContractInterpre
val returnedValue = dispatcher.interpretConstant(effectDeclaration.value) ?: return null
return ESReturns(returnedValue)
}
}
}
@@ -0,0 +1,15 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.contracts.model
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
class ESComponents(
val builtIns: KotlinBuiltIns
) {
val constants: ESConstants = ESConstants(builtIns)
}
@@ -19,11 +19,10 @@ package org.jetbrains.kotlin.contracts.model.functors
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.structure.ESConstant
import org.jetbrains.kotlin.contracts.model.structure.isReturns
import org.jetbrains.kotlin.contracts.model.structure.isWildcard
import org.jetbrains.kotlin.contracts.model.ESExpression
import org.jetbrains.kotlin.contracts.model.structure.*
abstract class AbstractBinaryFunctor : AbstractReducingFunctor() {
abstract class AbstractBinaryFunctor(constants: ESConstants) : AbstractReducingFunctor(constants) {
override fun doInvocation(arguments: List<Computation>): 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])
@@ -46,7 +45,14 @@ abstract class AbstractBinaryFunctor : AbstractReducingFunctor() {
return nonInterestingEffects + evaluatedByFunctor
}
protected fun foldConditionsWithOr(list: List<ConditionalEffect>): ESExpression? =
if (list.isEmpty())
null
else
list.map { it.condition }.reduce { acc, condition -> ESOr(constants, acc, condition) }
protected abstract fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect>
protected abstract fun invokeWithReturningEffects(
left: List<ConditionalEffect>,
right: List<ConditionalEffect>
@@ -16,19 +16,21 @@
package org.jetbrains.kotlin.contracts.model.functors
import org.jetbrains.kotlin.contracts.model.Computation
import org.jetbrains.kotlin.contracts.model.ESComponents
import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.Functor
import org.jetbrains.kotlin.contracts.model.Computation
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
/**
* Abstract implementation of Functor with some routine house-holding
* automatically performed. *
*/
abstract class AbstractReducingFunctor : Functor {
private val reducer = Reducer()
abstract class AbstractReducingFunctor(internal val constants: ESConstants) : Functor {
private val reducer = Reducer(constants)
override fun invokeWithArguments(arguments: List<Computation>): List<ESEffect> = reducer.reduceEffects(doInvocation(arguments))
protected abstract fun doInvocation(arguments: List<Computation>): List<ESEffect>
}
}
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.contracts.model.functors
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.structure.ESConstants
import org.jetbrains.kotlin.contracts.model.structure.isReturns
import org.jetbrains.kotlin.contracts.model.structure.isWildcard
@@ -29,7 +30,7 @@ import org.jetbrains.kotlin.contracts.model.structure.isWildcard
* It provides [applyToFinishingClauses] method for successors, which is guaranteed to
* be called only on clauses that haven't failed before reaching functor transformation.
*/
abstract class AbstractUnaryFunctor : AbstractReducingFunctor() {
abstract class AbstractUnaryFunctor(constants: ESConstants) : AbstractReducingFunctor(constants) {
override fun doInvocation(arguments: List<Computation>): List<ESEffect> {
assert(arguments.size == 1) { "Wrong size of arguments list for Unary operator: expected 1, got ${arguments.size}" }
return invokeWithArguments(arguments[0])
@@ -21,12 +21,12 @@ import org.jetbrains.kotlin.contracts.model.ConditionalEffect
import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.structure.*
class AndFunctor : AbstractBinaryFunctor() {
override fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect> = when (constant) {
ESConstant.TRUE -> computation.effects
ESConstant.FALSE -> emptyList()
class AndFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) {
override fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect> = when {
constant.isTrue -> computation.effects
constant.isFalse -> emptyList()
// This means that expression isn't typechecked properly
// This means that expression isn't typechecked properly
else -> computation.effects
}
@@ -48,20 +48,20 @@ class AndFunctor : AbstractBinaryFunctor() {
// Even if one of 'Returns(true)' is missing, we still can argue that other condition
// *must* be true when whole functor returns true
val conditionWhenTrue = applyWithDefault(whenLeftReturnsTrue, whenRightReturnsTrue, { l, r -> ESAnd(l, r) })
val conditionWhenTrue = applyWithDefault(whenLeftReturnsTrue, whenRightReturnsTrue) { l, r -> ESAnd(constants, l, r) }
// When whole And-functor returns false, we can only argue that one of arguments was false, and to do so we
// have to know *both* 'Returns(false)'-conditions
val conditionWhenFalse = applyIfBothNotNull(whenLeftReturnsFalse, whenRightReturnsFalse, { l, r -> ESOr(l, r) })
val conditionWhenFalse = applyIfBothNotNull(whenLeftReturnsFalse, whenRightReturnsFalse) { l, r -> ESOr(constants, l, r) }
val result = mutableListOf<ConditionalEffect>()
if (conditionWhenTrue != null) {
result.add(ConditionalEffect(conditionWhenTrue, ESReturns(true.lift())))
result.add(ConditionalEffect(conditionWhenTrue, ESReturns(constants.trueValue)))
}
if (conditionWhenFalse != null) {
result.add(ConditionalEffect(conditionWhenFalse, ESReturns(false.lift())))
result.add(ConditionalEffect(conditionWhenFalse, ESReturns(constants.falseValue)))
}
return result
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.ESValue
import org.jetbrains.kotlin.contracts.model.structure.*
class EqualsFunctor(val isNegated: Boolean) : AbstractReducingFunctor() {
class EqualsFunctor(constants: ESConstants, val isNegated: Boolean) : AbstractReducingFunctor(constants) {
/*
Equals is a bit tricky case to produce clauses, because e.g. if we want to emit "Returns(true)"-clause,
then we have to guarantee that we know *all* cases when 'true' could've been returned, and join
@@ -77,7 +77,7 @@ class EqualsFunctor(val isNegated: Boolean) : AbstractReducingFunctor() {
}
if (effect.simpleEffect.value == constant) {
val trueClause = ConditionalEffect(effect.condition, ESReturns(isNegated.not().lift()))
val trueClause = ConditionalEffect(effect.condition, ESReturns(constants.booleanValue(isNegated.not())))
resultingClauses.add(trueClause)
}
@@ -86,7 +86,7 @@ class EqualsFunctor(val isNegated: Boolean) : AbstractReducingFunctor() {
effect.simpleEffect.value,
constant
)) {
val falseClause = ConditionalEffect(effect.condition, ESReturns(isNegated.lift()))
val falseClause = ConditionalEffect(effect.condition, ESReturns(constants.booleanValue(isNegated)))
resultingClauses.add(falseClause)
}
}
@@ -108,8 +108,8 @@ class EqualsFunctor(val isNegated: Boolean) : AbstractReducingFunctor() {
private fun equateValues(left: ESValue, right: ESValue): List<ESEffect> {
return listOf(
ConditionalEffect(ESEqual(left, right, isNegated), ESReturns(true.lift())),
ConditionalEffect(ESEqual(left, right, isNegated.not()), ESReturns(false.lift()))
ConditionalEffect(ESEqual(constants, left, right, isNegated), ESReturns(constants.trueValue)),
ConditionalEffect(ESEqual(constants, left, right, isNegated.not()), ESReturns(constants.falseValue))
)
}
}
@@ -16,10 +16,6 @@
package org.jetbrains.kotlin.contracts.model.functors
import org.jetbrains.kotlin.contracts.model.ConditionalEffect
import org.jetbrains.kotlin.contracts.model.ESExpression
import org.jetbrains.kotlin.contracts.model.structure.ESOr
/**
* Applies [operation] to [first] and [second] if both not-null, otherwise returns null
*/
@@ -37,9 +33,3 @@ internal fun <F : R, S : R, R> applyWithDefault(first: F?, second: S?, operation
second == null -> first
else -> operation(first, second)
}
internal fun foldConditionsWithOr(list: List<ConditionalEffect>): ESExpression? =
if (list.isEmpty())
null
else
list.map { it.condition }.reduce { acc, condition -> ESOr(acc, condition) }
@@ -16,14 +16,18 @@
package org.jetbrains.kotlin.contracts.model.functors
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.ESValue
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
import org.jetbrains.kotlin.contracts.model.structure.ESIs
import org.jetbrains.kotlin.contracts.model.structure.ESReturns
import org.jetbrains.kotlin.contracts.model.*
import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.types.KotlinType
class IsFunctor(val type: KotlinType, val isNegated: Boolean) : AbstractReducingFunctor() {
class IsFunctor(constants: ESConstants, val type: KotlinType, val isNegated: Boolean) : AbstractReducingFunctor(constants) {
override fun doInvocation(arguments: List<Computation>): List<ESEffect> {
assert(arguments.size == 1, { "Wrong size of arguments list for Unary operator: expected 1, got ${arguments.size}" })
assert(arguments.size == 1) { "Wrong size of arguments list for Unary operator: expected 1, got ${arguments.size}" }
return invokeWithArguments(arguments[0])
}
@@ -36,10 +40,10 @@ class IsFunctor(val type: KotlinType, val isNegated: Boolean) : AbstractReducing
private fun invokeWithValue(value: ESValue): List<ConditionalEffect> {
val trueIs = ESIs(value, this)
val falseIs = ESIs(value, IsFunctor(type, isNegated.not()))
val falseIs = ESIs(value, IsFunctor(constants, type, isNegated.not()))
val trueResult = ConditionalEffect(trueIs, ESReturns(true.lift()))
val falseResult = ConditionalEffect(falseIs, ESReturns(false.lift()))
val trueResult = ConditionalEffect(trueIs, ESReturns(constants.trueValue))
val falseResult = ConditionalEffect(falseIs, ESReturns(constants.falseValue))
return listOf(trueResult, falseResult)
}
}
}
@@ -16,11 +16,13 @@
package org.jetbrains.kotlin.contracts.model.functors
import org.jetbrains.kotlin.contracts.model.structure.ESReturns
import org.jetbrains.kotlin.contracts.model.structure.ESConstant
import org.jetbrains.kotlin.contracts.model.ConditionalEffect
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
import org.jetbrains.kotlin.contracts.model.structure.ESReturns
import org.jetbrains.kotlin.contracts.model.structure.isFalse
import org.jetbrains.kotlin.contracts.model.structure.isTrue
class NotFunctor : AbstractUnaryFunctor() {
class NotFunctor(constants: ESConstants) : AbstractUnaryFunctor(constants) {
override fun invokeWithReturningEffects(list: List<ConditionalEffect>): List<ConditionalEffect> = list.mapNotNull {
val outcome = it.simpleEffect
@@ -28,10 +30,10 @@ class NotFunctor : AbstractUnaryFunctor() {
// can be non-boolean in case of type-errors in the whole expression, like "foo(bar) && 1"
val returnValue = (outcome as ESReturns).value
when (returnValue) {
ESConstant.TRUE -> ConditionalEffect(it.condition, ESReturns(ESConstant.FALSE))
ESConstant.FALSE -> ConditionalEffect(it.condition, ESReturns(ESConstant.TRUE))
when {
returnValue.isTrue -> ConditionalEffect(it.condition, ESReturns(constants.falseValue))
returnValue.isFalse -> ConditionalEffect(it.condition, ESReturns(constants.trueValue))
else -> null
}
}
}
}
@@ -21,12 +21,12 @@ import org.jetbrains.kotlin.contracts.model.ConditionalEffect
import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.structure.*
class OrFunctor : AbstractBinaryFunctor() {
override fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect> = when (constant) {
ESConstant.FALSE -> computation.effects
ESConstant.TRUE -> emptyList()
class OrFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) {
override fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect> = when {
constant.isFalse -> computation.effects
constant.isTrue -> emptyList()
// This means that expression isn't typechecked properly
// This means that expression isn't typechecked properly
else -> computation.effects
}
@@ -48,20 +48,20 @@ class OrFunctor : AbstractBinaryFunctor() {
// When whole Or-functor returns true, all we know is that one of arguments was true.
// So, to make a correct clause we have to know *both* 'Returns(true)'-conditions
val conditionWhenTrue = applyIfBothNotNull(whenLeftReturnsTrue, whenRightReturnsTrue, { l, r -> ESOr(l, r) })
val conditionWhenTrue = applyIfBothNotNull(whenLeftReturnsTrue, whenRightReturnsTrue) { l, r -> ESOr(constants, l, r) }
// Even if one of 'Returns(false)' is missing, we still can argue that other condition
// *must* be false when whole OR-functor returns false
val conditionWhenFalse = applyWithDefault(whenLeftReturnsFalse, whenRightReturnsFalse, { l, r -> ESAnd(l, r) })
val conditionWhenFalse = applyWithDefault(whenLeftReturnsFalse, whenRightReturnsFalse) { l, r -> ESAnd(constants, l, r) }
val result = mutableListOf<ConditionalEffect>()
if (conditionWhenTrue != null) {
result.add(ConditionalEffect(conditionWhenTrue, ESReturns(true.lift())))
result.add(ConditionalEffect(conditionWhenTrue, ESReturns(constants.trueValue)))
}
if (conditionWhenFalse != null) {
result.add(ConditionalEffect(conditionWhenFalse, ESReturns(false.lift())))
result.add(ConditionalEffect(conditionWhenFalse, ESReturns(constants.falseValue)))
}
return result
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.contracts.model.functors
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.contracts.model.*
import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.contracts.model.visitors.Substitutor
@@ -25,10 +24,10 @@ import org.jetbrains.kotlin.descriptors.ValueDescriptor
import org.jetbrains.kotlin.utils.addIfNotNull
class SubstitutingFunctor(
private val components: ESComponents,
private val basicEffects: List<ESEffect>,
private val ownerFunction: FunctionDescriptor,
private val builtIns: KotlinBuiltIns
) : AbstractReducingFunctor() {
private val ownerFunction: FunctionDescriptor
) : AbstractReducingFunctor(components.constants) {
override fun doInvocation(arguments: List<Computation>): List<ESEffect> {
if (basicEffects.isEmpty()) return emptyList()
@@ -41,7 +40,7 @@ class SubstitutingFunctor(
}
val substitutions = parameters.zip(arguments).toMap()
val substitutor = Substitutor(substitutions, builtIns)
val substitutor = Substitutor(substitutions, components.builtIns)
val substitutedClauses = mutableListOf<ESEffect>()
effectsLoop@ for (effect in basicEffects) {
@@ -22,20 +22,19 @@ import org.jetbrains.kotlin.contracts.model.ESOperator
import org.jetbrains.kotlin.contracts.model.ESValue
import org.jetbrains.kotlin.contracts.model.functors.*
class ESAnd(val left: ESExpression, val right: ESExpression) : ESOperator {
override val functor: AndFunctor = AndFunctor()
class ESAnd(val constants: ESConstants, val left: ESExpression, val right: ESExpression) : ESOperator {
override val functor: AndFunctor = AndFunctor(constants)
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitAnd(this)
}
class ESOr(val left: ESExpression, val right: ESExpression) : ESOperator {
override val functor: OrFunctor = OrFunctor()
class ESOr(val constants: ESConstants, val left: ESExpression, val right: ESExpression) : ESOperator {
override val functor: OrFunctor = OrFunctor(constants)
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitOr(this)
}
class ESNot(val arg: ESExpression) : ESOperator {
override val functor = NotFunctor()
class ESNot(val constants: ESConstants, val arg: ESExpression) : ESOperator {
override val functor = NotFunctor(constants)
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitNot(this)
}
class ESIs(val left: ESValue, override val functor: IsFunctor) : ESOperator {
@@ -43,10 +42,13 @@ class ESIs(val left: ESValue, override val functor: IsFunctor) : ESOperator {
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitIs(this)
}
class ESEqual(val left: ESValue, val right: ESValue, isNegated: Boolean) : ESOperator {
override val functor: EqualsFunctor = EqualsFunctor(isNegated)
class ESEqual(val constants: ESConstants, val left: ESValue, val right: ESValue, isNegated: Boolean) : ESOperator {
override val functor: EqualsFunctor = EqualsFunctor(constants, isNegated)
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitEqual(this)
}
fun ESExpression.and(other: ESExpression?): ESExpression = if (other == null) this else ESAnd(this, other)
fun ESExpression.or(other: ESExpression?): ESExpression = if (other == null) this else ESOr(this, other)
fun ESExpression.and(other: ESExpression?, constants: ESConstants): ESExpression =
if (other == null) this else ESAnd(constants, this, other)
fun ESExpression.or(other: ESExpression?, constants: ESConstants): ESExpression =
if (other == null) this else ESOr(constants, this, other)
@@ -16,7 +16,7 @@
package org.jetbrains.kotlin.contracts.model.structure
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.contracts.description.expressions.BooleanConstantReference
import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference
import org.jetbrains.kotlin.contracts.model.ESExpression
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.contracts.model.ESExpressionVisitor
import org.jetbrains.kotlin.contracts.model.ESValue
import org.jetbrains.kotlin.descriptors.ValueDescriptor
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import java.util.*
open class ESVariable(val descriptor: ValueDescriptor) : ESValue(descriptor.type) {
@@ -46,36 +45,29 @@ open class ESVariable(val descriptor: ValueDescriptor) : ESValue(descriptor.type
override fun toString(): String = descriptor.toString()
}
open class ESConstant private constructor(open val constantReference: ConstantReference, override val type: KotlinType) : ESValue(type) {
class ESConstant internal constructor(val constantReference: ConstantReference, override val type: KotlinType) : ESValue(type) {
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitConstant(this)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as ESConstant
if (constantReference != other.constantReference) return false
return true
}
override fun equals(other: Any?): Boolean = other is ESConstant && constantReference == other.constantReference
override fun hashCode(): Int = Objects.hashCode(constantReference)
override fun toString(): String = constantReference.name
companion object {
val TRUE = ESConstant(BooleanConstantReference.TRUE, DefaultBuiltIns.Instance.booleanType)
val FALSE = ESConstant(BooleanConstantReference.FALSE, DefaultBuiltIns.Instance.booleanType)
val NULL = ESConstant(ConstantReference.NULL, DefaultBuiltIns.Instance.nothingType.makeNullable())
val NOT_NULL = ESConstant(ConstantReference.NOT_NULL, DefaultBuiltIns.Instance.anyType)
val WILDCARD = ESConstant(ConstantReference.WILDCARD, DefaultBuiltIns.Instance.anyType.makeNullable())
}
fun isNullConstant(): Boolean = this == NULL || this == NOT_NULL
fun isNullConstant(): Boolean =
constantReference == ConstantReference.NULL || constantReference == ConstantReference.NOT_NULL
}
fun Boolean.lift(): ESConstant = if (this) ESConstant.TRUE else ESConstant.FALSE
class ESConstants internal constructor(builtIns: KotlinBuiltIns) {
val trueValue = ESConstant(BooleanConstantReference.TRUE, builtIns.booleanType)
val falseValue = ESConstant(BooleanConstantReference.FALSE, builtIns.booleanType)
val nullValue = ESConstant(ConstantReference.NULL, builtIns.nullableNothingType)
val notNullValue = ESConstant(ConstantReference.NOT_NULL, builtIns.anyType)
val wildcard = ESConstant(ConstantReference.WILDCARD, builtIns.nullableAnyType)
fun booleanValue(value: Boolean) =
if (value) trueValue else falseValue
}
internal val ESExpression.isTrue: Boolean
get() = this is ESConstant && constantReference == BooleanConstantReference.TRUE
@@ -16,11 +16,17 @@
package org.jetbrains.kotlin.contracts.model.visitors
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.contracts.model.*
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.contracts.model.ConditionalEffect
import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.ESExpressionVisitor
import org.jetbrains.kotlin.contracts.model.MutableContextInfo
import org.jetbrains.kotlin.contracts.model.structure.*
class InfoCollector(private val observedEffect: ESEffect) : ESExpressionVisitor<MutableContextInfo> {
class InfoCollector(
private val observedEffect: ESEffect,
private val constants: ESConstants
) : ESExpressionVisitor<MutableContextInfo> {
private var isInverted: Boolean = false
fun collectFromSchema(schema: List<ESEffect>): MutableContextInfo =
@@ -69,12 +75,11 @@ class InfoCollector(private val observedEffect: ESEffect) : ESExpressionVisitor<
return if (isInverted) leftInfo.and(rightInfo) else leftInfo.or(rightInfo)
}
override fun visitVariable(esVariable: ESVariable): MutableContextInfo {
return if (esVariable.type != DefaultBuiltIns.Instance.booleanType)
override fun visitVariable(esVariable: ESVariable): MutableContextInfo =
if (esVariable.type.let { it == null || !KotlinBuiltIns.isBoolean(it) })
MutableContextInfo.EMPTY
else
MutableContextInfo.EMPTY.equal(esVariable, isInverted.not().lift())
}
MutableContextInfo.EMPTY.equal(esVariable, constants.booleanValue(!isInverted))
override fun visitConstant(esConstant: ESConstant): MutableContextInfo = MutableContextInfo.EMPTY
@@ -85,4 +90,3 @@ class InfoCollector(private val observedEffect: ESEffect) : ESExpressionVisitor<
return result
}
}
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
* Reduces given list of effects by evaluating constant expressions,
* throwing away senseless checks and infeasible clauses, etc.
*/
class Reducer : ESExpressionVisitor<ESExpression?> {
class Reducer(private val constants: ESConstants) : ESExpressionVisitor<ESExpression?> {
fun reduceEffects(schema: List<ESEffect>): List<ESEffect> =
schema.mapNotNull { reduceEffect(it) }
@@ -59,16 +59,16 @@ class Reducer : ESExpressionVisitor<ESExpression?> {
// Result is unknown, do not evaluate
result ?: return ESIs(reducedArg, isOperator.functor)
return result.xor(isOperator.functor.isNegated).lift()
return constants.booleanValue(result.xor(isOperator.functor.isNegated))
}
override fun visitEqual(equal: ESEqual): ESExpression {
val reducedLeft = equal.left.accept(this) as ESValue
val reducedRight = equal.right
if (reducedLeft is ESConstant) return (reducedLeft == reducedRight).xor(equal.functor.isNegated).lift()
if (reducedLeft is ESConstant) return constants.booleanValue((reducedLeft == reducedRight).xor(equal.functor.isNegated))
return ESEqual(reducedLeft, reducedRight, equal.functor.isNegated)
return ESEqual(constants, reducedLeft, reducedRight, equal.functor.isNegated)
}
override fun visitAnd(and: ESAnd): ESExpression? {
@@ -79,7 +79,7 @@ class Reducer : ESExpressionVisitor<ESExpression?> {
reducedLeft.isFalse || reducedRight.isFalse -> reducedLeft
reducedLeft.isTrue -> reducedRight
reducedRight.isTrue -> reducedLeft
else -> ESAnd(reducedLeft, reducedRight)
else -> ESAnd(constants, reducedLeft, reducedRight)
}
}
@@ -91,16 +91,16 @@ class Reducer : ESExpressionVisitor<ESExpression?> {
reducedLeft.isTrue || reducedRight.isTrue -> reducedLeft
reducedLeft.isFalse -> reducedRight
reducedRight.isFalse -> reducedLeft
else -> ESOr(reducedLeft, reducedRight)
else -> ESOr(constants, reducedLeft, reducedRight)
}
}
override fun visitNot(not: ESNot): ESExpression? {
val reducedArg = not.arg.accept(this) ?: return null
return when (reducedArg) {
ESConstant.TRUE -> ESConstant.FALSE
ESConstant.FALSE -> ESConstant.TRUE
return when {
reducedArg.isTrue -> constants.falseValue
reducedArg.isFalse -> constants.trueValue
else -> reducedArg
}
}