diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index bd2d9398769..6051c8fbbdd 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -5,7 +5,10 @@ import llvm.LLVMModuleRef import org.jetbrains.kotlin.backend.jvm.descriptors.initialize import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.ir.Ir -import org.jetbrains.kotlin.backend.konan.llvm.* +import org.jetbrains.kotlin.backend.konan.llvm.Llvm +import org.jetbrains.kotlin.backend.konan.llvm.LlvmDeclarations +import org.jetbrains.kotlin.backend.konan.llvm.functionName +import org.jetbrains.kotlin.backend.konan.llvm.verifyModule import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl @@ -20,7 +23,7 @@ import java.lang.System.out internal class SpecialDescriptorsFactory(val context: Context) { private val outerThisDescriptors = mutableMapOf() - private val bridgesDescriptors = mutableMapOf() + private val bridgesDescriptors = mutableMapOf, FunctionDescriptor>() fun getOuterThisFieldDescriptor(innerClassDescriptor: ClassDescriptor): PropertyDescriptor = if (!innerClassDescriptor.isInner) throw AssertionError("Class is not inner: $innerClassDescriptor") @@ -34,24 +37,26 @@ internal class SpecialDescriptorsFactory(val context: Context) { false, false, false, false, false, false).initialize(outerClassDescriptor.defaultType, dispatchReceiverParameter = receiver) } - fun getBridgeDescriptor(descriptor: FunctionDescriptor): FunctionDescriptor { - return bridgesDescriptors.getOrPut(descriptor) { + fun getBridgeDescriptor(overriddenFunctionDescriptor: OverriddenFunctionDescriptor): FunctionDescriptor { + val descriptor = overriddenFunctionDescriptor.descriptor.original + assert(overriddenFunctionDescriptor.needBridge, + { "Function $descriptor is not needed in a bridge to call overridden function ${overriddenFunctionDescriptor.overriddenDescriptor}" }) + val bridgeDirections = overriddenFunctionDescriptor.bridgeDirections + return bridgesDescriptors.getOrPut(descriptor to bridgeDirections) { SimpleFunctionDescriptorImpl.create( descriptor.containingDeclaration, Annotations.EMPTY, - ("" + descriptor.functionName).synthesizedName, + ("" + descriptor.functionName).synthesizedName, CallableMemberDescriptor.Kind.DECLARATION, SourceElement.NO_SOURCE).apply { - initializeBridgeDescriptor(this, descriptor) + initializeBridgeDescriptor(this, descriptor, bridgeDirections.array) } } } - private fun initializeBridgeDescriptor(bridgeDescriptor: SimpleFunctionDescriptorImpl, descriptor: FunctionDescriptor) { - val bridgeDirections = descriptor.bridgeDirections - if (bridgeDirections.allNotNeeded()) - throw AssertionError("Function $descriptor is not needed in a bridge") - + private fun initializeBridgeDescriptor(bridgeDescriptor: SimpleFunctionDescriptorImpl, + descriptor: FunctionDescriptor, + bridgeDirections: Array) { val returnType = when (bridgeDirections[0]) { BridgeDirection.TO_VALUE_TYPE -> descriptor.returnType!! BridgeDirection.NOT_NEEDED -> descriptor.returnType @@ -66,7 +71,8 @@ internal class SpecialDescriptorsFactory(val context: Context) { bridgeDescriptor.initialize( extensionReceiverType, - descriptor.dispatchReceiverParameter, + // TODO: bug in descriptor - https://youtrack.jetbrains.com/issue/KT-16438. + (descriptor.containingDeclaration as ClassDescriptor).thisAsReceiverParameter, descriptor.typeParameters, descriptor.valueParameters.mapIndexed { index, valueParameterDescriptor -> when (bridgeDirections[index + 2]) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt index 9687b91b2fd..9afa7614f4e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt @@ -56,7 +56,9 @@ internal class KonanLower(val context: Context) { VarargInjectionLowering(context).runOnFilePostfix(irFile) } phaser.phase(KonanPhase.BRIDGES_BUILDING) { - BridgesBuilding(context).lower(irFile) + DelegationLowering(context).runOnFilePostfix(irFile) + BridgesBuilding(context).runOnFilePostfix(irFile) + DirectBridgesCallsLowering(context).runOnFilePostfix(irFile) } phaser.phase(KonanPhase.AUTOBOX) { Autoboxing(context).lower(irFile) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassVtablesBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassVtablesBuilder.kt index c066e9fbc8c..84f55edcf00 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassVtablesBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassVtablesBuilder.kt @@ -15,15 +15,38 @@ internal class OverriddenFunctionDescriptor(val descriptor: FunctionDescriptor, val needBridge: Boolean get() { if (descriptor.modality == Modality.ABSTRACT) return false - return descriptor.needBridgeTo(overriddenDescriptor) + return descriptor.kind == CallableMemberDescriptor.Kind.DELEGATION || descriptor.target.needBridgeTo(overriddenDescriptor) } - fun trivialOverride() = descriptor.original == overriddenDescriptor + val bridgeDirections: BridgeDirections + get() = descriptor.target.bridgeDirectionsTo(overriddenDescriptor) + + val canBeCalledVirtually: Boolean + // We check that either method is open, or one of declarations it overrides is open. + get() = overriddenDescriptor.isOverridable + || DescriptorUtils.getAllOverriddenDeclarations(overriddenDescriptor).any { it.isOverridable } + override fun toString(): String { return "(descriptor=$descriptor, overriddenDescriptor=$overriddenDescriptor)" } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is OverriddenFunctionDescriptor) return false + + if (descriptor != other.descriptor) return false + if (overriddenDescriptor != other.overriddenDescriptor) return false + + return true + } + + override fun hashCode(): Int { + var result = descriptor.hashCode() + result = 31 * result + overriddenDescriptor.hashCode() + return result + } } internal class ClassVtablesBuilder(val classDescriptor: ClassDescriptor, val context: Context) { @@ -45,50 +68,39 @@ internal class ClassVtablesBuilder(val classDescriptor: ClassDescriptor, val con if (overridingMethod == null) { superMethod } else { - if (!superMethod.trivialOverride()) - newVtableSlots.add(OverriddenFunctionDescriptor(overridingMethod, superMethod.descriptor)) - // Overriding method is defined in the current class and is not inherited - take it as is. - newVtableSlots.add(OverriddenFunctionDescriptor(overridingMethod, overridingMethod)) + newVtableSlots.add(OverriddenFunctionDescriptor(overridingMethod, superMethod.descriptor)) OverriddenFunctionDescriptor(overridingMethod, superMethod.overriddenDescriptor) } } - methods.filterNot { method -> inheritedVtableSlots.any { it.descriptor == method } } // Find newly defined methods. - // Select target because method might be taken from default implementation of interface. - .mapTo(newVtableSlots) { OverriddenFunctionDescriptor(it, it.target) } + // Add all possible (descriptor, overriddenDescriptor) edges for now, redundant will be removed later. + methods.mapTo(newVtableSlots) { OverriddenFunctionDescriptor(it, it) } - val list = inheritedVtableSlots + newVtableSlots.filter { it.descriptor.isOverridable }.sortedBy { - it.overriddenDescriptor.functionName.localHash.value - } - list + val inheritedVtableSlotsSet = inheritedVtableSlots.map { it.descriptor to it.bridgeDirections }.toSet() + + val filteredNewVtableSlots = newVtableSlots + .filterNot { inheritedVtableSlotsSet.contains(it.descriptor to it.bridgeDirections) } + .distinctBy { it.descriptor to it.bridgeDirections } + .filter { it.descriptor.isOverridable } + + inheritedVtableSlots + filteredNewVtableSlots.sortedBy { it.overriddenDescriptor.functionName.localHash.value } } fun vtableIndex(function: FunctionDescriptor): Int { - val target = function.target - val index = vtableEntries.indexOfFirst { it.overriddenDescriptor == target } + val bridgeDirections = function.target.bridgeDirectionsTo(function.original) + val index = vtableEntries.indexOfFirst { it.descriptor == function.original && it.bridgeDirections == bridgeDirections } if (index < 0) throw Error(function.toString() + " not in vtable of " + classDescriptor.toString()) return index } - private val ClassDescriptor.contributedMethodsWithOverridden: List - get() { - return contributedMethods.flatMap { method -> - method.allOverriddenDescriptors.map { OverriddenFunctionDescriptor(method, it) } - }.distinctBy { - Triple(it.overriddenDescriptor.functionName, it.descriptor, it.needBridge) - }.sortedBy { - it.overriddenDescriptor.functionName.localHash.value - } - } - val methodTableEntries: List by lazy { assert(!classDescriptor.isAbstract()) - classDescriptor.contributedMethodsWithOverridden.filter { - // We check that either method is open, or one of declarations it overrides - // is open. - it.overriddenDescriptor.isOverridable || DescriptorUtils.getAllOverriddenDeclarations(it.overriddenDescriptor).any { it.isOverridable } - } + classDescriptor.contributedMethods + .flatMap { method -> method.allOverriddenDescriptors.map { OverriddenFunctionDescriptor(method, it) } } + .filter { it.canBeCalledVirtually } + .distinctBy { Triple(it.overriddenDescriptor.functionName, it.descriptor, it.needBridge) } + .sortedBy { it.overriddenDescriptor.functionName.localHash.value } // TODO: probably method table should contain all accessible methods to improve binary compatibility } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index 46142597e43..7929c7649a5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -49,6 +49,7 @@ internal fun T.resolveFakeOverride(): T { val overridden = OverridingUtil.getOverriddenDeclarations(this) val filtered = OverridingUtil.filterOutOverridden(overridden) // TODO: is it correct to take first? + @Suppress("UNCHECKED_CAST") return filtered.first { it.modality != Modality.ABSTRACT } as T } } @@ -166,6 +167,7 @@ internal val T.allOverriddenDescriptors: List val result = mutableListOf() fun traverse(descriptor: T) { result.add(descriptor) + @Suppress("UNCHECKED_CAST") descriptor.overriddenDescriptors.forEach { traverse(it as T) } } traverse(this) @@ -209,12 +211,6 @@ internal fun FunctionDescriptor.hasReferenceAt(index: Int): Boolean { } } -private fun FunctionDescriptor.overridesFunWithReferenceAt(index: Int) - = allOverriddenDescriptors.any { it.original.hasReferenceAt(index) } - -private fun FunctionDescriptor.overridesFunWithValueTypeAt(index: Int) - = allOverriddenDescriptors.any { it.original.hasValueTypeAt(index) } - private fun FunctionDescriptor.needBridgeToAt(target: FunctionDescriptor, index: Int) = hasValueTypeAt(index) xor target.hasValueTypeAt(index) @@ -230,39 +226,60 @@ internal enum class BridgeDirection { TO_VALUE_TYPE } -private fun FunctionDescriptor.bridgeDirectionAt(index: Int) : BridgeDirection { - if (kind.isReal) { - if (hasValueTypeAt(index) && overridesFunWithReferenceAt(index)) - return BridgeDirection.FROM_VALUE_TYPE - return BridgeDirection.NOT_NEEDED - } - - val target = this.target - return when { - hasValueTypeAt(index) && target.hasValueTypeAt(index) && overridesFunWithReferenceAt(index) -> BridgeDirection.FROM_VALUE_TYPE - hasValueTypeAt(index) && target.hasReferenceAt(index) && overridesFunWithValueTypeAt(index) -> BridgeDirection.TO_VALUE_TYPE - else -> BridgeDirection.NOT_NEEDED +private fun FunctionDescriptor.bridgeDirectionToAt(target: FunctionDescriptor, index: Int): BridgeDirection { + when { + hasValueTypeAt(index) && target.hasReferenceAt(index) -> return BridgeDirection.FROM_VALUE_TYPE + hasReferenceAt(index) && target.hasValueTypeAt(index) -> return BridgeDirection.TO_VALUE_TYPE + else -> return BridgeDirection.NOT_NEEDED } } -private fun bridgesEqual(first: Array, second: Array) - = first.indices.none { first[it] != second[it] } +internal class BridgeDirections(val array: Array) { + constructor(parametersCount: Int): this(Array(parametersCount + 2, { BridgeDirection.NOT_NEEDED })) -internal fun Array.allNotNeeded() = this.all { it == BridgeDirection.NOT_NEEDED } + fun allNotNeeded(): Boolean = array.all { it == BridgeDirection.NOT_NEEDED } -internal val FunctionDescriptor.bridgeDirections: Array - get() { - val ourDirections = Array(this.valueParameters.size + 2, { BridgeDirection.NOT_NEEDED }) - if (modality == Modality.ABSTRACT) - return ourDirections - for (index in ourDirections.indices) - ourDirections[index] = this.bridgeDirectionAt(index) - - if (!kind.isReal && bridgesEqual(ourDirections, this.target.bridgeDirections)) { - // Bridge is inherited from supers - for (index in ourDirections.indices) - ourDirections[index] = BridgeDirection.NOT_NEEDED + override fun toString(): String { + val result = StringBuilder() + array.forEach { + result.append(when (it) { + BridgeDirection.FROM_VALUE_TYPE -> 'U' // unbox + BridgeDirection.TO_VALUE_TYPE -> 'B' // box + BridgeDirection.NOT_NEEDED -> 'N' // none + }) } - - return ourDirections + return result.toString() } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is BridgeDirections) return false + + return array.size == other.array.size + && array.indices.all { array[it] == other.array[it] } + } + + override fun hashCode(): Int { + var result = 0 + array.forEach { result = result * 31 + it.ordinal } + return result + } +} + +internal fun FunctionDescriptor.bridgeDirectionsTo(overriddenDescriptor: FunctionDescriptor): BridgeDirections { + val ourDirections = BridgeDirections(this.valueParameters.size) + if (modality == Modality.ABSTRACT) + return ourDirections + for (index in ourDirections.array.indices) + ourDirections.array[index] = this.bridgeDirectionToAt(overriddenDescriptor, index) + + val target = this.target + if (!kind.isReal + && OverridingUtil.overrides(target, overriddenDescriptor) + && ourDirections == target.bridgeDirectionsTo(overriddenDescriptor)) { + // Bridge is inherited from superclass. + return BridgeDirections(this.valueParameters.size) + } + + return ourDirections +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index e8e3bdd9bd5..d29d8c83971 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -2,23 +2,25 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.* import llvm.* -import org.jetbrains.kotlin.backend.konan.* -import org.jetbrains.kotlin.backend.konan.ir.* +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.KonanConfigKeys +import org.jetbrains.kotlin.backend.konan.KonanPhase +import org.jetbrains.kotlin.backend.konan.PhaseManager import org.jetbrains.kotlin.backend.konan.descriptors.* +import org.jetbrains.kotlin.backend.konan.ir.IrInlineFunctionBody +import org.jetbrains.kotlin.backend.konan.ir.getArguments +import org.jetbrains.kotlin.backend.konan.ir.ir2string import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptorBase +import org.jetbrains.kotlin.ir.descriptors.IrImplementingDelegateDescriptorImpl import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrSetterCallImpl import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe @@ -1222,7 +1224,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun evaluateGetField(value: IrGetField): LLVMValueRef { context.log("evaluateGetField : ${ir2string(value)}") - if (value.descriptor.dispatchReceiverParameter != null) { + if (value.descriptor.dispatchReceiverParameter != null + // TODO: hack because of IR bug: https://github.com/JetBrains/kotlin/tree/rr/dispatch_receiver_for_delegate_descriptor. + || value.descriptor is IrImplementingDelegateDescriptorImpl) { val thisPtr = evaluateExpression(value.receiver!!) return codegen.loadSlot( fieldPtrOfClass(thisPtr, value.descriptor), value.descriptor.isVar()) @@ -1251,7 +1255,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun evaluateSetField(value: IrSetField): LLVMValueRef { context.log("evaluateSetField : ${ir2string(value)}") val valueToAssign = evaluateExpression(value.value) - if (value.descriptor.dispatchReceiverParameter != null) { + + if (value.descriptor.dispatchReceiverParameter != null + // TODO: hack because of IR bug: https://github.com/JetBrains/kotlin/tree/rr/dispatch_receiver_for_delegate_descriptor. + || value.descriptor is IrImplementingDelegateDescriptorImpl) { val thisPtr = evaluateExpression(value.receiver!!) codegen.storeAnyGlobal(valueToAssign, fieldPtrOfClass(thisPtr, value.descriptor)) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt index b8244176c7a..07353def2d4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt @@ -7,6 +7,8 @@ import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.descriptors.IrImplementingDelegateDescriptorImpl +import org.jetbrains.kotlin.ir.descriptors.IrPropertyDelegateDescriptorImpl import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.name.FqName @@ -296,8 +298,12 @@ private class DeclarationsGeneratorVisitor(override val context: Context) : val descriptor = declaration.descriptor val dispatchReceiverParameter = descriptor.dispatchReceiverParameter - if (dispatchReceiverParameter != null) { - val containingClass = dispatchReceiverParameter.containingDeclaration + if (dispatchReceiverParameter != null + // TODO: hack because of IR bug: https://github.com/JetBrains/kotlin/tree/rr/dispatch_receiver_for_delegate_descriptor. + || descriptor is IrImplementingDelegateDescriptorImpl) { + val containingClass = dispatchReceiverParameter?.containingDeclaration + // TODO: hack because of IR bug: https://github.com/JetBrains/kotlin/tree/rr/dispatch_receiver_for_delegate_descriptor. + ?: descriptor.containingDeclaration val classDeclarations = this.classes[containingClass] ?: error(containingClass.toString()) val allFields = classDeclarations.fields diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt index 9390564aa71..de26d04b275 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt @@ -6,6 +6,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.constants.StringValue import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny @@ -169,7 +170,13 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { get() { val target = descriptor.target if (!needBridge) return target - val bridgeOwner = if (descriptor.bridgeDirections.allNotNeeded()) target else descriptor - return context.specialDescriptorsFactory.getBridgeDescriptor(bridgeOwner) + val bridgeOwner = if (!descriptor.kind.isReal + && OverridingUtil.overrides(target, overriddenDescriptor) + && descriptor.bridgeDirectionsTo(overriddenDescriptor).allNotNeeded()) { + target // Bridge is inherited from superclass. + } else { + descriptor + } + return context.specialDescriptorsFactory.getBridgeDescriptor(OverriddenFunctionDescriptor(bridgeOwner, overriddenDescriptor)) } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt index 5f29fe36887..a4b4bdc5efc 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt @@ -91,38 +91,6 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr } } - override fun visitCall(expression: IrCall): IrExpression { - val irCall = super.visitCall(expression) as IrCall - - val descriptor = irCall.descriptor as? FunctionDescriptor ?: return irCall - if (descriptor.modality == Modality.ABSTRACT || (irCall.superQualifier == null && descriptor.isOverridable)) - return irCall // A virtual call. box/unbox will be in the corresponding bridge. - - val target = descriptor.target - - if (!descriptor.original.needBridgeTo(target)) return irCall - - val overriddenCall = IrCallImpl(irCall.startOffset, irCall.endOffset, - target, remapTypeArguments(irCall, target)).apply { - dispatchReceiver = irCall.dispatchReceiver - extensionReceiver = irCall.extensionReceiver - mapValueParameters { irCall.getValueArgument(it)!! } - } - - return super.visitCall(overriddenCall) - } - - private fun remapTypeArguments(oldExpression: IrMemberAccessExpression, newCallee: CallableDescriptor): Map? { - val oldCallee = oldExpression.descriptor - - return if (oldCallee.typeParameters.isEmpty()) - null - else oldCallee.typeParameters.associateBy( - { newCallee.typeParameters[it.index] }, - { oldExpression.getTypeArgument(it)!! } - ) - } - /** * @return the [ValueType] given type represented in generated code as, * or `null` if represented as object reference. diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BridgesBuilding.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BridgesBuilding.kt index 6515a234a98..2a3f530c3af 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BridgesBuilding.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BridgesBuilding.kt @@ -1,63 +1,171 @@ package org.jetbrains.kotlin.backend.konan.lower -import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.BodyLoweringPass +import org.jetbrains.kotlin.backend.common.ClassLoweringPass import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET -import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.util.transformFlat import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid -import org.jetbrains.kotlin.types.typeUtil.isUnit +import org.jetbrains.kotlin.types.KotlinType -internal class BridgesBuilding(val context: Context) : FileLoweringPass { - override fun lower(irFile: IrFile) { - irFile.transformChildrenVoid(object : IrElementTransformerVoid() { - - override fun visitClass(declaration: IrClass): IrStatement { - val irClass = super.visitClass(declaration) as IrClass - - val functions = mutableSetOf() - irClass.declarations.forEach { - when (it) { - is IrFunction -> functions.add(it.descriptor) - is IrProperty -> { - functions.add(it.getter?.descriptor) - functions.add(it.setter?.descriptor) - } - } +internal class DirectBridgesCallsLowering(val context: Context) : BodyLoweringPass { + override fun lower(irBody: IrBody) { + irBody.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + expression.transformChildrenVoid(this) + val descriptor = expression.descriptor as? FunctionDescriptor ?: return expression + if (descriptor.modality == Modality.ABSTRACT + || (expression.superQualifier == null && descriptor.isOverridable)) { + // A virtual call. boxing/unboxing will be in the corresponding bridge. + return expression } - irClass.descriptor.contributedMethods.forEach { functions.add(it) } + val target = descriptor.target + val needBridge = descriptor.original.needBridgeTo(target) + if (descriptor.kind != CallableMemberDescriptor.Kind.DELEGATION && !needBridge) + return expression - functions.forEach { buildBridge(it, irClass) } + val toCall = if (needBridge) { + target + } else { + // Need to call delegating function. + context.specialDescriptorsFactory.getBridgeDescriptor(OverriddenFunctionDescriptor(descriptor, target)) + } - return irClass + return IrCallImpl(expression.startOffset, expression.endOffset, + toCall, remapTypeArguments(expression, toCall)).apply { + dispatchReceiver = expression.dispatchReceiver + extensionReceiver = expression.extensionReceiver + mapValueParameters { expression.getValueArgument(it)!! } + } + } + + private fun remapTypeArguments(oldExpression: IrMemberAccessExpression, newCallee: CallableDescriptor) + : Map? { + val oldCallee = oldExpression.descriptor + + return if (oldCallee.typeParameters.isEmpty()) + null + else oldCallee.typeParameters.associateBy( + { newCallee.typeParameters[it.index] }, + { oldExpression.getTypeArgument(it)!! } + ) } }) + } +} +private object DECLARATION_ORIGIN_BRIDGE_METHOD : + IrDeclarationOriginImpl("BRIDGE_METHOD") + +internal class DelegationLowering(val context: Context) : ClassLoweringPass { + override fun lower(irClass: IrClass) { + irClass.declarations.transformFlat { + when (it) { + is IrFunction -> { + val transformedFun = transformBridgeToDelegatedMethod(irClass, it) + if (transformedFun == null) null + else listOf(transformedFun) + } + is IrProperty -> { + val getter = transformBridgeToDelegatedMethod(irClass, it.getter) + val setter = transformBridgeToDelegatedMethod(irClass, it.setter) + if (getter != null) it.getter = getter + if (setter != null) it.setter = setter + null + } + else -> null + } + } } - private object DECLARATION_ORIGIN_BRIDGE_METHOD : - IrDeclarationOriginImpl("BRIDGE_METHOD") + // TODO: hack because of broken IR for synthesized delegated members: https://youtrack.jetbrains.com/issue/KT-16486. + private fun transformBridgeToDelegatedMethod(irClass: IrClass, irFunction: IrFunction?): IrFunction? { + if (irFunction == null || irFunction.descriptor.kind != CallableMemberDescriptor.Kind.DELEGATION) return null - private fun buildBridge(descriptor: FunctionDescriptor?, irClass: IrClass) { - if (descriptor == null) return - if (descriptor.bridgeDirections.allNotNeeded()) - return + val body = irFunction.body as? IrBlockBody + ?: throw AssertionError("Unexpected method body: ${irFunction.body}") + val statement = body.statements.single() + val delegatedCall = ((statement as? IrReturn)?.value ?: statement) as? IrCall + ?: throw AssertionError("Unexpected method body: $statement") + val propertyGetter = delegatedCall.dispatchReceiver as? IrGetValue + ?: throw AssertionError("Unexpected dispatch receiver: ${delegatedCall.dispatchReceiver}") + val propertyDescriptor = propertyGetter.descriptor as? PropertyDescriptor + ?: throw AssertionError("Unexpected dispatch receiver descriptor: ${propertyGetter.descriptor}") + val delegated = context.specialDescriptorsFactory.getBridgeDescriptor( + OverriddenFunctionDescriptor(irFunction.descriptor, delegatedCall.descriptor as FunctionDescriptor)) + val newFunction = IrFunctionImpl(irFunction.startOffset, irFunction.endOffset, DECLARATION_ORIGIN_BRIDGE_METHOD, delegated) + val irBlockBody = IrBlockBodyImpl(irFunction.startOffset, irFunction.endOffset) + val returnType = delegatedCall.descriptor.returnType!! + val irCall = IrCallImpl(irFunction.startOffset, irFunction.endOffset, returnType, delegatedCall.descriptor, null) + val receiver = IrGetValueImpl(irFunction.startOffset, irFunction.endOffset, irClass.descriptor.thisAsReceiverParameter) + irCall.dispatchReceiver = IrGetFieldImpl(irFunction.startOffset, irFunction.endOffset, propertyDescriptor, receiver) + irCall.extensionReceiver = delegated.extensionReceiverParameter?.let { extensionReceiver -> + IrGetValueImpl(irFunction.startOffset, irFunction.endOffset, extensionReceiver) + } + irCall.mapValueParameters { overriddenValueParameter -> + val delegatedValueParameter = delegated.valueParameters[overriddenValueParameter.index] + IrGetValueImpl(irFunction.startOffset, irFunction.endOffset, delegatedValueParameter) + } + if (KotlinBuiltIns.isUnit(returnType) || KotlinBuiltIns.isNothing(returnType)) { + irBlockBody.statements.add(irCall) + } else { + val irReturn = IrReturnImpl(irFunction.startOffset, irFunction.endOffset, context.builtIns.nothingType, delegated, irCall) + irBlockBody.statements.add(irReturn) + } + + newFunction.body = irBlockBody + return newFunction + } +} + +internal class BridgesBuilding(val context: Context) : ClassLoweringPass { + override fun lower(irClass: IrClass) { + val functions = mutableSetOf() + irClass.declarations.forEach { + when (it) { + is IrFunction -> functions.add(it.descriptor) + is IrProperty -> { + functions.add(it.getter?.descriptor) + functions.add(it.setter?.descriptor) + } + } + } + + irClass.descriptor.contributedMethods.forEach { functions.add(it) } + + functions.forEach { + it?.let { function -> + function.allOverriddenDescriptors + .map { OverriddenFunctionDescriptor(function, it) } + .filter { !it.bridgeDirections.allNotNeeded() } + .filter { it.canBeCalledVirtually } + .distinctBy { it.bridgeDirections } + .forEach { + buildBridge(it, irClass) + } + } + } + } + + private fun buildBridge(descriptor: OverriddenFunctionDescriptor, irClass: IrClass) { val bridgeDescriptor = context.specialDescriptorsFactory.getBridgeDescriptor(descriptor) + val target = descriptor.descriptor.target - val delegatingCall = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, descriptor.target, - superQualifier = descriptor.target.containingDeclaration as ClassDescriptor /* Call non-virtually */).apply { + val delegatingCall = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, target, + superQualifier = target.containingDeclaration as ClassDescriptor /* Call non-virtually */).apply { val dispatchReceiverParameter = bridgeDescriptor.dispatchReceiverParameter if (dispatchReceiverParameter != null) dispatchReceiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, dispatchReceiverParameter) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt index 5cf1eb82631..aa93dc26a7c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt @@ -2,6 +2,7 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.ClassLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.lower.SimpleMemberScope import org.jetbrains.kotlin.backend.common.runOnFilePostfix import org.jetbrains.kotlin.backend.jvm.descriptors.createValueParameter import org.jetbrains.kotlin.backend.jvm.descriptors.initialize @@ -37,6 +38,8 @@ import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.utils.addToStdlib.singletonList import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList +// TODO: cross-module usage. + internal data class LoweredSyntheticFunction(val functionDescriptor: FunctionDescriptor, val containingClass: ClassDescriptor) internal data class LoweredEnumEntry(val implObject: ClassDescriptor, val valuesProperty: PropertyDescriptor, @@ -173,7 +176,7 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { defaultEnumEntryConstructors.put(loweredEnumConstructor, constructorDescriptor) } - val memberScope = MemberScope.Empty + val memberScope = SimpleMemberScope(irClass.descriptor.unsubstitutedMemberScope.getContributedDescriptors().toList()) defaultClassDescriptor.initialize(memberScope, constructors, null) return defaultClass diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 40b1630bca8..fc1f56b8ac6 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -36,7 +36,11 @@ task clean { } task run() { - dependsOn(tasks.withType(KonanTest).matching { !(it instanceof RunExternalTestGroup) && it.enabled }) + String prefix = project.findProperty("prefix") + if (prefix != null) + dependsOn(tasks.withType(KonanTest).matching { !(it instanceof RunExternalTestGroup) && it.enabled && it.name.startsWith(prefix) }) + else + dependsOn(tasks.withType(KonanTest).matching { !(it instanceof RunExternalTestGroup) && it.enabled }) } task run_external () { @@ -362,6 +366,16 @@ task enum_companionObject(type: RunKonanTest) { source = "codegen/enum/companionObject.kt" } +task enum_interfaceCallNoEntryClass(type: RunKonanTest) { + goldValue = "('z3', 3)\n('z3', 3)\n" + source = "codegen/enum/interfaceCallNoEntryClass.kt" +} + +task enum_interfaceCallWithEntryClass(type: RunKonanTest) { + goldValue = "z1z2\nz1z2\n" + source = "codegen/enum/interfaceCallWithEntryClass.kt" +} + task innerClass_simple(type: RunKonanTest) { goldValue = "OK\n" source = "codegen/innerClass/simple.kt" @@ -537,6 +551,41 @@ task bridges_test13(type: RunKonanTest) { source = "codegen/bridges/test13.kt" } +task bridges_test14(type: RunKonanTest) { + goldValue = "42\n56\n42\n56\n56\n42\n156\n142\n" + source = "codegen/bridges/test14.kt" +} + +task bridges_test15(type: RunKonanTest) { + goldValue = "OK\n" + source = "codegen/bridges/test15.kt" +} + +task bridges_test16(type: RunKonanTest) { + goldValue = "OK\nOK\n" + source = "codegen/bridges/test16.kt" +} + +task classDelegation_method(type: RunKonanTest) { + goldValue = "OKOK\n" + source = "codegen/classDelegation/method.kt" +} + +task classDelegation_property(type: RunKonanTest) { + goldValue = "4242\n" + source = "codegen/classDelegation/property.kt" +} + +task classDelegation_generic(type: RunKonanTest) { + goldValue = "OK\n" + source = "codegen/classDelegation/generic.kt" +} + +task classDelegation_withBridge(type: RunKonanTest) { + goldValue = "OK\n" + source = "codegen/classDelegation/withBridge.kt" +} + task array0(type: RunKonanTest) { goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n" source = "runtime/collections/array0.kt" diff --git a/backend.native/tests/codegen/bridges/test14.kt b/backend.native/tests/codegen/bridges/test14.kt new file mode 100644 index 00000000000..26d94b08a75 --- /dev/null +++ b/backend.native/tests/codegen/bridges/test14.kt @@ -0,0 +1,44 @@ +open class A { + open fun foo(x: T, y: U) { + println(x.toString()) + println(y.toString()) + } +} + +interface I1 { + fun foo(x: Int, y: T) +} + +interface I2 { + fun foo(x: T, y: Int) +} + +class B : A(), I1, I2 { + var z: Int = 5 + var q: Int = 7 + override fun foo(x: Int, y: Int) { + z = x + q = y + } +} + +fun zzz(a: A) { + a.foo(42, 56) +} + +fun main(args: Array) { + val b = B() + zzz(b) + val a = A() + zzz(a) + println(b.z) + println(b.q) + val i1: I1 = b + i1.foo(56, 42) + println(b.z) + println(b.q) + val i2: I2 = b + i2.foo(156, 142) + println(b.z) + println(b.q) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/bridges/test15.kt b/backend.native/tests/codegen/bridges/test15.kt new file mode 100644 index 00000000000..a29e6f9a958 --- /dev/null +++ b/backend.native/tests/codegen/bridges/test15.kt @@ -0,0 +1,35 @@ +// non-generic interface, generic impl, vtable call + interface call +open class A { + open var size: T = 56 as T +} + +interface C { + var size: Int +} + +open class B : C, A() + +open class D: B() { + override var size: Int = 117 +} + +fun foo(a: A) { + a.size = 42 as T +} + +fun box(): String { + val b = B() + + foo(b) + if (b.size != 42) return "fail 1" + val d = D() + if (d.size != 117) return "fail 2" + foo(d) + if (d.size != 42) return "fail 3" + + return "OK" +} + +fun main(args: Array) { + println(box()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/bridges/test16.kt b/backend.native/tests/codegen/bridges/test16.kt new file mode 100644 index 00000000000..7a9aa8219cf --- /dev/null +++ b/backend.native/tests/codegen/bridges/test16.kt @@ -0,0 +1,20 @@ +interface A { + fun foo(): String +} + +abstract class C: A + +open class B: C() { + override fun foo(): String { + return "OK" + } +} + +fun bar(c: C) = c.foo() + +fun main(args: Array) { + val b = B() + val c: C = b + println(bar(b)) + println(bar(c)) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/classDelegation/generic.kt b/backend.native/tests/codegen/classDelegation/generic.kt new file mode 100644 index 00000000000..106e2a5af99 --- /dev/null +++ b/backend.native/tests/codegen/classDelegation/generic.kt @@ -0,0 +1,21 @@ +open class Content() { + override fun toString() = "OK" +} + +interface Box { + fun get(): E +} + +interface ContentBox : Box + +object Impl : ContentBox { + override fun get(): Content = Content() +} + +class ContentBoxDelegate() : ContentBox by (Impl as ContentBox) + +fun box() = ContentBoxDelegate().get().toString() + +fun main(args: Array) { + println(box()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/classDelegation/method.kt b/backend.native/tests/codegen/classDelegation/method.kt new file mode 100644 index 00000000000..214c697b35c --- /dev/null +++ b/backend.native/tests/codegen/classDelegation/method.kt @@ -0,0 +1,19 @@ +interface A { + fun foo(): T +} + +class B : A { + override fun foo() = "OK" +} + +class C(a: A) : A by a + +fun box(): String { + val c = C(B()) + val a: A = c + return c.foo() + a.foo() +} + +fun main(args: Array) { + println(box()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/classDelegation/property.kt b/backend.native/tests/codegen/classDelegation/property.kt new file mode 100644 index 00000000000..bd6be7e2e49 --- /dev/null +++ b/backend.native/tests/codegen/classDelegation/property.kt @@ -0,0 +1,19 @@ +interface A { + val x: Int +} + +class C: A { + override val x: Int = 42 +} + +class Q(a: A): A by a + +fun box(): String { + val q = Q(C()) + val a: A = q + return q.x.toString() + a.x.toString() +} + +fun main(args: Array) { + println(box()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/classDelegation/withBridge.kt b/backend.native/tests/codegen/classDelegation/withBridge.kt new file mode 100644 index 00000000000..1e0fc393050 --- /dev/null +++ b/backend.native/tests/codegen/classDelegation/withBridge.kt @@ -0,0 +1,28 @@ +interface A { + fun foo(t: T): String +} + +interface B { + fun foo(t: Int) = "B" +} + +class Z : B + +class Z1 : A, B by Z() + +fun box(): String { + val z1 = Z1() + val z1a: A = z1 + val z1b: B = z1 + + return when { + z1.foo( 0) != "B" -> "Fail #1" + z1a.foo( 0) != "B" -> "Fail #2" + z1b.foo( 0) != "B" -> "Fail #3" + else -> "OK" + } +} + +fun main(args: Array) { + println(box()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/enum/interfaceCallNoEntryClass.kt b/backend.native/tests/codegen/enum/interfaceCallNoEntryClass.kt new file mode 100644 index 00000000000..a340c7afb9f --- /dev/null +++ b/backend.native/tests/codegen/enum/interfaceCallNoEntryClass.kt @@ -0,0 +1,19 @@ +interface A { + fun foo(): String +} + +enum class Zzz(val zzz: String, val x: Int) : A { + Z1("z1", 1), + Z2("z2", 2), + Z3("z3", 3); + + override fun foo(): String{ + return "('$zzz', $x)" + } +} + +fun main(args: Array) { + println(Zzz.Z3.foo()) + val a: A = Zzz.Z3 + println(a.foo()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/enum/interfaceCallWithEntryClass.kt b/backend.native/tests/codegen/enum/interfaceCallWithEntryClass.kt new file mode 100644 index 00000000000..bc64cf70ae1 --- /dev/null +++ b/backend.native/tests/codegen/enum/interfaceCallWithEntryClass.kt @@ -0,0 +1,22 @@ +interface A { + fun f(): String +} + +enum class Zzz: A { + Z1 { + override fun f() = "z1" + }, + + Z2 { + override fun f() = "z2" + }; + + override fun f() = "" +} + +fun main(args: Array) { + println(Zzz.Z1.f() + Zzz.Z2.f()) + val a1: A = Zzz.Z1 + val a2: A = Zzz.Z2 + println(a1.f() + a2.f()) +} \ No newline at end of file