diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt index 639a8ed6c4c..931f728a726 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt @@ -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)) { diff --git a/compiler/testData/codegen/box/classLiteral/bound/smartCast.kt b/compiler/testData/codegen/box/classLiteral/bound/smartCast.kt new file mode 100644 index 00000000000..7b66b1e1786 --- /dev/null +++ b/compiler/testData/codegen/box/classLiteral/bound/smartCast.kt @@ -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" +} diff --git a/compiler/testData/codegen/light-analysis/classLiteral/bound/smartCast.txt b/compiler/testData/codegen/light-analysis/classLiteral/bound/smartCast.txt new file mode 100644 index 00000000000..0c1565787cf --- /dev/null +++ b/compiler/testData/codegen/light-analysis/classLiteral/bound/smartCast.txt @@ -0,0 +1,12 @@ +@kotlin.Metadata +public final class Foo { + private final @org.jetbrains.annotations.NotNull field s: java.lang.String + public method (@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 +} diff --git a/compiler/testData/diagnostics/tests/classLiteral/smartCast.kt b/compiler/testData/diagnostics/tests/classLiteral/smartCast.kt new file mode 100644 index 00000000000..0bae9c28f6d --- /dev/null +++ b/compiler/testData/diagnostics/tests/classLiteral/smartCast.kt @@ -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? = if (f != null) f::class else null + +fun test2(): KClass? { + var f: Foo? = null + if (f != null) { + run { f = null } + return f::class + } + return null +} diff --git a/compiler/testData/diagnostics/tests/classLiteral/smartCast.txt b/compiler/testData/diagnostics/tests/classLiteral/smartCast.txt new file mode 100644 index 00000000000..61190c2312d --- /dev/null +++ b/compiler/testData/diagnostics/tests/classLiteral/smartCast.txt @@ -0,0 +1,11 @@ +package + +public fun test(/*0*/ f: Foo?): kotlin.reflect.KClass? +public fun test2(): kotlin.reflect.KClass? + +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 +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 3d601406d60..95e0a4c464e 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -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") diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 97f4d76063b..e2cf60e5579 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 9c6c6d99877..3693b948e46 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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") diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 4ccfa3d8576..9d2e862d972 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.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")