From 7df13c986e448c959b73664fdf71968a24190651 Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Tue, 18 Jun 2019 13:56:00 +0300 Subject: [PATCH] [NI] Coerce to `Unit` expression in last block with explicit `Unit` #KT-32037 Fixed --- .../fir/FirDiagnosticsSmokeTestGenerated.java | 5 +++ .../calls/components/KotlinCallCompleter.kt | 31 +++++++++++++------ .../coercionToUnitForLastLambdaInLambda.kt | 13 ++++++++ .../coercionToUnitForLastLambdaInLambda.txt | 12 +++++++ .../coercionWithExpectedType.kt | 2 +- .../coercionWithExpectedTypeAndBound.kt | 6 ++-- .../indirectCoercionWithExpectedType.kt | 10 +++--- .../diagnostics/tests/regressions/kt353.kt | 2 +- .../checkers/DiagnosticsTestGenerated.java | 5 +++ .../DiagnosticsUsingJavacTestGenerated.java | 5 +++ 10 files changed, 72 insertions(+), 19 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForLastLambdaInLambda.kt create mode 100644 compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForLastLambdaInLambda.txt diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java index ed4fb97f45b..b427bcfc2da 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java @@ -9908,6 +9908,11 @@ public class FirDiagnosticsSmokeTestGenerated extends AbstractFirDiagnosticsSmok runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForIfAsLastExpressionInLambda.kt"); } + @TestMetadata("coercionToUnitForLastLambdaInLambda.kt") + public void testCoercionToUnitForLastLambdaInLambda() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForLastLambdaInLambda.kt"); + } + @TestMetadata("coercionWithExpectedType.kt") public void testCoercionWithExpectedType() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithExpectedType.kt"); diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt index 5bd1871cb98..12e0e5c3982 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.components import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem +import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.Empty.hasContradiction @@ -153,17 +154,29 @@ class KotlinCallCompleter( resolutionCallbacks: KotlinResolutionCallbacks ) { if (returnType == null) return - if (expectedType == null || TypeUtils.noExpectedType(expectedType)) return + if (expectedType == null || (TypeUtils.noExpectedType(expectedType) && expectedType !== TypeUtils.UNIT_EXPECTED_TYPE)) return - // This is needed to avoid multiple mismatch errors as we type check resulting type against expected one later - // Plus, it helps with IDE-tests where it's important to have particular diagnostics. - // Note that it aligns with the old inference, see CallCompleter.completeResolvedCallAndArguments - if (csBuilder.currentStorage().notFixedTypeVariables.isEmpty()) return + when { + csBuilder.currentStorage().notFixedTypeVariables.isEmpty() -> { + // This is needed to avoid multiple mismatch errors as we type check resulting type against expected one later + // Plus, it helps with IDE-tests where it's important to have particular diagnostics. + // Note that it aligns with the old inference, see CallCompleter.completeResolvedCallAndArguments + return + } - // We don't add expected type constraint for constant expression like "1 + 1" because of type coercion for numbers: - // val a: Long = 1 + 1, note that result type of "1 + 1" will be Int and adding constraint with Long will produce type mismatch - if (!resolutionCallbacks.isCompileTimeConstant(resolvedCall, expectedType)) { - csBuilder.addSubtypeConstraint(returnType, expectedType, ExpectedTypeConstraintPosition(resolvedCall.atom)) + expectedType === TypeUtils.UNIT_EXPECTED_TYPE -> + csBuilder.addSubtypeConstraintIfCompatible( + returnType, csBuilder.builtIns.unitType, ExpectedTypeConstraintPosition(resolvedCall.atom) + ) + + resolutionCallbacks.isCompileTimeConstant(resolvedCall, expectedType) -> { + // We don't add expected type constraint for constant expression like "1 + 1" because of type coercion for numbers: + // val a: Long = 1 + 1, note that result type of "1 + 1" will be Int and adding constraint with Long will produce type mismatch + return + } + + else -> + csBuilder.addSubtypeConstraint(returnType, expectedType, ExpectedTypeConstraintPosition(resolvedCall.atom)) } } diff --git a/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForLastLambdaInLambda.kt b/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForLastLambdaInLambda.kt new file mode 100644 index 00000000000..07288febcce --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForLastLambdaInLambda.kt @@ -0,0 +1,13 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun coerceToUnit(f: () -> Unit) {} + +class Inv + +fun builder(block: Inv.() -> Unit): K = TODO() + +fun test() { + coerceToUnit { + builder {} + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForLastLambdaInLambda.txt b/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForLastLambdaInLambda.txt new file mode 100644 index 00000000000..38b5a125dd1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForLastLambdaInLambda.txt @@ -0,0 +1,12 @@ +package + +public fun builder(/*0*/ block: Inv.() -> kotlin.Unit): K +public fun coerceToUnit(/*0*/ f: () -> kotlin.Unit): kotlin.Unit +public fun test(): kotlin.Unit + +public final class Inv { + public constructor Inv() + 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 +} diff --git a/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithExpectedType.kt b/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithExpectedType.kt index f76f9bd8751..ee12000834a 100644 --- a/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithExpectedType.kt +++ b/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithExpectedType.kt @@ -22,5 +22,5 @@ val b: () -> Unit = l@{ val c: () -> Unit = { // Interesting enough, for such expessions we use expected type Unit // (compare that with the previous case, where we didn't used expected type Unit for "hello") - materialize() + materialize() } diff --git a/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithExpectedTypeAndBound.kt b/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithExpectedTypeAndBound.kt index 7350ae0a2b4..c86551c0788 100644 --- a/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithExpectedTypeAndBound.kt +++ b/compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithExpectedTypeAndBound.kt @@ -3,11 +3,11 @@ fun materializeNumber(): T = TODO() fun a(): Unit = run { - materializeNumber() + materializeNumber() } fun b(): Unit = run { - run { - materializeNumber() + run { + materializeNumber() } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/coercionToUnit/indirectCoercionWithExpectedType.kt b/compiler/testData/diagnostics/tests/inference/coercionToUnit/indirectCoercionWithExpectedType.kt index a65aa2996d3..18767e081e6 100644 --- a/compiler/testData/diagnostics/tests/inference/coercionToUnit/indirectCoercionWithExpectedType.kt +++ b/compiler/testData/diagnostics/tests/inference/coercionToUnit/indirectCoercionWithExpectedType.kt @@ -5,27 +5,27 @@ fun materialize(): T = TODO() fun a(): Unit = run { run { // Ok, block is coerced, because it has (indirectly) Unit-expected type - "hello" + "hello" } } fun b(): Unit = run { // Ok, expected type is applied - materialize() + materialize() } fun c(): Unit = run { - run { + run { // Attention! // In OI expected type 'Unit' isn't applied here because of implementation quirks (note that OI still applies Unit in case 'e') // In NI, it is applied and call is correctly inferred, which is consistent with the previous case - materialize() + materialize() } } fun d(): Unit = run outer@{ run inner@{ - return@inner materialize() + return@inner materialize() } } diff --git a/compiler/testData/diagnostics/tests/regressions/kt353.kt b/compiler/testData/diagnostics/tests/regressions/kt353.kt index 89c5df433c1..df1c7cf517e 100644 --- a/compiler/testData/diagnostics/tests/regressions/kt353.kt +++ b/compiler/testData/diagnostics/tests/regressions/kt353.kt @@ -7,7 +7,7 @@ interface A { fun foo(a: A) { val g : () -> Unit = { - a.gen() //it works: Unit is derived + a.gen() //it works: Unit is derived } val u: Unit = a.gen() // Unit should be inferred diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 7c271d2a1ec..4cb4d082eee 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -9915,6 +9915,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForIfAsLastExpressionInLambda.kt"); } + @TestMetadata("coercionToUnitForLastLambdaInLambda.kt") + public void testCoercionToUnitForLastLambdaInLambda() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForLastLambdaInLambda.kt"); + } + @TestMetadata("coercionWithExpectedType.kt") public void testCoercionWithExpectedType() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithExpectedType.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index fcf3e3b6560..ec2dbabc21d 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -9910,6 +9910,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForIfAsLastExpressionInLambda.kt"); } + @TestMetadata("coercionToUnitForLastLambdaInLambda.kt") + public void testCoercionToUnitForLastLambdaInLambda() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForLastLambdaInLambda.kt"); + } + @TestMetadata("coercionWithExpectedType.kt") public void testCoercionWithExpectedType() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithExpectedType.kt");