From 6cd3d9f19a6e8d2b60a0f9e1c9af1fe912495ee9 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 21 Dec 2018 16:23:29 +0100 Subject: [PATCH] Do not use DefaultBuiltIns.Instance in ESConstant Add ESComponents and ESConstants to encapsulate usages of built-ins in different functors and operators --- .../kotlin/contracts/EffectSystem.kt | 40 +++++++++++++------ .../contracts/EffectsExtractingVisitor.kt | 30 ++++++++------ .../description/ContractDescription.kt | 4 +- .../interpretation/ConditionInterpreter.kt | 21 +++++----- .../ConstantValuesInterpreter.kt | 16 ++++---- .../ContractInterpretationDispatcher.kt | 8 ++-- .../interpretation/EffectsInterpreters.kt | 2 +- .../kotlin/contracts/model/ESComponents.kt | 15 +++++++ .../model/functors/AbstractBinaryFunctor.kt | 14 +++++-- .../model/functors/AbstractReducingFunctor.kt | 10 +++-- .../model/functors/AbstractUnaryFunctor.kt | 3 +- .../contracts/model/functors/AndFunctor.kt | 18 ++++----- .../contracts/model/functors/EqualsFunctor.kt | 10 ++--- .../contracts/model/functors/FunctorsUtils.kt | 10 ----- .../contracts/model/functors/IsFunctor.kt | 20 ++++++---- .../contracts/model/functors/NotFunctor.kt | 16 ++++---- .../contracts/model/functors/OrFunctor.kt | 18 ++++----- .../model/functors/SubstitutingFunctor.kt | 9 ++--- .../contracts/model/structure/Operators.kt | 24 ++++++----- .../contracts/model/structure/Values.kt | 38 +++++++----------- .../contracts/model/visitors/InfoCollector.kt | 20 ++++++---- .../contracts/model/visitors/Reducer.kt | 18 ++++----- 22 files changed, 202 insertions(+), 162 deletions(-) create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/contracts/model/ESComponents.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectSystem.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectSystem.kt index d108dabecd8..4d0951cc7d8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectSystem.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectSystem.kt @@ -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 } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectsExtractingVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectsExtractingVisitor.kt index 97a6a9a5778..61a6151cc4f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectsExtractingVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectsExtractingVisitor.kt @@ -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() { - 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 } } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/description/ContractDescription.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/description/ContractDescription.kt index f167bb96308..94e58e6d413 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/description/ContractDescription.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/description/ContractDescription.kt @@ -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 { module -> - ContractInterpretationDispatcher(module).convertContractDescriptorToFunctor(this) + val components = ESComponents(module.builtIns) + ContractInterpretationDispatcher(components).convertContractDescriptorToFunctor(this) } fun getFunctor(usageModule: ModuleDescriptor): Functor? = computeFunctor(usageModule) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ConditionInterpreter.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ConditionInterpreter.kt index 6e53195ec31..eaccc5ee8ef 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ConditionInterpreter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ConditionInterpreter.kt @@ -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 { +internal class ConditionInterpreter( + private val dispatcher: ContractInterpretationDispatcher +) : ContractDescriptionVisitor { + 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) -} \ No newline at end of file +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ConstantValuesInterpreter.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ConstantValuesInterpreter.kt index d228050f6d2..7878011946e 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ConstantValuesInterpreter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ConstantValuesInterpreter.kt @@ -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 } -} \ No newline at end of file +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ContractInterpretationDispatcher.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ContractInterpretationDispatcher.kt index daeda57346a..2eb83d420b3 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ContractInterpretationDispatcher.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ContractInterpretationDispatcher.kt @@ -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) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/EffectsInterpreters.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/EffectsInterpreters.kt index 6311c1ad340..2b63e269f67 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/EffectsInterpreters.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/EffectsInterpreters.kt @@ -51,4 +51,4 @@ internal class ReturnsEffectInterpreter(private val dispatcher: ContractInterpre val returnedValue = dispatcher.interpretConstant(effectDeclaration.value) ?: return null return ESReturns(returnedValue) } -} \ No newline at end of file +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/ESComponents.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/ESComponents.kt new file mode 100644 index 00000000000..d92ebebebe7 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/ESComponents.kt @@ -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) +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractBinaryFunctor.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractBinaryFunctor.kt index 039d037a524..5a6109331ec 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractBinaryFunctor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractBinaryFunctor.kt @@ -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): List { 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): 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 + protected abstract fun invokeWithReturningEffects( left: List, right: List diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractReducingFunctor.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractReducingFunctor.kt index a6208c2638b..7a8b4e7b40c 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractReducingFunctor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractReducingFunctor.kt @@ -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): List = reducer.reduceEffects(doInvocation(arguments)) protected abstract fun doInvocation(arguments: List): List -} \ No newline at end of file +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractUnaryFunctor.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractUnaryFunctor.kt index 8e4fc23b7c5..c943b0a7909 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractUnaryFunctor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractUnaryFunctor.kt @@ -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): List { assert(arguments.size == 1) { "Wrong size of arguments list for Unary operator: expected 1, got ${arguments.size}" } return invokeWithArguments(arguments[0]) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AndFunctor.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AndFunctor.kt index 380d4bf0a2a..36d9ffb8e49 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AndFunctor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AndFunctor.kt @@ -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 = when (constant) { - ESConstant.TRUE -> computation.effects - ESConstant.FALSE -> emptyList() +class AndFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) { + override fun invokeWithConstant(computation: Computation, constant: ESConstant): List = 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() 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 diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/EqualsFunctor.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/EqualsFunctor.kt index cc02569f8c5..bbe76b1945f 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/EqualsFunctor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/EqualsFunctor.kt @@ -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 { 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)) ) } } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/FunctorsUtils.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/FunctorsUtils.kt index ee6a1a17702..66acf5a13c0 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/FunctorsUtils.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/FunctorsUtils.kt @@ -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 applyWithDefault(first: F?, second: S?, operation second == null -> first else -> operation(first, second) } - -internal fun foldConditionsWithOr(list: List): ESExpression? = - if (list.isEmpty()) - null - else - list.map { it.condition }.reduce { acc, condition -> ESOr(acc, condition) } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/IsFunctor.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/IsFunctor.kt index 648125c9207..b8452686106 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/IsFunctor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/IsFunctor.kt @@ -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): List { - 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 { 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) } -} \ No newline at end of file +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/NotFunctor.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/NotFunctor.kt index 3577072c86e..ff933d59ac0 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/NotFunctor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/NotFunctor.kt @@ -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): List = 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 } } -} \ No newline at end of file +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/OrFunctor.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/OrFunctor.kt index 0cca101a07b..08e911f4510 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/OrFunctor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/OrFunctor.kt @@ -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 = when (constant) { - ESConstant.FALSE -> computation.effects - ESConstant.TRUE -> emptyList() +class OrFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) { + override fun invokeWithConstant(computation: Computation, constant: ESConstant): List = 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() 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 diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/SubstitutingFunctor.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/SubstitutingFunctor.kt index 1f5a147f7ca..584689dea1b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/SubstitutingFunctor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/SubstitutingFunctor.kt @@ -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, - private val ownerFunction: FunctionDescriptor, - private val builtIns: KotlinBuiltIns -) : AbstractReducingFunctor() { + private val ownerFunction: FunctionDescriptor +) : AbstractReducingFunctor(components.constants) { override fun doInvocation(arguments: List): List { 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() effectsLoop@ for (effect in basicEffects) { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Operators.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Operators.kt index 8d96c281114..87192c9bad1 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Operators.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Operators.kt @@ -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 accept(visitor: ESExpressionVisitor): 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 accept(visitor: ESExpressionVisitor): 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 accept(visitor: ESExpressionVisitor): 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 accept(visitor: ESExpressionVisitor): 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 accept(visitor: ESExpressionVisitor): 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) \ No newline at end of file +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) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Values.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Values.kt index 8480cdcfd6c..2248e79000d 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Values.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Values.kt @@ -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 accept(visitor: ESExpressionVisitor): 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 diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/visitors/InfoCollector.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/visitors/InfoCollector.kt index 4c3ba876898..e59c4abff1d 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/visitors/InfoCollector.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/visitors/InfoCollector.kt @@ -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 { +class InfoCollector( + private val observedEffect: ESEffect, + private val constants: ESConstants +) : ESExpressionVisitor { private var isInverted: Boolean = false fun collectFromSchema(schema: List): 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 } } - diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/visitors/Reducer.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/visitors/Reducer.kt index 758508d5b7c..d50b6fd05a3 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/visitors/Reducer.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/visitors/Reducer.kt @@ -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 { +class Reducer(private val constants: ESConstants) : ESExpressionVisitor { fun reduceEffects(schema: List): List = schema.mapNotNull { reduceEffect(it) } @@ -59,16 +59,16 @@ class Reducer : ESExpressionVisitor { // 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 { 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 { 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 } }