JVM_IR KT-47939 equality for fun interface constructor references

This commit is contained in:
Dmitry Petrov
2021-11-30 15:08:33 +03:00
committed by TeamCityServer
parent 50b0dae786
commit e179598457
15 changed files with 226 additions and 13 deletions
@@ -3585,11 +3585,23 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/funInterfaceConstructor"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("funInterfaceConstructedObjectsEquality.kt")
public void testFunInterfaceConstructedObjectsEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructedObjectsEquality.kt");
}
@Test
@TestMetadata("funInterfaceConstructor.kt")
public void testFunInterfaceConstructor() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructor.kt");
}
@Test
@TestMetadata("funInterfaceConstructorEquality.kt")
public void testFunInterfaceConstructorEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructorEquality.kt");
}
}
@Nested
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.*
@@ -571,7 +572,16 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
}
private val adaptedReferenceOriginalTarget: IrFunction? = adapteeCall?.symbol?.owner
private val isAdaptedReference = adaptedReferenceOriginalTarget != null
private val isAdaptedFunInterfaceConstructorReference =
callee.origin == IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR
private val constructedFunInterfaceSymbol: IrClassSymbol? =
if (isAdaptedFunInterfaceConstructorReference)
callee.returnType.classOrNull
?: throw AssertionError("Fun interface type expected: ${callee.returnType.render()}")
else
null
private val isAdaptedReference =
isAdaptedFunInterfaceConstructorReference || adaptedReferenceOriginalTarget != null
private val samInterface = samSuperType?.getClass()
private val isKotlinFunInterface = samInterface != null && !samInterface.isFromJava()
@@ -580,14 +590,15 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
isKotlinFunInterface && (isAdaptedReference || !isLambda)
private val superType =
samSuperType ?: when {
isLambda -> context.ir.symbols.lambdaClass
useOptimizedSuperClass -> when {
isAdaptedReference -> context.ir.symbols.adaptedFunctionReference
else -> context.ir.symbols.functionReferenceImpl
}
else -> context.ir.symbols.functionReference
}.defaultType
samSuperType
?: when {
isLambda -> context.ir.symbols.lambdaClass
useOptimizedSuperClass -> when {
isAdaptedReference -> context.ir.symbols.adaptedFunctionReference
else -> context.ir.symbols.functionReferenceImpl
}
else -> context.ir.symbols.functionReference
}.defaultType
private val functionReferenceClass = context.irFactory.buildClass {
setSourceRange(irFunctionReference)
@@ -770,7 +781,17 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
if (boundReceiver != null) {
call.putValueArgument(index++, generateBoundReceiver())
}
if (!isLambda && useOptimizedSuperClass) {
if (isAdaptedFunInterfaceConstructorReference) {
val owner = kClassReference(constructedFunInterfaceSymbol!!.owner.defaultType)
// owner: <FUN_INTERFACE_TYPE>
call.putValueArgument(index++, kClassToJavaClass(owner))
// name: ""
call.putValueArgument(index++, irString(""))
// signature: ""
call.putValueArgument(index++, irString(""))
// flags: 8 = 4 << 1
call.putValueArgument(index, irInt(8))
} else if (!isLambda && useOptimizedSuperClass) {
val callableReferenceTarget = adaptedReferenceOriginalTarget ?: callee
val owner = calculateOwnerKClass(callableReferenceTarget.parent)
call.putValueArgument(index++, kClassToJavaClass(owner))
@@ -824,7 +845,12 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
IrDeclarationOrigin.INSTANCE_RECEIVER,
functionReferenceClass.symbol.defaultType
)
if (isLambda) createLambdaInvokeMethod() else createFunctionReferenceInvokeMethod(receiverVar)
if (isLambda)
createLambdaInvokeMethod()
else if (isAdaptedFunInterfaceConstructorReference)
createFunInterfaceConstructorInvokeMethod()
else
createFunctionReferenceInvokeMethod(receiverVar)
}
// Inline the body of an anonymous function into the generated lambda subclass.
@@ -835,6 +861,17 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
}
valueParameters += valueParameterMap.values
body = callee.moveBodyTo(this, valueParameterMap)
}
private fun IrSimpleFunction.createFunInterfaceConstructorInvokeMethod() {
val adapterValueParameter = callee.valueParameters.singleOrNull()
?: throw AssertionError("Single value parameter expected: ${callee.render()}")
val invokeValueParameter = adapterValueParameter.copyTo(this, index = 0)
val valueParameterMap = mapOf(adapterValueParameter to invokeValueParameter)
valueParameters = listOf(invokeValueParameter)
body = callee.moveBodyTo(this, valueParameterMap)
callee.body = null
}
private fun IrSimpleFunction.createFunctionReferenceInvokeMethod(receiver: IrValueDeclaration?) {
@@ -223,6 +223,7 @@ private fun IrSimpleFunction.createMultifileDelegateIfNeeded(
origin == JvmLoweredDeclarationOrigin.INLINE_LAMBDA ||
origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA ||
origin == IrDeclarationOrigin.PROPERTY_DELEGATE ||
origin == IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR ||
// $annotations methods in the facade are only needed for const properties.
(origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_OR_TYPEALIAS_ANNOTATIONS &&
(metadata as? MetadataSource.Property)?.isConst != true)
@@ -145,7 +145,10 @@ class ClassCodegen private constructor(
val smap = context.getSourceMapper(irClass)
// 1. Any method other than `<clinit>` can add a field and a `<clinit>` statement:
for (method in irClass.declarations.filterIsInstance<IrFunction>()) {
if (method.name.asString() != "<clinit>" && method.origin != JvmLoweredDeclarationOrigin.INLINE_LAMBDA) {
if (method.name.asString() != "<clinit>" &&
method.origin != JvmLoweredDeclarationOrigin.INLINE_LAMBDA &&
method.origin != IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR
) {
generateMethod(method, smap)
}
}
@@ -0,0 +1,43 @@
// !LANGUAGE: +AllowKotlinFunInterfaceConstructorReference
// IGNORE_BACKEND: JVM
// ^ unsupported in old JVM BE
// IGNORE_BACKEND: WASM
// ^ wasm-function[1893]:0x1cf8a: RuntimeError: dereferencing a null pointer
// IGNORE_BACKEND: JS_IR
// ^ TypeError: tmp is not a function
// FILE: funInterfaceConstructorEquality.kt
val ks1: (() -> String) -> KSupplier<String> =
::KSupplier
val ks11Foo = ks1(::foo)
val ks21Foo = ks2(::foo)
fun box(): String {
if (ks11Foo != ks12Foo)
return "failed: ks11Foo != ks12Foo (same ctor, different source files)"
if (ks11Foo != ks21Foo)
return "failed: ks11Foo != ks21Foo (different ctors, same source file)"
if (ks11Foo != ks22Foo)
return "failed: ks11Foo != ks22Foo (different ctors, different source files)"
return "OK"
}
// FILE: KSupplier.kt
fun interface KSupplier<T> {
fun get(): T
}
fun foo() = "abc"
val ks2: (() -> String) -> KSupplier<String> =
::KSupplier
val ks12Foo = ks1(::foo)
val ks22Foo = ks2(::foo)
@@ -1,6 +1,7 @@
// !LANGUAGE: +AllowKotlinFunInterfaceConstructorReference
// IGNORE_BACKEND: JVM
// ^ feature supported in IR-based backends only
// ^ unsupported in old JVM BE
fun interface KSupplier<T> {
fun get(): T
@@ -0,0 +1,41 @@
// !LANGUAGE: +AllowKotlinFunInterfaceConstructorReference
// DONT_TARGET_EXACT_BACKEND: JVM
// ^ old JVM BE generates bogus code that fails in 'invoke', but works almost as expected in terms of equality
// IGNORE_BACKEND: WASM
// ^ Failed: ks1 != ks2 (same file, same SAM type)
// IGNORE_BACKEND: JS_IR
// ^ Failed: ks1 != ks2 (same file, same SAM type)
// FILE: funInterfaceConstructorEquality.kt
val ks1: (() -> String) -> KSupplier<String> =
::KSupplier
val ks2: (() -> String) -> KSupplier<String> =
::KSupplier
val kn1: (() -> Number) -> KSupplier<Number> =
::KSupplier
fun box(): String {
if (ks1 != ks2)
return "Failed: ks1 != ks2 (same file, same SAM type)"
if (ks1 != ks3)
return "Failed: ks1 != ks3 (different file, same SAM type)"
if (ks1 != kn1)
return "Failed: ks1 != kn1 (same file, same SAM interface, different type arguments)"
return "OK"
}
// FILE: KSupplier.kt
fun interface KSupplier<T> {
fun get(): T
}
val ks3: (() -> String) -> KSupplier<String> =
::KSupplier
@@ -3507,6 +3507,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/funInterfaceConstructor"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@Test
@TestMetadata("funInterfaceConstructedObjectsEquality.kt")
public void testFunInterfaceConstructedObjectsEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructedObjectsEquality.kt");
}
@Test
@TestMetadata("funInterfaceConstructor.kt")
public void testFunInterfaceConstructor() throws Exception {
@@ -3585,11 +3585,23 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/funInterfaceConstructor"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("funInterfaceConstructedObjectsEquality.kt")
public void testFunInterfaceConstructedObjectsEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructedObjectsEquality.kt");
}
@Test
@TestMetadata("funInterfaceConstructor.kt")
public void testFunInterfaceConstructor() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructor.kt");
}
@Test
@TestMetadata("funInterfaceConstructorEquality.kt")
public void testFunInterfaceConstructorEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructorEquality.kt");
}
}
@Nested
@@ -3068,6 +3068,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class FunInterfaceConstructor extends AbstractLightAnalysisModeTest {
@TestMetadata("funInterfaceConstructedObjectsEquality.kt")
public void ignoreFunInterfaceConstructedObjectsEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructedObjectsEquality.kt");
}
@TestMetadata("funInterfaceConstructor.kt")
public void ignoreFunInterfaceConstructor() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructor.kt");
@@ -2445,11 +2445,23 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/funInterfaceConstructor"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}
@Test
@TestMetadata("funInterfaceConstructedObjectsEquality.kt")
public void testFunInterfaceConstructedObjectsEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructedObjectsEquality.kt");
}
@Test
@TestMetadata("funInterfaceConstructor.kt")
public void testFunInterfaceConstructor() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructor.kt");
}
@Test
@TestMetadata("funInterfaceConstructorEquality.kt")
public void testFunInterfaceConstructorEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructorEquality.kt");
}
}
@Nested
@@ -2487,11 +2487,23 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/funInterfaceConstructor"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
@Test
@TestMetadata("funInterfaceConstructedObjectsEquality.kt")
public void testFunInterfaceConstructedObjectsEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructedObjectsEquality.kt");
}
@Test
@TestMetadata("funInterfaceConstructor.kt")
public void testFunInterfaceConstructor() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructor.kt");
}
@Test
@TestMetadata("funInterfaceConstructorEquality.kt")
public void testFunInterfaceConstructorEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructorEquality.kt");
}
}
@Nested
@@ -2216,10 +2216,20 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/funInterfaceConstructor"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
@TestMetadata("funInterfaceConstructedObjectsEquality.kt")
public void testFunInterfaceConstructedObjectsEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructedObjectsEquality.kt");
}
@TestMetadata("funInterfaceConstructor.kt")
public void testFunInterfaceConstructor() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructor.kt");
}
@TestMetadata("funInterfaceConstructorEquality.kt")
public void testFunInterfaceConstructorEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructorEquality.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/callableReference/function")
@@ -31,6 +31,12 @@ public class FunctionReference extends CallableReference implements FunctionBase
* fun useSuspend(f: suspend () -> Unit) {}
* useSuspend(::target)
* </pre></ul>
* <ul>4 - whether it is a synthetic <code>fun interface</code> constructor, i.e., <pre>
* fun interface KRunnable {
* fun run()
* }
* val kr: (() -> Unit) -> KRunnable = ::KRunnable
* </pre></ul>
* </li>
*/
@SinceKotlin(version = "1.4")
@@ -2517,11 +2517,23 @@ public class NativeExtBlackBoxTestGenerated extends AbstractNativeBlackBoxTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/funInterfaceConstructor"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("funInterfaceConstructedObjectsEquality.kt")
public void testFunInterfaceConstructedObjectsEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructedObjectsEquality.kt");
}
@Test
@TestMetadata("funInterfaceConstructor.kt")
public void testFunInterfaceConstructor() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructor.kt");
}
@Test
@TestMetadata("funInterfaceConstructorEquality.kt")
public void testFunInterfaceConstructorEquality() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/funInterfaceConstructor/funInterfaceConstructorEquality.kt");
}
}
@Nested