[NI] Fix unit coercion

Consider following case:

fun foo(): Unit = run { "hello" }

Previously, NI would analyze lambda body without expected type, because
it is a type variable 'R' from 'run', which hasn't been fixed yet. This
leads to treating "hello" as lambda-return argument and adding bogus
'String' constraint on 'R', and, consequently, type mismatch.

Now, we peek into current constraint system and check if return-type of
lambda is type variable with upper-Unit constraint (which is exactly
condition for its body to be Unit-coerced). If so, then we provide
expected Unit-type for body explicitly, and the rest will be done
automatically (in particular, in aforementioned example "hello" wouldn't
be treated as lambda return argument).
This commit is contained in:
Dmitry Savvinov
2018-01-17 19:03:11 +03:00
parent b3eeb2f735
commit 44920f42d8
14 changed files with 257 additions and 3 deletions
@@ -0,0 +1,26 @@
// !WITH_NEW_INFERENCE
fun <T> materialize(): T = TODO()
val a: () -> Unit = l@{
// Expected type 'Unit' is used here for inference
if (true) return@l materialize()
// Expected type here is Unit, but it also implies coercion,
// so we can end lambda body with statement
if (true) <!UNUSED_EXPRESSION!>42<!>
}
val b: () -> Unit = l@{
// Error, coercion can't be applied at this position!
if (true) return@l <!TYPE_MISMATCH!>"hello"<!>
// However, this is OK, because here coercion is applied
<!UNUSED_EXPRESSION!>"hello"<!>
}
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()
}
@@ -0,0 +1,6 @@
package
public val a: () -> kotlin.Unit
public val b: () -> kotlin.Unit
public val c: () -> kotlin.Unit
public fun </*0*/ T> materialize(): T
@@ -0,0 +1,13 @@
// !WITH_NEW_INFERENCE
fun <T : Number> materializeNumber(): T = TODO()
fun a(): Unit = run {
<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materializeNumber<!>()
}
fun b(): Unit = run {
run {
<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materializeNumber<!>()
}
}
@@ -0,0 +1,5 @@
package
public fun a(): kotlin.Unit
public fun b(): kotlin.Unit
public fun </*0*/ T : kotlin.Number> materializeNumber(): T
@@ -0,0 +1,19 @@
// !WITH_NEW_INFERENCE
fun <T> materialize(): T = TODO()
fun implicitCoercion() {
val <!UNUSED_VARIABLE!>a<!> = {
// Block is implicitly Unit-coerced, so it is allowed to place statement at the end of lambda
if (true) <!UNUSED_EXPRESSION!>42<!>
}
val <!UNUSED_VARIABLE!>b<!> = l@{
return@l
}
val <!UNUSED_VARIABLE!>c<!> = l@{
// Error: block doesn't have an expected type, so call can't be inferred!
<!NI;UNREACHABLE_CODE!>return@l<!> <!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materialize<!>()
}
}
@@ -0,0 +1,4 @@
package
public fun implicitCoercion(): kotlin.Unit
public fun </*0*/ T> materialize(): T
@@ -0,0 +1,36 @@
// !WITH_NEW_INFERENCE
fun <T> materialize(): T = TODO()
fun a(): Unit = run {
run {
// Ok, block is coerced, because it has (indirectly) Unit-expected type
"hello"
}
}
fun b(): Unit = run {
// Ok, expected type is applied
materialize()
}
fun c(): Unit = 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
<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materialize<!>()
}
}
fun d(): Unit = run outer@{
run inner@{
<!NI;UNREACHABLE_CODE!>return@inner<!> <!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materialize<!>()
}
}
fun e(): Unit = run outer@{
run inner@{
return@outer materialize()
}
}
@@ -0,0 +1,8 @@
package
public fun a(): kotlin.Unit
public fun b(): kotlin.Unit
public fun c(): kotlin.Unit
public fun d(): kotlin.Unit
public fun e(): kotlin.Unit
public fun </*0*/ T> materialize(): T
@@ -0,0 +1,26 @@
// !WITH_NEW_INFERENCE
// !CHECK_TYPE
fun noCoercionLastExpressionUsedAsReturnArgument() {
val a = {
42
}
a checkType { _<() -> Int>() }
}
fun noCoercionBlockHasExplicitType() {
val <!UNUSED_VARIABLE!>b<!>: () -> Int = {
<!TYPE_MISMATCH!>if (true) <!UNUSED_EXPRESSION!>42<!><!>
}
}
fun noCoercionBlockHasExplicitReturn() {
val <!UNUSED_VARIABLE!>c<!> = l@{
if (true) return@l 42
<!INVALID_IF_AS_EXPRESSION!>if<!> (true) 239
}
}
fun noCoercionInExpressionBody(): Unit = <!TYPE_MISMATCH!>"hello"<!>
@@ -0,0 +1,6 @@
package
public fun noCoercionBlockHasExplicitReturn(): kotlin.Unit
public fun noCoercionBlockHasExplicitType(): kotlin.Unit
public fun noCoercionInExpressionBody(): kotlin.Unit
public fun noCoercionLastExpressionUsedAsReturnArgument(): kotlin.Unit