Use non-allowed actual defaults in backends to workaround compiler exception
It's difficult to fix KT-22818 until the IR comes along, so we're providing a workaround where one can disable the ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS error and provide default values in the actual function, to avoid exception from the backend. #KT-22818
This commit is contained in:
@@ -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<ValueParameterDescriptor> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// !LANGUAGE: +MultiPlatformProjects
|
||||
// IGNORE_BACKEND: JVM_IR, JS_IR
|
||||
// FILE: common.kt
|
||||
|
||||
public expect fun <T> Array<out T>.copyInto(
|
||||
destination: Array<T>, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = size
|
||||
): Array<T>
|
||||
|
||||
// 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 <T> Array<out T>.copyInto(
|
||||
destination: Array<T>, destinationOffset: Int = 42, startIndex: Int = 43, endIndex: Int = size + 44
|
||||
): Array<T> {
|
||||
destination as Array<Int>
|
||||
destination[0] = destinationOffset
|
||||
destination[1] = startIndex
|
||||
destination[2] = endIndex
|
||||
return destination
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val a = Array<Int>(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]}"
|
||||
}
|
||||
+30
@@ -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"
|
||||
}
|
||||
+10
@@ -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");
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user