Do not use DefaultBuiltIns.Instance in ESConstant
Add ESComponents and ESConstants to encapsulate usages of built-ins in different functors and operators
This commit is contained in:
@@ -16,13 +16,18 @@
|
||||
|
||||
package org.jetbrains.kotlin.contracts
|
||||
|
||||
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
|
||||
import org.jetbrains.kotlin.contracts.model.structure.*
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESCalls
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESReturns
|
||||
import org.jetbrains.kotlin.contracts.model.structure.UNKNOWN_COMPUTATION
|
||||
import org.jetbrains.kotlin.contracts.model.visitors.InfoCollector
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
@@ -35,7 +40,17 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.ConditionalDataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
|
||||
class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dataFlowValueFactory: DataFlowValueFactory) {
|
||||
class EffectSystem(
|
||||
val languageVersionSettings: LanguageVersionSettings,
|
||||
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<*>,
|
||||
@@ -48,9 +63,9 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
|
||||
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return DataFlowInfo.EMPTY
|
||||
if (callExpression is KtDeclaration) return DataFlowInfo.EMPTY
|
||||
|
||||
val resultContextInfo = getContextInfoWhen(ESReturns(ESConstant.WILDCARD), callExpression, bindingTrace, moduleDescriptor)
|
||||
val resultContextInfo = getContextInfoWhen(ESReturns(constants.wildcard), callExpression, bindingTrace, moduleDescriptor)
|
||||
|
||||
return resultContextInfo.toDataFlowInfo(languageVersionSettings, moduleDescriptor.builtIns)
|
||||
return resultContextInfo.toDataFlowInfo(languageVersionSettings, builtIns)
|
||||
}
|
||||
|
||||
fun getDataFlowInfoWhenEquals(
|
||||
@@ -67,11 +82,10 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
|
||||
val rightComputation =
|
||||
getNonTrivialComputation(rightExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY
|
||||
|
||||
val effects = EqualsFunctor(false).invokeWithArguments(leftComputation, rightComputation)
|
||||
val effects = EqualsFunctor(constants, false).invokeWithArguments(leftComputation, rightComputation)
|
||||
|
||||
val builtIns = moduleDescriptor.builtIns
|
||||
val equalsContextInfo = InfoCollector(ESReturns(true.lift())).collectFromSchema(effects)
|
||||
val notEqualsContextInfo = InfoCollector(ESReturns(false.lift())).collectFromSchema(effects)
|
||||
val equalsContextInfo = InfoCollector(ESReturns(constants.trueValue), constants).collectFromSchema(effects)
|
||||
val notEqualsContextInfo = InfoCollector(ESReturns(constants.falseValue), constants).collectFromSchema(effects)
|
||||
|
||||
return ConditionalDataFlowInfo(
|
||||
equalsContextInfo.toDataFlowInfo(languageVersionSettings, builtIns),
|
||||
@@ -86,7 +100,7 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
|
||||
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return
|
||||
if (callExpression is KtDeclaration) return
|
||||
|
||||
val resultingContextInfo = getContextInfoWhen(ESReturns(ESConstant.WILDCARD), callExpression, bindingTrace, moduleDescriptor)
|
||||
val resultingContextInfo = getContextInfoWhen(ESReturns(constants.wildcard), callExpression, bindingTrace, moduleDescriptor)
|
||||
for (effect in resultingContextInfo.firedEffects) {
|
||||
val callsEffect = effect as? ESCalls ?: continue
|
||||
val lambdaExpression = (callsEffect.callable as? ESLambda)?.lambda ?: continue
|
||||
@@ -103,7 +117,7 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
|
||||
if (!languageVersionSettings.supportsFeature(LanguageFeature.UseReturnsEffect)) return DataFlowInfo.EMPTY
|
||||
if (condition == null) return DataFlowInfo.EMPTY
|
||||
|
||||
return getContextInfoWhen(ESReturns(value.lift()), condition, bindingTrace, moduleDescriptor)
|
||||
return getContextInfoWhen(ESReturns(constants.booleanValue(value)), condition, bindingTrace, moduleDescriptor)
|
||||
.toDataFlowInfo(languageVersionSettings, moduleDescriptor.builtIns)
|
||||
}
|
||||
|
||||
@@ -114,11 +128,11 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings, val dat
|
||||
moduleDescriptor: ModuleDescriptor
|
||||
): MutableContextInfo {
|
||||
val computation = getNonTrivialComputation(expression, bindingTrace, moduleDescriptor) ?: return MutableContextInfo.EMPTY
|
||||
return InfoCollector(observedEffect).collectFromSchema(computation.effects)
|
||||
return InfoCollector(observedEffect, constants).collectFromSchema(computation.effects)
|
||||
}
|
||||
|
||||
private fun getNonTrivialComputation(expression: KtExpression, trace: BindingTrace, moduleDescriptor: ModuleDescriptor): Computation? {
|
||||
val computation = EffectsExtractingVisitor(trace, moduleDescriptor, dataFlowValueFactory).extractOrGetCached(expression)
|
||||
return if (computation == UNKNOWN_COMPUTATION) null else computation
|
||||
val visitor = EffectsExtractingVisitor(trace, moduleDescriptor, dataFlowValueFactory, constants)
|
||||
return visitor.extractOrGetCached(expression).takeUnless { it == UNKNOWN_COMPUTATION }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,10 @@ 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.*
|
||||
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.parsing.isEqualsDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
@@ -51,9 +54,10 @@ import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
class EffectsExtractingVisitor(
|
||||
private val trace: BindingTrace,
|
||||
private val moduleDescriptor: ModuleDescriptor,
|
||||
private val dataFlowValueFactory: DataFlowValueFactory
|
||||
private val dataFlowValueFactory: DataFlowValueFactory,
|
||||
private val constants: ESConstants
|
||||
) : KtVisitor<Computation, Unit>() {
|
||||
private val builtIns: KotlinBuiltIns = moduleDescriptor.builtIns
|
||||
private val builtIns: KotlinBuiltIns get() = moduleDescriptor.builtIns
|
||||
|
||||
fun extractOrGetCached(element: KtElement): Computation {
|
||||
trace[BindingContext.EXPRESSION_EFFECTS, element]?.let { return it }
|
||||
@@ -70,7 +74,7 @@ class EffectsExtractingVisitor(
|
||||
return when {
|
||||
descriptor.isEqualsDescriptor() -> CallComputation(
|
||||
builtIns.booleanType,
|
||||
EqualsFunctor(false).invokeWithArguments(arguments)
|
||||
EqualsFunctor(constants, false).invokeWithArguments(arguments)
|
||||
)
|
||||
descriptor is ValueDescriptor -> ESDataFlowValue(
|
||||
descriptor,
|
||||
@@ -103,8 +107,8 @@ class EffectsExtractingVisitor(
|
||||
val value: Any? = compileTimeConstant.getValue(type)
|
||||
|
||||
return when (value) {
|
||||
is Boolean -> value.lift()
|
||||
null -> ESConstant.NULL
|
||||
is Boolean -> constants.booleanValue(value)
|
||||
null -> constants.nullValue
|
||||
else -> UNKNOWN_COMPUTATION
|
||||
}
|
||||
}
|
||||
@@ -114,7 +118,7 @@ class EffectsExtractingVisitor(
|
||||
val arg = extractOrGetCached(expression.leftHandSide)
|
||||
return CallComputation(
|
||||
builtIns.booleanType,
|
||||
IsFunctor(rightType, expression.isNegated).invokeWithArguments(listOf(arg))
|
||||
IsFunctor(constants, rightType, expression.isNegated).invokeWithArguments(listOf(arg))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -126,7 +130,7 @@ class EffectsExtractingVisitor(
|
||||
// null bypassing function's contract, so we have to filter them out
|
||||
|
||||
fun ESEffect.containsReturnsNull(): Boolean =
|
||||
isReturns { value == ESConstant.NULL } || this is ConditionalEffect && this.simpleEffect.containsReturnsNull()
|
||||
isReturns { value == constants.nullValue } || this is ConditionalEffect && this.simpleEffect.containsReturnsNull()
|
||||
|
||||
val effectsWithoutReturnsNull = computation.effects.filter { !it.containsReturnsNull() }
|
||||
return CallComputation(computation.type, effectsWithoutReturnsNull)
|
||||
@@ -139,10 +143,10 @@ class EffectsExtractingVisitor(
|
||||
val args = listOf(left, right)
|
||||
|
||||
return when (expression.operationToken) {
|
||||
KtTokens.EXCLEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(true).invokeWithArguments(args))
|
||||
KtTokens.EQEQ -> CallComputation(builtIns.booleanType, EqualsFunctor(false).invokeWithArguments(args))
|
||||
KtTokens.ANDAND -> CallComputation(builtIns.booleanType, AndFunctor().invokeWithArguments(args))
|
||||
KtTokens.OROR -> CallComputation(builtIns.booleanType, OrFunctor().invokeWithArguments(args))
|
||||
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))
|
||||
else -> UNKNOWN_COMPUTATION
|
||||
}
|
||||
}
|
||||
@@ -150,7 +154,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().invokeWithArguments(arg))
|
||||
KtTokens.EXCL -> CallComputation(builtIns.booleanType, NotFunctor(constants).invokeWithArguments(arg))
|
||||
else -> UNKNOWN_COMPUTATION
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user