Safe call arguments are now handled as nullable in generic resolver #KT-9985 Fixed

This commit is contained in:
Mikhail Glukhikh
2016-02-01 16:07:39 +03:00
parent 187694d1b0
commit 60d56b30bf
7 changed files with 103 additions and 8 deletions
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.*
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.RECEIVER_POSITION
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.VALUE_PARAMETER_POSITION
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.INCOMPLETE_TYPE_INFERENCE
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.OTHER_ERROR
@@ -154,28 +155,35 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
val resolutionResults = getResolutionResultsCachedData(argumentExpression, context)?.resolutionResults
if (resolutionResults == null || !resolutionResults.isSingleResult) return false
val resultingCall = resolutionResults.resultingCall
if (resultingCall.isCompleted) return false
val nestedCall = resolutionResults.resultingCall
if (nestedCall.isCompleted) return false
val argumentConstraintSystem = resultingCall.constraintSystem ?: return false
val nestedConstraintSystem = nestedCall.constraintSystem ?: return false
val candidateDescriptor = resultingCall.candidateDescriptor
val candidateDescriptor = nestedCall.candidateDescriptor
val returnType = candidateDescriptor.returnType ?: return false
val nestedTypeVariables = argumentConstraintSystem.getNestedTypeVariables(returnType)
val nestedTypeVariables = nestedConstraintSystem.getNestedTypeVariables(returnType)
// we add an additional type variable only if no information is inferred for it.
// otherwise we add currently inferred return type as before
if (nestedTypeVariables.any { argumentConstraintSystem.getTypeBounds(it).bounds.isNotEmpty() }) return false
if (nestedTypeVariables.any { nestedConstraintSystem.getTypeBounds(it).bounds.isNotEmpty() }) return false
val candidateWithFreshVariables = FunctionDescriptorUtil.alphaConvertTypeParameters(candidateDescriptor)
val conversion = candidateDescriptor.typeParameters.zip(candidateWithFreshVariables.typeParameters).toMap()
val freshVariables = returnType.getNestedTypeParameters().mapNotNull { conversion[it] }
builder.registerTypeVariables(resultingCall.call.toHandle(), freshVariables, external = true)
builder.registerTypeVariables(nestedCall.call.toHandle(), freshVariables, external = true)
// Looks not too nice, but safe call result must be nullable if receiver is nullable
val argumentExpressionType = candidateWithFreshVariables.returnType?.let {
if (nestedCall.isSafeCall && nestedCall.getExplicitReceiverValue()?.type?.let {TypeUtils.isNullableType(it) } ?: true ) {
TypeUtils.makeNullable(it)
}
else it
}
builder.addSubtypeConstraint(
candidateWithFreshVariables.returnType,
argumentExpressionType,
builder.compositeSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT),
constraintPosition
)
+42
View File
@@ -0,0 +1,42 @@
// !CHECK_TYPE
// Incorrect "type mismatch" error for generic extension safe call (required not-null, found nullable)
// FILE: B.java
public class B<T> {
public String gav() {
return ""
}
public static <T> B<T> create() {
return new B();
}
}
// FILE: A.kt
class A<T> {
fun gav() = ""
}
fun <R> foo(x: R) = x
fun <T> A<T>.bar() = ""
fun <T> B<T>.bar() = ""
fun foo(l: A<String>?) {
// No errors should be here
foo(l?.bar()) checkType { _<String?>() }
foo(l?.gav()) checkType { _<String?>() }
}
fun fooNotNull(l: A<String>) {
// No errors should be here
foo(l<!UNNECESSARY_SAFE_CALL!>?.<!>bar()) checkType { _<String>() }
foo(l<!UNNECESSARY_SAFE_CALL!>?.<!>gav()) checkType { _<String>() }
}
fun bar() {
val l = B.create<String>()
foo(l?.bar()) checkType { _<String?>() }
foo(l?.gav()) checkType { _<String?>() }
}
+27
View File
@@ -0,0 +1,27 @@
package
public fun bar(): kotlin.Unit
public fun foo(/*0*/ l: A<kotlin.String>?): kotlin.Unit
public fun </*0*/ R> foo(/*0*/ x: R): R
public fun fooNotNull(/*0*/ l: A<kotlin.String>): kotlin.Unit
public fun </*0*/ T> A<T>.bar(): kotlin.String
public fun </*0*/ T> B<T>.bar(): kotlin.String
public final class A</*0*/ T> {
public constructor A</*0*/ T>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun gav(): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public open class B</*0*/ T : kotlin.Any!> {
public constructor B</*0*/ T : kotlin.Any!>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open fun gav(): kotlin.String!
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public open fun </*0*/ T : kotlin.Any!> create(): B<T!>!
}
@@ -0,0 +1,3 @@
fun foo(l: List<String>?) {
Pair(l?.joinToString(), "")
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ l: kotlin.collections.List<kotlin.String>?): kotlin.Unit
@@ -6867,6 +6867,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("kt9985.kt")
public void testKt9985() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/kt9985.kt");
doTest(fileName);
}
@TestMetadata("Projections.kt")
public void testProjections() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/Projections.kt");
@@ -101,6 +101,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
doTest(fileName);
}
@TestMetadata("kt9985.kt")
public void testKt9985() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/kt9985.kt");
doTest(fileName);
}
@TestMetadata("outstar.kt")
public void testOutstar() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/outstar.kt");