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:
@@ -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)
|
||||
}
|
||||
}
|
||||
+35
@@ -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)
|
||||
}
|
||||
}
|
||||
+15
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -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);
|
||||
}
|
||||
|
||||
+1
-2
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user