Support smart cast for nullability in LHS of class literal
#KT-16291 Fixed
This commit is contained in:
+16
-7
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.resolve.calls.context.*
|
||||
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
|
||||
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsUtil
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
|
||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
|
||||
import org.jetbrains.kotlin.resolve.calls.util.createValueParametersForInvokeInFunctionType
|
||||
@@ -54,6 +55,7 @@ import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
|
||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.lang.UnsupportedOperationException
|
||||
@@ -107,16 +109,20 @@ class DoubleColonExpressionResolver(
|
||||
}
|
||||
else {
|
||||
val result = resolveDoubleColonLHS(expression, c)
|
||||
val type = result?.type
|
||||
if (type != null && !type.isError) {
|
||||
checkClassLiteral(c, expression, result)
|
||||
if (result != null && !result.type.isError) {
|
||||
val inherentType = result.type
|
||||
val dataFlowInfo = (result as? DoubleColonLHS.Expression)?.dataFlowInfo ?: c.dataFlowInfo
|
||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression.receiverExpression!!, inherentType, c)
|
||||
val type =
|
||||
if (!dataFlowInfo.getStableNullability(dataFlowValue).canBeNull()) inherentType.makeNotNullable()
|
||||
else inherentType
|
||||
checkClassLiteral(c, expression, type, result)
|
||||
val variance =
|
||||
if (result is DoubleColonLHS.Expression && !result.isObjectQualifier) Variance.OUT_VARIANCE else Variance.INVARIANT
|
||||
val kClassType = reflectionTypes.getKClassType(Annotations.EMPTY, type, variance)
|
||||
if (kClassType.isError) {
|
||||
c.trace.report(MISSING_DEPENDENCY_CLASS.on(expression.receiverExpression!!, KotlinBuiltIns.FQ_NAMES.kClass.toSafe()))
|
||||
}
|
||||
val dataFlowInfo = (result as? DoubleColonLHS.Expression)?.dataFlowInfo ?: c.dataFlowInfo
|
||||
return dataFlowAnalyzer.checkType(createTypeInfo(kClassType, dataFlowInfo), expression, c)
|
||||
}
|
||||
}
|
||||
@@ -124,9 +130,12 @@ class DoubleColonExpressionResolver(
|
||||
return createTypeInfo(ErrorUtils.createErrorType("Unresolved class"), c)
|
||||
}
|
||||
|
||||
private fun checkClassLiteral(c: ExpressionTypingContext, expression: KtClassLiteralExpression, result: DoubleColonLHS) {
|
||||
val type = result.type
|
||||
|
||||
private fun checkClassLiteral(
|
||||
c: ExpressionTypingContext,
|
||||
expression: KtClassLiteralExpression,
|
||||
type: KotlinType,
|
||||
result: DoubleColonLHS
|
||||
) {
|
||||
if (result is DoubleColonLHS.Expression) {
|
||||
if (!result.isObjectQualifier) {
|
||||
if (!type.isSubtypeOf(type.builtIns.anyType)) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// KT-16291 Smart cast doesn't work when getting class of instance
|
||||
|
||||
class Foo(val s: String) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other != null && other::class == this::class && s == (other as Foo).s
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
return if (Foo("a") == Foo("a")) "OK" else "Fail"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
@kotlin.Metadata
|
||||
public final class Foo {
|
||||
private final @org.jetbrains.annotations.NotNull field s: java.lang.String
|
||||
public method <init>(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
|
||||
public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean
|
||||
public final @org.jetbrains.annotations.NotNull method getS(): java.lang.String
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class SmartCastKt {
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// KT-16291 Smart cast doesn't work when getting class of instance
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class Foo {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other === null || other::class != this::class) return false
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
fun test(f: Foo?): KClass<out Foo>? = if (f != null) f::class else null
|
||||
|
||||
fun test2(): KClass<out Foo>? {
|
||||
var f: Foo? = null
|
||||
if (f != null) {
|
||||
run { f = null }
|
||||
return <!EXPRESSION_OF_NULLABLE_TYPE_IN_CLASS_LITERAL_LHS!>f<!>::class
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package
|
||||
|
||||
public fun test(/*0*/ f: Foo?): kotlin.reflect.KClass<out Foo>?
|
||||
public fun test2(): kotlin.reflect.KClass<out Foo>?
|
||||
|
||||
public final class Foo {
|
||||
public constructor Foo()
|
||||
public open override /*1*/ 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
@@ -2613,6 +2613,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classLiteral/bound/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartCast.kt")
|
||||
public void testSmartCast() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classLiteral/bound/smartCast.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/classLiteral/java")
|
||||
|
||||
@@ -3190,6 +3190,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartCast.kt")
|
||||
public void testSmartCast() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/classLiteral/smartCast.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("typealiases.kt")
|
||||
public void testTypealiases() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/classLiteral/typealiases.kt");
|
||||
|
||||
@@ -2613,6 +2613,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classLiteral/bound/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartCast.kt")
|
||||
public void testSmartCast() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classLiteral/bound/smartCast.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/classLiteral/java")
|
||||
|
||||
@@ -3190,6 +3190,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
}
|
||||
|
||||
@TestMetadata("smartCast.kt")
|
||||
public void testSmartCast() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classLiteral/bound/smartCast.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/classLiteral/java")
|
||||
|
||||
Reference in New Issue
Block a user