From 3269a7e6934bda6d15fff2fe5a305e1beefadb87 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 16 Mar 2020 22:26:46 +0100 Subject: [PATCH] Change behavior of equals/hashCode on adapted function references Function references are now equal if they refer to the same function, and if the parameter/return type adaptation, which happens when a reference is used where some function type is expected, is exactly the same. This includes the number of expected positional parameters (which can be affected by defaults/varargs), whether the coercion of vararg parameter to Array type happened, and whether the coercion of return type to Unit happened. #KT-37543 Fixed --- .../kotlin/codegen/ClosureCodegen.java | 40 +++++++++- .../kotlin/codegen/ExpressionCodegen.java | 11 +-- .../ir/FirBlackBoxCodegenTestGenerated.java | 73 +++++++++++++++++++ .../equality/capturedDefaults.kt | 64 ++++++++++++++++ .../equality/capturedVararg.kt | 66 +++++++++++++++++ .../equality/coercionToUnit.kt | 56 ++++++++++++++ .../equality/coercionToUnitWithDefaults.kt | 34 +++++++++ .../equality/coercionToUnitWithVararg.kt | 31 ++++++++ .../equality/extensionReceiverVsDefault.kt | 20 +++++ ...rcionToUnitIfFunctionAlreadyReturnsUnit.kt | 21 ++++++ .../equality/simpleEquality.kt | 47 ++++++++++++ .../varargAsArrayMemberOrExtension.kt | 29 ++++++++ .../equality/varargAsArrayWithDefaults.kt | 21 ++++++ .../equality/varargWithDefaults.kt | 22 ++++++ ...bleReferencesNotEqualToCallablesFromAPI.kt | 33 +++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 73 +++++++++++++++++++ .../LightAnalysisModeTestGenerated.java | 73 +++++++++++++++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 73 +++++++++++++++++++ .../IrJsCodegenBoxTestGenerated.java | 68 +++++++++++++++++ .../semantics/JsCodegenBoxTestGenerated.java | 68 +++++++++++++++++ .../jvm/internal/CallableReference.java | 10 +-- .../jvm/internal/FunctionReference.java | 27 ++++++- .../jvm/internal/PropertyReference.java | 2 +- .../kotlin-stdlib-runtime-merged.txt | 2 +- 24 files changed, 942 insertions(+), 22 deletions(-) create mode 100644 compiler/testData/codegen/box/callableReference/equality/capturedDefaults.kt create mode 100644 compiler/testData/codegen/box/callableReference/equality/capturedVararg.kt create mode 100644 compiler/testData/codegen/box/callableReference/equality/coercionToUnit.kt create mode 100644 compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithDefaults.kt create mode 100644 compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithVararg.kt create mode 100644 compiler/testData/codegen/box/callableReference/equality/extensionReceiverVsDefault.kt create mode 100644 compiler/testData/codegen/box/callableReference/equality/noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt create mode 100644 compiler/testData/codegen/box/callableReference/equality/simpleEquality.kt create mode 100644 compiler/testData/codegen/box/callableReference/equality/varargAsArrayMemberOrExtension.kt create mode 100644 compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt create mode 100644 compiler/testData/codegen/box/callableReference/equality/varargWithDefaults.kt create mode 100644 compiler/testData/codegen/box/reflection/methodsFromAny/adaptedCallableReferencesNotEqualToCallablesFromAPI.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java index 70cb23deb1c..263425d0c29 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java @@ -12,6 +12,7 @@ import kotlin.Unit; import kotlin.collections.CollectionsKt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.codegen.binding.CalculatedClosure; import org.jetbrains.kotlin.codegen.binding.CodegenBinding; import org.jetbrains.kotlin.codegen.context.ClosureContext; @@ -32,9 +33,11 @@ import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader; import org.jetbrains.kotlin.metadata.ProtoBuf; import org.jetbrains.kotlin.psi.KtElement; import org.jetbrains.kotlin.resolve.DescriptorUtils; +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt; import org.jetbrains.kotlin.resolve.scopes.MemberScope; +import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver; import org.jetbrains.kotlin.serialization.DescriptorSerializer; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.SimpleType; @@ -64,6 +67,7 @@ public class ClosureCodegen extends MemberCodegen { private final SamType samType; private final KotlinType superClassType; private final List superInterfaceTypes; + private final ResolvedCall functionReferenceCall; private final FunctionDescriptor functionReferenceTarget; private final FunctionGenerationStrategy strategy; protected final CalculatedClosure closure; @@ -80,7 +84,7 @@ public class ClosureCodegen extends MemberCodegen { @NotNull KtElement element, @Nullable SamType samType, @NotNull ClosureContext context, - @Nullable FunctionDescriptor functionReferenceTarget, + @Nullable ResolvedCall functionReferenceCall, @NotNull FunctionGenerationStrategy strategy, @NotNull MemberCodegen parentCodegen, @NotNull ClassBuilder classBuilder @@ -90,7 +94,8 @@ public class ClosureCodegen extends MemberCodegen { this.funDescriptor = context.getFunctionDescriptor(); this.classDescriptor = context.getContextDescriptor(); this.samType = samType; - this.functionReferenceTarget = functionReferenceTarget; + this.functionReferenceCall = functionReferenceCall; + this.functionReferenceTarget = functionReferenceCall != null ? functionReferenceCall.getResultingDescriptor() : null; this.strategy = strategy; if (samType == null) { @@ -510,7 +515,10 @@ public class ClosureCodegen extends MemberCodegen { generateCallableReferenceDeclarationContainerClass(iv, functionReferenceTarget, state); iv.aconst(functionReferenceTarget.getName().asString()); PropertyReferenceCodegen.generateCallableReferenceSignature(iv, functionReferenceTarget, state); - iv.aconst(isTopLevelCallableReference(functionReferenceTarget) ? 1 : 0); + int flags = + (isTopLevelCallableReference(functionReferenceTarget) ? 1 : 0) + + (calculateFunctionReferenceFlags(functionReferenceCall, funDescriptor) << 1); + iv.aconst(flags); superCtorArgTypes.add(JAVA_CLASS_TYPE); superCtorArgTypes.add(JAVA_STRING_TYPE); superCtorArgTypes.add(JAVA_STRING_TYPE); @@ -532,6 +540,32 @@ public class ClosureCodegen extends MemberCodegen { return constructor; } + private static int calculateFunctionReferenceFlags( + @NotNull ResolvedCall call, @NotNull FunctionDescriptor anonymousAdaptedFunction + ) { + boolean hasVarargMappedToElement = false; + CallableDescriptor target = call.getResultingDescriptor(); + int shift = + (call.getDispatchReceiver() instanceof TransientReceiver ? 1 : 0) + + (call.getExtensionReceiver() instanceof TransientReceiver ? 1 : 0); + for (int i = shift; + i < anonymousAdaptedFunction.getValueParameters().size() && i - shift < target.getValueParameters().size(); + i++) { + KotlinType varargElementType = target.getValueParameters().get(i - shift).getVarargElementType(); + if (varargElementType != null && !varargElementType.equals(anonymousAdaptedFunction.getValueParameters().get(i).getType())) { + hasVarargMappedToElement = true; + break; + } + } + + //noinspection ConstantConditions + boolean hasCoercionToUnit = KotlinBuiltIns.isUnit(anonymousAdaptedFunction.getReturnType()) && + !KotlinBuiltIns.isUnit(target.getReturnType()); + + return (hasVarargMappedToElement ? 1 : 0) + + ((hasCoercionToUnit ? 1 : 0) << 1); + } + protected int calculateArity() { int arity = funDescriptor.getValueParameters().size(); if (funDescriptor.getExtensionReceiverParameter() != null) arity++; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 0307d4a8e7c..be0eaa7b98c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -1023,7 +1023,7 @@ public class ExpressionCodegen extends KtVisitor impleme @NotNull FunctionDescriptor descriptor, @NotNull FunctionGenerationStrategy strategy, @Nullable SamType samType, - @Nullable FunctionDescriptor functionReferenceTarget, + @Nullable ResolvedCall functionReferenceCall, @Nullable StackValue functionReferenceReceiver ) { ClassBuilder cv = state.getFactory().newVisitor( @@ -1038,8 +1038,7 @@ public class ExpressionCodegen extends KtVisitor impleme descriptor, this, state.getTypeMapper() ) : this.context.intoClosure(descriptor, this, typeMapper); ClosureCodegen closureCodegen = coroutineCodegen != null ? coroutineCodegen : new ClosureCodegen( - state, declaration, samType, closureContext, - functionReferenceTarget, strategy, parentCodegen, cv + state, declaration, samType, closureContext, functionReferenceCall, strategy, parentCodegen, cv ); closureCodegen.generate(); @@ -3261,6 +3260,7 @@ public class ExpressionCodegen extends KtVisitor impleme } @Override + @SuppressWarnings("unchecked") public StackValue visitCallableReferenceExpression(@NotNull KtCallableReferenceExpression expression, StackValue data) { ResolvedCall resolvedCall = CallUtilKt.getResolvedCallWithAssert(expression.getCallableReference(), bindingContext); @@ -3274,10 +3274,7 @@ public class ExpressionCodegen extends KtVisitor impleme null, false ); - return genClosure( - expression, functionDescriptor, strategy, null, - (FunctionDescriptor) resolvedCall.getResultingDescriptor(), receiver - ); + return genClosure(expression, functionDescriptor, strategy, null, (ResolvedCall) resolvedCall, receiver); } return generatePropertyReference( diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index e8e2cc5224f..5f678fb817d 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -2174,6 +2174,74 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT } } + @TestMetadata("compiler/testData/codegen/box/callableReference/equality") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Equality extends AbstractFirBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTestWithCustomIgnoreDirective(this::doTest, TargetBackend.JVM_IR, testDataFilePath, "// IGNORE_BACKEND_FIR: "); + } + + public void testAllFilesPresentInEquality() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @TestMetadata("capturedDefaults.kt") + public void testCapturedDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/capturedDefaults.kt"); + } + + @TestMetadata("capturedVararg.kt") + public void testCapturedVararg() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/capturedVararg.kt"); + } + + @TestMetadata("coercionToUnit.kt") + public void testCoercionToUnit() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnit.kt"); + } + + @TestMetadata("coercionToUnitWithDefaults.kt") + public void testCoercionToUnitWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithDefaults.kt"); + } + + @TestMetadata("coercionToUnitWithVararg.kt") + public void testCoercionToUnitWithVararg() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithVararg.kt"); + } + + @TestMetadata("extensionReceiverVsDefault.kt") + public void testExtensionReceiverVsDefault() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/extensionReceiverVsDefault.kt"); + } + + @TestMetadata("noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt") + public void testNoCoercionToUnitIfFunctionAlreadyReturnsUnit() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt"); + } + + @TestMetadata("simpleEquality.kt") + public void testSimpleEquality() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/simpleEquality.kt"); + } + + @TestMetadata("varargAsArrayMemberOrExtension.kt") + public void testVarargAsArrayMemberOrExtension() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargAsArrayMemberOrExtension.kt"); + } + + @TestMetadata("varargAsArrayWithDefaults.kt") + public void testVarargAsArrayWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt"); + } + + @TestMetadata("varargWithDefaults.kt") + public void testVarargWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargWithDefaults.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/callableReference/function") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -23706,6 +23774,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT KotlinTestUtils.runTestWithCustomIgnoreDirective(this::doTest, TargetBackend.JVM_IR, testDataFilePath, "// IGNORE_BACKEND_FIR: "); } + @TestMetadata("adaptedCallableReferencesNotEqualToCallablesFromAPI.kt") + public void testAdaptedCallableReferencesNotEqualToCallablesFromAPI() throws Exception { + runTest("compiler/testData/codegen/box/reflection/methodsFromAny/adaptedCallableReferencesNotEqualToCallablesFromAPI.kt"); + } + public void testAllFilesPresentInMethodsFromAny() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/methodsFromAny"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } diff --git a/compiler/testData/codegen/box/callableReference/equality/capturedDefaults.kt b/compiler/testData/codegen/box/callableReference/equality/capturedDefaults.kt new file mode 100644 index 00000000000..a5b5b2fa6ef --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/equality/capturedDefaults.kt @@ -0,0 +1,64 @@ +// IGNORE_BACKEND: JVM_IR, JS, JS_IR, NATIVE +// IGNORE_BACKEND_FIR: JVM_IR +// FILE: test.kt + +fun checkEqual(x: Any, y: Any) { + if (x != y || y != x) throw AssertionError("$x and $y should be equal") + if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code") +} + +fun checkNotEqual(x: Any, y: Any) { + if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal") +} + +class V { + fun target(x: String = "x", y: String = "y", z: String = "z"): String = x + y + z +} + +private fun captureNoDefaults(f: (V, String, String, String) -> String): Any = f +private fun captureOneDefault(f: (V, String, String) -> String): Any = f +private fun captureAllDefaults(f: (V) -> String): Any = f + +private fun captureNoDefaultsBound(f: (String, String, String) -> String): Any = f +private fun captureOneDefaultBound(f: (String, String) -> String): Any = f +private fun captureAllDefaultsBound(f: () -> String): Any = f + +fun box(): String { + val v0 = V() + + checkEqual(captureNoDefaults(V::target), captureNoDefaults(V::target)) + checkEqual(captureNoDefaults(V::target), captureNoDefaultsFromOtherFile()) + + checkEqual(captureOneDefault(V::target), captureOneDefault(V::target)) + checkEqual(captureOneDefault(V::target), captureOneDefaultFromOtherFile()) + + checkEqual(captureAllDefaults(V::target), captureAllDefaults(V::target)) + + checkEqual(captureNoDefaultsBound(v0::target), captureNoDefaultsBound(v0::target)) + checkEqual(captureNoDefaultsBound(v0::target), captureNoDefaultsBoundFromOtherFile(v0)) + + checkEqual(captureOneDefaultBound(v0::target), captureOneDefaultBound(v0::target)) + + checkEqual(captureAllDefaultsBound(v0::target), captureAllDefaultsBound(v0::target)) + + + checkNotEqual(captureNoDefaults(V::target), captureOneDefault(V::target)) + checkNotEqual(captureNoDefaults(V::target), captureAllDefaults(V::target)) + + checkNotEqual(captureNoDefaultsBound(v0::target), captureOneDefaultBound(v0::target)) + checkNotEqual(captureNoDefaultsBound(v0::target), captureAllDefaultsBound(v0::target)) + + checkNotEqual(captureNoDefaults(V::target), captureNoDefaultsBoundFromOtherFile(v0)) + + return "OK" +} + +// FILE: fromOtherFile.kt + +private fun captureNoDefaults(f: (V, String, String, String) -> String): Any = f +private fun captureOneDefault(f: (V, String, String) -> String): Any = f +private fun captureNoDefaultsBound(f: (String, String, String) -> String): Any = f + +fun captureNoDefaultsFromOtherFile(): Any = captureNoDefaults(V::target) +fun captureOneDefaultFromOtherFile(): Any = captureOneDefault(V::target) +fun captureNoDefaultsBoundFromOtherFile(v0: V) = captureNoDefaultsBound(v0::target) diff --git a/compiler/testData/codegen/box/callableReference/equality/capturedVararg.kt b/compiler/testData/codegen/box/callableReference/equality/capturedVararg.kt new file mode 100644 index 00000000000..d0e414d9df5 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/equality/capturedVararg.kt @@ -0,0 +1,66 @@ +// IGNORE_BACKEND: JVM_IR, JS, JS_IR, NATIVE +// IGNORE_BACKEND_FIR: JVM_IR +// FILE: test.kt + +fun checkEqual(x: Any, y: Any) { + if (x != y || y != x) throw AssertionError("$x and $y should be equal") + if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code") +} + +fun checkNotEqual(x: Any, y: Any) { + if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal") +} + +class V { + fun target(vararg x: String) {} +} + +private fun captureVararg2(f: (V, String, String) -> Unit): Any = f +private fun captureVararg1(f: (V, String) -> Unit): Any = f +private fun captureVararg0(f: (V) -> Unit): Any = f +private fun captureVarargAsArray(f: (V, Array) -> Unit): Any = f + +private fun captureVararg2Bound(f: (String, String) -> Unit): Any = f +private fun captureVararg1Bound(f: (String) -> Unit): Any = f +private fun captureVararg0Bound(f: () -> Unit): Any = f +private fun captureVarargAsArrayBound(f: (Array) -> Unit): Any = f + +fun box(): String { + val v0 = V() + + checkEqual(captureVararg2(V::target), captureVararg2(V::target)) + checkEqual(captureVararg1(V::target), captureVararg1(V::target)) + checkEqual(captureVararg0(V::target), captureVararg0(V::target)) + checkEqual(captureVararg0(V::target), captureVararg0FromOtherFile()) + checkEqual(captureVarargAsArray(V::target), captureVarargAsArrayFromOtherFile()) + + checkEqual(captureVararg2Bound(v0::target), captureVararg2Bound(v0::target)) + checkEqual(captureVararg1Bound(v0::target), captureVararg1Bound(v0::target)) + checkEqual(captureVararg0Bound(v0::target), captureVararg0Bound(v0::target)) + checkEqual(captureVararg0Bound(v0::target), captureVararg0BoundFromOtherFile(v0)) + checkEqual(captureVarargAsArrayBound(v0::target), captureVarargAsArrayBoundFromOtherFile(v0)) + + + checkNotEqual(captureVararg2(V::target), captureVararg0(V::target)) + checkNotEqual(captureVararg2Bound(v0::target), captureVararg0Bound(v0::target)) + + checkNotEqual(captureVararg2(V::target), captureVarargAsArray(V::target)) + checkNotEqual(captureVararg1(V::target), captureVarargAsArray(V::target)) + checkNotEqual(captureVararg0(V::target), captureVarargAsArray(V::target)) + checkNotEqual(captureVararg1Bound(v0::target), captureVarargAsArrayBound(v0::target)) + checkNotEqual(captureVararg1Bound(v0::target), captureVarargAsArrayBoundFromOtherFile(v0)) + + return "OK" +} + +// FILE: fromOtherFile.kt + +private fun captureVararg0(f: (V) -> Unit): Any = f +private fun captureVararg0Bound(f: () -> Unit): Any = f +private fun captureVarargAsArray(f: (V, Array) -> Unit): Any = f +private fun captureVarargAsArrayBound(f: (Array) -> Unit): Any = f + +fun captureVararg0FromOtherFile(): Any = captureVararg0(V::target) +fun captureVararg0BoundFromOtherFile(v0: V): Any = captureVararg0Bound(v0::target) +fun captureVarargAsArrayFromOtherFile(): Any = captureVarargAsArray(V::target) +fun captureVarargAsArrayBoundFromOtherFile(v0: V): Any = captureVarargAsArrayBound(v0::target) diff --git a/compiler/testData/codegen/box/callableReference/equality/coercionToUnit.kt b/compiler/testData/codegen/box/callableReference/equality/coercionToUnit.kt new file mode 100644 index 00000000000..001611fd48e --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/equality/coercionToUnit.kt @@ -0,0 +1,56 @@ +// IGNORE_BACKEND: JVM_IR, JS, JS_IR, NATIVE +// IGNORE_BACKEND_FIR: JVM_IR +// FILE: test.kt + +fun checkEqual(x: Any, y: Any) { + if (x != y || y != x) throw AssertionError("$x and $y should be equal") + if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code") +} + +fun checkNotEqual(x: Any, y: Any) { + if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal") +} + +class V { + fun target(): String = "" +} + +private fun captureString(f: (V) -> String): Any = f +private fun captureUnit(f: (V) -> Unit): Any = f + +private fun captureStringBound(f: () -> String): Any = f +private fun captureUnitBound(f: () -> Unit): Any = f + +fun box(): String { + val v0 = V() + + checkEqual(captureString(V::target), captureString(V::target)) + checkEqual(captureString(V::target), captureStringFromOtherFile()) + checkEqual(captureUnit(V::target), captureUnit(V::target)) + checkEqual(captureUnit(V::target), captureUnitFromOtherFile()) + + checkEqual(captureStringBound(v0::target), captureStringBound(v0::target)) + checkEqual(captureStringBound(v0::target), captureStringBoundFromOtherFile(v0)) + checkEqual(captureUnitBound(v0::target), captureUnitBound(v0::target)) + checkEqual(captureUnitBound(v0::target), captureUnitBoundFromOtherFile(v0)) + + + checkNotEqual(captureString(V::target), captureUnit(V::target)) + checkNotEqual(captureStringBound(v0::target), captureUnitBound(v0::target)) + checkNotEqual(captureString(V::target), captureUnitBoundFromOtherFile(v0)) + + return "OK" +} + +// FILE: fromOtherFile.kt + +private fun captureString(f: (V) -> String): Any = f +private fun captureUnit(f: (V) -> Unit): Any = f + +private fun captureStringBound(f: () -> String): Any = f +private fun captureUnitBound(f: () -> Unit): Any = f + +fun captureStringFromOtherFile(): Any = captureString(V::target) +fun captureUnitFromOtherFile(): Any = captureUnit(V::target) +fun captureStringBoundFromOtherFile(v0: V): Any = captureStringBound(v0::target) +fun captureUnitBoundFromOtherFile(v0: V): Any = captureUnitBound(v0::target) diff --git a/compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithDefaults.kt b/compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithDefaults.kt new file mode 100644 index 00000000000..3a6d6bb849e --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithDefaults.kt @@ -0,0 +1,34 @@ +// IGNORE_BACKEND: JVM_IR, JS, JS_IR, NATIVE +// IGNORE_BACKEND_FIR: JVM_IR +// FILE: test.kt + +fun checkEqual(x: Any, y: Any) { + if (x != y || y != x) throw AssertionError("$x and $y should be equal") + if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code") +} + +fun checkNotEqual(x: Any, y: Any) { + if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal") +} + +fun target(x: Int, y: String = "", z: String = ""): Int = x + +fun captureNoDefaults(fn: (Int, String, String) -> Unit): Any = fn +fun captureOneDefault(fn: (Int, String) -> Unit): Any = fn +fun captureAllDefaults(fn: (Int) -> Unit): Any = fn + +fun box(): String { + checkEqual(captureNoDefaults(::target), captureNoDefaults(::target)) + checkEqual(captureOneDefault(::target), captureOneDefault(::target)) + checkEqual(captureAllDefaults(::target), captureAllDefaults(::target)) + checkEqual(captureNoDefaults(::target), captureNoDefaultsFromOtherFile()) + + checkNotEqual(captureNoDefaults(::target), captureOneDefault(::target)) + checkNotEqual(captureNoDefaults(::target), captureAllDefaults(::target)) + + return "OK" +} + +// FILE: fromOtherFile.kt + +fun captureNoDefaultsFromOtherFile(): Any = captureNoDefaults(::target) diff --git a/compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithVararg.kt b/compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithVararg.kt new file mode 100644 index 00000000000..53c7b09e8d5 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithVararg.kt @@ -0,0 +1,31 @@ +// IGNORE_BACKEND: JVM_IR, JS, JS_IR, NATIVE +// IGNORE_BACKEND_FIR: JVM_IR +// FILE: test.kt + +fun checkEqual(x: Any, y: Any) { + if (x != y || y != x) throw AssertionError("$x and $y should be equal") + if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code") +} + +fun checkNotEqual(x: Any, y: Any) { + if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal") +} + +fun target(x: Int, vararg ys: String): Int = x + ys.size + +fun captureVararg1(fn: (Int, String) -> Unit): Any = fn +fun captureVararg0(fn: (Int) -> Unit): Any = fn + +fun box(): String { + checkEqual(captureVararg1(::target), captureVararg1(::target)) + checkEqual(captureVararg0(::target), captureVararg0(::target)) + checkEqual(captureVararg1(::target), captureVararg1FromOtherFile()) + + checkNotEqual(captureVararg1(::target), captureVararg0(::target)) + + return "OK" +} + +// FILE: fromOtherFile.kt + +fun captureVararg1FromOtherFile(): Any = captureVararg1(::target) diff --git a/compiler/testData/codegen/box/callableReference/equality/extensionReceiverVsDefault.kt b/compiler/testData/codegen/box/callableReference/equality/extensionReceiverVsDefault.kt new file mode 100644 index 00000000000..134b926958c --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/equality/extensionReceiverVsDefault.kt @@ -0,0 +1,20 @@ +// FILE: test.kt + +fun checkNotEqual(x: Any, y: Any) { + if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal") +} + +fun target(s: String = "") {} + +fun capture1(fn: String.() -> Unit): Any = fn +fun capture2(fn: () -> Unit): Any = fn + +fun box(): String { + checkNotEqual(capture1(::target), capture2(::target)) + checkNotEqual(capture1(::target), captureFromOtherFile()) + return "OK" +} + +// FILE: fromOtherFile.kt + +fun captureFromOtherFile(): Any = capture2(::target) diff --git a/compiler/testData/codegen/box/callableReference/equality/noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt b/compiler/testData/codegen/box/callableReference/equality/noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt new file mode 100644 index 00000000000..91260d93460 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/equality/noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt @@ -0,0 +1,21 @@ +// IGNORE_BACKEND: JS, JS_IR, NATIVE +// FILE: test.kt + +fun checkEqual(x: Any, y: Any) { + if (x != y || y != x) throw AssertionError("$x and $y should be equal") + if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code") +} + +fun target(x: Int) {} + +fun use(fn: (Int) -> Unit): Any = fn + +fun box(): String { + checkEqual(use(::target), ::target) + checkEqual(useFromOtherFile(), ::target) + return "OK" +} + +// FILE: fromOtherFile.kt + +fun useFromOtherFile(): Any = use(::target) diff --git a/compiler/testData/codegen/box/callableReference/equality/simpleEquality.kt b/compiler/testData/codegen/box/callableReference/equality/simpleEquality.kt new file mode 100644 index 00000000000..17862d8ec72 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/equality/simpleEquality.kt @@ -0,0 +1,47 @@ +// IGNORE_BACKEND: JS, JS_IR, NATIVE +// IGNORE_BACKEND_FIR: JVM_IR +// FILE: test.kt + +fun checkEqual(x: Any, y: Any) { + if (x != y || y != x) throw AssertionError("$x and $y should be equal") + if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code") +} + +fun checkNotEqual(x: Any, y: Any) { + if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal") +} + +class V { + val memberVal: String = "" + fun memberFun(): String = "" +} + +val topLevelVar: String = "" +fun topLevelFun(): String = "" + +fun box(): String { + val v0 = V() + val v1 = V() + + checkEqual(::topLevelFun, ::topLevelFun) + checkEqual(::topLevelFun, referenceTopLevelFunFromOtherFile()) + checkEqual(::topLevelVar, ::topLevelVar) + checkEqual(::topLevelVar, referenceTopLevelVarFromOtherFile()) + + checkEqual(V::memberFun, V::memberFun) + checkEqual(v0::memberFun, v0::memberFun) + checkEqual(V::memberVal, V::memberVal) + checkEqual(v0::memberVal, v0::memberVal) + + checkNotEqual(v0::memberFun, V::memberFun) + checkNotEqual(v0::memberVal, V::memberVal) + checkNotEqual(v0::memberFun, v1::memberFun) + checkNotEqual(v0::memberVal, v1::memberVal) + + return "OK" +} + +// FILE: fromOtherFile.kt + +fun referenceTopLevelFunFromOtherFile() = ::topLevelFun +fun referenceTopLevelVarFromOtherFile() = ::topLevelVar diff --git a/compiler/testData/codegen/box/callableReference/equality/varargAsArrayMemberOrExtension.kt b/compiler/testData/codegen/box/callableReference/equality/varargAsArrayMemberOrExtension.kt new file mode 100644 index 00000000000..3beae0215d2 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/equality/varargAsArrayMemberOrExtension.kt @@ -0,0 +1,29 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// FILE: test.kt + +fun checkNotEqual(x: Any, y: Any) { + if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal") +} + +class C { + fun member(vararg xs: String) {} +} + +fun C.extension(vararg xs: String) {} + +fun capture1(fn: C.(String) -> Unit): Any = fn +fun capture2(fn: C.(Array) -> Unit): Any = fn + +fun box(): String { + checkNotEqual(capture1(C::member), capture2(C::member)) + checkNotEqual(capture1(C::member), captureMemberFromOtherFile()) + + checkNotEqual(capture1(C::extension), capture2(C::extension)) + checkNotEqual(capture1(C::extension), captureExtensionFromOtherFile()) + return "OK" +} + +// FILE: fromOtherFile.kt + +fun captureMemberFromOtherFile(): Any = capture2(C::member) +fun captureExtensionFromOtherFile(): Any = capture2(C::extension) diff --git a/compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt b/compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt new file mode 100644 index 00000000000..bec2e0d7716 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt @@ -0,0 +1,21 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// FILE: test.kt + +fun checkNotEqual(x: Any, y: Any) { + if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal") +} + +fun target(s1: String, vararg xs: Int, s2: String = "") {} + +fun capture1(fn: (String, IntArray, String) -> Unit): Any = fn +fun capture2(fn: (String, Int, Int) -> Unit): Any = fn + +fun box(): String { + checkNotEqual(capture1(::target), capture2(::target)) + checkNotEqual(capture1(::target), captureFromOtherFile()) + return "OK" +} + +// FILE: fromOtherFile.kt + +fun captureFromOtherFile(): Any = capture2(::target) diff --git a/compiler/testData/codegen/box/callableReference/equality/varargWithDefaults.kt b/compiler/testData/codegen/box/callableReference/equality/varargWithDefaults.kt new file mode 100644 index 00000000000..ba092c5778e --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/equality/varargWithDefaults.kt @@ -0,0 +1,22 @@ +// IGNORE_BACKEND: JVM_IR, JS, JS_IR, NATIVE +// IGNORE_BACKEND_FIR: JVM_IR +// FILE: test.kt + +fun checkEqual(x: Any, y: Any) { + if (x != y || y != x) throw AssertionError("$x and $y should be equal") + if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code") +} + +fun target(x: Int = 0, vararg ys: String) {} + +fun captureAll1(fn: () -> Unit): Any = fn +fun captureAll2(fn: () -> Unit): Any = fn + +fun box(): String { + checkEqual(captureAll1(::target), captureAll2(::target)) + return "OK" +} + +// FILE: fromOtherFile.kt + +fun captureAllFromOtherFile(): Any = captureAll1(::target) diff --git a/compiler/testData/codegen/box/reflection/methodsFromAny/adaptedCallableReferencesNotEqualToCallablesFromAPI.kt b/compiler/testData/codegen/box/reflection/methodsFromAny/adaptedCallableReferencesNotEqualToCallablesFromAPI.kt new file mode 100644 index 00000000000..dba0dc5cb15 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/methodsFromAny/adaptedCallableReferencesNotEqualToCallablesFromAPI.kt @@ -0,0 +1,33 @@ +// TARGET_BACKEND: JVM +// Temporarily ignored for JVM until KT-36024 is fixed. +// IGNORE_BACKEND: JVM +// IGNORE_BACKEND_FIR: JVM_IR +// WITH_REFLECT + +import kotlin.reflect.* +import kotlin.test.assertNotEquals + +class A { + fun foo(s: String = "", vararg xs: Long): String = "foo" +} + +fun checkNotEqual(x: Any, y: Any) { + assertNotEquals(x, y) + assertNotEquals(y, x) +} + +fun coercionToUnit(f: (A, String, LongArray) -> Unit): Any = f +fun varargToElement(f: (A, String, Long, Long) -> String): Any = f +fun defaultAndVararg(f: (A) -> String): Any = f +fun allOfTheAbove(f: (A) -> Unit): Any = f + +fun box(): String { + val foo = A::class.members.single { it.name == "foo" } + + checkNotEqual(coercionToUnit(A::foo), foo) + checkNotEqual(varargToElement(A::foo), foo) + checkNotEqual(defaultAndVararg(A::foo), foo) + checkNotEqual(allOfTheAbove(A::foo), foo) + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index f031d8a9ec4..29fa39ad775 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -2194,6 +2194,74 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/callableReference/equality") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Equality extends AbstractBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInEquality() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @TestMetadata("capturedDefaults.kt") + public void testCapturedDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/capturedDefaults.kt"); + } + + @TestMetadata("capturedVararg.kt") + public void testCapturedVararg() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/capturedVararg.kt"); + } + + @TestMetadata("coercionToUnit.kt") + public void testCoercionToUnit() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnit.kt"); + } + + @TestMetadata("coercionToUnitWithDefaults.kt") + public void testCoercionToUnitWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithDefaults.kt"); + } + + @TestMetadata("coercionToUnitWithVararg.kt") + public void testCoercionToUnitWithVararg() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithVararg.kt"); + } + + @TestMetadata("extensionReceiverVsDefault.kt") + public void testExtensionReceiverVsDefault() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/extensionReceiverVsDefault.kt"); + } + + @TestMetadata("noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt") + public void testNoCoercionToUnitIfFunctionAlreadyReturnsUnit() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt"); + } + + @TestMetadata("simpleEquality.kt") + public void testSimpleEquality() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/simpleEquality.kt"); + } + + @TestMetadata("varargAsArrayMemberOrExtension.kt") + public void testVarargAsArrayMemberOrExtension() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargAsArrayMemberOrExtension.kt"); + } + + @TestMetadata("varargAsArrayWithDefaults.kt") + public void testVarargAsArrayWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt"); + } + + @TestMetadata("varargWithDefaults.kt") + public void testVarargWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargWithDefaults.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/callableReference/function") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -25267,6 +25335,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); } + @TestMetadata("adaptedCallableReferencesNotEqualToCallablesFromAPI.kt") + public void testAdaptedCallableReferencesNotEqualToCallablesFromAPI() throws Exception { + runTest("compiler/testData/codegen/box/reflection/methodsFromAny/adaptedCallableReferencesNotEqualToCallablesFromAPI.kt"); + } + public void testAllFilesPresentInMethodsFromAny() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/methodsFromAny"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index da624ed0516..f523893fa34 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -2194,6 +2194,74 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes } } + @TestMetadata("compiler/testData/codegen/box/callableReference/equality") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Equality extends AbstractLightAnalysisModeTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInEquality() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @TestMetadata("capturedDefaults.kt") + public void testCapturedDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/capturedDefaults.kt"); + } + + @TestMetadata("capturedVararg.kt") + public void testCapturedVararg() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/capturedVararg.kt"); + } + + @TestMetadata("coercionToUnit.kt") + public void testCoercionToUnit() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnit.kt"); + } + + @TestMetadata("coercionToUnitWithDefaults.kt") + public void testCoercionToUnitWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithDefaults.kt"); + } + + @TestMetadata("coercionToUnitWithVararg.kt") + public void testCoercionToUnitWithVararg() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithVararg.kt"); + } + + @TestMetadata("extensionReceiverVsDefault.kt") + public void testExtensionReceiverVsDefault() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/extensionReceiverVsDefault.kt"); + } + + @TestMetadata("noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt") + public void testNoCoercionToUnitIfFunctionAlreadyReturnsUnit() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt"); + } + + @TestMetadata("simpleEquality.kt") + public void testSimpleEquality() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/simpleEquality.kt"); + } + + @TestMetadata("varargAsArrayMemberOrExtension.kt") + public void testVarargAsArrayMemberOrExtension() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargAsArrayMemberOrExtension.kt"); + } + + @TestMetadata("varargAsArrayWithDefaults.kt") + public void testVarargAsArrayWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt"); + } + + @TestMetadata("varargWithDefaults.kt") + public void testVarargWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargWithDefaults.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/callableReference/function") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -24080,6 +24148,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class MethodsFromAny extends AbstractLightAnalysisModeTest { + @TestMetadata("adaptedCallableReferencesNotEqualToCallablesFromAPI.kt") + public void ignoreAdaptedCallableReferencesNotEqualToCallablesFromAPI() throws Exception { + runTest("compiler/testData/codegen/box/reflection/methodsFromAny/adaptedCallableReferencesNotEqualToCallablesFromAPI.kt"); + } + private void runTest(String testDataFilePath) throws Exception { KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 29379a2f6ac..6da1d889624 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -2174,6 +2174,74 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } } + @TestMetadata("compiler/testData/codegen/box/callableReference/equality") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Equality extends AbstractIrBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInEquality() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @TestMetadata("capturedDefaults.kt") + public void testCapturedDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/capturedDefaults.kt"); + } + + @TestMetadata("capturedVararg.kt") + public void testCapturedVararg() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/capturedVararg.kt"); + } + + @TestMetadata("coercionToUnit.kt") + public void testCoercionToUnit() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnit.kt"); + } + + @TestMetadata("coercionToUnitWithDefaults.kt") + public void testCoercionToUnitWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithDefaults.kt"); + } + + @TestMetadata("coercionToUnitWithVararg.kt") + public void testCoercionToUnitWithVararg() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithVararg.kt"); + } + + @TestMetadata("extensionReceiverVsDefault.kt") + public void testExtensionReceiverVsDefault() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/extensionReceiverVsDefault.kt"); + } + + @TestMetadata("noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt") + public void testNoCoercionToUnitIfFunctionAlreadyReturnsUnit() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt"); + } + + @TestMetadata("simpleEquality.kt") + public void testSimpleEquality() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/simpleEquality.kt"); + } + + @TestMetadata("varargAsArrayMemberOrExtension.kt") + public void testVarargAsArrayMemberOrExtension() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargAsArrayMemberOrExtension.kt"); + } + + @TestMetadata("varargAsArrayWithDefaults.kt") + public void testVarargAsArrayWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt"); + } + + @TestMetadata("varargWithDefaults.kt") + public void testVarargWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargWithDefaults.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/callableReference/function") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -23706,6 +23774,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); } + @TestMetadata("adaptedCallableReferencesNotEqualToCallablesFromAPI.kt") + public void testAdaptedCallableReferencesNotEqualToCallablesFromAPI() throws Exception { + runTest("compiler/testData/codegen/box/reflection/methodsFromAny/adaptedCallableReferencesNotEqualToCallablesFromAPI.kt"); + } + public void testAllFilesPresentInMethodsFromAny() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/methodsFromAny"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 9201c3af6cb..88c809cca2e 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -1589,6 +1589,74 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/callableReference/equality") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Equality extends AbstractIrJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath); + } + + public void testAllFilesPresentInEquality() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); + } + + @TestMetadata("capturedDefaults.kt") + public void testCapturedDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/capturedDefaults.kt"); + } + + @TestMetadata("capturedVararg.kt") + public void testCapturedVararg() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/capturedVararg.kt"); + } + + @TestMetadata("coercionToUnit.kt") + public void testCoercionToUnit() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnit.kt"); + } + + @TestMetadata("coercionToUnitWithDefaults.kt") + public void testCoercionToUnitWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithDefaults.kt"); + } + + @TestMetadata("coercionToUnitWithVararg.kt") + public void testCoercionToUnitWithVararg() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithVararg.kt"); + } + + @TestMetadata("extensionReceiverVsDefault.kt") + public void testExtensionReceiverVsDefault() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/extensionReceiverVsDefault.kt"); + } + + @TestMetadata("noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt") + public void testNoCoercionToUnitIfFunctionAlreadyReturnsUnit() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt"); + } + + @TestMetadata("simpleEquality.kt") + public void testSimpleEquality() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/simpleEquality.kt"); + } + + @TestMetadata("varargAsArrayMemberOrExtension.kt") + public void testVarargAsArrayMemberOrExtension() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargAsArrayMemberOrExtension.kt"); + } + + @TestMetadata("varargAsArrayWithDefaults.kt") + public void testVarargAsArrayWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt"); + } + + @TestMetadata("varargWithDefaults.kt") + public void testVarargWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargWithDefaults.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/callableReference/function") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 562a712515a..5d908dfca14 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 @@ -1589,6 +1589,74 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/callableReference/equality") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Equality extends AbstractJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath); + } + + public void testAllFilesPresentInEquality() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); + } + + @TestMetadata("capturedDefaults.kt") + public void testCapturedDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/capturedDefaults.kt"); + } + + @TestMetadata("capturedVararg.kt") + public void testCapturedVararg() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/capturedVararg.kt"); + } + + @TestMetadata("coercionToUnit.kt") + public void testCoercionToUnit() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnit.kt"); + } + + @TestMetadata("coercionToUnitWithDefaults.kt") + public void testCoercionToUnitWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithDefaults.kt"); + } + + @TestMetadata("coercionToUnitWithVararg.kt") + public void testCoercionToUnitWithVararg() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/coercionToUnitWithVararg.kt"); + } + + @TestMetadata("extensionReceiverVsDefault.kt") + public void testExtensionReceiverVsDefault() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/extensionReceiverVsDefault.kt"); + } + + @TestMetadata("noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt") + public void testNoCoercionToUnitIfFunctionAlreadyReturnsUnit() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/noCoercionToUnitIfFunctionAlreadyReturnsUnit.kt"); + } + + @TestMetadata("simpleEquality.kt") + public void testSimpleEquality() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/simpleEquality.kt"); + } + + @TestMetadata("varargAsArrayMemberOrExtension.kt") + public void testVarargAsArrayMemberOrExtension() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargAsArrayMemberOrExtension.kt"); + } + + @TestMetadata("varargAsArrayWithDefaults.kt") + public void testVarargAsArrayWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt"); + } + + @TestMetadata("varargWithDefaults.kt") + public void testVarargWithDefaults() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/equality/varargWithDefaults.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/callableReference/function") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/CallableReference.java b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/CallableReference.java index dcde77e6df3..3bd45000b5b 100644 --- a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/CallableReference.java +++ b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/CallableReference.java @@ -62,20 +62,16 @@ public abstract class CallableReference implements KCallable, Serializable { @SinceKotlin(version = "1.1") protected CallableReference(Object receiver) { - this(receiver, null, null, null, 0); + this(receiver, null, null, null, false); } - /** - * @param flags Bitmask where bits represent the following flags:
- *
    • 0 - the owner of this reference is a package, not a class.
  • - */ @SinceKotlin(version = "1.4") - protected CallableReference(Object receiver, Class owner, String name, String signature, int flags) { + protected CallableReference(Object receiver, Class owner, String name, String signature, boolean isTopLevel) { this.receiver = receiver; this.owner = owner; this.name = name; this.signature = signature; - this.isTopLevel = (flags & 1) == 1; + this.isTopLevel = isTopLevel; } protected abstract KCallable computeReflected(); diff --git a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/FunctionReference.java b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/FunctionReference.java index 56c1ce7a4bd..e39f264fe3f 100644 --- a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/FunctionReference.java +++ b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/FunctionReference.java @@ -13,8 +13,26 @@ import kotlin.reflect.KFunction; public class FunctionReference extends CallableReference implements FunctionBase, KFunction { private final int arity; + /** + * Bitmask where bits represent the following flags:
    + *
  • + *
      0 - whether the vararg to element parameter type conversion happened, i.e.
      +     *         fun target(vararg xs: Int) {}
      +     *         fun use(f: (Int, Int, Int) -> Unit) {}
      +     *         use(::target)
      +     *     
    + *
      1 - whether coercion of return type to Unit happened, i.e.
      +     *         fun target(): Boolean = true
      +     *         fun use(f: () -> Unit) {}
      +     *         use(::target)
      +     *     
    + *
  • + */ + @SinceKotlin(version = "1.4") + private final int flags; + public FunctionReference(int arity) { - this.arity = arity; + this(arity, NO_RECEIVER, null, null, null, 0); } @SinceKotlin(version = "1.1") @@ -24,8 +42,9 @@ public class FunctionReference extends CallableReference implements FunctionBase @SinceKotlin(version = "1.4") public FunctionReference(int arity, Object receiver, Class owner, String name, String signature, int flags) { - super(receiver, owner, name, signature, flags); + super(receiver, owner, name, signature, (flags & 1) == 1); this.arity = arity; + this.flags = flags >> 1; } @Override @@ -81,9 +100,11 @@ public class FunctionReference extends CallableReference implements FunctionBase if (obj instanceof FunctionReference) { FunctionReference other = (FunctionReference) obj; - return (getOwner() == null ? other.getOwner() == null : getOwner().equals(other.getOwner())) && + return Intrinsics.areEqual(getOwner(), other.getOwner()) && getName().equals(other.getName()) && getSignature().equals(other.getSignature()) && + flags == other.flags && + arity == other.arity && Intrinsics.areEqual(getBoundReceiver(), other.getBoundReceiver()); } if (obj instanceof KFunction) { diff --git a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/PropertyReference.java b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/PropertyReference.java index 968962286c9..77a5422ff89 100644 --- a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/PropertyReference.java +++ b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/PropertyReference.java @@ -22,7 +22,7 @@ public abstract class PropertyReference extends CallableReference implements KPr @SinceKotlin(version = "1.4") public PropertyReference(Object receiver, Class owner, String name, String signature, int flags) { - super(receiver, owner, name, signature, flags); + super(receiver, owner, name, signature, (flags & 1) == 1); } @Override diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index 28ad08ebb12..96d8ac08304 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -3226,7 +3226,7 @@ public abstract class kotlin/jvm/internal/CallableReference : java/io/Serializab protected final field receiver Ljava/lang/Object; public fun ()V protected fun (Ljava/lang/Object;)V - protected fun (Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;I)V + protected fun (Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Z)V public fun call ([Ljava/lang/Object;)Ljava/lang/Object; public fun callBy (Ljava/util/Map;)Ljava/lang/Object; public fun compute ()Lkotlin/reflect/KCallable;