diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java index 0005fffd962..181674aef7b 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java @@ -19529,6 +19529,11 @@ public class FirDiagnosticsSmokeTestGenerated extends AbstractFirDiagnosticsSmok runTest("compiler/testData/diagnostics/tests/smartCasts/kt2865.kt"); } + @TestMetadata("kt30826.kt") + public void testKt30826() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/kt30826.kt"); + } + @TestMetadata("kt3224.kt") public void testKt3224() throws Exception { runTest("compiler/testData/diagnostics/tests/smartCasts/kt3224.kt"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt index aa3a155b2ae..41422ed21da 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.resolve.calls.smartcasts import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.diagnostics.Errors @@ -33,6 +34,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.typeUtil.expandIntersectionTypeIfNecessary import java.util.* class SmartCastManager { @@ -178,8 +180,13 @@ class SmartCastManager { recordExpressionType: Boolean ): SmartCastResult? { val calleeExpression = call?.calleeExpression + val expectedTypes = if (c.languageVersionSettings.supportsFeature(LanguageFeature.NewInference)) + expectedType.expandIntersectionTypeIfNecessary() + else + listOf(expectedType) + for (possibleType in c.dataFlowInfo.getCollectedTypes(dataFlowValue, c.languageVersionSettings)) { - if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType) && + if (expectedTypes.any { ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, it) } && (additionalPredicate == null || additionalPredicate(possibleType)) ) { if (expression != null) { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/AdditionalDiagnosticReporter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/AdditionalDiagnosticReporter.kt index 99bd2a05188..c316fa1a7ca 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/AdditionalDiagnosticReporter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/AdditionalDiagnosticReporter.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.types.typeUtil.expandIntersectionTypeIfNecessary // very initial state of component // todo: handle all diagnostic inside DiagnosticReporterByTrackingStrategy @@ -43,10 +44,14 @@ class AdditionalDiagnosticReporter(private val languageVersionSettings: Language expectedResultType: UnwrappedType ): SmartCastDiagnostic? { if (argument !is ExpressionKotlinCallArgument) return null - if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(argument.receiver.receiverValue.type, expectedResultType)) { - return SmartCastDiagnostic(argument, expectedResultType.unwrap(), candidate.atom) - } - return null + + val types = expectedResultType.expandIntersectionTypeIfNecessary() + + val argumentType = argument.receiver.receiverValue.type + val isSubtype = types.map { KotlinTypeChecker.DEFAULT.isSubtypeOf(argumentType, it) } + if (isSubtype.any { it }) return null + + return SmartCastDiagnostic(argument, types.first().unwrap(), candidate.atom) } private fun reportSmartCastOnReceiver( diff --git a/compiler/testData/diagnostics/tests/generics/nullability/smartCasts.kt b/compiler/testData/diagnostics/tests/generics/nullability/smartCasts.kt index bf66a69ee2f..daa9c8164a1 100644 --- a/compiler/testData/diagnostics/tests/generics/nullability/smartCasts.kt +++ b/compiler/testData/diagnostics/tests/generics/nullability/smartCasts.kt @@ -30,9 +30,9 @@ fun foo(x: T) { x.length x?.length - x.bar1() + x.bar1() x.bar2() - x.bar3() + x.bar3() } if (x is CharSequence) { diff --git a/compiler/testData/diagnostics/tests/generics/nullability/smartCastsValueArgument.kt b/compiler/testData/diagnostics/tests/generics/nullability/smartCastsValueArgument.kt index 61c24054112..92486a37bc2 100644 --- a/compiler/testData/diagnostics/tests/generics/nullability/smartCastsValueArgument.kt +++ b/compiler/testData/diagnostics/tests/generics/nullability/smartCastsValueArgument.kt @@ -24,7 +24,7 @@ fun foo(x: T) { y1 = x y2 = x - bar1(x) + bar1(x) bar2(x) bar3(x) } diff --git a/compiler/testData/diagnostics/tests/smartCasts/intersectionScope/unstableSmartCast.kt b/compiler/testData/diagnostics/tests/smartCasts/intersectionScope/unstableSmartCast.kt index 161ddd49724..ac1fb2d44fb 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/intersectionScope/unstableSmartCast.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/intersectionScope/unstableSmartCast.kt @@ -31,8 +31,8 @@ fun test() { x.foo().checkType { _() } x.baz("") x.baz(1).checkType { _() } - x.baz(1, 2) + x.baz(1, 2) - x.foobar().checkType { _() } + x.foobar().checkType { _() } } } diff --git a/compiler/testData/diagnostics/tests/smartCasts/kt30826.kt b/compiler/testData/diagnostics/tests/smartCasts/kt30826.kt new file mode 100644 index 00000000000..e501dc31090 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/kt30826.kt @@ -0,0 +1,19 @@ +// !WITH_NEW_INFERENCE +// Issue: KT-30826 + +interface I1 +interface I2 { + fun foo() {} +} + +class A : I1, I2 + +fun foo(x: I1?) { + var y = x + y as I2 + val bar = { + y.foo() // NPE in NI, smartcast impossible in OI + } + y = null + bar() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/kt30826.txt b/compiler/testData/diagnostics/tests/smartCasts/kt30826.txt new file mode 100644 index 00000000000..0a20c5a9131 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/kt30826.txt @@ -0,0 +1,24 @@ +package + +public fun foo(/*0*/ x: I1?): kotlin.Unit + +public final class A : I1, I2 { + public constructor A() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface I1 { + 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 interface I2 { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(): kotlin.Unit + 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-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.12.kt b/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.12.kt index f012662e00a..d99b68e46b1 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.12.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.12.kt @@ -61,7 +61,7 @@ fun T?.case_5() { equals(this) itest1() - apply { + apply { this this equals(this) @@ -69,7 +69,7 @@ fun T?.case_5() { this.equals(this) this.itest1() } - also { + also { it it it.itest1() @@ -89,14 +89,14 @@ fun T?.case_6() { equals(this) itest1() - apply { + apply { this equals(this) itest1() this.equals(this) this.itest1() } - also { + also { it it.itest1() it.equals(it) @@ -114,14 +114,14 @@ fun T.case_7() { x.equals(this) x.itest1() - x.apply { + x.apply { this equals(this) itest1() this.equals(this) this.itest1() } - x.also { + x.also { it it.itest1() it.equals(it) @@ -140,14 +140,14 @@ fun T.case_8() { equals(this) itest1() - apply { + apply { this equals(this) itest1() this.equals(this) this.itest1() } - also { + also { it it.itest1() it.equals(it) @@ -214,14 +214,14 @@ fun T?.case_11() { equals(this) itest1() - apply { + apply { this equals(this) itest1() this.equals(this) this.itest1() } - also { + also { it it.itest1() it.equals(it) @@ -1706,11 +1706,11 @@ fun T.case_65() { if (this != null) { this this.equals(x) - apply { + apply { this this.equals(this) } - also { + also { it it.equals(it) } diff --git a/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.13.kt b/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.13.kt index da81b2d6e9f..f7c4735cd50 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.13.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.13.kt @@ -65,7 +65,7 @@ fun case_5(x: T?) { x.equals(x) x.itest() - x.apply { + x.apply { this this equals(this) @@ -73,7 +73,7 @@ fun case_5(x: T?) { this.equals(x) this.itest() } - x.also { + x.also { it it it.itest() @@ -93,14 +93,14 @@ fun case_6(x: T?) { x.equals(x) x.itest() - x.apply { + x.apply { this equals(this) itest() this.equals(x) this.itest() } - x.also { + x.also { it it.itest() it.equals(it) @@ -118,14 +118,14 @@ fun case_7(y: T) { x.equals(x) x.itest() - x.apply { + x.apply { this equals(this) itest() this.equals(x) this.itest() } - x.also { + x.also { it it.itest() it.equals(it) @@ -144,14 +144,14 @@ fun case_8(x: T) { x.equals(x) x.itest() - x.apply { + x.apply { this equals(this) itest() this.equals(x) this.itest() } - x.also { + x.also { it it.itest() it.equals(it) @@ -218,14 +218,14 @@ fun case_11(x: T?) { x.equals(x) x.itest() - x.apply { + x.apply { this equals(this) itest() this.equals(x) this.itest() } - x.also { + x.also { it it.itest() it.equals(it) diff --git a/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.5.kt b/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.5.kt index ac6114aa209..b02605ca9cb 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.5.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.5.kt @@ -188,7 +188,7 @@ fun case_11(x: Any?) { x.itest1() x.itest2() - x.let { it.itest1(); it.itest2() } + x.let { it.itest1(); it.itest2() } } } } @@ -206,7 +206,7 @@ fun case_12(x: Any?) { x.itest1() x.itest2() - x.let { it.itest1(); it.itest2() } + x.let { it.itest1(); it.itest2() } } } diff --git a/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.6.kt b/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.6.kt index d0e41bd8e07..83ec484c871 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.6.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/type-inference/smart-casts/smart-casts-sources/p-4/pos/1.6.kt @@ -903,7 +903,7 @@ fun case_71(t: Any?) { t.itest2() t.itest() - t.let { it.itest1(); it.itest2() } + t.let { it.itest1(); it.itest2() } } } } @@ -924,7 +924,7 @@ fun case_72(t: Any?, z1: Nothing?) { t.itest2() t.itest() - t.let { it.itest1(); it.itest2() } + t.let { it.itest1(); it.itest2() } } } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index fee2db0232a..b015a645273 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -19606,6 +19606,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/smartCasts/kt2865.kt"); } + @TestMetadata("kt30826.kt") + public void testKt30826() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/kt30826.kt"); + } + @TestMetadata("kt3224.kt") public void testKt3224() throws Exception { runTest("compiler/testData/diagnostics/tests/smartCasts/kt3224.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index ec94134201c..e7fd466611c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -19531,6 +19531,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/smartCasts/kt2865.kt"); } + @TestMetadata("kt30826.kt") + public void testKt30826() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/kt30826.kt"); + } + @TestMetadata("kt3224.kt") public void testKt3224() throws Exception { runTest("compiler/testData/diagnostics/tests/smartCasts/kt3224.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index b888751cda2..3f074cbc687 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -243,3 +243,12 @@ val TypeParameterDescriptor.representativeUpperBound: KotlinType } ?: upperBounds.first() } +fun KotlinType.expandIntersectionTypeIfNecessary(): Collection { + if (constructor !is IntersectionTypeConstructor) return listOf(this) + val types = constructor.supertypes + return if (isMarkedNullable) { + types.map { it.makeNullable() } + } else { + types + } +} \ No newline at end of file