diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt index 6f27eb29691..20306b7250a 100644 --- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt +++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.utils.DFS object CodegenUtil { @JvmStatic @@ -200,16 +201,25 @@ object CodegenUtil { trace: DiagnosticSink? ): List { if (descriptor.isActual) { + val actualParameters = descriptor.valueParameters + if (actualParameters.any { it.declaresOrInheritsDefaultValue() }) { + // This is incorrect code: actual function cannot have default values, they should be declared in the expected function. + // But until KT-22818 is fixed, we need to provide a workaround for the exception that happens on complex default values + // in the expected function. One may suppress the error then, and declare default values _both_ in expect and actual. + // With this code, we'll generate actual default values if they're present, and expected default values otherwise. + return actualParameters + } + val expected = CodegenUtil.findExpectedFunctionForActual(descriptor) if (expected != null && expected.valueParameters.any(ValueParameterDescriptor::declaresDefaultValue)) { val element = DescriptorToSourceUtils.descriptorToDeclaration(expected) if (element == null) { if (trace != null) { val actualDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor) - ?: error("Not a source declaration: $descriptor") + ?: error("Not a source declaration: $descriptor") trace.report(Errors.EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND.on(actualDeclaration)) } - return descriptor.valueParameters + return actualParameters } return expected.valueParameters @@ -218,6 +228,16 @@ object CodegenUtil { return descriptor.valueParameters } + + // This function is private here because no one is supposed to use it except for the hack above. + // Please use ValueParameterDescriptor.hasDefaultValue instead. + private fun ValueParameterDescriptor.declaresOrInheritsDefaultValue(): Boolean { + return DFS.ifAny( + listOf(this), + { current -> current.overriddenDescriptors.map(ValueParameterDescriptor::getOriginal) }, + { it.declaresDefaultValue() } + ) + } } fun DeclarationDescriptor.isTopLevelInPackage(name: String, packageName: String): Boolean { @@ -226,4 +246,4 @@ fun DeclarationDescriptor.isTopLevelInPackage(name: String, packageName: String) val containingDeclaration = containingDeclaration as? PackageFragmentDescriptor ?: return false val packageFqName = containingDeclaration.fqName.asString() return packageName == packageFqName -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual.kt new file mode 100644 index 00000000000..8c83df656c0 --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual.kt @@ -0,0 +1,28 @@ +// !LANGUAGE: +MultiPlatformProjects +// IGNORE_BACKEND: JVM_IR, JS_IR +// FILE: common.kt + +public expect fun Array.copyInto( + destination: Array, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = size +): Array + +// FILE: platform.kt + +// This test should be updated once KT-22818 is fixed; default values are not allowed in the actual function +@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") +public actual fun Array.copyInto( + destination: Array, destinationOffset: Int = 42, startIndex: Int = 43, endIndex: Int = size + 44 +): Array { + destination as Array + destination[0] = destinationOffset + destination[1] = startIndex + destination[2] = endIndex + return destination +} + +fun box(): String { + val a = Array(3) { it } + val result = a.copyInto(a) + return if (result[0] == 42 && result[1] == 43 && result[2] == 47) "OK" + else "Fail: ${result[0]} ${result[1]} ${result[2]}" +} diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual2.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual2.kt new file mode 100644 index 00000000000..55afacd88b5 --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual2.kt @@ -0,0 +1,30 @@ +// !LANGUAGE: +MultiPlatformProjects +// IGNORE_BACKEND: JVM_IR, JS_IR +// FILE: common.kt + +expect interface I { + fun test(source: String = "expect") +} + +expect interface J : I + +// FILE: platform.kt + +actual interface I { + // This test should be updated once KT-22818 is fixed; default values are not allowed in the actual function + @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") + actual fun test(source: String = "actual") +} + +actual interface J : I { + override fun test(source: String) { + if (source != "actual") throw AssertionError(source) + } +} + +class K : J + +fun box(): String { + K().test() + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index b3c6f0f1029..4084bd2d48b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -14455,6 +14455,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/annotations.kt"); } + @TestMetadata("bothInExpectAndActual.kt") + public void testBothInExpectAndActual() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual.kt"); + } + + @TestMetadata("bothInExpectAndActual2.kt") + public void testBothInExpectAndActual2() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual2.kt"); + } + @TestMetadata("constructor.kt") public void testConstructor() throws Exception { runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/constructor.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 426298401d0..4cd86b59388 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -14465,6 +14465,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/annotations.kt"); } + @TestMetadata("bothInExpectAndActual.kt") + public void testBothInExpectAndActual() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual.kt"); + } + + @TestMetadata("bothInExpectAndActual2.kt") + public void testBothInExpectAndActual2() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual2.kt"); + } + @TestMetadata("constructor.kt") public void testConstructor() throws Exception { runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/constructor.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 3920f672348..d58420169b5 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -14460,6 +14460,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/annotations.kt"); } + @TestMetadata("bothInExpectAndActual.kt") + public void testBothInExpectAndActual() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual.kt"); + } + + @TestMetadata("bothInExpectAndActual2.kt") + public void testBothInExpectAndActual2() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual2.kt"); + } + @TestMetadata("constructor.kt") public void testConstructor() throws Exception { runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/constructor.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java index 1fcbf5a7446..154484f23fa 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java @@ -12640,6 +12640,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/defaultArguments"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS_IR, true); } + @TestMetadata("bothInExpectAndActual.kt") + public void testBothInExpectAndActual() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual.kt"); + } + + @TestMetadata("bothInExpectAndActual2.kt") + public void testBothInExpectAndActual2() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual2.kt"); + } + @TestMetadata("constructor.kt") public void testConstructor() throws Exception { runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/constructor.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 75d2ec266a1..b14f299b864 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -13700,6 +13700,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/defaultArguments"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("bothInExpectAndActual.kt") + public void testBothInExpectAndActual() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual.kt"); + } + + @TestMetadata("bothInExpectAndActual2.kt") + public void testBothInExpectAndActual2() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual2.kt"); + } + @TestMetadata("constructor.kt") public void testConstructor() throws Exception { runTest("compiler/testData/codegen/box/multiplatform/defaultArguments/constructor.kt");