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 8c152343e34..2b4a5731980 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 @@ -213,30 +213,41 @@ private fun IrTypeParameter.copySuperTypesFrom(source: IrTypeParameter) { } } +// Copy value parameters, dispatch receiver, and extension receiver from source to value parameters of this function. +// Type of dispatch receiver defaults to source's dispatch receiver. It is overridable in case the new function and the old one are used in +// different contexts and expect different type of dispatch receivers. The overriding type should be assign compatible to the old type. fun IrFunction.copyValueParametersToStatic( source: IrFunction, - origin: IrDeclarationOrigin + origin: IrDeclarationOrigin, + dispatchReceiverType: IrType? = source.dispatchReceiverParameter?.type ) { val target = this assert(target.valueParameters.isEmpty()) var shift = 0 - source.dispatchReceiverParameter?.let { p -> + source.dispatchReceiverParameter?.let { originalDispatchReceiver -> + assert(dispatchReceiverType!!.isSubtypeOfClass(originalDispatchReceiver.type.classOrNull!!)) + val type = dispatchReceiverType.remapTypeParameters( + (originalDispatchReceiver.parent as IrTypeParametersContainer).classIfConstructor, + target.classIfConstructor + ) + target.valueParameters.add( - p.copyTo( + originalDispatchReceiver.copyTo( target, - origin = p.origin, - index = p.index + shift++, + origin = originalDispatchReceiver.origin, + index = originalDispatchReceiver.index + shift++, + type = type, name = Name.identifier("\$this") ) ) } - source.extensionReceiverParameter?.let { p -> + source.extensionReceiverParameter?.let { originalExtensionReceiver -> target.valueParameters.add( - p.copyTo( + originalExtensionReceiver.copyTo( target, - origin = p.origin, - index = p.index + shift++, + origin = originalExtensionReceiver.origin, + index = originalExtensionReceiver.index + shift++, name = Name.identifier("\$receiver") ) ) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt index a96223cadf0..0230384866f 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt @@ -29,7 +29,9 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl +import org.jetbrains.kotlin.ir.types.isSubtypeOfClass import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid @@ -62,8 +64,18 @@ private class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElem if (expression.usesDefaultArguments()) { return super.visitFunctionAccess(expression) } + fun makeFunctionAccessorSymbolWithSuper(functionSymbol: IrFunctionSymbol): IrFunctionSymbol = + makeFunctionAccessorSymbol(functionSymbol, (expression as? IrCall)?.superQualifierSymbol) return super.visitExpression( - handleAccess(expression, expression.symbol, functionMap, ::makeFunctionAccessorSymbol, ::modifyFunctionAccessExpression) + handleAccess( + expression, + expression.symbol, + functionMap, + ::makeFunctionAccessorSymbolWithSuper, + ::modifyFunctionAccessExpression, + (expression as? IrCall)?.superQualifierSymbol, + (expression as? IrCall)?.dispatchReceiver?.type?.classifierOrNull as? IrClassSymbol + ) ) } @@ -80,20 +92,23 @@ private class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElem symbol: FromSyT, accumMap: MutableMap, symbolConverter: (FromSyT) -> ToSyT, - exprConverter: (ExprT, ToSyT) -> IrDeclarationReference + exprConverter: (ExprT, ToSyT) -> IrDeclarationReference, + superQualifierSymbol: IrClassSymbol? = null, + thisSymbol: IrClassSymbol? = null ): IrExpression = - if (!symbol.isAccessible()) { + if (!symbol.isAccessible(superQualifierSymbol != null, thisSymbol)) { val accessorSymbol = accumMap.getOrPut(symbol) { symbolConverter(symbol) } exprConverter(expression, accessorSymbol) } else { expression } - private fun makeFunctionAccessorSymbol(functionSymbol: IrFunctionSymbol): IrFunctionSymbol = when (functionSymbol) { - is IrConstructorSymbol -> functionSymbol.owner.makeConstructorAccessor().symbol - is IrSimpleFunctionSymbol -> functionSymbol.owner.makeSimpleFunctionAccessor().symbol - else -> error("Unknown subclass of IrFunctionSymbol") - } + private fun makeFunctionAccessorSymbol(functionSymbol: IrFunctionSymbol, superQualifierSymbol: IrClassSymbol?): IrFunctionSymbol = + when (functionSymbol) { + is IrConstructorSymbol -> functionSymbol.owner.makeConstructorAccessor().symbol + is IrSimpleFunctionSymbol -> functionSymbol.owner.makeSimpleFunctionAccessor(superQualifierSymbol).symbol + else -> error("Unknown subclass of IrFunctionSymbol") + } private fun IrConstructor.makeConstructorAccessor(): IrConstructor { val source = this @@ -132,7 +147,7 @@ private class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElem copyAllParamsToArgs(it, accessor) } - private fun IrSimpleFunction.makeSimpleFunctionAccessor(): IrSimpleFunction { + private fun IrSimpleFunction.makeSimpleFunctionAccessor(superQualifierSymbol: IrClassSymbol?): IrSimpleFunction { val source = this return buildFun { origin = JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR @@ -140,26 +155,41 @@ private class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElem visibility = Visibilities.PUBLIC isSuspend = source.isSuspend }.also { accessor -> - accessor.parent = source.parent + accessor.parent = if ((source.parent as? IrClass)?.isInterface == true) { + // Accessors for interfaces are only for private methods. They should always be placed in DefaultImpls. The only exception + // is private methods annotated with @JvmDefault; Only in such cases JVM default method is generated. + // TODO: Handle the exception after targeting Java 8 or newer, where JVM default methods are available. + context.declarationFactory.getDefaultImplsClass(source.parent as IrClass) + } else { + source.parent + } pendingTransformations.add { (accessor.parent as IrDeclarationContainer).declarations.add(accessor) } accessor.copyTypeParametersFrom(source, JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR) - accessor.copyValueParametersToStatic(source, JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR) + // When a method is defined in class C1 but called on C1's subclass C2, source.dispatchReceiverParameter.type can be resolved + // to C1, while the method symbol still bound to C2, which expects a receiver of type C2. Therefore, we need to specify + // dispatchReceiver's type using super qualifier's type explicitly. + accessor.copyValueParametersToStatic( + source, + JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR, + superQualifierSymbol?.owner?.defaultType ?: source.dispatchReceiverParameter?.type + ) accessor.returnType = source.returnType.remapTypeParameters(source, accessor) accessor.body = IrExpressionBodyImpl( UNDEFINED_OFFSET, UNDEFINED_OFFSET, - createSimpleFunctionCall(accessor, source.symbol) + createSimpleFunctionCall(accessor, source.symbol, superQualifierSymbol) ) } } - private fun createSimpleFunctionCall(accessor: IrFunction, targetSymbol: IrFunctionSymbol) = + private fun createSimpleFunctionCall(accessor: IrFunction, targetSymbol: IrFunctionSymbol, superQualifierSymbol: IrClassSymbol?) = IrCallImpl( UNDEFINED_OFFSET, UNDEFINED_OFFSET, accessor.returnType, targetSymbol, targetSymbol.descriptor, - targetSymbol.owner.typeParameters.size + targetSymbol.owner.typeParameters.size, + superQualifierSymbol = superQualifierSymbol ).also { copyAllParamsToArgs(it, accessor) } @@ -253,8 +283,7 @@ private class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElem oldExpression.type, accessorSymbol, accessorSymbol.descriptor, oldExpression.typeArgumentsCount, - oldExpression.origin, - oldExpression.superQualifierSymbol + oldExpression.origin ) is IrDelegatingConstructorCall -> IrDelegatingConstructorCallImpl( oldExpression.startOffset, oldExpression.endOffset, @@ -377,7 +406,7 @@ private class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElem return Name.identifier("access\$prop\$$setterName") } - private fun IrSymbol.isAccessible(): Boolean { + private fun IrSymbol.isAccessible(withSuper: Boolean, thisObjReference: IrClassSymbol?): Boolean { /// We assume that IR code that reaches us has been checked for correctness at the frontend. /// This function needs to single out those cases where Java accessibility rules differ from Kotlin's. @@ -390,7 +419,7 @@ private class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElem if (declaration is IrFunction && declaration.isInline) return true // The only two visibilities where Kotlin rules differ from JVM rules. - if (!Visibilities.isPrivate(declaration.visibility) && declaration.visibility != Visibilities.PROTECTED) return true + if (!withSuper && !Visibilities.isPrivate(declaration.visibility) && declaration.visibility != Visibilities.PROTECTED) return true // If local variables are accessible by Kotlin rules, they also are by Java rules. val symbolDeclarationContainer = (declaration.parent as? IrDeclarationContainer) as? IrElement ?: return true @@ -407,6 +436,12 @@ private class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElem (declaration.visibility == Visibilities.PROTECTED && !samePackage && !(symbolDeclarationContainer is IrClass && contextDeclarationContainer is IrClass && contextDeclarationContainer.isSubclassOf(symbolDeclarationContainer))) -> false + // Invoking with super qualifier is implemented by invokespecial, which requires + // 1. `this` to be assign compatible with current class. + // 2. the method is a member of a superclass of current class. + (withSuper && contextDeclarationContainer is IrClass && symbolDeclarationContainer is IrClass && + ((thisObjReference != null && !contextDeclarationContainer.symbol.isSubtypeOfClass(thisObjReference)) || + !(contextDeclarationContainer.isSubclassOf(symbolDeclarationContainer)))) -> false else -> true } } diff --git a/compiler/testData/codegen/box/closures/kt11634.kt b/compiler/testData/codegen/box/closures/kt11634.kt index 9a8f1ba38a6..7bd1880718f 100644 --- a/compiler/testData/codegen/box/closures/kt11634.kt +++ b/compiler/testData/codegen/box/closures/kt11634.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface A { fun foo(): String } diff --git a/compiler/testData/codegen/box/closures/kt11634_2.kt b/compiler/testData/codegen/box/closures/kt11634_2.kt index 9cdbb5ca8bc..1eb0e9ff096 100644 --- a/compiler/testData/codegen/box/closures/kt11634_2.kt +++ b/compiler/testData/codegen/box/closures/kt11634_2.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface A { fun foo(): String } diff --git a/compiler/testData/codegen/box/closures/kt11634_3.kt b/compiler/testData/codegen/box/closures/kt11634_3.kt index b8f4d7e433d..1fdaa65c236 100644 --- a/compiler/testData/codegen/box/closures/kt11634_3.kt +++ b/compiler/testData/codegen/box/closures/kt11634_3.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface A { fun foo(): String } diff --git a/compiler/testData/codegen/box/closures/kt11634_4.kt b/compiler/testData/codegen/box/closures/kt11634_4.kt index 5310cc76f40..08722aa81de 100644 --- a/compiler/testData/codegen/box/closures/kt11634_4.kt +++ b/compiler/testData/codegen/box/closures/kt11634_4.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface A { fun foo(): String } diff --git a/compiler/testData/codegen/box/jvm8/defaults/privateInDefaultImpls.kt b/compiler/testData/codegen/box/jvm8/defaults/privateInDefaultImpls.kt index 912c6b41a61..a5ab5728745 100644 --- a/compiler/testData/codegen/box/jvm8/defaults/privateInDefaultImpls.kt +++ b/compiler/testData/codegen/box/jvm8/defaults/privateInDefaultImpls.kt @@ -1,5 +1,4 @@ // !JVM_DEFAULT_MODE: enable -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // JVM_TARGET: 1.8 // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/jvm8/kt11969.kt b/compiler/testData/codegen/box/jvm8/kt11969.kt index bb6d713872d..cb91cfb3c6b 100644 --- a/compiler/testData/codegen/box/jvm8/kt11969.kt +++ b/compiler/testData/codegen/box/jvm8/kt11969.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // JVM_TARGET: 1.8 // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/reflection/enclosing/kt11969.kt b/compiler/testData/codegen/box/reflection/enclosing/kt11969.kt index f0a97fbd645..93ee323a1c3 100644 --- a/compiler/testData/codegen/box/reflection/enclosing/kt11969.kt +++ b/compiler/testData/codegen/box/reflection/enclosing/kt11969.kt @@ -1,5 +1,4 @@ // WITH_RUNTIME -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM interface Z { diff --git a/compiler/testData/codegen/box/regressions/kt13381.kt b/compiler/testData/codegen/box/regressions/kt13381.kt index 9bbaca3c317..b424d596455 100644 --- a/compiler/testData/codegen/box/regressions/kt13381.kt +++ b/compiler/testData/codegen/box/regressions/kt13381.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface A { // There must be no delegation methods for 'log' and 'bar' in C as they are private private val log: String get() = "O" diff --git a/compiler/testData/codegen/box/super/enclosedFun.kt b/compiler/testData/codegen/box/super/enclosedFun.kt index 3cadbe7664b..5f317b46f77 100644 --- a/compiler/testData/codegen/box/super/enclosedFun.kt +++ b/compiler/testData/codegen/box/super/enclosedFun.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface BK { fun x() : Int = 50 } diff --git a/compiler/testData/codegen/box/super/enclosedVar.kt b/compiler/testData/codegen/box/super/enclosedVar.kt index ff691d5893d..50477570820 100644 --- a/compiler/testData/codegen/box/super/enclosedVar.kt +++ b/compiler/testData/codegen/box/super/enclosedVar.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR open class M() { open var y = 500 } diff --git a/compiler/testData/codegen/box/super/innerClassLabeledSuper2.kt b/compiler/testData/codegen/box/super/innerClassLabeledSuper2.kt index 2392499ddc3..1a0bca8c437 100644 --- a/compiler/testData/codegen/box/super/innerClassLabeledSuper2.kt +++ b/compiler/testData/codegen/box/super/innerClassLabeledSuper2.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR //inspired by kt3492 interface BK { fun foo(): String diff --git a/compiler/testData/codegen/box/super/innerClassLabeledSuperProperty.kt b/compiler/testData/codegen/box/super/innerClassLabeledSuperProperty.kt index dc5cb617f6c..bb8098564f6 100644 --- a/compiler/testData/codegen/box/super/innerClassLabeledSuperProperty.kt +++ b/compiler/testData/codegen/box/super/innerClassLabeledSuperProperty.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface Base { val foo: String fun bar(): String diff --git a/compiler/testData/codegen/box/super/innerClassLabeledSuperProperty2.kt b/compiler/testData/codegen/box/super/innerClassLabeledSuperProperty2.kt index 98e5387d814..effc71e7925 100644 --- a/compiler/testData/codegen/box/super/innerClassLabeledSuperProperty2.kt +++ b/compiler/testData/codegen/box/super/innerClassLabeledSuperProperty2.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR //inspired by kt3492 interface Base { val foo: String diff --git a/compiler/testData/codegen/box/super/innerClassQualifiedFunctionCall.kt b/compiler/testData/codegen/box/super/innerClassQualifiedFunctionCall.kt index b984178f136..557905cedbe 100644 --- a/compiler/testData/codegen/box/super/innerClassQualifiedFunctionCall.kt +++ b/compiler/testData/codegen/box/super/innerClassQualifiedFunctionCall.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface T { open fun baz(): String = "T.baz" } diff --git a/compiler/testData/codegen/box/super/innerClassQualifiedPropertyAccess.kt b/compiler/testData/codegen/box/super/innerClassQualifiedPropertyAccess.kt index 368c659d96f..bdcd70ce962 100644 --- a/compiler/testData/codegen/box/super/innerClassQualifiedPropertyAccess.kt +++ b/compiler/testData/codegen/box/super/innerClassQualifiedPropertyAccess.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface T { open val baz: String get() = "T.baz" diff --git a/compiler/testData/codegen/box/super/kt3492ClassFun.kt b/compiler/testData/codegen/box/super/kt3492ClassFun.kt index 00504bcec6e..1dd8d1b2770 100644 --- a/compiler/testData/codegen/box/super/kt3492ClassFun.kt +++ b/compiler/testData/codegen/box/super/kt3492ClassFun.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR open class A { open fun foo2(): String = "OK" } diff --git a/compiler/testData/codegen/box/super/kt3492ClassProperty.kt b/compiler/testData/codegen/box/super/kt3492ClassProperty.kt index b54b5c14403..25feedb525f 100644 --- a/compiler/testData/codegen/box/super/kt3492ClassProperty.kt +++ b/compiler/testData/codegen/box/super/kt3492ClassProperty.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR open class A { open val foo: String = "OK" } diff --git a/compiler/testData/codegen/box/super/kt3492TraitFun.kt b/compiler/testData/codegen/box/super/kt3492TraitFun.kt index ee0683c0d7f..144e86036a3 100644 --- a/compiler/testData/codegen/box/super/kt3492TraitFun.kt +++ b/compiler/testData/codegen/box/super/kt3492TraitFun.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface ATrait { open fun foo2(): String = "OK" } diff --git a/compiler/testData/codegen/box/super/kt3492TraitProperty.kt b/compiler/testData/codegen/box/super/kt3492TraitProperty.kt index b7257c3a46c..c4c3298afcb 100644 --- a/compiler/testData/codegen/box/super/kt3492TraitProperty.kt +++ b/compiler/testData/codegen/box/super/kt3492TraitProperty.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface A { open val foo: String get() = "OK" diff --git a/compiler/testData/codegen/box/traits/privateInterfaceMethod.kt b/compiler/testData/codegen/box/traits/privateInterfaceMethod.kt index eb110832043..74b01ae58ff 100644 --- a/compiler/testData/codegen/box/traits/privateInterfaceMethod.kt +++ b/compiler/testData/codegen/box/traits/privateInterfaceMethod.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JS -// IGNORE_BACKEND: JVM_IR interface A { private fun foo() = "OK" diff --git a/compiler/testData/codegen/box/traits/syntheticAccessor.kt b/compiler/testData/codegen/box/traits/syntheticAccessor.kt index a6a1f6ab9aa..32ce13d9d57 100644 --- a/compiler/testData/codegen/box/traits/syntheticAccessor.kt +++ b/compiler/testData/codegen/box/traits/syntheticAccessor.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR var result = "fail" interface B { diff --git a/compiler/testData/codegen/box/traits/traitWithPrivateExtension.kt b/compiler/testData/codegen/box/traits/traitWithPrivateExtension.kt index 18087b76de0..5dee33c0792 100644 --- a/compiler/testData/codegen/box/traits/traitWithPrivateExtension.kt +++ b/compiler/testData/codegen/box/traits/traitWithPrivateExtension.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR open class B { val p = "OK" } diff --git a/compiler/testData/codegen/box/traits/traitWithPrivateMember.kt b/compiler/testData/codegen/box/traits/traitWithPrivateMember.kt index 636c5857df9..50debaa1e26 100644 --- a/compiler/testData/codegen/box/traits/traitWithPrivateMember.kt +++ b/compiler/testData/codegen/box/traits/traitWithPrivateMember.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface Z { fun testFun() : String { diff --git a/compiler/testData/codegen/box/traits/traitWithPrivateMemberAccessFromLambda.kt b/compiler/testData/codegen/box/traits/traitWithPrivateMemberAccessFromLambda.kt index ffa72888bec..0b2a16072d3 100644 --- a/compiler/testData/codegen/box/traits/traitWithPrivateMemberAccessFromLambda.kt +++ b/compiler/testData/codegen/box/traits/traitWithPrivateMemberAccessFromLambda.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR interface Z { fun testFun(): String {