Effects: add facade of EffectSystem

- Add facade of effect system in form of EffectSystem class, as well as
some other utility classes.

- Inject effect system facade classes into ExpressionTypingComponents

- Call to ContractParsingServices in ExpressionTypingVisitor to record
function contract (if any)

- Introduce FilteringTrace

- Create new FilteringTrace for statement in ExpressionTypingServices
that will serve as a cache for EffectSystem, filtering ES-related
slices from committing into parent.

- Extract and expose ConditionalDataFlowInfo

==========
Effect System introduction: 6/18
This commit is contained in:
Dmitry Savvinov
2017-10-03 15:02:32 +03:00
parent a46ccdbae1
commit b347e31fc4
9 changed files with 505 additions and 3 deletions
@@ -0,0 +1,66 @@
/*
* 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
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.model.ESValue
import org.jetbrains.kotlin.contracts.model.MutableContextInfo
import org.jetbrains.kotlin.contracts.model.structure.ESConstant
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
import org.jetbrains.kotlin.resolve.calls.smartcasts.IdentifierInfo
fun MutableContextInfo.toDataFlowInfo(languageVersionSettings: LanguageVersionSettings): DataFlowInfo {
var resultingDataFlowInfo = DataFlowInfoFactory.EMPTY
extractDataFlowStatements(equalValues) { leftDfv, rightValue ->
val rightDfv = rightValue.toDataFlowValue()
if (rightDfv != null) {
resultingDataFlowInfo = resultingDataFlowInfo.equate(leftDfv, rightDfv, false, languageVersionSettings)
}
IntArray(42) { it }
}
extractDataFlowStatements(notEqualValues) { leftDfv, rightValue ->
val rightDfv = rightValue.toDataFlowValue()
if (rightDfv != null) {
resultingDataFlowInfo = resultingDataFlowInfo.disequate(leftDfv, rightDfv, languageVersionSettings)
}
}
extractDataFlowStatements(subtypes) { leftDfv, type ->
resultingDataFlowInfo = resultingDataFlowInfo.establishSubtyping(leftDfv, type, languageVersionSettings)
}
return resultingDataFlowInfo
}
inline private fun <D> extractDataFlowStatements(dictionary: Map<ESValue, Set<D>>, callback: (DataFlowValue, D) -> Unit) {
for ((key, setOfValues) in dictionary) {
val leftDfv = key.toDataFlowValue() ?: continue
setOfValues.forEach { callback(leftDfv, it) }
}
}
private fun ESValue.toDataFlowValue(): DataFlowValue? = when (this) {
is ESDataFlowValue -> dataFlowValue
ESConstant.NULL -> DataFlowValue.nullValue(DefaultBuiltIns.Instance)
is ESConstant -> DataFlowValue(IdentifierInfo.NO, type)
else -> null
}
@@ -0,0 +1,32 @@
/*
* 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
import org.jetbrains.kotlin.contracts.model.ESValue
import org.jetbrains.kotlin.contracts.model.structure.ESVariable
import org.jetbrains.kotlin.contracts.model.ESExpressionVisitor
import org.jetbrains.kotlin.descriptors.ValueDescriptor
import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
class ESDataFlowValue(descriptor: ValueDescriptor, val dataFlowValue: DataFlowValue) : ESVariable(descriptor)
class ESLambda(val lambda: KtLambdaExpression) : ESValue(null) {
override fun <T> accept(visitor: ESExpressionVisitor<T>): T {
throw IllegalStateException("Lambdas shouldn't be visited by ESExpressionVisitor")
}
}
@@ -0,0 +1,124 @@
/*
* 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
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.model.structure.ESCalls
import org.jetbrains.kotlin.contracts.model.structure.ESReturns
import org.jetbrains.kotlin.contracts.model.functors.EqualsFunctor
import org.jetbrains.kotlin.contracts.model.structure.ESConstant
import org.jetbrains.kotlin.contracts.model.structure.UNKNOWN_COMPUTATION
import org.jetbrains.kotlin.contracts.model.structure.lift
import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.Computation
import org.jetbrains.kotlin.contracts.model.MutableContextInfo
import org.jetbrains.kotlin.contracts.model.visitors.InfoCollector
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.smartcasts.ConditionalDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
class EffectSystem(val languageVersionSettings: LanguageVersionSettings) {
fun getDataFlowInfoForFinishedCall(
resolvedCall: ResolvedCall<*>,
bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor
): DataFlowInfo {
if (!languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect)) return DataFlowInfo.EMPTY
// Prevent launch of effect system machinery on pointless cases (constants/enums/constructors/etc.)
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)
return resultContextInfo.toDataFlowInfo(languageVersionSettings)
}
fun getDataFlowInfoWhenEquals(
leftExpression: KtExpression?,
rightExpression: KtExpression?,
bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor
): ConditionalDataFlowInfo {
if (!languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect)) return ConditionalDataFlowInfo.EMPTY
if (leftExpression == null || rightExpression == null) return ConditionalDataFlowInfo.EMPTY
val leftComputation = getNonTrivialComputation(leftExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY
val rightComputation = getNonTrivialComputation(rightExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY
val effects = EqualsFunctor(false).invokeWithArguments(leftComputation, rightComputation)
val equalsContextInfo = InfoCollector(ESReturns(true.lift())).collectFromSchema(effects)
val notEqualsContextInfo = InfoCollector(ESReturns(false.lift())).collectFromSchema(effects)
return ConditionalDataFlowInfo(
equalsContextInfo.toDataFlowInfo(languageVersionSettings),
notEqualsContextInfo.toDataFlowInfo(languageVersionSettings)
)
}
fun recordDefiniteInvocations(resolvedCall: ResolvedCall<*>, bindingTrace: BindingTrace, moduleDescriptor: ModuleDescriptor) {
if (!languageVersionSettings.supportsFeature(LanguageFeature.CallsInPlaceEffect)) return
// Prevent launch of effect system machinery on pointless cases (constants/enums/constructors/etc.)
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return
if (callExpression is KtDeclaration) return
val resultingContextInfo = getContextInfoWhen(ESReturns(ESConstant.WILDCARD), callExpression, bindingTrace, moduleDescriptor)
for (effect in resultingContextInfo.firedEffects) {
val callsEffect = effect as? ESCalls ?: continue
val lambdaExpression = (callsEffect.callable as? ESLambda)?.lambda ?: continue
bindingTrace.record(BindingContext.LAMBDA_INVOCATIONS, lambdaExpression, callsEffect.kind)
}
}
fun extractDataFlowInfoFromCondition(
condition: KtExpression?,
value: Boolean,
bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor
): DataFlowInfo {
if (!languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect)) return DataFlowInfo.EMPTY
if (condition == null) return DataFlowInfo.EMPTY
return getContextInfoWhen(ESReturns(value.lift()), condition, bindingTrace, moduleDescriptor)
.toDataFlowInfo(languageVersionSettings)
}
private fun getContextInfoWhen(
observedEffect: ESEffect,
expression: KtExpression,
bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor
): MutableContextInfo {
val computation = getNonTrivialComputation(expression, bindingTrace, moduleDescriptor) ?: return MutableContextInfo.EMPTY
return InfoCollector(observedEffect).collectFromSchema(computation.effects)
}
private fun getNonTrivialComputation(expression: KtExpression, trace: BindingTrace, moduleDescriptor: ModuleDescriptor): Computation? {
val computation = EffectsExtractingVisitor(trace, moduleDescriptor).extractOrGetCached(expression)
return if (computation == UNKNOWN_COMPUTATION) null else computation
}
}
@@ -0,0 +1,170 @@
/*
* 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
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.contracts.interpretation.ContractInterpretationDispatcher
import org.jetbrains.kotlin.contracts.model.Computation
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.ESConstant
import org.jetbrains.kotlin.contracts.model.structure.UNKNOWN_COMPUTATION
import org.jetbrains.kotlin.contracts.model.structure.lift
import org.jetbrains.kotlin.contracts.parsing.isEqualsDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.ValueDescriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addIfNotNull
/**
* Visits a given PSI-tree of call (and nested calls, if any) and extracts information
* about effects of that call.
*/
class EffectsExtractingVisitor(
private val trace: BindingTrace,
private val moduleDescriptor: ModuleDescriptor
) : KtVisitor<Computation, Unit>() {
fun extractOrGetCached(element: KtElement): Computation {
trace[BindingContext.EXPRESSION_EFFECTS, element]?.let { return it }
return element.accept(this, Unit).also { trace.record(BindingContext.EXPRESSION_EFFECTS, element, it) }
}
override fun visitKtElement(element: KtElement, data: Unit): Computation {
val resolvedCall = element.getResolvedCall(trace.bindingContext) ?: return UNKNOWN_COMPUTATION
if (resolvedCall.isCallWithUnsupportedReceiver()) return UNKNOWN_COMPUTATION
val arguments = resolvedCall.getCallArgumentsAsComputations() ?: return UNKNOWN_COMPUTATION
val descriptor = resolvedCall.resultingDescriptor
return when {
descriptor.isEqualsDescriptor() -> CallComputation(DefaultBuiltIns.Instance.booleanType, EqualsFunctor(false).invokeWithArguments(arguments))
descriptor is ValueDescriptor -> ESDataFlowValue(descriptor, (element as KtExpression).createDataFlowValue() ?: return UNKNOWN_COMPUTATION)
descriptor is FunctionDescriptor -> CallComputation(descriptor.returnType, descriptor.getFunctor()?.invokeWithArguments(arguments) ?: emptyList())
else -> UNKNOWN_COMPUTATION
}
}
// We need lambdas only as arguments currently, and it is processed in 'visitElement' while parsing arguments of call.
// For all other cases we don't need lambdas.
override fun visitLambdaExpression(expression: KtLambdaExpression, data: Unit?): Computation = UNKNOWN_COMPUTATION
override fun visitParenthesizedExpression(expression: KtParenthesizedExpression, data: Unit): Computation =
KtPsiUtil.deparenthesize(expression)?.accept(this, data) ?: UNKNOWN_COMPUTATION
override fun visitConstantExpression(expression: KtConstantExpression, data: Unit): Computation {
val bindingContext = trace.bindingContext
val type: KotlinType = bindingContext.getType(expression) ?: return UNKNOWN_COMPUTATION
val compileTimeConstant: CompileTimeConstant<*>
= bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression) ?: return UNKNOWN_COMPUTATION
val value: Any? = compileTimeConstant.getValue(type)
return when (value) {
is Boolean -> value.lift()
null -> ESConstant.NULL
else -> UNKNOWN_COMPUTATION
}
}
override fun visitIsExpression(expression: KtIsExpression, data: Unit): Computation {
val rightType: KotlinType = trace[BindingContext.TYPE, expression.typeReference] ?: return UNKNOWN_COMPUTATION
val arg = extractOrGetCached(expression.leftHandSide)
return CallComputation(DefaultBuiltIns.Instance.booleanType, IsFunctor(rightType, expression.isNegated).invokeWithArguments(listOf(arg)))
}
override fun visitBinaryExpression(expression: KtBinaryExpression, data: Unit): Computation {
val left = extractOrGetCached(expression.left ?: return UNKNOWN_COMPUTATION)
val right = extractOrGetCached(expression.right ?: return UNKNOWN_COMPUTATION)
val args = listOf(left, right)
return when (expression.operationToken) {
KtTokens.EXCLEQ -> CallComputation(DefaultBuiltIns.Instance.booleanType, EqualsFunctor(true).invokeWithArguments(args))
KtTokens.EQEQ -> CallComputation(DefaultBuiltIns.Instance.booleanType, EqualsFunctor(false).invokeWithArguments(args))
KtTokens.ANDAND -> CallComputation(DefaultBuiltIns.Instance.booleanType, AndFunctor().invokeWithArguments(args))
KtTokens.OROR -> CallComputation(DefaultBuiltIns.Instance.booleanType, OrFunctor().invokeWithArguments(args))
else -> UNKNOWN_COMPUTATION
}
}
override fun visitUnaryExpression(expression: KtUnaryExpression, data: Unit): Computation {
val arg = extractOrGetCached(expression.baseExpression ?: return UNKNOWN_COMPUTATION)
return when (expression.operationToken) {
KtTokens.EXCL -> CallComputation(DefaultBuiltIns.Instance.booleanType, NotFunctor().invokeWithArguments(arg))
else -> UNKNOWN_COMPUTATION
}
}
private fun ReceiverValue.toComputation(): Computation = when (this) {
is ExpressionReceiver -> extractOrGetCached(expression)
else -> UNKNOWN_COMPUTATION
}
private fun KtExpression.createDataFlowValue(): DataFlowValue? {
return DataFlowValueFactory.createDataFlowValue(
expression = this,
type = trace.getType(this) ?: return null,
bindingContext = trace.bindingContext,
containingDeclarationOrModule = moduleDescriptor
)
}
private fun FunctionDescriptor.getFunctor(): Functor? {
trace[BindingContext.FUNCTOR, this]?.let { return it }
val functor = ContractInterpretationDispatcher().resolveFunctor(this) ?: return null
trace.record(BindingContext.FUNCTOR, this, functor)
return functor
}
private fun ResolvedCall<*>.isCallWithUnsupportedReceiver(): Boolean =
(extensionReceiver as? ExpressionReceiver)?.expression?.getResolvedCall(trace.bindingContext) == this ||
(dispatchReceiver as? ExpressionReceiver)?.expression?.getResolvedCall(trace.bindingContext) == this ||
(explicitReceiverKind == ExplicitReceiverKind.BOTH_RECEIVERS)
private fun ResolvedCall<*>.getCallArgumentsAsComputations(): List<Computation>? {
val arguments = mutableListOf<Computation>()
arguments.addIfNotNull(extensionReceiver?.toComputation())
arguments.addIfNotNull(dispatchReceiver?.toComputation())
valueArgumentsByIndex?.mapTo(arguments) {
val valueArgument = (it as? ExpressionValueArgument)?.valueArgument ?: return null
when (valueArgument) {
is KtLambdaArgument -> ESLambda(valueArgument.getLambdaExpression())
else -> extractOrGetCached(valueArgument.getArgumentExpression() ?: return null)
}
} ?: return null
return arguments
}
}
@@ -0,0 +1,42 @@
/*
* 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.resolve
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
/**
* Trace which allows to keep some slices hidden from the parent trace.
*
* Compared with TemporaryBindingTrace + TraceEntryFilter, FilteringTrace doesn't
* make extra moves for slices that should be definitely recorded into parent
* (like storing them in the local map, later re-committing into parent's, etc.)
*/
abstract class AbstractFilteringTrace(
private val parentTrace: BindingTrace,
name: String
) : DelegatingBindingTrace(parentTrace.bindingContext, name, true, BindingTraceFilter.ACCEPT_ALL, false) {
abstract protected fun <K, V> shouldBeHiddenFromParent(slice: WritableSlice<K, V>, key: K): Boolean
override fun <K, V> record(slice: WritableSlice<K, V>, key: K, value: V) {
if (shouldBeHiddenFromParent(slice, key)) super.record(slice, key, value) else parentTrace.record(slice, key, value)
}
override fun report(diagnostic: Diagnostic) {
parentTrace.report(diagnostic)
}
}
@@ -0,0 +1,35 @@
/*
* 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.resolve.calls.smartcasts
class ConditionalDataFlowInfo(val thenInfo: DataFlowInfo, val elseInfo: DataFlowInfo = thenInfo) {
fun and(other: ConditionalDataFlowInfo): ConditionalDataFlowInfo = when {
this == EMPTY -> other
other == EMPTY -> this
else -> ConditionalDataFlowInfo(this.thenInfo.and(other.thenInfo), this.elseInfo.and(other.elseInfo))
}
fun or(other: ConditionalDataFlowInfo): ConditionalDataFlowInfo = when {
this == EMPTY -> other
other == EMPTY -> this
else -> ConditionalDataFlowInfo(this.thenInfo.or(other.thenInfo), this.elseInfo.or(other.elseInfo))
}
companion object {
val EMPTY = ConditionalDataFlowInfo(DataFlowInfo.EMPTY)
}
}
@@ -20,6 +20,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.context.GlobalContext;
import org.jetbrains.kotlin.contracts.EffectSystem;
import org.jetbrains.kotlin.contracts.parsing.ContractParsingServices;
import org.jetbrains.kotlin.incremental.components.LookupTracker;
import org.jetbrains.kotlin.platform.PlatformToKotlinClassMap;
import org.jetbrains.kotlin.resolve.*;
@@ -64,6 +66,8 @@ public class ExpressionTypingComponents {
/*package*/ WrappedTypeFactory wrappedTypeFactory;
/*package*/ CollectionLiteralResolver collectionLiteralResolver;
/*package*/ DeprecationResolver deprecationResolver;
/*package*/ EffectSystem effectSystem;
/*package*/ ContractParsingServices contractParsingServices;
@Inject
public void setGlobalContext(@NotNull GlobalContext globalContext) {
@@ -181,7 +185,7 @@ public class ExpressionTypingComponents {
}
@Inject
public void setLocalVariableResolver(@NotNull LocalVariableResolver localVariableResolver) {
public void setLocalVariableResolver(@NotNull LocalVariableResolver localVariableResolver) {
this.localVariableResolver = localVariableResolver;
}
@@ -219,4 +223,14 @@ public class ExpressionTypingComponents {
public void setDeprecationResolver(DeprecationResolver deprecationResolver) {
this.deprecationResolver = deprecationResolver;
}
@Inject
public void setEffectSystem(@NotNull EffectSystem effectSystem) {
this.effectSystem = effectSystem;
}
@Inject
public void setContractParsingServices(@NotNull ContractParsingServices contractParsingServices) {
this.contractParsingServices = contractParsingServices;
}
}
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.resolve.scopes.TraceBasedLocalRedeclarationChecker;
import org.jetbrains.kotlin.types.ErrorUtils;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
import java.util.Iterator;
import java.util.List;
@@ -260,7 +261,20 @@ public class ExpressionTypingServices {
// Jump point data flow info
DataFlowInfo beforeJumpInfo = newContext.dataFlowInfo;
boolean jumpOutPossible = false;
boolean isFirstStatement = true;
for (Iterator<? extends KtElement> iterator = block.iterator(); iterator.hasNext(); ) {
// Use filtering trace to keep effect system cache only for one statement
AbstractFilteringTrace traceForSingleStatement = new AbstractFilteringTrace(context.trace, "trace for single statement") {
@Override
protected <K, V> boolean shouldBeHiddenFromParent(@NotNull WritableSlice<K, V> slice, K key) {
return slice == BindingContext.EXPRESSION_EFFECTS;
}
};
newContext = newContext.replaceBindingTrace(traceForSingleStatement);
KtElement statement = iterator.next();
if (!(statement instanceof KtExpression)) {
continue;
@@ -295,6 +309,12 @@ public class ExpressionTypingServices {
// We take current data flow info if jump there is not possible
}
blockLevelVisitor = new ExpressionTypingVisitorDispatcher.ForBlock(expressionTypingComponents, annotationChecker, scope);
expressionTypingComponents.contractParsingServices.checkContractAndRecordIfPresent(statementExpression, context.trace, scope, isFirstStatement);
if (isFirstStatement) {
isFirstStatement = false;
}
}
return result.replaceJumpOutPossible(jumpOutPossible).replaceJumpFlowInfo(beforeJumpInfo);
}
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.checkers.RttiExpressionInformation
import org.jetbrains.kotlin.resolve.calls.checkers.RttiOperation
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT
import org.jetbrains.kotlin.resolve.calls.smartcasts.ConditionalDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
@@ -379,8 +380,6 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
return newDataFlowInfo
}
private class ConditionalDataFlowInfo(val thenInfo: DataFlowInfo, val elseInfo: DataFlowInfo = thenInfo)
private fun checkTypeForExpressionCondition(
context: ExpressionTypingContext,
expression: KtExpression,