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:
Alexander Udalov
2018-12-21 16:23:29 +01:00
parent 9516d6e89b
commit 6cd3d9f19a
22 changed files with 202 additions and 162 deletions
@@ -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
}
}