[FIR] Make overriding generic callables independent of TP bounds order
This commit is contained in:
+47
@@ -0,0 +1,47 @@
|
|||||||
|
// FILE: Test.java
|
||||||
|
public class Test {
|
||||||
|
|
||||||
|
public interface I1 {}
|
||||||
|
public interface I2 {}
|
||||||
|
public interface I3 {}
|
||||||
|
|
||||||
|
public interface I123 extends I1, I2, I3 {}
|
||||||
|
|
||||||
|
public static class Base {
|
||||||
|
public <P extends I1 & I2 & I3> void foo(P p) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Derived extends Base {
|
||||||
|
@Override
|
||||||
|
public <P extends I1 & I3 & I2> void foo(P p) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DerivedRaw extends Base {
|
||||||
|
public void foo(I1 p) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
interface KI1
|
||||||
|
interface KI2
|
||||||
|
interface KI12 : KI1, KI2
|
||||||
|
|
||||||
|
open class KBase {
|
||||||
|
open fun <P> foo()
|
||||||
|
where P : KI1, P : KI2 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class KDerived : KBase() {
|
||||||
|
override fun <P> foo()
|
||||||
|
where P : KI2, P : KI1 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun callJava(derived: Test.Derived, derivedRaw: Test.DerivedRaw, v: Test.I123) {
|
||||||
|
derived.foo(v)
|
||||||
|
derivedRaw.foo(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun callKotlin(derived: KDerived) {
|
||||||
|
derived.foo<KI12>()
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
FILE: main.kt
|
||||||
|
public abstract interface KI1 : R|kotlin/Any| {
|
||||||
|
}
|
||||||
|
public abstract interface KI2 : R|kotlin/Any| {
|
||||||
|
}
|
||||||
|
public abstract interface KI12 : R|KI1|, R|KI2| {
|
||||||
|
}
|
||||||
|
public open class KBase : R|kotlin/Any| {
|
||||||
|
public constructor(): R|KBase| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
public open fun <P : R|KI1|, R|KI2|> foo(): R|kotlin/Unit| {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public final class KDerived : R|KBase| {
|
||||||
|
public constructor(): R|KDerived| {
|
||||||
|
super<R|KBase|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
public final override fun <P : R|KI2|, R|KI1|> foo(): R|kotlin/Unit| {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public final fun callJava(derived: R|Test.Derived|, derivedRaw: R|Test.DerivedRaw|, v: R|Test.I123|): R|kotlin/Unit| {
|
||||||
|
R|<local>/derived|.R|/Test.Derived.foo|<R|ft<Test.I123, Test.I123?>!|>(R|<local>/v|)
|
||||||
|
R|<local>/derivedRaw|.R|/Test.DerivedRaw.foo|(R|<local>/v|)
|
||||||
|
}
|
||||||
|
public final fun callKotlin(derived: R|KDerived|): R|kotlin/Unit| {
|
||||||
|
R|<local>/derived|.R|/KDerived.foo|<R|KI12|>()
|
||||||
|
}
|
||||||
Generated
+5
@@ -293,6 +293,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
|
|||||||
runTest("compiler/fir/analysis-tests/testData/resolve/objectInnerClass.kt");
|
runTest("compiler/fir/analysis-tests/testData/resolve/objectInnerClass.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("offOrderMultiBoundGenericOverride.kt")
|
||||||
|
public void testOffOrderMultiBoundGenericOverride() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/offOrderMultiBoundGenericOverride.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("problems2.kt")
|
@TestMetadata("problems2.kt")
|
||||||
public void testProblems2() throws Exception {
|
public void testProblems2() throws Exception {
|
||||||
runTest("compiler/fir/analysis-tests/testData/resolve/problems2.kt");
|
runTest("compiler/fir/analysis-tests/testData/resolve/problems2.kt");
|
||||||
|
|||||||
+5
@@ -293,6 +293,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
|
|||||||
runTest("compiler/fir/analysis-tests/testData/resolve/objectInnerClass.kt");
|
runTest("compiler/fir/analysis-tests/testData/resolve/objectInnerClass.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("offOrderMultiBoundGenericOverride.kt")
|
||||||
|
public void testOffOrderMultiBoundGenericOverride() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/offOrderMultiBoundGenericOverride.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("problems2.kt")
|
@TestMetadata("problems2.kt")
|
||||||
public void testProblems2() throws Exception {
|
public void testProblems2() throws Exception {
|
||||||
runTest("compiler/fir/analysis-tests/testData/resolve/problems2.kt");
|
runTest("compiler/fir/analysis-tests/testData/resolve/problems2.kt");
|
||||||
|
|||||||
@@ -7,14 +7,12 @@ package org.jetbrains.kotlin.fir.java.scopes
|
|||||||
|
|
||||||
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.FirCallableMemberDeclaration
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.modality
|
|
||||||
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
|
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
|
||||||
import org.jetbrains.kotlin.fir.java.enhancement.readOnlyToMutable
|
import org.jetbrains.kotlin.fir.java.enhancement.readOnlyToMutable
|
||||||
import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible
|
import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible
|
||||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
|
||||||
import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractOverrideChecker
|
import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractOverrideChecker
|
||||||
import org.jetbrains.kotlin.fir.typeContext
|
import org.jetbrains.kotlin.fir.typeContext
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
@@ -54,13 +52,29 @@ class JavaOverrideChecker internal constructor(
|
|||||||
substitutor
|
substitutor
|
||||||
)
|
)
|
||||||
|
|
||||||
|
override fun buildTypeParametersSubstitutorIfCompatible(
|
||||||
|
overrideCandidate: FirCallableMemberDeclaration<*>,
|
||||||
|
baseDeclaration: FirCallableMemberDeclaration<*>
|
||||||
|
): ConeSubstitutor? {
|
||||||
|
|
||||||
|
if (overrideCandidate.typeParameters.isEmpty() && baseDeclaration.typeParameters.isEmpty()) return ConeSubstitutor.Empty
|
||||||
|
|
||||||
|
val typeParametersErasure =
|
||||||
|
(overrideCandidate.typeParameters + baseDeclaration.typeParameters).associate {
|
||||||
|
val symbol = it.symbol
|
||||||
|
val firstBound = symbol.fir.bounds.first() // Note that in Java type parameter typed arguments always erased to first bound
|
||||||
|
symbol to firstBound.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack)
|
||||||
|
}
|
||||||
|
return substitutorByMap(typeParametersErasure)
|
||||||
|
}
|
||||||
|
|
||||||
override fun isOverriddenFunction(overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction): Boolean {
|
override fun isOverriddenFunction(overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction): Boolean {
|
||||||
// NB: overrideCandidate is from Java and has no receiver
|
// NB: overrideCandidate is from Java and has no receiver
|
||||||
val receiverTypeRef = baseDeclaration.receiverTypeRef
|
val receiverTypeRef = baseDeclaration.receiverTypeRef
|
||||||
val baseParameterTypes = listOfNotNull(receiverTypeRef) + baseDeclaration.valueParameters.map { it.returnTypeRef }
|
val baseParameterTypes = listOfNotNull(receiverTypeRef) + baseDeclaration.valueParameters.map { it.returnTypeRef }
|
||||||
|
|
||||||
if (overrideCandidate.valueParameters.size != baseParameterTypes.size) return false
|
if (overrideCandidate.valueParameters.size != baseParameterTypes.size) return false
|
||||||
val substitutor = getSubstitutorIfTypeParametersAreCompatible(overrideCandidate, baseDeclaration) ?: return false
|
val substitutor = buildTypeParametersSubstitutorIfCompatible(overrideCandidate, baseDeclaration) ?: return false
|
||||||
|
|
||||||
return overrideCandidate.valueParameters.zip(baseParameterTypes).all { (paramFromJava, baseType) ->
|
return overrideCandidate.valueParameters.zip(baseParameterTypes).all { (paramFromJava, baseType) ->
|
||||||
isEqualTypes(paramFromJava.returnTypeRef, baseType, substitutor)
|
isEqualTypes(paramFromJava.returnTypeRef, baseType, substitutor)
|
||||||
|
|||||||
+2
-20
@@ -16,28 +16,10 @@ abstract class FirAbstractOverrideChecker : FirOverrideChecker {
|
|||||||
|
|
||||||
protected abstract fun isEqualTypes(candidateTypeRef: FirTypeRef, baseTypeRef: FirTypeRef, substitutor: ConeSubstitutor): Boolean
|
protected abstract fun isEqualTypes(candidateTypeRef: FirTypeRef, baseTypeRef: FirTypeRef, substitutor: ConeSubstitutor): Boolean
|
||||||
|
|
||||||
private fun isCompatibleTypeParameters(
|
protected abstract fun buildTypeParametersSubstitutorIfCompatible(
|
||||||
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: FirCallableMemberDeclaration<*>,
|
overrideCandidate: FirCallableMemberDeclaration<*>,
|
||||||
baseDeclaration: FirCallableMemberDeclaration<*>
|
baseDeclaration: FirCallableMemberDeclaration<*>
|
||||||
): ConeSubstitutor? {
|
): ConeSubstitutor?
|
||||||
val substitutor = buildSubstitutorForOverridesCheck(overrideCandidate, baseDeclaration) ?: return null
|
|
||||||
if (
|
|
||||||
overrideCandidate.typeParameters.zip(baseDeclaration.typeParameters).any { (override, base) ->
|
|
||||||
!isCompatibleTypeParameters(override, base, substitutor)
|
|
||||||
}
|
|
||||||
) return null
|
|
||||||
return substitutor
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildSubstitutorForOverridesCheck(
|
fun buildSubstitutorForOverridesCheck(
|
||||||
|
|||||||
+57
-8
@@ -6,9 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.fir.scopes.impl
|
package org.jetbrains.kotlin.fir.scopes.impl
|
||||||
|
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
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.resolve.substitution.ConeSubstitutor
|
||||||
import org.jetbrains.kotlin.fir.typeContext
|
import org.jetbrains.kotlin.fir.typeContext
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
@@ -20,9 +18,7 @@ class FirStandardOverrideChecker(session: FirSession) : FirAbstractOverrideCheck
|
|||||||
|
|
||||||
private val context: ConeTypeContext = session.typeContext
|
private val context: ConeTypeContext = session.typeContext
|
||||||
|
|
||||||
private fun isEqualTypes(candidateType: ConeKotlinType, baseType: ConeKotlinType, substitutor: ConeSubstitutor): Boolean {
|
private fun isEqualTypes(substitutedCandidateType: ConeKotlinType, substitutedBaseType: ConeKotlinType): Boolean {
|
||||||
val substitutedCandidateType = substitutor.substituteOrSelf(candidateType)
|
|
||||||
val substitutedBaseType = substitutor.substituteOrSelf(baseType)
|
|
||||||
return with(context) {
|
return with(context) {
|
||||||
val baseIsFlexible = substitutedBaseType.isFlexible()
|
val baseIsFlexible = substitutedBaseType.isFlexible()
|
||||||
val candidateIsFlexible = substitutedCandidateType.isFlexible()
|
val candidateIsFlexible = substitutedCandidateType.isFlexible()
|
||||||
@@ -45,9 +41,62 @@ class FirStandardOverrideChecker(session: FirSession) : FirAbstractOverrideCheck
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isEqualTypes(candidateType: ConeKotlinType, baseType: ConeKotlinType, substitutor: ConeSubstitutor): Boolean {
|
||||||
|
val substitutedCandidateType = substitutor.substituteOrSelf(candidateType)
|
||||||
|
val substitutedBaseType = substitutor.substituteOrSelf(baseType)
|
||||||
|
return isEqualTypes(substitutedCandidateType, substitutedBaseType)
|
||||||
|
}
|
||||||
|
|
||||||
override fun isEqualTypes(candidateTypeRef: FirTypeRef, baseTypeRef: FirTypeRef, substitutor: ConeSubstitutor) =
|
override fun isEqualTypes(candidateTypeRef: FirTypeRef, baseTypeRef: FirTypeRef, substitutor: ConeSubstitutor) =
|
||||||
isEqualTypes((candidateTypeRef as FirResolvedTypeRef).type, (baseTypeRef as FirResolvedTypeRef).type, substitutor)
|
isEqualTypes((candidateTypeRef as FirResolvedTypeRef).type, (baseTypeRef as FirResolvedTypeRef).type, substitutor)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Good case complexity is O(1)
|
||||||
|
* Worst case complexity is O(N), where N is number of type-parameter bound's
|
||||||
|
*/
|
||||||
|
private fun isEqualBound(
|
||||||
|
overrideBound: FirTypeRef,
|
||||||
|
baseBound: FirTypeRef,
|
||||||
|
overrideTypeParameter: FirTypeParameter,
|
||||||
|
baseTypeParameter: FirTypeParameter,
|
||||||
|
substitutor: ConeSubstitutor
|
||||||
|
): Boolean {
|
||||||
|
val substitutedOverrideType = substitutor.substituteOrSelf(overrideBound.coneTypeUnsafe())
|
||||||
|
val substitutedBaseType = substitutor.substituteOrSelf(baseBound.coneTypeUnsafe())
|
||||||
|
|
||||||
|
if (isEqualTypes(substitutedOverrideType, substitutedBaseType)) return true
|
||||||
|
|
||||||
|
return overrideTypeParameter.bounds.any { bound -> isEqualTypes(bound.coneTypeUnsafe(), substitutedBaseType, substitutor) } &&
|
||||||
|
baseTypeParameter.bounds.any { bound -> isEqualTypes(bound.coneTypeUnsafe(), substitutedOverrideType, substitutor) }
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
if (overrideCandidate.bounds.size != baseDeclaration.bounds.size) return false
|
||||||
|
return overrideCandidate.bounds.zip(baseDeclaration.bounds)
|
||||||
|
.all { (aBound, bBound) -> isEqualBound(aBound, bBound, overrideCandidate, baseDeclaration, substitutor) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildTypeParametersSubstitutorIfCompatible(
|
||||||
|
overrideCandidate: FirCallableMemberDeclaration<*>,
|
||||||
|
baseDeclaration: FirCallableMemberDeclaration<*>
|
||||||
|
): ConeSubstitutor? {
|
||||||
|
val substitutor = buildSubstitutorForOverridesCheck(overrideCandidate, baseDeclaration) ?: return null
|
||||||
|
if (
|
||||||
|
overrideCandidate.typeParameters.isNotEmpty() &&
|
||||||
|
overrideCandidate.typeParameters.zip(baseDeclaration.typeParameters).any { (override, base) ->
|
||||||
|
!isCompatibleTypeParameters(override, base, substitutor)
|
||||||
|
}
|
||||||
|
) return null
|
||||||
|
return substitutor
|
||||||
|
}
|
||||||
|
|
||||||
private fun isEqualReceiverTypes(candidateTypeRef: FirTypeRef?, baseTypeRef: FirTypeRef?, substitutor: ConeSubstitutor): Boolean {
|
private fun isEqualReceiverTypes(candidateTypeRef: FirTypeRef?, baseTypeRef: FirTypeRef?, substitutor: ConeSubstitutor): Boolean {
|
||||||
return when {
|
return when {
|
||||||
candidateTypeRef != null && baseTypeRef != null -> isEqualTypes(candidateTypeRef, baseTypeRef, substitutor)
|
candidateTypeRef != null && baseTypeRef != null -> isEqualTypes(candidateTypeRef, baseTypeRef, substitutor)
|
||||||
@@ -58,7 +107,7 @@ class FirStandardOverrideChecker(session: FirSession) : FirAbstractOverrideCheck
|
|||||||
override fun isOverriddenFunction(overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction): Boolean {
|
override fun isOverriddenFunction(overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction): Boolean {
|
||||||
if (overrideCandidate.valueParameters.size != baseDeclaration.valueParameters.size) return false
|
if (overrideCandidate.valueParameters.size != baseDeclaration.valueParameters.size) return false
|
||||||
|
|
||||||
val substitutor = getSubstitutorIfTypeParametersAreCompatible(overrideCandidate, baseDeclaration) ?: return false
|
val substitutor = buildTypeParametersSubstitutorIfCompatible(overrideCandidate, baseDeclaration) ?: return false
|
||||||
|
|
||||||
if (!isEqualReceiverTypes(overrideCandidate.receiverTypeRef, baseDeclaration.receiverTypeRef, substitutor)) return false
|
if (!isEqualReceiverTypes(overrideCandidate.receiverTypeRef, baseDeclaration.receiverTypeRef, substitutor)) return false
|
||||||
|
|
||||||
@@ -73,7 +122,7 @@ class FirStandardOverrideChecker(session: FirSession) : FirAbstractOverrideCheck
|
|||||||
baseDeclaration: FirProperty
|
baseDeclaration: FirProperty
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (overrideCandidate !is FirProperty) return false
|
if (overrideCandidate !is FirProperty) return false
|
||||||
val substitutor = getSubstitutorIfTypeParametersAreCompatible(overrideCandidate, baseDeclaration) ?: return false
|
val substitutor = buildTypeParametersSubstitutorIfCompatible(overrideCandidate, baseDeclaration) ?: return false
|
||||||
return isEqualReceiverTypes(overrideCandidate.receiverTypeRef, baseDeclaration.receiverTypeRef, substitutor)
|
return isEqualReceiverTypes(overrideCandidate.receiverTypeRef, baseDeclaration.receiverTypeRef, substitutor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user