From c9b8f58a10b122ae431e26fdfce53c90bc75062a Mon Sep 17 00:00:00 2001 From: Georgy Bronnikov Date: Mon, 24 Dec 2018 12:04:17 +0300 Subject: [PATCH] Restore overriddenSymbols consistency in InterfaceDelegationLowering --- .../kotlin/backend/common/ir/IrUtils.kt | 11 ++- .../jvm/descriptors/JvmDeclarationFactory.kt | 7 +- .../jvm/lower/InterfaceDelegationLowering.kt | 88 ++++++++++++------- 3 files changed, 71 insertions(+), 35 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt index a94408f4a58..0974cc6fe68 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.backend.common.ir -import org.jetbrains.kotlin.backend.common.BackendContext import org.jetbrains.kotlin.backend.common.DumpIrTreeWithDescriptorsVisitor import org.jetbrains.kotlin.backend.common.deepCopyWithVariables import org.jetbrains.kotlin.backend.common.descriptors.WrappedClassConstructorDescriptor @@ -35,6 +34,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrExpressionBody import org.jetbrains.kotlin.ir.expressions.IrFunctionReference import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl @@ -162,7 +162,10 @@ fun IrValueParameter.copyTo( (parent as IrTypeParametersContainer).classIfConstructor, irFunction.classIfConstructor ), - varargElementType: IrType? = this.varargElementType + varargElementType: IrType? = this.varargElementType, + defaultValue: IrExpressionBody? = this.defaultValue, + isCrossinline: Boolean = this.isCrossinline, + isNoinline: Boolean = this.isNoinline ): IrValueParameter { val descriptor = WrappedValueParameterDescriptor(symbol.descriptor.annotations, symbol.descriptor.source) val symbol = IrValueParameterSymbolImpl(descriptor) @@ -382,3 +385,7 @@ fun IrClass.createImplicitParameterDeclarationWithWrappedDescriptor() { assert(typeParameters.isEmpty()) assert(descriptor.declaredTypeParameters.isEmpty()) } + +fun IrSimpleFunction.isMethodOfAny() = + ((valueParameters.size == 0 && name.asString().let { it == "hashCode" || it == "toString" }) || + (valueParameters.size == 1 && name.asString() == "equals" && valueParameters[0].type.isNullableAny())) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDeclarationFactory.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDeclarationFactory.kt index ad4cfc95937..7c1bc9c3b47 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDeclarationFactory.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDeclarationFactory.kt @@ -44,7 +44,7 @@ class JvmDeclarationFactory( private val outerThisDeclarations = HashMap() private val innerClassConstructors = HashMap() - private val defaultImplsMethods = HashMap() + private val defaultImplsMethods = HashMap() private val defaultImplsClasses = HashMap() override fun getFieldForEnumEntry(enumEntry: IrEnumEntry, type: IrType): IrField = @@ -108,7 +108,8 @@ class JvmDeclarationFactory( newValueParameters, oldDescriptor.returnType, oldDescriptor.modality, - oldDescriptor.visibility) + oldDescriptor.visibility + ) val symbol = IrConstructorSymbolImpl(newDescriptor) return IrConstructorImpl( oldConstructor.startOffset, @@ -188,7 +189,7 @@ class JvmDeclarationFactory( ).initialize(objectDescriptor.defaultType) } - fun getDefaultImplsFunction(interfaceFun: IrFunction): IrFunction { + fun getDefaultImplsFunction(interfaceFun: IrSimpleFunction): IrSimpleFunction { val parent = interfaceFun.parentAsClass assert(parent.isInterface) { "Parent of ${interfaceFun.dump()} should be interface" } return defaultImplsMethods.getOrPut(interfaceFun) { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InterfaceDelegationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InterfaceDelegationLowering.kt index 1dd200425d4..2e00fe88224 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InterfaceDelegationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InterfaceDelegationLowering.kt @@ -5,36 +5,30 @@ package org.jetbrains.kotlin.backend.jvm.lower -import org.jetbrains.kotlin.backend.common.ClassLoweringPass +import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom import org.jetbrains.kotlin.backend.common.lower.createIrBuilder -import org.jetbrains.kotlin.backend.common.CodegenUtil import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface import org.jetbrains.kotlin.codegen.OwnerKind import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME +import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET -import org.jetbrains.kotlin.ir.builders.irBlockBody -import org.jetbrains.kotlin.ir.builders.irCall -import org.jetbrains.kotlin.ir.builders.irGet -import org.jetbrains.kotlin.ir.builders.irReturn -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl -import org.jetbrains.kotlin.ir.util.hasAnnotation -import org.jetbrains.kotlin.ir.util.isInterface -import org.jetbrains.kotlin.ir.util.resolveFakeOverride -import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid -import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.name.Name +import java.util.function.UnaryOperator internal val interfaceDelegationPhase = makeIrFilePhase( ::InterfaceDelegationLowering, @@ -42,17 +36,28 @@ internal val interfaceDelegationPhase = makeIrFilePhase( description = "Delegate calls to interface members with default implementations to DefaultImpls" ) -private class InterfaceDelegationLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), ClassLoweringPass { +private class InterfaceDelegationLowering(val context: JvmBackendContext) : IrElementVisitorVoid, FileLoweringPass { val state: GenerationState = context.state - override fun lower(irClass: IrClass) { - if (irClass.isJvmInterface) return + val replacementMap = mutableMapOf() - irClass.transformChildrenVoid(this) - generateInterfaceMethods(irClass) + override fun lower(irFile: IrFile) { + irFile.acceptChildrenVoid(this) + // TODO: Replacer should be run on whole module, not on a single file. + irFile.acceptVoid(OverriddenSymbolsReplacer(replacementMap)) } + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitClass(declaration: IrClass) { + super.visitClass(declaration) + if (declaration.isJvmInterface) return + + generateInterfaceMethods(declaration) + } private fun generateInterfaceMethods(irClass: IrClass) { val (actualClass, isDefaultImplsGeneration) = if (irClass.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS) { @@ -61,11 +66,21 @@ private class InterfaceDelegationLowering(val context: JvmBackendContext) : IrEl Pair(irClass, false) } - val newDeclarations = mutableListOf() - for (function in actualClass.declarations) { - if (function !is IrSimpleFunction) continue + val toRemove = mutableListOf() + for (function in actualClass.functions.toList()) { // Copy the list, because we are adding new declarations from the loop if (function.origin !== IrDeclarationOrigin.FAKE_OVERRIDE) continue + // In classes, only generate interface delegation for functions immediately inherited from am interface. + // (Otherwise, delegation will be present in the parent class) + if (!isDefaultImplsGeneration && + function.overriddenSymbols.any { + !it.owner.parentAsClass.isInterface && + it.owner.modality != Modality.ABSTRACT + } + ) { + continue + } + val implementation = function.resolveFakeOverride() ?: continue if (!implementation.hasInterfaceParent() || Visibilities.isPrivate(implementation.visibility) || @@ -74,17 +89,19 @@ private class InterfaceDelegationLowering(val context: JvmBackendContext) : IrEl continue } - newDeclarations.add(generateDelegationToDefaultImpl(implementation, function, isDefaultImplsGeneration)) + val delegation = generateDelegationToDefaultImpl(irClass, implementation, function, isDefaultImplsGeneration) + toRemove.add(function) + replacementMap[function.symbol] = delegation.symbol } - - irClass.declarations.addAll(newDeclarations) + irClass.declarations.removeAll(toRemove) } private fun generateDelegationToDefaultImpl( + irClass: IrClass, interfaceFun: IrSimpleFunction, inheritedFun: IrSimpleFunction, isDefaultImplsGeneration: Boolean - ): IrFunction { + ): IrSimpleFunction { val defaultImplFun = context.declarationFactory.getDefaultImplsFunction(interfaceFun) val irFunction = @@ -117,6 +134,8 @@ private class InterfaceDelegationLowering(val context: JvmBackendContext) : IrEl } } else context.declarationFactory.getDefaultImplsFunction(inheritedFun) + irClass.declarations.add(irFunction) + context.createIrBuilder(irFunction.symbol, UNDEFINED_OFFSET, UNDEFINED_OFFSET).apply { irFunction.body = irBlockBody { +irReturn( @@ -133,9 +152,18 @@ private class InterfaceDelegationLowering(val context: JvmBackendContext) : IrEl return irFunction } - private fun IrSimpleFunction.isMethodOfAny() = - ((valueParameters.size == 0 && name.asString() in setOf("hashCode", "toString")) || - (valueParameters.size == 1 && name.asString() == "equals" && valueParameters[0].type == context.irBuiltIns.anyType)) + private class OverriddenSymbolsReplacer(val replacementMap: Map) : + IrElementVisitorVoid { + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitSimpleFunction(declaration: IrSimpleFunction) { + declaration.overriddenSymbols.replaceAll(UnaryOperator { symbol -> replacementMap[symbol] ?: symbol }) + super.visitSimpleFunction(declaration) + } + } private fun IrSimpleFunction.hasInterfaceParent() = (parent as? IrClass)?.isInterface == true