From 95544b0970176b87ded81a7c7f40855c3ad7334a Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Mon, 20 May 2019 13:16:29 +0300 Subject: [PATCH] Add wrappers on KotlinType in EffectSystem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also that commit removes usages of builtins inside effect system and starts refactoring of functor composition via composition instead of inheritance. There are some changes in testdata related to inference of recursive functions with implicit return types. After this commit they all are marked as unresolved. It happens because those functions have DeferredType as return type, and computing this type produces recursive exception, which provides “typechecker recursive problem” diagnostic. Before this commit, function call was completed successfully, because call completer didn’t computed that type, and computation of DeferredType were triggered only in `DataFlowAnalyzer.checkType`. Now, effect system tries to compute that type while wrapping KotlinTypes into ESTypes, and effect system itself is triggered in in call completer, so, call completion doesn’t finish and function call is marked as unresolved. #KT-31364 --- .../contracts/ContextInfoToDataFlowInfo.kt | 2 +- .../kotlin/contracts/EffectSystem.kt | 24 +++------ .../contracts/EffectsExtractingVisitor.kt | 45 ++++++++-------- .../description/ContractDescription.kt | 8 ++- .../interpretation/ConditionInterpreter.kt | 12 ++--- .../ConstantValuesInterpreter.kt | 12 ++--- .../ContractInterpretationDispatcher.kt | 7 ++- .../kotlin/contracts/model/Computation.kt | 4 +- .../kotlin/contracts/model/ESComponents.kt | 15 ------ .../kotlin/contracts/model/Functor.kt | 12 ++++- .../model/functors/AbstractBinaryFunctor.kt | 12 ++--- .../model/functors/AbstractReducingFunctor.kt | 36 ------------- .../model/functors/AbstractUnaryFunctor.kt | 10 ++-- .../contracts/model/functors/AndFunctor.kt | 10 ++-- .../contracts/model/functors/EqualsFunctor.kt | 38 +++++++------- .../contracts/model/functors/IsFunctor.kt | 18 +++---- .../contracts/model/functors/NotFunctor.kt | 6 +-- .../contracts/model/functors/OrFunctor.kt | 10 ++-- .../model/functors/SubstitutingFunctor.kt | 8 +-- .../contracts/model/structure/Computations.kt | 5 +- .../contracts/model/structure/Operators.kt | 24 ++++----- .../kotlin/contracts/model/structure/Types.kt | 51 +++++++++++++++++++ .../contracts/model/structure/Values.kt | 20 ++++---- .../contracts/model/visitors/InfoCollector.kt | 18 +++---- .../contracts/model/visitors/Reducer.kt | 24 +++++---- .../contracts/model/visitors/Substitutor.kt | 13 +++-- .../tests/RecursiveTypeInference.kt | 4 +- .../tests/inference/recursiveCalls/kt23531.kt | 8 +-- .../recursiveLocalFuns/localFactorial.kt | 2 +- .../recursiveLocalFuns/recursiveFun.kt | 2 +- .../recursiveLocalFuns/recursiveLambda.kt | 2 +- .../inference/recursiveLocalFuns/selfCall.kt | 2 +- .../diagnostics/tests/regressions/kt328.kt | 6 +-- .../testsWithStdLib/coroutines/kt18292.kt | 2 +- 34 files changed, 231 insertions(+), 241 deletions(-) delete mode 100644 compiler/resolution/src/org/jetbrains/kotlin/contracts/model/ESComponents.kt delete mode 100644 compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/AbstractReducingFunctor.kt create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/contracts/model/structure/Types.kt 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