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:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -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)
|
||||
|
||||
+12
-9
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -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)
|
||||
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
+10
-4
@@ -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>
|
||||
|
||||
+6
-4
@@ -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>
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+5
-5
@@ -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))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
-10
@@ -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
|
||||
|
||||
+4
-5
@@ -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) {
|
||||
|
||||
+13
-11
@@ -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
|
||||
|
||||
+12
-8
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user