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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user