diff --git a/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt b/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt index d95dddffe9f..7902c7558c2 100644 --- a/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt +++ b/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt @@ -21,7 +21,6 @@ import org.jetbrains.kotlin.cfg.Label import org.jetbrains.kotlin.cfg.containingDeclarationForPseudocode import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.* -import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind.* import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ThrowExceptionInstruction @@ -31,26 +30,18 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.parents -import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.BindingContext.* -import org.jetbrains.kotlin.resolve.DelegatingBindingTrace -import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunctionDescriptor -import org.jetbrains.kotlin.resolve.calls.ValueArgumentsToParametersMapper -import org.jetbrains.kotlin.resolve.calls.util.getCall -import org.jetbrains.kotlin.resolve.calls.util.isSafeCall -import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument import org.jetbrains.kotlin.resolve.calls.util.getExplicitReceiverValue -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo -import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind -import org.jetbrains.kotlin.resolve.calls.tasks.OldResolutionCandidate -import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy +import org.jetbrains.kotlin.resolve.calls.util.isSafeCall import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils -import java.util.* +import java.util.ArrayList +import java.util.LinkedHashSet fun getReceiverTypePredicate(resolvedCall: ResolvedCall<*>, receiverValue: ReceiverValue): TypePredicate? { val callableDescriptor = resolvedCall.resultingDescriptor ?: return null @@ -87,60 +78,6 @@ fun getExpectedTypePredicate( if (receiverValue != null) typePredicates.add(getReceiverTypePredicate(resolvedCall, receiverValue)) } - fun getTypePredicateForUnresolvedCallArgument(to: KtElement, inputValueIndex: Int): TypePredicate? { - if (inputValueIndex < 0) return null - val call = to.getCall(bindingContext) ?: return null - val callee = call.calleeExpression ?: return null - - val candidates = callee.getReferenceTargets(bindingContext) - .filterIsInstance() - .sortedBy { DescriptorRenderer.FQ_NAMES_IN_TYPES.render(it) } - if (candidates.isEmpty()) return null - - val explicitReceiver = call.explicitReceiver - val argValueOffset = if (explicitReceiver != null) 1 else 0 - - val predicates = ArrayList() - - for (candidate in candidates) { - val resolutionCandidate = OldResolutionCandidate.create( - call, - candidate, - null, - ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, - null - ) - val candidateCall = ResolvedCallImpl.create( - resolutionCandidate, - DelegatingBindingTrace(bindingContext, "Compute type predicates for unresolved call arguments"), - TracingStrategy.EMPTY, - DataFlowInfoForArgumentsImpl(DataFlowInfo.EMPTY, call) - ) - val status = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters( - call, TracingStrategy.EMPTY, candidateCall, LanguageVersionSettingsImpl.DEFAULT - ) - if (!status.isSuccess) continue - - val candidateArgumentMap = candidateCall.valueArguments - val callArguments = call.valueArguments - val i = inputValueIndex - argValueOffset - if (i < 0 || i >= callArguments.size) continue - - val mapping = candidateCall.getArgumentMapping(callArguments[i]) as? ArgumentMatch ?: continue - - val candidateParameter = mapping.valueParameter - val resolvedArgument = candidateArgumentMap[candidateParameter] - val expectedType = if (resolvedArgument is VarargValueArgument) - candidateParameter.varargElementType - else - candidateParameter.type - - predicates.add(if (expectedType != null) AllSubtypes(expectedType) else AllTypes) - } - - return or(predicates) - } - fun addTypePredicates(value: PseudoValue) { pseudocode.getUsages(value).forEach { when (it) { @@ -148,7 +85,7 @@ fun getExpectedTypePredicate( val returnElement = it.element val functionDescriptor = when (returnElement) { is KtReturnExpression -> returnElement.getTargetFunctionDescriptor(bindingContext) - else -> bindingContext[DECLARATION_TO_DESCRIPTOR, pseudocode.correspondingElement] + else -> bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, pseudocode.correspondingElement] } addSubtypesOf((functionDescriptor as? CallableDescriptor)?.returnType) } @@ -198,30 +135,25 @@ fun getExpectedTypePredicate( } is MagicInstruction -> when (it.kind) { - AND, OR -> + MagicKind.AND, MagicKind.OR -> addSubtypesOf(builtIns.booleanType) - LOOP_RANGE_ITERATION -> - addByExplicitReceiver(bindingContext[LOOP_RANGE_ITERATOR_RESOLVED_CALL, value.element as? KtExpression]) + MagicKind.LOOP_RANGE_ITERATION -> + addByExplicitReceiver(bindingContext[BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL, value.element as? KtExpression]) - VALUE_CONSUMER -> { + MagicKind.VALUE_CONSUMER -> { val element = it.element when (element) { element.getStrictParentOfType()?.condition -> addSubtypesOf(builtIns.booleanType) is KtProperty -> { - val propertyDescriptor = bindingContext[DECLARATION_TO_DESCRIPTOR, element] as? PropertyDescriptor + val propertyDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, element] as? PropertyDescriptor propertyDescriptor?.accessors?.map { - addByExplicitReceiver(bindingContext[DELEGATED_PROPERTY_RESOLVED_CALL, it]) + addByExplicitReceiver(bindingContext[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, it]) } } - is KtDelegatedSuperTypeEntry -> addSubtypesOf(bindingContext[TYPE, element.typeReference]) + is KtDelegatedSuperTypeEntry -> addSubtypesOf(bindingContext[BindingContext.TYPE, element.typeReference]) } } - - UNRESOLVED_CALL -> { - val typePredicate = getTypePredicateForUnresolvedCallArgument(it.element, it.inputValues.indexOf(value)) - typePredicates.add(typePredicate) - } else -> {} } } diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/cfg/AbstractPseudoValueTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/cfg/AbstractPseudoValueTest.kt index 900ba87690c..000d5cdfe0c 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/cfg/AbstractPseudoValueTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/cfg/AbstractPseudoValueTest.kt @@ -20,7 +20,6 @@ import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl import org.jetbrains.kotlin.cfg.pseudocode.TypePredicate -import org.jetbrains.kotlin.cfg.pseudocode.getExpectedTypePredicate import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValue import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtTreeVisitorVoid diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/cfg/pseudocodeUtils.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/cfg/pseudocodeUtils.kt new file mode 100644 index 00000000000..75b325413bf --- /dev/null +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/cfg/pseudocodeUtils.kt @@ -0,0 +1,260 @@ +/* + * 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.cfg + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.cfg.pseudocode.* +import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction +import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.* +import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind.* +import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction +import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruction +import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ThrowExceptionInstruction +import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingContext.* +import org.jetbrains.kotlin.resolve.DelegatingBindingTrace +import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets +import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunctionDescriptor +import org.jetbrains.kotlin.resolve.calls.ValueArgumentsToParametersMapper +import org.jetbrains.kotlin.resolve.calls.util.getCall +import org.jetbrains.kotlin.resolve.calls.util.isSafeCall +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.util.getExplicitReceiverValue +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind +import org.jetbrains.kotlin.resolve.calls.tasks.OldResolutionCandidate +import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy +import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils +import java.util.* + +fun getReceiverTypePredicate(resolvedCall: ResolvedCall<*>, receiverValue: ReceiverValue): TypePredicate? { + val callableDescriptor = resolvedCall.resultingDescriptor ?: return null + + when (receiverValue) { + resolvedCall.extensionReceiver -> { + val receiverParameter = callableDescriptor.extensionReceiverParameter + if (receiverParameter != null) return receiverParameter.type.getSubtypesPredicate() + } + resolvedCall.dispatchReceiver -> { + val rootCallableDescriptors = callableDescriptor.findTopMostOverriddenDescriptors() + return or(rootCallableDescriptors.mapNotNull { + it.dispatchReceiverParameter?.type?.let { TypeUtils.makeNullableIfNeeded(it, resolvedCall.call.isSafeCall()) } + ?.getSubtypesPredicate() + }) + } + } + + return null +} + +fun getExpectedTypePredicate( + value: PseudoValue, + bindingContext: BindingContext, + builtIns: KotlinBuiltIns +): TypePredicate { + val pseudocode = value.createdAt?.owner ?: return AllTypes + val typePredicates = LinkedHashSet() + + fun addSubtypesOf(jetType: KotlinType?) = typePredicates.add(jetType?.getSubtypesPredicate()) + + fun addByExplicitReceiver(resolvedCall: ResolvedCall<*>?) { + val receiverValue = (resolvedCall ?: return).getExplicitReceiverValue() + if (receiverValue != null) typePredicates.add(getReceiverTypePredicate(resolvedCall, receiverValue)) + } + + fun getTypePredicateForUnresolvedCallArgument(to: KtElement, inputValueIndex: Int): TypePredicate? { + if (inputValueIndex < 0) return null + val call = to.getCall(bindingContext) ?: return null + val callee = call.calleeExpression ?: return null + + val candidates = callee.getReferenceTargets(bindingContext) + .filterIsInstance() + .sortedBy { DescriptorRenderer.FQ_NAMES_IN_TYPES.render(it) } + if (candidates.isEmpty()) return null + + val explicitReceiver = call.explicitReceiver + val argValueOffset = if (explicitReceiver != null) 1 else 0 + + val predicates = ArrayList() + + for (candidate in candidates) { + val resolutionCandidate = OldResolutionCandidate.create( + call, + candidate, + null, + ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, + null + ) + val candidateCall = ResolvedCallImpl.create( + resolutionCandidate, + DelegatingBindingTrace(bindingContext, "Compute type predicates for unresolved call arguments"), + TracingStrategy.EMPTY, + DataFlowInfoForArgumentsImpl(DataFlowInfo.EMPTY, call) + ) + val status = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters( + call, TracingStrategy.EMPTY, candidateCall, LanguageVersionSettingsImpl.DEFAULT + ) + if (!status.isSuccess) continue + + val candidateArgumentMap = candidateCall.valueArguments + val callArguments = call.valueArguments + val i = inputValueIndex - argValueOffset + if (i < 0 || i >= callArguments.size) continue + + val mapping = candidateCall.getArgumentMapping(callArguments[i]) as? ArgumentMatch ?: continue + + val candidateParameter = mapping.valueParameter + val resolvedArgument = candidateArgumentMap[candidateParameter] + val expectedType = if (resolvedArgument is VarargValueArgument) + candidateParameter.varargElementType + else + candidateParameter.type + + predicates.add(if (expectedType != null) AllSubtypes(expectedType) else AllTypes) + } + + return or(predicates) + } + + fun addTypePredicates(value: PseudoValue) { + pseudocode.getUsages(value).forEach { + when (it) { + is ReturnValueInstruction -> { + val returnElement = it.element + val functionDescriptor = when (returnElement) { + is KtReturnExpression -> returnElement.getTargetFunctionDescriptor(bindingContext) + else -> bindingContext[DECLARATION_TO_DESCRIPTOR, pseudocode.correspondingElement] + } + addSubtypesOf((functionDescriptor as? CallableDescriptor)?.returnType) + } + + is ConditionalJumpInstruction -> + addSubtypesOf(builtIns.booleanType) + + is ThrowExceptionInstruction -> + addSubtypesOf(builtIns.throwable.defaultType) + + is MergeInstruction -> + addTypePredicates(it.outputValue) + + is AccessValueInstruction -> { + val accessTarget = it.target + val receiverValue = it.receiverValues[value] + if (receiverValue != null) { + typePredicates.add(getReceiverTypePredicate((accessTarget as AccessTarget.Call).resolvedCall, receiverValue)) + } else { + val expectedType = when (accessTarget) { + is AccessTarget.Call -> + (accessTarget.resolvedCall.resultingDescriptor as? VariableDescriptor)?.type + is AccessTarget.Declaration -> + accessTarget.descriptor.type + else -> + null + } + addSubtypesOf(expectedType) + } + } + + is CallInstruction -> { + val receiverValue = it.receiverValues[value] + if (receiverValue != null) { + typePredicates.add(getReceiverTypePredicate(it.resolvedCall, receiverValue)) + } else { + it.arguments[value]?.let { parameter -> + val expectedType = when (it.resolvedCall.valueArguments[parameter]) { + is VarargValueArgument -> + parameter.varargElementType + else -> + parameter.type + } + addSubtypesOf(expectedType) + } + } + } + + is MagicInstruction -> when (it.kind) { + AND, OR -> + addSubtypesOf(builtIns.booleanType) + + LOOP_RANGE_ITERATION -> + addByExplicitReceiver(bindingContext[LOOP_RANGE_ITERATOR_RESOLVED_CALL, value.element as? KtExpression]) + + VALUE_CONSUMER -> { + val element = it.element + when (element) { + element.getStrictParentOfType()?.condition -> addSubtypesOf(builtIns.booleanType) + is KtProperty -> { + val propertyDescriptor = bindingContext[DECLARATION_TO_DESCRIPTOR, element] as? PropertyDescriptor + propertyDescriptor?.accessors?.map { + addByExplicitReceiver(bindingContext[DELEGATED_PROPERTY_RESOLVED_CALL, it]) + } + } + is KtDelegatedSuperTypeEntry -> addSubtypesOf(bindingContext[TYPE, element.typeReference]) + } + } + + UNRESOLVED_CALL -> { + val typePredicate = getTypePredicateForUnresolvedCallArgument(it.element, it.inputValues.indexOf(value)) + typePredicates.add(typePredicate) + } + else -> {} + } + } + } + } + + addTypePredicates(value) + return and(typePredicates.filterNotNull()) +} + +val Instruction.sideEffectFree: Boolean + get() = owner.isSideEffectFree(this) + +fun Instruction.calcSideEffectFree(): Boolean { + if (this !is InstructionWithValue) return false + if (!inputValues.all { it.createdAt?.sideEffectFree == true }) return false + + return when (this) { + is ReadValueInstruction -> target.let { + when (it) { + is AccessTarget.Call -> when (it.resolvedCall.resultingDescriptor) { + is LocalVariableDescriptor, is ValueParameterDescriptor, is ReceiverParameterDescriptor -> true + else -> false + } + + else -> when (element) { + is KtNamedFunction -> element.name == null + is KtConstantExpression, is KtLambdaExpression, is KtStringTemplateExpression -> true + else -> false + } + } + } + + is MagicInstruction -> kind.sideEffectFree + + else -> false + } +}