From 2287435740dbfb869b9d9e700f65ca878f807b6e Mon Sep 17 00:00:00 2001 From: "simon.ogorodnik" Date: Mon, 1 Jun 2020 17:17:16 +0300 Subject: [PATCH] [FIR] KT-39033: Fix generic property override detection #KT-39033 Fixed --- .../genericReceiverPropertyOverride.kt | 15 +++++++ .../genericReceiverPropertyOverride.txt | 39 +++++++++++++++++++ .../fir/FirDiagnosticsTestGenerated.java | 5 +++ ...DiagnosticsWithLightTreeTestGenerated.java | 5 +++ .../scopes/impl/FirAbstractOverrideChecker.kt | 25 ++++++++---- .../scopes/impl/FirStandardOverrideChecker.kt | 14 +++---- 6 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/genericReceiverPropertyOverride.kt create mode 100644 compiler/fir/analysis-tests/testData/resolve/genericReceiverPropertyOverride.txt diff --git a/compiler/fir/analysis-tests/testData/resolve/genericReceiverPropertyOverride.kt b/compiler/fir/analysis-tests/testData/resolve/genericReceiverPropertyOverride.kt new file mode 100644 index 00000000000..74b6bed41ef --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/genericReceiverPropertyOverride.kt @@ -0,0 +1,15 @@ +abstract class A { + open val Inv.phasedFir: D get() = TODO() +} + +abstract class B : A() { + final override val Inv.phasedFir: D get() = TODO() +} + +abstract class Inv + +class C : B() { + fun foo(x: Inv) { + x.phasedFir + } +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolve/genericReceiverPropertyOverride.txt b/compiler/fir/analysis-tests/testData/resolve/genericReceiverPropertyOverride.txt new file mode 100644 index 00000000000..365fee2d8e1 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/genericReceiverPropertyOverride.txt @@ -0,0 +1,39 @@ +FILE: genericReceiverPropertyOverride.kt + public abstract class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + public open val R|Inv|.phasedFir: R|D| + public get(): R|D| { + ^ R|kotlin/TODO|() + } + + } + public abstract class B : R|A| { + public constructor(): R|B| { + super() + } + + public final override val R|Inv|.phasedFir: R|D| + public get(): R|D| { + ^ R|kotlin/TODO|() + } + + } + public abstract class Inv : R|kotlin/Any| { + public constructor(): R|Inv| { + super() + } + + } + public final class C : R|B| { + public constructor(): R|C| { + super() + } + + public final fun foo(x: R|Inv|): R|kotlin/Unit| { + (this@R|/C|, R|/x|).R|/B.phasedFir| + } + + } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index 41a75633c51..5f9cb3d2746 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -193,6 +193,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { runTest("compiler/fir/analysis-tests/testData/resolve/genericFunctions.kt"); } + @TestMetadata("genericReceiverPropertyOverride.kt") + public void testGenericReceiverPropertyOverride() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/genericReceiverPropertyOverride.kt"); + } + @TestMetadata("incorrectSuperCall.kt") public void testIncorrectSuperCall() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/incorrectSuperCall.kt"); diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java index 3b1521d884b..23cf86a9cbc 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java @@ -193,6 +193,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/genericFunctions.kt"); } + @TestMetadata("genericReceiverPropertyOverride.kt") + public void testGenericReceiverPropertyOverride() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/genericReceiverPropertyOverride.kt"); + } + @TestMetadata("incorrectSuperCall.kt") public void testIncorrectSuperCall() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/incorrectSuperCall.kt"); diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractOverrideChecker.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractOverrideChecker.kt index 1b6005e9545..191ff2d3eed 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractOverrideChecker.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractOverrideChecker.kt @@ -5,8 +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.declarations.* import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker @@ -17,15 +16,24 @@ abstract class FirAbstractOverrideChecker : FirOverrideChecker { protected abstract fun isEqualTypes(candidateTypeRef: FirTypeRef, baseTypeRef: FirTypeRef, substitutor: ConeSubstitutor): Boolean + private fun isCompatibleTypeParameters( + overrideCandidate: FirTypeParameterRef, + baseDeclaration: FirTypeParameterRef, + substitutor: ConeSubstitutor + ): Boolean { + if (overrideCandidate.symbol == baseDeclaration.symbol) return true + if (overrideCandidate !is FirTypeParameter || baseDeclaration !is FirTypeParameter) return false + return overrideCandidate.bounds.zip(baseDeclaration.bounds).all { (aBound, bBound) -> isEqualTypes(aBound, bBound, substitutor) } + } + protected fun getSubstitutorIfTypeParametersAreCompatible( - overrideCandidate: FirSimpleFunction, - baseDeclaration: FirSimpleFunction + overrideCandidate: FirCallableMemberDeclaration<*>, + baseDeclaration: FirCallableMemberDeclaration<*> ): ConeSubstitutor? { 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) - } + if ( + overrideCandidate.typeParameters.zip(baseDeclaration.typeParameters).any { (override, base) -> + !isCompatibleTypeParameters(override, base, substitutor) } ) return null return substitutor @@ -38,6 +46,7 @@ fun buildSubstitutorForOverridesCheck( ): ConeSubstitutor? { if (overrideCandidate.typeParameters.size != baseDeclaration.typeParameters.size) return null + if (baseDeclaration.typeParameters.isEmpty()) return ConeSubstitutor.Empty val types = baseDeclaration.typeParameters.map { ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), false) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirStandardOverrideChecker.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirStandardOverrideChecker.kt index ff39ae284ad..9dcc8157602 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirStandardOverrideChecker.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirStandardOverrideChecker.kt @@ -11,11 +11,7 @@ import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor 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.types.AbstractStrictEqualityTypeChecker.strictEqualTypes +import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.SimpleTypeMarker @@ -24,7 +20,7 @@ class FirStandardOverrideChecker(session: FirSession) : FirAbstractOverrideCheck private val context: ConeTypeContext = session.typeContext - private fun isEqualTypes(candidateType: ConeKotlinType, baseType: ConeKotlinType, substitutor: ConeSubstitutor): Boolean{ + private fun isEqualTypes(candidateType: ConeKotlinType, baseType: ConeKotlinType, substitutor: ConeSubstitutor): Boolean { val substitutedCandidateType = substitutor.substituteOrSelf(candidateType) val substitutedBaseType = substitutor.substituteOrSelf(baseType) return with(context) { @@ -76,8 +72,8 @@ class FirStandardOverrideChecker(session: FirSession) : FirAbstractOverrideCheck overrideCandidate: FirCallableMemberDeclaration<*>, baseDeclaration: FirProperty ): Boolean { - // TODO: substitutor - return overrideCandidate is FirProperty && - isEqualReceiverTypes(overrideCandidate.receiverTypeRef, baseDeclaration.receiverTypeRef, ConeSubstitutor.Empty) + if (overrideCandidate !is FirProperty) return false + val substitutor = getSubstitutorIfTypeParametersAreCompatible(overrideCandidate, baseDeclaration) ?: return false + return isEqualReceiverTypes(overrideCandidate.receiverTypeRef, baseDeclaration.receiverTypeRef, substitutor) } } \ No newline at end of file