From fd84202c084f9f67d9026dc39f258a921da2d7e8 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Thu, 29 Aug 2019 12:20:48 +0300 Subject: [PATCH] [FIR] Add fir node for qualified access with smartcast --- .../resolve/dfa/DummyFirDataFlowAnalyzer.kt | 5 +-- .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 2 +- .../resolve/dfa/FirDataFlowAnalyzerImpl.kt | 7 +-- .../transformers/FirBodyResolveTransformer.kt | 17 +++++-- .../expressions/FirExpressionWithSmartcast.kt | 37 +++++++++++++++ .../impl/FirExpressionWithSmartcastImpl.kt | 45 +++++++++++++++++++ .../fir/visitors/FirTransformerGenerated.kt | 8 ++++ .../fir/visitors/FirVisitorGenerated.kt | 4 ++ .../fir/visitors/FirVisitorVoidGenerated.kt | 8 ++++ 9 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirExpressionWithSmartcast.kt create mode 100644 compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/impl/FirExpressionWithSmartcastImpl.kt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DummyFirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DummyFirDataFlowAnalyzer.kt index 31de901d446..bef8d4dbf17 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DummyFirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DummyFirDataFlowAnalyzer.kt @@ -11,15 +11,14 @@ import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph import org.jetbrains.kotlin.fir.types.ConeKotlinType -import org.jetbrains.kotlin.fir.types.coneTypeUnsafe class DummyFirDataFlowAnalyzer : FirDataFlowAnalyzer() { companion object { private const val DUMMY = "" } - override fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): ConeKotlinType? { - return qualifiedAccessExpression.typeRef.coneTypeUnsafe() + override fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection? { + return null } override fun enterFunction(function: FirFunction<*>) {} 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 4ae2ade4caf..10945559f79 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 @@ -13,7 +13,7 @@ import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph import org.jetbrains.kotlin.fir.types.ConeKotlinType abstract class FirDataFlowAnalyzer { - abstract fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): ConeKotlinType? + abstract fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection? // ----------------------------------- Named function ----------------------------------- diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt index 876d229de86..6289ce31eef 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt @@ -39,13 +39,10 @@ class FirDataFlowAnalyzerImpl(transformer: FirBodyResolveTransformer) : FirDataF private val variableStorage = DataFlowVariableStorage() private val edges = mutableMapOf, Flow>().withDefault { Flow.EMPTY } - override fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): ConeKotlinType? { + override fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection? { val symbol: FirBasedSymbol<*> = qualifiedAccessExpression.resolvedSymbol ?: return null val variable = variableStorage[symbol]?.real ?: return null - val smartCastTypes = graphBuilder.lastNode.flow.approvedFacts(variable)?.exactType ?: return null - val smartCastType = context.myIntersectTypes(smartCastTypes.toList()) ?: return null - val originalType = qualifiedAccessExpression.typeRef.coneTypeSafe() ?: return null - return ConeTypeIntersector.intersectTypesFromSmartcasts(context, originalType, smartCastType) + return graphBuilder.lastNode.flow.approvedFacts(variable)?.exactType ?: return null } // ----------------------------------- Named function ----------------------------------- diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt index f9be62eaf6c..8cc6fe6cb05 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor import org.jetbrains.kotlin.fir.declarations.impl.FirValueParameterImpl import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionWithSmartcastImpl import org.jetbrains.kotlin.fir.references.FirExplicitThisReference import org.jetbrains.kotlin.fir.references.FirSimpleNamedReference import org.jetbrains.kotlin.fir.resolve.* @@ -246,11 +247,19 @@ open class FirBodyResolveTransformer( transformedCallee } if (result is FirQualifiedAccessExpression) { - dataFlowAnalyzer.getTypeUsingSmartcastInfo(result)?.let { typeWithSmartCasts -> - result.resultType = FirResolvedTypeRefImpl(result.psi, typeWithSmartCasts, result.resultType.annotations) - } + dataFlowAnalyzer.getTypeUsingSmartcastInfo(result)?.let { typesFromSmartCast -> + val allTypes = typesFromSmartCast.toMutableList().also { + it += result.resultType.coneTypeUnsafe() + } + val intersectedType = ConeTypeIntersector.intersectTypes(inferenceComponents.ctx as ConeInferenceContext, allTypes) + // TODO: add check that intersectedType is not equal to original type + FirExpressionWithSmartcastImpl(result, typesFromSmartCast).also { + it.resultType = FirResolvedTypeRefImpl(result.psi, intersectedType, result.resultType.annotations) + } + } ?: result + } else { + result } - result } } if (result is FirQualifiedAccessExpression) { diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirExpressionWithSmartcast.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirExpressionWithSmartcast.kt new file mode 100644 index 00000000000..8f3bf403b7e --- /dev/null +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/FirExpressionWithSmartcast.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.expressions + +import org.jetbrains.kotlin.fir.FirReference +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.fir.visitors.FirVisitor + +abstract class FirExpressionWithSmartcast( + open val originalExpression: FirQualifiedAccessExpression, + val typesFromSmartcast: Collection +) : FirQualifiedAccessExpression(originalExpression.psi) { + val originalType: FirTypeRef get() = originalExpression.typeRef + + final override val safe: Boolean + get() = originalExpression.safe + + final override val explicitReceiver: FirExpression? + get() = originalExpression.explicitReceiver + + final override val dispatchReceiver: FirExpression + get() = originalExpression.dispatchReceiver + + final override val extensionReceiver: FirExpression + get() = originalExpression.extensionReceiver + + final override val calleeReference: FirReference + get() = originalExpression.calleeReference + + override fun acceptChildren(visitor: FirVisitor, data: D) { + originalExpression.acceptChildren(visitor, data) + } +} \ No newline at end of file diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/impl/FirExpressionWithSmartcastImpl.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/impl/FirExpressionWithSmartcastImpl.kt new file mode 100644 index 00000000000..1ccea780ead --- /dev/null +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/impl/FirExpressionWithSmartcastImpl.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.expressions.impl + +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.transformSingle +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef +import org.jetbrains.kotlin.fir.visitors.FirTransformer + +class FirExpressionWithSmartcastImpl( + override var originalExpression: FirQualifiedAccessExpression, + typesFromSmartcast: Collection +) : FirExpressionWithSmartcast(originalExpression, typesFromSmartcast) { + init { + assert(originalExpression.typeRef is FirResolvedTypeRef) + } + + override fun transformChildren(transformer: FirTransformer, data: D): FirElement { + originalExpression = originalExpression.transformSingle(transformer, data) + return this + } + + override fun transformCalleeReference(transformer: FirTransformer, data: D): FirQualifiedAccess { + throw IllegalStateException() + } + + override fun transformExplicitReceiver(transformer: FirTransformer, data: D): FirQualifiedAccess { + throw IllegalStateException() + } + + override fun transformDispatchReceiver(transformer: FirTransformer, data: D): FirQualifiedAccess { + throw IllegalStateException() + } + + override fun transformExtensionReceiver(transformer: FirTransformer, data: D): FirQualifiedAccess { + throw IllegalStateException() + } +} \ No newline at end of file diff --git a/compiler/fir/tree/visitors/org/jetbrains/kotlin/fir/visitors/FirTransformerGenerated.kt b/compiler/fir/tree/visitors/org/jetbrains/kotlin/fir/visitors/FirTransformerGenerated.kt index 3aca69513e2..7848f69fabd 100644 --- a/compiler/fir/tree/visitors/org/jetbrains/kotlin/fir/visitors/FirTransformerGenerated.kt +++ b/compiler/fir/tree/visitors/org/jetbrains/kotlin/fir/visitors/FirTransformerGenerated.kt @@ -314,6 +314,10 @@ abstract class FirTransformer : FirVisitor { + return transformUnknownTypeExpression(expressionWithSmartcast, data) + } + open fun transformQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression, data: D): CompositeTransformResult { return transformUnknownTypeExpression(qualifiedAccessExpression, data) } @@ -630,6 +634,10 @@ abstract class FirTransformer : FirVisitor { + return transformExpressionWithSmartcast(expressionWithSmartcast, data) + } + final override fun visitField(field: FirField, data: D): CompositeTransformResult { return transformField(field, data) } diff --git a/compiler/fir/tree/visitors/org/jetbrains/kotlin/fir/visitors/FirVisitorGenerated.kt b/compiler/fir/tree/visitors/org/jetbrains/kotlin/fir/visitors/FirVisitorGenerated.kt index 089ea8f0880..6db62067b07 100644 --- a/compiler/fir/tree/visitors/org/jetbrains/kotlin/fir/visitors/FirVisitorGenerated.kt +++ b/compiler/fir/tree/visitors/org/jetbrains/kotlin/fir/visitors/FirVisitorGenerated.kt @@ -314,6 +314,10 @@ abstract class FirVisitor { return visitUnknownTypeExpression(errorExpression, data) } + open fun visitExpressionWithSmartcast(expressionWithSmartcast: FirExpressionWithSmartcast, data: D): R { + return visitUnknownTypeExpression(expressionWithSmartcast, data) + } + open fun visitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression, data: D): R { return visitUnknownTypeExpression(qualifiedAccessExpression, data) } diff --git a/compiler/fir/tree/visitors/org/jetbrains/kotlin/fir/visitors/FirVisitorVoidGenerated.kt b/compiler/fir/tree/visitors/org/jetbrains/kotlin/fir/visitors/FirVisitorVoidGenerated.kt index 59cda450bf8..bc3228ca702 100644 --- a/compiler/fir/tree/visitors/org/jetbrains/kotlin/fir/visitors/FirVisitorVoidGenerated.kt +++ b/compiler/fir/tree/visitors/org/jetbrains/kotlin/fir/visitors/FirVisitorVoidGenerated.kt @@ -314,6 +314,10 @@ abstract class FirVisitorVoid : FirVisitor() { visitUnknownTypeExpression(errorExpression, null) } + open fun visitExpressionWithSmartcast(expressionWithSmartcast: FirExpressionWithSmartcast) { + visitUnknownTypeExpression(expressionWithSmartcast, null) + } + open fun visitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) { visitUnknownTypeExpression(qualifiedAccessExpression, null) } @@ -630,6 +634,10 @@ abstract class FirVisitorVoid : FirVisitor() { visitExpression(expression) } + final override fun visitExpressionWithSmartcast(expressionWithSmartcast: FirExpressionWithSmartcast, data: Nothing?) { + visitExpressionWithSmartcast(expressionWithSmartcast) + } + final override fun visitField(field: FirField, data: Nothing?) { visitField(field) }