Add wrappers on KotlinType in EffectSystem

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
This commit is contained in:
Dmitriy Novozhilov
2019-05-20 13:16:29 +03:00
parent fb77e1f0bd
commit 95544b0970
34 changed files with 231 additions and 241 deletions
@@ -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
}
@@ -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 }
}
}
@@ -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<Computation, Unit>() {
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
}
}
@@ -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<ModuleDescriptor, Functor> { 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 {
@@ -25,33 +25,31 @@ import org.jetbrains.kotlin.contracts.model.structure.*
internal class ConditionInterpreter(
private val dispatcher: ContractInterpretationDispatcher
) : ContractDescriptionVisitor<ESExpression?, Unit> {
private val constants = dispatcher.components.constants
override fun visitLogicalOr(logicalOr: LogicalOr, data: Unit): ESExpression? {
val left = logicalOr.left.accept(this, data) ?: return null
val right = logicalOr.right.accept(this, data) ?: return null
return ESOr(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? =
@@ -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
}
}
@@ -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)
@@ -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.
@@ -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)
}
@@ -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<Computation>): List<ESEffect>
fun invokeWithArguments(arguments: List<Computation>, reducer: Reducer): List<ESEffect>
}
abstract class AbstractFunctor : Functor {
override fun invokeWithArguments(arguments: List<Computation>, reducer: Reducer): List<ESEffect> =
reducer.reduceEffects(doInvocation(arguments, reducer))
protected abstract fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect>
}
@@ -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<Computation>): List<ESEffect> {
abstract class AbstractBinaryFunctor : AbstractFunctor() {
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
assert(arguments.size == 2) { "Wrong size of arguments list for Binary functor: expected 2, got ${arguments.size}" }
return invokeWithArguments(arguments[0], arguments[1])
}
@@ -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<ESEffect>
@@ -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<Computation>): List<ESEffect> = reducer.reduceEffects(doInvocation(arguments))
protected abstract fun doInvocation(arguments: List<Computation>): List<ESEffect>
}
@@ -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<Computation>): List<ESEffect> {
abstract class AbstractUnaryFunctor : AbstractFunctor() {
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
assert(arguments.size == 1) { "Wrong size of arguments list for Unary operator: expected 1, got ${arguments.size}" }
return invokeWithArguments(arguments[0])
}
@@ -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<ESEffect> = 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<ConditionalEffect>()
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
@@ -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<Computation>): List<ESEffect> {
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
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<ESEffect> {
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))
)
}
}
@@ -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<Computation>): List<ESEffect> {
class IsFunctor(val type: ESType, val isNegated: Boolean) : AbstractFunctor() {
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
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<ConditionalEffect> {
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)
}
}
@@ -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<ConditionalEffect>): List<ConditionalEffect> = 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
}
}
@@ -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<ESEffect> = 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<ConditionalEffect>()
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
@@ -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<ESEffect>,
private val ownerFunction: FunctionDescriptor
) : AbstractReducingFunctor(components.constants) {
override fun doInvocation(arguments: List<Computation>): List<ESEffect> {
) : AbstractFunctor() {
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
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<ESEffect>()
effectsLoop@ for (effect in basicEffects) {
@@ -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<ESEffect>) : Computation
class CallComputation(override val type: ESType?, override val effects: List<ESEffect>) : Computation
object UNKNOWN_COMPUTATION : Computation {
override val type: KotlinType? = null
override val type: ESType? = null
override val effects: List<ESEffect> = emptyList()
}
@@ -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 <T> accept(visitor: ESExpressionVisitor<T>): 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 <T> accept(visitor: ESExpressionVisitor<T>): 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 <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitNot(this)
}
@@ -42,13 +42,13 @@ class ESIs(val left: ESValue, override val functor: IsFunctor) : ESOperator {
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitIs(this)
}
class ESEqual(val 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 <T> accept(visitor: ESExpressionVisitor<T>): 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)
@@ -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
@@ -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<ESEffect> = 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 <T> accept(visitor: ESExpressionVisitor<T>): 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 <T> accept(visitor: ESExpressionVisitor<T>): 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
@@ -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<MutableContextInfo> {
class InfoCollector(private val observedEffect: ESEffect, private val builtIns: KotlinBuiltIns) : ESExpressionVisitor<MutableContextInfo> {
private var isInverted: Boolean = false
fun collectFromSchema(schema: List<ESEffect>): 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
@@ -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<ESExpression?> {
class Reducer(private val builtIns: KotlinBuiltIns) : ESExpressionVisitor<ESExpression?> {
fun reduceEffects(schema: List<ESEffect>): List<ESEffect> =
schema.mapNotNull { reduceEffect(it) }
@@ -50,25 +51,28 @@ class Reducer(private val constants: ESConstants) : ESExpressionVisitor<ESExpres
override fun visitIs(isOperator: ESIs): ESExpression {
val reducedArg = isOperator.left.accept(this) as ESValue
val argType = reducedArg.type?.toKotlinType(builtIns)
val isType = isOperator.functor.type.toKotlinType(builtIns)
val result = when (reducedArg) {
is ESConstant -> 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<ESExpres
reducedLeft.isFalse || reducedRight.isFalse -> 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<ESExpres
reducedLeft.isTrue || reducedRight.isTrue -> 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<ESExpres
val reducedArg = not.arg.accept(this) ?: return null
return when {
reducedArg.isTrue -> constants.falseValue
reducedArg.isFalse -> constants.trueValue
reducedArg.isTrue -> ESConstants.falseValue
reducedArg.isFalse -> ESConstants.trueValue
else -> reducedArg
}
}
@@ -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<ESVariable, Computation>,
private val builtIns: KotlinBuiltIns
private val reducer: Reducer
) : ESExpressionVisitor<Computation?> {
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
@@ -11,13 +11,13 @@ package a
package b
fun foo() = bar()
fun bar() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>foo<!>()<!>
fun bar() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>foo<!>()<!>
// FILE: f.kt
package c
fun bazz() = bar()
fun foo() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bazz<!>()<!>
fun foo() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bazz<!>()<!>
fun bar() = foo()
@@ -13,7 +13,7 @@ fun insideJob1() = doTheJob1()
suspend fun insideJob2() = doTheJob2()
suspend fun insideJob3() = doTheJob3()
fun doTheJob0() = simpleAsync0 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>insideJob0<!>()<!> }
fun doTheJob1() = simpleAsync1 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>insideJob1<!>()<!> }
suspend fun doTheJob2() = simpleAsync2 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED, OI;NON_LOCAL_SUSPENSION_POINT!>insideJob2<!>()<!> }
suspend fun doTheJob3() = simpleAsync3 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>insideJob3<!>()<!> }
fun doTheJob0() = simpleAsync0 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>insideJob0<!>()<!> }
fun doTheJob1() = simpleAsync1 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>insideJob1<!>()<!> }
suspend fun doTheJob2() = simpleAsync2 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>insideJob2<!>()<!> }
suspend fun doTheJob3() = simpleAsync3 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>insideJob3<!>()<!> }
@@ -3,7 +3,7 @@
fun foo() {
fun fact(n: Int) = {
if (n > 0) {
<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>fact<!>(<!NI;DEBUG_INFO_MISSING_UNRESOLVED!>n<!> <!NI;DEBUG_INFO_MISSING_UNRESOLVED!>-<!> 1)<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>*<!> n
<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>fact<!>(<!DEBUG_INFO_MISSING_UNRESOLVED!>n<!> <!DEBUG_INFO_MISSING_UNRESOLVED!>-<!> 1)<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>*<!> n
}
else {
1
@@ -1,4 +1,4 @@
// !WITH_NEW_INFERENCE
fun foo() {
fun bar() = (fun() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar<!>()<!>)
fun bar() = (fun() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bar<!>()<!>)
}
@@ -1,6 +1,6 @@
// !WITH_NEW_INFERENCE
fun foo() {
fun bar() = {
<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar<!>()<!>
<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bar<!>()<!>
}
}
@@ -1,6 +1,6 @@
// !WITH_NEW_INFERENCE
fun foo() {
fun bar1() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar1<!>()<!>
fun bar1() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bar1<!>()<!>
fun bar2() = 1 + <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar2<!>()<!>
fun bar3() = id(<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar3<!>()<!>)
+3 -3
View File
@@ -2,11 +2,11 @@
//KT-328 Local function in function literals cause exceptions
fun bar1() = {
<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar1<!>()<!>
<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bar1<!>()<!>
}
fun bar2() = {
fun foo2() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar2<!>()<!>
fun foo2() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bar2<!>()<!>
}
//properties
@@ -25,5 +25,5 @@ val z = <!NI;DEBUG_INFO_MISSING_UNRESOLVED, OI;UNINITIALIZED_VARIABLE, TYPECHECK
fun block(f : () -> Unit) = f()
fun bar3() = block{ <!UNRESOLVED_REFERENCE!>foo3<!>() // <-- missing closing curly bracket
fun foo3() = block{ <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar3<!>()<!> }<!SYNTAX!><!>
fun foo3() = block{ <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bar3<!>()<!> }<!SYNTAX!><!>
@@ -15,6 +15,6 @@ suspend fun fib(n: Long) =
async {
when {
n < 2 -> n
else -> <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>fib<!>(<!NI;DEBUG_INFO_MISSING_UNRESOLVED!>n<!> <!NI;DEBUG_INFO_MISSING_UNRESOLVED!>-<!> 1)<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>await<!>() <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>+<!> fib(n - 2).<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>await<!>()
else -> <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>fib<!>(<!DEBUG_INFO_MISSING_UNRESOLVED!>n<!> <!DEBUG_INFO_MISSING_UNRESOLVED!>-<!> 1)<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>await<!>() <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>+<!> fib(n - 2).<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>await<!>()
}
}