[FIR] Fix smartcasts on member vals in init blocks

This commit is contained in:
Dmitriy Novozhilov
2020-02-10 12:09:53 +03:00
parent 36a6eedd9c
commit 6ed4229359
9 changed files with 83 additions and 30 deletions
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStackImpl import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStackImpl
import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.calls.ClassDispatchReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.resolve.dfa.contracts.buildContractFir import org.jetbrains.kotlin.fir.resolve.dfa.contracts.buildContractFir
@@ -629,14 +630,23 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
graphBuilder.exitConstExpresion(constExpression).mergeIncomingFlow() graphBuilder.exitConstExpresion(constExpression).mergeIncomingFlow()
} }
fun exitVariableDeclaration(variable: FirProperty) { fun exitLocalVariableDeclaration(variable: FirProperty) {
val node = graphBuilder.exitVariableDeclaration(variable).mergeIncomingFlow() val node = graphBuilder.exitVariableDeclaration(variable).mergeIncomingFlow()
val initializer = variable.initializer ?: return val initializer = variable.initializer ?: return
exitVariableInitialization(node, initializer, variable, isVariableDeclaration = true) exitVariableInitialization(node, initializer, variable, assignment = null)
} }
private fun exitVariableInitialization(node: CFGNode<*>, initializer: FirExpression, variable: FirProperty, isVariableDeclaration: Boolean) { fun exitVariableAssignment(assignment: FirVariableAssignment) {
var propertyVariable = variableStorage.getOrCreateRealVariable(variable.symbol, variable) val node = graphBuilder.exitVariableAssignment(assignment).mergeIncomingFlow()
val property = (assignment.lValue as? FirResolvedNamedReference)?.resolvedSymbol?.fir as? FirProperty ?: return
// TODO: add unstable smartcast
if (!property.isLocal && property.isVar) return
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) { if (!isVariableDeclaration) {
// Don't remove statements for variable under alias // Don't remove statements for variable under alias
if (propertyVariable.identifier.symbol == variable.symbol) { if (propertyVariable.identifier.symbol == variable.symbol) {
@@ -644,7 +654,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
} else { } else {
variableStorage.unboundPossiblyAliasedVariable(variable.symbol) variableStorage.unboundPossiblyAliasedVariable(variable.symbol)
// We should create new dataFlowVariable for local variable without alias // We should create new dataFlowVariable for local variable without alias
propertyVariable = variableStorage.getOrCreateRealVariable(variable.symbol, variable) propertyVariable = variableStorage.getOrCreateRealVariable(variable.symbol, assignment ?: variable)
} }
} }
@@ -674,12 +684,6 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
} }
} }
fun exitVariableAssignment(assignment: FirVariableAssignment) {
val node = graphBuilder.exitVariableAssignment(assignment).mergeIncomingFlow()
val property = (assignment.lValue as? FirResolvedNamedReference)?.resolvedSymbol?.fir as? FirProperty ?: return //error("left side of assignment should have symbol")
if (!property.isLocal) return
exitVariableInitialization(node, assignment.rValue, property, isVariableDeclaration = false)
}
fun exitThrowExceptionNode(throwExpression: FirThrowExpression) { fun exitThrowExceptionNode(throwExpression: FirThrowExpression) {
graphBuilder.exitThrowExceptionNode(throwExpression).mergeIncomingFlow() graphBuilder.exitThrowExceptionNode(throwExpression).mergeIncomingFlow()
@@ -6,10 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.dfa package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirWhenSubjectExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.references.FirThisReference import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
@@ -38,7 +35,7 @@ class VariableStorage {
fir: FirElement, fir: FirElement,
): Identifier { ): Identifier {
return localVariableAliases[symbol] ?: run { return localVariableAliases[symbol] ?: run {
val expression = fir as? FirQualifiedAccessExpression val expression = fir as? FirQualifiedAccess
Identifier( Identifier(
symbol, symbol,
expression?.dispatchReceiver?.takeIf { it != FirNoReceiverExpression }?.let(this::getOrCreateVariable), expression?.dispatchReceiver?.takeIf { it != FirNoReceiverExpression }?.let(this::getOrCreateVariable),
@@ -53,9 +50,11 @@ class VariableStorage {
private fun createRealVariableInternal(identifier: Identifier, originalFir: FirElement): RealVariable { private fun createRealVariableInternal(identifier: Identifier, originalFir: FirElement): RealVariable {
val receiver: FirExpression? val receiver: FirExpression?
val isThisReference: Boolean val isThisReference: Boolean
val expression = when (originalFir) { val expression: FirQualifiedAccess? = when (originalFir) {
is FirQualifiedAccessExpression -> originalFir is FirQualifiedAccessExpression -> originalFir
is FirWhenSubjectExpression -> originalFir.whenSubject.whenExpression.subject as? FirQualifiedAccessExpression is FirWhenSubjectExpression -> originalFir.whenSubject.whenExpression.subject as? FirQualifiedAccessExpression
is FirVariableAssignment -> originalFir
is FirArraySetCall -> originalFir
else -> null else -> null
} }
@@ -129,7 +129,7 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer)
variable.transformAccessors() variable.transformAccessors()
localScopes.lastOrNull()?.storeVariable(variable) localScopes.lastOrNull()?.storeVariable(variable)
variable.replaceResolvePhase(transformerPhase) variable.replaceResolvePhase(transformerPhase)
dataFlowAnalyzer.exitVariableDeclaration(variable) dataFlowAnalyzer.exitLocalVariableDeclaration(variable)
return variable.compose() return variable.compose()
} }
@@ -0,0 +1,55 @@
digraph smartCastInInit_kt {
graph [splines=ortho nodesep=3]
node [shape=box penwidth=2]
edge [penwidth=2]
subgraph cluster_0 {
color=red
0 [label="Enter function foo" style="filled" fillcolor=red];
1 [label="Exit function foo" style="filled" fillcolor=red];
}
0 -> {1};
subgraph cluster_1 {
color=red
2 [label="Enter function s" style="filled" fillcolor=red];
3 [label="Function call: R|kotlin/TODO|()"];
4 [label="Stub" style="filled" fillcolor=gray];
5 [label="Jump: ^s R|kotlin/TODO|()" style="filled" fillcolor=gray];
6 [label="Stub" style="filled" fillcolor=gray];
7 [label="Exit function s" style="filled" fillcolor=red];
}
2 -> {3};
3 -> {7};
3 -> {4} [style=dotted];
4 -> {5} [style=dotted];
5 -> {7 6} [style=dotted];
6 -> {7} [style=dotted];
subgraph cluster_2 {
color=red
8 [label="Enter function <init>" style="filled" fillcolor=red];
9 [label="Exit function <init>" style="filled" fillcolor=red];
}
8 -> {9};
subgraph cluster_3 {
color=red
10 [label="Enter function getter" style="filled" fillcolor=red];
11 [label="Exit function getter" style="filled" fillcolor=red];
}
10 -> {11};
subgraph cluster_4 {
color=red
12 [label="Enter property" style="filled" fillcolor=red];
13 [label="Exit property" style="filled" fillcolor=red];
}
12 -> {13};
}
@@ -9,6 +9,6 @@ class Main {
private val x: I private val x: I
init { init {
x = s() x = s()
x.<!UNRESOLVED_REFERENCE!>foo<!>() x.foo()
} }
} }
@@ -18,7 +18,7 @@ FILE: smartCastInInit.kt
init { init {
this@R|/Main|.R|/Main.x| = R|/s|() this@R|/Main|.R|/Main.x| = R|/s|()
this@R|/Main|.R|/Main.x|.<Unresolved name: foo>#() this@R|/Main|.R|/Main.x|.R|/S.foo|()
} }
} }
@@ -1176,11 +1176,6 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
runTest("compiler/fir/resolve/testData/resolve/problems/samConversionInConstructorCall.kt"); runTest("compiler/fir/resolve/testData/resolve/problems/samConversionInConstructorCall.kt");
} }
@TestMetadata("smartCastInInit.kt")
public void testSmartCastInInit() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/problems/smartCastInInit.kt");
}
@TestMetadata("syntheticsVsNormalProperties.kt") @TestMetadata("syntheticsVsNormalProperties.kt")
public void testSyntheticsVsNormalProperties() throws Exception { public void testSyntheticsVsNormalProperties() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/problems/syntheticsVsNormalProperties.kt"); runTest("compiler/fir/resolve/testData/resolve/problems/syntheticsVsNormalProperties.kt");
@@ -218,6 +218,11 @@ public class FirDiagnosticsWithCfgTestGenerated extends AbstractFirDiagnosticsWi
runTest("compiler/fir/resolve/testData/resolve/smartcasts/simpleIf.kt"); runTest("compiler/fir/resolve/testData/resolve/smartcasts/simpleIf.kt");
} }
@TestMetadata("smartCastInInit.kt")
public void testSmartCastInInit() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/smartCastInInit.kt");
}
@TestMetadata("smartcastAfterReassignment.kt") @TestMetadata("smartcastAfterReassignment.kt")
public void testSmartcastAfterReassignment() throws Exception { public void testSmartcastAfterReassignment() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.kt"); runTest("compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.kt");
@@ -1176,11 +1176,6 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/resolve/testData/resolve/problems/samConversionInConstructorCall.kt"); runTest("compiler/fir/resolve/testData/resolve/problems/samConversionInConstructorCall.kt");
} }
@TestMetadata("smartCastInInit.kt")
public void testSmartCastInInit() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/problems/smartCastInInit.kt");
}
@TestMetadata("syntheticsVsNormalProperties.kt") @TestMetadata("syntheticsVsNormalProperties.kt")
public void testSyntheticsVsNormalProperties() throws Exception { public void testSyntheticsVsNormalProperties() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/problems/syntheticsVsNormalProperties.kt"); runTest("compiler/fir/resolve/testData/resolve/problems/syntheticsVsNormalProperties.kt");