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 ESConstant -> when (constantReference) {
|
||||
ConstantReference.NULL -> DataFlowValue.nullValue(builtIns)
|
||||
else -> DataFlowValue(IdentifierInfo.NO, type)
|
||||
else -> DataFlowValue(IdentifierInfo.NO, type.toKotlinType(builtIns))
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.contracts.model.Computation
|
||||
import org.jetbrains.kotlin.contracts.model.ESComponents
|
||||
import org.jetbrains.kotlin.contracts.model.ESEffect
|
||||
import org.jetbrains.kotlin.contracts.model.MutableContextInfo
|
||||
import org.jetbrains.kotlin.contracts.model.functors.EqualsFunctor
|
||||
@@ -46,13 +45,6 @@ class EffectSystem(
|
||||
val dataFlowValueFactory: DataFlowValueFactory,
|
||||
val builtIns: KotlinBuiltIns
|
||||
) {
|
||||
// Lazy because this code is executed when the container is set up (before any resolution starts),
|
||||
// so builtins are not fully functional yet at that moment
|
||||
val components: ESComponents by lazy(LazyThreadSafetyMode.NONE) { ESComponents(builtIns) }
|
||||
|
||||
val constants: ESConstants
|
||||
get() = components.constants
|
||||
|
||||
fun getDataFlowInfoForFinishedCall(
|
||||
resolvedCall: ResolvedCall<*>,
|
||||
bindingTrace: BindingTrace,
|
||||
@@ -64,7 +56,7 @@ class EffectSystem(
|
||||
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return DataFlowInfo.EMPTY
|
||||
if (callExpression is KtDeclaration) return DataFlowInfo.EMPTY
|
||||
|
||||
val resultContextInfo = getContextInfoWhen(ESReturns(constants.wildcard), callExpression, bindingTrace, moduleDescriptor)
|
||||
val resultContextInfo = getContextInfoWhen(ESReturns(ESConstants.wildcard), callExpression, bindingTrace, moduleDescriptor)
|
||||
|
||||
return resultContextInfo.toDataFlowInfo(languageVersionSettings, builtIns)
|
||||
}
|
||||
@@ -83,10 +75,10 @@ class EffectSystem(
|
||||
val rightComputation =
|
||||
getNonTrivialComputation(rightExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY
|
||||
|
||||
val effects = EqualsFunctor(constants, false).invokeWithArguments(leftComputation, rightComputation)
|
||||
val effects = EqualsFunctor(false).invokeWithArguments(leftComputation, rightComputation)
|
||||
|
||||
val equalsContextInfo = InfoCollector(ESReturns(constants.trueValue), constants).collectFromSchema(effects)
|
||||
val notEqualsContextInfo = InfoCollector(ESReturns(constants.falseValue), constants).collectFromSchema(effects)
|
||||
val equalsContextInfo = InfoCollector(ESReturns(ESConstants.trueValue), builtIns).collectFromSchema(effects)
|
||||
val notEqualsContextInfo = InfoCollector(ESReturns(ESConstants.falseValue), builtIns).collectFromSchema(effects)
|
||||
|
||||
return ConditionalDataFlowInfo(
|
||||
equalsContextInfo.toDataFlowInfo(languageVersionSettings, builtIns),
|
||||
@@ -101,7 +93,7 @@ class EffectSystem(
|
||||
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return
|
||||
if (callExpression is KtDeclaration) return
|
||||
|
||||
val resultingContextInfo = getContextInfoWhen(ESReturns(constants.wildcard), callExpression, bindingTrace, moduleDescriptor)
|
||||
val resultingContextInfo = getContextInfoWhen(ESReturns(ESConstants.wildcard), callExpression, bindingTrace, moduleDescriptor)
|
||||
for (effect in resultingContextInfo.firedEffects) {
|
||||
val callsEffect = effect as? ESCalls ?: continue
|
||||
val lambdaExpression = (callsEffect.callable as? ESLambda)?.lambda ?: continue
|
||||
@@ -118,7 +110,7 @@ class EffectSystem(
|
||||
if (!languageVersionSettings.supportsFeature(LanguageFeature.UseReturnsEffect)) return DataFlowInfo.EMPTY
|
||||
if (condition == null) return DataFlowInfo.EMPTY
|
||||
|
||||
return getContextInfoWhen(ESReturns(constants.booleanValue(value)), condition, bindingTrace, moduleDescriptor)
|
||||
return getContextInfoWhen(ESReturns(ESConstants.booleanValue(value)), condition, bindingTrace, moduleDescriptor)
|
||||
.toDataFlowInfo(languageVersionSettings, moduleDescriptor.builtIns)
|
||||
}
|
||||
|
||||
@@ -133,11 +125,11 @@ class EffectSystem(
|
||||
}
|
||||
if (isInContractBlock) return MutableContextInfo.EMPTY
|
||||
val computation = getNonTrivialComputation(expression, bindingTrace, moduleDescriptor) ?: return MutableContextInfo.EMPTY
|
||||
return InfoCollector(observedEffect, constants).collectFromSchema(computation.effects)
|
||||
return InfoCollector(observedEffect, builtIns).collectFromSchema(computation.effects)
|
||||
}
|
||||
|
||||
private fun getNonTrivialComputation(expression: KtExpression, trace: BindingTrace, moduleDescriptor: ModuleDescriptor): Computation? {
|
||||
val visitor = EffectsExtractingVisitor(trace, moduleDescriptor, dataFlowValueFactory, constants, languageVersionSettings)
|
||||
val visitor = EffectsExtractingVisitor(trace, moduleDescriptor, dataFlowValueFactory, languageVersionSettings)
|
||||
return visitor.extractOrGetCached(expression).takeUnless { it == UNKNOWN_COMPUTATION }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,8 @@ import org.jetbrains.kotlin.contracts.model.ConditionalEffect
|
||||
import org.jetbrains.kotlin.contracts.model.ESEffect
|
||||
import org.jetbrains.kotlin.contracts.model.Functor
|
||||
import org.jetbrains.kotlin.contracts.model.functors.*
|
||||
import org.jetbrains.kotlin.contracts.model.structure.CallComputation
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
|
||||
import org.jetbrains.kotlin.contracts.model.structure.UNKNOWN_COMPUTATION
|
||||
import org.jetbrains.kotlin.contracts.model.structure.isReturns
|
||||
import org.jetbrains.kotlin.contracts.model.structure.*
|
||||
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
|
||||
import org.jetbrains.kotlin.contracts.parsing.isEqualsDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
@@ -58,10 +56,10 @@ class EffectsExtractingVisitor(
|
||||
private val trace: BindingTrace,
|
||||
private val moduleDescriptor: ModuleDescriptor,
|
||||
private val dataFlowValueFactory: DataFlowValueFactory,
|
||||
private val constants: ESConstants,
|
||||
private val languageVersionSettings: LanguageVersionSettings
|
||||
) : KtVisitor<Computation, Unit>() {
|
||||
private val builtIns: KotlinBuiltIns get() = moduleDescriptor.builtIns
|
||||
private val reducer: Reducer = Reducer(builtIns)
|
||||
|
||||
fun extractOrGetCached(element: KtElement): Computation {
|
||||
trace[BindingContext.EXPRESSION_EFFECTS, element]?.let { return it }
|
||||
@@ -77,17 +75,20 @@ class EffectsExtractingVisitor(
|
||||
val descriptor = resolvedCall.resultingDescriptor
|
||||
return when {
|
||||
descriptor.isEqualsDescriptor() -> CallComputation(
|
||||
builtIns.booleanType,
|
||||
EqualsFunctor(constants, false).invokeWithArguments(arguments)
|
||||
ESBooleanType,
|
||||
EqualsFunctor(false).invokeWithArguments(arguments, reducer)
|
||||
)
|
||||
descriptor is ValueDescriptor -> ESVariableWithDataFlowValue(
|
||||
descriptor,
|
||||
(element as KtExpression).createDataFlowValue() ?: return UNKNOWN_COMPUTATION
|
||||
)
|
||||
descriptor is FunctionDescriptor -> CallComputation(
|
||||
descriptor.returnType,
|
||||
descriptor.getFunctor()?.invokeWithArguments(arguments) ?: emptyList()
|
||||
)
|
||||
descriptor is FunctionDescriptor -> {
|
||||
val esType = descriptor.returnType?.toESType()
|
||||
CallComputation(
|
||||
esType,
|
||||
descriptor.getFunctor()?.invokeWithArguments(arguments, reducer) ?: emptyList()
|
||||
)
|
||||
}
|
||||
else -> UNKNOWN_COMPUTATION
|
||||
}
|
||||
}
|
||||
@@ -111,18 +112,18 @@ class EffectsExtractingVisitor(
|
||||
val value: Any? = compileTimeConstant.getValue(type)
|
||||
|
||||
return when (value) {
|
||||
is Boolean -> constants.booleanValue(value)
|
||||
null -> constants.nullValue
|
||||
is Boolean -> ESConstants.booleanValue(value)
|
||||
null -> ESConstants.nullValue
|
||||
else -> UNKNOWN_COMPUTATION
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitIsExpression(expression: KtIsExpression, data: Unit): Computation {
|
||||
val rightType: KotlinType = trace[BindingContext.TYPE, expression.typeReference] ?: return UNKNOWN_COMPUTATION
|
||||
val rightType = trace[BindingContext.TYPE, expression.typeReference]?.toESType() ?: return UNKNOWN_COMPUTATION
|
||||
val arg = extractOrGetCached(expression.leftHandSide)
|
||||
return CallComputation(
|
||||
builtIns.booleanType,
|
||||
IsFunctor(constants, rightType, expression.isNegated).invokeWithArguments(listOf(arg))
|
||||
ESBooleanType,
|
||||
IsFunctor(rightType, expression.isNegated).invokeWithArguments(listOf(arg), reducer)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -134,7 +135,7 @@ class EffectsExtractingVisitor(
|
||||
// null bypassing function's contract, so we have to filter them out
|
||||
|
||||
fun ESEffect.containsReturnsNull(): Boolean =
|
||||
isReturns { value == constants.nullValue } || this is ConditionalEffect && this.simpleEffect.containsReturnsNull()
|
||||
isReturns { value == ESConstants.nullValue } || this is ConditionalEffect && this.simpleEffect.containsReturnsNull()
|
||||
|
||||
val effectsWithoutReturnsNull = computation.effects.filter { !it.containsReturnsNull() }
|
||||
return CallComputation(computation.type, effectsWithoutReturnsNull)
|
||||
@@ -147,10 +148,10 @@ class EffectsExtractingVisitor(
|
||||
val args = listOf(left, right)
|
||||
|
||||
return when (expression.operationToken) {
|
||||
KtTokens.EXCLEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(constants, true).invokeWithArguments(args))
|
||||
KtTokens.EQEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(constants, false).invokeWithArguments(args))
|
||||
KtTokens.ANDAND -> CallComputation(builtIns.booleanType, AndFunctor(constants).invokeWithArguments(args))
|
||||
KtTokens.OROR -> CallComputation(builtIns.booleanType, OrFunctor(constants).invokeWithArguments(args))
|
||||
KtTokens.EXCLEQ -> CallComputation(ESBooleanType, EqualsFunctor(true).invokeWithArguments(args, reducer))
|
||||
KtTokens.EQEQ -> CallComputation(ESBooleanType, EqualsFunctor(false).invokeWithArguments(args, reducer))
|
||||
KtTokens.ANDAND -> CallComputation(ESBooleanType, AndFunctor().invokeWithArguments(args, reducer))
|
||||
KtTokens.OROR -> CallComputation(ESBooleanType, OrFunctor().invokeWithArguments(args, reducer))
|
||||
else -> UNKNOWN_COMPUTATION
|
||||
}
|
||||
}
|
||||
@@ -158,7 +159,7 @@ class EffectsExtractingVisitor(
|
||||
override fun visitUnaryExpression(expression: KtUnaryExpression, data: Unit): Computation {
|
||||
val arg = extractOrGetCached(expression.baseExpression ?: return UNKNOWN_COMPUTATION)
|
||||
return when (expression.operationToken) {
|
||||
KtTokens.EXCL -> CallComputation(builtIns.booleanType, NotFunctor(constants).invokeWithArguments(arg))
|
||||
KtTokens.EXCL -> CallComputation(ESBooleanType, NotFunctor().invokeWithArguments(arg))
|
||||
else -> UNKNOWN_COMPUTATION
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.contracts.description
|
||||
|
||||
import org.jetbrains.kotlin.contracts.interpretation.ContractInterpretationDispatcher
|
||||
import org.jetbrains.kotlin.contracts.model.ESComponents
|
||||
import org.jetbrains.kotlin.contracts.model.Functor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
@@ -40,12 +39,11 @@ open class ContractDescription(
|
||||
val ownerFunction: FunctionDescriptor,
|
||||
storageManager: StorageManager
|
||||
) {
|
||||
private val computeFunctor = storageManager.createMemoizedFunctionWithNullableValues<ModuleDescriptor, Functor> { module ->
|
||||
val components = ESComponents(module.builtIns)
|
||||
ContractInterpretationDispatcher(components).convertContractDescriptorToFunctor(this)
|
||||
private val computeFunctor = storageManager.createNullableLazyValue {
|
||||
ContractInterpretationDispatcher().convertContractDescriptorToFunctor(this)
|
||||
}
|
||||
|
||||
fun getFunctor(usageModule: ModuleDescriptor): Functor? = computeFunctor(usageModule)
|
||||
fun getFunctor(usageModule: ModuleDescriptor): Functor? = computeFunctor.invoke()
|
||||
}
|
||||
|
||||
interface ContractDescriptionElement {
|
||||
|
||||
+5
-7
@@ -25,33 +25,31 @@ import org.jetbrains.kotlin.contracts.model.structure.*
|
||||
internal class ConditionInterpreter(
|
||||
private val dispatcher: ContractInterpretationDispatcher
|
||||
) : ContractDescriptionVisitor<ESExpression?, Unit> {
|
||||
private val constants = dispatcher.components.constants
|
||||
|
||||
override fun visitLogicalOr(logicalOr: LogicalOr, data: Unit): ESExpression? {
|
||||
val left = logicalOr.left.accept(this, data) ?: return null
|
||||
val right = logicalOr.right.accept(this, data) ?: return null
|
||||
return ESOr(constants, left, right)
|
||||
return ESOr(left, right)
|
||||
}
|
||||
|
||||
override fun visitLogicalAnd(logicalAnd: LogicalAnd, data: Unit): ESExpression? {
|
||||
val left = logicalAnd.left.accept(this, data) ?: return null
|
||||
val right = logicalAnd.right.accept(this, data) ?: return null
|
||||
return ESAnd(constants, left, right)
|
||||
return ESAnd(left, right)
|
||||
}
|
||||
|
||||
override fun visitLogicalNot(logicalNot: LogicalNot, data: Unit): ESExpression? {
|
||||
val arg = logicalNot.arg.accept(this, data) ?: return null
|
||||
return ESNot(constants, arg)
|
||||
return ESNot(arg)
|
||||
}
|
||||
|
||||
override fun visitIsInstancePredicate(isInstancePredicate: IsInstancePredicate, data: Unit): ESExpression? {
|
||||
val esVariable = dispatcher.interpretVariable(isInstancePredicate.arg) ?: return null
|
||||
return ESIs(esVariable, IsFunctor(constants, isInstancePredicate.type, isInstancePredicate.isNegated))
|
||||
return ESIs(esVariable, IsFunctor(isInstancePredicate.type.toESType(), isInstancePredicate.isNegated))
|
||||
}
|
||||
|
||||
override fun visitIsNullPredicate(isNullPredicate: IsNullPredicate, data: Unit): ESExpression? {
|
||||
val variable = dispatcher.interpretVariable(isNullPredicate.arg) ?: return null
|
||||
return ESEqual(constants, variable, constants.nullValue, isNullPredicate.isNegated)
|
||||
return ESEqual(variable, ESConstants.nullValue, isNullPredicate.isNegated)
|
||||
}
|
||||
|
||||
override fun visitBooleanConstantDescriptor(booleanConstantDescriptor: BooleanConstantReference, data: Unit): ESExpression? =
|
||||
|
||||
+6
-6
@@ -22,12 +22,12 @@ import org.jetbrains.kotlin.contracts.model.structure.ESConstant
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
|
||||
|
||||
internal class ConstantValuesInterpreter {
|
||||
fun interpretConstant(constantReference: ConstantReference, constants: ESConstants): ESConstant? = when (constantReference) {
|
||||
BooleanConstantReference.TRUE -> constants.trueValue
|
||||
BooleanConstantReference.FALSE -> constants.falseValue
|
||||
ConstantReference.NULL -> constants.nullValue
|
||||
ConstantReference.NOT_NULL -> constants.notNullValue
|
||||
ConstantReference.WILDCARD -> constants.wildcard
|
||||
fun interpretConstant(constantReference: ConstantReference): ESConstant? = when (constantReference) {
|
||||
BooleanConstantReference.TRUE -> ESConstants.trueValue
|
||||
BooleanConstantReference.FALSE -> ESConstants.falseValue
|
||||
ConstantReference.NULL -> ESConstants.nullValue
|
||||
ConstantReference.NOT_NULL -> ESConstants.notNullValue
|
||||
ConstantReference.WILDCARD -> ESConstants.wildcard
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
+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.expressions.ConstantReference
|
||||
import org.jetbrains.kotlin.contracts.description.expressions.VariableReference
|
||||
import org.jetbrains.kotlin.contracts.model.ESComponents
|
||||
import org.jetbrains.kotlin.contracts.model.ESEffect
|
||||
import org.jetbrains.kotlin.contracts.model.ESExpression
|
||||
import org.jetbrains.kotlin.contracts.model.Functor
|
||||
@@ -33,7 +32,7 @@ import org.jetbrains.kotlin.contracts.model.structure.ESVariable
|
||||
/**
|
||||
* This class manages conversion of [ContractDescription] to [Functor]
|
||||
*/
|
||||
class ContractInterpretationDispatcher(val components: ESComponents) {
|
||||
class ContractInterpretationDispatcher {
|
||||
private val constantsInterpreter = ConstantValuesInterpreter()
|
||||
private val conditionInterpreter = ConditionInterpreter(this)
|
||||
private val conditionalEffectInterpreter = ConditionalEffectInterpreter(this)
|
||||
@@ -51,7 +50,7 @@ class ContractInterpretationDispatcher(val components: ESComponents) {
|
||||
}
|
||||
}
|
||||
|
||||
return SubstitutingFunctor(components, resultingClauses, contractDescription.ownerFunction)
|
||||
return SubstitutingFunctor(resultingClauses, contractDescription.ownerFunction)
|
||||
}
|
||||
|
||||
internal fun interpretEffect(effectDeclaration: EffectDeclaration): ESEffect? {
|
||||
@@ -60,7 +59,7 @@ class ContractInterpretationDispatcher(val components: ESComponents) {
|
||||
}
|
||||
|
||||
internal fun interpretConstant(constantReference: ConstantReference): ESConstant? =
|
||||
constantsInterpreter.interpretConstant(constantReference, components.constants)
|
||||
constantsInterpreter.interpretConstant(constantReference)
|
||||
|
||||
internal fun interpretCondition(booleanExpression: BooleanExpression): ESExpression? =
|
||||
booleanExpression.accept(conditionInterpreter, Unit)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.contracts.model
|
||||
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESType
|
||||
|
||||
/**
|
||||
* Generic abstraction of static information about some part of program.
|
||||
@@ -27,7 +27,7 @@ interface Computation {
|
||||
* If type is unknown or computation doesn't have a type (e.g. if
|
||||
* it is some construction, like "for"-loop), then type is 'null'
|
||||
*/
|
||||
val type: KotlinType?
|
||||
val type: ESType?
|
||||
|
||||
/**
|
||||
* List of all possible effects of this computation.
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.contracts.model
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
|
||||
|
||||
class ESComponents(
|
||||
val builtIns: KotlinBuiltIns
|
||||
) {
|
||||
val constants: ESConstants = ESConstants(builtIns)
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.contracts.model
|
||||
|
||||
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
|
||||
|
||||
/**
|
||||
* An abstraction of effect-generating nature of some computation.
|
||||
*
|
||||
@@ -25,5 +27,13 @@ package org.jetbrains.kotlin.contracts.model
|
||||
* values, it takes effects and returns effects.
|
||||
*/
|
||||
interface Functor {
|
||||
fun invokeWithArguments(arguments: List<Computation>): List<ESEffect>
|
||||
fun invokeWithArguments(arguments: List<Computation>, reducer: Reducer): List<ESEffect>
|
||||
}
|
||||
|
||||
|
||||
abstract class AbstractFunctor : Functor {
|
||||
override fun invokeWithArguments(arguments: List<Computation>, reducer: Reducer): List<ESEffect> =
|
||||
reducer.reduceEffects(doInvocation(arguments, reducer))
|
||||
|
||||
protected abstract fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect>
|
||||
}
|
||||
+5
-7
@@ -16,14 +16,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.contracts.model.functors
|
||||
|
||||
import org.jetbrains.kotlin.contracts.model.Computation
|
||||
import org.jetbrains.kotlin.contracts.model.ConditionalEffect
|
||||
import org.jetbrains.kotlin.contracts.model.ESEffect
|
||||
import org.jetbrains.kotlin.contracts.model.ESExpression
|
||||
import org.jetbrains.kotlin.contracts.model.*
|
||||
import org.jetbrains.kotlin.contracts.model.structure.*
|
||||
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
|
||||
|
||||
abstract class AbstractBinaryFunctor(constants: ESConstants) : AbstractReducingFunctor(constants) {
|
||||
override fun doInvocation(arguments: List<Computation>): List<ESEffect> {
|
||||
abstract class AbstractBinaryFunctor : AbstractFunctor() {
|
||||
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
|
||||
assert(arguments.size == 2) { "Wrong size of arguments list for Binary functor: expected 2, got ${arguments.size}" }
|
||||
return invokeWithArguments(arguments[0], arguments[1])
|
||||
}
|
||||
@@ -49,7 +47,7 @@ abstract class AbstractBinaryFunctor(constants: ESConstants) : AbstractReducingF
|
||||
if (list.isEmpty())
|
||||
null
|
||||
else
|
||||
list.map { it.condition }.reduce { acc, condition -> ESOr(constants, acc, condition) }
|
||||
list.map { it.condition }.reduce { acc, condition -> ESOr(acc, condition) }
|
||||
|
||||
protected abstract fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect>
|
||||
|
||||
|
||||
-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
|
||||
|
||||
import org.jetbrains.kotlin.contracts.model.Computation
|
||||
import org.jetbrains.kotlin.contracts.model.ConditionalEffect
|
||||
import org.jetbrains.kotlin.contracts.model.ESEffect
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
|
||||
import org.jetbrains.kotlin.contracts.model.*
|
||||
import org.jetbrains.kotlin.contracts.model.structure.isReturns
|
||||
import org.jetbrains.kotlin.contracts.model.structure.isWildcard
|
||||
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
|
||||
|
||||
/**
|
||||
* Unary functor that has sequential semantics, i.e. it won't apply to
|
||||
@@ -30,8 +28,8 @@ import org.jetbrains.kotlin.contracts.model.structure.isWildcard
|
||||
* It provides [applyToFinishingClauses] method for successors, which is guaranteed to
|
||||
* be called only on clauses that haven't failed before reaching functor transformation.
|
||||
*/
|
||||
abstract class AbstractUnaryFunctor(constants: ESConstants) : AbstractReducingFunctor(constants) {
|
||||
override fun doInvocation(arguments: List<Computation>): List<ESEffect> {
|
||||
abstract class AbstractUnaryFunctor : AbstractFunctor() {
|
||||
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
|
||||
assert(arguments.size == 1) { "Wrong size of arguments list for Unary operator: expected 1, got ${arguments.size}" }
|
||||
return invokeWithArguments(arguments[0])
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.contracts.model.ConditionalEffect
|
||||
import org.jetbrains.kotlin.contracts.model.ESEffect
|
||||
import org.jetbrains.kotlin.contracts.model.structure.*
|
||||
|
||||
class AndFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) {
|
||||
class AndFunctor : AbstractBinaryFunctor() {
|
||||
override fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect> = when {
|
||||
constant.isTrue -> computation.effects
|
||||
constant.isFalse -> emptyList()
|
||||
@@ -48,20 +48,20 @@ class AndFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) {
|
||||
|
||||
// Even if one of 'Returns(true)' is missing, we still can argue that other condition
|
||||
// *must* be true when whole functor returns true
|
||||
val conditionWhenTrue = applyWithDefault(whenLeftReturnsTrue, whenRightReturnsTrue) { l, r -> ESAnd(constants, l, r) }
|
||||
val conditionWhenTrue = applyWithDefault(whenLeftReturnsTrue, whenRightReturnsTrue) { l, r -> ESAnd(l, r) }
|
||||
|
||||
// When whole And-functor returns false, we can only argue that one of arguments was false, and to do so we
|
||||
// have to know *both* 'Returns(false)'-conditions
|
||||
val conditionWhenFalse = applyIfBothNotNull(whenLeftReturnsFalse, whenRightReturnsFalse) { l, r -> ESOr(constants, l, r) }
|
||||
val conditionWhenFalse = applyIfBothNotNull(whenLeftReturnsFalse, whenRightReturnsFalse) { l, r -> ESOr(l, r) }
|
||||
|
||||
val result = mutableListOf<ConditionalEffect>()
|
||||
|
||||
if (conditionWhenTrue != null) {
|
||||
result.add(ConditionalEffect(conditionWhenTrue, ESReturns(constants.trueValue)))
|
||||
result.add(ConditionalEffect(conditionWhenTrue, ESReturns(ESConstants.trueValue)))
|
||||
}
|
||||
|
||||
if (conditionWhenFalse != null) {
|
||||
result.add(ConditionalEffect(conditionWhenFalse, ESReturns(constants.falseValue)))
|
||||
result.add(ConditionalEffect(conditionWhenFalse, ESReturns(ESConstants.falseValue)))
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
+19
-19
@@ -16,14 +16,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.contracts.model.functors
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.contracts.model.Computation
|
||||
import org.jetbrains.kotlin.contracts.model.ConditionalEffect
|
||||
import org.jetbrains.kotlin.contracts.model.ESEffect
|
||||
import org.jetbrains.kotlin.contracts.model.ESValue
|
||||
import org.jetbrains.kotlin.contracts.model.*
|
||||
import org.jetbrains.kotlin.contracts.model.structure.*
|
||||
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
|
||||
|
||||
class EqualsFunctor(constants: ESConstants, val isNegated: Boolean) : AbstractReducingFunctor(constants) {
|
||||
class EqualsFunctor(val isNegated: Boolean) : AbstractFunctor() {
|
||||
/*
|
||||
Equals is a bit tricky case to produce clauses, because e.g. if we want to emit "Returns(true)"-clause,
|
||||
then we have to guarantee that we know *all* cases when 'true' could've been returned, and join
|
||||
@@ -41,7 +38,7 @@ class EqualsFunctor(constants: ESConstants, val isNegated: Boolean) : AbstractRe
|
||||
We don't want to code here fair analysis for general cases, because it's too complex. Instead, we just
|
||||
check some specific cases, which are useful enough in practice
|
||||
*/
|
||||
override fun doInvocation(arguments: List<Computation>): List<ESEffect> {
|
||||
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
|
||||
assert(arguments.size == 2) { "Equals functor expected 2 arguments, got ${arguments.size}" }
|
||||
|
||||
// TODO: AnnotationConstructorCaller kills this with implicit receiver. Investigate, how.
|
||||
@@ -77,16 +74,15 @@ class EqualsFunctor(constants: ESConstants, val isNegated: Boolean) : AbstractRe
|
||||
}
|
||||
|
||||
if (effect.simpleEffect.value == constant) {
|
||||
val trueClause = ConditionalEffect(effect.condition, ESReturns(constants.booleanValue(isNegated.not())))
|
||||
val trueClause = ConditionalEffect(effect.condition, ESReturns(ESConstants.booleanValue(isNegated.not())))
|
||||
resultingClauses.add(trueClause)
|
||||
}
|
||||
|
||||
if (effect.simpleEffect.value != constant && effect.simpleEffect.value is ESConstant && isSafeToProduceFalse(
|
||||
call,
|
||||
effect.simpleEffect.value,
|
||||
constant
|
||||
)) {
|
||||
val falseClause = ConditionalEffect(effect.condition, ESReturns(constants.booleanValue(isNegated)))
|
||||
call, effect.simpleEffect.value, constant
|
||||
)
|
||||
) {
|
||||
val falseClause = ConditionalEffect(effect.condition, ESReturns(ESConstants.booleanValue(isNegated)))
|
||||
resultingClauses.add(falseClause)
|
||||
}
|
||||
}
|
||||
@@ -96,11 +92,15 @@ class EqualsFunctor(constants: ESConstants, val isNegated: Boolean) : AbstractRe
|
||||
|
||||
// It is safe to produce false if we're comparing types which are isomorphic to Boolean. For such types we can be sure, that
|
||||
// if leftConstant != rightConstant, then this is the only way to produce 'false'.
|
||||
private fun isSafeToProduceFalse(leftCall: Computation, leftConstant: ESConstant, rightConstant: ESConstant): Boolean = when {
|
||||
// Comparison of Boolean
|
||||
KotlinBuiltIns.isBoolean(rightConstant.type) && leftCall.type != null && KotlinBuiltIns.isBoolean(leftCall.type!!) -> true
|
||||
private fun isSafeToProduceFalse(
|
||||
leftCall: Computation,
|
||||
leftConstant: ESConstant,
|
||||
rightConstant: ESConstant
|
||||
): Boolean = when {
|
||||
// Comparison of Boolean
|
||||
rightConstant.type.isBoolean() && leftCall.type.isBoolean() -> true
|
||||
|
||||
// Comparison of NULL/NOT_NULL, which is essentially Boolean
|
||||
// Comparison of NULL/NOT_NULL, which is essentially Boolean
|
||||
leftConstant.isNullConstant() && rightConstant.isNullConstant() -> true
|
||||
|
||||
else -> false
|
||||
@@ -108,8 +108,8 @@ class EqualsFunctor(constants: ESConstants, val isNegated: Boolean) : AbstractRe
|
||||
|
||||
private fun equateValues(left: ESValue, right: ESValue): List<ESEffect> {
|
||||
return listOf(
|
||||
ConditionalEffect(ESEqual(constants, left, right, isNegated), ESReturns(constants.trueValue)),
|
||||
ConditionalEffect(ESEqual(constants, left, right, isNegated.not()), ESReturns(constants.falseValue))
|
||||
ConditionalEffect(ESEqual(left, right, isNegated), ESReturns(ESConstants.trueValue)),
|
||||
ConditionalEffect(ESEqual(left, right, isNegated.not()), ESReturns(ESConstants.falseValue))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.contracts.model.functors
|
||||
|
||||
import org.jetbrains.kotlin.contracts.model.Computation
|
||||
import org.jetbrains.kotlin.contracts.model.ConditionalEffect
|
||||
import org.jetbrains.kotlin.contracts.model.ESEffect
|
||||
import org.jetbrains.kotlin.contracts.model.ESValue
|
||||
import org.jetbrains.kotlin.contracts.model.*
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESIs
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESReturns
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESType
|
||||
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
|
||||
|
||||
class IsFunctor(constants: ESConstants, val type: KotlinType, val isNegated: Boolean) : AbstractReducingFunctor(constants) {
|
||||
override fun doInvocation(arguments: List<Computation>): List<ESEffect> {
|
||||
class IsFunctor(val type: ESType, val isNegated: Boolean) : AbstractFunctor() {
|
||||
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
|
||||
assert(arguments.size == 1) { "Wrong size of arguments list for Unary operator: expected 1, got ${arguments.size}" }
|
||||
return invokeWithArguments(arguments[0])
|
||||
}
|
||||
@@ -40,10 +38,10 @@ class IsFunctor(constants: ESConstants, val type: KotlinType, val isNegated: Boo
|
||||
|
||||
private fun invokeWithValue(value: ESValue): List<ConditionalEffect> {
|
||||
val trueIs = ESIs(value, this)
|
||||
val falseIs = ESIs(value, IsFunctor(constants, type, isNegated.not()))
|
||||
val falseIs = ESIs(value, IsFunctor(type, isNegated.not()))
|
||||
|
||||
val trueResult = ConditionalEffect(trueIs, ESReturns(constants.trueValue))
|
||||
val falseResult = ConditionalEffect(falseIs, ESReturns(constants.falseValue))
|
||||
val trueResult = ConditionalEffect(trueIs, ESReturns(ESConstants.trueValue))
|
||||
val falseResult = ConditionalEffect(falseIs, ESReturns(ESConstants.falseValue))
|
||||
return listOf(trueResult, falseResult)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.contracts.model.structure.ESReturns
|
||||
import org.jetbrains.kotlin.contracts.model.structure.isFalse
|
||||
import org.jetbrains.kotlin.contracts.model.structure.isTrue
|
||||
|
||||
class NotFunctor(constants: ESConstants) : AbstractUnaryFunctor(constants) {
|
||||
class NotFunctor : AbstractUnaryFunctor() {
|
||||
override fun invokeWithReturningEffects(list: List<ConditionalEffect>): List<ConditionalEffect> = list.mapNotNull {
|
||||
val outcome = it.simpleEffect
|
||||
|
||||
@@ -31,8 +31,8 @@ class NotFunctor(constants: ESConstants) : AbstractUnaryFunctor(constants) {
|
||||
val returnValue = (outcome as ESReturns).value
|
||||
|
||||
when {
|
||||
returnValue.isTrue -> ConditionalEffect(it.condition, ESReturns(constants.falseValue))
|
||||
returnValue.isFalse -> ConditionalEffect(it.condition, ESReturns(constants.trueValue))
|
||||
returnValue.isTrue -> ConditionalEffect(it.condition, ESReturns(ESConstants.falseValue))
|
||||
returnValue.isFalse -> ConditionalEffect(it.condition, ESReturns(ESConstants.trueValue))
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.contracts.model.ConditionalEffect
|
||||
import org.jetbrains.kotlin.contracts.model.ESEffect
|
||||
import org.jetbrains.kotlin.contracts.model.structure.*
|
||||
|
||||
class OrFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) {
|
||||
class OrFunctor : AbstractBinaryFunctor() {
|
||||
override fun invokeWithConstant(computation: Computation, constant: ESConstant): List<ESEffect> = when {
|
||||
constant.isFalse -> computation.effects
|
||||
constant.isTrue -> emptyList()
|
||||
@@ -48,20 +48,20 @@ class OrFunctor(constants: ESConstants) : AbstractBinaryFunctor(constants) {
|
||||
|
||||
// When whole Or-functor returns true, all we know is that one of arguments was true.
|
||||
// So, to make a correct clause we have to know *both* 'Returns(true)'-conditions
|
||||
val conditionWhenTrue = applyIfBothNotNull(whenLeftReturnsTrue, whenRightReturnsTrue) { l, r -> ESOr(constants, l, r) }
|
||||
val conditionWhenTrue = applyIfBothNotNull(whenLeftReturnsTrue, whenRightReturnsTrue) { l, r -> ESOr(l, r) }
|
||||
|
||||
// Even if one of 'Returns(false)' is missing, we still can argue that other condition
|
||||
// *must* be false when whole OR-functor returns false
|
||||
val conditionWhenFalse = applyWithDefault(whenLeftReturnsFalse, whenRightReturnsFalse) { l, r -> ESAnd(constants, l, r) }
|
||||
val conditionWhenFalse = applyWithDefault(whenLeftReturnsFalse, whenRightReturnsFalse) { l, r -> ESAnd(l, r) }
|
||||
|
||||
val result = mutableListOf<ConditionalEffect>()
|
||||
|
||||
if (conditionWhenTrue != null) {
|
||||
result.add(ConditionalEffect(conditionWhenTrue, ESReturns(constants.trueValue)))
|
||||
result.add(ConditionalEffect(conditionWhenTrue, ESReturns(ESConstants.trueValue)))
|
||||
}
|
||||
|
||||
if (conditionWhenFalse != null) {
|
||||
result.add(ConditionalEffect(conditionWhenFalse, ESReturns(constants.falseValue)))
|
||||
result.add(ConditionalEffect(conditionWhenFalse, ESReturns(ESConstants.falseValue)))
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
+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.structure.*
|
||||
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
|
||||
import org.jetbrains.kotlin.contracts.model.visitors.Substitutor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueDescriptor
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class SubstitutingFunctor(
|
||||
private val components: ESComponents,
|
||||
private val basicEffects: List<ESEffect>,
|
||||
private val ownerFunction: FunctionDescriptor
|
||||
) : AbstractReducingFunctor(components.constants) {
|
||||
override fun doInvocation(arguments: List<Computation>): List<ESEffect> {
|
||||
) : AbstractFunctor() {
|
||||
override fun doInvocation(arguments: List<Computation>, reducer: Reducer): List<ESEffect> {
|
||||
if (basicEffects.isEmpty()) return emptyList()
|
||||
|
||||
val receiver =
|
||||
@@ -40,7 +40,7 @@ class SubstitutingFunctor(
|
||||
}
|
||||
|
||||
val substitutions = parameters.zip(arguments).toMap()
|
||||
val substitutor = Substitutor(substitutions, components.builtIns)
|
||||
val substitutor = Substitutor(substitutions, reducer)
|
||||
val substitutedClauses = mutableListOf<ESEffect>()
|
||||
|
||||
effectsLoop@ for (effect in basicEffects) {
|
||||
|
||||
+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.ESEffect
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
class CallComputation(override val type: KotlinType?, override val effects: List<ESEffect>) : Computation
|
||||
class CallComputation(override val type: ESType?, override val effects: List<ESEffect>) : Computation
|
||||
|
||||
object UNKNOWN_COMPUTATION : Computation {
|
||||
override val type: KotlinType? = null
|
||||
override val type: ESType? = null
|
||||
override val effects: List<ESEffect> = emptyList()
|
||||
}
|
||||
|
||||
+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.functors.*
|
||||
|
||||
class ESAnd(val constants: ESConstants, val left: ESExpression, val right: ESExpression) : ESOperator {
|
||||
override val functor: AndFunctor = AndFunctor(constants)
|
||||
class ESAnd(val left: ESExpression, val right: ESExpression) : ESOperator {
|
||||
override val functor: AndFunctor = AndFunctor()
|
||||
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitAnd(this)
|
||||
}
|
||||
|
||||
class ESOr(val constants: ESConstants, val left: ESExpression, val right: ESExpression) : ESOperator {
|
||||
override val functor: OrFunctor = OrFunctor(constants)
|
||||
class ESOr(val left: ESExpression, val right: ESExpression) : ESOperator {
|
||||
override val functor: OrFunctor = OrFunctor()
|
||||
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitOr(this)
|
||||
}
|
||||
|
||||
class ESNot(val constants: ESConstants, val arg: ESExpression) : ESOperator {
|
||||
override val functor = NotFunctor(constants)
|
||||
class ESNot(val arg: ESExpression) : ESOperator {
|
||||
override val functor = NotFunctor()
|
||||
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitNot(this)
|
||||
}
|
||||
|
||||
@@ -42,13 +42,13 @@ class ESIs(val left: ESValue, override val functor: IsFunctor) : ESOperator {
|
||||
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitIs(this)
|
||||
}
|
||||
|
||||
class ESEqual(val constants: ESConstants, val left: ESValue, val right: ESValue, isNegated: Boolean) : ESOperator {
|
||||
override val functor: EqualsFunctor = EqualsFunctor(constants, isNegated)
|
||||
class ESEqual(val left: ESValue, val right: ESValue, isNegated: Boolean) : ESOperator {
|
||||
override val functor: EqualsFunctor = EqualsFunctor(isNegated)
|
||||
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitEqual(this)
|
||||
}
|
||||
|
||||
fun ESExpression.and(other: ESExpression?, constants: ESConstants): ESExpression =
|
||||
if (other == null) this else ESAnd(constants, this, other)
|
||||
fun ESExpression.and(other: ESExpression?): ESExpression =
|
||||
if (other == null) this else ESAnd(this, other)
|
||||
|
||||
fun ESExpression.or(other: ESExpression?, constants: ESConstants): ESExpression =
|
||||
if (other == null) this else ESOr(constants, this, other)
|
||||
fun ESExpression.or(other: ESExpression?): ESExpression =
|
||||
if (other == null) this else ESOr(this, other)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.contracts.model.structure
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
sealed class ESType {
|
||||
abstract fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType
|
||||
}
|
||||
|
||||
abstract class ESBuiltInType : ESType()
|
||||
|
||||
object ESAnyType : ESBuiltInType() {
|
||||
override fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType = builtIns.anyType
|
||||
}
|
||||
|
||||
object ESNullableAnyType : ESBuiltInType() {
|
||||
override fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType = builtIns.nullableAnyType
|
||||
}
|
||||
|
||||
object ESNullableNothingType : ESBuiltInType() {
|
||||
override fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType = builtIns.nullableNothingType
|
||||
}
|
||||
|
||||
object ESNothingType : ESBuiltInType() {
|
||||
override fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType = builtIns.nothingType
|
||||
}
|
||||
|
||||
object ESBooleanType : ESType() {
|
||||
override fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType = builtIns.booleanType
|
||||
}
|
||||
|
||||
class ESKotlinType(val type: KotlinType) : ESType() {
|
||||
override fun toKotlinType(builtIns: KotlinBuiltIns): KotlinType = type
|
||||
}
|
||||
|
||||
|
||||
fun KotlinType.toESType() = when {
|
||||
KotlinBuiltIns.isBoolean(this) -> ESBooleanType
|
||||
KotlinBuiltIns.isAny(this) -> ESAnyType
|
||||
KotlinBuiltIns.isNullableAny(this) -> ESNullableAnyType
|
||||
KotlinBuiltIns.isNothing(this) -> ESNothingType
|
||||
KotlinBuiltIns.isNullableNothing(this) -> ESNullableNothingType
|
||||
else -> ESKotlinType(this)
|
||||
}
|
||||
|
||||
fun ESType?.isBoolean(): Boolean = this is ESBooleanType
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.contracts.model.structure
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.contracts.description.expressions.BooleanConstantReference
|
||||
import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference
|
||||
import org.jetbrains.kotlin.contracts.model.ESEffect
|
||||
@@ -25,7 +24,6 @@ import org.jetbrains.kotlin.contracts.model.ESExpressionVisitor
|
||||
import org.jetbrains.kotlin.contracts.model.ESValue
|
||||
import org.jetbrains.kotlin.descriptors.ValueDescriptor
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import java.util.*
|
||||
|
||||
|
||||
@@ -36,7 +34,7 @@ interface ESReceiver : ESValue {
|
||||
}
|
||||
|
||||
|
||||
abstract class AbstractESValue(override val type: KotlinType?) : ESValue {
|
||||
abstract class AbstractESValue(override val type: ESType?) : ESValue {
|
||||
override val effects: List<ESEffect> = listOf()
|
||||
}
|
||||
|
||||
@@ -57,7 +55,7 @@ open class ESReceiverValue(override val receiverValue: ReceiverValue) : Abstract
|
||||
*
|
||||
* [ESVariable] at points 2 and 3 must has consistent equality according to using them as keys
|
||||
*/
|
||||
open class ESVariable(val descriptor: ValueDescriptor) : AbstractESValue(descriptor.type) {
|
||||
open class ESVariable(val descriptor: ValueDescriptor) : AbstractESValue(descriptor.type.toESType()) {
|
||||
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitVariable(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@@ -82,7 +80,7 @@ open class ESVariable(val descriptor: ValueDescriptor) : AbstractESValue(descrip
|
||||
*
|
||||
* There is only few constants are supported (@see [ESConstant.Companion])
|
||||
*/
|
||||
class ESConstant internal constructor(val constantReference: ConstantReference, override val type: KotlinType) : AbstractESValue(type) {
|
||||
class ESConstant internal constructor(val constantReference: ConstantReference, override val type: ESType) : AbstractESValue(type) {
|
||||
override fun <T> accept(visitor: ESExpressionVisitor<T>): T = visitor.visitConstant(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean = other is ESConstant && constantReference == other.constantReference
|
||||
@@ -95,12 +93,12 @@ class ESConstant internal constructor(val constantReference: ConstantReference,
|
||||
constantReference == ConstantReference.NULL || constantReference == ConstantReference.NOT_NULL
|
||||
}
|
||||
|
||||
class ESConstants internal constructor(builtIns: KotlinBuiltIns) {
|
||||
val trueValue = ESConstant(BooleanConstantReference.TRUE, builtIns.booleanType)
|
||||
val falseValue = ESConstant(BooleanConstantReference.FALSE, builtIns.booleanType)
|
||||
val nullValue = ESConstant(ConstantReference.NULL, builtIns.nullableNothingType)
|
||||
val notNullValue = ESConstant(ConstantReference.NOT_NULL, builtIns.anyType)
|
||||
val wildcard = ESConstant(ConstantReference.WILDCARD, builtIns.nullableAnyType)
|
||||
object ESConstants {
|
||||
val trueValue = ESConstant(BooleanConstantReference.TRUE, ESBooleanType)
|
||||
val falseValue = ESConstant(BooleanConstantReference.FALSE, ESBooleanType)
|
||||
val nullValue = ESConstant(ConstantReference.NULL, ESNullableNothingType)
|
||||
val notNullValue = ESConstant(ConstantReference.NOT_NULL, ESAnyType)
|
||||
val wildcard = ESConstant(ConstantReference.WILDCARD, ESNullableAnyType)
|
||||
|
||||
fun booleanValue(value: Boolean) =
|
||||
if (value) trueValue else falseValue
|
||||
|
||||
+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.structure.*
|
||||
|
||||
class InfoCollector(
|
||||
private val observedEffect: ESEffect,
|
||||
private val constants: ESConstants
|
||||
) : ESExpressionVisitor<MutableContextInfo> {
|
||||
class InfoCollector(private val observedEffect: ESEffect, private val builtIns: KotlinBuiltIns) : ESExpressionVisitor<MutableContextInfo> {
|
||||
private var isInverted: Boolean = false
|
||||
|
||||
fun collectFromSchema(schema: List<ESEffect>): MutableContextInfo =
|
||||
@@ -41,18 +38,19 @@ class InfoCollector(
|
||||
|
||||
// Check for information from conditional effects
|
||||
return when (observedEffect.isImplies(effect.simpleEffect)) {
|
||||
// observed effect implies clause's effect => clause's effect was fired => clause's condition is true
|
||||
// observed effect implies clause's effect => clause's effect was fired => clause's condition is true
|
||||
true -> effect.condition.accept(this)
|
||||
|
||||
// Observed effect *may* or *doesn't* implies clause's - no useful information
|
||||
// Observed effect *may* or *doesn't* implies clause's - no useful information
|
||||
null, false -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitIs(isOperator: ESIs): MutableContextInfo = with(isOperator) {
|
||||
if (functor.isNegated != isInverted) MutableContextInfo.EMPTY.notSubtype(left, type) else MutableContextInfo.EMPTY.subtype(
|
||||
val isType = type.toKotlinType(builtIns)
|
||||
if (functor.isNegated != isInverted) MutableContextInfo.EMPTY.notSubtype(left, isType) else MutableContextInfo.EMPTY.subtype(
|
||||
left,
|
||||
type
|
||||
isType
|
||||
)
|
||||
}
|
||||
|
||||
@@ -76,10 +74,10 @@ class InfoCollector(
|
||||
}
|
||||
|
||||
override fun visitVariable(esVariable: ESVariable): MutableContextInfo =
|
||||
if (esVariable.type.let { it == null || !KotlinBuiltIns.isBoolean(it) })
|
||||
if (esVariable.type.let { !it.isBoolean() })
|
||||
MutableContextInfo.EMPTY
|
||||
else
|
||||
MutableContextInfo.EMPTY.equal(esVariable, constants.booleanValue(!isInverted))
|
||||
MutableContextInfo.EMPTY.equal(esVariable, ESConstants.booleanValue(!isInverted))
|
||||
|
||||
override fun visitConstant(esConstant: ESConstant): MutableContextInfo = MutableContextInfo.EMPTY
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.contracts.model.visitors
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.contracts.model.*
|
||||
import org.jetbrains.kotlin.contracts.model.structure.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
@@ -24,7 +25,7 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
* Reduces given list of effects by evaluating constant expressions,
|
||||
* throwing away senseless checks and infeasible clauses, etc.
|
||||
*/
|
||||
class Reducer(private val constants: ESConstants) : ESExpressionVisitor<ESExpression?> {
|
||||
class Reducer(private val builtIns: KotlinBuiltIns) : ESExpressionVisitor<ESExpression?> {
|
||||
fun reduceEffects(schema: List<ESEffect>): List<ESEffect> =
|
||||
schema.mapNotNull { reduceEffect(it) }
|
||||
|
||||
@@ -50,25 +51,28 @@ class Reducer(private val constants: ESConstants) : ESExpressionVisitor<ESExpres
|
||||
override fun visitIs(isOperator: ESIs): ESExpression {
|
||||
val reducedArg = isOperator.left.accept(this) as ESValue
|
||||
|
||||
val argType = reducedArg.type?.toKotlinType(builtIns)
|
||||
val isType = isOperator.functor.type.toKotlinType(builtIns)
|
||||
|
||||
val result = when (reducedArg) {
|
||||
is ESConstant -> reducedArg.type.isSubtypeOf(isOperator.functor.type)
|
||||
is ESVariable -> if (reducedArg.type?.isSubtypeOf(isOperator.functor.type) == true) true else null
|
||||
is ESConstant -> argType!!.isSubtypeOf(isType)
|
||||
is ESVariable -> if (argType?.isSubtypeOf(isType) == true) true else null
|
||||
else -> throw IllegalStateException("Unknown ESValue: $reducedArg")
|
||||
}
|
||||
|
||||
// Result is unknown, do not evaluate
|
||||
result ?: return ESIs(reducedArg, isOperator.functor)
|
||||
|
||||
return constants.booleanValue(result.xor(isOperator.functor.isNegated))
|
||||
return ESConstants.booleanValue(result.xor(isOperator.functor.isNegated))
|
||||
}
|
||||
|
||||
override fun visitEqual(equal: ESEqual): ESExpression {
|
||||
val reducedLeft = equal.left.accept(this) as ESValue
|
||||
val reducedRight = equal.right
|
||||
|
||||
if (reducedLeft is ESConstant) return constants.booleanValue((reducedLeft == reducedRight).xor(equal.functor.isNegated))
|
||||
if (reducedLeft is ESConstant) return ESConstants.booleanValue((reducedLeft == reducedRight).xor(equal.functor.isNegated))
|
||||
|
||||
return ESEqual(constants, reducedLeft, reducedRight, equal.functor.isNegated)
|
||||
return ESEqual(reducedLeft, reducedRight, equal.functor.isNegated)
|
||||
}
|
||||
|
||||
override fun visitAnd(and: ESAnd): ESExpression? {
|
||||
@@ -79,7 +83,7 @@ class Reducer(private val constants: ESConstants) : ESExpressionVisitor<ESExpres
|
||||
reducedLeft.isFalse || reducedRight.isFalse -> reducedLeft
|
||||
reducedLeft.isTrue -> reducedRight
|
||||
reducedRight.isTrue -> reducedLeft
|
||||
else -> ESAnd(constants, reducedLeft, reducedRight)
|
||||
else -> ESAnd(reducedLeft, reducedRight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +95,7 @@ class Reducer(private val constants: ESConstants) : ESExpressionVisitor<ESExpres
|
||||
reducedLeft.isTrue || reducedRight.isTrue -> reducedLeft
|
||||
reducedLeft.isFalse -> reducedRight
|
||||
reducedRight.isFalse -> reducedLeft
|
||||
else -> ESOr(constants, reducedLeft, reducedRight)
|
||||
else -> ESOr(reducedLeft, reducedRight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,8 +103,8 @@ class Reducer(private val constants: ESConstants) : ESExpressionVisitor<ESExpres
|
||||
val reducedArg = not.arg.accept(this) ?: return null
|
||||
|
||||
return when {
|
||||
reducedArg.isTrue -> constants.falseValue
|
||||
reducedArg.isFalse -> constants.trueValue
|
||||
reducedArg.isTrue -> ESConstants.falseValue
|
||||
reducedArg.isFalse -> ESConstants.trueValue
|
||||
else -> reducedArg
|
||||
}
|
||||
}
|
||||
|
||||
+6
-7
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.contracts.model.visitors
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.contracts.model.Computation
|
||||
import org.jetbrains.kotlin.contracts.model.ESExpression
|
||||
import org.jetbrains.kotlin.contracts.model.ESExpressionVisitor
|
||||
@@ -29,34 +28,34 @@ import org.jetbrains.kotlin.contracts.model.structure.*
|
||||
*/
|
||||
class Substitutor(
|
||||
private val substitutions: Map<ESVariable, Computation>,
|
||||
private val builtIns: KotlinBuiltIns
|
||||
private val reducer: Reducer
|
||||
) : ESExpressionVisitor<Computation?> {
|
||||
override fun visitIs(isOperator: ESIs): Computation? {
|
||||
val arg = isOperator.left.accept(this) ?: return null
|
||||
return CallComputation(builtIns.booleanType, isOperator.functor.invokeWithArguments(arg))
|
||||
return CallComputation(ESBooleanType, isOperator.functor.invokeWithArguments(arg))
|
||||
}
|
||||
|
||||
override fun visitNot(not: ESNot): Computation? {
|
||||
val arg = not.arg.accept(this) ?: return null
|
||||
return CallComputation(builtIns.booleanType, not.functor.invokeWithArguments(arg))
|
||||
return CallComputation(ESBooleanType, not.functor.invokeWithArguments(arg))
|
||||
}
|
||||
|
||||
override fun visitEqual(equal: ESEqual): Computation? {
|
||||
val left = equal.left.accept(this) ?: return null
|
||||
val right = equal.right.accept(this) ?: return null
|
||||
return CallComputation(builtIns.booleanType, equal.functor.invokeWithArguments(listOf(left, right)))
|
||||
return CallComputation(ESBooleanType, equal.functor.invokeWithArguments(listOf(left, right), reducer))
|
||||
}
|
||||
|
||||
override fun visitAnd(and: ESAnd): Computation? {
|
||||
val left = and.left.accept(this) ?: return null
|
||||
val right = and.right.accept(this) ?: return null
|
||||
return CallComputation(builtIns.booleanType, and.functor.invokeWithArguments(left, right))
|
||||
return CallComputation(ESBooleanType, and.functor.invokeWithArguments(left, right))
|
||||
}
|
||||
|
||||
override fun visitOr(or: ESOr): Computation? {
|
||||
val left = or.left.accept(this) ?: return null
|
||||
val right = or.right.accept(this) ?: return null
|
||||
return CallComputation(builtIns.booleanType, or.functor.invokeWithArguments(left, right))
|
||||
return CallComputation(ESBooleanType, or.functor.invokeWithArguments(left, right))
|
||||
}
|
||||
|
||||
override fun visitVariable(esVariable: ESVariable): Computation? = substitutions[esVariable] ?: esVariable
|
||||
|
||||
@@ -11,13 +11,13 @@ package a
|
||||
package b
|
||||
fun foo() = bar()
|
||||
|
||||
fun bar() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>foo<!>()<!>
|
||||
fun bar() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>foo<!>()<!>
|
||||
|
||||
// FILE: f.kt
|
||||
package c
|
||||
fun bazz() = bar()
|
||||
|
||||
fun foo() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bazz<!>()<!>
|
||||
fun foo() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bazz<!>()<!>
|
||||
|
||||
fun bar() = foo()
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ fun insideJob1() = doTheJob1()
|
||||
suspend fun insideJob2() = doTheJob2()
|
||||
suspend fun insideJob3() = doTheJob3()
|
||||
|
||||
fun doTheJob0() = simpleAsync0 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>insideJob0<!>()<!> }
|
||||
fun doTheJob1() = simpleAsync1 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>insideJob1<!>()<!> }
|
||||
suspend fun doTheJob2() = simpleAsync2 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED, OI;NON_LOCAL_SUSPENSION_POINT!>insideJob2<!>()<!> }
|
||||
suspend fun doTheJob3() = simpleAsync3 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>insideJob3<!>()<!> }
|
||||
fun doTheJob0() = simpleAsync0 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>insideJob0<!>()<!> }
|
||||
fun doTheJob1() = simpleAsync1 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>insideJob1<!>()<!> }
|
||||
suspend fun doTheJob2() = simpleAsync2 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>insideJob2<!>()<!> }
|
||||
suspend fun doTheJob3() = simpleAsync3 { <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>insideJob3<!>()<!> }
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
fun foo() {
|
||||
fun fact(n: Int) = {
|
||||
if (n > 0) {
|
||||
<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>fact<!>(<!NI;DEBUG_INFO_MISSING_UNRESOLVED!>n<!> <!NI;DEBUG_INFO_MISSING_UNRESOLVED!>-<!> 1)<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>*<!> n
|
||||
<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>fact<!>(<!DEBUG_INFO_MISSING_UNRESOLVED!>n<!> <!DEBUG_INFO_MISSING_UNRESOLVED!>-<!> 1)<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>*<!> n
|
||||
}
|
||||
else {
|
||||
1
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// !WITH_NEW_INFERENCE
|
||||
fun foo() {
|
||||
fun bar() = (fun() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar<!>()<!>)
|
||||
fun bar() = (fun() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bar<!>()<!>)
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// !WITH_NEW_INFERENCE
|
||||
fun foo() {
|
||||
fun bar() = {
|
||||
<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar<!>()<!>
|
||||
<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bar<!>()<!>
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// !WITH_NEW_INFERENCE
|
||||
fun foo() {
|
||||
fun bar1() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar1<!>()<!>
|
||||
fun bar1() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bar1<!>()<!>
|
||||
|
||||
fun bar2() = 1 + <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar2<!>()<!>
|
||||
fun bar3() = id(<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar3<!>()<!>)
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
//KT-328 Local function in function literals cause exceptions
|
||||
|
||||
fun bar1() = {
|
||||
<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar1<!>()<!>
|
||||
<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bar1<!>()<!>
|
||||
}
|
||||
|
||||
fun bar2() = {
|
||||
fun foo2() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar2<!>()<!>
|
||||
fun foo2() = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bar2<!>()<!>
|
||||
}
|
||||
|
||||
//properties
|
||||
@@ -25,5 +25,5 @@ val z = <!NI;DEBUG_INFO_MISSING_UNRESOLVED, OI;UNINITIALIZED_VARIABLE, TYPECHECK
|
||||
fun block(f : () -> Unit) = f()
|
||||
|
||||
fun bar3() = block{ <!UNRESOLVED_REFERENCE!>foo3<!>() // <-- missing closing curly bracket
|
||||
fun foo3() = block{ <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>bar3<!>()<!> }<!SYNTAX!><!>
|
||||
fun foo3() = block{ <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>bar3<!>()<!> }<!SYNTAX!><!>
|
||||
|
||||
|
||||
@@ -15,6 +15,6 @@ suspend fun fib(n: Long) =
|
||||
async {
|
||||
when {
|
||||
n < 2 -> n
|
||||
else -> <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED!>fib<!>(<!NI;DEBUG_INFO_MISSING_UNRESOLVED!>n<!> <!NI;DEBUG_INFO_MISSING_UNRESOLVED!>-<!> 1)<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>await<!>() <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>+<!> fib(n - 2).<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>await<!>()
|
||||
else -> <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!DEBUG_INFO_MISSING_UNRESOLVED!>fib<!>(<!DEBUG_INFO_MISSING_UNRESOLVED!>n<!> <!DEBUG_INFO_MISSING_UNRESOLVED!>-<!> 1)<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>await<!>() <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>+<!> fib(n - 2).<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>await<!>()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user