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 package org.jetbrains.kotlin.contracts
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.model.Computation 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.ESEffect
import org.jetbrains.kotlin.contracts.model.MutableContextInfo import org.jetbrains.kotlin.contracts.model.MutableContextInfo
import org.jetbrains.kotlin.contracts.model.functors.EqualsFunctor 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.contracts.model.visitors.InfoCollector
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.psi.KtCallExpression 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.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory 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( fun getDataFlowInfoForFinishedCall(
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
@@ -48,9 +63,9 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return DataFlowInfo.EMPTY val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return DataFlowInfo.EMPTY
if (callExpression is KtDeclaration) 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( fun getDataFlowInfoWhenEquals(
@@ -67,11 +82,10 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
val rightComputation = val rightComputation =
getNonTrivialComputation(rightExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY 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(constants.trueValue), constants).collectFromSchema(effects)
val equalsContextInfo = InfoCollector(ESReturns(true.lift())).collectFromSchema(effects) val notEqualsContextInfo = InfoCollector(ESReturns(constants.falseValue), constants).collectFromSchema(effects)
val notEqualsContextInfo = InfoCollector(ESReturns(false.lift())).collectFromSchema(effects)
return ConditionalDataFlowInfo( return ConditionalDataFlowInfo(
equalsContextInfo.toDataFlowInfo(languageVersionSettings, builtIns), equalsContextInfo.toDataFlowInfo(languageVersionSettings, builtIns),
@@ -86,7 +100,7 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return
if (callExpression is KtDeclaration) 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) { for (effect in resultingContextInfo.firedEffects) {
val callsEffect = effect as? ESCalls ?: continue val callsEffect = effect as? ESCalls ?: continue
val lambdaExpression = (callsEffect.callable as? ESLambda)?.lambda ?: 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 (!languageVersionSettings.supportsFeature(LanguageFeature.UseReturnsEffect)) return DataFlowInfo.EMPTY
if (condition == null) 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) .toDataFlowInfo(languageVersionSettings, moduleDescriptor.builtIns)
} }
@@ -114,11 +128,11 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
moduleDescriptor: ModuleDescriptor moduleDescriptor: ModuleDescriptor
): MutableContextInfo { ): MutableContextInfo {
val computation = getNonTrivialComputation(expression, bindingTrace, moduleDescriptor) ?: return MutableContextInfo.EMPTY 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? { private fun getNonTrivialComputation(expression: KtExpression, trace: BindingTrace, moduleDescriptor: ModuleDescriptor): Computation? {
val computation = EffectsExtractingVisitor(trace, moduleDescriptor, dataFlowValueFactory).extractOrGetCached(expression) val visitor = EffectsExtractingVisitor(trace, moduleDescriptor, dataFlowValueFactory, constants)
return if (computation == UNKNOWN_COMPUTATION) null else computation 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.ESEffect
import org.jetbrains.kotlin.contracts.model.Functor import org.jetbrains.kotlin.contracts.model.Functor
import org.jetbrains.kotlin.contracts.model.functors.* 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.contracts.parsing.isEqualsDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -51,9 +54,10 @@ import org.jetbrains.kotlin.utils.addIfNotNull
class EffectsExtractingVisitor( class EffectsExtractingVisitor(
private val trace: BindingTrace, private val trace: BindingTrace,
private val moduleDescriptor: ModuleDescriptor, private val moduleDescriptor: ModuleDescriptor,
private val dataFlowValueFactory: DataFlowValueFactory private val dataFlowValueFactory: DataFlowValueFactory,
private val constants: ESConstants
) : KtVisitor<Computation, Unit>() { ) : KtVisitor<Computation, Unit>() {
private val builtIns: KotlinBuiltIns = moduleDescriptor.builtIns private val builtIns: KotlinBuiltIns get() = moduleDescriptor.builtIns
fun extractOrGetCached(element: KtElement): Computation { fun extractOrGetCached(element: KtElement): Computation {
trace[BindingContext.EXPRESSION_EFFECTS, element]?.let { return it } trace[BindingContext.EXPRESSION_EFFECTS, element]?.let { return it }
@@ -70,7 +74,7 @@ class EffectsExtractingVisitor(
return when { return when {
descriptor.isEqualsDescriptor() -> CallComputation( descriptor.isEqualsDescriptor() -> CallComputation(
builtIns.booleanType, builtIns.booleanType,
EqualsFunctor(false).invokeWithArguments(arguments) EqualsFunctor(constants, false).invokeWithArguments(arguments)
) )
descriptor is ValueDescriptor -> ESDataFlowValue( descriptor is ValueDescriptor -> ESDataFlowValue(
descriptor, descriptor,
@@ -103,8 +107,8 @@ class EffectsExtractingVisitor(
val value: Any? = compileTimeConstant.getValue(type) val value: Any? = compileTimeConstant.getValue(type)
return when (value) { return when (value) {
is Boolean -> value.lift() is Boolean -> constants.booleanValue(value)
null -> ESConstant.NULL null -> constants.nullValue
else -> UNKNOWN_COMPUTATION else -> UNKNOWN_COMPUTATION
} }
} }
@@ -114,7 +118,7 @@ class EffectsExtractingVisitor(
val arg = extractOrGetCached(expression.leftHandSide) val arg = extractOrGetCached(expression.leftHandSide)
return CallComputation( return CallComputation(
builtIns.booleanType, 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 // null bypassing function's contract, so we have to filter them out
fun ESEffect.containsReturnsNull(): Boolean = 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() } val effectsWithoutReturnsNull = computation.effects.filter { !it.containsReturnsNull() }
return CallComputation(computation.type, effectsWithoutReturnsNull) return CallComputation(computation.type, effectsWithoutReturnsNull)
@@ -139,10 +143,10 @@ class EffectsExtractingVisitor(
val args = listOf(left, right) val args = listOf(left, right)
return when (expression.operationToken) { return when (expression.operationToken) {
KtTokens.EXCLEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(true).invokeWithArguments(args)) KtTokens.EXCLEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(constants, true).invokeWithArguments(args))
KtTokens.EQEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(false).invokeWithArguments(args)) KtTokens.EQEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(constants, false).invokeWithArguments(args))
KtTokens.ANDAND -> CallComputation(builtIns.booleanType, AndFunctor().invokeWithArguments(args)) KtTokens.ANDAND -> CallComputation(builtIns.booleanType, AndFunctor(constants).invokeWithArguments(args))
KtTokens.OROR -> CallComputation(builtIns.booleanType, OrFunctor().invokeWithArguments(args)) KtTokens.OROR -> CallComputation(builtIns.booleanType, OrFunctor(constants).invokeWithArguments(args))
else -> UNKNOWN_COMPUTATION else -> UNKNOWN_COMPUTATION
} }
} }
@@ -150,7 +154,7 @@ class EffectsExtractingVisitor(
override fun visitUnaryExpression(expression: KtUnaryExpression, data: Unit): Computation { override fun visitUnaryExpression(expression: KtUnaryExpression, data: Unit): Computation {
val arg = extractOrGetCached(expression.baseExpression ?: return UNKNOWN_COMPUTATION) val arg = extractOrGetCached(expression.baseExpression ?: return UNKNOWN_COMPUTATION)
return when (expression.operationToken) { return when (expression.operationToken) {
KtTokens.EXCL -> CallComputation(builtIns.booleanType, NotFunctor().invokeWithArguments(arg)) KtTokens.EXCL -> CallComputation(builtIns.booleanType, NotFunctor(constants).invokeWithArguments(arg))
else -> UNKNOWN_COMPUTATION else -> UNKNOWN_COMPUTATION
} }
} }
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.contracts.description package org.jetbrains.kotlin.contracts.description
import org.jetbrains.kotlin.contracts.interpretation.ContractInterpretationDispatcher 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.contracts.model.Functor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -40,7 +41,8 @@ open class ContractDescription(
storageManager: StorageManager storageManager: StorageManager
) { ) {
private val computeFunctor = storageManager.createMemoizedFunctionWithNullableValues<ModuleDescriptor, Functor> { module -> 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) fun getFunctor(usageModule: ModuleDescriptor): Functor? = computeFunctor(usageModule)
@@ -16,39 +16,42 @@
package org.jetbrains.kotlin.contracts.interpretation 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.description.expressions.*
import org.jetbrains.kotlin.contracts.model.ESExpression import org.jetbrains.kotlin.contracts.model.ESExpression
import org.jetbrains.kotlin.contracts.model.functors.IsFunctor import org.jetbrains.kotlin.contracts.model.functors.IsFunctor
import org.jetbrains.kotlin.contracts.model.structure.* import org.jetbrains.kotlin.contracts.model.structure.*
internal class ConditionInterpreter(private val dispatcher: ContractInterpretationDispatcher) : internal class ConditionInterpreter(
ContractDescriptionVisitor<ESExpression?, Unit> { private val dispatcher: ContractInterpretationDispatcher
) : ContractDescriptionVisitor<ESExpression?, Unit> {
private val constants = dispatcher.components.constants
override fun visitLogicalOr(logicalOr: LogicalOr, data: Unit): ESExpression? { override fun visitLogicalOr(logicalOr: LogicalOr, data: Unit): ESExpression? {
val left = logicalOr.left.accept(this, data) ?: return null val left = logicalOr.left.accept(this, data) ?: return null
val right = logicalOr.right.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? { override fun visitLogicalAnd(logicalAnd: LogicalAnd, data: Unit): ESExpression? {
val left = logicalAnd.left.accept(this, data) ?: return null val left = logicalAnd.left.accept(this, data) ?: return null
val right = logicalAnd.right.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? { override fun visitLogicalNot(logicalNot: LogicalNot, data: Unit): ESExpression? {
val arg = logicalNot.arg.accept(this, data) ?: return null val arg = logicalNot.arg.accept(this, data) ?: return null
return ESNot(arg) return ESNot(constants, arg)
} }
override fun visitIsInstancePredicate(isInstancePredicate: IsInstancePredicate, data: Unit): ESExpression? { override fun visitIsInstancePredicate(isInstancePredicate: IsInstancePredicate, data: Unit): ESExpression? {
val esVariable = dispatcher.interpretVariable(isInstancePredicate.arg) ?: return null 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? { override fun visitIsNullPredicate(isNullPredicate: IsNullPredicate, data: Unit): ESExpression? {
val variable = dispatcher.interpretVariable(isNullPredicate.arg) ?: return null 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? = 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? = override fun visitBooleanVariableReference(booleanVariableReference: BooleanVariableReference, data: Unit): ESExpression? =
dispatcher.interpretVariable(booleanVariableReference) 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.BooleanConstantReference
import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference
import org.jetbrains.kotlin.contracts.model.structure.ESConstant 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 { internal class ConstantValuesInterpreter {
fun interpretConstant(constantReference: ConstantReference): ESConstant? = when (constantReference) { fun interpretConstant(constantReference: ConstantReference, constants: ESConstants): ESConstant? = when (constantReference) {
BooleanConstantReference.TRUE -> true.lift() BooleanConstantReference.TRUE -> constants.trueValue
BooleanConstantReference.FALSE -> false.lift() BooleanConstantReference.FALSE -> constants.falseValue
ConstantReference.NULL -> ESConstant.NULL ConstantReference.NULL -> constants.nullValue
ConstantReference.NOT_NULL -> ESConstant.NOT_NULL ConstantReference.NOT_NULL -> constants.notNullValue
ConstantReference.WILDCARD -> ESConstant.WILDCARD ConstantReference.WILDCARD -> constants.wildcard
else -> null 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.EffectDeclaration
import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference
import org.jetbrains.kotlin.contracts.description.expressions.VariableReference 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.ESEffect
import org.jetbrains.kotlin.contracts.model.ESExpression import org.jetbrains.kotlin.contracts.model.ESExpression
import org.jetbrains.kotlin.contracts.model.Functor import org.jetbrains.kotlin.contracts.model.Functor
import org.jetbrains.kotlin.contracts.model.functors.SubstitutingFunctor import org.jetbrains.kotlin.contracts.model.functors.SubstitutingFunctor
import org.jetbrains.kotlin.contracts.model.structure.ESConstant import org.jetbrains.kotlin.contracts.model.structure.ESConstant
import org.jetbrains.kotlin.contracts.model.structure.ESVariable import org.jetbrains.kotlin.contracts.model.structure.ESVariable
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
/** /**
* This class manages conversion of [ContractDescription] to [Functor] * 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 constantsInterpreter = ConstantValuesInterpreter()
private val conditionInterpreter = ConditionInterpreter(this) private val conditionInterpreter = ConditionInterpreter(this)
private val conditionalEffectInterpreter = ConditionalEffectInterpreter(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? { internal fun interpretEffect(effectDeclaration: EffectDeclaration): ESEffect? {
@@ -60,7 +60,7 @@ class ContractInterpretationDispatcher(internal val module: ModuleDescriptor) {
} }
internal fun interpretConstant(constantReference: ConstantReference): ESConstant? = internal fun interpretConstant(constantReference: ConstantReference): ESConstant? =
constantsInterpreter.interpretConstant(constantReference) constantsInterpreter.interpretConstant(constantReference, components.constants)
internal fun interpretCondition(booleanExpression: BooleanExpression): ESExpression? = internal fun interpretCondition(booleanExpression: BooleanExpression): ESExpression? =
booleanExpression.accept(conditionInterpreter, Unit) booleanExpression.accept(conditionInterpreter, Unit)
@@ -51,4 +51,4 @@ internal class ReturnsEffectInterpreter(private val dispatcher: ContractInterpre
val returnedValue = dispatcher.interpretConstant(effectDeclaration.value) ?: return null val returnedValue = dispatcher.interpretConstant(effectDeclaration.value) ?: return null
return ESReturns(returnedValue) 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.Computation
import org.jetbrains.kotlin.contracts.model.ConditionalEffect import org.jetbrains.kotlin.contracts.model.ConditionalEffect
import org.jetbrains.kotlin.contracts.model.ESEffect import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.structure.ESConstant import org.jetbrains.kotlin.contracts.model.ESExpression
import org.jetbrains.kotlin.contracts.model.structure.isReturns import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.contracts.model.structure.isWildcard
abstract class AbstractBinaryFunctor : AbstractReducingFunctor() { abstract class AbstractBinaryFunctor(constants: ESConstants) : AbstractReducingFunctor(constants) {
override fun doInvocation(arguments: List<Computation>): List<ESEffect> { 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}" } assert(arguments.size == 2) { "Wrong size of arguments list for Binary functor: expected 2, got ${arguments.size}" }
return invokeWithArguments(arguments[0], arguments[1]) return invokeWithArguments(arguments[0], arguments[1])
@@ -46,7 +45,14 @@ abstract class AbstractBinaryFunctor : AbstractReducingFunctor() {
return nonInterestingEffects + evaluatedByFunctor 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 invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect>
protected abstract fun invokeWithReturningEffects( protected abstract fun invokeWithReturningEffects(
left: List<ConditionalEffect>, left: List<ConditionalEffect>,
right: List<ConditionalEffect> right: List<ConditionalEffect>
@@ -16,19 +16,21 @@
package org.jetbrains.kotlin.contracts.model.functors 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.ESEffect
import org.jetbrains.kotlin.contracts.model.Functor 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 import org.jetbrains.kotlin.contracts.model.visitors.Reducer
/** /**
* Abstract implementation of Functor with some routine house-holding * Abstract implementation of Functor with some routine house-holding
* automatically performed. * * automatically performed. *
*/ */
abstract class AbstractReducingFunctor : Functor { abstract class AbstractReducingFunctor(internal val constants: ESConstants) : Functor {
private val reducer = Reducer() private val reducer = Reducer(constants)
override fun invokeWithArguments(arguments: List<Computation>): List<ESEffect> = reducer.reduceEffects(doInvocation(arguments)) override fun invokeWithArguments(arguments: List<Computation>): List<ESEffect> = reducer.reduceEffects(doInvocation(arguments))
protected abstract fun doInvocation(arguments: List<Computation>): List<ESEffect> 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.Computation
import org.jetbrains.kotlin.contracts.model.ConditionalEffect import org.jetbrains.kotlin.contracts.model.ConditionalEffect
import org.jetbrains.kotlin.contracts.model.ESEffect 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.isReturns
import org.jetbrains.kotlin.contracts.model.structure.isWildcard 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 * It provides [applyToFinishingClauses] method for successors, which is guaranteed to
* be called only on clauses that haven't failed before reaching functor transformation. * 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> { 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]) 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.ESEffect
import org.jetbrains.kotlin.contracts.model.structure.* import org.jetbrains.kotlin.contracts.model.structure.*
class AndFunctor : AbstractBinaryFunctor() { class AndFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) {
override fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect> = when (constant) { override fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect> = when {
ESConstant.TRUE -> computation.effects constant.isTrue -> computation.effects
ESConstant.FALSE -> emptyList() constant.isFalse -> emptyList()
// This means that expression isn't typechecked properly // This means that expression isn't typechecked properly
else -> computation.effects 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 // Even if one of 'Returns(true)' is missing, we still can argue that other condition
// *must* be true when whole functor returns true // *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 // 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 // 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>() val result = mutableListOf<ConditionalEffect>()
if (conditionWhenTrue != null) { if (conditionWhenTrue != null) {
result.add(ConditionalEffect(conditionWhenTrue, ESReturns(true.lift()))) result.add(ConditionalEffect(conditionWhenTrue, ESReturns(constants.trueValue)))
} }
if (conditionWhenFalse != null) { if (conditionWhenFalse != null) {
result.add(ConditionalEffect(conditionWhenFalse, ESReturns(false.lift()))) result.add(ConditionalEffect(conditionWhenFalse, ESReturns(constants.falseValue)))
} }
return result 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.ESValue
import org.jetbrains.kotlin.contracts.model.structure.* 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, 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 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) { 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) resultingClauses.add(trueClause)
} }
@@ -86,7 +86,7 @@ class EqualsFunctor(val isNegated: Boolean) : AbstractReducingFunctor() {
effect.simpleEffect.value, effect.simpleEffect.value,
constant constant
)) { )) {
val falseClause = ConditionalEffect(effect.condition, ESReturns(isNegated.lift())) val falseClause = ConditionalEffect(effect.condition, ESReturns(constants.booleanValue(isNegated)))
resultingClauses.add(falseClause) resultingClauses.add(falseClause)
} }
} }
@@ -108,8 +108,8 @@ class EqualsFunctor(val isNegated: Boolean) : AbstractReducingFunctor() {
private fun equateValues(left: ESValue, right: ESValue): List<ESEffect> { private fun equateValues(left: ESValue, right: ESValue): List<ESEffect> {
return listOf( return listOf(
ConditionalEffect(ESEqual(left, right, isNegated), ESReturns(true.lift())), ConditionalEffect(ESEqual(constants, left, right, isNegated), ESReturns(constants.trueValue)),
ConditionalEffect(ESEqual(left, right, isNegated.not()), ESReturns(false.lift())) ConditionalEffect(ESEqual(constants, left, right, isNegated.not()), ESReturns(constants.falseValue))
) )
} }
} }
@@ -16,10 +16,6 @@
package org.jetbrains.kotlin.contracts.model.functors 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 * 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 second == null -> first
else -> operation(first, second) 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 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.structure.ESReturns
import org.jetbrains.kotlin.contracts.model.*
import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.types.KotlinType 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> { 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]) return invokeWithArguments(arguments[0])
} }
@@ -36,10 +40,10 @@ class IsFunctor(val type: KotlinType, val isNegated: Boolean) : AbstractReducing
private fun invokeWithValue(value: ESValue): List<ConditionalEffect> { private fun invokeWithValue(value: ESValue): List<ConditionalEffect> {
val trueIs = ESIs(value, this) 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 trueResult = ConditionalEffect(trueIs, ESReturns(constants.trueValue))
val falseResult = ConditionalEffect(falseIs, ESReturns(false.lift())) val falseResult = ConditionalEffect(falseIs, ESReturns(constants.falseValue))
return listOf(trueResult, falseResult) return listOf(trueResult, falseResult)
} }
} }
@@ -16,11 +16,13 @@
package org.jetbrains.kotlin.contracts.model.functors 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.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 { override fun invokeWithReturningEffects(list: List<ConditionalEffect>): List<ConditionalEffect> = list.mapNotNull {
val outcome = it.simpleEffect 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" // can be non-boolean in case of type-errors in the whole expression, like "foo(bar) && 1"
val returnValue = (outcome as ESReturns).value val returnValue = (outcome as ESReturns).value
when (returnValue) { when {
ESConstant.TRUE -> ConditionalEffect(it.condition, ESReturns(ESConstant.FALSE)) returnValue.isTrue -> ConditionalEffect(it.condition, ESReturns(constants.falseValue))
ESConstant.FALSE -> ConditionalEffect(it.condition, ESReturns(ESConstant.TRUE)) returnValue.isFalse -> ConditionalEffect(it.condition, ESReturns(constants.trueValue))
else -> null 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.ESEffect
import org.jetbrains.kotlin.contracts.model.structure.* import org.jetbrains.kotlin.contracts.model.structure.*
class OrFunctor : AbstractBinaryFunctor() { class OrFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) {
override fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect> = when (constant) { override fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect> = when {
ESConstant.FALSE -> computation.effects constant.isFalse -> computation.effects
ESConstant.TRUE -> emptyList() constant.isTrue -> emptyList()
// This means that expression isn't typechecked properly // This means that expression isn't typechecked properly
else -> computation.effects 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. // 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 // 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 // Even if one of 'Returns(false)' is missing, we still can argue that other condition
// *must* be false when whole OR-functor returns false // *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>() val result = mutableListOf<ConditionalEffect>()
if (conditionWhenTrue != null) { if (conditionWhenTrue != null) {
result.add(ConditionalEffect(conditionWhenTrue, ESReturns(true.lift()))) result.add(ConditionalEffect(conditionWhenTrue, ESReturns(constants.trueValue)))
} }
if (conditionWhenFalse != null) { if (conditionWhenFalse != null) {
result.add(ConditionalEffect(conditionWhenFalse, ESReturns(false.lift()))) result.add(ConditionalEffect(conditionWhenFalse, ESReturns(constants.falseValue)))
} }
return result return result
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.contracts.model.functors 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.*
import org.jetbrains.kotlin.contracts.model.structure.* import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.contracts.model.visitors.Substitutor import org.jetbrains.kotlin.contracts.model.visitors.Substitutor
@@ -25,10 +24,10 @@ import org.jetbrains.kotlin.descriptors.ValueDescriptor
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
class SubstitutingFunctor( class SubstitutingFunctor(
private val components: ESComponents,
private val basicEffects: List<ESEffect>, private val basicEffects: List<ESEffect>,
private val ownerFunction: FunctionDescriptor, private val ownerFunction: FunctionDescriptor
private val builtIns: KotlinBuiltIns ) : AbstractReducingFunctor(components.constants) {
) : AbstractReducingFunctor() {
override fun doInvocation(arguments: List<Computation>): List<ESEffect> { override fun doInvocation(arguments: List<Computation>): List<ESEffect> {
if (basicEffects.isEmpty()) return emptyList() if (basicEffects.isEmpty()) return emptyList()
@@ -41,7 +40,7 @@ class SubstitutingFunctor(
} }
val substitutions = parameters.zip(arguments).toMap() val substitutions = parameters.zip(arguments).toMap()
val substitutor = Substitutor(substitutions, builtIns) val substitutor = Substitutor(substitutions, components.builtIns)
val substitutedClauses = mutableListOf<ESEffect>() val substitutedClauses = mutableListOf<ESEffect>()
effectsLoop@ for (effect in basicEffects) { 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.ESValue
import org.jetbrains.kotlin.contracts.model.functors.* import org.jetbrains.kotlin.contracts.model.functors.*
class ESAnd(val left: ESExpression, val right: ESExpression) : ESOperator { class ESAnd(val constants: ESConstants, val left: ESExpression, val right: ESExpression) : ESOperator {
override val functor: AndFunctor = AndFunctor() override val functor: AndFunctor = AndFunctor(constants)
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitAnd(this) override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitAnd(this)
} }
class ESOr(val left: ESExpression, val right: ESExpression) : ESOperator { class ESOr(val constants: ESConstants, val left: ESExpression, val right: ESExpression) : ESOperator {
override val functor: OrFunctor = OrFunctor() override val functor: OrFunctor = OrFunctor(constants)
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitOr(this) override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitOr(this)
} }
class ESNot(val arg: ESExpression) : ESOperator { class ESNot(val constants: ESConstants, val arg: ESExpression) : ESOperator {
override val functor = NotFunctor() override val functor = NotFunctor(constants)
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitNot(this) override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitNot(this)
} }
class ESIs(val left: ESValue, override val functor: IsFunctor) : ESOperator { 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) override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitIs(this)
} }
class ESEqual(val left: ESValue, val right: ESValue, isNegated: Boolean) : ESOperator { class ESEqual(val constants: ESConstants, val left: ESValue, val right: ESValue, isNegated: Boolean) : ESOperator {
override val functor: EqualsFunctor = EqualsFunctor(isNegated) override val functor: EqualsFunctor = EqualsFunctor(constants, isNegated)
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitEqual(this) 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.and(other: ESExpression?, constants: ESConstants): ESExpression =
fun ESExpression.or(other: ESExpression?): ESExpression = if (other == null) this else ESOr(this, other) 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 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.BooleanConstantReference
import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference
import org.jetbrains.kotlin.contracts.model.ESExpression 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.contracts.model.ESValue
import org.jetbrains.kotlin.descriptors.ValueDescriptor import org.jetbrains.kotlin.descriptors.ValueDescriptor
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import java.util.* import java.util.*
open class ESVariable(val descriptor: ValueDescriptor) : ESValue(descriptor.type) { 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() 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 <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitConstant(this)
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean = other is ESConstant && constantReference == other.constantReference
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as ESConstant
if (constantReference != other.constantReference) return false
return true
}
override fun hashCode(): Int = Objects.hashCode(constantReference) override fun hashCode(): Int = Objects.hashCode(constantReference)
override fun toString(): String = constantReference.name override fun toString(): String = constantReference.name
companion object { fun isNullConstant(): Boolean =
val TRUE = ESConstant(BooleanConstantReference.TRUE, DefaultBuiltIns.Instance.booleanType) constantReference == ConstantReference.NULL || constantReference == ConstantReference.NOT_NULL
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 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 internal val ESExpression.isTrue: Boolean
get() = this is ESConstant && constantReference == BooleanConstantReference.TRUE get() = this is ESConstant && constantReference == BooleanConstantReference.TRUE
@@ -16,11 +16,17 @@
package org.jetbrains.kotlin.contracts.model.visitors package org.jetbrains.kotlin.contracts.model.visitors
import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.contracts.model.* 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.* 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 private var isInverted: Boolean = false
fun collectFromSchema(schema: List<ESEffect>): MutableContextInfo = 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) return if (isInverted) leftInfo.and(rightInfo) else leftInfo.or(rightInfo)
} }
override fun visitVariable(esVariable: ESVariable): MutableContextInfo { override fun visitVariable(esVariable: ESVariable): MutableContextInfo =
return if (esVariable.type != DefaultBuiltIns.Instance.booleanType) if (esVariable.type.let { it == null || !KotlinBuiltIns.isBoolean(it) })
MutableContextInfo.EMPTY MutableContextInfo.EMPTY
else else
MutableContextInfo.EMPTY.equal(esVariable, isInverted.not().lift()) MutableContextInfo.EMPTY.equal(esVariable, constants.booleanValue(!isInverted))
}
override fun visitConstant(esConstant: ESConstant): MutableContextInfo = MutableContextInfo.EMPTY override fun visitConstant(esConstant: ESConstant): MutableContextInfo = MutableContextInfo.EMPTY
@@ -85,4 +90,3 @@ class InfoCollector(private val observedEffect: ESEffect) : ESExpressionVisitor<
return result return result
} }
} }
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
* Reduces given list of effects by evaluating constant expressions, * Reduces given list of effects by evaluating constant expressions,
* throwing away senseless checks and infeasible clauses, etc. * 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> = fun reduceEffects(schema: List<ESEffect>): List<ESEffect> =
schema.mapNotNull { reduceEffect(it) } schema.mapNotNull { reduceEffect(it) }
@@ -59,16 +59,16 @@ class Reducer : ESExpressionVisitor<ESExpression?> {
// Result is unknown, do not evaluate // Result is unknown, do not evaluate
result ?: return ESIs(reducedArg, isOperator.functor) 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 { override fun visitEqual(equal: ESEqual): ESExpression {
val reducedLeft = equal.left.accept(this) as ESValue val reducedLeft = equal.left.accept(this) as ESValue
val reducedRight = equal.right 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? { override fun visitAnd(and: ESAnd): ESExpression? {
@@ -79,7 +79,7 @@ class Reducer : ESExpressionVisitor<ESExpression?> {
reducedLeft.isFalse || reducedRight.isFalse -> reducedLeft reducedLeft.isFalse || reducedRight.isFalse -> reducedLeft
reducedLeft.isTrue -> reducedRight reducedLeft.isTrue -> reducedRight
reducedRight.isTrue -> reducedLeft 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.isTrue || reducedRight.isTrue -> reducedLeft
reducedLeft.isFalse -> reducedRight reducedLeft.isFalse -> reducedRight
reducedRight.isFalse -> reducedLeft reducedRight.isFalse -> reducedLeft
else -> ESOr(reducedLeft, reducedRight) else -> ESOr(constants, reducedLeft, reducedRight)
} }
} }
override fun visitNot(not: ESNot): ESExpression? { override fun visitNot(not: ESNot): ESExpression? {
val reducedArg = not.arg.accept(this) ?: return null val reducedArg = not.arg.accept(this) ?: return null
return when (reducedArg) { return when {
ESConstant.TRUE -> ESConstant.FALSE reducedArg.isTrue -> constants.falseValue
ESConstant.FALSE -> ESConstant.TRUE reducedArg.isFalse -> constants.trueValue
else -> reducedArg else -> reducedArg
} }
} }