diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java index e2cda54efdf..cf62113a85b 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java @@ -1073,6 +1073,24 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest { public void testKt22714() throws Exception { runTest("compiler/testData/codegen/bytecodeText/checkcast/kt22714.kt"); } + + @Test + @TestMetadata("noCheckcastOnDelegatingDefaultImplsCall.kt") + public void testNoCheckcastOnDelegatingDefaultImplsCall() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnDelegatingDefaultImplsCall.kt"); + } + + @Test + @TestMetadata("noCheckcastOnDispatchReceiverOfMethodWithDefaultParameters.kt") + public void testNoCheckcastOnDispatchReceiverOfMethodWithDefaultParameters() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnDispatchReceiverOfMethodWithDefaultParameters.kt"); + } + + @Test + @TestMetadata("noCheckcastOnSuper.kt") + public void testNoCheckcastOnSuper() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnSuper.kt"); + } } @Nested diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 7d0867c024e..42755a4cdb1 100644 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -1050,6 +1050,12 @@ class ExpressionCodegen( MaterialValue(this, boxedRightType, expression.type) } + IrTypeOperator.REINTERPRET_CAST -> { + val targetType = typeMapper.mapType(typeOperand) + expression.argument.accept(this, data).materialize() + MaterialValue(this, targetType, typeOperand) + } + IrTypeOperator.INSTANCEOF -> { expression.argument.accept(this, data).materializeAt(context.irBuiltIns.anyNType) val type = typeMapper.boxType(typeOperand) diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt index 5059250bdde..a142e2bbaeb 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt @@ -153,7 +153,8 @@ private class InterfaceSuperCallsLowering(val context: JvmBackendContext) : IrEl } override fun visitCall(expression: IrCall): IrExpression { - if (expression.superQualifierSymbol?.owner?.isInterface != true || expression.isSuperToAny()) { + val superQualifierClass = expression.superQualifierSymbol?.owner + if (superQualifierClass == null || !superQualifierClass.isInterface || expression.isSuperToAny()) { return super.visitCall(expression) } diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt index 3441251c26f..1af54da6fca 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOptimizationLowering.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredStatementOrigin import org.jetbrains.kotlin.backend.jvm.ir.IrInlineScopeResolver import org.jetbrains.kotlin.backend.jvm.ir.findInlineCallSites +import org.jetbrains.kotlin.backend.jvm.ir.isInlineClassType import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.irGetField @@ -25,6 +26,7 @@ import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl import org.jetbrains.kotlin.ir.symbols.impl.IrPublicSymbolBase import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl import org.jetbrains.kotlin.ir.types.* @@ -84,7 +86,11 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass override fun visitCall(expression: IrCall, data: IrDeclaration?): IrExpression { expression.transformChildren(this, data) - if (expression.symbol.owner.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) { + val callee = expression.symbol.owner + + reinterpretMovedDispatchReceiver(expression, callee) + + if (callee.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) { return optimizePropertyAccess(expression, data) } @@ -103,6 +109,33 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass return expression } + private fun reinterpretMovedDispatchReceiver(expression: IrCall, callee: IrSimpleFunction) { + val movedDispatchReceiverIndex = callee.valueParameters.indexOfFirst { + it.origin == IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER + } + if (movedDispatchReceiverIndex >= 0) { + val movedDispatchReceiverParameter = callee.valueParameters[movedDispatchReceiverIndex] + expression.putValueArgument( + movedDispatchReceiverIndex, + expression.getValueArgument(movedDispatchReceiverIndex) + ?.reinterpretAsDispatchReceiverOfType(movedDispatchReceiverParameter.type) + ) + } + } + + // Given a dispatch receiver expression, wrap it in REINTERPRET_CAST to the given type, + // unless it's a value of inline class (which could be boxed at this point). + // Avoids a CHECKCAST on a moved dispatch receiver argument. + private fun IrExpression.reinterpretAsDispatchReceiverOfType(irType: IrType): IrExpression = + if (this.type.isInlineClassType()) + this + else + IrTypeOperatorCallImpl( + this.startOffset, this.endOffset, + irType, IrTypeOperator.REINTERPRET_CAST, irType, + this + ) + private fun optimizePropertyAccess(expression: IrCall, data: IrDeclaration?): IrExpression { val accessor = expression.symbol.owner as? IrSimpleFunction ?: return expression if (accessor.modality != Modality.FINAL || accessor.isExternal) return expression diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt index f23ff69a9fe..cd9f97dbdb0 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt @@ -50,7 +50,6 @@ enum class IrTypeOperator { /** * SAM conversion: value of functional type F is used where Single Abstract Method interface value is expected. - * Currently this is possible in Kotlin/JVM only, however, there's a big demand for SAM conversion for Kotlin interfaces. */ SAM_CONVERSION, @@ -61,7 +60,8 @@ enum class IrTypeOperator { IMPLICIT_DYNAMIC_CAST, /** - * C-like reinterpret_cast using as primitive type operation in JS + * C-like reinterpret_cast using as primitive type operation in JS. + * On JVM, tells back-end to treat argument as a value of a given type (even though exact JVM types might differ). */ REINTERPRET_CAST; } diff --git a/compiler/testData/codegen/box/inlineClasses/interfaceMethodCalls/interfaceSuperCall.kt b/compiler/testData/codegen/box/inlineClasses/interfaceMethodCalls/interfaceSuperCall.kt index 2f8434c6776..70061fab8b1 100644 --- a/compiler/testData/codegen/box/inlineClasses/interfaceMethodCalls/interfaceSuperCall.kt +++ b/compiler/testData/codegen/box/inlineClasses/interfaceMethodCalls/interfaceSuperCall.kt @@ -1,15 +1,58 @@ // WITH_STDLIB -interface A { - fun f(x: String) = x +interface IFoo { + fun foo(): String = "K" } @Suppress("OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE") @kotlin.jvm.JvmInline -value class B(val y: String) : A { - override fun f(x: String) = super.f(x + y) +value class IcStr(val y: String) : IFoo { + override fun foo(): String = y + super.foo() +} + +@Suppress("OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE") +@kotlin.jvm.JvmInline +value class IcInt(val i: Int) : IFoo { + override fun foo(): String = "O" + super.foo() +} + +@Suppress("OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE") +@kotlin.jvm.JvmInline +value class IcLong(val l: Long) : IFoo { + override fun foo(): String = "O" + super.foo() +} + +@Suppress("OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE") +@kotlin.jvm.JvmInline +value class IcAny(val a: Any?) : IFoo { + override fun foo(): String = "O" + super.foo() +} + +@Suppress("OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE") +@kotlin.jvm.JvmInline +value class IcOverIc(val o: IcLong) : IFoo { + override fun foo(): String = "O" + super.foo() +} + +@Suppress("OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE") +@kotlin.jvm.JvmInline +value class IcOverSuperInterface(val x: IFoo) : IFoo { + override fun foo(): String = "O" + super.foo() +} + +fun check(message: String, iFoo: IFoo) { + val actual = iFoo.foo() + if (actual != "OK") + throw Exception("$message: \"$actual\" != OK") } fun box(): String { - return B("K").f("O") + check("IcStr", IcStr("O")) + check("IcInt", IcInt(42)) + check("IcLong", IcLong(42L)) + check("IcAny", IcAny("")) + check("IcOverIc", IcOverIc(IcLong(42L))) + check("IcOverSuperInterface", IcOverSuperInterface(IcInt(42))) + + return "OK" } diff --git a/compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnDelegatingDefaultImplsCall.kt b/compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnDelegatingDefaultImplsCall.kt new file mode 100644 index 00000000000..fb017c7a6cf --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnDelegatingDefaultImplsCall.kt @@ -0,0 +1,24 @@ +interface Z { + fun testFun() : String { + return privateFun() + } + + fun testProperty() : String { + return privateProp + } + + private fun privateFun(): String { + return "O" + } + + private val privateProp: String + get() = "K" +} + +object Z2 : Z + +fun box() : String { + return Z2.testFun() + Z2.testProperty() +} + +// 0 CHECKCAST diff --git a/compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnDispatchReceiverOfMethodWithDefaultParameters.kt b/compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnDispatchReceiverOfMethodWithDefaultParameters.kt new file mode 100644 index 00000000000..37b4679bee4 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnDispatchReceiverOfMethodWithDefaultParameters.kt @@ -0,0 +1,12 @@ +open class A { + fun foo(i: Int = 42) {} +} + +class B : A() + +fun test() { + B().foo() +} + +// JVM_IR_TEMPLATES +// 0 CHECKCAST diff --git a/compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnSuper.kt b/compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnSuper.kt new file mode 100644 index 00000000000..71238a753a9 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnSuper.kt @@ -0,0 +1,9 @@ +interface Tr { + fun extra(): String = "e" +} + +class N : Tr { + override fun extra(): String = super.extra() +} + +// 0 CHECKCAST diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java index 12bea02a010..5444571b829 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java @@ -1061,6 +1061,24 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { public void testKt22714() throws Exception { runTest("compiler/testData/codegen/bytecodeText/checkcast/kt22714.kt"); } + + @Test + @TestMetadata("noCheckcastOnDelegatingDefaultImplsCall.kt") + public void testNoCheckcastOnDelegatingDefaultImplsCall() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnDelegatingDefaultImplsCall.kt"); + } + + @Test + @TestMetadata("noCheckcastOnDispatchReceiverOfMethodWithDefaultParameters.kt") + public void testNoCheckcastOnDispatchReceiverOfMethodWithDefaultParameters() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnDispatchReceiverOfMethodWithDefaultParameters.kt"); + } + + @Test + @TestMetadata("noCheckcastOnSuper.kt") + public void testNoCheckcastOnSuper() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnSuper.kt"); + } } @Nested diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java index 3f583db480b..b9598ef99a3 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java @@ -1073,6 +1073,24 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { public void testKt22714() throws Exception { runTest("compiler/testData/codegen/bytecodeText/checkcast/kt22714.kt"); } + + @Test + @TestMetadata("noCheckcastOnDelegatingDefaultImplsCall.kt") + public void testNoCheckcastOnDelegatingDefaultImplsCall() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnDelegatingDefaultImplsCall.kt"); + } + + @Test + @TestMetadata("noCheckcastOnDispatchReceiverOfMethodWithDefaultParameters.kt") + public void testNoCheckcastOnDispatchReceiverOfMethodWithDefaultParameters() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnDispatchReceiverOfMethodWithDefaultParameters.kt"); + } + + @Test + @TestMetadata("noCheckcastOnSuper.kt") + public void testNoCheckcastOnSuper() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/checkcast/noCheckcastOnSuper.kt"); + } } @Nested diff --git a/plugins/parcelize/parcelize-compiler/testData/codegen/customSimple.asm.ir.txt b/plugins/parcelize/parcelize-compiler/testData/codegen/customSimple.asm.ir.txt index f9d1e2de183..8f9fc368c82 100644 --- a/plugins/parcelize/parcelize-compiler/testData/codegen/customSimple.asm.ir.txt +++ b/plugins/parcelize/parcelize-compiler/testData/codegen/customSimple.asm.ir.txt @@ -11,7 +11,6 @@ final class User$Companion : java/lang/Object, kotlinx/parcelize/Parceler { LABEL (L0) LINENUMBER (10) ALOAD (0) - CHECKCAST (kotlinx/parcelize/Parceler) ILOAD (1) INVOKESTATIC (kotlinx/parcelize/Parceler$DefaultImpls, newArray, (Lkotlinx/parcelize/Parceler;I)[Ljava/lang/Object;) CHECKCAST ([LUser;)