[FIR] Fix smartcasts on receivers of implict invoke call

This commit is contained in:
Dmitriy Novozhilov
2019-11-13 15:37:15 +03:00
parent 660bcce90a
commit 833648d8e0
10 changed files with 116 additions and 31 deletions
@@ -13,12 +13,15 @@ import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.diagnostics.FirEmptyDiagnostic import org.jetbrains.kotlin.fir.diagnostics.FirEmptyDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.FirSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.FirSimpleDiagnostic
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirResolvable import org.jetbrains.kotlin.fir.expressions.FirResolvable
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionWithSmartcastImpl
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.FirThisReference import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
@@ -345,3 +348,16 @@ private fun BodyResolveComponents.typeFromSymbol(symbol: AbstractFirBasedSymbol<
else -> error("WTF ! $symbol") else -> error("WTF ! $symbol")
} }
} }
fun BodyResolveComponents.transformQualifiedAccessUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): FirQualifiedAccessExpression {
val typesFromSmartCast = dataFlowAnalyzer.getTypeUsingSmartcastInfo(qualifiedAccessExpression) ?: return qualifiedAccessExpression
val allTypes = typesFromSmartCast.toMutableList().also {
it += qualifiedAccessExpression.resultType.coneTypeUnsafe<ConeKotlinType>()
}
val intersectedType = ConeTypeIntersector.intersectTypes(inferenceComponents.ctx as ConeInferenceContext, allTypes)
// TODO: add check that intersectedType is not equal to original type
val intersectedTypeRef = FirResolvedTypeRefImpl(qualifiedAccessExpression.resultType.source, intersectedType).apply {
annotations += qualifiedAccessExpression.resultType.annotations
}
return FirExpressionWithSmartcastImpl(qualifiedAccessExpression, intersectedTypeRef, typesFromSmartCast)
}
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.calls.InferenceComponents import org.jetbrains.kotlin.fir.resolve.calls.InferenceComponents
import org.jetbrains.kotlin.fir.resolve.calls.ResolutionStageRunner import org.jetbrains.kotlin.fir.resolve.calls.ResolutionStageRunner
import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer
import org.jetbrains.kotlin.fir.resolve.transformers.FirSyntheticCallGenerator import org.jetbrains.kotlin.fir.resolve.transformers.FirSyntheticCallGenerator
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
import org.jetbrains.kotlin.fir.resolve.transformers.phasedFir import org.jetbrains.kotlin.fir.resolve.transformers.phasedFir
@@ -37,6 +38,7 @@ interface BodyResolveComponents : SessionHolder {
val callResolver: FirCallResolver val callResolver: FirCallResolver
val doubleColonExpressionResolver: FirDoubleColonExpressionResolver val doubleColonExpressionResolver: FirDoubleColonExpressionResolver
val syntheticCallGenerator: FirSyntheticCallGenerator val syntheticCallGenerator: FirSyntheticCallGenerator
val dataFlowAnalyzer: FirDataFlowAnalyzer
val <D> AbstractFirBasedSymbol<D>.phasedFir: D where D : FirDeclaration, D : FirSymbolOwner<D> val <D> AbstractFirBasedSymbol<D>.phasedFir: D where D : FirDeclaration, D : FirSymbolOwner<D>
get() = phasedFir(session, FirResolvePhase.DECLARATIONS) get() = phasedFir(session, FirResolvePhase.DECLARATIONS)
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.expressions.impl.FirQualifiedAccessExpressionImpl import org.jetbrains.kotlin.fir.expressions.impl.FirQualifiedAccessExpressionImpl
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.transformQualifiedAccessUsingSmartcastInfo
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.firUnsafe import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.firUnsafe
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -81,18 +82,21 @@ class InvokeReceiverCandidateCollector(
if (applicability >= CandidateApplicability.SYNTHETIC_RESOLVED) { if (applicability >= CandidateApplicability.SYNTHETIC_RESOLVED) {
val session = components.session val session = components.session
val explicitReceiver = FirQualifiedAccessExpressionImpl(null).apply {
calleeReference = FirNamedReferenceWithCandidate(
null,
(candidate.symbol as FirCallableSymbol<*>).callableId.callableName,
candidate
)
dispatchReceiver = candidate.dispatchReceiverExpression()
extensionReceiver = candidate.extensionReceiverExpression()
typeRef = towerResolver.typeCalculator.tryCalculateReturnType(candidate.symbol.firUnsafe())
}.let {
components.transformQualifiedAccessUsingSmartcastInfo(it)
}
val boundInvokeCallInfo = CallInfo( val boundInvokeCallInfo = CallInfo(
invokeCallInfo.callKind, invokeCallInfo.callKind,
FirQualifiedAccessExpressionImpl(null).apply { explicitReceiver,
calleeReference = FirNamedReferenceWithCandidate(
null,
(candidate.symbol as FirCallableSymbol<*>).callableId.callableName,
candidate
)
dispatchReceiver = candidate.dispatchReceiverExpression()
extensionReceiver = candidate.extensionReceiverExpression()
typeRef = towerResolver.typeCalculator.tryCalculateReturnType(candidate.symbol.firUnsafe())
},
invokeCallInfo.arguments, invokeCallInfo.arguments,
invokeCallInfo.isSafeCall, invokeCallInfo.isSafeCall,
invokeCallInfo.typeArguments, invokeCallInfo.typeArguments,
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.references.impl.FirExplicitThisReference
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
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.FirNamedReferenceWithCandidate
import org.jetbrains.kotlin.fir.resolve.dfa.Condition.* import org.jetbrains.kotlin.fir.resolve.dfa.Condition.*
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
@@ -542,10 +543,11 @@ class FirDataFlowAnalyzer(private val components: FirAbstractBodyResolveTransfor
private val FirResolvable.resolvedSymbol: AbstractFirBasedSymbol<*>? private val FirResolvable.resolvedSymbol: AbstractFirBasedSymbol<*>?
get() = calleeReference.let { get() = calleeReference.let {
if (it is FirExplicitThisReference) { when (it) {
it.boundSymbol is FirExplicitThisReference -> it.boundSymbol
} else { is FirResolvedNamedReference -> it.resolvedSymbol
(it as? FirResolvedNamedReference)?.resolvedSymbol is FirNamedReferenceWithCandidate -> it.candidateSymbol
else -> null
} }
} }
@@ -778,7 +780,7 @@ class FirDataFlowAnalyzer(private val components: FirAbstractBodyResolveTransfor
private val FirElement.realVariable: RealDataFlowVariable? private val FirElement.realVariable: RealDataFlowVariable?
get() { get() {
val symbol: AbstractFirBasedSymbol<*> = if (this is FirThisReceiverExpressionImpl) { val symbol: AbstractFirBasedSymbol<*> = if (this is FirThisReceiverExpression) {
calleeReference.boundSymbol calleeReference.boundSymbol
} else { } else {
resolvedSymbol resolvedSymbol
@@ -120,7 +120,7 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb
FirTypeResolveScopeForBodyResolve(topLevelScopes, implicitReceiverStack, localScopes), session FirTypeResolveScopeForBodyResolve(topLevelScopes, implicitReceiverStack, localScopes), session
) )
val callCompleter: FirCallCompleter = FirCallCompleter(transformer, this) val callCompleter: FirCallCompleter = FirCallCompleter(transformer, this)
val dataFlowAnalyzer: FirDataFlowAnalyzer = FirDataFlowAnalyzer(this) override val dataFlowAnalyzer: FirDataFlowAnalyzer = FirDataFlowAnalyzer(this)
override val syntheticCallGenerator: FirSyntheticCallGenerator = FirSyntheticCallGenerator(this, callCompleter) override val syntheticCallGenerator: FirSyntheticCallGenerator = FirSyntheticCallGenerator(this, callCompleter)
internal var containerIfAny: FirDeclaration? = null internal var containerIfAny: FirDeclaration? = null
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.fir.resolve.diagnostics.FirOperatorAmbiguityError
import org.jetbrains.kotlin.fir.resolve.diagnostics.FirVariableExpectedError import org.jetbrains.kotlin.fir.resolve.diagnostics.FirVariableExpectedError
import org.jetbrains.kotlin.fir.resolve.transformers.InvocationKindTransformer import org.jetbrains.kotlin.fir.resolve.transformers.InvocationKindTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.StoreReceiver import org.jetbrains.kotlin.fir.resolve.transformers.StoreReceiver
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
import org.jetbrains.kotlin.fir.scopes.impl.withReplacedConeType import org.jetbrains.kotlin.fir.scopes.impl.withReplacedConeType
import org.jetbrains.kotlin.fir.symbols.StandardClassIds import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
@@ -122,25 +121,12 @@ class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransformer) :
} }
} }
if (result is FirQualifiedAccessExpression) { if (result is FirQualifiedAccessExpression) {
result = transformQualifiedAccessUsingSmartcastInfo(result) result = components.transformQualifiedAccessUsingSmartcastInfo(result)
dataFlowAnalyzer.exitQualifiedAccessExpression(result) dataFlowAnalyzer.exitQualifiedAccessExpression(result)
} }
return result.compose() return result.compose()
} }
private fun transformQualifiedAccessUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): FirQualifiedAccessExpression {
val typesFromSmartCast = dataFlowAnalyzer.getTypeUsingSmartcastInfo(qualifiedAccessExpression) ?: return qualifiedAccessExpression
val allTypes = typesFromSmartCast.toMutableList().also {
it += qualifiedAccessExpression.resultType.coneTypeUnsafe<ConeKotlinType>()
}
val intersectedType = ConeTypeIntersector.intersectTypes(inferenceComponents.ctx as ConeInferenceContext, allTypes)
// TODO: add check that intersectedType is not equal to original type
val intersectedTypeRef = FirResolvedTypeRefImpl(qualifiedAccessExpression.resultType.source, intersectedType).apply {
annotations += qualifiedAccessExpression.resultType.annotations
}
return FirExpressionWithSmartcastImpl(qualifiedAccessExpression, intersectedTypeRef, typesFromSmartCast)
}
override fun transformFunctionCall(functionCall: FirFunctionCall, data: ResolutionMode): CompositeTransformResult<FirStatement> { override fun transformFunctionCall(functionCall: FirFunctionCall, data: ResolutionMode): CompositeTransformResult<FirStatement> {
if (functionCall.calleeReference is FirResolvedNamedReference && functionCall.resultType is FirImplicitTypeRef) { if (functionCall.calleeReference is FirResolvedNamedReference && functionCall.resultType is FirImplicitTypeRef) {
storeTypeFromCallee(functionCall) storeTypeFromCallee(functionCall)
@@ -0,0 +1,56 @@
digraph smartcastOnLambda_kt {
graph [splines=ortho nodesep=3]
node [shape=box penwidth=2]
edge [penwidth=2]
subgraph cluster_0 {
color=red
0 [label="Enter function test" style="filled" fillcolor=red];
subgraph cluster_1 {
color=blue
1 [label="Enter block"];
subgraph cluster_2 {
color=blue
2 [label="Enter when"];
subgraph cluster_3 {
color=blue
3 [label="Enter when branch condition "];
4 [label="Access variable R|<local>/func|"];
5 [label="Const: Null(null)"];
6 [label="Operator !="];
7 [label="Exit when branch condition"];
}
8 [label="Synthetic else branch"];
9 [label="Enter when branch result"];
subgraph cluster_4 {
color=blue
10 [label="Enter block"];
11 [label="Function call: R|<local>/func|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
12 [label="Exit block"];
}
13 [label="Exit when branch result"];
14 [label="Exit when"];
}
15 [label="Exit block"];
}
16 [label="Exit function test" style="filled" fillcolor=red];
}
0 -> {1};
1 -> {2};
2 -> {3};
3 -> {4};
4 -> {5};
5 -> {6};
6 -> {7};
7 -> {9 8};
8 -> {14};
9 -> {10};
10 -> {11};
11 -> {12};
12 -> {13};
13 -> {14};
14 -> {15};
15 -> {16};
}
@@ -0,0 +1,5 @@
fun test(func: (() -> Unit)?) {
if (func != null) {
func()
}
}
@@ -0,0 +1,9 @@
FILE: smartcastOnLambda.kt
public final fun test(func: R|() -> kotlin/Unit|): R|kotlin/Unit| {
when () {
!=(R|<local>/func|, Null(null)) -> {
R|<local>/func|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
}
}
}
@@ -193,6 +193,11 @@ public class FirDiagnosticsWithCfgTestGenerated extends AbstractFirDiagnosticsWi
runTest("compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.kt"); runTest("compiler/fir/resolve/testData/resolve/smartcasts/smartcastAfterReassignment.kt");
} }
@TestMetadata("smartcastOnLambda.kt")
public void testSmartcastOnLambda() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/smartcastOnLambda.kt");
}
@TestMetadata("when.kt") @TestMetadata("when.kt")
public void testWhen() throws Exception { public void testWhen() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/when.kt"); runTest("compiler/fir/resolve/testData/resolve/smartcasts/when.kt");