From 898a9a0f2cb0e7ecca95861650489ae5c1bde40e Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Thu, 19 Jan 2023 13:58:38 +0100 Subject: [PATCH] K2: apply SAM if argument has postponed lambdas #KT-53966 Fixed --- ...CompilerTestFE10TestdataTestGenerated.java | 6 ++++ ...irOldFrontendDiagnosticsTestGenerated.java | 6 ++++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 ++++ .../kotlin/fir/resolve/calls/Arguments.kt | 19 ++++++++++ .../conversionOnLambdaAsLastExpression.fir.kt | 18 ---------- .../conversionOnLambdaAsLastExpression.kt | 1 + .../samConversionWithCondition.fir.kt | 36 +++++++++++++++++-- .../samConversionWithCondition.kt | 34 +++++++++++++++++- .../samConversionWithCondition.txt | 5 ++- .../samConversionWithConditionJava.kt | 28 +++++++++++++++ .../samConversionWithConditionJava.txt | 30 ++++++++++++++++ .../test/runners/DiagnosticTestGenerated.java | 6 ++++ 12 files changed, 173 insertions(+), 22 deletions(-) delete mode 100644 compiler/testData/diagnostics/tests/samConversions/conversionOnLambdaAsLastExpression.fir.kt create mode 100644 compiler/testData/diagnostics/tests/samConversions/samConversionWithConditionJava.kt create mode 100644 compiler/testData/diagnostics/tests/samConversions/samConversionWithConditionJava.txt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 1c00132b3d0..5c5a1f96655 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -27578,6 +27578,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.kt"); } + @Test + @TestMetadata("samConversionWithConditionJava.kt") + public void testSamConversionWithConditionJava() throws Exception { + runTest("compiler/testData/diagnostics/tests/samConversions/samConversionWithConditionJava.kt"); + } + @Test @TestMetadata("samConversionsWithSmartCasts.kt") public void testSamConversionsWithSmartCasts() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 748ce550497..b1c074a88bd 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -27590,6 +27590,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.kt"); } + @Test + @TestMetadata("samConversionWithConditionJava.kt") + public void testSamConversionWithConditionJava() throws Exception { + runTest("compiler/testData/diagnostics/tests/samConversions/samConversionWithConditionJava.kt"); + } + @Test @TestMetadata("samConversionsWithSmartCasts.kt") public void testSamConversionsWithSmartCasts() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 2392871e8cf..ede4a749fb1 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -27578,6 +27578,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.kt"); } + @Test + @TestMetadata("samConversionWithConditionJava.kt") + public void testSamConversionWithConditionJava() throws Exception { + runTest("compiler/testData/diagnostics/tests/samConversions/samConversionWithConditionJava.kt"); + } + @Test @TestMetadata("samConversionsWithSmartCasts.kt") public void testSamConversionsWithSmartCasts() throws Exception { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index db8fcfa7fe5..cd472e7200c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.createFunctionType import org.jetbrains.kotlin.fir.resolve.dfa.unwrapSmartcastExpression import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.resolve.inference.LambdaWithTypeVariableAsExpectedTypeAtom import org.jetbrains.kotlin.fir.resolve.inference.model.ConeArgumentConstraintPosition import org.jetbrains.kotlin.fir.resolve.inference.model.ConeReceiverConstraintPosition import org.jetbrains.kotlin.fir.resolve.inference.preprocessCallableReference @@ -38,6 +39,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystem import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.model.CaptureStatus import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext +import org.jetbrains.kotlin.types.model.typeConstructor import org.jetbrains.kotlin.utils.addToStdlib.runIf val SAM_LOOKUP_NAME = Name.special("") @@ -525,6 +527,16 @@ fun FirExpression.isFunctional( if (classLikeExpectedFunctionType == null || coneType is ConeIntegerLiteralType) { return false } + + val namedReferenceWithCandidate = namedReferenceWithCandidate() + if (namedReferenceWithCandidate?.candidate?.postponedAtoms?.any { + it is LambdaWithTypeVariableAsExpectedTypeAtom && + it.expectedType.typeConstructor(session.typeContext) == coneType.typeConstructor(session.typeContext) + } == true + ) { + return true + } + val invokeSymbol = coneType.findContributedInvokeSymbol( session, scopeSession, classLikeExpectedFunctionType, shouldCalculateReturnTypesOfFakeOverrides = false @@ -567,6 +579,13 @@ fun FirExpression.isFunctional( } } +private fun FirExpression.namedReferenceWithCandidate(): FirNamedReferenceWithCandidate? = + when (this) { + is FirResolvable -> calleeReference as? FirNamedReferenceWithCandidate + is FirSafeCallExpression -> (selector as? FirExpression)?.namedReferenceWithCandidate() + else -> null + } + fun FirExpression.getExpectedType( parameter: FirValueParameter/*, languageVersionSettings: LanguageVersionSettings*/ ): ConeKotlinType { diff --git a/compiler/testData/diagnostics/tests/samConversions/conversionOnLambdaAsLastExpression.fir.kt b/compiler/testData/diagnostics/tests/samConversions/conversionOnLambdaAsLastExpression.fir.kt deleted file mode 100644 index d0a6a44e341..00000000000 --- a/compiler/testData/diagnostics/tests/samConversions/conversionOnLambdaAsLastExpression.fir.kt +++ /dev/null @@ -1,18 +0,0 @@ -// FILE: C.java - -public interface C { void on(String s); } - -// FILE: A.java - -public class A { void add(C c) {} } - -// FILE: test.kt - -class B : A() { - fun test(x: Any?) { - add(foo { { _ : String -> Unit } }) - add(x?.let { { _ : String -> Unit } }) - } -} - -fun foo(f: () -> T): T = f() diff --git a/compiler/testData/diagnostics/tests/samConversions/conversionOnLambdaAsLastExpression.kt b/compiler/testData/diagnostics/tests/samConversions/conversionOnLambdaAsLastExpression.kt index 07ada07330c..5585393b1ea 100644 --- a/compiler/testData/diagnostics/tests/samConversions/conversionOnLambdaAsLastExpression.kt +++ b/compiler/testData/diagnostics/tests/samConversions/conversionOnLambdaAsLastExpression.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // FILE: C.java public interface C { void on(String s); } diff --git a/compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.fir.kt b/compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.fir.kt index 5b5a44d1ea6..be554097580 100644 --- a/compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.fir.kt +++ b/compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.fir.kt @@ -1,10 +1,42 @@ -fun main() { +fun main(arg: Nothing?) { val flag = true consumeTicker( - if (flag) null else { num -> num.dec() } + if (flag) null else { num -> num.dec() } + ) + consumeTicker( + select({ num -> num.dec() }, null) + ) + consumeTicker( + try { { num -> num.dec() } } catch (e: Exception) { null } + ) + consumeTicker( + arg ?: { num -> num.dec() } + ) + consumeTicker( + if (!flag) ::tick else null + ) + consumeTicker( + select(::tick, null) + ) + consumeTicker( + select(if (flag) null else ::tick, null) + ) + consumeTicker( + // In fact ::tick is not used here + try { null } finally { ::tick } + ) + consumeTicker( + try { null } catch (e: Exception) { null } catch (e: Throwable) { ::tick } + ) + consumeTicker( + arg ?: ::tick ) } +fun tick(num: Int) {} + +fun select(a: T, b: T): T = a + fun consumeTicker(ticker: Ticker?) { } diff --git a/compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.kt b/compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.kt index f238e8ec9e3..9f4a92acaf8 100644 --- a/compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.kt +++ b/compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.kt @@ -1,10 +1,42 @@ -fun main() { +fun main(arg: Nothing?) { val flag = true consumeTicker( if (flag) null else { num -> num.dec() } ) + consumeTicker( + select({ num -> num.dec() }, null) + ) + consumeTicker( + try { { num -> num.dec() } } catch (e: Exception) { null } + ) + consumeTicker( + arg ?: { num -> num.dec() } + ) + consumeTicker( + if (!flag) ::tick else null + ) + consumeTicker( + select(::tick, null) + ) + consumeTicker( + select(if (flag) null else ::tick, null) + ) + consumeTicker( + // In fact ::tick is not used here + try { null } finally { ::tick } + ) + consumeTicker( + try { null } catch (e: Exception) { null } catch (e: Throwable) { ::tick } + ) + consumeTicker( + arg ?: ::tick + ) } +fun tick(num: Int) {} + +fun select(a: T, b: T): T = a + fun consumeTicker(ticker: Ticker?) { } diff --git a/compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.txt b/compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.txt index b278adf3f2e..6423bf87769 100644 --- a/compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.txt +++ b/compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.txt @@ -1,7 +1,9 @@ package public fun consumeTicker(/*0*/ ticker: Ticker?): kotlin.Unit -public fun main(): kotlin.Unit +public fun main(/*0*/ arg: kotlin.Nothing?): kotlin.Unit +public fun select(/*0*/ a: T, /*1*/ b: T): T +public fun tick(/*0*/ num: kotlin.Int): kotlin.Unit public fun interface Ticker { public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean @@ -9,3 +11,4 @@ public fun interface Ticker { public abstract fun tick(/*0*/ num: kotlin.Int): kotlin.Unit public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } + diff --git a/compiler/testData/diagnostics/tests/samConversions/samConversionWithConditionJava.kt b/compiler/testData/diagnostics/tests/samConversions/samConversionWithConditionJava.kt new file mode 100644 index 00000000000..d98a630010e --- /dev/null +++ b/compiler/testData/diagnostics/tests/samConversions/samConversionWithConditionJava.kt @@ -0,0 +1,28 @@ +// FIR_IDENTICAL +// FILE: Ticker.java + +public interface Ticker { + String tick(String s); +} + +// FILE: Tickers.java + +public class Tickers { + public static void consumeTicker(Ticker ticker) {} +} + +// FILE: Selectors.java + +public class Selectors { + public static T select(T a, T b) { + return a; + } +} + +// FILE: main.kt + +fun main(flag: Boolean) { + Tickers.consumeTicker(if (flag) null else { s -> s + s }) + + Tickers.consumeTicker(Selectors.select({ s -> s + s }, null)) +} diff --git a/compiler/testData/diagnostics/tests/samConversions/samConversionWithConditionJava.txt b/compiler/testData/diagnostics/tests/samConversions/samConversionWithConditionJava.txt new file mode 100644 index 00000000000..28255246f75 --- /dev/null +++ b/compiler/testData/diagnostics/tests/samConversions/samConversionWithConditionJava.txt @@ -0,0 +1,30 @@ +package + +public fun main(/*0*/ flag: kotlin.Boolean): kotlin.Unit + +public open class Selectors { + public constructor Selectors() + 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 override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public open fun select(/*0*/ a: T!, /*1*/ b: T!): T! +} + +public interface Ticker { + 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 tick(/*0*/ s: kotlin.String!): kotlin.String! + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class Tickers { + public constructor Tickers() + 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 override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public open fun consumeTicker(/*0*/ ticker: Ticker!): kotlin.Unit +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 6c731766da2..dbc58907b8d 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -27590,6 +27590,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/samConversions/samConversionWithCondition.kt"); } + @Test + @TestMetadata("samConversionWithConditionJava.kt") + public void testSamConversionWithConditionJava() throws Exception { + runTest("compiler/testData/diagnostics/tests/samConversions/samConversionWithConditionJava.kt"); + } + @Test @TestMetadata("samConversionsWithSmartCasts.kt") public void testSamConversionsWithSmartCasts() throws Exception {