[NI] Do not avoid trivial constraints if they aren't from upper bounds

Since we skipped trivial constraint with `Any?` from parameter type of
 function `equals`, the compiler thought that there is no proper
 constraints (upper bounds do not matter here) and marked resolved
 call as a failed one, then diagnostic about missing equals was added

 Also, tune `TrivialConstraintTypeInferenceOracle` for `Any?`-like
 constraints

 #KT-30724 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2019-04-01 02:58:56 +03:00
parent 5fa518fd55
commit c458393e2f
17 changed files with 74 additions and 26 deletions
@@ -2393,6 +2393,11 @@ public class FirDiagnosticsSmokeTestGenerated extends AbstractFirDiagnosticsSmok
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/overloadsMember.kt"); runTest("compiler/testData/diagnostics/tests/callableReference/resolve/overloadsMember.kt");
} }
@TestMetadata("resolveEqualsOperatorWithAnyExpectedType.kt")
public void testResolveEqualsOperatorWithAnyExpectedType() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/resolveEqualsOperatorWithAnyExpectedType.kt");
}
@TestMetadata("valVsFun.kt") @TestMetadata("valVsFun.kt")
public void testValVsFun() throws Exception { public void testValVsFun() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/valVsFun.kt"); runTest("compiler/testData/diagnostics/tests/callableReference/resolve/valVsFun.kt");
@@ -201,9 +201,8 @@ class KotlinCallCompleter(
val variableWithConstraints = csBuilder.currentStorage().notFixedTypeVariables[constructor] ?: return false val variableWithConstraints = csBuilder.currentStorage().notFixedTypeVariables[constructor] ?: return false
val constraints = variableWithConstraints.constraints val constraints = variableWithConstraints.constraints
return constraints.isNotEmpty() && constraints.all { return constraints.isNotEmpty() && constraints.all {
!trivialConstraintTypeInferenceOracle.isTrivialConstraint(it) && with(context) { !it.type.typeConstructor().isIntegerLiteralTypeConstructor() } &&
with(context) { !it.type.typeConstructor().isIntegerLiteralTypeConstructor() } && it.kind.isLower() && csBuilder.isProperType(it.type)
it.kind.isLower() && csBuilder.isProperType(it.type)
} }
} }
@@ -156,13 +156,19 @@ class ConstraintIncorporator(
if (baseConstraint.kind != ConstraintKind.UPPER) { if (baseConstraint.kind != ConstraintKind.UPPER) {
val generatedConstraintType = approximateCapturedTypes(typeForApproximation, toSuper = false) val generatedConstraintType = approximateCapturedTypes(typeForApproximation, toSuper = false)
if (!trivialConstraintTypeInferenceOracle.isGeneratedConstraintTrivial(otherConstraint, generatedConstraintType)) { if (!trivialConstraintTypeInferenceOracle.isGeneratedConstraintTrivial(
otherConstraint, generatedConstraintType, isSubtype = true
)
) {
addNewIncorporatedConstraint(generatedConstraintType, targetVariable.defaultType()) addNewIncorporatedConstraint(generatedConstraintType, targetVariable.defaultType())
} }
} }
if (baseConstraint.kind != ConstraintKind.LOWER) { if (baseConstraint.kind != ConstraintKind.LOWER) {
val generatedConstraintType = approximateCapturedTypes(typeForApproximation, toSuper = true) val generatedConstraintType = approximateCapturedTypes(typeForApproximation, toSuper = true)
if (!trivialConstraintTypeInferenceOracle.isGeneratedConstraintTrivial(otherConstraint, generatedConstraintType)) { if (!trivialConstraintTypeInferenceOracle.isGeneratedConstraintTrivial(
otherConstraint, generatedConstraintType, isSubtype = false
)
) {
addNewIncorporatedConstraint(targetVariable.defaultType(), generatedConstraintType) addNewIncorporatedConstraint(targetVariable.defaultType(), generatedConstraintType)
} }
} }
@@ -101,7 +101,11 @@ class ConstraintInjector(val constraintIncorporator: ConstraintIncorporator, val
} }
if (constraintType.isSimpleType()) { if (constraintType.isSimpleType()) {
if (constraint.kind == UPPER && constraintType.isNullableAny()) return true // T <: Any? if (constraint.position.from is DeclaredUpperBoundConstraintPosition &&
constraint.kind == UPPER && constraintType.isNullableAny()
) {
return true // T <: Any?
}
} }
return false return false
@@ -7,19 +7,19 @@ package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext import org.jetbrains.kotlin.types.typeUtil.contains
import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContextDelegate import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isNullableNothing
class TrivialConstraintTypeInferenceOracle(context: TypeSystemInferenceExtensionContextDelegate) : class TrivialConstraintTypeInferenceOracle(context: TypeSystemInferenceExtensionContextDelegate) :
TypeSystemInferenceExtensionContext by context { TypeSystemInferenceExtensionContext by context {
// The idea is to add knowledge that constraint `Nothing(?) <: T` is quite useless and // The idea is to add knowledge that constraint `Nothing(?) <: T` is quite useless and
// it's totally fine to go and resolve postponed argument without fixation T to Nothing(?). // it's totally fine to go and resolve postponed argument without fixation T to Nothing(?).
// In other words, constraint `Nothing(?) <: T` is *not* proper // In other words, constraint `Nothing(?) <: T` is *not* proper
fun isTrivialConstraint( fun isNotInterestingConstraint(constraint: Constraint): Boolean {
constraint: Constraint
): Boolean {
// TODO: probably we also can take into account `T <: Any(?)` constraints
return constraint.kind == ConstraintKind.LOWER && constraint.type.typeConstructor().isNothingConstructor() return constraint.kind == ConstraintKind.LOWER && constraint.type.typeConstructor().isNothingConstructor()
} }
@@ -39,9 +39,11 @@ class TrivialConstraintTypeInferenceOracle(context: TypeSystemInferenceExtension
// Therefore, here we avoid adding such trivial constraints to have stable constraint system // Therefore, here we avoid adding such trivial constraints to have stable constraint system
fun isGeneratedConstraintTrivial( fun isGeneratedConstraintTrivial(
otherConstraint: Constraint, otherConstraint: Constraint,
generatedConstraintType: KotlinTypeMarker generatedConstraintType: KotlinTypeMarker,
isSubtype: Boolean
): Boolean { ): Boolean {
if (generatedConstraintType.isNothing()) return true if (isSubtype && generatedConstraintType.isNothing()) return true
if (!isSubtype && generatedConstraintType.isNullableAny()) return true
// If type that will be used to generate new constraint already contains `Nothing(?)`, // If type that will be used to generate new constraint already contains `Nothing(?)`,
// then we can't decide that resulting constraint will be useless // then we can't decide that resulting constraint will be useless
@@ -107,7 +107,7 @@ class VariableFixationFinder(
private fun Context.variableHasTrivialOrNonProperConstraints(variable: TypeConstructorMarker): Boolean { private fun Context.variableHasTrivialOrNonProperConstraints(variable: TypeConstructorMarker): Boolean {
return notFixedTypeVariables[variable]?.constraints?.all { constraint -> return notFixedTypeVariables[variable]?.constraints?.all { constraint ->
val isProperConstraint = isProperArgumentConstraint(constraint) val isProperConstraint = isProperArgumentConstraint(constraint)
isProperConstraint && trivialConstraintTypeInferenceOracle.isTrivialConstraint(constraint) || !isProperConstraint isProperConstraint && trivialConstraintTypeInferenceOracle.isNotInterestingConstraint(constraint) || !isProperConstraint
} ?: false } ?: false
} }
@@ -0,0 +1,11 @@
// !LANGUAGE: +NewInference
interface Base
fun <K> materialize(): K = TODO()
fun <T : Base> Base.transform(): T = materialize()
fun test(child: Base) {
child == child.transform()
}
@@ -0,0 +1,11 @@
package
public fun </*0*/ K> materialize(): K
public fun test(/*0*/ child: Base): kotlin.Unit
public fun </*0*/ T : Base> Base.transform(): 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
}
@@ -6,6 +6,6 @@ fun foo() {
val x: Int? = null val x: Int? = null
bar(1 + (if (x == null) 0 else x)) bar(1 + (if (x == null) 0 else x))
bar(<!NI;TYPE_MISMATCH!>if (x == null) <!DEBUG_INFO_CONSTANT, NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, OI;TYPE_MISMATCH!>x<!> else x<!>) bar(<!NI;TYPE_MISMATCH!>if (x == null) <!DEBUG_INFO_CONSTANT, OI;TYPE_MISMATCH!>x<!> else x<!>)
if (x != null) bar(x + x/(x-x*x)) if (x != null) bar(x + x/(x-x*x))
} }
@@ -4,7 +4,7 @@ fun bar(x: Int) = x + 1
fun f1(x: Int?) { fun f1(x: Int?) {
bar(<!TYPE_MISMATCH!>x<!>) bar(<!TYPE_MISMATCH!>x<!>)
if (x != null) bar(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) if (x != null) bar(x<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
if (x == null) <!OI;UNREACHABLE_CODE!>bar(<!><!ALWAYS_NULL!>x<!>!!<!OI;UNREACHABLE_CODE!>)<!> if (x == null) <!UNREACHABLE_CODE!>bar(<!><!ALWAYS_NULL!>x<!>!!<!UNREACHABLE_CODE!>)<!>
} }
fun f2(x: Int?) { fun f2(x: Int?) {
@@ -13,7 +13,7 @@ class A(outer: Outer) {
var b: String by foo(<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>getMyProperty<!>()) var b: String by foo(<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>getMyProperty<!>())
var r: String by foo(outer.getContainer().<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>getMyProperty<!>()) var r: String by foo(outer.getContainer().<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>getMyProperty<!>())
var e: String by <!NI;DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, NI;TYPE_MISMATCH!><!OI;DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>+<!> <!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>foo<!>(<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>getMyProperty<!>())<!> var e: String by <!NI;DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, NI;TYPE_MISMATCH!><!OI;DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>+<!> <!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>foo<!>(<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>getMyProperty<!>())<!>
var f: String by <!NI;TYPE_MISMATCH!><!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>foo<!>(<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>getMyProperty<!>()) <!OI;DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>-<!> 1<!> var f: String by <!NI;DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, NI;TYPE_MISMATCH!><!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>foo<!>(<!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>getMyProperty<!>()) <!OI;DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>-<!> 1<!>
} }
fun <A, B> foo(<!UNUSED_PARAMETER!>a<!>: Any?) = MyProperty<A, B>() fun <A, B> foo(<!UNUSED_PARAMETER!>a<!>: Any?) = MyProperty<A, B>()
@@ -23,8 +23,8 @@ fun main() {
foo(<!DEBUG_INFO_SMARTCAST!>x<!>) foo(<!DEBUG_INFO_SMARTCAST!>x<!>)
} else { } else {
foo(<!DEBUG_INFO_CONSTANT, TYPE_MISMATCH!>x<!>) foo(<!DEBUG_INFO_CONSTANT, TYPE_MISMATCH!>x<!>)
<!OI;UNREACHABLE_CODE!>foo(<!><!ALWAYS_NULL!>x<!>!!<!OI;UNREACHABLE_CODE!>)<!> <!UNREACHABLE_CODE!>foo(<!><!ALWAYS_NULL!>x<!>!!<!UNREACHABLE_CODE!>)<!>
<!OI;UNREACHABLE_CODE!>foo(<!DEBUG_INFO_SMARTCAST!>x<!>)<!> <!UNREACHABLE_CODE!>foo(<!DEBUG_INFO_SMARTCAST!>x<!>)<!>
} }
foo(<!DEBUG_INFO_SMARTCAST!>x<!>) foo(<!DEBUG_INFO_SMARTCAST!>x<!>)
@@ -55,5 +55,5 @@ fun test() {
withGenericDefaults("") withGenericDefaults("")
<!OI;UNREACHABLE_CODE!><!OVERLOAD_RESOLUTION_AMBIGUITY!>wrong<!>(<!>null!!<!OI;UNREACHABLE_CODE!>)<!> <!UNREACHABLE_CODE!><!OVERLOAD_RESOLUTION_AMBIGUITY!>wrong<!>(<!>null!!<!UNREACHABLE_CODE!>)<!>
} }
@@ -8,15 +8,15 @@ fun test(a: Int?, b: Int?) {
} }
fun test(a: Int?, b: Int?, c: Int?) { fun test(a: Int?, b: Int?, c: Int?) {
bar(<!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH!>if (a == null) return else if (b == null) return else <!OI;TYPE_MISMATCH!>c<!><!>) bar(<!NI;TYPE_MISMATCH!>if (a == null) return else if (b == null) return else <!OI;TYPE_MISMATCH!>c<!><!>)
} }
fun test(a: Any?, b: Any?, c: Int?) { fun test(a: Any?, b: Any?, c: Int?) {
bar(<!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH!>if (a == null) if (b == null) <!OI;TYPE_MISMATCH!>c<!> else return else return<!>) bar(<!NI;TYPE_MISMATCH!>if (a == null) if (b == null) <!OI;TYPE_MISMATCH!>c<!> else return else return<!>)
} }
fun test(a: Int?, b: Any?, c: Int?) { fun test(a: Int?, b: Any?, c: Int?) {
bar(<!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH!>if (a == null) { bar(<!NI;TYPE_MISMATCH!>if (a == null) {
return return
} else { } else {
if (b == null) { if (b == null) {
+2 -2
View File
@@ -1,6 +1,6 @@
// !WITH_NEW_INFERENCE // !WITH_NEW_INFERENCE
fun test1(): Int { fun test1(): Int {
val x: String = <!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, NI;TYPE_MISMATCH!>if (true) { val x: String = <!NI;TYPE_MISMATCH!>if (true) {
when { when {
true -> <!OI;TYPE_MISMATCH!>Any()<!> true -> <!OI;TYPE_MISMATCH!>Any()<!>
else -> <!OI;NULL_FOR_NONNULL_TYPE!>null<!> else -> <!OI;NULL_FOR_NONNULL_TYPE!>null<!>
@@ -10,7 +10,7 @@ fun test1(): Int {
} }
fun test2(): Int { fun test2(): Int {
val x: String = <!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH!>when { val x: String = <!NI;TYPE_MISMATCH!>when {
true -> <!OI;TYPE_MISMATCH!>Any()<!> true -> <!OI;TYPE_MISMATCH!>Any()<!>
else -> null else -> null
} ?: return 0<!> } ?: return 0<!>
@@ -2400,6 +2400,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/overloadsMember.kt"); runTest("compiler/testData/diagnostics/tests/callableReference/resolve/overloadsMember.kt");
} }
@TestMetadata("resolveEqualsOperatorWithAnyExpectedType.kt")
public void testResolveEqualsOperatorWithAnyExpectedType() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/resolveEqualsOperatorWithAnyExpectedType.kt");
}
@TestMetadata("valVsFun.kt") @TestMetadata("valVsFun.kt")
public void testValVsFun() throws Exception { public void testValVsFun() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/valVsFun.kt"); runTest("compiler/testData/diagnostics/tests/callableReference/resolve/valVsFun.kt");
@@ -2395,6 +2395,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/overloadsMember.kt"); runTest("compiler/testData/diagnostics/tests/callableReference/resolve/overloadsMember.kt");
} }
@TestMetadata("resolveEqualsOperatorWithAnyExpectedType.kt")
public void testResolveEqualsOperatorWithAnyExpectedType() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/resolveEqualsOperatorWithAnyExpectedType.kt");
}
@TestMetadata("valVsFun.kt") @TestMetadata("valVsFun.kt")
public void testValVsFun() throws Exception { public void testValVsFun() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/valVsFun.kt"); runTest("compiler/testData/diagnostics/tests/callableReference/resolve/valVsFun.kt");