diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/AbstractFirOverrideScope.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/AbstractFirOverrideScope.kt index 1163d172552..2cf162d58af 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/AbstractFirOverrideScope.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/AbstractFirOverrideScope.kt @@ -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().type, b.cast().type) + private fun isEqualTypes(a: FirTypeRef, b: FirTypeRef, substitution: ConeSubstitutor) = + isEqualTypes(a.cast().type, b.cast().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? { 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 diff --git a/compiler/fir/resolve/testData/resolve/overrides/generics.kt b/compiler/fir/resolve/testData/resolve/overrides/generics.kt new file mode 100644 index 00000000000..b6c7d6d1d3a --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/overrides/generics.kt @@ -0,0 +1,15 @@ +interface I { + fun > f(t: List): Any// T = D, List == List +} + +abstract class Base { + fun > f(t: List) {} +} + + +class C : Base(), I + + +fun f(list: List) { + C().f(list) +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/overrides/generics.txt b/compiler/fir/resolve/testData/resolve/overrides/generics.txt new file mode 100644 index 00000000000..9f07dc62857 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/overrides/generics.txt @@ -0,0 +1,23 @@ +FILE: generics.kt + public abstract interface I : R|kotlin/Any| { + public abstract fun |> f(t: R|kotlin/collections/List|): R|kotlin/Any| + + } + public abstract class Base : R|kotlin/Any| { + public constructor(): R|Base| { + super() + } + + public final fun |> f(t: R|kotlin/collections/List|): R|kotlin/Unit| { + } + + } + public final class C : R|Base|, R|I| { + public constructor(): R|C| { + super() + } + + } + public final fun f(list: R|kotlin/collections/List|): R|kotlin/Unit| { + R|/C.C|().R|/Base.f|(R|/list|) + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java index e5145c51243..1a5d76ee282 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java @@ -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");