FIR: support proper overriding of generic functions

This commit is contained in:
Simon Ogorodnik
2019-04-25 14:56:04 +03:00
committed by Mikhail Glukhikh
parent 7b9f5293da
commit d19a250cbe
4 changed files with 80 additions and 20 deletions
@@ -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|)
}
@@ -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");