Support smart cast for nullability in LHS of class literal

#KT-16291 Fixed
This commit is contained in:
Alexander Udalov
2017-02-15 13:43:46 +03:00
parent bae955aafd
commit ba84338862
9 changed files with 97 additions and 7 deletions
@@ -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
}
@@ -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")