From a55c6f0c953298d711a5f70907cc3fbd2f52b26c Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Fri, 13 Oct 2017 14:43:42 +0300 Subject: [PATCH] Don't register safe cast type info for unstable values Important: to be removed in 1.3 So #KT-20752 Fixed --- .../smartcasts/DelegatingDataFlowInfo.kt | 20 +++++++--- .../tests/smartCasts/castchecks/impossible.kt | 25 ++++++++++++ .../smartCasts/castchecks/impossible.txt | 20 ++++++++++ .../tests/smartCasts/elvis/impossible.kt | 40 +++++++++++++++++++ .../tests/smartCasts/elvis/impossible.txt | 39 ++++++++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 12 ++++++ .../DiagnosticsUsingJavacTestGenerated.java | 12 ++++++ 7 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/smartCasts/castchecks/impossible.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/castchecks/impossible.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/elvis/impossible.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/elvis/impossible.txt 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 be4e324dd94..fbf5353e79d 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 @@ -96,9 +96,12 @@ internal class DelegatingDataFlowInfo private constructor( nullability: Nullability, languageVersionSettings: LanguageVersionSettings, typeInfo: SetMultimap? = null, - affectReceiver: Boolean = true + affectReceiver: Boolean = true, + recordUnstable: Boolean = true ): Boolean { - map.put(value, nullability) + if (value.isStable || recordUnstable) { + map.put(value, nullability) + } val identifierInfo = value.identifierInfo if (affectReceiver && !nullability.canBeNull() && @@ -108,7 +111,8 @@ internal class DelegatingDataFlowInfo private constructor( val receiverType = identifierInfo.receiverType if (identifierInfo.safe && receiverType != null) { val receiverValue = DataFlowValue(identifierInfo.receiverInfo, receiverType) - putNullabilityAndTypeInfo(map, receiverValue, nullability, languageVersionSettings, typeInfo) + putNullabilityAndTypeInfo(map, receiverValue, nullability, + languageVersionSettings, typeInfo, recordUnstable = recordUnstable) } } is IdentifierInfo.SafeCast -> { @@ -118,12 +122,16 @@ internal class DelegatingDataFlowInfo private constructor( languageVersionSettings.supportsFeature(LanguageFeature.SafeCastCheckBoundSmartCasts)) { val subjectValue = DataFlowValue(identifierInfo.subjectInfo, subjectType) - putNullabilityAndTypeInfo(map, subjectValue, nullability, languageVersionSettings, typeInfo) - typeInfo?.put(subjectValue, targetType) + putNullabilityAndTypeInfo(map, subjectValue, nullability, + languageVersionSettings, typeInfo, recordUnstable = false) + if (subjectValue.isStable) { + typeInfo?.put(subjectValue, targetType) + } } } is IdentifierInfo.Variable -> identifierInfo.bound?.let { - putNullabilityAndTypeInfo(map, it, nullability, languageVersionSettings, typeInfo) + putNullabilityAndTypeInfo(map, it, nullability, + languageVersionSettings, typeInfo, recordUnstable = recordUnstable) } } } diff --git a/compiler/testData/diagnostics/tests/smartCasts/castchecks/impossible.kt b/compiler/testData/diagnostics/tests/smartCasts/castchecks/impossible.kt new file mode 100644 index 00000000000..8948fe267e8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/castchecks/impossible.kt @@ -0,0 +1,25 @@ +// !LANGUAGE: +SafeCastCheckBoundSmartCasts +// See KT-20752 + +class Unstable { + val first: String? get() = null +} + +class StringList { + fun remove(s: String) = s +} + +fun StringList.remove(s: String?) = s ?: "" + +fun foo(list: StringList, arg: Unstable) { + list.remove(arg.first) + if (arg.first as? String != null) { + // Ideally should have smart cast impossible here + list.remove(arg.first) + } + val s = arg.first as? String + if (s != null) { + // Ideally should have smart cast impossible here + list.remove(arg.first) + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/castchecks/impossible.txt b/compiler/testData/diagnostics/tests/smartCasts/castchecks/impossible.txt new file mode 100644 index 00000000000..df3aada024a --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/castchecks/impossible.txt @@ -0,0 +1,20 @@ +package + +public fun foo(/*0*/ list: StringList, /*1*/ arg: Unstable): kotlin.Unit +public fun StringList.remove(/*0*/ s: kotlin.String?): kotlin.String + +public final class StringList { + public constructor StringList() + 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 final fun remove(/*0*/ s: kotlin.String): kotlin.String + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Unstable { + public constructor Unstable() + public final val first: kotlin.String? + 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 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/elvis/impossible.kt b/compiler/testData/diagnostics/tests/smartCasts/elvis/impossible.kt new file mode 100644 index 00000000000..738d3604e0e --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/elvis/impossible.kt @@ -0,0 +1,40 @@ +// !LANGUAGE: +BooleanElvisBoundSmartCasts +// See KT-20752 + +class Unstable { + val first: String? get() = null +} + +class StringList { + fun remove(s: String) = s +} + +fun StringList.remove(s: String?) = s ?: "" + +fun String.isEmpty() = this == "" + +fun foo(list: StringList, arg: Unstable) { + list.remove(arg.first) + if (arg.first?.isEmpty() ?: false) { + // Ideally should have smart cast impossible here + list.remove(arg.first) + } +} + +class UnstableBoolean { + val first: Boolean? get() = null +} + +class BooleanList { + fun remove(b: Boolean) = b +} + +fun BooleanList.remove(b: Boolean?) = b ?: false + +fun bar(list: BooleanList, arg: UnstableBoolean) { + list.remove(arg.first) + if (arg.first ?: false) { + // Ideally should have smart cast impossible here + list.remove(arg.first) + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/elvis/impossible.txt b/compiler/testData/diagnostics/tests/smartCasts/elvis/impossible.txt new file mode 100644 index 00000000000..d26ecf94d99 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/elvis/impossible.txt @@ -0,0 +1,39 @@ +package + +public fun bar(/*0*/ list: BooleanList, /*1*/ arg: UnstableBoolean): kotlin.Unit +public fun foo(/*0*/ list: StringList, /*1*/ arg: Unstable): kotlin.Unit +public fun kotlin.String.isEmpty(): kotlin.Boolean +public fun BooleanList.remove(/*0*/ b: kotlin.Boolean?): kotlin.Boolean +public fun StringList.remove(/*0*/ s: kotlin.String?): kotlin.String + +public final class BooleanList { + public constructor BooleanList() + 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 final fun remove(/*0*/ b: kotlin.Boolean): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class StringList { + public constructor StringList() + 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 final fun remove(/*0*/ s: kotlin.String): kotlin.String + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Unstable { + public constructor Unstable() + public final val first: kotlin.String? + 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 UnstableBoolean { + public constructor UnstableBoolean() + public final val first: kotlin.Boolean? + 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index ace190ab800..ade99189a63 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -20910,6 +20910,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("impossible.kt") + public void testImpossible() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/castchecks/impossible.kt"); + doTest(fileName); + } + @TestMetadata("insideCall.kt") public void testInsideCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/castchecks/insideCall.kt"); @@ -20942,6 +20948,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/elvis/basicOn.kt"); doTest(fileName); } + + @TestMetadata("impossible.kt") + public void testImpossible() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/elvis/impossible.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/inference") diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 6d23e5b9327..cf26eb2294e 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -20910,6 +20910,12 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing doTest(fileName); } + @TestMetadata("impossible.kt") + public void testImpossible() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/castchecks/impossible.kt"); + doTest(fileName); + } + @TestMetadata("insideCall.kt") public void testInsideCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/castchecks/insideCall.kt"); @@ -20942,6 +20948,12 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/elvis/basicOn.kt"); doTest(fileName); } + + @TestMetadata("impossible.kt") + public void testImpossible() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/elvis/impossible.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/inference")