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 ESDataFlowValue -> dataFlowValue
is ESConstant -> when (constantReference) { is ESConstant -> when (constantReference) {
ConstantReference.NULL -> DataFlowValue.nullValue(builtIns) ConstantReference.NULL -> DataFlowValue.nullValue(builtIns)
else -> DataFlowValue(IdentifierInfo.NO, type) else -> DataFlowValue(IdentifierInfo.NO, type.toKotlinType(builtIns))
} }
else -> null else -> null
} }
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.model.Computation 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.ESEffect
import org.jetbrains.kotlin.contracts.model.MutableContextInfo import org.jetbrains.kotlin.contracts.model.MutableContextInfo
import org.jetbrains.kotlin.contracts.model.functors.EqualsFunctor import org.jetbrains.kotlin.contracts.model.functors.EqualsFunctor
@@ -46,13 +45,6 @@ class EffectSystem(
val dataFlowValueFactory: DataFlowValueFactory, val dataFlowValueFactory: DataFlowValueFactory,
val builtIns: KotlinBuiltIns 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( fun getDataFlowInfoForFinishedCall(
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
@@ -64,7 +56,7 @@ class EffectSystem(
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return DataFlowInfo.EMPTY val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return DataFlowInfo.EMPTY
if (callExpression is KtDeclaration) 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) return resultContextInfo.toDataFlowInfo(languageVersionSettings, builtIns)
} }
@@ -83,10 +75,10 @@ class EffectSystem(
val rightComputation = val rightComputation =
getNonTrivialComputation(rightExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY 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 equalsContextInfo = InfoCollector(ESReturns(ESConstants.trueValue), builtIns).collectFromSchema(effects)
val notEqualsContextInfo = InfoCollector(ESReturns(constants.falseValue), constants).collectFromSchema(effects) val notEqualsContextInfo = InfoCollector(ESReturns(ESConstants.falseValue), builtIns).collectFromSchema(effects)
return ConditionalDataFlowInfo( return ConditionalDataFlowInfo(
equalsContextInfo.toDataFlowInfo(languageVersionSettings, builtIns), equalsContextInfo.toDataFlowInfo(languageVersionSettings, builtIns),
@@ -101,7 +93,7 @@ class EffectSystem(
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return
if (callExpression is KtDeclaration) 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) { for (effect in resultingContextInfo.firedEffects) {
val callsEffect = effect as? ESCalls ?: continue val callsEffect = effect as? ESCalls ?: continue
val lambdaExpression = (callsEffect.callable as? ESLambda)?.lambda ?: continue val lambdaExpression = (callsEffect.callable as? ESLambda)?.lambda ?: continue
@@ -118,7 +110,7 @@ class EffectSystem(
if (!languageVersionSettings.supportsFeature(LanguageFeature.UseReturnsEffect)) return DataFlowInfo.EMPTY if (!languageVersionSettings.supportsFeature(LanguageFeature.UseReturnsEffect)) return DataFlowInfo.EMPTY
if (condition == null) 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) .toDataFlowInfo(languageVersionSettings, moduleDescriptor.builtIns)
} }
@@ -133,11 +125,11 @@ class EffectSystem(
} }
if (isInContractBlock) return MutableContextInfo.EMPTY if (isInContractBlock) return MutableContextInfo.EMPTY
val computation = getNonTrivialComputation(expression, bindingTrace, moduleDescriptor) ?: 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? { 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 } 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.ESEffect
import org.jetbrains.kotlin.contracts.model.Functor import org.jetbrains.kotlin.contracts.model.Functor
import org.jetbrains.kotlin.contracts.model.functors.* import org.jetbrains.kotlin.contracts.model.functors.*
import org.jetbrains.kotlin.contracts.model.structure.CallComputation import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.contracts.model.structure.ESConstants import org.jetbrains.kotlin.contracts.model.visitors.Reducer
import org.jetbrains.kotlin.contracts.model.structure.UNKNOWN_COMPUTATION
import org.jetbrains.kotlin.contracts.model.structure.isReturns
import org.jetbrains.kotlin.contracts.parsing.isEqualsDescriptor import org.jetbrains.kotlin.contracts.parsing.isEqualsDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -58,10 +56,10 @@ class EffectsExtractingVisitor(
private val trace: BindingTrace, private val trace: BindingTrace,
private val moduleDescriptor: ModuleDescriptor, private val moduleDescriptor: ModuleDescriptor,
private val dataFlowValueFactory: DataFlowValueFactory, private val dataFlowValueFactory: DataFlowValueFactory,
private val constants: ESConstants,
private val languageVersionSettings: LanguageVersionSettings private val languageVersionSettings: LanguageVersionSettings
) : KtVisitor<Computation, Unit>() { ) : KtVisitor<Computation, Unit>() {
private val builtIns: KotlinBuiltIns get() = moduleDescriptor.builtIns private val builtIns: KotlinBuiltIns get() = moduleDescriptor.builtIns
private val reducer: Reducer = Reducer(builtIns)
fun extractOrGetCached(element: KtElement): Computation { fun extractOrGetCached(element: KtElement): Computation {
trace[BindingContext.EXPRESSION_EFFECTS, element]?.let { return it } trace[BindingContext.EXPRESSION_EFFECTS, element]?.let { return it }
@@ -77,17 +75,20 @@ class EffectsExtractingVisitor(
val descriptor = resolvedCall.resultingDescriptor val descriptor = resolvedCall.resultingDescriptor
return when { return when {
descriptor.isEqualsDescriptor() -> CallComputation( descriptor.isEqualsDescriptor() -> CallComputation(
builtIns.booleanType, ESBooleanType,
EqualsFunctor(constants, false).invokeWithArguments(arguments) EqualsFunctor(false).invokeWithArguments(arguments, reducer)
) )
descriptor is ValueDescriptor -> ESVariableWithDataFlowValue( descriptor is ValueDescriptor -> ESVariableWithDataFlowValue(
descriptor, descriptor,
(element as KtExpression).createDataFlowValue() ?: return UNKNOWN_COMPUTATION (element as KtExpression).createDataFlowValue() ?: return UNKNOWN_COMPUTATION
) )
descriptor is FunctionDescriptor -> CallComputation( descriptor is FunctionDescriptor -> {
descriptor.returnType, val esType = descriptor.returnType?.toESType()
descriptor.getFunctor()?.invokeWithArguments(arguments) ?: emptyList() CallComputation(
) esType,
descriptor.getFunctor()?.invokeWithArguments(arguments, reducer) ?: emptyList()
)
}
else -> UNKNOWN_COMPUTATION else -> UNKNOWN_COMPUTATION
} }
} }
@@ -111,18 +112,18 @@ class EffectsExtractingVisitor(
val value: Any? = compileTimeConstant.getValue(type) val value: Any? = compileTimeConstant.getValue(type)
return when (value) { return when (value) {
is Boolean -> constants.booleanValue(value) is Boolean -> ESConstants.booleanValue(value)
null -> constants.nullValue null -> ESConstants.nullValue
else -> UNKNOWN_COMPUTATION else -> UNKNOWN_COMPUTATION
} }
} }
override fun visitIsExpression(expression: KtIsExpression, data: Unit): 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) val arg = extractOrGetCached(expression.leftHandSide)
return CallComputation( return CallComputation(
builtIns.booleanType, ESBooleanType,
IsFunctor(constants, rightType, expression.isNegated).invokeWithArguments(listOf(arg)) 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 // null bypassing function's contract, so we have to filter them out
fun ESEffect.containsReturnsNull(): Boolean = 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() } val effectsWithoutReturnsNull = computation.effects.filter { !it.containsReturnsNull() }
return CallComputation(computation.type, effectsWithoutReturnsNull) return CallComputation(computation.type, effectsWithoutReturnsNull)
@@ -147,10 +148,10 @@ class EffectsExtractingVisitor(
val args = listOf(left, right) val args = listOf(left, right)
return when (expression.operationToken) { return when (expression.operationToken) {
KtTokens.EXCLEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(constants, true).invokeWithArguments(args)) KtTokens.EXCLEQ -> CallComputation(ESBooleanType, EqualsFunctor(true).invokeWithArguments(args, reducer))
KtTokens.EQEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(constants, false).invokeWithArguments(args)) KtTokens.EQEQ -> CallComputation(ESBooleanType, EqualsFunctor(false).invokeWithArguments(args, reducer))
KtTokens.ANDAND -> CallComputation(builtIns.booleanType, AndFunctor(constants).invokeWithArguments(args)) KtTokens.ANDAND -> CallComputation(ESBooleanType, AndFunctor().invokeWithArguments(args, reducer))
KtTokens.OROR -> CallComputation(builtIns.booleanType, OrFunctor(constants).invokeWithArguments(args)) KtTokens.OROR -> CallComputation(ESBooleanType, OrFunctor().invokeWithArguments(args, reducer))
else -> UNKNOWN_COMPUTATION else -> UNKNOWN_COMPUTATION
} }
} }
@@ -158,7 +159,7 @@ class EffectsExtractingVisitor(
override fun visitUnaryExpression(expression: KtUnaryExpression, data: Unit): Computation { override fun visitUnaryExpression(expression: KtUnaryExpression, data: Unit): Computation {
val arg = extractOrGetCached(expression.baseExpression ?: return UNKNOWN_COMPUTATION) val arg = extractOrGetCached(expression.baseExpression ?: return UNKNOWN_COMPUTATION)
return when (expression.operationToken) { return when (expression.operationToken) {
KtTokens.EXCL -> CallComputation(builtIns.booleanType, NotFunctor(constants).invokeWithArguments(arg)) KtTokens.EXCL -> CallComputation(ESBooleanType, NotFunctor().invokeWithArguments(arg))
else -> UNKNOWN_COMPUTATION else -> UNKNOWN_COMPUTATION
} }
} }
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.contracts.description package org.jetbrains.kotlin.contracts.description
import org.jetbrains.kotlin.contracts.interpretation.ContractInterpretationDispatcher 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.contracts.model.Functor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -40,12 +39,11 @@ open class ContractDescription(
val ownerFunction: FunctionDescriptor, val ownerFunction: FunctionDescriptor,
storageManager: StorageManager storageManager: StorageManager
) { ) {
private val computeFunctor = storageManager.createMemoizedFunctionWithNullableValues<ModuleDescriptor, Functor> { module -> private val computeFunctor = storageManager.createNullableLazyValue {
val components = ESComponents(module.builtIns) ContractInterpretationDispatcher().convertContractDescriptorToFunctor(this)
ContractInterpretationDispatcher(components).convertContractDescriptorToFunctor(this)
} }
fun getFunctor(usageModule: ModuleDescriptor): Functor? = computeFunctor(usageModule) fun getFunctor(usageModule: ModuleDescriptor): Functor? = computeFunctor.invoke()
} }
interface ContractDescriptionElement { interface ContractDescriptionElement {
@@ -25,33 +25,31 @@ import org.jetbrains.kotlin.contracts.model.structure.*
internal class ConditionInterpreter( internal class ConditionInterpreter(
private val dispatcher: ContractInterpretationDispatcher private val dispatcher: ContractInterpretationDispatcher
) : ContractDescriptionVisitor<ESExpression?, Unit> { ) : ContractDescriptionVisitor<ESExpression?, Unit> {
private val constants = dispatcher.components.constants
override fun visitLogicalOr(logicalOr: LogicalOr, data: Unit): ESExpression? { override fun visitLogicalOr(logicalOr: LogicalOr, data: Unit): ESExpression? {
val left = logicalOr.left.accept(this, data) ?: return null val left = logicalOr.left.accept(this, data) ?: return null
val right = logicalOr.right.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? { override fun visitLogicalAnd(logicalAnd: LogicalAnd, data: Unit): ESExpression? {
val left = logicalAnd.left.accept(this, data) ?: return null val left = logicalAnd.left.accept(this, data) ?: return null
val right = logicalAnd.right.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? { override fun visitLogicalNot(logicalNot: LogicalNot, data: Unit): ESExpression? {
val arg = logicalNot.arg.accept(this, data) ?: return null val arg = logicalNot.arg.accept(this, data) ?: return null
return ESNot(constants, arg) return ESNot(arg)
} }
override fun visitIsInstancePredicate(isInstancePredicate: IsInstancePredicate, data: Unit): ESExpression? { override fun visitIsInstancePredicate(isInstancePredicate: IsInstancePredicate, data: Unit): ESExpression? {
val esVariable = dispatcher.interpretVariable(isInstancePredicate.arg) ?: return null 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? { override fun visitIsNullPredicate(isNullPredicate: IsNullPredicate, data: Unit): ESExpression? {
val variable = dispatcher.interpretVariable(isNullPredicate.arg) ?: return null 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? = 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 import org.jetbrains.kotlin.contracts.model.structure.ESConstants
internal class ConstantValuesInterpreter { internal class ConstantValuesInterpreter {
fun interpretConstant(constantReference: ConstantReference, constants: ESConstants): ESConstant? = when (constantReference) { fun interpretConstant(constantReference: ConstantReference): ESConstant? = when (constantReference) {
BooleanConstantReference.TRUE -> constants.trueValue BooleanConstantReference.TRUE -> ESConstants.trueValue
BooleanConstantReference.FALSE -> constants.falseValue BooleanConstantReference.FALSE -> ESConstants.falseValue
ConstantReference.NULL -> constants.nullValue ConstantReference.NULL -> ESConstants.nullValue
ConstantReference.NOT_NULL -> constants.notNullValue ConstantReference.NOT_NULL -> ESConstants.notNullValue
ConstantReference.WILDCARD -> constants.wildcard ConstantReference.WILDCARD -> ESConstants.wildcard
else -> null 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.EffectDeclaration
import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference
import org.jetbrains.kotlin.contracts.description.expressions.VariableReference 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.ESEffect
import org.jetbrains.kotlin.contracts.model.ESExpression import org.jetbrains.kotlin.contracts.model.ESExpression
import org.jetbrains.kotlin.contracts.model.Functor 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] * This class manages conversion of [ContractDescription] to [Functor]
*/ */
class ContractInterpretationDispatcher(val components: ESComponents) { class ContractInterpretationDispatcher {
private val constantsInterpreter = ConstantValuesInterpreter() private val constantsInterpreter = ConstantValuesInterpreter()
private val conditionInterpreter = ConditionInterpreter(this) private val conditionInterpreter = ConditionInterpreter(this)
private val conditionalEffectInterpreter = ConditionalEffectInterpreter(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? { internal fun interpretEffect(effectDeclaration: EffectDeclaration): ESEffect? {
@@ -60,7 +59,7 @@ class ContractInterpretationDispatcher(val components: ESComponents) {
} }
internal fun interpretConstant(constantReference: ConstantReference): ESConstant? = internal fun interpretConstant(constantReference: ConstantReference): ESConstant? =
constantsInterpreter.interpretConstant(constantReference, components.constants) constantsInterpreter.interpretConstant(constantReference)
internal fun interpretCondition(booleanExpression: BooleanExpression): ESExpression? = internal fun interpretCondition(booleanExpression: BooleanExpression): ESExpression? =
booleanExpression.accept(conditionInterpreter, Unit) booleanExpression.accept(conditionInterpreter, Unit)
@@ -16,7 +16,7 @@
package org.jetbrains.kotlin.contracts.model 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. * 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 * 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' * 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. * 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 package org.jetbrains.kotlin.contracts.model
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
/** /**
* An abstraction of effect-generating nature of some computation. * 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. * values, it takes effects and returns effects.
*/ */
interface Functor { 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 package org.jetbrains.kotlin.contracts.model.functors
import org.jetbrains.kotlin.contracts.model.Computation import org.jetbrains.kotlin.contracts.model.*
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.structure.* import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
abstract class AbstractBinaryFunctor(constants: ESConstants) : AbstractReducingFunctor(constants) { abstract class AbstractBinaryFunctor : AbstractFunctor() {
override fun doInvocation(arguments: List<Computation>): List<ESEffect> { 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}" } assert(arguments.size == 2) { "Wrong size of arguments list for Binary functor: expected 2, got ${arguments.size}" }
return invokeWithArguments(arguments[0], arguments[1]) return invokeWithArguments(arguments[0], arguments[1])
} }
@@ -49,7 +47,7 @@ abstract class AbstractBinaryFunctor(constants: ESConstants) : AbstractReducingF
if (list.isEmpty()) if (list.isEmpty())
null null
else 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> 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 package org.jetbrains.kotlin.contracts.model.functors
import org.jetbrains.kotlin.contracts.model.Computation import org.jetbrains.kotlin.contracts.model.*
import org.jetbrains.kotlin.contracts.model.ConditionalEffect
import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
import org.jetbrains.kotlin.contracts.model.structure.isReturns import org.jetbrains.kotlin.contracts.model.structure.isReturns
import org.jetbrains.kotlin.contracts.model.structure.isWildcard 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 * 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 * It provides [applyToFinishingClauses] method for successors, which is guaranteed to
* be called only on clauses that haven't failed before reaching functor transformation. * be called only on clauses that haven't failed before reaching functor transformation.
*/ */
abstract class AbstractUnaryFunctor(constants: ESConstants) : AbstractReducingFunctor(constants) { abstract class AbstractUnaryFunctor : AbstractFunctor() {
override fun doInvocation(arguments: List<Computation>): List<ESEffect> { 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}" } assert(arguments.size == 1) { "Wrong size of arguments list for Unary operator: expected 1, got ${arguments.size}" }
return invokeWithArguments(arguments[0]) 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.ESEffect
import org.jetbrains.kotlin.contracts.model.structure.* 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 { override fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect> = when {
constant.isTrue -> computation.effects constant.isTrue -> computation.effects
constant.isFalse -> emptyList() 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 // Even if one of 'Returns(true)' is missing, we still can argue that other condition
// *must* be true when whole functor returns true // *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 // 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 // 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>() val result = mutableListOf<ConditionalEffect>()
if (conditionWhenTrue != null) { if (conditionWhenTrue != null) {
result.add(ConditionalEffect(conditionWhenTrue, ESReturns(constants.trueValue))) result.add(ConditionalEffect(conditionWhenTrue, ESReturns(ESConstants.trueValue)))
} }
if (conditionWhenFalse != null) { if (conditionWhenFalse != null) {
result.add(ConditionalEffect(conditionWhenFalse, ESReturns(constants.falseValue))) result.add(ConditionalEffect(conditionWhenFalse, ESReturns(ESConstants.falseValue)))
} }
return result return result
@@ -16,14 +16,11 @@
package org.jetbrains.kotlin.contracts.model.functors package org.jetbrains.kotlin.contracts.model.functors
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.contracts.model.*
import org.jetbrains.kotlin.contracts.model.Computation
import org.jetbrains.kotlin.contracts.model.ConditionalEffect
import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.ESValue
import org.jetbrains.kotlin.contracts.model.structure.* 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, 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 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 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 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}" } assert(arguments.size == 2) { "Equals functor expected 2 arguments, got ${arguments.size}" }
// TODO: AnnotationConstructorCaller kills this with implicit receiver. Investigate, how. // 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) { 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) resultingClauses.add(trueClause)
} }
if (effect.simpleEffect.value != constant && effect.simpleEffect.value is ESConstant && isSafeToProduceFalse( if (effect.simpleEffect.value != constant && effect.simpleEffect.value is ESConstant && isSafeToProduceFalse(
call, call, effect.simpleEffect.value, constant
effect.simpleEffect.value, )
constant ) {
)) { val falseClause = ConditionalEffect(effect.condition, ESReturns(ESConstants.booleanValue(isNegated)))
val falseClause = ConditionalEffect(effect.condition, ESReturns(constants.booleanValue(isNegated)))
resultingClauses.add(falseClause) 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 // 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'. // if leftConstant != rightConstant, then this is the only way to produce 'false'.
private fun isSafeToProduceFalse(leftCall: Computation, leftConstant: ESConstant, rightConstant: ESConstant): Boolean = when { private fun isSafeToProduceFalse(
// Comparison of Boolean leftCall: Computation,
KotlinBuiltIns.isBoolean(rightConstant.type) && leftCall.type != null && KotlinBuiltIns.isBoolean(leftCall.type!!) -> true 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 leftConstant.isNullConstant() && rightConstant.isNullConstant() -> true
else -> false else -> false
@@ -108,8 +108,8 @@ class EqualsFunctor(constants: ESConstants, val isNegated: Boolean) : AbstractRe
private fun equateValues(left: ESValue, right: ESValue): List<ESEffect> { private fun equateValues(left: ESValue, right: ESValue): List<ESEffect> {
return listOf( return listOf(
ConditionalEffect(ESEqual(constants, left, right, isNegated), ESReturns(constants.trueValue)), ConditionalEffect(ESEqual(left, right, isNegated), ESReturns(ESConstants.trueValue)),
ConditionalEffect(ESEqual(constants, left, right, isNegated.not()), ESReturns(constants.falseValue)) ConditionalEffect(ESEqual(left, right, isNegated.not()), ESReturns(ESConstants.falseValue))
) )
} }
} }
@@ -16,17 +16,15 @@
package org.jetbrains.kotlin.contracts.model.functors package org.jetbrains.kotlin.contracts.model.functors
import org.jetbrains.kotlin.contracts.model.Computation import org.jetbrains.kotlin.contracts.model.*
import org.jetbrains.kotlin.contracts.model.ConditionalEffect
import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.ESValue
import org.jetbrains.kotlin.contracts.model.structure.ESConstants import org.jetbrains.kotlin.contracts.model.structure.ESConstants
import org.jetbrains.kotlin.contracts.model.structure.ESIs import org.jetbrains.kotlin.contracts.model.structure.ESIs
import org.jetbrains.kotlin.contracts.model.structure.ESReturns 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) { class IsFunctor(val type: ESType, val isNegated: Boolean) : AbstractFunctor() {
override fun doInvocation(arguments: List<Computation>): List<ESEffect> { 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}" } assert(arguments.size == 1) { "Wrong size of arguments list for Unary operator: expected 1, got ${arguments.size}" }
return invokeWithArguments(arguments[0]) 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> { private fun invokeWithValue(value: ESValue): List<ConditionalEffect> {
val trueIs = ESIs(value, this) 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 trueResult = ConditionalEffect(trueIs, ESReturns(ESConstants.trueValue))
val falseResult = ConditionalEffect(falseIs, ESReturns(constants.falseValue)) val falseResult = ConditionalEffect(falseIs, ESReturns(ESConstants.falseValue))
return listOf(trueResult, falseResult) 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.isFalse
import org.jetbrains.kotlin.contracts.model.structure.isTrue 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 { override fun invokeWithReturningEffects(list: List<ConditionalEffect>): List<ConditionalEffect> = list.mapNotNull {
val outcome = it.simpleEffect val outcome = it.simpleEffect
@@ -31,8 +31,8 @@ class NotFunctor(constants: ESConstants) : AbstractUnaryFunctor(constants) {
val returnValue = (outcome as ESReturns).value val returnValue = (outcome as ESReturns).value
when { when {
returnValue.isTrue -> ConditionalEffect(it.condition, ESReturns(constants.falseValue)) returnValue.isTrue -> ConditionalEffect(it.condition, ESReturns(ESConstants.falseValue))
returnValue.isFalse -> ConditionalEffect(it.condition, ESReturns(constants.trueValue)) returnValue.isFalse -> ConditionalEffect(it.condition, ESReturns(ESConstants.trueValue))
else -> null 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.ESEffect
import org.jetbrains.kotlin.contracts.model.structure.* 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 { override fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect> = when {
constant.isFalse -> computation.effects constant.isFalse -> computation.effects
constant.isTrue -> emptyList() 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. // 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 // 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 // Even if one of 'Returns(false)' is missing, we still can argue that other condition
// *must* be false when whole OR-functor returns false // *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>() val result = mutableListOf<ConditionalEffect>()
if (conditionWhenTrue != null) { if (conditionWhenTrue != null) {
result.add(ConditionalEffect(conditionWhenTrue, ESReturns(constants.trueValue))) result.add(ConditionalEffect(conditionWhenTrue, ESReturns(ESConstants.trueValue)))
} }
if (conditionWhenFalse != null) { if (conditionWhenFalse != null) {
result.add(ConditionalEffect(conditionWhenFalse, ESReturns(constants.falseValue))) result.add(ConditionalEffect(conditionWhenFalse, ESReturns(ESConstants.falseValue)))
} }
return result 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.*
import org.jetbrains.kotlin.contracts.model.structure.* 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.contracts.model.visitors.Substitutor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueDescriptor import org.jetbrains.kotlin.descriptors.ValueDescriptor
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
class SubstitutingFunctor( class SubstitutingFunctor(
private val components: ESComponents,
private val basicEffects: List<ESEffect>, private val basicEffects: List<ESEffect>,
private val ownerFunction: FunctionDescriptor private val ownerFunction: FunctionDescriptor
) : AbstractReducingFunctor(components.constants) { ) : AbstractFunctor() {
override fun doInvocation(arguments: List<Computation>): List<ESEffect> { override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
if (basicEffects.isEmpty()) return emptyList() if (basicEffects.isEmpty()) return emptyList()
val receiver = val receiver =
@@ -40,7 +40,7 @@ class SubstitutingFunctor(
} }
val substitutions = parameters.zip(arguments).toMap() val substitutions = parameters.zip(arguments).toMap()
val substitutor = Substitutor(substitutions, components.builtIns) val substitutor = Substitutor(substitutions, reducer)
val substitutedClauses = mutableListOf<ESEffect>() val substitutedClauses = mutableListOf<ESEffect>()
effectsLoop@ for (effect in basicEffects) { 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.Computation
import org.jetbrains.kotlin.contracts.model.ESEffect 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 { object UNKNOWN_COMPUTATION : Computation {
override val type: KotlinType? = null override val type: ESType? = null
override val effects: List<ESEffect> = emptyList() 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.ESValue
import org.jetbrains.kotlin.contracts.model.functors.* import org.jetbrains.kotlin.contracts.model.functors.*
class ESAnd(val constants: ESConstants, val left: ESExpression, val right: ESExpression) : ESOperator { class ESAnd(val left: ESExpression, val right: ESExpression) : ESOperator {
override val functor: AndFunctor = AndFunctor(constants) override val functor: AndFunctor = AndFunctor()
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitAnd(this) override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitAnd(this)
} }
class ESOr(val constants: ESConstants, val left: ESExpression, val right: ESExpression) : ESOperator { class ESOr(val left: ESExpression, val right: ESExpression) : ESOperator {
override val functor: OrFunctor = OrFunctor(constants) override val functor: OrFunctor = OrFunctor()
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitOr(this) override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitOr(this)
} }
class ESNot(val constants: ESConstants, val arg: ESExpression) : ESOperator { class ESNot(val arg: ESExpression) : ESOperator {
override val functor = NotFunctor(constants) override val functor = NotFunctor()
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitNot(this) 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) 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 { class ESEqual(val left: ESValue, val right: ESValue, isNegated: Boolean) : ESOperator {
override val functor: EqualsFunctor = EqualsFunctor(constants, isNegated) override val functor: EqualsFunctor = EqualsFunctor(isNegated)
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitEqual(this) override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitEqual(this)
} }
fun ESExpression.and(other: ESExpression?, constants: ESConstants): ESExpression = fun ESExpression.and(other: ESExpression?): ESExpression =
if (other == null) this else ESAnd(constants, this, other) if (other == null) this else ESAnd(this, other)
fun ESExpression.or(other: ESExpression?, constants: ESConstants): ESExpression = fun ESExpression.or(other: ESExpression?): ESExpression =
if (other == null) this else ESOr(constants, this, other) 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 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.BooleanConstantReference
import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference
import org.jetbrains.kotlin.contracts.model.ESEffect 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.contracts.model.ESValue
import org.jetbrains.kotlin.descriptors.ValueDescriptor import org.jetbrains.kotlin.descriptors.ValueDescriptor
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.KotlinType
import java.util.* 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() 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 * [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 <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitVariable(this)
override fun equals(other: Any?): Boolean { 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]) * 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 <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitConstant(this)
override fun equals(other: Any?): Boolean = other is ESConstant && constantReference == other.constantReference 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 constantReference == ConstantReference.NULL || constantReference == ConstantReference.NOT_NULL
} }
class ESConstants internal constructor(builtIns: KotlinBuiltIns) { object ESConstants {
val trueValue = ESConstant(BooleanConstantReference.TRUE, builtIns.booleanType) val trueValue = ESConstant(BooleanConstantReference.TRUE, ESBooleanType)
val falseValue = ESConstant(BooleanConstantReference.FALSE, builtIns.booleanType) val falseValue = ESConstant(BooleanConstantReference.FALSE, ESBooleanType)
val nullValue = ESConstant(ConstantReference.NULL, builtIns.nullableNothingType) val nullValue = ESConstant(ConstantReference.NULL, ESNullableNothingType)
val notNullValue = ESConstant(ConstantReference.NOT_NULL, builtIns.anyType) val notNullValue = ESConstant(ConstantReference.NOT_NULL, ESAnyType)
val wildcard = ESConstant(ConstantReference.WILDCARD, builtIns.nullableAnyType) val wildcard = ESConstant(ConstantReference.WILDCARD, ESNullableAnyType)
fun booleanValue(value: Boolean) = fun booleanValue(value: Boolean) =
if (value) trueValue else falseValue 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.MutableContextInfo
import org.jetbrains.kotlin.contracts.model.structure.* import org.jetbrains.kotlin.contracts.model.structure.*
class InfoCollector( class InfoCollector(private val observedEffect: ESEffect, private val builtIns: KotlinBuiltIns) : ESExpressionVisitor<MutableContextInfo> {
private val observedEffect: ESEffect,
private val constants: ESConstants
) : ESExpressionVisitor<MutableContextInfo> {
private var isInverted: Boolean = false private var isInverted: Boolean = false
fun collectFromSchema(schema: List<ESEffect>): MutableContextInfo = fun collectFromSchema(schema: List<ESEffect>): MutableContextInfo =
@@ -41,18 +38,19 @@ class InfoCollector(
// Check for information from conditional effects // Check for information from conditional effects
return when (observedEffect.isImplies(effect.simpleEffect)) { 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) 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 null, false -> null
} }
} }
override fun visitIs(isOperator: ESIs): MutableContextInfo = with(isOperator) { 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, left,
type isType
) )
} }
@@ -76,10 +74,10 @@ class InfoCollector(
} }
override fun visitVariable(esVariable: ESVariable): MutableContextInfo = override fun visitVariable(esVariable: ESVariable): MutableContextInfo =
if (esVariable.type.let { it == null || !KotlinBuiltIns.isBoolean(it) }) if (esVariable.type.let { !it.isBoolean() })
MutableContextInfo.EMPTY MutableContextInfo.EMPTY
else else
MutableContextInfo.EMPTY.equal(esVariable, constants.booleanValue(!isInverted)) MutableContextInfo.EMPTY.equal(esVariable, ESConstants.booleanValue(!isInverted))
override fun visitConstant(esConstant: ESConstant): MutableContextInfo = MutableContextInfo.EMPTY override fun visitConstant(esConstant: ESConstant): MutableContextInfo = MutableContextInfo.EMPTY
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.contracts.model.visitors 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.*
import org.jetbrains.kotlin.contracts.model.structure.* import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf 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, * Reduces given list of effects by evaluating constant expressions,
* throwing away senseless checks and infeasible clauses, etc. * 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> = fun reduceEffects(schema: List<ESEffect>): List<ESEffect> =
schema.mapNotNull { reduceEffect(it) } schema.mapNotNull { reduceEffect(it) }
@@ -50,25 +51,28 @@ class Reducer(private val constants: ESConstants) : ESExpressionVisitor<ESExpres
override fun visitIs(isOperator: ESIs): ESExpression { override fun visitIs(isOperator: ESIs): ESExpression {
val reducedArg = isOperator.left.accept(this) as ESValue 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) { val result = when (reducedArg) {
is ESConstant -> reducedArg.type.isSubtypeOf(isOperator.functor.type) is ESConstant -> argType!!.isSubtypeOf(isType)
is ESVariable -> if (reducedArg.type?.isSubtypeOf(isOperator.functor.type) == true) true else null is ESVariable -> if (argType?.isSubtypeOf(isType) == true) true else null
else -> throw IllegalStateException("Unknown ESValue: $reducedArg") else -> throw IllegalStateException("Unknown ESValue: $reducedArg")
} }
// Result is unknown, do not evaluate // Result is unknown, do not evaluate
result ?: return ESIs(reducedArg, isOperator.functor) 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 { override fun visitEqual(equal: ESEqual): ESExpression {
val reducedLeft = equal.left.accept(this) as ESValue val reducedLeft = equal.left.accept(this) as ESValue
val reducedRight = equal.right 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? { override fun visitAnd(and: ESAnd): ESExpression? {
@@ -79,7 +83,7 @@ class Reducer(private val constants: ESConstants) : ESExpressionVisitor<ESExpres
reducedLeft.isFalse || reducedRight.isFalse -> reducedLeft reducedLeft.isFalse || reducedRight.isFalse -> reducedLeft
reducedLeft.isTrue -> reducedRight reducedLeft.isTrue -> reducedRight
reducedRight.isTrue -> reducedLeft 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.isTrue || reducedRight.isTrue -> reducedLeft
reducedLeft.isFalse -> reducedRight reducedLeft.isFalse -> reducedRight
reducedRight.isFalse -> reducedLeft 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 val reducedArg = not.arg.accept(this) ?: return null
return when { return when {
reducedArg.isTrue -> constants.falseValue reducedArg.isTrue -> ESConstants.falseValue
reducedArg.isFalse -> constants.trueValue reducedArg.isFalse -> ESConstants.trueValue
else -> reducedArg else -> reducedArg
} }
} }
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.contracts.model.visitors 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.Computation
import org.jetbrains.kotlin.contracts.model.ESExpression import org.jetbrains.kotlin.contracts.model.ESExpression
import org.jetbrains.kotlin.contracts.model.ESExpressionVisitor import org.jetbrains.kotlin.contracts.model.ESExpressionVisitor
@@ -29,34 +28,34 @@ import org.jetbrains.kotlin.contracts.model.structure.*
*/ */
class Substitutor( class Substitutor(
private val substitutions: Map<ESVariable, Computation>, private val substitutions: Map<ESVariable, Computation>,
private val builtIns: KotlinBuiltIns private val reducer: Reducer
) : ESExpressionVisitor<Computation?> { ) : ESExpressionVisitor<Computation?> {
override fun visitIs(isOperator: ESIs): Computation? { override fun visitIs(isOperator: ESIs): Computation? {
val arg = isOperator.left.accept(this) ?: return null 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? { override fun visitNot(not: ESNot): Computation? {
val arg = not.arg.accept(this) ?: return null 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? { override fun visitEqual(equal: ESEqual): Computation? {
val left = equal.left.accept(this) ?: return null val left = equal.left.accept(this) ?: return null
val right = equal.right.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? { override fun visitAnd(and: ESAnd): Computation? {
val left = and.left.accept(this) ?: return null val left = and.left.accept(this) ?: return null
val right = and.right.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? { override fun visitOr(or: ESOr): Computation? {
val left = or.left.accept(this) ?: return null val left = or.left.accept(this) ?: return null
val right = or.right.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 override fun visitVariable(esVariable: ESVariable): Computation? = substitutions[esVariable] ?: esVariable
@@ -11,13 +11,13 @@ package a
package b package b
fun foo() = bar() 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 // FILE: f.kt
package c package c
fun bazz() = bar() 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() fun bar() = foo()
@@ -13,7 +13,7 @@ fun insideJob1() = doTheJob1()
suspend fun insideJob2() = doTheJob2() suspend fun insideJob2() = doTheJob2()
suspend fun insideJob3() = doTheJob3() suspend fun insideJob3() = doTheJob3()
fun doTheJob0() = simpleAsync0 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>insideJob0<!>()<!> } fun doTheJob0() = simpleAsync0 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>insideJob0<!>()<!> }
fun doTheJob1() = simpleAsync1 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>insideJob1<!>()<!> } fun doTheJob1() = simpleAsync1 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!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 doTheJob2() = simpleAsync2 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>insideJob2<!>()<!> }
suspend fun doTheJob3() = simpleAsync3 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>insideJob3<!>()<!> } suspend fun doTheJob3() = simpleAsync3 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>insideJob3<!>()<!> }
@@ -3,7 +3,7 @@
fun foo() { fun foo() {
fun fact(n: Int) = { fun fact(n: Int) = {
if (n > 0) { 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 { else {
1 1
@@ -1,4 +1,4 @@
// !WITH_NEW_INFERENCE // !WITH_NEW_INFERENCE
fun foo() { 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 // !WITH_NEW_INFERENCE
fun foo() { fun foo() {
fun bar() = { 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 // !WITH_NEW_INFERENCE
fun foo() { 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 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<!>()<!>) 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 //KT-328 Local function in function literals cause exceptions
fun bar1() = { 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 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 //properties
@@ -25,5 +25,5 @@ val z = <!NI;DEBUG_INFO_MISSING_UNRESOLVED, OI;UNINITIALIZED_VARIABLE, TYPECHECK
fun block(f : () -> Unit) = f() fun block(f : () -> Unit) = f()
fun bar3() = block{ <!UNRESOLVED_REFERENCE!>foo3<!>() // <-- missing closing curly bracket 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 { async {
when { when {
n < 2 -> n 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<!>()
} }
} }