From f9712597e1a1e432678b994f4c68dacd9c12f42b Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 5 Sep 2023 19:57:31 +0200 Subject: [PATCH] IR: support IrExternalOverridabilityCondition in IrOverrideChecker The code is analogous to OverridingUtil and ExternalOverridabilityCondition. --- .../actualizer/ActualFakeOverridesAdder.kt | 4 +- .../actualizer/FakeOverrideRebuilder.kt | 3 +- .../IrExternalOverridabilityCondition.kt | 50 ++++++++++++++++ .../kotlin/ir/overrides/IrOverrideChecker.kt | 57 +++++++++++++++++-- .../kotlin/ir/overrides/IrOverridingUtil.kt | 5 +- .../org/jetbrains/kotlin/ir/util/IrUtils.kt | 2 +- .../backend/common/overrides/FakeOverrides.kt | 6 +- 7 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrExternalOverridabilityCondition.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ActualFakeOverridesAdder.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ActualFakeOverridesAdder.kt index 679223160a4..ed8826222b8 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ActualFakeOverridesAdder.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ActualFakeOverridesAdder.kt @@ -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() 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, diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/FakeOverrideRebuilder.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/FakeOverrideRebuilder.kt index fbd85a5da3a..651ec02c117 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/FakeOverrideRebuilder.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/FakeOverrideRebuilder.kt @@ -76,7 +76,8 @@ class FakeOverrideRebuilder( mangler, typeSystemContext, friendModules, - PartialLinkageSupportForLinker.DISABLED + PartialLinkageSupportForLinker.DISABLED, + externalOverridabilityConditions = emptyList(), // TODO: KT-61370, KT-61804. ) fun rebuildFakeOverrides() { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrExternalOverridabilityCondition.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrExternalOverridabilityCondition.kt new file mode 100644 index 00000000000..a5ef3ca9bc5 --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrExternalOverridabilityCondition.kt @@ -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 +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverrideChecker.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverrideChecker.kt index 886c56648b6..3ab2aa36424 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverrideChecker.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverrideChecker.kt @@ -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, +) { 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 diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt index 655cb5a3281..14acd768ee5 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt @@ -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, ) { - private val overrideChecker = IrOverrideChecker(typeSystem) + private val overrideChecker = IrOverrideChecker(typeSystem, externalOverridabilityConditions) private val originals = mutableMapOf() private val IrOverridableMember.original get() = originals[this] ?: error("No original for ${this.render()}") private val originalSuperTypes = mutableMapOf() diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt index 5a0ae554e2c..0f969556b5c 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt @@ -1219,7 +1219,7 @@ fun IrClass.addFakeOverrides( implementedMembers: List = emptyList(), ignoredParentSymbols: List = emptyList() ) { - IrOverridingUtil(typeSystem, FakeOverrideBuilderForLowerings()) + IrOverridingUtil(typeSystem, FakeOverrideBuilderForLowerings(), emptyList()) .buildFakeOverridesForClassUsingOverriddenSymbols(this, implementedMembers = implementedMembers, compatibilityMode = false, diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/overrides/FakeOverrides.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/overrides/FakeOverrides.kt index f8f3c55470a..851327f44d9 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/overrides/FakeOverrides.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/overrides/FakeOverrides.kt @@ -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 = emptyList(), ) : FakeOverrideBuilderStrategy( friendModules = friendModules, unimplementedOverridesStrategy = if (partialLinkageSupport.isEnabled) @@ -91,7 +93,7 @@ class FakeOverrideBuilder( ) { private val haveFakeOverrides = mutableSetOf() - 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.