[FIR] Implement new bound smartcast algorithm

#KT-36055 Fixed
This commit is contained in:
Dmitriy Novozhilov
2020-02-10 17:14:58 +03:00
parent 40f907ec2f
commit 6735cc8937
103 changed files with 2197 additions and 309 deletions
@@ -448,7 +448,7 @@ private fun BodyResolveComponents.typeFromSymbol(symbol: AbstractFirBasedSymbol<
fun BodyResolveComponents.transformQualifiedAccessUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): FirQualifiedAccessExpression { fun BodyResolveComponents.transformQualifiedAccessUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): FirQualifiedAccessExpression {
val typesFromSmartCast = dataFlowAnalyzer.getTypeUsingSmartcastInfo(qualifiedAccessExpression) ?: return qualifiedAccessExpression val typesFromSmartCast = dataFlowAnalyzer.getTypeUsingSmartcastInfo(qualifiedAccessExpression) ?: return qualifiedAccessExpression
val allTypes = typesFromSmartCast.toMutableList().also { val allTypes = typesFromSmartCast.also {
it += qualifiedAccessExpression.resultType.coneTypeUnsafe<ConeKotlinType>() it += qualifiedAccessExpression.resultType.coneTypeUnsafe<ConeKotlinType>()
} }
val intersectedType = ConeTypeIntersector.intersectTypes(inferenceComponents.ctx as ConeInferenceContext, allTypes) val intersectedType = ConeTypeIntersector.intersectTypes(inferenceComponents.ctx as ConeInferenceContext, allTypes)
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.dfa package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference
import org.jetbrains.kotlin.fir.contracts.description.ConeConditionalEffectDeclaration import org.jetbrains.kotlin.fir.contracts.description.ConeConditionalEffectDeclaration
import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference
@@ -14,8 +15,8 @@ import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStackImpl import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStackImpl
import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.calls.ClassDispatchReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.resolve.dfa.contracts.buildContractFir import org.jetbrains.kotlin.fir.resolve.dfa.contracts.buildContractFir
import org.jetbrains.kotlin.fir.resolve.dfa.contracts.createArgumentsMapping import org.jetbrains.kotlin.fir.resolve.dfa.contracts.createArgumentsMapping
@@ -29,7 +30,6 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.transformSingle import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@UseExperimental(DfaInternals::class) @UseExperimental(DfaInternals::class)
abstract class FirDataFlowAnalyzer<FLOW : Flow>( abstract class FirDataFlowAnalyzer<FLOW : Flow>(
@@ -61,7 +61,11 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
} }
override fun updateAllReceivers(flow: PersistentFlow) { override fun updateAllReceivers(flow: PersistentFlow) {
receiverStack.mapNotNull { variableStorage[it.boundSymbol, it.receiverExpression] }.forEach { processUpdatedReceiverVariable(flow, it) } receiverStack.forEach {
variableStorage.getRealVariable(it.boundSymbol, it.receiverExpression, flow)?.let { variable ->
processUpdatedReceiverVariable(flow, variable)
}
}
} }
} }
} }
@@ -71,7 +75,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private val context: ConeInferenceContext = components.inferenceComponents.ctx private val context: ConeInferenceContext = components.inferenceComponents.ctx
private val graphBuilder = ControlFlowGraphBuilder() private val graphBuilder = ControlFlowGraphBuilder()
protected val variableStorage = VariableStorage() protected val variableStorage: VariableStorage = VariableStorage(components.session)
private val flowOnNodes = mutableMapOf<CFGNode<*>, FLOW>() private val flowOnNodes = mutableMapOf<CFGNode<*>, FLOW>()
private val variablesForWhenConditions = mutableMapOf<WhenBranchConditionExitNode, DataFlowVariable>() private val variablesForWhenConditions = mutableMapOf<WhenBranchConditionExitNode, DataFlowVariable>()
@@ -83,14 +87,21 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
// ----------------------------------- Requests ----------------------------------- // ----------------------------------- Requests -----------------------------------
fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection<ConeKotlinType>? { fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): MutableList<ConeKotlinType>? {
/* /*
* DataFlowAnalyzer holds variables only for declarations that have some smartcast (or can have) * DataFlowAnalyzer holds variables only for declarations that have some smartcast (or can have)
* If there is no useful information there is no data flow variable also * If there is no useful information there is no data flow variable also
*/ */
val symbol: AbstractFirBasedSymbol<*> = qualifiedAccessExpression.symbol ?: return null val symbol: AbstractFirBasedSymbol<*> = qualifiedAccessExpression.symbol ?: return null
val variable = variableStorage[symbol, qualifiedAccessExpression] ?: return null val flow = graphBuilder.lastNode.flow
return graphBuilder.lastNode.flow.getTypeStatement(variable)?.exactType var variable = variableStorage.getRealVariableWithoutUnwrappingAlias(symbol, qualifiedAccessExpression, flow) ?: return null
val result = mutableListOf<ConeKotlinType>()
flow.directAliasMap[variable]?.let {
result += it.originalType
variable = it
}
flow.getTypeStatement(variable)?.exactType?.let { result += it }
return result.takeIf { it.isNotEmpty() }
} }
fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): List<FirStatement> { fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): List<FirStatement> {
@@ -164,7 +175,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).mergeIncomingFlow() val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).mergeIncomingFlow()
if (typeOperatorCall.operation !in FirOperation.TYPES) return if (typeOperatorCall.operation !in FirOperation.TYPES) return
val type = typeOperatorCall.conversionTypeRef.coneTypeUnsafe<ConeKotlinType>() val type = typeOperatorCall.conversionTypeRef.coneTypeUnsafe<ConeKotlinType>()
val operandVariable = variableStorage.getOrCreateVariable(typeOperatorCall.argument) val operandVariable = variableStorage.getOrCreateVariable(node.previousFlow, typeOperatorCall.argument)
val flow = node.flow val flow = node.flow
when (val operation = typeOperatorCall.operation) { when (val operation = typeOperatorCall.operation) {
@@ -251,7 +262,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val isEq = operation.isEq() val isEq = operation.isEq()
val expressionVariable = variableStorage.createSyntheticVariable(node.fir) val expressionVariable = variableStorage.createSyntheticVariable(node.fir)
val flow = node.flow val flow = node.flow
val operandVariable = variableStorage.getOrCreateVariable(operand) val operandVariable = variableStorage.getOrCreateVariable(node.previousFlow, operand)
// expression == const -> expression != null // expression == const -> expression != null
flow.addImplication((expressionVariable eq isEq) implies (operandVariable notEq null)) flow.addImplication((expressionVariable eq isEq) implies (operandVariable notEq null))
if (operandVariable is RealVariable) { if (operandVariable is RealVariable) {
@@ -296,7 +307,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun processEqNull(node: OperatorCallNode, operand: FirExpression, operation: FirOperation) { private fun processEqNull(node: OperatorCallNode, operand: FirExpression, operation: FirOperation) {
val flow = node.flow val flow = node.flow
val expressionVariable = variableStorage.createSyntheticVariable(node.fir) val expressionVariable = variableStorage.createSyntheticVariable(node.fir)
val operandVariable = variableStorage.getOrCreateVariable(operand) val operandVariable = variableStorage.getOrCreateVariable(node.previousFlow, operand)
val isEq = operation.isEq() val isEq = operation.isEq()
@@ -336,7 +347,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
// Add `Any` to the set of possible types; the intersection type `T? & Any` will be reduced to `T` after smartcast. // Add `Any` to the set of possible types; the intersection type `T? & Any` will be reduced to `T` after smartcast.
val node = graphBuilder.exitCheckNotNullCall(checkNotNullCall).mergeIncomingFlow() val node = graphBuilder.exitCheckNotNullCall(checkNotNullCall).mergeIncomingFlow()
val argument = checkNotNullCall.argument val argument = checkNotNullCall.argument
val operandVariable = variableStorage.getOrCreateRealVariable(argument.symbol, argument) ?: return val operandVariable = variableStorage.getOrCreateRealVariable(node.previousFlow, argument.symbol, argument) ?: return
node.flow.addTypeStatement(operandVariable typeEq any) node.flow.addTypeStatement(operandVariable typeEq any)
logicSystem.approveStatementsInsideFlow( logicSystem.approveStatementsInsideFlow(
node.flow, node.flow,
@@ -370,10 +381,11 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val (conditionExitNode, branchEnterNode) = graphBuilder.exitWhenBranchCondition(whenBranch) val (conditionExitNode, branchEnterNode) = graphBuilder.exitWhenBranchCondition(whenBranch)
conditionExitNode.mergeIncomingFlow() conditionExitNode.mergeIncomingFlow()
val conditionVariable = variableStorage.getOrCreateVariable(whenBranch.condition) val conditionExitFlow = conditionExitNode.flow
val conditionVariable = variableStorage.getOrCreateVariable(conditionExitFlow, whenBranch.condition)
variablesForWhenConditions[conditionExitNode] = conditionVariable variablesForWhenConditions[conditionExitNode] = conditionVariable
branchEnterNode.flow = logicSystem.approveStatementsInsideFlow( branchEnterNode.flow = logicSystem.approveStatementsInsideFlow(
conditionExitNode.flow, conditionExitFlow,
conditionVariable eq true, conditionVariable eq true,
shouldForkFlow = true, shouldForkFlow = true,
shouldRemoveSynthetics = false shouldRemoveSynthetics = false
@@ -387,18 +399,19 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
fun exitWhenExpression(whenExpression: FirWhenExpression) { fun exitWhenExpression(whenExpression: FirWhenExpression) {
val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression) val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression)
if (syntheticElseNode != null) { if (syntheticElseNode != null) {
syntheticElseNode.mergeIncomingFlow()
val previousConditionExitNode = syntheticElseNode.firstPreviousNode as? WhenBranchConditionExitNode val previousConditionExitNode = syntheticElseNode.firstPreviousNode as? WhenBranchConditionExitNode
// previous node for syntheticElseNode can be not WhenBranchConditionExitNode in case of `when` without any branches // previous node for syntheticElseNode can be not WhenBranchConditionExitNode in case of `when` without any branches
// in that case there will be when enter or subject access node // in that case there will be when enter or subject access node
if (previousConditionExitNode != null) { if (previousConditionExitNode != null) {
val conditionVariable = variablesForWhenConditions.remove(previousConditionExitNode)!! val conditionVariable = variablesForWhenConditions.remove(previousConditionExitNode)!!
syntheticElseNode.flow = logicSystem.approveStatementsInsideFlow( syntheticElseNode.flow = logicSystem.approveStatementsInsideFlow(
syntheticElseNode.flow, previousConditionExitNode.flow,
conditionVariable eq false, conditionVariable eq false,
shouldForkFlow = true, shouldForkFlow = true,
shouldRemoveSynthetics = true shouldRemoveSynthetics = true
) )
} else {
syntheticElseNode.mergeIncomingFlow()
} }
} }
whenExitNode.mergeIncomingFlow(updateReceivers = true) whenExitNode.mergeIncomingFlow(updateReceivers = true)
@@ -409,7 +422,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun exitCommonLoop(exitNode: LoopExitNode) { private fun exitCommonLoop(exitNode: LoopExitNode) {
val singlePreviousNode = exitNode.previousNodes.singleOrNull { !it.isDead } val singlePreviousNode = exitNode.previousNodes.singleOrNull { !it.isDead }
if (singlePreviousNode is LoopConditionExitNode) { if (singlePreviousNode is LoopConditionExitNode) {
val variable = variableStorage.getOrCreateVariable(singlePreviousNode.fir) val variable = variableStorage.getOrCreateVariable(exitNode.previousFlow, singlePreviousNode.fir)
exitNode.flow = logicSystem.approveStatementsInsideFlow( exitNode.flow = logicSystem.approveStatementsInsideFlow(
exitNode.flow, exitNode.flow,
variable eq false, variable eq false,
@@ -428,15 +441,15 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
fun exitWhileLoopCondition(loop: FirLoop) { fun exitWhileLoopCondition(loop: FirLoop) {
val (loopConditionExitNode, loopBlockEnterNode) = graphBuilder.exitWhileLoopCondition(loop) val (loopConditionExitNode, loopBlockEnterNode) = graphBuilder.exitWhileLoopCondition(loop)
loopConditionExitNode.mergeIncomingFlow() loopConditionExitNode.mergeIncomingFlow()
loopBlockEnterNode.mergeIncomingFlow() val conditionExitFlow = loopConditionExitNode.flow
variableStorage[loop.condition]?.let { conditionVariable -> loopBlockEnterNode.flow = variableStorage.getVariable(loop.condition, conditionExitFlow)?.let { conditionVariable ->
loopBlockEnterNode.flow = logicSystem.approveStatementsInsideFlow( logicSystem.approveStatementsInsideFlow(
loopBlockEnterNode.flow, conditionExitFlow,
conditionVariable eq true, conditionVariable eq true,
shouldForkFlow = false, shouldForkFlow = true,
shouldRemoveSynthetics = false shouldRemoveSynthetics = false
) )
} } ?: logicSystem.forkFlow(conditionExitFlow)
} }
fun exitWhileLoop(loop: FirLoop) { fun exitWhileLoop(loop: FirLoop) {
@@ -526,7 +539,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
?.withNullability(ConeNullability.NOT_NULL) ?.withNullability(ConeNullability.NOT_NULL)
?: return@let ?: return@let
val variable = variableStorage.getOrCreateVariable(receiver) val variable = variableStorage.getOrCreateVariable(flow, receiver)
if (variable is RealVariable) { if (variable is RealVariable) {
if (shouldFork) { if (shouldFork) {
flow = logicSystem.forkFlow(flow) flow = logicSystem.forkFlow(flow)
@@ -563,11 +576,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun exitSafeCall(qualifiedAccess: FirQualifiedAccess) { private fun exitSafeCall(qualifiedAccess: FirQualifiedAccess) {
if (!qualifiedAccess.safe) return if (!qualifiedAccess.safe) return
val node = graphBuilder.exitSafeCall(qualifiedAccess).mergeIncomingFlow() val node = graphBuilder.exitSafeCall(qualifiedAccess).mergeIncomingFlow()
val variable = variableStorage.getOrCreateVariable(qualifiedAccess) val previousFlow = node.previousFlow
val variable = variableStorage.getOrCreateVariable(previousFlow, qualifiedAccess)
val receiverVariable = when (variable) { val receiverVariable = when (variable) {
// There is some bug with invokes. See KT-36014 // There is some bug with invokes. See KT-36014
is RealVariable -> variable.explicitReceiverVariable ?: return is RealVariable -> variable.explicitReceiverVariable ?: return
is SyntheticVariable -> variableStorage.getOrCreateVariable(qualifiedAccess.explicitReceiver!!) is SyntheticVariable -> variableStorage.getOrCreateVariable(previousFlow, qualifiedAccess.explicitReceiver!!)
} }
logicSystem.addImplication(node.flow, (variable notEq null) implies (receiverVariable notEq null)) logicSystem.addImplication(node.flow, (variable notEq null) implies (receiverVariable notEq null))
if (receiverVariable.isReal()) { if (receiverVariable.isReal()) {
@@ -582,12 +596,13 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val argumentsMapping = createArgumentsMapping(functionCall) ?: return val argumentsMapping = createArgumentsMapping(functionCall) ?: return
contractDescriptionVisitingMode = true contractDescriptionVisitingMode = true
graphBuilder.enterContract(functionCall).mergeIncomingFlow() graphBuilder.enterContract(functionCall).mergeIncomingFlow()
val functionCallVariable = variableStorage.getOrCreateVariable(functionCall) val lastFlow = graphBuilder.lastNode.flow
val functionCallVariable = variableStorage.getOrCreateVariable(lastFlow, functionCall)
for (conditionalEffect in conditionalEffects) { for (conditionalEffect in conditionalEffects) {
val fir = conditionalEffect.buildContractFir(argumentsMapping) ?: continue val fir = conditionalEffect.buildContractFir(argumentsMapping) ?: continue
val effect = conditionalEffect.effect as? ConeReturnsEffectDeclaration ?: continue val effect = conditionalEffect.effect as? ConeReturnsEffectDeclaration ?: continue
fir.transformSingle(components.transformer, ResolutionMode.ContextDependent) fir.transformSingle(components.transformer, ResolutionMode.ContextDependent)
val argumentVariable = variableStorage.getOrCreateVariable(fir) val argumentVariable = variableStorage.getOrCreateVariable(lastFlow, fir)
val lastNode = graphBuilder.lastNode val lastNode = graphBuilder.lastNode
when (val value = effect.value) { when (val value = effect.value) {
ConeConstantReference.WILDCARD -> { ConeConstantReference.WILDCARD -> {
@@ -644,21 +659,21 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
exitVariableInitialization(node, assignment.rValue, property, assignment) exitVariableInitialization(node, assignment.rValue, property, assignment)
} }
private fun exitVariableInitialization(node: CFGNode<*>, initializer: FirExpression, variable: FirProperty, assignment: FirVariableAssignment?) { private fun exitVariableInitialization(node: CFGNode<*>, initializer: FirExpression, property: FirProperty, assignment: FirVariableAssignment?) {
var propertyVariable = variableStorage.getOrCreateRealVariable(variable.symbol, assignment ?: variable) val flow = node.flow
val isVariableDeclaration = assignment == null val propertyVariable = variableStorage.getOrCreateRealVariableWithoutUnwrappingAlias(flow, property.symbol, assignment ?: property)
if (!isVariableDeclaration) { val isAssignment = assignment != null
// Don't remove statements for variable under alias if (isAssignment) {
if (propertyVariable.identifier.symbol == variable.symbol) { logicSystem.removeLocalVariableAlias(flow, propertyVariable)
node.flow.removeAllAboutVariable(propertyVariable) flow.removeAllAboutVariable(propertyVariable)
} else {
variableStorage.unboundPossiblyAliasedVariable(variable.symbol)
// We should create new dataFlowVariable for local variable without alias
propertyVariable = variableStorage.getOrCreateRealVariable(variable.symbol, assignment ?: variable)
}
} }
variableStorage[initializer]?.safeAs<SyntheticVariable>()?.let { initializerVariable -> variableStorage.getOrCreateRealVariable(flow, initializer.symbol, initializer)?.let { initializerVariable ->
logicSystem.addLocalVariableAlias(flow, propertyVariable, initializerVariable)
// node.flow.addImplication((propertyVariable notEq null) implies (initializerVariable notEq null))
}
variableStorage.getSyntheticVariable(initializer)?.let { initializerVariable ->
/* /*
* That part is needed for cases like that: * That part is needed for cases like that:
* *
@@ -668,19 +683,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
* x.length * x.length
* } * }
*/ */
logicSystem.replaceVariableFromConditionInStatements(node.flow, initializerVariable, propertyVariable) logicSystem.replaceVariableFromConditionInStatements(flow, initializerVariable, propertyVariable)
return
} }
variableStorage.getOrCreateRealVariable(initializer.symbol, initializer)?.let { initializerVariable -> if (isAssignment) {
if (initializerVariable.isStable) { if (initializer is FirConstExpression<*> && initializer.kind == FirConstKind.Null) return
variableStorage.attachSymbolToVariable(variable.symbol, initializerVariable) flow.addTypeStatement(propertyVariable typeEq initializer.typeRef.coneTypeUnsafe<ConeKotlinType>())
}
node.flow.addImplication((propertyVariable notEq null) implies (initializerVariable notEq null))
}
if (!isVariableDeclaration) {
node.flow.addTypeStatement(propertyVariable typeEq initializer.typeRef.coneTypeUnsafe<ConeKotlinType>())
} }
} }
@@ -722,7 +730,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun exitLeftArgumentOfBinaryBooleanOperator(leftNode: CFGNode<*>, rightNode: CFGNode<*>, isAnd: Boolean) { private fun exitLeftArgumentOfBinaryBooleanOperator(leftNode: CFGNode<*>, rightNode: CFGNode<*>, isAnd: Boolean) {
val parentFlow = leftNode.firstPreviousNode.flow val parentFlow = leftNode.firstPreviousNode.flow
leftNode.flow = logicSystem.forkFlow(parentFlow) leftNode.flow = logicSystem.forkFlow(parentFlow)
val leftOperandVariable = variableStorage.getOrCreateVariable(leftNode.firstPreviousNode.fir) val leftOperandVariable = variableStorage.getOrCreateVariable(parentFlow, leftNode.firstPreviousNode.fir)
rightNode.flow = logicSystem.approveStatementsInsideFlow( rightNode.flow = logicSystem.approveStatementsInsideFlow(
parentFlow, parentFlow,
leftOperandVariable eq isAnd, leftOperandVariable eq isAnd,
@@ -750,9 +758,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
* But since conditions with const are rare it can be delayed * But since conditions with const are rare it can be delayed
*/ */
val leftVariable = variableStorage.getOrCreateVariable(binaryLogicExpression.leftOperand) val leftVariable = variableStorage.getOrCreateVariable(flow, binaryLogicExpression.leftOperand)
val rightVariable = variableStorage.getOrCreateVariable(binaryLogicExpression.rightOperand) val rightVariable = variableStorage.getOrCreateVariable(flow, binaryLogicExpression.rightOperand)
val operatorVariable = variableStorage.getOrCreateVariable(binaryLogicExpression) val operatorVariable = variableStorage.getOrCreateVariable(flow, binaryLogicExpression)
val (conditionalFromLeft, conditionalFromRight, approvedFromRight) = logicSystem.collectInfoForBooleanOperator( val (conditionalFromLeft, conditionalFromRight, approvedFromRight) = logicSystem.collectInfoForBooleanOperator(
flowFromLeft, flowFromLeft,
@@ -792,8 +800,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) { private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) {
val booleanExpressionVariable = variableStorage.getOrCreateVariable(node.firstPreviousNode.fir) val previousFlow = node.previousFlow
val variable = variableStorage.getOrCreateVariable(functionCall) val booleanExpressionVariable = variableStorage.getOrCreateVariable(previousFlow, node.firstPreviousNode.fir)
val variable = variableStorage.getOrCreateVariable(previousFlow, functionCall)
logicSystem.replaceVariableFromConditionInStatements( logicSystem.replaceVariableFromConditionInStatements(
node.flow, node.flow,
booleanExpressionVariable, booleanExpressionVariable,
@@ -856,4 +865,17 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
if (variable == null) return if (variable == null) return
logicSystem.removeAllAboutVariable(this, variable) logicSystem.removeAllAboutVariable(this, variable)
} }
private val CFGNode<*>.previousFlow : FLOW
get() = firstPreviousNode.flow
} }
@DfaInternals
fun FirElement.extractReturnType(): ConeKotlinType = when (this) {
is FirVariable<*> -> returnTypeRef.coneTypeUnsafe()
is FirSimpleFunction -> receiverTypeRef?.coneTypeUnsafe()
is FirAnonymousFunction -> receiverTypeRef?.coneTypeUnsafe()
is FirRegularClass -> defaultType()
is FirAnonymousObject -> typeRef.coneTypeUnsafe()
else -> null
} ?: throw IllegalArgumentException("Unsupported fir: $this")
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.dfa package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.commonSuperTypeOrNull import org.jetbrains.kotlin.fir.types.commonSuperTypeOrNull
@@ -14,6 +15,13 @@ abstract class Flow {
abstract fun getImplications(variable: DataFlowVariable): Collection<Implication> abstract fun getImplications(variable: DataFlowVariable): Collection<Implication>
abstract fun getVariablesInTypeStatements(): Collection<RealVariable> abstract fun getVariablesInTypeStatements(): Collection<RealVariable>
abstract fun removeOperations(variable: DataFlowVariable): Collection<Implication> abstract fun removeOperations(variable: DataFlowVariable): Collection<Implication>
abstract val directAliasMap: Map<RealVariable, RealVariable>
abstract val backwardsAliasMap: Map<RealVariable, List<RealVariable>>
}
fun Flow.unwrapVariable(variable: RealVariable): RealVariable {
return directAliasMap[variable] ?: variable
} }
abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceContext) { abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceContext) {
@@ -45,6 +53,9 @@ abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceCont
shouldRemoveSynthetics: Boolean, shouldRemoveSynthetics: Boolean,
): FLOW ): FLOW
abstract fun addLocalVariableAlias(flow: FLOW, alias: RealVariable, underlyingVariable: RealVariable)
abstract fun removeLocalVariableAlias(flow: FLOW, alias: RealVariable)
protected abstract fun getImplicationsWithVariable(flow: FLOW, variable: DataFlowVariable): Collection<Implication> protected abstract fun getImplicationsWithVariable(flow: FLOW, variable: DataFlowVariable): Collection<Implication>
// ------------------------------- Callbacks for updating implicit receiver stack ------------------------------- // ------------------------------- Callbacks for updating implicit receiver stack -------------------------------
@@ -7,8 +7,11 @@ package org.jetbrains.kotlin.fir.resolve.dfa
import com.google.common.collect.ArrayListMultimap import com.google.common.collect.ArrayListMultimap
import kotlinx.collections.immutable.* import kotlinx.collections.immutable.*
import org.jetbrains.kotlin.fir.declarations.FirVariable
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
import java.util.* import java.util.*
import kotlin.NoSuchElementException import kotlin.NoSuchElementException
@@ -42,12 +45,26 @@ class PersistentFlow : Flow {
var logicStatements: PersistentImplications var logicStatements: PersistentImplications
val level: Int val level: Int
var approvedTypeStatementsDiff: PersistentApprovedTypeStatements = persistentHashMapOf() var approvedTypeStatementsDiff: PersistentApprovedTypeStatements = persistentHashMapOf()
var updatedAliasDiff: PersistentSet<RealVariable> = persistentSetOf()
/*
* val x = a
* val y = a
*
* directAliasMap: { x -> a, y -> a}
* backwardsAliasMap: { a -> [x, y] }
*/
override var directAliasMap: PersistentMap<RealVariable, RealVariable>
override var backwardsAliasMap: PersistentMap<RealVariable, PersistentList<RealVariable>>
constructor(previousFlow: PersistentFlow) { constructor(previousFlow: PersistentFlow) {
this.previousFlow = previousFlow this.previousFlow = previousFlow
approvedTypeStatements = previousFlow.approvedTypeStatements approvedTypeStatements = previousFlow.approvedTypeStatements
logicStatements = previousFlow.logicStatements logicStatements = previousFlow.logicStatements
level = previousFlow.level + 1 level = previousFlow.level + 1
directAliasMap = previousFlow.directAliasMap
backwardsAliasMap = previousFlow.backwardsAliasMap
} }
constructor() { constructor() {
@@ -55,6 +72,9 @@ class PersistentFlow : Flow {
approvedTypeStatements = persistentHashMapOf() approvedTypeStatements = persistentHashMapOf()
logicStatements = persistentHashMapOf() logicStatements = persistentHashMapOf()
level = 1 level = 1
directAliasMap = persistentMapOf()
backwardsAliasMap = persistentMapOf()
} }
override fun getTypeStatement(variable: RealVariable): TypeStatement? { override fun getTypeStatement(variable: RealVariable): TypeStatement? {
@@ -90,8 +110,22 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
override fun joinFlow(flows: Collection<PersistentFlow>): PersistentFlow { override fun joinFlow(flows: Collection<PersistentFlow>): PersistentFlow {
if (flows.isEmpty()) return createEmptyFlow() if (flows.isEmpty()) return createEmptyFlow()
flows.singleOrNull()?.let { return it } flows.singleOrNull()?.let { return it }
val flowsSize = flows.size
val aliasedVariablesThatDontChangeAlias = mutableMapOf<RealVariable, RealVariable>()
flows.flatMapTo(mutableSetOf()) { it.directAliasMap.keys }.forEach { aliasedVariable ->
val originals = flows.map { it.directAliasMap[aliasedVariable] ?: return@forEach }
if (originals.size != flowsSize) return@forEach
val firstOriginal = originals.first()
if (originals.all { it == firstOriginal }) {
aliasedVariablesThatDontChangeAlias[aliasedVariable] = firstOriginal
}
}
val commonFlow = flows.reduce(::lowestCommonFlow) val commonFlow = flows.reduce(::lowestCommonFlow)
val commonVariables = flows.map { it.diffVariablesIterable(commonFlow).toList() } val commonVariables = flows.map {
it.diffVariablesIterable(commonFlow, aliasedVariablesThatDontChangeAlias.keys).toList()
}
.intersectSets() .intersectSets()
.takeIf { it.isNotEmpty() } .takeIf { it.isNotEmpty() }
?: return commonFlow ?: return commonFlow
@@ -105,19 +139,48 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
} }
} }
for ((alias, underlyingVariable) in aliasedVariablesThatDontChangeAlias) {
addLocalVariableAlias(commonFlow,alias, underlyingVariable)
}
updateAllReceivers(commonFlow) updateAllReceivers(commonFlow)
return commonFlow return commonFlow
} }
override fun addLocalVariableAlias(flow: PersistentFlow, alias: RealVariable, underlyingVariable: RealVariable) {
removeLocalVariableAlias(flow, alias)
flow.directAliasMap = flow.directAliasMap.put(alias, underlyingVariable)
flow.backwardsAliasMap = flow.backwardsAliasMap.put(
underlyingVariable,
{ persistentListOf(alias) },
{ variables -> variables + alias }
)
}
override fun removeLocalVariableAlias(flow: PersistentFlow, alias: RealVariable) {
flow.updatedAliasDiff += alias
val original = flow.directAliasMap[alias] ?: return
flow.directAliasMap = flow.directAliasMap.remove(alias)
val variables = flow.backwardsAliasMap.getValue(original)
flow.backwardsAliasMap = flow.backwardsAliasMap.put(original, variables - alias)
}
@UseExperimental(DfaInternals::class)
private fun PersistentFlow.getApprovedTypeStatementsDiff(variable: RealVariable, parentFlow: PersistentFlow): MutableTypeStatement { private fun PersistentFlow.getApprovedTypeStatementsDiff(variable: RealVariable, parentFlow: PersistentFlow): MutableTypeStatement {
var flow = this var flow = this
val result = MutableTypeStatement(variable) val result = MutableTypeStatement(variable)
while (flow != parentFlow) { val variableUnderAlias = directAliasMap[variable]
flow.approvedTypeStatementsDiff[variable]?.let { if (variableUnderAlias == null) {
result += it while (flow != parentFlow) {
flow.approvedTypeStatementsDiff[variable]?.let {
result += it
}
flow = flow.previousFlow!!
} }
flow = flow.previousFlow!! } else {
result.exactType += variableUnderAlias.originalType
flow.approvedTypeStatements[variableUnderAlias]?.let { result += it }
} }
return result return result
} }
@@ -126,10 +189,19 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
* This is an iterable over real variable that has known facts in flow range * This is an iterable over real variable that has known facts in flow range
* from [this] to [parentFlow] * from [this] to [parentFlow]
*/ */
private fun PersistentFlow.diffVariablesIterable(parentFlow: PersistentFlow): Iterable<RealVariable> = private fun PersistentFlow.diffVariablesIterable(
parentFlow: PersistentFlow,
aliasedVariablesThatDontChangeAlias: Set<RealVariable>
): Iterable<RealVariable> =
object : DiffIterable<RealVariable>(parentFlow, this) { object : DiffIterable<RealVariable>(parentFlow, this) {
override fun extractIterator(flow: PersistentFlow): Iterator<RealVariable> { override fun extractIterator(flow: PersistentFlow): Iterator<RealVariable> {
return flow.approvedTypeStatementsDiff.keys.iterator() val variablesWithNewInfo = flow.approvedTypeStatementsDiff.keys
val updatedVariables = ArrayList(variablesWithNewInfo)
updatedVariables += flow.updatedAliasDiff
variablesWithNewInfo.flatMapTo(updatedVariables) { variableWithNewInfo ->
flow.backwardsAliasMap[variableWithNewInfo]?.filter { it !in aliasedVariablesThatDontChangeAlias } ?: emptyList()
}
return updatedVariables.iterator()
} }
} }
@@ -5,23 +5,40 @@
package org.jetbrains.kotlin.fir.resolve.dfa package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirAnonymousObject
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.modality
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
import org.jetbrains.kotlin.fir.references.FirThisReference import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@UseExperimental(DfaInternals::class) @UseExperimental(DfaInternals::class)
class VariableStorage { class VariableStorage(val session: FirSession) {
private var counter = 1 private var counter = 1
private val realVariables: MutableMap<Identifier, RealVariable> = HashMap() private val realVariables: MutableMap<Identifier, RealVariable> = HashMap()
private val syntheticVariables: MutableMap<FirElement, SyntheticVariable> = HashMap() private val syntheticVariables: MutableMap<FirElement, SyntheticVariable> = HashMap()
private val localVariableAliases: MutableMap<AbstractFirBasedSymbol<*>, Identifier> = HashMap()
fun getOrCreateRealVariable(symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable { fun getOrCreateRealVariableWithoutUnwrappingAlias(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable {
val realFir = fir.unwrapElement() val realFir = fir.unwrapElement()
val identifier = getIdentifierBySymbol(symbol, realFir) val identifier = getIdentifierBySymbol(flow, symbol, realFir)
return realVariables.getOrPut(identifier) { createRealVariableInternal(identifier, realFir) } return realVariables.getOrPut(identifier) { createRealVariableInternal(flow, identifier, realFir) }
}
private fun getOrCreateRealVariable(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable {
val variable = getOrCreateRealVariableWithoutUnwrappingAlias(flow, symbol, fir)
return flow.directAliasMap[variable] ?: variable
} }
private fun FirElement.unwrapElement(): FirElement = when (this) { private fun FirElement.unwrapElement(): FirElement = when (this) {
@@ -31,23 +48,22 @@ class VariableStorage {
} }
private fun getIdentifierBySymbol( private fun getIdentifierBySymbol(
flow: Flow,
symbol: AbstractFirBasedSymbol<*>, symbol: AbstractFirBasedSymbol<*>,
fir: FirElement, fir: FirElement,
): Identifier { ): Identifier {
return localVariableAliases[symbol] ?: run { val expression = fir as? FirQualifiedAccess
val expression = fir as? FirQualifiedAccess return Identifier(
Identifier( symbol,
symbol, expression?.dispatchReceiver?.takeIf { it != FirNoReceiverExpression }?.let { getOrCreateVariable(flow, it) },
expression?.dispatchReceiver?.takeIf { it != FirNoReceiverExpression }?.let(this::getOrCreateVariable), expression?.extensionReceiver?.takeIf { it != FirNoReceiverExpression }?.let { getOrCreateVariable(flow, it) }
expression?.extensionReceiver?.takeIf { it != FirNoReceiverExpression }?.let(this::getOrCreateVariable) )
)
}
} }
/** /**
* [originalFir] used for extracting expression under <when_subject> and extracting receiver * [originalFir] used for extracting expression under <when_subject> and extracting receiver
*/ */
private fun createRealVariableInternal(identifier: Identifier, originalFir: FirElement): RealVariable { private fun createRealVariableInternal(flow: Flow, identifier: Identifier, originalFir: FirElement): RealVariable {
val receiver: FirExpression? val receiver: FirExpression?
val isThisReference: Boolean val isThisReference: Boolean
val expression: FirQualifiedAccess? = when (originalFir) { val expression: FirQualifiedAccess? = when (originalFir) {
@@ -66,57 +82,62 @@ class VariableStorage {
isThisReference = false isThisReference = false
} }
val receiverVariable = receiver?.let { getOrCreateVariable(it) } val receiverVariable = receiver?.let { getOrCreateVariable(flow, it) }
return RealVariable(identifier, isThisReference, receiverVariable, counter++) val originalType: ConeKotlinType = when (originalFir) {
is FirExpression -> originalFir.typeRef.coneTypeUnsafe()
is FirProperty -> originalFir.returnTypeRef.coneTypeUnsafe()
is FirVariableAssignment -> identifier.symbol.fir.extractReturnType()
else -> throw IllegalStateException("Should not be here: $originalFir")
}
return RealVariable(identifier, isThisReference, receiverVariable, originalType, counter++)
} }
@JvmName("getOrCreateRealVariableOrNull") @JvmName("getOrCreateRealVariableOrNull")
fun getOrCreateRealVariable(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement): RealVariable? = fun getOrCreateRealVariable(flow: Flow, symbol: AbstractFirBasedSymbol<*>?, fir: FirElement): RealVariable? =
symbol?.let { getOrCreateRealVariable(it, fir) } symbol.takeIf { it.isStable(fir) }?.let { getOrCreateRealVariable(flow, it, fir) }
fun createSyntheticVariable(fir: FirElement): SyntheticVariable = fun createSyntheticVariable(fir: FirElement): SyntheticVariable =
SyntheticVariable(fir, counter++).also { syntheticVariables[fir] = it } SyntheticVariable(fir, counter++).also { syntheticVariables[fir] = it }
fun getOrCreateVariable(fir: FirElement): DataFlowVariable { fun getOrCreateVariable(flow: Flow, fir: FirElement): DataFlowVariable {
val realFir = fir.unwrapElement() val realFir = fir.unwrapElement()
return when (val symbol = realFir.symbol) { val symbol = realFir.symbol
null -> syntheticVariables[realFir] ?: createSyntheticVariable(realFir) return if (symbol.isStable(realFir)) {
else -> getOrCreateRealVariable(symbol, realFir) getOrCreateRealVariable(flow, symbol!!, realFir)
} else {
syntheticVariables[realFir] ?: createSyntheticVariable(realFir)
} }
} }
/** fun getRealVariableWithoutUnwrappingAlias(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable? {
* Also removes existing real variable for [varSymbol] if it exists val realFir = fir.unwrapElement()
*/ return symbol.takeIf { it.isStable(realFir) }?.let {
fun attachSymbolToVariable(varSymbol: AbstractFirBasedSymbol<*>, targetVariable: RealVariable) { realVariables[getIdentifierBySymbol(flow, it, realFir.unwrapElement())]
localVariableAliases[varSymbol] = targetVariable.identifier }
realVariables.remove(Identifier(varSymbol, null, null))
} }
operator fun get(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement): RealVariable? { fun getRealVariable(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable? {
return symbol?.let { realVariables[getIdentifierBySymbol(it, fir.unwrapElement())] } return getRealVariableWithoutUnwrappingAlias(symbol, fir, flow)?.let { flow.unwrapVariable(it) }
} }
operator fun get(fir: FirElement): DataFlowVariable? { fun getSyntheticVariable(fir: FirElement): SyntheticVariable? {
return syntheticVariables[fir.unwrapElement()]
}
fun getVariable(fir: FirElement, flow: Flow): DataFlowVariable? {
val realFir = fir.unwrapElement() val realFir = fir.unwrapElement()
val symbol = realFir.symbol val symbol = realFir.symbol
return if (symbol != null) { return if (symbol.isStable(fir)) {
get(symbol, realFir) getRealVariable(symbol, realFir, flow)
} else { } else {
syntheticVariables[realFir] getSyntheticVariable(fir)
} }
} }
fun removeRealVariable(symbol: AbstractFirBasedSymbol<*>) { fun removeRealVariable(symbol: AbstractFirBasedSymbol<*>) {
// TODO: this shit fails
// assert(!localVariableAliases.containsValue(symbol))
realVariables.remove(Identifier(symbol, null, null)) realVariables.remove(Identifier(symbol, null, null))
} }
fun unboundPossiblyAliasedVariable(symbol: AbstractFirBasedSymbol<*>) {
localVariableAliases.remove(symbol)
}
fun removeSyntheticVariable(variable: DataFlowVariable) { fun removeSyntheticVariable(variable: DataFlowVariable) {
if (variable !is SyntheticVariable) return if (variable !is SyntheticVariable) return
syntheticVariables.remove(variable.fir) syntheticVariables.remove(variable.fir)
@@ -126,6 +147,44 @@ class VariableStorage {
counter = 0 counter = 0
realVariables.clear() realVariables.clear()
syntheticVariables.clear() syntheticVariables.clear()
localVariableAliases.clear() }
@UseExperimental(ExperimentalContracts::class)
fun AbstractFirBasedSymbol<*>?.isStable(originalFir: FirElement): Boolean {
contract {
returns(true) implies(this@isStable != null)
}
when (this) {
is FirFunctionSymbol<*>,
is FirClassSymbol<*>,
is FirBackingFieldSymbol -> return true
null -> return false
}
if (originalFir is FirThisReceiverExpression) return true
if (this !is FirVariableSymbol<*>) return false
val property = this.fir as? FirProperty ?: return true
return when {
property.isLocal -> true
property.isVar -> false
property.receiverTypeRef != null -> false
property.getter.let { it != null && it !is FirDefaultPropertyAccessor } -> false
property.modality != Modality.FINAL -> {
val dispatchReceiver = (originalFir as? FirQualifiedAccess)?.dispatchReceiver ?: return false
val propertyClassName = (this as FirPropertySymbol).callableId.classId
val receiverType = dispatchReceiver.typeRef.coneTypeSafe<ConeClassLikeType>()?.fullyExpandedType(session) ?: return false
val receiverSymbol = receiverType.fullyExpandedType(session).lookupTag.toSymbol(session) ?: return false
val receiverClassName = receiverSymbol.classId
if (propertyClassName != receiverClassName) {
when (val receiverFir = receiverSymbol.fir) {
is FirAnonymousObject -> true
is FirRegularClass -> receiverFir.modality == Modality.FINAL
else -> throw IllegalStateException("Should not be here: $receiverFir")
}
} else false
}
else -> true
}
} }
} }
@@ -93,7 +93,7 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
} }
} }
val CFGNode<*>.firstPreviousNode: CFGNode<*> get() = previousNodes.first() val CFGNode<*>.firstPreviousNode: CFGNode<*> get() = previousNodes[0]
interface EnterNodeMarker interface EnterNodeMarker
interface ExitNodeMarker interface ExitNodeMarker
@@ -41,6 +41,7 @@ class RealVariable(
val identifier: Identifier, val identifier: Identifier,
val isThisReference: Boolean, val isThisReference: Boolean,
val explicitReceiverVariable: DataFlowVariable?, val explicitReceiverVariable: DataFlowVariable?,
val originalType: ConeKotlinType,
variableIndexForDebug: Int variableIndexForDebug: Int
) : DataFlowVariable(variableIndexForDebug) { ) : DataFlowVariable(variableIndexForDebug) {
override val isStable: Boolean by lazy { override val isStable: Boolean by lazy {
@@ -51,6 +52,8 @@ class RealVariable(
property.isLocal -> true property.isLocal -> true
property.isVar -> false property.isVar -> false
property.modality != Modality.FINAL -> false property.modality != Modality.FINAL -> false
property.receiverTypeRef != null -> false
property.getter != null -> false
// TODO: getters, delegates // TODO: getters, delegates
else -> true else -> true
} }
@@ -5,18 +5,26 @@
package org.jetbrains.kotlin.fir.resolve.dfa package org.jetbrains.kotlin.fir.resolve.dfa
import kotlinx.collections.immutable.PersistentMap
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSymbolOwner import org.jetbrains.kotlin.fir.FirSymbolOwner
import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference
import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.FirThisReference import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
import org.jetbrains.kotlin.fir.resolve.directExpansionType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirAccessorSymbol import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.types.ConeFlexibleType
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneTypeSafe import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -67,6 +75,20 @@ internal inline fun <K, V> MutableMap<K, V>.put(key: K, value: V, remappingFunct
} }
} }
@UseExperimental(ExperimentalContracts::class)
internal inline fun <K, V> PersistentMap<K, V>.put(key: K, valueProducer: () -> V, remappingFunction: (existing: V) -> V): PersistentMap<K, V> {
contract {
callsInPlace(remappingFunction, InvocationKind.AT_MOST_ONCE)
callsInPlace(valueProducer, InvocationKind.AT_MOST_ONCE)
}
val existing = this[key]
return if (existing == null) {
put(key, valueProducer())
} else {
put(key, remappingFunction(existing))
}
}
@DfaInternals @DfaInternals
internal fun FirOperation.invert(): FirOperation = when (this) { internal fun FirOperation.invert(): FirOperation = when (this) {
FirOperation.EQ -> FirOperation.NOT_EQ FirOperation.EQ -> FirOperation.NOT_EQ
@@ -119,3 +141,8 @@ internal val FirResolvable.symbol: AbstractFirBasedSymbol<*>?
is FirNamedReferenceWithCandidate -> reference.candidateSymbol is FirNamedReferenceWithCandidate -> reference.candidateSymbol
else -> null else -> null
} }
//val ConeKotlinType.isNothingOrNullableNothing: Boolean = when (this) {
// is ConeFlexibleType -> lowerBound.isNothingOrNullableNothing
// else -> false
//}
@@ -1,14 +0,0 @@
interface A {
val s: String
}
fun test(list: List<A>) {
var goodA: A? = null
for (a in list) {
if (goodA == null) {
goodA = a
continue
}
goodA.<!INAPPLICABLE_CANDIDATE!>s<!>
}
}
@@ -1,23 +0,0 @@
FILE: noBoundSmartcastAfterContinue.kt
public abstract interface A : R|kotlin/Any| {
public abstract val s: R|kotlin/String|
public get(): R|kotlin/String|
}
public final fun test(list: R|kotlin/collections/List<A>|): R|kotlin/Unit| {
lvar goodA: R|A?| = Null(null)
lval <range>: R|kotlin/collections/List<A>| = R|<local>/list|
lval <iterator>: R|kotlin/collections/Iterator<A>| = R|<local>/<range>|.R|FakeOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<A>|>|()
while(R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()) {
lval a: R|A| = R|<local>/<iterator>|.R|FakeOverride<kotlin/collections/Iterator.next: R|A|>|()
when () {
==(R|<local>/goodA|, Null(null)) -> {
R|<local>/goodA| = R|<local>/a|
continue@@@[R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()]
}
}
R|<local>/goodA|.<Inapplicable(WRONG_RECEIVER): [/A.s]>#
}
}
@@ -0,0 +1,809 @@
digraph noBoundSmartcastAfterContinue_kt {
graph [splines=ortho nodesep=3]
node [shape=box penwidth=2]
edge [penwidth=2]
subgraph cluster_0 {
color=red
0 [label="Enter function <init>" style="filled" fillcolor=red];
1 [label="Exit function <init>" style="filled" fillcolor=red];
}
0 -> {1};
subgraph cluster_1 {
color=red
2 [label="Enter function getter" style="filled" fillcolor=red];
3 [label="Exit function getter" style="filled" fillcolor=red];
}
2 -> {3};
subgraph cluster_2 {
color=red
4 [label="Enter property" style="filled" fillcolor=red];
5 [label="Const: String()"];
6 [label="Exit property" style="filled" fillcolor=red];
}
4 -> {5};
5 -> {6};
subgraph cluster_3 {
color=red
7 [label="Enter function test_0" style="filled" fillcolor=red];
8 [label="Const: Null(null)"];
9 [label="Variable declaration: lvar goodA: R|A?|"];
10 [label="Access variable R|<local>/list|"];
11 [label="Variable declaration: lval <range>: R|kotlin/collections/List<A>|"];
12 [label="Access variable R|<local>/<range>|"];
13 [label="Function call: R|<local>/<range>|.R|FakeOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<A>|>|()"];
14 [label="Variable declaration: lval <iterator>: R|kotlin/collections/Iterator<A>|"];
subgraph cluster_4 {
color=blue
15 [label="Enter while loop"];
subgraph cluster_5 {
color=blue
16 [label="Enter loop condition"];
17 [label="Access variable R|<local>/<iterator>|"];
18 [label="Function call: R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()"];
19 [label="Exit loop condition"];
}
subgraph cluster_6 {
color=blue
20 [label="Enter loop block"];
subgraph cluster_7 {
color=blue
21 [label="Enter block"];
22 [label="Access variable R|<local>/<iterator>|"];
23 [label="Function call: R|<local>/<iterator>|.R|FakeOverride<kotlin/collections/Iterator.next: R|A|>|()"];
24 [label="Variable declaration: lval a: R|A|"];
subgraph cluster_8 {
color=blue
25 [label="Enter when"];
subgraph cluster_9 {
color=blue
26 [label="Enter when branch condition "];
27 [label="Access variable R|<local>/goodA|"];
28 [label="Const: Null(null)"];
29 [label="Operator =="];
30 [label="Exit when branch condition"];
}
31 [label="Synthetic else branch"];
32 [label="Enter when branch result"];
subgraph cluster_10 {
color=blue
33 [label="Enter block"];
34 [label="Access variable R|<local>/a|"];
35 [label="Assignmenet: R|<local>/goodA|"];
36 [label="Jump: continue@@@[R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()] "];
37 [label="Stub" style="filled" fillcolor=gray];
38 [label="Exit block" style="filled" fillcolor=gray];
}
39 [label="Exit when branch result" style="filled" fillcolor=gray];
40 [label="Exit when"];
}
41 [label="Access variable R|<local>/goodA|"];
42 [label="Access variable R|/A.s|"];
43 [label="Exit block"];
}
44 [label="Exit loop block"];
}
45 [label="Exit whileloop"];
}
46 [label="Exit function test_0" style="filled" fillcolor=red];
}
7 -> {8};
8 -> {9};
9 -> {10};
10 -> {11};
11 -> {12};
12 -> {13};
13 -> {14};
14 -> {15};
15 -> {16};
16 -> {17};
17 -> {18};
18 -> {19};
19 -> {45 20};
20 -> {21};
21 -> {22};
22 -> {23};
23 -> {24};
24 -> {25};
25 -> {26};
26 -> {27};
27 -> {28};
28 -> {29};
29 -> {30};
30 -> {32 31};
31 -> {40};
32 -> {33};
33 -> {34};
34 -> {35};
35 -> {36};
36 -> {15};
36 -> {37} [style=dotted];
37 -> {38} [style=dotted];
38 -> {39} [style=dotted];
39 -> {40} [style=dotted];
40 -> {41};
41 -> {42};
42 -> {43};
43 -> {44};
44 -> {16};
45 -> {46};
subgraph cluster_11 {
color=red
47 [label="Enter function test_1" style="filled" fillcolor=red];
48 [label="Variable declaration: lval x: R|kotlin/Any|"];
subgraph cluster_12 {
color=blue
49 [label="Enter when"];
subgraph cluster_13 {
color=blue
50 [label="Enter when branch condition "];
51 [label="Access variable R|<local>/b|"];
52 [label="Exit when branch condition"];
}
subgraph cluster_14 {
color=blue
53 [label="Enter when branch condition else"];
54 [label="Exit when branch condition"];
}
55 [label="Enter when branch result"];
subgraph cluster_15 {
color=blue
56 [label="Enter block"];
57 [label="Access variable R|<local>/a|"];
58 [label="Assignmenet: R|<local>/x|"];
59 [label="Exit block"];
}
60 [label="Exit when branch result"];
61 [label="Enter when branch result"];
subgraph cluster_16 {
color=blue
62 [label="Enter block"];
63 [label="Function call: R|/A.A|()"];
64 [label="Assignmenet: R|<local>/x|"];
65 [label="Exit block"];
}
66 [label="Exit when branch result"];
67 [label="Exit when"];
}
68 [label="Access variable R|<local>/x|"];
69 [label="Access variable R|/A.s|"];
70 [label="Exit function test_1" style="filled" fillcolor=red];
}
47 -> {48};
48 -> {49};
49 -> {50};
50 -> {51};
51 -> {52};
52 -> {61 53};
53 -> {54};
54 -> {55};
55 -> {56};
56 -> {57};
57 -> {58};
58 -> {59};
59 -> {60};
60 -> {67};
61 -> {62};
62 -> {63};
63 -> {64};
64 -> {65};
65 -> {66};
66 -> {67};
67 -> {68};
68 -> {69};
69 -> {70};
subgraph cluster_17 {
color=red
71 [label="Enter function test_2" style="filled" fillcolor=red];
72 [label="Variable declaration: lval x: R|kotlin/Any|"];
subgraph cluster_18 {
color=blue
73 [label="Enter when"];
subgraph cluster_19 {
color=blue
74 [label="Enter when branch condition "];
75 [label="Access variable R|<local>/b|"];
76 [label="Exit when branch condition"];
}
subgraph cluster_20 {
color=blue
77 [label="Enter when branch condition else"];
78 [label="Exit when branch condition"];
}
79 [label="Enter when branch result"];
subgraph cluster_21 {
color=blue
80 [label="Enter block"];
81 [label="Access variable R|<local>/a|"];
82 [label="Assignmenet: R|<local>/x|"];
83 [label="Access variable R|<local>/a|"];
84 [label="Type operator: a as A"];
85 [label="Exit block"];
}
86 [label="Exit when branch result"];
87 [label="Enter when branch result"];
subgraph cluster_22 {
color=blue
88 [label="Enter block"];
89 [label="Function call: R|/A.A|()"];
90 [label="Assignmenet: R|<local>/x|"];
91 [label="Exit block"];
}
92 [label="Exit when branch result"];
93 [label="Exit when"];
}
94 [label="Access variable R|<local>/x|"];
95 [label="Access variable R|/A.s|"];
96 [label="Exit function test_2" style="filled" fillcolor=red];
}
71 -> {72};
72 -> {73};
73 -> {74};
74 -> {75};
75 -> {76};
76 -> {87 77};
77 -> {78};
78 -> {79};
79 -> {80};
80 -> {81};
81 -> {82};
82 -> {83};
83 -> {84};
84 -> {85};
85 -> {86};
86 -> {93};
87 -> {88};
88 -> {89};
89 -> {90};
90 -> {91};
91 -> {92};
92 -> {93};
93 -> {94};
94 -> {95};
95 -> {96};
subgraph cluster_23 {
color=red
97 [label="Enter function test_3" style="filled" fillcolor=red];
98 [label="Variable declaration: lval x: R|kotlin/Any|"];
subgraph cluster_24 {
color=blue
99 [label="Enter when"];
subgraph cluster_25 {
color=blue
100 [label="Enter when branch condition "];
101 [label="Access variable R|<local>/b|"];
102 [label="Exit when branch condition"];
}
subgraph cluster_26 {
color=blue
103 [label="Enter when branch condition else"];
104 [label="Exit when branch condition"];
}
105 [label="Enter when branch result"];
subgraph cluster_27 {
color=blue
106 [label="Enter block"];
107 [label="Access variable R|<local>/a|"];
108 [label="Type operator: a as A"];
109 [label="Access variable R|<local>/a|"];
110 [label="Assignmenet: R|<local>/x|"];
111 [label="Exit block"];
}
112 [label="Exit when branch result"];
113 [label="Enter when branch result"];
subgraph cluster_28 {
color=blue
114 [label="Enter block"];
115 [label="Function call: R|/A.A|()"];
116 [label="Assignmenet: R|<local>/x|"];
117 [label="Exit block"];
}
118 [label="Exit when branch result"];
119 [label="Exit when"];
}
120 [label="Access variable R|<local>/x|"];
121 [label="Access variable R|/A.s|"];
122 [label="Exit function test_3" style="filled" fillcolor=red];
}
97 -> {98};
98 -> {99};
99 -> {100};
100 -> {101};
101 -> {102};
102 -> {113 103};
103 -> {104};
104 -> {105};
105 -> {106};
106 -> {107};
107 -> {108};
108 -> {109};
109 -> {110};
110 -> {111};
111 -> {112};
112 -> {119};
113 -> {114};
114 -> {115};
115 -> {116};
116 -> {117};
117 -> {118};
118 -> {119};
119 -> {120};
120 -> {121};
121 -> {122};
subgraph cluster_29 {
color=red
123 [label="Enter function test_4" style="filled" fillcolor=red];
124 [label="Variable declaration: lval x: R|kotlin/Any|"];
subgraph cluster_30 {
color=blue
125 [label="Enter when"];
subgraph cluster_31 {
color=blue
126 [label="Enter when branch condition "];
127 [label="Access variable R|<local>/b|"];
128 [label="Exit when branch condition"];
}
subgraph cluster_32 {
color=blue
129 [label="Enter when branch condition else"];
130 [label="Exit when branch condition"];
}
131 [label="Enter when branch result"];
subgraph cluster_33 {
color=blue
132 [label="Enter block"];
133 [label="Access variable R|<local>/a|"];
134 [label="Assignmenet: R|<local>/x|"];
135 [label="Exit block"];
}
136 [label="Exit when branch result"];
137 [label="Enter when branch result"];
subgraph cluster_34 {
color=blue
138 [label="Enter block"];
139 [label="Access variable R|<local>/a|"];
140 [label="Assignmenet: R|<local>/x|"];
141 [label="Exit block"];
}
142 [label="Exit when branch result"];
143 [label="Exit when"];
}
144 [label="Access variable R|<local>/x|"];
145 [label="Type operator: x as A"];
146 [label="Access variable R|<local>/x|"];
147 [label="Access variable R|/A.s|"];
148 [label="Access variable R|<local>/a|"];
149 [label="Access variable R|/A.s|"];
150 [label="Exit function test_4" style="filled" fillcolor=red];
}
123 -> {124};
124 -> {125};
125 -> {126};
126 -> {127};
127 -> {128};
128 -> {137 129};
129 -> {130};
130 -> {131};
131 -> {132};
132 -> {133};
133 -> {134};
134 -> {135};
135 -> {136};
136 -> {143};
137 -> {138};
138 -> {139};
139 -> {140};
140 -> {141};
141 -> {142};
142 -> {143};
143 -> {144};
144 -> {145};
145 -> {146};
146 -> {147};
147 -> {148};
148 -> {149};
149 -> {150};
subgraph cluster_35 {
color=red
151 [label="Enter function test_5" style="filled" fillcolor=red];
152 [label="Variable declaration: lval x: R|kotlin/Any|"];
subgraph cluster_36 {
color=blue
153 [label="Enter when"];
subgraph cluster_37 {
color=blue
154 [label="Enter when branch condition "];
155 [label="Access variable R|<local>/b|"];
156 [label="Exit when branch condition"];
}
subgraph cluster_38 {
color=blue
157 [label="Enter when branch condition else"];
158 [label="Exit when branch condition"];
}
159 [label="Enter when branch result"];
subgraph cluster_39 {
color=blue
160 [label="Enter block"];
161 [label="Access variable R|<local>/a|"];
162 [label="Assignmenet: R|<local>/x|"];
163 [label="Exit block"];
}
164 [label="Exit when branch result"];
165 [label="Enter when branch result"];
subgraph cluster_40 {
color=blue
166 [label="Enter block"];
167 [label="Access variable R|<local>/a|"];
168 [label="Assignmenet: R|<local>/x|"];
169 [label="Exit block"];
}
170 [label="Exit when branch result"];
171 [label="Exit when"];
}
172 [label="Access variable R|<local>/a|"];
173 [label="Type operator: a as A"];
174 [label="Access variable R|<local>/x|"];
175 [label="Access variable R|/A.s|"];
176 [label="Access variable R|<local>/a|"];
177 [label="Access variable R|/A.s|"];
178 [label="Exit function test_5" style="filled" fillcolor=red];
}
151 -> {152};
152 -> {153};
153 -> {154};
154 -> {155};
155 -> {156};
156 -> {165 157};
157 -> {158};
158 -> {159};
159 -> {160};
160 -> {161};
161 -> {162};
162 -> {163};
163 -> {164};
164 -> {171};
165 -> {166};
166 -> {167};
167 -> {168};
168 -> {169};
169 -> {170};
170 -> {171};
171 -> {172};
172 -> {173};
173 -> {174};
174 -> {175};
175 -> {176};
176 -> {177};
177 -> {178};
subgraph cluster_41 {
color=red
179 [label="Enter function test_6" style="filled" fillcolor=red];
180 [label="Variable declaration: lval x: R|kotlin/Any|"];
181 [label="Access variable R|<local>/a|"];
182 [label="Assignmenet: R|<local>/x|"];
183 [label="Access variable R|<local>/x|"];
184 [label="Access variable R|/A.s|"];
185 [label="Exit function test_6" style="filled" fillcolor=red];
}
179 -> {180};
180 -> {181};
181 -> {182};
182 -> {183};
183 -> {184};
184 -> {185};
subgraph cluster_42 {
color=red
186 [label="Enter function test_7" style="filled" fillcolor=red];
187 [label="Const: Null(null)"];
188 [label="Variable declaration: lval z: R|kotlin/String?|"];
189 [label="Access variable R|<local>/z|"];
190 [label="Variable declaration: lvar y: R|kotlin/String?|"];
191 [label="Access variable R|<local>/y|"];
192 [label="Variable declaration: lval x: R|kotlin/String?|"];
subgraph cluster_43 {
color=blue
193 [label="Enter when"];
subgraph cluster_44 {
color=blue
194 [label="Enter when branch condition "];
195 [label="Access variable R|<local>/x|"];
196 [label="Const: Null(null)"];
197 [label="Operator !="];
198 [label="Exit when branch condition"];
}
199 [label="Synthetic else branch"];
200 [label="Enter when branch result"];
subgraph cluster_45 {
color=blue
201 [label="Enter block"];
202 [label="Access variable R|<local>/x|"];
203 [label="Access variable R|kotlin/String.length|"];
204 [label="Access variable R|<local>/y|"];
205 [label="Access variable R|kotlin/String.length|"];
206 [label="Access variable R|<local>/z|"];
207 [label="Access variable R|kotlin/String.length|"];
208 [label="Exit block"];
}
209 [label="Exit when branch result"];
210 [label="Exit when"];
}
subgraph cluster_46 {
color=blue
211 [label="Enter when"];
subgraph cluster_47 {
color=blue
212 [label="Enter when branch condition "];
213 [label="Access variable R|<local>/y|"];
214 [label="Const: Null(null)"];
215 [label="Operator !="];
216 [label="Exit when branch condition"];
}
217 [label="Synthetic else branch"];
218 [label="Enter when branch result"];
subgraph cluster_48 {
color=blue
219 [label="Enter block"];
220 [label="Access variable R|<local>/x|"];
221 [label="Access variable R|kotlin/String.length|"];
222 [label="Access variable R|<local>/y|"];
223 [label="Access variable R|kotlin/String.length|"];
224 [label="Access variable R|<local>/z|"];
225 [label="Access variable R|kotlin/String.length|"];
226 [label="Exit block"];
}
227 [label="Exit when branch result"];
228 [label="Exit when"];
}
subgraph cluster_49 {
color=blue
229 [label="Enter when"];
subgraph cluster_50 {
color=blue
230 [label="Enter when branch condition "];
231 [label="Access variable R|<local>/z|"];
232 [label="Const: Null(null)"];
233 [label="Operator !="];
234 [label="Exit when branch condition"];
}
235 [label="Synthetic else branch"];
236 [label="Enter when branch result"];
subgraph cluster_51 {
color=blue
237 [label="Enter block"];
238 [label="Access variable R|<local>/x|"];
239 [label="Access variable R|kotlin/String.length|"];
240 [label="Access variable R|<local>/y|"];
241 [label="Access variable R|kotlin/String.length|"];
242 [label="Access variable R|<local>/z|"];
243 [label="Access variable R|kotlin/String.length|"];
244 [label="Exit block"];
}
245 [label="Exit when branch result"];
246 [label="Exit when"];
}
247 [label="Const: Null(null)"];
248 [label="Assignmenet: R|<local>/y|"];
subgraph cluster_52 {
color=blue
249 [label="Enter when"];
subgraph cluster_53 {
color=blue
250 [label="Enter when branch condition "];
251 [label="Access variable R|<local>/x|"];
252 [label="Const: Null(null)"];
253 [label="Operator !="];
254 [label="Exit when branch condition"];
}
255 [label="Synthetic else branch"];
256 [label="Enter when branch result"];
subgraph cluster_54 {
color=blue
257 [label="Enter block"];
258 [label="Access variable R|<local>/x|"];
259 [label="Access variable R|kotlin/String.length|"];
260 [label="Access variable R|<local>/y|"];
261 [label="Access variable <Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#"];
262 [label="Access variable R|<local>/z|"];
263 [label="Access variable R|kotlin/String.length|"];
264 [label="Exit block"];
}
265 [label="Exit when branch result"];
266 [label="Exit when"];
}
subgraph cluster_55 {
color=blue
267 [label="Enter when"];
subgraph cluster_56 {
color=blue
268 [label="Enter when branch condition "];
269 [label="Access variable R|<local>/y|"];
270 [label="Const: Null(null)"];
271 [label="Operator !="];
272 [label="Exit when branch condition"];
}
273 [label="Synthetic else branch"];
274 [label="Enter when branch result"];
subgraph cluster_57 {
color=blue
275 [label="Enter block"];
276 [label="Access variable R|<local>/x|"];
277 [label="Access variable <Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#"];
278 [label="Access variable R|<local>/y|"];
279 [label="Access variable R|kotlin/String.length|"];
280 [label="Access variable R|<local>/z|"];
281 [label="Access variable <Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#"];
282 [label="Exit block"];
}
283 [label="Exit when branch result"];
284 [label="Exit when"];
}
subgraph cluster_58 {
color=blue
285 [label="Enter when"];
subgraph cluster_59 {
color=blue
286 [label="Enter when branch condition "];
287 [label="Access variable R|<local>/z|"];
288 [label="Const: Null(null)"];
289 [label="Operator !="];
290 [label="Exit when branch condition"];
}
291 [label="Synthetic else branch"];
292 [label="Enter when branch result"];
subgraph cluster_60 {
color=blue
293 [label="Enter block"];
294 [label="Access variable R|<local>/x|"];
295 [label="Access variable R|kotlin/String.length|"];
296 [label="Access variable R|<local>/y|"];
297 [label="Access variable <Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#"];
298 [label="Access variable R|<local>/z|"];
299 [label="Access variable R|kotlin/String.length|"];
300 [label="Exit block"];
}
301 [label="Exit when branch result"];
302 [label="Exit when"];
}
303 [label="Exit function test_7" style="filled" fillcolor=red];
}
186 -> {187};
187 -> {188};
188 -> {189};
189 -> {190};
190 -> {191};
191 -> {192};
192 -> {193};
193 -> {194};
194 -> {195};
195 -> {196};
196 -> {197};
197 -> {198};
198 -> {200 199};
199 -> {210};
200 -> {201};
201 -> {202};
202 -> {203};
203 -> {204};
204 -> {205};
205 -> {206};
206 -> {207};
207 -> {208};
208 -> {209};
209 -> {210};
210 -> {211};
211 -> {212};
212 -> {213};
213 -> {214};
214 -> {215};
215 -> {216};
216 -> {218 217};
217 -> {228};
218 -> {219};
219 -> {220};
220 -> {221};
221 -> {222};
222 -> {223};
223 -> {224};
224 -> {225};
225 -> {226};
226 -> {227};
227 -> {228};
228 -> {229};
229 -> {230};
230 -> {231};
231 -> {232};
232 -> {233};
233 -> {234};
234 -> {236 235};
235 -> {246};
236 -> {237};
237 -> {238};
238 -> {239};
239 -> {240};
240 -> {241};
241 -> {242};
242 -> {243};
243 -> {244};
244 -> {245};
245 -> {246};
246 -> {247};
247 -> {248};
248 -> {249};
249 -> {250};
250 -> {251};
251 -> {252};
252 -> {253};
253 -> {254};
254 -> {256 255};
255 -> {266};
256 -> {257};
257 -> {258};
258 -> {259};
259 -> {260};
260 -> {261};
261 -> {262};
262 -> {263};
263 -> {264};
264 -> {265};
265 -> {266};
266 -> {267};
267 -> {268};
268 -> {269};
269 -> {270};
270 -> {271};
271 -> {272};
272 -> {274 273};
273 -> {284};
274 -> {275};
275 -> {276};
276 -> {277};
277 -> {278};
278 -> {279};
279 -> {280};
280 -> {281};
281 -> {282};
282 -> {283};
283 -> {284};
284 -> {285};
285 -> {286};
286 -> {287};
287 -> {288};
288 -> {289};
289 -> {290};
290 -> {292 291};
291 -> {302};
292 -> {293};
293 -> {294};
294 -> {295};
295 -> {296};
296 -> {297};
297 -> {298};
298 -> {299};
299 -> {300};
300 -> {301};
301 -> {302};
302 -> {303};
}
@@ -0,0 +1,120 @@
class A {
val s: String = ""
}
fun test_0(list: List<A>) {
var goodA: A? = null
for (a in list) {
if (goodA == null) {
goodA = a
continue
}
goodA.s
}
}
fun test_1(a: A, b: Boolean) {
val x: Any
if (b) {
x = A()
} else {
x = a
}
x.s
}
fun test_2(a: Any, b: Boolean) {
val x: Any
if (b) {
//x1
x = A()
} else {
//x2 = a
x = a
a as A
}
x.s
}
fun test_3(a: Any, b: Boolean) {
val x: Any
if (b) {
x = A()
} else {
a as A
x = a
}
x.s
}
fun test_4(a: Any, b: Boolean) {
val x: Any
if (b) {
x = a
} else {
x = a
}
x as A
x.s
a.s
}
fun test_5(a: Any, b: Boolean) {
val x: Any
if (b) {
x = a
} else {
x = a
}
a as A
x.s
a.s
}
fun test_6(a: A) {
val x: Any
x = a
x.s
}
fun test_7() {
val z: String? = null
var y : String? = z
val x: String? = y
if (x != null) {
x.length // OK
y.length // OK
z.length // OK
}
if (y != null) {
x.length // OK
y.length // OK
z.length // OK
}
if (z != null) {
x.length // OK
y.length // OK
z.length // OK
}
y = null
if (x != null) {
x.length // OK
y.<!INAPPLICABLE_CANDIDATE!>length<!> // Bad
z.length // OK
}
if (y != null) {
x.<!INAPPLICABLE_CANDIDATE!>length<!> // Bad
y.length // OK
z.<!INAPPLICABLE_CANDIDATE!>length<!> // Bad
}
if (z != null) {
x.length // OK
y.<!INAPPLICABLE_CANDIDATE!>length<!> // Bad
z.length // OK
}
}
@@ -0,0 +1,157 @@
FILE: noBoundSmartcastAfterContinue.kt
public final class A : R|kotlin/Any| {
public constructor(): R|A| {
super<R|kotlin/Any|>()
}
public final val s: R|kotlin/String| = String()
public get(): R|kotlin/String|
}
public final fun test_0(list: R|kotlin/collections/List<A>|): R|kotlin/Unit| {
lvar goodA: R|A?| = Null(null)
lval <range>: R|kotlin/collections/List<A>| = R|<local>/list|
lval <iterator>: R|kotlin/collections/Iterator<A>| = R|<local>/<range>|.R|FakeOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<A>|>|()
while(R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()) {
lval a: R|A| = R|<local>/<iterator>|.R|FakeOverride<kotlin/collections/Iterator.next: R|A|>|()
when () {
==(R|<local>/goodA|, Null(null)) -> {
R|<local>/goodA| = R|<local>/a|
continue@@@[R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()]
}
}
R|<local>/goodA|.R|/A.s|
}
}
public final fun test_1(a: R|A|, b: R|kotlin/Boolean|): R|kotlin/Unit| {
lval x: R|kotlin/Any|
when () {
R|<local>/b| -> {
R|<local>/x| = R|/A.A|()
}
else -> {
R|<local>/x| = R|<local>/a|
}
}
R|<local>/x|.R|/A.s|
}
public final fun test_2(a: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| {
lval x: R|kotlin/Any|
when () {
R|<local>/b| -> {
R|<local>/x| = R|/A.A|()
}
else -> {
R|<local>/x| = R|<local>/a|
(R|<local>/a| as R|A|)
}
}
R|<local>/x|.R|/A.s|
}
public final fun test_3(a: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| {
lval x: R|kotlin/Any|
when () {
R|<local>/b| -> {
R|<local>/x| = R|/A.A|()
}
else -> {
(R|<local>/a| as R|A|)
R|<local>/x| = R|<local>/a|
}
}
R|<local>/x|.R|/A.s|
}
public final fun test_4(a: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| {
lval x: R|kotlin/Any|
when () {
R|<local>/b| -> {
R|<local>/x| = R|<local>/a|
}
else -> {
R|<local>/x| = R|<local>/a|
}
}
(R|<local>/x| as R|A|)
R|<local>/x|.R|/A.s|
R|<local>/a|.R|/A.s|
}
public final fun test_5(a: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| {
lval x: R|kotlin/Any|
when () {
R|<local>/b| -> {
R|<local>/x| = R|<local>/a|
}
else -> {
R|<local>/x| = R|<local>/a|
}
}
(R|<local>/a| as R|A|)
R|<local>/x|.R|/A.s|
R|<local>/a|.R|/A.s|
}
public final fun test_6(a: R|A|): R|kotlin/Unit| {
lval x: R|kotlin/Any|
R|<local>/x| = R|<local>/a|
R|<local>/x|.R|/A.s|
}
public final fun test_7(): R|kotlin/Unit| {
lval z: R|kotlin/String?| = Null(null)
lvar y: R|kotlin/String?| = R|<local>/z|
lval x: R|kotlin/String?| = R|<local>/y|
when () {
!=(R|<local>/x|, Null(null)) -> {
R|<local>/x|.R|kotlin/String.length|
R|<local>/y|.R|kotlin/String.length|
R|<local>/z|.R|kotlin/String.length|
}
}
when () {
!=(R|<local>/y|, Null(null)) -> {
R|<local>/x|.R|kotlin/String.length|
R|<local>/y|.R|kotlin/String.length|
R|<local>/z|.R|kotlin/String.length|
}
}
when () {
!=(R|<local>/z|, Null(null)) -> {
R|<local>/x|.R|kotlin/String.length|
R|<local>/y|.R|kotlin/String.length|
R|<local>/z|.R|kotlin/String.length|
}
}
R|<local>/y| = Null(null)
when () {
!=(R|<local>/x|, Null(null)) -> {
R|<local>/x|.R|kotlin/String.length|
R|<local>/y|.<Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#
R|<local>/z|.R|kotlin/String.length|
}
}
when () {
!=(R|<local>/y|, Null(null)) -> {
R|<local>/x|.<Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#
R|<local>/y|.R|kotlin/String.length|
R|<local>/z|.<Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#
}
}
when () {
!=(R|<local>/z|, Null(null)) -> {
R|<local>/x|.R|kotlin/String.length|
R|<local>/y|.<Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#
R|<local>/z|.R|kotlin/String.length|
}
}
}
@@ -499,11 +499,11 @@ digraph nullability_kt {
172 [label="Access variable R|/Q.data|"]; 172 [label="Access variable R|/Q.data|"];
173 [label="Access variable R|<local>/q|"]; 173 [label="Access variable R|<local>/q|"];
174 [label="Access variable R|/Q.data|"]; 174 [label="Access variable R|/Q.data|"];
175 [label="Access variable R|/MyData.s|"]; 175 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
176 [label="Access variable R|<local>/q|"]; 176 [label="Access variable R|<local>/q|"];
177 [label="Access variable R|/Q.data|"]; 177 [label="Access variable R|/Q.data|"];
178 [label="Access variable R|/MyData.s|"]; 178 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
179 [label="Function call: R|<local>/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|()"]; 179 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()"];
180 [label="Exit block"]; 180 [label="Exit block"];
} }
181 [label="Exit when branch result"]; 181 [label="Exit when branch result"];
@@ -596,11 +596,11 @@ digraph nullability_kt {
216 [label="Access variable R|/Q.data|"]; 216 [label="Access variable R|/Q.data|"];
217 [label="Access variable R|<local>/q|"]; 217 [label="Access variable R|<local>/q|"];
218 [label="Access variable R|/Q.data|"]; 218 [label="Access variable R|/Q.data|"];
219 [label="Access variable R|/MyData.s|"]; 219 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
220 [label="Access variable R|<local>/q|"]; 220 [label="Access variable R|<local>/q|"];
221 [label="Access variable R|/Q.data|"]; 221 [label="Access variable R|/Q.data|"];
222 [label="Access variable R|/MyData.s|"]; 222 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
223 [label="Function call: R|<local>/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|()"]; 223 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()"];
224 [label="Exit function test_6" style="filled" fillcolor=red]; 224 [label="Exit function test_6" style="filled" fillcolor=red];
} }
@@ -1288,11 +1288,11 @@ digraph nullability_kt {
486 [label="Access variable R|/QImplWithCustomGetter.data|"]; 486 [label="Access variable R|/QImplWithCustomGetter.data|"];
487 [label="Access variable R|<local>/q|"]; 487 [label="Access variable R|<local>/q|"];
488 [label="Access variable R|/QImplWithCustomGetter.data|"]; 488 [label="Access variable R|/QImplWithCustomGetter.data|"];
489 [label="Access variable R|/MyData.s|"]; 489 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
490 [label="Access variable R|<local>/q|"]; 490 [label="Access variable R|<local>/q|"];
491 [label="Access variable R|/QImplWithCustomGetter.data|"]; 491 [label="Access variable R|/QImplWithCustomGetter.data|"];
492 [label="Access variable R|/MyData.s|"]; 492 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
493 [label="Function call: R|<local>/q|.R|/QImplWithCustomGetter.data|.R|/MyData.s|.R|kotlin/Int.inc|()"]; 493 [label="Function call: R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()"];
494 [label="Exit block"]; 494 [label="Exit block"];
} }
495 [label="Exit when branch result"]; 495 [label="Exit when branch result"];
@@ -1365,11 +1365,11 @@ digraph nullability_kt {
518 [label="Access variable R|/QImplMutable.data|"]; 518 [label="Access variable R|/QImplMutable.data|"];
519 [label="Access variable R|<local>/q|"]; 519 [label="Access variable R|<local>/q|"];
520 [label="Access variable R|/QImplMutable.data|"]; 520 [label="Access variable R|/QImplMutable.data|"];
521 [label="Access variable R|/MyData.s|"]; 521 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
522 [label="Access variable R|<local>/q|"]; 522 [label="Access variable R|<local>/q|"];
523 [label="Access variable R|/QImplMutable.data|"]; 523 [label="Access variable R|/QImplMutable.data|"];
524 [label="Access variable R|/MyData.s|"]; 524 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
525 [label="Function call: R|<local>/q|.R|/QImplMutable.data|.R|/MyData.s|.R|kotlin/Int.inc|()"]; 525 [label="Function call: R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()"];
526 [label="Exit block"]; 526 [label="Exit block"];
} }
527 [label="Exit when branch result"]; 527 [label="Exit when branch result"];
@@ -62,23 +62,21 @@ fun test_4(x: A?) {
x.foo() x.foo()
} }
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
fun test_5(q: Q?) { fun test_5(q: Q?) {
// `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData. // `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData.
if (q?.data?.s?.inc() != null) { if (q?.data?.s?.inc() != null) {
q.data // good q.data // good
q.data.s // should be bad q.data.<!INAPPLICABLE_CANDIDATE!>s<!> // should be bad
q.data.s.inc() // should be bad q.data.<!INAPPLICABLE_CANDIDATE!>s<!>.<!AMBIGUITY!>inc<!>() // should be bad
} }
} }
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
fun test_6(q: Q?) { fun test_6(q: Q?) {
// `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData. // `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData.
q?.data?.s?.inc() ?: return q?.data?.s?.inc() ?: return
q.data // good q.data // good
q.data.s // should be bad q.data.<!INAPPLICABLE_CANDIDATE!>s<!> // should be bad
q.data.s.inc() // should be bad q.data.<!INAPPLICABLE_CANDIDATE!>s<!>.<!AMBIGUITY!>inc<!>() // should be bad
} }
fun test_7(q: Q?) { fun test_7(q: Q?) {
@@ -139,7 +137,6 @@ fun test_10(a: Int?, b: Int?) {
b.<!AMBIGUITY!>inc<!>() b.<!AMBIGUITY!>inc<!>()
} }
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
fun test_11(q: QImpl?, q2: QImpl) { fun test_11(q: QImpl?, q2: QImpl) {
// `q.data` is a property with the default getter, so we CAN smartcast it to non-nullable MyData. // `q.data` is a property with the default getter, so we CAN smartcast it to non-nullable MyData.
if (q?.data?.s?.inc() != null) { if (q?.data?.s?.inc() != null) {
@@ -160,22 +157,20 @@ fun test_11(q: QImpl?, q2: QImpl) {
} }
} }
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
fun test_12(q: QImplWithCustomGetter?) { fun test_12(q: QImplWithCustomGetter?) {
// `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData. // `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData.
if (q?.data?.s?.inc() != null) { if (q?.data?.s?.inc() != null) {
q.data // good q.data // good
q.data.s // should be bad q.data.<!INAPPLICABLE_CANDIDATE!>s<!> // should be bad
q.data.s.inc() // should be bad q.data.<!INAPPLICABLE_CANDIDATE!>s<!>.<!AMBIGUITY!>inc<!>() // should be bad
} }
} }
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
fun test_13(q: QImplMutable?) { fun test_13(q: QImplMutable?) {
// `q.data` is a property that is mutable, so we can NOT smartcast it to non-nullable MyData. // `q.data` is a property that is mutable, so we can NOT smartcast it to non-nullable MyData.
if (q?.data?.s?.inc() != null) { if (q?.data?.s?.inc() != null) {
q.data // good q.data // good
q.data.s // should be bad q.data.<!INAPPLICABLE_CANDIDATE!>s<!> // should be bad
q.data.s.inc() // should be bad q.data.<!INAPPLICABLE_CANDIDATE!>s<!>.<!AMBIGUITY!>inc<!>() // should be bad
} }
} }
@@ -110,8 +110,8 @@ FILE: nullability.kt
when () { when () {
!=(R|<local>/q|?.R|/Q.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> { !=(R|<local>/q|?.R|/Q.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> {
R|<local>/q|.R|/Q.data| R|<local>/q|.R|/Q.data|
R|<local>/q|.R|/Q.data|.R|/MyData.s| R|<local>/q|.R|/Q.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#
R|<local>/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|() R|<local>/q|.R|/Q.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
} }
} }
@@ -127,8 +127,8 @@ FILE: nullability.kt
} }
R|<local>/q|.R|/Q.data| R|<local>/q|.R|/Q.data|
R|<local>/q|.R|/Q.data|.R|/MyData.s| R|<local>/q|.R|/Q.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#
R|<local>/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|() R|<local>/q|.R|/Q.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
} }
public final fun test_7(q: R|Q?|): R|kotlin/Unit| { public final fun test_7(q: R|Q?|): R|kotlin/Unit| {
when () { when () {
@@ -232,8 +232,8 @@ FILE: nullability.kt
when () { when () {
!=(R|<local>/q|?.R|/QImplWithCustomGetter.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> { !=(R|<local>/q|?.R|/QImplWithCustomGetter.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> {
R|<local>/q|.R|/QImplWithCustomGetter.data| R|<local>/q|.R|/QImplWithCustomGetter.data|
R|<local>/q|.R|/QImplWithCustomGetter.data|.R|/MyData.s| R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#
R|<local>/q|.R|/QImplWithCustomGetter.data|.R|/MyData.s|.R|kotlin/Int.inc|() R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
} }
} }
@@ -242,8 +242,8 @@ FILE: nullability.kt
when () { when () {
!=(R|<local>/q|?.R|/QImplMutable.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> { !=(R|<local>/q|?.R|/QImplMutable.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> {
R|<local>/q|.R|/QImplMutable.data| R|<local>/q|.R|/QImplMutable.data|
R|<local>/q|.R|/QImplMutable.data|.R|/MyData.s| R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#
R|<local>/q|.R|/QImplMutable.data|.R|/MyData.s|.R|kotlin/Int.inc|() R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(WRONG_RECEIVER): [/MyData.s]>#.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
} }
} }
@@ -0,0 +1,132 @@
digraph overridenOpenVal_kt {
graph [splines=ortho nodesep=3]
node [shape=box penwidth=2]
edge [penwidth=2]
subgraph cluster_0 {
color=red
0 [label="Enter function <init>" style="filled" fillcolor=red];
1 [label="Exit function <init>" style="filled" fillcolor=red];
}
0 -> {1};
subgraph cluster_1 {
color=red
2 [label="Enter function getter" style="filled" fillcolor=red];
3 [label="Exit function getter" style="filled" fillcolor=red];
}
2 -> {3};
subgraph cluster_2 {
color=red
4 [label="Enter property" style="filled" fillcolor=red];
5 [label="Access variable R|<local>/x|"];
6 [label="Exit property" style="filled" fillcolor=red];
}
4 -> {5};
5 -> {6};
subgraph cluster_3 {
color=red
7 [label="Enter function <init>" style="filled" fillcolor=red];
8 [label="Access variable R|<local>/x|"];
9 [label="Exit function <init>" style="filled" fillcolor=red];
}
7 -> {8};
8 -> {9};
subgraph cluster_4 {
color=red
10 [label="Enter function test_1" style="filled" fillcolor=red];
subgraph cluster_5 {
color=blue
11 [label="Enter when"];
subgraph cluster_6 {
color=blue
12 [label="Enter when branch condition "];
13 [label="Access variable R|/A.x|"];
14 [label="Type operator: x is String"];
15 [label="Exit when branch condition"];
}
16 [label="Synthetic else branch"];
17 [label="Enter when branch result"];
subgraph cluster_7 {
color=blue
18 [label="Enter block"];
19 [label="Access variable R|/A.x|"];
20 [label="Access variable R|kotlin/String.length|"];
21 [label="Exit block"];
}
22 [label="Exit when branch result"];
23 [label="Exit when"];
}
24 [label="Exit function test_1" style="filled" fillcolor=red];
}
10 -> {11};
11 -> {12};
12 -> {13};
13 -> {14};
14 -> {15};
15 -> {17 16};
16 -> {23};
17 -> {18};
18 -> {19};
19 -> {20};
20 -> {21};
21 -> {22};
22 -> {23};
23 -> {24};
subgraph cluster_8 {
color=red
25 [label="Enter function test_2" style="filled" fillcolor=red];
subgraph cluster_9 {
color=blue
26 [label="Enter when"];
subgraph cluster_10 {
color=blue
27 [label="Enter when branch condition "];
28 [label="Access variable R|<local>/b|"];
29 [label="Access variable R|/A.x|"];
30 [label="Type operator: b.x is String"];
31 [label="Exit when branch condition"];
}
32 [label="Synthetic else branch"];
33 [label="Enter when branch result"];
subgraph cluster_11 {
color=blue
34 [label="Enter block"];
35 [label="Access variable R|<local>/b|"];
36 [label="Access variable R|/A.x|"];
37 [label="Access variable R|kotlin/String.length|"];
38 [label="Exit block"];
}
39 [label="Exit when branch result"];
40 [label="Exit when"];
}
41 [label="Exit function test_2" style="filled" fillcolor=red];
}
25 -> {26};
26 -> {27};
27 -> {28};
28 -> {29};
29 -> {30};
30 -> {31};
31 -> {33 32};
32 -> {40};
33 -> {34};
34 -> {35};
35 -> {36};
36 -> {37};
37 -> {38};
38 -> {39};
39 -> {40};
40 -> {41};
}
@@ -0,0 +1,15 @@
open class A(open val x: Any)
class B(x: Any) : A(x) {
fun test_1() {
if (x is String) {
x.length
}
}
}
fun test_2(b: B) {
if (b.x is String) {
b.x.length
}
}
@@ -0,0 +1,33 @@
FILE: overridenOpenVal.kt
public open class A : R|kotlin/Any| {
public constructor(x: R|kotlin/Any|): R|A| {
super<R|kotlin/Any|>()
}
public open val x: R|kotlin/Any| = R|<local>/x|
public get(): R|kotlin/Any|
}
public final class B : R|A| {
public constructor(x: R|kotlin/Any|): R|B| {
super<R|A|>(R|<local>/x|)
}
public final fun test_1(): R|kotlin/Unit| {
when () {
(this@R|/B|.R|/A.x| is R|kotlin/String|) -> {
this@R|/B|.R|/A.x|.R|kotlin/String.length|
}
}
}
}
public final fun test_2(b: R|B|): R|kotlin/Unit| {
when () {
(R|<local>/b|.R|/A.x| is R|kotlin/String|) -> {
R|<local>/b|.R|/A.x|.R|kotlin/String.length|
}
}
}
@@ -88,7 +88,7 @@ digraph smartcastAfterReassignment_kt {
35 [label="Const: Null(null)"]; 35 [label="Const: Null(null)"];
36 [label="Assignmenet: R|<local>/x|"]; 36 [label="Assignmenet: R|<local>/x|"];
37 [label="Access variable R|<local>/x|"]; 37 [label="Access variable R|<local>/x|"];
38 [label="Access variable <Unresolved name: length>#"]; 38 [label="Access variable <Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#"];
39 [label="Exit function test_3" style="filled" fillcolor=red]; 39 [label="Exit function test_3" style="filled" fillcolor=red];
} }
@@ -17,5 +17,5 @@ fun test_3() {
x = "" x = ""
x.length x.length
x = null x = null
x.<!UNRESOLVED_REFERENCE!>length<!> x.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
@@ -19,5 +19,5 @@ FILE: smartcastAfterReassignment.kt
R|<local>/x| = String() R|<local>/x| = String()
R|<local>/x|.R|kotlin/String.length| R|<local>/x|.R|kotlin/String.length|
R|<local>/x| = Null(null) R|<local>/x| = Null(null)
R|<local>/x|.<Unresolved name: length># R|<local>/x|.<Inapplicable(WRONG_RECEIVER): [kotlin/String.length]>#
} }
@@ -0,0 +1,280 @@
digraph smartcastToNothing_kt {
graph [splines=ortho nodesep=3]
node [shape=box penwidth=2]
edge [penwidth=2]
subgraph cluster_0 {
color=red
0 [label="Enter function getNothing" style="filled" fillcolor=red];
1 [label="Function call: R|java/lang/Exception.Exception|()"];
2 [label="Throw: throw R|java/lang/Exception.Exception|()"];
3 [label="Stub" style="filled" fillcolor=gray];
4 [label="Jump: ^getNothing throw R|java/lang/Exception.Exception|()" style="filled" fillcolor=gray];
5 [label="Stub" style="filled" fillcolor=gray];
6 [label="Exit function getNothing" style="filled" fillcolor=red];
}
0 -> {1};
1 -> {2};
2 -> {6};
2 -> {3} [style=dotted];
3 -> {4} [style=dotted];
4 -> {6 5} [style=dotted];
5 -> {6} [style=dotted];
subgraph cluster_1 {
color=red
7 [label="Enter function getNullableNothing" style="filled" fillcolor=red];
8 [label="Const: Null(null)"];
9 [label="Jump: ^getNullableNothing Null(null)"];
10 [label="Stub" style="filled" fillcolor=gray];
11 [label="Exit function getNullableNothing" style="filled" fillcolor=red];
}
7 -> {8};
8 -> {9};
9 -> {11};
9 -> {10} [style=dotted];
10 -> {11} [style=dotted];
subgraph cluster_2 {
color=red
12 [label="Enter function getter" style="filled" fillcolor=red];
13 [label="Const: Int(1)"];
14 [label="Jump: ^ Int(1)"];
15 [label="Stub" style="filled" fillcolor=gray];
16 [label="Exit function getter" style="filled" fillcolor=red];
}
12 -> {13};
13 -> {14};
14 -> {16};
14 -> {15} [style=dotted];
15 -> {16} [style=dotted];
subgraph cluster_3 {
color=red
17 [label="Enter property" style="filled" fillcolor=red];
18 [label="Exit property" style="filled" fillcolor=red];
}
17 -> {18};
subgraph cluster_4 {
color=red
19 [label="Enter function getter" style="filled" fillcolor=red];
20 [label="Const: Int(2)"];
21 [label="Jump: ^ Int(2)"];
22 [label="Stub" style="filled" fillcolor=gray];
23 [label="Exit function getter" style="filled" fillcolor=red];
}
19 -> {20};
20 -> {21};
21 -> {23};
21 -> {22} [style=dotted];
22 -> {23} [style=dotted];
subgraph cluster_5 {
color=red
24 [label="Enter property" style="filled" fillcolor=red];
25 [label="Exit property" style="filled" fillcolor=red];
}
24 -> {25};
subgraph cluster_6 {
color=red
26 [label="Enter function myListOf" style="filled" fillcolor=red];
27 [label="Const: Null(null)"];
28 [label="Check not null: Null(null)!!"];
29 [label="Jump: ^myListOf Null(null)!!"];
30 [label="Stub" style="filled" fillcolor=gray];
31 [label="Exit function myListOf" style="filled" fillcolor=red];
}
26 -> {27};
27 -> {28};
28 -> {29};
29 -> {31};
29 -> {30} [style=dotted];
30 -> {31} [style=dotted];
subgraph cluster_7 {
color=red
32 [label="Enter function <init>" style="filled" fillcolor=red];
33 [label="Exit function <init>" style="filled" fillcolor=red];
}
32 -> {33};
subgraph cluster_8 {
color=red
34 [label="Enter function getter" style="filled" fillcolor=red];
35 [label="Exit function getter" style="filled" fillcolor=red];
}
34 -> {35};
subgraph cluster_9 {
color=red
36 [label="Enter property" style="filled" fillcolor=red];
37 [label="Const: Int(1)"];
38 [label="Exit property" style="filled" fillcolor=red];
}
36 -> {37};
37 -> {38};
subgraph cluster_10 {
color=red
39 [label="Enter function getter" style="filled" fillcolor=red];
40 [label="Exit function getter" style="filled" fillcolor=red];
}
39 -> {40};
subgraph cluster_11 {
color=red
41 [label="Enter property" style="filled" fillcolor=red];
42 [label="Const: Boolean(true)"];
43 [label="Exit property" style="filled" fillcolor=red];
}
41 -> {42};
42 -> {43};
subgraph cluster_12 {
color=red
44 [label="Enter function test_0" style="filled" fillcolor=red];
45 [label="Const: Null(null)"];
46 [label="Variable declaration: lvar s: R|A?|"];
47 [label="Access variable R|<local>/results|"];
48 [label="Variable declaration: lval <range>: R|kotlin/collections/List<kotlin/Nothing>|"];
49 [label="Access variable R|<local>/<range>|"];
50 [label="Function call: R|<local>/<range>|.R|FakeOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<kotlin/Nothing>|>|()"];
51 [label="Variable declaration: lval <iterator>: R|kotlin/collections/Iterator<kotlin/Nothing>|"];
subgraph cluster_13 {
color=blue
52 [label="Enter while loop"];
subgraph cluster_14 {
color=blue
53 [label="Enter loop condition"];
54 [label="Access variable R|<local>/<iterator>|"];
55 [label="Function call: R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()"];
56 [label="Exit loop condition"];
}
subgraph cluster_15 {
color=blue
57 [label="Enter loop block"];
subgraph cluster_16 {
color=blue
58 [label="Enter block"];
59 [label="Access variable R|<local>/<iterator>|"];
60 [label="Function call: R|<local>/<iterator>|.R|FakeOverride<kotlin/collections/Iterator.next: R|kotlin/Nothing|>|()"];
61 [label="Stub" style="filled" fillcolor=gray];
62 [label="Variable declaration: lval result: R|kotlin/Nothing|" style="filled" fillcolor=gray];
63 [label="Access variable R|<local>/result|" style="filled" fillcolor=gray];
64 [label="Stub" style="filled" fillcolor=gray];
65 [label="Assignmenet: R|<local>/s|" style="filled" fillcolor=gray];
subgraph cluster_17 {
color=blue
66 [label="Enter when" style="filled" fillcolor=gray];
subgraph cluster_18 {
color=blue
67 [label="Enter when branch condition " style="filled" fillcolor=gray];
68 [label="Access variable R|<local>/result|" style="filled" fillcolor=gray];
69 [label="Stub" style="filled" fillcolor=gray];
70 [label="Access variable <Unresolved name: b>#" style="filled" fillcolor=gray];
71 [label="Exit when branch condition" style="filled" fillcolor=gray];
}
72 [label="Synthetic else branch" style="filled" fillcolor=gray];
73 [label="Enter when branch result" style="filled" fillcolor=gray];
subgraph cluster_19 {
color=blue
74 [label="Enter block" style="filled" fillcolor=gray];
75 [label="Jump: break@@@[R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()] " style="filled" fillcolor=gray];
76 [label="Stub" style="filled" fillcolor=gray];
77 [label="Exit block" style="filled" fillcolor=gray];
}
78 [label="Exit when branch result" style="filled" fillcolor=gray];
79 [label="Exit when" style="filled" fillcolor=gray];
}
80 [label="Exit block" style="filled" fillcolor=gray];
}
81 [label="Exit loop block" style="filled" fillcolor=gray];
}
82 [label="Exit whileloop"];
}
83 [label="Access variable R|<local>/s|"];
84 [label="Enter safe call"];
85 [label="Postponed enter to lambda"];
subgraph cluster_20 {
color=blue
86 [label="Enter function anonymousFunction"];
87 [label="Access variable R|<local>/it|"];
88 [label="Access variable R|/A.a|"];
89 [label="Exit function anonymousFunction"];
}
90 [label="Postponed exit from lambda"];
91 [label="Function call: R|<local>/s|?.R|kotlin/let|<R|A|, R|kotlin/Int|>(<L> = let@fun <anonymous>(it: R|A|): R|kotlin/Int| <kind=EXACTLY_ONCE> {
^ R|<local>/it|.R|/A.a|
}
)"];
92 [label="Exit safe call"];
93 [label="Exit function test_0" style="filled" fillcolor=red];
}
44 -> {45};
45 -> {46};
46 -> {47};
47 -> {48};
48 -> {49};
49 -> {50};
50 -> {51};
51 -> {52};
52 -> {53};
53 -> {54};
54 -> {55};
55 -> {56};
56 -> {82 57};
57 -> {58};
58 -> {59};
59 -> {60};
60 -> {93};
60 -> {61} [style=dotted];
61 -> {62} [style=dotted];
62 -> {63} [style=dotted];
63 -> {93 64} [style=dotted];
64 -> {65} [style=dotted];
65 -> {66} [style=dotted];
66 -> {67} [style=dotted];
67 -> {68} [style=dotted];
68 -> {93 69} [style=dotted];
69 -> {70} [style=dotted];
70 -> {71} [style=dotted];
71 -> {73 72} [style=dotted];
72 -> {79} [style=dotted];
73 -> {74} [style=dotted];
74 -> {75} [style=dotted];
75 -> {82 76} [style=dotted];
76 -> {77} [style=dotted];
77 -> {78} [style=dotted];
78 -> {79} [style=dotted];
79 -> {80} [style=dotted];
80 -> {81} [style=dotted];
81 -> {53} [style=dotted];
82 -> {83};
83 -> {84 92};
84 -> {85};
85 -> {86};
85 -> {90} [color=red];
86 -> {87};
87 -> {88};
88 -> {89};
89 -> {90} [color=green];
90 -> {91};
91 -> {92};
92 -> {93} [style=dotted];
}
@@ -0,0 +1,23 @@
fun getNothing(): Nothing = throw Exception()
fun getNullableNothing(): Nothing? = null
val String?.q: Int get() = 1
val String.qq: Int get() = 2
fun <T> myListOf(x: T): List<T> = null!!
class A {
val a: Int = 1
val b: Boolean = true
}
fun test_0(results: List<Nothing>) {
var s: A? = null
for (result in results) {
s = result
if (result.<!UNRESOLVED_REFERENCE!>b<!>) {
break
}
}
s?.let { it.a }
}
@@ -0,0 +1,50 @@
FILE: smartcastToNothing.kt
public final fun getNothing(): R|kotlin/Nothing| {
^getNothing throw R|java/lang/Exception.Exception|()
}
public final fun getNullableNothing(): R|kotlin/Nothing?| {
^getNullableNothing Null(null)
}
public final val R|kotlin/String?|.q: R|kotlin/Int|
public get(): R|kotlin/Int| {
^ Int(1)
}
public final val R|kotlin/String|.qq: R|kotlin/Int|
public get(): R|kotlin/Int| {
^ Int(2)
}
public final fun <T> myListOf(x: R|T|): R|kotlin/collections/List<T>| {
^myListOf Null(null)!!
}
public final class A : R|kotlin/Any| {
public constructor(): R|A| {
super<R|kotlin/Any|>()
}
public final val a: R|kotlin/Int| = Int(1)
public get(): R|kotlin/Int|
public final val b: R|kotlin/Boolean| = Boolean(true)
public get(): R|kotlin/Boolean|
}
public final fun test_0(results: R|kotlin/collections/List<kotlin/Nothing>|): R|kotlin/Unit| {
lvar s: R|A?| = Null(null)
lval <range>: R|kotlin/collections/List<kotlin/Nothing>| = R|<local>/results|
lval <iterator>: R|kotlin/collections/Iterator<kotlin/Nothing>| = R|<local>/<range>|.R|FakeOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<kotlin/Nothing>|>|()
while(R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()) {
lval result: R|kotlin/Nothing| = R|<local>/<iterator>|.R|FakeOverride<kotlin/collections/Iterator.next: R|kotlin/Nothing|>|()
R|<local>/s| = R|<local>/result|
when () {
R|<local>/result|.<Unresolved name: b># -> {
break@@@[R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()]
}
}
}
R|<local>/s|?.R|kotlin/let|<R|A|, R|kotlin/Int|>(<L> = let@fun <anonymous>(it: R|A|): R|kotlin/Int| <kind=EXACTLY_ONCE> {
^ R|<local>/it|.R|/A.a|
}
)
}
@@ -0,0 +1,100 @@
digraph thisOfExtensionProperty_kt {
graph [splines=ortho nodesep=3]
node [shape=box penwidth=2]
edge [penwidth=2]
subgraph cluster_0 {
color=red
0 [label="Enter function getter" style="filled" fillcolor=red];
1 [label="Exit function getter" style="filled" fillcolor=red];
}
0 -> {1};
subgraph cluster_1 {
color=red
2 [label="Enter property" style="filled" fillcolor=red];
3 [label="Exit property" style="filled" fillcolor=red];
}
2 -> {3};
subgraph cluster_2 {
color=red
4 [label="Enter function getter" style="filled" fillcolor=red];
subgraph cluster_3 {
color=blue
5 [label="Enter &&"];
6 [label="Access variable this@R|/check_1|"];
7 [label="Type operator: this is B"];
8 [label="Exit left part of &&"];
9 [label="Enter right part of &&"];
10 [label="Access variable R|/B.b|"];
11 [label="Exit &&"];
}
12 [label="Jump: ^ (this@R|/check_1| is R|B|) && this@R|/check_1|.R|/B.b|"];
13 [label="Stub" style="filled" fillcolor=gray];
14 [label="Exit function getter" style="filled" fillcolor=red];
}
4 -> {5};
5 -> {6};
6 -> {7};
7 -> {8};
8 -> {11 9};
9 -> {10};
10 -> {11};
11 -> {12};
12 -> {14};
12 -> {13} [style=dotted];
13 -> {14} [style=dotted];
subgraph cluster_4 {
color=red
15 [label="Enter property" style="filled" fillcolor=red];
16 [label="Exit property" style="filled" fillcolor=red];
}
15 -> {16};
subgraph cluster_5 {
color=red
17 [label="Enter function getter" style="filled" fillcolor=red];
subgraph cluster_6 {
color=blue
18 [label="Enter &&"];
19 [label="Access variable this@R|/check_2|"];
20 [label="Type operator: this is B"];
21 [label="Exit left part of &&"];
22 [label="Enter right part of &&"];
23 [label="Access variable this@R|/check_2|"];
24 [label="Access variable R|/B.b|"];
25 [label="Exit &&"];
}
26 [label="Jump: ^ (this@R|/check_2| is R|B|) && this@R|/check_2|.R|/B.b|"];
27 [label="Stub" style="filled" fillcolor=gray];
28 [label="Exit function getter" style="filled" fillcolor=red];
}
17 -> {18};
18 -> {19};
19 -> {20};
20 -> {21};
21 -> {25 22};
22 -> {23};
23 -> {24};
24 -> {25};
25 -> {26};
26 -> {28};
26 -> {27} [style=dotted];
27 -> {28} [style=dotted];
subgraph cluster_7 {
color=red
29 [label="Enter property" style="filled" fillcolor=red];
30 [label="Exit property" style="filled" fillcolor=red];
}
29 -> {30};
}
@@ -0,0 +1,12 @@
interface A
interface B {
val b: Boolean
}
val A.check_1: Boolean
get() = this is B && b
val A.check_2: Boolean
get() = this is B && this.b
// 1713
@@ -0,0 +1,16 @@
FILE: thisOfExtensionProperty.kt
public abstract interface A : R|kotlin/Any| {
}
public abstract interface B : R|kotlin/Any| {
public abstract val b: R|kotlin/Boolean|
public get(): R|kotlin/Boolean|
}
public final val R|A|.check_1: R|kotlin/Boolean|
public get(): R|kotlin/Boolean| {
^ (this@R|/check_1| is R|B|) && this@R|/check_1|.R|/B.b|
}
public final val R|A|.check_2: R|kotlin/Boolean|
public get(): R|kotlin/Boolean| {
^ (this@R|/check_2| is R|B|) && this@R|/check_2|.R|/B.b|
}
@@ -1166,11 +1166,6 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
runTest("compiler/fir/resolve/testData/resolve/problems/nestedClassContructor.kt"); runTest("compiler/fir/resolve/testData/resolve/problems/nestedClassContructor.kt");
} }
@TestMetadata("noBoundSmartcastAfterContinue.kt")
public void testNoBoundSmartcastAfterContinue() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/problems/noBoundSmartcastAfterContinue.kt");
}
@TestMetadata("propertyFromJavaPlusAssign.kt") @TestMetadata("propertyFromJavaPlusAssign.kt")
public void testPropertyFromJavaPlusAssign() throws Exception { public void testPropertyFromJavaPlusAssign() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/problems/propertyFromJavaPlusAssign.kt"); runTest("compiler/fir/resolve/testData/resolve/problems/propertyFromJavaPlusAssign.kt");
@@ -143,6 +143,11 @@ public class FirDiagnosticsWithCfgTestGenerated extends AbstractFirDiagnosticsWi
runTest("compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts.kt"); runTest("compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts.kt");
} }
@TestMetadata("boundSmartcastsInBranches.kt")
public void testBoundSmartcastsInBranches() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcastsInBranches.kt");
}
@TestMetadata("casts.kt") @TestMetadata("casts.kt")
public void testCasts() throws Exception { public void testCasts() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/casts.kt"); runTest("compiler/fir/resolve/testData/resolve/smartcasts/casts.kt");
@@ -203,6 +208,11 @@ public class FirDiagnosticsWithCfgTestGenerated extends AbstractFirDiagnosticsWi
runTest("compiler/fir/resolve/testData/resolve/smartcasts/nullability.kt"); runTest("compiler/fir/resolve/testData/resolve/smartcasts/nullability.kt");
} }
@TestMetadata("overridenOpenVal.kt")
public void testOverridenOpenVal() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/overridenOpenVal.kt");
}
@TestMetadata("returns.kt") @TestMetadata("returns.kt")
public void testReturns() throws Exception { public void testReturns() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/returns.kt"); runTest("compiler/fir/resolve/testData/resolve/smartcasts/returns.kt");
@@ -238,6 +248,16 @@ public class FirDiagnosticsWithCfgTestGenerated extends AbstractFirDiagnosticsWi
runTest("compiler/fir/resolve/testData/resolve/smartcasts/smartcastOnLambda.kt"); runTest("compiler/fir/resolve/testData/resolve/smartcasts/smartcastOnLambda.kt");
} }
@TestMetadata("smartcastToNothing.kt")
public void testSmartcastToNothing() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/smartcastToNothing.kt");
}
@TestMetadata("thisOfExtensionProperty.kt")
public void testThisOfExtensionProperty() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/thisOfExtensionProperty.kt");
}
@TestMetadata("when.kt") @TestMetadata("when.kt")
public void testWhen() throws Exception { public void testWhen() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/when.kt"); runTest("compiler/fir/resolve/testData/resolve/smartcasts/when.kt");
@@ -1166,11 +1166,6 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/resolve/testData/resolve/problems/nestedClassContructor.kt"); runTest("compiler/fir/resolve/testData/resolve/problems/nestedClassContructor.kt");
} }
@TestMetadata("noBoundSmartcastAfterContinue.kt")
public void testNoBoundSmartcastAfterContinue() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/problems/noBoundSmartcastAfterContinue.kt");
}
@TestMetadata("propertyFromJavaPlusAssign.kt") @TestMetadata("propertyFromJavaPlusAssign.kt")
public void testPropertyFromJavaPlusAssign() throws Exception { public void testPropertyFromJavaPlusAssign() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/problems/propertyFromJavaPlusAssign.kt"); runTest("compiler/fir/resolve/testData/resolve/problems/propertyFromJavaPlusAssign.kt");
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A { open class A {
open fun foo(): Any = "A" open fun foo(): Any = "A"
} }
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A<T, U> { interface A<T, U> {
fun foo(t: T, u: U) = "A" fun foo(t: T, u: U) = "A"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
var result = "" var result = ""
interface D1 { interface D1 {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface D1 { interface D1 {
fun foo(): Any fun foo(): Any
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A(val value: String) { open class A(val value: String) {
fun component1() = value fun component1() = value
} }
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A<T> { interface A<T> {
fun foo(t: T, u: Int) = "A" fun foo(t: T, u: Int) = "A"
} }
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
var result = "" var result = ""
interface Base interface Base
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T> { open class A<T> {
open fun foo(t: T) = "A" open fun foo(t: T) = "A"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T, U, V> { open class A<T, U, V> {
open fun foo(t: T, u: U, v: V) = "A" open fun foo(t: T, u: U, v: V) = "A"
} }
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T> { open class A<T> {
open fun foo(t: T) = "A" open fun foo(t: T) = "A"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T> { open class A<T> {
open fun <U> foo(t: T, u: U) = "A" open fun <U> foo(t: T, u: U) = "A"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T : Number>(val t: T) { open class A<T : Number>(val t: T) {
open fun foo(): T = t open fun foo(): T = t
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A<T> { interface A<T> {
fun foo(t: T) = "A" fun foo(t: T) = "A"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T : Number> { open class A<T : Number> {
open fun foo(t: T) = "A" open fun foo(t: T) = "A"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
abstract class A<T> { abstract class A<T> {
abstract fun foo(t: T): String abstract fun foo(t: T): String
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T : U, U> { open class A<T : U, U> {
open fun foo(t: T, u: U) = "A" open fun foo(t: T, u: U) = "A"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T> { open class A<T> {
open fun <U> foo(t: T, u: U) = "A" open fun <U> foo(t: T, u: U) = "A"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T> { open class A<T> {
open fun foo(t: T) = "A" open fun foo(t: T) = "A"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T : Number> { open class A<T : Number> {
open fun foo(t: T) = "A" open fun foo(t: T) = "A"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A { interface A {
fun foo(): Any = "A" fun foo(): Any = "A"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A<T> { interface A<T> {
fun foo(t: T, u: Int) = "A" fun foo(t: T, u: Int) = "A"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A<T> { interface A<T> {
fun foo(t: T, u: Int) = "A" fun foo(t: T, u: Int) = "A"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A<T> { interface A<T> {
fun foo(t: T) = "A" fun foo(t: T) = "A"
} }
@@ -1,3 +1,4 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// FILE: Test.java // FILE: Test.java
@@ -6,10 +6,10 @@ class A() {
fun f(): Unit { fun f(): Unit {
var x: Int? = 1 var x: Int? = 1
x = null x = null
x + 1 x <!INAPPLICABLE_CANDIDATE!>+<!> 1
x plus 1 x <!INAPPLICABLE_CANDIDATE!>plus<!> 1
x < 1 x < 1
x += 1 <!UNRESOLVED_REFERENCE!>x += 1<!>
x == 1 x == 1
x != 1 x != 1
@@ -22,8 +22,8 @@ fun f(): Unit {
x === 1 x === 1
x !== 1 x !== 1
x..2 x<!INAPPLICABLE_CANDIDATE!>..<!>2
x in 1..2 x <!INAPPLICABLE_CANDIDATE!>in<!> 1..2
val y : Boolean? = true val y : Boolean? = true
false || y false || y
@@ -11,7 +11,7 @@ fun foo() {
while (y != null) { while (y != null) {
bar(y) bar(y)
} }
bar(y) <!INAPPLICABLE_CANDIDATE!>bar<!>(y)
val z: Int? = null val z: Int? = null
while (z == null) { while (z == null) {
@@ -22,5 +22,5 @@ fun whileWithNoCondition(x: Int?) {
while (<!SYNTAX!><!>) { while (<!SYNTAX!><!>) {
x!! x!!
} }
checkSubtype<Int>(x) <!INAPPLICABLE_CANDIDATE!>checkSubtype<!><Int>(x)
} }
@@ -10,8 +10,8 @@ class A<out T, in E> {
fun foo(x: A<String, Any?>, cs: CharSequence, ls: List<CharSequence>) { fun foo(x: A<String, Any?>, cs: CharSequence, ls: List<CharSequence>) {
val y: A<CharSequence, String> = x val y: A<CharSequence, String> = x
y.foo(cs) y.<!INAPPLICABLE_CANDIDATE!>foo<!>(cs)
val s: String = y.foo(cs, ls) val s: String = y.<!INAPPLICABLE_CANDIDATE!>foo<!>(cs, ls)
val ls2: List<String> = y.bar() val ls2: List<String> = y.bar()
} }
@@ -9,7 +9,7 @@ class CtxImpl : Ctx {
open class Test(open val ctx: Ctx) { open class Test(open val ctx: Ctx) {
fun test() { fun test() {
when (ctx) { when (ctx) {
is CtxImpl -> ctx.doJob(2) is CtxImpl -> ctx.<!UNRESOLVED_REFERENCE!>doJob<!>(2)
} }
} }
} }
@@ -9,6 +9,6 @@ var a: A? = null
fun smartCastInterference(b: B) { fun smartCastInterference(b: B) {
if (a != null) { if (a != null) {
a.foo(b) a.<!INAPPLICABLE_CANDIDATE!>foo<!>(b)
} }
} }
@@ -2,8 +2,8 @@
fun foo(): String { fun foo(): String {
var s: String? var s: String?
s = null s = null
s?.<!UNRESOLVED_REFERENCE!>length<!> s?.length
s.<!UNRESOLVED_REFERENCE!>length<!> s.<!INAPPLICABLE_CANDIDATE!>length<!>
if (s == null) return s!! if (s == null) return s!!
var t: String? = "y" var t: String? = "y"
if (t == null) t = "x" if (t == null) t = "x"
@@ -41,7 +41,7 @@ fun f(a: SomeClass?) {
if (aa as? SomeSubClass != null) { if (aa as? SomeSubClass != null) {
aa = null aa = null
// 'aa' cannot be cast to SomeSubClass // 'aa' cannot be cast to SomeSubClass
aa.<!UNRESOLVED_REFERENCE!>hashCode<!>() aa.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
aa.<!UNRESOLVED_REFERENCE!>foo<!> aa.<!UNRESOLVED_REFERENCE!>foo<!>
(aa as? SomeSubClass).<!INAPPLICABLE_CANDIDATE!>foo<!> (aa as? SomeSubClass).<!INAPPLICABLE_CANDIDATE!>foo<!>
(aa as SomeSubClass).foo (aa as SomeSubClass).foo
@@ -50,7 +50,7 @@ fun f(a: SomeClass?) {
aa = null aa = null
if (b != null) { if (b != null) {
// 'aa' cannot be cast to SomeSubClass // 'aa' cannot be cast to SomeSubClass
aa.<!UNRESOLVED_REFERENCE!>hashCode<!>() aa.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
aa.<!UNRESOLVED_REFERENCE!>foo<!> aa.<!UNRESOLVED_REFERENCE!>foo<!>
(aa as? SomeSubClass).<!INAPPLICABLE_CANDIDATE!>foo<!> (aa as? SomeSubClass).<!INAPPLICABLE_CANDIDATE!>foo<!>
(aa as SomeSubClass).foo (aa as SomeSubClass).foo
@@ -28,11 +28,11 @@ fun test() {
x.foo().checkType { _<CharSequence?>() } x.foo().checkType { _<CharSequence?>() }
if (x is B && x is C) { if (x is B && x is C) {
x.foo().checkType { <!INAPPLICABLE_CANDIDATE!>_<!><CharSequence?>() } x.foo().checkType { _<CharSequence?>() }
x.baz("") x.baz("")
x.baz(1).checkType { <!INAPPLICABLE_CANDIDATE!>_<!><Unit>() } x.baz(1).checkType { _<Unit>() }
x.baz(1, 2) x.<!INAPPLICABLE_CANDIDATE!>baz<!>(1, 2)
x.foobar().checkType { _<String>() } x.<!UNRESOLVED_REFERENCE!>foobar<!>().<!INAPPLICABLE_CANDIDATE!>checkType<!> { <!UNRESOLVED_REFERENCE!>_<!><String>() }
} }
} }
@@ -6,5 +6,5 @@ public fun foo(p: String?): Int {
if (x()) break if (x()) break
} }
// p is nullable because it's possible loop body is not executed at all // p is nullable because it's possible loop body is not executed at all
return p.length return p.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
@@ -7,5 +7,5 @@ public fun foo(p: String?): Int {
if (x()) break if (x()) break
} }
// Smart cast should not work in this case, see KT-6284 // Smart cast should not work in this case, see KT-6284
return p.length return p.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
@@ -8,7 +8,7 @@ fun foo() {
} }
// TODO: this testdata fixates undesired behavior (it should be an unsafe call) // TODO: this testdata fixates undesired behavior (it should be an unsafe call)
x.<!UNRESOLVED_REFERENCE!>length<!> // 'x' is unsoundly smartcasted here x.length // 'x' is unsoundly smartcasted here
} }
fun bar() { fun bar() {
@@ -19,5 +19,5 @@ fun bar() {
} }
// TODO: this testdata fixates undesired behavior (it should be an unsafe call) // TODO: this testdata fixates undesired behavior (it should be an unsafe call)
x.<!UNRESOLVED_REFERENCE!>size<!> // 'x' is unsoundly smartcasted here x.size // 'x' is unsoundly smartcasted here
} }
@@ -7,7 +7,7 @@ fun foo() {
break break
} }
x.<!UNRESOLVED_REFERENCE!>length<!> // 'x' is unsoundly smartcasted here x.length // 'x' is unsoundly smartcasted here
} }
fun bar() { fun bar() {
@@ -17,5 +17,5 @@ fun bar() {
break break
} }
x.<!UNRESOLVED_REFERENCE!>size<!> // 'x' is unsoundly smartcasted here x.size // 'x' is unsoundly smartcasted here
} }
@@ -9,5 +9,5 @@ fun foo(arg: Int?) {
} }
if (x != null) x = 42 if (x != null) x = 42
// Unsafe because of lambda // Unsafe because of lambda
x.<!UNRESOLVED_REFERENCE!>hashCode<!>() x.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
} }
@@ -5,7 +5,7 @@ public class X {
public fun fn(): Int { public fun fn(): Int {
if (y != null) if (y != null)
// With non-default getter smartcast is not possible // With non-default getter smartcast is not possible
return y.length return y.<!INAPPLICABLE_CANDIDATE!>length<!>
else else
return 0 return 0
} }
@@ -7,5 +7,5 @@ infix fun Int.bar(i: Int) = i
fun test() { fun test() {
val p = A() val p = A()
// For open value properties, smart casts should not work // For open value properties, smart casts should not work
if (p.foo is Int) p.foo bar 11 if (p.foo is Int) p.foo <!INAPPLICABLE_CANDIDATE!>bar<!> 11
} }
@@ -4,10 +4,10 @@ public class X {
public fun fn(): Int { public fun fn(): Int {
if (x != null) if (x != null)
// Smartcast is not possible for variable properties // Smartcast is not possible for variable properties
return x.length return x.<!INAPPLICABLE_CANDIDATE!>length<!>
else if (y != null) else if (y != null)
// Even if they are private // Even if they are private
return y.length return y.<!INAPPLICABLE_CANDIDATE!>length<!>
else else
return 0 return 0
} }
@@ -15,6 +15,6 @@ fun bar(): Int {
return when(ss) { return when(ss) {
"abc" -> ss "abc" -> ss
else -> "xyz" else -> "xyz"
}.length }.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
@@ -8,7 +8,7 @@ fun test1() {
if (newC != null) { if (newC != null) {
c = newC c = newC
} }
<!INAPPLICABLE_CANDIDATE!>foo<!>(c) foo(c)
} }
fun test2() { fun test2() {
@@ -19,7 +19,7 @@ fun test2() {
if (newC is String) { if (newC is String) {
c = newC c = newC
} }
<!INAPPLICABLE_CANDIDATE!>foo<!>(c) foo(c)
} }
fun test3() { fun test3() {
@@ -30,6 +30,6 @@ fun test3() {
if (newC == null) return if (newC == null) return
c = newC c = newC
<!INAPPLICABLE_CANDIDATE!>foo<!>(c) foo(c)
} }
@@ -6,5 +6,5 @@ fun foo(x: Int, f: () -> Unit, y: Int) {}
fun bar() { fun bar() {
var x: Int? var x: Int?
x = 4 x = 4
foo(x, { x = null; x.<!UNRESOLVED_REFERENCE!>hashCode<!>() }, x) foo(x, { x = null; x.<!INAPPLICABLE_CANDIDATE!>hashCode<!>() }, x)
} }
@@ -13,5 +13,5 @@ fun list(start: String) {
e = e.next() e = e.next()
} }
// e can never be null but we do not know it // e can never be null but we do not know it
e.<!INAPPLICABLE_CANDIDATE!>hashCode<!>() e.hashCode()
} }
@@ -5,7 +5,7 @@ fun foo() {
v = "abc" v = "abc"
v.length v.length
v = null v = null
v.<!UNRESOLVED_REFERENCE!>length<!> v.<!INAPPLICABLE_CANDIDATE!>length<!>
v = "abc" v = "abc"
v.length v.length
} }
@@ -6,7 +6,7 @@ fun foo() {
val y = x val y = x
x = null x = null
if (y != null) { if (y != null) {
x.<!UNRESOLVED_REFERENCE!>hashCode<!>() x.hashCode()
} }
} }
@@ -23,7 +23,7 @@ fun bar(s: String?) {
val hashCode = ss?.hashCode() val hashCode = ss?.hashCode()
ss = null ss = null
if (hashCode != null) { if (hashCode != null) {
ss.<!UNRESOLVED_REFERENCE!>hashCode<!>() ss.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
} }
} }
@@ -44,7 +44,7 @@ fun gaz(s: String?) {
x = null x = null
} }
run { run {
x.<!INAPPLICABLE_CANDIDATE!>hashCode<!>() x.hashCode()
} }
} }
} }
@@ -11,11 +11,11 @@ fun list(start: SomeObject) {
// In theory smart cast is possible here // In theory smart cast is possible here
// But in practice we have a loop with changing e // But in practice we have a loop with changing e
// ?: should we "or" entrance type info with condition type info? // ?: should we "or" entrance type info with condition type info?
if (<!UNRESOLVED_REFERENCE!>!<!>e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>()) if (!e.doSomething())
break break
// Smart cast here is still not possible // Smart cast here is still not possible
e = e.<!INAPPLICABLE_CANDIDATE!>next<!>() e = e.next()
} while (e != null) } while (e != null)
// e can be null because of next() // e can be null because of next()
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>() e.doSomething()
} }
@@ -8,5 +8,5 @@ public fun foo(pp: String?): Int {
p = null p = null
} while (!x()) } while (!x())
// Smart cast is NOT possible here // Smart cast is NOT possible here
return p.<!INAPPLICABLE_CANDIDATE!>length<!> return p.length
} }
@@ -9,8 +9,8 @@ fun list(start: SomeObject): SomeObject {
var e: SomeObject? = start var e: SomeObject? = start
for (i in 0..42) { for (i in 0..42) {
// Unsafe calls because of nullable e at the beginning // Unsafe calls because of nullable e at the beginning
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>() e.doSomething()
e = e.<!INAPPLICABLE_CANDIDATE!>next<!>() e = e.next()
} }
// Smart cast is not possible here due to next() // Smart cast is not possible here due to next()
return e return e
@@ -16,5 +16,5 @@ fun list(start: SomeObject) {
e = e.next() e = e.next()
} }
// e can be null because of next() // e can be null because of next()
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>() e.doSomething()
} }
@@ -76,5 +76,5 @@ fun test6() {
finally { finally {
a = null a = null
} }
a.<!UNRESOLVED_REFERENCE!>hashCode<!>() // a is null here a.<!INAPPLICABLE_CANDIDATE!>hashCode<!>() // a is null here
} }
@@ -4,5 +4,5 @@ fun foo() {
// It is possible in principle to provide smart cast here // It is possible in principle to provide smart cast here
v.<!INAPPLICABLE_CANDIDATE!>length<!> v.<!INAPPLICABLE_CANDIDATE!>length<!>
v = null v = null
v.<!UNRESOLVED_REFERENCE!>length<!> v.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
@@ -6,5 +6,5 @@ fun foo() {
try { try {
s = null s = null
} catch (ex: Exception) {} } catch (ex: Exception) {}
s.<!UNRESOLVED_REFERENCE!>hashCode<!>() s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
} }
@@ -12,5 +12,5 @@ fun foo() {
finally { finally {
bar() bar()
} }
s.<!UNRESOLVED_REFERENCE!>hashCode<!>() s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
} }
@@ -6,5 +6,5 @@ fun foo() {
try { try {
s = null s = null
} catch (ex: Exception) {} } catch (ex: Exception) {}
s.<!UNRESOLVED_REFERENCE!>hashCode<!>() s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
} }
@@ -2,5 +2,5 @@
fun foo(): Int { fun foo(): Int {
var i: Int? = 42 var i: Int? = 42
i = null i = null
return i + 1 return i <!INAPPLICABLE_CANDIDATE!>+<!> 1
} }
@@ -2,5 +2,5 @@
fun foo(): Int { fun foo(): Int {
var s: String? = "abc" var s: String? = "abc"
s = null s = null
return s.<!UNRESOLVED_REFERENCE!>length<!> return s.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
@@ -9,5 +9,5 @@ public fun foo(pp: String?): Int {
} }
// Smart cast is NOT possible here // Smart cast is NOT possible here
// (we could provide it but p = null makes it much harder) // (we could provide it but p = null makes it much harder)
return p.<!INAPPLICABLE_CANDIDATE!>length<!> return p.length
} }
@@ -9,5 +9,5 @@ public fun foo(pp: String?): Int {
} }
// Smart cast is NOT possible here // Smart cast is NOT possible here
// (we could provide it but p = null makes it much harder) // (we could provide it but p = null makes it much harder)
return p.<!INAPPLICABLE_CANDIDATE!>length<!> return p.length
} }
@@ -9,5 +9,5 @@ public fun foo(pp: String?): Int {
} }
// Smart cast is NOT possible here // Smart cast is NOT possible here
// (we could provide it but p = null makes it much harder) // (we could provide it but p = null makes it much harder)
return p.<!INAPPLICABLE_CANDIDATE!>length<!> return p.length
} }
@@ -13,5 +13,5 @@ fun list(start: SomeObject) {
e = e.next() e = e.next()
} }
// e can be null because of next() // e can be null because of next()
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>() e.doSomething()
} }
@@ -27,11 +27,11 @@ fun testUnsafeCaptureVarInInitializer() {
val s = when (val y = run { x = null; 32 }) { val s = when (val y = run { x = null; 32 }) {
0 -> { 0 -> {
x.<!UNRESOLVED_REFERENCE!>inc<!>() // NB smart cast should be impossible x.<!INAPPLICABLE_CANDIDATE!>inc<!>() // NB smart cast should be impossible
"0" "0"
} }
else -> "!= 0" else -> "!= 0"
} }
x.<!UNRESOLVED_REFERENCE!>inc<!>() // NB smart cast should be impossible x.<!INAPPLICABLE_CANDIDATE!>inc<!>() // NB smart cast should be impossible
} }
@@ -3,5 +3,5 @@
fun foo(y: String) { fun foo(y: String) {
var x: String? = null var x: String? = null
y.let { x = it } y.let { x = it }
x.<!INAPPLICABLE_CANDIDATE!>length<!> // Smart cast is not possible x.length // Smart cast is not possible
} }
@@ -4,6 +4,6 @@ fun foo(y: String?) {
var x: String? = null var x: String? = null
if (x != null) { if (x != null) {
y?.let { x = it } y?.let { x = it }
x.<!INAPPLICABLE_CANDIDATE!>length<!> // not-null or not-null x.length // not-null or not-null
} }
} }
@@ -15,7 +15,7 @@ fun test1(s: String?) {
catch (e: Exception) { catch (e: Exception) {
requireNotNull(s) requireNotNull(s)
} }
t2.<!UNRESOLVED_REFERENCE!>not<!>() t2.<!INAPPLICABLE_CANDIDATE!>not<!>()
s.length s.length
} }
} }
@@ -45,7 +45,7 @@ fun test3() {
s = null s = null
return return
} }
s.<!UNRESOLVED_REFERENCE!>length<!> s.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
fun test4() { fun test4() {
@@ -61,7 +61,7 @@ fun test4() {
catch (e: ExcB) { catch (e: ExcB) {
} }
s.<!UNRESOLVED_REFERENCE!>length<!> s.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
fun test5(s: String?) { fun test5(s: String?) {
@@ -16,7 +16,7 @@ fun test1(s: String?) {
catch (e: Exception) { catch (e: Exception) {
requireNotNull(s) requireNotNull(s)
} }
t2.<!UNRESOLVED_REFERENCE!>not<!>() t2.<!INAPPLICABLE_CANDIDATE!>not<!>()
s.length s.length
} }
} }
@@ -46,7 +46,7 @@ fun test3() {
s = null s = null
return return
} }
s.<!UNRESOLVED_REFERENCE!>length<!> s.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
fun test4() { fun test4() {
@@ -62,7 +62,7 @@ fun test4() {
catch (e: ExcB) { catch (e: ExcB) {
} }
s.<!UNRESOLVED_REFERENCE!>length<!> s.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
fun test5(s: String?) { fun test5(s: String?) {
@@ -13,15 +13,15 @@ fun test1() {
try { try {
x = null x = null
} catch (e: Exception) { } catch (e: Exception) {
x.<!UNRESOLVED_REFERENCE!>length<!> // smartcast shouldn't be allowed (OOME could happen after `x = null`) x.<!INAPPLICABLE_CANDIDATE!>length<!> // smartcast shouldn't be allowed (OOME could happen after `x = null`)
throw e throw e
} }
finally { finally {
// smartcast shouldn't be allowed, `x = null` could've happened // smartcast shouldn't be allowed, `x = null` could've happened
x.<!UNRESOLVED_REFERENCE!>length<!> x.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
// smartcast shouldn't be allowed, `x = null` could've happened // smartcast shouldn't be allowed, `x = null` could've happened
x.<!UNRESOLVED_REFERENCE!>length<!> x.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
// With old DFA of try/catch info about unsound smartcasts after try // With old DFA of try/catch info about unsound smartcasts after try
@@ -33,12 +33,12 @@ fun test2() {
try { try {
x = null x = null
} catch (e: Exception) { } catch (e: Exception) {
x.<!UNRESOLVED_REFERENCE!>length<!> x.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
finally { finally {
x.<!UNRESOLVED_REFERENCE!>length<!> x.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
x.<!UNRESOLVED_REFERENCE!>length<!> x.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
fun test3() { fun test3() {
@@ -49,7 +49,7 @@ fun test3() {
} catch (e: Exception) { } catch (e: Exception) {
t2 = null t2 = null
} }
t2.<!UNRESOLVED_REFERENCE!>not<!>() // wrong smartcast, NPE t2.<!INAPPLICABLE_CANDIDATE!>not<!>() // wrong smartcast, NPE
} }
} }
@@ -59,7 +59,7 @@ fun test4() {
try { try {
t2 = null t2 = null
} finally { } } finally { }
t2.<!UNRESOLVED_REFERENCE!>not<!>() // wrong smartcast, NPE t2.<!INAPPLICABLE_CANDIDATE!>not<!>() // wrong smartcast, NPE
} }
} }
@@ -80,10 +80,10 @@ fun test5() {
} }
finally { finally {
s1.length s1.length
s2.<!UNRESOLVED_REFERENCE!>length<!> s2.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
s1.length s1.length
s2.<!UNRESOLVED_REFERENCE!>length<!> s2.<!INAPPLICABLE_CANDIDATE!>length<!>
} }
fun test6(s1: String?, s2: String?) { fun test6(s1: String?, s2: String?) {
@@ -97,10 +97,10 @@ fun test6(s1: String?, s2: String?) {
return return
} }
finally { finally {
s.<!UNRESOLVED_REFERENCE!>length<!> s.<!INAPPLICABLE_CANDIDATE!>length<!>
requireNotNull(s2) requireNotNull(s2)
} }
s.<!UNRESOLVED_REFERENCE!>length<!> s.<!INAPPLICABLE_CANDIDATE!>length<!>
s1.length s1.length
s2.length s2.length
} }

Some files were not shown because too many files have changed in this diff Show More