FIR: Reimplement implicit types calculator

Make it works through a single component tracking computation status
instead of storing it in the nodes
This commit is contained in:
Denis Zharkov
2020-01-23 11:35:56 +03:00
parent f256547cc8
commit c295f2dc25
11 changed files with 311 additions and 36 deletions
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase.*
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformerAdapter
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirImplicitTypeBodyResolveTransformerAdapter
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirImplicitTypeBodyResolveTransformerAdapter2
import org.jetbrains.kotlin.fir.visitors.FirTransformer
// TODO: add FirSession parameter
@@ -20,7 +21,7 @@ fun FirResolvePhase.createTransformerByPhase(): FirTransformer<Nothing?> {
SEALED_CLASS_INHERITORS -> FirSealedClassInheritorsTransformer()
TYPES -> FirTypeResolveTransformer()
STATUS -> FirStatusResolveTransformer()
IMPLICIT_TYPES_BODY_RESOLVE -> FirImplicitTypeBodyResolveTransformerAdapter()
IMPLICIT_TYPES_BODY_RESOLVE -> FirImplicitTypeBodyResolveTransformerAdapter2()
BODY_RESOLVE -> FirBodyResolveTransformerAdapter()
}
}
@@ -69,7 +69,6 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb
protected inline val symbolProvider: FirSymbolProvider get() = components.symbolProvider
protected inline val returnTypeCalculator: ReturnTypeCalculator get() = components.returnTypeCalculator
protected inline val implicitReceiverStack: ImplicitReceiverStack get() = components.implicitReceiverStack
protected inline val inferenceComponents: InferenceComponents get() = components.inferenceComponents
protected inline val resolutionStageRunner: ResolutionStageRunner get() = components.resolutionStageRunner
@@ -105,7 +104,7 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb
override val symbolProvider: FirSymbolProvider = session.firSymbolProvider
override val returnTypeCalculator: ReturnTypeCalculator = ReturnTypeCalculatorWithJump(session, scopeSession)
override val returnTypeCalculator: ReturnTypeCalculator = transformer.returnTypeCalculator
override val implicitReceiverStack: ImplicitReceiverStack = ImplicitReceiverStackImpl()
override val inferenceComponents: InferenceComponents = inferenceComponents(session, returnTypeCalculator, scopeSession)
override val resolutionStageRunner: ResolutionStageRunner = ResolutionStageRunner(inferenceComponents)
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorWithJump
import org.jetbrains.kotlin.fir.scopes.addImportingScopes
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
@@ -26,7 +28,8 @@ open class FirBodyResolveTransformer(
session: FirSession,
phase: FirResolvePhase,
override var implicitTypeOnly: Boolean,
scopeSession: ScopeSession
scopeSession: ScopeSession,
val returnTypeCalculator: ReturnTypeCalculator = ReturnTypeCalculatorWithJump(session, scopeSession)
) : FirAbstractBodyResolveTransformer(phase) {
private var packageFqName = FqName.ROOT
@@ -173,7 +176,7 @@ open class FirBodyResolveTransformer(
return declarationsTransformer.transformDeclarationStatus(declarationStatus, data)
}
override fun transformProperty(property: FirProperty, data: ResolutionMode): CompositeTransformResult<FirDeclaration> {
override fun transformProperty(property: FirProperty, data: ResolutionMode): CompositeTransformResult<FirProperty> {
return declarationsTransformer.transformProperty(property, data)
}
@@ -185,7 +188,7 @@ open class FirBodyResolveTransformer(
return declarationsTransformer.transformAnonymousObject(anonymousObject, data)
}
override fun transformSimpleFunction(simpleFunction: FirSimpleFunction, data: ResolutionMode): CompositeTransformResult<FirDeclaration> {
override fun transformSimpleFunction(simpleFunction: FirSimpleFunction, data: ResolutionMode): CompositeTransformResult<FirSimpleFunction> {
return declarationsTransformer.transformSimpleFunction(simpleFunction, data)
}
@@ -74,7 +74,7 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer)
}
}
override fun transformProperty(property: FirProperty, data: ResolutionMode): CompositeTransformResult<FirDeclaration> {
override fun transformProperty(property: FirProperty, data: ResolutionMode): CompositeTransformResult<FirProperty> {
return withScopeCleanup(topLevelScopes) {
prepareTypeParameterOwnerForBodyResolve(property)
if (property.isLocal) {
@@ -120,7 +120,7 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer)
}
}
private fun transformLocalVariable(variable: FirProperty): CompositeTransformResult<FirDeclaration> {
private fun transformLocalVariable(variable: FirProperty): CompositeTransformResult<FirProperty> {
assert(variable.isLocal)
val resolutionMode = withExpectedType(variable.returnTypeRef)
variable.transformInitializer(transformer, resolutionMode)
@@ -297,7 +297,7 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer)
}
}
override fun transformSimpleFunction(simpleFunction: FirSimpleFunction, data: ResolutionMode): CompositeTransformResult<FirDeclaration> {
override fun transformSimpleFunction(simpleFunction: FirSimpleFunction, data: ResolutionMode): CompositeTransformResult<FirSimpleFunction> {
return withScopeCleanup(topLevelScopes) {
prepareTypeParameterOwnerForBodyResolve(simpleFunction)
val containingDeclaration = components.containerIfAny
@@ -327,21 +327,32 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer)
}
}
private fun transformFunctionWithGivenSignature(
function: FirFunction<*>,
private fun <F : FirFunction<F>> transformFunctionWithGivenSignature(
function: F,
returnTypeRef: FirTypeRef
): CompositeTransformResult<FirDeclaration> {
): CompositeTransformResult<F> {
if (function is FirSimpleFunction) {
localScopes.lastOrNull()?.storeDeclaration(function)
}
val result = transformFunction(function, withExpectedType(returnTypeRef)).single as FirFunction<*>
@Suppress("UNCHECKED_CAST")
val result = transformFunction(function, withExpectedType(returnTypeRef)).single as F
val body = result.body
return if (result.returnTypeRef is FirImplicitTypeRef && body != null) {
result.transformReturnTypeRef(transformer, withExpectedType(body.resultType))
result
} else {
result
}.compose()
if (result.returnTypeRef is FirImplicitTypeRef) {
if (body != null) {
result.transformReturnTypeRef(transformer, withExpectedType(body.resultType))
} else {
result.transformReturnTypeRef(
transformer,
withExpectedType(
FirErrorTypeRefImpl(null, FirSimpleDiagnostic("empty body", DiagnosticKind.Other))
)
)
}
}
return result.compose()
}
override fun <F : FirFunction<F>> transformFunction(function: FirFunction<F>, data: ResolutionMode): CompositeTransformResult<FirStatement> {
@@ -466,7 +477,7 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer)
val writer = FirCallCompletionResultsWriterTransformer(
session,
ConeSubstitutor.Empty,
returnTypeCalculator,
components.returnTypeCalculator,
inferenceComponents.approximator,
integerOperatorsTypeUpdater,
integerLiteralTypeApproximator
@@ -0,0 +1,248 @@
/*
* Copyright 2010-2020 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.
*/
@file:Suppress("DuplicatedCode")
package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.diagnostics.FirSimpleDiagnostic
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.firProvider
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
import org.jetbrains.kotlin.fir.resolve.transformers.TransformImplicitType
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirErrorTypeRefImpl
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.compose
@Deprecated("It is temp", level = DeprecationLevel.WARNING, replaceWith = ReplaceWith("TODO(\"что-то нормальное\")"))
class FirImplicitTypeBodyResolveTransformerAdapter2 : FirTransformer<Nothing?>() {
private val scopeSession = ScopeSession()
private val implicitBodyResolveComputationSession = ImplicitBodyResolveComputationSession()
override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> {
return element.compose()
}
override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult<FirFile> {
val session = file.session
scopeSession.returnTypeCalculator = MyReturnTypeCalculatorWithJump(session, scopeSession, implicitBodyResolveComputationSession)
val transformer = MyFirBodyResolveTransformer(
session,
scopeSession,
implicitBodyResolveComputationSession
)
return file.transform(transformer, ResolutionMode.ContextIndependent)
}
}
private open class MyFirBodyResolveTransformer(
session: FirSession,
scopeSession: ScopeSession,
private val implicitBodyResolveComputationSession: ImplicitBodyResolveComputationSession
) : FirBodyResolveTransformer(
session,
phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
implicitTypeOnly = true,
scopeSession = scopeSession,
returnTypeCalculator = MyReturnTypeCalculatorWithJump(session, scopeSession, implicitBodyResolveComputationSession)
) {
override fun transformSimpleFunction(
simpleFunction: FirSimpleFunction,
data: ResolutionMode
): CompositeTransformResult<FirSimpleFunction> {
return computeCachedTransformationResult(simpleFunction) {
super.transformSimpleFunction(simpleFunction, data)
}
}
override fun transformProperty(property: FirProperty, data: ResolutionMode): CompositeTransformResult<FirProperty> {
return computeCachedTransformationResult(property) {
super.transformProperty(property, data)
}
}
private fun <D : FirCallableMemberDeclaration<D>> computeCachedTransformationResult(
member: D,
transform: () -> CompositeTransformResult<D>
): CompositeTransformResult<D> {
if (!implicitTypeOnly && member.returnTypeRef is FirResolvedTypeRef) {
return transform()
}
if (member.returnTypeRef is FirResolvedTypeRef) return member.compose()
val symbol = member.symbol
val status = implicitBodyResolveComputationSession.getStatus(symbol)
if (status is ImplicitBodyResolveComputationStatus.Computed) {
@Suppress("UNCHECKED_CAST")
return status.transformedDeclaration.compose() as CompositeTransformResult<D>
}
// If somebody has started resolution recursively (from ReturnTypeCalculator), one has to track it's not being computed already
require(status is ImplicitBodyResolveComputationStatus.NotComputed) {
"Unexpected status in transformCallableMember ($status) for ${member.render()}"
}
implicitBodyResolveComputationSession.startComputing(symbol)
val result = transform()
implicitBodyResolveComputationSession.storeResult(symbol, result.single)
return result
}
}
private class MyReturnTypeCalculatorWithJump(
private val session: FirSession,
private val scopeSession: ScopeSession,
private val implicitBodyResolveComputationSession: ImplicitBodyResolveComputationSession
) : ReturnTypeCalculator {
override fun tryCalculateReturnType(declaration: FirTypedDeclaration): FirResolvedTypeRef {
if (declaration is FirValueParameter && declaration.returnTypeRef is FirImplicitTypeRef) {
// TODO?
declaration.transformReturnTypeRef(
TransformImplicitType,
FirErrorTypeRefImpl(
null,
FirSimpleDiagnostic("Unsupported: implicit VP type")
)
)
}
val returnTypeRef = declaration.returnTypeRef
if (returnTypeRef is FirResolvedTypeRef) return returnTypeRef
require(declaration is FirCallableMemberDeclaration<*>) { "${declaration::class}: ${declaration.render()}" }
return when (val status = implicitBodyResolveComputationSession.getStatus(declaration.symbol)) {
is ImplicitBodyResolveComputationStatus.Computed -> status.resolvedTypeRef
is ImplicitBodyResolveComputationStatus.Computing ->
FirErrorTypeRefImpl(null, FirSimpleDiagnostic("cycle", DiagnosticKind.RecursionInImplicitTypes))
else -> computeReturnTypeRef(declaration)
}
}
private fun computeReturnTypeRef(declaration: FirCallableMemberDeclaration<*>): FirResolvedTypeRef {
val symbol = declaration.symbol
val id = symbol.callableId
val provider = session.firProvider
val file = provider.getFirCallableContainerFile(symbol)
val outerClasses = generateSequence(id.classId) { classId ->
classId.outerClassId
}.mapTo(mutableListOf()) { provider.getFirClassifierByFqName(it) }
if (file == null || outerClasses.any { it == null }) {
return FirErrorTypeRefImpl(
null,
FirSimpleDiagnostic(
"Cannot calculate return type (local class/object?)",
DiagnosticKind.InferenceError
)
)
}
val designation = (listOf(file) + outerClasses.filterNotNull().asReversed() + listOf(declaration))
val transformer = MyFirDesignatedBodyResolveTransformer(
designation.iterator(),
file.session,
scopeSession,
implicitBodyResolveComputationSession
)
file.transform<FirElement, ResolutionMode>(transformer, ResolutionMode.ContextDependent)
val transformedDeclaration = transformer.lastResult as? FirCallableMemberDeclaration<*>
?: error("Unexpected lastResult: ${transformer.lastResult?.render()}")
val newReturnTypeRef = transformedDeclaration.returnTypeRef
require(newReturnTypeRef is FirResolvedTypeRef) { transformedDeclaration.render() }
return newReturnTypeRef
}
}
private class MyFirDesignatedBodyResolveTransformer(
private val designation: Iterator<FirElement>,
session: FirSession,
scopeSession: ScopeSession,
implicitBodyResolveComputationSession: ImplicitBodyResolveComputationSession
) : MyFirBodyResolveTransformer(
session,
scopeSession,
implicitBodyResolveComputationSession
) {
var lastResult: FirElement? = null
override fun <E : FirElement> transformElement(element: E, data: ResolutionMode): CompositeTransformResult<E> {
if (designation.hasNext()) {
val result = designation.next().transform<E, ResolutionMode>(this, data).single
if (!designation.hasNext()) {
lastResult = result
}
return result.compose()
}
return super.transformElement(element, data)
}
override fun transformDeclaration(declaration: FirDeclaration, data: ResolutionMode): CompositeTransformResult<FirDeclaration> {
return components.withContainer(declaration) {
declaration.replaceResolvePhase(transformerPhase)
transformElement(declaration, data)
}
}
}
private class ImplicitBodyResolveComputationSession {
private val implicitBodyResolveStatusMap = hashMapOf<FirCallableSymbol<*>, ImplicitBodyResolveComputationStatus>()
fun getStatus(symbol: FirCallableSymbol<*>): ImplicitBodyResolveComputationStatus {
return implicitBodyResolveStatusMap[symbol] ?: ImplicitBodyResolveComputationStatus.NotComputed
}
fun startComputing(symbol: FirCallableSymbol<*>) {
require(implicitBodyResolveStatusMap[symbol] == null) {
"Unexpected static in startComputing for $symbol: ${implicitBodyResolveStatusMap[symbol]}"
}
implicitBodyResolveStatusMap[symbol] = ImplicitBodyResolveComputationStatus.Computing
}
fun storeResult(
symbol: FirCallableSymbol<*>,
transformedDeclaration: FirCallableMemberDeclaration<*>
) {
require(implicitBodyResolveStatusMap[symbol] == ImplicitBodyResolveComputationStatus.Computing) {
"Unexpected static in storeResult for $symbol: ${implicitBodyResolveStatusMap[symbol]}"
}
val returnTypeRef = transformedDeclaration.returnTypeRef
require(returnTypeRef is FirResolvedTypeRef) {
"Not FirResolvedTypeRef (${transformedDeclaration.receiverTypeRef?.render()}) in storeResult for: ${symbol.fir.render()}"
}
implicitBodyResolveStatusMap[symbol] = ImplicitBodyResolveComputationStatus.Computed(returnTypeRef, transformedDeclaration)
}
}
private sealed class ImplicitBodyResolveComputationStatus {
object NotComputed : ImplicitBodyResolveComputationStatus()
object Computing : ImplicitBodyResolveComputationStatus()
class Computed(
val resolvedTypeRef: FirResolvedTypeRef,
val transformedDeclaration: FirCallableMemberDeclaration<*>
) : ImplicitBodyResolveComputationStatus()
}
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorWithJump
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.impl.*
@@ -80,7 +81,8 @@ class FirClassSubstitutionScope(
useSiteMemberScope.processClassifiersByName(name, processor)
}
private val typeCalculator by lazy { ReturnTypeCalculatorWithJump(session, scopeSession) }
private val typeCalculator =
(scopeSession.returnTypeCalculator as ReturnTypeCalculator?) ?: ReturnTypeCalculatorWithJump(session, scopeSession)
private fun ConeKotlinType.substitute(): ConeKotlinType? {
return substitutor.substituteOrNull(this)
@@ -221,7 +223,7 @@ class FirClassSubstitutionScope(
FirSimpleFunctionImpl(
source,
session,
baseFunction.returnTypeRef.withReplacedConeType(newReturnType),
baseFunction.returnTypeRef.withReplacedReturnType(newReturnType),
baseFunction.receiverTypeRef?.withReplacedConeType(newReceiverType),
name,
baseFunction.status,
@@ -286,7 +288,7 @@ class FirClassSubstitutionScope(
FirPropertyImpl(
source,
session,
baseProperty.returnTypeRef.withReplacedConeType(newReturnType),
baseProperty.returnTypeRef.withReplacedReturnType(newReturnType),
baseProperty.receiverTypeRef?.withReplacedConeType(newReceiverType),
name,
null,
@@ -337,6 +339,16 @@ class FirClassSubstitutionScope(
}
}
// Unlike other cases, return types may be implicit, i.e. unresolved
// But in that cases newType should also be `null`
fun FirTypeRef.withReplacedReturnType(newType: ConeKotlinType?): FirTypeRef {
require(this is FirResolvedTypeRef || newType == null)
if (newType == null) return this
return FirResolvedTypeRefImpl(source, newType).apply {
annotations += this@withReplacedReturnType.annotations
}
}
fun FirTypeRef.withReplacedConeType(newType: ConeKotlinType?): FirResolvedTypeRef {
require(this is FirResolvedTypeRef)
@@ -345,5 +357,4 @@ fun FirTypeRef.withReplacedConeType(newType: ConeKotlinType?): FirResolvedTypeRe
return FirResolvedTypeRefImpl(source, newType).apply {
annotations += this@withReplacedConeType.annotations
}
}
@@ -10,6 +10,8 @@ import org.jetbrains.kotlin.fir.scopes.FirScope
class ScopeSession {
private val scopes = hashMapOf<Any, HashMap<ScopeSessionKey<*, *>, FirScope>>()
var returnTypeCalculator: Any? = null
@Deprecated(level = DeprecationLevel.ERROR, message = "Only for getOrBuild")
fun scopes() = scopes
@@ -7,7 +7,7 @@ object Outer {
get() = 0
override fun get(index: Int): Char {
<!UNRESOLVED_REFERENCE!>checkSubtype<!><CharSequence>(x)
<!INAPPLICABLE_CANDIDATE!>checkSubtype<!><CharSequence>(x)
return ' '
}
@@ -17,7 +17,7 @@ object Outer {
}
}
set(q) {
<!INAPPLICABLE_CANDIDATE!>checkSubtype<!><CharSequence>(x)
checkSubtype<CharSequence>(x)
y = q
x = q
}
@@ -27,10 +27,10 @@ object Outer {
fun foo() {
x = y
<!INAPPLICABLE_CANDIDATE!>checkSubtype<!><CharSequence>(x)
<!INAPPLICABLE_CANDIDATE!>checkSubtype<!><CharSequence>(y)
checkSubtype<CharSequence>(x)
checkSubtype<CharSequence>(y)
x.<!UNRESOLVED_REFERENCE!>bar<!>()
y.<!UNRESOLVED_REFERENCE!>bar<!>()
x.bar()
y.bar()
}
}
@@ -10,8 +10,8 @@ class A {
val a get() = b
val b get() = a
val z1 get() = <!UNRESOLVED_REFERENCE!>id<!>(z1)
val z2 get() = <!UNRESOLVED_REFERENCE!>l<!>(z2)
val z1 get() = <!INAPPLICABLE_CANDIDATE!>id<!>(z1)
val z2 get() = <!INAPPLICABLE_CANDIDATE!>l<!>(z2)
val u get() = field
}
@@ -7,7 +7,7 @@ class Test {
val a = y;
}
val z = y.<!UNRESOLVED_REFERENCE!>a<!>;
val z = y.a;
}
@@ -24,6 +24,6 @@ class Test2 {
val y = 1
}
val b = a.x
val b = a.<!UNRESOLVED_REFERENCE!>x<!>
val c = a.y
}
}
+1 -1
View File
@@ -26,4 +26,4 @@ class Test2 {
val b = <!NI;TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!><!NI;DEBUG_INFO_MISSING_UNRESOLVED, OI;TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>a<!>.<!NI;DEBUG_INFO_MISSING_UNRESOLVED, OI;DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>x<!><!>
val c = a.y
}
}