From 334c42e8ab1067ab6c92c935a44ea8be3f554aa2 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Tue, 12 Mar 2019 17:37:37 +0300 Subject: [PATCH] FIR: Implement deep implicit types resolution --- .../kotlin/fir/resolve/FirProvider.kt | 3 + .../kotlin/fir/resolve/ResolveUtils.kt | 5 + .../kotlin/fir/resolve/ScopeUtils.kt | 34 ++ .../kotlin/fir/resolve/calls/CallResolver.kt | 311 +++++++++++++ .../fir/resolve/impl/FirProviderImpl.kt | 6 + .../transformers/FirBodyResolveTransformer.kt | 418 ++++-------------- .../FirTotalResolveTransformer.kt | 1 + .../expresssions/invoke/implicitTypeOrder.kt | 10 + .../expresssions/invoke/implicitTypeOrder.txt | 18 + .../expresssions/localImplicitBodies.kt | 7 + .../expresssions/localImplicitBodies.txt | 17 + .../fir/FirResolveTestCaseGenerated.java | 10 + .../kotlin/fir/FirResolveTestTotalKotlin.kt | 2 +- .../kotlin/fir/types/FirErrorTypeRef.kt | 2 +- .../fir/types/impl/FirErrorTypeRefImpl.kt | 4 + 15 files changed, 514 insertions(+), 334 deletions(-) create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.kt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.txt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.kt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.txt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirProvider.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirProvider.kt index d21e528ead5..2f68720622b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirProvider.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirProvider.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.resolve import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirDeclarationContainer import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration import org.jetbrains.kotlin.fir.service @@ -29,6 +30,8 @@ interface FirProvider : FirSymbolProvider { fun getFirClassifierContainerFile(fqName: ClassId): FirFile + fun getFirCallableContainerFile(callableId: CallableId): FirFile? + companion object { fun getInstance(session: FirSession): FirProvider = session.service() } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt index 38586ed3f2f..bd5ecc0caac 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt @@ -6,7 +6,12 @@ package org.jetbrains.kotlin.fir.resolve import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.FirTypeParameter import org.jetbrains.kotlin.fir.declarations.expandedConeType +import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe +import org.jetbrains.kotlin.fir.scopes.FirScope +import org.jetbrains.kotlin.fir.scopes.impl.FirCompositeScope import org.jetbrains.kotlin.fir.symbols.* import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt new file mode 100644 index 00000000000..242dfb0024e --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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.resolve + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.FirTypeParameter +import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe +import org.jetbrains.kotlin.fir.scopes.FirScope +import org.jetbrains.kotlin.fir.scopes.impl.FirCompositeScope +import org.jetbrains.kotlin.fir.types.* + +fun ConeKotlinType.scope(useSiteSession: FirSession): FirScope? { + return when (this) { + is ConeKotlinErrorType -> null + is ConeClassErrorType -> null + is ConeAbbreviatedType -> directExpansionType(useSiteSession)?.scope(useSiteSession) + is ConeClassLikeType -> { + val fir = this.lookupTag.toSymbol(useSiteSession)?.firUnsafe() ?: return null + fir.buildUseSiteScope(useSiteSession) + } + is ConeTypeParameterType -> { + val fir = this.lookupTag.toSymbol(useSiteSession)?.firUnsafe() ?: return null + FirCompositeScope(fir.bounds.mapNotNullTo(mutableListOf()) { it.coneTypeUnsafe().scope(useSiteSession) }) + } + else -> error("Failed type ${this}") + } +} + + + diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt new file mode 100644 index 00000000000..b225d11553e --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt @@ -0,0 +1,311 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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.resolve.calls + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.FirFunction +import org.jetbrains.kotlin.fir.resolve.scope +import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator +import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe +import org.jetbrains.kotlin.fir.scopes.FirPosition +import org.jetbrains.kotlin.fir.scopes.FirScope +import org.jetbrains.kotlin.fir.scopes.ProcessorAction +import org.jetbrains.kotlin.fir.scopes.processClassifiersByNameWithAction +import org.jetbrains.kotlin.fir.symbols.* +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.name.Name +import java.util.* +import kotlin.collections.LinkedHashSet + +class CallResolver(val typeCalculator: ReturnTypeCalculator) { + + lateinit var checkers: List + lateinit var scopes: List + + fun runTowerResolver(): ApplicabilityChecker? { + processedScopes.clear() + + val names = LinkedHashSet() + + var successChecker: ApplicabilityChecker? = null + + for ((index, scope) in scopes.asReversed().withIndex()) { + checkers.forEach { + it.updateNames(names) + } + processedScopes.add(scope) + + names.forEach { name -> + fun process(symbol: ConeSymbol): ProcessorAction { + for (checker in checkers) { + checker.consumeCandidate(index, symbol, this) + } + return ProcessorAction.NEXT + } + + scope.processClassifiersByNameWithAction(name, FirPosition.OTHER, ::process) + scope.processPropertiesByName(name, ::process) + scope.processFunctionsByName(name, ::process) + } + + successChecker = checkers.maxBy { it.currentApplicability } + + if (successChecker?.currentApplicability == CandidateApplicability.RESOLVED) { + break + } + + } + + return successChecker + } + + lateinit var session: FirSession + val processedScopes = mutableListOf() + +} + + +enum class CandidateApplicability { + HIDDEN, + PARAMETER_MAPPING_ERROR, + SYNTHETIC_RESOLVED, + RESOLVED +} + + +// TODO: Extract from this transformer +abstract class ApplicabilityChecker { + + val groupNumbers = mutableListOf() + val candidates = mutableListOf() + + + var currentApplicability = CandidateApplicability.HIDDEN + + var expectedType: FirTypeRef? = null + var explicitReceiverType: FirTypeRef? = null + + + fun newDataSet() { + groupNumbers.clear() + candidates.clear() + expectedType = null + currentApplicability = CandidateApplicability.HIDDEN + } + + + fun isSubtypeOf(superType: FirTypeRef?, subType: FirTypeRef?): Boolean { + if (superType == null && subType == null) return true + if (superType != null && subType != null) return true + return false + } + + + protected open fun getApplicability(group: Int, symbol: ConeSymbol): CandidateApplicability { + val declaration = (symbol as? FirBasedSymbol<*>)?.fir + ?: return CandidateApplicability.HIDDEN + declaration as FirDeclaration + + if (declaration is FirCallableDeclaration) { + if ((declaration.receiverTypeRef == null) != (explicitReceiverType == null)) return CandidateApplicability.PARAMETER_MAPPING_ERROR + } + + return CandidateApplicability.RESOLVED + } + + open fun consumeCandidate(group: Int, symbol: ConeSymbol, resolver: CallResolver) { + val applicability = getApplicability(group, symbol) + + if (applicability > currentApplicability) { + groupNumbers.clear() + candidates.clear() + currentApplicability = applicability + } + + + if (applicability == currentApplicability) { + candidates.add(symbol) + groupNumbers.add(group) + } + } + + abstract fun updateNames(names: LinkedHashSet) + + open fun isSuccessful(index: Int, candidate: ConeSymbol): Boolean { + return true + } + + open fun successCandidates(): List { + if (groupNumbers.isEmpty()) return emptyList() + val result = mutableListOf() + var bestGroup = groupNumbers.first() + for ((index, candidate) in candidates.withIndex()) { + val group = groupNumbers[index] + if (!isSuccessful(index, candidate)) continue + if (bestGroup > group) { + bestGroup = group + result.clear() + } + if (bestGroup == group) { + result.add(candidate) + } + } + return result + } +} + +open class VariableApplicabilityChecker(val name: Name) : ApplicabilityChecker() { + override fun updateNames(names: LinkedHashSet) { + names.add(name) + } + + override fun consumeCandidate(group: Int, symbol: ConeSymbol, resolver: CallResolver) { + if (symbol !is ConeVariableSymbol) return + if (symbol.callableId.callableName != name) return + super.consumeCandidate(group, symbol, resolver) + } +} + +class VariableInvokeApplicabilityChecker(val variableName: Name) : FunctionApplicabilityChecker(invoke) { + + val variableChecker = object : VariableApplicabilityChecker(variableName) { + override fun isSuccessful(index: Int, candidate: ConeSymbol): Boolean { + return matchedProperties[index] + } + } + private var matchedProperties = BitSet() + private var lookupInvoke = false + + override fun updateNames(names: LinkedHashSet) { + names.add(variableName) + if (lookupInvoke) { + names.add(name) + } + } + + private fun isInvokeApplicableOn(propertySymbol: ConeSymbol, invokeSymbol: ConeCallableSymbol): Boolean { + return true //TODO: Actual type-check here + } + + override fun getApplicability(group: Int, symbol: ConeSymbol): CandidateApplicability { + + symbol as ConeCallableSymbol + val declaration = (symbol as? FirBasedSymbol<*>)?.fir + ?: return CandidateApplicability.HIDDEN + declaration as FirDeclaration + + if (declaration is FirFunction) { + if (declaration.valueParameters.size != parameterCount) return CandidateApplicability.PARAMETER_MAPPING_ERROR + } + + var applicable = false + + fun processCandidates(candidates: Iterable>) { + for ((index, candidate) in candidates) { + val invokeApplicableOn = isInvokeApplicableOn(candidate, symbol) + if (invokeApplicableOn) { + applicable = true + } + matchedProperties[index] = invokeApplicableOn + + } + } + + if (group == -1) { + processCandidates(listOf(variableChecker.candidates.withIndex().last())) + } else { + processCandidates(variableChecker.candidates.withIndex()) + } + + if (applicable) { + return CandidateApplicability.RESOLVED + } + return CandidateApplicability.PARAMETER_MAPPING_ERROR + } + + private fun checkSuccess(): Boolean { + return currentApplicability == CandidateApplicability.RESOLVED + } + + + override fun consumeCandidate(group: Int, symbol: ConeSymbol, resolver: CallResolver) { + if (symbol is ConeVariableSymbol && symbol.callableId.callableName == variableName) { + variableChecker.consumeCandidate(group, symbol, resolver) + + val lastCandidate = variableChecker.candidates.lastOrNull() + if (variableChecker.currentApplicability == CandidateApplicability.RESOLVED && lastCandidate == symbol) { + val receiverScope = + resolver.typeCalculator.tryCalculateReturnType(lastCandidate.firUnsafe())?.type + ?.scope(resolver.session) + + + lookupInvoke = true + + receiverScope?.processFunctionsByName(invoke) { candidate -> + this.consumeCandidate(-1, candidate, resolver) + ProcessorAction.NEXT + } + if (checkSuccess()) return + + for ((index, scope) in resolver.processedScopes.withIndex()) { + scope.processFunctionsByName(invoke) { candidate -> + this.consumeCandidate(index, candidate, resolver) + ProcessorAction.NEXT + } + if (checkSuccess()) return + } + + } + } + + super.consumeCandidate(group, symbol, resolver) + } + + + companion object { + val invoke = Name.identifier("invoke") + } +} + + +class ClassifierApplicabilityChecker(val name: Name) : ApplicabilityChecker() { + override fun updateNames(names: LinkedHashSet) { + names.add(name) + } + + override fun consumeCandidate(group: Int, symbol: ConeSymbol, resolver: CallResolver) { + if (symbol !is ConeClassifierSymbol) return + if (symbol.toLookupTag().name != name) return + super.consumeCandidate(group, symbol, resolver) + } + +} + +open class FunctionApplicabilityChecker(val name: Name) : ApplicabilityChecker() { + + var parameterCount = 0 + + override fun updateNames(names: LinkedHashSet) { + names.add(name) + } + + override fun getApplicability(group: Int, symbol: ConeSymbol): CandidateApplicability { + val declaration = (symbol as FirBasedSymbol<*>).fir + + if (declaration is FirFunction) { + if (declaration.valueParameters.size != parameterCount) return CandidateApplicability.PARAMETER_MAPPING_ERROR + } + return super.getApplicability(group, symbol) + } + + override fun consumeCandidate(group: Int, symbol: ConeSymbol, resolver: CallResolver) { + if (symbol !is ConeFunctionSymbol) return + if (symbol.callableId.callableName != name) return + super.consumeCandidate(group, symbol, resolver) + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirProviderImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirProviderImpl.kt index cef3fc9b545..5a9c5f9223a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirProviderImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirProviderImpl.kt @@ -15,6 +15,10 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName class FirProviderImpl(val session: FirSession) : FirProvider { + override fun getFirCallableContainerFile(callableId: CallableId): FirFile? { + return callableContainerMap[callableId] + } + override fun getClassLikeSymbolByFqName(classId: ClassId): ConeClassLikeSymbol? { return (getFirClassifierByFqName(classId) as? FirSymbolOwner<*>)?.symbol as? ConeClassLikeSymbol } @@ -59,6 +63,7 @@ class FirProviderImpl(val session: FirSession) : FirProvider { override fun visitCallableMemberDeclaration(callableMemberDeclaration: FirCallableMemberDeclaration) { val callableId = (callableMemberDeclaration.symbol as ConeCallableSymbol).callableId callableMap.merge(callableId, listOf(callableMemberDeclaration)) { a, b -> a + b } + callableContainerMap[callableId] = file } override fun visitConstructor(constructor: FirConstructor) { @@ -79,6 +84,7 @@ class FirProviderImpl(val session: FirSession) : FirProvider { private val classifierMap = mutableMapOf() private val classifierContainerFileMap = mutableMapOf() private val callableMap = mutableMapOf>() + private val callableContainerMap = mutableMapOf() override fun getFirFilesByPackage(fqName: FqName): List { return fileMap[fqName].orEmpty() 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 4f46f84957f..999ea8d6d73 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 @@ -14,12 +14,13 @@ import org.jetbrains.kotlin.fir.expressions.impl.FirQualifiedAccessExpressionImp import org.jetbrains.kotlin.fir.references.FirErrorNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedCallableReferenceImpl import org.jetbrains.kotlin.fir.references.FirSimpleNamedReference +import org.jetbrains.kotlin.fir.resolve.FirProvider import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider -import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.resolve.calls.* +import org.jetbrains.kotlin.fir.resolve.scope import org.jetbrains.kotlin.fir.scopes.* import org.jetbrains.kotlin.fir.scopes.impl.* import org.jetbrains.kotlin.fir.symbols.* -import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult import org.jetbrains.kotlin.fir.visitors.FirTransformer import org.jetbrains.kotlin.fir.visitors.compose @@ -31,10 +32,9 @@ import org.jetbrains.kotlin.fir.types.impl.FirErrorTypeRefImpl import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.utils.addIfNotNull -import java.util.* -import kotlin.collections.LinkedHashSet +import org.jetbrains.kotlin.utils.addToStdlib.cast -class FirBodyResolveTransformer(val session: FirSession) : FirTransformer() { +open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOnly: Boolean) : FirTransformer() { val symbolProvider = session.service() @@ -71,55 +71,6 @@ class FirBodyResolveTransformer(val session: FirSession) : FirTransformer( } - private fun ConeClassLikeType.buildSubstitutionScope( - useSiteSession: FirSession, - unsubstituted: FirScope, - regularClass: FirRegularClass - ): FirClassSubstitutionScope? { - if (this.typeArguments.isEmpty()) return null - - val substitution = regularClass.typeParameters.zip(this.typeArguments) { typeParameter, typeArgument -> - typeParameter.symbol to (typeArgument as? ConeTypedProjection)?.type - }.filter { (_, type) -> type != null }.toMap() as Map - - return FirClassSubstitutionScope(useSiteSession, unsubstituted, substitution, true) - } - - private fun FirRegularClass.buildUseSiteScope(useSiteSession: FirSession = session): FirClassUseSiteScope { - val superTypeScope = FirCompositeScope(mutableListOf()) - val declaredScope = FirClassDeclaredMemberScope(this, useSiteSession) - lookupSuperTypes(this, lookupInterfaces = true, deep = false, useSiteSession = useSiteSession) - .mapNotNullTo(superTypeScope.scopes) { useSiteSuperType -> - if (useSiteSuperType is ConeClassErrorType) return@mapNotNullTo null - val symbol = useSiteSuperType.lookupTag.toSymbol(useSiteSession) - if (symbol is FirClassSymbol) { - val scope = symbol.fir.buildUseSiteScope(useSiteSession) - useSiteSuperType.buildSubstitutionScope(useSiteSession, scope, symbol.fir) ?: scope - } else { - null - } - } - return FirClassUseSiteScope(useSiteSession, superTypeScope, declaredScope, true) - } - - fun ConeKotlinType.scope(useSiteSession: FirSession): FirScope? { - return when (this) { - is ConeKotlinErrorType -> null - is ConeClassErrorType -> null - is ConeAbbreviatedType -> directExpansion.scope(useSiteSession) - is ConeClassLikeType -> { - val fir = this.lookupTag.toSymbol(useSiteSession)?.firUnsafe() ?: return null - fir.buildUseSiteScope(useSiteSession) - } - is ConeTypeParameterType -> { - val fir = this.lookupTag.toSymbol(useSiteSession)?.firUnsafe() ?: return null - FirCompositeScope(fir.bounds.mapNotNullTo(mutableListOf()) { it.coneTypeUnsafe().scope(useSiteSession) }) - } - else -> error("Failed type ${this}") - } - } - - inline fun withLabel(labelName: Name, type: ConeKotlinType, block: () -> T): T { labels.put(labelName, type) val result = block() @@ -167,286 +118,18 @@ class FirBodyResolveTransformer(val session: FirSession) : FirTransformer( val labels = LinkedHashMultimap.create() - enum class CandidateApplicability { - HIDDEN, - PARAMETER_MAPPING_ERROR, - SYNTHETIC_RESOLVED, - RESOLVED - } - - - // TODO: Extract from this transformer - abstract class ApplicabilityChecker { - - val groupNumbers = mutableListOf() - val candidates = mutableListOf() - - - var currentApplicability = CandidateApplicability.HIDDEN - - var expectedType: FirTypeRef? = null - var explicitReceiverType: FirTypeRef? = null - - - fun newDataSet() { - groupNumbers.clear() - candidates.clear() - expectedType = null - currentApplicability = CandidateApplicability.HIDDEN - } - - - fun isSubtypeOf(superType: FirTypeRef?, subType: FirTypeRef?): Boolean { - if (superType == null && subType == null) return true - if (superType != null && subType != null) return true - return false - } - - - protected open fun getApplicability(group: Int, symbol: ConeSymbol): CandidateApplicability { - val declaration = (symbol as? FirBasedSymbol<*>)?.fir - ?: return CandidateApplicability.HIDDEN - declaration as FirDeclaration - - if (declaration is FirCallableDeclaration) { - if ((declaration.receiverTypeRef == null) != (explicitReceiverType == null)) return CandidateApplicability.PARAMETER_MAPPING_ERROR - } - - return CandidateApplicability.RESOLVED - } - - open fun consumeCandidate(group: Int, symbol: ConeSymbol) { - val applicability = getApplicability(group, symbol) - - if (applicability > currentApplicability) { - groupNumbers.clear() - candidates.clear() - currentApplicability = applicability - } - - - if (applicability == currentApplicability) { - candidates.add(symbol) - groupNumbers.add(group) - } - } - - abstract fun updateNames(names: LinkedHashSet) - - open fun isSuccessful(index: Int, candidate: ConeSymbol): Boolean { - return true - } - - open fun successCandidates(): List { - if (groupNumbers.isEmpty()) return emptyList() - val result = mutableListOf() - var bestGroup = groupNumbers.first() - for ((index, candidate) in candidates.withIndex()) { - val group = groupNumbers[index] - if (!isSuccessful(index, candidate)) continue - if (bestGroup > group) { - bestGroup = group - result.clear() - } - if (bestGroup == group) { - result.add(candidate) - } - } - return result - } - } - - open class VariableApplicabilityChecker(val name: Name) : ApplicabilityChecker() { - override fun updateNames(names: LinkedHashSet) { - names.add(name) - } - - override fun consumeCandidate(group: Int, symbol: ConeSymbol) { - if (symbol !is ConeVariableSymbol) return - if (symbol.callableId.callableName != name) return - super.consumeCandidate(group, symbol) - } - } - - inner class VariableInvokeApplicabilityChecker(val variableName: Name) : FunctionApplicabilityChecker(invoke) { - - val variableChecker = object : VariableApplicabilityChecker(variableName) { - override fun isSuccessful(index: Int, candidate: ConeSymbol): Boolean { - return matchedProperties[index] - } - } - private var matchedProperties = BitSet() - private var lookupInvoke = false - - override fun updateNames(names: LinkedHashSet) { - names.add(variableName) - if (lookupInvoke) { - names.add(name) - } - } - - private fun isInvokeApplicableOn(propertySymbol: ConeSymbol, invokeSymbol: ConeCallableSymbol): Boolean { - return true //TODO: Actual type-check here - } - - override fun getApplicability(group: Int, symbol: ConeSymbol): CandidateApplicability { - - symbol as ConeCallableSymbol - val declaration = (symbol as? FirBasedSymbol<*>)?.fir - ?: return CandidateApplicability.HIDDEN - declaration as FirDeclaration - - if (declaration is FirFunction) { - if (declaration.valueParameters.size != parameterCount) return CandidateApplicability.PARAMETER_MAPPING_ERROR - } - - var applicable = false - - fun processCandidates(candidates: Iterable>) { - for ((index, candidate) in candidates) { - val invokeApplicableOn = isInvokeApplicableOn(candidate, symbol) - if (invokeApplicableOn) { - applicable = true - } - matchedProperties[index] = invokeApplicableOn - - } - } - - if (group == -1) { - processCandidates(listOf(variableChecker.candidates.withIndex().last())) - } else { - processCandidates(variableChecker.candidates.withIndex()) - } - - if (applicable) { - return CandidateApplicability.RESOLVED - } - return CandidateApplicability.PARAMETER_MAPPING_ERROR - } - - private fun checkSuccess(): Boolean { - return currentApplicability == CandidateApplicability.RESOLVED - } - - override fun consumeCandidate(group: Int, symbol: ConeSymbol) { - if (symbol is ConeVariableSymbol && symbol.callableId.callableName == variableName) { - variableChecker.consumeCandidate(group, symbol) - - val lastCandidate = variableChecker.candidates.lastOrNull() - if (variableChecker.currentApplicability == CandidateApplicability.RESOLVED && lastCandidate == symbol) { - val receiverScope = - (lastCandidate as FirBasedSymbol).fir.returnTypeRef.coneTypeUnsafe().scope(session) - - - lookupInvoke = true - - receiverScope?.processFunctionsByName(invoke) { candidate -> - this.consumeCandidate(-1, candidate) - ProcessorAction.NEXT - } - if (checkSuccess()) return - - for ((index, scope) in processedScopes.withIndex()) { - scope.processFunctionsByName(invoke) { candidate -> - this.consumeCandidate(index, candidate) - ProcessorAction.NEXT - } - if (checkSuccess()) return - } - - } - } - - super.consumeCandidate(group, symbol) - } - - - } - - companion object { - val invoke = Name.identifier("invoke") - } - - class ClassifierApplicabilityChecker(val name: Name) : ApplicabilityChecker() { - override fun updateNames(names: LinkedHashSet) { - names.add(name) - } - - override fun consumeCandidate(group: Int, symbol: ConeSymbol) { - if (symbol !is ConeClassifierSymbol) return - if (symbol.toLookupTag().name != name) return - super.consumeCandidate(group, symbol) - } - - } - - open class FunctionApplicabilityChecker(val name: Name) : ApplicabilityChecker() { - - var parameterCount = 0 - - override fun updateNames(names: LinkedHashSet) { - names.add(name) - } - - override fun getApplicability(group: Int, symbol: ConeSymbol): CandidateApplicability { - val declaration = (symbol as FirBasedSymbol<*>).fir - - if (declaration is FirFunction) { - if (declaration.valueParameters.size != parameterCount) return CandidateApplicability.PARAMETER_MAPPING_ERROR - } - return super.getApplicability(group, symbol) - } - - override fun consumeCandidate(group: Int, symbol: ConeSymbol) { - if (symbol !is ConeFunctionSymbol) return - if (symbol.callableId.callableName != name) return - super.consumeCandidate(group, symbol) - } - } - - val processedScopes = mutableListOf() + val jump = ReturnTypeCalculatorWithJump() private fun runTowerResolver( checkers: List ): ApplicabilityChecker? { + val callResolver = CallResolver(jump) + callResolver.scopes = (scopes + localScopes) + callResolver.checkers = checkers + callResolver.session = session - processedScopes.clear() - - val names = LinkedHashSet() - - var successChecker: ApplicabilityChecker? = null - - for ((index, scope) in (scopes + localScopes).asReversed().withIndex()) { - checkers.forEach { - it.updateNames(names) - } - processedScopes.add(scope) - - names.forEach { name -> - fun process(symbol: ConeSymbol): ProcessorAction { - for (checker in checkers) { - checker.consumeCandidate(index, symbol) - } - return ProcessorAction.NEXT - } - - scope.processClassifiersByNameWithAction(name, FirPosition.OTHER, ::process) - scope.processPropertiesByName(name, ::process) - scope.processFunctionsByName(name, ::process) - } - - successChecker = checkers.maxBy { it.currentApplicability } - - if (successChecker?.currentApplicability == CandidateApplicability.RESOLVED) { - break - } - - } - - return successChecker - + return callResolver.runTowerResolver() } private fun storeTypeFromCallee(access: T) where T : FirQualifiedAccess, T : FirExpression { @@ -455,7 +138,7 @@ class FirBodyResolveTransformer(val session: FirSession) : FirTransformer( is FirErrorNamedReference -> FirErrorTypeRefImpl(session, access.psi, newCallee.errorReason) is FirResolvedCallableReference -> - (newCallee.callableSymbol as FirBasedSymbol).fir.returnTypeRef + jump.tryCalculateReturnType(newCallee.callableSymbol.firUnsafe())!! else -> return } } @@ -631,6 +314,8 @@ class FirBodyResolveTransformer(val session: FirSession) : FirTransformer( override fun transformNamedFunction(namedFunction: FirNamedFunction, data: Any?): CompositeTransformResult { + if (namedFunction.returnTypeRef !is FirImplicitTypeRef && implicitTypeOnly) return namedFunction.compose() + val receiverTypeRef = namedFunction.receiverTypeRef fun transform(): CompositeTransformResult { localScopes.lastOrNull()?.storeDeclaration(namedFunction) @@ -664,7 +349,8 @@ class FirBodyResolveTransformer(val session: FirSession) : FirTransformer( initializer != null -> { variable.transformReturnTypeRef(this, initializer.resultType) } - variable.delegate != null -> {} + variable.delegate != null -> { + } } } if (variable !is FirProperty) { @@ -674,13 +360,76 @@ class FirBodyResolveTransformer(val session: FirSession) : FirTransformer( } override fun transformProperty(property: FirProperty, data: Any?): CompositeTransformResult { + if (property.returnTypeRef !is FirImplicitTypeRef && implicitTypeOnly) return property.compose() return transformVariable(property, data) } - private fun FirElement.visitNoTransform(transformer: FirTransformer, data: D) { + fun FirElement.visitNoTransform(transformer: FirTransformer, data: D) { val result = this.transform(transformer, data) require(result.single === this) } + + inner class ReturnTypeCalculatorWithJump : ReturnTypeCalculator { + override fun tryCalculateReturnType(declaration: FirTypedDeclaration): FirResolvedTypeRef? { + val returnTypeRef = declaration.returnTypeRef + if (returnTypeRef is FirResolvedTypeRef) return returnTypeRef + require(declaration is FirCallableMemberDeclaration) + + + val id = (declaration.symbol as ConeCallableSymbol).callableId + + val provider = session.service() + + val file = provider.getFirCallableContainerFile(id) ?: FirErrorTypeRefImpl( + session, + null, + "I don't know what todo" + ) + + val outerClasses = generateSequence(id.classId) { classId -> + classId.outerClassId + }.mapTo(mutableListOf()) { provider.getFirClassifierByFqName(it)!! } + + + val transformer = FirDesignatedBodyResolveTransformer( + (listOf(file) + outerClasses.asReversed() + listOf(declaration)).iterator(), + file.session + ) + + transformer.transformElement(file, null) + + val newReturnTypeRef = declaration.returnTypeRef + require(newReturnTypeRef is FirResolvedTypeRef) { declaration.render() } + return newReturnTypeRef + } + + } +} + + +class FirDesignatedBodyResolveTransformer(val designation: Iterator, session: FirSession) : + FirBodyResolveTransformer(session, implicitTypeOnly = true) { + + override fun transformElement(element: E, data: Any?): CompositeTransformResult { + if (designation.hasNext()) { + designation.next().visitNoTransform(this, data) + return element.compose() + } + return super.transformElement(element, data) + } +} + + +@Deprecated("It is temp", level = DeprecationLevel.WARNING, replaceWith = ReplaceWith("TODO(\"что-то нормальное\")")) +class FirImplicitTypeBodyResolveTransformerAdapter : FirTransformer() { + override fun transformElement(element: E, data: Nothing?): CompositeTransformResult { + return element.compose() + } + + override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult { + val transformer = FirBodyResolveTransformer(file.session, implicitTypeOnly = true) + return file.transform(transformer, null) + } } @@ -691,7 +440,7 @@ class FirBodyResolveTransformerAdapter : FirTransformer() { } override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult { - val transformer = FirBodyResolveTransformer(file.session) + val transformer = FirBodyResolveTransformer(file.session, implicitTypeOnly = false) return file.transform(transformer, null) } } @@ -700,4 +449,9 @@ class FirBodyResolveTransformerAdapter : FirTransformer() { inline fun ConeSymbol.firUnsafe(): T { this as FirBasedSymbol<*> return this.fir as T +} + + +interface ReturnTypeCalculator { + fun tryCalculateReturnType(declaration: FirTypedDeclaration): FirResolvedTypeRef? } \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTotalResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTotalResolveTransformer.kt index 9ca69d43005..d76872e3fae 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTotalResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTotalResolveTransformer.kt @@ -15,6 +15,7 @@ class FirTotalResolveTransformer { FirSupertypeResolverTransformer(), FirTypeResolveTransformer(), FirStatusResolveTransformer(), + FirImplicitTypeBodyResolveTransformerAdapter(), FirBodyResolveTransformerAdapter() ) diff --git a/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.kt b/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.kt new file mode 100644 index 00000000000..47deb73c006 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.kt @@ -0,0 +1,10 @@ + +class A { + fun bar() = foo() + + fun invoke() = this +} + +fun create() = A() + +val foo = create() \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.txt b/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.txt new file mode 100644 index 00000000000..914802ffdd0 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.txt @@ -0,0 +1,18 @@ +FILE: implicitTypeOrder.kt + public final class A { + public constructor(): super() + + public final fun bar(): R|A| { + ^bar R|/foo|.R|/A.invoke|() + } + + public final fun invoke(): R|A| { + ^invoke this# + } + + } + public final fun create(): R|A| { + ^create R|/A.A|() + } + public final val foo: R|A| = R|/create|() + public get(): diff --git a/compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.kt b/compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.kt new file mode 100644 index 00000000000..5d937d62e08 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.kt @@ -0,0 +1,7 @@ +fun foo() { + val x = object { + fun sss() = abc() + fun abc() = 1 + } + val g = x.sss() +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.txt b/compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.txt new file mode 100644 index 00000000000..ae18d1b3673 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.txt @@ -0,0 +1,17 @@ +FILE: localImplicitBodies.kt + public final fun foo(): R|kotlin/Unit| { + lval x: R|kotlin/Unit| = object : { + public constructor(): super() + + public final fun sss(): { + ^sss #() + } + + public final fun abc(): R|kotlin/Int| { + ^abc Int(1) + } + + } + + lval g: = R|/x|.R|/sss|() + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java index f27e616c5d8..0990b78c3e4 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java @@ -159,6 +159,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/resolve/expresssions"), Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("localImplicitBodies.kt") + public void testLocalImplicitBodies() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("compiler/fir/resolve/testData/resolve/expresssions/simple.kt"); @@ -206,6 +211,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase { runTest("compiler/fir/resolve/testData/resolve/expresssions/invoke/farInvokeExtension.kt"); } + @TestMetadata("implicitTypeOrder.kt") + public void testImplicitTypeOrder() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/expresssions/invoke/implicitTypeOrder.kt"); + } + @TestMetadata("threeReceivers.kt") public void testThreeReceivers() throws Exception { runTest("compiler/fir/resolve/testData/resolve/expresssions/invoke/threeReceivers.kt"); diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestTotalKotlin.kt b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestTotalKotlin.kt index 137c85cae77..99f09a06bf6 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestTotalKotlin.kt +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestTotalKotlin.kt @@ -65,7 +65,7 @@ class FirResolveTestTotalKotlin : AbstractFirResolveWithSessionTestCase() { val scope = ProjectScope.getContentScope(project) val session = createSession(project, scope) - val builder = RawFirBuilder(session, stubMode = true) + val builder = RawFirBuilder(session, stubMode = false) val totalTransformer = FirTotalResolveTransformer() val firFiles = ktFiles.toList().progress("Loading FIR").map { diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirErrorTypeRef.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirErrorTypeRef.kt index f01f3625011..d17ca06e49d 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirErrorTypeRef.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirErrorTypeRef.kt @@ -7,7 +7,7 @@ package org.jetbrains.kotlin.fir.types import org.jetbrains.kotlin.fir.visitors.FirVisitor -interface FirErrorTypeRef : FirTypeRef { +interface FirErrorTypeRef : FirResolvedTypeRef { val reason: String override fun accept(visitor: FirVisitor, data: D): R = diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/impl/FirErrorTypeRefImpl.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/impl/FirErrorTypeRefImpl.kt index bde446c8b71..1f01e877db8 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/impl/FirErrorTypeRefImpl.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/impl/FirErrorTypeRefImpl.kt @@ -7,6 +7,8 @@ package org.jetbrains.kotlin.fir.types.impl import com.intellij.psi.PsiElement import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType +import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.FirErrorTypeRef import org.jetbrains.kotlin.fir.visitors.FirVisitor @@ -15,6 +17,8 @@ class FirErrorTypeRefImpl( psi: PsiElement?, override val reason: String ) : FirAbstractAnnotatedTypeRef(session, psi, false), FirErrorTypeRef { + override val type: ConeKotlinType = ConeKotlinErrorType(reason) + override fun accept(visitor: FirVisitor, data: D): R { return super.accept(visitor, data) }