IR: support IrExternalOverridabilityCondition in IrOverrideChecker

The code is analogous to OverridingUtil and
ExternalOverridabilityCondition.
This commit is contained in:
Alexander Udalov
2023-09-05 19:57:31 +02:00
parent 86eb79e095
commit f9712597e1
7 changed files with 115 additions and 12 deletions
@@ -37,7 +37,7 @@ internal class ActualFakeOverridesAdder(
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext,
private val typeSystemContext: IrTypeSystemContext
) : IrElementVisitorVoid {
private val overrideChecker = IrOverrideChecker(typeSystemContext)
private val overrideChecker = IrOverrideChecker(typeSystemContext, emptyList())
private val missingActualMembersMap = mutableMapOf<IrClass, FakeOverrideInfo>()
override fun visitClass(declaration: IrClass) {
@@ -93,7 +93,7 @@ internal class ActualFakeOverridesAdder(
@Suppress("UNCHECKED_CAST")
val override = klass.declarations.firstOrNull {
it is IrOverridableMember &&
overrideChecker.isOverridableByWithoutExternalConditions(
overrideChecker.isOverridableBy(
superMember = memberFromSupertype,
subMember = it,
checkIsInlineFlag = false,
@@ -76,7 +76,8 @@ class FakeOverrideRebuilder(
mangler,
typeSystemContext,
friendModules,
PartialLinkageSupportForLinker.DISABLED
PartialLinkageSupportForLinker.DISABLED,
externalOverridabilityConditions = emptyList(), // TODO: KT-61370, KT-61804.
)
fun rebuildFakeOverrides() {
@@ -0,0 +1,50 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.overrides
import org.jetbrains.kotlin.ir.declarations.IrOverridableMember
interface IrExternalOverridabilityCondition {
enum class Result {
OVERRIDABLE,
CONFLICT,
INCOMPATIBLE,
UNKNOWN
}
enum class Contract {
CONFLICTS_ONLY,
SUCCESS_ONLY,
BOTH
}
/**
* Determines whether [superMember] is overridable by [subMember]. The returned result can be one of the following:
*
* - [Result.OVERRIDABLE] means that it is definitely overridable, and neither the general override checking algorithm, nor other
* external overridability conditions can refute that.
* - [Result.CONFLICT] means that it is an error to have both declarations available in the same class, so the compiler should report
* an error.
* - [Result.INCOMPATIBLE] means that it is not overridable, but it's OK to have both declarations available in the same class because
* they don't cause a conflict.
* - [Result.UNKNOWN] means that this overridability condition cannot claim anything about the overridability, and the final result
* will be based on the general override checking algorithm and the other external overridability conditions.
*/
fun isOverridable(
superMember: IrOverridableMember,
subMember: IrOverridableMember,
): Result
/**
* Specifies what values can be returned by [isOverridable]. Used as an optimization to reorder overridability conditions to prevent
* redundant calculations.
*
* - [Contract.CONFLICTS_ONLY] means that [isOverridable] can return [Result.CONFLICT], [Result.INCOMPATIBLE] or [Result.UNKNOWN].
* - [Contract.SUCCESS_ONLY] means that [isOverridable] can return [Result.OVERRIDABLE] or [Result.UNKNOWN].
* - [Contract.BOTH] means that [isOverridable] can return any result.
*/
val contract: Contract
}
@@ -6,6 +6,9 @@
package org.jetbrains.kotlin.ir.overrides
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.overrides.IrExternalOverridabilityCondition.Contract.CONFLICTS_ONLY
import org.jetbrains.kotlin.ir.overrides.IrExternalOverridabilityCondition.Contract.SUCCESS_ONLY
import org.jetbrains.kotlin.ir.overrides.IrExternalOverridabilityCondition.Result.*
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.types.IrTypeSystemContextWithAdditionalAxioms
import org.jetbrains.kotlin.ir.types.createIrTypeCheckerState
@@ -13,7 +16,10 @@ import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo
import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.*
import org.jetbrains.kotlin.types.AbstractTypeChecker
class IrOverrideChecker(private val typeSystem: IrTypeSystemContext) {
class IrOverrideChecker(
private val typeSystem: IrTypeSystemContext,
private val externalOverridabilityConditions: List<IrExternalOverridabilityCondition>,
) {
fun getBothWaysOverridability(
overriderDescriptor: IrOverridableMember,
candidateDescriptor: IrOverridableMember,
@@ -38,11 +44,12 @@ class IrOverrideChecker(private val typeSystem: IrTypeSystemContext) {
subMember: IrOverridableMember,
checkIsInlineFlag: Boolean,
): OverrideCompatibilityInfo {
return isOverridableByWithoutExternalConditions(superMember, subMember, checkIsInlineFlag)
// The frontend goes into external overridability condition details here, but don't deal with them in IR (yet?).
val basicResult = isOverridableByWithoutExternalConditions(superMember, subMember, checkIsInlineFlag)
return runExternalOverridabilityConditions(superMember, subMember, basicResult)
}
fun isOverridableByWithoutExternalConditions(
private fun isOverridableByWithoutExternalConditions(
superMember: IrOverridableMember,
subMember: IrOverridableMember,
checkIsInlineFlag: Boolean,
@@ -122,6 +129,48 @@ class IrOverrideChecker(private val typeSystem: IrTypeSystemContext) {
return success()
}
private fun runExternalOverridabilityConditions(
superMember: IrOverridableMember,
subMember: IrOverridableMember,
basicResult: OverrideCompatibilityInfo,
): OverrideCompatibilityInfo {
var wasSuccess = basicResult.result == OverrideCompatibilityInfo.Result.OVERRIDABLE
for (externalCondition in externalOverridabilityConditions) {
// Do not run CONFLICTS_ONLY while there was no success
if (externalCondition.contract == CONFLICTS_ONLY) continue
if (wasSuccess && externalCondition.contract == SUCCESS_ONLY) continue
val result =
externalCondition.isOverridable(superMember, subMember)
when (result) {
OVERRIDABLE -> wasSuccess = true
CONFLICT -> return conflict("External condition failed")
INCOMPATIBLE -> return incompatible("External condition")
UNKNOWN -> {}
}
}
if (!wasSuccess) return basicResult
// Search for conflicts from external conditions
for (externalCondition in externalOverridabilityConditions) {
// Run all conditions that was not run before (i.e. CONFLICTS_ONLY)
if (externalCondition.contract != CONFLICTS_ONLY) continue
val result =
externalCondition.isOverridable(superMember, subMember)
when (result) {
CONFLICT -> return conflict("External condition failed")
INCOMPATIBLE -> return incompatible("External condition")
OVERRIDABLE -> error(
"Contract violation in ${externalCondition.javaClass} condition. It's not supposed to end with success"
)
UNKNOWN -> {}
}
}
return success()
}
}
private val IrSimpleFunction?.hasExtensionReceiver: Boolean
@@ -33,9 +33,10 @@ import org.jetbrains.kotlin.utils.memoryOptimizedMapNotNull
class IrOverridingUtil(
private val typeSystem: IrTypeSystemContext,
private val fakeOverrideBuilder: FakeOverrideBuilderStrategy
private val fakeOverrideBuilder: FakeOverrideBuilderStrategy,
private val externalOverridabilityConditions: List<IrExternalOverridabilityCondition>,
) {
private val overrideChecker = IrOverrideChecker(typeSystem)
private val overrideChecker = IrOverrideChecker(typeSystem, externalOverridabilityConditions)
private val originals = mutableMapOf<IrOverridableMember, IrOverridableMember>()
private val IrOverridableMember.original get() = originals[this] ?: error("No original for ${this.render()}")
private val originalSuperTypes = mutableMapOf<IrOverridableMember, IrType>()
@@ -1219,7 +1219,7 @@ fun IrClass.addFakeOverrides(
implementedMembers: List<IrOverridableMember> = emptyList(),
ignoredParentSymbols: List<IrSymbol> = emptyList()
) {
IrOverridingUtil(typeSystem, FakeOverrideBuilderForLowerings())
IrOverridingUtil(typeSystem, FakeOverrideBuilderForLowerings(), emptyList())
.buildFakeOverridesForClassUsingOverriddenSymbols(this,
implementedMembers = implementedMembers,
compatibilityMode = false,
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildTypeParameter
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.linkage.partial.IrUnimplementedOverridesStrategy.ProcessAsFakeOverrides
import org.jetbrains.kotlin.ir.overrides.FakeOverrideBuilderStrategy
import org.jetbrains.kotlin.ir.overrides.IrExternalOverridabilityCondition
import org.jetbrains.kotlin.ir.overrides.IrOverridingUtil
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
@@ -81,7 +82,8 @@ class FakeOverrideBuilder(
val platformSpecificClassFilter: FakeOverrideClassFilter = DefaultFakeOverrideClassFilter,
private val fakeOverrideDeclarationTable: DeclarationTable = FakeOverrideDeclarationTable(mangler) { builder, table ->
IdSignatureSerializer(builder, table)
}
},
externalOverridabilityConditions: List<IrExternalOverridabilityCondition> = emptyList(),
) : FakeOverrideBuilderStrategy(
friendModules = friendModules,
unimplementedOverridesStrategy = if (partialLinkageSupport.isEnabled)
@@ -91,7 +93,7 @@ class FakeOverrideBuilder(
) {
private val haveFakeOverrides = mutableSetOf<IrClass>()
private val irOverridingUtil = IrOverridingUtil(typeSystem, this)
private val irOverridingUtil = IrOverridingUtil(typeSystem, this, externalOverridabilityConditions)
private val irBuiltIns = typeSystem.irBuiltIns
// TODO: The declaration table is needed for the signaturer.