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.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
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.scopes.FirScope
|
||||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
|
import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
|
||||||
import org.jetbrains.kotlin.fir.typeContext
|
import org.jetbrains.kotlin.fir.typeContext
|
||||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.fir.types.ConeTypeContext
|
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
|
||||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
|
||||||
import org.jetbrains.kotlin.types.AbstractStrictEqualityTypeChecker
|
import org.jetbrains.kotlin.types.AbstractStrictEqualityTypeChecker
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||||
|
|
||||||
@@ -25,33 +25,51 @@ abstract class AbstractFirOverrideScope(val session: FirSession) : FirScope {
|
|||||||
|
|
||||||
val context: ConeTypeContext = session.typeContext
|
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) =
|
private fun isEqualTypes(a: FirTypeRef, b: FirTypeRef, substitution: ConeSubstitutor) =
|
||||||
isEqualTypes(a.cast<FirResolvedTypeRef>().type, b.cast<FirResolvedTypeRef>().type)
|
isEqualTypes(a.cast<FirResolvedTypeRef>().type, b.cast<FirResolvedTypeRef>().type, substitution)
|
||||||
|
|
||||||
private fun isOverriddenFunCheck(member: FirNamedFunction, self: FirNamedFunction): Boolean {
|
private fun isOverriddenFunCheck(member: FirNamedFunction, self: FirNamedFunction): Boolean {
|
||||||
return member.valueParameters.size == self.valueParameters.size &&
|
if (member.valueParameters.size != self.valueParameters.size) return false
|
||||||
member.valueParameters.zip(self.valueParameters).all { (memberParam, selfParam) ->
|
if (member.typeParameters.size != self.typeParameters.size) return false
|
||||||
isEqualTypes(memberParam.returnTypeRef, selfParam.returnTypeRef)
|
|
||||||
}
|
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? {
|
protected fun ConeCallableSymbol.isOverridden(seen: Set<ConeCallableSymbol>): ConeCallableSymbol? {
|
||||||
if (overrides.containsKey(this)) return overrides[this]
|
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 {
|
fun similarFunctionsOrBothProperties(declaration: FirCallableDeclaration, self: FirCallableDeclaration): Boolean {
|
||||||
return when (declaration) {
|
return when (declaration) {
|
||||||
is FirNamedFunction -> self is FirNamedFunction && isOverriddenFunCheck(declaration, self)
|
is FirNamedFunction -> self is FirNamedFunction && isOverriddenFunCheck(declaration, self)
|
||||||
is FirConstructor -> false
|
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")
|
else -> error("Unknown fir callable type: $declaration, $self")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +78,6 @@ abstract class AbstractFirOverrideScope(val session: FirSession) : FirScope {
|
|||||||
val overriding = seen.firstOrNull {
|
val overriding = seen.firstOrNull {
|
||||||
val member = (it as AbstractFirBasedSymbol<*>).fir as FirCallableMemberDeclaration
|
val member = (it as AbstractFirBasedSymbol<*>).fir as FirCallableMemberDeclaration
|
||||||
self.modality != Modality.FINAL
|
self.modality != Modality.FINAL
|
||||||
&& sameReceivers(member.receiverTypeRef, self.receiverTypeRef)
|
|
||||||
&& similarFunctionsOrBothProperties(member, self)
|
&& similarFunctionsOrBothProperties(member, self)
|
||||||
} // TODO: two or more overrides for one fun?
|
} // TODO: two or more overrides for one fun?
|
||||||
overrides[this] = overriding
|
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);
|
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")
|
@TestMetadata("simple.kt")
|
||||||
public void testSimple() throws Exception {
|
public void testSimple() throws Exception {
|
||||||
runTest("compiler/fir/resolve/testData/resolve/overrides/simple.kt");
|
runTest("compiler/fir/resolve/testData/resolve/overrides/simple.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user