FIR: support proper overriding of generic functions
This commit is contained in:
committed by
Mikhail Glukhikh
parent
7b9f5293da
commit
d19a250cbe
+37
-20
@@ -8,14 +8,14 @@ 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.*
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.typeContext
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.ConeTypeContext
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||
import org.jetbrains.kotlin.types.AbstractStrictEqualityTypeChecker
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
|
||||
@@ -25,33 +25,51 @@ abstract class AbstractFirOverrideScope(val session: FirSession) : FirScope {
|
||||
|
||||
val context: ConeTypeContext = session.typeContext
|
||||
|
||||
private fun isEqualTypes(a: ConeKotlinType, b: ConeKotlinType) = AbstractStrictEqualityTypeChecker.strictEqualTypes(context, a, b)
|
||||
private fun isEqualTypes(a: ConeKotlinType, b: ConeKotlinType, substitution: ConeSubstitutor) =
|
||||
AbstractStrictEqualityTypeChecker.strictEqualTypes(context, substitution.substituteOrSelf(a), substitution.substituteOrSelf(b))
|
||||
|
||||
private fun isEqualTypes(a: FirTypeRef, b: FirTypeRef) =
|
||||
isEqualTypes(a.cast<FirResolvedTypeRef>().type, b.cast<FirResolvedTypeRef>().type)
|
||||
private fun isEqualTypes(a: FirTypeRef, b: FirTypeRef, substitution: ConeSubstitutor) =
|
||||
isEqualTypes(a.cast<FirResolvedTypeRef>().type, b.cast<FirResolvedTypeRef>().type, substitution)
|
||||
|
||||
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.returnTypeRef, selfParam.returnTypeRef)
|
||||
}
|
||||
if (member.valueParameters.size != self.valueParameters.size) return false
|
||||
if (member.typeParameters.size != self.typeParameters.size) return false
|
||||
|
||||
val types = self.typeParameters.map {
|
||||
ConeTypeParameterTypeImpl(it.symbol, false)
|
||||
}
|
||||
val substitution = ConeSubstitutorByMap(member.typeParameters.map { it.symbol }.zip(types).toMap())
|
||||
if (!member.typeParameters.zip(self.typeParameters).all { (a, b) ->
|
||||
a.bounds.size == b.bounds.size &&
|
||||
a.bounds.zip(b.bounds).all { (aBound, bBound) -> isEqualTypes(aBound, bBound, substitution) }
|
||||
}
|
||||
) return false
|
||||
if (!sameReceivers(member.receiverTypeRef, self.receiverTypeRef, substitution)) return false
|
||||
|
||||
return member.valueParameters.zip(self.valueParameters).all { (memberParam, selfParam) ->
|
||||
isEqualTypes(memberParam.returnTypeRef, selfParam.returnTypeRef, substitution)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sameReceivers(memberTypeRef: FirTypeRef?, selfTypeRef: FirTypeRef?, substitution: ConeSubstitutor): Boolean {
|
||||
return when {
|
||||
memberTypeRef != null && selfTypeRef != null -> isEqualTypes(memberTypeRef, selfTypeRef, substitution)
|
||||
else -> memberTypeRef == null && selfTypeRef == null
|
||||
}
|
||||
}
|
||||
|
||||
protected fun ConeCallableSymbol.isOverridden(seen: Set<ConeCallableSymbol>): ConeCallableSymbol? {
|
||||
if (overrides.containsKey(this)) return overrides[this]
|
||||
|
||||
fun sameReceivers(memberTypeRef: FirTypeRef?, selfTypeRef: FirTypeRef?): Boolean {
|
||||
return when {
|
||||
memberTypeRef != null && selfTypeRef != null -> isEqualTypes(memberTypeRef, selfTypeRef)
|
||||
else -> memberTypeRef == null && selfTypeRef == null
|
||||
}
|
||||
}
|
||||
|
||||
fun similarFunctionsOrBothProperties(declaration: FirCallableDeclaration, self: FirCallableDeclaration): Boolean {
|
||||
return when (declaration) {
|
||||
is FirNamedFunction -> self is FirNamedFunction && isOverriddenFunCheck(declaration, self)
|
||||
is FirConstructor -> false
|
||||
is FirProperty -> self is FirProperty
|
||||
is FirProperty -> self is FirProperty && sameReceivers(
|
||||
declaration.receiverTypeRef,
|
||||
self.receiverTypeRef,
|
||||
ConeSubstitutor.Empty // TODO
|
||||
)
|
||||
else -> error("Unknown fir callable type: $declaration, $self")
|
||||
}
|
||||
}
|
||||
@@ -60,7 +78,6 @@ abstract class AbstractFirOverrideScope(val session: FirSession) : FirScope {
|
||||
val overriding = seen.firstOrNull {
|
||||
val member = (it as AbstractFirBasedSymbol<*>).fir as FirCallableMemberDeclaration
|
||||
self.modality != Modality.FINAL
|
||||
&& sameReceivers(member.receiverTypeRef, self.receiverTypeRef)
|
||||
&& similarFunctionsOrBothProperties(member, self)
|
||||
} // TODO: two or more overrides for one fun?
|
||||
overrides[this] = overriding
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
interface I {
|
||||
fun <T : Comparable<T>> f(t: List<T>): Any// T = D, List<D> == List<D>
|
||||
}
|
||||
|
||||
abstract class Base {
|
||||
fun <D : Comparable<D>> f(t: List<D>) {}
|
||||
}
|
||||
|
||||
|
||||
class C : Base(), I
|
||||
|
||||
|
||||
fun f(list: List<Int>) {
|
||||
C().f(list)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
FILE: generics.kt
|
||||
public abstract interface I : R|kotlin/Any| {
|
||||
public abstract fun <T : R|kotlin/Comparable<T>|> f(t: R|kotlin/collections/List<T>|): R|kotlin/Any|
|
||||
|
||||
}
|
||||
public abstract class Base : R|kotlin/Any| {
|
||||
public constructor(): R|Base| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final fun <D : R|kotlin/Comparable<D>|> f(t: R|kotlin/collections/List<D>|): R|kotlin/Unit| {
|
||||
}
|
||||
|
||||
}
|
||||
public final class C : R|Base|, R|I| {
|
||||
public constructor(): R|C| {
|
||||
super<R|Base|>()
|
||||
}
|
||||
|
||||
}
|
||||
public final fun f(list: R|kotlin/collections/List<kotlin/Int>|): R|kotlin/Unit| {
|
||||
R|/C.C|().R|/Base.f|<R|kotlin/Int|>(R|<local>/list|)
|
||||
}
|
||||
+5
@@ -490,6 +490,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/resolve/overrides"), Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("generics.kt")
|
||||
public void testGenerics() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/overrides/generics.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/overrides/simple.kt");
|
||||
|
||||
Reference in New Issue
Block a user