diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.kt index b3f033290ce..f2d96b12735 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.kt @@ -73,9 +73,8 @@ interface DataFlowInfo { /** * Call this function when it's known than a == b. - * sameTypes should be true iff we have guarantee that a and b have the same type */ - fun equate(a: DataFlowValue, b: DataFlowValue, sameTypes: Boolean, languageVersionSettings: LanguageVersionSettings): DataFlowInfo + fun equate(a: DataFlowValue, b: DataFlowValue, identityEquals: Boolean, languageVersionSettings: LanguageVersionSettings): DataFlowInfo /** * Call this function when it's known than a != b diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.kt index 7a068811262..9f0ed04500a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.kt @@ -169,7 +169,7 @@ internal class DelegatingDataFlowInfo private constructor( } override fun equate( - a: DataFlowValue, b: DataFlowValue, sameTypes: Boolean, languageVersionSettings: LanguageVersionSettings + a: DataFlowValue, b: DataFlowValue, identityEquals: Boolean, languageVersionSettings: LanguageVersionSettings ): DataFlowInfo { val builder = Maps.newHashMap() val nullabilityOfA = getStableNullability(a) @@ -180,7 +180,7 @@ internal class DelegatingDataFlowInfo private constructor( // NB: == has no guarantees of type equality, see KT-11280 for the example val newTypeInfo = newTypeInfo() - if (sameTypes) { + if (identityEquals || !nullabilityOfA.canBeNonNull() || !nullabilityOfB.canBeNonNull()) { newTypeInfo.putAll(a, getStableTypes(b, false)) newTypeInfo.putAll(b, getStableTypes(a, false)) if (a.type != b.type) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java index e54c25f488a..34f5b265295 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java @@ -178,10 +178,11 @@ public class DataFlowAnalyzer { } if (equals != null) { if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue - boolean byIdentity = operationToken == KtTokens.EQEQEQ || operationToken == KtTokens.EXCLEQEQEQ || - typeHasEqualsFromAny(lhsType, condition); + boolean identityEquals = operationToken == KtTokens.EQEQEQ || + operationToken == KtTokens.EXCLEQEQEQ || + typeHasEqualsFromAny(lhsType, condition); result.set(context.dataFlowInfo - .equate(leftValue, rightValue, byIdentity, languageVersionSettings) + .equate(leftValue, rightValue, identityEquals, languageVersionSettings) .and(expressionFlowInfo)); } else { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt index 0783615369a..a49b9f1ef42 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt @@ -407,8 +407,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping val result = noChange(newContext) return ConditionalDataFlowInfo( result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue, - DataFlowAnalyzer.typeHasEqualsFromAny(subjectType, expression), - components.languageVersionSettings), + identityEquals = DataFlowAnalyzer.typeHasEqualsFromAny(subjectType, expression), + languageVersionSettings = components.languageVersionSettings), result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue, components.languageVersionSettings) diff --git a/compiler/testData/diagnostics/tests/smartCasts/equals.kt b/compiler/testData/diagnostics/tests/smartCasts/equals.kt new file mode 100644 index 00000000000..b05e0e80aaa --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/equals.kt @@ -0,0 +1,25 @@ +fun foo(x: String?) = x + +class Test + +class TestWithEquals { + override fun equals(other: Any?) = super.equals(other) +} + +fun bar(i: Test?) { + if (i == null) foo(i) +} + +fun bar(i: TestWithEquals?) { + if (i == null) foo(i) + if (null == i) foo(i) + when (i) { + null -> foo(i) + } +} + +fun gav(i: TestWithEquals?, j: TestWithEquals?) { + if (j == null) { + if (i == j) foo(i) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/equals.txt b/compiler/testData/diagnostics/tests/smartCasts/equals.txt new file mode 100644 index 00000000000..76b063c9af2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/equals.txt @@ -0,0 +1,20 @@ +package + +public fun bar(/*0*/ i: Test?): kotlin.Unit +public fun bar(/*0*/ i: TestWithEquals?): kotlin.Unit +public fun foo(/*0*/ x: kotlin.String?): kotlin.String? +public fun gav(/*0*/ i: TestWithEquals?, /*1*/ j: TestWithEquals?): kotlin.Unit + +public final class Test { + public constructor Test() + public open override /*1*/ /*fake_override*/ 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 +} + +public final class TestWithEquals { + public constructor TestWithEquals() + 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/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 61cd16f6342..245ebac10f2 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -19132,6 +19132,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("equals.kt") + public void testEquals() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/equals.kt"); + doTest(fileName); + } + @TestMetadata("exclUnderAnd.kt") public void testExclUnderAnd() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/exclUnderAnd.kt");