[NI] Coerce to Unit expression in last block with explicit Unit
#KT-32037 Fixed
This commit is contained in:
+5
@@ -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");
|
||||
|
||||
+22
-9
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+13
@@ -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 {}
|
||||
}
|
||||
}
|
||||
compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionToUnitForLastLambdaInLambda.txt
Vendored
+12
@@ -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
|
||||
}
|
||||
+1
-1
@@ -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()
|
||||
}
|
||||
|
||||
Vendored
+3
-3
@@ -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<!>()
|
||||
}
|
||||
}
|
||||
Vendored
+5
-5
@@ -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<!>()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
Generated
+5
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user