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:
Alexander Udalov
2018-09-18 18:07:19 +03:00
parent 18b53f331a
commit 3ca81b95c2
8 changed files with 131 additions and 3 deletions
@@ -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
}
}
@@ -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]}"
}
@@ -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"
}
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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");