Fix coercion to Unit when variable already have other constraints

Don't add `Unit` if variable has an upper constraint T <: A.

 It's impossible to coerce variable T to Unit as constraint system will
 be always contradictory: T := Unit => Unit should be subtype of A

 #KT-39900 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2020-07-02 18:15:18 +03:00
parent 026f54f3d8
commit 0ee7306d9c
10 changed files with 76 additions and 13 deletions
@@ -10664,6 +10664,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithoutExpectedType.kt"); runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithoutExpectedType.kt");
} }
@TestMetadata("coerctionToUnitForATypeWithUpperBound.kt")
public void testCoerctionToUnitForATypeWithUpperBound() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coerctionToUnitForATypeWithUpperBound.kt");
}
@TestMetadata("coersionWithAnonymousFunctionsAndUnresolved.kt") @TestMetadata("coersionWithAnonymousFunctionsAndUnresolved.kt")
public void testCoersionWithAnonymousFunctionsAndUnresolved() throws Exception { public void testCoersionWithAnonymousFunctionsAndUnresolved() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coersionWithAnonymousFunctionsAndUnresolved.kt"); runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coersionWithAnonymousFunctionsAndUnresolved.kt");
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible import org.jetbrains.kotlin.resolve.calls.inference.addEqualityConstraintIfCompatible
import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter 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.components.KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode
import org.jetbrains.kotlin.resolve.calls.inference.components.TrivialConstraintTypeInferenceOracle import org.jetbrains.kotlin.resolve.calls.inference.components.TrivialConstraintTypeInferenceOracle
@@ -285,7 +285,7 @@ class KotlinCallCompleter(
} }
expectedType === TypeUtils.UNIT_EXPECTED_TYPE -> expectedType === TypeUtils.UNIT_EXPECTED_TYPE ->
csBuilder.addSubtypeConstraintIfCompatible( csBuilder.addEqualityConstraintIfCompatible(
returnType, csBuilder.builtIns.unitType, ExpectedTypeConstraintPosition(resolvedCall.atom) returnType, csBuilder.builtIns.unitType, ExpectedTypeConstraintPosition(resolvedCall.atom)
) )
@@ -35,7 +35,6 @@ class PostponedArgumentsAnalyzer(
fun canBeProper(type: KotlinTypeMarker): Boolean fun canBeProper(type: KotlinTypeMarker): Boolean
fun hasUpperOrEqualUnitConstraint(type: KotlinTypeMarker): Boolean fun hasUpperOrEqualUnitConstraint(type: KotlinTypeMarker): Boolean
fun hasEqualNothingConstraint(type: KotlinTypeMarker): Boolean
// mutable operations // mutable operations
fun addOtherSystem(otherSystem: ConstraintStorage) fun addOtherSystem(otherSystem: ConstraintStorage)
@@ -123,7 +122,7 @@ class PostponedArgumentsAnalyzer(
c.canBeProper(rawReturnType) -> substitute(rawReturnType) c.canBeProper(rawReturnType) -> substitute(rawReturnType)
// For Unit-coercion // For Unit-coercion
c.hasUpperOrEqualUnitConstraint(rawReturnType) && !c.hasEqualNothingConstraint(rawReturnType) -> builtIns.unitType c.hasUpperOrEqualUnitConstraint(rawReturnType) -> builtIns.unitType
else -> null else -> null
} }
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.resolve.calls.inference package org.jetbrains.kotlin.resolve.calls.inference
import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.model.CallableReferenceKotlinCallArgument import org.jetbrains.kotlin.resolve.calls.model.CallableReferenceKotlinCallArgument
@@ -62,9 +63,30 @@ fun ConstraintSystemBuilder.addSubtypeConstraintIfCompatible(
lowerType: KotlinTypeMarker, lowerType: KotlinTypeMarker,
upperType: KotlinTypeMarker, upperType: KotlinTypeMarker,
position: ConstraintPosition position: ConstraintPosition
): Boolean =
addConstraintIfCompatible(lowerType, upperType, position, ConstraintKind.LOWER)
fun ConstraintSystemBuilder.addEqualityConstraintIfCompatible(
lowerType: KotlinTypeMarker,
upperType: KotlinTypeMarker,
position: ConstraintPosition
): Boolean =
addConstraintIfCompatible(lowerType, upperType, position, ConstraintKind.EQUALITY)
private fun ConstraintSystemBuilder.addConstraintIfCompatible(
lowerType: KotlinTypeMarker,
upperType: KotlinTypeMarker,
position: ConstraintPosition,
kind: ConstraintKind
) = ) =
runTransaction { runTransaction {
if (!hasContradiction) addSubtypeConstraint(lowerType, upperType, position) if (!hasContradiction) {
when (kind) {
ConstraintKind.LOWER -> addSubtypeConstraint(lowerType, upperType, position)
ConstraintKind.UPPER -> addSubtypeConstraint(upperType, lowerType, position)
ConstraintKind.EQUALITY -> addEqualityConstraint(lowerType, upperType, position)
}
}
!hasContradiction !hasContradiction
} }
@@ -389,10 +389,4 @@ class NewConstraintSystemImpl(
val constraints = storage.notFixedTypeVariables[type.typeConstructor()]?.constraints ?: return false val constraints = storage.notFixedTypeVariables[type.typeConstructor()]?.constraints ?: return false
return constraints.any { (it.kind == ConstraintKind.UPPER || it.kind == ConstraintKind.EQUALITY) && it.type.isUnit() } return constraints.any { (it.kind == ConstraintKind.UPPER || it.kind == ConstraintKind.EQUALITY) && it.type.isUnit() }
} }
override fun hasEqualNothingConstraint(type: KotlinTypeMarker): Boolean {
checkState(State.BUILDING, State.COMPLETION, State.FREEZED)
val constraints = storage.notFixedTypeVariables[type.typeConstructor()]?.constraints ?: return false
return constraints.any { (it.kind == ConstraintKind.EQUALITY) && it.type.isNothing() }
}
} }
@@ -3,11 +3,11 @@
fun <T : Number> materializeNumber(): T = TODO() fun <T : Number> materializeNumber(): T = TODO()
fun a(): Unit = run { fun a(): Unit = run {
<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materializeNumber<!>() <!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materializeNumber<!>()
} }
fun b(): Unit = run { fun b(): Unit = run {
run { run {
<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materializeNumber<!>() <!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materializeNumber<!>()
} }
} }
@@ -0,0 +1,15 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
interface Base
class Foo : Base
fun <T : Base> myRun(action: () -> T): T = action()
fun foo(f: (Foo) -> Unit) {}
fun main() {
foo { f: Foo ->
myRun { f }
}
}
@@ -0,0 +1,18 @@
package
public fun foo(/*0*/ f: (Foo) -> kotlin.Unit): kotlin.Unit
public fun main(): kotlin.Unit
public fun </*0*/ T : Base> myRun(/*0*/ action: () -> T): T
public interface Base {
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
}
public final class Foo : Base {
public constructor Foo()
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
}
@@ -10671,6 +10671,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithoutExpectedType.kt"); runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithoutExpectedType.kt");
} }
@TestMetadata("coerctionToUnitForATypeWithUpperBound.kt")
public void testCoerctionToUnitForATypeWithUpperBound() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coerctionToUnitForATypeWithUpperBound.kt");
}
@TestMetadata("coersionWithAnonymousFunctionsAndUnresolved.kt") @TestMetadata("coersionWithAnonymousFunctionsAndUnresolved.kt")
public void testCoersionWithAnonymousFunctionsAndUnresolved() throws Exception { public void testCoersionWithAnonymousFunctionsAndUnresolved() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coersionWithAnonymousFunctionsAndUnresolved.kt"); runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coersionWithAnonymousFunctionsAndUnresolved.kt");
@@ -10666,6 +10666,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithoutExpectedType.kt"); runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coercionWithoutExpectedType.kt");
} }
@TestMetadata("coerctionToUnitForATypeWithUpperBound.kt")
public void testCoerctionToUnitForATypeWithUpperBound() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coerctionToUnitForATypeWithUpperBound.kt");
}
@TestMetadata("coersionWithAnonymousFunctionsAndUnresolved.kt") @TestMetadata("coersionWithAnonymousFunctionsAndUnresolved.kt")
public void testCoersionWithAnonymousFunctionsAndUnresolved() throws Exception { public void testCoersionWithAnonymousFunctionsAndUnresolved() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coersionWithAnonymousFunctionsAndUnresolved.kt"); runTest("compiler/testData/diagnostics/tests/inference/coercionToUnit/coersionWithAnonymousFunctionsAndUnresolved.kt");