From 5722f889d86be744eda4e32f73a247c93fd64994 Mon Sep 17 00:00:00 2001 From: pyos Date: Fri, 14 Aug 2020 10:53:18 +0200 Subject: [PATCH] FIR2IR: scan the entire interface tree for delegatable members --- .../generators/DelegatedMemberGenerator.kt | 64 ++++++++++++++----- .../codegen/box/classes/delegation4.kt | 1 - .../box/classes/delegationMethodsWithArgs.kt | 1 - .../testData/codegen/box/classes/kt3546.kt | 1 - .../codegen/box/delegation/diamond2.kt | 1 - 5 files changed, 48 insertions(+), 20 deletions(-) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DelegatedMemberGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DelegatedMemberGenerator.kt index 8e5c66aaae1..34f5ea927e4 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DelegatedMemberGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DelegatedMemberGenerator.kt @@ -35,13 +35,11 @@ import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection -import org.jetbrains.kotlin.ir.util.classId -import org.jetbrains.kotlin.ir.util.defaultType -import org.jetbrains.kotlin.ir.util.hasAnnotation -import org.jetbrains.kotlin.ir.util.isFakeOverride +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.utils.DFS /** * A generator for delegated members from implementation by delegation. @@ -56,22 +54,56 @@ internal class DelegatedMemberGenerator( // Generate delegated members for [subClass]. The synthetic field [irField] has the super interface type. fun generate(irField: IrField, subClass: IrClass) { - val superClass = (irField.type as IrSimpleTypeImpl).classOrNull?.owner ?: return - val superClassId = superClass.classId ?: return - superClass.declarations.filter { - it.isDelegatable() && !subClass.declarations.any { decl -> isOverriding(irBuiltIns, decl, it) } - }.forEach { - if (it is IrSimpleFunction) { - val firFunction = declarationStorage.findOverriddenFirFunction(it, superClassId) ?: return - val function = generateDelegatedFunction(subClass, irField, it, firFunction) - subClass.addMember(function) - } else if (it is IrProperty) { - val firProperty = declarationStorage.findOverriddenFirProperty(it, superClassId) ?: return - generateDelegatedProperty(subClass, irField, it, firProperty) + val delegateClass = (irField.type as IrSimpleTypeImpl).classOrNull?.owner ?: return + val subClasses = mutableMapOf(delegateClass to mutableListOf(subClass)) + DFS.dfs( + listOf(delegateClass), { node -> node.superTypes.mapNotNull { it.classOrNull?.owner } }, + object : DFS.NodeHandler { + override fun beforeChildren(current: IrClass): Boolean { + for (superType in current.superTypes) { + superType.classOrNull?.owner?.let { subClasses.getOrPut(it) { mutableListOf() }.add(current) } + } + return true + } + + override fun afterChildren(current: IrClass) = Unit + override fun result() = Unit + } + ) + + for ((superClass, mayOverride) in subClasses) { + val classId = superClass.classId ?: continue + if (classId == irBuiltIns.anyClass.owner.classId) continue + for (member in superClass.declarations) { + val delegatable = member.isDelegatable() && !DFS.ifAny(mayOverride, { subClasses[it] ?: emptyList() }) { + // Delegate to the most specific version of each member. + it.declarations.any { !it.isFakeOverride && it.overrides(member) } + } + if (!delegatable) continue + if (member is IrSimpleFunction) { + val firFunction = declarationStorage.findOverriddenFirFunction(member, classId) ?: return + val function = generateDelegatedFunction(subClass, irField, member, firFunction) + subClass.addMember(function) + } else if (member is IrProperty) { + val firProperty = declarationStorage.findOverriddenFirProperty(member, classId) ?: return + generateDelegatedProperty(subClass, irField, member, firProperty) + } } } } + private fun IrDeclaration.overrides(other: IrDeclaration) = when { + // Imported declarations have overridden symbols, so check that first. + this is IrProperty && other is IrProperty -> + getter?.let { getter -> other.getter?.symbol in getter.overriddenSymbols } + ?: setter?.let { setter -> other.setter?.symbol in setter.overriddenSymbols } + ?: false + this is IrSimpleFunction && other is IrSimpleFunction -> + other.symbol in overriddenSymbols + else -> + false + } || isOverriding(irBuiltIns, this, other) // TODO check if this behaves correctly with generic arguments + private fun IrDeclaration.isDelegatable(): Boolean { return isOverridable() && !isFakeOverride diff --git a/compiler/testData/codegen/box/classes/delegation4.kt b/compiler/testData/codegen/box/classes/delegation4.kt index 390f93a3b15..7a9c9449e58 100644 --- a/compiler/testData/codegen/box/classes/delegation4.kt +++ b/compiler/testData/codegen/box/classes/delegation4.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR interface First { public open fun foo() : Int } diff --git a/compiler/testData/codegen/box/classes/delegationMethodsWithArgs.kt b/compiler/testData/codegen/box/classes/delegationMethodsWithArgs.kt index 79e85f1c381..a8c0e37230c 100644 --- a/compiler/testData/codegen/box/classes/delegationMethodsWithArgs.kt +++ b/compiler/testData/codegen/box/classes/delegationMethodsWithArgs.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR package test interface TextField { diff --git a/compiler/testData/codegen/box/classes/kt3546.kt b/compiler/testData/codegen/box/classes/kt3546.kt index ea4eec76d1b..b4d36c1f3e6 100644 --- a/compiler/testData/codegen/box/classes/kt3546.kt +++ b/compiler/testData/codegen/box/classes/kt3546.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR interface A { fun test(): String } diff --git a/compiler/testData/codegen/box/delegation/diamond2.kt b/compiler/testData/codegen/box/delegation/diamond2.kt index d5783770ecb..daf19245ade 100644 --- a/compiler/testData/codegen/box/delegation/diamond2.kt +++ b/compiler/testData/codegen/box/delegation/diamond2.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR // SKIP_JDK6 // TARGET_BACKEND: JVM // FILE: Base.java