提交 840750ee 编写于 作者: S Simon Ogorodnik 提交者: Mikhail Glukhikh

Implement basic use-site scopes and override mapping

This commit also introduces separate scope processors:
processFunctionsByName and processPropertiesByName, in addition to
existing processClassifiersByName

Ad-hock call resolver starts to discover ambiguities, which leads
to some ambiguity problems in MPP tests with deep supertype hierarchy

Related to KT-29636
上级 6ea2abfc
......@@ -7,13 +7,18 @@ package org.jetbrains.kotlin.fir.resolve.transformers
import org.jetbrains.kotlin.fir.FirNamedReference
import org.jetbrains.kotlin.fir.FirResolvedCallableReference
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedCallableReferenceImpl
import org.jetbrains.kotlin.fir.scopes.FirPosition
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.NEXT
import org.jetbrains.kotlin.fir.scopes.impl.FirClassDeclaredMemberScope
import org.jetbrains.kotlin.fir.scopes.impl.FirClassUseSiteScope
import org.jetbrains.kotlin.fir.scopes.impl.FirCompositeScope
import org.jetbrains.kotlin.fir.scopes.impl.FirTopLevelDeclaredMemberScope
import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
......@@ -30,39 +35,111 @@ class FirAccessResolveTransformer : FirAbstractTreeTransformerWithSuperTypes(rev
}
}
private fun FirRegularClass.buildUseSiteScope(useSiteSession: FirSession = session): FirClassUseSiteScope {
val superTypeScope = FirCompositeScope(mutableListOf())
val declaredScope = FirClassDeclaredMemberScope(this, useSiteSession)
lookupSuperTypes(this, lookupInterfaces = true).asReversed()
.mapNotNullTo(superTypeScope.scopes) { useSiteSuperType ->
if (useSiteSuperType is ConeClassErrorType) return@mapNotNullTo null
val symbol = useSiteSuperType.symbol
if (symbol is FirClassSymbol) {
symbol.fir.buildUseSiteScope(useSiteSession)
} else {
null
}
}
return FirClassUseSiteScope(useSiteSession, superTypeScope, declaredScope, true)
}
override fun transformRegularClass(regularClass: FirRegularClass, data: Nothing?): CompositeTransformResult<FirDeclaration> {
return withScopeCleanup {
val useSiteSession = regularClass.session
lookupSuperTypes(regularClass, lookupInterfaces = true).asReversed()
.mapNotNullTo(towerScope.scopes) { useSiteSuperType ->
if (useSiteSuperType is ConeClassErrorType) return@mapNotNullTo null
val symbol = useSiteSuperType.symbol
if (symbol is FirClassSymbol) {
FirClassDeclaredMemberScope(symbol.fir, useSiteSession)
} else {
null
}
}
towerScope.scopes += FirClassDeclaredMemberScope(regularClass, useSiteSession)
towerScope.scopes += regularClass.buildUseSiteScope()
super.transformRegularClass(regularClass, data)
}
}
var lookupFunctions = false
var lookupProperties = false
inline fun <T> withNewSettings(block: () -> T): T {
val prevFunctions = lookupFunctions
val prevProperties = lookupProperties
val result = block()
lookupFunctions = prevFunctions
lookupProperties = prevProperties
return result
}
override fun transformFunctionCall(functionCall: FirFunctionCall, data: Nothing?): CompositeTransformResult<FirStatement> {
return withNewSettings {
lookupFunctions = true
lookupProperties = false
super.transformFunctionCall(functionCall, data)
}
}
override fun transformQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression,
data: Nothing?
): CompositeTransformResult<FirStatement> {
return withNewSettings {
lookupProperties = true
lookupFunctions = false
super.transformQualifiedAccessExpression(qualifiedAccessExpression, data)
}
}
override fun transformCallableReferenceAccess(
callableReferenceAccess: FirCallableReferenceAccess,
data: Nothing?
): CompositeTransformResult<FirStatement> {
return withNewSettings {
lookupProperties = true
lookupFunctions = true
super.transformCallableReferenceAccess(callableReferenceAccess, data)
}
}
override fun transformAssignment(assignment: FirAssignment, data: Nothing?): CompositeTransformResult<FirStatement> {
return withNewSettings {
lookupProperties = true
lookupFunctions = false
super.transformAssignment(assignment, data)
}
}
override fun transformNamedReference(namedReference: FirNamedReference, data: Nothing?): CompositeTransformResult<FirNamedReference> {
if (namedReference is FirResolvedCallableReference) return namedReference.compose()
val name = namedReference.name
var symbol: ConeCallableSymbol? = null
towerScope.processClassifiersByName(name, FirPosition.OTHER) {
symbol = it as? ConeCallableSymbol
// I'm lucky today!!! (the first symbol we have found is the one we need)
it is ConeCallableSymbol
val referents = mutableListOf<ConeCallableSymbol>()
fun collect(it: ConeCallableSymbol): ProcessorAction {
referents.add(it)
return NEXT
}
val callableSymbol = symbol ?: return FirErrorNamedReference(
namedReference.session, namedReference.psi, "Unresolved name: $name"
).compose()
return FirResolvedCallableReferenceImpl(
namedReference.session, namedReference.psi,
name, callableSymbol
).compose()
if (lookupFunctions)
towerScope.processFunctionsByName(name, ::collect)
if (lookupProperties)
towerScope.processPropertiesByName(name, ::collect)
return when (referents.size) {
0 -> FirErrorNamedReference(
namedReference.session, namedReference.psi, "Unresolved name: $name"
).compose()
1 -> FirResolvedCallableReferenceImpl(
namedReference.session, namedReference.psi,
name, referents.single()
).compose()
else -> FirErrorNamedReference(
namedReference.session, namedReference.psi, "Ambiguity: $name, ${referents.map { it.callableId }}"
).compose()
}
}
}
\ No newline at end of file
......@@ -5,6 +5,9 @@
package org.jetbrains.kotlin.fir.scopes
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.NEXT
import org.jetbrains.kotlin.fir.symbols.ConeFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.ConePropertySymbol
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
import org.jetbrains.kotlin.name.Name
......@@ -13,10 +16,34 @@ interface FirScope {
name: Name,
position: FirPosition,
processor: (ConeSymbol) -> Boolean
): Boolean
): Boolean = true
fun processFunctionsByName(
name: Name,
processor: (ConeFunctionSymbol) -> ProcessorAction
): ProcessorAction = NEXT
fun processPropertiesByName(
name: Name,
processor: (ConePropertySymbol) -> ProcessorAction
): ProcessorAction = NEXT
}
enum class FirPosition(val allowTypeParameters: Boolean = true) {
SUPER_TYPE_OR_EXPANSION(allowTypeParameters = false),
OTHER
}
enum class ProcessorAction {
STOP,
NEXT;
operator fun not(): Boolean {
return when (this) {
STOP -> true
NEXT -> false
}
}
fun next() = this == NEXT
}
\ No newline at end of file
......@@ -7,9 +7,12 @@ package org.jetbrains.kotlin.fir.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.scopes.FirPosition
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.NEXT
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.STOP
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
import org.jetbrains.kotlin.fir.symbols.ConeFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.ConePropertySymbol
import org.jetbrains.kotlin.name.Name
class FirClassDeclaredMemberScope(
......@@ -19,17 +22,23 @@ class FirClassDeclaredMemberScope(
) : FirAbstractProviderBasedScope(session, lookupInFir) {
private val classId = klass.symbol.classId
override fun processClassifiersByName(
name: Name,
position: FirPosition,
processor: (ConeSymbol) -> Boolean
): Boolean {
override fun processFunctionsByName(name: Name, processor: (ConeFunctionSymbol) -> ProcessorAction): ProcessorAction {
val symbols = provider.getCallableSymbols(CallableId(classId.packageFqName, classId.relativeClassName, name))
for (symbol in symbols) {
if (!processor(symbol)) {
return false
if (symbol is ConeFunctionSymbol && !processor(symbol)) {
return STOP
}
}
return true
return NEXT
}
override fun processPropertiesByName(name: Name, processor: (ConePropertySymbol) -> ProcessorAction): ProcessorAction {
val symbols = provider.getCallableSymbols(CallableId(classId.packageFqName, classId.relativeClassName, name))
for (symbol in symbols) {
if (symbol is ConePropertySymbol && !processor(symbol)) {
return STOP
}
}
return NEXT
}
}
\ No newline at end of file
/*
* 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.scopes.impl
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableMember
import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.FirNamedFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.NEXT
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.STOP
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
import org.jetbrains.kotlin.fir.symbols.ConeFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.ConePropertySymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirResolvedType
import org.jetbrains.kotlin.fir.types.FirType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.cast
class FirClassUseSiteScope(
session: FirSession,
private val superTypesScope: FirScope,
private val declaredMemberScope: FirClassDeclaredMemberScope,
lookupInFir: Boolean
) : FirAbstractProviderBasedScope(session, lookupInFir) {
//base symbol as key
val overrides = mutableMapOf<ConeCallableSymbol, ConeCallableSymbol?>()
@Suppress("UNUSED_PARAMETER")
private fun isSubtypeOf(subType: ConeKotlinType, superType: ConeKotlinType): Boolean {
// TODO: introduce normal sub-typing
return true
}
private fun isSubtypeOf(subType: FirType, superType: FirType) =
isSubtypeOf(subType.cast<FirResolvedType>().type, superType.cast<FirResolvedType>().type)
@Suppress("UNUSED_PARAMETER")
private fun isEqualTypes(a: ConeKotlinType, b: ConeKotlinType): Boolean {
// TODO: introduce normal type comparison
return true
}
private fun isEqualTypes(a: FirType, b: FirType) = isEqualTypes(a.cast<FirResolvedType>().type, b.cast<FirResolvedType>().type)
private fun isOverriddenFunCheck(member: FirNamedFunction, self: FirNamedFunction): Boolean {
return member.valueParameters.size == self.valueParameters.size &&
member.valueParameters.zip(self.valueParameters).all { (memberParam, selfParam) ->
isEqualTypes(memberParam.returnType, selfParam.returnType)
}
}
private fun ConeCallableSymbol.isOverridden(seen: Set<ConeCallableSymbol>): ConeCallableSymbol? {
if (overrides.containsKey(this)) return overrides[this]
fun sameReceivers(memberType: FirType?, selfType: FirType?): Boolean {
return when {
memberType != null && selfType != null -> isEqualTypes(memberType, selfType)
else -> memberType == null && selfType == null
}
}
fun similarFunctionsOrBothProperties(member: FirCallableMember, self: FirCallableMember): Boolean {
return when (member) {
is FirNamedFunction -> self is FirNamedFunction && isOverriddenFunCheck(member, self)
is FirConstructor -> false
is FirProperty -> self is FirProperty
else -> error("Unknown fir callable type: $member, $self")
}
}
val self = (this as AbstractFirBasedSymbol<*>).fir as FirCallableMember
val overriding = seen.firstOrNull {
val member = (it as AbstractFirBasedSymbol<*>).fir as FirCallableMember
member.isOverride && self.modality != Modality.FINAL
&& sameReceivers(member.receiverType, self.receiverType)
&& isSubtypeOf(member.returnType, self.returnType)
&& similarFunctionsOrBothProperties(member, self)
} // TODO: two or more overrides for one fun?
overrides[this] = overriding
return overriding
}
override fun processFunctionsByName(name: Name, processor: (ConeFunctionSymbol) -> ProcessorAction): ProcessorAction {
val seen = mutableSetOf<ConeCallableSymbol>()
if (!declaredMemberScope.processFunctionsByName(name) {
seen += it
processor(it)
}
) return STOP
return superTypesScope.processFunctionsByName(name) {
val overriddenBy = it.isOverridden(seen)
if (overriddenBy == null) {
processor(it)
} else {
NEXT
}
}
}
override fun processPropertiesByName(name: Name, processor: (ConePropertySymbol) -> ProcessorAction): ProcessorAction {
val seen = mutableSetOf<ConeCallableSymbol>()
if (!declaredMemberScope.processPropertiesByName(name) {
seen += it
processor(it)
}
) return STOP
return superTypesScope.processPropertiesByName(name) {
val overriddenBy = it.isOverridden(seen)
if (overriddenBy == null) {
processor(it)
} else {
NEXT
}
}
}
}
......@@ -8,6 +8,11 @@ package org.jetbrains.kotlin.fir.scopes.impl
import org.jetbrains.kotlin.fir.scopes.FirPosition
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.FirTypeParameterScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.NEXT
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.STOP
import org.jetbrains.kotlin.fir.symbols.ConeFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.ConePropertySymbol
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
import org.jetbrains.kotlin.name.Name
......@@ -29,4 +34,27 @@ class FirCompositeScope(
}
return true
}
private inline fun <T> processComposite(
process: FirScope.(Name, (T) -> ProcessorAction) -> ProcessorAction,
name: Name,
noinline processor: (T) -> ProcessorAction
): ProcessorAction {
val scopes = if (reversedPriority) scopes.asReversed() else scopes
for (scope in scopes) {
if (!scope.process(name, processor)) {
return STOP
}
}
return NEXT
}
override fun processFunctionsByName(name: Name, processor: (ConeFunctionSymbol) -> ProcessorAction): ProcessorAction {
return processComposite(FirScope::processFunctionsByName, name, processor)
}
override fun processPropertiesByName(name: Name, processor: (ConePropertySymbol) -> ProcessorAction): ProcessorAction {
return processComposite(FirScope::processPropertiesByName, name, processor)
}
}
\ No newline at end of file
......@@ -7,9 +7,12 @@ package org.jetbrains.kotlin.fir.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.scopes.FirPosition
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.NEXT
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.STOP
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
import org.jetbrains.kotlin.fir.symbols.ConeFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.ConePropertySymbol
import org.jetbrains.kotlin.name.Name
class FirTopLevelDeclaredMemberScope(
......@@ -19,17 +22,23 @@ class FirTopLevelDeclaredMemberScope(
) : FirAbstractProviderBasedScope(session, lookupInFir) {
private val packageFqName = file.packageFqName
override fun processClassifiersByName(
name: Name,
position: FirPosition,
processor: (ConeSymbol) -> Boolean
): Boolean {
override fun processFunctionsByName(name: Name, processor: (ConeFunctionSymbol) -> ProcessorAction): ProcessorAction {
val symbols = provider.getCallableSymbols(CallableId(packageFqName, name))
for (symbol in symbols) {
if (!processor(symbol)) {
return false
if (symbol is ConeFunctionSymbol && !processor(symbol)) {
return STOP
}
}
return true
return NEXT
}
override fun processPropertiesByName(name: Name, processor: (ConePropertySymbol) -> ProcessorAction): ProcessorAction {
val symbols = provider.getCallableSymbols(CallableId(packageFqName, name))
for (symbol in symbols) {
if (symbol is ConePropertySymbol && !processor(symbol)) {
return STOP
}
}
return NEXT
}
}
\ No newline at end of file
open class A {
open fun foo(): A = this
open fun bar(): A = this
}
class B : A() {
override fun foo(): B = this
fun bar(): B = this // Ambiguity, no override here
fun test() {
foo()
bar()
}
}
FILE: simple.kt
public open class A {
public constructor(): super<R|kotlin/Any|>()
public open function foo(): R|A| {
return@@@foo this#
}
public open function bar(): R|A| {
return@@@bar this#
}
}
public final class B : R|A| {
public constructor(): super<R|A|>()
public final override function foo(): R|B| {
return@@@foo this#
}
public final function bar(): R|B| {
return@@@bar this#
}
public final function test(): R|kotlin/Unit| {
R|/B.foo|()
<Ambiguity: bar, [/B.bar, /A.bar]>#()
}
}
......@@ -243,6 +243,24 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase {
}
}
@TestMetadata("compiler/fir/resolve/testData/resolve/overrides")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Overrides extends AbstractFirResolveTestCase {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInOverrides() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/resolve/overrides"), Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/overrides/simple.kt");
}
}
@TestMetadata("compiler/fir/resolve/testData/resolve/references")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
......
......@@ -13,8 +13,8 @@ FILE: jvm.kt
public constructor(): super<R|B|>()
public final function test(): R|kotlin/Unit| {
R|/A.foo|()
R|/A.bar|()
<Ambiguity: foo, [/A.foo, /A.foo]>#()
<Ambiguity: bar, [/A.bar, /A.bar]>#()
}
}
......
......@@ -22,7 +22,7 @@ FILE: jvm.kt
public constructor(): super<R|B|>()
public final function test(): R|kotlin/Unit| {
R|/A.foo|()
<Ambiguity: foo, [/A.foo, /A.foo]>#()
<Unresolved name: bar>#()
<Unresolved name: baz>#()
}
......@@ -33,8 +33,8 @@ FILE: jvm.kt
public final function test(): R|kotlin/Unit| {
R|/A.foo|()
R|/X.bar|()
R|/Y.baz|()
<Ambiguity: bar, [/X.bar, /X.bar]>#()
<Ambiguity: baz, [/Y.baz, /Y.baz]>#()
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册