From a45f73867a2552ca616eacd9053f37540137cc8e Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Tue, 4 Aug 2020 11:50:02 +0300 Subject: [PATCH] Add compatibility resolve when SAM conversion was applied partially #KT-40646 Fixed --- ...irOldFrontendDiagnosticsTestGenerated.java | 5 +++ .../calls/components/ResolutionParts.kt | 22 +++++++++++++ .../calls/components/SamTypeConversions.kt | 22 ++++++++++++- .../calls/model/KotlinResolverContext.kt | 1 + .../javaMemberAgainstExtension.fir.kt | 31 +++++++++++++++++++ .../javaMemberAgainstExtension.kt | 30 ++++++++++++++++++ .../javaMemberAgainstExtension.txt | 20 ++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 5 +++ .../DiagnosticsUsingJavacTestGenerated.java | 5 +++ 9 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.fir.kt create mode 100644 compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.kt create mode 100644 compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.txt diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index a2a0cd770b4..559adbdee7b 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -19831,6 +19831,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/samConversions/GenericSubstitutionKT.kt"); } + @TestMetadata("javaMemberAgainstExtension.kt") + public void testJavaMemberAgainstExtension() throws Exception { + runTest("compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.kt"); + } + @TestMetadata("OverloadPriority.kt") public void testOverloadPriority() throws Exception { runTest("compiler/testData/diagnostics/tests/samConversions/OverloadPriority.kt"); diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt index eb366d626a7..db178085826 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt @@ -273,6 +273,28 @@ internal object CompatibilityOfTypeVariableAsIntersectionTypePart : ResolutionPa } } +internal object CompatibilityOfPartiallyApplicableSamConversion : ResolutionPart() { + override fun KotlinResolutionCandidate.process(workIndex: Int) { + if (resolvedCall.argumentsWithConversion.isEmpty()) return + if (resolvedCall.argumentsWithConversion.size == candidateDescriptor.valueParameters.size) return + + for (argument in kotlinCall.argumentsInParenthesis) { + if (resolvedCall.argumentsWithConversion[argument] != null) continue + + val expectedParameterType = argument.getExpectedType( + resolvedCall.argumentToCandidateParameter[argument] ?: continue, + callComponents.languageVersionSettings + ) + + // argument for the parameter doesn't have a conversion but parameter can be converted => we need a compatibility resolve + if (SamTypeConversions.isJavaParameterCanBeConverted(this, expectedParameterType)) { + markCandidateForCompatibilityResolve() + return + } + } + } +} + internal object CheckExplicitReceiverKindConsistency : ResolutionPart() { private fun KotlinResolutionCandidate.hasError(): Nothing = error( diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SamTypeConversions.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SamTypeConversions.kt index 14edf5d9fe1..7b0d60431a0 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SamTypeConversions.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SamTypeConversions.kt @@ -11,7 +11,6 @@ import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ParameterDescriptor import org.jetbrains.kotlin.incremental.record -import org.jetbrains.kotlin.resolve.calls.inference.model.LowerPriorityToPreserveCompatibility import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.sam.SAM_LOOKUP_NAME import org.jetbrains.kotlin.resolve.sam.getFunctionTypeForPossibleSamType @@ -127,4 +126,25 @@ object SamTypeConversions : ParameterTypeConversion { // now conversions for Kotlin candidates are possible, so we have to perform compatibility resolve return !candidate.callComponents.samConversionOracle.isJavaApplicableCandidate(candidate.resolvedCall.candidateDescriptor) } + + fun isJavaParameterCanBeConverted( + candidate: KotlinResolutionCandidate, + expectedParameterType: UnwrappedType + ): Boolean { + val callComponents = candidate.callComponents + + val samConversionOracle = callComponents.samConversionOracle + if (!samConversionOracle.isJavaApplicableCandidate(candidate.resolvedCall.candidateDescriptor)) return false + + val declarationDescriptor = expectedParameterType.constructor.declarationDescriptor + if (declarationDescriptor is ClassDescriptor && declarationDescriptor.isDefinitelyNotSamInterface) return false + + val convertedType = + callComponents.samConversionResolver.getFunctionTypeForPossibleSamType( + expectedParameterType, + callComponents.samConversionOracle + ) + + return convertedType != null + } } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinResolverContext.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinResolverContext.kt index 41b482853d4..dd371b1c5f0 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinResolverContext.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinResolverContext.kt @@ -229,6 +229,7 @@ enum class KotlinCallKind(vararg resolutionPart: ResolutionPart) { CheckExternalArgument, EagerResolveOfCallableReferences, CompatibilityOfTypeVariableAsIntersectionTypePart, + CompatibilityOfPartiallyApplicableSamConversion, PostponedVariablesInitializerResolutionPart ), INVOKE(*FUNCTION.resolutionSequence.toTypedArray()), diff --git a/compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.fir.kt b/compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.fir.kt new file mode 100644 index 00000000000..9c8e2764d6e --- /dev/null +++ b/compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.fir.kt @@ -0,0 +1,31 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +// FILE: Observer.java +public interface Observer { + void onChanged(K k); +} + +// FILE: LiveData.java +public class LiveData { + public void observe(java.lang.Runnable r, Observer o) {} +} + +// FILE: extension.kt +fun LiveData.observe(a: Any, observer: (T) -> Unit): Observer { + return Observer { observer(it) } +} + +// FILE: test.kt +fun test1(r: Runnable, l: LiveData): Observer = l.observe(r) { } // partial conversion + +fun test2(r: Runnable, o: Observer, l: LiveData) { + val a = l.observe(r, o) // no conversion + a + + val b = l.observe({}, {}) // conversion for all arguments + b + + val c = l.observe({}) {} // conversion for all arguments + c +} + diff --git a/compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.kt b/compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.kt new file mode 100644 index 00000000000..993a3815092 --- /dev/null +++ b/compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.kt @@ -0,0 +1,30 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +// FILE: Observer.java +public interface Observer { + void onChanged(K k); +} + +// FILE: LiveData.java +public class LiveData { + public void observe(java.lang.Runnable r, Observer o) {} +} + +// FILE: extension.kt +fun LiveData.observe(a: Any, observer: (T) -> Unit): Observer { + return Observer { observer(it) } +} + +// FILE: test.kt +fun test1(r: Runnable, l: LiveData): Observer = l.observe(r) { } // partial conversion + +fun test2(r: Runnable, o: Observer, l: LiveData) { + val a = l.observe(r, o) // no conversion + a + + val b = l.observe({}, {}) // conversion for all arguments + b + + val c = l.observe({}) {} // conversion for all arguments + c +} diff --git a/compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.txt b/compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.txt new file mode 100644 index 00000000000..f22802dab86 --- /dev/null +++ b/compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.txt @@ -0,0 +1,20 @@ +package + +public fun test1(/*0*/ r: java.lang.Runnable, /*1*/ l: LiveData): Observer +public fun test2(/*0*/ r: java.lang.Runnable, /*1*/ o: Observer, /*2*/ l: LiveData): kotlin.Unit +public fun LiveData.observe(/*0*/ a: kotlin.Any, /*1*/ observer: (T) -> kotlin.Unit): Observer + +public open class LiveData { + public constructor LiveData() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open fun observe(/*0*/ r: java.lang.Runnable!, /*1*/ o: Observer!): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface Observer { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract fun onChanged(/*0*/ k: K!): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 88e4083acd6..7cd4afb8277 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -19843,6 +19843,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali runTest("compiler/testData/diagnostics/tests/samConversions/GenericSubstitutionKT.kt"); } + @TestMetadata("javaMemberAgainstExtension.kt") + public void testJavaMemberAgainstExtension() throws Exception { + runTest("compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.kt"); + } + @TestMetadata("OverloadPriority.kt") public void testOverloadPriority() throws Exception { runTest("compiler/testData/diagnostics/tests/samConversions/OverloadPriority.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 77c7ea49af6..16d4f46eed8 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -19833,6 +19833,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/samConversions/GenericSubstitutionKT.kt"); } + @TestMetadata("javaMemberAgainstExtension.kt") + public void testJavaMemberAgainstExtension() throws Exception { + runTest("compiler/testData/diagnostics/tests/samConversions/javaMemberAgainstExtension.kt"); + } + @TestMetadata("OverloadPriority.kt") public void testOverloadPriority() throws Exception { runTest("compiler/testData/diagnostics/tests/samConversions/OverloadPriority.kt");