diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt index 4b8b8776f16..e80c63d96f7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt @@ -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() } val intersectedType = ConeTypeIntersector.intersectTypes(inferenceComponents.ctx as ConeInferenceContext, allTypes) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt index 5f2eb2c2f79..2f8008b59da 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt @@ -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( @@ -61,7 +61,11 @@ abstract class FirDataFlowAnalyzer( } 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( 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, FLOW>() private val variablesForWhenConditions = mutableMapOf() @@ -83,14 +87,21 @@ abstract class FirDataFlowAnalyzer( // ----------------------------------- Requests ----------------------------------- - fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection? { + fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): MutableList? { /* * 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() + 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 { @@ -164,7 +175,7 @@ abstract class FirDataFlowAnalyzer( val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).mergeIncomingFlow() if (typeOperatorCall.operation !in FirOperation.TYPES) return val type = typeOperatorCall.conversionTypeRef.coneTypeUnsafe() - 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( 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( 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( // 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( 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( 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( 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( 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( ?.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( 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( 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( 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()?.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( * 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()) + if (isAssignment) { + if (initializer is FirConstExpression<*> && initializer.kind == FirConstKind.Null) return + flow.addTypeStatement(propertyVariable typeEq initializer.typeRef.coneTypeUnsafe()) } } @@ -722,7 +730,7 @@ abstract class FirDataFlowAnalyzer( 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( * 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( 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( 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") diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/LogicSystem.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/LogicSystem.kt index 90cf243cab6..44910dcdf8a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/LogicSystem.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/LogicSystem.kt @@ -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 abstract fun getVariablesInTypeStatements(): Collection abstract fun removeOperations(variable: DataFlowVariable): Collection + + abstract val directAliasMap: Map + abstract val backwardsAliasMap: Map> +} + +fun Flow.unwrapVariable(variable: RealVariable): RealVariable { + return directAliasMap[variable] ?: variable } abstract class LogicSystem(protected val context: ConeInferenceContext) { @@ -45,6 +53,9 @@ abstract class LogicSystem(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 // ------------------------------- Callbacks for updating implicit receiver stack ------------------------------- diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/PersistentLogicSystem.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/PersistentLogicSystem.kt index dca078597ed..51c7f4093b8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/PersistentLogicSystem.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/PersistentLogicSystem.kt @@ -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 = persistentSetOf() + + /* + * val x = a + * val y = a + * + * directAliasMap: { x -> a, y -> a} + * backwardsAliasMap: { a -> [x, y] } + */ + override var directAliasMap: PersistentMap + override var backwardsAliasMap: PersistentMap> 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 { if (flows.isEmpty()) return createEmptyFlow() flows.singleOrNull()?.let { return it } + val flowsSize = flows.size + + val aliasedVariablesThatDontChangeAlias = mutableMapOf() + 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 = + private fun PersistentFlow.diffVariablesIterable( + parentFlow: PersistentFlow, + aliasedVariablesThatDontChangeAlias: Set + ): Iterable = object : DiffIterable(parentFlow, this) { override fun extractIterator(flow: PersistentFlow): Iterator { - 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() } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/VariableStorage.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/VariableStorage.kt index 2eb6c124fda..70fb8383876 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/VariableStorage.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/VariableStorage.kt @@ -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 = HashMap() private val syntheticVariables: MutableMap = HashMap() - private val localVariableAliases: MutableMap, 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 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()?.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 + } } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt index b46a9239465..eb883e1a7ee 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt @@ -93,7 +93,7 @@ sealed class CFGNode(val owner: ControlFlowGraph, val level: } } -val CFGNode<*>.firstPreviousNode: CFGNode<*> get() = previousNodes.first() +val CFGNode<*>.firstPreviousNode: CFGNode<*> get() = previousNodes[0] interface EnterNodeMarker interface ExitNodeMarker diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/model.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/model.kt index ad3b4c4d918..068e0f786ba 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/model.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/model.kt @@ -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 } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/util.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/util.kt index ebe0cad620a..64b479f30b9 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/util.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/util.kt @@ -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 MutableMap.put(key: K, value: V, remappingFunct } } +@UseExperimental(ExperimentalContracts::class) +internal inline fun PersistentMap.put(key: K, valueProducer: () -> V, remappingFunction: (existing: V) -> V): PersistentMap { + 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 +//} diff --git a/compiler/fir/resolve/testData/resolve/problems/noBoundSmartcastAfterContinue.kt b/compiler/fir/resolve/testData/resolve/problems/noBoundSmartcastAfterContinue.kt deleted file mode 100644 index 9b45b6a5aed..00000000000 --- a/compiler/fir/resolve/testData/resolve/problems/noBoundSmartcastAfterContinue.kt +++ /dev/null @@ -1,14 +0,0 @@ -interface A { - val s: String -} - -fun test(list: List) { - var goodA: A? = null - for (a in list) { - if (goodA == null) { - goodA = a - continue - } - goodA.s - } -} diff --git a/compiler/fir/resolve/testData/resolve/problems/noBoundSmartcastAfterContinue.txt b/compiler/fir/resolve/testData/resolve/problems/noBoundSmartcastAfterContinue.txt deleted file mode 100644 index 75c9840af3b..00000000000 --- a/compiler/fir/resolve/testData/resolve/problems/noBoundSmartcastAfterContinue.txt +++ /dev/null @@ -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|): R|kotlin/Unit| { - lvar goodA: R|A?| = Null(null) - lval : R|kotlin/collections/List| = R|/list| - lval : R|kotlin/collections/Iterator| = R|/|.R|FakeOverride|>|() - while(R|/|.R|kotlin/collections/Iterator.hasNext|()) { - lval a: R|A| = R|/|.R|FakeOverride|() - when () { - ==(R|/goodA|, Null(null)) -> { - R|/goodA| = R|/a| - continue@@@[R|/|.R|kotlin/collections/Iterator.hasNext|()] - } - } - - R|/goodA|.# - } - - } diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcastsInBranches.dot b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcastsInBranches.dot new file mode 100644 index 00000000000..60d68912b4a --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcastsInBranches.dot @@ -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 " style="filled" fillcolor=red]; + 1 [label="Exit function " 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|/list|"]; + 11 [label="Variable declaration: lval : R|kotlin/collections/List|"]; + 12 [label="Access variable R|/|"]; + 13 [label="Function call: R|/|.R|FakeOverride|>|()"]; + 14 [label="Variable declaration: lval : R|kotlin/collections/Iterator|"]; + 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|/|"]; + 18 [label="Function call: R|/|.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|/|"]; + 23 [label="Function call: R|/|.R|FakeOverride|()"]; + 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|/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|/a|"]; + 35 [label="Assignmenet: R|/goodA|"]; + 36 [label="Jump: continue@@@[R|/|.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|/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|/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|/a|"]; + 58 [label="Assignmenet: R|/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|/x|"]; + 65 [label="Exit block"]; + } + 66 [label="Exit when branch result"]; + 67 [label="Exit when"]; + } + 68 [label="Access variable R|/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|/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|/a|"]; + 82 [label="Assignmenet: R|/x|"]; + 83 [label="Access variable R|/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|/x|"]; + 91 [label="Exit block"]; + } + 92 [label="Exit when branch result"]; + 93 [label="Exit when"]; + } + 94 [label="Access variable R|/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|/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|/a|"]; + 108 [label="Type operator: a as A"]; + 109 [label="Access variable R|/a|"]; + 110 [label="Assignmenet: R|/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|/x|"]; + 117 [label="Exit block"]; + } + 118 [label="Exit when branch result"]; + 119 [label="Exit when"]; + } + 120 [label="Access variable R|/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|/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|/a|"]; + 134 [label="Assignmenet: R|/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|/a|"]; + 140 [label="Assignmenet: R|/x|"]; + 141 [label="Exit block"]; + } + 142 [label="Exit when branch result"]; + 143 [label="Exit when"]; + } + 144 [label="Access variable R|/x|"]; + 145 [label="Type operator: x as A"]; + 146 [label="Access variable R|/x|"]; + 147 [label="Access variable R|/A.s|"]; + 148 [label="Access variable R|/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|/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|/a|"]; + 162 [label="Assignmenet: R|/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|/a|"]; + 168 [label="Assignmenet: R|/x|"]; + 169 [label="Exit block"]; + } + 170 [label="Exit when branch result"]; + 171 [label="Exit when"]; + } + 172 [label="Access variable R|/a|"]; + 173 [label="Type operator: a as A"]; + 174 [label="Access variable R|/x|"]; + 175 [label="Access variable R|/A.s|"]; + 176 [label="Access variable R|/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|/a|"]; + 182 [label="Assignmenet: R|/x|"]; + 183 [label="Access variable R|/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|/z|"]; + 190 [label="Variable declaration: lvar y: R|kotlin/String?|"]; + 191 [label="Access variable R|/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|/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|/x|"]; + 203 [label="Access variable R|kotlin/String.length|"]; + 204 [label="Access variable R|/y|"]; + 205 [label="Access variable R|kotlin/String.length|"]; + 206 [label="Access variable R|/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|/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|/x|"]; + 221 [label="Access variable R|kotlin/String.length|"]; + 222 [label="Access variable R|/y|"]; + 223 [label="Access variable R|kotlin/String.length|"]; + 224 [label="Access variable R|/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|/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|/x|"]; + 239 [label="Access variable R|kotlin/String.length|"]; + 240 [label="Access variable R|/y|"]; + 241 [label="Access variable R|kotlin/String.length|"]; + 242 [label="Access variable R|/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|/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|/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|/x|"]; + 259 [label="Access variable R|kotlin/String.length|"]; + 260 [label="Access variable R|/y|"]; + 261 [label="Access variable #"]; + 262 [label="Access variable R|/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|/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|/x|"]; + 277 [label="Access variable #"]; + 278 [label="Access variable R|/y|"]; + 279 [label="Access variable R|kotlin/String.length|"]; + 280 [label="Access variable R|/z|"]; + 281 [label="Access variable #"]; + 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|/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|/x|"]; + 295 [label="Access variable R|kotlin/String.length|"]; + 296 [label="Access variable R|/y|"]; + 297 [label="Access variable #"]; + 298 [label="Access variable R|/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}; + +} diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcastsInBranches.kt b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcastsInBranches.kt new file mode 100644 index 00000000000..cd1ca76d80d --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcastsInBranches.kt @@ -0,0 +1,120 @@ +class A { + val s: String = "" +} + +fun test_0(list: List) { + 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.length // Bad + z.length // OK + } + if (y != null) { + x.length // Bad + y.length // OK + z.length // Bad + } + + if (z != null) { + x.length // OK + y.length // Bad + z.length // OK + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcastsInBranches.txt b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcastsInBranches.txt new file mode 100644 index 00000000000..d8fb5f162fe --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcastsInBranches.txt @@ -0,0 +1,157 @@ +FILE: noBoundSmartcastAfterContinue.kt + public final class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + public final val s: R|kotlin/String| = String() + public get(): R|kotlin/String| + + } + public final fun test_0(list: R|kotlin/collections/List|): R|kotlin/Unit| { + lvar goodA: R|A?| = Null(null) + lval : R|kotlin/collections/List| = R|/list| + lval : R|kotlin/collections/Iterator| = R|/|.R|FakeOverride|>|() + while(R|/|.R|kotlin/collections/Iterator.hasNext|()) { + lval a: R|A| = R|/|.R|FakeOverride|() + when () { + ==(R|/goodA|, Null(null)) -> { + R|/goodA| = R|/a| + continue@@@[R|/|.R|kotlin/collections/Iterator.hasNext|()] + } + } + + R|/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|/b| -> { + R|/x| = R|/A.A|() + } + else -> { + R|/x| = R|/a| + } + } + + R|/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|/b| -> { + R|/x| = R|/A.A|() + } + else -> { + R|/x| = R|/a| + (R|/a| as R|A|) + } + } + + R|/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|/b| -> { + R|/x| = R|/A.A|() + } + else -> { + (R|/a| as R|A|) + R|/x| = R|/a| + } + } + + R|/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|/b| -> { + R|/x| = R|/a| + } + else -> { + R|/x| = R|/a| + } + } + + (R|/x| as R|A|) + R|/x|.R|/A.s| + R|/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|/b| -> { + R|/x| = R|/a| + } + else -> { + R|/x| = R|/a| + } + } + + (R|/a| as R|A|) + R|/x|.R|/A.s| + R|/a|.R|/A.s| + } + public final fun test_6(a: R|A|): R|kotlin/Unit| { + lval x: R|kotlin/Any| + R|/x| = R|/a| + R|/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|/z| + lval x: R|kotlin/String?| = R|/y| + when () { + !=(R|/x|, Null(null)) -> { + R|/x|.R|kotlin/String.length| + R|/y|.R|kotlin/String.length| + R|/z|.R|kotlin/String.length| + } + } + + when () { + !=(R|/y|, Null(null)) -> { + R|/x|.R|kotlin/String.length| + R|/y|.R|kotlin/String.length| + R|/z|.R|kotlin/String.length| + } + } + + when () { + !=(R|/z|, Null(null)) -> { + R|/x|.R|kotlin/String.length| + R|/y|.R|kotlin/String.length| + R|/z|.R|kotlin/String.length| + } + } + + R|/y| = Null(null) + when () { + !=(R|/x|, Null(null)) -> { + R|/x|.R|kotlin/String.length| + R|/y|.# + R|/z|.R|kotlin/String.length| + } + } + + when () { + !=(R|/y|, Null(null)) -> { + R|/x|.# + R|/y|.R|kotlin/String.length| + R|/z|.# + } + } + + when () { + !=(R|/z|, Null(null)) -> { + R|/x|.R|kotlin/String.length| + R|/y|.# + R|/z|.R|kotlin/String.length| + } + } + + } diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/nullability.dot b/compiler/fir/resolve/testData/resolve/smartcasts/nullability.dot index 81f38c43649..053015f6303 100644 --- a/compiler/fir/resolve/testData/resolve/smartcasts/nullability.dot +++ b/compiler/fir/resolve/testData/resolve/smartcasts/nullability.dot @@ -499,11 +499,11 @@ digraph nullability_kt { 172 [label="Access variable R|/Q.data|"]; 173 [label="Access variable R|/q|"]; 174 [label="Access variable R|/Q.data|"]; - 175 [label="Access variable R|/MyData.s|"]; + 175 [label="Access variable #"]; 176 [label="Access variable R|/q|"]; 177 [label="Access variable R|/Q.data|"]; - 178 [label="Access variable R|/MyData.s|"]; - 179 [label="Function call: R|/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|()"]; + 178 [label="Access variable #"]; + 179 [label="Function call: R|/q|.R|/Q.data|.#.#()"]; 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|/q|"]; 218 [label="Access variable R|/Q.data|"]; - 219 [label="Access variable R|/MyData.s|"]; + 219 [label="Access variable #"]; 220 [label="Access variable R|/q|"]; 221 [label="Access variable R|/Q.data|"]; - 222 [label="Access variable R|/MyData.s|"]; - 223 [label="Function call: R|/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|()"]; + 222 [label="Access variable #"]; + 223 [label="Function call: R|/q|.R|/Q.data|.#.#()"]; 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|/q|"]; 488 [label="Access variable R|/QImplWithCustomGetter.data|"]; - 489 [label="Access variable R|/MyData.s|"]; + 489 [label="Access variable #"]; 490 [label="Access variable R|/q|"]; 491 [label="Access variable R|/QImplWithCustomGetter.data|"]; - 492 [label="Access variable R|/MyData.s|"]; - 493 [label="Function call: R|/q|.R|/QImplWithCustomGetter.data|.R|/MyData.s|.R|kotlin/Int.inc|()"]; + 492 [label="Access variable #"]; + 493 [label="Function call: R|/q|.R|/QImplWithCustomGetter.data|.#.#()"]; 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|/q|"]; 520 [label="Access variable R|/QImplMutable.data|"]; - 521 [label="Access variable R|/MyData.s|"]; + 521 [label="Access variable #"]; 522 [label="Access variable R|/q|"]; 523 [label="Access variable R|/QImplMutable.data|"]; - 524 [label="Access variable R|/MyData.s|"]; - 525 [label="Function call: R|/q|.R|/QImplMutable.data|.R|/MyData.s|.R|kotlin/Int.inc|()"]; + 524 [label="Access variable #"]; + 525 [label="Function call: R|/q|.R|/QImplMutable.data|.#.#()"]; 526 [label="Exit block"]; } 527 [label="Exit when branch result"]; diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/nullability.kt b/compiler/fir/resolve/testData/resolve/smartcasts/nullability.kt index a12dc4d94e6..0baed6a62bc 100644 --- a/compiler/fir/resolve/testData/resolve/smartcasts/nullability.kt +++ b/compiler/fir/resolve/testData/resolve/smartcasts/nullability.kt @@ -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.s // should be bad + q.data.s.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.s // should be bad + q.data.s.inc() // should be bad } fun test_7(q: Q?) { @@ -139,7 +137,6 @@ fun test_10(a: Int?, b: Int?) { b.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.s // should be bad + q.data.s.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.s // should be bad + q.data.s.inc() // should be bad } } diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/nullability.txt b/compiler/fir/resolve/testData/resolve/smartcasts/nullability.txt index f6684da3000..f63c7cf74cc 100644 --- a/compiler/fir/resolve/testData/resolve/smartcasts/nullability.txt +++ b/compiler/fir/resolve/testData/resolve/smartcasts/nullability.txt @@ -110,8 +110,8 @@ FILE: nullability.kt when () { !=(R|/q|?.R|/Q.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> { R|/q|.R|/Q.data| - R|/q|.R|/Q.data|.R|/MyData.s| - R|/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|() + R|/q|.R|/Q.data|.# + R|/q|.R|/Q.data|.#.#() } } @@ -127,8 +127,8 @@ FILE: nullability.kt } R|/q|.R|/Q.data| - R|/q|.R|/Q.data|.R|/MyData.s| - R|/q|.R|/Q.data|.R|/MyData.s|.R|kotlin/Int.inc|() + R|/q|.R|/Q.data|.# + R|/q|.R|/Q.data|.#.#() } public final fun test_7(q: R|Q?|): R|kotlin/Unit| { when () { @@ -232,8 +232,8 @@ FILE: nullability.kt when () { !=(R|/q|?.R|/QImplWithCustomGetter.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> { R|/q|.R|/QImplWithCustomGetter.data| - R|/q|.R|/QImplWithCustomGetter.data|.R|/MyData.s| - R|/q|.R|/QImplWithCustomGetter.data|.R|/MyData.s|.R|kotlin/Int.inc|() + R|/q|.R|/QImplWithCustomGetter.data|.# + R|/q|.R|/QImplWithCustomGetter.data|.#.#() } } @@ -242,8 +242,8 @@ FILE: nullability.kt when () { !=(R|/q|?.R|/QImplMutable.data|?.R|/MyData.s|?.R|kotlin/Int.inc|(), Null(null)) -> { R|/q|.R|/QImplMutable.data| - R|/q|.R|/QImplMutable.data|.R|/MyData.s| - R|/q|.R|/QImplMutable.data|.R|/MyData.s|.R|kotlin/Int.inc|() + R|/q|.R|/QImplMutable.data|.# + R|/q|.R|/QImplMutable.data|.#.#() } } diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/overridenOpenVal.dot b/compiler/fir/resolve/testData/resolve/smartcasts/overridenOpenVal.dot new file mode 100644 index 00000000000..d449afc5048 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/overridenOpenVal.dot @@ -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 " style="filled" fillcolor=red]; + 1 [label="Exit function " 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|/x|"]; + 6 [label="Exit property" style="filled" fillcolor=red]; + } + + 4 -> {5}; + 5 -> {6}; + + subgraph cluster_3 { + color=red + 7 [label="Enter function " style="filled" fillcolor=red]; + 8 [label="Access variable R|/x|"]; + 9 [label="Exit function " 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|/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|/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}; + +} diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/overridenOpenVal.kt b/compiler/fir/resolve/testData/resolve/smartcasts/overridenOpenVal.kt new file mode 100644 index 00000000000..eef6f8a1aac --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/overridenOpenVal.kt @@ -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 + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/overridenOpenVal.txt b/compiler/fir/resolve/testData/resolve/smartcasts/overridenOpenVal.txt new file mode 100644 index 00000000000..e534caf4fc5 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/overridenOpenVal.txt @@ -0,0 +1,33 @@ +FILE: overridenOpenVal.kt + public open class A : R|kotlin/Any| { + public constructor(x: R|kotlin/Any|): R|A| { + super() + } + + public open val x: R|kotlin/Any| = R|/x| + public get(): R|kotlin/Any| + + } + public final class B : R|A| { + public constructor(x: R|kotlin/Any|): R|B| { + super(R|/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|/b|.R|/A.x| is R|kotlin/String|) -> { + R|/b|.R|/A.x|.R|kotlin/String.length| + } + } + + } diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.dot b/compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.dot index 833941f07ee..5d9382ce36e 100644 --- a/compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.dot +++ b/compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.dot @@ -88,7 +88,7 @@ digraph smartcastAfterReassignment_kt { 35 [label="Const: Null(null)"]; 36 [label="Assignmenet: R|/x|"]; 37 [label="Access variable R|/x|"]; - 38 [label="Access variable #"]; + 38 [label="Access variable #"]; 39 [label="Exit function test_3" style="filled" fillcolor=red]; } diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.kt b/compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.kt index 3ab2a944798..4c2866f5e1a 100644 --- a/compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.kt +++ b/compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.kt @@ -17,5 +17,5 @@ fun test_3() { x = "" x.length x = null - x.length + x.length } \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.txt b/compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.txt index 67786b1a543..afd95360515 100644 --- a/compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.txt +++ b/compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.txt @@ -19,5 +19,5 @@ FILE: smartcastAfterReassignment.kt R|/x| = String() R|/x|.R|kotlin/String.length| R|/x| = Null(null) - R|/x|.# + R|/x|.# } diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/smartcastToNothing.dot b/compiler/fir/resolve/testData/resolve/smartcasts/smartcastToNothing.dot new file mode 100644 index 00000000000..f6389130fce --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/smartcastToNothing.dot @@ -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 " style="filled" fillcolor=red]; + 33 [label="Exit function " 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|/results|"]; + 48 [label="Variable declaration: lval : R|kotlin/collections/List|"]; + 49 [label="Access variable R|/|"]; + 50 [label="Function call: R|/|.R|FakeOverride|>|()"]; + 51 [label="Variable declaration: lval : R|kotlin/collections/Iterator|"]; + 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|/|"]; + 55 [label="Function call: R|/|.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|/|"]; + 60 [label="Function call: R|/|.R|FakeOverride|()"]; + 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|/result|" style="filled" fillcolor=gray]; + 64 [label="Stub" style="filled" fillcolor=gray]; + 65 [label="Assignmenet: R|/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|/result|" style="filled" fillcolor=gray]; + 69 [label="Stub" style="filled" fillcolor=gray]; + 70 [label="Access variable #" 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|/|.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|/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|/it|"]; + 88 [label="Access variable R|/A.a|"]; + 89 [label="Exit function anonymousFunction"]; + } + 90 [label="Postponed exit from lambda"]; + 91 [label="Function call: R|/s|?.R|kotlin/let|( = let@fun (it: R|A|): R|kotlin/Int| { + ^ R|/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]; + +} diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/smartcastToNothing.kt b/compiler/fir/resolve/testData/resolve/smartcasts/smartcastToNothing.kt new file mode 100644 index 00000000000..947639eacf6 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/smartcastToNothing.kt @@ -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 myListOf(x: T): List = null!! + +class A { + val a: Int = 1 + val b: Boolean = true +} + +fun test_0(results: List) { + var s: A? = null + for (result in results) { + s = result + if (result.b) { + break + } + } + s?.let { it.a } +} diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/smartcastToNothing.txt b/compiler/fir/resolve/testData/resolve/smartcasts/smartcastToNothing.txt new file mode 100644 index 00000000000..e5b073fd93c --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/smartcastToNothing.txt @@ -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 myListOf(x: R|T|): R|kotlin/collections/List| { + ^myListOf Null(null)!! + } + public final class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + 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|): R|kotlin/Unit| { + lvar s: R|A?| = Null(null) + lval : R|kotlin/collections/List| = R|/results| + lval : R|kotlin/collections/Iterator| = R|/|.R|FakeOverride|>|() + while(R|/|.R|kotlin/collections/Iterator.hasNext|()) { + lval result: R|kotlin/Nothing| = R|/|.R|FakeOverride|() + R|/s| = R|/result| + when () { + R|/result|.# -> { + break@@@[R|/|.R|kotlin/collections/Iterator.hasNext|()] + } + } + + } + + R|/s|?.R|kotlin/let|( = let@fun (it: R|A|): R|kotlin/Int| { + ^ R|/it|.R|/A.a| + } + ) + } diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/thisOfExtensionProperty.dot b/compiler/fir/resolve/testData/resolve/smartcasts/thisOfExtensionProperty.dot new file mode 100644 index 00000000000..75040b8ab88 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/thisOfExtensionProperty.dot @@ -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}; + +} diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/thisOfExtensionProperty.kt b/compiler/fir/resolve/testData/resolve/smartcasts/thisOfExtensionProperty.kt new file mode 100644 index 00000000000..c276790a309 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/thisOfExtensionProperty.kt @@ -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 \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/thisOfExtensionProperty.txt b/compiler/fir/resolve/testData/resolve/smartcasts/thisOfExtensionProperty.txt new file mode 100644 index 00000000000..7b2a7c72c36 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/thisOfExtensionProperty.txt @@ -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| + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index af23daa1a95..728bde9f151 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -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"); diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithCfgTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithCfgTestGenerated.java index b9c2cecb669..092702abf78 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithCfgTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithCfgTestGenerated.java @@ -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"); diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java index 48c066ecd1e..f3fdf0016b3 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java @@ -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"); diff --git a/compiler/testData/codegen/box/bridges/complexMultiInheritance.kt b/compiler/testData/codegen/box/bridges/complexMultiInheritance.kt index 960aa2fccbe..227bd67042c 100644 --- a/compiler/testData/codegen/box/bridges/complexMultiInheritance.kt +++ b/compiler/testData/codegen/box/bridges/complexMultiInheritance.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR open class A { open fun foo(): Any = "A" } diff --git a/compiler/testData/codegen/box/bridges/diamond.kt b/compiler/testData/codegen/box/bridges/diamond.kt index e9568e095e9..ef1990bf937 100644 --- a/compiler/testData/codegen/box/bridges/diamond.kt +++ b/compiler/testData/codegen/box/bridges/diamond.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR interface A { fun foo(t: T, u: U) = "A" } diff --git a/compiler/testData/codegen/box/bridges/fakeOverrideOfTraitImpl.kt b/compiler/testData/codegen/box/bridges/fakeOverrideOfTraitImpl.kt index 9a66de323d1..7827f53a892 100644 --- a/compiler/testData/codegen/box/bridges/fakeOverrideOfTraitImpl.kt +++ b/compiler/testData/codegen/box/bridges/fakeOverrideOfTraitImpl.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR var result = "" interface D1 { diff --git a/compiler/testData/codegen/box/bridges/fakeOverrideWithSeveralSuperDeclarations.kt b/compiler/testData/codegen/box/bridges/fakeOverrideWithSeveralSuperDeclarations.kt index 5bf3b980336..c9844928db7 100644 --- a/compiler/testData/codegen/box/bridges/fakeOverrideWithSeveralSuperDeclarations.kt +++ b/compiler/testData/codegen/box/bridges/fakeOverrideWithSeveralSuperDeclarations.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR interface D1 { fun foo(): Any } diff --git a/compiler/testData/codegen/box/bridges/fakeOverrideWithSynthesizedImplementation.kt b/compiler/testData/codegen/box/bridges/fakeOverrideWithSynthesizedImplementation.kt index 2784d2f2701..075bbca0443 100644 --- a/compiler/testData/codegen/box/bridges/fakeOverrideWithSynthesizedImplementation.kt +++ b/compiler/testData/codegen/box/bridges/fakeOverrideWithSynthesizedImplementation.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR open class A(val value: String) { fun component1() = value } diff --git a/compiler/testData/codegen/box/bridges/kt12416.kt b/compiler/testData/codegen/box/bridges/kt12416.kt index 68562d39717..c50c81671f5 100644 --- a/compiler/testData/codegen/box/bridges/kt12416.kt +++ b/compiler/testData/codegen/box/bridges/kt12416.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR interface A { fun foo(t: T, u: Int) = "A" } diff --git a/compiler/testData/codegen/box/bridges/kt318.kt b/compiler/testData/codegen/box/bridges/kt318.kt index d82e3df1c15..72e5991297c 100644 --- a/compiler/testData/codegen/box/bridges/kt318.kt +++ b/compiler/testData/codegen/box/bridges/kt318.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR var result = "" interface Base diff --git a/compiler/testData/codegen/box/bridges/longChainOneBridge.kt b/compiler/testData/codegen/box/bridges/longChainOneBridge.kt index 87849c76857..6d1d79c28e7 100644 --- a/compiler/testData/codegen/box/bridges/longChainOneBridge.kt +++ b/compiler/testData/codegen/box/bridges/longChainOneBridge.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR open class A { open fun foo(t: T) = "A" } diff --git a/compiler/testData/codegen/box/bridges/manyTypeArgumentsSubstitutedSuccessively.kt b/compiler/testData/codegen/box/bridges/manyTypeArgumentsSubstitutedSuccessively.kt index 07bccaeeb57..82306ddf3f4 100644 --- a/compiler/testData/codegen/box/bridges/manyTypeArgumentsSubstitutedSuccessively.kt +++ b/compiler/testData/codegen/box/bridges/manyTypeArgumentsSubstitutedSuccessively.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR open class A { open fun foo(t: T, u: U, v: V) = "A" } diff --git a/compiler/testData/codegen/box/bridges/simple.kt b/compiler/testData/codegen/box/bridges/simple.kt index 5f5b4d075b3..284d7cbb44c 100644 --- a/compiler/testData/codegen/box/bridges/simple.kt +++ b/compiler/testData/codegen/box/bridges/simple.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR open class A { open fun foo(t: T) = "A" } diff --git a/compiler/testData/codegen/box/bridges/simpleGenericMethod.kt b/compiler/testData/codegen/box/bridges/simpleGenericMethod.kt index 0bcd417f79c..81e6c8607c1 100644 --- a/compiler/testData/codegen/box/bridges/simpleGenericMethod.kt +++ b/compiler/testData/codegen/box/bridges/simpleGenericMethod.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR open class A { open fun foo(t: T, u: U) = "A" } diff --git a/compiler/testData/codegen/box/bridges/simpleReturnType.kt b/compiler/testData/codegen/box/bridges/simpleReturnType.kt index 43cb7df722d..ffd0bf84892 100644 --- a/compiler/testData/codegen/box/bridges/simpleReturnType.kt +++ b/compiler/testData/codegen/box/bridges/simpleReturnType.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR open class A(val t: T) { open fun foo(): T = t } diff --git a/compiler/testData/codegen/box/bridges/simpleTraitImpl.kt b/compiler/testData/codegen/box/bridges/simpleTraitImpl.kt index 64dc38fa5f5..1e1ccbab17e 100644 --- a/compiler/testData/codegen/box/bridges/simpleTraitImpl.kt +++ b/compiler/testData/codegen/box/bridges/simpleTraitImpl.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR interface A { fun foo(t: T) = "A" } diff --git a/compiler/testData/codegen/box/bridges/simpleUpperBound.kt b/compiler/testData/codegen/box/bridges/simpleUpperBound.kt index c46365116ac..dea1f7eb647 100644 --- a/compiler/testData/codegen/box/bridges/simpleUpperBound.kt +++ b/compiler/testData/codegen/box/bridges/simpleUpperBound.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR open class A { open fun foo(t: T) = "A" } diff --git a/compiler/testData/codegen/box/bridges/substitutionInSuperClass/abstractFun.kt b/compiler/testData/codegen/box/bridges/substitutionInSuperClass/abstractFun.kt index 4e3773eb1db..39e1b1935f0 100644 --- a/compiler/testData/codegen/box/bridges/substitutionInSuperClass/abstractFun.kt +++ b/compiler/testData/codegen/box/bridges/substitutionInSuperClass/abstractFun.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR abstract class A { abstract fun foo(t: T): String } diff --git a/compiler/testData/codegen/box/bridges/substitutionInSuperClass/boundedTypeArguments.kt b/compiler/testData/codegen/box/bridges/substitutionInSuperClass/boundedTypeArguments.kt index f8d01b274ab..3db4852ed2b 100644 --- a/compiler/testData/codegen/box/bridges/substitutionInSuperClass/boundedTypeArguments.kt +++ b/compiler/testData/codegen/box/bridges/substitutionInSuperClass/boundedTypeArguments.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR open class A { open fun foo(t: T, u: U) = "A" } diff --git a/compiler/testData/codegen/box/bridges/substitutionInSuperClass/genericMethod.kt b/compiler/testData/codegen/box/bridges/substitutionInSuperClass/genericMethod.kt index 84360e455b7..0119eba58a8 100644 --- a/compiler/testData/codegen/box/bridges/substitutionInSuperClass/genericMethod.kt +++ b/compiler/testData/codegen/box/bridges/substitutionInSuperClass/genericMethod.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR open class A { open fun foo(t: T, u: U) = "A" } diff --git a/compiler/testData/codegen/box/bridges/substitutionInSuperClass/simple.kt b/compiler/testData/codegen/box/bridges/substitutionInSuperClass/simple.kt index f3b0f27f752..c6c2637ef22 100644 --- a/compiler/testData/codegen/box/bridges/substitutionInSuperClass/simple.kt +++ b/compiler/testData/codegen/box/bridges/substitutionInSuperClass/simple.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR open class A { open fun foo(t: T) = "A" } diff --git a/compiler/testData/codegen/box/bridges/substitutionInSuperClass/upperBound.kt b/compiler/testData/codegen/box/bridges/substitutionInSuperClass/upperBound.kt index 10f8a72a7a7..f6d4dccfc40 100644 --- a/compiler/testData/codegen/box/bridges/substitutionInSuperClass/upperBound.kt +++ b/compiler/testData/codegen/box/bridges/substitutionInSuperClass/upperBound.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR open class A { open fun foo(t: T) = "A" } diff --git a/compiler/testData/codegen/box/bridges/traitImplInheritsTraitImpl.kt b/compiler/testData/codegen/box/bridges/traitImplInheritsTraitImpl.kt index 8fc1e290a84..ec0cf44081a 100644 --- a/compiler/testData/codegen/box/bridges/traitImplInheritsTraitImpl.kt +++ b/compiler/testData/codegen/box/bridges/traitImplInheritsTraitImpl.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR interface A { fun foo(): Any = "A" } diff --git a/compiler/testData/codegen/box/bridges/twoParentsWithDifferentMethodsTwoBridges.kt b/compiler/testData/codegen/box/bridges/twoParentsWithDifferentMethodsTwoBridges.kt index 5a3f3c746d4..38d7cb2431f 100644 --- a/compiler/testData/codegen/box/bridges/twoParentsWithDifferentMethodsTwoBridges.kt +++ b/compiler/testData/codegen/box/bridges/twoParentsWithDifferentMethodsTwoBridges.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR interface A { fun foo(t: T, u: Int) = "A" } diff --git a/compiler/testData/codegen/box/bridges/twoParentsWithDifferentMethodsTwoBridges2.kt b/compiler/testData/codegen/box/bridges/twoParentsWithDifferentMethodsTwoBridges2.kt index 259742243fb..07af5afaa4e 100644 --- a/compiler/testData/codegen/box/bridges/twoParentsWithDifferentMethodsTwoBridges2.kt +++ b/compiler/testData/codegen/box/bridges/twoParentsWithDifferentMethodsTwoBridges2.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR interface A { fun foo(t: T, u: Int) = "A" } diff --git a/compiler/testData/codegen/box/bridges/twoParentsWithTheSameMethodOneBridge.kt b/compiler/testData/codegen/box/bridges/twoParentsWithTheSameMethodOneBridge.kt index bd9cc05e392..de57467d8b1 100644 --- a/compiler/testData/codegen/box/bridges/twoParentsWithTheSameMethodOneBridge.kt +++ b/compiler/testData/codegen/box/bridges/twoParentsWithTheSameMethodOneBridge.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR interface A { fun foo(t: T) = "A" } diff --git a/compiler/testData/codegen/box/javaInterop/notNullAssertions/paramAssertionMessage.kt b/compiler/testData/codegen/box/javaInterop/notNullAssertions/paramAssertionMessage.kt index 8dfe3d599cf..741951b8d24 100644 --- a/compiler/testData/codegen/box/javaInterop/notNullAssertions/paramAssertionMessage.kt +++ b/compiler/testData/codegen/box/javaInterop/notNullAssertions/paramAssertionMessage.kt @@ -1,3 +1,4 @@ +// IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM // FILE: Test.java diff --git a/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.fir.kt b/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.fir.kt index cbbc4eb27b1..b0f6947b64e 100644 --- a/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.fir.kt +++ b/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.fir.kt @@ -6,10 +6,10 @@ class A() { fun f(): Unit { var x: Int? = 1 x = null - x + 1 - x plus 1 + x + 1 + x plus 1 x < 1 - x += 1 + x += 1 x == 1 x != 1 @@ -22,8 +22,8 @@ fun f(): Unit { x === 1 x !== 1 - x..2 - x in 1..2 + x..2 + x in 1..2 val y : Boolean? = true false || y diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/While.fir.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/While.fir.kt index 495feeb6f45..1c3f30fb578 100644 --- a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/While.fir.kt +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/While.fir.kt @@ -11,7 +11,7 @@ fun foo() { while (y != null) { bar(y) } - bar(y) + bar(y) val z: Int? = null while (z == null) { diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/WhileCondition.fir.kt b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/WhileCondition.fir.kt index 43948d78597..85e3b6e9cdd 100644 --- a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/WhileCondition.fir.kt +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/WhileCondition.fir.kt @@ -22,5 +22,5 @@ fun whileWithNoCondition(x: Int?) { while () { x!! } - checkSubtype(x) + checkSubtype(x) } diff --git a/compiler/testData/diagnostics/tests/generics/suppressVarianceConflict.fir.kt b/compiler/testData/diagnostics/tests/generics/suppressVarianceConflict.fir.kt index 553af5275bc..ddb591346bb 100644 --- a/compiler/testData/diagnostics/tests/generics/suppressVarianceConflict.fir.kt +++ b/compiler/testData/diagnostics/tests/generics/suppressVarianceConflict.fir.kt @@ -10,8 +10,8 @@ class A { fun foo(x: A, cs: CharSequence, ls: List) { val y: A = x - y.foo(cs) - val s: String = y.foo(cs, ls) + y.foo(cs) + val s: String = y.foo(cs, ls) val ls2: List = y.bar() } diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWhenOpenGetterWithOverloading.fir.kt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWhenOpenGetterWithOverloading.fir.kt index 92eb68bb5a3..eef4481310c 100644 --- a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWhenOpenGetterWithOverloading.fir.kt +++ b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWhenOpenGetterWithOverloading.fir.kt @@ -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.doJob(2) } } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWithOverloadedExtensions.fir.kt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWithOverloadedExtensions.fir.kt index f3b15e045ba..d82782e63de 100644 --- a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWithOverloadedExtensions.fir.kt +++ b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWithOverloadedExtensions.fir.kt @@ -9,6 +9,6 @@ var a: A? = null fun smartCastInterference(b: B) { if (a != null) { - a.foo(b) + a.foo(b) } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.fir.kt index bfd506229e2..92202f02d93 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.fir.kt @@ -2,8 +2,8 @@ fun foo(): String { var s: String? s = null - s?.length - s.length + s?.length + s.length if (s == null) return s!! var t: String? = "y" if (t == null) t = "x" diff --git a/compiler/testData/diagnostics/tests/smartCasts/castchecks/variables.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/castchecks/variables.fir.kt index c15a3857cbd..489665f3578 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/castchecks/variables.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/castchecks/variables.fir.kt @@ -41,7 +41,7 @@ fun f(a: SomeClass?) { if (aa as? SomeSubClass != null) { aa = null // 'aa' cannot be cast to SomeSubClass - aa.hashCode() + aa.hashCode() aa.foo (aa as? SomeSubClass).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.hashCode() + aa.hashCode() aa.foo (aa as? SomeSubClass).foo (aa as SomeSubClass).foo diff --git a/compiler/testData/diagnostics/tests/smartCasts/intersectionScope/unstableSmartCast.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/intersectionScope/unstableSmartCast.fir.kt index 9ebd05446bc..31282d67b4f 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/intersectionScope/unstableSmartCast.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/intersectionScope/unstableSmartCast.fir.kt @@ -28,11 +28,11 @@ fun test() { x.foo().checkType { _() } if (x is B && x is C) { - x.foo().checkType { _() } + x.foo().checkType { _() } x.baz("") - x.baz(1).checkType { _() } - x.baz(1, 2) + x.baz(1).checkType { _() } + x.baz(1, 2) - x.foobar().checkType { _() } + x.foobar().checkType { _() } } } diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileSimple.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileSimple.fir.kt index cfe59782992..641f5dbccf8 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/loops/whileSimple.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileSimple.fir.kt @@ -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.length } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.fir.kt index 3eeedc78531..70958f55081 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.fir.kt @@ -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.length } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.fir.kt index 949f54a32c9..4f1e577fd70 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.fir.kt @@ -8,7 +8,7 @@ fun foo() { } // TODO: this testdata fixates undesired behavior (it should be an unsafe call) - x.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.size // 'x' is unsoundly smartcasted here + x.size // 'x' is unsoundly smartcasted here } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.fir.kt index 0b651c5beea..5eca313d3e3 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.fir.kt @@ -7,7 +7,7 @@ fun foo() { break } - x.length // 'x' is unsoundly smartcasted here + x.length // 'x' is unsoundly smartcasted here } fun bar() { @@ -17,5 +17,5 @@ fun bar() { break } - x.size // 'x' is unsoundly smartcasted here + x.size // 'x' is unsoundly smartcasted here } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.fir.kt index e6df621d176..9059f8bb307 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.fir.kt @@ -9,5 +9,5 @@ fun foo(arg: Int?) { } if (x != null) x = 42 // Unsafe because of lambda - x.hashCode() + x.hashCode() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVals/customGetter.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/publicVals/customGetter.fir.kt index 094ffda8ced..a6c9e093e31 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVals/customGetter.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/publicVals/customGetter.fir.kt @@ -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.length else return 0 } diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVals/open.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/publicVals/open.fir.kt index cb56a5826d3..b0456ab94de 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVals/open.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/publicVals/open.fir.kt @@ -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 bar 11 } diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVals/var.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/publicVals/var.fir.kt index ffff8aba67a..e413b118cbf 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVals/var.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/publicVals/var.fir.kt @@ -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.length else if (y != null) // Even if they are private - return y.length + return y.length else return 0 } diff --git a/compiler/testData/diagnostics/tests/smartCasts/smartCastOnWhen.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/smartCastOnWhen.fir.kt index 9a1e791282b..21e5d618786 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/smartCastOnWhen.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/smartCastOnWhen.fir.kt @@ -15,6 +15,6 @@ fun bar(): Int { return when(ss) { "abc" -> ss else -> "xyz" - }.length + }.length } diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/assignmentConversion.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/assignmentConversion.fir.kt index d1b35b3fd20..fd40e6eb581 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/variables/assignmentConversion.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/assignmentConversion.fir.kt @@ -8,7 +8,7 @@ fun test1() { if (newC != null) { c = newC } - foo(c) + foo(c) } fun test2() { @@ -19,7 +19,7 @@ fun test2() { if (newC is String) { c = newC } - foo(c) + foo(c) } fun test3() { @@ -30,6 +30,6 @@ fun test3() { if (newC == null) return c = newC - foo(c) + foo(c) } diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.fir.kt index 5c49ba03947..ac221151837 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.fir.kt @@ -6,5 +6,5 @@ fun foo(x: Int, f: () -> Unit, y: Int) {} fun bar() { var x: Int? x = 4 - foo(x, { x = null; x.hashCode() }, x) + foo(x, { x = null; x.hashCode() }, x) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/whileWithBreak.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/whileWithBreak.fir.kt index 737f8147759..b34bb73ed5e 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/variables/whileWithBreak.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/whileWithBreak.fir.kt @@ -13,5 +13,5 @@ fun list(start: String) { e = e.next() } // e can never be null but we do not know it - e.hashCode() + e.hashCode() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/assignment.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/assignment.fir.kt index fabd049f830..853571a14f0 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/assignment.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/assignment.fir.kt @@ -5,7 +5,7 @@ fun foo() { v = "abc" v.length v = null - v.length + v.length v = "abc" v.length } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/boundInitializerWrong.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/boundInitializerWrong.fir.kt index 1f3539b9756..d424d141dd5 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/boundInitializerWrong.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/boundInitializerWrong.fir.kt @@ -6,7 +6,7 @@ fun foo() { val y = x x = null if (y != null) { - x.hashCode() + x.hashCode() } } @@ -23,7 +23,7 @@ fun bar(s: String?) { val hashCode = ss?.hashCode() ss = null if (hashCode != null) { - ss.hashCode() + ss.hashCode() } } diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/capturedInClosureModifiedBefore.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/capturedInClosureModifiedBefore.fir.kt index 208823d186c..5a017f5d5b1 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/capturedInClosureModifiedBefore.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/capturedInClosureModifiedBefore.fir.kt @@ -44,7 +44,7 @@ fun gaz(s: String?) { x = null } run { - x.hashCode() + x.hashCode() } } } diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithBreak.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithBreak.fir.kt index 4b3a3fe77df..3b1b5feabe3 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithBreak.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithBreak.fir.kt @@ -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 (!e.doSomething()) + if (!e.doSomething()) break // Smart cast here is still not possible - e = e.next() + e = e.next() } while (e != null) // e can be null because of next() - e.doSomething() + e.doSomething() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithMiddleBreak.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithMiddleBreak.fir.kt index eb631f4c3a0..2b916f90fb0 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithMiddleBreak.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithMiddleBreak.fir.kt @@ -8,5 +8,5 @@ public fun foo(pp: String?): Int { p = null } while (!x()) // Smart cast is NOT possible here - return p.length + return p.length } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEach.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEach.fir.kt index 40a920e0373..fe84aec9785 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEach.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEach.fir.kt @@ -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.doSomething() - e = e.next() + e.doSomething() + e = e.next() } // Smart cast is not possible here due to next() return e diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infiniteWhileWithBreak.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infiniteWhileWithBreak.fir.kt index 0f13f5c561c..f13e2013809 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infiniteWhileWithBreak.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infiniteWhileWithBreak.fir.kt @@ -16,5 +16,5 @@ fun list(start: SomeObject) { e = e.next() } // e can be null because of next() - e.doSomething() + e.doSomething() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initInTryReturnInCatch.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initInTryReturnInCatch.fir.kt index 0788bc8222c..4163683d0d1 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initInTryReturnInCatch.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initInTryReturnInCatch.fir.kt @@ -76,5 +76,5 @@ fun test6() { finally { a = null } - a.hashCode() // a is null here + a.hashCode() // a is null here } diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initialization.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initialization.fir.kt index 6f206aa7d26..a5f4d31c26c 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initialization.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initialization.fir.kt @@ -4,5 +4,5 @@ fun foo() { // It is possible in principle to provide smart cast here v.length v = null - v.length + v.length } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTry.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTry.fir.kt index 71d9ce5881d..3a1e43458db 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTry.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTry.fir.kt @@ -6,5 +6,5 @@ fun foo() { try { s = null } catch (ex: Exception) {} - s.hashCode() + s.hashCode() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryFinally.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryFinally.fir.kt index fa0415c2556..ed810c42613 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryFinally.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryFinally.fir.kt @@ -12,5 +12,5 @@ fun foo() { finally { bar() } - s.hashCode() + s.hashCode() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryUnsound.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryUnsound.fir.kt index f6f286f9e36..95d50e9267d 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryUnsound.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryUnsound.fir.kt @@ -6,5 +6,5 @@ fun foo() { try { s = null } catch (ex: Exception) {} - s.hashCode() + s.hashCode() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.fir.kt index 2cb01a3ef43..918c32fc744 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.fir.kt @@ -2,5 +2,5 @@ fun foo(): Int { var i: Int? = 42 i = null - return i + 1 + return i + 1 } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNull.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNull.fir.kt index 4fbd84ed5f8..ffc12c87ecd 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNull.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNull.fir.kt @@ -2,5 +2,5 @@ fun foo(): Int { var s: String? = "abc" s = null - return s.length + return s.length } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrue.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrue.fir.kt index c288987a722..964baa75abc 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrue.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrue.fir.kt @@ -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.length + return p.length } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrueWithBracketSet.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrueWithBracketSet.fir.kt index d2c647b41e5..2fe1af49ac4 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrueWithBracketSet.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrueWithBracketSet.fir.kt @@ -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.length + return p.length } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrueWithBrackets.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrueWithBrackets.fir.kt index b2e4c1531a5..2c3a154ffdf 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrueWithBrackets.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrueWithBrackets.fir.kt @@ -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.length + return p.length } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileWithBreak.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileWithBreak.fir.kt index 70f77472023..0d5e3c38d9f 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileWithBreak.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileWithBreak.fir.kt @@ -13,5 +13,5 @@ fun list(start: SomeObject) { e = e.next() } // e can be null because of next() - e.doSomething() + e.doSomething() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/when/withSubjectVariable/capturingInInitializer.fir.kt b/compiler/testData/diagnostics/tests/when/withSubjectVariable/capturingInInitializer.fir.kt index ef0abfe66e9..d8ed1e80497 100644 --- a/compiler/testData/diagnostics/tests/when/withSubjectVariable/capturingInInitializer.fir.kt +++ b/compiler/testData/diagnostics/tests/when/withSubjectVariable/capturingInInitializer.fir.kt @@ -27,11 +27,11 @@ fun testUnsafeCaptureVarInInitializer() { val s = when (val y = run { x = null; 32 }) { 0 -> { - x.inc() // NB smart cast should be impossible + x.inc() // NB smart cast should be impossible "0" } else -> "!= 0" } - x.inc() // NB smart cast should be impossible + x.inc() // NB smart cast should be impossible } \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letAlwaysChangesToNotNull.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letAlwaysChangesToNotNull.fir.kt index 74556f770bc..3fa4b2468ec 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letAlwaysChangesToNotNull.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letAlwaysChangesToNotNull.fir.kt @@ -3,5 +3,5 @@ fun foo(y: String) { var x: String? = null y.let { x = it } - x.length // Smart cast is not possible + x.length // Smart cast is not possible } diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letMergeNotNull.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letMergeNotNull.fir.kt index 6a9784436fc..833bb2fecce 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letMergeNotNull.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letMergeNotNull.fir.kt @@ -4,6 +4,6 @@ fun foo(y: String?) { var x: String? = null if (x != null) { y?.let { x = it } - x.length // not-null or not-null + x.length // not-null or not-null } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/tryCatch/correctSmartcasts.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/tryCatch/correctSmartcasts.fir.kt index cb8f7e1632f..34a9ab25056 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/tryCatch/correctSmartcasts.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/tryCatch/correctSmartcasts.fir.kt @@ -15,7 +15,7 @@ fun test1(s: String?) { catch (e: Exception) { requireNotNull(s) } - t2.not() + t2.not() s.length } } @@ -45,7 +45,7 @@ fun test3() { s = null return } - s.length + s.length } fun test4() { @@ -61,7 +61,7 @@ fun test4() { catch (e: ExcB) { } - s.length + s.length } fun test5(s: String?) { diff --git a/compiler/testData/diagnostics/testsWithStdLib/tryCatch/correctSmartcasts_after.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/tryCatch/correctSmartcasts_after.fir.kt index 89584581b8c..ca50d78463d 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/tryCatch/correctSmartcasts_after.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/tryCatch/correctSmartcasts_after.fir.kt @@ -16,7 +16,7 @@ fun test1(s: String?) { catch (e: Exception) { requireNotNull(s) } - t2.not() + t2.not() s.length } } @@ -46,7 +46,7 @@ fun test3() { s = null return } - s.length + s.length } fun test4() { @@ -62,7 +62,7 @@ fun test4() { catch (e: ExcB) { } - s.length + s.length } fun test5(s: String?) { diff --git a/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts.fir.kt index cb73d68b460..1cadd1dd25a 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts.fir.kt @@ -13,15 +13,15 @@ fun test1() { try { x = null } catch (e: Exception) { - x.length // smartcast shouldn't be allowed (OOME could happen after `x = null`) + x.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.length + x.length } // smartcast shouldn't be allowed, `x = null` could've happened - x.length + x.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.length + x.length } finally { - x.length + x.length } - x.length + x.length } fun test3() { @@ -49,7 +49,7 @@ fun test3() { } catch (e: Exception) { t2 = null } - t2.not() // wrong smartcast, NPE + t2.not() // wrong smartcast, NPE } } @@ -59,7 +59,7 @@ fun test4() { try { t2 = null } finally { } - t2.not() // wrong smartcast, NPE + t2.not() // wrong smartcast, NPE } } @@ -80,10 +80,10 @@ fun test5() { } finally { s1.length - s2.length + s2.length } s1.length - s2.length + s2.length } fun test6(s1: String?, s2: String?) { @@ -97,10 +97,10 @@ fun test6(s1: String?, s2: String?) { return } finally { - s.length + s.length requireNotNull(s2) } - s.length + s.length s1.length s2.length } \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts_after.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts_after.fir.kt index 44309a29e07..2cd1a23c5ea 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts_after.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/tryCatch/falsePositiveSmartcasts_after.fir.kt @@ -14,15 +14,15 @@ fun test1() { try { x = null } catch (e: Exception) { - x.length // smartcast shouldn't be allowed (OOME could happen after `x = null`) + x.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.length + x.length } // smartcast shouldn't be allowed, `x = null` could've happened - x.length + x.length } // With old DFA of try/catch info about unsound smartcasts after try @@ -35,12 +35,12 @@ fun test2() { x = null } catch (e: Exception) { // BAD - x.length + x.length } finally { - x.length + x.length } - x.length + x.length } fun test3() { @@ -51,7 +51,7 @@ fun test3() { } catch (e: Exception) { t2 = null } - t2.not() // wrong smartcast, NPE + t2.not() // wrong smartcast, NPE } } @@ -61,7 +61,7 @@ fun test4() { try { t2 = null } finally { } - t2.not() // wrong smartcast, NPE + t2.not() // wrong smartcast, NPE } } @@ -82,10 +82,10 @@ fun test5() { } finally { s1.length - s2.length + s2.length } s1.length - s2.length + s2.length } fun test6(s1: String?, s2: String?) { @@ -99,10 +99,10 @@ fun test6(s1: String?, s2: String?) { return } finally { - s.length + s.length requireNotNull(s2) } - s.length + s.length s1.length s2.length } \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirResolution.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirResolution.kt index 7ffa22628a7..4872e4e970d 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirResolution.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/fir/FirResolution.kt @@ -12,7 +12,7 @@ object FirResolution { private const val optionName = "kotlin.use.fir.resolution" private val initialEnabledValue: Boolean by lazy { - Registry.`is`(optionName, /* defaultValue = */ false) + Registry.`is`(optionName, /* defaultValue = */ true) } private var changedEnabledValue: Boolean? = null diff --git a/prepare/idea-plugin/build.gradle.kts b/prepare/idea-plugin/build.gradle.kts index 046e7015a36..e75b5e2ca4e 100644 --- a/prepare/idea-plugin/build.gradle.kts +++ b/prepare/idea-plugin/build.gradle.kts @@ -142,6 +142,7 @@ dependencies { embedded(protobufFull()) embedded(kotlinBuiltins()) + libraries(commonDep("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.2")) libraries(commonDep("javax.inject")) libraries(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8")) libraries(commonDep("org.jetbrains", "markdown"))