FIR: Select most specific members from supertypes scope

This commit is contained in:
Denis Zharkov
2019-12-05 14:27:47 +03:00
parent e5166e0e7d
commit e58711cde3
7 changed files with 315 additions and 30 deletions
@@ -25,7 +25,7 @@ abstract class AbstractFirOverrideScope(val session: FirSession, protected val o
return overrideChecker.isOverriddenProperty(overrideCandidate, baseDeclaration)
}
private fun similarFunctionsOrBothProperties(
protected fun similarFunctionsOrBothProperties(
overrideCandidate: FirCallableMemberDeclaration<*>,
baseDeclaration: FirCallableMemberDeclaration<*>
): Boolean {
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.scopes.impl
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
@@ -20,12 +21,7 @@ abstract class FirAbstractOverrideChecker : FirOverrideChecker {
overrideCandidate: FirSimpleFunction,
baseDeclaration: FirSimpleFunction
): ConeSubstitutor? {
if (overrideCandidate.typeParameters.size != baseDeclaration.typeParameters.size) return null
val types = baseDeclaration.typeParameters.map {
ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), false)
}
val substitutor = substitutorByMap(overrideCandidate.typeParameters.map { it.symbol }.zip(types).toMap())
val substitutor = buildSubstitutorForOverridesCheck(overrideCandidate, baseDeclaration) ?: return null
if (!overrideCandidate.typeParameters.zip(baseDeclaration.typeParameters).all { (a, b) ->
a.bounds.size == b.bounds.size && a.bounds.zip(b.bounds).all { (aBound, bBound) ->
isEqualTypes(aBound, bBound, substitutor)
@@ -34,4 +30,16 @@ abstract class FirAbstractOverrideChecker : FirOverrideChecker {
) return null
return substitutor
}
}
}
fun buildSubstitutorForOverridesCheck(
overrideCandidate: FirCallableMemberDeclaration<*>,
baseDeclaration: FirCallableMemberDeclaration<*>
): ConeSubstitutor? {
if (overrideCandidate.typeParameters.size != baseDeclaration.typeParameters.size) return null
val types = baseDeclaration.typeParameters.map {
ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), false)
}
return substitutorByMap(overrideCandidate.typeParameters.map { it.symbol }.zip(types).toMap())
}
@@ -5,14 +5,28 @@
package org.jetbrains.kotlin.fir.scopes.impl
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.types.ConeFlexibleType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeCheckerContext
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
import java.util.*
import kotlin.collections.HashSet
class FirSuperTypeScope private constructor(
session: FirSession,
@@ -26,6 +40,8 @@ class FirSuperTypeScope private constructor(
private val absentClassifiers = mutableSetOf<Name>()
private val typeContext = ConeTypeCheckerContext(isErrorTypeEqualsToAnything = false, isStubTypeEqualsToAnything = false, session)
override fun processFunctionsByName(name: Name, processor: (FirFunctionSymbol<*>) -> ProcessorAction): ProcessorAction {
processCallablesByName(name, processor, absentFunctions, FirScope::processFunctionsByName)?.let { return it }
return super.processFunctionsByName(name, processor)
@@ -46,32 +62,161 @@ class FirSuperTypeScope private constructor(
return ProcessorAction.NEXT
}
val accepted = HashSet<FirCallableSymbol<*>>()
val pending = mutableListOf<FirCallableSymbol<*>>()
var empty = true
for (scope in scopes) {
if (scope.processCallables(name) {
empty = false
if (it !in accepted && it.getOverridden(accepted) == null) {
pending += it
processor(it)
} else {
ProcessorAction.NEXT
}
}.stop()
) {
return ProcessorAction.STOP
val membersByScope = scopes.mapNotNull { scope ->
val resultForScope = mutableListOf<D>()
scope.processCallables(name) {
if (it !is FirConstructorSymbol) {
resultForScope.add(it)
}
ProcessorAction.NEXT
}
accepted += pending
pending.clear()
resultForScope.takeIf { it.isNotEmpty() }
}
if (empty) {
absentNames += name
if (membersByScope.isEmpty()) {
absentNames.add(name)
return null
}
membersByScope.singleOrNull()?.let { members ->
for (member in members) {
if (processor(member).stop()) return ProcessorAction.STOP
}
return ProcessorAction.NEXT
}
val allMembers = membersByScope.flattenTo(LinkedList())
while (allMembers.isNotEmpty()) {
val maxByVisibility = findMemberWithMaxVisibility(allMembers)
val extractedOverrides = extractBothWaysOverridable(maxByVisibility, allMembers)
val mostSpecific = selectMostSpecificMember(extractedOverrides)
if (processor(mostSpecific).stop()) return ProcessorAction.STOP
}
return null
}
private fun <D : FirCallableSymbol<*>> selectMostSpecificMember(
overridables: Collection<D>
): D {
require(overridables.isNotEmpty()) { "Should have at least one overridable descriptor" }
if (overridables.size == 1) {
return overridables.first()
}
val candidates: MutableCollection<D> = ArrayList(2)
var transitivelyMostSpecific: D = overridables.first()
for (candidate in overridables) {
if (overridables.all { isMoreSpecific(candidate, it) }) {
candidates.add(candidate)
}
if (isMoreSpecific(candidate, transitivelyMostSpecific) && !isMoreSpecific(transitivelyMostSpecific, candidate)) {
transitivelyMostSpecific = candidate
}
}
when {
candidates.isEmpty() -> {
return transitivelyMostSpecific
}
candidates.size == 1 -> {
return candidates.first()
}
else -> {
candidates.firstOrNull {
val type = it.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>()
type != null && type !is ConeFlexibleType
}?.let { return it }
return candidates.first()
}
}
}
private fun isMoreSpecific(
a: FirCallableSymbol<*>,
b: FirCallableSymbol<*>
): Boolean {
val aFir = a.fir
val bFir = b.fir
if (aFir !is FirCallableMemberDeclaration<*> || bFir !is FirCallableMemberDeclaration<*>) return false
val substitutor = buildSubstitutorForOverridesCheck(aFir, bFir) ?: return false
val aReturnType = a.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>()?.let(substitutor::substituteOrSelf) ?: return false
val bReturnType = b.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: return false
if (aFir is FirSimpleFunction) {
require(bFir is FirSimpleFunction) { "b is " + b.javaClass }
return isTypeMoreSpecific(aReturnType, bReturnType)
}
if (aFir is FirProperty) {
require(bFir is FirProperty) { "b is " + b.javaClass }
// TODO: if (!OverridingUtil.isAccessorMoreSpecific(pa.getSetter(), pb.getSetter())) return false
return if (aFir.isVar && bFir.isVar) {
AbstractTypeChecker.equalTypes(typeContext as AbstractTypeCheckerContext, aReturnType, bReturnType)
} else { // both vals or var vs val: val can't be more specific then var
!(!aFir.isVar && bFir.isVar) && isTypeMoreSpecific(aReturnType, bReturnType)
}
}
throw IllegalArgumentException("Unexpected callable: " + a.javaClass)
}
private fun isTypeMoreSpecific(a: ConeKotlinType, b: ConeKotlinType): Boolean =
AbstractTypeChecker.isSubtypeOf(typeContext as AbstractTypeCheckerContext, a, b)
private fun <D : FirCallableSymbol<*>> findMemberWithMaxVisibility(members: Collection<D>): D {
assert(members.isNotEmpty())
var member: D? = null
for (candidate in members) {
if (member == null) {
member = candidate
continue
}
val result = Visibilities.compare(
(member.fir as FirCallableMemberDeclaration<*>).status.visibility,
(candidate.fir as FirCallableMemberDeclaration<*>).status.visibility
)
if (result != null && result < 0) {
member = candidate
}
}
return member!!
}
private fun <D : FirCallableSymbol<*>> extractBothWaysOverridable(
overrider: D,
members: MutableCollection<D>
): Collection<D> {
val result = mutableListOf<D>().apply { add(overrider) }
val iterator = members.iterator()
val overrideCandidate = overrider.fir as FirCallableMemberDeclaration<*>
while (iterator.hasNext()) {
val next = iterator.next()
if (next == overrider) {
iterator.remove()
continue
}
if (similarFunctionsOrBothProperties(overrideCandidate, next.fir as FirCallableMemberDeclaration<*>)) {
result.add(next)
iterator.remove()
}
}
return result
}
override fun processClassifiersByName(name: Name, processor: (FirClassifierSymbol<*>) -> ProcessorAction): ProcessorAction {
if (name in absentClassifiers) {
return ProcessorAction.NEXT
@@ -0,0 +1,41 @@
// !CHECK_TYPE
interface A {
val x: CharSequence
fun foo(): String?
fun <T, F> bar(x: T, y: F): F?
}
interface B {
var x: String
fun foo(): String
fun <X, Y> bar(x: X, y: Y): Y
}
interface C {
val x: String
}
interface D1 : A, B
interface D2 : B, A
interface D3 : A, C
fun main(d1: D1, d2: D2, d3: D3) {
d1.x.checkType { _<String>() }
d1.x = ""
d1.foo().checkType { _<String>() }
d1.bar(1, "").checkType { _<String>() }
d2.x.checkType { _<String>() }
d2.x = ""
d2.foo().checkType { _<String>() }
d2.bar(1, "").checkType { _<String>() }
d3.x.checkType { _<String>() }
d3.x = ""
d3.foo().checkType { _<String?>() }
d3.bar(1, "").checkType { _<String?>() }
}
@@ -0,0 +1,86 @@
FILE: simpleMostSpecific.kt
public abstract interface A : R|kotlin/Any| {
public abstract val x: R|kotlin/CharSequence|
public get(): R|kotlin/CharSequence|
public abstract fun foo(): R|kotlin/String?|
public abstract fun <T, F> bar(x: R|T|, y: R|F|): R|F?|
}
public abstract interface B : R|kotlin/Any| {
public abstract var x: R|kotlin/String|
public get(): R|kotlin/String|
public set(value: R|kotlin/String|): R|kotlin/Unit|
public abstract fun foo(): R|kotlin/String|
public abstract fun <X, Y> bar(x: R|X|, y: R|Y|): R|Y|
}
public abstract interface C : R|kotlin/Any| {
public abstract val x: R|kotlin/String|
public get(): R|kotlin/String|
}
public abstract interface D1 : R|A|, R|B| {
}
public abstract interface D2 : R|B|, R|A| {
}
public abstract interface D3 : R|A|, R|C| {
}
public final fun main(d1: R|D1|, d2: R|D2|, d3: R|D3|): R|kotlin/Unit| {
R|<local>/d1|.R|/B.x|.R|tests/_checkType/checkType|<R|kotlin/String|>(<L> = checkType@fun R|tests/_checkType/Inv<kotlin/String>|.<anonymous>(): R|kotlin/Unit| {
this@R|special/anonymous|.R|tests/_checkType/_|<R|kotlin/String|>()
}
)
R|<local>/d1|.R|/B.x| = String()
R|<local>/d1|.R|/B.foo|().R|tests/_checkType/checkType|<R|kotlin/String|>(<L> = checkType@fun R|tests/_checkType/Inv<kotlin/String>|.<anonymous>(): R|kotlin/Unit| {
this@R|special/anonymous|.R|tests/_checkType/_|<R|kotlin/String|>()
}
)
R|<local>/d1|.R|/B.bar|<R|kotlin/Int|, R|kotlin/String|>(Int(1), String()).R|tests/_checkType/checkType|<R|kotlin/String|>(<L> = checkType@fun R|tests/_checkType/Inv<kotlin/String>|.<anonymous>(): R|kotlin/Unit| {
this@R|special/anonymous|.R|tests/_checkType/_|<R|kotlin/String|>()
}
)
R|<local>/d2|.R|/B.x|.R|tests/_checkType/checkType|<R|kotlin/String|>(<L> = checkType@fun R|tests/_checkType/Inv<kotlin/String>|.<anonymous>(): R|kotlin/Unit| {
this@R|special/anonymous|.R|tests/_checkType/_|<R|kotlin/String|>()
}
)
R|<local>/d2|.R|/B.x| = String()
R|<local>/d2|.R|/B.foo|().R|tests/_checkType/checkType|<R|kotlin/String|>(<L> = checkType@fun R|tests/_checkType/Inv<kotlin/String>|.<anonymous>(): R|kotlin/Unit| {
this@R|special/anonymous|.R|tests/_checkType/_|<R|kotlin/String|>()
}
)
R|<local>/d2|.R|/B.bar|<R|kotlin/Int|, R|kotlin/String|>(Int(1), String()).R|tests/_checkType/checkType|<R|kotlin/String|>(<L> = checkType@fun R|tests/_checkType/Inv<kotlin/String>|.<anonymous>(): R|kotlin/Unit| {
this@R|special/anonymous|.R|tests/_checkType/_|<R|kotlin/String|>()
}
)
R|<local>/d3|.R|/C.x|.R|tests/_checkType/checkType|<R|kotlin/String|>(<L> = checkType@fun R|tests/_checkType/Inv<kotlin/String>|.<anonymous>(): R|kotlin/Unit| {
this@R|special/anonymous|.R|tests/_checkType/_|<R|kotlin/String|>()
}
)
R|<local>/d3|.R|/C.x| = String()
R|<local>/d3|.R|/A.foo|().R|tests/_checkType/checkType|<R|kotlin/String?|>(<L> = checkType@fun R|tests/_checkType/Inv<kotlin/String?>|.<anonymous>(): R|kotlin/Unit| {
this@R|special/anonymous|.R|tests/_checkType/_|<R|kotlin/String?|>()
}
)
R|<local>/d3|.R|/A.bar|<R|kotlin/Int|, R|kotlin/String|>(Int(1), String()).R|tests/_checkType/checkType|<R|kotlin/String?|>(<L> = checkType@fun R|tests/_checkType/Inv<kotlin/String?>|.<anonymous>(): R|kotlin/Unit| {
this@R|special/anonymous|.R|tests/_checkType/_|<R|kotlin/String?|>()
}
)
}
FILE: CHECK_TYPE.kt
public final fun <T> checkSubtype(t: R|T|): R|T| {
^checkSubtype R|<local>/t|
}
public final class Inv<T> : R|kotlin/Any| {
public constructor<T>(): R|tests/_checkType/Inv<T>| {
super<R|kotlin/Any|>()
}
}
public final fun <E> R|tests/_checkType/Inv<E>|._(): R|kotlin/Unit| {
}
public final infix fun <T> R|T|.checkType(f: R|tests/_checkType/Inv<T>.() -> kotlin/Unit|): R|kotlin/Unit| {
}
@@ -873,6 +873,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
runTest("compiler/fir/resolve/testData/resolve/overrides/simpleFakeOverride.kt");
}
@TestMetadata("simpleMostSpecific.kt")
public void testSimpleMostSpecific() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/overrides/simpleMostSpecific.kt");
}
@TestMetadata("supertypeGenerics.kt")
public void testSupertypeGenerics() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/overrides/supertypeGenerics.kt");
+3 -3
View File
@@ -201,10 +201,10 @@ FILE fqName:<root> fileName:/kt30020.kt
overridden:
public abstract fun isEmpty (): kotlin.Boolean declared in kotlin.collections.List
$this: VALUE_PARAMETER name:<this> type:kotlin.collections.List
FUN FAKE_OVERRIDE name:iterator visibility:public modality:ABSTRACT <> ($this:kotlin.collections.List) returnType:kotlin.collections.Iterator<kotlin.Int> [fake_override,operator]
FUN FAKE_OVERRIDE name:iterator visibility:public modality:ABSTRACT <> ($this:kotlin.collections.MutableCollection) returnType:kotlin.collections.MutableIterator<kotlin.Int> [fake_override,operator]
overridden:
public abstract fun iterator (): kotlin.collections.Iterator<E of kotlin.collections.MutableList> [operator] declared in kotlin.collections.List
$this: VALUE_PARAMETER name:<this> type:kotlin.collections.List
public abstract fun iterator (): kotlin.collections.MutableIterator<E of kotlin.collections.MutableList> [operator] declared in kotlin.collections.MutableCollection
$this: VALUE_PARAMETER name:<this> type:kotlin.collections.MutableCollection
FUN FAKE_OVERRIDE name:lastIndexOf visibility:public modality:ABSTRACT <> ($this:kotlin.collections.List, element:kotlin.Int) returnType:kotlin.Int [fake_override]
overridden:
public abstract fun lastIndexOf (element: E of kotlin.collections.MutableList): kotlin.Int declared in kotlin.collections.List