diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/ContextInfoToDataFlowInfo.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/ContextInfoToDataFlowInfo.kt index 8d1f5bcfe35..12426e8f9ae 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/ContextInfoToDataFlowInfo.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/ContextInfoToDataFlowInfo.kt @@ -67,7 +67,7 @@ private fun ESValue.toDataFlowValue(builtIns: KotlinBuiltIns): DataFlowValue? = is ESDataFlowValue -> dataFlowValue is ESConstant -> when (constantReference) { ConstantReference.NULL -> DataFlowValue.nullValue(builtIns) - else -> DataFlowValue(IdentifierInfo.NO, type) + else -> DataFlowValue(IdentifierInfo.NO, type.toKotlinType(builtIns)) } else -> null } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectSystem.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectSystem.kt index 1ad4058a3d8..0f4e22ae088 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectSystem.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectSystem.kt @@ -20,7 +20,6 @@ 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 @@ -46,13 +45,6 @@ class EffectSystem( 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<*>, bindingTrace: BindingTrace, @@ -64,7 +56,7 @@ class EffectSystem( val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return DataFlowInfo.EMPTY if (callExpression is KtDeclaration) return DataFlowInfo.EMPTY - val resultContextInfo = getContextInfoWhen(ESReturns(constants.wildcard), callExpression, bindingTrace, moduleDescriptor) + val resultContextInfo = getContextInfoWhen(ESReturns(ESConstants.wildcard), callExpression, bindingTrace, moduleDescriptor) return resultContextInfo.toDataFlowInfo(languageVersionSettings, builtIns) } @@ -83,10 +75,10 @@ class EffectSystem( val rightComputation = getNonTrivialComputation(rightExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY - val effects = EqualsFunctor(constants, false).invokeWithArguments(leftComputation, rightComputation) + val effects = EqualsFunctor(false).invokeWithArguments(leftComputation, rightComputation) - val equalsContextInfo = InfoCollector(ESReturns(constants.trueValue), constants).collectFromSchema(effects) - val notEqualsContextInfo = InfoCollector(ESReturns(constants.falseValue), constants).collectFromSchema(effects) + val equalsContextInfo = InfoCollector(ESReturns(ESConstants.trueValue), builtIns).collectFromSchema(effects) + val notEqualsContextInfo = InfoCollector(ESReturns(ESConstants.falseValue), builtIns).collectFromSchema(effects) return ConditionalDataFlowInfo( equalsContextInfo.toDataFlowInfo(languageVersionSettings, builtIns), @@ -101,7 +93,7 @@ class EffectSystem( val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return if (callExpression is KtDeclaration) return - val resultingContextInfo = getContextInfoWhen(ESReturns(constants.wildcard), callExpression, bindingTrace, moduleDescriptor) + val resultingContextInfo = getContextInfoWhen(ESReturns(ESConstants.wildcard), callExpression, bindingTrace, moduleDescriptor) for (effect in resultingContextInfo.firedEffects) { val callsEffect = effect as? ESCalls ?: continue val lambdaExpression = (callsEffect.callable as? ESLambda)?.lambda ?: continue @@ -118,7 +110,7 @@ class EffectSystem( if (!languageVersionSettings.supportsFeature(LanguageFeature.UseReturnsEffect)) return DataFlowInfo.EMPTY if (condition == null) return DataFlowInfo.EMPTY - return getContextInfoWhen(ESReturns(constants.booleanValue(value)), condition, bindingTrace, moduleDescriptor) + return getContextInfoWhen(ESReturns(ESConstants.booleanValue(value)), condition, bindingTrace, moduleDescriptor) .toDataFlowInfo(languageVersionSettings, moduleDescriptor.builtIns) } @@ -133,11 +125,11 @@ class EffectSystem( } if (isInContractBlock) return MutableContextInfo.EMPTY val computation = getNonTrivialComputation(expression, bindingTrace, moduleDescriptor) ?: return MutableContextInfo.EMPTY - return InfoCollector(observedEffect, constants).collectFromSchema(computation.effects) + return InfoCollector(observedEffect, builtIns).collectFromSchema(computation.effects) } private fun getNonTrivialComputation(expression: KtExpression, trace: BindingTrace, moduleDescriptor: ModuleDescriptor): Computation? { - val visitor = EffectsExtractingVisitor(trace, moduleDescriptor, dataFlowValueFactory, constants, languageVersionSettings) + val visitor = EffectsExtractingVisitor(trace, moduleDescriptor, dataFlowValueFactory, languageVersionSettings) 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 a1b2435d951..b78d77f75ee 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectsExtractingVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectsExtractingVisitor.kt @@ -25,10 +25,8 @@ 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.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.model.structure.* +import org.jetbrains.kotlin.contracts.model.visitors.Reducer import org.jetbrains.kotlin.contracts.parsing.isEqualsDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor @@ -58,10 +56,10 @@ class EffectsExtractingVisitor( private val trace: BindingTrace, private val moduleDescriptor: ModuleDescriptor, private val dataFlowValueFactory: DataFlowValueFactory, - private val constants: ESConstants, private val languageVersionSettings: LanguageVersionSettings ) : KtVisitor() { private val builtIns: KotlinBuiltIns get() = moduleDescriptor.builtIns + private val reducer: Reducer = Reducer(builtIns) fun extractOrGetCached(element: KtElement): Computation { trace[BindingContext.EXPRESSION_EFFECTS, element]?.let { return it } @@ -77,17 +75,20 @@ class EffectsExtractingVisitor( val descriptor = resolvedCall.resultingDescriptor return when { descriptor.isEqualsDescriptor() -> CallComputation( - builtIns.booleanType, - EqualsFunctor(constants, false).invokeWithArguments(arguments) + ESBooleanType, + EqualsFunctor(false).invokeWithArguments(arguments, reducer) ) descriptor is ValueDescriptor -> ESVariableWithDataFlowValue( descriptor, (element as KtExpression).createDataFlowValue() ?: return UNKNOWN_COMPUTATION ) - descriptor is FunctionDescriptor -> CallComputation( - descriptor.returnType, - descriptor.getFunctor()?.invokeWithArguments(arguments) ?: emptyList() - ) + descriptor is FunctionDescriptor -> { + val esType = descriptor.returnType?.toESType() + CallComputation( + esType, + descriptor.getFunctor()?.invokeWithArguments(arguments, reducer) ?: emptyList() + ) + } else -> UNKNOWN_COMPUTATION } } @@ -111,18 +112,18 @@ class EffectsExtractingVisitor( val value: Any? = compileTimeConstant.getValue(type) return when (value) { - is Boolean -> constants.booleanValue(value) - null -> constants.nullValue + is Boolean -> ESConstants.booleanValue(value) + null -> ESConstants.nullValue else -> UNKNOWN_COMPUTATION } } override fun visitIsExpression(expression: KtIsExpression, data: Unit): Computation { - val rightType: KotlinType = trace[BindingContext.TYPE, expression.typeReference] ?: return UNKNOWN_COMPUTATION + val rightType = trace[BindingContext.TYPE, expression.typeReference]?.toESType() ?: return UNKNOWN_COMPUTATION val arg = extractOrGetCached(expression.leftHandSide) return CallComputation( - builtIns.booleanType, - IsFunctor(constants, rightType, expression.isNegated).invokeWithArguments(listOf(arg)) + ESBooleanType, + IsFunctor(rightType, expression.isNegated).invokeWithArguments(listOf(arg), reducer) ) } @@ -134,7 +135,7 @@ class EffectsExtractingVisitor( // null bypassing function's contract, so we have to filter them out fun ESEffect.containsReturnsNull(): Boolean = - isReturns { value == constants.nullValue } || this is ConditionalEffect && this.simpleEffect.containsReturnsNull() + isReturns { value == ESConstants.nullValue } || this is ConditionalEffect && this.simpleEffect.containsReturnsNull() val effectsWithoutReturnsNull = computation.effects.filter { !it.containsReturnsNull() } return CallComputation(computation.type, effectsWithoutReturnsNull) @@ -147,10 +148,10 @@ class EffectsExtractingVisitor( val args = listOf(left, right) return when (expression.operationToken) { - 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)) + KtTokens.EXCLEQ -> CallComputation(ESBooleanType, EqualsFunctor(true).invokeWithArguments(args, reducer)) + KtTokens.EQEQ -> CallComputation(ESBooleanType, EqualsFunctor(false).invokeWithArguments(args, reducer)) + KtTokens.ANDAND -> CallComputation(ESBooleanType, AndFunctor().invokeWithArguments(args, reducer)) + KtTokens.OROR -> CallComputation(ESBooleanType, OrFunctor().invokeWithArguments(args, reducer)) else -> UNKNOWN_COMPUTATION } } @@ -158,7 +159,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(constants).invokeWithArguments(arg)) + KtTokens.EXCL -> CallComputation(ESBooleanType, NotFunctor().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 94e58e6d413..827c65ecddd 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/description/ContractDescription.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/description/ContractDescription.kt @@ -17,7 +17,6 @@ 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,12 +39,11 @@ open class ContractDescription( val ownerFunction: FunctionDescriptor, storageManager: StorageManager ) { - private val computeFunctor = storageManager.createMemoizedFunctionWithNullableValues { module -> - val components = ESComponents(module.builtIns) - ContractInterpretationDispatcher(components).convertContractDescriptorToFunctor(this) + private val computeFunctor = storageManager.createNullableLazyValue { + ContractInterpretationDispatcher().convertContractDescriptorToFunctor(this) } - fun getFunctor(usageModule: ModuleDescriptor): Functor? = computeFunctor(usageModule) + fun getFunctor(usageModule: ModuleDescriptor): Functor? = computeFunctor.invoke() } interface ContractDescriptionElement { 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 eaccc5ee8ef..374ea9d564c 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ConditionInterpreter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ConditionInterpreter.kt @@ -25,33 +25,31 @@ import org.jetbrains.kotlin.contracts.model.structure.* 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(constants, left, right) + return ESOr(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(constants, left, right) + return ESAnd(left, right) } override fun visitLogicalNot(logicalNot: LogicalNot, data: Unit): ESExpression? { val arg = logicalNot.arg.accept(this, data) ?: return null - return ESNot(constants, arg) + return ESNot(arg) } override fun visitIsInstancePredicate(isInstancePredicate: IsInstancePredicate, data: Unit): ESExpression? { val esVariable = dispatcher.interpretVariable(isInstancePredicate.arg) ?: return null - return ESIs(esVariable, IsFunctor(constants, isInstancePredicate.type, isInstancePredicate.isNegated)) + return ESIs(esVariable, IsFunctor(isInstancePredicate.type.toESType(), isInstancePredicate.isNegated)) } override fun visitIsNullPredicate(isNullPredicate: IsNullPredicate, data: Unit): ESExpression? { val variable = dispatcher.interpretVariable(isNullPredicate.arg) ?: return null - return ESEqual(constants, variable, constants.nullValue, isNullPredicate.isNegated) + return ESEqual(variable, ESConstants.nullValue, isNullPredicate.isNegated) } override fun visitBooleanConstantDescriptor(booleanConstantDescriptor: BooleanConstantReference, data: Unit): ESExpression? = 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 7878011946e..abd64797e5b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ConstantValuesInterpreter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ConstantValuesInterpreter.kt @@ -22,12 +22,12 @@ import org.jetbrains.kotlin.contracts.model.structure.ESConstant import org.jetbrains.kotlin.contracts.model.structure.ESConstants internal class ConstantValuesInterpreter { - 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 + fun interpretConstant(constantReference: ConstantReference): ESConstant? = when (constantReference) { + BooleanConstantReference.TRUE -> ESConstants.trueValue + BooleanConstantReference.FALSE -> ESConstants.falseValue + ConstantReference.NULL -> ESConstants.nullValue + ConstantReference.NOT_NULL -> ESConstants.notNullValue + ConstantReference.WILDCARD -> ESConstants.wildcard else -> null } } 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 2eb83d420b3..525c6c90990 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ContractInterpretationDispatcher.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/interpretation/ContractInterpretationDispatcher.kt @@ -22,7 +22,6 @@ 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 @@ -33,7 +32,7 @@ import org.jetbrains.kotlin.contracts.model.structure.ESVariable /** * This class manages conversion of [ContractDescription] to [Functor] */ -class ContractInterpretationDispatcher(val components: ESComponents) { +class ContractInterpretationDispatcher { private val constantsInterpreter = ConstantValuesInterpreter() private val conditionInterpreter = ConditionInterpreter(this) private val conditionalEffectInterpreter = ConditionalEffectInterpreter(this) @@ -51,7 +50,7 @@ class ContractInterpretationDispatcher(val components: ESComponents) { } } - return SubstitutingFunctor(components, resultingClauses, contractDescription.ownerFunction) + return SubstitutingFunctor(resultingClauses, contractDescription.ownerFunction) } internal fun interpretEffect(effectDeclaration: EffectDeclaration): ESEffect? { @@ -60,7 +59,7 @@ class ContractInterpretationDispatcher(val components: ESComponents) { } internal fun interpretConstant(constantReference: ConstantReference): ESConstant? = - constantsInterpreter.interpretConstant(constantReference, components.constants) + constantsInterpreter.interpretConstant(constantReference) internal fun interpretCondition(booleanExpression: BooleanExpression): ESExpression? = booleanExpression.accept(conditionInterpreter, Unit) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/Computation.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/Computation.kt index d98ce302f5c..6076383e9ff 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/Computation.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/Computation.kt @@ -16,7 +16,7 @@ package org.jetbrains.kotlin.contracts.model -import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.contracts.model.structure.ESType /** * Generic abstraction of static information about some part of program. @@ -27,7 +27,7 @@ interface Computation { * If type is unknown or computation doesn't have a type (e.g. if * it is some construction, like "for"-loop), then type is 'null' */ - val type: KotlinType? + val type: ESType? /** * List of all possible effects of this computation. diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/ESComponents.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/ESComponents.kt deleted file mode 100644 index 1fcc2737901..00000000000 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/ESComponents.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. - * 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/Functor.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/Functor.kt index c2dd2bdab00..b811086f2ad 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/Functor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/Functor.kt @@ -16,6 +16,8 @@ package org.jetbrains.kotlin.contracts.model +import org.jetbrains.kotlin.contracts.model.visitors.Reducer + /** * An abstraction of effect-generating nature of some computation. * @@ -25,5 +27,13 @@ package org.jetbrains.kotlin.contracts.model * values, it takes effects and returns effects. */ interface Functor { - fun invokeWithArguments(arguments: List): List + fun invokeWithArguments(arguments: List, reducer: Reducer): List +} + + +abstract class AbstractFunctor : Functor { + override fun invokeWithArguments(arguments: List, reducer: Reducer): List = + reducer.reduceEffects(doInvocation(arguments, reducer)) + + protected abstract fun doInvocation(arguments: List, reducer: Reducer): List } \ No newline at end of file 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 5a6109331ec..969a29e7574 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 @@ -16,14 +16,12 @@ 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.ESExpression +import org.jetbrains.kotlin.contracts.model.* import org.jetbrains.kotlin.contracts.model.structure.* +import org.jetbrains.kotlin.contracts.model.visitors.Reducer -abstract class AbstractBinaryFunctor(constants: ESConstants) : AbstractReducingFunctor(constants) { - override fun doInvocation(arguments: List): List { +abstract class AbstractBinaryFunctor : AbstractFunctor() { + override fun doInvocation(arguments: List, reducer: Reducer): List { assert(arguments.size == 2) { "Wrong size of arguments list for Binary functor: expected 2, got ${arguments.size}" } return invokeWithArguments(arguments[0], arguments[1]) } @@ -49,7 +47,7 @@ abstract class AbstractBinaryFunctor(constants: ESConstants) : AbstractReducingF if (list.isEmpty()) null else - list.map { it.condition }.reduce { acc, condition -> ESOr(constants, acc, condition) } + list.map { it.condition }.reduce { acc, condition -> ESOr(acc, condition) } protected abstract fun invokeWithConstant(computation: Computation, constant: ESConstant): 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 deleted file mode 100644 index 7a8b4e7b40c..00000000000 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractReducingFunctor.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2010-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -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.structure.ESConstants -import org.jetbrains.kotlin.contracts.model.visitors.Reducer - -/** - * Abstract implementation of Functor with some routine house-holding - * automatically performed. * - */ -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 -} 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 c943b0a7909..b54fbbb0425 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 @@ -16,12 +16,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.ESConstants +import org.jetbrains.kotlin.contracts.model.* import org.jetbrains.kotlin.contracts.model.structure.isReturns import org.jetbrains.kotlin.contracts.model.structure.isWildcard +import org.jetbrains.kotlin.contracts.model.visitors.Reducer /** * Unary functor that has sequential semantics, i.e. it won't apply to @@ -30,8 +28,8 @@ 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(constants: ESConstants) : AbstractReducingFunctor(constants) { - override fun doInvocation(arguments: List): List { +abstract class AbstractUnaryFunctor : AbstractFunctor() { + override fun doInvocation(arguments: List, reducer: Reducer): 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 36d9ffb8e49..653f04ea83b 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,7 +21,7 @@ import org.jetbrains.kotlin.contracts.model.ConditionalEffect import org.jetbrains.kotlin.contracts.model.ESEffect import org.jetbrains.kotlin.contracts.model.structure.* -class AndFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) { +class AndFunctor : AbstractBinaryFunctor() { override fun invokeWithConstant(computation: Computation, constant: ESConstant): List = when { constant.isTrue -> computation.effects constant.isFalse -> emptyList() @@ -48,20 +48,20 @@ class AndFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) { // 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(constants, l, r) } + val conditionWhenTrue = applyWithDefault(whenLeftReturnsTrue, whenRightReturnsTrue) { l, r -> ESAnd(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(constants, l, r) } + val conditionWhenFalse = applyIfBothNotNull(whenLeftReturnsFalse, whenRightReturnsFalse) { l, r -> ESOr(l, r) } val result = mutableListOf() if (conditionWhenTrue != null) { - result.add(ConditionalEffect(conditionWhenTrue, ESReturns(constants.trueValue))) + result.add(ConditionalEffect(conditionWhenTrue, ESReturns(ESConstants.trueValue))) } if (conditionWhenFalse != null) { - result.add(ConditionalEffect(conditionWhenFalse, ESReturns(constants.falseValue))) + result.add(ConditionalEffect(conditionWhenFalse, ESReturns(ESConstants.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 bbe76b1945f..10553ed1d16 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 @@ -16,14 +16,11 @@ package org.jetbrains.kotlin.contracts.model.functors -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -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.* import org.jetbrains.kotlin.contracts.model.structure.* +import org.jetbrains.kotlin.contracts.model.visitors.Reducer -class EqualsFunctor(constants: ESConstants, val isNegated: Boolean) : AbstractReducingFunctor(constants) { +class EqualsFunctor(val isNegated: Boolean) : AbstractFunctor() { /* 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 @@ -41,7 +38,7 @@ class EqualsFunctor(constants: ESConstants, val isNegated: Boolean) : AbstractRe We don't want to code here fair analysis for general cases, because it's too complex. Instead, we just check some specific cases, which are useful enough in practice */ - override fun doInvocation(arguments: List): List { + override fun doInvocation(arguments: List, reducer: Reducer): List { assert(arguments.size == 2) { "Equals functor expected 2 arguments, got ${arguments.size}" } // TODO: AnnotationConstructorCaller kills this with implicit receiver. Investigate, how. @@ -77,16 +74,15 @@ class EqualsFunctor(constants: ESConstants, val isNegated: Boolean) : AbstractRe } if (effect.simpleEffect.value == constant) { - val trueClause = ConditionalEffect(effect.condition, ESReturns(constants.booleanValue(isNegated.not()))) + val trueClause = ConditionalEffect(effect.condition, ESReturns(ESConstants.booleanValue(isNegated.not()))) resultingClauses.add(trueClause) } if (effect.simpleEffect.value != constant && effect.simpleEffect.value is ESConstant && isSafeToProduceFalse( - call, - effect.simpleEffect.value, - constant - )) { - val falseClause = ConditionalEffect(effect.condition, ESReturns(constants.booleanValue(isNegated))) + call, effect.simpleEffect.value, constant + ) + ) { + val falseClause = ConditionalEffect(effect.condition, ESReturns(ESConstants.booleanValue(isNegated))) resultingClauses.add(falseClause) } } @@ -96,11 +92,15 @@ class EqualsFunctor(constants: ESConstants, val isNegated: Boolean) : AbstractRe // It is safe to produce false if we're comparing types which are isomorphic to Boolean. For such types we can be sure, that // if leftConstant != rightConstant, then this is the only way to produce 'false'. - private fun isSafeToProduceFalse(leftCall: Computation, leftConstant: ESConstant, rightConstant: ESConstant): Boolean = when { - // Comparison of Boolean - KotlinBuiltIns.isBoolean(rightConstant.type) && leftCall.type != null && KotlinBuiltIns.isBoolean(leftCall.type!!) -> true + private fun isSafeToProduceFalse( + leftCall: Computation, + leftConstant: ESConstant, + rightConstant: ESConstant + ): Boolean = when { + // Comparison of Boolean + rightConstant.type.isBoolean() && leftCall.type.isBoolean() -> true - // Comparison of NULL/NOT_NULL, which is essentially Boolean + // Comparison of NULL/NOT_NULL, which is essentially Boolean leftConstant.isNullConstant() && rightConstant.isNullConstant() -> true else -> false @@ -108,8 +108,8 @@ class EqualsFunctor(constants: ESConstants, val isNegated: Boolean) : AbstractRe private fun equateValues(left: ESValue, right: ESValue): List { return listOf( - ConditionalEffect(ESEqual(constants, left, right, isNegated), ESReturns(constants.trueValue)), - ConditionalEffect(ESEqual(constants, left, right, isNegated.not()), ESReturns(constants.falseValue)) + ConditionalEffect(ESEqual(left, right, isNegated), ESReturns(ESConstants.trueValue)), + ConditionalEffect(ESEqual(left, right, isNegated.not()), ESReturns(ESConstants.falseValue)) ) } } 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 b8452686106..2d0714ad418 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,17 +16,15 @@ 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.* 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.types.KotlinType +import org.jetbrains.kotlin.contracts.model.structure.ESType +import org.jetbrains.kotlin.contracts.model.visitors.Reducer -class IsFunctor(constants: ESConstants, val type: KotlinType, val isNegated: Boolean) : AbstractReducingFunctor(constants) { - override fun doInvocation(arguments: List): List { +class IsFunctor(val type: ESType, val isNegated: Boolean) : AbstractFunctor() { + override fun doInvocation(arguments: List, reducer: Reducer): List { assert(arguments.size == 1) { "Wrong size of arguments list for Unary operator: expected 1, got ${arguments.size}" } return invokeWithArguments(arguments[0]) } @@ -40,10 +38,10 @@ class IsFunctor(constants: ESConstants, val type: KotlinType, val isNegated: Boo private fun invokeWithValue(value: ESValue): List { val trueIs = ESIs(value, this) - val falseIs = ESIs(value, IsFunctor(constants, type, isNegated.not())) + val falseIs = ESIs(value, IsFunctor(type, isNegated.not())) - val trueResult = ConditionalEffect(trueIs, ESReturns(constants.trueValue)) - val falseResult = ConditionalEffect(falseIs, ESReturns(constants.falseValue)) + val trueResult = ConditionalEffect(trueIs, ESReturns(ESConstants.trueValue)) + val falseResult = ConditionalEffect(falseIs, ESReturns(ESConstants.falseValue)) return listOf(trueResult, falseResult) } } 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 ff933d59ac0..35c989b600b 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 @@ -22,7 +22,7 @@ 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(constants: ESConstants) : AbstractUnaryFunctor(constants) { +class NotFunctor : AbstractUnaryFunctor() { override fun invokeWithReturningEffects(list: List): List = list.mapNotNull { val outcome = it.simpleEffect @@ -31,8 +31,8 @@ class NotFunctor(constants: ESConstants) : AbstractUnaryFunctor(constants) { val returnValue = (outcome as ESReturns).value when { - returnValue.isTrue -> ConditionalEffect(it.condition, ESReturns(constants.falseValue)) - returnValue.isFalse -> ConditionalEffect(it.condition, ESReturns(constants.trueValue)) + returnValue.isTrue -> ConditionalEffect(it.condition, ESReturns(ESConstants.falseValue)) + returnValue.isFalse -> ConditionalEffect(it.condition, ESReturns(ESConstants.trueValue)) else -> null } } 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 08e911f4510..3ce187d7204 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,7 +21,7 @@ import org.jetbrains.kotlin.contracts.model.ConditionalEffect import org.jetbrains.kotlin.contracts.model.ESEffect import org.jetbrains.kotlin.contracts.model.structure.* -class OrFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) { +class OrFunctor : AbstractBinaryFunctor() { override fun invokeWithConstant(computation: Computation, constant: ESConstant): List = when { constant.isFalse -> computation.effects constant.isTrue -> emptyList() @@ -48,20 +48,20 @@ class OrFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) { // 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(constants, l, r) } + val conditionWhenTrue = applyIfBothNotNull(whenLeftReturnsTrue, whenRightReturnsTrue) { l, r -> ESOr(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(constants, l, r) } + val conditionWhenFalse = applyWithDefault(whenLeftReturnsFalse, whenRightReturnsFalse) { l, r -> ESAnd(l, r) } val result = mutableListOf() if (conditionWhenTrue != null) { - result.add(ConditionalEffect(conditionWhenTrue, ESReturns(constants.trueValue))) + result.add(ConditionalEffect(conditionWhenTrue, ESReturns(ESConstants.trueValue))) } if (conditionWhenFalse != null) { - result.add(ConditionalEffect(conditionWhenFalse, ESReturns(constants.falseValue))) + result.add(ConditionalEffect(conditionWhenFalse, ESReturns(ESConstants.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 584689dea1b..d49cf147cd9 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 @@ -18,17 +18,17 @@ package org.jetbrains.kotlin.contracts.model.functors import org.jetbrains.kotlin.contracts.model.* import org.jetbrains.kotlin.contracts.model.structure.* +import org.jetbrains.kotlin.contracts.model.visitors.Reducer import org.jetbrains.kotlin.contracts.model.visitors.Substitutor import org.jetbrains.kotlin.descriptors.FunctionDescriptor 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 -) : AbstractReducingFunctor(components.constants) { - override fun doInvocation(arguments: List): List { +) : AbstractFunctor() { + override fun doInvocation(arguments: List, reducer: Reducer): List { if (basicEffects.isEmpty()) return emptyList() val receiver = @@ -40,7 +40,7 @@ class SubstitutingFunctor( } val substitutions = parameters.zip(arguments).toMap() - val substitutor = Substitutor(substitutions, components.builtIns) + val substitutor = Substitutor(substitutions, reducer) val substitutedClauses = mutableListOf() effectsLoop@ for (effect in basicEffects) { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Computations.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Computations.kt index 0651e64931e..ec794245455 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Computations.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Computations.kt @@ -18,11 +18,10 @@ package org.jetbrains.kotlin.contracts.model.structure import org.jetbrains.kotlin.contracts.model.Computation import org.jetbrains.kotlin.contracts.model.ESEffect -import org.jetbrains.kotlin.types.KotlinType -class CallComputation(override val type: KotlinType?, override val effects: List) : Computation +class CallComputation(override val type: ESType?, override val effects: List) : Computation object UNKNOWN_COMPUTATION : Computation { - override val type: KotlinType? = null + override val type: ESType? = null override val effects: List = emptyList() } 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 87192c9bad1..d01513ee6dc 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,18 +22,18 @@ import org.jetbrains.kotlin.contracts.model.ESOperator import org.jetbrains.kotlin.contracts.model.ESValue import org.jetbrains.kotlin.contracts.model.functors.* -class ESAnd(val constants: ESConstants, val left: ESExpression, val right: ESExpression) : ESOperator { - override val functor: AndFunctor = AndFunctor(constants) +class ESAnd(val left: ESExpression, val right: ESExpression) : ESOperator { + override val functor: AndFunctor = AndFunctor() override fun accept(visitor: ESExpressionVisitor): T = visitor.visitAnd(this) } -class ESOr(val constants: ESConstants, val left: ESExpression, val right: ESExpression) : ESOperator { - override val functor: OrFunctor = OrFunctor(constants) +class ESOr(val left: ESExpression, val right: ESExpression) : ESOperator { + override val functor: OrFunctor = OrFunctor() override fun accept(visitor: ESExpressionVisitor): T = visitor.visitOr(this) } -class ESNot(val constants: ESConstants, val arg: ESExpression) : ESOperator { - override val functor = NotFunctor(constants) +class ESNot(val arg: ESExpression) : ESOperator { + override val functor = NotFunctor() override fun accept(visitor: ESExpressionVisitor): T = visitor.visitNot(this) } @@ -42,13 +42,13 @@ class ESIs(val left: ESValue, override val functor: IsFunctor) : ESOperator { override fun accept(visitor: ESExpressionVisitor): T = visitor.visitIs(this) } -class ESEqual(val constants: ESConstants, val left: ESValue, val right: ESValue, isNegated: Boolean) : ESOperator { - override val functor: EqualsFunctor = EqualsFunctor(constants, isNegated) +class ESEqual(val left: ESValue, val right: ESValue, isNegated: Boolean) : ESOperator { + override val functor: EqualsFunctor = EqualsFunctor(isNegated) override fun accept(visitor: ESExpressionVisitor): T = visitor.visitEqual(this) } -fun ESExpression.and(other: ESExpression?, constants: ESConstants): ESExpression = - if (other == null) this else ESAnd(constants, this, other) +fun ESExpression.and(other: ESExpression?): ESExpression = + if (other == null) this else ESAnd(this, other) -fun ESExpression.or(other: ESExpression?, constants: ESConstants): ESExpression = - if (other == null) this else ESOr(constants, this, other) +fun ESExpression.or(other: ESExpression?): ESExpression = + if (other == null) this else ESOr(this, other) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Types.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Types.kt new file mode 100644 index 00000000000..38d33843871 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Types.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.structure + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.types.KotlinType + +sealed class ESType { + abstract fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType +} + +abstract class ESBuiltInType : ESType() + +object ESAnyType : ESBuiltInType() { + override fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType = builtIns.anyType +} + +object ESNullableAnyType : ESBuiltInType() { + override fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType = builtIns.nullableAnyType +} + +object ESNullableNothingType : ESBuiltInType() { + override fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType = builtIns.nullableNothingType +} + +object ESNothingType : ESBuiltInType() { + override fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType = builtIns.nothingType +} + +object ESBooleanType : ESType() { + override fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType = builtIns.booleanType +} + +class ESKotlinType(val type: KotlinType) : ESType() { + override fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType = type +} + + +fun KotlinType.toESType() = when { + KotlinBuiltIns.isBoolean(this) -> ESBooleanType + KotlinBuiltIns.isAny(this) -> ESAnyType + KotlinBuiltIns.isNullableAny(this) -> ESNullableAnyType + KotlinBuiltIns.isNothing(this) -> ESNothingType + KotlinBuiltIns.isNullableNothing(this) -> ESNullableNothingType + else -> ESKotlinType(this) +} + +fun ESType?.isBoolean(): Boolean = this is ESBooleanType \ No newline at end of file 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 cca24cf57dd..8e876ced879 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,6 @@ package org.jetbrains.kotlin.contracts.model.structure -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.ESEffect @@ -25,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.resolve.scopes.receivers.ReceiverValue -import org.jetbrains.kotlin.types.KotlinType import java.util.* @@ -36,7 +34,7 @@ interface ESReceiver : ESValue { } -abstract class AbstractESValue(override val type: KotlinType?) : ESValue { +abstract class AbstractESValue(override val type: ESType?) : ESValue { override val effects: List = listOf() } @@ -57,7 +55,7 @@ open class ESReceiverValue(override val receiverValue: ReceiverValue) : Abstract * * [ESVariable] at points 2 and 3 must has consistent equality according to using them as keys */ -open class ESVariable(val descriptor: ValueDescriptor) : AbstractESValue(descriptor.type) { +open class ESVariable(val descriptor: ValueDescriptor) : AbstractESValue(descriptor.type.toESType()) { override fun accept(visitor: ESExpressionVisitor): T = visitor.visitVariable(this) override fun equals(other: Any?): Boolean { @@ -82,7 +80,7 @@ open class ESVariable(val descriptor: ValueDescriptor) : AbstractESValue(descrip * * There is only few constants are supported (@see [ESConstant.Companion]) */ -class ESConstant internal constructor(val constantReference: ConstantReference, override val type: KotlinType) : AbstractESValue(type) { +class ESConstant internal constructor(val constantReference: ConstantReference, override val type: ESType) : AbstractESValue(type) { override fun accept(visitor: ESExpressionVisitor): T = visitor.visitConstant(this) override fun equals(other: Any?): Boolean = other is ESConstant && constantReference == other.constantReference @@ -95,12 +93,12 @@ class ESConstant internal constructor(val constantReference: ConstantReference, constantReference == ConstantReference.NULL || constantReference == ConstantReference.NOT_NULL } -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) +object ESConstants { + val trueValue = ESConstant(BooleanConstantReference.TRUE, ESBooleanType) + val falseValue = ESConstant(BooleanConstantReference.FALSE, ESBooleanType) + val nullValue = ESConstant(ConstantReference.NULL, ESNullableNothingType) + val notNullValue = ESConstant(ConstantReference.NOT_NULL, ESAnyType) + val wildcard = ESConstant(ConstantReference.WILDCARD, ESNullableAnyType) fun booleanValue(value: Boolean) = if (value) trueValue else falseValue 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 fc62ce7bb6c..428ff4fb859 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 @@ -23,10 +23,7 @@ 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, - private val constants: ESConstants -) : ESExpressionVisitor { +class InfoCollector(private val observedEffect: ESEffect, private val builtIns: KotlinBuiltIns) : ESExpressionVisitor { private var isInverted: Boolean = false fun collectFromSchema(schema: List): MutableContextInfo = @@ -41,18 +38,19 @@ class InfoCollector( // Check for information from conditional effects return when (observedEffect.isImplies(effect.simpleEffect)) { - // observed effect implies clause's effect => clause's effect was fired => clause's condition is true + // observed effect implies clause's effect => clause's effect was fired => clause's condition is true true -> effect.condition.accept(this) - // Observed effect *may* or *doesn't* implies clause's - no useful information + // Observed effect *may* or *doesn't* implies clause's - no useful information null, false -> null } } override fun visitIs(isOperator: ESIs): MutableContextInfo = with(isOperator) { - if (functor.isNegated != isInverted) MutableContextInfo.EMPTY.notSubtype(left, type) else MutableContextInfo.EMPTY.subtype( + val isType = type.toKotlinType(builtIns) + if (functor.isNegated != isInverted) MutableContextInfo.EMPTY.notSubtype(left, isType) else MutableContextInfo.EMPTY.subtype( left, - type + isType ) } @@ -76,10 +74,10 @@ class InfoCollector( } override fun visitVariable(esVariable: ESVariable): MutableContextInfo = - if (esVariable.type.let { it == null || !KotlinBuiltIns.isBoolean(it) }) + if (esVariable.type.let { !it.isBoolean() }) MutableContextInfo.EMPTY else - MutableContextInfo.EMPTY.equal(esVariable, constants.booleanValue(!isInverted)) + MutableContextInfo.EMPTY.equal(esVariable, ESConstants.booleanValue(!isInverted)) override fun visitConstant(esConstant: ESConstant): MutableContextInfo = MutableContextInfo.EMPTY 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 733edfe2f24..1b26a3af027 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 @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.contracts.model.visitors +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.contracts.model.* import org.jetbrains.kotlin.contracts.model.structure.* import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf @@ -24,7 +25,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(private val constants: ESConstants) : ESExpressionVisitor { +class Reducer(private val builtIns: KotlinBuiltIns) : ESExpressionVisitor { fun reduceEffects(schema: List): List = schema.mapNotNull { reduceEffect(it) } @@ -50,25 +51,28 @@ class Reducer(private val constants: ESConstants) : ESExpressionVisitor reducedArg.type.isSubtypeOf(isOperator.functor.type) - is ESVariable -> if (reducedArg.type?.isSubtypeOf(isOperator.functor.type) == true) true else null + is ESConstant -> argType!!.isSubtypeOf(isType) + is ESVariable -> if (argType?.isSubtypeOf(isType) == true) true else null else -> throw IllegalStateException("Unknown ESValue: $reducedArg") } // Result is unknown, do not evaluate result ?: return ESIs(reducedArg, isOperator.functor) - return constants.booleanValue(result.xor(isOperator.functor.isNegated)) + return ESConstants.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 constants.booleanValue((reducedLeft == reducedRight).xor(equal.functor.isNegated)) + if (reducedLeft is ESConstant) return ESConstants.booleanValue((reducedLeft == reducedRight).xor(equal.functor.isNegated)) - return ESEqual(constants, reducedLeft, reducedRight, equal.functor.isNegated) + return ESEqual(reducedLeft, reducedRight, equal.functor.isNegated) } override fun visitAnd(and: ESAnd): ESExpression? { @@ -79,7 +83,7 @@ class Reducer(private val constants: ESConstants) : ESExpressionVisitor reducedLeft reducedLeft.isTrue -> reducedRight reducedRight.isTrue -> reducedLeft - else -> ESAnd(constants, reducedLeft, reducedRight) + else -> ESAnd(reducedLeft, reducedRight) } } @@ -91,7 +95,7 @@ class Reducer(private val constants: ESConstants) : ESExpressionVisitor reducedLeft reducedLeft.isFalse -> reducedRight reducedRight.isFalse -> reducedLeft - else -> ESOr(constants, reducedLeft, reducedRight) + else -> ESOr(reducedLeft, reducedRight) } } @@ -99,8 +103,8 @@ class Reducer(private val constants: ESConstants) : ESExpressionVisitor constants.falseValue - reducedArg.isFalse -> constants.trueValue + reducedArg.isTrue -> ESConstants.falseValue + reducedArg.isFalse -> ESConstants.trueValue else -> reducedArg } } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/visitors/Substitutor.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/visitors/Substitutor.kt index d5f12880e78..f8b509d5629 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/visitors/Substitutor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/visitors/Substitutor.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.contracts.model.visitors -import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.contracts.model.Computation import org.jetbrains.kotlin.contracts.model.ESExpression import org.jetbrains.kotlin.contracts.model.ESExpressionVisitor @@ -29,34 +28,34 @@ import org.jetbrains.kotlin.contracts.model.structure.* */ class Substitutor( private val substitutions: Map, - private val builtIns: KotlinBuiltIns + private val reducer: Reducer ) : ESExpressionVisitor { override fun visitIs(isOperator: ESIs): Computation? { val arg = isOperator.left.accept(this) ?: return null - return CallComputation(builtIns.booleanType, isOperator.functor.invokeWithArguments(arg)) + return CallComputation(ESBooleanType, isOperator.functor.invokeWithArguments(arg)) } override fun visitNot(not: ESNot): Computation? { val arg = not.arg.accept(this) ?: return null - return CallComputation(builtIns.booleanType, not.functor.invokeWithArguments(arg)) + return CallComputation(ESBooleanType, not.functor.invokeWithArguments(arg)) } override fun visitEqual(equal: ESEqual): Computation? { val left = equal.left.accept(this) ?: return null val right = equal.right.accept(this) ?: return null - return CallComputation(builtIns.booleanType, equal.functor.invokeWithArguments(listOf(left, right))) + return CallComputation(ESBooleanType, equal.functor.invokeWithArguments(listOf(left, right), reducer)) } override fun visitAnd(and: ESAnd): Computation? { val left = and.left.accept(this) ?: return null val right = and.right.accept(this) ?: return null - return CallComputation(builtIns.booleanType, and.functor.invokeWithArguments(left, right)) + return CallComputation(ESBooleanType, and.functor.invokeWithArguments(left, right)) } override fun visitOr(or: ESOr): Computation? { val left = or.left.accept(this) ?: return null val right = or.right.accept(this) ?: return null - return CallComputation(builtIns.booleanType, or.functor.invokeWithArguments(left, right)) + return CallComputation(ESBooleanType, or.functor.invokeWithArguments(left, right)) } override fun visitVariable(esVariable: ESVariable): Computation? = substitutions[esVariable] ?: esVariable diff --git a/compiler/testData/diagnostics/tests/RecursiveTypeInference.kt b/compiler/testData/diagnostics/tests/RecursiveTypeInference.kt index 7b111c06075..72e5afa997b 100644 --- a/compiler/testData/diagnostics/tests/RecursiveTypeInference.kt +++ b/compiler/testData/diagnostics/tests/RecursiveTypeInference.kt @@ -11,13 +11,13 @@ package a package b fun foo() = bar() - fun bar() = foo() + fun bar() = foo() // FILE: f.kt package c fun bazz() = bar() - fun foo() = bazz() + fun foo() = bazz() fun bar() = foo() diff --git a/compiler/testData/diagnostics/tests/inference/recursiveCalls/kt23531.kt b/compiler/testData/diagnostics/tests/inference/recursiveCalls/kt23531.kt index 702f566be91..68eeafcea57 100644 --- a/compiler/testData/diagnostics/tests/inference/recursiveCalls/kt23531.kt +++ b/compiler/testData/diagnostics/tests/inference/recursiveCalls/kt23531.kt @@ -13,7 +13,7 @@ fun insideJob1() = doTheJob1() suspend fun insideJob2() = doTheJob2() suspend fun insideJob3() = doTheJob3() -fun doTheJob0() = simpleAsync0 { insideJob0() } -fun doTheJob1() = simpleAsync1 { insideJob1() } -suspend fun doTheJob2() = simpleAsync2 { insideJob2() } -suspend fun doTheJob3() = simpleAsync3 { insideJob3() } +fun doTheJob0() = simpleAsync0 { insideJob0() } +fun doTheJob1() = simpleAsync1 { insideJob1() } +suspend fun doTheJob2() = simpleAsync2 { insideJob2() } +suspend fun doTheJob3() = simpleAsync3 { insideJob3() } diff --git a/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/localFactorial.kt b/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/localFactorial.kt index 6144b92cd5f..e4352e4ac4b 100644 --- a/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/localFactorial.kt +++ b/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/localFactorial.kt @@ -3,7 +3,7 @@ fun foo() { fun fact(n: Int) = { if (n > 0) { - fact(n - 1) * n + fact(n - 1) * n } else { 1 diff --git a/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/recursiveFun.kt b/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/recursiveFun.kt index c8de63c917d..5c3e0d4b4e0 100644 --- a/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/recursiveFun.kt +++ b/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/recursiveFun.kt @@ -1,4 +1,4 @@ // !WITH_NEW_INFERENCE fun foo() { - fun bar() = (fun() = bar()) + fun bar() = (fun() = bar()) } diff --git a/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/recursiveLambda.kt b/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/recursiveLambda.kt index ab29170dd9a..b125d59d23a 100644 --- a/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/recursiveLambda.kt +++ b/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/recursiveLambda.kt @@ -1,6 +1,6 @@ // !WITH_NEW_INFERENCE fun foo() { fun bar() = { - bar() + bar() } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/selfCall.kt b/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/selfCall.kt index 061413babac..695d1762643 100644 --- a/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/selfCall.kt +++ b/compiler/testData/diagnostics/tests/inference/recursiveLocalFuns/selfCall.kt @@ -1,6 +1,6 @@ // !WITH_NEW_INFERENCE fun foo() { - fun bar1() = bar1() + fun bar1() = bar1() fun bar2() = 1 + bar2() fun bar3() = id(bar3()) diff --git a/compiler/testData/diagnostics/tests/regressions/kt328.kt b/compiler/testData/diagnostics/tests/regressions/kt328.kt index 14b6a7e1f93..e9bbf3d1671 100644 --- a/compiler/testData/diagnostics/tests/regressions/kt328.kt +++ b/compiler/testData/diagnostics/tests/regressions/kt328.kt @@ -2,11 +2,11 @@ //KT-328 Local function in function literals cause exceptions fun bar1() = { - bar1() + bar1() } fun bar2() = { - fun foo2() = bar2() + fun foo2() = bar2() } //properties @@ -25,5 +25,5 @@ val z = Unit) = f() fun bar3() = block{ foo3() // <-- missing closing curly bracket -fun foo3() = block{ bar3() } +fun foo3() = block{ bar3() } diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/kt18292.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/kt18292.kt index bed3e6a0856..d67d076b847 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/kt18292.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/kt18292.kt @@ -15,6 +15,6 @@ suspend fun fib(n: Long) = async { when { n < 2 -> n - else -> fib(n - 1).await() + fib(n - 2).await() + else -> fib(n - 1).await() + fib(n - 2).await() } } \ No newline at end of file