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:
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-5
@@ -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 {
|
||||||
|
|||||||
+5
-7
@@ -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? =
|
||||||
|
|||||||
+6
-6
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-4
@@ -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>
|
||||||
}
|
}
|
||||||
+5
-7
@@ -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>
|
||||||
|
|
||||||
|
|||||||
-36
@@ -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>
|
|
||||||
}
|
|
||||||
+4
-6
@@ -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
|
||||||
|
|||||||
+19
-19
@@ -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
|
||||||
|
|||||||
+4
-4
@@ -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) {
|
||||||
|
|||||||
+2
-3
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-12
@@ -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
|
||||||
|
|||||||
+8
-10
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-7
@@ -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<!>()<!> }
|
||||||
|
|||||||
+1
-1
@@ -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
-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
-1
@@ -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
-1
@@ -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<!>()<!>)
|
||||||
|
|||||||
@@ -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<!>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user