[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 {
val typesFromSmartCast = dataFlowAnalyzer.getTypeUsingSmartcastInfo(qualifiedAccessExpression) ?: return qualifiedAccessExpression
val allTypes = typesFromSmartCast.toMutableList().also {
val allTypes = typesFromSmartCast.also {
it += qualifiedAccessExpression.resultType.coneTypeUnsafe<ConeKotlinType>()
}
val intersectedType = ConeTypeIntersector.intersectTypes(inferenceComponents.ctx as ConeInferenceContext, allTypes)
@@ -5,6 +5,7 @@
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.ConeConditionalEffectDeclaration
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.resolve.ImplicitReceiverStackImpl
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.defaultType
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.createArgumentsMapping
@@ -29,7 +30,6 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@UseExperimental(DfaInternals::class)
abstract class FirDataFlowAnalyzer<FLOW : Flow>(
@@ -61,7 +61,11 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
}
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 graphBuilder = ControlFlowGraphBuilder()
protected val variableStorage = VariableStorage()
protected val variableStorage: VariableStorage = VariableStorage(components.session)
private val flowOnNodes = mutableMapOf<CFGNode<*>, FLOW>()
private val variablesForWhenConditions = mutableMapOf<WhenBranchConditionExitNode, DataFlowVariable>()
@@ -83,14 +87,21 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
// ----------------------------------- 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)
* If there is no useful information there is no data flow variable also
*/
val symbol: AbstractFirBasedSymbol<*> = qualifiedAccessExpression.symbol ?: return null
val variable = variableStorage[symbol, qualifiedAccessExpression] ?: return null
return graphBuilder.lastNode.flow.getTypeStatement(variable)?.exactType
val flow = graphBuilder.lastNode.flow
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> {
@@ -164,7 +175,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).mergeIncomingFlow()
if (typeOperatorCall.operation !in FirOperation.TYPES) return
val type = typeOperatorCall.conversionTypeRef.coneTypeUnsafe<ConeKotlinType>()
val operandVariable = variableStorage.getOrCreateVariable(typeOperatorCall.argument)
val operandVariable = variableStorage.getOrCreateVariable(node.previousFlow, typeOperatorCall.argument)
val flow = node.flow
when (val operation = typeOperatorCall.operation) {
@@ -251,7 +262,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val isEq = operation.isEq()
val expressionVariable = variableStorage.createSyntheticVariable(node.fir)
val flow = node.flow
val operandVariable = variableStorage.getOrCreateVariable(operand)
val operandVariable = variableStorage.getOrCreateVariable(node.previousFlow, operand)
// expression == const -> expression != null
flow.addImplication((expressionVariable eq isEq) implies (operandVariable notEq null))
if (operandVariable is RealVariable) {
@@ -296,7 +307,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun processEqNull(node: OperatorCallNode, operand: FirExpression, operation: FirOperation) {
val flow = node.flow
val expressionVariable = variableStorage.createSyntheticVariable(node.fir)
val operandVariable = variableStorage.getOrCreateVariable(operand)
val operandVariable = variableStorage.getOrCreateVariable(node.previousFlow, operand)
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.
val node = graphBuilder.exitCheckNotNullCall(checkNotNullCall).mergeIncomingFlow()
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)
logicSystem.approveStatementsInsideFlow(
node.flow,
@@ -370,10 +381,11 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val (conditionExitNode, branchEnterNode) = graphBuilder.exitWhenBranchCondition(whenBranch)
conditionExitNode.mergeIncomingFlow()
val conditionVariable = variableStorage.getOrCreateVariable(whenBranch.condition)
val conditionExitFlow = conditionExitNode.flow
val conditionVariable = variableStorage.getOrCreateVariable(conditionExitFlow, whenBranch.condition)
variablesForWhenConditions[conditionExitNode] = conditionVariable
branchEnterNode.flow = logicSystem.approveStatementsInsideFlow(
conditionExitNode.flow,
conditionExitFlow,
conditionVariable eq true,
shouldForkFlow = true,
shouldRemoveSynthetics = false
@@ -387,18 +399,19 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
fun exitWhenExpression(whenExpression: FirWhenExpression) {
val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression)
if (syntheticElseNode != null) {
syntheticElseNode.mergeIncomingFlow()
val previousConditionExitNode = syntheticElseNode.firstPreviousNode as? WhenBranchConditionExitNode
// 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
if (previousConditionExitNode != null) {
val conditionVariable = variablesForWhenConditions.remove(previousConditionExitNode)!!
syntheticElseNode.flow = logicSystem.approveStatementsInsideFlow(
syntheticElseNode.flow,
previousConditionExitNode.flow,
conditionVariable eq false,
shouldForkFlow = true,
shouldRemoveSynthetics = true
)
} else {
syntheticElseNode.mergeIncomingFlow()
}
}
whenExitNode.mergeIncomingFlow(updateReceivers = true)
@@ -409,7 +422,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun exitCommonLoop(exitNode: LoopExitNode) {
val singlePreviousNode = exitNode.previousNodes.singleOrNull { !it.isDead }
if (singlePreviousNode is LoopConditionExitNode) {
val variable = variableStorage.getOrCreateVariable(singlePreviousNode.fir)
val variable = variableStorage.getOrCreateVariable(exitNode.previousFlow, singlePreviousNode.fir)
exitNode.flow = logicSystem.approveStatementsInsideFlow(
exitNode.flow,
variable eq false,
@@ -428,15 +441,15 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
fun exitWhileLoopCondition(loop: FirLoop) {
val (loopConditionExitNode, loopBlockEnterNode) = graphBuilder.exitWhileLoopCondition(loop)
loopConditionExitNode.mergeIncomingFlow()
loopBlockEnterNode.mergeIncomingFlow()
variableStorage[loop.condition]?.let { conditionVariable ->
loopBlockEnterNode.flow = logicSystem.approveStatementsInsideFlow(
loopBlockEnterNode.flow,
val conditionExitFlow = loopConditionExitNode.flow
loopBlockEnterNode.flow = variableStorage.getVariable(loop.condition, conditionExitFlow)?.let { conditionVariable ->
logicSystem.approveStatementsInsideFlow(
conditionExitFlow,
conditionVariable eq true,
shouldForkFlow = false,
shouldForkFlow = true,
shouldRemoveSynthetics = false
)
}
} ?: logicSystem.forkFlow(conditionExitFlow)
}
fun exitWhileLoop(loop: FirLoop) {
@@ -526,7 +539,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
?.withNullability(ConeNullability.NOT_NULL)
?: return@let
val variable = variableStorage.getOrCreateVariable(receiver)
val variable = variableStorage.getOrCreateVariable(flow, receiver)
if (variable is RealVariable) {
if (shouldFork) {
flow = logicSystem.forkFlow(flow)
@@ -563,11 +576,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun exitSafeCall(qualifiedAccess: FirQualifiedAccess) {
if (!qualifiedAccess.safe) return
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) {
// There is some bug with invokes. See KT-36014
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))
if (receiverVariable.isReal()) {
@@ -582,12 +596,13 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val argumentsMapping = createArgumentsMapping(functionCall) ?: return
contractDescriptionVisitingMode = true
graphBuilder.enterContract(functionCall).mergeIncomingFlow()
val functionCallVariable = variableStorage.getOrCreateVariable(functionCall)
val lastFlow = graphBuilder.lastNode.flow
val functionCallVariable = variableStorage.getOrCreateVariable(lastFlow, functionCall)
for (conditionalEffect in conditionalEffects) {
val fir = conditionalEffect.buildContractFir(argumentsMapping) ?: continue
val effect = conditionalEffect.effect as? ConeReturnsEffectDeclaration ?: continue
fir.transformSingle(components.transformer, ResolutionMode.ContextDependent)
val argumentVariable = variableStorage.getOrCreateVariable(fir)
val argumentVariable = variableStorage.getOrCreateVariable(lastFlow, fir)
val lastNode = graphBuilder.lastNode
when (val value = effect.value) {
ConeConstantReference.WILDCARD -> {
@@ -644,21 +659,21 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
exitVariableInitialization(node, assignment.rValue, property, assignment)
}
private fun exitVariableInitialization(node: CFGNode<*>, initializer: FirExpression, variable: FirProperty, assignment: FirVariableAssignment?) {
var propertyVariable = variableStorage.getOrCreateRealVariable(variable.symbol, assignment ?: variable)
val isVariableDeclaration = assignment == null
if (!isVariableDeclaration) {
// Don't remove statements for variable under alias
if (propertyVariable.identifier.symbol == variable.symbol) {
node.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)
}
private fun exitVariableInitialization(node: CFGNode<*>, initializer: FirExpression, property: FirProperty, assignment: FirVariableAssignment?) {
val flow = node.flow
val propertyVariable = variableStorage.getOrCreateRealVariableWithoutUnwrappingAlias(flow, property.symbol, assignment ?: property)
val isAssignment = assignment != null
if (isAssignment) {
logicSystem.removeLocalVariableAlias(flow, propertyVariable)
flow.removeAllAboutVariable(propertyVariable)
}
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:
*
@@ -668,19 +683,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
* x.length
* }
*/
logicSystem.replaceVariableFromConditionInStatements(node.flow, initializerVariable, propertyVariable)
return
logicSystem.replaceVariableFromConditionInStatements(flow, initializerVariable, propertyVariable)
}
variableStorage.getOrCreateRealVariable(initializer.symbol, initializer)?.let { initializerVariable ->
if (initializerVariable.isStable) {
variableStorage.attachSymbolToVariable(variable.symbol, initializerVariable)
}
node.flow.addImplication((propertyVariable notEq null) implies (initializerVariable notEq null))
}
if (!isVariableDeclaration) {
node.flow.addTypeStatement(propertyVariable typeEq initializer.typeRef.coneTypeUnsafe<ConeKotlinType>())
if (isAssignment) {
if (initializer is FirConstExpression<*> && initializer.kind == FirConstKind.Null) return
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) {
val parentFlow = leftNode.firstPreviousNode.flow
leftNode.flow = logicSystem.forkFlow(parentFlow)
val leftOperandVariable = variableStorage.getOrCreateVariable(leftNode.firstPreviousNode.fir)
val leftOperandVariable = variableStorage.getOrCreateVariable(parentFlow, leftNode.firstPreviousNode.fir)
rightNode.flow = logicSystem.approveStatementsInsideFlow(
parentFlow,
leftOperandVariable eq isAnd,
@@ -750,9 +758,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
* But since conditions with const are rare it can be delayed
*/
val leftVariable = variableStorage.getOrCreateVariable(binaryLogicExpression.leftOperand)
val rightVariable = variableStorage.getOrCreateVariable(binaryLogicExpression.rightOperand)
val operatorVariable = variableStorage.getOrCreateVariable(binaryLogicExpression)
val leftVariable = variableStorage.getOrCreateVariable(flow, binaryLogicExpression.leftOperand)
val rightVariable = variableStorage.getOrCreateVariable(flow, binaryLogicExpression.rightOperand)
val operatorVariable = variableStorage.getOrCreateVariable(flow, binaryLogicExpression)
val (conditionalFromLeft, conditionalFromRight, approvedFromRight) = logicSystem.collectInfoForBooleanOperator(
flowFromLeft,
@@ -792,8 +800,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) {
val booleanExpressionVariable = variableStorage.getOrCreateVariable(node.firstPreviousNode.fir)
val variable = variableStorage.getOrCreateVariable(functionCall)
val previousFlow = node.previousFlow
val booleanExpressionVariable = variableStorage.getOrCreateVariable(previousFlow, node.firstPreviousNode.fir)
val variable = variableStorage.getOrCreateVariable(previousFlow, functionCall)
logicSystem.replaceVariableFromConditionInStatements(
node.flow,
booleanExpressionVariable,
@@ -856,4 +865,17 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
if (variable == null) return
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
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.commonSuperTypeOrNull
@@ -14,6 +15,13 @@ abstract class Flow {
abstract fun getImplications(variable: DataFlowVariable): Collection<Implication>
abstract fun getVariablesInTypeStatements(): Collection<RealVariable>
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) {
@@ -45,6 +53,9 @@ abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceCont
shouldRemoveSynthetics: Boolean,
): 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>
// ------------------------------- Callbacks for updating implicit receiver stack -------------------------------
@@ -7,8 +7,11 @@ package org.jetbrains.kotlin.fir.resolve.dfa
import com.google.common.collect.ArrayListMultimap
import kotlinx.collections.immutable.*
import org.jetbrains.kotlin.fir.declarations.FirVariable
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.coneTypeUnsafe
import java.util.*
import kotlin.NoSuchElementException
@@ -42,12 +45,26 @@ class PersistentFlow : Flow {
var logicStatements: PersistentImplications
val level: Int
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) {
this.previousFlow = previousFlow
approvedTypeStatements = previousFlow.approvedTypeStatements
logicStatements = previousFlow.logicStatements
level = previousFlow.level + 1
directAliasMap = previousFlow.directAliasMap
backwardsAliasMap = previousFlow.backwardsAliasMap
}
constructor() {
@@ -55,6 +72,9 @@ class PersistentFlow : Flow {
approvedTypeStatements = persistentHashMapOf()
logicStatements = persistentHashMapOf()
level = 1
directAliasMap = persistentMapOf()
backwardsAliasMap = persistentMapOf()
}
override fun getTypeStatement(variable: RealVariable): TypeStatement? {
@@ -90,8 +110,22 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
override fun joinFlow(flows: Collection<PersistentFlow>): PersistentFlow {
if (flows.isEmpty()) return createEmptyFlow()
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 commonVariables = flows.map { it.diffVariablesIterable(commonFlow).toList() }
val commonVariables = flows.map {
it.diffVariablesIterable(commonFlow, aliasedVariablesThatDontChangeAlias.keys).toList()
}
.intersectSets()
.takeIf { it.isNotEmpty() }
?: return commonFlow
@@ -105,19 +139,48 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
}
}
for ((alias, underlyingVariable) in aliasedVariablesThatDontChangeAlias) {
addLocalVariableAlias(commonFlow,alias, underlyingVariable)
}
updateAllReceivers(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 {
var flow = this
val result = MutableTypeStatement(variable)
while (flow != parentFlow) {
flow.approvedTypeStatementsDiff[variable]?.let {
result += it
val variableUnderAlias = directAliasMap[variable]
if (variableUnderAlias == null) {
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
}
@@ -126,10 +189,19 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
* This is an iterable over real variable that has known facts in flow range
* 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) {
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
import org.jetbrains.kotlin.descriptors.Modality
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.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
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.impl.*
import org.jetbrains.kotlin.fir.types.*
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@UseExperimental(DfaInternals::class)
class VariableStorage {
class VariableStorage(val session: FirSession) {
private var counter = 1
private val realVariables: MutableMap<Identifier, RealVariable> = 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 identifier = getIdentifierBySymbol(symbol, realFir)
return realVariables.getOrPut(identifier) { createRealVariableInternal(identifier, realFir) }
val identifier = getIdentifierBySymbol(flow, symbol, 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) {
@@ -31,23 +48,22 @@ class VariableStorage {
}
private fun getIdentifierBySymbol(
flow: Flow,
symbol: AbstractFirBasedSymbol<*>,
fir: FirElement,
): Identifier {
return localVariableAliases[symbol] ?: run {
val expression = fir as? FirQualifiedAccess
Identifier(
symbol,
expression?.dispatchReceiver?.takeIf { it != FirNoReceiverExpression }?.let(this::getOrCreateVariable),
expression?.extensionReceiver?.takeIf { it != FirNoReceiverExpression }?.let(this::getOrCreateVariable)
)
}
val expression = fir as? FirQualifiedAccess
return Identifier(
symbol,
expression?.dispatchReceiver?.takeIf { it != FirNoReceiverExpression }?.let { getOrCreateVariable(flow, it) },
expression?.extensionReceiver?.takeIf { it != FirNoReceiverExpression }?.let { getOrCreateVariable(flow, it) }
)
}
/**
* [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 isThisReference: Boolean
val expression: FirQualifiedAccess? = when (originalFir) {
@@ -66,57 +82,62 @@ class VariableStorage {
isThisReference = false
}
val receiverVariable = receiver?.let { getOrCreateVariable(it) }
return RealVariable(identifier, isThisReference, receiverVariable, counter++)
val receiverVariable = receiver?.let { getOrCreateVariable(flow, it) }
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")
fun getOrCreateRealVariable(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement): RealVariable? =
symbol?.let { getOrCreateRealVariable(it, fir) }
fun getOrCreateRealVariable(flow: Flow, symbol: AbstractFirBasedSymbol<*>?, fir: FirElement): RealVariable? =
symbol.takeIf { it.isStable(fir) }?.let { getOrCreateRealVariable(flow, it, fir) }
fun createSyntheticVariable(fir: FirElement): SyntheticVariable =
SyntheticVariable(fir, counter++).also { syntheticVariables[fir] = it }
fun getOrCreateVariable(fir: FirElement): DataFlowVariable {
fun getOrCreateVariable(flow: Flow, fir: FirElement): DataFlowVariable {
val realFir = fir.unwrapElement()
return when (val symbol = realFir.symbol) {
null -> syntheticVariables[realFir] ?: createSyntheticVariable(realFir)
else -> getOrCreateRealVariable(symbol, realFir)
val symbol = realFir.symbol
return if (symbol.isStable(realFir)) {
getOrCreateRealVariable(flow, symbol!!, realFir)
} else {
syntheticVariables[realFir] ?: createSyntheticVariable(realFir)
}
}
/**
* Also removes existing real variable for [varSymbol] if it exists
*/
fun attachSymbolToVariable(varSymbol: AbstractFirBasedSymbol<*>, targetVariable: RealVariable) {
localVariableAliases[varSymbol] = targetVariable.identifier
realVariables.remove(Identifier(varSymbol, null, null))
fun getRealVariableWithoutUnwrappingAlias(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable? {
val realFir = fir.unwrapElement()
return symbol.takeIf { it.isStable(realFir) }?.let {
realVariables[getIdentifierBySymbol(flow, it, realFir.unwrapElement())]
}
}
operator fun get(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement): RealVariable? {
return symbol?.let { realVariables[getIdentifierBySymbol(it, fir.unwrapElement())] }
fun getRealVariable(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable? {
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 symbol = realFir.symbol
return if (symbol != null) {
get(symbol, realFir)
return if (symbol.isStable(fir)) {
getRealVariable(symbol, realFir, flow)
} else {
syntheticVariables[realFir]
getSyntheticVariable(fir)
}
}
fun removeRealVariable(symbol: AbstractFirBasedSymbol<*>) {
// TODO: this shit fails
// assert(!localVariableAliases.containsValue(symbol))
realVariables.remove(Identifier(symbol, null, null))
}
fun unboundPossiblyAliasedVariable(symbol: AbstractFirBasedSymbol<*>) {
localVariableAliases.remove(symbol)
}
fun removeSyntheticVariable(variable: DataFlowVariable) {
if (variable !is SyntheticVariable) return
syntheticVariables.remove(variable.fir)
@@ -126,6 +147,44 @@ class VariableStorage {
counter = 0
realVariables.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 ExitNodeMarker
@@ -41,6 +41,7 @@ class RealVariable(
val identifier: Identifier,
val isThisReference: Boolean,
val explicitReceiverVariable: DataFlowVariable?,
val originalType: ConeKotlinType,
variableIndexForDebug: Int
) : DataFlowVariable(variableIndexForDebug) {
override val isStable: Boolean by lazy {
@@ -51,6 +52,8 @@ class RealVariable(
property.isLocal -> true
property.isVar -> false
property.modality != Modality.FINAL -> false
property.receiverTypeRef != null -> false
property.getter != null -> false
// TODO: getters, delegates
else -> true
}
@@ -5,18 +5,26 @@
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.FirSession
import org.jetbrains.kotlin.fir.FirSymbolOwner
import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference
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.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.FirThisReference
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.impl.FirAccessorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeFlexibleType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
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
internal fun FirOperation.invert(): FirOperation = when (this) {
FirOperation.EQ -> FirOperation.NOT_EQ
@@ -119,3 +141,8 @@ internal val FirResolvable.symbol: AbstractFirBasedSymbol<*>?
is FirNamedReferenceWithCandidate -> reference.candidateSymbol
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|"];
173 [label="Access variable R|<local>/q|"];
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|"];
177 [label="Access variable R|/Q.data|"];
178 [label="Access variable R|/MyData.s|"];
179 [label="Function call: R|<local>/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|()"];
178 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
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"];
}
181 [label="Exit when branch result"];
@@ -596,11 +596,11 @@ digraph nullability_kt {
216 [label="Access variable R|/Q.data|"];
217 [label="Access variable R|<local>/q|"];
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|"];
221 [label="Access variable R|/Q.data|"];
222 [label="Access variable R|/MyData.s|"];
223 [label="Function call: R|<local>/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|()"];
222 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
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];
}
@@ -1288,11 +1288,11 @@ digraph nullability_kt {
486 [label="Access variable R|/QImplWithCustomGetter.data|"];
487 [label="Access variable R|<local>/q|"];
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|"];
491 [label="Access variable R|/QImplWithCustomGetter.data|"];
492 [label="Access variable R|/MyData.s|"];
493 [label="Function call: R|<local>/q|.R|/QImplWithCustomGetter.data|.R|/MyData.s|.R|kotlin/Int.inc|()"];
492 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
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"];
}
495 [label="Exit when branch result"];
@@ -1365,11 +1365,11 @@ digraph nullability_kt {
518 [label="Access variable R|/QImplMutable.data|"];
519 [label="Access variable R|<local>/q|"];
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|"];
523 [label="Access variable R|/QImplMutable.data|"];
524 [label="Access variable R|/MyData.s|"];
525 [label="Function call: R|<local>/q|.R|/QImplMutable.data|.R|/MyData.s|.R|kotlin/Int.inc|()"];
524 [label="Access variable <Inapplicable(WRONG_RECEIVER): [/MyData.s]>#"];
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"];
}
527 [label="Exit when branch result"];
@@ -62,23 +62,21 @@ fun test_4(x: A?) {
x.foo()
}
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
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.
if (q?.data?.s?.inc() != null) {
q.data // good
q.data.s // should be bad
q.data.s.inc() // should be bad
q.data.<!INAPPLICABLE_CANDIDATE!>s<!> // 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?) {
// `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 // good
q.data.s // should be bad
q.data.s.inc() // should be bad
q.data.<!INAPPLICABLE_CANDIDATE!>s<!> // should be bad
q.data.<!INAPPLICABLE_CANDIDATE!>s<!>.<!AMBIGUITY!>inc<!>() // should be bad
}
fun test_7(q: Q?) {
@@ -139,7 +137,6 @@ fun test_10(a: Int?, b: Int?) {
b.<!AMBIGUITY!>inc<!>()
}
// TODO: Fix this -- see comment in FirDataFlowAnalyzer.getRealVariablesForSafeCallChain()
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.
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?) {
// `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) {
q.data // good
q.data.s // should be bad
q.data.s.inc() // should be bad
q.data.<!INAPPLICABLE_CANDIDATE!>s<!> // 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?) {
// `q.data` is a property that is mutable, so we can NOT smartcast it to non-nullable MyData.
if (q?.data?.s?.inc() != null) {
q.data // good
q.data.s // should be bad
q.data.s.inc() // should be bad
q.data.<!INAPPLICABLE_CANDIDATE!>s<!> // should be bad
q.data.<!INAPPLICABLE_CANDIDATE!>s<!>.<!AMBIGUITY!>inc<!>() // should be bad
}
}
@@ -110,8 +110,8 @@ FILE: nullability.kt
when () {
!=(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|/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]>#
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|/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]>#
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| {
when () {
@@ -232,8 +232,8 @@ FILE: nullability.kt
when () {
!=(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|/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]>#
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 () {
!=(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|/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]>#
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)"];
36 [label="Assignmenet: 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];
}
@@ -17,5 +17,5 @@ fun test_3() {
x = ""
x.length
x = null
x.<!UNRESOLVED_REFERENCE!>length<!>
x.<!INAPPLICABLE_CANDIDATE!>length<!>
}
@@ -19,5 +19,5 @@ FILE: smartcastAfterReassignment.kt
R|<local>/x| = String()
R|<local>/x|.R|kotlin/String.length|
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");
}
@TestMetadata("noBoundSmartcastAfterContinue.kt")
public void testNoBoundSmartcastAfterContinue() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/problems/noBoundSmartcastAfterContinue.kt");
}
@TestMetadata("propertyFromJavaPlusAssign.kt")
public void testPropertyFromJavaPlusAssign() throws Exception {
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");
}
@TestMetadata("boundSmartcastsInBranches.kt")
public void testBoundSmartcastsInBranches() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcastsInBranches.kt");
}
@TestMetadata("casts.kt")
public void testCasts() throws Exception {
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");
}
@TestMetadata("overridenOpenVal.kt")
public void testOverridenOpenVal() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/overridenOpenVal.kt");
}
@TestMetadata("returns.kt")
public void testReturns() throws Exception {
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");
}
@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")
public void testWhen() throws Exception {
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");
}
@TestMetadata("noBoundSmartcastAfterContinue.kt")
public void testNoBoundSmartcastAfterContinue() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/problems/noBoundSmartcastAfterContinue.kt");
}
@TestMetadata("propertyFromJavaPlusAssign.kt")
public void testPropertyFromJavaPlusAssign() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/problems/propertyFromJavaPlusAssign.kt");
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A {
open fun foo(): Any = "A"
}
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A<T, U> {
fun foo(t: T, u: U) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
var result = ""
interface D1 {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface D1 {
fun foo(): Any
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A(val value: String) {
fun component1() = value
}
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A<T> {
fun foo(t: T, u: Int) = "A"
}
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
var result = ""
interface Base
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T> {
open fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T, U, V> {
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 fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T> {
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 fun foo(): T = t
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A<T> {
fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T : Number> {
open fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
abstract class A<T> {
abstract fun foo(t: T): String
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T : U, U> {
open fun foo(t: T, u: U) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T> {
open fun <U> foo(t: T, u: U) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T> {
open fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
open class A<T : Number> {
open fun foo(t: T) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A {
fun foo(): Any = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A<T> {
fun foo(t: T, u: Int) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A<T> {
fun foo(t: T, u: Int) = "A"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
interface A<T> {
fun foo(t: T) = "A"
}
@@ -1,3 +1,4 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// FILE: Test.java
@@ -6,10 +6,10 @@ class A() {
fun f(): Unit {
var x: Int? = 1
x = null
x + 1
x plus 1
x <!INAPPLICABLE_CANDIDATE!>+<!> 1
x <!INAPPLICABLE_CANDIDATE!>plus<!> 1
x < 1
x += 1
<!UNRESOLVED_REFERENCE!>x += 1<!>
x == 1
x != 1
@@ -22,8 +22,8 @@ fun f(): Unit {
x === 1
x !== 1
x..2
x in 1..2
x<!INAPPLICABLE_CANDIDATE!>..<!>2
x <!INAPPLICABLE_CANDIDATE!>in<!> 1..2
val y : Boolean? = true
false || y
@@ -11,7 +11,7 @@ fun foo() {
while (y != null) {
bar(y)
}
bar(y)
<!INAPPLICABLE_CANDIDATE!>bar<!>(y)
val z: Int? = null
while (z == null) {
@@ -22,5 +22,5 @@ fun whileWithNoCondition(x: Int?) {
while (<!SYNTAX!><!>) {
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>) {
val y: A<CharSequence, String> = x
y.foo(cs)
val s: String = y.foo(cs, ls)
y.<!INAPPLICABLE_CANDIDATE!>foo<!>(cs)
val s: String = y.<!INAPPLICABLE_CANDIDATE!>foo<!>(cs, ls)
val ls2: List<String> = y.bar()
}
@@ -9,7 +9,7 @@ class CtxImpl : Ctx {
open class Test(open val ctx: Ctx) {
fun test() {
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) {
if (a != null) {
a.foo(b)
a.<!INAPPLICABLE_CANDIDATE!>foo<!>(b)
}
}
@@ -2,8 +2,8 @@
fun foo(): String {
var s: String?
s = null
s?.<!UNRESOLVED_REFERENCE!>length<!>
s.<!UNRESOLVED_REFERENCE!>length<!>
s?.length
s.<!INAPPLICABLE_CANDIDATE!>length<!>
if (s == null) return s!!
var t: String? = "y"
if (t == null) t = "x"
@@ -41,7 +41,7 @@ fun f(a: SomeClass?) {
if (aa as? SomeSubClass != null) {
aa = null
// 'aa' cannot be cast to SomeSubClass
aa.<!UNRESOLVED_REFERENCE!>hashCode<!>()
aa.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
aa.<!UNRESOLVED_REFERENCE!>foo<!>
(aa as? SomeSubClass).<!INAPPLICABLE_CANDIDATE!>foo<!>
(aa as SomeSubClass).foo
@@ -50,7 +50,7 @@ fun f(a: SomeClass?) {
aa = null
if (b != null) {
// 'aa' cannot be cast to SomeSubClass
aa.<!UNRESOLVED_REFERENCE!>hashCode<!>()
aa.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
aa.<!UNRESOLVED_REFERENCE!>foo<!>
(aa as? SomeSubClass).<!INAPPLICABLE_CANDIDATE!>foo<!>
(aa as SomeSubClass).foo
@@ -28,11 +28,11 @@ fun test() {
x.foo().checkType { _<CharSequence?>() }
if (x is B && x is C) {
x.foo().checkType { <!INAPPLICABLE_CANDIDATE!>_<!><CharSequence?>() }
x.foo().checkType { _<CharSequence?>() }
x.baz("")
x.baz(1).checkType { <!INAPPLICABLE_CANDIDATE!>_<!><Unit>() }
x.baz(1, 2)
x.baz(1).checkType { _<Unit>() }
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
}
// 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
}
// 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)
x.<!UNRESOLVED_REFERENCE!>length<!> // 'x' is unsoundly smartcasted here
x.length // 'x' is unsoundly smartcasted here
}
fun bar() {
@@ -19,5 +19,5 @@ fun bar() {
}
// 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
}
x.<!UNRESOLVED_REFERENCE!>length<!> // 'x' is unsoundly smartcasted here
x.length // 'x' is unsoundly smartcasted here
}
fun bar() {
@@ -17,5 +17,5 @@ fun bar() {
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
// Unsafe because of lambda
x.<!UNRESOLVED_REFERENCE!>hashCode<!>()
x.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
}
@@ -5,7 +5,7 @@ public class X {
public fun fn(): Int {
if (y != null)
// With non-default getter smartcast is not possible
return y.length
return y.<!INAPPLICABLE_CANDIDATE!>length<!>
else
return 0
}
@@ -7,5 +7,5 @@ infix fun Int.bar(i: Int) = i
fun test() {
val p = A()
// 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 {
if (x != null)
// Smartcast is not possible for variable properties
return x.length
return x.<!INAPPLICABLE_CANDIDATE!>length<!>
else if (y != null)
// Even if they are private
return y.length
return y.<!INAPPLICABLE_CANDIDATE!>length<!>
else
return 0
}
@@ -15,6 +15,6 @@ fun bar(): Int {
return when(ss) {
"abc" -> ss
else -> "xyz"
}.length
}.<!INAPPLICABLE_CANDIDATE!>length<!>
}
@@ -8,7 +8,7 @@ fun test1() {
if (newC != null) {
c = newC
}
<!INAPPLICABLE_CANDIDATE!>foo<!>(c)
foo(c)
}
fun test2() {
@@ -19,7 +19,7 @@ fun test2() {
if (newC is String) {
c = newC
}
<!INAPPLICABLE_CANDIDATE!>foo<!>(c)
foo(c)
}
fun test3() {
@@ -30,6 +30,6 @@ fun test3() {
if (newC == null) return
c = newC
<!INAPPLICABLE_CANDIDATE!>foo<!>(c)
foo(c)
}
@@ -6,5 +6,5 @@ fun foo(x: Int, f: () -> Unit, y: Int) {}
fun bar() {
var x: Int?
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 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.length
v = null
v.<!UNRESOLVED_REFERENCE!>length<!>
v.<!INAPPLICABLE_CANDIDATE!>length<!>
v = "abc"
v.length
}
@@ -6,7 +6,7 @@ fun foo() {
val y = x
x = null
if (y != null) {
x.<!UNRESOLVED_REFERENCE!>hashCode<!>()
x.hashCode()
}
}
@@ -23,7 +23,7 @@ fun bar(s: String?) {
val hashCode = ss?.hashCode()
ss = null
if (hashCode != null) {
ss.<!UNRESOLVED_REFERENCE!>hashCode<!>()
ss.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
}
}
@@ -44,7 +44,7 @@ fun gaz(s: String?) {
x = null
}
run {
x.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
x.hashCode()
}
}
}
@@ -11,11 +11,11 @@ fun list(start: SomeObject) {
// In theory smart cast is possible here
// But in practice we have a loop with changing e
// ?: should we "or" entrance type info with condition type info?
if (<!UNRESOLVED_REFERENCE!>!<!>e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>())
if (!e.doSomething())
break
// Smart cast here is still not possible
e = e.<!INAPPLICABLE_CANDIDATE!>next<!>()
e = e.next()
} while (e != null)
// 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
} while (!x())
// 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
for (i in 0..42) {
// Unsafe calls because of nullable e at the beginning
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>()
e = e.<!INAPPLICABLE_CANDIDATE!>next<!>()
e.doSomething()
e = e.next()
}
// Smart cast is not possible here due to next()
return e
@@ -16,5 +16,5 @@ fun list(start: SomeObject) {
e = e.next()
}
// e can be null because of next()
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>()
e.doSomething()
}
@@ -76,5 +76,5 @@ fun test6() {
finally {
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
v.<!INAPPLICABLE_CANDIDATE!>length<!>
v = null
v.<!UNRESOLVED_REFERENCE!>length<!>
v.<!INAPPLICABLE_CANDIDATE!>length<!>
}
@@ -6,5 +6,5 @@ fun foo() {
try {
s = null
} catch (ex: Exception) {}
s.<!UNRESOLVED_REFERENCE!>hashCode<!>()
s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
}
@@ -12,5 +12,5 @@ fun foo() {
finally {
bar()
}
s.<!UNRESOLVED_REFERENCE!>hashCode<!>()
s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
}
@@ -6,5 +6,5 @@ fun foo() {
try {
s = null
} catch (ex: Exception) {}
s.<!UNRESOLVED_REFERENCE!>hashCode<!>()
s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
}
@@ -2,5 +2,5 @@
fun foo(): Int {
var i: Int? = 42
i = null
return i + 1
return i <!INAPPLICABLE_CANDIDATE!>+<!> 1
}
@@ -2,5 +2,5 @@
fun foo(): Int {
var s: String? = "abc"
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
// (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
// (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
// (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 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 }) {
0 -> {
x.<!UNRESOLVED_REFERENCE!>inc<!>() // NB smart cast should be impossible
x.<!INAPPLICABLE_CANDIDATE!>inc<!>() // NB smart cast should be impossible
"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) {
var x: String? = null
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
if (x != null) {
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) {
requireNotNull(s)
}
t2.<!UNRESOLVED_REFERENCE!>not<!>()
t2.<!INAPPLICABLE_CANDIDATE!>not<!>()
s.length
}
}
@@ -45,7 +45,7 @@ fun test3() {
s = null
return
}
s.<!UNRESOLVED_REFERENCE!>length<!>
s.<!INAPPLICABLE_CANDIDATE!>length<!>
}
fun test4() {
@@ -61,7 +61,7 @@ fun test4() {
catch (e: ExcB) {
}
s.<!UNRESOLVED_REFERENCE!>length<!>
s.<!INAPPLICABLE_CANDIDATE!>length<!>
}
fun test5(s: String?) {
@@ -16,7 +16,7 @@ fun test1(s: String?) {
catch (e: Exception) {
requireNotNull(s)
}
t2.<!UNRESOLVED_REFERENCE!>not<!>()
t2.<!INAPPLICABLE_CANDIDATE!>not<!>()
s.length
}
}
@@ -46,7 +46,7 @@ fun test3() {
s = null
return
}
s.<!UNRESOLVED_REFERENCE!>length<!>
s.<!INAPPLICABLE_CANDIDATE!>length<!>
}
fun test4() {
@@ -62,7 +62,7 @@ fun test4() {
catch (e: ExcB) {
}
s.<!UNRESOLVED_REFERENCE!>length<!>
s.<!INAPPLICABLE_CANDIDATE!>length<!>
}
fun test5(s: String?) {
@@ -13,15 +13,15 @@ fun test1() {
try {
x = null
} 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
}
finally {
// 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
x.<!UNRESOLVED_REFERENCE!>length<!>
x.<!INAPPLICABLE_CANDIDATE!>length<!>
}
// With old DFA of try/catch info about unsound smartcasts after try
@@ -33,12 +33,12 @@ fun test2() {
try {
x = null
} catch (e: Exception) {
x.<!UNRESOLVED_REFERENCE!>length<!>
x.<!INAPPLICABLE_CANDIDATE!>length<!>
}
finally {
x.<!UNRESOLVED_REFERENCE!>length<!>
x.<!INAPPLICABLE_CANDIDATE!>length<!>
}
x.<!UNRESOLVED_REFERENCE!>length<!>
x.<!INAPPLICABLE_CANDIDATE!>length<!>
}
fun test3() {
@@ -49,7 +49,7 @@ fun test3() {
} catch (e: Exception) {
t2 = null
}
t2.<!UNRESOLVED_REFERENCE!>not<!>() // wrong smartcast, NPE
t2.<!INAPPLICABLE_CANDIDATE!>not<!>() // wrong smartcast, NPE
}
}
@@ -59,7 +59,7 @@ fun test4() {
try {
t2 = null
} finally { }
t2.<!UNRESOLVED_REFERENCE!>not<!>() // wrong smartcast, NPE
t2.<!INAPPLICABLE_CANDIDATE!>not<!>() // wrong smartcast, NPE
}
}
@@ -80,10 +80,10 @@ fun test5() {
}
finally {
s1.length
s2.<!UNRESOLVED_REFERENCE!>length<!>
s2.<!INAPPLICABLE_CANDIDATE!>length<!>
}
s1.length
s2.<!UNRESOLVED_REFERENCE!>length<!>
s2.<!INAPPLICABLE_CANDIDATE!>length<!>
}
fun test6(s1: String?, s2: String?) {
@@ -97,10 +97,10 @@ fun test6(s1: String?, s2: String?) {
return
}
finally {
s.<!UNRESOLVED_REFERENCE!>length<!>
s.<!INAPPLICABLE_CANDIDATE!>length<!>
requireNotNull(s2)
}
s.<!UNRESOLVED_REFERENCE!>length<!>
s.<!INAPPLICABLE_CANDIDATE!>length<!>
s1.length
s2.length
}

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