[FIR] Fix smartcasts on receivers of implict invoke call
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
+14
-10
@@ -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,
|
||||||
|
|||||||
+7
-5
@@ -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
|
||||||
|
|||||||
+1
-1
@@ -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
|
||||||
|
|||||||
+1
-15
@@ -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|>|()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Generated
+5
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user