Safe call arguments are now handled as nullable in generic resolver #KT-9985 Fixed
This commit is contained in:
+16
-8
@@ -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
|
||||
)
|
||||
|
||||
@@ -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?>() }
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user