[NI] Coerce to Unit expression in last block with explicit Unit

#KT-32037 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2019-06-18 13:56:00 +03:00
parent 25342240b5
commit 7df13c986e
10 changed files with 72 additions and 19 deletions
@@ -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");
@@ -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))
}
}
@@ -0,0 +1,13 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun coerceToUnit(f: () -> Unit) {}
class Inv<T>
fun <K> builder(block: Inv<K>.() -> Unit): K = TODO()
fun test() {
coerceToUnit {
builder {}
}
}
@@ -0,0 +1,12 @@
package
public fun </*0*/ K> builder(/*0*/ block: Inv<K>.() -> kotlin.Unit): K
public fun coerceToUnit(/*0*/ f: () -> kotlin.Unit): kotlin.Unit
public fun test(): kotlin.Unit
public final class Inv</*0*/ T> {
public constructor Inv</*0*/ T>()
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
}
@@ -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")
<!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materialize<!>()
materialize()
}
@@ -3,11 +3,11 @@
fun <T : Number> materializeNumber(): T = TODO()
fun a(): Unit = run {
<!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materializeNumber<!>()
<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materializeNumber<!>()
}
fun b(): Unit = run {
<!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>run<!> {
<!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materializeNumber<!>()
run {
<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materializeNumber<!>()
}
}
@@ -5,27 +5,27 @@ fun <T> materialize(): T = TODO()
fun a(): Unit = run {
run {
// Ok, block is coerced, because it has (indirectly) Unit-expected type
"hello"
<!NI;UNUSED_EXPRESSION!>"hello"<!>
}
}
fun b(): Unit = run {
// Ok, expected type is applied
<!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materialize<!>()
materialize()
}
fun c(): Unit = run {
<!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>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
<!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materialize<!>()
<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materialize<!>()
}
}
fun d(): Unit = run outer@{
run inner@{
<!NI;UNREACHABLE_CODE!>return@inner<!> <!NI;IMPLICIT_NOTHING_AS_TYPE_PARAMETER, NI;IMPLICIT_NOTHING_AS_TYPE_PARAMETER, OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materialize<!>()
return@inner <!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materialize<!>()
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ interface A {
fun foo(a: A) {
val <!UNUSED_VARIABLE!>g<!> : () -> Unit = {
a.<!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>gen<!>() //it works: Unit is derived
a.gen() //it works: Unit is derived
}
val <!UNUSED_VARIABLE!>u<!>: Unit = a.gen() // Unit should be inferred
@@ -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");
@@ -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");