FIR: Select most specific members from supertypes scope
This commit is contained in:
+1
-1
@@ -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 {
|
||||
|
||||
+15
-7
@@ -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())
|
||||
}
|
||||
|
||||
+164
-19
@@ -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| {
|
||||
}
|
||||
+5
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user