diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt index ef9bda7d3b9..2e1fd389be1 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt @@ -128,8 +128,12 @@ class ResultTypeResolver( newSupertypes.add(supertype) } - TypeUtils.getDefaultPrimitiveNumberType(numberSupertypes)?.let { - newSupertypes.add(it.unwrap()) + + val representativeNumberType = TypeUtils.getDefaultPrimitiveNumberType(numberSupertypes) + if (representativeNumberType != null) { + newSupertypes.add(representativeNumberType.unwrap()) + } else { + newSupertypes.addAll(numberSupertypes.map { it.unwrap() }) } intersectTypes(newSupertypes).makeNullableAsSpecified(commonSuperType.isMarkedNullable) diff --git a/compiler/testData/diagnostics/tests/generics/nullability/inferNotNullTypeFromIntersectionOfNullableTypes.kt b/compiler/testData/diagnostics/tests/generics/nullability/inferNotNullTypeFromIntersectionOfNullableTypes.kt new file mode 100644 index 00000000000..d07d1064a52 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/nullability/inferNotNullTypeFromIntersectionOfNullableTypes.kt @@ -0,0 +1,38 @@ +// !LANGUAGE: +NewInference + +fun simpleTypeAndNumberType(b: Comparable<*>?) { + if (b is Byte?) { + b!!.dec() + } +} + +fun typeParmeterAndNumberType(b: T?) { + if (b is Byte?) { + b!!.dec() + } +} + +fun anyAndNumberType(b: Any?) { + if (b is Byte?) { + b!!.dec() + } +} + +fun comparableAndNumberType(b: Comparable?) { + if (b is Byte?) { + b!!.dec() + } +} + +object SeparateTypes { + interface A + interface B { + fun foo() {} + } + + fun separate(a: A?) { + if (a is B?) { + a!!.foo() + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/generics/nullability/inferNotNullTypeFromIntersectionOfNullableTypes.txt b/compiler/testData/diagnostics/tests/generics/nullability/inferNotNullTypeFromIntersectionOfNullableTypes.txt new file mode 100644 index 00000000000..8f7d3d3508f --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/nullability/inferNotNullTypeFromIntersectionOfNullableTypes.txt @@ -0,0 +1,27 @@ +package + +public fun anyAndNumberType(/*0*/ b: kotlin.Any?): kotlin.Unit +public fun comparableAndNumberType(/*0*/ b: kotlin.Comparable?): kotlin.Unit +public fun simpleTypeAndNumberType(/*0*/ b: kotlin.Comparable<*>?): kotlin.Unit +public fun typeParmeterAndNumberType(/*0*/ b: T?): kotlin.Unit + +public object SeparateTypes { + private constructor SeparateTypes() + 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 separate(/*0*/ a: SeparateTypes.A?): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public interface A { + 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 B { + 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/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 8e591403c78..cdc21f7996f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -8420,6 +8420,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/generics/nullability/functionalBound.kt"); } + @TestMetadata("inferNotNullTypeFromIntersectionOfNullableTypes.kt") + public void testInferNotNullTypeFromIntersectionOfNullableTypes() throws Exception { + runTest("compiler/testData/diagnostics/tests/generics/nullability/inferNotNullTypeFromIntersectionOfNullableTypes.kt"); + } + @TestMetadata("nullToGeneric.kt") public void testNullToGeneric() throws Exception { runTest("compiler/testData/diagnostics/tests/generics/nullability/nullToGeneric.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 48fb9a64219..daa2fbab270 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -8420,6 +8420,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/generics/nullability/functionalBound.kt"); } + @TestMetadata("inferNotNullTypeFromIntersectionOfNullableTypes.kt") + public void testInferNotNullTypeFromIntersectionOfNullableTypes() throws Exception { + runTest("compiler/testData/diagnostics/tests/generics/nullability/inferNotNullTypeFromIntersectionOfNullableTypes.kt"); + } + @TestMetadata("nullToGeneric.kt") public void testNullToGeneric() throws Exception { runTest("compiler/testData/diagnostics/tests/generics/nullability/nullToGeneric.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/IntersectionTypeConstructor.kt b/core/descriptors/src/org/jetbrains/kotlin/types/IntersectionTypeConstructor.kt index d3a23fef7fa..b2092cbf6e3 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/IntersectionTypeConstructor.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/IntersectionTypeConstructor.kt @@ -21,7 +21,6 @@ import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.TypeIntersectionScope - import java.util.* class IntersectionTypeConstructor(typesToIntersect: Collection) : TypeConstructor { @@ -63,3 +62,22 @@ class IntersectionTypeConstructor(typesToIntersect: Collection) : Ty override fun hashCode(): Int = hashCode } + +inline fun IntersectionTypeConstructor.transformComponents( + predicate: (KotlinType) -> Boolean = { true }, + transform: (KotlinType) -> KotlinType +): IntersectionTypeConstructor? { + var changed = false + val newSupertypes = supertypes.map { + if (predicate(it)) { + changed = true + transform(it) + } else { + it + } + } + + if (!changed) return null + + return IntersectionTypeConstructor(newSupertypes) +} \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/SpecialTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/types/SpecialTypes.kt index 9b622ac8080..900137eae0d 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/SpecialTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/SpecialTypes.kt @@ -111,7 +111,28 @@ val KotlinType.isDefinitelyNotNullType: Boolean get() = unwrap() is DefinitelyNotNullType fun SimpleType.makeSimpleTypeDefinitelyNotNullOrNotNull(): SimpleType = - DefinitelyNotNullType.makeDefinitelyNotNull(this) ?: makeNullableAsSpecified(false) + DefinitelyNotNullType.makeDefinitelyNotNull(this) + ?: makeIntersectionTypeDefinitelyNotNullOrNotNull() + ?: makeNullableAsSpecified(false) fun UnwrappedType.makeDefinitelyNotNullOrNotNull(): UnwrappedType = - DefinitelyNotNullType.makeDefinitelyNotNull(this) ?: makeNullableAsSpecified(false) + DefinitelyNotNullType.makeDefinitelyNotNull(this) + ?: makeIntersectionTypeDefinitelyNotNullOrNotNull() + ?: makeNullableAsSpecified(false) + +private fun KotlinType.makeIntersectionTypeDefinitelyNotNullOrNotNull(): SimpleType? { + val typeConstructor = constructor as? IntersectionTypeConstructor ?: return null + val definitelyNotNullConstructor = typeConstructor.makeDefinitelyNotNullOrNotNull() ?: return null + + return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope( + annotations, + definitelyNotNullConstructor, + listOf(), + false, + definitelyNotNullConstructor.createScopeForKotlinType() + ) +} + +private fun IntersectionTypeConstructor.makeDefinitelyNotNullOrNotNull(): IntersectionTypeConstructor? { + return transformComponents({ TypeUtils.isNullableType(it) }, { it.unwrap().makeDefinitelyNotNullOrNotNull() }) +} diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt index ad25c01a2d0..f3986ffce5a 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt @@ -153,8 +153,7 @@ object NewKotlinTypeChecker : KotlinTypeChecker { } is IntersectionTypeConstructor -> if (type.isMarkedNullable) { - val newSuperTypes = constructor.supertypes.map { it.makeNullable() } - val newConstructor = IntersectionTypeConstructor(newSuperTypes) + val newConstructor = constructor.transformComponents(transform = { it.makeNullable() }) ?: constructor return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(type.annotations, newConstructor, listOf(), false, newConstructor.createScopeForKotlinType()) } }